Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
/*****************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.0 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
* License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is Curl.
*
* The Initial Developer of the Original Code is Daniel Stenberg.
*
* Portions created by the Initial Developer are Copyright (C) 1998.
* All Rights Reserved.
*
* ------------------------------------------------------------
* Main author:
* - Daniel Stenberg <Daniel.Stenberg@haxx.nu>
*
* http://curl.haxx.nu
*
* $Source$
* $Revision$
* $Date$
* $Author$
* $State$
* $Locker$
*
* ------------------------------------------------------------
****************************************************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#include "setup.h"
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
#include <winsock.h>
#else /* some kind of unix */
#include <netinet/in.h>
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#include <sys/utsname.h>
#include <netdb.h>
#endif
#if defined(WIN32) && defined(__GNUC__) || defined(__MINGW32__)
#include <errno.h>
#endif
#ifdef HAVE_INET_NTOA_R
#include "inet_ntoa_r.h"
#endif
#include <curl/curl.h>
#include "urldata.h"
#include "sendf.h"
#include "if2ip.h"
#include "hostip.h"
#include "progress.h"
#include "download.h"
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
/* returns last node in linked list */
static struct curl_slist *slist_get_last(struct curl_slist *list)
{
struct curl_slist *item;
/* if caller passed us a NULL, return now */
if (!list)
return NULL;
/* loop through to find the last item */
item = list;
while (item->next) {
item = item->next;
}
return item;
}
/* append a struct to the linked list. It always retunrs the address of the
* first record, so that you can sure this function as an initialization
* function as well as an append function. If you find this bothersome,
* then simply create a separate _init function and call it appropriately from
* within the proram. */
struct curl_slist *curl_slist_append(struct curl_slist *list, char *data)
{
struct curl_slist *last;
struct curl_slist *new_item;
new_item = (struct curl_slist *) malloc(sizeof(struct curl_slist));
if (new_item) {
new_item->next = NULL;
new_item->data = strdup(data);
}
else {
fprintf(stderr, "Cannot allocate memory for QUOTE list.\n");
exit(-1);
}
if (list) {
last = slist_get_last(list);
last->next = new_item;
return list;
}
/* if this is the first item, then new_item *is* the list */
return new_item;
}
/* be nice and clean up resources */
void curl_slist_free_all(struct curl_slist *list)
{
struct curl_slist *next;
struct curl_slist *item;
if (!list)
return;
item = list;
do {
next = item->next;
if (item->data) {
free(item->data);
}
free(item);
item = next;
} while (next);
}
static CURLcode AllowServerConnect(struct UrlData *data,
int sock)
{
fd_set rdset;
struct timeval dt;
FD_ZERO(&rdset);
FD_SET(sock, &rdset);
/* we give the server 10 seconds to connect to us */
dt.tv_sec = 10;
dt.tv_usec = 0;
switch ( select(sock+1, &rdset, NULL, NULL, &dt)) {
case -1: /* error */
/* let's die here */
failf(data, "Error while waiting for server connect");
case 0: /* timeout */
/* let's die here */
failf(data, "Timeout while waiting for server connect");
default:
/* we have received data here */
{
int s;
size_t size = sizeof(struct sockaddr_in);
struct sockaddr_in add;
getsockname(sock, (struct sockaddr *) &add, (int *)&size);
s=accept(sock, (struct sockaddr *) &add, (int *)&size);
if( -1 == s) {
/* DIE! */
failf(data, "Error accept()ing server connect");
}
infof(data, "Connection accepted from server\n");
data->secondarysocket = s;
}
break;
}
}
/* --- parse FTP server responses --- */
#define lastline(line) (isdigit((int)line[0]) && isdigit((int)line[1]) && \
isdigit((int)line[2]) && (' ' == line[3]))
int GetLastResponse(int sockfd, char *buf,
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
struct UrlData *data)
{
int nread;
int read_rc=1;
char *ptr;
do {
ptr=buf;
/* get us a full line, terminated with a newline */
for(nread=0;
(nread<BUFSIZE) && read_rc;
nread++, ptr++) {
#ifdef USE_SSLEAY
if (data->use_ssl) {
read_rc = SSL_read(data->ssl, ptr, 1);
}
else {
#endif
read_rc = sread(sockfd, ptr, 1);
#ifdef USE_SSLEAY
}
#endif /* USE_SSLEAY */
if (*ptr == '\n')
break;
}
*ptr=0; /* zero terminate */
fputs("< ", data->err);
fwrite(buf, 1, nread, data->err);
fputs("\n", data->err);
}
} while(read_rc &&
(nread<4 || !lastline(buf)) );
return nread;
}
/* -- who are we? -- */
#if defined(HAVE_GETHOSTNAME)
gethostname(buf, buf_size);
#elif defined(HAVE_UNAME)
strncpy(buf, uname(&ugnm) < 0 ? "localhost" : ugnm.nodename, buf_size - 1);
buf[buf_size - 1] = '\0';
#else
/* We have no means of finding the local host name! */
strncpy(buf, "localhost", buf_size);
buf[buf_size - 1] = '\0';
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
}
#if 0
/*
* URLfix()
*
* This function returns a string converted FROM the input URL format to a
* format that is more likely usable for the remote server. That is, all
* special characters (found as %XX-codes) will be eascaped with \<letter>.
*/
static char *URLfix(char *string)
{
/* The length of the new string can't be longer than twice the original
string, if all letters are '+'... */
int alloc = strlen(string)*2;
char *ns = malloc(alloc);
unsigned char in;
int index=0;
int hex;
while(*string) {
in = *string;
switch(in) {
case '+':
ns[index++] = '\\';
ns[index++] = ' ';
string++;
continue;
case '%':
/* encoded part */
if(sscanf(string+1, "%02X", &hex)) {
ns[index++] = '\\';
ns[index++] = hex;
string+=3;
continue;
}
/* FALLTHROUGH */
default:
ns[index++] = in;
string++;
}
}
ns[index]=0; /* terminate it */
return ns;
}
#endif
/* ftp_connect() should do everything that is to be considered a part
of the connection phase. */
CURLcode ftp_connect(struct connectdata *conn)
{
/* this is FTP and no proxy */
size_t nread;
ftp = (struct FTP *)malloc(sizeof(struct FTP));
if(!ftp)
return CURLE_OUT_OF_MEMORY;
memset(ftp, 0, sizeof(struct FTP));
data->proto.ftp = ftp;
/* get some initial data into the ftp struct */
ftp->bytecountp = &conn->bytecount;
ftp->user = data->user;
ftp->passwd = data->passwd;
/* The first thing we do is wait for the "220*" line: */
nread = GetLastResponse(data->firstsocket, buf, data);
if(strncmp(buf, "220", 3)) {
failf(data, "This doesn't seem like a nice ftp-server response");
return CURLE_FTP_WEIRD_SERVER_REPLY;
sendf(data->firstsocket, data, "USER %s\r\n", ftp->user);
/* wait for feedback */
nread = GetLastResponse(data->firstsocket, buf, data);
if(!strncmp(buf, "530", 3)) {
/* 530 User ... access denied
(the server denies to log the specified user) */
failf(data, "Access denied: %s", &buf[4]);
}
else if(!strncmp(buf, "331", 3)) {
/* 331 Password required for ...
(the server requires to send the user's password too) */
sendf(data->firstsocket, data, "PASS %s\r\n", ftp->passwd);
nread = GetLastResponse(data->firstsocket, buf, data);
if(!strncmp(buf, "530", 3)) {
/* 530 Login incorrect.
(the username and/or the password are incorrect) */
failf(data, "the username and/or the password are incorrect");
return CURLE_FTP_USER_PASSWORD_INCORRECT;
}
else if(!strncmp(buf, "230", 3)) {
/* 230 User ... logged in.
(user successfully logged in) */
infof(data, "We have successfully logged in\n");
}
else {
failf(data, "Odd return code after PASS");
}
}
else if(! strncmp(buf, "230", 3)) {
/* 230 User ... logged in.
(the user logged in without password) */
infof(data, "We have successfully logged in\n");
}
else {
failf(data, "Odd return code after USER");
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
return CURLE_FTP_WEIRD_USER_REPLY;
}
return CURLE_OK;
}
/* argument is already checked for validity */
CURLcode ftp_done(struct connectdata *conn)
{
struct UrlData *data = conn->data;
struct FTP *ftp = data->proto.ftp;
size_t nread;
char *buf = data->buffer; /* this is our buffer */
struct curl_slist *qitem; /* QUOTE item */
if(data->bits.upload) {
if((-1 != data->infilesize) && (data->infilesize != *ftp->bytecountp)) {
failf(data, "Wrote only partial file (%d out of %d bytes)",
*ftp->bytecountp, data->infilesize);
return CURLE_PARTIAL_FILE;
}
}
else {
if((-1 != conn->size) && (conn->size != *ftp->bytecountp) &&
(data->maxdownload != *ftp->bytecountp)) {
failf(data, "Received only partial file");
return CURLE_PARTIAL_FILE;
}
else if(0 == *ftp->bytecountp) {
failf(data, "No data was received!");
return CURLE_FTP_COULDNT_RETR_FILE;
}
}
/* shut down the socket to inform the server we're done */
sclose(data->secondarysocket);
data->secondarysocket = -1;
/* now let's see what the server says about the transfer we
just performed: */
nread = GetLastResponse(data->firstsocket, buf, data);
/* 226 Transfer complete */
if(strncmp(buf, "226", 3)) {
failf(data, "%s", buf+4);
return CURLE_FTP_WRITE_ERROR;
}
/* Send any post-transfer QUOTE strings? */
if(data->postquote) {
qitem = data->postquote;
/* Send all QUOTE strings in same order as on command-line */
while (qitem) {
/* Send string */
if (qitem->data) {
sendf(data->firstsocket, data, "%s\r\n", qitem->data);
nread = GetLastResponse(data->firstsocket, buf, data);
if (buf[0] != '2') {
failf(data, "QUOT string not accepted: %s",
qitem->data);
return CURLE_FTP_QUOTE_ERROR;
}
}
qitem = qitem->next;
}
if(ftp->file)
free(ftp->file);
if(ftp->dir)
free(ftp->dir);
/* TBD: the ftp struct is still allocated here */
return CURLE_OK;
}
static
CURLcode _ftp(struct connectdata *conn)
{
/* this is FTP and no proxy */
size_t nread;
CURLcode result;
struct UrlData *data=conn->data;
char *buf = data->buffer; /* this is our buffer */
/* for the ftp PORT mode */
int portsock=-1;
struct sockaddr_in serv_addr;
char hostent_buf[512];
#if defined (HAVE_INET_NTOA_R)
char ntoa_buf[64];
#endif
struct curl_slist *qitem; /* QUOTE item */
/* the ftp struct is already inited in ftp_connect() */
struct FTP *ftp = data->proto.ftp;
long *bytecountp = ftp->bytecountp;
/* Send any QUOTE strings? */
if(data->quote) {
qitem = data->quote;
/* Send all QUOTE strings in same order as on command-line */
while (qitem) {
/* Send string */
if (qitem->data) {
sendf(data->firstsocket, data, "%s\r\n", qitem->data);
nread = GetLastResponse(data->firstsocket, buf, data);
if (buf[0] != '2') {
failf(data, "QUOT string not accepted: %s",
qitem->data);
}
}
qitem = qitem->next;
}
}
/* If we have selected NOBODY, it means that we only want file information.
Which in FTP can't be much more than the file size! */
/* The SIZE command is _not_ RFC 959 specified, and therefor many servers
may not support it! It is however the only way we have to get a file's
size! */
int filesize;
sendf(data->firstsocket, data, "SIZE %s\r\n", ftp->file);
nread = GetLastResponse(data->firstsocket, buf, data);
if(strncmp(buf, "213", 3)) {
failf(data, "Couldn't get file size: %s", buf+4);
}
/* get the size from the ascii string: */
filesize = atoi(buf+4);
sprintf(buf, "Content-Length: %d\n", filesize);
if(strlen(buf) != data->fwrite(buf, 1, strlen(buf), data->out)) {
failf (data, "Failed writing output");
}
if(data->writeheader) {
/* the header is requested to be written to this file */
if(strlen(buf) != data->fwrite (buf, 1, strlen(buf),
data->writeheader)) {
}
/* We have chosen to use the PORT command */
struct sockaddr_in sa;
struct hostent *h=NULL;
size_t size;
unsigned short porttouse;
if(if2ip(data->ftpport, myhost, sizeof(myhost))) {
h = GetHost(data, myhost, hostent_buf, sizeof(hostent_buf));
h = GetHost(data, data->ftpport, hostent_buf, sizeof(hostent_buf));
if(! *myhost) {
h=GetHost(data, getmyhost(myhost,sizeof(myhost)), hostent_buf, sizeof(hostent_buf));
}
infof(data, "We connect from %s\n", myhost);
if ( h ) {
if( (portsock = socket(AF_INET, SOCK_STREAM, 0)) >= 0 ) {
memset((char *)&sa, 0, sizeof(sa));
memcpy((char *)&sa.sin_addr,
h->h_addr,
h->h_length);
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = INADDR_ANY;
sa.sin_port = 0;
size = sizeof(sa);
if(bind(portsock, (struct sockaddr *)&sa, size) >= 0) {
/* we succeeded to bind */
struct sockaddr_in add;
size = sizeof(add);
if(getsockname(portsock, (struct sockaddr *) &add,
(int *)&size)<0) {
failf(data, "getsockname() failed");
}
porttouse = ntohs(add.sin_port);
if ( listen(portsock, 1) < 0 ) {
failf(data, "listen(2) failed on socket");
}
}
else {
failf(data, "bind(2) failed on socket");
}
}
else {
failf(data, "socket(2) failed (%s)");
}
}
else {
failf(data, "could't find my own IP address (%s)", myhost);
}
{
struct in_addr in;
unsigned short ip[5];
(void) memcpy(&in.s_addr, *h->h_addr_list, sizeof (in.s_addr));
#if defined (HAVE_INET_NTOA_R)
sscanf( inet_ntoa_r(in, ntoa_buf, sizeof(ntoa_buf)), "%hu.%hu.%hu.%hu",
&ip[0], &ip[1], &ip[2], &ip[3]);
#else
sscanf( inet_ntoa(in), "%hu.%hu.%hu.%hu",
&ip[0], &ip[1], &ip[2], &ip[3]);
sendf(data->firstsocket, data, "PORT %d,%d,%d,%d,%d,%d\r\n",
ip[0], ip[1], ip[2], ip[3],
porttouse >> 8,
porttouse & 255);
}
nread = GetLastResponse(data->firstsocket, buf, data);
if(strncmp(buf, "200", 3)) {
failf(data, "Server does not grok PORT, try without it!");
}
}
else { /* we use the PASV command */
sendf(data->firstsocket, data, "PASV\r\n");
nread = GetLastResponse(data->firstsocket, buf, data);
if(strncmp(buf, "227", 3)) {
failf(data, "Odd return code after PASV");
}
else {
int ip[4];
int port[2];
unsigned short newport;
char newhost[32];
struct hostent *he;
/*
* New 227-parser June 3rd 1999.
* It now scans for a sequence of six comma-separated numbers and
* will take them as IP+port indicators.
*
* Found reply-strings include:
* "227 Entering Passive Mode (127,0,0,1,4,51)"
* "227 Data transfer will passively listen to 127,0,0,1,4,51"
* "227 Entering passive mode. 127,0,0,1,4,51"
*/
while(*str) {
if (6 == sscanf(str, "%d,%d,%d,%d,%d,%d",
&ip[0], &ip[1], &ip[2], &ip[3],
&port[0], &port[1]))
break;
str++;
}
if(!*str) {
failf(data, "Couldn't interpret this 227-reply: %s", buf);
}
sprintf(newhost, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
he = GetHost(data, newhost, hostent_buf, sizeof(hostent_buf));
if(!he) {
failf(data, "Can't resolve new host %s", newhost);
}
newport = (port[0]<<8) + port[1];
data->secondarysocket = socket(AF_INET, SOCK_STREAM, 0);
memset((char *) &serv_addr, '\0', sizeof(serv_addr));
memcpy((char *)&(serv_addr.sin_addr), he->h_addr, he->h_length);
serv_addr.sin_family = he->h_addrtype;
serv_addr.sin_port = htons(newport);
#if defined(HAVE_INET_ADDR)
# if defined(HAVE_GETHOSTBYADDR_R)
# if defined(HAVE_GETHOSTBYADDR_R)
# if (GETHOSTBYADDR_R_NARGS < 8)
/* Solaris and IRIX */
answer = gethostbyaddr_r((char *) &address, sizeof(address), AF_INET,
(struct hostent *)hostent_buf,
hostent_buf + sizeof(*answer),
sizeof(hostent_buf) - sizeof(*answer),
&h_errnop);
# else
/* Linux style */
if(gethostbyaddr_r((char *) &address, sizeof(address), AF_INET,
(struct hostent *)hostent_buf,
hostent_buf + sizeof(*answer),
sizeof(hostent_buf) - sizeof(*answer),
&answer,
&h_errnop))
answer=NULL; /* error */
# endif
# else
answer = gethostbyaddr((char *) &address, sizeof(address), AF_INET);
#else
answer = NULL;
#endif
(void) memcpy(&in.s_addr, *he->h_addr_list, sizeof (in.s_addr));
infof(data, "Connecting to %s (%s) port %u\n",
answer?answer->h_name:newhost,
#if defined(HAVE_INET_NTOA_R)
ip_addr = inet_ntoa_r(in, ntoa_buf, sizeof(ntoa_buf)),
}
if (connect(data->secondarysocket, (struct sockaddr *) &serv_addr,
sizeof(serv_addr)) < 0) {
switch(errno) {
#ifdef ECONNREFUSED
/* this should be made nicer */
case ECONNREFUSED:
failf(data, "Connection refused by ftp server");
break;
#endif
#ifdef EINTR
case EINTR:
failf(data, "Connection timeouted to ftp server");
break;
#endif
default:
failf(data, "Can't connect to ftp server");
break;
}
}
}
}
/* we have the (new) data connection ready */
/* change directory first */
if(ftp->dir && ftp->dir[0]) {
sendf(data->firstsocket, data, "CWD %s\r\n", ftp->dir);
nread = GetLastResponse(data->firstsocket, buf, data);
if(strncmp(buf, "250", 3)) {
failf(data, "Couldn't change to directory %s", ftp->dir);
return CURLE_FTP_ACCESS_DENIED;
}
}
if(data->bits.upload) {
/* Set type to binary (unless specified ASCII) */
sendf(data->firstsocket, data, "TYPE %s\r\n",
nread = GetLastResponse(data->firstsocket, buf, data);
if(strncmp(buf, "200", 3)) {
failf(data, "Couldn't set %s mode",
(data->bits.ftp_ascii)?"ASCII":"binary");
return (data->bits.ftp_ascii)? CURLE_FTP_COULDNT_SET_ASCII:
CURLE_FTP_COULDNT_SET_BINARY;
}
if(data->resume_from) {
/* we're about to continue the uploading of a file */
/* 1. get already existing file's size. We use the SIZE
command for this which may not exist in the server!
The SIZE command is not in RFC959. */
/* 2. This used to set REST. But since we can do append, we
don't another ftp command. We just skip the source file
offset and then we APPEND the rest on the file instead */
/* 3. pass file-size number of bytes in the source file */
/* 4. lower the infilesize counter */
/* => transfer as usual */
if(data->resume_from < 0 ) {
/* we could've got a specified offset from the command line,
but now we know we didn't */
sendf(data->firstsocket, data, "SIZE %s\r\n", ftp->file);
nread = GetLastResponse(data->firstsocket, buf, data);
if(strncmp(buf, "213", 3)) {
failf(data, "Couldn't get file size: %s", buf+4);
}
/* get the size from the ascii string: */
data->resume_from = atoi(buf+4);
}
if(data->resume_from) {
/* do we still game? */
int passed=0;
#if 0
/* Set resume file transfer offset */
infof(data, "Instructs server to resume from offset %d\n",
data->resume_from);
sendf(data->firstsocket, data, "REST %d\r\n", data->resume_from);
nread = GetLastResponse(data->firstsocket, buf, data);
if(strncmp(buf, "350", 3)) {
failf(data, "Couldn't use REST: %s", buf+4);
#endif
/* Now, let's read off the proper amount of bytes from the
input. If we knew it was a proper file we could've just
fseek()ed but we only have a stream here */
do {
int readthisamountnow = (data->resume_from - passed);
int actuallyread;
if(readthisamountnow > BUFSIZE)
readthisamountnow = BUFSIZE;
actuallyread =
data->fread(data->buffer, 1, readthisamountnow, data->in);
passed += actuallyread;
if(actuallyread != readthisamountnow) {
failf(data, "Could only read %d bytes from the input\n",
passed);
}
}
while(passed != data->resume_from);
/* now, decrease the size of the read */
if(data->infilesize>0) {
data->infilesize -= data->resume_from;
if(data->infilesize <= 0) {
infof(data, "File already completely uploaded\n");
}
}
/* we've passed, proceed as normal */
}
}
/* Send everything on data->in to the socket */
/* we append onto the file instead of rewriting it */
sendf(data->firstsocket, data, "APPE %s\r\n", ftp->file);
sendf(data->firstsocket, data, "STOR %s\r\n", ftp->file);
nread = GetLastResponse(data->firstsocket, buf, data);
if(atoi(buf)>=400) {
failf(data, "Failed FTP upload:%s", buf+3);
/* oops, we never close the sockets! */
result = AllowServerConnect(data, portsock);
if( result )
return result;
}
*bytecountp=0;
/* When we know we're uploading a specified file, we can get the file
size prior to the actual upload. */
pgrsSetUploadSize(data, data->infilesize);
#if 0
result = Transfer(conn, -1, -1, FALSE, NULL, /* no download */
data->secondarysocket, bytecountp);
if(result)
return result;
}
else {
/* Retrieve file or directory */
bool dirlist=FALSE;
long downloadsize=-1;
if(data->bits.set_range && data->range) {
long from, to;
int totalsize=-1;
char *ptr;
char *ptr2;
from=strtol(data->range, &ptr, 0);
while(ptr && *ptr && (isspace((int)*ptr) || (*ptr=='-')))
ptr++;
to=strtol(ptr, &ptr2, 0);
if(ptr == ptr2) {
/* we didn't get any digit */
to=-1;
}
infof(data, "FTP RANGE %d to end of file\n", from);
totalsize = -from;
data->maxdownload = -from;
data->resume_from = from;
infof(data, "FTP RANGE the last %d bytes\n", totalsize);
data->maxdownload = totalsize+1; /* include the last mentioned byte */
data->resume_from = from;
infof(data, "FTP RANGE from %d getting %d bytes\n", from, data->maxdownload);
}
infof(data, "range-download from %d to %d, totally %d bytes\n",
from, to, totalsize);
}
if(!ppath[0])
/* make sure this becomes a valid name */
ppath="./";
#endif
if((data->bits.ftp_list_only) || !ftp->file) {
/* The specified path ends with a slash, and therefore we think this
is a directory that is requested, use LIST. But before that we
need to set ASCII transfer mode. */
dirlist = TRUE;
/* Set type to ASCII */
sendf(data->firstsocket, data, "TYPE A\r\n");
nread = GetLastResponse(data->firstsocket, buf, data);
if(strncmp(buf, "200", 3)) {
failf(data, "Couldn't set ascii mode");
}
/* if this output is to be machine-parsed, the NLST command will be
better used since the LIST command output is not specified or
standard in any way */
sendf(data->firstsocket, data, "%s\r\n",
(data->bits.ftp_list_only?"NLST":"LIST"));
}
else {
/* Set type to binary (unless specified ASCII) */
sendf(data->firstsocket, data, "TYPE %s\r\n",
(data->bits.ftp_list_only)?"A":"I");
nread = GetLastResponse(data->firstsocket, buf, data);
if(strncmp(buf, "200", 3)) {
failf(data, "Couldn't set %s mode",
(data->bits.ftp_ascii)?"ASCII":"binary");
return (data->bits.ftp_ascii)? CURLE_FTP_COULDNT_SET_ASCII:
CURLE_FTP_COULDNT_SET_BINARY;
}
if(data->resume_from) {
/* Daniel: (August 4, 1999)
*
* We start with trying to use the SIZE command to figure out the size