1
/*****************************************************************************
3
* Project ___| | | | _ \| |
5
* | (__| |_| | _ <| |___
6
* \___|\___/|_| \_\_____|
8
* Copyright (C) 2001, Daniel Stenberg, <daniel@haxx.se>, et al.
10
* In order to be useful for every potential user, curl and libcurl are
11
* dual-licensed under the MPL and the MIT/X-derivate licenses.
13
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
14
* copies of the Software, and permit persons to whom the Software is
15
* furnished to do so, under the terms of the MPL or the MIT/X-derivate
16
* licenses. You may pick one of these licenses.
18
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19
* KIND, either express or implied.
21
* $Id: ftp.c,v 1.130 2002/02/28 23:31:23 bagder Exp $
22
*****************************************************************************/
36
#ifdef HAVE_SYS_SELECT_H
37
#include <sys/select.h>
40
#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
42
#else /* some kind of unix */
43
#ifdef HAVE_SYS_SOCKET_H
44
#include <sys/socket.h>
46
#include <sys/types.h>
47
#ifdef HAVE_NETINET_IN_H
48
#include <netinet/in.h>
50
#ifdef HAVE_ARPA_INET_H
51
#include <arpa/inet.h>
53
#include <sys/utsname.h>
63
#if defined(WIN32) && defined(__GNUC__) || defined(__MINGW32__)
67
#include <curl/curl.h>
76
#include "http.h" /* for HTTP proxy tunnel stuff */
88
#if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL)
89
#include "inet_ntoa_r.h"
92
#define _MPRINTF_REPLACE /* use our functions only */
93
#include <curl/mprintf.h>
95
/* The last #include file should be: */
100
/* Local API functions */
101
static CURLcode ftp_sendquote(struct connectdata *conn, struct curl_slist *quote);
102
static CURLcode ftp_cwd(struct connectdata *conn, char *path);
104
/* easy-to-use macro: */
105
#define FTPSENDF(x,y,z) if((result = Curl_ftpsendf(x,y,z))) return result
107
/***********************************************************************
109
* AllowServerConnect()
111
* When we've issue the PORT command, we have told the server to connect
112
* to us. This function will sit and wait here until the server has
116
static CURLcode AllowServerConnect(struct SessionHandle *data,
117
struct connectdata *conn,
125
FD_SET(sock, &rdset);
127
/* we give the server 10 seconds to connect to us */
131
switch (select(sock+1, &rdset, NULL, NULL, &dt)) {
134
failf(data, "Error while waiting for server connect");
135
return CURLE_FTP_PORT_FAILED;
136
case 0: /* timeout */
138
failf(data, "Timeout while waiting for server connect");
139
return CURLE_FTP_PORT_FAILED;
141
/* we have received data here */
144
size_t size = sizeof(struct sockaddr_in);
145
struct sockaddr_in add;
147
getsockname(sock, (struct sockaddr *) &add, (socklen_t *)&size);
148
s=accept(sock, (struct sockaddr *) &add, (socklen_t *)&size);
150
sclose(sock); /* close the first socket */
154
failf(data, "Error accept()ing server connect");
155
return CURLE_FTP_PORT_FAILED;
157
infof(data, "Connection accepted from server\n");
159
conn->secondarysocket = s;
167
/* --- parse FTP server responses --- */
170
* Curl_GetFTPResponse() is supposed to be invoked after each command sent to
171
* a remote FTP server. This function will wait and read all lines of the
172
* response and extract the relevant return code for the invoking function.
175
int Curl_GetFTPResponse(char *buf,
176
struct connectdata *conn,
179
/* Brand new implementation.
180
* We cannot read just one byte per read() and then go back to select()
181
* as it seems that the OpenSSL read() stuff doesn't grok that properly.
183
* Alas, read as much as possible, split up into lines, use the ending
184
* line in a response or continue reading. */
186
int sockfd = conn->firstsocket;
187
int nread; /* total size read */
188
int perline; /* count bytes per line */
192
int timeout = 3600; /* default timeout in seconds */
193
struct timeval interval;
196
struct SessionHandle *data = conn->data;
198
int code=0; /* default "error code" to return */
201
#define SELECT_ERROR 1 /* select() problems */
202
#define SELECT_TIMEOUT 2 /* took too long */
203
#define SELECT_MEMORY 3 /* no available memory */
204
#define SELECT_CALLBACK 4 /* aborted by callback */
206
int error = SELECT_OK;
208
struct FTP *ftp = conn->proto.ftp;
211
*ftpcode = 0; /* 0 for errors */
213
if(data->set.timeout) {
214
/* if timeout is requested, find out how much remaining time we have */
215
timeout = data->set.timeout - /* timeout time */
216
Curl_tvdiff(Curl_tvnow(), conn->now)/1000; /* spent time */
218
failf(data, "Transfer aborted due to timeout");
219
return -SELECT_TIMEOUT; /* already too little time */
223
FD_ZERO (&readfd); /* clear it */
224
FD_SET (sockfd, &readfd); /* read socket */
226
/* get this in a backup variable to be able to restore it on each lap in the
237
while((nread<BUFSIZE) && (keepon && !error)) {
238
readfd = rkeepfd; /* set every lap */
239
interval.tv_sec = timeout;
240
interval.tv_usec = 0;
243
switch (select (sockfd+1, &readfd, NULL, NULL, &interval)) {
244
case -1: /* select() error, stop reading */
245
error = SELECT_ERROR;
246
failf(data, "Transfer aborted due to select() error");
248
case 0: /* timeout */
249
error = SELECT_TIMEOUT;
250
failf(data, "Transfer aborted due to timeout");
256
if(SELECT_OK == error) {
258
* This code previously didn't use the kerberos sec_read() code
259
* to read, but when we use Curl_read() it may do so. Do confirm
260
* that this is still ok and then remove this comment!
263
/* we had data in the "cache", copy that instead of doing an actual
265
memcpy(ptr, ftp->cache, ftp->cache_size);
266
gotbytes = ftp->cache_size;
267
free(ftp->cache); /* free the cache */
268
ftp->cache = NULL; /* clear the pointer */
269
ftp->cache_size = 0; /* zero the size just in case */
272
int res = Curl_read(conn, sockfd, ptr,
273
BUFSIZE-nread, &gotbytes);
276
continue; /* go looping again */
284
else if(gotbytes <= 0) {
286
error = SELECT_ERROR;
287
failf(data, "Connection aborted");
290
/* we got a whole chunk of data, which can be anything from one
291
* byte to a set of lines and possible just a piece of the last
296
for(i = 0; i < gotbytes; ptr++, i++) {
299
/* a newline is CRLF in ftp-talk, so the CR is ignored as
300
the line isn't really terminated until the LF comes */
303
/* output debug output if that is requested */
304
if(data->set.verbose) {
305
fputs("< ", data->set.err);
306
fwrite(line_start, perline, 1, data->set.err);
307
/* no need to output LF here, it is part of the data */
311
* We pass all response-lines to the callback function registered
312
* for "headers". The response lines can be seen as a kind of
315
result = Curl_client_write(data, CLIENTWRITE_HEADER,
316
line_start, perline);
318
return -SELECT_CALLBACK;
320
#define lastline(line) (isdigit((int)line[0]) && isdigit((int)line[1]) && \
321
isdigit((int)line[2]) && (' ' == line[3]))
323
if(perline>3 && lastline(line_start)) {
324
/* This is the end of the last line, copy the last
325
* line to the start of the buffer and zero terminate,
326
* for old times sake (and krb4)! */
329
for(meow=line_start, n=0; meow<ptr; meow++, n++)
331
*meow=0; /* zero terminate */
333
line_start = ptr+1; /* advance pointer */
334
i++; /* skip this before getting out */
337
perline=0; /* line starts over here */
341
if(!keepon && (i != gotbytes)) {
342
/* We found the end of the response lines, but we didn't parse the
343
full chunk of data we have read from the server. We therefore
344
need to store the rest of the data to be checked on the next
345
invoke as it may actually contain another end of response
346
already! Cleverly figured out by Eric Lavigne in December
348
ftp->cache_size = gotbytes - i;
349
ftp->cache = (char *)malloc(ftp->cache_size);
351
memcpy(ftp->cache, line_start, ftp->cache_size);
353
return -SELECT_MEMORY; /**BANG**/
355
} /* there was data */
357
} /* while there's buffer left and loop is requested */
363
/* handle the security-oriented responses 6xx ***/
364
/* FIXME: some errorchecking perhaps... ***/
367
Curl_sec_read_msg(conn, buf, prot_safe);
370
Curl_sec_read_msg(conn, buf, prot_private);
373
Curl_sec_read_msg(conn, buf, prot_confidential);
376
/* normal ftp stuff we pass through! */
385
*ftpcode=code; /* return the initial number like this */
387
return nread; /* total amount of bytes read */
392
* This function is only used by code that works on IPv4. When we add proper
393
* support for that functionality with IPv6, this function can go in again.
395
/* -- who are we? -- */
396
static char *getmyhost(char *buf, int buf_size)
398
#if defined(HAVE_GETHOSTNAME)
399
gethostname(buf, buf_size);
400
#elif defined(HAVE_UNAME)
402
strncpy(buf, uname(&ugnm) < 0 ? "localhost" : ugnm.nodename, buf_size - 1);
403
buf[buf_size - 1] = '\0';
405
/* We have no means of finding the local host name! */
406
strncpy(buf, "localhost", buf_size);
407
buf[buf_size - 1] = '\0';
412
#endif /* ipv4-only function */
415
/* ftp_connect() should do everything that is to be considered a part
416
of the connection phase. */
417
CURLcode Curl_ftp_connect(struct connectdata *conn)
419
/* this is FTP and no proxy */
421
struct SessionHandle *data=conn->data;
422
char *buf = data->state.buffer; /* this is our buffer */
427
ftp = (struct FTP *)malloc(sizeof(struct FTP));
429
return CURLE_OUT_OF_MEMORY;
431
memset(ftp, 0, sizeof(struct FTP));
432
conn->proto.ftp = ftp;
434
/* We always support persistant connections on ftp */
435
conn->bits.close = FALSE;
437
/* get some initial data into the ftp struct */
438
ftp->bytecountp = &conn->bytecount;
440
/* no need to duplicate them, the data struct won't change */
441
ftp->user = data->state.user;
442
ftp->passwd = data->state.passwd;
444
if (data->set.tunnel_thru_httpproxy) {
445
/* We want "seamless" FTP operations through HTTP proxy tunnel */
446
result = Curl_ConnectHTTPProxyTunnel(conn, conn->firstsocket,
447
conn->hostname, conn->remote_port);
448
if(CURLE_OK != result)
452
if(conn->protocol & PROT_FTPS) {
453
/* FTPS is simply ftp with SSL for the control channel */
454
/* now, perform the SSL initialization for this socket */
455
result = Curl_SSLConnect(conn);
461
/* The first thing we do is wait for the "220*" line: */
462
nread = Curl_GetFTPResponse(buf, conn, &ftpcode);
464
return CURLE_OPERATION_TIMEOUTED;
467
failf(data, "This doesn't seem like a nice ftp-server response");
468
return CURLE_FTP_WEIRD_SERVER_REPLY;
472
/* if not anonymous login, try a secure login */
475
/* request data protection level (default is 'clear') */
476
Curl_sec_request_prot(conn, "private");
478
/* We set private first as default, in case the line below fails to
480
Curl_sec_request_prot(conn, data->set.krb4_level);
482
if(Curl_sec_login(conn) != 0)
483
infof(data, "Logging in with password in cleartext!\n");
485
infof(data, "Authentication successful\n");
490
FTPSENDF(conn, "USER %s", ftp->user);
492
/* wait for feedback */
493
nread = Curl_GetFTPResponse(buf, conn, &ftpcode);
495
return CURLE_OPERATION_TIMEOUTED;
498
/* 530 User ... access denied
499
(the server denies to log the specified user) */
500
failf(data, "Access denied: %s", &buf[4]);
501
return CURLE_FTP_ACCESS_DENIED;
503
else if(ftpcode == 331) {
504
/* 331 Password required for ...
505
(the server requires to send the user's password too) */
506
FTPSENDF(conn, "PASS %s", ftp->passwd);
507
nread = Curl_GetFTPResponse(buf, conn, &ftpcode);
509
return CURLE_OPERATION_TIMEOUTED;
512
/* 530 Login incorrect.
513
(the username and/or the password are incorrect) */
514
failf(data, "the username and/or the password are incorrect");
515
return CURLE_FTP_USER_PASSWORD_INCORRECT;
517
else if(ftpcode == 230) {
518
/* 230 User ... logged in.
519
(user successfully logged in) */
521
infof(data, "We have successfully logged in\n");
524
failf(data, "Odd return code after PASS");
525
return CURLE_FTP_WEIRD_PASS_REPLY;
528
else if(buf[0] == '2') {
529
/* 230 User ... logged in.
530
(the user logged in without password) */
531
infof(data, "We have successfully logged in\n");
533
/* we are logged in (with Kerberos)
534
* now set the requested protection level
536
if(conn->sec_complete)
537
Curl_sec_set_protection_level(conn);
539
/* we may need to issue a KAUTH here to have access to the files
540
* do it if user supplied a password
542
if(data->state.passwd && *data->state.passwd)
543
Curl_krb_kauth(conn);
547
failf(data, "Odd return code after USER");
548
return CURLE_FTP_WEIRD_USER_REPLY;
551
/* send PWD to discover our entry point */
552
FTPSENDF(conn, "PWD", NULL);
554
/* wait for feedback */
555
nread = Curl_GetFTPResponse(buf, conn, &ftpcode);
557
return CURLE_OPERATION_TIMEOUTED;
560
char *dir = (char *)malloc(nread+1);
562
char *ptr=&buf[4]; /* start on the first letter */
564
/* Reply format is like
565
257<space>"<directory-name>"<space><commentary> and the RFC959 says
567
The directory name can contain any character; embedded double-quotes
568
should be escaped by double-quotes (the "quote-doubling" convention).
571
/* it started good */
576
/* "quote-doubling" */
582
*store = '\0'; /* zero terminate */
583
break; /* get out of this loop */
591
ftp->entrypath =dir; /* remember this */
592
infof(data, "Entry path is '%s'\n", ftp->entrypath);
595
/* couldn't get the path */
600
/* We couldn't read the PWD response! */
606
/***********************************************************************
610
* The DONE function. This does what needs to be done after a single DO has
613
* Input argument is already checked for validity.
615
CURLcode Curl_ftp_done(struct connectdata *conn)
617
struct SessionHandle *data = conn->data;
618
struct FTP *ftp = conn->proto.ftp;
620
char *buf = data->state.buffer; /* this is our buffer */
622
CURLcode result=CURLE_OK;
624
if(data->set.upload) {
625
if((-1 != data->set.infilesize) && (data->set.infilesize != *ftp->bytecountp)) {
626
failf(data, "Wrote only partial file (%d out of %d bytes)",
627
*ftp->bytecountp, data->set.infilesize);
628
return CURLE_PARTIAL_FILE;
632
if((-1 != conn->size) && (conn->size != *ftp->bytecountp) &&
633
(conn->maxdownload != *ftp->bytecountp)) {
634
failf(data, "Received only partial file: %d bytes", *ftp->bytecountp);
635
return CURLE_PARTIAL_FILE;
637
else if(!conn->bits.resume_done &&
638
!data->set.no_body &&
639
(0 == *ftp->bytecountp)) {
640
/* We consider this an error, but there's no true FTP error received
641
why we need to continue to "read out" the server response too.
642
We don't want to leave a "waiting" server reply if we'll get told
643
to make a second request on this same connection! */
644
failf(data, "No data was received!");
645
result = CURLE_FTP_COULDNT_RETR_FILE;
650
Curl_sec_fflush_fd(conn, conn->secondarysocket);
652
/* shut down the socket to inform the server we're done */
653
sclose(conn->secondarysocket);
654
conn->secondarysocket = -1;
656
if(!data->set.no_body && !conn->bits.resume_done) {
657
/* now let's see what the server says about the transfer we
659
nread = Curl_GetFTPResponse(buf, conn, &ftpcode);
661
return CURLE_OPERATION_TIMEOUTED;
663
/* 226 Transfer complete, 250 Requested file action okay, completed. */
664
if((ftpcode != 226) && (ftpcode != 250)) {
665
failf(data, "server did not report OK, got %d", ftpcode);
666
return CURLE_FTP_WRITE_ERROR;
670
conn->bits.resume_done = FALSE; /* clean this for next connection */
672
/* Send any post-transfer QUOTE strings? */
673
if(!result && data->set.postquote)
674
result = ftp_sendquote(conn, data->set.postquote);
679
/***********************************************************************
683
* Where a 'quote' means a list of custom commands to send to the server.
684
* The quote list is passed as an argument.
688
CURLcode ftp_sendquote(struct connectdata *conn, struct curl_slist *quote)
690
struct curl_slist *item;
698
FTPSENDF(conn, "%s", item->data);
700
nread = Curl_GetFTPResponse(conn->data->state.buffer, conn, &ftpcode);
702
return CURLE_OPERATION_TIMEOUTED;
704
if (ftpcode >= 400) {
705
failf(conn->data, "QUOT string not accepted: %s", item->data);
706
return CURLE_FTP_QUOTE_ERROR;
716
/***********************************************************************
720
* Send 'CWD' to the remote server to Change Working Directory.
721
* It is the ftp version of the unix 'cd' command.
724
CURLcode ftp_cwd(struct connectdata *conn, char *path)
730
FTPSENDF(conn, "CWD %s", path);
731
nread = Curl_GetFTPResponse(
732
conn->data->state.buffer, conn, &ftpcode);
734
return CURLE_OPERATION_TIMEOUTED;
736
if (ftpcode != 250) {
737
failf(conn->data, "Couldn't cd to %s", path);
738
return CURLE_FTP_ACCESS_DENIED;
744
/***********************************************************************
748
* Get the timestamp of the given file.
751
CURLcode ftp_getfiletime(struct connectdata *conn, char *file)
753
CURLcode result=CURLE_OK;
754
int ftpcode; /* for ftp status */
756
char *buf = conn->data->state.buffer;
758
/* we have requested to get the modified-time of the file, this is yet
759
again a grey area as the MDTM is not kosher RFC959 */
760
FTPSENDF(conn, "MDTM %s", file);
762
nread = Curl_GetFTPResponse(buf, conn, &ftpcode);
764
return CURLE_OPERATION_TIMEOUTED;
767
/* we got a time. Format should be: "YYYYMMDDHHMMSS[.sss]" where the
768
last .sss part is optional and means fractions of a second */
769
int year, month, day, hour, minute, second;
770
if(6 == sscanf(buf+4, "%04d%02d%02d%02d%02d%02d",
771
&year, &month, &day, &hour, &minute, &second)) {
772
/* we have a time, reformat it */
773
time_t secs=time(NULL);
774
sprintf(buf, "%04d%02d%02d %02d:%02d:%02d",
775
year, month, day, hour, minute, second);
776
/* now, convert this into a time() value: */
777
conn->data->info.filetime = curl_getdate(buf, &secs);
780
infof(conn->data, "unsupported MDTM reply format\n");
786
/***********************************************************************
790
* Set transfer type. We only deal with ASCII or BINARY so this function
793
static CURLcode ftp_transfertype(struct connectdata *conn,
796
struct SessionHandle *data = conn->data;
799
char *buf=data->state.buffer;
802
FTPSENDF(conn, "TYPE %s", ascii?"A":"I");
804
nread = Curl_GetFTPResponse(buf, conn, &ftpcode);
806
return CURLE_OPERATION_TIMEOUTED;
809
failf(data, "Couldn't set %s mode",
810
ascii?"ASCII":"binary");
811
return ascii? CURLE_FTP_COULDNT_SET_ASCII:CURLE_FTP_COULDNT_SET_BINARY;
817
/***********************************************************************
821
* Returns the file size (in bytes) of the given remote file.
825
CURLcode ftp_getsize(struct connectdata *conn, char *file,
828
struct SessionHandle *data = conn->data;
831
char *buf=data->state.buffer;
834
FTPSENDF(conn, "SIZE %s", file);
835
nread = Curl_GetFTPResponse(buf, conn, &ftpcode);
837
return CURLE_OPERATION_TIMEOUTED;
840
/* get the size from the ascii string: */
844
return CURLE_FTP_COULDNT_GET_SIZE;
849
/***************************************************************************
853
* This function only outputs some informationals about this second connection
854
* when we've issued a PASV command before and thus we have connected to a
855
* possibly new IP address.
859
ftp_pasv_verbose(struct connectdata *conn,
860
Curl_ipconnect *addr,
861
char *newhost, /* ascii version */
865
/*****************************************************************
867
* IPv4-only code section
871
struct hostent * answer;
873
#ifdef HAVE_INET_NTOA_R
876
/* The array size trick below is to make this a large chunk of memory
877
suitably 8-byte aligned on 64-bit platforms. This was thoughtfully
878
suggested by Philip Gladstone. */
879
long bigbuf[9000 / sizeof(long)];
881
#if defined(HAVE_INET_ADDR)
883
# if defined(HAVE_GETHOSTBYADDR_R)
886
char *hostent_buf = (char *)bigbuf; /* get a char * to the buffer */
888
address = inet_addr(newhost);
889
# ifdef HAVE_GETHOSTBYADDR_R
891
# ifdef HAVE_GETHOSTBYADDR_R_5
892
/* AIX, Digital Unix (OSF1, Tru64) style:
893
extern int gethostbyaddr_r(char *addr, size_t len, int type,
894
struct hostent *htent, struct hostent_data *ht_data); */
896
/* Fred Noz helped me try this out, now it at least compiles! */
898
/* Bjorn Reese (November 28 2001):
899
The Tru64 man page on gethostbyaddr_r() says that
900
the hostent struct must be filled with zeroes before the call to
901
gethostbyaddr_r(). */
903
memset(hostent_buf, 0, sizeof(struct hostent));
905
if(gethostbyaddr_r((char *) &address,
906
sizeof(address), AF_INET,
907
(struct hostent *)hostent_buf,
908
hostent_buf + sizeof(*answer)))
912
# ifdef HAVE_GETHOSTBYADDR_R_7
913
/* Solaris and IRIX */
914
answer = gethostbyaddr_r((char *) &address, sizeof(address), AF_INET,
915
(struct hostent *)bigbuf,
916
hostent_buf + sizeof(*answer),
917
sizeof(hostent_buf) - sizeof(*answer),
920
# ifdef HAVE_GETHOSTBYADDR_R_8
922
if(gethostbyaddr_r((char *) &address, sizeof(address), AF_INET,
923
(struct hostent *)hostent_buf,
924
hostent_buf + sizeof(*answer),
925
sizeof(hostent_buf) - sizeof(*answer),
928
answer=NULL; /* error */
932
answer = gethostbyaddr((char *) &address, sizeof(address), AF_INET);
937
(void) memcpy(&in.s_addr, addr, sizeof (Curl_ipconnect));
938
infof(conn->data, "Connecting to %s (%s) port %u\n",
939
answer?answer->h_name:newhost,
940
#if defined(HAVE_INET_NTOA_R)
941
inet_ntoa_r(in, ntoa_buf, sizeof(ntoa_buf)),
948
/*****************************************************************
950
* IPv6-only code section
952
char hbuf[NI_MAXHOST]; /* ~1KB */
953
char nbuf[NI_MAXHOST]; /* ~1KB */
954
char sbuf[NI_MAXSERV]; /* around 32 */
955
#ifdef NI_WITHSCOPEID
956
const int niflags = NI_NUMERICHOST | NI_NUMERICSERV | NI_WITHSCOPEID;
958
const int niflags = NI_NUMERICHOST | NI_NUMERICSERV;
960
port = 0; /* unused, prevent warning */
961
if (getnameinfo(addr->ai_addr, addr->ai_addrlen,
962
nbuf, sizeof(nbuf), sbuf, sizeof(sbuf), niflags)) {
963
snprintf(nbuf, sizeof(nbuf), "?");
964
snprintf(sbuf, sizeof(sbuf), "?");
967
if (getnameinfo(addr->ai_addr, addr->ai_addrlen,
968
hbuf, sizeof(hbuf), NULL, 0, 0)) {
969
infof(conn->data, "Connecting to %s (%s) port %s\n", nbuf, newhost, sbuf);
972
infof(conn->data, "Connecting to %s (%s) port %s\n", hbuf, nbuf, sbuf);
977
/***********************************************************************
981
* Send the proper PORT command. PORT is the ftp client's way of telling the
982
* server that *WE* open a port that we listen on an awaits the server to
983
* connect to. This is the opposite of PASV.
987
CURLcode ftp_use_port(struct connectdata *conn)
989
struct SessionHandle *data=conn->data;
992
char *buf = data->state.buffer; /* this is our buffer */
993
int ftpcode; /* receive FTP response codes in this */
997
/******************************************************************
999
* Here's a piece of IPv6-specific code coming up
1003
struct addrinfo hints, *res, *ai;
1004
struct sockaddr_storage ss;
1006
char hbuf[NI_MAXHOST];
1008
struct sockaddr *sa=(struct sockaddr *)&ss;
1009
#ifdef NI_WITHSCOPEID
1010
const int niflags = NI_NUMERICHOST | NI_NUMERICSERV | NI_WITHSCOPEID;
1012
const int niflags = NI_NUMERICHOST | NI_NUMERICSERV;
1017
char portmsgbuf[4096], tmp[4096];
1019
const char *mode[] = { "EPRT", "LPRT", "PORT", NULL };
1023
* we should use Curl_if2ip? given pickiness of recent ftpd,
1024
* I believe we should use the same address as the control connection.
1027
if (getsockname(conn->firstsocket, (struct sockaddr *)&ss, &sslen) < 0)
1028
return CURLE_FTP_PORT_FAILED;
1030
if (getnameinfo((struct sockaddr *)&ss, sslen, hbuf, sizeof(hbuf), NULL, 0,
1032
return CURLE_FTP_PORT_FAILED;
1034
memset(&hints, 0, sizeof(hints));
1035
hints.ai_family = sa->sa_family;
1036
/*hints.ai_family = ss.ss_family;
1037
this way can be used if sockaddr_storage is properly defined, as glibc
1039
hints.ai_socktype = SOCK_STREAM;
1040
hints.ai_flags = AI_PASSIVE;
1042
if (getaddrinfo(hbuf, (char *)"0", &hints, &res))
1043
return CURLE_FTP_PORT_FAILED;
1046
for (ai = res; ai; ai = ai->ai_next) {
1047
portsock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
1051
if (bind(portsock, ai->ai_addr, ai->ai_addrlen) < 0) {
1057
if (listen(portsock, 1) < 0) {
1067
failf(data, strerror(errno));
1068
return CURLE_FTP_PORT_FAILED;
1072
if (getsockname(portsock, sa, &sslen) < 0) {
1073
failf(data, strerror(errno));
1074
return CURLE_FTP_PORT_FAILED;
1077
for (modep = (char **)mode; modep && *modep; modep++) {
1080
switch (sa->sa_family) {
1082
ap = (unsigned char *)&((struct sockaddr_in *)&ss)->sin_addr;
1083
alen = sizeof(((struct sockaddr_in *)&ss)->sin_addr);
1084
pp = (unsigned char *)&((struct sockaddr_in *)&ss)->sin_port;
1085
plen = sizeof(((struct sockaddr_in *)&ss)->sin_port);
1090
ap = (unsigned char *)&((struct sockaddr_in6 *)&ss)->sin6_addr;
1091
alen = sizeof(((struct sockaddr_in6 *)&ss)->sin6_addr);
1092
pp = (unsigned char *)&((struct sockaddr_in6 *)&ss)->sin6_port;
1093
plen = sizeof(((struct sockaddr_in6 *)&ss)->sin6_port);
1099
lprtaf = eprtaf = -1;
1103
if (strcmp(*modep, "EPRT") == 0) {
1106
if (getnameinfo((struct sockaddr *)&ss, sslen,
1107
portmsgbuf, sizeof(portmsgbuf), tmp, sizeof(tmp), niflags))
1110
/* do not transmit IPv6 scope identifier to the wire */
1111
if (sa->sa_family == AF_INET6) {
1112
char *q = strchr(portmsgbuf, '%');
1117
result = Curl_ftpsendf(conn, "%s |%d|%s|%s|", *modep, eprtaf,
1121
} else if (strcmp(*modep, "LPRT") == 0 ||
1122
strcmp(*modep, "PORT") == 0) {
1125
if (strcmp(*modep, "LPRT") == 0 && lprtaf < 0)
1127
if (strcmp(*modep, "PORT") == 0 && sa->sa_family != AF_INET)
1130
portmsgbuf[0] = '\0';
1131
if (strcmp(*modep, "LPRT") == 0) {
1132
snprintf(tmp, sizeof(tmp), "%d,%d", lprtaf, alen);
1133
if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >=
1134
sizeof(portmsgbuf)) {
1139
for (i = 0; i < alen; i++) {
1141
snprintf(tmp, sizeof(tmp), ",%u", ap[i]);
1143
snprintf(tmp, sizeof(tmp), "%u", ap[i]);
1145
if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >=
1146
sizeof(portmsgbuf)) {
1151
if (strcmp(*modep, "LPRT") == 0) {
1152
snprintf(tmp, sizeof(tmp), ",%d", plen);
1154
if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >= sizeof(portmsgbuf))
1158
for (i = 0; i < plen; i++) {
1159
snprintf(tmp, sizeof(tmp), ",%u", pp[i]);
1161
if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >=
1162
sizeof(portmsgbuf)) {
1167
result = Curl_ftpsendf(conn, "%s %s", *modep, portmsgbuf);
1172
nread = Curl_GetFTPResponse(buf, conn, &ftpcode);
1174
return CURLE_OPERATION_TIMEOUTED;
1176
if (ftpcode != 200) {
1177
failf(data, "Server does not grok %s", *modep);
1186
return CURLE_FTP_PORT_FAILED;
1188
/* we set the secondary socket variable to this for now, it
1189
is only so that the cleanup function will close it in case
1190
we fail before the true secondary stuff is made */
1191
conn->secondarysocket = portsock;
1194
/******************************************************************
1196
* Here's a piece of IPv4-specific code coming up
1199
struct sockaddr_in sa;
1200
struct hostent *h=NULL;
1201
char *hostdataptr=NULL;
1202
unsigned short porttouse;
1203
char myhost[256] = "";
1205
if(data->set.ftpport) {
1206
if(Curl_if2ip(data->set.ftpport, myhost, sizeof(myhost))) {
1207
h = Curl_resolv(data, myhost, 0, &hostdataptr);
1210
int len = strlen(data->set.ftpport);
1212
h = Curl_resolv(data, data->set.ftpport, 0, &hostdataptr);
1214
strcpy(myhost, data->set.ftpport); /* buffer overflow risk */
1218
char *tmp_host = getmyhost(myhost, sizeof(myhost));
1219
h=Curl_resolv(data, tmp_host, 0, &hostdataptr);
1221
infof(data, "We connect from %s\n", myhost);
1224
if( (portsock = socket(AF_INET, SOCK_STREAM, 0)) >= 0 ) {
1227
/* we set the secondary socket variable to this for now, it
1228
is only so that the cleanup function will close it in case
1229
we fail before the true secondary stuff is made */
1230
conn->secondarysocket = portsock;
1232
memset((char *)&sa, 0, sizeof(sa));
1233
memcpy((char *)&sa.sin_addr,
1236
sa.sin_family = AF_INET;
1237
sa.sin_addr.s_addr = INADDR_ANY;
1241
if(bind(portsock, (struct sockaddr *)&sa, size) >= 0) {
1242
/* we succeeded to bind */
1243
struct sockaddr_in add;
1244
socklen_t socksize = sizeof(add);
1246
if(getsockname(portsock, (struct sockaddr *) &add,
1248
failf(data, "getsockname() failed");
1249
return CURLE_FTP_PORT_FAILED;
1251
porttouse = ntohs(add.sin_port);
1253
if ( listen(portsock, 1) < 0 ) {
1254
failf(data, "listen(2) failed on socket");
1256
return CURLE_FTP_PORT_FAILED;
1260
failf(data, "bind(2) failed on socket");
1262
return CURLE_FTP_PORT_FAILED;
1266
failf(data, "socket(2) failed (%s)");
1268
return CURLE_FTP_PORT_FAILED;
1272
failf(data, "could't find my own IP address (%s)", myhost);
1273
return CURLE_FTP_PORT_FAILED;
1276
#ifdef HAVE_INET_NTOA_R
1280
unsigned short ip[5];
1281
(void) memcpy(&in.s_addr, *h->h_addr_list, sizeof (in.s_addr));
1282
#ifdef HAVE_INET_NTOA_R
1283
/* ignore the return code from inet_ntoa_r() as it is int or
1284
char * depending on system */
1285
inet_ntoa_r(in, ntoa_buf, sizeof(ntoa_buf));
1286
sscanf( ntoa_buf, "%hu.%hu.%hu.%hu",
1287
&ip[0], &ip[1], &ip[2], &ip[3]);
1289
sscanf( inet_ntoa(in), "%hu.%hu.%hu.%hu",
1290
&ip[0], &ip[1], &ip[2], &ip[3]);
1292
result=Curl_ftpsendf(conn, "PORT %d,%d,%d,%d,%d,%d",
1293
ip[0], ip[1], ip[2], ip[3],
1300
nread = Curl_GetFTPResponse(buf, conn, &ftpcode);
1302
return CURLE_OPERATION_TIMEOUTED;
1304
if(ftpcode != 200) {
1305
failf(data, "Server does not grok PORT, try without it!");
1306
return CURLE_FTP_PORT_FAILED;
1308
#endif /* end of ipv4-specific code */
1313
/***********************************************************************
1317
* Send the PASV command. PASV is the ftp client's way of asking the server to
1318
* open a second port that we can connect to (for the data transfer). This is
1319
* the opposite of PORT.
1323
CURLcode ftp_use_pasv(struct connectdata *conn)
1325
struct SessionHandle *data = conn->data;
1327
char *buf = data->state.buffer; /* this is our buffer */
1328
int ftpcode; /* receive FTP response codes in this */
1330
Curl_addrinfo *addr=NULL;
1331
Curl_ipconnect *conninfo;
1334
Here's the excecutive summary on what to do:
1336
PASV is RFC959, expect:
1337
227 Entering Passive Mode (a1,a2,a3,a4,p1,p2)
1339
LPSV is RFC1639, expect:
1340
228 Entering Long Passive Mode (4,4,a1,a2,a3,a4,2,p1,p2)
1342
EPSV is RFC2428, expect:
1343
229 Entering Extended Passive Mode (|||port|)
1348
const char *mode[] = { "EPSV", "PASV", NULL };
1349
int results[] = { 229, 227, 0 };
1352
char *mode[] = { "EPSV", "LPSV", "PASV", NULL };
1353
int results[] = { 229, 228, 227, 0 };
1355
const char *mode[] = { "PASV", NULL };
1356
int results[] = { 227, 0 };
1360
unsigned short connectport; /* the local port connect() should use! */
1361
unsigned short newport; /* remote port, not necessary the local one */
1362
char *hostdataptr=NULL;
1364
/* newhost must be able to hold a full IP-style address in ASCII, which
1365
in the IPv6 case means 5*8-1 = 39 letters */
1367
char *newhostp=NULL;
1369
for (modeoff = (data->set.ftp_use_epsv?0:1);
1370
mode[modeoff]; modeoff++) {
1371
result = Curl_ftpsendf(conn, mode[modeoff]);
1374
nread = Curl_GetFTPResponse(buf, conn, &ftpcode);
1376
return CURLE_OPERATION_TIMEOUTED;
1377
if (ftpcode == results[modeoff])
1381
if (!mode[modeoff]) {
1382
failf(data, "Odd return code after PASV");
1383
return CURLE_FTP_WEIRD_PASV_REPLY;
1385
else if (227 == results[modeoff]) {
1391
* New 227-parser June 3rd 1999.
1392
* It now scans for a sequence of six comma-separated numbers and
1393
* will take them as IP+port indicators.
1395
* Found reply-strings include:
1396
* "227 Entering Passive Mode (127,0,0,1,4,51)"
1397
* "227 Data transfer will passively listen to 127,0,0,1,4,51"
1398
* "227 Entering passive mode. 127,0,0,1,4,51"
1402
if (6 == sscanf(str, "%d,%d,%d,%d,%d,%d",
1403
&ip[0], &ip[1], &ip[2], &ip[3],
1404
&port[0], &port[1]))
1410
failf(data, "Couldn't interpret this 227-reply: %s", buf);
1411
return CURLE_FTP_WEIRD_227_FORMAT;
1414
sprintf(newhost, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
1416
newport = (port[0]<<8) + port[1];
1419
else if (229 == results[modeoff]) {
1420
char *ptr = strchr(buf, '(');
1425
if(5 == sscanf(ptr, "%c%c%c%u%c",
1431
/* the four separators should be identical */
1434
/* we should use the same host we already are connected to */
1435
newhostp = conn->name;
1441
failf(data, "Weirdly formatted EPSV reply");
1442
return CURLE_FTP_WEIRD_PASV_REPLY;
1447
return CURLE_FTP_CANT_RECONNECT;
1449
if(data->change.proxy) {
1451
* This is a tunnel through a http proxy and we need to connect to the
1452
* proxy again here. We already have the name info for it since the
1455
addr = conn->hostaddr;
1457
(unsigned short)conn->port; /* we connect to the proxy's port */
1460
/* normal, direct, ftp connection */
1461
addr = Curl_resolv(data, newhostp, newport, &hostdataptr);
1463
failf(data, "Can't resolve new host %s", newhost);
1464
return CURLE_FTP_CANT_GET_HOST;
1466
connectport = newport; /* we connect to the remote port */
1469
result = Curl_connecthost(conn,
1472
&conn->secondarysocket,
1475
if((CURLE_OK == result) &&
1477
/* this just dumps information about this second connection */
1478
ftp_pasv_verbose(conn, conninfo, newhost, connectport);
1480
if(CURLE_OK != result)
1483
if (data->set.tunnel_thru_httpproxy) {
1484
/* We want "seamless" FTP operations through HTTP proxy tunnel */
1485
result = Curl_ConnectHTTPProxyTunnel(conn, conn->secondarysocket,
1487
if(CURLE_OK != result)
1494
/***********************************************************************
1498
* This is the actual DO function for FTP. Get a file/directory according to
1499
* the options previously setup.
1503
CURLcode ftp_perform(struct connectdata *conn)
1505
/* this is FTP and no proxy */
1508
struct SessionHandle *data=conn->data;
1509
char *buf = data->state.buffer; /* this is our buffer */
1511
/* the ftp struct is already inited in ftp_connect() */
1512
struct FTP *ftp = conn->proto.ftp;
1514
long *bytecountp = ftp->bytecountp;
1515
int ftpcode; /* for ftp status */
1517
/* Send any QUOTE strings? */
1518
if(data->set.quote) {
1519
if ((result = ftp_sendquote(conn, data->set.quote)) != CURLE_OK)
1523
/* This is a re-used connection. Since we change directory to where the
1524
transfer is taking place, we must now get back to the original dir
1525
where we ended up after login: */
1526
if (conn->bits.reuse) {
1527
if ((result = ftp_cwd(conn, ftp->entrypath)) != CURLE_OK)
1531
/* change directory first! */
1532
if(ftp->dir && ftp->dir[0]) {
1533
if ((result = ftp_cwd(conn, ftp->dir)) != CURLE_OK)
1537
/* Requested time of file? */
1538
if(data->set.get_filetime && ftp->file) {
1539
result = ftp_getfiletime(conn, ftp->file);
1544
/* If we have selected NOBODY and HEADER, it means that we only want file
1545
information. Which in FTP can't be much more than the file size and
1547
if(data->set.no_body && data->set.include_header) {
1548
/* The SIZE command is _not_ RFC 959 specified, and therefor many servers
1549
may not support it! It is however the only way we have to get a file's
1553
/* Some servers return different sizes for different modes, and thus we
1554
must set the proper type before we check the size */
1555
result = ftp_transfertype(conn, data->set.ftp_ascii);
1559
/* failing to get size is not a serious error */
1560
result = ftp_getsize(conn, ftp->file, &filesize);
1562
if(CURLE_OK == result) {
1563
sprintf(buf, "Content-Length: %d\r\n", filesize);
1564
result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0);
1569
/* If we asked for a time of the file and we actually got one as
1570
well, we "emulate" a HTTP-style header in our output. */
1572
#ifdef HAVE_STRFTIME
1573
if(data->set.get_filetime && data->info.filetime) {
1575
#ifdef HAVE_LOCALTIME_R
1577
tm = (struct tm *)localtime_r(&data->info.filetime, &buffer);
1579
tm = localtime((unsigned long *)&data->info.filetime);
1581
/* format: "Tue, 15 Nov 1994 12:45:26 GMT" */
1582
strftime(buf, BUFSIZE-1, "Last-Modified: %a, %d %b %Y %H:%M:%S %Z\r\n",
1584
result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0);
1593
if(data->set.no_body)
1594
/* don't transfer the data */
1596
/* Get us a second connection up and connected */
1597
else if(data->set.ftp_use_port) {
1598
/* We have chosen to use the PORT command */
1599
result = ftp_use_port(conn);
1600
if(CURLE_OK == result)
1601
/* we have the data connection ready */
1602
infof(data, "Connected the data stream with PORT!\n");
1605
/* We have chosen (this is default) to use the PASV command */
1606
result = ftp_use_pasv(conn);
1607
if(CURLE_OK == result)
1608
infof(data, "Connected the data stream with PASV!\n");
1614
if(data->set.upload) {
1616
/* Set type to binary (unless specified ASCII) */
1617
result = ftp_transfertype(conn, data->set.ftp_ascii);
1621
/* Send any PREQUOTE strings after transfer type is set? (Wesley Laxton)*/
1622
if(data->set.prequote) {
1623
if ((result = ftp_sendquote(conn, data->set.prequote)) != CURLE_OK)
1627
if(conn->resume_from) {
1628
/* we're about to continue the uploading of a file */
1629
/* 1. get already existing file's size. We use the SIZE
1630
command for this which may not exist in the server!
1631
The SIZE command is not in RFC959. */
1633
/* 2. This used to set REST. But since we can do append, we
1634
don't another ftp command. We just skip the source file
1635
offset and then we APPEND the rest on the file instead */
1637
/* 3. pass file-size number of bytes in the source file */
1638
/* 4. lower the infilesize counter */
1639
/* => transfer as usual */
1641
if(conn->resume_from < 0 ) {
1642
/* we could've got a specified offset from the command line,
1643
but now we know we didn't */
1646
if(CURLE_OK != ftp_getsize(conn, ftp->file, &gottensize)) {
1647
failf(data, "Couldn't get remote file size");
1648
return CURLE_FTP_COULDNT_GET_SIZE;
1650
conn->resume_from = gottensize;
1653
if(conn->resume_from) {
1654
/* do we still game? */
1656
/* enable append instead */
1657
data->set.ftp_append = 1;
1659
/* Now, let's read off the proper amount of bytes from the
1660
input. If we knew it was a proper file we could've just
1661
fseek()ed but we only have a stream here */
1663
int readthisamountnow = (conn->resume_from - passed);
1666
if(readthisamountnow > BUFSIZE)
1667
readthisamountnow = BUFSIZE;
1670
data->set.fread(data->state.buffer, 1, readthisamountnow,
1673
passed += actuallyread;
1674
if(actuallyread != readthisamountnow) {
1675
failf(data, "Could only read %d bytes from the input", passed);
1676
return CURLE_FTP_COULDNT_USE_REST;
1679
while(passed != conn->resume_from);
1681
/* now, decrease the size of the read */
1682
if(data->set.infilesize>0) {
1683
data->set.infilesize -= conn->resume_from;
1685
if(data->set.infilesize <= 0) {
1686
infof(data, "File already completely uploaded\n");
1688
/* no data to transfer */
1689
result=Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1691
/* Set resume done so that we won't get any error in
1692
* Curl_ftp_done() because we didn't transfer the amount of bytes
1693
* that the local file file obviously is */
1694
conn->bits.resume_done = TRUE;
1699
/* we've passed, proceed as normal */
1703
/* Send everything on data->set.in to the socket */
1704
if(data->set.ftp_append) {
1705
/* we append onto the file instead of rewriting it */
1706
FTPSENDF(conn, "APPE %s", ftp->file);
1709
FTPSENDF(conn, "STOR %s", ftp->file);
1712
nread = Curl_GetFTPResponse(buf, conn, &ftpcode);
1714
return CURLE_OPERATION_TIMEOUTED;
1717
failf(data, "Failed FTP upload:%s", buf+3);
1718
/* oops, we never close the sockets! */
1719
return CURLE_FTP_COULDNT_STOR_FILE;
1722
if(data->set.ftp_use_port) {
1723
/* PORT means we are now awaiting the server to connect to us. */
1724
result = AllowServerConnect(data, conn, conn->secondarysocket);
1731
/* When we know we're uploading a specified file, we can get the file
1732
size prior to the actual upload. */
1734
Curl_pgrsSetUploadSize(data, data->set.infilesize);
1736
result = Curl_Transfer(conn, -1, -1, FALSE, NULL, /* no download */
1737
conn->secondarysocket, bytecountp);
1742
else if(!data->set.no_body) {
1743
/* Retrieve file or directory */
1745
long downloadsize=-1;
1747
if(conn->bits.use_range && conn->range) {
1753
from=strtol(conn->range, &ptr, 0);
1754
while(ptr && *ptr && (isspace((int)*ptr) || (*ptr=='-')))
1756
to=strtol(ptr, &ptr2, 0);
1758
/* we didn't get any digit */
1761
if((-1 == to) && (from>=0)) {
1763
conn->resume_from = from;
1764
infof(data, "FTP RANGE %d to end of file\n", from);
1769
conn->maxdownload = -from;
1770
conn->resume_from = from;
1771
infof(data, "FTP RANGE the last %d bytes\n", totalsize);
1775
totalsize = to-from;
1776
conn->maxdownload = totalsize+1; /* include the last mentioned byte */
1777
conn->resume_from = from;
1778
infof(data, "FTP RANGE from %d getting %d bytes\n", from,
1781
infof(data, "range-download from %d to %d, totally %d bytes\n",
1782
from, to, totalsize);
1785
if((data->set.ftp_list_only) || !ftp->file) {
1786
/* The specified path ends with a slash, and therefore we think this
1787
is a directory that is requested, use LIST. But before that we
1788
need to set ASCII transfer mode. */
1791
/* Set type to ASCII */
1792
result = ftp_transfertype(conn, TRUE /* ASCII enforced */);
1796
/* if this output is to be machine-parsed, the NLST command will be
1797
better used since the LIST command output is not specified or
1798
standard in any way */
1800
FTPSENDF(conn, "%s",
1801
data->set.customrequest?data->set.customrequest:
1802
(data->set.ftp_list_only?"NLST":"LIST"));
1807
/* Set type to binary (unless specified ASCII) */
1808
result = ftp_transfertype(conn, data->set.ftp_ascii);
1812
/* Send any PREQUOTE strings after transfer type is set? (Wesley Laxton)*/
1813
if(data->set.prequote) {
1814
if ((result = ftp_sendquote(conn, data->set.prequote)) != CURLE_OK)
1818
/* Attempt to get the size, it'll be useful in some cases: for resumed
1819
downloads and when talking to servers that don't give away the size
1820
in the RETR response line. */
1821
result = ftp_getsize(conn, ftp->file, &foundsize);
1822
if(CURLE_OK == result)
1823
downloadsize = foundsize;
1825
if(conn->resume_from) {
1827
/* Daniel: (August 4, 1999)
1829
* We start with trying to use the SIZE command to figure out the size
1830
* of the file we're gonna get. If we can get the size, this is by far
1831
* the best way to know if we're trying to resume beyond the EOF.
1833
* Daniel, November 28, 2001. We *always* get the size on downloads
1834
* now, so it is done before this even when not doing resumes. I saved
1835
* the comment above for nostalgical reasons! ;-)
1837
if(CURLE_OK != result) {
1838
infof(data, "ftp server doesn't support SIZE\n");
1839
/* We couldn't get the size and therefore we can't know if there
1840
really is a part of the file left to get, although the server
1841
will just close the connection when we start the connection so it
1842
won't cause us any harm, just not make us exit as nicely. */
1845
/* We got a file size report, so we check that there actually is a
1846
part of the file left to get, or else we go home. */
1847
if(conn->resume_from< 0) {
1848
/* We're supposed to download the last abs(from) bytes */
1849
if(foundsize < -conn->resume_from) {
1850
failf(data, "Offset (%d) was beyond file size (%d)",
1851
conn->resume_from, foundsize);
1852
return CURLE_FTP_BAD_DOWNLOAD_RESUME;
1854
/* convert to size to download */
1855
downloadsize = -conn->resume_from;
1856
/* download from where? */
1857
conn->resume_from = foundsize - downloadsize;
1860
if(foundsize < conn->resume_from) {
1861
failf(data, "Offset (%d) was beyond file size (%d)",
1862
conn->resume_from, foundsize);
1863
return CURLE_FTP_BAD_DOWNLOAD_RESUME;
1865
/* Now store the number of bytes we are expected to download */
1866
downloadsize = foundsize-conn->resume_from;
1870
if (downloadsize == 0) {
1871
/* no data to transfer */
1872
result=Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1873
infof(data, "File already completely downloaded\n");
1875
/* Set resume done so that we won't get any error in Curl_ftp_done()
1876
* because we didn't transfer the amount of bytes that the remote
1877
* file obviously is */
1878
conn->bits.resume_done = TRUE;
1883
/* Set resume file transfer offset */
1884
infof(data, "Instructs server to resume from offset %d\n",
1887
FTPSENDF(conn, "REST %d", conn->resume_from);
1889
nread = Curl_GetFTPResponse(buf, conn, &ftpcode);
1891
return CURLE_OPERATION_TIMEOUTED;
1893
if(ftpcode != 350) {
1894
failf(data, "Couldn't use REST: %s", buf+4);
1895
return CURLE_FTP_COULDNT_USE_REST;
1899
FTPSENDF(conn, "RETR %s", ftp->file);
1902
nread = Curl_GetFTPResponse(buf, conn, &ftpcode);
1904
return CURLE_OPERATION_TIMEOUTED;
1906
if((ftpcode == 150) || (ftpcode == 125)) {
1910
150 Opening BINARY mode data connection for /etc/passwd (2241
1911
bytes). (ok, the file is being transfered)
1914
150 Opening ASCII mode data connection for /bin/ls
1917
150 ASCII data connection for /bin/ls (137.167.104.91,37445) (0 bytes).
1920
150 Opening ASCII mode data connection for /linux/fisk/kpanelrc (0.0.0.0,0) (545 bytes).
1923
125 Data connection already open; Transfer starting. */
1925
int size=-1; /* default unknown size */
1928
!data->set.ftp_ascii &&
1929
(-1 == downloadsize)) {
1931
* It seems directory listings either don't show the size or very
1932
* often uses size 0 anyway. ASCII transfers may very well turn out
1933
* that the transfered amount of data is not the same as this line
1934
* tells, why using this number in those cases only confuses us.
1936
* Example D above makes this parsing a little tricky */
1938
bytes=strstr(buf, " bytes");
1940
int index=bytes-buf;
1941
/* this is a hint there is size information in there! ;-) */
1943
/* scan for the parenthesis and break there */
1946
/* if only skip digits, or else we're in deep trouble */
1947
if(!isdigit((int)*bytes)) {
1951
/* one more estep backwards */
1954
/* only if we have nothing but digits: */
1956
/* get the number! */
1962
else if(downloadsize > -1)
1963
size = downloadsize;
1965
if(data->set.ftp_use_port) {
1966
result = AllowServerConnect(data, conn, conn->secondarysocket);
1971
infof(data, "Getting file with size: %d\n", size);
1974
result=Curl_Transfer(conn, conn->secondarysocket, size, FALSE,
1976
-1, NULL); /* no upload here */
1981
failf(data, "%s", buf+4);
1982
return CURLE_FTP_COULDNT_RETR_FILE;
1986
/* end of transfer */
1991
/***********************************************************************
1995
* This function is registered as 'curl_do' function. It decodes the path
1996
* parts etc as a wrapper to the actual DO function (ftp_perform).
1998
* The input argument is already checked for validity.
2000
CURLcode Curl_ftp(struct connectdata *conn)
2004
struct SessionHandle *data = conn->data;
2006
int dirlength=0; /* 0 forces strlen() */
2008
/* the ftp struct is already inited in ftp_connect() */
2009
ftp = conn->proto.ftp;
2011
/* We split the path into dir and file parts *before* we URLdecode
2013
ftp->file = strrchr(conn->ppath, '/');
2015
if(ftp->file != conn->ppath)
2016
dirlength=ftp->file-conn->ppath; /* don't count the traling slash */
2018
ftp->file++; /* point to the first letter in the file name part or
2022
ftp->file = conn->ppath; /* there's only a file part */
2026
ftp->file = curl_unescape(ftp->file, 0);
2027
if(NULL == ftp->file) {
2028
failf(data, "no memory");
2029
return CURLE_OUT_OF_MEMORY;
2033
ftp->file=NULL; /* instead of point to a zero byte, we make it a NULL
2036
ftp->urlpath = conn->ppath;
2038
ftp->dir = curl_unescape(ftp->urlpath, dirlength);
2039
if(NULL == ftp->dir) {
2042
failf(data, "no memory");
2043
return CURLE_OUT_OF_MEMORY; /* failure */
2049
retcode = ftp_perform(conn);
2051
/* clean up here, success or error doesn't matter */
2057
ftp->file = ftp->dir = NULL; /* zero */
2062
/***********************************************************************
2066
* Sends the formated string as a ftp command to a ftp server
2068
* NOTE: we build the command in a fixed-length buffer, which sets length
2069
* restrictions on the command!
2071
CURLcode Curl_ftpsendf(struct connectdata *conn,
2072
const char *fmt, ...)
2074
ssize_t bytes_written;
2078
CURLcode res = CURLE_OK;
2082
vsnprintf(s, 250, fmt, ap);
2085
if(conn->data->set.verbose)
2086
fprintf(conn->data->set.err, "> %s\n", s);
2088
strcat(s, "\r\n"); /* append a trailing CRLF */
2091
write_len = strlen(s);
2094
res = Curl_write(conn, conn->firstsocket, sptr, write_len,
2100
if(bytes_written != write_len) {
2101
write_len -= bytes_written;
2102
sptr += bytes_written;
2111
/***********************************************************************
2113
* Curl_ftp_disconnect()
2115
* Disconnect from an FTP server. Cleanup protocol-specific per-connection
2118
CURLcode Curl_ftp_disconnect(struct connectdata *conn)
2120
struct FTP *ftp= conn->proto.ftp;
2122
/* The FTP session may or may not have been allocated/setup at this point! */
2125
free(ftp->entrypath);
2134
* eval: (load-file "../curl-mode.el")
2136
* vim600: fdm=marker
2137
* vim: et sw=2 ts=2 sts=2 tw=78