1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al.
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at http://curl.haxx.se/docs/copyright.html.
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
22 ***************************************************************************/
26 #ifndef CURL_DISABLE_FTP
37 #ifdef HAVE_SYS_SELECT_H
38 #include <sys/select.h>
41 #if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
43 #else /* some kind of unix */
44 #ifdef HAVE_SYS_SOCKET_H
45 #include <sys/socket.h>
47 #include <sys/types.h>
48 #ifdef HAVE_NETINET_IN_H
49 #include <netinet/in.h>
51 #ifdef HAVE_ARPA_INET_H
52 #include <arpa/inet.h>
54 #include <sys/utsname.h>
64 #if defined(WIN32) && defined(__GNUC__) || defined(__MINGW32__)
68 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
70 #define in_addr_t unsigned long
73 #include <curl/curl.h>
82 #include "http.h" /* for HTTP proxy tunnel stuff */
90 #include "strtoofft.h"
96 #include "inet_ntop.h"
98 #if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL)
99 #include "inet_ntoa_r.h"
102 #define _MPRINTF_REPLACE /* use our functions only */
103 #include <curl/mprintf.h>
105 /* The last #include file should be: */
107 #include "memdebug.h"
110 #ifdef HAVE_NI_WITHSCOPEID
111 #define NIFLAGS NI_NUMERICHOST | NI_NUMERICSERV | NI_WITHSCOPEID
113 #define NIFLAGS NI_NUMERICHOST | NI_NUMERICSERV
116 /* Local API functions */
117 static CURLcode ftp_sendquote(struct connectdata *conn,
118 struct curl_slist *quote);
119 static CURLcode ftp_cwd(struct connectdata *conn, char *path);
120 static CURLcode ftp_mkd(struct connectdata *conn, char *path);
121 static CURLcode ftp_cwd_and_mkd(struct connectdata *conn, char *path);
122 static CURLcode ftp_quit(struct connectdata *conn);
123 static CURLcode ftp_3rdparty_pretransfer(struct connectdata *conn);
124 static CURLcode ftp_3rdparty_transfer(struct connectdata *conn);
125 static CURLcode ftp_regular_transfer(struct connectdata *conn);
126 static CURLcode ftp_3rdparty(struct connectdata *conn);
128 /* easy-to-use macro: */
129 #define FTPSENDF(x,y,z) if((result = Curl_ftpsendf(x,y,z))) return result
131 static void freedirs(struct FTP *ftp)
135 for (i=0; i < ftp->dirdepth; i++){
150 /***********************************************************************
152 * AllowServerConnect()
154 * When we've issue the PORT command, we have told the server to connect
155 * to us. This function will sit and wait here until the server has
159 static CURLcode AllowServerConnect(struct connectdata *conn)
163 struct SessionHandle *data = conn->data;
164 curl_socket_t sock = conn->sock[SECONDARYSOCKET];
165 struct timeval now = Curl_tvnow();
166 long timespent = Curl_tvdiff(Curl_tvnow(), now)/1000;
167 long timeout = data->set.connecttimeout?data->set.connecttimeout:
168 (data->set.timeout?data->set.timeout: 0);
172 FD_SET(sock, &rdset);
175 timeout -= timespent;
177 failf(data, "Timed out before server could connect to us");
178 return CURLE_OPERATION_TIMEDOUT;
182 /* we give the server 60 seconds to connect to us, or a custom timeout */
183 dt.tv_sec = (int)(timeout?timeout:60);
186 switch (select(sock+1, &rdset, NULL, NULL, &dt)) {
189 failf(data, "Error while waiting for server connect");
190 return CURLE_FTP_PORT_FAILED;
191 case 0: /* timeout */
193 failf(data, "Timeout while waiting for server connect");
194 return CURLE_FTP_PORT_FAILED;
196 /* we have received data here */
199 size_t size = sizeof(struct sockaddr_in);
200 struct sockaddr_in add;
202 getsockname(sock, (struct sockaddr *) &add, (socklen_t *)&size);
203 s=accept(sock, (struct sockaddr *) &add, (socklen_t *)&size);
205 sclose(sock); /* close the first socket */
207 if (CURL_SOCKET_BAD == s) {
209 failf(data, "Error accept()ing server connect");
210 return CURLE_FTP_PORT_FAILED;
212 infof(data, "Connection accepted from server\n");
214 conn->sock[SECONDARYSOCKET] = s;
215 Curl_nonblock(s, TRUE); /* enable non-blocking */
224 /* --- parse FTP server responses --- */
227 * Curl_GetFTPResponse() is supposed to be invoked after each command sent to
228 * a remote FTP server. This function will wait and read all lines of the
229 * response and extract the relevant return code for the invoking function.
232 CURLcode Curl_GetFTPResponse(ssize_t *nreadp, /* return number of bytes read */
233 struct connectdata *conn,
234 int *ftpcode) /* return the ftp-code */
236 /* Brand new implementation.
237 * We cannot read just one byte per read() and then go back to select()
238 * as it seems that the OpenSSL read() stuff doesn't grok that properly.
240 * Alas, read as much as possible, split up into lines, use the ending
241 * line in a response or continue reading. */
243 curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
244 int perline; /* count bytes per line */
248 long timeout; /* timeout in seconds */
249 struct timeval interval;
252 struct SessionHandle *data = conn->data;
254 int code=0; /* default ftp "error code" to return */
255 char *buf = data->state.buffer;
256 CURLcode result = CURLE_OK;
257 struct FTP *ftp = conn->proto.ftp;
258 struct timeval now = Curl_tvnow();
261 *ftpcode = 0; /* 0 for errors */
263 FD_ZERO (&readfd); /* clear it */
264 FD_SET (sockfd, &readfd); /* read socket */
266 /* get this in a backup variable to be able to restore it on each lap in the
277 while((*nreadp<BUFSIZE) && (keepon && !result)) {
278 /* check and reset timeout value every lap */
279 if(data->set.ftp_response_timeout )
280 /* if CURLOPT_FTP_RESPONSE_TIMEOUT is set, use that to determine
281 remaining time. Also, use "now" as opposed to "conn->now"
282 because ftp_response_timeout is only supposed to govern
283 the response for any given ftp response, not for the time
284 from connect to the given ftp response. */
285 timeout = data->set.ftp_response_timeout - /* timeout time */
286 Curl_tvdiff(Curl_tvnow(), now)/1000; /* spent time */
287 else if(data->set.timeout)
288 /* if timeout is requested, find out how much remaining time we have */
289 timeout = data->set.timeout - /* timeout time */
290 Curl_tvdiff(Curl_tvnow(), conn->now)/1000; /* spent time */
292 /* Even without a requested timeout, we only wait response_time
293 seconds for the full response to arrive before we bail out */
294 timeout = ftp->response_time -
295 Curl_tvdiff(Curl_tvnow(), now)/1000; /* spent time */
298 failf(data, "FTP response timeout");
299 return CURLE_OPERATION_TIMEDOUT; /* already too little time */
303 readfd = rkeepfd; /* set every lap */
304 interval.tv_sec = 1; /* use 1 second timeout intervals */
305 interval.tv_usec = 0;
307 switch (select (sockfd+1, &readfd, NULL, NULL, &interval)) {
308 case -1: /* select() error, stop reading */
309 result = CURLE_RECV_ERROR;
310 failf(data, "FTP response aborted due to select() error: %d", errno);
312 case 0: /* timeout */
313 if(Curl_pgrsUpdate(conn))
314 return CURLE_ABORTED_BY_CALLBACK;
315 continue; /* just continue in our loop for the timeout duration */
321 if(CURLE_OK == result) {
323 * This code previously didn't use the kerberos sec_read() code
324 * to read, but when we use Curl_read() it may do so. Do confirm
325 * that this is still ok and then remove this comment!
328 /* we had data in the "cache", copy that instead of doing an actual
331 * Dave Meyer, December 2003:
332 * ftp->cache_size is cast to int here. This should be safe,
333 * because it would have been populated with something of size
334 * int to begin with, even though its datatype may be larger
337 memcpy(ptr, ftp->cache, (int)ftp->cache_size);
338 gotbytes = (int)ftp->cache_size;
339 free(ftp->cache); /* free the cache */
340 ftp->cache = NULL; /* clear the pointer */
341 ftp->cache_size = 0; /* zero the size just in case */
344 int res = Curl_read(conn, sockfd, ptr, BUFSIZE-*nreadp, &gotbytes);
347 continue; /* go looping again */
355 else if(gotbytes <= 0) {
357 result = CURLE_RECV_ERROR;
358 failf(data, "FTP response reading failed");
361 /* we got a whole chunk of data, which can be anything from one
362 * byte to a set of lines and possible just a piece of the last
366 conn->headerbytecount += gotbytes;
369 for(i = 0; i < gotbytes; ptr++, i++) {
372 /* a newline is CRLF in ftp-talk, so the CR is ignored as
373 the line isn't really terminated until the LF comes */
375 /* output debug output if that is requested */
376 if(data->set.verbose)
377 Curl_debug(data, CURLINFO_HEADER_IN, line_start, perline, conn->host.dispname);
380 * We pass all response-lines to the callback function registered
381 * for "headers". The response lines can be seen as a kind of
384 result = Curl_client_write(data, CLIENTWRITE_HEADER,
385 line_start, perline);
389 #define lastline(line) (isdigit((int)line[0]) && isdigit((int)line[1]) && \
390 isdigit((int)line[2]) && (' ' == line[3]))
392 if(perline>3 && lastline(line_start)) {
393 /* This is the end of the last line, copy the last
394 * line to the start of the buffer and zero terminate,
395 * for old times sake (and krb4)! */
398 for(meow=line_start, n=0; meow<ptr; meow++, n++)
400 *meow=0; /* zero terminate */
402 line_start = ptr+1; /* advance pointer */
403 i++; /* skip this before getting out */
406 perline=0; /* line starts over here */
410 if(!keepon && (i != gotbytes)) {
411 /* We found the end of the response lines, but we didn't parse the
412 full chunk of data we have read from the server. We therefore
413 need to store the rest of the data to be checked on the next
414 invoke as it may actually contain another end of response
415 already! Cleverly figured out by Eric Lavigne in December
417 ftp->cache_size = gotbytes - i;
418 ftp->cache = (char *)malloc((int)ftp->cache_size);
420 memcpy(ftp->cache, line_start, (int)ftp->cache_size);
422 return CURLE_OUT_OF_MEMORY; /**BANG**/
424 } /* there was data */
426 } /* while there's buffer left and loop is requested */
432 /* handle the security-oriented responses 6xx ***/
433 /* FIXME: some errorchecking perhaps... ***/
436 Curl_sec_read_msg(conn, buf, prot_safe);
439 Curl_sec_read_msg(conn, buf, prot_private);
442 Curl_sec_read_msg(conn, buf, prot_confidential);
445 /* normal ftp stuff we pass through! */
451 *ftpcode=code; /* return the initial number like this */
453 /* store the latest code for later retrieval */
454 conn->data->info.httpcode=code;
459 static const char *ftpauth[]= {
464 * Curl_ftp_connect() should do everything that is to be considered a part of
465 * the connection phase.
467 CURLcode Curl_ftp_connect(struct connectdata *conn)
469 /* this is FTP and no proxy */
471 struct SessionHandle *data=conn->data;
472 char *buf = data->state.buffer; /* this is our buffer */
477 ftp = (struct FTP *)malloc(sizeof(struct FTP));
479 return CURLE_OUT_OF_MEMORY;
481 memset(ftp, 0, sizeof(struct FTP));
482 conn->proto.ftp = ftp;
484 /* We always support persistant connections on ftp */
485 conn->bits.close = FALSE;
487 /* get some initial data into the ftp struct */
488 ftp->bytecountp = &conn->bytecount;
490 /* no need to duplicate them, this connectdata struct won't change */
491 ftp->user = conn->user;
492 ftp->passwd = conn->passwd;
493 ftp->response_time = 3600; /* set default response time-out */
495 #ifndef CURL_DISABLE_HTTP
496 if (conn->bits.tunnel_proxy) {
497 /* We want "seamless" FTP operations through HTTP proxy tunnel */
498 result = Curl_ConnectHTTPProxyTunnel(conn, FIRSTSOCKET,
499 conn->host.name, conn->remote_port);
500 if(CURLE_OK != result)
503 #endif /* CURL_DISABLE_HTTP */
505 if(conn->protocol & PROT_FTPS) {
506 /* FTPS is simply ftp with SSL for the control channel */
507 /* now, perform the SSL initialization for this socket */
508 result = Curl_SSLConnect(conn, FIRSTSOCKET);
513 /* The first thing we do is wait for the "220*" line: */
514 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
519 failf(data, "This doesn't seem like a nice ftp-server response");
520 return CURLE_FTP_WEIRD_SERVER_REPLY;
524 /* if not anonymous login, try a secure login */
527 /* request data protection level (default is 'clear') */
528 Curl_sec_request_prot(conn, "private");
530 /* We set private first as default, in case the line below fails to
532 Curl_sec_request_prot(conn, data->set.krb4_level);
534 if(Curl_sec_login(conn) != 0)
535 infof(data, "Logging in with password in cleartext!\n");
537 infof(data, "Authentication successful\n");
541 if(data->set.ftp_ssl && !conn->ssl[FIRSTSOCKET].use) {
542 /* we don't have a SSL/TLS connection, try a FTPS connection now */
544 for (try = 0; ftpauth[try]; try++) {
546 FTPSENDF(conn, "AUTH %s", ftpauth[try]);
548 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
553 /* RFC2228 (page 5) says:
555 * If the server is willing to accept the named security mechanism, and
556 * does not require any security data, it must respond with reply code
560 if((ftpcode == 234) || (ftpcode == 334)) {
561 result = Curl_SSLConnect(conn, FIRSTSOCKET);
564 conn->protocol |= PROT_FTPS;
565 conn->ssl[SECONDARYSOCKET].use = FALSE; /* clear-text data */
572 FTPSENDF(conn, "USER %s", ftp->user?ftp->user:"");
574 /* wait for feedback */
575 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
580 /* 530 User ... access denied
581 (the server denies to log the specified user) */
582 failf(data, "Access denied: %s", &buf[4]);
583 return CURLE_FTP_ACCESS_DENIED;
585 else if(ftpcode == 331) {
586 /* 331 Password required for ...
587 (the server requires to send the user's password too) */
588 FTPSENDF(conn, "PASS %s", ftp->passwd?ftp->passwd:"");
589 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
594 /* 530 Login incorrect.
595 (the username and/or the password are incorrect) */
596 failf(data, "the username and/or the password are incorrect");
597 return CURLE_FTP_USER_PASSWORD_INCORRECT;
599 else if(ftpcode == 230) {
600 /* 230 User ... logged in.
601 (user successfully logged in) */
603 infof(data, "We have successfully logged in\n");
606 failf(data, "Odd return code after PASS");
607 return CURLE_FTP_WEIRD_PASS_REPLY;
610 else if(buf[0] == '2') {
611 /* 230 User ... logged in.
612 (the user logged in without password) */
613 infof(data, "We have successfully logged in\n");
614 if (conn->ssl[FIRSTSOCKET].use) {
616 /* We are logged in with Kerberos, now set the requested protection
619 if(conn->sec_complete)
620 Curl_sec_set_protection_level(conn);
622 /* We may need to issue a KAUTH here to have access to the files
623 * do it if user supplied a password
625 if(conn->passwd && *conn->passwd) {
626 result = Curl_krb_kauth(conn);
634 failf(data, "Odd return code after USER");
635 return CURLE_FTP_WEIRD_USER_REPLY;
638 if(conn->ssl[FIRSTSOCKET].use) {
639 /* PBSZ = PROTECTION BUFFER SIZE.
641 The 'draft-murray-auth-ftp-ssl' (draft 12, page 7) says:
643 Specifically, the PROT command MUST be preceded by a PBSZ command
644 and a PBSZ command MUST be preceded by a successful security data
645 exchange (the TLS negotiation in this case)
649 Thus the PBSZ command must still be issued, but must have a parameter
650 of '0' to indicate that no buffering is taking place and the data
651 connection should not be encapsulated.
653 FTPSENDF(conn, "PBSZ %d", 0);
654 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
658 /* For TLS, the data connection can have one of two security levels.
660 1)Clear (requested by 'PROT C')
662 2)Private (requested by 'PROT P')
664 if(!conn->ssl[SECONDARYSOCKET].use) {
665 FTPSENDF(conn, "PROT %c", 'P');
666 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
671 /* We have enabled SSL for the data connection! */
672 conn->ssl[SECONDARYSOCKET].use = TRUE;
674 /* FTP servers typically responds with 500 if they decide to reject
679 /* send PWD to discover our entry point */
680 FTPSENDF(conn, "PWD", NULL);
682 /* wait for feedback */
683 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
688 char *dir = (char *)malloc(nread+1);
690 char *ptr=&buf[4]; /* start on the first letter */
693 return CURLE_OUT_OF_MEMORY;
695 /* Reply format is like
696 257<space>"<directory-name>"<space><commentary> and the RFC959 says
698 The directory name can contain any character; embedded double-quotes
699 should be escaped by double-quotes (the "quote-doubling" convention).
702 /* it started good */
707 /* "quote-doubling" */
713 *store = '\0'; /* zero terminate */
714 break; /* get out of this loop */
722 ftp->entrypath =dir; /* remember this */
723 infof(data, "Entry path is '%s'\n", ftp->entrypath);
726 /* couldn't get the path */
728 infof(data, "Failed to figure out path\n");
733 /* We couldn't read the PWD response! */
739 /***********************************************************************
743 * The DONE function. This does what needs to be done after a single DO has
746 * Input argument is already checked for validity.
748 CURLcode Curl_ftp_done(struct connectdata *conn, CURLcode status)
750 struct SessionHandle *data = conn->data;
751 struct FTP *ftp = conn->proto.ftp;
754 CURLcode result=CURLE_OK;
756 bool was_ctl_valid = ftp->ctl_valid;
758 /* free the dir tree and file parts */
761 ftp->ctl_valid = FALSE;
763 if(data->set.upload) {
764 if((-1 != data->set.infilesize) &&
765 (data->set.infilesize != *ftp->bytecountp) &&
767 failf(data, "Uploaded unaligned file size (%" FORMAT_OFF_T
768 " out of %" FORMAT_OFF_T " bytes)",
769 *ftp->bytecountp, data->set.infilesize);
770 conn->bits.close = TRUE; /* close this connection since we don't
771 know what state this error leaves us in */
772 return CURLE_PARTIAL_FILE;
776 if((-1 != conn->size) && (conn->size != *ftp->bytecountp) &&
777 (conn->maxdownload != *ftp->bytecountp)) {
778 failf(data, "Received only partial file: %" FORMAT_OFF_T " bytes",
780 conn->bits.close = TRUE; /* close this connection since we don't
781 know what state this error leaves us in */
782 return CURLE_PARTIAL_FILE;
784 else if(!ftp->dont_check &&
787 /* We consider this an error, but there's no true FTP error received
788 why we need to continue to "read out" the server response too.
789 We don't want to leave a "waiting" server reply if we'll get told
790 to make a second request on this same connection! */
791 failf(data, "No data was received!");
792 result = CURLE_FTP_COULDNT_RETR_FILE;
797 case CURLE_BAD_DOWNLOAD_RESUME:
798 case CURLE_FTP_WEIRD_PASV_REPLY:
799 case CURLE_FTP_PORT_FAILED:
800 case CURLE_FTP_COULDNT_SET_BINARY:
801 case CURLE_FTP_COULDNT_RETR_FILE:
802 case CURLE_FTP_ACCESS_DENIED:
803 /* the connection stays alive fine even though this happened */
805 case CURLE_OK: /* doesn't affect the control connection's status */
806 ftp->ctl_valid = was_ctl_valid;
808 default: /* by default, an error means the control connection is
809 wedged and should not be used anymore */
810 ftp->ctl_valid = FALSE;
815 Curl_sec_fflush_fd(conn, conn->sock[SECONDARYSOCKET]);
817 /* shut down the socket to inform the server we're done */
818 sclose(conn->sock[SECONDARYSOCKET]);
819 conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
821 if(!ftp->no_transfer && !status) {
822 /* Let's see what the server says about the transfer we just performed,
823 * but lower the timeout as sometimes this connection has died while the
824 * data has been transfered. This happens when doing through NATs etc that
825 * abandon old silent connections.
827 ftp->response_time = 60; /* give it only a minute for now */
829 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
831 ftp->response_time = 3600; /* set this back to one hour waits */
833 if(!nread && (CURLE_OPERATION_TIMEDOUT == result)) {
834 failf(data, "control connection looks dead");
841 if(!ftp->dont_check) {
842 /* 226 Transfer complete, 250 Requested file action okay, completed. */
843 if((ftpcode != 226) && (ftpcode != 250)) {
844 failf(data, "server did not report OK, got %d", ftpcode);
845 return CURLE_FTP_WRITE_ERROR;
850 /* clear these for next connection */
851 ftp->no_transfer = FALSE;
852 ftp->dont_check = FALSE;
854 if (!result && conn->sec_conn) { /* 3rd party transfer */
855 /* "done" with the secondary connection */
856 result = Curl_ftp_done(conn->sec_conn, status);
859 /* Send any post-transfer QUOTE strings? */
860 if(!status && !result && data->set.postquote)
861 result = ftp_sendquote(conn, data->set.postquote);
866 /***********************************************************************
870 * Where a 'quote' means a list of custom commands to send to the server.
871 * The quote list is passed as an argument.
875 CURLcode ftp_sendquote(struct connectdata *conn, struct curl_slist *quote)
877 struct curl_slist *item;
885 FTPSENDF(conn, "%s", item->data);
887 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
891 if (ftpcode >= 400) {
892 failf(conn->data, "QUOT string not accepted: %s", item->data);
893 return CURLE_FTP_QUOTE_ERROR;
903 /***********************************************************************
907 * Get the timestamp of the given file.
910 CURLcode ftp_getfiletime(struct connectdata *conn, char *file)
912 CURLcode result=CURLE_OK;
913 int ftpcode; /* for ftp status */
915 char *buf = conn->data->state.buffer;
917 /* we have requested to get the modified-time of the file, this is yet
918 again a grey area as the MDTM is not kosher RFC959 */
919 FTPSENDF(conn, "MDTM %s", file);
921 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
928 /* we got a time. Format should be: "YYYYMMDDHHMMSS[.sss]" where the
929 last .sss part is optional and means fractions of a second */
930 int year, month, day, hour, minute, second;
931 if(6 == sscanf(buf+4, "%04d%02d%02d%02d%02d%02d",
932 &year, &month, &day, &hour, &minute, &second)) {
933 /* we have a time, reformat it */
934 time_t secs=time(NULL);
935 snprintf(buf, sizeof(conn->data->state.buffer),
936 "%04d%02d%02d %02d:%02d:%02d GMT",
937 year, month, day, hour, minute, second);
938 /* now, convert this into a time() value: */
939 conn->data->info.filetime = curl_getdate(buf, &secs);
944 infof(conn->data, "unsupported MDTM reply format\n");
946 case 550: /* "No such file or directory" */
947 failf(conn->data, "Given file does not exist");
948 result = CURLE_FTP_COULDNT_RETR_FILE;
954 /***********************************************************************
958 * Set transfer type. We only deal with ASCII or BINARY so this function
961 static CURLcode ftp_transfertype(struct connectdata *conn,
964 struct SessionHandle *data = conn->data;
969 FTPSENDF(conn, "TYPE %s", ascii?"A":"I");
971 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
976 failf(data, "Couldn't set %s mode",
977 ascii?"ASCII":"binary");
978 return ascii? CURLE_FTP_COULDNT_SET_ASCII:CURLE_FTP_COULDNT_SET_BINARY;
984 /***********************************************************************
988 * Returns the file size (in bytes) of the given remote file.
992 CURLcode ftp_getsize(struct connectdata *conn, char *file,
995 struct SessionHandle *data = conn->data;
998 char *buf=data->state.buffer;
1001 FTPSENDF(conn, "SIZE %s", file);
1002 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
1006 if(ftpcode == 213) {
1007 /* get the size from the ascii string: */
1008 *size = curlx_strtoofft(buf+4, NULL, 0);
1011 return CURLE_FTP_COULDNT_GET_SIZE;
1016 /***************************************************************************
1018 * ftp_pasv_verbose()
1020 * This function only outputs some informationals about this second connection
1021 * when we've issued a PASV command before and thus we have connected to a
1022 * possibly new IP address.
1026 ftp_pasv_verbose(struct connectdata *conn,
1028 char *newhost, /* ascii version */
1032 Curl_printable_address(ai, buf, sizeof(buf));
1033 infof(conn->data, "Connecting to %s (%s) port %d\n", newhost, buf, port);
1036 /***********************************************************************
1040 * Send the proper PORT command. PORT is the ftp client's way of telling the
1041 * server that *WE* open a port that we listen on an awaits the server to
1042 * connect to. This is the opposite of PASV.
1046 CURLcode ftp_use_port(struct connectdata *conn)
1048 struct SessionHandle *data=conn->data;
1049 curl_socket_t portsock= CURL_SOCKET_BAD;
1051 int ftpcode; /* receive FTP response codes in this */
1055 /******************************************************************
1057 * Here's a piece of IPv6-specific code coming up
1061 struct addrinfo hints, *res, *ai;
1062 struct sockaddr_storage ss;
1064 char hbuf[NI_MAXHOST];
1066 struct sockaddr *sa=(struct sockaddr *)&ss;
1069 char portmsgbuf[1024], tmp[1024];
1071 const char *mode[] = { "EPRT", "LPRT", "PORT", NULL };
1077 * we should use Curl_if2ip? given pickiness of recent ftpd,
1078 * I believe we should use the same address as the control connection.
1081 rc = getsockname(conn->sock[FIRSTSOCKET], (struct sockaddr *)&ss, &sslen);
1083 failf(data, "getsockname() returned %d\n", rc);
1084 return CURLE_FTP_PORT_FAILED;
1087 rc = getnameinfo((struct sockaddr *)&ss, sslen, hbuf, sizeof(hbuf), NULL, 0,
1090 failf(data, "getnameinfo() returned %d\n", rc);
1091 return CURLE_FTP_PORT_FAILED;
1094 memset(&hints, 0, sizeof(hints));
1095 hints.ai_family = sa->sa_family;
1096 /*hints.ai_family = ss.ss_family;
1097 this way can be used if sockaddr_storage is properly defined, as glibc
1099 hints.ai_socktype = SOCK_STREAM;
1100 hints.ai_flags = AI_PASSIVE;
1102 rc = getaddrinfo(hbuf, NULL, &hints, &res);
1104 failf(data, "getaddrinfo() returned %d\n", rc);
1105 return CURLE_FTP_PORT_FAILED;
1108 portsock = CURL_SOCKET_BAD;
1110 for (ai = res; ai; ai = ai->ai_next) {
1112 * Workaround for AIX5 getaddrinfo() problem (it doesn't set ai_socktype):
1114 if (ai->ai_socktype == 0)
1115 ai->ai_socktype = hints.ai_socktype;
1117 portsock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
1118 if (portsock == CURL_SOCKET_BAD) {
1119 error = Curl_ourerrno();
1123 if (bind(portsock, ai->ai_addr, ai->ai_addrlen) < 0) {
1124 error = Curl_ourerrno();
1126 portsock = CURL_SOCKET_BAD;
1130 if (listen(portsock, 1) < 0) {
1131 error = Curl_ourerrno();
1133 portsock = CURL_SOCKET_BAD;
1140 if (portsock == CURL_SOCKET_BAD) {
1141 failf(data, "%s", Curl_strerror(conn,error));
1142 return CURLE_FTP_PORT_FAILED;
1146 if (getsockname(portsock, sa, &sslen) < 0) {
1147 failf(data, "%s", Curl_strerror(conn,Curl_ourerrno()));
1148 return CURLE_FTP_PORT_FAILED;
1151 for (modep = (char **)(data->set.ftp_use_eprt?&mode[0]:&mode[2]);
1152 modep && *modep; modep++) {
1156 switch (sa->sa_family) {
1158 ap = (unsigned char *)&((struct sockaddr_in *)&ss)->sin_addr;
1159 alen = sizeof(((struct sockaddr_in *)&ss)->sin_addr);
1160 pp = (unsigned char *)&((struct sockaddr_in *)&ss)->sin_port;
1161 plen = sizeof(((struct sockaddr_in *)&ss)->sin_port);
1166 ap = (unsigned char *)&((struct sockaddr_in6 *)&ss)->sin6_addr;
1167 alen = sizeof(((struct sockaddr_in6 *)&ss)->sin6_addr);
1168 pp = (unsigned char *)&((struct sockaddr_in6 *)&ss)->sin6_port;
1169 plen = sizeof(((struct sockaddr_in6 *)&ss)->sin6_port);
1175 lprtaf = eprtaf = -1;
1179 if (strcmp(*modep, "EPRT") == 0) {
1182 if (getnameinfo((struct sockaddr *)&ss, sslen,
1183 portmsgbuf, sizeof(portmsgbuf), tmp, sizeof(tmp),
1187 /* do not transmit IPv6 scope identifier to the wire */
1188 if (sa->sa_family == AF_INET6) {
1189 char *q = strchr(portmsgbuf, '%');
1194 result = Curl_ftpsendf(conn, "%s |%d|%s|%s|", *modep, eprtaf,
1199 else if (strcmp(*modep, "LPRT") == 0 ||
1200 strcmp(*modep, "PORT") == 0) {
1203 if (strcmp(*modep, "LPRT") == 0 && lprtaf < 0)
1205 if (strcmp(*modep, "PORT") == 0 && sa->sa_family != AF_INET)
1208 portmsgbuf[0] = '\0';
1209 if (strcmp(*modep, "LPRT") == 0) {
1210 snprintf(tmp, sizeof(tmp), "%d,%d", lprtaf, alen);
1211 if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >=
1212 sizeof(portmsgbuf)) {
1217 for (i = 0; i < alen; i++) {
1219 snprintf(tmp, sizeof(tmp), ",%u", ap[i]);
1221 snprintf(tmp, sizeof(tmp), "%u", ap[i]);
1223 if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >=
1224 sizeof(portmsgbuf)) {
1229 if (strcmp(*modep, "LPRT") == 0) {
1230 snprintf(tmp, sizeof(tmp), ",%d", plen);
1232 if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >= sizeof(portmsgbuf))
1236 for (i = 0; i < plen; i++) {
1237 snprintf(tmp, sizeof(tmp), ",%u", pp[i]);
1239 if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >=
1240 sizeof(portmsgbuf)) {
1245 result = Curl_ftpsendf(conn, "%s %s", *modep, portmsgbuf);
1250 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
1254 if (ftpcode != 200) {
1263 failf(data, "PORT command attempts failed");
1264 return CURLE_FTP_PORT_FAILED;
1266 /* we set the secondary socket variable to this for now, it
1267 is only so that the cleanup function will close it in case
1268 we fail before the true secondary stuff is made */
1269 conn->sock[SECONDARYSOCKET] = portsock;
1272 /******************************************************************
1274 * Here's a piece of IPv4-specific code coming up
1277 struct sockaddr_in sa;
1278 unsigned short porttouse;
1279 char myhost[256] = "";
1280 bool sa_filled_in = FALSE;
1281 Curl_addrinfo *addr = NULL;
1282 unsigned short ip[4];
1284 if(data->set.ftpport) {
1287 /* First check if the given name is an IP address */
1288 in=inet_addr(data->set.ftpport);
1290 if(in != CURL_INADDR_NONE)
1291 /* this is an IPv4 address */
1292 addr = Curl_ip2addr(in, data->set.ftpport, 0);
1294 if(Curl_if2ip(data->set.ftpport, myhost, sizeof(myhost))) {
1295 /* The interface to IP conversion provided a dotted address */
1296 in=inet_addr(myhost);
1297 addr = Curl_ip2addr(in, myhost, 0);
1299 else if(strlen(data->set.ftpport)> 1) {
1300 /* might be a host name! */
1301 struct Curl_dns_entry *h=NULL;
1302 int rc = Curl_resolv(conn, myhost, 0, &h);
1303 if(rc == CURLRESOLV_PENDING)
1304 rc = Curl_wait_for_resolv(conn, &h);
1307 /* when we return from this function, we can forget about this entry
1308 to we can unlock it now already */
1309 Curl_resolv_unlock(data, h);
1312 } /* CURL_INADDR_NONE */
1313 } /* data->set.ftpport */
1316 /* pick a suitable default here */
1321 if (getsockname(conn->sock[FIRSTSOCKET],
1322 (struct sockaddr *)&sa, &sslen) < 0) {
1323 failf(data, "getsockname() failed");
1324 return CURLE_FTP_PORT_FAILED;
1327 sa_filled_in = TRUE; /* the sa struct is filled in */
1330 if (addr || sa_filled_in) {
1331 portsock = socket(AF_INET, SOCK_STREAM, 0);
1332 if(CURL_SOCKET_BAD != portsock) {
1335 /* we set the secondary socket variable to this for now, it
1336 is only so that the cleanup function will close it in case
1337 we fail before the true secondary stuff is made */
1338 conn->sock[SECONDARYSOCKET] = portsock;
1341 memcpy(&sa, addr->ai_addr, sizeof(sa));
1342 sa.sin_addr.s_addr = INADDR_ANY;
1348 if(bind(portsock, (struct sockaddr *)&sa, size) >= 0) {
1349 /* we succeeded to bind */
1350 struct sockaddr_in add;
1351 socklen_t socksize = sizeof(add);
1353 if(getsockname(portsock, (struct sockaddr *) &add,
1355 failf(data, "getsockname() failed");
1356 return CURLE_FTP_PORT_FAILED;
1358 porttouse = ntohs(add.sin_port);
1360 if ( listen(portsock, 1) < 0 ) {
1361 failf(data, "listen(2) failed on socket");
1362 return CURLE_FTP_PORT_FAILED;
1366 failf(data, "bind(2) failed on socket");
1367 return CURLE_FTP_PORT_FAILED;
1371 failf(data, "socket(2) failed (%s)");
1372 return CURLE_FTP_PORT_FAILED;
1376 failf(data, "could't find IP address to use");
1377 return CURLE_FTP_PORT_FAILED;
1381 Curl_inet_ntop(AF_INET, &((struct sockaddr_in *)&sa)->sin_addr,
1382 myhost, sizeof(myhost));
1384 Curl_printable_address(addr, myhost, sizeof(myhost));
1386 if(4 == sscanf(myhost, "%hu.%hu.%hu.%hu",
1387 &ip[0], &ip[1], &ip[2], &ip[3])) {
1389 infof(data, "Telling server to connect to %d.%d.%d.%d:%d\n",
1390 ip[0], ip[1], ip[2], ip[3], porttouse);
1392 result=Curl_ftpsendf(conn, "PORT %d,%d,%d,%d,%d,%d",
1393 ip[0], ip[1], ip[2], ip[3],
1401 return CURLE_FTP_PORT_FAILED;
1403 Curl_freeaddrinfo(addr);
1405 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
1409 if(ftpcode != 200) {
1410 failf(data, "Server does not grok PORT, try without it!");
1411 return CURLE_FTP_PORT_FAILED;
1413 #endif /* end of ipv4-specific code */
1418 /***********************************************************************
1422 * Send the PASV command. PASV is the ftp client's way of asking the server to
1423 * open a second port that we can connect to (for the data transfer). This is
1424 * the opposite of PORT.
1428 CURLcode ftp_use_pasv(struct connectdata *conn,
1431 struct SessionHandle *data = conn->data;
1433 char *buf = data->state.buffer; /* this is our buffer */
1434 int ftpcode; /* receive FTP response codes in this */
1436 struct Curl_dns_entry *addr=NULL;
1437 Curl_addrinfo *conninfo;
1441 Here's the excecutive summary on what to do:
1443 PASV is RFC959, expect:
1444 227 Entering Passive Mode (a1,a2,a3,a4,p1,p2)
1446 LPSV is RFC1639, expect:
1447 228 Entering Long Passive Mode (4,4,a1,a2,a3,a4,2,p1,p2)
1449 EPSV is RFC2428, expect:
1450 229 Entering Extended Passive Mode (|||port|)
1454 const char *mode[] = { "EPSV", "PASV", NULL };
1455 int results[] = { 229, 227, 0 };
1457 unsigned short connectport; /* the local port connect() should use! */
1458 unsigned short newport=0; /* remote port, not necessary the local one */
1460 /* newhost must be able to hold a full IP-style address in ASCII, which
1461 in the IPv6 case means 5*8-1 = 39 letters */
1463 char *newhostp=NULL;
1465 for (modeoff = (data->set.ftp_use_epsv?0:1);
1466 mode[modeoff]; modeoff++) {
1467 result = Curl_ftpsendf(conn, "%s", mode[modeoff]);
1470 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
1473 if (ftpcode == results[modeoff])
1477 if (!mode[modeoff]) {
1478 failf(data, "Odd return code after PASV");
1479 return CURLE_FTP_WEIRD_PASV_REPLY;
1481 else if (227 == results[modeoff]) {
1487 * New 227-parser June 3rd 1999.
1488 * It now scans for a sequence of six comma-separated numbers and
1489 * will take them as IP+port indicators.
1491 * Found reply-strings include:
1492 * "227 Entering Passive Mode (127,0,0,1,4,51)"
1493 * "227 Data transfer will passively listen to 127,0,0,1,4,51"
1494 * "227 Entering passive mode. 127,0,0,1,4,51"
1498 if (6 == sscanf(str, "%d,%d,%d,%d,%d,%d",
1499 &ip[0], &ip[1], &ip[2], &ip[3],
1500 &port[0], &port[1]))
1506 failf(data, "Couldn't interpret this 227-reply: %s", buf);
1507 return CURLE_FTP_WEIRD_227_FORMAT;
1510 snprintf(newhost, sizeof(newhost),
1511 "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
1513 newport = (port[0]<<8) + port[1];
1515 else if (229 == results[modeoff]) {
1516 char *ptr = strchr(buf, '(');
1521 if(5 == sscanf(ptr, "%c%c%c%u%c",
1527 char sep1 = separator[0];
1530 /* The four separators should be identical, or else this is an oddly
1531 formatted reply and we bail out immediately. */
1532 for(i=1; i<4; i++) {
1533 if(separator[i] != sep1) {
1534 ptr=NULL; /* set to NULL to signal error */
1541 /* we should use the same host we already are connected to */
1542 newhostp = conn->host.name;
1549 failf(data, "Weirdly formatted EPSV reply");
1550 return CURLE_FTP_WEIRD_PASV_REPLY;
1554 return CURLE_FTP_CANT_RECONNECT;
1556 if(data->change.proxy && *data->change.proxy) {
1558 * This is a tunnel through a http proxy and we need to connect to the
1561 * We don't want to rely on a former host lookup that might've expired
1562 * now, instead we remake the lookup here and now!
1564 rc = Curl_resolv(conn, conn->proxy.name, (int)conn->port, &addr);
1565 if(rc == CURLRESOLV_PENDING)
1566 rc = Curl_wait_for_resolv(conn, &addr);
1569 (unsigned short)conn->port; /* we connect to the proxy's port */
1573 /* normal, direct, ftp connection */
1574 rc = Curl_resolv(conn, newhostp, newport, &addr);
1575 if(rc == CURLRESOLV_PENDING)
1576 rc = Curl_wait_for_resolv(conn, &addr);
1579 failf(data, "Can't resolve new host %s:%d", newhostp, newport);
1580 return CURLE_FTP_CANT_GET_HOST;
1582 connectport = newport; /* we connect to the remote port */
1585 result = Curl_connecthost(conn,
1587 &conn->sock[SECONDARYSOCKET],
1591 Curl_resolv_unlock(data, addr); /* we're done using this address */
1597 * When this is used from the multi interface, this might've returned with
1598 * the 'connected' set to FALSE and thus we are now awaiting a non-blocking
1599 * connect to connect and we should not be "hanging" here waiting.
1602 if(data->set.verbose)
1603 /* this just dumps information about this second connection */
1604 ftp_pasv_verbose(conn, conninfo, newhostp, connectport);
1606 #ifndef CURL_DISABLE_HTTP
1607 if(conn->bits.tunnel_proxy) {
1608 /* We want "seamless" FTP operations through HTTP proxy tunnel */
1609 result = Curl_ConnectHTTPProxyTunnel(conn, SECONDARYSOCKET,
1611 if(CURLE_OK != result)
1614 #endif /* CURL_DISABLE_HTTP */
1620 * Curl_ftp_nextconnect()
1622 * This function shall be called when the second FTP connection has been
1623 * established and is confirmed connected.
1626 CURLcode Curl_ftp_nextconnect(struct connectdata *conn)
1628 struct SessionHandle *data=conn->data;
1629 char *buf = data->state.buffer; /* this is our buffer */
1632 int ftpcode; /* for ftp status */
1634 /* the ftp struct is already inited in Curl_ftp_connect() */
1635 struct FTP *ftp = conn->proto.ftp;
1636 curl_off_t *bytecountp = ftp->bytecountp;
1638 if(data->set.upload) {
1640 /* Set type to binary (unless specified ASCII) */
1641 result = ftp_transfertype(conn, data->set.ftp_ascii);
1645 /* Send any PREQUOTE strings after transfer type is set? (Wesley Laxton)*/
1646 if(data->set.prequote) {
1647 if ((result = ftp_sendquote(conn, data->set.prequote)) != CURLE_OK)
1651 if(conn->resume_from) {
1652 /* we're about to continue the uploading of a file */
1653 /* 1. get already existing file's size. We use the SIZE
1654 command for this which may not exist in the server!
1655 The SIZE command is not in RFC959. */
1657 /* 2. This used to set REST. But since we can do append, we
1658 don't another ftp command. We just skip the source file
1659 offset and then we APPEND the rest on the file instead */
1661 /* 3. pass file-size number of bytes in the source file */
1662 /* 4. lower the infilesize counter */
1663 /* => transfer as usual */
1665 if(conn->resume_from < 0 ) {
1666 /* we could've got a specified offset from the command line,
1667 but now we know we didn't */
1668 curl_off_t gottensize;
1670 if(CURLE_OK != ftp_getsize(conn, ftp->file, &gottensize)) {
1671 failf(data, "Couldn't get remote file size");
1672 return CURLE_FTP_COULDNT_GET_SIZE;
1674 conn->resume_from = gottensize;
1677 if(conn->resume_from) {
1678 /* do we still game? */
1679 curl_off_t passed=0;
1680 /* enable append instead */
1681 data->set.ftp_append = 1;
1683 /* Now, let's read off the proper amount of bytes from the
1684 input. If we knew it was a proper file we could've just
1685 fseek()ed but we only have a stream here */
1687 curl_off_t readthisamountnow = (conn->resume_from - passed);
1688 curl_off_t actuallyread;
1690 if(readthisamountnow > BUFSIZE)
1691 readthisamountnow = BUFSIZE;
1694 conn->fread(data->state.buffer, 1, (size_t)readthisamountnow,
1697 passed += actuallyread;
1698 if(actuallyread != readthisamountnow) {
1699 failf(data, "Could only read %" FORMAT_OFF_T
1700 " bytes from the input", passed);
1701 return CURLE_FTP_COULDNT_USE_REST;
1704 while(passed != conn->resume_from);
1706 /* now, decrease the size of the read */
1707 if(data->set.infilesize>0) {
1708 data->set.infilesize -= conn->resume_from;
1710 if(data->set.infilesize <= 0) {
1711 infof(data, "File already completely uploaded\n");
1713 /* no data to transfer */
1714 result=Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1716 /* Set no_transfer so that we won't get any error in
1717 * Curl_ftp_done() because we didn't transfer anything! */
1718 ftp->no_transfer = TRUE;
1723 /* we've passed, proceed as normal */
1727 /* Send everything on data->state.in to the socket */
1728 if(data->set.ftp_append) {
1729 /* we append onto the file instead of rewriting it */
1730 FTPSENDF(conn, "APPE %s", ftp->file);
1733 FTPSENDF(conn, "STOR %s", ftp->file);
1736 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
1741 failf(data, "Failed FTP upload:%s", buf+3);
1742 /* oops, we never close the sockets! */
1743 return CURLE_FTP_COULDNT_STOR_FILE;
1746 if(data->set.ftp_use_port) {
1747 /* PORT means we are now awaiting the server to connect to us. */
1748 result = AllowServerConnect(conn);
1753 if(conn->ssl[SECONDARYSOCKET].use) {
1754 /* since we only have a plaintext TCP connection here, we must now
1756 infof(data, "Doing the SSL/TLS handshake on the data stream\n");
1757 result = Curl_SSLConnect(conn, SECONDARYSOCKET);
1764 /* When we know we're uploading a specified file, we can get the file
1765 size prior to the actual upload. */
1767 Curl_pgrsSetUploadSize(data, data->set.infilesize);
1769 result = Curl_Transfer(conn, -1, -1, FALSE, NULL, /* no download */
1770 SECONDARYSOCKET, bytecountp);
1775 else if(!conn->bits.no_body) {
1776 /* Retrieve file or directory */
1778 curl_off_t downloadsize=-1;
1780 if(conn->bits.use_range && conn->range) {
1781 curl_off_t from, to;
1782 curl_off_t totalsize=-1;
1786 from=curlx_strtoofft(conn->range, &ptr, 0);
1787 while(ptr && *ptr && (isspace((int)*ptr) || (*ptr=='-')))
1789 to=curlx_strtoofft(ptr, &ptr2, 0);
1791 /* we didn't get any digit */
1794 if((-1 == to) && (from>=0)) {
1796 conn->resume_from = from;
1797 infof(data, "FTP RANGE %" FORMAT_OFF_T " to end of file\n", from);
1802 conn->maxdownload = -from;
1803 conn->resume_from = from;
1804 infof(data, "FTP RANGE the last %" FORMAT_OFF_T " bytes\n", totalsize);
1808 totalsize = to-from;
1809 conn->maxdownload = totalsize+1; /* include the last mentioned byte */
1810 conn->resume_from = from;
1811 infof(data, "FTP RANGE from %" FORMAT_OFF_T
1812 " getting %" FORMAT_OFF_T " bytes\n", from, conn->maxdownload);
1814 infof(data, "range-download from %" FORMAT_OFF_T
1815 " to %" FORMAT_OFF_T ", totally %" FORMAT_OFF_T " bytes\n",
1816 from, to, conn->maxdownload);
1817 ftp->dont_check = TRUE; /* dont check for successful transfer */
1820 if((data->set.ftp_list_only) || !ftp->file) {
1821 /* The specified path ends with a slash, and therefore we think this
1822 is a directory that is requested, use LIST. But before that we
1823 need to set ASCII transfer mode. */
1826 /* Set type to ASCII */
1827 result = ftp_transfertype(conn, TRUE /* ASCII enforced */);
1831 /* if this output is to be machine-parsed, the NLST command will be
1832 better used since the LIST command output is not specified or
1833 standard in any way */
1835 FTPSENDF(conn, "%s",
1836 data->set.customrequest?data->set.customrequest:
1837 (data->set.ftp_list_only?"NLST":"LIST"));
1840 curl_off_t foundsize;
1842 /* Set type to binary (unless specified ASCII) */
1843 result = ftp_transfertype(conn, data->set.ftp_ascii);
1847 /* Send any PREQUOTE strings after transfer type is set? */
1848 if(data->set.prequote) {
1849 if ((result = ftp_sendquote(conn, data->set.prequote)) != CURLE_OK)
1853 /* Attempt to get the size, it'll be useful in some cases: for resumed
1854 downloads and when talking to servers that don't give away the size
1855 in the RETR response line. */
1856 result = ftp_getsize(conn, ftp->file, &foundsize);
1857 if(CURLE_OK == result) {
1858 if (data->set.max_filesize && foundsize > data->set.max_filesize) {
1859 failf(data, "Maximum file size exceeded");
1860 return CURLE_FILESIZE_EXCEEDED;
1862 downloadsize = foundsize;
1865 if(conn->resume_from) {
1867 /* Daniel: (August 4, 1999)
1869 * We start with trying to use the SIZE command to figure out the size
1870 * of the file we're gonna get. If we can get the size, this is by far
1871 * the best way to know if we're trying to resume beyond the EOF.
1873 * Daniel, November 28, 2001. We *always* get the size on downloads
1874 * now, so it is done before this even when not doing resumes. I saved
1875 * the comment above for nostalgical reasons! ;-)
1877 if(CURLE_OK != result) {
1878 infof(data, "ftp server doesn't support SIZE\n");
1879 /* We couldn't get the size and therefore we can't know if there
1880 really is a part of the file left to get, although the server
1881 will just close the connection when we start the connection so it
1882 won't cause us any harm, just not make us exit as nicely. */
1885 /* We got a file size report, so we check that there actually is a
1886 part of the file left to get, or else we go home. */
1887 if(conn->resume_from< 0) {
1888 /* We're supposed to download the last abs(from) bytes */
1889 if(foundsize < -conn->resume_from) {
1890 failf(data, "Offset (%" FORMAT_OFF_T
1891 ") was beyond file size (%" FORMAT_OFF_T ")",
1892 conn->resume_from, foundsize);
1893 return CURLE_FTP_BAD_DOWNLOAD_RESUME;
1895 /* convert to size to download */
1896 downloadsize = -conn->resume_from;
1897 /* download from where? */
1898 conn->resume_from = foundsize - downloadsize;
1901 if(foundsize < conn->resume_from) {
1902 failf(data, "Offset (%" FORMAT_OFF_T
1903 ") was beyond file size (%" FORMAT_OFF_T ")",
1904 conn->resume_from, foundsize);
1905 return CURLE_FTP_BAD_DOWNLOAD_RESUME;
1907 /* Now store the number of bytes we are expected to download */
1908 downloadsize = foundsize-conn->resume_from;
1912 if (downloadsize == 0) {
1913 /* no data to transfer */
1914 result=Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1915 infof(data, "File already completely downloaded\n");
1917 /* Set no_transfer so that we won't get any error in Curl_ftp_done()
1918 * because we didn't transfer the any file */
1919 ftp->no_transfer = TRUE;
1923 /* Set resume file transfer offset */
1924 infof(data, "Instructs server to resume from offset %" FORMAT_OFF_T
1928 FTPSENDF(conn, "REST %" FORMAT_OFF_T, conn->resume_from);
1930 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
1934 if(ftpcode != 350) {
1935 failf(data, "Couldn't use REST: %s", buf+4);
1936 return CURLE_FTP_COULDNT_USE_REST;
1940 FTPSENDF(conn, "RETR %s", ftp->file);
1943 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
1947 if((ftpcode == 150) || (ftpcode == 125)) {
1951 150 Opening BINARY mode data connection for /etc/passwd (2241
1952 bytes). (ok, the file is being transfered)
1955 150 Opening ASCII mode data connection for /bin/ls
1958 150 ASCII data connection for /bin/ls (137.167.104.91,37445) (0 bytes).
1961 150 Opening ASCII mode data connection for /linux/fisk/kpanelrc (0.0.0.0,0) (545 bytes).
1964 125 Data connection already open; Transfer starting. */
1966 curl_off_t size=-1; /* default unknown size */
1970 * It appears that there are FTP-servers that return size 0 for files
1971 * when SIZE is used on the file while being in BINARY mode. To work
1972 * around that (stupid) behavior, we attempt to parse the RETR response
1973 * even if the SIZE returned size zero.
1975 * Debugging help from Salvatore Sorrentino on February 26, 2003.
1979 !data->set.ftp_ascii &&
1980 (downloadsize < 1)) {
1982 * It seems directory listings either don't show the size or very
1983 * often uses size 0 anyway. ASCII transfers may very well turn out
1984 * that the transfered amount of data is not the same as this line
1985 * tells, why using this number in those cases only confuses us.
1987 * Example D above makes this parsing a little tricky */
1989 bytes=strstr(buf, " bytes");
1992 /* this is a hint there is size information in there! ;-) */
1994 /* scan for the parenthesis and break there */
1997 /* if only skip digits, or else we're in deep trouble */
1998 if(!isdigit((int)*bytes)) {
2002 /* one more estep backwards */
2005 /* only if we have nothing but digits: */
2007 /* get the number! */
2008 size = curlx_strtoofft(bytes, NULL, 0);
2013 else if(downloadsize > -1)
2014 size = downloadsize;
2016 if(data->set.ftp_use_port) {
2017 result = AllowServerConnect(conn);
2022 if(conn->ssl[SECONDARYSOCKET].use) {
2023 /* since we only have a plaintext TCP connection here, we must now
2025 infof(data, "Doing the SSL/TLS handshake on the data stream\n");
2026 result = Curl_SSLConnect(conn, SECONDARYSOCKET);
2031 if(size > conn->maxdownload && conn->maxdownload > 0)
2032 size = conn->size = conn->maxdownload;
2034 infof(data, "Getting file with size: %" FORMAT_OFF_T "\n", size);
2037 result=Curl_Transfer(conn, SECONDARYSOCKET, size, FALSE,
2039 -1, NULL); /* no upload here */
2044 if(dirlist && (ftpcode == 450)) {
2045 /* simply no matching files */
2046 ftp->no_transfer = TRUE; /* don't think we should download anything */
2049 failf(data, "%s", buf+4);
2050 return CURLE_FTP_COULDNT_RETR_FILE;
2055 /* end of transfer */
2060 /***********************************************************************
2064 * This is the actual DO function for FTP. Get a file/directory according to
2065 * the options previously setup.
2069 CURLcode ftp_perform(struct connectdata *conn,
2070 bool *connected) /* for the TCP connect status after
2073 /* this is FTP and no proxy */
2074 CURLcode result=CURLE_OK;
2075 struct SessionHandle *data=conn->data;
2076 char *buf = data->state.buffer; /* this is our buffer */
2078 /* the ftp struct is already inited in Curl_ftp_connect() */
2079 struct FTP *ftp = conn->proto.ftp;
2081 /* Send any QUOTE strings? */
2082 if(data->set.quote) {
2083 if ((result = ftp_sendquote(conn, data->set.quote)) != CURLE_OK)
2087 /* This is a re-used connection. Since we change directory to where the
2088 transfer is taking place, we must now get back to the original dir
2089 where we ended up after login: */
2090 if (conn->bits.reuse && ftp->entrypath) {
2091 if ((result = ftp_cwd_and_mkd(conn, ftp->entrypath)) != CURLE_OK)
2096 int i; /* counter for loop */
2097 for (i=0; i < ftp->dirdepth; i++) {
2098 /* RFC 1738 says empty components should be respected too, but
2099 that is plain stupid since CWD can't be used with an empty argument */
2100 if ((result = ftp_cwd_and_mkd(conn, ftp->dirs[i])) != CURLE_OK)
2105 /* Requested time of file or time-depended transfer? */
2106 if((data->set.get_filetime || data->set.timecondition) &&
2108 result = ftp_getfiletime(conn, ftp->file);
2111 case CURLE_FTP_COULDNT_RETR_FILE:
2113 if(data->set.timecondition) {
2114 if((data->info.filetime > 0) && (data->set.timevalue > 0)) {
2115 switch(data->set.timecondition) {
2116 case CURL_TIMECOND_IFMODSINCE:
2118 if(data->info.filetime < data->set.timevalue) {
2119 infof(data, "The requested document is not new enough\n");
2120 ftp->no_transfer = TRUE; /* mark this to not transfer data */
2124 case CURL_TIMECOND_IFUNMODSINCE:
2125 if(data->info.filetime > data->set.timevalue) {
2126 infof(data, "The requested document is not old enough\n");
2127 ftp->no_transfer = TRUE; /* mark this to not transfer data */
2134 infof(data, "Skipping time comparison\n");
2143 /* If we have selected NOBODY and HEADER, it means that we only want file
2144 information. Which in FTP can't be much more than the file size and
2146 if(conn->bits.no_body && data->set.include_header && ftp->file) {
2147 /* The SIZE command is _not_ RFC 959 specified, and therefor many servers
2148 may not support it! It is however the only way we have to get a file's
2150 curl_off_t filesize;
2154 ftp->no_transfer = TRUE; /* this means no actual transfer is made */
2156 /* Some servers return different sizes for different modes, and thus we
2157 must set the proper type before we check the size */
2158 result = ftp_transfertype(conn, data->set.ftp_ascii);
2162 /* failing to get size is not a serious error */
2163 result = ftp_getsize(conn, ftp->file, &filesize);
2165 if(CURLE_OK == result) {
2166 snprintf(buf, sizeof(data->state.buffer),
2167 "Content-Length: %" FORMAT_OFF_T "\r\n", filesize);
2168 result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0);
2173 /* Determine if server can respond to REST command and therefore
2174 whether it can do a range */
2175 FTPSENDF(conn, "REST 0", NULL);
2176 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
2178 if ((CURLE_OK == result) && (ftpcode == 350)) {
2179 result = Curl_client_write(data, CLIENTWRITE_BOTH,
2180 (char *)"Accept-ranges: bytes\r\n", 0);
2185 /* If we asked for a time of the file and we actually got one as
2186 well, we "emulate" a HTTP-style header in our output. */
2188 #ifdef HAVE_STRFTIME
2189 if(data->set.get_filetime && (data->info.filetime>=0) ) {
2191 time_t clock = (time_t)data->info.filetime;
2192 #ifdef HAVE_GMTIME_R
2194 tm = (struct tm *)gmtime_r(&clock, &buffer);
2196 tm = gmtime(&clock);
2198 /* format: "Tue, 15 Nov 1994 12:45:26" */
2199 strftime(buf, BUFSIZE-1, "Last-Modified: %a, %d %b %Y %H:%M:%S GMT\r\n",
2201 result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0);
2210 if(conn->bits.no_body)
2211 /* doesn't really transfer any data */
2212 ftp->no_transfer = TRUE;
2213 /* Get us a second connection up and connected */
2214 else if(data->set.ftp_use_port) {
2215 /* We have chosen to use the PORT command */
2216 result = ftp_use_port(conn);
2217 if(CURLE_OK == result) {
2218 /* we have the data connection ready */
2219 infof(data, "Ordered connect of the data stream with PORT!\n");
2220 *connected = TRUE; /* mark us "still connected" */
2224 /* We have chosen (this is default) to use the PASV command */
2225 result = ftp_use_pasv(conn, connected);
2226 if(CURLE_OK == result && *connected)
2227 infof(data, "Connected the data stream with PASV!\n");
2233 /***********************************************************************
2237 * This function is registered as 'curl_do' function. It decodes the path
2238 * parts etc as a wrapper to the actual DO function (ftp_perform).
2240 * The input argument is already checked for validity.
2242 CURLcode Curl_ftp(struct connectdata *conn)
2244 CURLcode retcode = CURLE_OK;
2246 if (conn->sec_conn) /* 3rd party transfer */
2247 retcode = ftp_3rdparty(conn);
2249 retcode = ftp_regular_transfer(conn);
2254 /***********************************************************************
2258 * Sends the formated string as a ftp command to a ftp server
2260 * NOTE: we build the command in a fixed-length buffer, which sets length
2261 * restrictions on the command!
2263 CURLcode Curl_ftpsendf(struct connectdata *conn,
2264 const char *fmt, ...)
2266 ssize_t bytes_written;
2270 CURLcode res = CURLE_OK;
2274 vsnprintf(s, 250, fmt, ap);
2277 strcat(s, "\r\n"); /* append a trailing CRLF */
2280 write_len = strlen(s);
2283 res = Curl_write(conn, conn->sock[FIRSTSOCKET], sptr, write_len,
2289 if(conn->data->set.verbose)
2290 Curl_debug(conn->data, CURLINFO_HEADER_OUT, sptr, bytes_written, conn->host.dispname);
2292 if(bytes_written != (ssize_t)write_len) {
2293 write_len -= bytes_written;
2294 sptr += bytes_written;
2303 /***********************************************************************
2307 * This should be called before calling sclose() on an ftp control connection
2308 * (not data connections). We should then wait for the response from the
2309 * server before returning. The calling code should then try to close the
2313 static CURLcode ftp_quit(struct connectdata *conn)
2317 CURLcode ret = CURLE_OK;
2319 if(conn->proto.ftp->ctl_valid) {
2320 ret = Curl_ftpsendf(conn, "%s", "QUIT");
2322 ret = Curl_GetFTPResponse(&nread, conn, &ftpcode);
2328 /***********************************************************************
2330 * Curl_ftp_disconnect()
2332 * Disconnect from an FTP server. Cleanup protocol-specific per-connection
2335 CURLcode Curl_ftp_disconnect(struct connectdata *conn)
2337 struct FTP *ftp= conn->proto.ftp;
2339 /* We cannot send quit unconditionally. If this connection is stale or
2340 bad in any way, sending quit and waiting around here will make the
2341 disconnect wait in vain and cause more problems than we need to.
2343 ftp_quit() will check the state of ftp->ctl_valid. If it's ok it
2344 will try to send the QUIT command, otherwise it will just return.
2347 /* The FTP session may or may not have been allocated/setup at this point! */
2349 (void)ftp_quit(conn); /* ignore errors on the QUIT */
2352 free(ftp->entrypath);
2362 /***********************************************************************
2366 * Makes a directory on the FTP server.
2370 static CURLcode ftp_mkd(struct connectdata *conn, char *path)
2372 CURLcode result=CURLE_OK;
2373 int ftpcode; /* for ftp status */
2376 /* Create a directory on the remote server */
2377 FTPSENDF(conn, "MKD %s", path);
2379 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
2386 infof( conn->data , "Created remote directory %s\n" , path );
2389 failf(conn->data, "Permission denied to make directory %s", path);
2390 result = CURLE_FTP_ACCESS_DENIED;
2393 failf(conn->data, "unrecognized MKD response: %d", ftpcode );
2394 result = CURLE_FTP_ACCESS_DENIED;
2400 /***********************************************************************
2404 * Send 'CWD' to the remote server to Change Working Directory. It is the ftp
2405 * version of the unix 'cd' command. This function is only called from the
2406 * ftp_cwd_and_mkd() function these days.
2408 * This function does NOT call failf().
2411 CURLcode ftp_cwd(struct connectdata *conn, char *path)
2417 FTPSENDF(conn, "CWD %s", path);
2418 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
2420 /* According to RFC959, CWD is supposed to return 250 on success, but
2421 there seem to be non-compliant FTP servers out there that return 200,
2422 so we accept any '2xy' code here. */
2423 if (ftpcode/100 != 2)
2424 result = CURLE_FTP_ACCESS_DENIED;
2430 /***********************************************************************
2434 * Change to the given directory. If the directory is not present, and we
2435 * have been told to allow it, then create the directory and cd to it.
2438 static CURLcode ftp_cwd_and_mkd(struct connectdata *conn, char *path)
2442 result = ftp_cwd(conn, path);
2444 if(conn->data->set.ftp_create_missing_dirs) {
2445 result = ftp_mkd(conn, path);
2447 /* ftp_mkd() calls failf() itself */
2449 result = ftp_cwd(conn, path);
2452 failf(conn->data, "Couldn't cd to %s", path);
2459 /***********************************************************************
2461 * ftp_3rdparty_pretransfer()
2463 * Preparation for 3rd party transfer.
2466 static CURLcode ftp_3rdparty_pretransfer(struct connectdata *conn)
2468 CURLcode result = CURLE_OK;
2469 struct SessionHandle *data = conn->data;
2470 struct connectdata *sec_conn = conn->sec_conn;
2472 /* sets transfer type */
2473 result = ftp_transfertype(conn, data->set.ftp_ascii);
2477 result = ftp_transfertype(sec_conn, data->set.ftp_ascii);
2481 /* Send any PREQUOTE strings after transfer type is set? */
2482 if (data->set.source_prequote) {
2483 /* sends command(s) to source server before file transfer */
2484 result = ftp_sendquote(sec_conn, data->set.source_prequote);
2486 if (!result && data->set.prequote)
2487 result = ftp_sendquote(conn, data->set.prequote);
2494 /***********************************************************************
2496 * ftp_3rdparty_transfer()
2498 * Performs 3rd party transfer.
2501 static CURLcode ftp_3rdparty_transfer(struct connectdata *conn)
2503 CURLcode result = CURLE_OK;
2505 int ftpcode, ip[4], port[2];
2506 struct SessionHandle *data = conn->data;
2507 struct connectdata *sec_conn = conn->sec_conn;
2508 char *buf = data->state.buffer; /* this is our buffer */
2511 const char *stor_cmd;
2512 struct connectdata *pasv_conn;
2513 struct connectdata *port_conn;
2515 if (data->set.pasvHost == CURL_TARGET_PASV) {
2517 port_conn = sec_conn;
2520 pasv_conn = sec_conn;
2524 /* sets the passive mode */
2525 FTPSENDF(pasv_conn, "%s", "PASV");
2526 result = Curl_GetFTPResponse(&nread, pasv_conn, &ftpcode);
2527 if (result) return result;
2528 if (ftpcode != 227) {
2529 failf(data, "Odd return code after PASV:%s", buf + 3);
2530 return CURLE_FTP_WEIRD_PASV_REPLY;
2534 if (6 == sscanf(str, "%d,%d,%d,%d,%d,%d",
2535 &ip[0], &ip[1], &ip[2], &ip[3], &port[0], &port[1]))
2541 failf(pasv_conn->data, "Couldn't interpret this 227-reply: %s", buf);
2542 return CURLE_FTP_WEIRD_227_FORMAT;
2545 snprintf(pasv_port, sizeof(pasv_port), "%d,%d,%d,%d,%d,%d", ip[0], ip[1],
2546 ip[2], ip[3], port[0], port[1]);
2548 /* sets data connection between remote hosts */
2549 FTPSENDF(port_conn, "PORT %s", pasv_port);
2550 result = Curl_GetFTPResponse(&nread, port_conn, &ftpcode);
2554 if (ftpcode != 200) {
2555 failf(data, "PORT command attempts failed:%s", buf + 3);
2556 return CURLE_FTP_PORT_FAILED;
2559 /* we might append onto the file instead of overwriting it */
2560 stor_cmd = data->set.ftp_append?"APPE":"STOR";
2562 /* transfers file between remote hosts */
2563 FTPSENDF(sec_conn, "RETR %s", data->set.source_path);
2565 if(data->set.pasvHost == CURL_TARGET_PASV) {
2567 result = Curl_GetFTPResponse(&nread, sec_conn, &ftpcode);
2571 if (ftpcode != 150) {
2572 failf(data, "Failed RETR: %s", buf + 4);
2573 return CURLE_FTP_COULDNT_RETR_FILE;
2576 result = Curl_ftpsendf(conn, "%s %s", stor_cmd, conn->path);
2577 if(CURLE_OK == result)
2578 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
2582 if (ftpcode != 150) {
2583 failf(data, "Failed FTP upload: %s", buf + 4);
2584 return CURLE_FTP_COULDNT_STOR_FILE;
2590 result = Curl_ftpsendf(conn, "%s %s", stor_cmd, conn->path);
2591 if(CURLE_OK == result)
2592 result = Curl_GetFTPResponse(&nread, sec_conn, &ftpcode);
2596 if (ftpcode != 150) {
2597 failf(data, "Failed FTP upload: %s", buf + 4);
2598 return CURLE_FTP_COULDNT_STOR_FILE;
2601 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
2605 if (ftpcode != 150) {
2606 failf(data, "Failed FTP upload: %s", buf + 4);
2607 return CURLE_FTP_COULDNT_STOR_FILE;
2616 /***********************************************************************
2618 * ftp_regular_transfer()
2620 * The input argument is already checked for validity.
2621 * Performs a regular transfer between local and remote hosts.
2623 * ftp->ctl_valid starts out as FALSE, and gets set to TRUE if we reach the
2624 * Curl_ftp_done() function without finding any major problem.
2627 CURLcode ftp_regular_transfer(struct connectdata *conn)
2629 CURLcode retcode=CURLE_OK;
2631 struct SessionHandle *data = conn->data;
2634 char *slash_pos; /* position of the first '/' char in curpos */
2635 char *cur_pos=conn->path; /* current position in ppath. point at the begin
2636 of next path component */
2638 /* the ftp struct is already inited in ftp_connect() */
2639 ftp = conn->proto.ftp;
2640 ftp->ctl_valid = FALSE;
2641 conn->size = -1; /* make sure this is unknown at this point */
2643 Curl_pgrsSetUploadCounter(data, 0);
2644 Curl_pgrsSetDownloadCounter(data, 0);
2645 Curl_pgrsSetUploadSize(data, 0);
2646 Curl_pgrsSetDownloadSize(data, 0);
2649 ftp->diralloc = 5; /* default dir depth to allocate */
2650 ftp->dirs = (char **)malloc(ftp->diralloc * sizeof(ftp->dirs[0]));
2652 return CURLE_OUT_OF_MEMORY;
2653 ftp->dirs[0] = NULL; /* to start with */
2655 /* parse the URL path into separate path components */
2656 while((slash_pos=strchr(cur_pos, '/'))) {
2657 /* 1 or 0 to indicate absolute directory */
2658 bool absolute_dir = (cur_pos - conn->path > 0) && (ftp->dirdepth == 0);
2660 /* seek out the next path component */
2661 if (slash_pos-cur_pos) {
2662 /* we skip empty path components, like "x//y" since the FTP command CWD
2663 requires a parameter and a non-existant parameter a) doesn't work on
2664 many servers and b) has no effect on the others. */
2665 int len = (int)(slash_pos - cur_pos + absolute_dir);
2666 ftp->dirs[ftp->dirdepth] = curl_unescape(cur_pos - absolute_dir, len);
2668 if (!ftp->dirs[ftp->dirdepth]) { /* run out of memory ... */
2669 failf(data, "no memory");
2671 return CURLE_OUT_OF_MEMORY;
2675 cur_pos = slash_pos + 1; /* jump to the rest of the string */
2680 cur_pos = slash_pos + 1; /* jump to the rest of the string */
2681 if(++ftp->dirdepth >= ftp->diralloc) {
2684 ftp->diralloc *= 2; /* double the size each time */
2685 bigger = realloc(ftp->dirs, ftp->diralloc * sizeof(ftp->dirs[0]));
2688 return CURLE_OUT_OF_MEMORY;
2690 ftp->dirs = (char **)bigger;
2695 ftp->file = cur_pos; /* the rest is the file name */
2698 ftp->file = curl_unescape(ftp->file, 0);
2699 if(NULL == ftp->file) {
2701 failf(data, "no memory");
2702 return CURLE_OUT_OF_MEMORY;
2706 ftp->file=NULL; /* instead of point to a zero byte, we make it a NULL
2709 retcode = ftp_perform(conn, &connected);
2711 if(CURLE_OK == retcode) {
2713 retcode = Curl_ftp_nextconnect(conn);
2715 if(retcode && (conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD)) {
2716 /* Failure detected, close the second socket if it was created already */
2717 sclose(conn->sock[SECONDARYSOCKET]);
2718 conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
2721 if(ftp->no_transfer)
2722 /* no data to transfer */
2723 retcode=Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
2725 /* since we didn't connect now, we want do_more to get called */
2726 conn->bits.do_more = TRUE;
2731 ftp->ctl_valid = TRUE; /* seems good */
2738 /***********************************************************************
2742 * The input argument is already checked for validity.
2743 * Performs a 3rd party transfer between two remote hosts.
2745 static CURLcode ftp_3rdparty(struct connectdata *conn)
2747 CURLcode retcode = CURLE_OK;
2749 conn->proto.ftp->ctl_valid = conn->sec_conn->proto.ftp->ctl_valid = TRUE;
2750 conn->size = conn->sec_conn->size = -1;
2752 retcode = ftp_3rdparty_pretransfer(conn);
2754 retcode = ftp_3rdparty_transfer(conn);
2759 #endif /* CURL_DISABLE_FTP */