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 */
547 switch(data->set.ftpsslauth) {
548 case CURLFTPAUTH_DEFAULT:
549 case CURLFTPAUTH_SSL:
553 case CURLFTPAUTH_TLS:
558 failf(data, "unsupported parameter to CURLOPT_FTPSSLAUTH: %d\n",
559 data->set.ftpsslauth);
560 return CURLE_FAILED_INIT; /* we don't know what to do */
563 for (try = start; ftpauth[count]; try=trynext, count++) {
565 FTPSENDF(conn, "AUTH %s", ftpauth[try]);
567 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
572 /* RFC2228 (page 5) says:
574 * If the server is willing to accept the named security mechanism, and
575 * does not require any security data, it must respond with reply code
579 if((ftpcode == 234) || (ftpcode == 334)) {
580 result = Curl_SSLConnect(conn, FIRSTSOCKET);
583 conn->protocol |= PROT_FTPS;
584 conn->ssl[SECONDARYSOCKET].use = FALSE; /* clear-text data */
591 FTPSENDF(conn, "USER %s", ftp->user?ftp->user:"");
593 /* wait for feedback */
594 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
599 /* 530 User ... access denied
600 (the server denies to log the specified user) */
601 failf(data, "Access denied: %s", &buf[4]);
602 return CURLE_FTP_ACCESS_DENIED;
604 else if(ftpcode == 331) {
605 /* 331 Password required for ...
606 (the server requires to send the user's password too) */
607 FTPSENDF(conn, "PASS %s", ftp->passwd?ftp->passwd:"");
608 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
613 /* 530 Login incorrect.
614 (the username and/or the password are incorrect)
616 530 Sorry, the maximum number of allowed users are already connected
618 failf(data, "not logged in: %s", &buf[4]);
619 return CURLE_FTP_USER_PASSWORD_INCORRECT;
621 else if(ftpcode/100 == 2) {
622 /* 230 User ... logged in.
623 (user successfully logged in)
625 Apparently, proftpd with SSL returns 232 here at times. */
627 infof(data, "We have successfully logged in\n");
630 failf(data, "Odd return code after PASS");
631 return CURLE_FTP_WEIRD_PASS_REPLY;
634 else if(buf[0] == '2') {
635 /* 230 User ... logged in.
636 (the user logged in without password) */
637 infof(data, "We have successfully logged in\n");
638 if (conn->ssl[FIRSTSOCKET].use) {
640 /* We are logged in with Kerberos, now set the requested protection
643 if(conn->sec_complete)
644 Curl_sec_set_protection_level(conn);
646 /* We may need to issue a KAUTH here to have access to the files
647 * do it if user supplied a password
649 if(conn->passwd && *conn->passwd) {
650 result = Curl_krb_kauth(conn);
658 failf(data, "Odd return code after USER");
659 return CURLE_FTP_WEIRD_USER_REPLY;
662 if(conn->ssl[FIRSTSOCKET].use) {
663 /* PBSZ = PROTECTION BUFFER SIZE.
665 The 'draft-murray-auth-ftp-ssl' (draft 12, page 7) says:
667 Specifically, the PROT command MUST be preceded by a PBSZ command
668 and a PBSZ command MUST be preceded by a successful security data
669 exchange (the TLS negotiation in this case)
673 Thus the PBSZ command must still be issued, but must have a parameter
674 of '0' to indicate that no buffering is taking place and the data
675 connection should not be encapsulated.
677 FTPSENDF(conn, "PBSZ %d", 0);
678 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
682 /* For TLS, the data connection can have one of two security levels.
684 1)Clear (requested by 'PROT C')
686 2)Private (requested by 'PROT P')
688 if(!conn->ssl[SECONDARYSOCKET].use) {
689 FTPSENDF(conn, "PROT %c", 'P');
690 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
695 /* We have enabled SSL for the data connection! */
696 conn->ssl[SECONDARYSOCKET].use = TRUE;
698 /* FTP servers typically responds with 500 if they decide to reject
703 /* send PWD to discover our entry point */
704 FTPSENDF(conn, "PWD", NULL);
706 /* wait for feedback */
707 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
712 char *dir = (char *)malloc(nread+1);
714 char *ptr=&buf[4]; /* start on the first letter */
717 return CURLE_OUT_OF_MEMORY;
719 /* Reply format is like
720 257<space>"<directory-name>"<space><commentary> and the RFC959 says
722 The directory name can contain any character; embedded double-quotes
723 should be escaped by double-quotes (the "quote-doubling" convention).
726 /* it started good */
731 /* "quote-doubling" */
737 *store = '\0'; /* zero terminate */
738 break; /* get out of this loop */
746 ftp->entrypath =dir; /* remember this */
747 infof(data, "Entry path is '%s'\n", ftp->entrypath);
750 /* couldn't get the path */
752 infof(data, "Failed to figure out path\n");
757 /* We couldn't read the PWD response! */
763 /***********************************************************************
767 * The DONE function. This does what needs to be done after a single DO has
770 * Input argument is already checked for validity.
772 CURLcode Curl_ftp_done(struct connectdata *conn, CURLcode status)
774 struct SessionHandle *data = conn->data;
775 struct FTP *ftp = conn->proto.ftp;
778 CURLcode result=CURLE_OK;
780 bool was_ctl_valid = ftp->ctl_valid;
782 /* free the dir tree and file parts */
785 ftp->ctl_valid = FALSE;
787 if(data->set.upload) {
788 if((-1 != data->set.infilesize) &&
789 (data->set.infilesize != *ftp->bytecountp) &&
791 failf(data, "Uploaded unaligned file size (%" FORMAT_OFF_T
792 " out of %" FORMAT_OFF_T " bytes)",
793 *ftp->bytecountp, data->set.infilesize);
794 conn->bits.close = TRUE; /* close this connection since we don't
795 know what state this error leaves us in */
796 return CURLE_PARTIAL_FILE;
800 if((-1 != conn->size) && (conn->size != *ftp->bytecountp) &&
801 (conn->maxdownload != *ftp->bytecountp)) {
802 failf(data, "Received only partial file: %" FORMAT_OFF_T " bytes",
804 conn->bits.close = TRUE; /* close this connection since we don't
805 know what state this error leaves us in */
806 return CURLE_PARTIAL_FILE;
808 else if(!ftp->dont_check &&
811 /* We consider this an error, but there's no true FTP error received
812 why we need to continue to "read out" the server response too.
813 We don't want to leave a "waiting" server reply if we'll get told
814 to make a second request on this same connection! */
815 failf(data, "No data was received!");
816 result = CURLE_FTP_COULDNT_RETR_FILE;
821 case CURLE_BAD_DOWNLOAD_RESUME:
822 case CURLE_FTP_WEIRD_PASV_REPLY:
823 case CURLE_FTP_PORT_FAILED:
824 case CURLE_FTP_COULDNT_SET_BINARY:
825 case CURLE_FTP_COULDNT_RETR_FILE:
826 case CURLE_FTP_ACCESS_DENIED:
827 /* the connection stays alive fine even though this happened */
829 case CURLE_OK: /* doesn't affect the control connection's status */
830 ftp->ctl_valid = was_ctl_valid;
832 default: /* by default, an error means the control connection is
833 wedged and should not be used anymore */
834 ftp->ctl_valid = FALSE;
839 Curl_sec_fflush_fd(conn, conn->sock[SECONDARYSOCKET]);
841 /* shut down the socket to inform the server we're done */
842 sclose(conn->sock[SECONDARYSOCKET]);
843 conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
845 if(!ftp->no_transfer && !status) {
846 /* Let's see what the server says about the transfer we just performed,
847 * but lower the timeout as sometimes this connection has died while the
848 * data has been transfered. This happens when doing through NATs etc that
849 * abandon old silent connections.
851 ftp->response_time = 60; /* give it only a minute for now */
853 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
855 ftp->response_time = 3600; /* set this back to one hour waits */
857 if(!nread && (CURLE_OPERATION_TIMEDOUT == result)) {
858 failf(data, "control connection looks dead");
865 if(!ftp->dont_check) {
866 /* 226 Transfer complete, 250 Requested file action okay, completed. */
867 if((ftpcode != 226) && (ftpcode != 250)) {
868 failf(data, "server did not report OK, got %d", ftpcode);
869 return CURLE_FTP_WRITE_ERROR;
874 /* clear these for next connection */
875 ftp->no_transfer = FALSE;
876 ftp->dont_check = FALSE;
878 if (!result && conn->sec_conn) { /* 3rd party transfer */
879 /* "done" with the secondary connection */
880 result = Curl_ftp_done(conn->sec_conn, status);
883 /* Send any post-transfer QUOTE strings? */
884 if(!status && !result && data->set.postquote)
885 result = ftp_sendquote(conn, data->set.postquote);
890 /***********************************************************************
894 * Where a 'quote' means a list of custom commands to send to the server.
895 * The quote list is passed as an argument.
899 CURLcode ftp_sendquote(struct connectdata *conn, struct curl_slist *quote)
901 struct curl_slist *item;
909 FTPSENDF(conn, "%s", item->data);
911 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
915 if (ftpcode >= 400) {
916 failf(conn->data, "QUOT string not accepted: %s", item->data);
917 return CURLE_FTP_QUOTE_ERROR;
927 /***********************************************************************
931 * Get the timestamp of the given file.
934 CURLcode ftp_getfiletime(struct connectdata *conn, char *file)
936 CURLcode result=CURLE_OK;
937 int ftpcode; /* for ftp status */
939 char *buf = conn->data->state.buffer;
941 /* we have requested to get the modified-time of the file, this is yet
942 again a grey area as the MDTM is not kosher RFC959 */
943 FTPSENDF(conn, "MDTM %s", file);
945 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
952 /* we got a time. Format should be: "YYYYMMDDHHMMSS[.sss]" where the
953 last .sss part is optional and means fractions of a second */
954 int year, month, day, hour, minute, second;
955 if(6 == sscanf(buf+4, "%04d%02d%02d%02d%02d%02d",
956 &year, &month, &day, &hour, &minute, &second)) {
957 /* we have a time, reformat it */
958 time_t secs=time(NULL);
959 /* using the good old yacc/bison yuck */
960 snprintf(buf, sizeof(conn->data->state.buffer),
961 "%04d%02d%02d %02d:%02d:%02d GMT",
962 year, month, day, hour, minute, second);
963 /* now, convert this into a time() value: */
964 conn->data->info.filetime = curl_getdate(buf, &secs);
969 infof(conn->data, "unsupported MDTM reply format\n");
971 case 550: /* "No such file or directory" */
972 failf(conn->data, "Given file does not exist");
973 result = CURLE_FTP_COULDNT_RETR_FILE;
979 /***********************************************************************
983 * Set transfer type. We only deal with ASCII or BINARY so this function
986 static CURLcode ftp_transfertype(struct connectdata *conn,
989 struct SessionHandle *data = conn->data;
994 FTPSENDF(conn, "TYPE %s", ascii?"A":"I");
996 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
1000 if(ftpcode != 200) {
1001 failf(data, "Couldn't set %s mode",
1002 ascii?"ASCII":"binary");
1003 return ascii? CURLE_FTP_COULDNT_SET_ASCII:CURLE_FTP_COULDNT_SET_BINARY;
1009 /***********************************************************************
1013 * Returns the file size (in bytes) of the given remote file.
1017 CURLcode ftp_getsize(struct connectdata *conn, char *file,
1020 struct SessionHandle *data = conn->data;
1023 char *buf=data->state.buffer;
1026 FTPSENDF(conn, "SIZE %s", file);
1027 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
1031 if(ftpcode == 213) {
1032 /* get the size from the ascii string: */
1033 *size = curlx_strtoofft(buf+4, NULL, 0);
1036 return CURLE_FTP_COULDNT_GET_SIZE;
1041 /***************************************************************************
1043 * ftp_pasv_verbose()
1045 * This function only outputs some informationals about this second connection
1046 * when we've issued a PASV command before and thus we have connected to a
1047 * possibly new IP address.
1051 ftp_pasv_verbose(struct connectdata *conn,
1053 char *newhost, /* ascii version */
1057 Curl_printable_address(ai, buf, sizeof(buf));
1058 infof(conn->data, "Connecting to %s (%s) port %d\n", newhost, buf, port);
1061 /***********************************************************************
1065 * Send the proper PORT command. PORT is the ftp client's way of telling the
1066 * server that *WE* open a port that we listen on an awaits the server to
1067 * connect to. This is the opposite of PASV.
1071 CURLcode ftp_use_port(struct connectdata *conn)
1073 struct SessionHandle *data=conn->data;
1074 curl_socket_t portsock= CURL_SOCKET_BAD;
1076 int ftpcode; /* receive FTP response codes in this */
1080 /******************************************************************
1082 * Here's a piece of IPv6-specific code coming up
1086 struct addrinfo hints, *res, *ai;
1087 struct sockaddr_storage ss;
1089 char hbuf[NI_MAXHOST];
1091 struct sockaddr *sa=(struct sockaddr *)&ss;
1094 char portmsgbuf[1024], tmp[1024];
1096 const char *mode[] = { "EPRT", "LPRT", "PORT", NULL };
1102 * we should use Curl_if2ip? given pickiness of recent ftpd,
1103 * I believe we should use the same address as the control connection.
1106 rc = getsockname(conn->sock[FIRSTSOCKET], (struct sockaddr *)&ss, &sslen);
1108 failf(data, "getsockname() returned %d\n", rc);
1109 return CURLE_FTP_PORT_FAILED;
1112 rc = getnameinfo((struct sockaddr *)&ss, sslen, hbuf, sizeof(hbuf), NULL, 0,
1115 failf(data, "getnameinfo() returned %d\n", rc);
1116 return CURLE_FTP_PORT_FAILED;
1119 memset(&hints, 0, sizeof(hints));
1120 hints.ai_family = sa->sa_family;
1121 /*hints.ai_family = ss.ss_family;
1122 this way can be used if sockaddr_storage is properly defined, as glibc
1124 hints.ai_socktype = SOCK_STREAM;
1125 hints.ai_flags = AI_PASSIVE;
1127 rc = getaddrinfo(hbuf, NULL, &hints, &res);
1129 failf(data, "getaddrinfo() returned %d\n", rc);
1130 return CURLE_FTP_PORT_FAILED;
1133 portsock = CURL_SOCKET_BAD;
1135 for (ai = res; ai; ai = ai->ai_next) {
1137 * Workaround for AIX5 getaddrinfo() problem (it doesn't set ai_socktype):
1139 if (ai->ai_socktype == 0)
1140 ai->ai_socktype = hints.ai_socktype;
1142 portsock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
1143 if (portsock == CURL_SOCKET_BAD) {
1144 error = Curl_ourerrno();
1148 if (bind(portsock, ai->ai_addr, ai->ai_addrlen) < 0) {
1149 error = Curl_ourerrno();
1151 portsock = CURL_SOCKET_BAD;
1155 if (listen(portsock, 1) < 0) {
1156 error = Curl_ourerrno();
1158 portsock = CURL_SOCKET_BAD;
1165 if (portsock == CURL_SOCKET_BAD) {
1166 failf(data, "%s", Curl_strerror(conn,error));
1167 return CURLE_FTP_PORT_FAILED;
1171 if (getsockname(portsock, sa, &sslen) < 0) {
1172 failf(data, "%s", Curl_strerror(conn,Curl_ourerrno()));
1173 return CURLE_FTP_PORT_FAILED;
1176 for (modep = (char **)(data->set.ftp_use_eprt?&mode[0]:&mode[2]);
1177 modep && *modep; modep++) {
1181 switch (sa->sa_family) {
1183 ap = (unsigned char *)&((struct sockaddr_in *)&ss)->sin_addr;
1184 alen = sizeof(((struct sockaddr_in *)&ss)->sin_addr);
1185 pp = (unsigned char *)&((struct sockaddr_in *)&ss)->sin_port;
1186 plen = sizeof(((struct sockaddr_in *)&ss)->sin_port);
1191 ap = (unsigned char *)&((struct sockaddr_in6 *)&ss)->sin6_addr;
1192 alen = sizeof(((struct sockaddr_in6 *)&ss)->sin6_addr);
1193 pp = (unsigned char *)&((struct sockaddr_in6 *)&ss)->sin6_port;
1194 plen = sizeof(((struct sockaddr_in6 *)&ss)->sin6_port);
1200 lprtaf = eprtaf = -1;
1204 if (strcmp(*modep, "EPRT") == 0) {
1207 if (getnameinfo((struct sockaddr *)&ss, sslen,
1208 portmsgbuf, sizeof(portmsgbuf), tmp, sizeof(tmp),
1212 /* do not transmit IPv6 scope identifier to the wire */
1213 if (sa->sa_family == AF_INET6) {
1214 char *q = strchr(portmsgbuf, '%');
1219 result = Curl_ftpsendf(conn, "%s |%d|%s|%s|", *modep, eprtaf,
1224 else if (strcmp(*modep, "LPRT") == 0 ||
1225 strcmp(*modep, "PORT") == 0) {
1228 if (strcmp(*modep, "LPRT") == 0 && lprtaf < 0)
1230 if (strcmp(*modep, "PORT") == 0 && sa->sa_family != AF_INET)
1233 portmsgbuf[0] = '\0';
1234 if (strcmp(*modep, "LPRT") == 0) {
1235 snprintf(tmp, sizeof(tmp), "%d,%d", lprtaf, alen);
1236 if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >=
1237 sizeof(portmsgbuf)) {
1242 for (i = 0; i < alen; i++) {
1244 snprintf(tmp, sizeof(tmp), ",%u", ap[i]);
1246 snprintf(tmp, sizeof(tmp), "%u", ap[i]);
1248 if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >=
1249 sizeof(portmsgbuf)) {
1254 if (strcmp(*modep, "LPRT") == 0) {
1255 snprintf(tmp, sizeof(tmp), ",%d", plen);
1257 if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >= sizeof(portmsgbuf))
1261 for (i = 0; i < plen; i++) {
1262 snprintf(tmp, sizeof(tmp), ",%u", pp[i]);
1264 if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >=
1265 sizeof(portmsgbuf)) {
1270 result = Curl_ftpsendf(conn, "%s %s", *modep, portmsgbuf);
1275 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
1279 if (ftpcode != 200) {
1288 failf(data, "PORT command attempts failed");
1289 return CURLE_FTP_PORT_FAILED;
1291 /* we set the secondary socket variable to this for now, it
1292 is only so that the cleanup function will close it in case
1293 we fail before the true secondary stuff is made */
1294 conn->sock[SECONDARYSOCKET] = portsock;
1297 /******************************************************************
1299 * Here's a piece of IPv4-specific code coming up
1302 struct sockaddr_in sa;
1303 unsigned short porttouse;
1304 char myhost[256] = "";
1305 bool sa_filled_in = FALSE;
1306 Curl_addrinfo *addr = NULL;
1307 unsigned short ip[4];
1309 if(data->set.ftpport) {
1312 /* First check if the given name is an IP address */
1313 in=inet_addr(data->set.ftpport);
1315 if(in != CURL_INADDR_NONE)
1316 /* this is an IPv4 address */
1317 addr = Curl_ip2addr(in, data->set.ftpport, 0);
1319 if(Curl_if2ip(data->set.ftpport, myhost, sizeof(myhost))) {
1320 /* The interface to IP conversion provided a dotted address */
1321 in=inet_addr(myhost);
1322 addr = Curl_ip2addr(in, myhost, 0);
1324 else if(strlen(data->set.ftpport)> 1) {
1325 /* might be a host name! */
1326 struct Curl_dns_entry *h=NULL;
1327 int rc = Curl_resolv(conn, myhost, 0, &h);
1328 if(rc == CURLRESOLV_PENDING)
1329 rc = Curl_wait_for_resolv(conn, &h);
1332 /* when we return from this function, we can forget about this entry
1333 to we can unlock it now already */
1334 Curl_resolv_unlock(data, h);
1337 } /* CURL_INADDR_NONE */
1338 } /* data->set.ftpport */
1341 /* pick a suitable default here */
1346 if (getsockname(conn->sock[FIRSTSOCKET],
1347 (struct sockaddr *)&sa, &sslen) < 0) {
1348 failf(data, "getsockname() failed");
1349 return CURLE_FTP_PORT_FAILED;
1352 sa_filled_in = TRUE; /* the sa struct is filled in */
1355 if (addr || sa_filled_in) {
1356 portsock = socket(AF_INET, SOCK_STREAM, 0);
1357 if(CURL_SOCKET_BAD != portsock) {
1360 /* we set the secondary socket variable to this for now, it
1361 is only so that the cleanup function will close it in case
1362 we fail before the true secondary stuff is made */
1363 conn->sock[SECONDARYSOCKET] = portsock;
1366 memcpy(&sa, addr->ai_addr, sizeof(sa));
1367 sa.sin_addr.s_addr = INADDR_ANY;
1373 if(bind(portsock, (struct sockaddr *)&sa, size) >= 0) {
1374 /* we succeeded to bind */
1375 struct sockaddr_in add;
1376 socklen_t socksize = sizeof(add);
1378 if(getsockname(portsock, (struct sockaddr *) &add,
1380 failf(data, "getsockname() failed");
1381 return CURLE_FTP_PORT_FAILED;
1383 porttouse = ntohs(add.sin_port);
1385 if ( listen(portsock, 1) < 0 ) {
1386 failf(data, "listen(2) failed on socket");
1387 return CURLE_FTP_PORT_FAILED;
1391 failf(data, "bind(2) failed on socket");
1392 return CURLE_FTP_PORT_FAILED;
1396 failf(data, "socket(2) failed (%s)");
1397 return CURLE_FTP_PORT_FAILED;
1401 failf(data, "could't find IP address to use");
1402 return CURLE_FTP_PORT_FAILED;
1406 Curl_inet_ntop(AF_INET, &((struct sockaddr_in *)&sa)->sin_addr,
1407 myhost, sizeof(myhost));
1409 Curl_printable_address(addr, myhost, sizeof(myhost));
1411 if(4 == sscanf(myhost, "%hu.%hu.%hu.%hu",
1412 &ip[0], &ip[1], &ip[2], &ip[3])) {
1414 infof(data, "Telling server to connect to %d.%d.%d.%d:%d\n",
1415 ip[0], ip[1], ip[2], ip[3], porttouse);
1417 result=Curl_ftpsendf(conn, "PORT %d,%d,%d,%d,%d,%d",
1418 ip[0], ip[1], ip[2], ip[3],
1426 return CURLE_FTP_PORT_FAILED;
1428 Curl_freeaddrinfo(addr);
1430 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
1434 if(ftpcode != 200) {
1435 failf(data, "Server does not grok PORT, try without it!");
1436 return CURLE_FTP_PORT_FAILED;
1438 #endif /* end of ipv4-specific code */
1443 /***********************************************************************
1447 * Send the PASV command. PASV is the ftp client's way of asking the server to
1448 * open a second port that we can connect to (for the data transfer). This is
1449 * the opposite of PORT.
1453 CURLcode ftp_use_pasv(struct connectdata *conn,
1456 struct SessionHandle *data = conn->data;
1458 char *buf = data->state.buffer; /* this is our buffer */
1459 int ftpcode; /* receive FTP response codes in this */
1461 struct Curl_dns_entry *addr=NULL;
1462 Curl_addrinfo *conninfo;
1466 Here's the excecutive summary on what to do:
1468 PASV is RFC959, expect:
1469 227 Entering Passive Mode (a1,a2,a3,a4,p1,p2)
1471 LPSV is RFC1639, expect:
1472 228 Entering Long Passive Mode (4,4,a1,a2,a3,a4,2,p1,p2)
1474 EPSV is RFC2428, expect:
1475 229 Entering Extended Passive Mode (|||port|)
1479 const char *mode[] = { "EPSV", "PASV", NULL };
1480 int results[] = { 229, 227, 0 };
1482 unsigned short connectport; /* the local port connect() should use! */
1483 unsigned short newport=0; /* remote port, not necessary the local one */
1485 /* newhost must be able to hold a full IP-style address in ASCII, which
1486 in the IPv6 case means 5*8-1 = 39 letters */
1488 char *newhostp=NULL;
1490 for (modeoff = (data->set.ftp_use_epsv?0:1);
1491 mode[modeoff]; modeoff++) {
1492 result = Curl_ftpsendf(conn, "%s", mode[modeoff]);
1495 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
1498 if (ftpcode == results[modeoff])
1502 if (!mode[modeoff]) {
1503 failf(data, "Odd return code after PASV");
1504 return CURLE_FTP_WEIRD_PASV_REPLY;
1506 else if (227 == results[modeoff]) {
1512 * New 227-parser June 3rd 1999.
1513 * It now scans for a sequence of six comma-separated numbers and
1514 * will take them as IP+port indicators.
1516 * Found reply-strings include:
1517 * "227 Entering Passive Mode (127,0,0,1,4,51)"
1518 * "227 Data transfer will passively listen to 127,0,0,1,4,51"
1519 * "227 Entering passive mode. 127,0,0,1,4,51"
1523 if (6 == sscanf(str, "%d,%d,%d,%d,%d,%d",
1524 &ip[0], &ip[1], &ip[2], &ip[3],
1525 &port[0], &port[1]))
1531 failf(data, "Couldn't interpret this 227-reply: %s", buf);
1532 return CURLE_FTP_WEIRD_227_FORMAT;
1535 snprintf(newhost, sizeof(newhost),
1536 "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
1538 newport = (port[0]<<8) + port[1];
1540 else if (229 == results[modeoff]) {
1541 char *ptr = strchr(buf, '(');
1546 if(5 == sscanf(ptr, "%c%c%c%u%c",
1552 char sep1 = separator[0];
1555 /* The four separators should be identical, or else this is an oddly
1556 formatted reply and we bail out immediately. */
1557 for(i=1; i<4; i++) {
1558 if(separator[i] != sep1) {
1559 ptr=NULL; /* set to NULL to signal error */
1566 /* we should use the same host we already are connected to */
1567 newhostp = conn->host.name;
1574 failf(data, "Weirdly formatted EPSV reply");
1575 return CURLE_FTP_WEIRD_PASV_REPLY;
1579 return CURLE_FTP_CANT_RECONNECT;
1581 if(data->change.proxy && *data->change.proxy) {
1583 * This is a tunnel through a http proxy and we need to connect to the
1586 * We don't want to rely on a former host lookup that might've expired
1587 * now, instead we remake the lookup here and now!
1589 rc = Curl_resolv(conn, conn->proxy.name, (int)conn->port, &addr);
1590 if(rc == CURLRESOLV_PENDING)
1591 rc = Curl_wait_for_resolv(conn, &addr);
1594 (unsigned short)conn->port; /* we connect to the proxy's port */
1598 /* normal, direct, ftp connection */
1599 rc = Curl_resolv(conn, newhostp, newport, &addr);
1600 if(rc == CURLRESOLV_PENDING)
1601 rc = Curl_wait_for_resolv(conn, &addr);
1604 failf(data, "Can't resolve new host %s:%d", newhostp, newport);
1605 return CURLE_FTP_CANT_GET_HOST;
1607 connectport = newport; /* we connect to the remote port */
1610 result = Curl_connecthost(conn,
1612 &conn->sock[SECONDARYSOCKET],
1616 Curl_resolv_unlock(data, addr); /* we're done using this address */
1622 * When this is used from the multi interface, this might've returned with
1623 * the 'connected' set to FALSE and thus we are now awaiting a non-blocking
1624 * connect to connect and we should not be "hanging" here waiting.
1627 if(data->set.verbose)
1628 /* this just dumps information about this second connection */
1629 ftp_pasv_verbose(conn, conninfo, newhostp, connectport);
1631 #ifndef CURL_DISABLE_HTTP
1632 if(conn->bits.tunnel_proxy) {
1633 /* We want "seamless" FTP operations through HTTP proxy tunnel */
1634 result = Curl_ConnectHTTPProxyTunnel(conn, SECONDARYSOCKET,
1636 if(CURLE_OK != result)
1639 #endif /* CURL_DISABLE_HTTP */
1645 * Curl_ftp_nextconnect()
1647 * This function shall be called when the second FTP connection has been
1648 * established and is confirmed connected.
1651 CURLcode Curl_ftp_nextconnect(struct connectdata *conn)
1653 struct SessionHandle *data=conn->data;
1654 char *buf = data->state.buffer; /* this is our buffer */
1657 int ftpcode; /* for ftp status */
1659 /* the ftp struct is already inited in Curl_ftp_connect() */
1660 struct FTP *ftp = conn->proto.ftp;
1661 curl_off_t *bytecountp = ftp->bytecountp;
1663 if(data->set.upload) {
1665 /* Set type to binary (unless specified ASCII) */
1666 result = ftp_transfertype(conn, data->set.ftp_ascii);
1670 /* Send any PREQUOTE strings after transfer type is set? (Wesley Laxton)*/
1671 if(data->set.prequote) {
1672 if ((result = ftp_sendquote(conn, data->set.prequote)) != CURLE_OK)
1676 if(conn->resume_from) {
1677 /* we're about to continue the uploading of a file */
1678 /* 1. get already existing file's size. We use the SIZE
1679 command for this which may not exist in the server!
1680 The SIZE command is not in RFC959. */
1682 /* 2. This used to set REST. But since we can do append, we
1683 don't another ftp command. We just skip the source file
1684 offset and then we APPEND the rest on the file instead */
1686 /* 3. pass file-size number of bytes in the source file */
1687 /* 4. lower the infilesize counter */
1688 /* => transfer as usual */
1690 if(conn->resume_from < 0 ) {
1691 /* we could've got a specified offset from the command line,
1692 but now we know we didn't */
1693 curl_off_t gottensize;
1695 if(CURLE_OK != ftp_getsize(conn, ftp->file, &gottensize)) {
1696 failf(data, "Couldn't get remote file size");
1697 return CURLE_FTP_COULDNT_GET_SIZE;
1699 conn->resume_from = gottensize;
1702 if(conn->resume_from) {
1703 /* do we still game? */
1704 curl_off_t passed=0;
1705 /* enable append instead */
1706 data->set.ftp_append = 1;
1708 /* Now, let's read off the proper amount of bytes from the
1709 input. If we knew it was a proper file we could've just
1710 fseek()ed but we only have a stream here */
1712 curl_off_t readthisamountnow = (conn->resume_from - passed);
1713 curl_off_t actuallyread;
1715 if(readthisamountnow > BUFSIZE)
1716 readthisamountnow = BUFSIZE;
1718 actuallyread = (curl_off_t)
1719 conn->fread(data->state.buffer, 1, (size_t)readthisamountnow,
1722 passed += actuallyread;
1723 if(actuallyread != readthisamountnow) {
1724 failf(data, "Could only read %" FORMAT_OFF_T
1725 " bytes from the input", passed);
1726 return CURLE_FTP_COULDNT_USE_REST;
1729 while(passed != conn->resume_from);
1731 /* now, decrease the size of the read */
1732 if(data->set.infilesize>0) {
1733 data->set.infilesize -= conn->resume_from;
1735 if(data->set.infilesize <= 0) {
1736 infof(data, "File already completely uploaded\n");
1738 /* no data to transfer */
1739 result=Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1741 /* Set no_transfer so that we won't get any error in
1742 * Curl_ftp_done() because we didn't transfer anything! */
1743 ftp->no_transfer = TRUE;
1748 /* we've passed, proceed as normal */
1752 /* Send everything on data->state.in to the socket */
1753 if(data->set.ftp_append) {
1754 /* we append onto the file instead of rewriting it */
1755 FTPSENDF(conn, "APPE %s", ftp->file);
1758 FTPSENDF(conn, "STOR %s", ftp->file);
1761 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
1766 failf(data, "Failed FTP upload:%s", buf+3);
1767 /* oops, we never close the sockets! */
1768 return CURLE_FTP_COULDNT_STOR_FILE;
1771 if(data->set.ftp_use_port) {
1772 /* PORT means we are now awaiting the server to connect to us. */
1773 result = AllowServerConnect(conn);
1778 if(conn->ssl[SECONDARYSOCKET].use) {
1779 /* since we only have a plaintext TCP connection here, we must now
1781 infof(data, "Doing the SSL/TLS handshake on the data stream\n");
1782 result = Curl_SSLConnect(conn, SECONDARYSOCKET);
1789 /* When we know we're uploading a specified file, we can get the file
1790 size prior to the actual upload. */
1792 Curl_pgrsSetUploadSize(data, data->set.infilesize);
1794 result = Curl_Transfer(conn, -1, -1, FALSE, NULL, /* no download */
1795 SECONDARYSOCKET, bytecountp);
1800 else if(!conn->bits.no_body) {
1801 /* Retrieve file or directory */
1803 curl_off_t downloadsize=-1;
1805 if(conn->bits.use_range && conn->range) {
1806 curl_off_t from, to;
1807 curl_off_t totalsize=-1;
1811 from=curlx_strtoofft(conn->range, &ptr, 0);
1812 while(ptr && *ptr && (isspace((int)*ptr) || (*ptr=='-')))
1814 to=curlx_strtoofft(ptr, &ptr2, 0);
1816 /* we didn't get any digit */
1819 if((-1 == to) && (from>=0)) {
1821 conn->resume_from = from;
1822 infof(data, "FTP RANGE %" FORMAT_OFF_T " to end of file\n", from);
1827 conn->maxdownload = -from;
1828 conn->resume_from = from;
1829 infof(data, "FTP RANGE the last %" FORMAT_OFF_T " bytes\n", totalsize);
1833 totalsize = to-from;
1834 conn->maxdownload = totalsize+1; /* include the last mentioned byte */
1835 conn->resume_from = from;
1836 infof(data, "FTP RANGE from %" FORMAT_OFF_T
1837 " getting %" FORMAT_OFF_T " bytes\n", from, conn->maxdownload);
1839 infof(data, "range-download from %" FORMAT_OFF_T
1840 " to %" FORMAT_OFF_T ", totally %" FORMAT_OFF_T " bytes\n",
1841 from, to, conn->maxdownload);
1842 ftp->dont_check = TRUE; /* dont check for successful transfer */
1845 if((data->set.ftp_list_only) || !ftp->file) {
1846 /* The specified path ends with a slash, and therefore we think this
1847 is a directory that is requested, use LIST. But before that we
1848 need to set ASCII transfer mode. */
1851 /* Set type to ASCII */
1852 result = ftp_transfertype(conn, TRUE /* ASCII enforced */);
1856 /* if this output is to be machine-parsed, the NLST command will be
1857 better used since the LIST command output is not specified or
1858 standard in any way */
1860 FTPSENDF(conn, "%s",
1861 data->set.customrequest?data->set.customrequest:
1862 (data->set.ftp_list_only?"NLST":"LIST"));
1865 curl_off_t foundsize;
1867 /* Set type to binary (unless specified ASCII) */
1868 result = ftp_transfertype(conn, data->set.ftp_ascii);
1872 /* Send any PREQUOTE strings after transfer type is set? */
1873 if(data->set.prequote) {
1874 if ((result = ftp_sendquote(conn, data->set.prequote)) != CURLE_OK)
1878 /* Attempt to get the size, it'll be useful in some cases: for resumed
1879 downloads and when talking to servers that don't give away the size
1880 in the RETR response line. */
1881 result = ftp_getsize(conn, ftp->file, &foundsize);
1882 if(CURLE_OK == result) {
1883 if (data->set.max_filesize && foundsize > data->set.max_filesize) {
1884 failf(data, "Maximum file size exceeded");
1885 return CURLE_FILESIZE_EXCEEDED;
1887 downloadsize = foundsize;
1890 if(conn->resume_from) {
1892 /* Daniel: (August 4, 1999)
1894 * We start with trying to use the SIZE command to figure out the size
1895 * of the file we're gonna get. If we can get the size, this is by far
1896 * the best way to know if we're trying to resume beyond the EOF.
1898 * Daniel, November 28, 2001. We *always* get the size on downloads
1899 * now, so it is done before this even when not doing resumes. I saved
1900 * the comment above for nostalgical reasons! ;-)
1902 if(CURLE_OK != result) {
1903 infof(data, "ftp server doesn't support SIZE\n");
1904 /* We couldn't get the size and therefore we can't know if there
1905 really is a part of the file left to get, although the server
1906 will just close the connection when we start the connection so it
1907 won't cause us any harm, just not make us exit as nicely. */
1910 /* We got a file size report, so we check that there actually is a
1911 part of the file left to get, or else we go home. */
1912 if(conn->resume_from< 0) {
1913 /* We're supposed to download the last abs(from) bytes */
1914 if(foundsize < -conn->resume_from) {
1915 failf(data, "Offset (%" FORMAT_OFF_T
1916 ") was beyond file size (%" FORMAT_OFF_T ")",
1917 conn->resume_from, foundsize);
1918 return CURLE_FTP_BAD_DOWNLOAD_RESUME;
1920 /* convert to size to download */
1921 downloadsize = -conn->resume_from;
1922 /* download from where? */
1923 conn->resume_from = foundsize - downloadsize;
1926 if(foundsize < conn->resume_from) {
1927 failf(data, "Offset (%" FORMAT_OFF_T
1928 ") was beyond file size (%" FORMAT_OFF_T ")",
1929 conn->resume_from, foundsize);
1930 return CURLE_FTP_BAD_DOWNLOAD_RESUME;
1932 /* Now store the number of bytes we are expected to download */
1933 downloadsize = foundsize-conn->resume_from;
1937 if (downloadsize == 0) {
1938 /* no data to transfer */
1939 result=Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1940 infof(data, "File already completely downloaded\n");
1942 /* Set no_transfer so that we won't get any error in Curl_ftp_done()
1943 * because we didn't transfer the any file */
1944 ftp->no_transfer = TRUE;
1948 /* Set resume file transfer offset */
1949 infof(data, "Instructs server to resume from offset %" FORMAT_OFF_T
1953 FTPSENDF(conn, "REST %" FORMAT_OFF_T, conn->resume_from);
1955 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
1959 if(ftpcode != 350) {
1960 failf(data, "Couldn't use REST: %s", buf+4);
1961 return CURLE_FTP_COULDNT_USE_REST;
1965 FTPSENDF(conn, "RETR %s", ftp->file);
1968 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
1972 if((ftpcode == 150) || (ftpcode == 125)) {
1976 150 Opening BINARY mode data connection for /etc/passwd (2241
1977 bytes). (ok, the file is being transfered)
1980 150 Opening ASCII mode data connection for /bin/ls
1983 150 ASCII data connection for /bin/ls (137.167.104.91,37445) (0 bytes).
1986 150 Opening ASCII mode data connection for /linux/fisk/kpanelrc (0.0.0.0,0) (545 bytes).
1989 125 Data connection already open; Transfer starting. */
1991 curl_off_t size=-1; /* default unknown size */
1995 * It appears that there are FTP-servers that return size 0 for files
1996 * when SIZE is used on the file while being in BINARY mode. To work
1997 * around that (stupid) behavior, we attempt to parse the RETR response
1998 * even if the SIZE returned size zero.
2000 * Debugging help from Salvatore Sorrentino on February 26, 2003.
2004 !data->set.ftp_ascii &&
2005 (downloadsize < 1)) {
2007 * It seems directory listings either don't show the size or very
2008 * often uses size 0 anyway. ASCII transfers may very well turn out
2009 * that the transfered amount of data is not the same as this line
2010 * tells, why using this number in those cases only confuses us.
2012 * Example D above makes this parsing a little tricky */
2014 bytes=strstr(buf, " bytes");
2017 /* this is a hint there is size information in there! ;-) */
2019 /* scan for the parenthesis and break there */
2022 /* if only skip digits, or else we're in deep trouble */
2023 if(!isdigit((int)*bytes)) {
2027 /* one more estep backwards */
2030 /* only if we have nothing but digits: */
2032 /* get the number! */
2033 size = curlx_strtoofft(bytes, NULL, 0);
2038 else if(downloadsize > -1)
2039 size = downloadsize;
2041 if(data->set.ftp_use_port) {
2042 result = AllowServerConnect(conn);
2047 if(conn->ssl[SECONDARYSOCKET].use) {
2048 /* since we only have a plaintext TCP connection here, we must now
2050 infof(data, "Doing the SSL/TLS handshake on the data stream\n");
2051 result = Curl_SSLConnect(conn, SECONDARYSOCKET);
2056 if(size > conn->maxdownload && conn->maxdownload > 0)
2057 size = conn->size = conn->maxdownload;
2059 infof(data, "Getting file with size: %" FORMAT_OFF_T "\n", size);
2062 result=Curl_Transfer(conn, SECONDARYSOCKET, size, FALSE,
2064 -1, NULL); /* no upload here */
2069 if(dirlist && (ftpcode == 450)) {
2070 /* simply no matching files */
2071 ftp->no_transfer = TRUE; /* don't think we should download anything */
2074 failf(data, "%s", buf+4);
2075 return CURLE_FTP_COULDNT_RETR_FILE;
2080 /* end of transfer */
2085 /***********************************************************************
2089 * This is the actual DO function for FTP. Get a file/directory according to
2090 * the options previously setup.
2094 CURLcode ftp_perform(struct connectdata *conn,
2095 bool *connected) /* for the TCP connect status after
2098 /* this is FTP and no proxy */
2099 CURLcode result=CURLE_OK;
2100 struct SessionHandle *data=conn->data;
2101 char *buf = data->state.buffer; /* this is our buffer */
2103 /* the ftp struct is already inited in Curl_ftp_connect() */
2104 struct FTP *ftp = conn->proto.ftp;
2106 /* Send any QUOTE strings? */
2107 if(data->set.quote) {
2108 if ((result = ftp_sendquote(conn, data->set.quote)) != CURLE_OK)
2112 /* This is a re-used connection. Since we change directory to where the
2113 transfer is taking place, we must now get back to the original dir
2114 where we ended up after login: */
2115 if (conn->bits.reuse && ftp->entrypath) {
2116 if ((result = ftp_cwd_and_mkd(conn, ftp->entrypath)) != CURLE_OK)
2121 int i; /* counter for loop */
2122 for (i=0; i < ftp->dirdepth; i++) {
2123 /* RFC 1738 says empty components should be respected too, but
2124 that is plain stupid since CWD can't be used with an empty argument */
2125 if ((result = ftp_cwd_and_mkd(conn, ftp->dirs[i])) != CURLE_OK)
2130 /* Requested time of file or time-depended transfer? */
2131 if((data->set.get_filetime || data->set.timecondition) &&
2133 result = ftp_getfiletime(conn, ftp->file);
2136 case CURLE_FTP_COULDNT_RETR_FILE:
2138 if(data->set.timecondition) {
2139 if((data->info.filetime > 0) && (data->set.timevalue > 0)) {
2140 switch(data->set.timecondition) {
2141 case CURL_TIMECOND_IFMODSINCE:
2143 if(data->info.filetime < data->set.timevalue) {
2144 infof(data, "The requested document is not new enough\n");
2145 ftp->no_transfer = TRUE; /* mark this to not transfer data */
2149 case CURL_TIMECOND_IFUNMODSINCE:
2150 if(data->info.filetime > data->set.timevalue) {
2151 infof(data, "The requested document is not old enough\n");
2152 ftp->no_transfer = TRUE; /* mark this to not transfer data */
2159 infof(data, "Skipping time comparison\n");
2168 /* If we have selected NOBODY and HEADER, it means that we only want file
2169 information. Which in FTP can't be much more than the file size and
2171 if(conn->bits.no_body && data->set.include_header && ftp->file) {
2172 /* The SIZE command is _not_ RFC 959 specified, and therefor many servers
2173 may not support it! It is however the only way we have to get a file's
2175 curl_off_t filesize;
2179 ftp->no_transfer = TRUE; /* this means no actual transfer is made */
2181 /* Some servers return different sizes for different modes, and thus we
2182 must set the proper type before we check the size */
2183 result = ftp_transfertype(conn, data->set.ftp_ascii);
2187 /* failing to get size is not a serious error */
2188 result = ftp_getsize(conn, ftp->file, &filesize);
2190 if(CURLE_OK == result) {
2191 snprintf(buf, sizeof(data->state.buffer),
2192 "Content-Length: %" FORMAT_OFF_T "\r\n", filesize);
2193 result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0);
2198 /* Determine if server can respond to REST command and therefore
2199 whether it can do a range */
2200 FTPSENDF(conn, "REST 0", NULL);
2201 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
2203 if ((CURLE_OK == result) && (ftpcode == 350)) {
2204 result = Curl_client_write(data, CLIENTWRITE_BOTH,
2205 (char *)"Accept-ranges: bytes\r\n", 0);
2210 /* If we asked for a time of the file and we actually got one as
2211 well, we "emulate" a HTTP-style header in our output. */
2213 #ifdef HAVE_STRFTIME
2214 if(data->set.get_filetime && (data->info.filetime>=0) ) {
2216 time_t clock = (time_t)data->info.filetime;
2217 #ifdef HAVE_GMTIME_R
2219 tm = (struct tm *)gmtime_r(&clock, &buffer);
2221 tm = gmtime(&clock);
2223 /* format: "Tue, 15 Nov 1994 12:45:26" */
2224 strftime(buf, BUFSIZE-1, "Last-Modified: %a, %d %b %Y %H:%M:%S GMT\r\n",
2226 result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0);
2235 if(conn->bits.no_body)
2236 /* doesn't really transfer any data */
2237 ftp->no_transfer = TRUE;
2238 /* Get us a second connection up and connected */
2239 else if(data->set.ftp_use_port) {
2240 /* We have chosen to use the PORT command */
2241 result = ftp_use_port(conn);
2242 if(CURLE_OK == result) {
2243 /* we have the data connection ready */
2244 infof(data, "Ordered connect of the data stream with PORT!\n");
2245 *connected = TRUE; /* mark us "still connected" */
2249 /* We have chosen (this is default) to use the PASV command */
2250 result = ftp_use_pasv(conn, connected);
2251 if(CURLE_OK == result && *connected)
2252 infof(data, "Connected the data stream with PASV!\n");
2258 /***********************************************************************
2262 * This function is registered as 'curl_do' function. It decodes the path
2263 * parts etc as a wrapper to the actual DO function (ftp_perform).
2265 * The input argument is already checked for validity.
2267 CURLcode Curl_ftp(struct connectdata *conn)
2269 CURLcode retcode = CURLE_OK;
2271 if (conn->sec_conn) /* 3rd party transfer */
2272 retcode = ftp_3rdparty(conn);
2274 retcode = ftp_regular_transfer(conn);
2279 /***********************************************************************
2283 * Sends the formated string as a ftp command to a ftp server
2285 * NOTE: we build the command in a fixed-length buffer, which sets length
2286 * restrictions on the command!
2288 CURLcode Curl_ftpsendf(struct connectdata *conn,
2289 const char *fmt, ...)
2291 ssize_t bytes_written;
2295 CURLcode res = CURLE_OK;
2299 vsnprintf(s, 250, fmt, ap);
2302 strcat(s, "\r\n"); /* append a trailing CRLF */
2305 write_len = strlen(s);
2308 res = Curl_write(conn, conn->sock[FIRSTSOCKET], sptr, write_len,
2314 if(conn->data->set.verbose)
2315 Curl_debug(conn->data, CURLINFO_HEADER_OUT, sptr, bytes_written, conn->host.dispname);
2317 if(bytes_written != (ssize_t)write_len) {
2318 write_len -= bytes_written;
2319 sptr += bytes_written;
2328 /***********************************************************************
2332 * This should be called before calling sclose() on an ftp control connection
2333 * (not data connections). We should then wait for the response from the
2334 * server before returning. The calling code should then try to close the
2338 static CURLcode ftp_quit(struct connectdata *conn)
2342 CURLcode ret = CURLE_OK;
2344 if(conn->proto.ftp->ctl_valid) {
2345 ret = Curl_ftpsendf(conn, "%s", "QUIT");
2347 ret = Curl_GetFTPResponse(&nread, conn, &ftpcode);
2353 /***********************************************************************
2355 * Curl_ftp_disconnect()
2357 * Disconnect from an FTP server. Cleanup protocol-specific per-connection
2360 CURLcode Curl_ftp_disconnect(struct connectdata *conn)
2362 struct FTP *ftp= conn->proto.ftp;
2364 /* We cannot send quit unconditionally. If this connection is stale or
2365 bad in any way, sending quit and waiting around here will make the
2366 disconnect wait in vain and cause more problems than we need to.
2368 ftp_quit() will check the state of ftp->ctl_valid. If it's ok it
2369 will try to send the QUIT command, otherwise it will just return.
2372 /* The FTP session may or may not have been allocated/setup at this point! */
2374 (void)ftp_quit(conn); /* ignore errors on the QUIT */
2377 free(ftp->entrypath);
2387 /***********************************************************************
2391 * Makes a directory on the FTP server.
2395 static CURLcode ftp_mkd(struct connectdata *conn, char *path)
2397 CURLcode result=CURLE_OK;
2398 int ftpcode; /* for ftp status */
2401 /* Create a directory on the remote server */
2402 FTPSENDF(conn, "MKD %s", path);
2404 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
2411 infof( conn->data , "Created remote directory %s\n" , path );
2414 failf(conn->data, "Permission denied to make directory %s", path);
2415 result = CURLE_FTP_ACCESS_DENIED;
2418 failf(conn->data, "unrecognized MKD response: %d", ftpcode );
2419 result = CURLE_FTP_ACCESS_DENIED;
2425 /***********************************************************************
2429 * Send 'CWD' to the remote server to Change Working Directory. It is the ftp
2430 * version of the unix 'cd' command. This function is only called from the
2431 * ftp_cwd_and_mkd() function these days.
2433 * This function does NOT call failf().
2436 CURLcode ftp_cwd(struct connectdata *conn, char *path)
2442 FTPSENDF(conn, "CWD %s", path);
2443 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
2445 /* According to RFC959, CWD is supposed to return 250 on success, but
2446 there seem to be non-compliant FTP servers out there that return 200,
2447 so we accept any '2xy' code here. */
2448 if (ftpcode/100 != 2)
2449 result = CURLE_FTP_ACCESS_DENIED;
2455 /***********************************************************************
2459 * Change to the given directory. If the directory is not present, and we
2460 * have been told to allow it, then create the directory and cd to it.
2463 static CURLcode ftp_cwd_and_mkd(struct connectdata *conn, char *path)
2467 result = ftp_cwd(conn, path);
2469 if(conn->data->set.ftp_create_missing_dirs) {
2470 result = ftp_mkd(conn, path);
2472 /* ftp_mkd() calls failf() itself */
2474 result = ftp_cwd(conn, path);
2477 failf(conn->data, "Couldn't CWD to %s", path);
2484 /***********************************************************************
2486 * ftp_3rdparty_pretransfer()
2488 * Preparation for 3rd party transfer.
2491 static CURLcode ftp_3rdparty_pretransfer(struct connectdata *conn)
2493 CURLcode result = CURLE_OK;
2494 struct SessionHandle *data = conn->data;
2495 struct connectdata *sec_conn = conn->sec_conn;
2497 /* sets transfer type */
2498 result = ftp_transfertype(conn, data->set.ftp_ascii);
2502 result = ftp_transfertype(sec_conn, data->set.ftp_ascii);
2506 /* Send any PREQUOTE strings after transfer type is set? */
2507 if (data->set.source_prequote) {
2508 /* sends command(s) to source server before file transfer */
2509 result = ftp_sendquote(sec_conn, data->set.source_prequote);
2511 if (!result && data->set.prequote)
2512 result = ftp_sendquote(conn, data->set.prequote);
2519 /***********************************************************************
2521 * ftp_3rdparty_transfer()
2523 * Performs 3rd party transfer.
2526 static CURLcode ftp_3rdparty_transfer(struct connectdata *conn)
2528 CURLcode result = CURLE_OK;
2530 int ftpcode, ip[4], port[2];
2531 struct SessionHandle *data = conn->data;
2532 struct connectdata *sec_conn = conn->sec_conn;
2533 char *buf = data->state.buffer; /* this is our buffer */
2536 const char *stor_cmd;
2537 struct connectdata *pasv_conn;
2538 struct connectdata *port_conn;
2540 if (data->set.pasvHost == CURL_TARGET_PASV) {
2542 port_conn = sec_conn;
2545 pasv_conn = sec_conn;
2549 /* sets the passive mode */
2550 FTPSENDF(pasv_conn, "%s", "PASV");
2551 result = Curl_GetFTPResponse(&nread, pasv_conn, &ftpcode);
2552 if (result) return result;
2553 if (ftpcode != 227) {
2554 failf(data, "Odd return code after PASV:%s", buf + 3);
2555 return CURLE_FTP_WEIRD_PASV_REPLY;
2559 if (6 == sscanf(str, "%d,%d,%d,%d,%d,%d",
2560 &ip[0], &ip[1], &ip[2], &ip[3], &port[0], &port[1]))
2566 failf(pasv_conn->data, "Couldn't interpret this 227-reply: %s", buf);
2567 return CURLE_FTP_WEIRD_227_FORMAT;
2570 snprintf(pasv_port, sizeof(pasv_port), "%d,%d,%d,%d,%d,%d", ip[0], ip[1],
2571 ip[2], ip[3], port[0], port[1]);
2573 /* sets data connection between remote hosts */
2574 FTPSENDF(port_conn, "PORT %s", pasv_port);
2575 result = Curl_GetFTPResponse(&nread, port_conn, &ftpcode);
2579 if (ftpcode != 200) {
2580 failf(data, "PORT command attempts failed:%s", buf + 3);
2581 return CURLE_FTP_PORT_FAILED;
2584 /* we might append onto the file instead of overwriting it */
2585 stor_cmd = data->set.ftp_append?"APPE":"STOR";
2587 /* transfers file between remote hosts */
2588 FTPSENDF(sec_conn, "RETR %s", data->set.source_path);
2590 if(data->set.pasvHost == CURL_TARGET_PASV) {
2592 result = Curl_GetFTPResponse(&nread, sec_conn, &ftpcode);
2596 if (ftpcode != 150) {
2597 failf(data, "Failed RETR: %s", buf + 4);
2598 return CURLE_FTP_COULDNT_RETR_FILE;
2601 result = Curl_ftpsendf(conn, "%s %s", stor_cmd, conn->path);
2602 if(CURLE_OK == result)
2603 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
2607 if (ftpcode != 150) {
2608 failf(data, "Failed FTP upload: %s", buf + 4);
2609 return CURLE_FTP_COULDNT_STOR_FILE;
2615 result = Curl_ftpsendf(conn, "%s %s", stor_cmd, conn->path);
2616 if(CURLE_OK == result)
2617 result = Curl_GetFTPResponse(&nread, sec_conn, &ftpcode);
2621 if (ftpcode != 150) {
2622 failf(data, "Failed FTP upload: %s", buf + 4);
2623 return CURLE_FTP_COULDNT_STOR_FILE;
2626 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
2630 if (ftpcode != 150) {
2631 failf(data, "Failed FTP upload: %s", buf + 4);
2632 return CURLE_FTP_COULDNT_STOR_FILE;
2641 /***********************************************************************
2643 * ftp_regular_transfer()
2645 * The input argument is already checked for validity.
2646 * Performs a regular transfer between local and remote hosts.
2648 * ftp->ctl_valid starts out as FALSE, and gets set to TRUE if we reach the
2649 * Curl_ftp_done() function without finding any major problem.
2652 CURLcode ftp_regular_transfer(struct connectdata *conn)
2654 CURLcode retcode=CURLE_OK;
2656 struct SessionHandle *data = conn->data;
2659 char *slash_pos; /* position of the first '/' char in curpos */
2660 char *cur_pos=conn->path; /* current position in ppath. point at the begin
2661 of next path component */
2663 /* the ftp struct is already inited in ftp_connect() */
2664 ftp = conn->proto.ftp;
2665 ftp->ctl_valid = FALSE;
2666 conn->size = -1; /* make sure this is unknown at this point */
2668 Curl_pgrsSetUploadCounter(data, 0);
2669 Curl_pgrsSetDownloadCounter(data, 0);
2670 Curl_pgrsSetUploadSize(data, 0);
2671 Curl_pgrsSetDownloadSize(data, 0);
2674 ftp->diralloc = 5; /* default dir depth to allocate */
2675 ftp->dirs = (char **)malloc(ftp->diralloc * sizeof(ftp->dirs[0]));
2677 return CURLE_OUT_OF_MEMORY;
2678 ftp->dirs[0] = NULL; /* to start with */
2680 /* parse the URL path into separate path components */
2681 while((slash_pos=strchr(cur_pos, '/'))) {
2682 /* 1 or 0 to indicate absolute directory */
2683 bool absolute_dir = (cur_pos - conn->path > 0) && (ftp->dirdepth == 0);
2685 /* seek out the next path component */
2686 if (slash_pos-cur_pos) {
2687 /* we skip empty path components, like "x//y" since the FTP command CWD
2688 requires a parameter and a non-existant parameter a) doesn't work on
2689 many servers and b) has no effect on the others. */
2690 int len = (int)(slash_pos - cur_pos + absolute_dir);
2691 ftp->dirs[ftp->dirdepth] = curl_unescape(cur_pos - absolute_dir, len);
2693 if (!ftp->dirs[ftp->dirdepth]) { /* run out of memory ... */
2694 failf(data, "no memory");
2696 return CURLE_OUT_OF_MEMORY;
2700 cur_pos = slash_pos + 1; /* jump to the rest of the string */
2705 cur_pos = slash_pos + 1; /* jump to the rest of the string */
2706 if(++ftp->dirdepth >= ftp->diralloc) {
2709 ftp->diralloc *= 2; /* double the size each time */
2710 bigger = realloc(ftp->dirs, ftp->diralloc * sizeof(ftp->dirs[0]));
2713 return CURLE_OUT_OF_MEMORY;
2715 ftp->dirs = (char **)bigger;
2720 ftp->file = cur_pos; /* the rest is the file name */
2723 ftp->file = curl_unescape(ftp->file, 0);
2724 if(NULL == ftp->file) {
2726 failf(data, "no memory");
2727 return CURLE_OUT_OF_MEMORY;
2731 ftp->file=NULL; /* instead of point to a zero byte, we make it a NULL
2734 retcode = ftp_perform(conn, &connected);
2736 if(CURLE_OK == retcode) {
2738 retcode = Curl_ftp_nextconnect(conn);
2740 if(retcode && (conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD)) {
2741 /* Failure detected, close the second socket if it was created already */
2742 sclose(conn->sock[SECONDARYSOCKET]);
2743 conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
2746 if(ftp->no_transfer)
2747 /* no data to transfer */
2748 retcode=Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
2750 /* since we didn't connect now, we want do_more to get called */
2751 conn->bits.do_more = TRUE;
2756 ftp->ctl_valid = TRUE; /* seems good */
2763 /***********************************************************************
2767 * The input argument is already checked for validity.
2768 * Performs a 3rd party transfer between two remote hosts.
2770 static CURLcode ftp_3rdparty(struct connectdata *conn)
2772 CURLcode retcode = CURLE_OK;
2774 conn->proto.ftp->ctl_valid = conn->sec_conn->proto.ftp->ctl_valid = TRUE;
2775 conn->size = conn->sec_conn->size = -1;
2777 retcode = ftp_3rdparty_pretransfer(conn);
2779 retcode = ftp_3rdparty_transfer(conn);
2784 #endif /* CURL_DISABLE_FTP */