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)
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) {
1350 /* we're about to continue the uploading of a file */
1351 /* 1. get already existing file's size. We use the SIZE command for this
1352 which may not exist in the server! The SIZE command is not in
1355 /* 2. This used to set REST. But since we can do append, we
1356 don't another ftp command. We just skip the source file
1357 offset and then we APPEND the rest on the file instead */
1359 /* 3. pass file-size number of bytes in the source file */
1360 /* 4. lower the infilesize counter */
1361 /* => transfer as usual */
1363 if(conn->resume_from < 0 ) {
1364 /* Got no given size to start from, figure it out */
1365 NBFTPSENDF(conn, "SIZE %s", ftp->file);
1366 state(conn, FTP_STOR_SIZE);
1371 data->set.ftp_append = TRUE;
1373 /* Let's read off the proper amount of bytes from the input. If we knew it
1374 was a proper file we could've just fseek()ed but we only have a stream
1377 /* TODO: allow the ioctlfunction to provide a fast forward function that
1378 can be used here and use this method only as a fallback! */
1380 curl_off_t readthisamountnow = (conn->resume_from - passed);
1381 curl_off_t actuallyread;
1383 if(readthisamountnow > BUFSIZE)
1384 readthisamountnow = BUFSIZE;
1386 actuallyread = (curl_off_t)
1387 conn->fread(data->state.buffer, 1, (size_t)readthisamountnow,
1390 passed += actuallyread;
1391 if(actuallyread != readthisamountnow) {
1392 failf(data, "Could only read %" FORMAT_OFF_T
1393 " bytes from the input", passed);
1394 return CURLE_FTP_COULDNT_USE_REST;
1396 } while(passed != conn->resume_from);
1398 /* now, decrease the size of the read */
1399 if(data->set.infilesize>0) {
1400 data->set.infilesize -= conn->resume_from;
1402 if(data->set.infilesize <= 0) {
1403 infof(data, "File already completely uploaded\n");
1405 /* no data to transfer */
1406 result=Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1408 /* Set no_transfer so that we won't get any error in
1409 * Curl_ftp_done() because we didn't transfer anything! */
1410 ftp->no_transfer = TRUE;
1412 state(conn, FTP_STOP);
1416 /* we've passed, proceed as normal */
1419 NBFTPSENDF(conn, data->set.ftp_append?"APPE %s":"STOR %s",
1422 state(conn, FTP_STOR);
1427 static CURLcode ftp_state_quote(struct connectdata *conn,
1431 CURLcode result = CURLE_OK;
1432 struct FTP *ftp = conn->proto.ftp;
1433 struct SessionHandle *data = conn->data;
1435 struct curl_slist *item;
1440 item = data->set.quote;
1442 case FTP_RETR_PREQUOTE:
1443 case FTP_STOR_PREQUOTE:
1444 item = data->set.prequote;
1447 item = data->set.postquote;
1459 /* Skip count1 items in the linked list */
1460 while((i< ftp->count1) && item) {
1465 NBFTPSENDF(conn, "%s", item->data);
1466 state(conn, instate);
1472 /* No more quote to send, continue to ... */
1476 result = ftp_state_cwd(conn);
1478 case FTP_RETR_PREQUOTE:
1479 NBFTPSENDF(conn, "SIZE %s", ftp->file);
1480 state(conn, FTP_RETR_SIZE);
1482 case FTP_STOR_PREQUOTE:
1483 result = ftp_state_ul_setup(conn);
1493 static CURLcode ftp_state_pasv_resp(struct connectdata *conn,
1496 struct FTP *ftp = conn->proto.ftp;
1498 struct SessionHandle *data=conn->data;
1499 Curl_addrinfo *conninfo;
1500 struct Curl_dns_entry *addr=NULL;
1502 unsigned short connectport; /* the local port connect() should use! */
1503 unsigned short newport=0; /* remote port */
1506 /* newhost must be able to hold a full IP-style address in ASCII, which
1507 in the IPv6 case means 5*8-1 = 39 letters */
1508 #define NEWHOST_BUFSIZE 48
1509 char newhost[NEWHOST_BUFSIZE];
1510 char *str=&data->state.buffer[4]; /* start on the first letter */
1512 if((ftp->count1 == 0) &&
1514 /* positive EPSV response */
1515 char *ptr = strchr(str, '(');
1520 if(5 == sscanf(ptr, "%c%c%c%u%c",
1526 char sep1 = separator[0];
1529 /* The four separators should be identical, or else this is an oddly
1530 formatted reply and we bail out immediately. */
1531 for(i=1; i<4; i++) {
1532 if(separator[i] != sep1) {
1533 ptr=NULL; /* set to NULL to signal error */
1540 /* use the same IP we are already connected to */
1541 snprintf(newhost, NEWHOST_BUFSIZE, "%s", conn->ip_addr_str, newhost);
1548 failf(data, "Weirdly formatted EPSV reply");
1549 return CURLE_FTP_WEIRD_PASV_REPLY;
1552 else if((ftp->count1 == 1) &&
1554 /* positive PASV response */
1559 * Scan for a sequence of six comma-separated numbers and use them as
1560 * IP+port indicators.
1562 * Found reply-strings include:
1563 * "227 Entering Passive Mode (127,0,0,1,4,51)"
1564 * "227 Data transfer will passively listen to 127,0,0,1,4,51"
1565 * "227 Entering passive mode. 127,0,0,1,4,51"
1568 if (6 == sscanf(str, "%d,%d,%d,%d,%d,%d",
1569 &ip[0], &ip[1], &ip[2], &ip[3],
1570 &port[0], &port[1]))
1576 failf(data, "Couldn't interpret the 227-response");
1577 return CURLE_FTP_WEIRD_227_FORMAT;
1580 snprintf(newhost, sizeof(newhost),
1581 "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
1582 newport = (port[0]<<8) + port[1];
1584 else if(ftp->count1 == 0) {
1585 /* EPSV failed, move on to PASV */
1587 /* disable it for next transfer */
1588 conn->bits.ftp_use_epsv = FALSE;
1589 infof(data, "disabling EPSV usage\n");
1591 NBFTPSENDF(conn, "PASV", NULL);
1593 /* remain in the FTP_PASV state */
1597 failf(data, "Bad PASV/EPSV response: %03d", ftpcode);
1598 return CURLE_FTP_WEIRD_PASV_REPLY;
1601 /* we got OK from server */
1603 if(data->change.proxy && *data->change.proxy) {
1605 * This is a tunnel through a http proxy and we need to connect to the
1608 * We don't want to rely on a former host lookup that might've expired
1609 * now, instead we remake the lookup here and now!
1611 rc = Curl_resolv(conn, conn->proxy.name, (int)conn->port, &addr);
1612 if(rc == CURLRESOLV_PENDING)
1614 rc = Curl_wait_for_resolv(conn, &addr);
1617 (unsigned short)conn->port; /* we connect to the proxy's port */
1621 /* normal, direct, ftp connection */
1622 rc = Curl_resolv(conn, newhost, newport, &addr);
1623 if(rc == CURLRESOLV_PENDING)
1625 rc = Curl_wait_for_resolv(conn, &addr);
1628 failf(data, "Can't resolve new host %s:%d", newhost, newport);
1629 return CURLE_FTP_CANT_GET_HOST;
1631 connectport = newport; /* we connect to the remote port */
1634 result = Curl_connecthost(conn,
1636 &conn->sock[SECONDARYSOCKET],
1640 Curl_resolv_unlock(data, addr); /* we're done using this address */
1645 conn->bits.tcpconnect = connected; /* simply TRUE or FALSE */
1648 * When this is used from the multi interface, this might've returned with
1649 * the 'connected' set to FALSE and thus we are now awaiting a non-blocking
1650 * connect to connect and we should not be "hanging" here waiting.
1653 if(data->set.verbose)
1654 /* this just dumps information about this second connection */
1655 ftp_pasv_verbose(conn, conninfo, newhost, connectport);
1657 #ifndef CURL_DISABLE_HTTP
1658 if(conn->bits.tunnel_proxy) {
1659 /* FIX: this MUST wait for a proper connect first if 'connected' is
1663 /* We want "seamless" FTP operations through HTTP proxy tunnel */
1664 result = Curl_ConnectHTTPProxyTunnel(conn, SECONDARYSOCKET,
1666 if(CURLE_OK != result)
1669 #endif /* CURL_DISABLE_HTTP */
1671 state(conn, FTP_STOP); /* this phase is completed */
1676 static CURLcode ftp_state_port_resp(struct connectdata *conn,
1679 struct FTP *ftp = conn->proto.ftp;
1680 struct SessionHandle *data = conn->data;
1681 ftpport fcmd = (ftpport)ftp->count1;
1682 CURLcode result = CURLE_OK;
1684 if(ftpcode != 200) {
1685 /* the command failed */
1688 infof(data, "disabling EPRT usage\n");
1689 conn->bits.ftp_use_eprt = FALSE;
1691 else if (LPRT == fcmd) {
1692 infof(data, "disabling LPRT usage\n");
1693 conn->bits.ftp_use_lprt = FALSE;
1698 failf(data, "Failed to do PORT");
1699 result = CURLE_FTP_PORT_FAILED;
1703 result = ftp_state_use_port(conn, fcmd);
1706 infof(data, "Connect data stream actively\n");
1707 state(conn, FTP_STOP); /* end of DO phase */
1713 static CURLcode ftp_state_mdtm_resp(struct connectdata *conn,
1716 CURLcode result = CURLE_OK;
1717 struct FTP *ftp = conn->proto.ftp;
1718 struct SessionHandle *data=conn->data;
1723 /* we got a time. Format should be: "YYYYMMDDHHMMSS[.sss]" where the
1724 last .sss part is optional and means fractions of a second */
1725 int year, month, day, hour, minute, second;
1726 char *buf = data->state.buffer;
1727 if(6 == sscanf(buf+4, "%04d%02d%02d%02d%02d%02d",
1728 &year, &month, &day, &hour, &minute, &second)) {
1729 /* we have a time, reformat it */
1730 time_t secs=time(NULL);
1731 /* using the good old yacc/bison yuck */
1732 snprintf(buf, sizeof(conn->data->state.buffer),
1733 "%04d%02d%02d %02d:%02d:%02d GMT",
1734 year, month, day, hour, minute, second);
1735 /* now, convert this into a time() value: */
1736 data->info.filetime = curl_getdate(buf, &secs);
1739 /* If we asked for a time of the file and we actually got one as well,
1740 we "emulate" a HTTP-style header in our output. */
1742 if(data->set.get_filetime && (data->info.filetime>=0) ) {
1744 time_t clock = (time_t)data->info.filetime;
1745 #ifdef HAVE_GMTIME_R
1747 tm = (struct tm *)gmtime_r(&clock, &buffer);
1749 tm = gmtime(&clock);
1751 /* format: "Tue, 15 Nov 1994 12:45:26" */
1752 snprintf(buf, BUFSIZE-1,
1753 "Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n",
1754 Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
1756 Curl_month[tm->tm_mon],
1761 result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0);
1768 infof(data, "unsupported MDTM reply format\n");
1770 case 550: /* "No such file or directory" */
1771 failf(data, "Given file does not exist");
1772 result = CURLE_FTP_COULDNT_RETR_FILE;
1776 if(data->set.timecondition) {
1777 if((data->info.filetime > 0) && (data->set.timevalue > 0)) {
1778 switch(data->set.timecondition) {
1779 case CURL_TIMECOND_IFMODSINCE:
1781 if(data->info.filetime < data->set.timevalue) {
1782 infof(data, "The requested document is not new enough\n");
1783 ftp->no_transfer = TRUE; /* mark this to not transfer data */
1784 state(conn, FTP_STOP);
1788 case CURL_TIMECOND_IFUNMODSINCE:
1789 if(data->info.filetime > data->set.timevalue) {
1790 infof(data, "The requested document is not old enough\n");
1791 ftp->no_transfer = TRUE; /* mark this to not transfer data */
1792 state(conn, FTP_STOP);
1799 infof(data, "Skipping time comparison\n");
1804 result = ftp_state_post_mdtm(conn);
1809 static CURLcode ftp_state_type_resp(struct connectdata *conn,
1813 CURLcode result = CURLE_OK;
1814 struct SessionHandle *data=conn->data;
1816 if(ftpcode != 200) {
1817 failf(data, "Couldn't set desired mode");
1818 return CURLE_FTP_COULDNT_SET_BINARY; /* FIX */
1820 if(instate == FTP_TYPE)
1821 result = ftp_state_post_type(conn);
1822 else if(instate == FTP_LIST_TYPE)
1823 result = ftp_state_post_listtype(conn);
1824 else if(instate == FTP_RETR_TYPE)
1825 result = ftp_state_post_retrtype(conn);
1826 else if(instate == FTP_STOR_TYPE)
1827 result = ftp_state_post_stortype(conn);
1832 static CURLcode ftp_state_post_retr_size(struct connectdata *conn,
1833 curl_off_t filesize)
1835 CURLcode result = CURLE_OK;
1836 struct SessionHandle *data=conn->data;
1837 struct FTP *ftp = conn->proto.ftp;
1839 if (data->set.max_filesize && (filesize > data->set.max_filesize)) {
1840 failf(data, "Maximum file size exceeded");
1841 return CURLE_FILESIZE_EXCEEDED;
1843 ftp->downloadsize = filesize;
1845 if(conn->resume_from) {
1846 /* We always (attempt to) get the size of downloads, so it is done before
1847 this even when not doing resumes. */
1848 if(filesize == -1) {
1849 infof(data, "ftp server doesn't support SIZE\n");
1850 /* We couldn't get the size and therefore we can't know if there really
1851 is a part of the file left to get, although the server will just
1852 close the connection when we start the connection so it won't cause
1853 us any harm, just not make us exit as nicely. */
1856 /* We got a file size report, so we check that there actually is a
1857 part of the file left to get, or else we go home. */
1858 if(conn->resume_from< 0) {
1859 /* We're supposed to download the last abs(from) bytes */
1860 if(filesize < -conn->resume_from) {
1861 failf(data, "Offset (%" FORMAT_OFF_T
1862 ") was beyond file size (%" FORMAT_OFF_T ")",
1863 conn->resume_from, filesize);
1864 return CURLE_BAD_DOWNLOAD_RESUME;
1866 /* convert to size to download */
1867 ftp->downloadsize = -conn->resume_from;
1868 /* download from where? */
1869 conn->resume_from = filesize - ftp->downloadsize;
1872 if(filesize < conn->resume_from) {
1873 failf(data, "Offset (%" FORMAT_OFF_T
1874 ") was beyond file size (%" FORMAT_OFF_T ")",
1875 conn->resume_from, filesize);
1876 return CURLE_BAD_DOWNLOAD_RESUME;
1878 /* Now store the number of bytes we are expected to download */
1879 ftp->downloadsize = filesize-conn->resume_from;
1883 if(ftp->downloadsize == 0) {
1884 /* no data to transfer */
1885 result=Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1886 infof(data, "File already completely downloaded\n");
1888 /* Set no_transfer so that we won't get any error in Curl_ftp_done()
1889 * because we didn't transfer the any file */
1890 ftp->no_transfer = TRUE;
1891 state(conn, FTP_STOP);
1895 /* Set resume file transfer offset */
1896 infof(data, "Instructs server to resume from offset %" FORMAT_OFF_T
1897 "\n", conn->resume_from);
1899 NBFTPSENDF(conn, "REST %" FORMAT_OFF_T, conn->resume_from);
1901 state(conn, FTP_RETR_REST);
1906 NBFTPSENDF(conn, "RETR %s", ftp->file);
1907 state(conn, FTP_RETR);
1913 static CURLcode ftp_state_size_resp(struct connectdata *conn,
1917 CURLcode result = CURLE_OK;
1918 struct SessionHandle *data=conn->data;
1919 curl_off_t filesize;
1920 char *buf = data->state.buffer;
1922 /* get the size from the ascii string: */
1923 filesize = (ftpcode == 213)?curlx_strtoofft(buf+4, NULL, 0):-1;
1925 if(instate == FTP_SIZE) {
1926 if(-1 != filesize) {
1927 snprintf(buf, sizeof(data->state.buffer),
1928 "Content-Length: %" FORMAT_OFF_T "\r\n", filesize);
1929 result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0);
1933 result = ftp_state_post_size(conn);
1935 else if(instate == FTP_RETR_SIZE)
1936 result = ftp_state_post_retr_size(conn, filesize);
1937 else if(instate == FTP_STOR_SIZE) {
1938 conn->resume_from = filesize;
1939 result = ftp_state_ul_setup(conn);
1945 static CURLcode ftp_state_rest_resp(struct connectdata *conn,
1949 CURLcode result = CURLE_OK;
1950 struct FTP *ftp = conn->proto.ftp;
1955 if (ftpcode == 350) {
1956 result = Curl_client_write(conn->data, CLIENTWRITE_BOTH,
1957 (char *)"Accept-ranges: bytes\r\n", 0);
1962 result = ftp_state_post_rest(conn);
1966 if (ftpcode != 350) {
1967 failf(conn->data, "Couldn't use REST");
1968 result = CURLE_FTP_COULDNT_USE_REST;
1971 NBFTPSENDF(conn, "RETR %s", ftp->file);
1972 state(conn, FTP_RETR);
1980 static CURLcode ftp_state_stor_resp(struct connectdata *conn,
1983 CURLcode result = CURLE_OK;
1984 struct SessionHandle *data = conn->data;
1985 struct FTP *ftp = conn->proto.ftp;
1988 failf(data, "Failed FTP upload: %0d", ftpcode);
1989 /* oops, we never close the sockets! */
1990 return CURLE_FTP_COULDNT_STOR_FILE;
1993 if(data->set.ftp_use_port) {
1995 /* PORT means we are now awaiting the server to connect to us. */
1996 result = AllowServerConnect(conn);
2001 if(conn->ssl[SECONDARYSOCKET].use) {
2002 /* since we only have a plaintext TCP connection here, we must now
2004 infof(data, "Doing the SSL/TLS handshake on the data stream\n");
2006 result = Curl_SSLConnect(conn, SECONDARYSOCKET);
2011 *(ftp->bytecountp)=0;
2013 /* When we know we're uploading a specified file, we can get the file
2014 size prior to the actual upload. */
2016 Curl_pgrsSetUploadSize(data, data->set.infilesize);
2018 result = Curl_Transfer(conn, -1, -1, FALSE, NULL, /* no download */
2019 SECONDARYSOCKET, ftp->bytecountp);
2020 state(conn, FTP_STOP);
2025 /* for LIST and RETR responses */
2026 static CURLcode ftp_state_get_resp(struct connectdata *conn,
2030 CURLcode result = CURLE_OK;
2031 struct SessionHandle *data = conn->data;
2032 struct FTP *ftp = conn->proto.ftp;
2033 char *buf = data->state.buffer;
2035 if((ftpcode == 150) || (ftpcode == 125)) {
2039 150 Opening BINARY mode data connection for /etc/passwd (2241
2040 bytes). (ok, the file is being transfered)
2043 150 Opening ASCII mode data connection for /bin/ls
2046 150 ASCII data connection for /bin/ls (137.167.104.91,37445) (0 bytes).
2049 150 Opening ASCII mode data connection for /linux/fisk/kpanelrc (0.0.0.0,0) (545 bytes).
2052 125 Data connection already open; Transfer starting. */
2054 curl_off_t size=-1; /* default unknown size */
2058 * It appears that there are FTP-servers that return size 0 for files when
2059 * SIZE is used on the file while being in BINARY mode. To work around
2060 * that (stupid) behavior, we attempt to parse the RETR response even if
2061 * the SIZE returned size zero.
2063 * Debugging help from Salvatore Sorrentino on February 26, 2003.
2066 if((instate != FTP_LIST) &&
2067 !data->set.ftp_ascii &&
2068 (ftp->downloadsize < 1)) {
2070 * It seems directory listings either don't show the size or very
2071 * often uses size 0 anyway. ASCII transfers may very well turn out
2072 * that the transfered amount of data is not the same as this line
2073 * tells, why using this number in those cases only confuses us.
2075 * Example D above makes this parsing a little tricky */
2077 bytes=strstr(buf, " bytes");
2080 /* this is a hint there is size information in there! ;-) */
2082 /* scan for the left parenthesis and break there */
2085 /* skip only digits */
2086 if(!isdigit((int)*bytes)) {
2090 /* one more estep backwards */
2093 /* if we have nothing but digits: */
2095 /* get the number! */
2096 size = curlx_strtoofft(bytes, NULL, 0);
2100 else if(ftp->downloadsize > -1)
2101 size = ftp->downloadsize;
2103 if(data->set.ftp_use_port) {
2105 result = AllowServerConnect(conn);
2110 if(conn->ssl[SECONDARYSOCKET].use) {
2111 /* since we only have a plaintext TCP connection here, we must now
2113 infof(data, "Doing the SSL/TLS handshake on the data stream\n");
2114 result = Curl_SSLConnect(conn, SECONDARYSOCKET);
2119 if(size > conn->maxdownload && conn->maxdownload > 0)
2120 size = conn->size = conn->maxdownload;
2122 if(instate != FTP_LIST)
2123 infof(data, "Getting file with size: %" FORMAT_OFF_T "\n", size);
2126 result=Curl_Transfer(conn, SECONDARYSOCKET, size, FALSE,
2128 -1, NULL); /* no upload here */
2132 state(conn, FTP_STOP);
2135 if((instate == FTP_LIST) && (ftpcode == 450)) {
2136 /* simply no matching files in the dir listing */
2137 ftp->no_transfer = TRUE; /* don't download anything */
2138 state(conn, FTP_STOP); /* this phase is over */
2141 failf(data, "%s", buf+4);
2142 return CURLE_FTP_COULDNT_RETR_FILE;
2149 /* after USER, PASS and ACCT */
2150 static CURLcode ftp_state_loggedin(struct connectdata *conn)
2152 CURLcode result = CURLE_OK;
2153 struct SessionHandle *data = conn->data;
2154 infof(data, "We have successfully logged in\n");
2157 /* We are logged in with Kerberos, now set the requested
2160 if(conn->sec_complete)
2162 Curl_sec_set_protection_level(conn);
2164 /* We may need to issue a KAUTH here to have access to the files
2165 * do it if user supplied a password
2167 if(conn->passwd && *conn->passwd) {
2169 result = Curl_krb_kauth(conn);
2174 if(conn->ssl[FIRSTSOCKET].use) {
2175 /* PBSZ = PROTECTION BUFFER SIZE.
2177 The 'draft-murray-auth-ftp-ssl' (draft 12, page 7) says:
2179 Specifically, the PROT command MUST be preceded by a PBSZ
2180 command and a PBSZ command MUST be preceded by a successful
2181 security data exchange (the TLS negotiation in this case)
2183 ... (and on page 8):
2185 Thus the PBSZ command must still be issued, but must have a
2186 parameter of '0' to indicate that no buffering is taking place
2187 and the data connection should not be encapsulated.
2189 NBFTPSENDF(conn, "PBSZ %d", 0);
2190 state(conn, FTP_PBSZ);
2193 result = ftp_state_pwd(conn);
2198 /* for USER and PASS responses */
2199 static CURLcode ftp_state_user_resp(struct connectdata *conn,
2203 CURLcode result = CURLE_OK;
2204 struct SessionHandle *data = conn->data;
2205 struct FTP *ftp = conn->proto.ftp;
2206 (void)instate; /* no use for this yet */
2208 if((ftpcode == 331) && (ftp->state == FTP_USER)) {
2209 /* 331 Password required for ...
2210 (the server requires to send the user's password too) */
2211 NBFTPSENDF(conn, "PASS %s", ftp->passwd?ftp->passwd:"");
2212 state(conn, FTP_PASS);
2214 else if(ftpcode/100 == 2) {
2215 /* 230 User ... logged in.
2216 (the user logged in with or without password) */
2217 result = ftp_state_loggedin(conn);
2219 else if(ftpcode == 332) {
2220 if(data->set.ftp_account) {
2221 NBFTPSENDF(conn, "ACCT %s", data->set.ftp_account);
2222 state(conn, FTP_ACCT);
2225 failf(data, "ACCT requested but none available");
2226 result = CURLE_LOGIN_DENIED;
2230 /* All other response codes, like:
2232 530 User ... access denied
2233 (the server denies to log the specified user) */
2234 failf(data, "Access denied: %03d", ftpcode);
2235 result = CURLE_LOGIN_DENIED;
2240 /* for ACCT response */
2241 static CURLcode ftp_state_acct_resp(struct connectdata *conn,
2244 CURLcode result = CURLE_OK;
2245 struct SessionHandle *data = conn->data;
2246 if(ftpcode != 230) {
2247 failf(data, "ACCT rejected by server: %03d", ftpcode);
2248 result = CURLE_FTP_WEIRD_PASS_REPLY; /* FIX */
2251 result = ftp_state_loggedin(conn);
2257 static CURLcode ftp_statemach_act(struct connectdata *conn)
2260 curl_socket_t sock = conn->sock[FIRSTSOCKET];
2261 struct SessionHandle *data=conn->data;
2263 struct FTP *ftp = conn->proto.ftp;
2264 static const char * const ftpauth[] = {
2270 /* we have a piece of a command still left to send */
2272 result = Curl_write(conn, sock, ftp->sendthis + ftp->sendsize -
2273 ftp->sendleft, ftp->sendleft, &written);
2277 if(written != (ssize_t)ftp->sendleft) {
2278 /* only a fraction was sent */
2279 ftp->sendleft -= written;
2282 free(ftp->sendthis);
2284 ftp->sendleft = ftp->sendsize = 0;
2285 ftp->response = Curl_tvnow();
2290 /* we read a piece of response */
2291 result = ftp_readresp(sock, conn, &ftpcode, &nread);
2296 /* we have now received a full FTP server response */
2297 switch(ftp->state) {
2299 if(ftpcode != 220) {
2300 failf(data, "This doesn't seem like a nice ftp-server response");
2301 return CURLE_FTP_WEIRD_SERVER_REPLY;
2304 /* We have received a 220 response fine, now we proceed. */
2306 if(data->set.krb4) {
2307 /* If not anonymous login, try a secure login. Note that this
2308 procedure is still BLOCKING. */
2310 Curl_sec_request_prot(conn, "private");
2311 /* We set private first as default, in case the line below fails to
2312 set a valid level */
2313 Curl_sec_request_prot(conn, data->set.krb4_level);
2315 if(Curl_sec_login(conn) != 0)
2316 infof(data, "Logging in with password in cleartext!\n");
2318 infof(data, "Authentication successful\n");
2322 if(data->set.ftp_ssl && !conn->ssl[FIRSTSOCKET].use) {
2323 /* We don't have a SSL/TLS connection yet, but FTPS is
2324 requested. Try a FTPS connection now */
2327 switch(data->set.ftpsslauth) {
2328 case CURLFTPAUTH_DEFAULT:
2329 case CURLFTPAUTH_SSL:
2330 ftp->count2 = 1; /* add one to get next */
2333 case CURLFTPAUTH_TLS:
2334 ftp->count2 = -1; /* subtract one to get next */
2338 failf(data, "unsupported parameter to CURLOPT_FTPSSLAUTH: %d\n",
2339 data->set.ftpsslauth);
2340 return CURLE_FAILED_INIT; /* we don't know what to do */
2342 NBFTPSENDF(conn, "AUTH %s", ftpauth[ftp->count1]);
2343 state(conn, FTP_AUTH);
2346 ftp_state_user(conn);
2354 /* we have gotten the response to a previous AUTH command */
2356 /* RFC2228 (page 5) says:
2358 * If the server is willing to accept the named security mechanism,
2359 * and does not require any security data, it must respond with
2360 * reply code 234/334.
2363 if((ftpcode == 234) || (ftpcode == 334)) {
2364 /* Curl_SSLConnect is BLOCKING */
2365 result = Curl_SSLConnect(conn, FIRSTSOCKET);
2368 conn->protocol |= PROT_FTPS;
2369 conn->ssl[SECONDARYSOCKET].use = FALSE; /* clear-text data */
2371 else if(ftp->count3 < 1) {
2373 ftp->count1 += ftp->count2; /* get next attempt */
2374 NBFTPSENDF(conn, "AUTH %s", ftpauth[ftp->count1]);
2375 /* remain in this same state */
2378 result = ftp_state_user(conn);
2386 result = ftp_state_user_resp(conn, ftpcode, ftp->state);
2390 result = ftp_state_acct_resp(conn, ftpcode);
2394 /* FIX: check response code */
2396 /* For TLS, the data connection can have one of two security levels.
2398 1) Clear (requested by 'PROT C')
2400 2)Private (requested by 'PROT P')
2402 if(!conn->ssl[SECONDARYSOCKET].use) {
2403 NBFTPSENDF(conn, "PROT %c", 'P');
2404 state(conn, FTP_PROT);
2407 result = ftp_state_pwd(conn);
2415 if(ftpcode/100 == 2)
2416 /* We have enabled SSL for the data connection! */
2417 conn->ssl[SECONDARYSOCKET].use = TRUE;
2418 /* FTP servers typically responds with 500 if they decide to reject
2420 else if(data->set.ftp_ssl> CURLFTPSSL_CONTROL)
2421 /* we failed and bails out */
2422 return CURLE_FTP_SSL_FAILED;
2424 result = ftp_state_pwd(conn);
2430 if(ftpcode == 257) {
2431 char *dir = (char *)malloc(nread+1);
2433 char *ptr=&data->state.buffer[4]; /* start on the first letter */
2436 return CURLE_OUT_OF_MEMORY;
2438 /* Reply format is like
2439 257<space>"<directory-name>"<space><commentary> and the RFC959
2442 The directory name can contain any character; embedded
2443 double-quotes should be escaped by double-quotes (the
2444 "quote-doubling" convention).
2447 /* it started good */
2449 while(ptr && *ptr) {
2451 if('\"' == ptr[1]) {
2452 /* "quote-doubling" */
2458 *store = '\0'; /* zero terminate */
2459 break; /* get out of this loop */
2467 ftp->entrypath =dir; /* remember this */
2468 infof(data, "Entry path is '%s'\n", ftp->entrypath);
2471 /* couldn't get the path */
2473 infof(data, "Failed to figure out path\n");
2476 state(conn, FTP_STOP); /* we are done with the CONNECT phase! */
2477 infof(data, "protocol connect phase DONE\n");
2482 case FTP_RETR_PREQUOTE:
2483 case FTP_STOR_PREQUOTE:
2484 if(ftpcode >= 400) {
2485 failf(conn->data, "QUOT command failed with %03d", ftpcode);
2486 return CURLE_FTP_QUOTE_ERROR;
2488 result = ftp_state_quote(conn, FALSE, ftp->state);
2495 if(ftpcode/100 != 2) {
2496 /* failure to CWD there */
2497 if(conn->data->set.ftp_create_missing_dirs &&
2498 ftp->count1 && !ftp->count2) {
2500 ftp->count2++; /* counter to prevent CWD-MKD loops */
2501 NBFTPSENDF(conn, "MKD %s", ftp->dirs[ftp->count1 - 1]);
2502 state(conn, FTP_MKD);
2505 /* return failure */
2506 return CURLE_FTP_ACCESS_DENIED;
2511 if(++ftp->count1 <= ftp->dirdepth) {
2513 NBFTPSENDF(conn, "CWD %s", ftp->dirs[ftp->count1 - 1]);
2516 result = ftp_state_post_cwd(conn);
2524 if(ftpcode/100 != 2) {
2525 /* failure to MKD the dir */
2526 failf(data, "Failed to MKD dir: %03d", ftpcode);
2527 return CURLE_FTP_ACCESS_DENIED;
2529 state(conn, FTP_CWD);
2531 NBFTPSENDF(conn, "CWD %s", ftp->dirs[ftp->count1 - 1]);
2535 result = ftp_state_mdtm_resp(conn, ftpcode);
2542 result = ftp_state_type_resp(conn, ftpcode, ftp->state);
2548 result = ftp_state_size_resp(conn, ftpcode, ftp->state);
2553 result = ftp_state_rest_resp(conn, ftpcode, ftp->state);
2557 result = ftp_state_pasv_resp(conn, ftpcode);
2561 result = ftp_state_port_resp(conn, ftpcode);
2566 result = ftp_state_get_resp(conn, ftpcode, ftp->state);
2570 result = ftp_state_stor_resp(conn, ftpcode);
2574 /* fallthrough, just stop! */
2576 /* internal error */
2577 state(conn, FTP_STOP);
2585 /* Returns timeout in ms. 0 or negative number means the timeout has already
2587 static long ftp_state_timeout(struct connectdata *conn)
2589 struct SessionHandle *data=conn->data;
2590 struct FTP *ftp = conn->proto.ftp;
2591 long timeout_ms=360000; /* in milliseconds */
2593 if(data->set.ftp_response_timeout )
2594 /* if CURLOPT_FTP_RESPONSE_TIMEOUT is set, use that to determine remaining
2595 time. Also, use ftp->response because FTP_RESPONSE_TIMEOUT is supposed
2596 to govern the response for any given ftp response, not for the time
2597 from connect to the given ftp response. */
2598 timeout_ms = data->set.ftp_response_timeout*1000 - /* timeout time */
2599 Curl_tvdiff(Curl_tvnow(), ftp->response); /* spent time */
2600 else if(data->set.timeout)
2601 /* if timeout is requested, find out how much remaining time we have */
2602 timeout_ms = data->set.timeout*1000 - /* timeout time */
2603 Curl_tvdiff(Curl_tvnow(), conn->now); /* spent time */
2605 /* Without a requested timeout, we only wait 'response_time' seconds for
2606 the full response to arrive before we bail out */
2607 timeout_ms = ftp->response_time*1000 -
2608 Curl_tvdiff(Curl_tvnow(), ftp->response); /* spent time */
2614 /* called repeatedly until done from multi.c */
2615 CURLcode Curl_ftp_multi_statemach(struct connectdata *conn,
2618 curl_socket_t sock = conn->sock[FIRSTSOCKET];
2620 struct SessionHandle *data=conn->data;
2621 struct FTP *ftp = conn->proto.ftp;
2622 CURLcode result = CURLE_OK;
2623 long timeout_ms = ftp_state_timeout(conn);
2625 *done = FALSE; /* default to not done yet */
2627 if(timeout_ms <= 0) {
2628 failf(data, "FTP response timeout");
2629 return CURLE_OPERATION_TIMEDOUT;
2632 rc = Curl_select(ftp->sendleft?CURL_SOCKET_BAD:sock, /* reading */
2633 ftp->sendleft?sock:CURL_SOCKET_BAD, /* writing */
2637 failf(data, "select error");
2638 return CURLE_OUT_OF_MEMORY;
2641 result = ftp_statemach_act(conn);
2642 *done = (ftp->state == FTP_STOP);
2644 /* if rc == 0, then select() timed out */
2649 static CURLcode ftp_easy_statemach(struct connectdata *conn)
2651 curl_socket_t sock = conn->sock[FIRSTSOCKET];
2653 struct SessionHandle *data=conn->data;
2654 struct FTP *ftp = conn->proto.ftp;
2655 CURLcode result = CURLE_OK;
2657 while(ftp->state != FTP_STOP) {
2658 long timeout_ms = ftp_state_timeout(conn);
2660 if(timeout_ms <=0 ) {
2661 failf(data, "FTP response timeout");
2662 return CURLE_OPERATION_TIMEDOUT; /* already too little time */
2665 rc = Curl_select(ftp->sendleft?CURL_SOCKET_BAD:sock, /* reading */
2666 ftp->sendleft?sock:CURL_SOCKET_BAD, /* writing */
2670 failf(data, "select error");
2671 return CURLE_OUT_OF_MEMORY;
2674 result = CURLE_OPERATION_TIMEDOUT;
2678 result = ftp_statemach_act(conn);
2688 * Curl_ftp_connect() should do everything that is to be considered a part of
2689 * the connection phase.
2691 * The variable 'done' points to will be TRUE if the protocol-layer connect
2692 * phase is done when this function returns, or FALSE is not. When called as
2693 * a part of the easy interface, it will always be TRUE.
2695 CURLcode Curl_ftp_connect(struct connectdata *conn,
2696 bool *done) /* see description above */
2701 *done = FALSE; /* default to not done yet */
2703 ftp = (struct FTP *)calloc(sizeof(struct FTP), 1);
2705 return CURLE_OUT_OF_MEMORY;
2707 conn->proto.ftp = ftp;
2709 /* We always support persistant connections on ftp */
2710 conn->bits.close = FALSE;
2712 /* get some initial data into the ftp struct */
2713 ftp->bytecountp = &conn->bytecount;
2715 /* no need to duplicate them, this connectdata struct won't change */
2716 ftp->user = conn->user;
2717 ftp->passwd = conn->passwd;
2718 if (isBadFtpString(ftp->user) || isBadFtpString(ftp->passwd))
2719 return CURLE_URL_MALFORMAT;
2721 ftp->response_time = 3600; /* set default response time-out */
2723 #ifndef CURL_DISABLE_HTTP
2724 if (conn->bits.tunnel_proxy) {
2726 /* We want "seamless" FTP operations through HTTP proxy tunnel */
2727 result = Curl_ConnectHTTPProxyTunnel(conn, FIRSTSOCKET,
2728 conn->host.name, conn->remote_port);
2729 if(CURLE_OK != result)
2732 #endif /* CURL_DISABLE_HTTP */
2734 if(conn->protocol & PROT_FTPS) {
2736 /* FTPS is simply ftp with SSL for the control channel */
2737 /* now, perform the SSL initialization for this socket */
2738 result = Curl_SSLConnect(conn, FIRSTSOCKET);
2743 /* When we connect, we start in the state where we await the 220
2745 state(conn, FTP_WAIT220);
2746 ftp->response = Curl_tvnow(); /* start response time-out now! */
2748 if(conn->data->state.used_interface == Curl_if_multi)
2749 result = Curl_ftp_multi_statemach(conn, done);
2751 result = ftp_easy_statemach(conn);
2759 /***********************************************************************
2763 * The DONE function. This does what needs to be done after a single DO has
2766 * Input argument is already checked for validity.
2768 CURLcode Curl_ftp_done(struct connectdata *conn, CURLcode status)
2770 struct SessionHandle *data = conn->data;
2771 struct FTP *ftp = conn->proto.ftp;
2774 CURLcode result=CURLE_OK;
2775 bool was_ctl_valid = ftp->ctl_valid;
2780 /* now store a copy of the directory we are in */
2782 free(ftp->prevpath);
2784 path = curl_unescape(conn->path, 0); /* get the "raw" path */
2786 return CURLE_OUT_OF_MEMORY;
2788 flen = ftp->file?strlen(ftp->file):0; /* file is "raw" already */
2789 dlen = strlen(path)-flen;
2791 ftp->prevpath = path;
2793 /* if 'path' is not the whole string */
2794 ftp->prevpath[dlen]=0; /* terminate */
2795 infof(data, "Remembering we are in dir %s\n", ftp->prevpath);
2798 ftp->prevpath = NULL; /* no path */
2801 /* free the dir tree and file parts */
2804 ftp->ctl_valid = FALSE;
2806 if(data->set.upload) {
2807 if((-1 != data->set.infilesize) &&
2808 (data->set.infilesize != *ftp->bytecountp) &&
2810 failf(data, "Uploaded unaligned file size (%" FORMAT_OFF_T
2811 " out of %" FORMAT_OFF_T " bytes)",
2812 *ftp->bytecountp, data->set.infilesize);
2813 conn->bits.close = TRUE; /* close this connection since we don't
2814 know what state this error leaves us in */
2815 return CURLE_PARTIAL_FILE;
2819 if((-1 != conn->size) && (conn->size != *ftp->bytecountp) &&
2820 (conn->maxdownload != *ftp->bytecountp)) {
2821 failf(data, "Received only partial file: %" FORMAT_OFF_T " bytes",
2823 conn->bits.close = TRUE; /* close this connection since we don't
2824 know what state this error leaves us in */
2825 return CURLE_PARTIAL_FILE;
2827 else if(!ftp->dont_check &&
2828 !*ftp->bytecountp &&
2830 /* We consider this an error, but there's no true FTP error received
2831 why we need to continue to "read out" the server response too.
2832 We don't want to leave a "waiting" server reply if we'll get told
2833 to make a second request on this same connection! */
2834 failf(data, "No data was received!");
2835 result = CURLE_FTP_COULDNT_RETR_FILE;
2840 case CURLE_BAD_DOWNLOAD_RESUME:
2841 case CURLE_FTP_WEIRD_PASV_REPLY:
2842 case CURLE_FTP_PORT_FAILED:
2843 case CURLE_FTP_COULDNT_SET_BINARY:
2844 case CURLE_FTP_COULDNT_RETR_FILE:
2845 case CURLE_FTP_ACCESS_DENIED:
2846 /* the connection stays alive fine even though this happened */
2848 case CURLE_OK: /* doesn't affect the control connection's status */
2849 ftp->ctl_valid = was_ctl_valid;
2851 default: /* by default, an error means the control connection is
2852 wedged and should not be used anymore */
2853 ftp->ctl_valid = FALSE;
2858 Curl_sec_fflush_fd(conn, conn->sock[SECONDARYSOCKET]);
2861 /* shut down the socket to inform the server we're done */
2864 shutdown(conn->sock[SECONDARYSOCKET],2); /* SD_BOTH */
2867 sclose(conn->sock[SECONDARYSOCKET]);
2869 conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
2871 if(!ftp->no_transfer && !status) {
2872 /* Let's see what the server says about the transfer we just performed,
2873 * but lower the timeout as sometimes this connection has died while the
2874 * data has been transfered. This happens when doing through NATs etc that
2875 * abandon old silent connections.
2877 ftp->response_time = 60; /* give it only a minute for now */
2879 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
2881 ftp->response_time = 3600; /* set this back to one hour waits */
2883 if(!nread && (CURLE_OPERATION_TIMEDOUT == result)) {
2884 failf(data, "control connection looks dead");
2891 if(!ftp->dont_check) {
2892 /* 226 Transfer complete, 250 Requested file action okay, completed. */
2893 if((ftpcode != 226) && (ftpcode != 250)) {
2894 failf(data, "server did not report OK, got %d", ftpcode);
2895 return CURLE_FTP_WRITE_ERROR;
2900 /* clear these for next connection */
2901 ftp->no_transfer = FALSE;
2902 ftp->dont_check = FALSE;
2904 if (!result && conn->sec_conn) { /* 3rd party transfer */
2905 /* "done" with the secondary connection */
2906 result = Curl_ftp_done(conn->sec_conn, status);
2909 /* Send any post-transfer QUOTE strings? */
2910 if(!status && !result && data->set.postquote)
2911 result = ftp_sendquote(conn, data->set.postquote);
2916 /***********************************************************************
2920 * Where a 'quote' means a list of custom commands to send to the server.
2921 * The quote list is passed as an argument.
2925 CURLcode ftp_sendquote(struct connectdata *conn, struct curl_slist *quote)
2927 struct curl_slist *item;
2935 FTPSENDF(conn, "%s", item->data);
2937 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
2941 if (ftpcode >= 400) {
2942 failf(conn->data, "QUOT string not accepted: %s", item->data);
2943 return CURLE_FTP_QUOTE_ERROR;
2953 /***********************************************************************
2955 * ftp_transfertype()
2957 * Set transfer type. We only deal with ASCII or BINARY so this function
2960 static CURLcode ftp_transfertype(struct connectdata *conn,
2963 struct SessionHandle *data = conn->data;
2968 FTPSENDF(conn, "TYPE %s", ascii?"A":"I");
2970 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
2974 if(ftpcode != 200) {
2975 failf(data, "Couldn't set %s mode",
2976 ascii?"ASCII":"binary");
2977 return ascii? CURLE_FTP_COULDNT_SET_ASCII:CURLE_FTP_COULDNT_SET_BINARY;
2983 /***************************************************************************
2985 * ftp_pasv_verbose()
2987 * This function only outputs some informationals about this second connection
2988 * when we've issued a PASV command before and thus we have connected to a
2989 * possibly new IP address.
2993 ftp_pasv_verbose(struct connectdata *conn,
2995 char *newhost, /* ascii version */
2999 Curl_printable_address(ai, buf, sizeof(buf));
3000 infof(conn->data, "Connecting to %s (%s) port %d\n", newhost, buf, port);
3004 Check if this is a range download, and if so, set the internal variables
3008 static CURLcode ftp_range(struct connectdata *conn)
3010 curl_off_t from, to;
3011 curl_off_t totalsize=-1;
3014 struct SessionHandle *data = conn->data;
3015 struct FTP *ftp = conn->proto.ftp;
3017 if(conn->bits.use_range && conn->range) {
3018 from=curlx_strtoofft(conn->range, &ptr, 0);
3019 while(ptr && *ptr && (isspace((int)*ptr) || (*ptr=='-')))
3021 to=curlx_strtoofft(ptr, &ptr2, 0);
3023 /* we didn't get any digit */
3026 if((-1 == to) && (from>=0)) {
3028 conn->resume_from = from;
3029 infof(data, "FTP RANGE %" FORMAT_OFF_T " to end of file\n", from);
3034 conn->maxdownload = -from;
3035 conn->resume_from = from;
3036 infof(data, "FTP RANGE the last %" FORMAT_OFF_T " bytes\n", totalsize);
3040 totalsize = to-from;
3041 conn->maxdownload = totalsize+1; /* include the last mentioned byte */
3042 conn->resume_from = from;
3043 infof(data, "FTP RANGE from %" FORMAT_OFF_T
3044 " getting %" FORMAT_OFF_T " bytes\n", from, conn->maxdownload);
3046 infof(data, "range-download from %" FORMAT_OFF_T
3047 " to %" FORMAT_OFF_T ", totally %" FORMAT_OFF_T " bytes\n",
3048 from, to, conn->maxdownload);
3049 ftp->dont_check = TRUE; /* dont check for successful transfer */
3056 * Curl_ftp_nextconnect()
3058 * This function shall be called when the second FTP (data) connection is
3062 CURLcode Curl_ftp_nextconnect(struct connectdata *conn)
3064 struct SessionHandle *data=conn->data;
3065 CURLcode result = CURLE_OK;
3067 /* the ftp struct is inited in Curl_ftp_connect() */
3068 struct FTP *ftp = conn->proto.ftp;
3070 infof(data, "DO-MORE phase starts\n");
3072 if(!ftp->no_transfer && !conn->bits.no_body) {
3073 /* a transfer is about to take place */
3075 if(data->set.upload) {
3076 NBFTPSENDF(conn, "TYPE %c", data->set.ftp_ascii?'A':'I');
3077 state(conn, FTP_STOR_TYPE);
3081 ftp->downloadsize = -1; /* unknown as of yet */
3083 result = ftp_range(conn);
3086 else if((data->set.ftp_list_only) || !ftp->file) {
3087 /* The specified path ends with a slash, and therefore we think this
3088 is a directory that is requested, use LIST. But before that we
3089 need to set ASCII transfer mode. */
3090 NBFTPSENDF(conn, "TYPE A", NULL);
3091 state(conn, FTP_LIST_TYPE);
3094 NBFTPSENDF(conn, "TYPE %c", data->set.ftp_ascii?'A':'I');
3095 state(conn, FTP_RETR_TYPE);
3098 result = ftp_easy_statemach(conn);
3101 if(ftp->no_transfer)
3102 /* no data to transfer. FIX: it feels like a kludge to have this here
3104 result=Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
3106 /* end of transfer */
3107 infof(data, "DO-MORE phase ends\n");
3114 /***********************************************************************
3118 * This is the actual DO function for FTP. Get a file/directory according to
3119 * the options previously setup.
3123 CURLcode ftp_perform(struct connectdata *conn,
3124 bool *connected, /* connect status after PASV / PORT */
3127 /* this is FTP and no proxy */
3128 CURLcode result=CURLE_OK;
3129 struct SessionHandle *data=conn->data;
3131 infof(data, "DO phase starts\n");
3133 *dophase_done = FALSE; /* not done yet */
3135 /* start the first command in the DO phase */
3136 result = ftp_state_quote(conn, TRUE, FTP_QUOTE);
3140 /* run the state-machine */
3141 if(conn->data->state.used_interface == Curl_if_multi)
3142 result = Curl_ftp_multi_statemach(conn, dophase_done);
3144 result = ftp_easy_statemach(conn);
3145 *dophase_done = TRUE; /* with the easy interface we are done here */
3147 *connected = conn->bits.tcpconnect;
3150 infof(data, "DO phase is comlete\n");
3155 /***********************************************************************
3159 * This function is registered as 'curl_do' function. It decodes the path
3160 * parts etc as a wrapper to the actual DO function (ftp_perform).
3162 * The input argument is already checked for validity.
3164 CURLcode Curl_ftp(struct connectdata *conn, bool *done)
3166 CURLcode retcode = CURLE_OK;
3168 *done = FALSE; /* default to false */
3170 retcode = ftp_parse_url_path(conn);
3174 if (conn->sec_conn) {
3175 /* 3rd party transfer */
3176 *done = TRUE; /* BLOCKING */
3177 retcode = ftp_3rdparty(conn);
3180 retcode = ftp_regular_transfer(conn, done);
3185 /***********************************************************************
3187 * Curl_(nb)ftpsendf()
3189 * Sends the formated string as a ftp command to a ftp server
3191 * NOTE: we build the command in a fixed-length buffer, which sets length
3192 * restrictions on the command!
3194 * The "nb" version is made to Never Block.
3196 CURLcode Curl_nbftpsendf(struct connectdata *conn,
3197 const char *fmt, ...)
3199 ssize_t bytes_written;
3203 CURLcode res = CURLE_OK;
3204 struct FTP *ftp = conn->proto.ftp;
3205 struct SessionHandle *data = conn->data;
3209 vsnprintf(s, 250, fmt, ap);
3212 strcat(s, "\r\n"); /* append a trailing CRLF */
3215 write_len = strlen(s);
3217 res = Curl_write(conn, conn->sock[FIRSTSOCKET], sptr, write_len,
3223 if(conn->data->set.verbose)
3224 Curl_debug(conn->data, CURLINFO_HEADER_OUT, sptr, bytes_written,
3227 if(bytes_written != (ssize_t)write_len) {
3228 /* the whole chunk was not sent, store the rest of the data */
3229 write_len -= bytes_written;
3230 sptr += bytes_written;
3231 ftp->sendthis = malloc(write_len);
3233 memcpy(ftp->sendthis, sptr, write_len);
3234 ftp->sendsize=ftp->sendleft=write_len;
3237 failf(data, "out of memory");
3238 res = CURLE_OUT_OF_MEMORY;
3242 ftp->response = Curl_tvnow();
3247 CURLcode Curl_ftpsendf(struct connectdata *conn,
3248 const char *fmt, ...)
3250 ssize_t bytes_written;
3254 CURLcode res = CURLE_OK;
3258 vsnprintf(s, 250, fmt, ap);
3261 strcat(s, "\r\n"); /* append a trailing CRLF */
3264 write_len = strlen(s);
3267 res = Curl_write(conn, conn->sock[FIRSTSOCKET], sptr, write_len,
3273 if(conn->data->set.verbose)
3274 Curl_debug(conn->data, CURLINFO_HEADER_OUT, sptr, bytes_written, conn);
3276 if(bytes_written != (ssize_t)write_len) {
3277 write_len -= bytes_written;
3278 sptr += bytes_written;
3287 /***********************************************************************
3291 * This should be called before calling sclose() on an ftp control connection
3292 * (not data connections). We should then wait for the response from the
3293 * server before returning. The calling code should then try to close the
3297 static CURLcode ftp_quit(struct connectdata *conn)
3299 CURLcode result = CURLE_OK;
3301 if(conn->proto.ftp->ctl_valid) {
3302 NBFTPSENDF(conn, "QUIT", NULL);
3303 state(conn, FTP_QUIT);
3305 result = ftp_easy_statemach(conn);
3311 /***********************************************************************
3313 * Curl_ftp_disconnect()
3315 * Disconnect from an FTP server. Cleanup protocol-specific per-connection
3316 * resources. BLOCKING.
3318 CURLcode Curl_ftp_disconnect(struct connectdata *conn)
3320 struct FTP *ftp= conn->proto.ftp;
3322 /* We cannot send quit unconditionally. If this connection is stale or
3323 bad in any way, sending quit and waiting around here will make the
3324 disconnect wait in vain and cause more problems than we need to.
3326 ftp_quit() will check the state of ftp->ctl_valid. If it's ok it
3327 will try to send the QUIT command, otherwise it will just return.
3330 /* The FTP session may or may not have been allocated/setup at this point! */
3332 (void)ftp_quit(conn); /* ignore errors on the QUIT */
3335 free(ftp->entrypath);
3342 free(ftp->prevpath);
3343 ftp->prevpath = NULL;
3349 /***********************************************************************
3353 * Makes a directory on the FTP server.
3357 static CURLcode ftp_mkd(struct connectdata *conn, char *path)
3359 CURLcode result=CURLE_OK;
3360 int ftpcode; /* for ftp status */
3363 /* Create a directory on the remote server */
3364 FTPSENDF(conn, "MKD %s", path);
3366 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
3373 infof( conn->data , "Created remote directory %s\n" , path );
3376 failf(conn->data, "Permission denied to make directory %s", path);
3377 result = CURLE_FTP_ACCESS_DENIED;
3380 failf(conn->data, "unrecognized MKD response: %d", ftpcode );
3381 result = CURLE_FTP_ACCESS_DENIED;
3387 /***********************************************************************
3391 * Send 'CWD' to the remote server to Change Working Directory. It is the ftp
3392 * version of the unix 'cd' command. This function is only called from the
3393 * ftp_cwd_and_mkd() function these days.
3395 * This function does NOT call failf().
3398 CURLcode ftp_cwd(struct connectdata *conn, char *path)
3404 FTPSENDF(conn, "CWD %s", path);
3405 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
3407 /* According to RFC959, CWD is supposed to return 250 on success, but
3408 there seem to be non-compliant FTP servers out there that return 200,
3409 so we accept any '2xy' code here. */
3410 if (ftpcode/100 != 2)
3411 result = CURLE_FTP_ACCESS_DENIED;
3417 /***********************************************************************
3421 * Change to the given directory. If the directory is not present, and we
3422 * have been told to allow it, then create the directory and cd to it.
3425 static CURLcode ftp_cwd_and_mkd(struct connectdata *conn, char *path)
3429 result = ftp_cwd(conn, path);
3431 if(conn->data->set.ftp_create_missing_dirs) {
3432 result = ftp_mkd(conn, path);
3434 /* ftp_mkd() calls failf() itself */
3436 result = ftp_cwd(conn, path);
3439 failf(conn->data, "Couldn't CWD to %s", path);
3446 /***********************************************************************
3448 * ftp_3rdparty_pretransfer()
3450 * Preparation for 3rd party transfer.
3453 static CURLcode ftp_3rdparty_pretransfer(struct connectdata *conn)
3455 CURLcode result = CURLE_OK;
3456 struct SessionHandle *data = conn->data;
3457 struct connectdata *sec_conn = conn->sec_conn;
3459 conn->xfertype = TARGET3RD;
3460 sec_conn->xfertype = SOURCE3RD;
3462 /* sets transfer type */
3463 result = ftp_transfertype(conn, data->set.ftp_ascii);
3467 result = ftp_transfertype(sec_conn, data->set.ftp_ascii);
3471 /* Send any PREQUOTE strings after transfer type is set? */
3472 if (data->set.source_prequote) {
3473 /* sends command(s) to source server before file transfer */
3474 result = ftp_sendquote(sec_conn, data->set.source_prequote);
3476 if (!result && data->set.prequote)
3477 result = ftp_sendquote(conn, data->set.prequote);
3484 /***********************************************************************
3486 * ftp_3rdparty_transfer()
3488 * Performs 3rd party transfer.
3491 static CURLcode ftp_3rdparty_transfer(struct connectdata *conn)
3493 CURLcode result = CURLE_OK;
3495 int ftpcode, ip[4], port[2];
3496 struct SessionHandle *data = conn->data;
3497 struct connectdata *sec_conn = conn->sec_conn;
3498 char *buf = data->state.buffer; /* this is our buffer */
3501 const char *stor_cmd;
3502 struct connectdata *pasv_conn;
3503 struct connectdata *port_conn;
3505 if (data->set.ftpport == NULL) {
3507 port_conn = sec_conn;
3510 pasv_conn = sec_conn;
3514 result = ftp_cwd_and_create_path(conn);
3518 /* sets the passive mode */
3519 FTPSENDF(pasv_conn, "%s", "PASV");
3520 result = Curl_GetFTPResponse(&nread, pasv_conn, &ftpcode);
3524 if (ftpcode != 227) {
3525 failf(data, "Odd return code after PASV: %03d", ftpcode);
3526 return CURLE_FTP_WEIRD_PASV_REPLY;
3530 if (6 == sscanf(str, "%d,%d,%d,%d,%d,%d",
3531 &ip[0], &ip[1], &ip[2], &ip[3], &port[0], &port[1]))
3537 failf(pasv_conn->data, "Couldn't interpret the 227-reply");
3538 return CURLE_FTP_WEIRD_227_FORMAT;
3541 snprintf(pasv_port, sizeof(pasv_port), "%d,%d,%d,%d,%d,%d", ip[0], ip[1],
3542 ip[2], ip[3], port[0], port[1]);
3544 /* sets data connection between remote hosts */
3545 FTPSENDF(port_conn, "PORT %s", pasv_port);
3546 result = Curl_GetFTPResponse(&nread, port_conn, &ftpcode);
3550 if (ftpcode != 200) {
3551 failf(data, "PORT command attempts failed: %03d", ftpcode);
3552 return CURLE_FTP_PORT_FAILED;
3555 /* we might append onto the file instead of overwriting it */
3556 stor_cmd = data->set.ftp_append?"APPE":"STOR";
3558 /* transfers file between remote hosts */
3559 /* FIX: this should send a series of CWD commands and then RETR only the
3560 ftp->file file. The conn->path "full path" is not unescaped. Test case
3562 FTPSENDF(sec_conn, "RETR %s", sec_conn->path);
3564 if(!data->set.ftpport) {
3566 result = Curl_GetFTPResponse(&nread, sec_conn, &ftpcode);
3570 if((ftpcode != 150) && (ftpcode != 125)) {
3571 failf(data, "Failed RETR: %03d", ftpcode);
3572 return CURLE_FTP_COULDNT_RETR_FILE;
3575 result = Curl_ftpsendf(conn, "%s %s", stor_cmd, conn->proto.ftp->file);
3576 if(CURLE_OK == result)
3577 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
3581 if (ftpcode >= 400) {
3582 failf(data, "Failed FTP upload: %03d", ftpcode);
3583 return CURLE_FTP_COULDNT_STOR_FILE;
3589 result = Curl_ftpsendf(conn, "%s %s", stor_cmd, conn->proto.ftp->file);
3590 if(CURLE_OK == result)
3591 result = Curl_GetFTPResponse(&nread, sec_conn, &ftpcode);
3595 if (ftpcode >= 400) {
3596 failf(data, "Failed FTP upload: %03d", ftpcode);
3597 return CURLE_FTP_COULDNT_STOR_FILE;
3600 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
3604 if((ftpcode != 150) && (ftpcode != 125)) {
3605 failf(data, "Failed FTP upload: %03d", ftpcode);
3606 return CURLE_FTP_COULDNT_STOR_FILE;
3615 /***********************************************************************
3617 * ftp_parse_url_path()
3619 * Parse the URL path into separate path components.
3623 CURLcode ftp_parse_url_path(struct connectdata *conn)
3625 CURLcode retcode = CURLE_OK;
3626 struct SessionHandle *data = conn->data;
3630 char *slash_pos; /* position of the first '/' char in curpos */
3631 char *cur_pos = conn->path; /* current position in path. point at the begin
3632 of next path component */
3634 /* the ftp struct is already inited in ftp_connect() */
3635 ftp = conn->proto.ftp;
3636 ftp->ctl_valid = FALSE;
3639 ftp->diralloc = 5; /* default dir depth to allocate */
3640 ftp->dirs = (char **)calloc(ftp->diralloc, sizeof(ftp->dirs[0]));
3642 return CURLE_OUT_OF_MEMORY;
3644 /* parse the URL path into separate path components */
3645 while((slash_pos=strchr(cur_pos, '/'))) {
3646 /* 1 or 0 to indicate absolute directory */
3647 bool absolute_dir = (cur_pos - conn->path > 0) && (ftp->dirdepth == 0);
3649 /* seek out the next path component */
3650 if (slash_pos-cur_pos) {
3651 /* we skip empty path components, like "x//y" since the FTP command CWD
3652 requires a parameter and a non-existant parameter a) doesn't work on
3653 many servers and b) has no effect on the others. */
3654 int len = (int)(slash_pos - cur_pos + absolute_dir);
3655 ftp->dirs[ftp->dirdepth] = curl_unescape(cur_pos - absolute_dir, len);
3657 if (!ftp->dirs[ftp->dirdepth]) { /* run out of memory ... */
3658 failf(data, "no memory");
3660 return CURLE_OUT_OF_MEMORY;
3662 if (isBadFtpString(ftp->dirs[ftp->dirdepth])) {
3664 return CURLE_URL_MALFORMAT;
3668 cur_pos = slash_pos + 1; /* jump to the rest of the string */
3673 cur_pos = slash_pos + 1; /* jump to the rest of the string */
3674 if(++ftp->dirdepth >= ftp->diralloc) {
3677 ftp->diralloc *= 2; /* double the size each time */
3678 bigger = realloc(ftp->dirs, ftp->diralloc * sizeof(ftp->dirs[0]));
3682 return CURLE_OUT_OF_MEMORY;
3684 ftp->dirs = (char **)bigger;
3689 ftp->file = cur_pos; /* the rest is the file name */
3692 ftp->file = curl_unescape(ftp->file, 0);
3693 if(NULL == ftp->file) {
3695 failf(data, "no memory");
3696 return CURLE_OUT_OF_MEMORY;
3698 if (isBadFtpString(ftp->file)) {
3700 return CURLE_URL_MALFORMAT;
3704 ftp->file=NULL; /* instead of point to a zero byte, we make it a NULL
3707 ftp->cwddone = FALSE; /* default to not done */
3710 /* prevpath is "raw" so we convert the input path before we compare the
3712 char *path = curl_unescape(conn->path, 0);
3714 return CURLE_OUT_OF_MEMORY;
3716 dlen = strlen(path) - (ftp->file?strlen(ftp->file):0);
3717 if((dlen == strlen(ftp->prevpath)) &&
3718 curl_strnequal(path, ftp->prevpath, dlen)) {
3719 infof(data, "Request has same path as previous transfer\n");
3720 ftp->cwddone = TRUE;
3730 /***********************************************************************
3732 * ftp_cwd_and_create_path()
3734 * Creates full path on remote target host.
3738 CURLcode ftp_cwd_and_create_path(struct connectdata *conn)
3740 CURLcode result = CURLE_OK;
3741 /* the ftp struct is already inited in Curl_ftp_connect() */
3742 struct FTP *ftp = conn->proto.ftp;
3746 /* already done and fine */
3749 /* This is a re-used connection. Since we change directory to where the
3750 transfer is taking place, we must now get back to the original dir
3751 where we ended up after login: */
3752 if (conn->bits.reuse && ftp->entrypath) {
3753 if ((result = ftp_cwd_and_mkd(conn, ftp->entrypath)) != CURLE_OK)
3757 for (i=0; i < ftp->dirdepth; i++) {
3758 /* RFC 1738 says empty components should be respected too, but
3759 that is plain stupid since CWD can't be used with an empty argument */
3760 if ((result = ftp_cwd_and_mkd(conn, ftp->dirs[i])) != CURLE_OK)
3767 /* call this when the DO phase has completed */
3768 static CURLcode ftp_dophase_done(struct connectdata *conn,
3771 CURLcode result = CURLE_OK;
3772 struct FTP *ftp = conn->proto.ftp;
3775 result = Curl_ftp_nextconnect(conn);
3777 if(result && (conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD)) {
3778 /* Failure detected, close the second socket if it was created already */
3779 sclose(conn->sock[SECONDARYSOCKET]);
3780 conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
3783 if(ftp->no_transfer)
3784 /* no data to transfer */
3785 result=Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
3787 /* since we didn't connect now, we want do_more to get called */
3788 conn->bits.do_more = TRUE;
3790 ftp->ctl_valid = TRUE; /* seems good */
3795 /* called from multi.c while DOing */
3796 CURLcode Curl_ftp_doing(struct connectdata *conn,
3800 result = Curl_ftp_multi_statemach(conn, dophase_done);
3803 result = ftp_dophase_done(conn, FALSE /* not connected */);
3805 infof(conn->data, "DO phase is comlete\n");
3810 /***********************************************************************
3812 * ftp_regular_transfer()
3814 * The input argument is already checked for validity.
3816 * Performs all commands done before a regular transfer between a local and a
3819 * ftp->ctl_valid starts out as FALSE, and gets set to TRUE if we reach the
3820 * Curl_ftp_done() function without finding any major problem.
3823 CURLcode ftp_regular_transfer(struct connectdata *conn,
3826 CURLcode result=CURLE_OK;
3828 struct SessionHandle *data = conn->data;
3831 /* the ftp struct is already inited in ftp_connect() */
3832 ftp = conn->proto.ftp;
3833 conn->size = -1; /* make sure this is unknown at this point */
3835 Curl_pgrsSetUploadCounter(data, 0);
3836 Curl_pgrsSetDownloadCounter(data, 0);
3837 Curl_pgrsSetUploadSize(data, 0);
3838 Curl_pgrsSetDownloadSize(data, 0);
3840 ftp->ctl_valid = TRUE; /* starts good */
3842 result = ftp_perform(conn,
3843 &connected, /* have we connected after PASV/PORT */
3844 dophase_done); /* all commands in the DO-phase done? */
3846 if(CURLE_OK == result) {
3849 /* the DO phase has not completed yet */
3852 result = ftp_dophase_done(conn, connected);
3864 /***********************************************************************
3868 * The input argument is already checked for validity.
3869 * Performs a 3rd party transfer between two remote hosts.
3871 static CURLcode ftp_3rdparty(struct connectdata *conn)
3873 CURLcode result = CURLE_OK;
3875 conn->proto.ftp->ctl_valid = conn->sec_conn->proto.ftp->ctl_valid = TRUE;
3876 conn->size = conn->sec_conn->size = -1;
3878 result = ftp_3rdparty_pretransfer(conn);
3880 result = ftp_3rdparty_transfer(conn);
3885 #endif /* CURL_DISABLE_FTP */