1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2005, 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
38 #if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
40 #else /* probably some kind of unix */
41 #ifdef HAVE_SYS_SOCKET_H
42 #include <sys/socket.h>
44 #include <sys/types.h>
45 #ifdef HAVE_NETINET_IN_H
46 #include <netinet/in.h>
48 #ifdef HAVE_ARPA_INET_H
49 #include <arpa/inet.h>
52 #include <sys/utsname.h>
63 #if defined(WIN32) && defined(__GNUC__) || defined(__MINGW32__)
67 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
69 #define in_addr_t unsigned long
72 #include <curl/curl.h>
81 #include "http.h" /* for HTTP proxy tunnel stuff */
88 #include "strtoofft.h"
94 #include "inet_ntop.h"
96 #include "parsedate.h" /* for the week day and month names */
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_parse_url_path(struct connectdata *conn);
126 static CURLcode ftp_cwd_and_create_path(struct connectdata *conn);
127 static CURLcode ftp_regular_transfer(struct connectdata *conn, bool *done);
128 static CURLcode ftp_3rdparty(struct connectdata *conn);
129 static void ftp_pasv_verbose(struct connectdata *conn,
131 char *newhost, /* ascii version */
133 static CURLcode ftp_state_post_rest(struct connectdata *conn);
134 static CURLcode ftp_state_post_cwd(struct connectdata *conn);
135 static CURLcode ftp_state_quote(struct connectdata *conn,
136 bool init, ftpstate instate);
138 /* easy-to-use macro: */
139 #define FTPSENDF(x,y,z) if((result = Curl_ftpsendf(x,y,z))) return result
140 #define NBFTPSENDF(x,y,z) if((result = Curl_nbftpsendf(x,y,z))) return result
142 static void freedirs(struct FTP *ftp)
146 for (i=0; i < ftp->dirdepth; i++){
161 /* Returns non-zero iff the given string contains CR (0x0D) or LF (0x0A), which
162 are not allowed within RFC 959 <string>.
164 static bool isBadFtpString(const char *string)
166 return strchr(string, 0x0D) != NULL || strchr(string, 0x0A) != NULL;
169 /***********************************************************************
171 * AllowServerConnect()
173 * When we've issue the PORT command, we have told the server to connect
174 * to us. This function will sit and wait here until the server has
178 static CURLcode AllowServerConnect(struct connectdata *conn)
181 struct SessionHandle *data = conn->data;
182 curl_socket_t sock = conn->sock[SECONDARYSOCKET];
183 struct timeval now = Curl_tvnow();
184 long timespent = Curl_tvdiff(Curl_tvnow(), now)/1000;
185 long timeout = data->set.connecttimeout?data->set.connecttimeout:
186 (data->set.timeout?data->set.timeout: 0);
189 timeout -= timespent;
191 failf(data, "Timed out before server could connect to us");
192 return CURLE_OPERATION_TIMEDOUT;
196 /* We allow the server 60 seconds to connect to us, or a custom timeout.
197 Note the typecast here. */
198 timeout_ms = (timeout?(int)timeout:60) * 1000;
200 switch (Curl_select(sock, CURL_SOCKET_BAD, timeout_ms)) {
203 failf(data, "Error while waiting for server connect");
204 return CURLE_FTP_PORT_FAILED;
205 case 0: /* timeout */
207 failf(data, "Timeout while waiting for server connect");
208 return CURLE_FTP_PORT_FAILED;
210 /* we have received data here */
213 size_t size = sizeof(struct sockaddr_in);
214 struct sockaddr_in add;
216 getsockname(sock, (struct sockaddr *) &add, (socklen_t *)&size);
217 s=accept(sock, (struct sockaddr *) &add, (socklen_t *)&size);
219 sclose(sock); /* close the first socket */
221 if (CURL_SOCKET_BAD == s) {
223 failf(data, "Error accept()ing server connect");
224 return CURLE_FTP_PORT_FAILED;
226 infof(data, "Connection accepted from server\n");
228 conn->sock[SECONDARYSOCKET] = s;
229 Curl_nonblock(s, TRUE); /* enable non-blocking */
238 static CURLcode ftp_readresp(curl_socket_t sockfd,
239 struct connectdata *conn,
240 int *ftpcode, /* return the ftp-code if done */
241 size_t *size) /* size of the response */
243 int perline; /* count bytes per line */
247 struct SessionHandle *data = conn->data;
249 char *buf = data->state.buffer;
250 CURLcode result = CURLE_OK;
251 struct FTP *ftp = conn->proto.ftp;
255 *ftpcode = 0; /* 0 for errors or not done */
263 while((ftp->nread_resp<BUFSIZE) && (keepon && !result)) {
266 /* we had data in the "cache", copy that instead of doing an actual
269 * ftp->cache_size is cast to int here. This should be safe,
270 * because it would have been populated with something of size
271 * int to begin with, even though its datatype may be larger
274 memcpy(ptr, ftp->cache, (int)ftp->cache_size);
275 gotbytes = (int)ftp->cache_size;
276 free(ftp->cache); /* free the cache */
277 ftp->cache = NULL; /* clear the pointer */
278 ftp->cache_size = 0; /* zero the size just in case */
281 int res = Curl_read(conn, sockfd, ptr, BUFSIZE-ftp->nread_resp,
285 return CURLE_OK; /* return */
293 else if(gotbytes <= 0) {
295 result = CURLE_RECV_ERROR;
296 failf(data, "FTP response reading failed");
299 /* we got a whole chunk of data, which can be anything from one
300 * byte to a set of lines and possible just a piece of the last
304 conn->headerbytecount += gotbytes;
306 ftp->nread_resp += gotbytes;
307 for(i = 0; i < gotbytes; ptr++, i++) {
310 /* a newline is CRLF in ftp-talk, so the CR is ignored as
311 the line isn't really terminated until the LF comes */
313 /* output debug output if that is requested */
314 if(data->set.verbose)
315 Curl_debug(data, CURLINFO_HEADER_IN, line_start, perline, conn);
318 * We pass all response-lines to the callback function registered
319 * for "headers". The response lines can be seen as a kind of
322 result = Curl_client_write(data, CLIENTWRITE_HEADER,
323 line_start, perline);
327 #define lastline(line) (isdigit((int)line[0]) && isdigit((int)line[1]) && \
328 isdigit((int)line[2]) && (' ' == line[3]))
330 if(perline>3 && lastline(line_start)) {
331 /* This is the end of the last line, copy the last line to the
332 start of the buffer and zero terminate, for old times sake (and
336 for(meow=line_start, n=0; meow<ptr; meow++, n++)
338 *meow=0; /* zero terminate */
340 line_start = ptr+1; /* advance pointer */
341 i++; /* skip this before getting out */
343 *size = ftp->nread_resp; /* size of the response */
344 ftp->nread_resp = 0; /* restart */
347 perline=0; /* line starts over here */
351 if(!keepon && (i != gotbytes)) {
352 /* We found the end of the response lines, but we didn't parse the
353 full chunk of data we have read from the server. We therefore need
354 to store the rest of the data to be checked on the next invoke as
355 it may actually contain another end of response already! */
356 ftp->cache_size = gotbytes - i;
357 ftp->cache = (char *)malloc((int)ftp->cache_size);
359 memcpy(ftp->cache, line_start, (int)ftp->cache_size);
361 return CURLE_OUT_OF_MEMORY; /**BANG**/
363 } /* there was data */
365 } /* while there's buffer left and loop is requested */
371 /* handle the security-oriented responses 6xx ***/
372 /* FIXME: some errorchecking perhaps... ***/
375 Curl_sec_read_msg(conn, buf, prot_safe);
378 Curl_sec_read_msg(conn, buf, prot_private);
381 Curl_sec_read_msg(conn, buf, prot_confidential);
384 /* normal ftp stuff we pass through! */
389 *ftpcode=code; /* return the initial number like this */
392 /* store the latest code for later retrieval */
393 conn->data->info.httpcode=code;
398 /* --- parse FTP server responses --- */
401 * Curl_GetFTPResponse() is supposed to be invoked after each command sent to
402 * a remote FTP server. This function will wait and read all lines of the
403 * response and extract the relevant return code for the invoking function.
406 CURLcode Curl_GetFTPResponse(ssize_t *nreadp, /* return number of bytes read */
407 struct connectdata *conn,
408 int *ftpcode) /* return the ftp-code */
411 * We cannot read just one byte per read() and then go back to select() as
412 * the OpenSSL read() doesn't grok that properly.
414 * Alas, read as much as possible, split up into lines, use the ending
415 * line in a response or continue reading. */
417 curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
418 int perline; /* count bytes per line */
422 long timeout; /* timeout in seconds */
424 struct SessionHandle *data = conn->data;
426 int code=0; /* default ftp "error code" to return */
427 char *buf = data->state.buffer;
428 CURLcode result = CURLE_OK;
429 struct FTP *ftp = conn->proto.ftp;
430 struct timeval now = Curl_tvnow();
433 *ftpcode = 0; /* 0 for errors */
442 while((*nreadp<BUFSIZE) && (keepon && !result)) {
443 /* check and reset timeout value every lap */
444 if(data->set.ftp_response_timeout )
445 /* if CURLOPT_FTP_RESPONSE_TIMEOUT is set, use that to determine
446 remaining time. Also, use "now" as opposed to "conn->now"
447 because ftp_response_timeout is only supposed to govern
448 the response for any given ftp response, not for the time
449 from connect to the given ftp response. */
450 timeout = data->set.ftp_response_timeout - /* timeout time */
451 Curl_tvdiff(Curl_tvnow(), now)/1000; /* spent time */
452 else if(data->set.timeout)
453 /* if timeout is requested, find out how much remaining time we have */
454 timeout = data->set.timeout - /* timeout time */
455 Curl_tvdiff(Curl_tvnow(), conn->now)/1000; /* spent time */
457 /* Even without a requested timeout, we only wait response_time
458 seconds for the full response to arrive before we bail out */
459 timeout = ftp->response_time -
460 Curl_tvdiff(Curl_tvnow(), now)/1000; /* spent time */
463 failf(data, "FTP response timeout");
464 return CURLE_OPERATION_TIMEDOUT; /* already too little time */
468 interval_ms = 1 * 1000; /* use 1 second timeout intervals */
470 switch (Curl_select(sockfd, CURL_SOCKET_BAD, interval_ms)) {
471 case -1: /* select() error, stop reading */
472 result = CURLE_RECV_ERROR;
473 failf(data, "FTP response aborted due to select() error: %d", errno);
475 case 0: /* timeout */
476 if(Curl_pgrsUpdate(conn))
477 return CURLE_ABORTED_BY_CALLBACK;
478 continue; /* just continue in our loop for the timeout duration */
484 if(CURLE_OK == result) {
486 * This code previously didn't use the kerberos sec_read() code
487 * to read, but when we use Curl_read() it may do so. Do confirm
488 * that this is still ok and then remove this comment!
491 /* we had data in the "cache", copy that instead of doing an actual
494 * Dave Meyer, December 2003:
495 * ftp->cache_size is cast to int here. This should be safe,
496 * because it would have been populated with something of size
497 * int to begin with, even though its datatype may be larger
500 memcpy(ptr, ftp->cache, (int)ftp->cache_size);
501 gotbytes = (int)ftp->cache_size;
502 free(ftp->cache); /* free the cache */
503 ftp->cache = NULL; /* clear the pointer */
504 ftp->cache_size = 0; /* zero the size just in case */
507 int res = Curl_read(conn, sockfd, ptr, BUFSIZE-*nreadp, &gotbytes);
510 continue; /* go looping again */
518 else if(gotbytes <= 0) {
520 result = CURLE_RECV_ERROR;
521 failf(data, "FTP response reading failed");
524 /* we got a whole chunk of data, which can be anything from one
525 * byte to a set of lines and possible just a piece of the last
529 conn->headerbytecount += gotbytes;
532 for(i = 0; i < gotbytes; ptr++, i++) {
535 /* a newline is CRLF in ftp-talk, so the CR is ignored as
536 the line isn't really terminated until the LF comes */
538 /* output debug output if that is requested */
539 if(data->set.verbose)
540 Curl_debug(data, CURLINFO_HEADER_IN, line_start, perline, conn);
543 * We pass all response-lines to the callback function registered
544 * for "headers". The response lines can be seen as a kind of
547 result = Curl_client_write(data, CLIENTWRITE_HEADER,
548 line_start, perline);
552 #define lastline(line) (isdigit((int)line[0]) && isdigit((int)line[1]) && \
553 isdigit((int)line[2]) && (' ' == line[3]))
555 if(perline>3 && lastline(line_start)) {
556 /* This is the end of the last line, copy the last
557 * line to the start of the buffer and zero terminate,
558 * for old times sake (and krb4)! */
561 for(meow=line_start, n=0; meow<ptr; meow++, n++)
563 *meow=0; /* zero terminate */
565 line_start = ptr+1; /* advance pointer */
566 i++; /* skip this before getting out */
569 perline=0; /* line starts over here */
573 if(!keepon && (i != gotbytes)) {
574 /* We found the end of the response lines, but we didn't parse the
575 full chunk of data we have read from the server. We therefore
576 need to store the rest of the data to be checked on the next
577 invoke as it may actually contain another end of response
578 already! Cleverly figured out by Eric Lavigne in December
580 ftp->cache_size = gotbytes - i;
581 ftp->cache = (char *)malloc((int)ftp->cache_size);
583 memcpy(ftp->cache, line_start, (int)ftp->cache_size);
585 return CURLE_OUT_OF_MEMORY; /**BANG**/
587 } /* there was data */
589 } /* while there's buffer left and loop is requested */
595 /* handle the security-oriented responses 6xx ***/
596 /* FIXME: some errorchecking perhaps... ***/
599 Curl_sec_read_msg(conn, buf, prot_safe);
602 Curl_sec_read_msg(conn, buf, prot_private);
605 Curl_sec_read_msg(conn, buf, prot_confidential);
608 /* normal ftp stuff we pass through! */
614 *ftpcode=code; /* return the initial number like this */
616 /* store the latest code for later retrieval */
617 conn->data->info.httpcode=code;
622 /* This is the ONLY way to change FTP state! */
623 static void state(struct connectdata *conn,
627 /* for debug purposes */
628 const char *names[]={
662 struct FTP *ftp = conn->proto.ftp;
664 if(ftp->state != state)
665 infof(conn->data, "FTP %p state change from %s to %s\n",
666 ftp, names[ftp->state], names[state]);
671 static CURLcode ftp_state_user(struct connectdata *conn)
674 struct FTP *ftp = conn->proto.ftp;
676 NBFTPSENDF(conn, "USER %s", ftp->user?ftp->user:"");
678 state(conn, FTP_USER);
683 static CURLcode ftp_state_pwd(struct connectdata *conn)
687 /* send PWD to discover our entry point */
688 NBFTPSENDF(conn, "PWD", NULL);
689 state(conn, FTP_PWD);
694 /* For the FTP "protocol connect" and "doing" phases only */
695 CURLcode Curl_ftp_fdset(struct connectdata *conn,
697 fd_set *write_fd_set,
700 struct FTP *ftp = conn->proto.ftp;
701 curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
705 FD_SET(sockfd, write_fd_set);
709 FD_SET(sockfd, read_fd_set);
712 if((int)sockfd > *max_fdp)
713 *max_fdp = (int)sockfd;
718 /* This is called after the FTP_QUOTE state is passed.
720 ftp_state_cwd() sends the range of PWD commands to the server to change to
721 the correct directory. It may also need to send MKD commands to create
722 missing ones, if that option is enabled.
724 static CURLcode ftp_state_cwd(struct connectdata *conn)
726 CURLcode result = CURLE_OK;
727 struct FTP *ftp = conn->proto.ftp;
730 /* already done and fine */
731 result = ftp_state_post_cwd(conn);
734 if (conn->bits.reuse && ftp->entrypath) {
735 /* This is a re-used connection. Since we change directory to where the
736 transfer is taking place, we must first get back to the original dir
737 where we ended up after login: */
738 ftp->count1 = 0; /* we count this as the first path, then we add one
739 for all upcoming ones in the ftp->dirs[] array */
740 NBFTPSENDF(conn, "CWD %s", ftp->entrypath);
741 state(conn, FTP_CWD);
746 /* issue the first CWD, the rest is sent when the CWD responses are
748 NBFTPSENDF(conn, "CWD %s", ftp->dirs[ftp->count1 -1]);
749 state(conn, FTP_CWD);
752 /* No CWD necessary */
753 result = ftp_state_post_cwd(conn);
760 typedef enum { EPRT, LPRT, PORT, DONE } ftpport;
762 static CURLcode ftp_state_use_port(struct connectdata *conn,
763 ftpport fcmd) /* start with this */
766 CURLcode result = CURLE_OK;
767 struct FTP *ftp = conn->proto.ftp;
768 struct SessionHandle *data=conn->data;
769 curl_socket_t portsock= CURL_SOCKET_BAD;
772 /******************************************************************
773 * IPv6-specific section
776 struct addrinfo *res, *ai;
777 struct sockaddr_storage ss;
779 char hbuf[NI_MAXHOST];
780 struct sockaddr *sa=(struct sockaddr *)&ss;
783 char portmsgbuf[1024], tmp[1024];
784 const char *mode[] = { "EPRT", "LPRT", "PORT", NULL };
788 struct Curl_dns_entry *h=NULL;
790 if(data->set.ftpport && (strlen(data->set.ftpport) > 1)) {
791 /* attempt to get the address of the given interface name */
792 if(!Curl_if2ip(data->set.ftpport, hbuf, sizeof(hbuf)))
793 /* not an interface, use the given string as host name instead */
794 host = data->set.ftpport;
796 host = hbuf; /* use the hbuf for host name */
797 } /* data->set.ftpport */
800 /* not an interface and not a host name, get default by extracting
801 the IP from the control connection */
804 rc = getsockname(conn->sock[FIRSTSOCKET], (struct sockaddr *)&ss, &sslen);
806 failf(data, "getsockname() returned %d\n", rc);
807 return CURLE_FTP_PORT_FAILED;
810 rc = getnameinfo((struct sockaddr *)&ss, sslen, hbuf, sizeof(hbuf), NULL,
813 failf(data, "getnameinfo() returned %d\n", rc);
814 return CURLE_FTP_PORT_FAILED;
816 host = hbuf; /* use this host name */
819 rc = Curl_resolv(conn, host, 0, &h);
820 if(rc == CURLRESOLV_PENDING)
821 rc = Curl_wait_for_resolv(conn, &h);
824 /* when we return from this function, we can forget about this entry
825 to we can unlock it now already */
826 Curl_resolv_unlock(data, h);
829 res = NULL; /* failure! */
831 portsock = CURL_SOCKET_BAD;
833 for (ai = res; ai; ai = ai->ai_next) {
835 * Workaround for AIX5 getaddrinfo() problem (it doesn't set ai_socktype):
837 if (ai->ai_socktype == 0)
838 ai->ai_socktype = SOCK_STREAM;
840 portsock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
841 if (portsock == CURL_SOCKET_BAD) {
842 error = Curl_ourerrno();
846 if (bind(portsock, ai->ai_addr, ai->ai_addrlen) < 0) {
847 error = Curl_ourerrno();
849 portsock = CURL_SOCKET_BAD;
853 if (listen(portsock, 1) < 0) {
854 error = Curl_ourerrno();
856 portsock = CURL_SOCKET_BAD;
863 if (portsock == CURL_SOCKET_BAD) {
864 failf(data, "%s", Curl_strerror(conn,error));
865 return CURLE_FTP_PORT_FAILED;
869 if (getsockname(portsock, sa, &sslen) < 0) {
870 failf(data, "%s", Curl_strerror(conn,Curl_ourerrno()));
871 return CURLE_FTP_PORT_FAILED;
875 if(!conn->bits.ftp_use_eprt && conn->bits.ipv6)
876 /* EPRT is disabled but we are connected to a IPv6 host, so we ignore the
877 request and enable EPRT again! */
878 conn->bits.ftp_use_eprt = TRUE;
881 for (; fcmd != DONE; fcmd++) {
885 if(!conn->bits.ftp_use_eprt && (EPRT == fcmd))
886 /* if disabled, goto next */
889 if(!conn->bits.ftp_use_lprt && (LPRT == fcmd))
890 /* if disabled, goto next */
893 switch (sa->sa_family) {
895 ap = (unsigned char *)&((struct sockaddr_in *)&ss)->sin_addr;
896 alen = sizeof(((struct sockaddr_in *)&ss)->sin_addr);
897 pp = (unsigned char *)&((struct sockaddr_in *)&ss)->sin_port;
898 plen = sizeof(((struct sockaddr_in *)&ss)->sin_port);
903 ap = (unsigned char *)&((struct sockaddr_in6 *)&ss)->sin6_addr;
904 alen = sizeof(((struct sockaddr_in6 *)&ss)->sin6_addr);
905 pp = (unsigned char *)&((struct sockaddr_in6 *)&ss)->sin6_port;
906 plen = sizeof(((struct sockaddr_in6 *)&ss)->sin6_port);
912 lprtaf = eprtaf = -1;
919 if (getnameinfo((struct sockaddr *)&ss, sslen,
920 portmsgbuf, sizeof(portmsgbuf), tmp, sizeof(tmp),
924 /* do not transmit IPv6 scope identifier to the wire */
925 if (sa->sa_family == AF_INET6) {
926 char *q = strchr(portmsgbuf, '%');
931 result = Curl_nbftpsendf(conn, "%s |%d|%s|%s|", mode[fcmd], eprtaf,
937 else if ((LPRT == fcmd) || (PORT == fcmd)) {
940 if ((LPRT == fcmd) && lprtaf < 0)
942 if ((PORT == fcmd) && sa->sa_family != AF_INET)
945 portmsgbuf[0] = '\0';
947 snprintf(tmp, sizeof(tmp), "%d,%d", lprtaf, alen);
948 if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >=
949 sizeof(portmsgbuf)) {
954 for (i = 0; i < alen; i++) {
956 snprintf(tmp, sizeof(tmp), ",%u", ap[i]);
958 snprintf(tmp, sizeof(tmp), "%u", ap[i]);
960 if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >=
961 sizeof(portmsgbuf)) {
967 snprintf(tmp, sizeof(tmp), ",%d", plen);
969 if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >= sizeof(portmsgbuf))
973 for (i = 0; i < plen; i++) {
974 snprintf(tmp, sizeof(tmp), ",%u", pp[i]);
976 if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >=
977 sizeof(portmsgbuf)) {
982 result = Curl_nbftpsendf(conn, "%s %s", mode[fcmd], portmsgbuf);
989 /* store which command was sent */
992 /* we set the secondary socket variable to this for now, it is only so that
993 the cleanup function will close it in case we fail before the true
994 secondary stuff is made */
995 if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET])
996 sclose(conn->sock[SECONDARYSOCKET]);
997 conn->sock[SECONDARYSOCKET] = portsock;
1000 /******************************************************************
1001 * IPv4-specific section
1003 struct sockaddr_in sa;
1004 unsigned short porttouse;
1005 char myhost[256] = "";
1006 bool sa_filled_in = FALSE;
1007 Curl_addrinfo *addr = NULL;
1008 unsigned short ip[4];
1009 (void)fcmd; /* not used in the IPv4 code */
1010 if(data->set.ftpport) {
1013 /* First check if the given name is an IP address */
1014 in=inet_addr(data->set.ftpport);
1016 if(in != CURL_INADDR_NONE)
1017 /* this is an IPv4 address */
1018 addr = Curl_ip2addr(in, data->set.ftpport, 0);
1020 if(Curl_if2ip(data->set.ftpport, myhost, sizeof(myhost))) {
1021 /* The interface to IP conversion provided a dotted address */
1022 in=inet_addr(myhost);
1023 addr = Curl_ip2addr(in, myhost, 0);
1025 else if(strlen(data->set.ftpport)> 1) {
1026 /* might be a host name! */
1027 struct Curl_dns_entry *h=NULL;
1028 int rc = Curl_resolv(conn, myhost, 0, &h);
1029 if(rc == CURLRESOLV_PENDING)
1031 rc = Curl_wait_for_resolv(conn, &h);
1034 /* when we return from this function, we can forget about this entry
1035 so we can unlock it now already */
1036 Curl_resolv_unlock(data, h);
1039 } /* CURL_INADDR_NONE */
1040 } /* data->set.ftpport */
1043 /* pick a suitable default here */
1048 if (getsockname(conn->sock[FIRSTSOCKET],
1049 (struct sockaddr *)&sa, &sslen) < 0) {
1050 failf(data, "getsockname() failed");
1051 return CURLE_FTP_PORT_FAILED;
1054 sa_filled_in = TRUE; /* the sa struct is filled in */
1057 if (addr || sa_filled_in) {
1058 portsock = socket(AF_INET, SOCK_STREAM, 0);
1059 if(CURL_SOCKET_BAD != portsock) {
1062 /* we set the secondary socket variable to this for now, it
1063 is only so that the cleanup function will close it in case
1064 we fail before the true secondary stuff is made */
1065 if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET])
1066 sclose(conn->sock[SECONDARYSOCKET]);
1067 conn->sock[SECONDARYSOCKET] = portsock;
1070 memcpy(&sa, addr->ai_addr, sizeof(sa));
1071 sa.sin_addr.s_addr = INADDR_ANY;
1077 if(bind(portsock, (struct sockaddr *)&sa, size) >= 0) {
1078 /* we succeeded to bind */
1079 struct sockaddr_in add;
1080 socklen_t socksize = sizeof(add);
1082 if(getsockname(portsock, (struct sockaddr *) &add,
1084 failf(data, "getsockname() failed");
1085 return CURLE_FTP_PORT_FAILED;
1087 porttouse = ntohs(add.sin_port);
1089 if ( listen(portsock, 1) < 0 ) {
1090 failf(data, "listen(2) failed on socket");
1091 return CURLE_FTP_PORT_FAILED;
1095 failf(data, "bind(2) failed on socket");
1096 return CURLE_FTP_PORT_FAILED;
1100 failf(data, "socket(2) failed (%s)");
1101 return CURLE_FTP_PORT_FAILED;
1105 failf(data, "could't find IP address to use");
1106 return CURLE_FTP_PORT_FAILED;
1110 Curl_inet_ntop(AF_INET, &((struct sockaddr_in *)&sa)->sin_addr,
1111 myhost, sizeof(myhost));
1113 Curl_printable_address(addr, myhost, sizeof(myhost));
1115 if(4 == sscanf(myhost, "%hu.%hu.%hu.%hu",
1116 &ip[0], &ip[1], &ip[2], &ip[3])) {
1118 infof(data, "Telling server to connect to %d.%d.%d.%d:%d\n",
1119 ip[0], ip[1], ip[2], ip[3], porttouse);
1121 result=Curl_nbftpsendf(conn, "PORT %d,%d,%d,%d,%d,%d",
1122 ip[0], ip[1], ip[2], ip[3],
1123 porttouse >> 8, porttouse & 255);
1128 return CURLE_FTP_PORT_FAILED;
1130 Curl_freeaddrinfo(addr);
1134 #endif /* end of ipv4-specific code */
1136 state(conn, FTP_PORT);
1140 static CURLcode ftp_state_use_pasv(struct connectdata *conn)
1142 struct FTP *ftp = conn->proto.ftp;
1143 CURLcode result = CURLE_OK;
1145 Here's the excecutive summary on what to do:
1147 PASV is RFC959, expect:
1148 227 Entering Passive Mode (a1,a2,a3,a4,p1,p2)
1150 LPSV is RFC1639, expect:
1151 228 Entering Long Passive Mode (4,4,a1,a2,a3,a4,2,p1,p2)
1153 EPSV is RFC2428, expect:
1154 229 Entering Extended Passive Mode (|||port|)
1158 const char *mode[] = { "EPSV", "PASV", NULL };
1162 if(!conn->bits.ftp_use_epsv && conn->bits.ipv6)
1163 /* EPSV is disabled but we are connected to a IPv6 host, so we ignore the
1164 request and enable EPSV again! */
1165 conn->bits.ftp_use_epsv = TRUE;
1168 modeoff = conn->bits.ftp_use_epsv?0:1;
1170 result = Curl_nbftpsendf(conn, "%s", mode[modeoff]);
1174 ftp->count1 = modeoff;
1175 state(conn, FTP_PASV);
1176 infof(conn->data, "Connect data stream passively\n");
1181 /* REST is the last command in the chain of commands when a "head"-like
1182 request is made. Thus, if an actual transfer is to be made this is where
1183 we take off for real. */
1184 static CURLcode ftp_state_post_rest(struct connectdata *conn)
1186 CURLcode result = CURLE_OK;
1187 struct FTP *ftp = conn->proto.ftp;
1188 struct SessionHandle *data = conn->data;
1190 if(ftp->no_transfer || conn->bits.no_body) {
1191 /* then we're done with a "head"-like request, goto STOP */
1192 state(conn, FTP_STOP);
1194 /* doesn't transfer any data */
1195 ftp->no_transfer = TRUE;
1197 else if(data->set.ftp_use_port) {
1198 /* We have chosen to use the PORT (or similar) command */
1199 result = ftp_state_use_port(conn, EPRT);
1202 /* We have chosen (this is default) to use the PASV (or similar) command */
1203 result = ftp_state_use_pasv(conn);
1208 static CURLcode ftp_state_post_size(struct connectdata *conn)
1210 CURLcode result = CURLE_OK;
1211 struct FTP *ftp = conn->proto.ftp;
1213 if(ftp->no_transfer) {
1214 /* if a "head"-like request is being made */
1216 /* Determine if server can respond to REST command and therefore
1217 whether it supports range */
1218 NBFTPSENDF(conn, "REST %d", 0);
1220 state(conn, FTP_REST);
1223 result = ftp_state_post_rest(conn);
1228 static CURLcode ftp_state_post_type(struct connectdata *conn)
1230 CURLcode result = CURLE_OK;
1231 struct FTP *ftp = conn->proto.ftp;
1233 if(ftp->no_transfer) {
1234 /* if a "head"-like request is being made */
1236 /* we know ftp->file is a valid pointer to a file name */
1237 NBFTPSENDF(conn, "SIZE %s", ftp->file);
1239 state(conn, FTP_SIZE);
1242 result = ftp_state_post_size(conn);
1247 static CURLcode ftp_state_post_listtype(struct connectdata *conn)
1249 CURLcode result = CURLE_OK;
1250 struct SessionHandle *data = conn->data;
1252 /* If this output is to be machine-parsed, the NLST command might be better
1253 to use, since the LIST command output is not specified or standard in any
1254 way. It has turned out that the NLST list output is not the same on all
1255 servers either... */
1257 NBFTPSENDF(conn, "%s",
1258 data->set.customrequest?data->set.customrequest:
1259 (data->set.ftp_list_only?"NLST":"LIST"));
1261 state(conn, FTP_LIST);
1266 static CURLcode ftp_state_post_retrtype(struct connectdata *conn)
1268 CURLcode result = CURLE_OK;
1270 /* We've sent the TYPE, now we must send the list of prequote strings */
1272 result = ftp_state_quote(conn, TRUE, FTP_RETR_PREQUOTE);
1277 static CURLcode ftp_state_post_stortype(struct connectdata *conn)
1279 CURLcode result = CURLE_OK;
1281 /* We've sent the TYPE, now we must send the list of prequote strings */
1283 result = ftp_state_quote(conn, TRUE, FTP_STOR_PREQUOTE);
1288 static CURLcode ftp_state_post_mdtm(struct connectdata *conn)
1290 CURLcode result = CURLE_OK;
1291 struct FTP *ftp = conn->proto.ftp;
1292 struct SessionHandle *data = conn->data;
1294 /* If we have selected NOBODY and HEADER, it means that we only want file
1295 information. Which in FTP can't be much more than the file size and
1297 if(conn->bits.no_body && data->set.include_header && ftp->file) {
1298 /* The SIZE command is _not_ RFC 959 specified, and therefor many servers
1299 may not support it! It is however the only way we have to get a file's
1302 ftp->no_transfer = TRUE; /* this means no actual transfer will be made */
1304 /* Some servers return different sizes for different modes, and thus we
1305 must set the proper type before we check the size */
1306 NBFTPSENDF(conn, "TYPE %c",
1307 data->set.ftp_ascii?'A':'I');
1308 state(conn, FTP_TYPE);
1311 result = ftp_state_post_type(conn);
1316 /* This is called after the CWD commands have been done in the beginning of
1318 static CURLcode ftp_state_post_cwd(struct connectdata *conn)
1320 CURLcode result = CURLE_OK;
1321 struct FTP *ftp = conn->proto.ftp;
1322 struct SessionHandle *data = conn->data;
1324 /* Requested time of file or time-depended transfer? */
1325 if((data->set.get_filetime || data->set.timecondition) && ftp->file) {
1327 /* we have requested to get the modified-time of the file, this is a white
1328 spot as the MDTM is not mentioned in RFC959 */
1329 NBFTPSENDF(conn, "MDTM %s", ftp->file);
1331 state(conn, FTP_MDTM);
1334 result = ftp_state_post_mdtm(conn);
1340 /* This is called after the TYPE and possible quote commands have been sent */
1341 static CURLcode ftp_state_ul_setup(struct connectdata *conn,
1344 CURLcode result = CURLE_OK;
1345 struct FTP *ftp = conn->proto.ftp;
1346 struct SessionHandle *data = conn->data;
1347 curl_off_t passed=0;
1349 if((conn->resume_from && !sizechecked) ||
1350 ((conn->resume_from > 0) && sizechecked)) {
1351 /* we're about to continue the uploading of a file */
1352 /* 1. get already existing file's size. We use the SIZE command for this
1353 which may not exist in the server! The SIZE command is not in
1356 /* 2. This used to set REST. But since we can do append, we
1357 don't another ftp command. We just skip the source file
1358 offset and then we APPEND the rest on the file instead */
1360 /* 3. pass file-size number of bytes in the source file */
1361 /* 4. lower the infilesize counter */
1362 /* => transfer as usual */
1364 if(conn->resume_from < 0 ) {
1365 /* Got no given size to start from, figure it out */
1366 NBFTPSENDF(conn, "SIZE %s", ftp->file);
1367 state(conn, FTP_STOR_SIZE);
1372 data->set.ftp_append = TRUE;
1374 /* Let's read off the proper amount of bytes from the input. If we knew it
1375 was a proper file we could've just fseek()ed but we only have a stream
1378 /* TODO: allow the ioctlfunction to provide a fast forward function that
1379 can be used here and use this method only as a fallback! */
1381 curl_off_t readthisamountnow = (conn->resume_from - passed);
1382 curl_off_t actuallyread;
1384 if(readthisamountnow > BUFSIZE)
1385 readthisamountnow = BUFSIZE;
1387 actuallyread = (curl_off_t)
1388 conn->fread(data->state.buffer, 1, (size_t)readthisamountnow,
1391 passed += actuallyread;
1392 if(actuallyread != readthisamountnow) {
1393 failf(data, "Could only read %" FORMAT_OFF_T
1394 " bytes from the input", passed);
1395 return CURLE_FTP_COULDNT_USE_REST;
1397 } while(passed != conn->resume_from);
1399 /* now, decrease the size of the read */
1400 if(data->set.infilesize>0) {
1401 data->set.infilesize -= conn->resume_from;
1403 if(data->set.infilesize <= 0) {
1404 infof(data, "File already completely uploaded\n");
1406 /* no data to transfer */
1407 result=Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1409 /* Set no_transfer so that we won't get any error in
1410 * Curl_ftp_done() because we didn't transfer anything! */
1411 ftp->no_transfer = TRUE;
1413 state(conn, FTP_STOP);
1417 /* we've passed, proceed as normal */
1420 NBFTPSENDF(conn, data->set.ftp_append?"APPE %s":"STOR %s",
1423 state(conn, FTP_STOR);
1428 static CURLcode ftp_state_quote(struct connectdata *conn,
1432 CURLcode result = CURLE_OK;
1433 struct FTP *ftp = conn->proto.ftp;
1434 struct SessionHandle *data = conn->data;
1436 struct curl_slist *item;
1441 item = data->set.quote;
1443 case FTP_RETR_PREQUOTE:
1444 case FTP_STOR_PREQUOTE:
1445 item = data->set.prequote;
1448 item = data->set.postquote;
1460 /* Skip count1 items in the linked list */
1461 while((i< ftp->count1) && item) {
1466 NBFTPSENDF(conn, "%s", item->data);
1467 state(conn, instate);
1473 /* No more quote to send, continue to ... */
1477 result = ftp_state_cwd(conn);
1479 case FTP_RETR_PREQUOTE:
1480 NBFTPSENDF(conn, "SIZE %s", ftp->file);
1481 state(conn, FTP_RETR_SIZE);
1483 case FTP_STOR_PREQUOTE:
1484 result = ftp_state_ul_setup(conn, FALSE);
1494 static CURLcode ftp_state_pasv_resp(struct connectdata *conn,
1497 struct FTP *ftp = conn->proto.ftp;
1499 struct SessionHandle *data=conn->data;
1500 Curl_addrinfo *conninfo;
1501 struct Curl_dns_entry *addr=NULL;
1503 unsigned short connectport; /* the local port connect() should use! */
1504 unsigned short newport=0; /* remote port */
1507 /* newhost must be able to hold a full IP-style address in ASCII, which
1508 in the IPv6 case means 5*8-1 = 39 letters */
1509 #define NEWHOST_BUFSIZE 48
1510 char newhost[NEWHOST_BUFSIZE];
1511 char *str=&data->state.buffer[4]; /* start on the first letter */
1513 if((ftp->count1 == 0) &&
1515 /* positive EPSV response */
1516 char *ptr = strchr(str, '(');
1521 if(5 == sscanf(ptr, "%c%c%c%u%c",
1527 const 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 /* use the same IP we are already connected to */
1542 snprintf(newhost, NEWHOST_BUFSIZE, "%s", conn->ip_addr_str);
1549 failf(data, "Weirdly formatted EPSV reply");
1550 return CURLE_FTP_WEIRD_PASV_REPLY;
1553 else if((ftp->count1 == 1) &&
1555 /* positive PASV response */
1560 * Scan for a sequence of six comma-separated numbers and use them as
1561 * IP+port indicators.
1563 * Found reply-strings include:
1564 * "227 Entering Passive Mode (127,0,0,1,4,51)"
1565 * "227 Data transfer will passively listen to 127,0,0,1,4,51"
1566 * "227 Entering passive mode. 127,0,0,1,4,51"
1569 if (6 == sscanf(str, "%d,%d,%d,%d,%d,%d",
1570 &ip[0], &ip[1], &ip[2], &ip[3],
1571 &port[0], &port[1]))
1577 failf(data, "Couldn't interpret the 227-response");
1578 return CURLE_FTP_WEIRD_227_FORMAT;
1581 snprintf(newhost, sizeof(newhost),
1582 "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
1583 newport = (port[0]<<8) + port[1];
1585 else if(ftp->count1 == 0) {
1586 /* EPSV failed, move on to PASV */
1588 /* disable it for next transfer */
1589 conn->bits.ftp_use_epsv = FALSE;
1590 infof(data, "disabling EPSV usage\n");
1592 NBFTPSENDF(conn, "PASV", NULL);
1594 /* remain in the FTP_PASV state */
1598 failf(data, "Bad PASV/EPSV response: %03d", ftpcode);
1599 return CURLE_FTP_WEIRD_PASV_REPLY;
1602 /* we got OK from server */
1604 if(data->change.proxy && *data->change.proxy) {
1606 * This is a tunnel through a http proxy and we need to connect to the
1609 * We don't want to rely on a former host lookup that might've expired
1610 * now, instead we remake the lookup here and now!
1612 rc = Curl_resolv(conn, conn->proxy.name, (int)conn->port, &addr);
1613 if(rc == CURLRESOLV_PENDING)
1615 rc = Curl_wait_for_resolv(conn, &addr);
1618 (unsigned short)conn->port; /* we connect to the proxy's port */
1622 /* normal, direct, ftp connection */
1623 rc = Curl_resolv(conn, newhost, newport, &addr);
1624 if(rc == CURLRESOLV_PENDING)
1626 rc = Curl_wait_for_resolv(conn, &addr);
1629 failf(data, "Can't resolve new host %s:%d", newhost, newport);
1630 return CURLE_FTP_CANT_GET_HOST;
1632 connectport = newport; /* we connect to the remote port */
1635 result = Curl_connecthost(conn,
1637 &conn->sock[SECONDARYSOCKET],
1641 Curl_resolv_unlock(data, addr); /* we're done using this address */
1646 conn->bits.tcpconnect = connected; /* simply TRUE or FALSE */
1649 * When this is used from the multi interface, this might've returned with
1650 * the 'connected' set to FALSE and thus we are now awaiting a non-blocking
1651 * connect to connect and we should not be "hanging" here waiting.
1654 if(data->set.verbose)
1655 /* this just dumps information about this second connection */
1656 ftp_pasv_verbose(conn, conninfo, newhost, connectport);
1658 #ifndef CURL_DISABLE_HTTP
1659 if(conn->bits.tunnel_proxy) {
1660 /* FIX: this MUST wait for a proper connect first if 'connected' is
1664 /* We want "seamless" FTP operations through HTTP proxy tunnel */
1665 result = Curl_ConnectHTTPProxyTunnel(conn, SECONDARYSOCKET,
1667 if(CURLE_OK != result)
1670 #endif /* CURL_DISABLE_HTTP */
1672 state(conn, FTP_STOP); /* this phase is completed */
1677 static CURLcode ftp_state_port_resp(struct connectdata *conn,
1680 struct FTP *ftp = conn->proto.ftp;
1681 struct SessionHandle *data = conn->data;
1682 ftpport fcmd = (ftpport)ftp->count1;
1683 CURLcode result = CURLE_OK;
1685 if(ftpcode != 200) {
1686 /* the command failed */
1689 infof(data, "disabling EPRT usage\n");
1690 conn->bits.ftp_use_eprt = FALSE;
1692 else if (LPRT == fcmd) {
1693 infof(data, "disabling LPRT usage\n");
1694 conn->bits.ftp_use_lprt = FALSE;
1699 failf(data, "Failed to do PORT");
1700 result = CURLE_FTP_PORT_FAILED;
1704 result = ftp_state_use_port(conn, fcmd);
1707 infof(data, "Connect data stream actively\n");
1708 state(conn, FTP_STOP); /* end of DO phase */
1714 static CURLcode ftp_state_mdtm_resp(struct connectdata *conn,
1717 CURLcode result = CURLE_OK;
1718 struct FTP *ftp = conn->proto.ftp;
1719 struct SessionHandle *data=conn->data;
1724 /* we got a time. Format should be: "YYYYMMDDHHMMSS[.sss]" where the
1725 last .sss part is optional and means fractions of a second */
1726 int year, month, day, hour, minute, second;
1727 char *buf = data->state.buffer;
1728 if(6 == sscanf(buf+4, "%04d%02d%02d%02d%02d%02d",
1729 &year, &month, &day, &hour, &minute, &second)) {
1730 /* we have a time, reformat it */
1731 time_t secs=time(NULL);
1732 /* using the good old yacc/bison yuck */
1733 snprintf(buf, sizeof(conn->data->state.buffer),
1734 "%04d%02d%02d %02d:%02d:%02d GMT",
1735 year, month, day, hour, minute, second);
1736 /* now, convert this into a time() value: */
1737 data->info.filetime = curl_getdate(buf, &secs);
1740 /* If we asked for a time of the file and we actually got one as well,
1741 we "emulate" a HTTP-style header in our output. */
1743 if(conn->bits.no_body &&
1744 data->set.include_header &&
1746 data->set.get_filetime &&
1747 (data->info.filetime>=0) ) {
1749 time_t clock = (time_t)data->info.filetime;
1750 #ifdef HAVE_GMTIME_R
1752 tm = (struct tm *)gmtime_r(&clock, &buffer);
1754 tm = gmtime(&clock);
1756 /* format: "Tue, 15 Nov 1994 12:45:26" */
1757 snprintf(buf, BUFSIZE-1,
1758 "Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n",
1759 Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
1761 Curl_month[tm->tm_mon],
1766 result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0);
1769 } /* end of a ridiculous amount of conditionals */
1773 infof(data, "unsupported MDTM reply format\n");
1775 case 550: /* "No such file or directory" */
1776 failf(data, "Given file does not exist");
1777 result = CURLE_FTP_COULDNT_RETR_FILE;
1781 if(data->set.timecondition) {
1782 if((data->info.filetime > 0) && (data->set.timevalue > 0)) {
1783 switch(data->set.timecondition) {
1784 case CURL_TIMECOND_IFMODSINCE:
1786 if(data->info.filetime < data->set.timevalue) {
1787 infof(data, "The requested document is not new enough\n");
1788 ftp->no_transfer = TRUE; /* mark this to not transfer data */
1789 state(conn, FTP_STOP);
1793 case CURL_TIMECOND_IFUNMODSINCE:
1794 if(data->info.filetime > data->set.timevalue) {
1795 infof(data, "The requested document is not old enough\n");
1796 ftp->no_transfer = TRUE; /* mark this to not transfer data */
1797 state(conn, FTP_STOP);
1804 infof(data, "Skipping time comparison\n");
1809 result = ftp_state_post_mdtm(conn);
1814 static CURLcode ftp_state_type_resp(struct connectdata *conn,
1818 CURLcode result = CURLE_OK;
1819 struct SessionHandle *data=conn->data;
1821 if(ftpcode != 200) {
1822 failf(data, "Couldn't set desired mode");
1823 return CURLE_FTP_COULDNT_SET_BINARY; /* FIX */
1825 if(instate == FTP_TYPE)
1826 result = ftp_state_post_type(conn);
1827 else if(instate == FTP_LIST_TYPE)
1828 result = ftp_state_post_listtype(conn);
1829 else if(instate == FTP_RETR_TYPE)
1830 result = ftp_state_post_retrtype(conn);
1831 else if(instate == FTP_STOR_TYPE)
1832 result = ftp_state_post_stortype(conn);
1837 static CURLcode ftp_state_post_retr_size(struct connectdata *conn,
1838 curl_off_t filesize)
1840 CURLcode result = CURLE_OK;
1841 struct SessionHandle *data=conn->data;
1842 struct FTP *ftp = conn->proto.ftp;
1844 if (data->set.max_filesize && (filesize > data->set.max_filesize)) {
1845 failf(data, "Maximum file size exceeded");
1846 return CURLE_FILESIZE_EXCEEDED;
1848 ftp->downloadsize = filesize;
1850 if(conn->resume_from) {
1851 /* We always (attempt to) get the size of downloads, so it is done before
1852 this even when not doing resumes. */
1853 if(filesize == -1) {
1854 infof(data, "ftp server doesn't support SIZE\n");
1855 /* We couldn't get the size and therefore we can't know if there really
1856 is a part of the file left to get, although the server will just
1857 close the connection when we start the connection so it won't cause
1858 us any harm, just not make us exit as nicely. */
1861 /* We got a file size report, so we check that there actually is a
1862 part of the file left to get, or else we go home. */
1863 if(conn->resume_from< 0) {
1864 /* We're supposed to download the last abs(from) bytes */
1865 if(filesize < -conn->resume_from) {
1866 failf(data, "Offset (%" FORMAT_OFF_T
1867 ") was beyond file size (%" FORMAT_OFF_T ")",
1868 conn->resume_from, filesize);
1869 return CURLE_BAD_DOWNLOAD_RESUME;
1871 /* convert to size to download */
1872 ftp->downloadsize = -conn->resume_from;
1873 /* download from where? */
1874 conn->resume_from = filesize - ftp->downloadsize;
1877 if(filesize < conn->resume_from) {
1878 failf(data, "Offset (%" FORMAT_OFF_T
1879 ") was beyond file size (%" FORMAT_OFF_T ")",
1880 conn->resume_from, filesize);
1881 return CURLE_BAD_DOWNLOAD_RESUME;
1883 /* Now store the number of bytes we are expected to download */
1884 ftp->downloadsize = filesize-conn->resume_from;
1888 if(ftp->downloadsize == 0) {
1889 /* no data to transfer */
1890 result=Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1891 infof(data, "File already completely downloaded\n");
1893 /* Set no_transfer so that we won't get any error in Curl_ftp_done()
1894 * because we didn't transfer the any file */
1895 ftp->no_transfer = TRUE;
1896 state(conn, FTP_STOP);
1900 /* Set resume file transfer offset */
1901 infof(data, "Instructs server to resume from offset %" FORMAT_OFF_T
1902 "\n", conn->resume_from);
1904 NBFTPSENDF(conn, "REST %" FORMAT_OFF_T, conn->resume_from);
1906 state(conn, FTP_RETR_REST);
1911 NBFTPSENDF(conn, "RETR %s", ftp->file);
1912 state(conn, FTP_RETR);
1918 static CURLcode ftp_state_size_resp(struct connectdata *conn,
1922 CURLcode result = CURLE_OK;
1923 struct SessionHandle *data=conn->data;
1924 curl_off_t filesize;
1925 char *buf = data->state.buffer;
1927 /* get the size from the ascii string: */
1928 filesize = (ftpcode == 213)?curlx_strtoofft(buf+4, NULL, 0):-1;
1930 if(instate == FTP_SIZE) {
1931 if(-1 != filesize) {
1932 snprintf(buf, sizeof(data->state.buffer),
1933 "Content-Length: %" FORMAT_OFF_T "\r\n", filesize);
1934 result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0);
1938 result = ftp_state_post_size(conn);
1940 else if(instate == FTP_RETR_SIZE)
1941 result = ftp_state_post_retr_size(conn, filesize);
1942 else if(instate == FTP_STOR_SIZE) {
1943 conn->resume_from = filesize;
1944 result = ftp_state_ul_setup(conn, TRUE);
1950 static CURLcode ftp_state_rest_resp(struct connectdata *conn,
1954 CURLcode result = CURLE_OK;
1955 struct FTP *ftp = conn->proto.ftp;
1960 if (ftpcode == 350) {
1961 result = Curl_client_write(conn->data, CLIENTWRITE_BOTH,
1962 (char *)"Accept-ranges: bytes\r\n", 0);
1967 result = ftp_state_post_rest(conn);
1971 if (ftpcode != 350) {
1972 failf(conn->data, "Couldn't use REST");
1973 result = CURLE_FTP_COULDNT_USE_REST;
1976 NBFTPSENDF(conn, "RETR %s", ftp->file);
1977 state(conn, FTP_RETR);
1985 static CURLcode ftp_state_stor_resp(struct connectdata *conn,
1988 CURLcode result = CURLE_OK;
1989 struct SessionHandle *data = conn->data;
1990 struct FTP *ftp = conn->proto.ftp;
1993 failf(data, "Failed FTP upload: %0d", ftpcode);
1994 /* oops, we never close the sockets! */
1995 return CURLE_FTP_COULDNT_STOR_FILE;
1998 if(data->set.ftp_use_port) {
2000 /* PORT means we are now awaiting the server to connect to us. */
2001 result = AllowServerConnect(conn);
2006 if(conn->ssl[SECONDARYSOCKET].use) {
2007 /* since we only have a plaintext TCP connection here, we must now
2009 infof(data, "Doing the SSL/TLS handshake on the data stream\n");
2011 result = Curl_SSLConnect(conn, SECONDARYSOCKET);
2016 *(ftp->bytecountp)=0;
2018 /* When we know we're uploading a specified file, we can get the file
2019 size prior to the actual upload. */
2021 Curl_pgrsSetUploadSize(data, data->set.infilesize);
2023 result = Curl_Transfer(conn, -1, -1, FALSE, NULL, /* no download */
2024 SECONDARYSOCKET, ftp->bytecountp);
2025 state(conn, FTP_STOP);
2030 /* for LIST and RETR responses */
2031 static CURLcode ftp_state_get_resp(struct connectdata *conn,
2035 CURLcode result = CURLE_OK;
2036 struct SessionHandle *data = conn->data;
2037 struct FTP *ftp = conn->proto.ftp;
2038 char *buf = data->state.buffer;
2040 if((ftpcode == 150) || (ftpcode == 125)) {
2044 150 Opening BINARY mode data connection for /etc/passwd (2241
2045 bytes). (ok, the file is being transfered)
2048 150 Opening ASCII mode data connection for /bin/ls
2051 150 ASCII data connection for /bin/ls (137.167.104.91,37445) (0 bytes).
2054 150 Opening ASCII mode data connection for /linux/fisk/kpanelrc (0.0.0.0,0) (545 bytes).
2057 125 Data connection already open; Transfer starting. */
2059 curl_off_t size=-1; /* default unknown size */
2063 * It appears that there are FTP-servers that return size 0 for files when
2064 * SIZE is used on the file while being in BINARY mode. To work around
2065 * that (stupid) behavior, we attempt to parse the RETR response even if
2066 * the SIZE returned size zero.
2068 * Debugging help from Salvatore Sorrentino on February 26, 2003.
2071 if((instate != FTP_LIST) &&
2072 !data->set.ftp_ascii &&
2073 (ftp->downloadsize < 1)) {
2075 * It seems directory listings either don't show the size or very
2076 * often uses size 0 anyway. ASCII transfers may very well turn out
2077 * that the transfered amount of data is not the same as this line
2078 * tells, why using this number in those cases only confuses us.
2080 * Example D above makes this parsing a little tricky */
2082 bytes=strstr(buf, " bytes");
2085 /* this is a hint there is size information in there! ;-) */
2087 /* scan for the left parenthesis and break there */
2090 /* skip only digits */
2091 if(!isdigit((int)*bytes)) {
2095 /* one more estep backwards */
2098 /* if we have nothing but digits: */
2100 /* get the number! */
2101 size = curlx_strtoofft(bytes, NULL, 0);
2105 else if(ftp->downloadsize > -1)
2106 size = ftp->downloadsize;
2108 if(data->set.ftp_use_port) {
2110 result = AllowServerConnect(conn);
2115 if(conn->ssl[SECONDARYSOCKET].use) {
2116 /* since we only have a plaintext TCP connection here, we must now
2118 infof(data, "Doing the SSL/TLS handshake on the data stream\n");
2119 result = Curl_SSLConnect(conn, SECONDARYSOCKET);
2124 if(size > conn->maxdownload && conn->maxdownload > 0)
2125 size = conn->size = conn->maxdownload;
2127 if(instate != FTP_LIST)
2128 infof(data, "Getting file with size: %" FORMAT_OFF_T "\n", size);
2131 result=Curl_Transfer(conn, SECONDARYSOCKET, size, FALSE,
2133 -1, NULL); /* no upload here */
2137 state(conn, FTP_STOP);
2140 if((instate == FTP_LIST) && (ftpcode == 450)) {
2141 /* simply no matching files in the dir listing */
2142 ftp->no_transfer = TRUE; /* don't download anything */
2143 state(conn, FTP_STOP); /* this phase is over */
2146 failf(data, "%s", buf+4);
2147 return CURLE_FTP_COULDNT_RETR_FILE;
2154 /* after USER, PASS and ACCT */
2155 static CURLcode ftp_state_loggedin(struct connectdata *conn)
2157 CURLcode result = CURLE_OK;
2158 struct SessionHandle *data = conn->data;
2159 infof(data, "We have successfully logged in\n");
2162 if(data->set.krb4) {
2163 /* We are logged in, asked to use Kerberos. Set the requested
2166 if(conn->sec_complete)
2168 Curl_sec_set_protection_level(conn);
2170 /* We may need to issue a KAUTH here to have access to the files
2171 * do it if user supplied a password
2173 if(conn->passwd && *conn->passwd) {
2175 result = Curl_krb_kauth(conn);
2181 if(conn->ssl[FIRSTSOCKET].use) {
2182 /* PBSZ = PROTECTION BUFFER SIZE.
2184 The 'draft-murray-auth-ftp-ssl' (draft 12, page 7) says:
2186 Specifically, the PROT command MUST be preceded by a PBSZ
2187 command and a PBSZ command MUST be preceded by a successful
2188 security data exchange (the TLS negotiation in this case)
2190 ... (and on page 8):
2192 Thus the PBSZ command must still be issued, but must have a
2193 parameter of '0' to indicate that no buffering is taking place
2194 and the data connection should not be encapsulated.
2196 NBFTPSENDF(conn, "PBSZ %d", 0);
2197 state(conn, FTP_PBSZ);
2200 result = ftp_state_pwd(conn);
2205 /* for USER and PASS responses */
2206 static CURLcode ftp_state_user_resp(struct connectdata *conn,
2210 CURLcode result = CURLE_OK;
2211 struct SessionHandle *data = conn->data;
2212 struct FTP *ftp = conn->proto.ftp;
2213 (void)instate; /* no use for this yet */
2215 if((ftpcode == 331) && (ftp->state == FTP_USER)) {
2216 /* 331 Password required for ...
2217 (the server requires to send the user's password too) */
2218 NBFTPSENDF(conn, "PASS %s", ftp->passwd?ftp->passwd:"");
2219 state(conn, FTP_PASS);
2221 else if(ftpcode/100 == 2) {
2222 /* 230 User ... logged in.
2223 (the user logged in with or without password) */
2224 result = ftp_state_loggedin(conn);
2226 else if(ftpcode == 332) {
2227 if(data->set.ftp_account) {
2228 NBFTPSENDF(conn, "ACCT %s", data->set.ftp_account);
2229 state(conn, FTP_ACCT);
2232 failf(data, "ACCT requested but none available");
2233 result = CURLE_LOGIN_DENIED;
2237 /* All other response codes, like:
2239 530 User ... access denied
2240 (the server denies to log the specified user) */
2241 failf(data, "Access denied: %03d", ftpcode);
2242 result = CURLE_LOGIN_DENIED;
2247 /* for ACCT response */
2248 static CURLcode ftp_state_acct_resp(struct connectdata *conn,
2251 CURLcode result = CURLE_OK;
2252 struct SessionHandle *data = conn->data;
2253 if(ftpcode != 230) {
2254 failf(data, "ACCT rejected by server: %03d", ftpcode);
2255 result = CURLE_FTP_WEIRD_PASS_REPLY; /* FIX */
2258 result = ftp_state_loggedin(conn);
2264 static CURLcode ftp_statemach_act(struct connectdata *conn)
2267 curl_socket_t sock = conn->sock[FIRSTSOCKET];
2268 struct SessionHandle *data=conn->data;
2270 struct FTP *ftp = conn->proto.ftp;
2271 static const char * const ftpauth[] = {
2277 /* we have a piece of a command still left to send */
2279 result = Curl_write(conn, sock, ftp->sendthis + ftp->sendsize -
2280 ftp->sendleft, ftp->sendleft, &written);
2284 if(written != (ssize_t)ftp->sendleft) {
2285 /* only a fraction was sent */
2286 ftp->sendleft -= written;
2289 free(ftp->sendthis);
2291 ftp->sendleft = ftp->sendsize = 0;
2292 ftp->response = Curl_tvnow();
2297 /* we read a piece of response */
2298 result = ftp_readresp(sock, conn, &ftpcode, &nread);
2303 /* we have now received a full FTP server response */
2304 switch(ftp->state) {
2306 if(ftpcode != 220) {
2307 failf(data, "This doesn't seem like a nice ftp-server response");
2308 return CURLE_FTP_WEIRD_SERVER_REPLY;
2311 /* We have received a 220 response fine, now we proceed. */
2313 if(data->set.krb4) {
2314 /* If not anonymous login, try a secure login. Note that this
2315 procedure is still BLOCKING. */
2317 Curl_sec_request_prot(conn, "private");
2318 /* We set private first as default, in case the line below fails to
2319 set a valid level */
2320 Curl_sec_request_prot(conn, data->set.krb4_level);
2322 if(Curl_sec_login(conn) != 0)
2323 infof(data, "Logging in with password in cleartext!\n");
2325 infof(data, "Authentication successful\n");
2329 if(data->set.ftp_ssl && !conn->ssl[FIRSTSOCKET].use) {
2330 /* We don't have a SSL/TLS connection yet, but FTPS is
2331 requested. Try a FTPS connection now */
2334 switch(data->set.ftpsslauth) {
2335 case CURLFTPAUTH_DEFAULT:
2336 case CURLFTPAUTH_SSL:
2337 ftp->count2 = 1; /* add one to get next */
2340 case CURLFTPAUTH_TLS:
2341 ftp->count2 = -1; /* subtract one to get next */
2345 failf(data, "unsupported parameter to CURLOPT_FTPSSLAUTH: %d\n",
2346 data->set.ftpsslauth);
2347 return CURLE_FAILED_INIT; /* we don't know what to do */
2349 NBFTPSENDF(conn, "AUTH %s", ftpauth[ftp->count1]);
2350 state(conn, FTP_AUTH);
2353 ftp_state_user(conn);
2361 /* we have gotten the response to a previous AUTH command */
2363 /* RFC2228 (page 5) says:
2365 * If the server is willing to accept the named security mechanism,
2366 * and does not require any security data, it must respond with
2367 * reply code 234/334.
2370 if((ftpcode == 234) || (ftpcode == 334)) {
2371 /* Curl_SSLConnect is BLOCKING */
2372 result = Curl_SSLConnect(conn, FIRSTSOCKET);
2375 conn->protocol |= PROT_FTPS;
2376 conn->ssl[SECONDARYSOCKET].use = FALSE; /* clear-text data */
2378 else if(ftp->count3 < 1) {
2380 ftp->count1 += ftp->count2; /* get next attempt */
2381 NBFTPSENDF(conn, "AUTH %s", ftpauth[ftp->count1]);
2382 /* remain in this same state */
2385 result = ftp_state_user(conn);
2393 result = ftp_state_user_resp(conn, ftpcode, ftp->state);
2397 result = ftp_state_acct_resp(conn, ftpcode);
2401 /* FIX: check response code */
2403 /* For TLS, the data connection can have one of two security levels.
2405 1) Clear (requested by 'PROT C')
2407 2)Private (requested by 'PROT P')
2409 if(!conn->ssl[SECONDARYSOCKET].use) {
2410 NBFTPSENDF(conn, "PROT %c", 'P');
2411 state(conn, FTP_PROT);
2414 result = ftp_state_pwd(conn);
2422 if(ftpcode/100 == 2)
2423 /* We have enabled SSL for the data connection! */
2424 conn->ssl[SECONDARYSOCKET].use = TRUE;
2425 /* FTP servers typically responds with 500 if they decide to reject
2427 else if(data->set.ftp_ssl> CURLFTPSSL_CONTROL)
2428 /* we failed and bails out */
2429 return CURLE_FTP_SSL_FAILED;
2431 result = ftp_state_pwd(conn);
2437 if(ftpcode == 257) {
2438 char *dir = (char *)malloc(nread+1);
2440 char *ptr=&data->state.buffer[4]; /* start on the first letter */
2443 return CURLE_OUT_OF_MEMORY;
2445 /* Reply format is like
2446 257<space>"<directory-name>"<space><commentary> and the RFC959
2449 The directory name can contain any character; embedded
2450 double-quotes should be escaped by double-quotes (the
2451 "quote-doubling" convention).
2454 /* it started good */
2456 while(ptr && *ptr) {
2458 if('\"' == ptr[1]) {
2459 /* "quote-doubling" */
2465 *store = '\0'; /* zero terminate */
2466 break; /* get out of this loop */
2474 ftp->entrypath =dir; /* remember this */
2475 infof(data, "Entry path is '%s'\n", ftp->entrypath);
2478 /* couldn't get the path */
2480 infof(data, "Failed to figure out path\n");
2483 state(conn, FTP_STOP); /* we are done with the CONNECT phase! */
2484 infof(data, "protocol connect phase DONE\n");
2489 case FTP_RETR_PREQUOTE:
2490 case FTP_STOR_PREQUOTE:
2491 if(ftpcode >= 400) {
2492 failf(conn->data, "QUOT command failed with %03d", ftpcode);
2493 return CURLE_FTP_QUOTE_ERROR;
2495 result = ftp_state_quote(conn, FALSE, ftp->state);
2502 if(ftpcode/100 != 2) {
2503 /* failure to CWD there */
2504 if(conn->data->set.ftp_create_missing_dirs &&
2505 ftp->count1 && !ftp->count2) {
2507 ftp->count2++; /* counter to prevent CWD-MKD loops */
2508 NBFTPSENDF(conn, "MKD %s", ftp->dirs[ftp->count1 - 1]);
2509 state(conn, FTP_MKD);
2512 /* return failure */
2513 return CURLE_FTP_ACCESS_DENIED;
2518 if(++ftp->count1 <= ftp->dirdepth) {
2520 NBFTPSENDF(conn, "CWD %s", ftp->dirs[ftp->count1 - 1]);
2523 result = ftp_state_post_cwd(conn);
2531 if(ftpcode/100 != 2) {
2532 /* failure to MKD the dir */
2533 failf(data, "Failed to MKD dir: %03d", ftpcode);
2534 return CURLE_FTP_ACCESS_DENIED;
2536 state(conn, FTP_CWD);
2538 NBFTPSENDF(conn, "CWD %s", ftp->dirs[ftp->count1 - 1]);
2542 result = ftp_state_mdtm_resp(conn, ftpcode);
2549 result = ftp_state_type_resp(conn, ftpcode, ftp->state);
2555 result = ftp_state_size_resp(conn, ftpcode, ftp->state);
2560 result = ftp_state_rest_resp(conn, ftpcode, ftp->state);
2564 result = ftp_state_pasv_resp(conn, ftpcode);
2568 result = ftp_state_port_resp(conn, ftpcode);
2573 result = ftp_state_get_resp(conn, ftpcode, ftp->state);
2577 result = ftp_state_stor_resp(conn, ftpcode);
2581 /* fallthrough, just stop! */
2583 /* internal error */
2584 state(conn, FTP_STOP);
2592 /* Returns timeout in ms. 0 or negative number means the timeout has already
2594 static long ftp_state_timeout(struct connectdata *conn)
2596 struct SessionHandle *data=conn->data;
2597 struct FTP *ftp = conn->proto.ftp;
2598 long timeout_ms=360000; /* in milliseconds */
2600 if(data->set.ftp_response_timeout )
2601 /* if CURLOPT_FTP_RESPONSE_TIMEOUT is set, use that to determine remaining
2602 time. Also, use ftp->response because FTP_RESPONSE_TIMEOUT is supposed
2603 to govern the response for any given ftp response, not for the time
2604 from connect to the given ftp response. */
2605 timeout_ms = data->set.ftp_response_timeout*1000 - /* timeout time */
2606 Curl_tvdiff(Curl_tvnow(), ftp->response); /* spent time */
2607 else if(data->set.timeout)
2608 /* if timeout is requested, find out how much remaining time we have */
2609 timeout_ms = data->set.timeout*1000 - /* timeout time */
2610 Curl_tvdiff(Curl_tvnow(), conn->now); /* spent time */
2612 /* Without a requested timeout, we only wait 'response_time' seconds for
2613 the full response to arrive before we bail out */
2614 timeout_ms = ftp->response_time*1000 -
2615 Curl_tvdiff(Curl_tvnow(), ftp->response); /* spent time */
2621 /* called repeatedly until done from multi.c */
2622 CURLcode Curl_ftp_multi_statemach(struct connectdata *conn,
2625 curl_socket_t sock = conn->sock[FIRSTSOCKET];
2627 struct SessionHandle *data=conn->data;
2628 struct FTP *ftp = conn->proto.ftp;
2629 CURLcode result = CURLE_OK;
2630 long timeout_ms = ftp_state_timeout(conn);
2632 *done = FALSE; /* default to not done yet */
2634 if(timeout_ms <= 0) {
2635 failf(data, "FTP response timeout");
2636 return CURLE_OPERATION_TIMEDOUT;
2639 rc = Curl_select(ftp->sendleft?CURL_SOCKET_BAD:sock, /* reading */
2640 ftp->sendleft?sock:CURL_SOCKET_BAD, /* writing */
2644 failf(data, "select error");
2645 return CURLE_OUT_OF_MEMORY;
2648 result = ftp_statemach_act(conn);
2649 *done = (ftp->state == FTP_STOP);
2651 /* if rc == 0, then select() timed out */
2656 static CURLcode ftp_easy_statemach(struct connectdata *conn)
2658 curl_socket_t sock = conn->sock[FIRSTSOCKET];
2660 struct SessionHandle *data=conn->data;
2661 struct FTP *ftp = conn->proto.ftp;
2662 CURLcode result = CURLE_OK;
2664 while(ftp->state != FTP_STOP) {
2665 long timeout_ms = ftp_state_timeout(conn);
2667 if(timeout_ms <=0 ) {
2668 failf(data, "FTP response timeout");
2669 return CURLE_OPERATION_TIMEDOUT; /* already too little time */
2672 rc = Curl_select(ftp->sendleft?CURL_SOCKET_BAD:sock, /* reading */
2673 ftp->sendleft?sock:CURL_SOCKET_BAD, /* writing */
2677 failf(data, "select error");
2678 return CURLE_OUT_OF_MEMORY;
2681 result = CURLE_OPERATION_TIMEDOUT;
2685 result = ftp_statemach_act(conn);
2695 * Curl_ftp_connect() should do everything that is to be considered a part of
2696 * the connection phase.
2698 * The variable 'done' points to will be TRUE if the protocol-layer connect
2699 * phase is done when this function returns, or FALSE is not. When called as
2700 * a part of the easy interface, it will always be TRUE.
2702 CURLcode Curl_ftp_connect(struct connectdata *conn,
2703 bool *done) /* see description above */
2708 *done = FALSE; /* default to not done yet */
2710 ftp = (struct FTP *)calloc(sizeof(struct FTP), 1);
2712 return CURLE_OUT_OF_MEMORY;
2714 conn->proto.ftp = ftp;
2716 /* We always support persistant connections on ftp */
2717 conn->bits.close = FALSE;
2719 /* get some initial data into the ftp struct */
2720 ftp->bytecountp = &conn->bytecount;
2722 /* no need to duplicate them, this connectdata struct won't change */
2723 ftp->user = conn->user;
2724 ftp->passwd = conn->passwd;
2725 if (isBadFtpString(ftp->user) || isBadFtpString(ftp->passwd))
2726 return CURLE_URL_MALFORMAT;
2728 ftp->response_time = 3600; /* set default response time-out */
2730 #ifndef CURL_DISABLE_HTTP
2731 if (conn->bits.tunnel_proxy) {
2733 /* We want "seamless" FTP operations through HTTP proxy tunnel */
2734 result = Curl_ConnectHTTPProxyTunnel(conn, FIRSTSOCKET,
2735 conn->host.name, conn->remote_port);
2736 if(CURLE_OK != result)
2739 #endif /* CURL_DISABLE_HTTP */
2741 if(conn->protocol & PROT_FTPS) {
2743 /* FTPS is simply ftp with SSL for the control channel */
2744 /* now, perform the SSL initialization for this socket */
2745 result = Curl_SSLConnect(conn, FIRSTSOCKET);
2750 /* When we connect, we start in the state where we await the 220
2752 state(conn, FTP_WAIT220);
2753 ftp->response = Curl_tvnow(); /* start response time-out now! */
2755 if(conn->data->state.used_interface == Curl_if_multi)
2756 result = Curl_ftp_multi_statemach(conn, done);
2758 result = ftp_easy_statemach(conn);
2766 /***********************************************************************
2770 * The DONE function. This does what needs to be done after a single DO has
2773 * Input argument is already checked for validity.
2775 CURLcode Curl_ftp_done(struct connectdata *conn, CURLcode status)
2777 struct SessionHandle *data = conn->data;
2778 struct FTP *ftp = conn->proto.ftp;
2781 CURLcode result=CURLE_OK;
2782 bool was_ctl_valid = ftp->ctl_valid;
2787 /* now store a copy of the directory we are in */
2789 free(ftp->prevpath);
2791 path = curl_unescape(conn->path, 0); /* get the "raw" path */
2793 return CURLE_OUT_OF_MEMORY;
2795 flen = ftp->file?strlen(ftp->file):0; /* file is "raw" already */
2796 dlen = strlen(path)-flen;
2798 ftp->prevpath = path;
2800 /* if 'path' is not the whole string */
2801 ftp->prevpath[dlen]=0; /* terminate */
2802 infof(data, "Remembering we are in dir %s\n", ftp->prevpath);
2805 ftp->prevpath = NULL; /* no path */
2808 /* free the dir tree and file parts */
2811 ftp->ctl_valid = FALSE;
2813 if(data->set.upload) {
2814 if((-1 != data->set.infilesize) &&
2815 (data->set.infilesize != *ftp->bytecountp) &&
2817 failf(data, "Uploaded unaligned file size (%" FORMAT_OFF_T
2818 " out of %" FORMAT_OFF_T " bytes)",
2819 *ftp->bytecountp, data->set.infilesize);
2820 conn->bits.close = TRUE; /* close this connection since we don't
2821 know what state this error leaves us in */
2822 return CURLE_PARTIAL_FILE;
2826 if((-1 != conn->size) && (conn->size != *ftp->bytecountp) &&
2827 (conn->maxdownload != *ftp->bytecountp)) {
2828 failf(data, "Received only partial file: %" FORMAT_OFF_T " bytes",
2830 conn->bits.close = TRUE; /* close this connection since we don't
2831 know what state this error leaves us in */
2832 return CURLE_PARTIAL_FILE;
2834 else if(!ftp->dont_check &&
2835 !*ftp->bytecountp &&
2837 /* We consider this an error, but there's no true FTP error received
2838 why we need to continue to "read out" the server response too.
2839 We don't want to leave a "waiting" server reply if we'll get told
2840 to make a second request on this same connection! */
2841 failf(data, "No data was received!");
2842 result = CURLE_FTP_COULDNT_RETR_FILE;
2847 case CURLE_BAD_DOWNLOAD_RESUME:
2848 case CURLE_FTP_WEIRD_PASV_REPLY:
2849 case CURLE_FTP_PORT_FAILED:
2850 case CURLE_FTP_COULDNT_SET_BINARY:
2851 case CURLE_FTP_COULDNT_RETR_FILE:
2852 case CURLE_FTP_ACCESS_DENIED:
2853 /* the connection stays alive fine even though this happened */
2855 case CURLE_OK: /* doesn't affect the control connection's status */
2856 ftp->ctl_valid = was_ctl_valid;
2858 default: /* by default, an error means the control connection is
2859 wedged and should not be used anymore */
2860 ftp->ctl_valid = FALSE;
2865 Curl_sec_fflush_fd(conn, conn->sock[SECONDARYSOCKET]);
2868 /* shut down the socket to inform the server we're done */
2871 shutdown(conn->sock[SECONDARYSOCKET],2); /* SD_BOTH */
2874 sclose(conn->sock[SECONDARYSOCKET]);
2876 conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
2878 if(!ftp->no_transfer && !status) {
2879 /* Let's see what the server says about the transfer we just performed,
2880 * but lower the timeout as sometimes this connection has died while the
2881 * data has been transfered. This happens when doing through NATs etc that
2882 * abandon old silent connections.
2884 ftp->response_time = 60; /* give it only a minute for now */
2886 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
2888 ftp->response_time = 3600; /* set this back to one hour waits */
2890 if(!nread && (CURLE_OPERATION_TIMEDOUT == result)) {
2891 failf(data, "control connection looks dead");
2898 if(!ftp->dont_check) {
2899 /* 226 Transfer complete, 250 Requested file action okay, completed. */
2900 if((ftpcode != 226) && (ftpcode != 250)) {
2901 failf(data, "server did not report OK, got %d", ftpcode);
2902 return CURLE_FTP_WRITE_ERROR;
2907 /* clear these for next connection */
2908 ftp->no_transfer = FALSE;
2909 ftp->dont_check = FALSE;
2911 if (!result && conn->sec_conn) { /* 3rd party transfer */
2912 /* "done" with the secondary connection */
2913 result = Curl_ftp_done(conn->sec_conn, status);
2916 /* Send any post-transfer QUOTE strings? */
2917 if(!status && !result && data->set.postquote)
2918 result = ftp_sendquote(conn, data->set.postquote);
2923 /***********************************************************************
2927 * Where a 'quote' means a list of custom commands to send to the server.
2928 * The quote list is passed as an argument.
2932 CURLcode ftp_sendquote(struct connectdata *conn, struct curl_slist *quote)
2934 struct curl_slist *item;
2942 FTPSENDF(conn, "%s", item->data);
2944 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
2948 if (ftpcode >= 400) {
2949 failf(conn->data, "QUOT string not accepted: %s", item->data);
2950 return CURLE_FTP_QUOTE_ERROR;
2960 /***********************************************************************
2962 * ftp_transfertype()
2964 * Set transfer type. We only deal with ASCII or BINARY so this function
2967 static CURLcode ftp_transfertype(struct connectdata *conn,
2970 struct SessionHandle *data = conn->data;
2975 FTPSENDF(conn, "TYPE %s", ascii?"A":"I");
2977 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
2981 if(ftpcode != 200) {
2982 failf(data, "Couldn't set %s mode",
2983 ascii?"ASCII":"binary");
2984 return ascii? CURLE_FTP_COULDNT_SET_ASCII:CURLE_FTP_COULDNT_SET_BINARY;
2990 /***************************************************************************
2992 * ftp_pasv_verbose()
2994 * This function only outputs some informationals about this second connection
2995 * when we've issued a PASV command before and thus we have connected to a
2996 * possibly new IP address.
3000 ftp_pasv_verbose(struct connectdata *conn,
3002 char *newhost, /* ascii version */
3006 Curl_printable_address(ai, buf, sizeof(buf));
3007 infof(conn->data, "Connecting to %s (%s) port %d\n", newhost, buf, port);
3011 Check if this is a range download, and if so, set the internal variables
3015 static CURLcode ftp_range(struct connectdata *conn)
3017 curl_off_t from, to;
3018 curl_off_t totalsize=-1;
3021 struct SessionHandle *data = conn->data;
3022 struct FTP *ftp = conn->proto.ftp;
3024 if(conn->bits.use_range && conn->range) {
3025 from=curlx_strtoofft(conn->range, &ptr, 0);
3026 while(ptr && *ptr && (isspace((int)*ptr) || (*ptr=='-')))
3028 to=curlx_strtoofft(ptr, &ptr2, 0);
3030 /* we didn't get any digit */
3033 if((-1 == to) && (from>=0)) {
3035 conn->resume_from = from;
3036 infof(data, "FTP RANGE %" FORMAT_OFF_T " to end of file\n", from);
3041 conn->maxdownload = -from;
3042 conn->resume_from = from;
3043 infof(data, "FTP RANGE the last %" FORMAT_OFF_T " bytes\n", totalsize);
3047 totalsize = to-from;
3048 conn->maxdownload = totalsize+1; /* include the last mentioned byte */
3049 conn->resume_from = from;
3050 infof(data, "FTP RANGE from %" FORMAT_OFF_T
3051 " getting %" FORMAT_OFF_T " bytes\n", from, conn->maxdownload);
3053 infof(data, "range-download from %" FORMAT_OFF_T
3054 " to %" FORMAT_OFF_T ", totally %" FORMAT_OFF_T " bytes\n",
3055 from, to, conn->maxdownload);
3056 ftp->dont_check = TRUE; /* dont check for successful transfer */
3063 * Curl_ftp_nextconnect()
3065 * This function shall be called when the second FTP (data) connection is
3069 CURLcode Curl_ftp_nextconnect(struct connectdata *conn)
3071 struct SessionHandle *data=conn->data;
3072 CURLcode result = CURLE_OK;
3074 /* the ftp struct is inited in Curl_ftp_connect() */
3075 struct FTP *ftp = conn->proto.ftp;
3077 infof(data, "DO-MORE phase starts\n");
3079 if(!ftp->no_transfer && !conn->bits.no_body) {
3080 /* a transfer is about to take place */
3082 if(data->set.upload) {
3083 NBFTPSENDF(conn, "TYPE %c", data->set.ftp_ascii?'A':'I');
3084 state(conn, FTP_STOR_TYPE);
3088 ftp->downloadsize = -1; /* unknown as of yet */
3090 result = ftp_range(conn);
3093 else if((data->set.ftp_list_only) || !ftp->file) {
3094 /* The specified path ends with a slash, and therefore we think this
3095 is a directory that is requested, use LIST. But before that we
3096 need to set ASCII transfer mode. */
3097 NBFTPSENDF(conn, "TYPE A", NULL);
3098 state(conn, FTP_LIST_TYPE);
3101 NBFTPSENDF(conn, "TYPE %c", data->set.ftp_ascii?'A':'I');
3102 state(conn, FTP_RETR_TYPE);
3105 result = ftp_easy_statemach(conn);
3108 if(ftp->no_transfer)
3109 /* no data to transfer. FIX: it feels like a kludge to have this here
3111 result=Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
3113 /* end of transfer */
3114 infof(data, "DO-MORE phase ends\n");
3121 /***********************************************************************
3125 * This is the actual DO function for FTP. Get a file/directory according to
3126 * the options previously setup.
3130 CURLcode ftp_perform(struct connectdata *conn,
3131 bool *connected, /* connect status after PASV / PORT */
3134 /* this is FTP and no proxy */
3135 CURLcode result=CURLE_OK;
3136 struct SessionHandle *data=conn->data;
3138 infof(data, "DO phase starts\n");
3140 *dophase_done = FALSE; /* not done yet */
3142 /* start the first command in the DO phase */
3143 result = ftp_state_quote(conn, TRUE, FTP_QUOTE);
3147 /* run the state-machine */
3148 if(conn->data->state.used_interface == Curl_if_multi)
3149 result = Curl_ftp_multi_statemach(conn, dophase_done);
3151 result = ftp_easy_statemach(conn);
3152 *dophase_done = TRUE; /* with the easy interface we are done here */
3154 *connected = conn->bits.tcpconnect;
3157 infof(data, "DO phase is comlete\n");
3162 /***********************************************************************
3166 * This function is registered as 'curl_do' function. It decodes the path
3167 * parts etc as a wrapper to the actual DO function (ftp_perform).
3169 * The input argument is already checked for validity.
3171 CURLcode Curl_ftp(struct connectdata *conn, bool *done)
3173 CURLcode retcode = CURLE_OK;
3175 *done = FALSE; /* default to false */
3177 retcode = ftp_parse_url_path(conn);
3181 if (conn->sec_conn) {
3182 /* 3rd party transfer */
3183 *done = TRUE; /* BLOCKING */
3184 retcode = ftp_3rdparty(conn);
3187 retcode = ftp_regular_transfer(conn, done);
3192 /***********************************************************************
3194 * Curl_(nb)ftpsendf()
3196 * Sends the formated string as a ftp command to a ftp server
3198 * NOTE: we build the command in a fixed-length buffer, which sets length
3199 * restrictions on the command!
3201 * The "nb" version is made to Never Block.
3203 CURLcode Curl_nbftpsendf(struct connectdata *conn,
3204 const char *fmt, ...)
3206 ssize_t bytes_written;
3210 CURLcode res = CURLE_OK;
3211 struct FTP *ftp = conn->proto.ftp;
3212 struct SessionHandle *data = conn->data;
3216 vsnprintf(s, 250, fmt, ap);
3219 strcat(s, "\r\n"); /* append a trailing CRLF */
3222 write_len = strlen(s);
3224 res = Curl_write(conn, conn->sock[FIRSTSOCKET], sptr, write_len,
3230 if(conn->data->set.verbose)
3231 Curl_debug(conn->data, CURLINFO_HEADER_OUT, sptr, bytes_written,
3234 if(bytes_written != (ssize_t)write_len) {
3235 /* the whole chunk was not sent, store the rest of the data */
3236 write_len -= bytes_written;
3237 sptr += bytes_written;
3238 ftp->sendthis = malloc(write_len);
3240 memcpy(ftp->sendthis, sptr, write_len);
3241 ftp->sendsize=ftp->sendleft=write_len;
3244 failf(data, "out of memory");
3245 res = CURLE_OUT_OF_MEMORY;
3249 ftp->response = Curl_tvnow();
3254 CURLcode Curl_ftpsendf(struct connectdata *conn,
3255 const char *fmt, ...)
3257 ssize_t bytes_written;
3261 CURLcode res = CURLE_OK;
3265 vsnprintf(s, 250, fmt, ap);
3268 strcat(s, "\r\n"); /* append a trailing CRLF */
3271 write_len = strlen(s);
3274 res = Curl_write(conn, conn->sock[FIRSTSOCKET], sptr, write_len,
3280 if(conn->data->set.verbose)
3281 Curl_debug(conn->data, CURLINFO_HEADER_OUT, sptr, bytes_written, conn);
3283 if(bytes_written != (ssize_t)write_len) {
3284 write_len -= bytes_written;
3285 sptr += bytes_written;
3294 /***********************************************************************
3298 * This should be called before calling sclose() on an ftp control connection
3299 * (not data connections). We should then wait for the response from the
3300 * server before returning. The calling code should then try to close the
3304 static CURLcode ftp_quit(struct connectdata *conn)
3306 CURLcode result = CURLE_OK;
3308 if(conn->proto.ftp->ctl_valid) {
3309 NBFTPSENDF(conn, "QUIT", NULL);
3310 state(conn, FTP_QUIT);
3312 result = ftp_easy_statemach(conn);
3318 /***********************************************************************
3320 * Curl_ftp_disconnect()
3322 * Disconnect from an FTP server. Cleanup protocol-specific per-connection
3323 * resources. BLOCKING.
3325 CURLcode Curl_ftp_disconnect(struct connectdata *conn)
3327 struct FTP *ftp= conn->proto.ftp;
3329 /* We cannot send quit unconditionally. If this connection is stale or
3330 bad in any way, sending quit and waiting around here will make the
3331 disconnect wait in vain and cause more problems than we need to.
3333 ftp_quit() will check the state of ftp->ctl_valid. If it's ok it
3334 will try to send the QUIT command, otherwise it will just return.
3337 /* The FTP session may or may not have been allocated/setup at this point! */
3339 (void)ftp_quit(conn); /* ignore errors on the QUIT */
3342 free(ftp->entrypath);
3349 free(ftp->prevpath);
3350 ftp->prevpath = NULL;
3356 /***********************************************************************
3360 * Makes a directory on the FTP server.
3364 static CURLcode ftp_mkd(struct connectdata *conn, char *path)
3366 CURLcode result=CURLE_OK;
3367 int ftpcode; /* for ftp status */
3370 /* Create a directory on the remote server */
3371 FTPSENDF(conn, "MKD %s", path);
3373 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
3380 infof( conn->data , "Created remote directory %s\n" , path );
3383 failf(conn->data, "Permission denied to make directory %s", path);
3384 result = CURLE_FTP_ACCESS_DENIED;
3387 failf(conn->data, "unrecognized MKD response: %d", ftpcode );
3388 result = CURLE_FTP_ACCESS_DENIED;
3394 /***********************************************************************
3398 * Send 'CWD' to the remote server to Change Working Directory. It is the ftp
3399 * version of the unix 'cd' command. This function is only called from the
3400 * ftp_cwd_and_mkd() function these days.
3402 * This function does NOT call failf().
3405 CURLcode ftp_cwd(struct connectdata *conn, char *path)
3411 FTPSENDF(conn, "CWD %s", path);
3412 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
3414 /* According to RFC959, CWD is supposed to return 250 on success, but
3415 there seem to be non-compliant FTP servers out there that return 200,
3416 so we accept any '2xy' code here. */
3417 if (ftpcode/100 != 2)
3418 result = CURLE_FTP_ACCESS_DENIED;
3424 /***********************************************************************
3428 * Change to the given directory. If the directory is not present, and we
3429 * have been told to allow it, then create the directory and cd to it.
3432 static CURLcode ftp_cwd_and_mkd(struct connectdata *conn, char *path)
3436 result = ftp_cwd(conn, path);
3438 if(conn->data->set.ftp_create_missing_dirs) {
3439 result = ftp_mkd(conn, path);
3441 /* ftp_mkd() calls failf() itself */
3443 result = ftp_cwd(conn, path);
3446 failf(conn->data, "Couldn't CWD to %s", path);
3453 /***********************************************************************
3455 * ftp_3rdparty_pretransfer()
3457 * Preparation for 3rd party transfer.
3460 static CURLcode ftp_3rdparty_pretransfer(struct connectdata *conn)
3462 CURLcode result = CURLE_OK;
3463 struct SessionHandle *data = conn->data;
3464 struct connectdata *sec_conn = conn->sec_conn;
3466 conn->xfertype = TARGET3RD;
3467 sec_conn->xfertype = SOURCE3RD;
3469 /* sets transfer type */
3470 result = ftp_transfertype(conn, data->set.ftp_ascii);
3474 result = ftp_transfertype(sec_conn, data->set.ftp_ascii);
3478 /* Send any PREQUOTE strings after transfer type is set? */
3479 if (data->set.source_prequote) {
3480 /* sends command(s) to source server before file transfer */
3481 result = ftp_sendquote(sec_conn, data->set.source_prequote);
3483 if (!result && data->set.prequote)
3484 result = ftp_sendquote(conn, data->set.prequote);
3491 /***********************************************************************
3493 * ftp_3rdparty_transfer()
3495 * Performs 3rd party transfer.
3498 static CURLcode ftp_3rdparty_transfer(struct connectdata *conn)
3500 CURLcode result = CURLE_OK;
3502 int ftpcode, ip[4], port[2];
3503 struct SessionHandle *data = conn->data;
3504 struct connectdata *sec_conn = conn->sec_conn;
3505 char *buf = data->state.buffer; /* this is our buffer */
3508 const char *stor_cmd;
3509 struct connectdata *pasv_conn;
3510 struct connectdata *port_conn;
3512 if (data->set.ftpport == NULL) {
3514 port_conn = sec_conn;
3517 pasv_conn = sec_conn;
3521 result = ftp_cwd_and_create_path(conn);
3525 /* sets the passive mode */
3526 FTPSENDF(pasv_conn, "%s", "PASV");
3527 result = Curl_GetFTPResponse(&nread, pasv_conn, &ftpcode);
3531 if (ftpcode != 227) {
3532 failf(data, "Odd return code after PASV: %03d", ftpcode);
3533 return CURLE_FTP_WEIRD_PASV_REPLY;
3537 if (6 == sscanf(str, "%d,%d,%d,%d,%d,%d",
3538 &ip[0], &ip[1], &ip[2], &ip[3], &port[0], &port[1]))
3544 failf(pasv_conn->data, "Couldn't interpret the 227-reply");
3545 return CURLE_FTP_WEIRD_227_FORMAT;
3548 snprintf(pasv_port, sizeof(pasv_port), "%d,%d,%d,%d,%d,%d", ip[0], ip[1],
3549 ip[2], ip[3], port[0], port[1]);
3551 /* sets data connection between remote hosts */
3552 FTPSENDF(port_conn, "PORT %s", pasv_port);
3553 result = Curl_GetFTPResponse(&nread, port_conn, &ftpcode);
3557 if (ftpcode != 200) {
3558 failf(data, "PORT command attempts failed: %03d", ftpcode);
3559 return CURLE_FTP_PORT_FAILED;
3562 /* we might append onto the file instead of overwriting it */
3563 stor_cmd = data->set.ftp_append?"APPE":"STOR";
3565 /* transfers file between remote hosts */
3566 /* FIX: this should send a series of CWD commands and then RETR only the
3567 ftp->file file. The conn->path "full path" is not unescaped. Test case
3569 FTPSENDF(sec_conn, "RETR %s", sec_conn->path);
3571 if(!data->set.ftpport) {
3573 result = Curl_GetFTPResponse(&nread, sec_conn, &ftpcode);
3577 if((ftpcode != 150) && (ftpcode != 125)) {
3578 failf(data, "Failed RETR: %03d", ftpcode);
3579 return CURLE_FTP_COULDNT_RETR_FILE;
3582 result = Curl_ftpsendf(conn, "%s %s", stor_cmd, conn->proto.ftp->file);
3583 if(CURLE_OK == result)
3584 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
3588 if (ftpcode >= 400) {
3589 failf(data, "Failed FTP upload: %03d", ftpcode);
3590 return CURLE_FTP_COULDNT_STOR_FILE;
3596 result = Curl_ftpsendf(conn, "%s %s", stor_cmd, conn->proto.ftp->file);
3597 if(CURLE_OK == result)
3598 result = Curl_GetFTPResponse(&nread, sec_conn, &ftpcode);
3602 if (ftpcode >= 400) {
3603 failf(data, "Failed FTP upload: %03d", ftpcode);
3604 return CURLE_FTP_COULDNT_STOR_FILE;
3607 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
3611 if((ftpcode != 150) && (ftpcode != 125)) {
3612 failf(data, "Failed FTP upload: %03d", ftpcode);
3613 return CURLE_FTP_COULDNT_STOR_FILE;
3622 /***********************************************************************
3624 * ftp_parse_url_path()
3626 * Parse the URL path into separate path components.
3630 CURLcode ftp_parse_url_path(struct connectdata *conn)
3632 CURLcode retcode = CURLE_OK;
3633 struct SessionHandle *data = conn->data;
3637 char *slash_pos; /* position of the first '/' char in curpos */
3638 char *cur_pos = conn->path; /* current position in path. point at the begin
3639 of next path component */
3641 /* the ftp struct is already inited in ftp_connect() */
3642 ftp = conn->proto.ftp;
3643 ftp->ctl_valid = FALSE;
3646 ftp->diralloc = 5; /* default dir depth to allocate */
3647 ftp->dirs = (char **)calloc(ftp->diralloc, sizeof(ftp->dirs[0]));
3649 return CURLE_OUT_OF_MEMORY;
3651 /* parse the URL path into separate path components */
3652 while((slash_pos=strchr(cur_pos, '/'))) {
3653 /* 1 or 0 to indicate absolute directory */
3654 bool absolute_dir = (cur_pos - conn->path > 0) && (ftp->dirdepth == 0);
3656 /* seek out the next path component */
3657 if (slash_pos-cur_pos) {
3658 /* we skip empty path components, like "x//y" since the FTP command CWD
3659 requires a parameter and a non-existant parameter a) doesn't work on
3660 many servers and b) has no effect on the others. */
3661 int len = (int)(slash_pos - cur_pos + absolute_dir);
3662 ftp->dirs[ftp->dirdepth] = curl_unescape(cur_pos - absolute_dir, len);
3664 if (!ftp->dirs[ftp->dirdepth]) { /* run out of memory ... */
3665 failf(data, "no memory");
3667 return CURLE_OUT_OF_MEMORY;
3669 if (isBadFtpString(ftp->dirs[ftp->dirdepth])) {
3671 return CURLE_URL_MALFORMAT;
3675 cur_pos = slash_pos + 1; /* jump to the rest of the string */
3680 cur_pos = slash_pos + 1; /* jump to the rest of the string */
3681 if(++ftp->dirdepth >= ftp->diralloc) {
3684 ftp->diralloc *= 2; /* double the size each time */
3685 bigger = realloc(ftp->dirs, ftp->diralloc * sizeof(ftp->dirs[0]));
3689 return CURLE_OUT_OF_MEMORY;
3691 ftp->dirs = (char **)bigger;
3696 ftp->file = cur_pos; /* the rest is the file name */
3699 ftp->file = curl_unescape(ftp->file, 0);
3700 if(NULL == ftp->file) {
3702 failf(data, "no memory");
3703 return CURLE_OUT_OF_MEMORY;
3705 if (isBadFtpString(ftp->file)) {
3707 return CURLE_URL_MALFORMAT;
3711 ftp->file=NULL; /* instead of point to a zero byte, we make it a NULL
3714 ftp->cwddone = FALSE; /* default to not done */
3717 /* prevpath is "raw" so we convert the input path before we compare the
3719 char *path = curl_unescape(conn->path, 0);
3721 return CURLE_OUT_OF_MEMORY;
3723 dlen = strlen(path) - (ftp->file?strlen(ftp->file):0);
3724 if((dlen == strlen(ftp->prevpath)) &&
3725 curl_strnequal(path, ftp->prevpath, dlen)) {
3726 infof(data, "Request has same path as previous transfer\n");
3727 ftp->cwddone = TRUE;
3737 /***********************************************************************
3739 * ftp_cwd_and_create_path()
3741 * Creates full path on remote target host.
3745 CURLcode ftp_cwd_and_create_path(struct connectdata *conn)
3747 CURLcode result = CURLE_OK;
3748 /* the ftp struct is already inited in Curl_ftp_connect() */
3749 struct FTP *ftp = conn->proto.ftp;
3753 /* already done and fine */
3756 /* This is a re-used connection. Since we change directory to where the
3757 transfer is taking place, we must now get back to the original dir
3758 where we ended up after login: */
3759 if (conn->bits.reuse && ftp->entrypath) {
3760 if ((result = ftp_cwd_and_mkd(conn, ftp->entrypath)) != CURLE_OK)
3764 for (i=0; i < ftp->dirdepth; i++) {
3765 /* RFC 1738 says empty components should be respected too, but
3766 that is plain stupid since CWD can't be used with an empty argument */
3767 if ((result = ftp_cwd_and_mkd(conn, ftp->dirs[i])) != CURLE_OK)
3774 /* call this when the DO phase has completed */
3775 static CURLcode ftp_dophase_done(struct connectdata *conn,
3778 CURLcode result = CURLE_OK;
3779 struct FTP *ftp = conn->proto.ftp;
3782 result = Curl_ftp_nextconnect(conn);
3784 if(result && (conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD)) {
3785 /* Failure detected, close the second socket if it was created already */
3786 sclose(conn->sock[SECONDARYSOCKET]);
3787 conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
3790 if(ftp->no_transfer)
3791 /* no data to transfer */
3792 result=Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
3794 /* since we didn't connect now, we want do_more to get called */
3795 conn->bits.do_more = TRUE;
3797 ftp->ctl_valid = TRUE; /* seems good */
3802 /* called from multi.c while DOing */
3803 CURLcode Curl_ftp_doing(struct connectdata *conn,
3807 result = Curl_ftp_multi_statemach(conn, dophase_done);
3810 result = ftp_dophase_done(conn, FALSE /* not connected */);
3812 infof(conn->data, "DO phase is comlete\n");
3817 /***********************************************************************
3819 * ftp_regular_transfer()
3821 * The input argument is already checked for validity.
3823 * Performs all commands done before a regular transfer between a local and a
3826 * ftp->ctl_valid starts out as FALSE, and gets set to TRUE if we reach the
3827 * Curl_ftp_done() function without finding any major problem.
3830 CURLcode ftp_regular_transfer(struct connectdata *conn,
3833 CURLcode result=CURLE_OK;
3835 struct SessionHandle *data = conn->data;
3838 /* the ftp struct is already inited in ftp_connect() */
3839 ftp = conn->proto.ftp;
3840 conn->size = -1; /* make sure this is unknown at this point */
3842 Curl_pgrsSetUploadCounter(data, 0);
3843 Curl_pgrsSetDownloadCounter(data, 0);
3844 Curl_pgrsSetUploadSize(data, 0);
3845 Curl_pgrsSetDownloadSize(data, 0);
3847 ftp->ctl_valid = TRUE; /* starts good */
3849 result = ftp_perform(conn,
3850 &connected, /* have we connected after PASV/PORT */
3851 dophase_done); /* all commands in the DO-phase done? */
3853 if(CURLE_OK == result) {
3856 /* the DO phase has not completed yet */
3859 result = ftp_dophase_done(conn, connected);
3871 /***********************************************************************
3875 * The input argument is already checked for validity.
3876 * Performs a 3rd party transfer between two remote hosts.
3878 static CURLcode ftp_3rdparty(struct connectdata *conn)
3880 CURLcode result = CURLE_OK;
3882 conn->proto.ftp->ctl_valid = conn->sec_conn->proto.ftp->ctl_valid = TRUE;
3883 conn->size = conn->sec_conn->size = -1;
3885 result = ftp_3rdparty_pretransfer(conn);
3887 result = ftp_3rdparty_transfer(conn);
3892 #endif /* CURL_DISABLE_FTP */