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 */
89 #include "strtoofft.h"
95 #include "inet_ntop.h"
97 #include "parsedate.h" /* for the week day and month names */
99 #if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL)
100 #include "inet_ntoa_r.h"
103 #define _MPRINTF_REPLACE /* use our functions only */
104 #include <curl/mprintf.h>
106 /* The last #include file should be: */
108 #include "memdebug.h"
111 #ifdef HAVE_NI_WITHSCOPEID
112 #define NIFLAGS NI_NUMERICHOST | NI_NUMERICSERV | NI_WITHSCOPEID
114 #define NIFLAGS NI_NUMERICHOST | NI_NUMERICSERV
117 /* Local API functions */
118 static CURLcode ftp_sendquote(struct connectdata *conn,
119 struct curl_slist *quote);
120 static CURLcode ftp_cwd(struct connectdata *conn, char *path);
121 static CURLcode ftp_mkd(struct connectdata *conn, char *path);
122 static CURLcode ftp_cwd_and_mkd(struct connectdata *conn, char *path);
123 static CURLcode ftp_quit(struct connectdata *conn);
124 static CURLcode ftp_3rdparty_pretransfer(struct connectdata *conn);
125 static CURLcode ftp_3rdparty_transfer(struct connectdata *conn);
126 static CURLcode ftp_parse_url_path(struct connectdata *conn);
127 static CURLcode ftp_cwd_and_create_path(struct connectdata *conn);
128 static CURLcode ftp_regular_transfer(struct connectdata *conn, bool *done);
129 static CURLcode ftp_3rdparty(struct connectdata *conn);
130 static void ftp_pasv_verbose(struct connectdata *conn,
132 char *newhost, /* ascii version */
134 static CURLcode ftp_state_post_rest(struct connectdata *conn);
135 static CURLcode ftp_state_post_cwd(struct connectdata *conn);
136 static CURLcode ftp_state_quote(struct connectdata *conn,
137 bool init, ftpstate instate);
139 /* easy-to-use macro: */
140 #define FTPSENDF(x,y,z) if((result = Curl_ftpsendf(x,y,z))) return result
141 #define NBFTPSENDF(x,y,z) if((result = Curl_nbftpsendf(x,y,z))) return result
143 static void freedirs(struct FTP *ftp)
147 for (i=0; i < ftp->dirdepth; i++){
162 /* Returns non-zero iff the given string contains CR (0x0D) or LF (0x0A), which
163 are not allowed within RFC 959 <string>.
165 static bool isBadFtpString(const char *string)
167 return strchr(string, 0x0D) != NULL || strchr(string, 0x0A) != NULL;
170 /***********************************************************************
172 * AllowServerConnect()
174 * When we've issue the PORT command, we have told the server to connect
175 * to us. This function will sit and wait here until the server has
179 static CURLcode AllowServerConnect(struct connectdata *conn)
182 struct SessionHandle *data = conn->data;
183 curl_socket_t sock = conn->sock[SECONDARYSOCKET];
184 struct timeval now = Curl_tvnow();
185 long timespent = Curl_tvdiff(Curl_tvnow(), now)/1000;
186 long timeout = data->set.connecttimeout?data->set.connecttimeout:
187 (data->set.timeout?data->set.timeout: 0);
190 timeout -= timespent;
192 failf(data, "Timed out before server could connect to us");
193 return CURLE_OPERATION_TIMEDOUT;
197 /* We allow the server 60 seconds to connect to us, or a custom timeout.
198 Note the typecast here. */
199 timeout_ms = (timeout?(int)timeout:60) * 1000;
201 switch (Curl_select(sock, CURL_SOCKET_BAD, timeout_ms)) {
204 failf(data, "Error while waiting for server connect");
205 return CURLE_FTP_PORT_FAILED;
206 case 0: /* timeout */
208 failf(data, "Timeout while waiting for server connect");
209 return CURLE_FTP_PORT_FAILED;
211 /* we have received data here */
214 size_t size = sizeof(struct sockaddr_in);
215 struct sockaddr_in add;
217 getsockname(sock, (struct sockaddr *) &add, (socklen_t *)&size);
218 s=accept(sock, (struct sockaddr *) &add, (socklen_t *)&size);
220 sclose(sock); /* close the first socket */
222 if (CURL_SOCKET_BAD == s) {
224 failf(data, "Error accept()ing server connect");
225 return CURLE_FTP_PORT_FAILED;
227 infof(data, "Connection accepted from server\n");
229 conn->sock[SECONDARYSOCKET] = s;
230 Curl_nonblock(s, TRUE); /* enable non-blocking */
239 static CURLcode ftp_readresp(curl_socket_t sockfd,
240 struct connectdata *conn,
241 int *ftpcode, /* return the ftp-code if done */
242 size_t *size) /* size of the response */
244 int perline; /* count bytes per line */
248 struct SessionHandle *data = conn->data;
250 char *buf = data->state.buffer;
251 CURLcode result = CURLE_OK;
252 struct FTP *ftp = conn->proto.ftp;
256 *ftpcode = 0; /* 0 for errors or not done */
264 while((ftp->nread_resp<BUFSIZE) && (keepon && !result)) {
267 /* we had data in the "cache", copy that instead of doing an actual
270 * ftp->cache_size is cast to int here. This should be safe,
271 * because it would have been populated with something of size
272 * int to begin with, even though its datatype may be larger
275 memcpy(ptr, ftp->cache, (int)ftp->cache_size);
276 gotbytes = (int)ftp->cache_size;
277 free(ftp->cache); /* free the cache */
278 ftp->cache = NULL; /* clear the pointer */
279 ftp->cache_size = 0; /* zero the size just in case */
282 int res = Curl_read(conn, sockfd, ptr, BUFSIZE-ftp->nread_resp,
286 return CURLE_OK; /* return */
294 else if(gotbytes <= 0) {
296 result = CURLE_RECV_ERROR;
297 failf(data, "FTP response reading failed");
300 /* we got a whole chunk of data, which can be anything from one
301 * byte to a set of lines and possible just a piece of the last
305 conn->headerbytecount += gotbytes;
307 ftp->nread_resp += gotbytes;
308 for(i = 0; i < gotbytes; ptr++, i++) {
311 /* a newline is CRLF in ftp-talk, so the CR is ignored as
312 the line isn't really terminated until the LF comes */
314 /* output debug output if that is requested */
315 if(data->set.verbose)
316 Curl_debug(data, CURLINFO_HEADER_IN, line_start, perline, conn);
319 * We pass all response-lines to the callback function registered
320 * for "headers". The response lines can be seen as a kind of
323 result = Curl_client_write(data, CLIENTWRITE_HEADER,
324 line_start, perline);
328 #define lastline(line) (isdigit((int)line[0]) && isdigit((int)line[1]) && \
329 isdigit((int)line[2]) && (' ' == line[3]))
331 if(perline>3 && lastline(line_start)) {
332 /* This is the end of the last line, copy the last line to the
333 start of the buffer and zero terminate, for old times sake (and
337 for(meow=line_start, n=0; meow<ptr; meow++, n++)
339 *meow=0; /* zero terminate */
341 line_start = ptr+1; /* advance pointer */
342 i++; /* skip this before getting out */
344 *size = ftp->nread_resp; /* size of the response */
345 ftp->nread_resp = 0; /* restart */
348 perline=0; /* line starts over here */
352 if(!keepon && (i != gotbytes)) {
353 /* We found the end of the response lines, but we didn't parse the
354 full chunk of data we have read from the server. We therefore need
355 to store the rest of the data to be checked on the next invoke as
356 it may actually contain another end of response already! */
357 ftp->cache_size = gotbytes - i;
358 ftp->cache = (char *)malloc((int)ftp->cache_size);
360 memcpy(ftp->cache, line_start, (int)ftp->cache_size);
362 return CURLE_OUT_OF_MEMORY; /**BANG**/
364 } /* there was data */
366 } /* while there's buffer left and loop is requested */
372 /* handle the security-oriented responses 6xx ***/
373 /* FIXME: some errorchecking perhaps... ***/
376 Curl_sec_read_msg(conn, buf, prot_safe);
379 Curl_sec_read_msg(conn, buf, prot_private);
382 Curl_sec_read_msg(conn, buf, prot_confidential);
385 /* normal ftp stuff we pass through! */
390 *ftpcode=code; /* return the initial number like this */
393 /* store the latest code for later retrieval */
394 conn->data->info.httpcode=code;
399 /* --- parse FTP server responses --- */
402 * Curl_GetFTPResponse() is supposed to be invoked after each command sent to
403 * a remote FTP server. This function will wait and read all lines of the
404 * response and extract the relevant return code for the invoking function.
407 CURLcode Curl_GetFTPResponse(ssize_t *nreadp, /* return number of bytes read */
408 struct connectdata *conn,
409 int *ftpcode) /* return the ftp-code */
412 * We cannot read just one byte per read() and then go back to select() as
413 * the OpenSSL read() doesn't grok that properly.
415 * Alas, read as much as possible, split up into lines, use the ending
416 * line in a response or continue reading. */
418 curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
419 int perline; /* count bytes per line */
423 long timeout; /* timeout in seconds */
425 struct SessionHandle *data = conn->data;
427 int code=0; /* default ftp "error code" to return */
428 char *buf = data->state.buffer;
429 CURLcode result = CURLE_OK;
430 struct FTP *ftp = conn->proto.ftp;
431 struct timeval now = Curl_tvnow();
434 *ftpcode = 0; /* 0 for errors */
443 while((*nreadp<BUFSIZE) && (keepon && !result)) {
444 /* check and reset timeout value every lap */
445 if(data->set.ftp_response_timeout )
446 /* if CURLOPT_FTP_RESPONSE_TIMEOUT is set, use that to determine
447 remaining time. Also, use "now" as opposed to "conn->now"
448 because ftp_response_timeout is only supposed to govern
449 the response for any given ftp response, not for the time
450 from connect to the given ftp response. */
451 timeout = data->set.ftp_response_timeout - /* timeout time */
452 Curl_tvdiff(Curl_tvnow(), now)/1000; /* spent time */
453 else if(data->set.timeout)
454 /* if timeout is requested, find out how much remaining time we have */
455 timeout = data->set.timeout - /* timeout time */
456 Curl_tvdiff(Curl_tvnow(), conn->now)/1000; /* spent time */
458 /* Even without a requested timeout, we only wait response_time
459 seconds for the full response to arrive before we bail out */
460 timeout = ftp->response_time -
461 Curl_tvdiff(Curl_tvnow(), now)/1000; /* spent time */
464 failf(data, "FTP response timeout");
465 return CURLE_OPERATION_TIMEDOUT; /* already too little time */
469 interval_ms = 1 * 1000; /* use 1 second timeout intervals */
471 switch (Curl_select(sockfd, CURL_SOCKET_BAD, interval_ms)) {
472 case -1: /* select() error, stop reading */
473 result = CURLE_RECV_ERROR;
474 failf(data, "FTP response aborted due to select() error: %d", errno);
476 case 0: /* timeout */
477 if(Curl_pgrsUpdate(conn))
478 return CURLE_ABORTED_BY_CALLBACK;
479 continue; /* just continue in our loop for the timeout duration */
485 if(CURLE_OK == result) {
487 * This code previously didn't use the kerberos sec_read() code
488 * to read, but when we use Curl_read() it may do so. Do confirm
489 * that this is still ok and then remove this comment!
492 /* we had data in the "cache", copy that instead of doing an actual
495 * Dave Meyer, December 2003:
496 * ftp->cache_size is cast to int here. This should be safe,
497 * because it would have been populated with something of size
498 * int to begin with, even though its datatype may be larger
501 memcpy(ptr, ftp->cache, (int)ftp->cache_size);
502 gotbytes = (int)ftp->cache_size;
503 free(ftp->cache); /* free the cache */
504 ftp->cache = NULL; /* clear the pointer */
505 ftp->cache_size = 0; /* zero the size just in case */
508 int res = Curl_read(conn, sockfd, ptr, BUFSIZE-*nreadp, &gotbytes);
511 continue; /* go looping again */
519 else if(gotbytes <= 0) {
521 result = CURLE_RECV_ERROR;
522 failf(data, "FTP response reading failed");
525 /* we got a whole chunk of data, which can be anything from one
526 * byte to a set of lines and possible just a piece of the last
530 conn->headerbytecount += gotbytes;
533 for(i = 0; i < gotbytes; ptr++, i++) {
536 /* a newline is CRLF in ftp-talk, so the CR is ignored as
537 the line isn't really terminated until the LF comes */
539 /* output debug output if that is requested */
540 if(data->set.verbose)
541 Curl_debug(data, CURLINFO_HEADER_IN, line_start, perline, conn);
544 * We pass all response-lines to the callback function registered
545 * for "headers". The response lines can be seen as a kind of
548 result = Curl_client_write(data, CLIENTWRITE_HEADER,
549 line_start, perline);
553 #define lastline(line) (isdigit((int)line[0]) && isdigit((int)line[1]) && \
554 isdigit((int)line[2]) && (' ' == line[3]))
556 if(perline>3 && lastline(line_start)) {
557 /* This is the end of the last line, copy the last
558 * line to the start of the buffer and zero terminate,
559 * for old times sake (and krb4)! */
562 for(meow=line_start, n=0; meow<ptr; meow++, n++)
564 *meow=0; /* zero terminate */
566 line_start = ptr+1; /* advance pointer */
567 i++; /* skip this before getting out */
570 perline=0; /* line starts over here */
574 if(!keepon && (i != gotbytes)) {
575 /* We found the end of the response lines, but we didn't parse the
576 full chunk of data we have read from the server. We therefore
577 need to store the rest of the data to be checked on the next
578 invoke as it may actually contain another end of response
579 already! Cleverly figured out by Eric Lavigne in December
581 ftp->cache_size = gotbytes - i;
582 ftp->cache = (char *)malloc((int)ftp->cache_size);
584 memcpy(ftp->cache, line_start, (int)ftp->cache_size);
586 return CURLE_OUT_OF_MEMORY; /**BANG**/
588 } /* there was data */
590 } /* while there's buffer left and loop is requested */
596 /* handle the security-oriented responses 6xx ***/
597 /* FIXME: some errorchecking perhaps... ***/
600 Curl_sec_read_msg(conn, buf, prot_safe);
603 Curl_sec_read_msg(conn, buf, prot_private);
606 Curl_sec_read_msg(conn, buf, prot_confidential);
609 /* normal ftp stuff we pass through! */
615 *ftpcode=code; /* return the initial number like this */
617 /* store the latest code for later retrieval */
618 conn->data->info.httpcode=code;
623 /* This is the ONLY way to change FTP state! */
624 static void state(struct connectdata *conn,
628 /* for debug purposes */
629 const char *names[]={
663 struct FTP *ftp = conn->proto.ftp;
665 if(ftp->state != state)
666 infof(conn->data, "FTP %p state change from %s to %s\n",
667 ftp, names[ftp->state], names[state]);
672 static CURLcode ftp_state_user(struct connectdata *conn)
675 struct FTP *ftp = conn->proto.ftp;
677 NBFTPSENDF(conn, "USER %s", ftp->user?ftp->user:"");
679 state(conn, FTP_USER);
684 static CURLcode ftp_state_pwd(struct connectdata *conn)
688 /* send PWD to discover our entry point */
689 NBFTPSENDF(conn, "PWD", NULL);
690 state(conn, FTP_PWD);
695 /* For the FTP "protocol connect" and "doing" phases only */
696 CURLcode Curl_ftp_fdset(struct connectdata *conn,
698 fd_set *write_fd_set,
701 struct FTP *ftp = conn->proto.ftp;
702 curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
706 FD_SET(sockfd, write_fd_set);
710 FD_SET(sockfd, read_fd_set);
713 if((int)sockfd > *max_fdp)
714 *max_fdp = (int)sockfd;
719 /* This is called after the FTP_QUOTE state is passed.
721 ftp_state_cwd() sends the range of PWD commands to the server to change to
722 the correct directory. It may also need to send MKD commands to create
723 missing ones, if that option is enabled.
725 static CURLcode ftp_state_cwd(struct connectdata *conn)
727 CURLcode result = CURLE_OK;
728 struct FTP *ftp = conn->proto.ftp;
731 /* already done and fine */
732 result = ftp_state_post_cwd(conn);
735 if (conn->bits.reuse && ftp->entrypath) {
736 /* This is a re-used connection. Since we change directory to where the
737 transfer is taking place, we must first get back to the original dir
738 where we ended up after login: */
739 ftp->count1 = 0; /* we count this as the first path, then we add one
740 for all upcoming ones in the ftp->dirs[] array */
741 NBFTPSENDF(conn, "CWD %s", ftp->entrypath);
742 state(conn, FTP_CWD);
747 /* issue the first CWD, the rest is sent when the CWD responses are
749 NBFTPSENDF(conn, "CWD %s", ftp->dirs[ftp->count1 -1]);
750 state(conn, FTP_CWD);
753 /* No CWD necessary */
754 result = ftp_state_post_cwd(conn);
761 typedef enum { EPRT, LPRT, PORT, DONE } ftpport;
763 static CURLcode ftp_state_use_port(struct connectdata *conn,
764 ftpport fcmd) /* start with this */
767 CURLcode result = CURLE_OK;
768 struct FTP *ftp = conn->proto.ftp;
769 struct SessionHandle *data=conn->data;
770 curl_socket_t portsock= CURL_SOCKET_BAD;
773 /******************************************************************
774 * IPv6-specific section
777 struct addrinfo *res, *ai;
778 struct sockaddr_storage ss;
780 char hbuf[NI_MAXHOST];
781 struct sockaddr *sa=(struct sockaddr *)&ss;
784 char portmsgbuf[1024], tmp[1024];
785 const char *mode[] = { "EPRT", "LPRT", "PORT", NULL };
789 struct Curl_dns_entry *h=NULL;
791 if(data->set.ftpport && (strlen(data->set.ftpport) > 1)) {
792 /* attempt to get the address of the given interface name */
793 if(!Curl_if2ip(data->set.ftpport, hbuf, sizeof(hbuf)))
794 /* not an interface, use the given string as host name instead */
795 host = data->set.ftpport;
797 host = hbuf; /* use the hbuf for host name */
798 } /* data->set.ftpport */
801 /* not an interface and not a host name, get default by extracting
802 the IP from the control connection */
805 rc = getsockname(conn->sock[FIRSTSOCKET], (struct sockaddr *)&ss, &sslen);
807 failf(data, "getsockname() returned %d\n", rc);
808 return CURLE_FTP_PORT_FAILED;
811 rc = getnameinfo((struct sockaddr *)&ss, sslen, hbuf, sizeof(hbuf), NULL,
814 failf(data, "getnameinfo() returned %d\n", rc);
815 return CURLE_FTP_PORT_FAILED;
817 host = hbuf; /* use this host name */
820 rc = Curl_resolv(conn, host, 0, &h);
821 if(rc == CURLRESOLV_PENDING)
822 rc = Curl_wait_for_resolv(conn, &h);
825 /* when we return from this function, we can forget about this entry
826 to we can unlock it now already */
827 Curl_resolv_unlock(data, h);
830 res = NULL; /* failure! */
832 portsock = CURL_SOCKET_BAD;
834 for (ai = res; ai; ai = ai->ai_next) {
836 * Workaround for AIX5 getaddrinfo() problem (it doesn't set ai_socktype):
838 if (ai->ai_socktype == 0)
839 ai->ai_socktype = SOCK_STREAM;
841 portsock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
842 if (portsock == CURL_SOCKET_BAD) {
843 error = Curl_ourerrno();
847 if (bind(portsock, ai->ai_addr, ai->ai_addrlen) < 0) {
848 error = Curl_ourerrno();
850 portsock = CURL_SOCKET_BAD;
854 if (listen(portsock, 1) < 0) {
855 error = Curl_ourerrno();
857 portsock = CURL_SOCKET_BAD;
864 if (portsock == CURL_SOCKET_BAD) {
865 failf(data, "%s", Curl_strerror(conn,error));
866 return CURLE_FTP_PORT_FAILED;
870 if (getsockname(portsock, sa, &sslen) < 0) {
871 failf(data, "%s", Curl_strerror(conn,Curl_ourerrno()));
872 return CURLE_FTP_PORT_FAILED;
876 if(!conn->bits.ftp_use_eprt && conn->bits.ipv6)
877 /* EPRT is disabled but we are connected to a IPv6 host, so we ignore the
878 request and enable EPRT again! */
879 conn->bits.ftp_use_eprt = TRUE;
882 for (; fcmd != DONE; fcmd++) {
886 if(!conn->bits.ftp_use_eprt && (EPRT == fcmd))
887 /* if disabled, goto next */
890 if(!conn->bits.ftp_use_lprt && (LPRT == fcmd))
891 /* if disabled, goto next */
894 switch (sa->sa_family) {
896 ap = (unsigned char *)&((struct sockaddr_in *)&ss)->sin_addr;
897 alen = sizeof(((struct sockaddr_in *)&ss)->sin_addr);
898 pp = (unsigned char *)&((struct sockaddr_in *)&ss)->sin_port;
899 plen = sizeof(((struct sockaddr_in *)&ss)->sin_port);
904 ap = (unsigned char *)&((struct sockaddr_in6 *)&ss)->sin6_addr;
905 alen = sizeof(((struct sockaddr_in6 *)&ss)->sin6_addr);
906 pp = (unsigned char *)&((struct sockaddr_in6 *)&ss)->sin6_port;
907 plen = sizeof(((struct sockaddr_in6 *)&ss)->sin6_port);
913 lprtaf = eprtaf = -1;
920 if (getnameinfo((struct sockaddr *)&ss, sslen,
921 portmsgbuf, sizeof(portmsgbuf), tmp, sizeof(tmp),
925 /* do not transmit IPv6 scope identifier to the wire */
926 if (sa->sa_family == AF_INET6) {
927 char *q = strchr(portmsgbuf, '%');
932 result = Curl_nbftpsendf(conn, "%s |%d|%s|%s|", mode[fcmd], eprtaf,
938 else if ((LPRT == fcmd) || (PORT == fcmd)) {
941 if ((LPRT == fcmd) && lprtaf < 0)
943 if ((PORT == fcmd) && sa->sa_family != AF_INET)
946 portmsgbuf[0] = '\0';
948 snprintf(tmp, sizeof(tmp), "%d,%d", lprtaf, alen);
949 if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >=
950 sizeof(portmsgbuf)) {
955 for (i = 0; i < alen; i++) {
957 snprintf(tmp, sizeof(tmp), ",%u", ap[i]);
959 snprintf(tmp, sizeof(tmp), "%u", ap[i]);
961 if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >=
962 sizeof(portmsgbuf)) {
968 snprintf(tmp, sizeof(tmp), ",%d", plen);
970 if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >= sizeof(portmsgbuf))
974 for (i = 0; i < plen; i++) {
975 snprintf(tmp, sizeof(tmp), ",%u", pp[i]);
977 if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >=
978 sizeof(portmsgbuf)) {
983 result = Curl_nbftpsendf(conn, "%s %s", mode[fcmd], portmsgbuf);
990 /* store which command was sent */
993 /* we set the secondary socket variable to this for now, it is only so that
994 the cleanup function will close it in case we fail before the true
995 secondary stuff is made */
996 if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET])
997 sclose(conn->sock[SECONDARYSOCKET]);
998 conn->sock[SECONDARYSOCKET] = portsock;
1001 /******************************************************************
1002 * IPv4-specific section
1004 struct sockaddr_in sa;
1005 unsigned short porttouse;
1006 char myhost[256] = "";
1007 bool sa_filled_in = FALSE;
1008 Curl_addrinfo *addr = NULL;
1009 unsigned short ip[4];
1010 (void)fcmd; /* not used in the IPv4 code */
1011 if(data->set.ftpport) {
1014 /* First check if the given name is an IP address */
1015 in=inet_addr(data->set.ftpport);
1017 if(in != CURL_INADDR_NONE)
1018 /* this is an IPv4 address */
1019 addr = Curl_ip2addr(in, data->set.ftpport, 0);
1021 if(Curl_if2ip(data->set.ftpport, myhost, sizeof(myhost))) {
1022 /* The interface to IP conversion provided a dotted address */
1023 in=inet_addr(myhost);
1024 addr = Curl_ip2addr(in, myhost, 0);
1026 else if(strlen(data->set.ftpport)> 1) {
1027 /* might be a host name! */
1028 struct Curl_dns_entry *h=NULL;
1029 int rc = Curl_resolv(conn, myhost, 0, &h);
1030 if(rc == CURLRESOLV_PENDING)
1032 rc = Curl_wait_for_resolv(conn, &h);
1035 /* when we return from this function, we can forget about this entry
1036 so we can unlock it now already */
1037 Curl_resolv_unlock(data, h);
1040 } /* CURL_INADDR_NONE */
1041 } /* data->set.ftpport */
1044 /* pick a suitable default here */
1049 if (getsockname(conn->sock[FIRSTSOCKET],
1050 (struct sockaddr *)&sa, &sslen) < 0) {
1051 failf(data, "getsockname() failed");
1052 return CURLE_FTP_PORT_FAILED;
1055 sa_filled_in = TRUE; /* the sa struct is filled in */
1058 if (addr || sa_filled_in) {
1059 portsock = socket(AF_INET, SOCK_STREAM, 0);
1060 if(CURL_SOCKET_BAD != portsock) {
1063 /* we set the secondary socket variable to this for now, it
1064 is only so that the cleanup function will close it in case
1065 we fail before the true secondary stuff is made */
1066 if(-1 != conn->sock[SECONDARYSOCKET])
1067 sclose(conn->sock[SECONDARYSOCKET]);
1068 conn->sock[SECONDARYSOCKET] = portsock;
1071 memcpy(&sa, addr->ai_addr, sizeof(sa));
1072 sa.sin_addr.s_addr = INADDR_ANY;
1078 if(bind(portsock, (struct sockaddr *)&sa, size) >= 0) {
1079 /* we succeeded to bind */
1080 struct sockaddr_in add;
1081 socklen_t socksize = sizeof(add);
1083 if(getsockname(portsock, (struct sockaddr *) &add,
1085 failf(data, "getsockname() failed");
1086 return CURLE_FTP_PORT_FAILED;
1088 porttouse = ntohs(add.sin_port);
1090 if ( listen(portsock, 1) < 0 ) {
1091 failf(data, "listen(2) failed on socket");
1092 return CURLE_FTP_PORT_FAILED;
1096 failf(data, "bind(2) failed on socket");
1097 return CURLE_FTP_PORT_FAILED;
1101 failf(data, "socket(2) failed (%s)");
1102 return CURLE_FTP_PORT_FAILED;
1106 failf(data, "could't find IP address to use");
1107 return CURLE_FTP_PORT_FAILED;
1111 Curl_inet_ntop(AF_INET, &((struct sockaddr_in *)&sa)->sin_addr,
1112 myhost, sizeof(myhost));
1114 Curl_printable_address(addr, myhost, sizeof(myhost));
1116 if(4 == sscanf(myhost, "%hu.%hu.%hu.%hu",
1117 &ip[0], &ip[1], &ip[2], &ip[3])) {
1119 infof(data, "Telling server to connect to %d.%d.%d.%d:%d\n",
1120 ip[0], ip[1], ip[2], ip[3], porttouse);
1122 result=Curl_nbftpsendf(conn, "PORT %d,%d,%d,%d,%d,%d",
1123 ip[0], ip[1], ip[2], ip[3],
1124 porttouse >> 8, porttouse & 255);
1129 return CURLE_FTP_PORT_FAILED;
1131 Curl_freeaddrinfo(addr);
1135 #endif /* end of ipv4-specific code */
1137 state(conn, FTP_PORT);
1141 static CURLcode ftp_state_use_pasv(struct connectdata *conn)
1143 struct FTP *ftp = conn->proto.ftp;
1144 CURLcode result = CURLE_OK;
1146 Here's the excecutive summary on what to do:
1148 PASV is RFC959, expect:
1149 227 Entering Passive Mode (a1,a2,a3,a4,p1,p2)
1151 LPSV is RFC1639, expect:
1152 228 Entering Long Passive Mode (4,4,a1,a2,a3,a4,2,p1,p2)
1154 EPSV is RFC2428, expect:
1155 229 Entering Extended Passive Mode (|||port|)
1159 const char *mode[] = { "EPSV", "PASV", NULL };
1163 if(!conn->bits.ftp_use_epsv && conn->bits.ipv6)
1164 /* EPSV is disabled but we are connected to a IPv6 host, so we ignore the
1165 request and enable EPSV again! */
1166 conn->bits.ftp_use_epsv = TRUE;
1169 modeoff = conn->bits.ftp_use_epsv?0:1;
1171 result = Curl_nbftpsendf(conn, "%s", mode[modeoff]);
1175 ftp->count1 = modeoff;
1176 state(conn, FTP_PASV);
1177 infof(conn->data, "Connect data stream passively\n");
1182 /* REST is the last command in the chain of commands when a "head"-like
1183 request is made. Thus, if an actual transfer is to be made this is where
1184 we take off for real. */
1185 static CURLcode ftp_state_post_rest(struct connectdata *conn)
1187 CURLcode result = CURLE_OK;
1188 struct FTP *ftp = conn->proto.ftp;
1189 struct SessionHandle *data = conn->data;
1191 if(ftp->no_transfer || conn->bits.no_body) {
1192 /* then we're done with a "head"-like request, goto STOP */
1193 state(conn, FTP_STOP);
1195 /* doesn't transfer any data */
1196 ftp->no_transfer = TRUE;
1198 else if(data->set.ftp_use_port) {
1199 /* We have chosen to use the PORT (or similar) command */
1200 result = ftp_state_use_port(conn, EPRT);
1203 /* We have chosen (this is default) to use the PASV (or similar) command */
1204 result = ftp_state_use_pasv(conn);
1209 static CURLcode ftp_state_post_size(struct connectdata *conn)
1211 CURLcode result = CURLE_OK;
1212 struct FTP *ftp = conn->proto.ftp;
1214 if(ftp->no_transfer) {
1215 /* if a "head"-like request is being made */
1217 /* Determine if server can respond to REST command and therefore
1218 whether it supports range */
1219 NBFTPSENDF(conn, "REST %d", 0);
1221 state(conn, FTP_REST);
1224 result = ftp_state_post_rest(conn);
1229 static CURLcode ftp_state_post_type(struct connectdata *conn)
1231 CURLcode result = CURLE_OK;
1232 struct FTP *ftp = conn->proto.ftp;
1234 if(ftp->no_transfer) {
1235 /* if a "head"-like request is being made */
1237 /* we know ftp->file is a valid pointer to a file name */
1238 NBFTPSENDF(conn, "SIZE %s", ftp->file);
1240 state(conn, FTP_SIZE);
1243 result = ftp_state_post_size(conn);
1248 static CURLcode ftp_state_post_listtype(struct connectdata *conn)
1250 CURLcode result = CURLE_OK;
1251 struct SessionHandle *data = conn->data;
1253 /* If this output is to be machine-parsed, the NLST command might be better
1254 to use, since the LIST command output is not specified or standard in any
1255 way. It has turned out that the NLST list output is not the same on all
1256 servers either... */
1258 NBFTPSENDF(conn, "%s",
1259 data->set.customrequest?data->set.customrequest:
1260 (data->set.ftp_list_only?"NLST":"LIST"));
1262 state(conn, FTP_LIST);
1267 static CURLcode ftp_state_post_retrtype(struct connectdata *conn)
1269 CURLcode result = CURLE_OK;
1271 /* We've sent the TYPE, now we must send the list of prequote strings */
1273 result = ftp_state_quote(conn, TRUE, FTP_RETR_PREQUOTE);
1278 static CURLcode ftp_state_post_stortype(struct connectdata *conn)
1280 CURLcode result = CURLE_OK;
1282 /* We've sent the TYPE, now we must send the list of prequote strings */
1284 result = ftp_state_quote(conn, TRUE, FTP_STOR_PREQUOTE);
1289 static CURLcode ftp_state_post_mdtm(struct connectdata *conn)
1291 CURLcode result = CURLE_OK;
1292 struct FTP *ftp = conn->proto.ftp;
1293 struct SessionHandle *data = conn->data;
1295 /* If we have selected NOBODY and HEADER, it means that we only want file
1296 information. Which in FTP can't be much more than the file size and
1298 if(conn->bits.no_body && data->set.include_header && ftp->file) {
1299 /* The SIZE command is _not_ RFC 959 specified, and therefor many servers
1300 may not support it! It is however the only way we have to get a file's
1303 ftp->no_transfer = TRUE; /* this means no actual transfer will be made */
1305 /* Some servers return different sizes for different modes, and thus we
1306 must set the proper type before we check the size */
1307 NBFTPSENDF(conn, "TYPE %c",
1308 data->set.ftp_ascii?'A':'I');
1309 state(conn, FTP_TYPE);
1312 result = ftp_state_post_type(conn);
1317 /* This is called after the CWD commands have been done in the beginning of
1319 static CURLcode ftp_state_post_cwd(struct connectdata *conn)
1321 CURLcode result = CURLE_OK;
1322 struct FTP *ftp = conn->proto.ftp;
1323 struct SessionHandle *data = conn->data;
1325 /* Requested time of file or time-depended transfer? */
1326 if((data->set.get_filetime || data->set.timecondition) && ftp->file) {
1328 /* we have requested to get the modified-time of the file, this is a white
1329 spot as the MDTM is not mentioned in RFC959 */
1330 NBFTPSENDF(conn, "MDTM %s", ftp->file);
1332 state(conn, FTP_MDTM);
1335 result = ftp_state_post_mdtm(conn);
1341 /* This is called after the TYPE and possible quote commands have been sent */
1342 static CURLcode ftp_state_ul_setup(struct connectdata *conn,
1345 CURLcode result = CURLE_OK;
1346 struct FTP *ftp = conn->proto.ftp;
1347 struct SessionHandle *data = conn->data;
1348 curl_off_t passed=0;
1350 if((conn->resume_from && !sizechecked) ||
1351 ((conn->resume_from > 0) && sizechecked)) {
1352 /* we're about to continue the uploading of a file */
1353 /* 1. get already existing file's size. We use the SIZE command for this
1354 which may not exist in the server! The SIZE command is not in
1357 /* 2. This used to set REST. But since we can do append, we
1358 don't another ftp command. We just skip the source file
1359 offset and then we APPEND the rest on the file instead */
1361 /* 3. pass file-size number of bytes in the source file */
1362 /* 4. lower the infilesize counter */
1363 /* => transfer as usual */
1365 if(conn->resume_from < 0 ) {
1366 /* Got no given size to start from, figure it out */
1367 NBFTPSENDF(conn, "SIZE %s", ftp->file);
1368 state(conn, FTP_STOR_SIZE);
1373 data->set.ftp_append = TRUE;
1375 /* Let's read off the proper amount of bytes from the input. If we knew it
1376 was a proper file we could've just fseek()ed but we only have a stream
1379 /* TODO: allow the ioctlfunction to provide a fast forward function that
1380 can be used here and use this method only as a fallback! */
1382 curl_off_t readthisamountnow = (conn->resume_from - passed);
1383 curl_off_t actuallyread;
1385 if(readthisamountnow > BUFSIZE)
1386 readthisamountnow = BUFSIZE;
1388 actuallyread = (curl_off_t)
1389 conn->fread(data->state.buffer, 1, (size_t)readthisamountnow,
1392 passed += actuallyread;
1393 if(actuallyread != readthisamountnow) {
1394 failf(data, "Could only read %" FORMAT_OFF_T
1395 " bytes from the input", passed);
1396 return CURLE_FTP_COULDNT_USE_REST;
1398 } while(passed != conn->resume_from);
1400 /* now, decrease the size of the read */
1401 if(data->set.infilesize>0) {
1402 data->set.infilesize -= conn->resume_from;
1404 if(data->set.infilesize <= 0) {
1405 infof(data, "File already completely uploaded\n");
1407 /* no data to transfer */
1408 result=Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1410 /* Set no_transfer so that we won't get any error in
1411 * Curl_ftp_done() because we didn't transfer anything! */
1412 ftp->no_transfer = TRUE;
1414 state(conn, FTP_STOP);
1418 /* we've passed, proceed as normal */
1421 NBFTPSENDF(conn, data->set.ftp_append?"APPE %s":"STOR %s",
1424 state(conn, FTP_STOR);
1429 static CURLcode ftp_state_quote(struct connectdata *conn,
1433 CURLcode result = CURLE_OK;
1434 struct FTP *ftp = conn->proto.ftp;
1435 struct SessionHandle *data = conn->data;
1437 struct curl_slist *item;
1442 item = data->set.quote;
1444 case FTP_RETR_PREQUOTE:
1445 case FTP_STOR_PREQUOTE:
1446 item = data->set.prequote;
1449 item = data->set.postquote;
1461 /* Skip count1 items in the linked list */
1462 while((i< ftp->count1) && item) {
1467 NBFTPSENDF(conn, "%s", item->data);
1468 state(conn, instate);
1474 /* No more quote to send, continue to ... */
1478 result = ftp_state_cwd(conn);
1480 case FTP_RETR_PREQUOTE:
1481 NBFTPSENDF(conn, "SIZE %s", ftp->file);
1482 state(conn, FTP_RETR_SIZE);
1484 case FTP_STOR_PREQUOTE:
1485 result = ftp_state_ul_setup(conn, FALSE);
1495 static CURLcode ftp_state_pasv_resp(struct connectdata *conn,
1498 struct FTP *ftp = conn->proto.ftp;
1500 struct SessionHandle *data=conn->data;
1501 Curl_addrinfo *conninfo;
1502 struct Curl_dns_entry *addr=NULL;
1504 unsigned short connectport; /* the local port connect() should use! */
1505 unsigned short newport=0; /* remote port */
1508 /* newhost must be able to hold a full IP-style address in ASCII, which
1509 in the IPv6 case means 5*8-1 = 39 letters */
1510 #define NEWHOST_BUFSIZE 48
1511 char newhost[NEWHOST_BUFSIZE];
1512 char *str=&data->state.buffer[4]; /* start on the first letter */
1514 if((ftp->count1 == 0) &&
1516 /* positive EPSV response */
1517 char *ptr = strchr(str, '(');
1522 if(5 == sscanf(ptr, "%c%c%c%u%c",
1528 char sep1 = separator[0];
1531 /* The four separators should be identical, or else this is an oddly
1532 formatted reply and we bail out immediately. */
1533 for(i=1; i<4; i++) {
1534 if(separator[i] != sep1) {
1535 ptr=NULL; /* set to NULL to signal error */
1542 /* use the same IP we are already connected to */
1543 snprintf(newhost, NEWHOST_BUFSIZE, "%s", conn->ip_addr_str, newhost);
1550 failf(data, "Weirdly formatted EPSV reply");
1551 return CURLE_FTP_WEIRD_PASV_REPLY;
1554 else if((ftp->count1 == 1) &&
1556 /* positive PASV response */
1561 * Scan for a sequence of six comma-separated numbers and use them as
1562 * IP+port indicators.
1564 * Found reply-strings include:
1565 * "227 Entering Passive Mode (127,0,0,1,4,51)"
1566 * "227 Data transfer will passively listen to 127,0,0,1,4,51"
1567 * "227 Entering passive mode. 127,0,0,1,4,51"
1570 if (6 == sscanf(str, "%d,%d,%d,%d,%d,%d",
1571 &ip[0], &ip[1], &ip[2], &ip[3],
1572 &port[0], &port[1]))
1578 failf(data, "Couldn't interpret the 227-response");
1579 return CURLE_FTP_WEIRD_227_FORMAT;
1582 snprintf(newhost, sizeof(newhost),
1583 "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
1584 newport = (port[0]<<8) + port[1];
1586 else if(ftp->count1 == 0) {
1587 /* EPSV failed, move on to PASV */
1589 /* disable it for next transfer */
1590 conn->bits.ftp_use_epsv = FALSE;
1591 infof(data, "disabling EPSV usage\n");
1593 NBFTPSENDF(conn, "PASV", NULL);
1595 /* remain in the FTP_PASV state */
1599 failf(data, "Bad PASV/EPSV response: %03d", ftpcode);
1600 return CURLE_FTP_WEIRD_PASV_REPLY;
1603 /* we got OK from server */
1605 if(data->change.proxy && *data->change.proxy) {
1607 * This is a tunnel through a http proxy and we need to connect to the
1610 * We don't want to rely on a former host lookup that might've expired
1611 * now, instead we remake the lookup here and now!
1613 rc = Curl_resolv(conn, conn->proxy.name, (int)conn->port, &addr);
1614 if(rc == CURLRESOLV_PENDING)
1616 rc = Curl_wait_for_resolv(conn, &addr);
1619 (unsigned short)conn->port; /* we connect to the proxy's port */
1623 /* normal, direct, ftp connection */
1624 rc = Curl_resolv(conn, newhost, newport, &addr);
1625 if(rc == CURLRESOLV_PENDING)
1627 rc = Curl_wait_for_resolv(conn, &addr);
1630 failf(data, "Can't resolve new host %s:%d", newhost, newport);
1631 return CURLE_FTP_CANT_GET_HOST;
1633 connectport = newport; /* we connect to the remote port */
1636 result = Curl_connecthost(conn,
1638 &conn->sock[SECONDARYSOCKET],
1642 Curl_resolv_unlock(data, addr); /* we're done using this address */
1647 conn->bits.tcpconnect = connected; /* simply TRUE or FALSE */
1650 * When this is used from the multi interface, this might've returned with
1651 * the 'connected' set to FALSE and thus we are now awaiting a non-blocking
1652 * connect to connect and we should not be "hanging" here waiting.
1655 if(data->set.verbose)
1656 /* this just dumps information about this second connection */
1657 ftp_pasv_verbose(conn, conninfo, newhost, connectport);
1659 #ifndef CURL_DISABLE_HTTP
1660 if(conn->bits.tunnel_proxy) {
1661 /* FIX: this MUST wait for a proper connect first if 'connected' is
1665 /* We want "seamless" FTP operations through HTTP proxy tunnel */
1666 result = Curl_ConnectHTTPProxyTunnel(conn, SECONDARYSOCKET,
1668 if(CURLE_OK != result)
1671 #endif /* CURL_DISABLE_HTTP */
1673 state(conn, FTP_STOP); /* this phase is completed */
1678 static CURLcode ftp_state_port_resp(struct connectdata *conn,
1681 struct FTP *ftp = conn->proto.ftp;
1682 struct SessionHandle *data = conn->data;
1683 ftpport fcmd = (ftpport)ftp->count1;
1684 CURLcode result = CURLE_OK;
1686 if(ftpcode != 200) {
1687 /* the command failed */
1690 infof(data, "disabling EPRT usage\n");
1691 conn->bits.ftp_use_eprt = FALSE;
1693 else if (LPRT == fcmd) {
1694 infof(data, "disabling LPRT usage\n");
1695 conn->bits.ftp_use_lprt = FALSE;
1700 failf(data, "Failed to do PORT");
1701 result = CURLE_FTP_PORT_FAILED;
1705 result = ftp_state_use_port(conn, fcmd);
1708 infof(data, "Connect data stream actively\n");
1709 state(conn, FTP_STOP); /* end of DO phase */
1715 static CURLcode ftp_state_mdtm_resp(struct connectdata *conn,
1718 CURLcode result = CURLE_OK;
1719 struct FTP *ftp = conn->proto.ftp;
1720 struct SessionHandle *data=conn->data;
1725 /* we got a time. Format should be: "YYYYMMDDHHMMSS[.sss]" where the
1726 last .sss part is optional and means fractions of a second */
1727 int year, month, day, hour, minute, second;
1728 char *buf = data->state.buffer;
1729 if(6 == sscanf(buf+4, "%04d%02d%02d%02d%02d%02d",
1730 &year, &month, &day, &hour, &minute, &second)) {
1731 /* we have a time, reformat it */
1732 time_t secs=time(NULL);
1733 /* using the good old yacc/bison yuck */
1734 snprintf(buf, sizeof(conn->data->state.buffer),
1735 "%04d%02d%02d %02d:%02d:%02d GMT",
1736 year, month, day, hour, minute, second);
1737 /* now, convert this into a time() value: */
1738 data->info.filetime = curl_getdate(buf, &secs);
1741 /* If we asked for a time of the file and we actually got one as well,
1742 we "emulate" a HTTP-style header in our output. */
1744 if(conn->bits.no_body &&
1745 data->set.include_header &&
1747 data->set.get_filetime &&
1748 (data->info.filetime>=0) ) {
1750 time_t clock = (time_t)data->info.filetime;
1751 #ifdef HAVE_GMTIME_R
1753 tm = (struct tm *)gmtime_r(&clock, &buffer);
1755 tm = gmtime(&clock);
1757 /* format: "Tue, 15 Nov 1994 12:45:26" */
1758 snprintf(buf, BUFSIZE-1,
1759 "Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n",
1760 Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
1762 Curl_month[tm->tm_mon],
1767 result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0);
1770 } /* end of a ridiculous amount of conditionals */
1774 infof(data, "unsupported MDTM reply format\n");
1776 case 550: /* "No such file or directory" */
1777 failf(data, "Given file does not exist");
1778 result = CURLE_FTP_COULDNT_RETR_FILE;
1782 if(data->set.timecondition) {
1783 if((data->info.filetime > 0) && (data->set.timevalue > 0)) {
1784 switch(data->set.timecondition) {
1785 case CURL_TIMECOND_IFMODSINCE:
1787 if(data->info.filetime < data->set.timevalue) {
1788 infof(data, "The requested document is not new enough\n");
1789 ftp->no_transfer = TRUE; /* mark this to not transfer data */
1790 state(conn, FTP_STOP);
1794 case CURL_TIMECOND_IFUNMODSINCE:
1795 if(data->info.filetime > data->set.timevalue) {
1796 infof(data, "The requested document is not old enough\n");
1797 ftp->no_transfer = TRUE; /* mark this to not transfer data */
1798 state(conn, FTP_STOP);
1805 infof(data, "Skipping time comparison\n");
1810 result = ftp_state_post_mdtm(conn);
1815 static CURLcode ftp_state_type_resp(struct connectdata *conn,
1819 CURLcode result = CURLE_OK;
1820 struct SessionHandle *data=conn->data;
1822 if(ftpcode != 200) {
1823 failf(data, "Couldn't set desired mode");
1824 return CURLE_FTP_COULDNT_SET_BINARY; /* FIX */
1826 if(instate == FTP_TYPE)
1827 result = ftp_state_post_type(conn);
1828 else if(instate == FTP_LIST_TYPE)
1829 result = ftp_state_post_listtype(conn);
1830 else if(instate == FTP_RETR_TYPE)
1831 result = ftp_state_post_retrtype(conn);
1832 else if(instate == FTP_STOR_TYPE)
1833 result = ftp_state_post_stortype(conn);
1838 static CURLcode ftp_state_post_retr_size(struct connectdata *conn,
1839 curl_off_t filesize)
1841 CURLcode result = CURLE_OK;
1842 struct SessionHandle *data=conn->data;
1843 struct FTP *ftp = conn->proto.ftp;
1845 if (data->set.max_filesize && (filesize > data->set.max_filesize)) {
1846 failf(data, "Maximum file size exceeded");
1847 return CURLE_FILESIZE_EXCEEDED;
1849 ftp->downloadsize = filesize;
1851 if(conn->resume_from) {
1852 /* We always (attempt to) get the size of downloads, so it is done before
1853 this even when not doing resumes. */
1854 if(filesize == -1) {
1855 infof(data, "ftp server doesn't support SIZE\n");
1856 /* We couldn't get the size and therefore we can't know if there really
1857 is a part of the file left to get, although the server will just
1858 close the connection when we start the connection so it won't cause
1859 us any harm, just not make us exit as nicely. */
1862 /* We got a file size report, so we check that there actually is a
1863 part of the file left to get, or else we go home. */
1864 if(conn->resume_from< 0) {
1865 /* We're supposed to download the last abs(from) bytes */
1866 if(filesize < -conn->resume_from) {
1867 failf(data, "Offset (%" FORMAT_OFF_T
1868 ") was beyond file size (%" FORMAT_OFF_T ")",
1869 conn->resume_from, filesize);
1870 return CURLE_BAD_DOWNLOAD_RESUME;
1872 /* convert to size to download */
1873 ftp->downloadsize = -conn->resume_from;
1874 /* download from where? */
1875 conn->resume_from = filesize - ftp->downloadsize;
1878 if(filesize < conn->resume_from) {
1879 failf(data, "Offset (%" FORMAT_OFF_T
1880 ") was beyond file size (%" FORMAT_OFF_T ")",
1881 conn->resume_from, filesize);
1882 return CURLE_BAD_DOWNLOAD_RESUME;
1884 /* Now store the number of bytes we are expected to download */
1885 ftp->downloadsize = filesize-conn->resume_from;
1889 if(ftp->downloadsize == 0) {
1890 /* no data to transfer */
1891 result=Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1892 infof(data, "File already completely downloaded\n");
1894 /* Set no_transfer so that we won't get any error in Curl_ftp_done()
1895 * because we didn't transfer the any file */
1896 ftp->no_transfer = TRUE;
1897 state(conn, FTP_STOP);
1901 /* Set resume file transfer offset */
1902 infof(data, "Instructs server to resume from offset %" FORMAT_OFF_T
1903 "\n", conn->resume_from);
1905 NBFTPSENDF(conn, "REST %" FORMAT_OFF_T, conn->resume_from);
1907 state(conn, FTP_RETR_REST);
1912 NBFTPSENDF(conn, "RETR %s", ftp->file);
1913 state(conn, FTP_RETR);
1919 static CURLcode ftp_state_size_resp(struct connectdata *conn,
1923 CURLcode result = CURLE_OK;
1924 struct SessionHandle *data=conn->data;
1925 curl_off_t filesize;
1926 char *buf = data->state.buffer;
1928 /* get the size from the ascii string: */
1929 filesize = (ftpcode == 213)?curlx_strtoofft(buf+4, NULL, 0):-1;
1931 if(instate == FTP_SIZE) {
1932 if(-1 != filesize) {
1933 snprintf(buf, sizeof(data->state.buffer),
1934 "Content-Length: %" FORMAT_OFF_T "\r\n", filesize);
1935 result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0);
1939 result = ftp_state_post_size(conn);
1941 else if(instate == FTP_RETR_SIZE)
1942 result = ftp_state_post_retr_size(conn, filesize);
1943 else if(instate == FTP_STOR_SIZE) {
1944 conn->resume_from = filesize;
1945 result = ftp_state_ul_setup(conn, TRUE);
1951 static CURLcode ftp_state_rest_resp(struct connectdata *conn,
1955 CURLcode result = CURLE_OK;
1956 struct FTP *ftp = conn->proto.ftp;
1961 if (ftpcode == 350) {
1962 result = Curl_client_write(conn->data, CLIENTWRITE_BOTH,
1963 (char *)"Accept-ranges: bytes\r\n", 0);
1968 result = ftp_state_post_rest(conn);
1972 if (ftpcode != 350) {
1973 failf(conn->data, "Couldn't use REST");
1974 result = CURLE_FTP_COULDNT_USE_REST;
1977 NBFTPSENDF(conn, "RETR %s", ftp->file);
1978 state(conn, FTP_RETR);
1986 static CURLcode ftp_state_stor_resp(struct connectdata *conn,
1989 CURLcode result = CURLE_OK;
1990 struct SessionHandle *data = conn->data;
1991 struct FTP *ftp = conn->proto.ftp;
1994 failf(data, "Failed FTP upload: %0d", ftpcode);
1995 /* oops, we never close the sockets! */
1996 return CURLE_FTP_COULDNT_STOR_FILE;
1999 if(data->set.ftp_use_port) {
2001 /* PORT means we are now awaiting the server to connect to us. */
2002 result = AllowServerConnect(conn);
2007 if(conn->ssl[SECONDARYSOCKET].use) {
2008 /* since we only have a plaintext TCP connection here, we must now
2010 infof(data, "Doing the SSL/TLS handshake on the data stream\n");
2012 result = Curl_SSLConnect(conn, SECONDARYSOCKET);
2017 *(ftp->bytecountp)=0;
2019 /* When we know we're uploading a specified file, we can get the file
2020 size prior to the actual upload. */
2022 Curl_pgrsSetUploadSize(data, data->set.infilesize);
2024 result = Curl_Transfer(conn, -1, -1, FALSE, NULL, /* no download */
2025 SECONDARYSOCKET, ftp->bytecountp);
2026 state(conn, FTP_STOP);
2031 /* for LIST and RETR responses */
2032 static CURLcode ftp_state_get_resp(struct connectdata *conn,
2036 CURLcode result = CURLE_OK;
2037 struct SessionHandle *data = conn->data;
2038 struct FTP *ftp = conn->proto.ftp;
2039 char *buf = data->state.buffer;
2041 if((ftpcode == 150) || (ftpcode == 125)) {
2045 150 Opening BINARY mode data connection for /etc/passwd (2241
2046 bytes). (ok, the file is being transfered)
2049 150 Opening ASCII mode data connection for /bin/ls
2052 150 ASCII data connection for /bin/ls (137.167.104.91,37445) (0 bytes).
2055 150 Opening ASCII mode data connection for /linux/fisk/kpanelrc (0.0.0.0,0) (545 bytes).
2058 125 Data connection already open; Transfer starting. */
2060 curl_off_t size=-1; /* default unknown size */
2064 * It appears that there are FTP-servers that return size 0 for files when
2065 * SIZE is used on the file while being in BINARY mode. To work around
2066 * that (stupid) behavior, we attempt to parse the RETR response even if
2067 * the SIZE returned size zero.
2069 * Debugging help from Salvatore Sorrentino on February 26, 2003.
2072 if((instate != FTP_LIST) &&
2073 !data->set.ftp_ascii &&
2074 (ftp->downloadsize < 1)) {
2076 * It seems directory listings either don't show the size or very
2077 * often uses size 0 anyway. ASCII transfers may very well turn out
2078 * that the transfered amount of data is not the same as this line
2079 * tells, why using this number in those cases only confuses us.
2081 * Example D above makes this parsing a little tricky */
2083 bytes=strstr(buf, " bytes");
2086 /* this is a hint there is size information in there! ;-) */
2088 /* scan for the left parenthesis and break there */
2091 /* skip only digits */
2092 if(!isdigit((int)*bytes)) {
2096 /* one more estep backwards */
2099 /* if we have nothing but digits: */
2101 /* get the number! */
2102 size = curlx_strtoofft(bytes, NULL, 0);
2106 else if(ftp->downloadsize > -1)
2107 size = ftp->downloadsize;
2109 if(data->set.ftp_use_port) {
2111 result = AllowServerConnect(conn);
2116 if(conn->ssl[SECONDARYSOCKET].use) {
2117 /* since we only have a plaintext TCP connection here, we must now
2119 infof(data, "Doing the SSL/TLS handshake on the data stream\n");
2120 result = Curl_SSLConnect(conn, SECONDARYSOCKET);
2125 if(size > conn->maxdownload && conn->maxdownload > 0)
2126 size = conn->size = conn->maxdownload;
2128 if(instate != FTP_LIST)
2129 infof(data, "Getting file with size: %" FORMAT_OFF_T "\n", size);
2132 result=Curl_Transfer(conn, SECONDARYSOCKET, size, FALSE,
2134 -1, NULL); /* no upload here */
2138 state(conn, FTP_STOP);
2141 if((instate == FTP_LIST) && (ftpcode == 450)) {
2142 /* simply no matching files in the dir listing */
2143 ftp->no_transfer = TRUE; /* don't download anything */
2144 state(conn, FTP_STOP); /* this phase is over */
2147 failf(data, "%s", buf+4);
2148 return CURLE_FTP_COULDNT_RETR_FILE;
2155 /* after USER, PASS and ACCT */
2156 static CURLcode ftp_state_loggedin(struct connectdata *conn)
2158 CURLcode result = CURLE_OK;
2159 struct SessionHandle *data = conn->data;
2160 infof(data, "We have successfully logged in\n");
2163 if(data->set.krb4) {
2164 /* We are logged in, asked to use Kerberos. Set the requested
2167 if(conn->sec_complete)
2169 Curl_sec_set_protection_level(conn);
2171 /* We may need to issue a KAUTH here to have access to the files
2172 * do it if user supplied a password
2174 if(conn->passwd && *conn->passwd) {
2176 result = Curl_krb_kauth(conn);
2182 if(conn->ssl[FIRSTSOCKET].use) {
2183 /* PBSZ = PROTECTION BUFFER SIZE.
2185 The 'draft-murray-auth-ftp-ssl' (draft 12, page 7) says:
2187 Specifically, the PROT command MUST be preceded by a PBSZ
2188 command and a PBSZ command MUST be preceded by a successful
2189 security data exchange (the TLS negotiation in this case)
2191 ... (and on page 8):
2193 Thus the PBSZ command must still be issued, but must have a
2194 parameter of '0' to indicate that no buffering is taking place
2195 and the data connection should not be encapsulated.
2197 NBFTPSENDF(conn, "PBSZ %d", 0);
2198 state(conn, FTP_PBSZ);
2201 result = ftp_state_pwd(conn);
2206 /* for USER and PASS responses */
2207 static CURLcode ftp_state_user_resp(struct connectdata *conn,
2211 CURLcode result = CURLE_OK;
2212 struct SessionHandle *data = conn->data;
2213 struct FTP *ftp = conn->proto.ftp;
2214 (void)instate; /* no use for this yet */
2216 if((ftpcode == 331) && (ftp->state == FTP_USER)) {
2217 /* 331 Password required for ...
2218 (the server requires to send the user's password too) */
2219 NBFTPSENDF(conn, "PASS %s", ftp->passwd?ftp->passwd:"");
2220 state(conn, FTP_PASS);
2222 else if(ftpcode/100 == 2) {
2223 /* 230 User ... logged in.
2224 (the user logged in with or without password) */
2225 result = ftp_state_loggedin(conn);
2227 else if(ftpcode == 332) {
2228 if(data->set.ftp_account) {
2229 NBFTPSENDF(conn, "ACCT %s", data->set.ftp_account);
2230 state(conn, FTP_ACCT);
2233 failf(data, "ACCT requested but none available");
2234 result = CURLE_LOGIN_DENIED;
2238 /* All other response codes, like:
2240 530 User ... access denied
2241 (the server denies to log the specified user) */
2242 failf(data, "Access denied: %03d", ftpcode);
2243 result = CURLE_LOGIN_DENIED;
2248 /* for ACCT response */
2249 static CURLcode ftp_state_acct_resp(struct connectdata *conn,
2252 CURLcode result = CURLE_OK;
2253 struct SessionHandle *data = conn->data;
2254 if(ftpcode != 230) {
2255 failf(data, "ACCT rejected by server: %03d", ftpcode);
2256 result = CURLE_FTP_WEIRD_PASS_REPLY; /* FIX */
2259 result = ftp_state_loggedin(conn);
2265 static CURLcode ftp_statemach_act(struct connectdata *conn)
2268 curl_socket_t sock = conn->sock[FIRSTSOCKET];
2269 struct SessionHandle *data=conn->data;
2271 struct FTP *ftp = conn->proto.ftp;
2272 static const char * const ftpauth[] = {
2278 /* we have a piece of a command still left to send */
2280 result = Curl_write(conn, sock, ftp->sendthis + ftp->sendsize -
2281 ftp->sendleft, ftp->sendleft, &written);
2285 if(written != (ssize_t)ftp->sendleft) {
2286 /* only a fraction was sent */
2287 ftp->sendleft -= written;
2290 free(ftp->sendthis);
2292 ftp->sendleft = ftp->sendsize = 0;
2293 ftp->response = Curl_tvnow();
2298 /* we read a piece of response */
2299 result = ftp_readresp(sock, conn, &ftpcode, &nread);
2304 /* we have now received a full FTP server response */
2305 switch(ftp->state) {
2307 if(ftpcode != 220) {
2308 failf(data, "This doesn't seem like a nice ftp-server response");
2309 return CURLE_FTP_WEIRD_SERVER_REPLY;
2312 /* We have received a 220 response fine, now we proceed. */
2314 if(data->set.krb4) {
2315 /* If not anonymous login, try a secure login. Note that this
2316 procedure is still BLOCKING. */
2318 Curl_sec_request_prot(conn, "private");
2319 /* We set private first as default, in case the line below fails to
2320 set a valid level */
2321 Curl_sec_request_prot(conn, data->set.krb4_level);
2323 if(Curl_sec_login(conn) != 0)
2324 infof(data, "Logging in with password in cleartext!\n");
2326 infof(data, "Authentication successful\n");
2330 if(data->set.ftp_ssl && !conn->ssl[FIRSTSOCKET].use) {
2331 /* We don't have a SSL/TLS connection yet, but FTPS is
2332 requested. Try a FTPS connection now */
2335 switch(data->set.ftpsslauth) {
2336 case CURLFTPAUTH_DEFAULT:
2337 case CURLFTPAUTH_SSL:
2338 ftp->count2 = 1; /* add one to get next */
2341 case CURLFTPAUTH_TLS:
2342 ftp->count2 = -1; /* subtract one to get next */
2346 failf(data, "unsupported parameter to CURLOPT_FTPSSLAUTH: %d\n",
2347 data->set.ftpsslauth);
2348 return CURLE_FAILED_INIT; /* we don't know what to do */
2350 NBFTPSENDF(conn, "AUTH %s", ftpauth[ftp->count1]);
2351 state(conn, FTP_AUTH);
2354 ftp_state_user(conn);
2362 /* we have gotten the response to a previous AUTH command */
2364 /* RFC2228 (page 5) says:
2366 * If the server is willing to accept the named security mechanism,
2367 * and does not require any security data, it must respond with
2368 * reply code 234/334.
2371 if((ftpcode == 234) || (ftpcode == 334)) {
2372 /* Curl_SSLConnect is BLOCKING */
2373 result = Curl_SSLConnect(conn, FIRSTSOCKET);
2376 conn->protocol |= PROT_FTPS;
2377 conn->ssl[SECONDARYSOCKET].use = FALSE; /* clear-text data */
2379 else if(ftp->count3 < 1) {
2381 ftp->count1 += ftp->count2; /* get next attempt */
2382 NBFTPSENDF(conn, "AUTH %s", ftpauth[ftp->count1]);
2383 /* remain in this same state */
2386 result = ftp_state_user(conn);
2394 result = ftp_state_user_resp(conn, ftpcode, ftp->state);
2398 result = ftp_state_acct_resp(conn, ftpcode);
2402 /* FIX: check response code */
2404 /* For TLS, the data connection can have one of two security levels.
2406 1) Clear (requested by 'PROT C')
2408 2)Private (requested by 'PROT P')
2410 if(!conn->ssl[SECONDARYSOCKET].use) {
2411 NBFTPSENDF(conn, "PROT %c", 'P');
2412 state(conn, FTP_PROT);
2415 result = ftp_state_pwd(conn);
2423 if(ftpcode/100 == 2)
2424 /* We have enabled SSL for the data connection! */
2425 conn->ssl[SECONDARYSOCKET].use = TRUE;
2426 /* FTP servers typically responds with 500 if they decide to reject
2428 else if(data->set.ftp_ssl> CURLFTPSSL_CONTROL)
2429 /* we failed and bails out */
2430 return CURLE_FTP_SSL_FAILED;
2432 result = ftp_state_pwd(conn);
2438 if(ftpcode == 257) {
2439 char *dir = (char *)malloc(nread+1);
2441 char *ptr=&data->state.buffer[4]; /* start on the first letter */
2444 return CURLE_OUT_OF_MEMORY;
2446 /* Reply format is like
2447 257<space>"<directory-name>"<space><commentary> and the RFC959
2450 The directory name can contain any character; embedded
2451 double-quotes should be escaped by double-quotes (the
2452 "quote-doubling" convention).
2455 /* it started good */
2457 while(ptr && *ptr) {
2459 if('\"' == ptr[1]) {
2460 /* "quote-doubling" */
2466 *store = '\0'; /* zero terminate */
2467 break; /* get out of this loop */
2475 ftp->entrypath =dir; /* remember this */
2476 infof(data, "Entry path is '%s'\n", ftp->entrypath);
2479 /* couldn't get the path */
2481 infof(data, "Failed to figure out path\n");
2484 state(conn, FTP_STOP); /* we are done with the CONNECT phase! */
2485 infof(data, "protocol connect phase DONE\n");
2490 case FTP_RETR_PREQUOTE:
2491 case FTP_STOR_PREQUOTE:
2492 if(ftpcode >= 400) {
2493 failf(conn->data, "QUOT command failed with %03d", ftpcode);
2494 return CURLE_FTP_QUOTE_ERROR;
2496 result = ftp_state_quote(conn, FALSE, ftp->state);
2503 if(ftpcode/100 != 2) {
2504 /* failure to CWD there */
2505 if(conn->data->set.ftp_create_missing_dirs &&
2506 ftp->count1 && !ftp->count2) {
2508 ftp->count2++; /* counter to prevent CWD-MKD loops */
2509 NBFTPSENDF(conn, "MKD %s", ftp->dirs[ftp->count1 - 1]);
2510 state(conn, FTP_MKD);
2513 /* return failure */
2514 return CURLE_FTP_ACCESS_DENIED;
2519 if(++ftp->count1 <= ftp->dirdepth) {
2521 NBFTPSENDF(conn, "CWD %s", ftp->dirs[ftp->count1 - 1]);
2524 result = ftp_state_post_cwd(conn);
2532 if(ftpcode/100 != 2) {
2533 /* failure to MKD the dir */
2534 failf(data, "Failed to MKD dir: %03d", ftpcode);
2535 return CURLE_FTP_ACCESS_DENIED;
2537 state(conn, FTP_CWD);
2539 NBFTPSENDF(conn, "CWD %s", ftp->dirs[ftp->count1 - 1]);
2543 result = ftp_state_mdtm_resp(conn, ftpcode);
2550 result = ftp_state_type_resp(conn, ftpcode, ftp->state);
2556 result = ftp_state_size_resp(conn, ftpcode, ftp->state);
2561 result = ftp_state_rest_resp(conn, ftpcode, ftp->state);
2565 result = ftp_state_pasv_resp(conn, ftpcode);
2569 result = ftp_state_port_resp(conn, ftpcode);
2574 result = ftp_state_get_resp(conn, ftpcode, ftp->state);
2578 result = ftp_state_stor_resp(conn, ftpcode);
2582 /* fallthrough, just stop! */
2584 /* internal error */
2585 state(conn, FTP_STOP);
2593 /* Returns timeout in ms. 0 or negative number means the timeout has already
2595 static long ftp_state_timeout(struct connectdata *conn)
2597 struct SessionHandle *data=conn->data;
2598 struct FTP *ftp = conn->proto.ftp;
2599 long timeout_ms=360000; /* in milliseconds */
2601 if(data->set.ftp_response_timeout )
2602 /* if CURLOPT_FTP_RESPONSE_TIMEOUT is set, use that to determine remaining
2603 time. Also, use ftp->response because FTP_RESPONSE_TIMEOUT is supposed
2604 to govern the response for any given ftp response, not for the time
2605 from connect to the given ftp response. */
2606 timeout_ms = data->set.ftp_response_timeout*1000 - /* timeout time */
2607 Curl_tvdiff(Curl_tvnow(), ftp->response); /* spent time */
2608 else if(data->set.timeout)
2609 /* if timeout is requested, find out how much remaining time we have */
2610 timeout_ms = data->set.timeout*1000 - /* timeout time */
2611 Curl_tvdiff(Curl_tvnow(), conn->now); /* spent time */
2613 /* Without a requested timeout, we only wait 'response_time' seconds for
2614 the full response to arrive before we bail out */
2615 timeout_ms = ftp->response_time*1000 -
2616 Curl_tvdiff(Curl_tvnow(), ftp->response); /* spent time */
2622 /* called repeatedly until done from multi.c */
2623 CURLcode Curl_ftp_multi_statemach(struct connectdata *conn,
2626 curl_socket_t sock = conn->sock[FIRSTSOCKET];
2628 struct SessionHandle *data=conn->data;
2629 struct FTP *ftp = conn->proto.ftp;
2630 CURLcode result = CURLE_OK;
2631 long timeout_ms = ftp_state_timeout(conn);
2633 *done = FALSE; /* default to not done yet */
2635 if(timeout_ms <= 0) {
2636 failf(data, "FTP response timeout");
2637 return CURLE_OPERATION_TIMEDOUT;
2640 rc = Curl_select(ftp->sendleft?CURL_SOCKET_BAD:sock, /* reading */
2641 ftp->sendleft?sock:CURL_SOCKET_BAD, /* writing */
2645 failf(data, "select error");
2646 return CURLE_OUT_OF_MEMORY;
2649 result = ftp_statemach_act(conn);
2650 *done = (ftp->state == FTP_STOP);
2652 /* if rc == 0, then select() timed out */
2657 static CURLcode ftp_easy_statemach(struct connectdata *conn)
2659 curl_socket_t sock = conn->sock[FIRSTSOCKET];
2661 struct SessionHandle *data=conn->data;
2662 struct FTP *ftp = conn->proto.ftp;
2663 CURLcode result = CURLE_OK;
2665 while(ftp->state != FTP_STOP) {
2666 long timeout_ms = ftp_state_timeout(conn);
2668 if(timeout_ms <=0 ) {
2669 failf(data, "FTP response timeout");
2670 return CURLE_OPERATION_TIMEDOUT; /* already too little time */
2673 rc = Curl_select(ftp->sendleft?CURL_SOCKET_BAD:sock, /* reading */
2674 ftp->sendleft?sock:CURL_SOCKET_BAD, /* writing */
2678 failf(data, "select error");
2679 return CURLE_OUT_OF_MEMORY;
2682 result = CURLE_OPERATION_TIMEDOUT;
2686 result = ftp_statemach_act(conn);
2696 * Curl_ftp_connect() should do everything that is to be considered a part of
2697 * the connection phase.
2699 * The variable 'done' points to will be TRUE if the protocol-layer connect
2700 * phase is done when this function returns, or FALSE is not. When called as
2701 * a part of the easy interface, it will always be TRUE.
2703 CURLcode Curl_ftp_connect(struct connectdata *conn,
2704 bool *done) /* see description above */
2709 *done = FALSE; /* default to not done yet */
2711 ftp = (struct FTP *)calloc(sizeof(struct FTP), 1);
2713 return CURLE_OUT_OF_MEMORY;
2715 conn->proto.ftp = ftp;
2717 /* We always support persistant connections on ftp */
2718 conn->bits.close = FALSE;
2720 /* get some initial data into the ftp struct */
2721 ftp->bytecountp = &conn->bytecount;
2723 /* no need to duplicate them, this connectdata struct won't change */
2724 ftp->user = conn->user;
2725 ftp->passwd = conn->passwd;
2726 if (isBadFtpString(ftp->user) || isBadFtpString(ftp->passwd))
2727 return CURLE_URL_MALFORMAT;
2729 ftp->response_time = 3600; /* set default response time-out */
2731 #ifndef CURL_DISABLE_HTTP
2732 if (conn->bits.tunnel_proxy) {
2734 /* We want "seamless" FTP operations through HTTP proxy tunnel */
2735 result = Curl_ConnectHTTPProxyTunnel(conn, FIRSTSOCKET,
2736 conn->host.name, conn->remote_port);
2737 if(CURLE_OK != result)
2740 #endif /* CURL_DISABLE_HTTP */
2742 if(conn->protocol & PROT_FTPS) {
2744 /* FTPS is simply ftp with SSL for the control channel */
2745 /* now, perform the SSL initialization for this socket */
2746 result = Curl_SSLConnect(conn, FIRSTSOCKET);
2751 /* When we connect, we start in the state where we await the 220
2753 state(conn, FTP_WAIT220);
2754 ftp->response = Curl_tvnow(); /* start response time-out now! */
2756 if(conn->data->state.used_interface == Curl_if_multi)
2757 result = Curl_ftp_multi_statemach(conn, done);
2759 result = ftp_easy_statemach(conn);
2767 /***********************************************************************
2771 * The DONE function. This does what needs to be done after a single DO has
2774 * Input argument is already checked for validity.
2776 CURLcode Curl_ftp_done(struct connectdata *conn, CURLcode status)
2778 struct SessionHandle *data = conn->data;
2779 struct FTP *ftp = conn->proto.ftp;
2782 CURLcode result=CURLE_OK;
2783 bool was_ctl_valid = ftp->ctl_valid;
2788 /* now store a copy of the directory we are in */
2790 free(ftp->prevpath);
2792 path = curl_unescape(conn->path, 0); /* get the "raw" path */
2794 return CURLE_OUT_OF_MEMORY;
2796 flen = ftp->file?strlen(ftp->file):0; /* file is "raw" already */
2797 dlen = strlen(path)-flen;
2799 ftp->prevpath = path;
2801 /* if 'path' is not the whole string */
2802 ftp->prevpath[dlen]=0; /* terminate */
2803 infof(data, "Remembering we are in dir %s\n", ftp->prevpath);
2806 ftp->prevpath = NULL; /* no path */
2809 /* free the dir tree and file parts */
2812 ftp->ctl_valid = FALSE;
2814 if(data->set.upload) {
2815 if((-1 != data->set.infilesize) &&
2816 (data->set.infilesize != *ftp->bytecountp) &&
2818 failf(data, "Uploaded unaligned file size (%" FORMAT_OFF_T
2819 " out of %" FORMAT_OFF_T " bytes)",
2820 *ftp->bytecountp, data->set.infilesize);
2821 conn->bits.close = TRUE; /* close this connection since we don't
2822 know what state this error leaves us in */
2823 return CURLE_PARTIAL_FILE;
2827 if((-1 != conn->size) && (conn->size != *ftp->bytecountp) &&
2828 (conn->maxdownload != *ftp->bytecountp)) {
2829 failf(data, "Received only partial file: %" FORMAT_OFF_T " bytes",
2831 conn->bits.close = TRUE; /* close this connection since we don't
2832 know what state this error leaves us in */
2833 return CURLE_PARTIAL_FILE;
2835 else if(!ftp->dont_check &&
2836 !*ftp->bytecountp &&
2838 /* We consider this an error, but there's no true FTP error received
2839 why we need to continue to "read out" the server response too.
2840 We don't want to leave a "waiting" server reply if we'll get told
2841 to make a second request on this same connection! */
2842 failf(data, "No data was received!");
2843 result = CURLE_FTP_COULDNT_RETR_FILE;
2848 case CURLE_BAD_DOWNLOAD_RESUME:
2849 case CURLE_FTP_WEIRD_PASV_REPLY:
2850 case CURLE_FTP_PORT_FAILED:
2851 case CURLE_FTP_COULDNT_SET_BINARY:
2852 case CURLE_FTP_COULDNT_RETR_FILE:
2853 case CURLE_FTP_ACCESS_DENIED:
2854 /* the connection stays alive fine even though this happened */
2856 case CURLE_OK: /* doesn't affect the control connection's status */
2857 ftp->ctl_valid = was_ctl_valid;
2859 default: /* by default, an error means the control connection is
2860 wedged and should not be used anymore */
2861 ftp->ctl_valid = FALSE;
2866 Curl_sec_fflush_fd(conn, conn->sock[SECONDARYSOCKET]);
2869 /* shut down the socket to inform the server we're done */
2872 shutdown(conn->sock[SECONDARYSOCKET],2); /* SD_BOTH */
2875 sclose(conn->sock[SECONDARYSOCKET]);
2877 conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
2879 if(!ftp->no_transfer && !status) {
2880 /* Let's see what the server says about the transfer we just performed,
2881 * but lower the timeout as sometimes this connection has died while the
2882 * data has been transfered. This happens when doing through NATs etc that
2883 * abandon old silent connections.
2885 ftp->response_time = 60; /* give it only a minute for now */
2887 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
2889 ftp->response_time = 3600; /* set this back to one hour waits */
2891 if(!nread && (CURLE_OPERATION_TIMEDOUT == result)) {
2892 failf(data, "control connection looks dead");
2899 if(!ftp->dont_check) {
2900 /* 226 Transfer complete, 250 Requested file action okay, completed. */
2901 if((ftpcode != 226) && (ftpcode != 250)) {
2902 failf(data, "server did not report OK, got %d", ftpcode);
2903 return CURLE_FTP_WRITE_ERROR;
2908 /* clear these for next connection */
2909 ftp->no_transfer = FALSE;
2910 ftp->dont_check = FALSE;
2912 if (!result && conn->sec_conn) { /* 3rd party transfer */
2913 /* "done" with the secondary connection */
2914 result = Curl_ftp_done(conn->sec_conn, status);
2917 /* Send any post-transfer QUOTE strings? */
2918 if(!status && !result && data->set.postquote)
2919 result = ftp_sendquote(conn, data->set.postquote);
2924 /***********************************************************************
2928 * Where a 'quote' means a list of custom commands to send to the server.
2929 * The quote list is passed as an argument.
2933 CURLcode ftp_sendquote(struct connectdata *conn, struct curl_slist *quote)
2935 struct curl_slist *item;
2943 FTPSENDF(conn, "%s", item->data);
2945 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
2949 if (ftpcode >= 400) {
2950 failf(conn->data, "QUOT string not accepted: %s", item->data);
2951 return CURLE_FTP_QUOTE_ERROR;
2961 /***********************************************************************
2963 * ftp_transfertype()
2965 * Set transfer type. We only deal with ASCII or BINARY so this function
2968 static CURLcode ftp_transfertype(struct connectdata *conn,
2971 struct SessionHandle *data = conn->data;
2976 FTPSENDF(conn, "TYPE %s", ascii?"A":"I");
2978 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
2982 if(ftpcode != 200) {
2983 failf(data, "Couldn't set %s mode",
2984 ascii?"ASCII":"binary");
2985 return ascii? CURLE_FTP_COULDNT_SET_ASCII:CURLE_FTP_COULDNT_SET_BINARY;
2991 /***************************************************************************
2993 * ftp_pasv_verbose()
2995 * This function only outputs some informationals about this second connection
2996 * when we've issued a PASV command before and thus we have connected to a
2997 * possibly new IP address.
3001 ftp_pasv_verbose(struct connectdata *conn,
3003 char *newhost, /* ascii version */
3007 Curl_printable_address(ai, buf, sizeof(buf));
3008 infof(conn->data, "Connecting to %s (%s) port %d\n", newhost, buf, port);
3012 Check if this is a range download, and if so, set the internal variables
3016 static CURLcode ftp_range(struct connectdata *conn)
3018 curl_off_t from, to;
3019 curl_off_t totalsize=-1;
3022 struct SessionHandle *data = conn->data;
3023 struct FTP *ftp = conn->proto.ftp;
3025 if(conn->bits.use_range && conn->range) {
3026 from=curlx_strtoofft(conn->range, &ptr, 0);
3027 while(ptr && *ptr && (isspace((int)*ptr) || (*ptr=='-')))
3029 to=curlx_strtoofft(ptr, &ptr2, 0);
3031 /* we didn't get any digit */
3034 if((-1 == to) && (from>=0)) {
3036 conn->resume_from = from;
3037 infof(data, "FTP RANGE %" FORMAT_OFF_T " to end of file\n", from);
3042 conn->maxdownload = -from;
3043 conn->resume_from = from;
3044 infof(data, "FTP RANGE the last %" FORMAT_OFF_T " bytes\n", totalsize);
3048 totalsize = to-from;
3049 conn->maxdownload = totalsize+1; /* include the last mentioned byte */
3050 conn->resume_from = from;
3051 infof(data, "FTP RANGE from %" FORMAT_OFF_T
3052 " getting %" FORMAT_OFF_T " bytes\n", from, conn->maxdownload);
3054 infof(data, "range-download from %" FORMAT_OFF_T
3055 " to %" FORMAT_OFF_T ", totally %" FORMAT_OFF_T " bytes\n",
3056 from, to, conn->maxdownload);
3057 ftp->dont_check = TRUE; /* dont check for successful transfer */
3064 * Curl_ftp_nextconnect()
3066 * This function shall be called when the second FTP (data) connection is
3070 CURLcode Curl_ftp_nextconnect(struct connectdata *conn)
3072 struct SessionHandle *data=conn->data;
3073 CURLcode result = CURLE_OK;
3075 /* the ftp struct is inited in Curl_ftp_connect() */
3076 struct FTP *ftp = conn->proto.ftp;
3078 infof(data, "DO-MORE phase starts\n");
3080 if(!ftp->no_transfer && !conn->bits.no_body) {
3081 /* a transfer is about to take place */
3083 if(data->set.upload) {
3084 NBFTPSENDF(conn, "TYPE %c", data->set.ftp_ascii?'A':'I');
3085 state(conn, FTP_STOR_TYPE);
3089 ftp->downloadsize = -1; /* unknown as of yet */
3091 result = ftp_range(conn);
3094 else if((data->set.ftp_list_only) || !ftp->file) {
3095 /* The specified path ends with a slash, and therefore we think this
3096 is a directory that is requested, use LIST. But before that we
3097 need to set ASCII transfer mode. */
3098 NBFTPSENDF(conn, "TYPE A", NULL);
3099 state(conn, FTP_LIST_TYPE);
3102 NBFTPSENDF(conn, "TYPE %c", data->set.ftp_ascii?'A':'I');
3103 state(conn, FTP_RETR_TYPE);
3106 result = ftp_easy_statemach(conn);
3109 if(ftp->no_transfer)
3110 /* no data to transfer. FIX: it feels like a kludge to have this here
3112 result=Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
3114 /* end of transfer */
3115 infof(data, "DO-MORE phase ends\n");
3122 /***********************************************************************
3126 * This is the actual DO function for FTP. Get a file/directory according to
3127 * the options previously setup.
3131 CURLcode ftp_perform(struct connectdata *conn,
3132 bool *connected, /* connect status after PASV / PORT */
3135 /* this is FTP and no proxy */
3136 CURLcode result=CURLE_OK;
3137 struct SessionHandle *data=conn->data;
3139 infof(data, "DO phase starts\n");
3141 *dophase_done = FALSE; /* not done yet */
3143 /* start the first command in the DO phase */
3144 result = ftp_state_quote(conn, TRUE, FTP_QUOTE);
3148 /* run the state-machine */
3149 if(conn->data->state.used_interface == Curl_if_multi)
3150 result = Curl_ftp_multi_statemach(conn, dophase_done);
3152 result = ftp_easy_statemach(conn);
3153 *dophase_done = TRUE; /* with the easy interface we are done here */
3155 *connected = conn->bits.tcpconnect;
3158 infof(data, "DO phase is comlete\n");
3163 /***********************************************************************
3167 * This function is registered as 'curl_do' function. It decodes the path
3168 * parts etc as a wrapper to the actual DO function (ftp_perform).
3170 * The input argument is already checked for validity.
3172 CURLcode Curl_ftp(struct connectdata *conn, bool *done)
3174 CURLcode retcode = CURLE_OK;
3176 *done = FALSE; /* default to false */
3178 retcode = ftp_parse_url_path(conn);
3182 if (conn->sec_conn) {
3183 /* 3rd party transfer */
3184 *done = TRUE; /* BLOCKING */
3185 retcode = ftp_3rdparty(conn);
3188 retcode = ftp_regular_transfer(conn, done);
3193 /***********************************************************************
3195 * Curl_(nb)ftpsendf()
3197 * Sends the formated string as a ftp command to a ftp server
3199 * NOTE: we build the command in a fixed-length buffer, which sets length
3200 * restrictions on the command!
3202 * The "nb" version is made to Never Block.
3204 CURLcode Curl_nbftpsendf(struct connectdata *conn,
3205 const char *fmt, ...)
3207 ssize_t bytes_written;
3211 CURLcode res = CURLE_OK;
3212 struct FTP *ftp = conn->proto.ftp;
3213 struct SessionHandle *data = conn->data;
3217 vsnprintf(s, 250, fmt, ap);
3220 strcat(s, "\r\n"); /* append a trailing CRLF */
3223 write_len = strlen(s);
3225 res = Curl_write(conn, conn->sock[FIRSTSOCKET], sptr, write_len,
3231 if(conn->data->set.verbose)
3232 Curl_debug(conn->data, CURLINFO_HEADER_OUT, sptr, bytes_written,
3235 if(bytes_written != (ssize_t)write_len) {
3236 /* the whole chunk was not sent, store the rest of the data */
3237 write_len -= bytes_written;
3238 sptr += bytes_written;
3239 ftp->sendthis = malloc(write_len);
3241 memcpy(ftp->sendthis, sptr, write_len);
3242 ftp->sendsize=ftp->sendleft=write_len;
3245 failf(data, "out of memory");
3246 res = CURLE_OUT_OF_MEMORY;
3250 ftp->response = Curl_tvnow();
3255 CURLcode Curl_ftpsendf(struct connectdata *conn,
3256 const char *fmt, ...)
3258 ssize_t bytes_written;
3262 CURLcode res = CURLE_OK;
3266 vsnprintf(s, 250, fmt, ap);
3269 strcat(s, "\r\n"); /* append a trailing CRLF */
3272 write_len = strlen(s);
3275 res = Curl_write(conn, conn->sock[FIRSTSOCKET], sptr, write_len,
3281 if(conn->data->set.verbose)
3282 Curl_debug(conn->data, CURLINFO_HEADER_OUT, sptr, bytes_written, conn);
3284 if(bytes_written != (ssize_t)write_len) {
3285 write_len -= bytes_written;
3286 sptr += bytes_written;
3295 /***********************************************************************
3299 * This should be called before calling sclose() on an ftp control connection
3300 * (not data connections). We should then wait for the response from the
3301 * server before returning. The calling code should then try to close the
3305 static CURLcode ftp_quit(struct connectdata *conn)
3307 CURLcode result = CURLE_OK;
3309 if(conn->proto.ftp->ctl_valid) {
3310 NBFTPSENDF(conn, "QUIT", NULL);
3311 state(conn, FTP_QUIT);
3313 result = ftp_easy_statemach(conn);
3319 /***********************************************************************
3321 * Curl_ftp_disconnect()
3323 * Disconnect from an FTP server. Cleanup protocol-specific per-connection
3324 * resources. BLOCKING.
3326 CURLcode Curl_ftp_disconnect(struct connectdata *conn)
3328 struct FTP *ftp= conn->proto.ftp;
3330 /* We cannot send quit unconditionally. If this connection is stale or
3331 bad in any way, sending quit and waiting around here will make the
3332 disconnect wait in vain and cause more problems than we need to.
3334 ftp_quit() will check the state of ftp->ctl_valid. If it's ok it
3335 will try to send the QUIT command, otherwise it will just return.
3338 /* The FTP session may or may not have been allocated/setup at this point! */
3340 (void)ftp_quit(conn); /* ignore errors on the QUIT */
3343 free(ftp->entrypath);
3350 free(ftp->prevpath);
3351 ftp->prevpath = NULL;
3357 /***********************************************************************
3361 * Makes a directory on the FTP server.
3365 static CURLcode ftp_mkd(struct connectdata *conn, char *path)
3367 CURLcode result=CURLE_OK;
3368 int ftpcode; /* for ftp status */
3371 /* Create a directory on the remote server */
3372 FTPSENDF(conn, "MKD %s", path);
3374 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
3381 infof( conn->data , "Created remote directory %s\n" , path );
3384 failf(conn->data, "Permission denied to make directory %s", path);
3385 result = CURLE_FTP_ACCESS_DENIED;
3388 failf(conn->data, "unrecognized MKD response: %d", ftpcode );
3389 result = CURLE_FTP_ACCESS_DENIED;
3395 /***********************************************************************
3399 * Send 'CWD' to the remote server to Change Working Directory. It is the ftp
3400 * version of the unix 'cd' command. This function is only called from the
3401 * ftp_cwd_and_mkd() function these days.
3403 * This function does NOT call failf().
3406 CURLcode ftp_cwd(struct connectdata *conn, char *path)
3412 FTPSENDF(conn, "CWD %s", path);
3413 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
3415 /* According to RFC959, CWD is supposed to return 250 on success, but
3416 there seem to be non-compliant FTP servers out there that return 200,
3417 so we accept any '2xy' code here. */
3418 if (ftpcode/100 != 2)
3419 result = CURLE_FTP_ACCESS_DENIED;
3425 /***********************************************************************
3429 * Change to the given directory. If the directory is not present, and we
3430 * have been told to allow it, then create the directory and cd to it.
3433 static CURLcode ftp_cwd_and_mkd(struct connectdata *conn, char *path)
3437 result = ftp_cwd(conn, path);
3439 if(conn->data->set.ftp_create_missing_dirs) {
3440 result = ftp_mkd(conn, path);
3442 /* ftp_mkd() calls failf() itself */
3444 result = ftp_cwd(conn, path);
3447 failf(conn->data, "Couldn't CWD to %s", path);
3454 /***********************************************************************
3456 * ftp_3rdparty_pretransfer()
3458 * Preparation for 3rd party transfer.
3461 static CURLcode ftp_3rdparty_pretransfer(struct connectdata *conn)
3463 CURLcode result = CURLE_OK;
3464 struct SessionHandle *data = conn->data;
3465 struct connectdata *sec_conn = conn->sec_conn;
3467 conn->xfertype = TARGET3RD;
3468 sec_conn->xfertype = SOURCE3RD;
3470 /* sets transfer type */
3471 result = ftp_transfertype(conn, data->set.ftp_ascii);
3475 result = ftp_transfertype(sec_conn, data->set.ftp_ascii);
3479 /* Send any PREQUOTE strings after transfer type is set? */
3480 if (data->set.source_prequote) {
3481 /* sends command(s) to source server before file transfer */
3482 result = ftp_sendquote(sec_conn, data->set.source_prequote);
3484 if (!result && data->set.prequote)
3485 result = ftp_sendquote(conn, data->set.prequote);
3492 /***********************************************************************
3494 * ftp_3rdparty_transfer()
3496 * Performs 3rd party transfer.
3499 static CURLcode ftp_3rdparty_transfer(struct connectdata *conn)
3501 CURLcode result = CURLE_OK;
3503 int ftpcode, ip[4], port[2];
3504 struct SessionHandle *data = conn->data;
3505 struct connectdata *sec_conn = conn->sec_conn;
3506 char *buf = data->state.buffer; /* this is our buffer */
3509 const char *stor_cmd;
3510 struct connectdata *pasv_conn;
3511 struct connectdata *port_conn;
3513 if (data->set.ftpport == NULL) {
3515 port_conn = sec_conn;
3518 pasv_conn = sec_conn;
3522 result = ftp_cwd_and_create_path(conn);
3526 /* sets the passive mode */
3527 FTPSENDF(pasv_conn, "%s", "PASV");
3528 result = Curl_GetFTPResponse(&nread, pasv_conn, &ftpcode);
3532 if (ftpcode != 227) {
3533 failf(data, "Odd return code after PASV: %03d", ftpcode);
3534 return CURLE_FTP_WEIRD_PASV_REPLY;
3538 if (6 == sscanf(str, "%d,%d,%d,%d,%d,%d",
3539 &ip[0], &ip[1], &ip[2], &ip[3], &port[0], &port[1]))
3545 failf(pasv_conn->data, "Couldn't interpret the 227-reply");
3546 return CURLE_FTP_WEIRD_227_FORMAT;
3549 snprintf(pasv_port, sizeof(pasv_port), "%d,%d,%d,%d,%d,%d", ip[0], ip[1],
3550 ip[2], ip[3], port[0], port[1]);
3552 /* sets data connection between remote hosts */
3553 FTPSENDF(port_conn, "PORT %s", pasv_port);
3554 result = Curl_GetFTPResponse(&nread, port_conn, &ftpcode);
3558 if (ftpcode != 200) {
3559 failf(data, "PORT command attempts failed: %03d", ftpcode);
3560 return CURLE_FTP_PORT_FAILED;
3563 /* we might append onto the file instead of overwriting it */
3564 stor_cmd = data->set.ftp_append?"APPE":"STOR";
3566 /* transfers file between remote hosts */
3567 /* FIX: this should send a series of CWD commands and then RETR only the
3568 ftp->file file. The conn->path "full path" is not unescaped. Test case
3570 FTPSENDF(sec_conn, "RETR %s", sec_conn->path);
3572 if(!data->set.ftpport) {
3574 result = Curl_GetFTPResponse(&nread, sec_conn, &ftpcode);
3578 if((ftpcode != 150) && (ftpcode != 125)) {
3579 failf(data, "Failed RETR: %03d", ftpcode);
3580 return CURLE_FTP_COULDNT_RETR_FILE;
3583 result = Curl_ftpsendf(conn, "%s %s", stor_cmd, conn->proto.ftp->file);
3584 if(CURLE_OK == result)
3585 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
3589 if (ftpcode >= 400) {
3590 failf(data, "Failed FTP upload: %03d", ftpcode);
3591 return CURLE_FTP_COULDNT_STOR_FILE;
3597 result = Curl_ftpsendf(conn, "%s %s", stor_cmd, conn->proto.ftp->file);
3598 if(CURLE_OK == result)
3599 result = Curl_GetFTPResponse(&nread, sec_conn, &ftpcode);
3603 if (ftpcode >= 400) {
3604 failf(data, "Failed FTP upload: %03d", ftpcode);
3605 return CURLE_FTP_COULDNT_STOR_FILE;
3608 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
3612 if((ftpcode != 150) && (ftpcode != 125)) {
3613 failf(data, "Failed FTP upload: %03d", ftpcode);
3614 return CURLE_FTP_COULDNT_STOR_FILE;
3623 /***********************************************************************
3625 * ftp_parse_url_path()
3627 * Parse the URL path into separate path components.
3631 CURLcode ftp_parse_url_path(struct connectdata *conn)
3633 CURLcode retcode = CURLE_OK;
3634 struct SessionHandle *data = conn->data;
3638 char *slash_pos; /* position of the first '/' char in curpos */
3639 char *cur_pos = conn->path; /* current position in path. point at the begin
3640 of next path component */
3642 /* the ftp struct is already inited in ftp_connect() */
3643 ftp = conn->proto.ftp;
3644 ftp->ctl_valid = FALSE;
3647 ftp->diralloc = 5; /* default dir depth to allocate */
3648 ftp->dirs = (char **)calloc(ftp->diralloc, sizeof(ftp->dirs[0]));
3650 return CURLE_OUT_OF_MEMORY;
3652 /* parse the URL path into separate path components */
3653 while((slash_pos=strchr(cur_pos, '/'))) {
3654 /* 1 or 0 to indicate absolute directory */
3655 bool absolute_dir = (cur_pos - conn->path > 0) && (ftp->dirdepth == 0);
3657 /* seek out the next path component */
3658 if (slash_pos-cur_pos) {
3659 /* we skip empty path components, like "x//y" since the FTP command CWD
3660 requires a parameter and a non-existant parameter a) doesn't work on
3661 many servers and b) has no effect on the others. */
3662 int len = (int)(slash_pos - cur_pos + absolute_dir);
3663 ftp->dirs[ftp->dirdepth] = curl_unescape(cur_pos - absolute_dir, len);
3665 if (!ftp->dirs[ftp->dirdepth]) { /* run out of memory ... */
3666 failf(data, "no memory");
3668 return CURLE_OUT_OF_MEMORY;
3670 if (isBadFtpString(ftp->dirs[ftp->dirdepth])) {
3672 return CURLE_URL_MALFORMAT;
3676 cur_pos = slash_pos + 1; /* jump to the rest of the string */
3681 cur_pos = slash_pos + 1; /* jump to the rest of the string */
3682 if(++ftp->dirdepth >= ftp->diralloc) {
3685 ftp->diralloc *= 2; /* double the size each time */
3686 bigger = realloc(ftp->dirs, ftp->diralloc * sizeof(ftp->dirs[0]));
3690 return CURLE_OUT_OF_MEMORY;
3692 ftp->dirs = (char **)bigger;
3697 ftp->file = cur_pos; /* the rest is the file name */
3700 ftp->file = curl_unescape(ftp->file, 0);
3701 if(NULL == ftp->file) {
3703 failf(data, "no memory");
3704 return CURLE_OUT_OF_MEMORY;
3706 if (isBadFtpString(ftp->file)) {
3708 return CURLE_URL_MALFORMAT;
3712 ftp->file=NULL; /* instead of point to a zero byte, we make it a NULL
3715 ftp->cwddone = FALSE; /* default to not done */
3718 /* prevpath is "raw" so we convert the input path before we compare the
3720 char *path = curl_unescape(conn->path, 0);
3722 return CURLE_OUT_OF_MEMORY;
3724 dlen = strlen(path) - (ftp->file?strlen(ftp->file):0);
3725 if((dlen == strlen(ftp->prevpath)) &&
3726 curl_strnequal(path, ftp->prevpath, dlen)) {
3727 infof(data, "Request has same path as previous transfer\n");
3728 ftp->cwddone = TRUE;
3738 /***********************************************************************
3740 * ftp_cwd_and_create_path()
3742 * Creates full path on remote target host.
3746 CURLcode ftp_cwd_and_create_path(struct connectdata *conn)
3748 CURLcode result = CURLE_OK;
3749 /* the ftp struct is already inited in Curl_ftp_connect() */
3750 struct FTP *ftp = conn->proto.ftp;
3754 /* already done and fine */
3757 /* This is a re-used connection. Since we change directory to where the
3758 transfer is taking place, we must now get back to the original dir
3759 where we ended up after login: */
3760 if (conn->bits.reuse && ftp->entrypath) {
3761 if ((result = ftp_cwd_and_mkd(conn, ftp->entrypath)) != CURLE_OK)
3765 for (i=0; i < ftp->dirdepth; i++) {
3766 /* RFC 1738 says empty components should be respected too, but
3767 that is plain stupid since CWD can't be used with an empty argument */
3768 if ((result = ftp_cwd_and_mkd(conn, ftp->dirs[i])) != CURLE_OK)
3775 /* call this when the DO phase has completed */
3776 static CURLcode ftp_dophase_done(struct connectdata *conn,
3779 CURLcode result = CURLE_OK;
3780 struct FTP *ftp = conn->proto.ftp;
3783 result = Curl_ftp_nextconnect(conn);
3785 if(result && (conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD)) {
3786 /* Failure detected, close the second socket if it was created already */
3787 sclose(conn->sock[SECONDARYSOCKET]);
3788 conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
3791 if(ftp->no_transfer)
3792 /* no data to transfer */
3793 result=Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
3795 /* since we didn't connect now, we want do_more to get called */
3796 conn->bits.do_more = TRUE;
3798 ftp->ctl_valid = TRUE; /* seems good */
3803 /* called from multi.c while DOing */
3804 CURLcode Curl_ftp_doing(struct connectdata *conn,
3808 result = Curl_ftp_multi_statemach(conn, dophase_done);
3811 result = ftp_dophase_done(conn, FALSE /* not connected */);
3813 infof(conn->data, "DO phase is comlete\n");
3818 /***********************************************************************
3820 * ftp_regular_transfer()
3822 * The input argument is already checked for validity.
3824 * Performs all commands done before a regular transfer between a local and a
3827 * ftp->ctl_valid starts out as FALSE, and gets set to TRUE if we reach the
3828 * Curl_ftp_done() function without finding any major problem.
3831 CURLcode ftp_regular_transfer(struct connectdata *conn,
3834 CURLcode result=CURLE_OK;
3836 struct SessionHandle *data = conn->data;
3839 /* the ftp struct is already inited in ftp_connect() */
3840 ftp = conn->proto.ftp;
3841 conn->size = -1; /* make sure this is unknown at this point */
3843 Curl_pgrsSetUploadCounter(data, 0);
3844 Curl_pgrsSetDownloadCounter(data, 0);
3845 Curl_pgrsSetUploadSize(data, 0);
3846 Curl_pgrsSetDownloadSize(data, 0);
3848 ftp->ctl_valid = TRUE; /* starts good */
3850 result = ftp_perform(conn,
3851 &connected, /* have we connected after PASV/PORT */
3852 dophase_done); /* all commands in the DO-phase done? */
3854 if(CURLE_OK == result) {
3857 /* the DO phase has not completed yet */
3860 result = ftp_dophase_done(conn, connected);
3872 /***********************************************************************
3876 * The input argument is already checked for validity.
3877 * Performs a 3rd party transfer between two remote hosts.
3879 static CURLcode ftp_3rdparty(struct connectdata *conn)
3881 CURLcode result = CURLE_OK;
3883 conn->proto.ftp->ctl_valid = conn->sec_conn->proto.ftp->ctl_valid = TRUE;
3884 conn->size = conn->sec_conn->size = -1;
3886 result = ftp_3rdparty_pretransfer(conn);
3888 result = ftp_3rdparty_transfer(conn);
3893 #endif /* CURL_DISABLE_FTP */