1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2011, 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.
21 ***************************************************************************/
25 #ifndef CURL_DISABLE_FTP
31 #ifdef HAVE_SYS_SOCKET_H
32 #include <sys/socket.h>
34 #ifdef HAVE_NETINET_IN_H
35 #include <netinet/in.h>
37 #ifdef HAVE_ARPA_INET_H
38 #include <arpa/inet.h>
41 #include <sys/utsname.h>
51 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
53 #define in_addr_t unsigned long
56 #include <curl/curl.h>
64 #include "http.h" /* for HTTP proxy tunnel stuff */
68 #include "ftplistparser.h"
70 #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
74 #include "strtoofft.h"
79 #include "inet_ntop.h"
80 #include "inet_pton.h"
82 #include "parsedate.h" /* for the week day and month names */
83 #include "sockaddr.h" /* required for Curl_sockaddr_storage */
87 #include "speedcheck.h"
89 #include "http_proxy.h"
90 #include "non-ascii.h"
92 #define _MPRINTF_REPLACE /* use our functions only */
93 #include <curl/mprintf.h>
95 #include "curl_memory.h"
96 /* The last #include file should be: */
100 #define NI_MAXHOST 1025
102 #ifndef INET_ADDRSTRLEN
103 #define INET_ADDRSTRLEN 16
106 #ifdef CURL_DISABLE_VERBOSE_STRINGS
107 #define ftp_pasv_verbose(a,b,c,d) Curl_nop_stmt
110 /* Local API functions */
111 static CURLcode ftp_sendquote(struct connectdata *conn,
112 struct curl_slist *quote);
113 static CURLcode ftp_quit(struct connectdata *conn);
114 static CURLcode ftp_parse_url_path(struct connectdata *conn);
115 static CURLcode ftp_regular_transfer(struct connectdata *conn, bool *done);
116 #ifndef CURL_DISABLE_VERBOSE_STRINGS
117 static void ftp_pasv_verbose(struct connectdata *conn,
119 char *newhost, /* ascii version */
122 static CURLcode ftp_state_post_rest(struct connectdata *conn);
123 static CURLcode ftp_state_post_cwd(struct connectdata *conn);
124 static CURLcode ftp_state_quote(struct connectdata *conn,
125 bool init, ftpstate instate);
126 static CURLcode ftp_nb_type(struct connectdata *conn,
127 bool ascii, ftpstate newstate);
128 static int ftp_need_type(struct connectdata *conn,
130 static CURLcode ftp_do(struct connectdata *conn, bool *done);
131 static CURLcode ftp_done(struct connectdata *conn,
132 CURLcode, bool premature);
133 static CURLcode ftp_connect(struct connectdata *conn, bool *done);
134 static CURLcode ftp_disconnect(struct connectdata *conn, bool dead_connection);
135 static CURLcode ftp_nextconnect(struct connectdata *conn);
136 static CURLcode ftp_multi_statemach(struct connectdata *conn, bool *done);
137 static int ftp_getsock(struct connectdata *conn, curl_socket_t *socks,
139 static int ftp_domore_getsock(struct connectdata *conn, curl_socket_t *socks,
141 static CURLcode ftp_doing(struct connectdata *conn,
143 static CURLcode ftp_setup_connection(struct connectdata * conn);
145 static CURLcode init_wc_data(struct connectdata *conn);
146 static CURLcode wc_statemach(struct connectdata *conn);
148 static void wc_data_dtor(void *ptr);
150 static CURLcode ftp_state_post_retr_size(struct connectdata *conn,
151 curl_off_t filesize);
153 /* easy-to-use macro: */
154 #define FTPSENDF(x,y,z) if((result = Curl_ftpsendf(x,y,z)) != CURLE_OK) \
156 #define PPSENDF(x,y,z) if((result = Curl_pp_sendf(x,y,z)) != CURLE_OK) \
161 * FTP protocol handler.
164 const struct Curl_handler Curl_handler_ftp = {
166 ftp_setup_connection, /* setup_connection */
169 ftp_nextconnect, /* do_more */
170 ftp_connect, /* connect_it */
171 ftp_multi_statemach, /* connecting */
172 ftp_doing, /* doing */
173 ftp_getsock, /* proto_getsock */
174 ftp_getsock, /* doing_getsock */
175 ftp_domore_getsock, /* domore_getsock */
176 ZERO_NULL, /* perform_getsock */
177 ftp_disconnect, /* disconnect */
178 ZERO_NULL, /* readwrite */
179 PORT_FTP, /* defport */
180 CURLPROTO_FTP, /* protocol */
181 PROTOPT_DUAL | PROTOPT_CLOSEACTION | PROTOPT_NEEDSPWD /* flags */
187 * FTPS protocol handler.
190 const struct Curl_handler Curl_handler_ftps = {
192 ftp_setup_connection, /* setup_connection */
195 ftp_nextconnect, /* do_more */
196 ftp_connect, /* connect_it */
197 ftp_multi_statemach, /* connecting */
198 ftp_doing, /* doing */
199 ftp_getsock, /* proto_getsock */
200 ftp_getsock, /* doing_getsock */
201 ftp_domore_getsock, /* domore_getsock */
202 ZERO_NULL, /* perform_getsock */
203 ftp_disconnect, /* disconnect */
204 ZERO_NULL, /* readwrite */
205 PORT_FTPS, /* defport */
206 CURLPROTO_FTP | CURLPROTO_FTPS, /* protocol */
207 PROTOPT_SSL | PROTOPT_DUAL | PROTOPT_CLOSEACTION |
208 PROTOPT_NEEDSPWD /* flags */
212 #ifndef CURL_DISABLE_HTTP
214 * HTTP-proxyed FTP protocol handler.
217 static const struct Curl_handler Curl_handler_ftp_proxy = {
219 ZERO_NULL, /* setup_connection */
220 Curl_http, /* do_it */
221 Curl_http_done, /* done */
222 ZERO_NULL, /* do_more */
223 ZERO_NULL, /* connect_it */
224 ZERO_NULL, /* connecting */
225 ZERO_NULL, /* doing */
226 ZERO_NULL, /* proto_getsock */
227 ZERO_NULL, /* doing_getsock */
228 ZERO_NULL, /* domore_getsock */
229 ZERO_NULL, /* perform_getsock */
230 ZERO_NULL, /* disconnect */
231 ZERO_NULL, /* readwrite */
232 PORT_FTP, /* defport */
233 CURLPROTO_HTTP, /* protocol */
234 PROTOPT_NONE /* flags */
240 * HTTP-proxyed FTPS protocol handler.
243 static const struct Curl_handler Curl_handler_ftps_proxy = {
245 ZERO_NULL, /* setup_connection */
246 Curl_http, /* do_it */
247 Curl_http_done, /* done */
248 ZERO_NULL, /* do_more */
249 ZERO_NULL, /* connect_it */
250 ZERO_NULL, /* connecting */
251 ZERO_NULL, /* doing */
252 ZERO_NULL, /* proto_getsock */
253 ZERO_NULL, /* doing_getsock */
254 ZERO_NULL, /* domore_getsock */
255 ZERO_NULL, /* perform_getsock */
256 ZERO_NULL, /* disconnect */
257 ZERO_NULL, /* readwrite */
258 PORT_FTPS, /* defport */
259 CURLPROTO_HTTP, /* protocol */
260 PROTOPT_NONE /* flags */
267 * NOTE: back in the old days, we added code in the FTP code that made NOBODY
268 * requests on files respond with headers passed to the client/stdout that
269 * looked like HTTP ones.
271 * This approach is not very elegant, it causes confusion and is error-prone.
272 * It is subject for removal at the next (or at least a future) soname bump.
273 * Until then you can test the effects of the removal by undefining the
274 * following define named CURL_FTP_HTTPSTYLE_HEAD.
276 #define CURL_FTP_HTTPSTYLE_HEAD 1
278 static void freedirs(struct ftp_conn *ftpc)
282 for(i=0; i < ftpc->dirdepth; i++) {
298 /* Returns non-zero if the given string contains CR (\r) or LF (\n),
299 which are not allowed within RFC 959 <string>.
300 Note: The input string is in the client's encoding which might
301 not be ASCII, so escape sequences \r & \n must be used instead
302 of hex values 0x0d & 0x0a.
304 static bool isBadFtpString(const char *string)
306 return ((NULL != strchr(string, '\r')) ||
307 (NULL != strchr(string, '\n'))) ? TRUE : FALSE;
310 /***********************************************************************
312 * AllowServerConnect()
314 * When we've issue the PORT command, we have told the server to connect
315 * to us. This function will sit and wait here until the server has
319 static CURLcode AllowServerConnect(struct connectdata *conn)
321 struct SessionHandle *data = conn->data;
322 curl_socket_t sock = conn->sock[SECONDARYSOCKET];
325 curl_socket_t s = CURL_SOCKET_BAD;
327 struct Curl_sockaddr_storage add;
329 struct sockaddr_in add;
331 curl_socklen_t size = (curl_socklen_t) sizeof(add);
334 timeout_ms = Curl_timeleft(data, NULL, TRUE);
337 /* if a timeout was already reached, bail out */
338 failf(data, "Timeout while waiting for server connect");
339 return CURLE_OPERATION_TIMEDOUT;
342 interval_ms = 1000; /* use 1 second timeout intervals */
343 if(timeout_ms < interval_ms)
344 interval_ms = timeout_ms;
346 switch (Curl_socket_ready(sock, CURL_SOCKET_BAD, interval_ms)) {
349 failf(data, "Error while waiting for server connect");
350 return CURLE_FTP_PORT_FAILED;
351 case 0: /* timeout */
354 /* we have received data here */
355 if(0 == getsockname(sock, (struct sockaddr *) &add, &size)) {
358 s=accept(sock, (struct sockaddr *) &add, &size);
360 Curl_closesocket(conn, sock); /* close the first socket */
362 if(CURL_SOCKET_BAD == s) {
363 failf(data, "Error accept()ing server connect");
364 return CURLE_FTP_PORT_FAILED;
366 infof(data, "Connection accepted from server\n");
368 conn->sock[SECONDARYSOCKET] = s;
369 curlx_nonblock(s, TRUE); /* enable non-blocking */
373 /* never reaches this point */
376 /* macro to check for a three-digit ftp status code at the start of the
378 #define STATUSCODE(line) (ISDIGIT(line[0]) && ISDIGIT(line[1]) && \
381 /* macro to check for the last line in an FTP server response */
382 #define LASTLINE(line) (STATUSCODE(line) && (' ' == line[3]))
384 static int ftp_endofresp(struct pingpong *pp,
387 char *line = pp->linestart_resp;
388 size_t len = pp->nread_resp;
390 if((len > 3) && LASTLINE(line)) {
391 *code = curlx_sltosi(strtol(line, NULL, 10));
397 static CURLcode ftp_readresp(curl_socket_t sockfd,
399 int *ftpcode, /* return the ftp-code if done */
400 size_t *size) /* size of the response */
402 struct connectdata *conn = pp->conn;
403 #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
404 struct SessionHandle *data = conn->data;
405 char * const buf = data->state.buffer;
407 CURLcode result = CURLE_OK;
410 result = Curl_pp_readresp(sockfd, pp, &code, size);
412 #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
413 /* handle the security-oriented responses 6xx ***/
414 /* FIXME: some errorchecking perhaps... ***/
417 code = Curl_sec_read_msg(conn, buf, PROT_SAFE);
420 code = Curl_sec_read_msg(conn, buf, PROT_PRIVATE);
423 code = Curl_sec_read_msg(conn, buf, PROT_CONFIDENTIAL);
426 /* normal ftp stuff we pass through! */
431 /* store the latest code for later retrieval */
432 conn->data->info.httpcode=code;
438 /* 421 means "Service not available, closing control connection." and FTP
439 * servers use it to signal that idle session timeout has been exceeded.
440 * If we ignored the response, it could end up hanging in some cases. */
441 return CURLE_OPERATION_TIMEDOUT;
446 /* --- parse FTP server responses --- */
449 * Curl_GetFTPResponse() is a BLOCKING function to read the full response
450 * from a server after a command.
454 CURLcode Curl_GetFTPResponse(ssize_t *nreadp, /* return number of bytes read */
455 struct connectdata *conn,
456 int *ftpcode) /* return the ftp-code */
459 * We cannot read just one byte per read() and then go back to select() as
460 * the OpenSSL read() doesn't grok that properly.
462 * Alas, read as much as possible, split up into lines, use the ending
463 * line in a response or continue reading. */
465 curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
466 long timeout; /* timeout in milliseconds */
468 struct SessionHandle *data = conn->data;
469 CURLcode result = CURLE_OK;
470 struct ftp_conn *ftpc = &conn->proto.ftpc;
471 struct pingpong *pp = &ftpc->pp;
474 int value_to_be_ignored=0;
477 *ftpcode = 0; /* 0 for errors */
479 /* make the pointer point to something for the rest of this function */
480 ftpcode = &value_to_be_ignored;
484 while(!*ftpcode && !result) {
485 /* check and reset timeout value every lap */
486 timeout = Curl_pp_state_timeout(pp);
489 failf(data, "FTP response timeout");
490 return CURLE_OPERATION_TIMEDOUT; /* already too little time */
493 interval_ms = 1000; /* use 1 second timeout intervals */
494 if(timeout < interval_ms)
495 interval_ms = timeout;
498 * Since this function is blocking, we need to wait here for input on the
499 * connection and only then we call the response reading function. We do
500 * timeout at least every second to make the timeout check run.
502 * A caution here is that the ftp_readresp() function has a cache that may
503 * contain pieces of a response from the previous invoke and we need to
504 * make sure we don't just wait for input while there is unhandled data in
505 * that cache. But also, if the cache is there, we call ftp_readresp() and
506 * the cache wasn't good enough to continue we must not just busy-loop
507 * around this function.
511 if(pp->cache && (cache_skip < 2)) {
513 * There's a cache left since before. We then skipping the wait for
514 * socket action, unless this is the same cache like the previous round
515 * as then the cache was deemed not enough to act on and we then need to
516 * wait for more data anyway.
520 switch (Curl_socket_ready(sockfd, CURL_SOCKET_BAD, interval_ms)) {
521 case -1: /* select() error, stop reading */
522 failf(data, "FTP response aborted due to select/poll error: %d",
524 return CURLE_RECV_ERROR;
526 case 0: /* timeout */
527 if(Curl_pgrsUpdate(conn))
528 return CURLE_ABORTED_BY_CALLBACK;
529 continue; /* just continue in our loop for the timeout duration */
531 default: /* for clarity */
535 result = ftp_readresp(sockfd, pp, ftpcode, &nread);
539 if(!nread && pp->cache)
540 /* bump cache skip counter as on repeated skips we must wait for more
544 /* when we got data or there is no cache left, we reset the cache skip
550 } /* while there's buffer left and loop is requested */
552 pp->pending_resp = FALSE;
557 /* This is the ONLY way to change FTP state! */
558 static void state(struct connectdata *conn,
561 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
562 /* for debug purposes */
563 static const char * const names[]={
601 struct ftp_conn *ftpc = &conn->proto.ftpc;
602 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
603 if(ftpc->state != newstate)
604 infof(conn->data, "FTP %p state change from %s to %s\n",
605 ftpc, names[ftpc->state], names[newstate]);
607 ftpc->state = newstate;
610 static CURLcode ftp_state_user(struct connectdata *conn)
613 struct FTP *ftp = conn->data->state.proto.ftp;
615 PPSENDF(&conn->proto.ftpc.pp, "USER %s", ftp->user?ftp->user:"");
617 state(conn, FTP_USER);
618 conn->data->state.ftp_trying_alternative = FALSE;
623 static CURLcode ftp_state_pwd(struct connectdata *conn)
627 /* send PWD to discover our entry point */
628 PPSENDF(&conn->proto.ftpc.pp, "PWD", NULL);
629 state(conn, FTP_PWD);
634 /* For the FTP "protocol connect" and "doing" phases only */
635 static int ftp_getsock(struct connectdata *conn,
636 curl_socket_t *socks,
639 return Curl_pp_getsock(&conn->proto.ftpc.pp, socks, numsocks);
642 /* For the FTP "DO_MORE" phase only */
643 static int ftp_domore_getsock(struct connectdata *conn, curl_socket_t *socks,
646 struct ftp_conn *ftpc = &conn->proto.ftpc;
649 return GETSOCK_BLANK;
651 /* When in DO_MORE state, we could be either waiting for us to connect to a
652 remote site, or we could wait for that site to connect to us. Or just
653 handle ordinary commands.
655 When waiting for a connect, we will be in FTP_STOP state and then we wait
656 for the secondary socket to become writeable. If we're in another state,
657 we're still handling commands on the control (primary) connection.
661 switch(ftpc->state) {
665 return Curl_pp_getsock(&conn->proto.ftpc.pp, socks, numsocks);
668 socks[0] = conn->sock[SECONDARYSOCKET];
670 return GETSOCK_READSOCK(0);
673 /* This is called after the FTP_QUOTE state is passed.
675 ftp_state_cwd() sends the range of CWD commands to the server to change to
676 the correct directory. It may also need to send MKD commands to create
677 missing ones, if that option is enabled.
679 static CURLcode ftp_state_cwd(struct connectdata *conn)
681 CURLcode result = CURLE_OK;
682 struct ftp_conn *ftpc = &conn->proto.ftpc;
685 /* already done and fine */
686 result = ftp_state_post_cwd(conn);
688 ftpc->count2 = 0; /* count2 counts failed CWDs */
690 /* count3 is set to allow a MKD to fail once. In the case when first CWD
691 fails and then MKD fails (due to another session raced it to create the
692 dir) this then allows for a second try to CWD to it */
693 ftpc->count3 = (conn->data->set.ftp_create_missing_dirs==2)?1:0;
695 if(conn->bits.reuse && ftpc->entrypath) {
696 /* This is a re-used connection. Since we change directory to where the
697 transfer is taking place, we must first get back to the original dir
698 where we ended up after login: */
699 ftpc->count1 = 0; /* we count this as the first path, then we add one
700 for all upcoming ones in the ftp->dirs[] array */
701 PPSENDF(&conn->proto.ftpc.pp, "CWD %s", ftpc->entrypath);
702 state(conn, FTP_CWD);
707 /* issue the first CWD, the rest is sent when the CWD responses are
709 PPSENDF(&conn->proto.ftpc.pp, "CWD %s", ftpc->dirs[ftpc->count1 -1]);
710 state(conn, FTP_CWD);
713 /* No CWD necessary */
714 result = ftp_state_post_cwd(conn);
727 static CURLcode ftp_state_use_port(struct connectdata *conn,
728 ftpport fcmd) /* start with this */
731 CURLcode result = CURLE_OK;
732 struct ftp_conn *ftpc = &conn->proto.ftpc;
733 struct SessionHandle *data=conn->data;
734 curl_socket_t portsock= CURL_SOCKET_BAD;
735 char myhost[256] = "";
737 struct Curl_sockaddr_storage ss;
738 Curl_addrinfo *res, *ai;
739 curl_socklen_t sslen;
740 char hbuf[NI_MAXHOST];
741 struct sockaddr *sa=(struct sockaddr *)&ss;
742 struct sockaddr_in * const sa4 = (void *)sa;
744 struct sockaddr_in6 * const sa6 = (void *)sa;
747 static const char mode[][5] = { "EPRT", "PORT" };
751 char *string_ftpport = data->set.str[STRING_FTPPORT];
752 struct Curl_dns_entry *h=NULL;
753 unsigned short port_min = 0;
754 unsigned short port_max = 0;
759 /* Step 1, figure out what is requested,
761 * (ipv4|ipv6|domain|interface)?(:port(-range)?)?
764 if(data->set.str[STRING_FTPPORT] &&
765 (strlen(data->set.str[STRING_FTPPORT]) > 1)) {
768 size_t addrlen = INET6_ADDRSTRLEN > strlen(string_ftpport) ?
769 INET6_ADDRSTRLEN : strlen(string_ftpport);
771 size_t addrlen = INET_ADDRSTRLEN > strlen(string_ftpport) ?
772 INET_ADDRSTRLEN : strlen(string_ftpport);
774 char *ip_start = string_ftpport;
776 char *port_start = NULL;
777 char *port_sep = NULL;
779 addr = calloc(addrlen+1, 1);
781 return CURLE_OUT_OF_MEMORY;
784 if(*string_ftpport == '[') {
785 /* [ipv6]:port(-range) */
786 ip_start = string_ftpport + 1;
787 if((ip_end = strchr(string_ftpport, ']')) != NULL )
788 strncpy(addr, ip_start, ip_end - ip_start);
792 if(*string_ftpport == ':') {
794 ip_end = string_ftpport;
796 else if((ip_end = strchr(string_ftpport, ':')) != NULL) {
797 /* either ipv6 or (ipv4|domain|interface):port(-range) */
799 if(Curl_inet_pton(AF_INET6, string_ftpport, sa6) == 1) {
801 port_min = port_max = 0;
802 strcpy(addr, string_ftpport);
803 ip_end = NULL; /* this got no port ! */
807 /* (ipv4|domain|interface):port(-range) */
808 strncpy(addr, string_ftpport, ip_end - ip_start );
812 strcpy(addr, string_ftpport);
816 if((port_start = strchr(ip_end, ':')) != NULL) {
817 port_min = curlx_ultous(strtoul(port_start+1, NULL, 10));
818 if((port_sep = strchr(port_start, '-')) != NULL) {
819 port_max = curlx_ultous(strtoul(port_sep + 1, NULL, 10));
826 /* correct errors like:
828 * :-4711 , in this case port_min is (unsigned)-1,
829 * therefore port_min > port_max for all cases
830 * but port_max = (unsigned)-1
832 if(port_min > port_max )
833 port_min = port_max = 0;
837 /* attempt to get the address of the given interface name */
838 if(!Curl_if2ip(conn->ip_addr->ai_family, addr,
840 /* not an interface, use the given string as host name instead */
843 host = hbuf; /* use the hbuf for host name */
846 /* there was only a port(-range) given, default the host */
848 } /* data->set.ftpport */
851 /* not an interface and not a host name, get default by extracting
852 the IP from the control connection */
855 if(getsockname(conn->sock[FIRSTSOCKET], sa, &sslen)) {
856 failf(data, "getsockname() failed: %s",
857 Curl_strerror(conn, SOCKERRNO) );
860 return CURLE_FTP_PORT_FAILED;
862 switch(sa->sa_family) {
865 Curl_inet_ntop(sa->sa_family, &sa6->sin6_addr, hbuf, sizeof(hbuf));
869 Curl_inet_ntop(sa->sa_family, &sa4->sin_addr, hbuf, sizeof(hbuf));
872 host = hbuf; /* use this host name */
875 /* resolv ip/host to ip */
876 rc = Curl_resolv(conn, host, 0, &h);
877 if(rc == CURLRESOLV_PENDING)
878 (void)Curl_resolver_wait_resolv(conn, &h);
881 /* when we return from this function, we can forget about this entry
882 to we can unlock it now already */
883 Curl_resolv_unlock(data, h);
886 res = NULL; /* failure! */
892 failf(data, "failed to resolve the address provided to PORT: %s", host);
893 return CURLE_FTP_PORT_FAILED;
896 /* step 2, create a socket for the requested address */
898 portsock = CURL_SOCKET_BAD;
900 for(ai = res; ai; ai = ai->ai_next) {
902 * Workaround for AIX5 getaddrinfo() problem (it doesn't set ai_socktype):
904 if(ai->ai_socktype == 0)
905 ai->ai_socktype = conn->socktype;
907 portsock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
908 if(portsock == CURL_SOCKET_BAD) {
915 failf(data, "socket failure: %s", Curl_strerror(conn, error));
916 return CURLE_FTP_PORT_FAILED;
919 /* step 3, bind to a suitable local address */
921 memcpy(sa, ai->ai_addr, ai->ai_addrlen);
922 sslen = ai->ai_addrlen;
924 for(port = port_min; port <= port_max;) {
925 if(sa->sa_family == AF_INET)
926 sa4->sin_port = htons(port);
929 sa6->sin6_port = htons(port);
931 /* Try binding the given address. */
932 if(bind(portsock, sa, sslen) ) {
935 if(error == EADDRNOTAVAIL) {
937 /* The requested bind address is not local. Use the address used for
938 * the control connection instead and restart the port loop
940 failf(data, "bind(port=%hu) failed: %s", port,
941 Curl_strerror(conn, error) );
944 if(getsockname(conn->sock[FIRSTSOCKET], sa, &sslen)) {
945 failf(data, "getsockname() failed: %s",
946 Curl_strerror(conn, SOCKERRNO) );
947 Curl_closesocket(conn, portsock);
948 return CURLE_FTP_PORT_FAILED;
953 else if(error != EADDRINUSE && error != EACCES) {
954 failf(data, "bind(port=%hu) failed: %s", port,
955 Curl_strerror(conn, error) );
956 Curl_closesocket(conn, portsock);
957 return CURLE_FTP_PORT_FAILED;
966 /* maybe all ports were in use already*/
967 if(port > port_max) {
968 failf(data, "bind() failed, we ran out of ports!");
969 Curl_closesocket(conn, portsock);
970 return CURLE_FTP_PORT_FAILED;
973 /* get the name again after the bind() so that we can extract the
974 port number it uses now */
976 if(getsockname(portsock, (struct sockaddr *)sa, &sslen)) {
977 failf(data, "getsockname() failed: %s",
978 Curl_strerror(conn, SOCKERRNO) );
979 Curl_closesocket(conn, portsock);
980 return CURLE_FTP_PORT_FAILED;
983 /* step 4, listen on the socket */
985 if(listen(portsock, 1)) {
986 failf(data, "socket failure: %s", Curl_strerror(conn, SOCKERRNO));
987 Curl_closesocket(conn, portsock);
988 return CURLE_FTP_PORT_FAILED;
991 /* step 5, send the proper FTP command */
993 /* get a plain printable version of the numerical address to work with
995 Curl_printable_address(ai, myhost, sizeof(myhost));
998 if(!conn->bits.ftp_use_eprt && conn->bits.ipv6)
999 /* EPRT is disabled but we are connected to a IPv6 host, so we ignore the
1000 request and enable EPRT again! */
1001 conn->bits.ftp_use_eprt = TRUE;
1004 for(; fcmd != DONE; fcmd++) {
1006 if(!conn->bits.ftp_use_eprt && (EPRT == fcmd))
1007 /* if disabled, goto next */
1010 if((PORT == fcmd) && sa->sa_family != AF_INET)
1011 /* PORT is ipv4 only */
1014 switch (sa->sa_family) {
1016 port = ntohs(sa4->sin_port);
1020 port = ntohs(sa6->sin6_port);
1024 continue; /* might as well skip this */
1029 * Two fine examples from RFC2428;
1031 * EPRT |1|132.235.1.2|6275|
1033 * EPRT |2|1080::8:800:200C:417A|5282|
1036 result = Curl_pp_sendf(&ftpc->pp, "%s |%d|%s|%hu|", mode[fcmd],
1037 sa->sa_family == AF_INET?1:2,
1040 failf(data, "Failure sending EPRT command: %s",
1041 curl_easy_strerror(result));
1042 Curl_closesocket(conn, portsock);
1043 /* don't retry using PORT */
1044 ftpc->count1 = PORT;
1046 state(conn, FTP_STOP);
1051 else if(PORT == fcmd) {
1052 char *source = myhost;
1055 /* translate x.x.x.x to x,x,x,x */
1056 while(source && *source) {
1065 snprintf(dest, 20, ",%d,%d", (int)(port>>8), (int)(port&0xff));
1067 result = Curl_pp_sendf(&ftpc->pp, "%s %s", mode[fcmd], tmp);
1069 failf(data, "Failure sending PORT command: %s",
1070 curl_easy_strerror(result));
1071 Curl_closesocket(conn, portsock);
1073 state(conn, FTP_STOP);
1080 /* store which command was sent */
1081 ftpc->count1 = fcmd;
1083 /* we set the secondary socket variable to this for now, it is only so that
1084 the cleanup function will close it in case we fail before the true
1085 secondary stuff is made */
1086 if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET])
1087 Curl_closesocket(conn, conn->sock[SECONDARYSOCKET]);
1088 conn->sock[SECONDARYSOCKET] = portsock;
1090 /* this tcpconnect assignment below is a hackish work-around to make the
1091 multi interface with active FTP work - as it will not wait for a
1092 (passive) connect in Curl_is_connected().
1094 The *proper* fix is to make sure that the active connection from the
1095 server is done in a non-blocking way. Currently, it is still BLOCKING.
1097 conn->bits.tcpconnect[SECONDARYSOCKET] = TRUE;
1099 state(conn, FTP_PORT);
1103 static CURLcode ftp_state_use_pasv(struct connectdata *conn)
1105 struct ftp_conn *ftpc = &conn->proto.ftpc;
1106 CURLcode result = CURLE_OK;
1108 Here's the excecutive summary on what to do:
1110 PASV is RFC959, expect:
1111 227 Entering Passive Mode (a1,a2,a3,a4,p1,p2)
1113 LPSV is RFC1639, expect:
1114 228 Entering Long Passive Mode (4,4,a1,a2,a3,a4,2,p1,p2)
1116 EPSV is RFC2428, expect:
1117 229 Entering Extended Passive Mode (|||port|)
1121 static const char mode[][5] = { "EPSV", "PASV" };
1125 if(!conn->bits.ftp_use_epsv && conn->bits.ipv6)
1126 /* EPSV is disabled but we are connected to a IPv6 host, so we ignore the
1127 request and enable EPSV again! */
1128 conn->bits.ftp_use_epsv = TRUE;
1131 modeoff = conn->bits.ftp_use_epsv?0:1;
1133 PPSENDF(&ftpc->pp, "%s", mode[modeoff]);
1135 ftpc->count1 = modeoff;
1136 state(conn, FTP_PASV);
1137 infof(conn->data, "Connect data stream passively\n");
1142 /* REST is the last command in the chain of commands when a "head"-like
1143 request is made. Thus, if an actual transfer is to be made this is where
1144 we take off for real. */
1145 static CURLcode ftp_state_post_rest(struct connectdata *conn)
1147 CURLcode result = CURLE_OK;
1148 struct FTP *ftp = conn->data->state.proto.ftp;
1149 struct SessionHandle *data = conn->data;
1151 if(ftp->transfer != FTPTRANSFER_BODY) {
1152 /* doesn't transfer any data */
1154 /* still possibly do PRE QUOTE jobs */
1155 state(conn, FTP_RETR_PREQUOTE);
1156 result = ftp_state_quote(conn, TRUE, FTP_RETR_PREQUOTE);
1158 else if(data->set.ftp_use_port) {
1159 /* We have chosen to use the PORT (or similar) command */
1160 result = ftp_state_use_port(conn, EPRT);
1163 /* We have chosen (this is default) to use the PASV (or similar) command */
1164 if(data->set.ftp_use_pret) {
1165 /* The user has requested that we send a PRET command
1166 to prepare the server for the upcoming PASV */
1167 if(!conn->proto.ftpc.file) {
1168 PPSENDF(&conn->proto.ftpc.pp, "PRET %s",
1169 data->set.str[STRING_CUSTOMREQUEST]?
1170 data->set.str[STRING_CUSTOMREQUEST]:
1171 (data->set.ftp_list_only?"NLST":"LIST"));
1173 else if(data->set.upload) {
1174 PPSENDF(&conn->proto.ftpc.pp, "PRET STOR %s", conn->proto.ftpc.file);
1177 PPSENDF(&conn->proto.ftpc.pp, "PRET RETR %s", conn->proto.ftpc.file);
1179 state(conn, FTP_PRET);
1182 result = ftp_state_use_pasv(conn);
1188 static CURLcode ftp_state_post_size(struct connectdata *conn)
1190 CURLcode result = CURLE_OK;
1191 struct FTP *ftp = conn->data->state.proto.ftp;
1192 struct ftp_conn *ftpc = &conn->proto.ftpc;
1194 if((ftp->transfer != FTPTRANSFER_BODY) && ftpc->file) {
1195 /* if a "head"-like request is being made (on a file) */
1197 /* Determine if server can respond to REST command and therefore
1198 whether it supports range */
1199 PPSENDF(&conn->proto.ftpc.pp, "REST %d", 0);
1201 state(conn, FTP_REST);
1204 result = ftp_state_post_rest(conn);
1209 static CURLcode ftp_state_post_type(struct connectdata *conn)
1211 CURLcode result = CURLE_OK;
1212 struct FTP *ftp = conn->data->state.proto.ftp;
1213 struct ftp_conn *ftpc = &conn->proto.ftpc;
1215 if((ftp->transfer == FTPTRANSFER_INFO) && ftpc->file) {
1216 /* if a "head"-like request is being made (on a file) */
1218 /* we know ftpc->file is a valid pointer to a file name */
1219 PPSENDF(&ftpc->pp, "SIZE %s", ftpc->file);
1221 state(conn, FTP_SIZE);
1224 result = ftp_state_post_size(conn);
1229 static CURLcode ftp_state_post_listtype(struct connectdata *conn)
1231 CURLcode result = CURLE_OK;
1232 struct SessionHandle *data = conn->data;
1234 /* If this output is to be machine-parsed, the NLST command might be better
1235 to use, since the LIST command output is not specified or standard in any
1236 way. It has turned out that the NLST list output is not the same on all
1237 servers either... */
1240 if FTPFILE_NOCWD was specified, we are currently in
1241 the user's home directory, so we should add the path
1242 as argument for the LIST / NLST / or custom command.
1243 Whether the server will support this, is uncertain.
1245 The other ftp_filemethods will CWD into dir/dir/ first and
1246 then just do LIST (in that case: nothing to do here)
1248 char *cmd,*lstArg,*slashPos;
1251 if((data->set.ftp_filemethod == FTPFILE_NOCWD) &&
1253 data->state.path[0] &&
1254 strchr(data->state.path,'/')) {
1256 lstArg = strdup(data->state.path);
1258 return CURLE_OUT_OF_MEMORY;
1260 /* Check if path does not end with /, as then we cut off the file part */
1261 if(lstArg[strlen(lstArg) - 1] != '/') {
1263 /* chop off the file part if format is dir/dir/file */
1264 slashPos = strrchr(lstArg,'/');
1266 *(slashPos+1) = '\0';
1270 cmd = aprintf( "%s%s%s",
1271 data->set.str[STRING_CUSTOMREQUEST]?
1272 data->set.str[STRING_CUSTOMREQUEST]:
1273 (data->set.ftp_list_only?"NLST":"LIST"),
1275 lstArg? lstArg: "" );
1280 return CURLE_OUT_OF_MEMORY;
1283 result = Curl_pp_sendf(&conn->proto.ftpc.pp, "%s", cmd);
1290 if(result != CURLE_OK)
1293 state(conn, FTP_LIST);
1298 static CURLcode ftp_state_post_retrtype(struct connectdata *conn)
1300 CURLcode result = CURLE_OK;
1302 /* We've sent the TYPE, now we must send the list of prequote strings */
1304 result = ftp_state_quote(conn, TRUE, FTP_RETR_PREQUOTE);
1309 static CURLcode ftp_state_post_stortype(struct connectdata *conn)
1311 CURLcode result = CURLE_OK;
1313 /* We've sent the TYPE, now we must send the list of prequote strings */
1315 result = ftp_state_quote(conn, TRUE, FTP_STOR_PREQUOTE);
1320 static CURLcode ftp_state_post_mdtm(struct connectdata *conn)
1322 CURLcode result = CURLE_OK;
1323 struct FTP *ftp = conn->data->state.proto.ftp;
1324 struct SessionHandle *data = conn->data;
1325 struct ftp_conn *ftpc = &conn->proto.ftpc;
1327 /* If we have selected NOBODY and HEADER, it means that we only want file
1328 information. Which in FTP can't be much more than the file size and
1330 if(data->set.opt_no_body && ftpc->file &&
1331 ftp_need_type(conn, data->set.prefer_ascii)) {
1332 /* The SIZE command is _not_ RFC 959 specified, and therefor many servers
1333 may not support it! It is however the only way we have to get a file's
1336 ftp->transfer = FTPTRANSFER_INFO;
1337 /* this means no actual transfer will be made */
1339 /* Some servers return different sizes for different modes, and thus we
1340 must set the proper type before we check the size */
1341 result = ftp_nb_type(conn, data->set.prefer_ascii, FTP_TYPE);
1346 result = ftp_state_post_type(conn);
1351 /* This is called after the CWD commands have been done in the beginning of
1353 static CURLcode ftp_state_post_cwd(struct connectdata *conn)
1355 CURLcode result = CURLE_OK;
1356 struct SessionHandle *data = conn->data;
1357 struct ftp_conn *ftpc = &conn->proto.ftpc;
1359 /* Requested time of file or time-depended transfer? */
1360 if((data->set.get_filetime || data->set.timecondition) && ftpc->file) {
1362 /* we have requested to get the modified-time of the file, this is a white
1363 spot as the MDTM is not mentioned in RFC959 */
1364 PPSENDF(&ftpc->pp, "MDTM %s", ftpc->file);
1366 state(conn, FTP_MDTM);
1369 result = ftp_state_post_mdtm(conn);
1375 /* This is called after the TYPE and possible quote commands have been sent */
1376 static CURLcode ftp_state_ul_setup(struct connectdata *conn,
1379 CURLcode result = CURLE_OK;
1380 struct FTP *ftp = conn->data->state.proto.ftp;
1381 struct SessionHandle *data = conn->data;
1382 struct ftp_conn *ftpc = &conn->proto.ftpc;
1383 int seekerr = CURL_SEEKFUNC_OK;
1385 if((data->state.resume_from && !sizechecked) ||
1386 ((data->state.resume_from > 0) && sizechecked)) {
1387 /* we're about to continue the uploading of a file */
1388 /* 1. get already existing file's size. We use the SIZE command for this
1389 which may not exist in the server! The SIZE command is not in
1392 /* 2. This used to set REST. But since we can do append, we
1393 don't another ftp command. We just skip the source file
1394 offset and then we APPEND the rest on the file instead */
1396 /* 3. pass file-size number of bytes in the source file */
1397 /* 4. lower the infilesize counter */
1398 /* => transfer as usual */
1400 if(data->state.resume_from < 0 ) {
1401 /* Got no given size to start from, figure it out */
1402 PPSENDF(&ftpc->pp, "SIZE %s", ftpc->file);
1403 state(conn, FTP_STOR_SIZE);
1408 data->set.ftp_append = TRUE;
1410 /* Let's read off the proper amount of bytes from the input. */
1411 if(conn->seek_func) {
1412 seekerr = conn->seek_func(conn->seek_client, data->state.resume_from,
1416 if(seekerr != CURL_SEEKFUNC_OK) {
1417 if(seekerr != CURL_SEEKFUNC_CANTSEEK) {
1418 failf(data, "Could not seek stream");
1419 return CURLE_FTP_COULDNT_USE_REST;
1421 /* seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */
1423 curl_off_t passed=0;
1425 size_t readthisamountnow =
1426 (data->state.resume_from - passed > CURL_OFF_T_C(BUFSIZE)) ?
1427 BUFSIZE : curlx_sotouz(data->state.resume_from - passed);
1429 size_t actuallyread =
1430 conn->fread_func(data->state.buffer, 1, readthisamountnow,
1433 passed += actuallyread;
1434 if((actuallyread == 0) || (actuallyread > readthisamountnow)) {
1435 /* this checks for greater-than only to make sure that the
1436 CURL_READFUNC_ABORT return code still aborts */
1437 failf(data, "Failed to read data");
1438 return CURLE_FTP_COULDNT_USE_REST;
1440 } while(passed < data->state.resume_from);
1443 /* now, decrease the size of the read */
1444 if(data->set.infilesize>0) {
1445 data->set.infilesize -= data->state.resume_from;
1447 if(data->set.infilesize <= 0) {
1448 infof(data, "File already completely uploaded\n");
1450 /* no data to transfer */
1451 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1453 /* Set ->transfer so that we won't get any error in
1454 * ftp_done() because we didn't transfer anything! */
1455 ftp->transfer = FTPTRANSFER_NONE;
1457 state(conn, FTP_STOP);
1461 /* we've passed, proceed as normal */
1464 PPSENDF(&ftpc->pp, data->set.ftp_append?"APPE %s":"STOR %s",
1467 state(conn, FTP_STOR);
1472 static CURLcode ftp_state_quote(struct connectdata *conn,
1476 CURLcode result = CURLE_OK;
1477 struct SessionHandle *data = conn->data;
1478 struct FTP *ftp = data->state.proto.ftp;
1479 struct ftp_conn *ftpc = &conn->proto.ftpc;
1481 struct curl_slist *item;
1486 item = data->set.quote;
1488 case FTP_RETR_PREQUOTE:
1489 case FTP_STOR_PREQUOTE:
1490 item = data->set.prequote;
1493 item = data->set.postquote;
1499 * 'count1' to iterate over the commands to send
1500 * 'count2' to store wether to allow commands to fail
1511 /* Skip count1 items in the linked list */
1512 while((i< ftpc->count1) && item) {
1517 char *cmd = item->data;
1520 ftpc->count2 = 1; /* the sent command is allowed to fail */
1523 ftpc->count2 = 0; /* failure means cancel operation */
1525 PPSENDF(&ftpc->pp, "%s", cmd);
1526 state(conn, instate);
1532 /* No more quote to send, continue to ... */
1536 result = ftp_state_cwd(conn);
1538 case FTP_RETR_PREQUOTE:
1539 if(ftp->transfer != FTPTRANSFER_BODY)
1540 state(conn, FTP_STOP);
1542 if(ftpc->known_filesize != -1) {
1543 Curl_pgrsSetDownloadSize(data, ftpc->known_filesize);
1544 result = ftp_state_post_retr_size(conn, ftpc->known_filesize);
1547 PPSENDF(&ftpc->pp, "SIZE %s", ftpc->file);
1548 state(conn, FTP_RETR_SIZE);
1552 case FTP_STOR_PREQUOTE:
1553 result = ftp_state_ul_setup(conn, FALSE);
1563 static CURLcode ftp_state_pasv_resp(struct connectdata *conn,
1566 struct ftp_conn *ftpc = &conn->proto.ftpc;
1568 struct SessionHandle *data=conn->data;
1569 Curl_addrinfo *conninfo;
1570 struct Curl_dns_entry *addr=NULL;
1572 unsigned short connectport; /* the local port connect() should use! */
1573 unsigned short newport=0; /* remote port */
1576 /* newhost must be able to hold a full IP-style address in ASCII, which
1577 in the IPv6 case means 5*8-1 = 39 letters */
1578 #define NEWHOST_BUFSIZE 48
1579 char newhost[NEWHOST_BUFSIZE];
1580 char *str=&data->state.buffer[4]; /* start on the first letter */
1582 if((ftpc->count1 == 0) &&
1584 /* positive EPSV response */
1585 char *ptr = strchr(str, '(');
1590 if(5 == sscanf(ptr, "%c%c%c%u%c",
1596 const char sep1 = separator[0];
1599 /* The four separators should be identical, or else this is an oddly
1600 formatted reply and we bail out immediately. */
1601 for(i=1; i<4; i++) {
1602 if(separator[i] != sep1) {
1603 ptr=NULL; /* set to NULL to signal error */
1608 newport = (unsigned short)(num & 0xffff);
1610 if(conn->bits.tunnel_proxy ||
1611 conn->proxytype == CURLPROXY_SOCKS5 ||
1612 conn->proxytype == CURLPROXY_SOCKS5_HOSTNAME ||
1613 conn->proxytype == CURLPROXY_SOCKS4 ||
1614 conn->proxytype == CURLPROXY_SOCKS4A)
1615 /* proxy tunnel -> use other host info because ip_addr_str is the
1616 proxy address not the ftp host */
1617 snprintf(newhost, sizeof(newhost), "%s", conn->host.name);
1619 /* use the same IP we are already connected to */
1620 snprintf(newhost, NEWHOST_BUFSIZE, "%s", conn->ip_addr_str);
1627 failf(data, "Weirdly formatted EPSV reply");
1628 return CURLE_FTP_WEIRD_PASV_REPLY;
1631 else if((ftpc->count1 == 1) &&
1633 /* positive PASV response */
1638 * Scan for a sequence of six comma-separated numbers and use them as
1639 * IP+port indicators.
1641 * Found reply-strings include:
1642 * "227 Entering Passive Mode (127,0,0,1,4,51)"
1643 * "227 Data transfer will passively listen to 127,0,0,1,4,51"
1644 * "227 Entering passive mode. 127,0,0,1,4,51"
1647 if(6 == sscanf(str, "%d,%d,%d,%d,%d,%d",
1648 &ip[0], &ip[1], &ip[2], &ip[3],
1649 &port[0], &port[1]))
1655 failf(data, "Couldn't interpret the 227-response");
1656 return CURLE_FTP_WEIRD_227_FORMAT;
1659 /* we got OK from server */
1660 if(data->set.ftp_skip_ip) {
1661 /* told to ignore the remotely given IP but instead use the one we used
1662 for the control connection */
1663 infof(data, "Skips %d.%d.%d.%d for data connection, uses %s instead\n",
1664 ip[0], ip[1], ip[2], ip[3],
1666 if(conn->bits.tunnel_proxy ||
1667 conn->proxytype == CURLPROXY_SOCKS5 ||
1668 conn->proxytype == CURLPROXY_SOCKS5_HOSTNAME ||
1669 conn->proxytype == CURLPROXY_SOCKS4 ||
1670 conn->proxytype == CURLPROXY_SOCKS4A)
1671 /* proxy tunnel -> use other host info because ip_addr_str is the
1672 proxy address not the ftp host */
1673 snprintf(newhost, sizeof(newhost), "%s", conn->host.name);
1675 snprintf(newhost, sizeof(newhost), "%s", conn->ip_addr_str);
1678 snprintf(newhost, sizeof(newhost),
1679 "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
1680 newport = (unsigned short)(((port[0]<<8) + port[1]) & 0xffff);
1682 else if(ftpc->count1 == 0) {
1683 /* EPSV failed, move on to PASV */
1685 /* disable it for next transfer */
1686 conn->bits.ftp_use_epsv = FALSE;
1687 infof(data, "disabling EPSV usage\n");
1689 PPSENDF(&ftpc->pp, "PASV", NULL);
1691 /* remain in the FTP_PASV state */
1695 failf(data, "Bad PASV/EPSV response: %03d", ftpcode);
1696 return CURLE_FTP_WEIRD_PASV_REPLY;
1699 if(data->set.str[STRING_PROXY] && *data->set.str[STRING_PROXY]) {
1701 * This is a tunnel through a http proxy and we need to connect to the
1704 * We don't want to rely on a former host lookup that might've expired
1705 * now, instead we remake the lookup here and now!
1707 rc = Curl_resolv(conn, conn->proxy.name, (int)conn->port, &addr);
1708 if(rc == CURLRESOLV_PENDING)
1709 /* BLOCKING, ignores the return code but 'addr' will be NULL in
1711 (void)Curl_resolver_wait_resolv(conn, &addr);
1714 (unsigned short)conn->port; /* we connect to the proxy's port */
1717 failf(data, "Can't resolve proxy host %s:%hu",
1718 conn->proxy.name, connectport);
1719 return CURLE_FTP_CANT_GET_HOST;
1723 /* normal, direct, ftp connection */
1724 rc = Curl_resolv(conn, newhost, newport, &addr);
1725 if(rc == CURLRESOLV_PENDING)
1727 (void)Curl_resolver_wait_resolv(conn, &addr);
1729 connectport = newport; /* we connect to the remote port */
1732 failf(data, "Can't resolve new host %s:%hu", newhost, connectport);
1733 return CURLE_FTP_CANT_GET_HOST;
1737 result = Curl_connecthost(conn,
1739 &conn->sock[SECONDARYSOCKET],
1743 Curl_resolv_unlock(data, addr); /* we're done using this address */
1745 if(result && ftpc->count1 == 0 && ftpcode == 229) {
1746 infof(data, "got positive EPSV response, but can't connect. "
1747 "Disabling EPSV\n");
1748 /* disable it for next transfer */
1749 conn->bits.ftp_use_epsv = FALSE;
1750 data->state.errorbuf = FALSE; /* allow error message to get rewritten */
1751 PPSENDF(&ftpc->pp, "PASV", NULL);
1753 /* remain in the FTP_PASV state */
1760 conn->bits.tcpconnect[SECONDARYSOCKET] = connected;
1763 * When this is used from the multi interface, this might've returned with
1764 * the 'connected' set to FALSE and thus we are now awaiting a non-blocking
1765 * connect to connect and we should not be "hanging" here waiting.
1768 if(data->set.verbose)
1769 /* this just dumps information about this second connection */
1770 ftp_pasv_verbose(conn, conninfo, newhost, connectport);
1772 switch(conn->proxytype) {
1773 /* FIX: this MUST wait for a proper connect first if 'connected' is
1775 case CURLPROXY_SOCKS5:
1776 case CURLPROXY_SOCKS5_HOSTNAME:
1777 result = Curl_SOCKS5(conn->proxyuser, conn->proxypasswd, newhost, newport,
1778 SECONDARYSOCKET, conn);
1780 case CURLPROXY_SOCKS4:
1781 result = Curl_SOCKS4(conn->proxyuser, newhost, newport,
1782 SECONDARYSOCKET, conn, FALSE);
1784 case CURLPROXY_SOCKS4A:
1785 result = Curl_SOCKS4(conn->proxyuser, newhost, newport,
1786 SECONDARYSOCKET, conn, TRUE);
1788 case CURLPROXY_HTTP:
1789 case CURLPROXY_HTTP_1_0:
1790 /* do nothing here. handled later. */
1793 failf(data, "unknown proxytype option given");
1794 result = CURLE_COULDNT_CONNECT;
1801 if(conn->bits.tunnel_proxy && conn->bits.httpproxy) {
1802 /* FIX: this MUST wait for a proper connect first if 'connected' is
1806 /* We want "seamless" FTP operations through HTTP proxy tunnel */
1808 /* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the member
1809 * conn->proto.http; we want FTP through HTTP and we have to change the
1810 * member temporarily for connecting to the HTTP proxy. After
1811 * Curl_proxyCONNECT we have to set back the member to the original struct
1814 struct HTTP http_proxy;
1815 struct FTP *ftp_save = data->state.proto.ftp;
1816 memset(&http_proxy, 0, sizeof(http_proxy));
1817 data->state.proto.http = &http_proxy;
1819 result = Curl_proxyCONNECT(conn, SECONDARYSOCKET, newhost, newport);
1821 data->state.proto.ftp = ftp_save;
1827 conn->bits.tcpconnect[SECONDARYSOCKET] = TRUE;
1829 state(conn, FTP_STOP); /* this phase is completed */
1834 static CURLcode ftp_state_port_resp(struct connectdata *conn,
1837 struct SessionHandle *data = conn->data;
1838 struct ftp_conn *ftpc = &conn->proto.ftpc;
1839 ftpport fcmd = (ftpport)ftpc->count1;
1840 CURLcode result = CURLE_OK;
1842 if(ftpcode != 200) {
1843 /* the command failed */
1846 infof(data, "disabling EPRT usage\n");
1847 conn->bits.ftp_use_eprt = FALSE;
1852 failf(data, "Failed to do PORT");
1853 result = CURLE_FTP_PORT_FAILED;
1857 result = ftp_state_use_port(conn, fcmd);
1860 infof(data, "Connect data stream actively\n");
1861 state(conn, FTP_STOP); /* end of DO phase */
1867 static CURLcode ftp_state_mdtm_resp(struct connectdata *conn,
1870 CURLcode result = CURLE_OK;
1871 struct SessionHandle *data=conn->data;
1872 struct FTP *ftp = data->state.proto.ftp;
1873 struct ftp_conn *ftpc = &conn->proto.ftpc;
1878 /* we got a time. Format should be: "YYYYMMDDHHMMSS[.sss]" where the
1879 last .sss part is optional and means fractions of a second */
1880 int year, month, day, hour, minute, second;
1881 char *buf = data->state.buffer;
1882 if(6 == sscanf(buf+4, "%04d%02d%02d%02d%02d%02d",
1883 &year, &month, &day, &hour, &minute, &second)) {
1884 /* we have a time, reformat it */
1885 time_t secs=time(NULL);
1886 /* using the good old yacc/bison yuck */
1887 snprintf(buf, sizeof(conn->data->state.buffer),
1888 "%04d%02d%02d %02d:%02d:%02d GMT",
1889 year, month, day, hour, minute, second);
1890 /* now, convert this into a time() value: */
1891 data->info.filetime = (long)curl_getdate(buf, &secs);
1894 #ifdef CURL_FTP_HTTPSTYLE_HEAD
1895 /* If we asked for a time of the file and we actually got one as well,
1896 we "emulate" a HTTP-style header in our output. */
1898 if(data->set.opt_no_body &&
1900 data->set.get_filetime &&
1901 (data->info.filetime>=0) ) {
1902 time_t filetime = (time_t)data->info.filetime;
1904 const struct tm *tm = &buffer;
1906 result = Curl_gmtime(filetime, &buffer);
1910 /* format: "Tue, 15 Nov 1994 12:45:26" */
1911 snprintf(buf, BUFSIZE-1,
1912 "Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n",
1913 Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
1915 Curl_month[tm->tm_mon],
1920 result = Curl_client_write(conn, CLIENTWRITE_BOTH, buf, 0);
1923 } /* end of a ridiculous amount of conditionals */
1928 infof(data, "unsupported MDTM reply format\n");
1930 case 550: /* "No such file or directory" */
1931 failf(data, "Given file does not exist");
1932 result = CURLE_FTP_COULDNT_RETR_FILE;
1936 if(data->set.timecondition) {
1937 if((data->info.filetime > 0) && (data->set.timevalue > 0)) {
1938 switch(data->set.timecondition) {
1939 case CURL_TIMECOND_IFMODSINCE:
1941 if(data->info.filetime <= data->set.timevalue) {
1942 infof(data, "The requested document is not new enough\n");
1943 ftp->transfer = FTPTRANSFER_NONE; /* mark to not transfer data */
1944 data->info.timecond = TRUE;
1945 state(conn, FTP_STOP);
1949 case CURL_TIMECOND_IFUNMODSINCE:
1950 if(data->info.filetime > data->set.timevalue) {
1951 infof(data, "The requested document is not old enough\n");
1952 ftp->transfer = FTPTRANSFER_NONE; /* mark to not transfer data */
1953 data->info.timecond = TRUE;
1954 state(conn, FTP_STOP);
1961 infof(data, "Skipping time comparison\n");
1966 result = ftp_state_post_mdtm(conn);
1971 static CURLcode ftp_state_type_resp(struct connectdata *conn,
1975 CURLcode result = CURLE_OK;
1976 struct SessionHandle *data=conn->data;
1978 if(ftpcode/100 != 2) {
1979 /* "sasserftpd" and "(u)r(x)bot ftpd" both responds with 226 after a
1980 successful 'TYPE I'. While that is not as RFC959 says, it is still a
1981 positive response code and we allow that. */
1982 failf(data, "Couldn't set desired mode");
1983 return CURLE_FTP_COULDNT_SET_TYPE;
1986 infof(data, "Got a %03d response code instead of the assumed 200\n",
1989 if(instate == FTP_TYPE)
1990 result = ftp_state_post_type(conn);
1991 else if(instate == FTP_LIST_TYPE)
1992 result = ftp_state_post_listtype(conn);
1993 else if(instate == FTP_RETR_TYPE)
1994 result = ftp_state_post_retrtype(conn);
1995 else if(instate == FTP_STOR_TYPE)
1996 result = ftp_state_post_stortype(conn);
2001 static CURLcode ftp_state_post_retr_size(struct connectdata *conn,
2002 curl_off_t filesize)
2004 CURLcode result = CURLE_OK;
2005 struct SessionHandle *data=conn->data;
2006 struct FTP *ftp = data->state.proto.ftp;
2007 struct ftp_conn *ftpc = &conn->proto.ftpc;
2009 if(data->set.max_filesize && (filesize > data->set.max_filesize)) {
2010 failf(data, "Maximum file size exceeded");
2011 return CURLE_FILESIZE_EXCEEDED;
2013 ftp->downloadsize = filesize;
2015 if(data->state.resume_from) {
2016 /* We always (attempt to) get the size of downloads, so it is done before
2017 this even when not doing resumes. */
2018 if(filesize == -1) {
2019 infof(data, "ftp server doesn't support SIZE\n");
2020 /* We couldn't get the size and therefore we can't know if there really
2021 is a part of the file left to get, although the server will just
2022 close the connection when we start the connection so it won't cause
2023 us any harm, just not make us exit as nicely. */
2026 /* We got a file size report, so we check that there actually is a
2027 part of the file left to get, or else we go home. */
2028 if(data->state.resume_from< 0) {
2029 /* We're supposed to download the last abs(from) bytes */
2030 if(filesize < -data->state.resume_from) {
2031 failf(data, "Offset (%" FORMAT_OFF_T
2032 ") was beyond file size (%" FORMAT_OFF_T ")",
2033 data->state.resume_from, filesize);
2034 return CURLE_BAD_DOWNLOAD_RESUME;
2036 /* convert to size to download */
2037 ftp->downloadsize = -data->state.resume_from;
2038 /* download from where? */
2039 data->state.resume_from = filesize - ftp->downloadsize;
2042 if(filesize < data->state.resume_from) {
2043 failf(data, "Offset (%" FORMAT_OFF_T
2044 ") was beyond file size (%" FORMAT_OFF_T ")",
2045 data->state.resume_from, filesize);
2046 return CURLE_BAD_DOWNLOAD_RESUME;
2048 /* Now store the number of bytes we are expected to download */
2049 ftp->downloadsize = filesize-data->state.resume_from;
2053 if(ftp->downloadsize == 0) {
2054 /* no data to transfer */
2055 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
2056 infof(data, "File already completely downloaded\n");
2058 /* Set ->transfer so that we won't get any error in ftp_done()
2059 * because we didn't transfer the any file */
2060 ftp->transfer = FTPTRANSFER_NONE;
2061 state(conn, FTP_STOP);
2065 /* Set resume file transfer offset */
2066 infof(data, "Instructs server to resume from offset %" FORMAT_OFF_T
2067 "\n", data->state.resume_from);
2069 PPSENDF(&ftpc->pp, "REST %" FORMAT_OFF_T, data->state.resume_from);
2071 state(conn, FTP_RETR_REST);
2076 PPSENDF(&ftpc->pp, "RETR %s", ftpc->file);
2077 state(conn, FTP_RETR);
2083 static CURLcode ftp_state_size_resp(struct connectdata *conn,
2087 CURLcode result = CURLE_OK;
2088 struct SessionHandle *data=conn->data;
2089 curl_off_t filesize;
2090 char *buf = data->state.buffer;
2092 /* get the size from the ascii string: */
2093 filesize = (ftpcode == 213)?curlx_strtoofft(buf+4, NULL, 0):-1;
2095 if(instate == FTP_SIZE) {
2096 #ifdef CURL_FTP_HTTPSTYLE_HEAD
2097 if(-1 != filesize) {
2098 snprintf(buf, sizeof(data->state.buffer),
2099 "Content-Length: %" FORMAT_OFF_T "\r\n", filesize);
2100 result = Curl_client_write(conn, CLIENTWRITE_BOTH, buf, 0);
2105 Curl_pgrsSetDownloadSize(data, filesize);
2106 result = ftp_state_post_size(conn);
2108 else if(instate == FTP_RETR_SIZE) {
2109 Curl_pgrsSetDownloadSize(data, filesize);
2110 result = ftp_state_post_retr_size(conn, filesize);
2112 else if(instate == FTP_STOR_SIZE) {
2113 data->state.resume_from = filesize;
2114 result = ftp_state_ul_setup(conn, TRUE);
2120 static CURLcode ftp_state_rest_resp(struct connectdata *conn,
2124 CURLcode result = CURLE_OK;
2125 struct ftp_conn *ftpc = &conn->proto.ftpc;
2130 #ifdef CURL_FTP_HTTPSTYLE_HEAD
2131 if(ftpcode == 350) {
2132 char buffer[24]= { "Accept-ranges: bytes\r\n" };
2133 result = Curl_client_write(conn, CLIENTWRITE_BOTH, buffer, 0);
2138 result = ftp_state_post_rest(conn);
2142 if(ftpcode != 350) {
2143 failf(conn->data, "Couldn't use REST");
2144 result = CURLE_FTP_COULDNT_USE_REST;
2147 PPSENDF(&ftpc->pp, "RETR %s", ftpc->file);
2148 state(conn, FTP_RETR);
2156 static CURLcode ftp_state_stor_resp(struct connectdata *conn,
2159 CURLcode result = CURLE_OK;
2160 struct SessionHandle *data = conn->data;
2161 struct FTP *ftp = data->state.proto.ftp;
2164 failf(data, "Failed FTP upload: %0d", ftpcode);
2165 /* oops, we never close the sockets! */
2166 return CURLE_UPLOAD_FAILED;
2169 if(data->set.ftp_use_port) {
2171 /* PORT means we are now awaiting the server to connect to us. */
2172 result = AllowServerConnect(conn);
2177 if(conn->ssl[SECONDARYSOCKET].use) {
2178 /* since we only have a plaintext TCP connection here, we must now
2180 infof(data, "Doing the SSL/TLS handshake on the data stream\n");
2182 result = Curl_ssl_connect(conn, SECONDARYSOCKET);
2187 *(ftp->bytecountp)=0;
2189 /* When we know we're uploading a specified file, we can get the file
2190 size prior to the actual upload. */
2192 Curl_pgrsSetUploadSize(data, data->set.infilesize);
2194 /* set the SO_SNDBUF for the secondary socket for those who need it */
2195 Curl_sndbufset(conn->sock[SECONDARYSOCKET]);
2197 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, /* no download */
2198 SECONDARYSOCKET, ftp->bytecountp);
2199 state(conn, FTP_STOP);
2201 conn->proto.ftpc.pp.pending_resp = TRUE; /* expect a server response */
2206 /* for LIST and RETR responses */
2207 static CURLcode ftp_state_get_resp(struct connectdata *conn,
2211 CURLcode result = CURLE_OK;
2212 struct SessionHandle *data = conn->data;
2213 struct FTP *ftp = data->state.proto.ftp;
2214 char *buf = data->state.buffer;
2216 if((ftpcode == 150) || (ftpcode == 125)) {
2220 150 Opening BINARY mode data connection for /etc/passwd (2241
2221 bytes). (ok, the file is being transferred)
2224 150 Opening ASCII mode data connection for /bin/ls
2227 150 ASCII data connection for /bin/ls (137.167.104.91,37445) (0 bytes).
2230 150 Opening ASCII mode data connection for [file] (0.0.0.0,0) (545 bytes)
2233 125 Data connection already open; Transfer starting. */
2235 curl_off_t size=-1; /* default unknown size */
2239 * It appears that there are FTP-servers that return size 0 for files when
2240 * SIZE is used on the file while being in BINARY mode. To work around
2241 * that (stupid) behavior, we attempt to parse the RETR response even if
2242 * the SIZE returned size zero.
2244 * Debugging help from Salvatore Sorrentino on February 26, 2003.
2247 if((instate != FTP_LIST) &&
2248 !data->set.prefer_ascii &&
2249 (ftp->downloadsize < 1)) {
2251 * It seems directory listings either don't show the size or very
2252 * often uses size 0 anyway. ASCII transfers may very well turn out
2253 * that the transferred amount of data is not the same as this line
2254 * tells, why using this number in those cases only confuses us.
2256 * Example D above makes this parsing a little tricky */
2258 bytes=strstr(buf, " bytes");
2260 long in=(long)(bytes-buf);
2261 /* this is a hint there is size information in there! ;-) */
2263 /* scan for the left parenthesis and break there */
2266 /* skip only digits */
2267 if(!ISDIGIT(*bytes)) {
2271 /* one more estep backwards */
2274 /* if we have nothing but digits: */
2276 /* get the number! */
2277 size = curlx_strtoofft(bytes, NULL, 0);
2281 else if(ftp->downloadsize > -1)
2282 size = ftp->downloadsize;
2284 if(data->set.ftp_use_port) {
2286 result = AllowServerConnect(conn);
2291 if(conn->ssl[SECONDARYSOCKET].use) {
2292 /* since we only have a plaintext TCP connection here, we must now
2294 infof(data, "Doing the SSL/TLS handshake on the data stream\n");
2295 result = Curl_ssl_connect(conn, SECONDARYSOCKET);
2300 if(size > data->req.maxdownload && data->req.maxdownload > 0)
2301 size = data->req.size = data->req.maxdownload;
2302 else if((instate != FTP_LIST) && (data->set.prefer_ascii))
2303 size = -1; /* kludge for servers that understate ASCII mode file size */
2305 infof(data, "Maxdownload = %" FORMAT_OFF_T "\n", data->req.maxdownload);
2307 if(instate != FTP_LIST)
2308 infof(data, "Getting file with size: %" FORMAT_OFF_T "\n", size);
2311 Curl_setup_transfer(conn, SECONDARYSOCKET, size, FALSE,
2312 ftp->bytecountp, -1, NULL); /* no upload here */
2314 conn->proto.ftpc.pp.pending_resp = TRUE; /* expect server response */
2315 state(conn, FTP_STOP);
2318 if((instate == FTP_LIST) && (ftpcode == 450)) {
2319 /* simply no matching files in the dir listing */
2320 ftp->transfer = FTPTRANSFER_NONE; /* don't download anything */
2321 state(conn, FTP_STOP); /* this phase is over */
2324 failf(data, "RETR response: %03d", ftpcode);
2325 return instate == FTP_RETR && ftpcode == 550?
2326 CURLE_REMOTE_FILE_NOT_FOUND:
2327 CURLE_FTP_COULDNT_RETR_FILE;
2334 /* after USER, PASS and ACCT */
2335 static CURLcode ftp_state_loggedin(struct connectdata *conn)
2337 CURLcode result = CURLE_OK;
2340 if(conn->data->set.krb) {
2341 /* We may need to issue a KAUTH here to have access to the files
2342 * do it if user supplied a password
2344 if(conn->passwd && *conn->passwd) {
2346 result = Curl_krb_kauth(conn);
2352 if(conn->ssl[FIRSTSOCKET].use) {
2353 /* PBSZ = PROTECTION BUFFER SIZE.
2355 The 'draft-murray-auth-ftp-ssl' (draft 12, page 7) says:
2357 Specifically, the PROT command MUST be preceded by a PBSZ
2358 command and a PBSZ command MUST be preceded by a successful
2359 security data exchange (the TLS negotiation in this case)
2361 ... (and on page 8):
2363 Thus the PBSZ command must still be issued, but must have a
2364 parameter of '0' to indicate that no buffering is taking place
2365 and the data connection should not be encapsulated.
2367 PPSENDF(&conn->proto.ftpc.pp, "PBSZ %d", 0);
2368 state(conn, FTP_PBSZ);
2371 result = ftp_state_pwd(conn);
2376 /* for USER and PASS responses */
2377 static CURLcode ftp_state_user_resp(struct connectdata *conn,
2381 CURLcode result = CURLE_OK;
2382 struct SessionHandle *data = conn->data;
2383 struct FTP *ftp = data->state.proto.ftp;
2384 struct ftp_conn *ftpc = &conn->proto.ftpc;
2385 (void)instate; /* no use for this yet */
2387 /* some need password anyway, and others just return 2xx ignored */
2388 if((ftpcode == 331) && (ftpc->state == FTP_USER)) {
2389 /* 331 Password required for ...
2390 (the server requires to send the user's password too) */
2391 PPSENDF(&ftpc->pp, "PASS %s", ftp->passwd?ftp->passwd:"");
2392 state(conn, FTP_PASS);
2394 else if(ftpcode/100 == 2) {
2395 /* 230 User ... logged in.
2396 (the user logged in with or without password) */
2397 result = ftp_state_loggedin(conn);
2399 else if(ftpcode == 332) {
2400 if(data->set.str[STRING_FTP_ACCOUNT]) {
2401 PPSENDF(&ftpc->pp, "ACCT %s", data->set.str[STRING_FTP_ACCOUNT]);
2402 state(conn, FTP_ACCT);
2405 failf(data, "ACCT requested but none available");
2406 result = CURLE_LOGIN_DENIED;
2410 /* All other response codes, like:
2412 530 User ... access denied
2413 (the server denies to log the specified user) */
2415 if(conn->data->set.str[STRING_FTP_ALTERNATIVE_TO_USER] &&
2416 !conn->data->state.ftp_trying_alternative) {
2417 /* Ok, USER failed. Let's try the supplied command. */
2418 PPSENDF(&conn->proto.ftpc.pp, "%s",
2419 conn->data->set.str[STRING_FTP_ALTERNATIVE_TO_USER]);
2420 conn->data->state.ftp_trying_alternative = TRUE;
2421 state(conn, FTP_USER);
2425 failf(data, "Access denied: %03d", ftpcode);
2426 result = CURLE_LOGIN_DENIED;
2432 /* for ACCT response */
2433 static CURLcode ftp_state_acct_resp(struct connectdata *conn,
2436 CURLcode result = CURLE_OK;
2437 struct SessionHandle *data = conn->data;
2438 if(ftpcode != 230) {
2439 failf(data, "ACCT rejected by server: %03d", ftpcode);
2440 result = CURLE_FTP_WEIRD_PASS_REPLY; /* FIX */
2443 result = ftp_state_loggedin(conn);
2449 static CURLcode ftp_statemach_act(struct connectdata *conn)
2452 curl_socket_t sock = conn->sock[FIRSTSOCKET];
2453 struct SessionHandle *data=conn->data;
2455 struct ftp_conn *ftpc = &conn->proto.ftpc;
2456 struct pingpong *pp = &ftpc->pp;
2457 static const char ftpauth[][4] = { "SSL", "TLS" };
2461 return Curl_pp_flushsend(pp);
2463 /* we read a piece of response */
2464 result = ftp_readresp(sock, pp, &ftpcode, &nread);
2469 /* we have now received a full FTP server response */
2470 switch(ftpc->state) {
2472 if(ftpcode != 220) {
2473 failf(data, "Got a %03d ftp-server response when 220 was expected",
2475 return CURLE_FTP_WEIRD_SERVER_REPLY;
2478 /* We have received a 220 response fine, now we proceed. */
2479 #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
2481 /* If not anonymous login, try a secure login. Note that this
2482 procedure is still BLOCKING. */
2484 Curl_sec_request_prot(conn, "private");
2485 /* We set private first as default, in case the line below fails to
2486 set a valid level */
2487 Curl_sec_request_prot(conn, data->set.str[STRING_KRB_LEVEL]);
2489 if(Curl_sec_login(conn) != CURLE_OK)
2490 infof(data, "Logging in with password in cleartext!\n");
2492 infof(data, "Authentication successful\n");
2496 if(data->set.ftp_ssl && !conn->ssl[FIRSTSOCKET].use) {
2497 /* We don't have a SSL/TLS connection yet, but FTPS is
2498 requested. Try a FTPS connection now */
2501 switch(data->set.ftpsslauth) {
2502 case CURLFTPAUTH_DEFAULT:
2503 case CURLFTPAUTH_SSL:
2504 ftpc->count2 = 1; /* add one to get next */
2507 case CURLFTPAUTH_TLS:
2508 ftpc->count2 = -1; /* subtract one to get next */
2512 failf(data, "unsupported parameter to CURLOPT_FTPSSLAUTH: %d",
2513 (int)data->set.ftpsslauth);
2514 return CURLE_UNKNOWN_OPTION; /* we don't know what to do */
2516 PPSENDF(&ftpc->pp, "AUTH %s", ftpauth[ftpc->count1]);
2517 state(conn, FTP_AUTH);
2520 result = ftp_state_user(conn);
2528 /* we have gotten the response to a previous AUTH command */
2530 /* RFC2228 (page 5) says:
2532 * If the server is willing to accept the named security mechanism,
2533 * and does not require any security data, it must respond with
2534 * reply code 234/334.
2537 if((ftpcode == 234) || (ftpcode == 334)) {
2538 /* Curl_ssl_connect is BLOCKING */
2539 result = Curl_ssl_connect(conn, FIRSTSOCKET);
2540 if(CURLE_OK == result) {
2541 conn->ssl[SECONDARYSOCKET].use = FALSE; /* clear-text data */
2542 result = ftp_state_user(conn);
2545 else if(ftpc->count3 < 1) {
2547 ftpc->count1 += ftpc->count2; /* get next attempt */
2548 result = Curl_pp_sendf(&ftpc->pp, "AUTH %s", ftpauth[ftpc->count1]);
2549 /* remain in this same state */
2552 if(data->set.ftp_ssl > CURLUSESSL_TRY)
2553 /* we failed and CURLUSESSL_CONTROL or CURLUSESSL_ALL is set */
2554 result = CURLE_USE_SSL_FAILED;
2556 /* ignore the failure and continue */
2557 result = ftp_state_user(conn);
2566 result = ftp_state_user_resp(conn, ftpcode, ftpc->state);
2570 result = ftp_state_acct_resp(conn, ftpcode);
2574 PPSENDF(&ftpc->pp, "PROT %c",
2575 data->set.ftp_ssl == CURLUSESSL_CONTROL ? 'C' : 'P');
2576 state(conn, FTP_PROT);
2581 if(ftpcode/100 == 2)
2582 /* We have enabled SSL for the data connection! */
2583 conn->ssl[SECONDARYSOCKET].use =
2584 (data->set.ftp_ssl != CURLUSESSL_CONTROL) ? TRUE : FALSE;
2585 /* FTP servers typically responds with 500 if they decide to reject
2587 else if(data->set.ftp_ssl > CURLUSESSL_CONTROL)
2588 /* we failed and bails out */
2589 return CURLE_USE_SSL_FAILED;
2591 if(data->set.ftp_ccc) {
2592 /* CCC - Clear Command Channel
2594 PPSENDF(&ftpc->pp, "CCC", NULL);
2595 state(conn, FTP_CCC);
2598 result = ftp_state_pwd(conn);
2606 /* First shut down the SSL layer (note: this call will block) */
2607 result = Curl_ssl_shutdown(conn, FIRSTSOCKET);
2610 failf(conn->data, "Failed to clear the command channel (CCC)");
2615 /* Then continue as normal */
2616 result = ftp_state_pwd(conn);
2622 if(ftpcode == 257) {
2623 char *ptr=&data->state.buffer[4]; /* start on the first letter */
2627 dir = malloc(nread + 1);
2629 return CURLE_OUT_OF_MEMORY;
2631 /* Reply format is like
2632 257<space>"<directory-name>"<space><commentary> and the RFC959
2635 The directory name can contain any character; embedded
2636 double-quotes should be escaped by double-quotes (the
2637 "quote-doubling" convention).
2640 /* it started good */
2642 for(store = dir; *ptr;) {
2644 if('\"' == ptr[1]) {
2645 /* "quote-doubling" */
2651 *store = '\0'; /* zero terminate */
2652 break; /* get out of this loop */
2661 /* If the path name does not look like an absolute path (i.e.: it
2662 does not start with a '/'), we probably need some server-dependent
2663 adjustments. For example, this is the case when connecting to
2664 an OS400 FTP server: this server supports two name syntaxes,
2665 the default one being incompatible with standard pathes. In
2666 addition, this server switches automatically to the regular path
2667 syntax when one is encountered in a command: this results in
2668 having an entrypath in the wrong syntax when later used in CWD.
2669 The method used here is to check the server OS: we do it only
2670 if the path name looks strange to minimize overhead on other
2673 if(!ftpc->server_os && dir[0] != '/') {
2675 result = Curl_pp_sendf(&ftpc->pp, "SYST", NULL);
2676 if(result != CURLE_OK) {
2680 Curl_safefree(ftpc->entrypath);
2681 ftpc->entrypath = dir; /* remember this */
2682 infof(data, "Entry path is '%s'\n", ftpc->entrypath);
2683 /* also save it where getinfo can access it: */
2684 data->state.most_recent_ftp_entrypath = ftpc->entrypath;
2685 state(conn, FTP_SYST);
2689 Curl_safefree(ftpc->entrypath);
2690 ftpc->entrypath = dir; /* remember this */
2691 infof(data, "Entry path is '%s'\n", ftpc->entrypath);
2692 /* also save it where getinfo can access it: */
2693 data->state.most_recent_ftp_entrypath = ftpc->entrypath;
2696 /* couldn't get the path */
2698 infof(data, "Failed to figure out path\n");
2701 state(conn, FTP_STOP); /* we are done with the CONNECT phase! */
2702 DEBUGF(infof(data, "protocol connect phase DONE\n"));
2706 if(ftpcode == 215) {
2707 char *ptr=&data->state.buffer[4]; /* start on the first letter */
2711 os = malloc(nread + 1);
2713 return CURLE_OUT_OF_MEMORY;
2715 /* Reply format is like
2716 215<space><OS-name><space><commentary>
2720 for(store = os; *ptr && *ptr != ' ';)
2722 *store = '\0'; /* zero terminate */
2724 /* Check for special servers here. */
2726 if(strequal(os, "OS/400")) {
2727 /* Force OS400 name format 1. */
2728 result = Curl_pp_sendf(&ftpc->pp, "SITE NAMEFMT 1", NULL);
2729 if(result != CURLE_OK) {
2733 /* remember target server OS */
2734 Curl_safefree(ftpc->server_os);
2735 ftpc->server_os = os;
2736 state(conn, FTP_NAMEFMT);
2740 /* Nothing special for the target server. */
2741 /* remember target server OS */
2742 Curl_safefree(ftpc->server_os);
2743 ftpc->server_os = os;
2747 /* Cannot identify server OS. Continue anyway and cross fingers. */
2750 state(conn, FTP_STOP); /* we are done with the CONNECT phase! */
2751 DEBUGF(infof(data, "protocol connect phase DONE\n"));
2755 if(ftpcode == 250) {
2756 /* Name format change successful: reload initial path. */
2757 ftp_state_pwd(conn);
2761 state(conn, FTP_STOP); /* we are done with the CONNECT phase! */
2762 DEBUGF(infof(data, "protocol connect phase DONE\n"));
2767 case FTP_RETR_PREQUOTE:
2768 case FTP_STOR_PREQUOTE:
2769 if((ftpcode >= 400) && !ftpc->count2) {
2770 /* failure response code, and not allowed to fail */
2771 failf(conn->data, "QUOT command failed with %03d", ftpcode);
2772 return CURLE_QUOTE_ERROR;
2774 result = ftp_state_quote(conn, FALSE, ftpc->state);
2781 if(ftpcode/100 != 2) {
2782 /* failure to CWD there */
2783 if(conn->data->set.ftp_create_missing_dirs &&
2784 ftpc->count1 && !ftpc->count2) {
2786 ftpc->count2++; /* counter to prevent CWD-MKD loops */
2787 PPSENDF(&ftpc->pp, "MKD %s", ftpc->dirs[ftpc->count1 - 1]);
2788 state(conn, FTP_MKD);
2791 /* return failure */
2792 failf(data, "Server denied you to change to the given directory");
2793 ftpc->cwdfail = TRUE; /* don't remember this path as we failed
2795 return CURLE_REMOTE_ACCESS_DENIED;
2801 if(++ftpc->count1 <= ftpc->dirdepth) {
2803 PPSENDF(&ftpc->pp, "CWD %s", ftpc->dirs[ftpc->count1 - 1]);
2806 result = ftp_state_post_cwd(conn);
2814 if((ftpcode/100 != 2) && !ftpc->count3--) {
2815 /* failure to MKD the dir */
2816 failf(data, "Failed to MKD dir: %03d", ftpcode);
2817 return CURLE_REMOTE_ACCESS_DENIED;
2819 state(conn, FTP_CWD);
2821 PPSENDF(&ftpc->pp, "CWD %s", ftpc->dirs[ftpc->count1 - 1]);
2825 result = ftp_state_mdtm_resp(conn, ftpcode);
2832 result = ftp_state_type_resp(conn, ftpcode, ftpc->state);
2838 result = ftp_state_size_resp(conn, ftpcode, ftpc->state);
2843 result = ftp_state_rest_resp(conn, ftpcode, ftpc->state);
2847 if(ftpcode != 200) {
2848 /* there only is this one standard OK return code. */
2849 failf(data, "PRET command not accepted: %03d", ftpcode);
2850 return CURLE_FTP_PRET_FAILED;
2852 result = ftp_state_use_pasv(conn);
2856 result = ftp_state_pasv_resp(conn, ftpcode);
2860 result = ftp_state_port_resp(conn, ftpcode);
2865 result = ftp_state_get_resp(conn, ftpcode, ftpc->state);
2869 result = ftp_state_stor_resp(conn, ftpcode);
2873 /* fallthrough, just stop! */
2875 /* internal error */
2876 state(conn, FTP_STOP);
2885 /* called repeatedly until done from multi.c */
2886 static CURLcode ftp_multi_statemach(struct connectdata *conn,
2889 struct ftp_conn *ftpc = &conn->proto.ftpc;
2890 CURLcode result = Curl_pp_multi_statemach(&ftpc->pp);
2892 /* Check for the state outside of the Curl_socket_ready() return code checks
2893 since at times we are in fact already in this state when this function
2895 *done = (ftpc->state == FTP_STOP) ? TRUE : FALSE;
2900 static CURLcode ftp_easy_statemach(struct connectdata *conn)
2902 struct ftp_conn *ftpc = &conn->proto.ftpc;
2903 struct pingpong *pp = &ftpc->pp;
2904 CURLcode result = CURLE_OK;
2906 while(ftpc->state != FTP_STOP) {
2907 result = Curl_pp_easy_statemach(pp);
2916 * Allocate and initialize the struct FTP for the current SessionHandle. If
2920 #if defined(__INTEL_COMPILER) && (__INTEL_COMPILER == 910) && \
2921 defined(__OPTIMIZE__) && defined(__unix__) && defined(__i386__)
2922 /* workaround icc 9.1 optimizer issue */
2923 #pragma optimize("", off)
2926 static CURLcode ftp_init(struct connectdata *conn)
2930 if(NULL == conn->data->state.proto.ftp) {
2931 conn->data->state.proto.ftp = malloc(sizeof(struct FTP));
2932 if(NULL == conn->data->state.proto.ftp)
2933 return CURLE_OUT_OF_MEMORY;
2936 ftp = conn->data->state.proto.ftp;
2938 /* get some initial data into the ftp struct */
2939 ftp->bytecountp = &conn->data->req.bytecount;
2940 ftp->transfer = FTPTRANSFER_BODY;
2941 ftp->downloadsize = 0;
2943 /* No need to duplicate user+password, the connectdata struct won't change
2944 during a session, but we re-init them here since on subsequent inits
2945 since the conn struct may have changed or been replaced.
2947 ftp->user = conn->user;
2948 ftp->passwd = conn->passwd;
2949 if(isBadFtpString(ftp->user))
2950 return CURLE_URL_MALFORMAT;
2951 if(isBadFtpString(ftp->passwd))
2952 return CURLE_URL_MALFORMAT;
2954 conn->proto.ftpc.known_filesize = -1; /* unknown size for now */
2959 #if defined(__INTEL_COMPILER) && (__INTEL_COMPILER == 910) && \
2960 defined(__OPTIMIZE__) && defined(__unix__) && defined(__i386__)
2961 /* workaround icc 9.1 optimizer issue */
2962 #pragma optimize("", on)
2966 * ftp_connect() should do everything that is to be considered a part of
2967 * the connection phase.
2969 * The variable 'done' points to will be TRUE if the protocol-layer connect
2970 * phase is done when this function returns, or FALSE is not. When called as
2971 * a part of the easy interface, it will always be TRUE.
2973 static CURLcode ftp_connect(struct connectdata *conn,
2974 bool *done) /* see description above */
2977 struct ftp_conn *ftpc = &conn->proto.ftpc;
2978 struct SessionHandle *data=conn->data;
2979 struct pingpong *pp = &ftpc->pp;
2981 *done = FALSE; /* default to not done yet */
2983 /* If there already is a protocol-specific struct allocated for this
2984 sessionhandle, deal with it */
2985 Curl_reset_reqproto(conn);
2987 result = ftp_init(conn);
2988 if(CURLE_OK != result)
2991 /* We always support persistent connections on ftp */
2992 conn->bits.close = FALSE;
2994 pp->response_time = RESP_TIMEOUT; /* set default response time-out */
2995 pp->statemach_act = ftp_statemach_act;
2996 pp->endofresp = ftp_endofresp;
2999 if(conn->bits.tunnel_proxy && conn->bits.httpproxy) {
3000 /* for FTP over HTTP proxy */
3001 struct HTTP http_proxy;
3002 struct FTP *ftp_save;
3005 /* We want "seamless" FTP operations through HTTP proxy tunnel */
3007 /* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the member
3008 * conn->proto.http; we want FTP through HTTP and we have to change the
3009 * member temporarily for connecting to the HTTP proxy. After
3010 * Curl_proxyCONNECT we have to set back the member to the original struct
3013 ftp_save = data->state.proto.ftp;
3014 memset(&http_proxy, 0, sizeof(http_proxy));
3015 data->state.proto.http = &http_proxy;
3017 result = Curl_proxyCONNECT(conn, FIRSTSOCKET,
3018 conn->host.name, conn->remote_port);
3020 data->state.proto.ftp = ftp_save;
3022 if(CURLE_OK != result)
3026 if(conn->handler->flags & PROTOPT_SSL) {
3028 result = Curl_ssl_connect(conn, FIRSTSOCKET);
3033 Curl_pp_init(pp); /* init the generic pingpong data */
3035 /* When we connect, we start in the state where we await the 220
3037 state(conn, FTP_WAIT220);
3039 if(data->state.used_interface == Curl_if_multi)
3040 result = ftp_multi_statemach(conn, done);
3042 result = ftp_easy_statemach(conn);
3050 /***********************************************************************
3054 * The DONE function. This does what needs to be done after a single DO has
3057 * Input argument is already checked for validity.
3059 static CURLcode ftp_done(struct connectdata *conn, CURLcode status,
3062 struct SessionHandle *data = conn->data;
3063 struct FTP *ftp = data->state.proto.ftp;
3064 struct ftp_conn *ftpc = &conn->proto.ftpc;
3065 struct pingpong *pp = &ftpc->pp;
3068 CURLcode result=CURLE_OK;
3069 bool was_ctl_valid = ftpc->ctl_valid;
3071 const char *path_to_use = data->state.path;
3074 /* When the easy handle is removed from the multi while libcurl is still
3075 * trying to resolve the host name, it seems that the ftp struct is not
3076 * yet initialized, but the removal action calls Curl_done() which calls
3077 * this function. So we simply return success if no ftp pointer is set.
3082 case CURLE_BAD_DOWNLOAD_RESUME:
3083 case CURLE_FTP_WEIRD_PASV_REPLY:
3084 case CURLE_FTP_PORT_FAILED:
3085 case CURLE_FTP_COULDNT_SET_TYPE:
3086 case CURLE_FTP_COULDNT_RETR_FILE:
3087 case CURLE_UPLOAD_FAILED:
3088 case CURLE_REMOTE_ACCESS_DENIED:
3089 case CURLE_FILESIZE_EXCEEDED:
3090 case CURLE_REMOTE_FILE_NOT_FOUND:
3091 case CURLE_WRITE_ERROR:
3092 /* the connection stays alive fine even though this happened */
3094 case CURLE_OK: /* doesn't affect the control connection's status */
3096 ftpc->ctl_valid = was_ctl_valid;
3099 /* until we cope better with prematurely ended requests, let them
3100 * fallback as if in complete failure */
3101 default: /* by default, an error means the control connection is
3102 wedged and should not be used anymore */
3103 ftpc->ctl_valid = FALSE;
3104 ftpc->cwdfail = TRUE; /* set this TRUE to prevent us to remember the
3105 current path, as this connection is going */
3106 conn->bits.close = TRUE; /* marked for closure */
3107 result = status; /* use the already set error code */
3111 /* now store a copy of the directory we are in */
3113 free(ftpc->prevpath);
3115 if(data->set.wildcardmatch) {
3116 if(data->set.chunk_end && ftpc->file) {
3117 data->set.chunk_end(data->wildcard.customptr);
3119 ftpc->known_filesize = -1;
3122 /* get the "raw" path */
3123 path = curl_easy_unescape(data, path_to_use, 0, NULL);
3125 /* out of memory, but we can limp along anyway (and should try to
3126 * since we're in the out of memory cleanup path) */
3127 ftpc->prevpath = NULL; /* no path */
3130 size_t flen = ftpc->file?strlen(ftpc->file):0; /* file is "raw" already */
3131 size_t dlen = strlen(path)-flen;
3132 if(!ftpc->cwdfail) {
3133 if(dlen && (data->set.ftp_filemethod != FTPFILE_NOCWD)) {
3134 ftpc->prevpath = path;
3136 /* if 'path' is not the whole string */
3137 ftpc->prevpath[dlen]=0; /* terminate */
3140 /* we never changed dir */
3141 ftpc->prevpath=strdup("");
3145 infof(data, "Remembering we are in dir \"%s\"\n", ftpc->prevpath);
3148 ftpc->prevpath = NULL; /* no path */
3152 /* free the dir tree and file parts */
3155 /* shut down the socket to inform the server we're done */
3158 shutdown(conn->sock[SECONDARYSOCKET],2); /* SD_BOTH */
3161 if(conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD) {
3162 if(!result && ftpc->dont_check && data->req.maxdownload > 0)
3163 /* partial download completed */
3164 result = Curl_pp_sendf(pp, "ABOR");
3166 if(conn->ssl[SECONDARYSOCKET].use) {
3167 /* The secondary socket is using SSL so we must close down that part
3168 first before we close the socket for real */
3169 Curl_ssl_close(conn, SECONDARYSOCKET);
3171 /* Note that we keep "use" set to TRUE since that (next) connection is
3172 still requested to use SSL */
3174 if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET]) {
3175 Curl_closesocket(conn, conn->sock[SECONDARYSOCKET]);
3176 conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
3180 if(!result && (ftp->transfer == FTPTRANSFER_BODY) && ftpc->ctl_valid &&
3181 pp->pending_resp && !premature) {
3183 * Let's see what the server says about the transfer we just performed,
3184 * but lower the timeout as sometimes this connection has died while the
3185 * data has been transferred. This happens when doing through NATs etc that
3186 * abandon old silent connections.
3188 long old_time = pp->response_time;
3190 pp->response_time = 60*1000; /* give it only a minute for now */
3191 pp->response = Curl_tvnow(); /* timeout relative now */
3193 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
3195 pp->response_time = old_time; /* set this back to previous value */
3197 if(!nread && (CURLE_OPERATION_TIMEDOUT == result)) {
3198 failf(data, "control connection looks dead");
3199 ftpc->ctl_valid = FALSE; /* mark control connection as bad */
3200 conn->bits.close = TRUE; /* mark for closure */
3206 if(ftpc->dont_check && data->req.maxdownload > 0) {
3207 /* we have just sent ABOR and there is no reliable way to check if it was
3208 * successful or not; we have to close the connection now */
3209 infof(data, "partial download completed, closing connection\n");
3210 conn->bits.close = TRUE; /* mark for closure */
3214 if(!ftpc->dont_check) {
3215 /* 226 Transfer complete, 250 Requested file action okay, completed. */
3216 if((ftpcode != 226) && (ftpcode != 250)) {
3217 failf(data, "server did not report OK, got %d", ftpcode);
3218 result = CURLE_PARTIAL_FILE;
3223 if(result || premature)
3224 /* the response code from the transfer showed an error already so no
3225 use checking further */
3227 else if(data->set.upload) {
3228 if((-1 != data->set.infilesize) &&
3229 (data->set.infilesize != *ftp->bytecountp) &&
3231 (ftp->transfer == FTPTRANSFER_BODY)) {
3232 failf(data, "Uploaded unaligned file size (%" FORMAT_OFF_T
3233 " out of %" FORMAT_OFF_T " bytes)",
3234 *ftp->bytecountp, data->set.infilesize);
3235 result = CURLE_PARTIAL_FILE;
3239 if((-1 != data->req.size) &&
3240 (data->req.size != *ftp->bytecountp) &&
3241 #ifdef CURL_DO_LINEEND_CONV
3242 /* Most FTP servers don't adjust their file SIZE response for CRLFs, so
3243 * we'll check to see if the discrepancy can be explained by the number
3244 * of CRLFs we've changed to LFs.
3246 ((data->req.size + data->state.crlf_conversions) !=
3247 *ftp->bytecountp) &&
3248 #endif /* CURL_DO_LINEEND_CONV */
3249 (data->req.maxdownload != *ftp->bytecountp)) {
3250 failf(data, "Received only partial file: %" FORMAT_OFF_T " bytes",
3252 result = CURLE_PARTIAL_FILE;
3254 else if(!ftpc->dont_check &&
3255 !*ftp->bytecountp &&
3256 (data->req.size>0)) {
3257 failf(data, "No data was received!");
3258 result = CURLE_FTP_COULDNT_RETR_FILE;
3262 /* clear these for next connection */
3263 ftp->transfer = FTPTRANSFER_BODY;
3264 ftpc->dont_check = FALSE;
3266 /* Send any post-transfer QUOTE strings? */
3267 if(!status && !result && !premature && data->set.postquote)
3268 result = ftp_sendquote(conn, data->set.postquote);
3273 /***********************************************************************
3277 * Where a 'quote' means a list of custom commands to send to the server.
3278 * The quote list is passed as an argument.
3284 CURLcode ftp_sendquote(struct connectdata *conn, struct curl_slist *quote)
3286 struct curl_slist *item;
3290 struct ftp_conn *ftpc = &conn->proto.ftpc;
3291 struct pingpong *pp = &ftpc->pp;
3296 char *cmd = item->data;
3297 bool acceptfail = FALSE;
3299 /* if a command starts with an asterisk, which a legal FTP command never
3300 can, the command will be allowed to fail without it causing any
3301 aborts or cancels etc. It will cause libcurl to act as if the command
3302 is successful, whatever the server reponds. */
3309 FTPSENDF(conn, "%s", cmd);
3311 pp->response = Curl_tvnow(); /* timeout relative now */
3313 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
3317 if(!acceptfail && (ftpcode >= 400)) {
3318 failf(conn->data, "QUOT string not accepted: %s", cmd);
3319 return CURLE_QUOTE_ERROR;
3329 /***********************************************************************
3333 * Returns TRUE if we in the current situation should send TYPE
3335 static int ftp_need_type(struct connectdata *conn,
3338 return conn->proto.ftpc.transfertype != (ascii_wanted?'A':'I');
3341 /***********************************************************************
3345 * Set TYPE. We only deal with ASCII or BINARY so this function
3347 * If the transfer type is not sent, simulate on OK response in newstate
3349 static CURLcode ftp_nb_type(struct connectdata *conn,
3350 bool ascii, ftpstate newstate)
3352 struct ftp_conn *ftpc = &conn->proto.ftpc;
3354 char want = (char)(ascii?'A':'I');
3356 if(ftpc->transfertype == want) {
3357 state(conn, newstate);
3358 return ftp_state_type_resp(conn, 200, newstate);
3361 PPSENDF(&ftpc->pp, "TYPE %c", want);
3362 state(conn, newstate);
3364 /* keep track of our current transfer type */
3365 ftpc->transfertype = want;
3369 /***************************************************************************
3371 * ftp_pasv_verbose()
3373 * This function only outputs some informationals about this second connection
3374 * when we've issued a PASV command before and thus we have connected to a
3375 * possibly new IP address.
3378 #ifndef CURL_DISABLE_VERBOSE_STRINGS
3380 ftp_pasv_verbose(struct connectdata *conn,
3382 char *newhost, /* ascii version */
3386 Curl_printable_address(ai, buf, sizeof(buf));
3387 infof(conn->data, "Connecting to %s (%s) port %d\n", newhost, buf, port);
3392 Check if this is a range download, and if so, set the internal variables
3396 static CURLcode ftp_range(struct connectdata *conn)
3398 curl_off_t from, to;
3401 struct SessionHandle *data = conn->data;
3402 struct ftp_conn *ftpc = &conn->proto.ftpc;
3404 if(data->state.use_range && data->state.range) {
3405 from=curlx_strtoofft(data->state.range, &ptr, 0);
3406 while(*ptr && (ISSPACE(*ptr) || (*ptr=='-')))
3408 to=curlx_strtoofft(ptr, &ptr2, 0);
3410 /* we didn't get any digit */
3413 if((-1 == to) && (from>=0)) {
3415 data->state.resume_from = from;
3416 DEBUGF(infof(conn->data, "FTP RANGE %" FORMAT_OFF_T " to end of file\n",
3421 data->req.maxdownload = -from;
3422 data->state.resume_from = from;
3423 DEBUGF(infof(conn->data, "FTP RANGE the last %" FORMAT_OFF_T " bytes\n",
3428 data->req.maxdownload = (to-from)+1; /* include last byte */
3429 data->state.resume_from = from;
3430 DEBUGF(infof(conn->data, "FTP RANGE from %" FORMAT_OFF_T
3431 " getting %" FORMAT_OFF_T " bytes\n",
3432 from, data->req.maxdownload));
3434 DEBUGF(infof(conn->data, "range-download from %" FORMAT_OFF_T
3435 " to %" FORMAT_OFF_T ", totally %" FORMAT_OFF_T " bytes\n",
3436 from, to, data->req.maxdownload));
3437 ftpc->dont_check = TRUE; /* dont check for successful transfer */
3440 data->req.maxdownload = -1;
3448 * This function shall be called when the second FTP (data) connection is
3452 static CURLcode ftp_nextconnect(struct connectdata *conn)
3454 struct SessionHandle *data=conn->data;
3455 struct ftp_conn *ftpc = &conn->proto.ftpc;
3456 CURLcode result = CURLE_OK;
3458 /* the ftp struct is inited in ftp_connect() */
3459 struct FTP *ftp = data->state.proto.ftp;
3461 DEBUGF(infof(data, "DO-MORE phase starts\n"));
3463 if(ftp->transfer <= FTPTRANSFER_INFO) {
3464 /* a transfer is about to take place, or if not a file name was given
3465 so we'll do a SIZE on it later and then we need the right TYPE first */
3467 if(data->set.upload) {
3468 result = ftp_nb_type(conn, data->set.prefer_ascii, FTP_STOR_TYPE);
3474 ftp->downloadsize = -1; /* unknown as of yet */
3476 result = ftp_range(conn);
3479 else if(data->set.ftp_list_only || !ftpc->file) {
3480 /* The specified path ends with a slash, and therefore we think this
3481 is a directory that is requested, use LIST. But before that we
3482 need to set ASCII transfer mode. */
3484 /* But only if a body transfer was requested. */
3485 if(ftp->transfer == FTPTRANSFER_BODY) {
3486 result = ftp_nb_type(conn, TRUE, FTP_LIST_TYPE);
3490 /* otherwise just fall through */
3493 result = ftp_nb_type(conn, data->set.prefer_ascii, FTP_RETR_TYPE);
3498 result = ftp_easy_statemach(conn);
3501 if((result == CURLE_OK) && (ftp->transfer != FTPTRANSFER_BODY))
3502 /* no data to transfer. FIX: it feels like a kludge to have this here
3504 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
3506 /* end of transfer */
3507 DEBUGF(infof(data, "DO-MORE phase ends with %d\n", (int)result));
3514 /***********************************************************************
3518 * This is the actual DO function for FTP. Get a file/directory according to
3519 * the options previously setup.
3523 CURLcode ftp_perform(struct connectdata *conn,
3524 bool *connected, /* connect status after PASV / PORT */
3527 /* this is FTP and no proxy */
3528 CURLcode result=CURLE_OK;
3530 DEBUGF(infof(conn->data, "DO phase starts\n"));
3532 if(conn->data->set.opt_no_body) {
3533 /* requested no body means no transfer... */
3534 struct FTP *ftp = conn->data->state.proto.ftp;
3535 ftp->transfer = FTPTRANSFER_INFO;
3539 *dophase_done = FALSE; /* not done yet */
3541 /* start the first command in the DO phase */
3542 result = ftp_state_quote(conn, TRUE, FTP_QUOTE);
3546 /* run the state-machine */
3547 if(conn->data->state.used_interface == Curl_if_multi)
3548 result = ftp_multi_statemach(conn, dophase_done);
3550 result = ftp_easy_statemach(conn);
3551 *dophase_done = TRUE; /* with the easy interface we are done here */
3553 *connected = conn->bits.tcpconnect[FIRSTSOCKET];
3556 DEBUGF(infof(conn->data, "DO phase is complete\n"));
3561 static void wc_data_dtor(void *ptr)
3563 struct ftp_wc_tmpdata *tmp = ptr;
3565 Curl_ftp_parselist_data_free(&tmp->parser);
3569 static CURLcode init_wc_data(struct connectdata *conn)
3572 char *path = conn->data->state.path;
3573 struct WildcardData *wildcard = &(conn->data->wildcard);
3574 CURLcode ret = CURLE_OK;
3575 struct ftp_wc_tmpdata *ftp_tmp;
3577 last_slash = strrchr(conn->data->state.path, '/');
3580 if(last_slash[0] == '\0') {
3581 wildcard->state = CURLWC_CLEAN;
3582 ret = ftp_parse_url_path(conn);
3586 wildcard->pattern = strdup(last_slash);
3587 if(!wildcard->pattern)
3588 return CURLE_OUT_OF_MEMORY;
3589 last_slash[0] = '\0'; /* cut file from path */
3592 else { /* there is only 'wildcard pattern' or nothing */
3594 wildcard->pattern = strdup(path);
3595 if(!wildcard->pattern)
3596 return CURLE_OUT_OF_MEMORY;
3599 else { /* only list */
3600 wildcard->state = CURLWC_CLEAN;
3601 ret = ftp_parse_url_path(conn);
3606 /* program continues only if URL is not ending with slash, allocate needed
3607 resources for wildcard transfer */
3609 /* allocate ftp protocol specific temporary wildcard data */
3610 ftp_tmp = malloc(sizeof(struct ftp_wc_tmpdata));
3612 return CURLE_OUT_OF_MEMORY;
3615 /* INITIALIZE parselist structure */
3616 ftp_tmp->parser = Curl_ftp_parselist_data_alloc();
3617 if(!ftp_tmp->parser) {
3619 return CURLE_OUT_OF_MEMORY;
3622 wildcard->tmp = ftp_tmp; /* put it to the WildcardData tmp pointer */
3623 wildcard->tmp_dtor = wc_data_dtor;
3625 /* wildcard does not support NOCWD option (assert it?) */
3626 if(conn->data->set.ftp_filemethod == FTPFILE_NOCWD)
3627 conn->data->set.ftp_filemethod = FTPFILE_MULTICWD;
3629 /* try to parse ftp url */
3630 ret = ftp_parse_url_path(conn);
3635 /* backup old write_function */
3636 ftp_tmp->backup.write_function = conn->data->set.fwrite_func;
3637 /* parsing write function */
3638 conn->data->set.fwrite_func = Curl_ftp_parselist;
3639 /* backup old file descriptor */
3640 ftp_tmp->backup.file_descriptor = conn->data->set.out;
3641 /* let the writefunc callback know what curl pointer is working with */
3642 conn->data->set.out = conn;
3644 wildcard->path = strdup(conn->data->state.path);
3645 if(!wildcard->path) {
3646 return CURLE_OUT_OF_MEMORY;
3649 infof(conn->data, "Wildcard - Parsing started\n");
3653 /* This is called recursively */
3654 static CURLcode wc_statemach(struct connectdata *conn)
3656 struct WildcardData * const wildcard = &(conn->data->wildcard);
3657 CURLcode ret = CURLE_OK;
3659 switch (wildcard->state) {
3661 ret = init_wc_data(conn);
3662 if(wildcard->state == CURLWC_CLEAN)
3666 wildcard->state = ret ? CURLWC_ERROR : CURLWC_MATCHING;
3669 case CURLWC_MATCHING: {
3670 /* In this state is LIST response successfully parsed, so lets restore
3671 previous WRITEFUNCTION callback and WRITEDATA pointer */
3672 struct ftp_wc_tmpdata *ftp_tmp = wildcard->tmp;
3673 conn->data->set.fwrite_func = ftp_tmp->backup.write_function;
3674 conn->data->set.out = ftp_tmp->backup.file_descriptor;
3675 wildcard->state = CURLWC_DOWNLOADING;
3677 if(Curl_ftp_parselist_geterror(ftp_tmp->parser)) {
3678 /* error found in LIST parsing */
3679 wildcard->state = CURLWC_CLEAN;
3680 return wc_statemach(conn);
3682 else if(wildcard->filelist->size == 0) {
3683 /* no corresponding file */
3684 wildcard->state = CURLWC_CLEAN;
3685 return CURLE_REMOTE_FILE_NOT_FOUND;
3687 return wc_statemach(conn);
3690 case CURLWC_DOWNLOADING: {
3691 /* filelist has at least one file, lets get first one */
3692 struct ftp_conn *ftpc = &conn->proto.ftpc;
3693 struct curl_fileinfo *finfo = wildcard->filelist->head->ptr;
3694 char *tmp_path = malloc(strlen(conn->data->state.path) +
3695 strlen(finfo->filename) + 1);
3697 return CURLE_OUT_OF_MEMORY;
3701 /* make full path to matched file */
3702 strcat(tmp_path, wildcard->path);
3703 strcat(tmp_path, finfo->filename);
3704 /* switch default "state.pathbuffer" and tmp_path, good to see
3705 ftp_parse_url_path function to understand this trick */
3706 Curl_safefree(conn->data->state.pathbuffer);
3707 conn->data->state.pathbuffer = tmp_path;
3708 conn->data->state.path = tmp_path;
3710 infof(conn->data, "Wildcard - START of \"%s\"\n", finfo->filename);
3711 if(conn->data->set.chunk_bgn) {
3712 long userresponse = conn->data->set.chunk_bgn(
3713 finfo, wildcard->customptr, (int)wildcard->filelist->size);
3714 switch(userresponse) {
3715 case CURL_CHUNK_BGN_FUNC_SKIP:
3716 infof(conn->data, "Wildcard - \"%s\" skipped by user\n",
3718 wildcard->state = CURLWC_SKIP;
3719 return wc_statemach(conn);
3720 case CURL_CHUNK_BGN_FUNC_FAIL:
3721 return CURLE_CHUNK_FAILED;
3725 if(finfo->filetype != CURLFILETYPE_FILE) {
3726 wildcard->state = CURLWC_SKIP;
3727 return wc_statemach(conn);
3730 if(finfo->flags & CURLFINFOFLAG_KNOWN_SIZE)
3731 ftpc->known_filesize = finfo->size;
3733 ret = ftp_parse_url_path(conn);
3738 /* we don't need the Curl_fileinfo of first file anymore */
3739 Curl_llist_remove(wildcard->filelist, wildcard->filelist->head, NULL);
3741 if(wildcard->filelist->size == 0) { /* remains only one file to down. */
3742 wildcard->state = CURLWC_CLEAN;
3743 /* after that will be ftp_do called once again and no transfer
3744 will be done because of CURLWC_CLEAN state */
3750 if(conn->data->set.chunk_end)
3751 conn->data->set.chunk_end(conn->data->wildcard.customptr);
3752 Curl_llist_remove(wildcard->filelist, wildcard->filelist->head, NULL);
3753 wildcard->state = (wildcard->filelist->size == 0) ?
3754 CURLWC_CLEAN : CURLWC_DOWNLOADING;
3755 return wc_statemach(conn);
3758 case CURLWC_CLEAN: {
3759 struct ftp_wc_tmpdata *ftp_tmp = wildcard->tmp;
3762 ret = Curl_ftp_parselist_geterror(ftp_tmp->parser);
3764 wildcard->state = ret ? CURLWC_ERROR : CURLWC_DONE;
3775 /***********************************************************************
3779 * This function is registered as 'curl_do' function. It decodes the path
3780 * parts etc as a wrapper to the actual DO function (ftp_perform).
3782 * The input argument is already checked for validity.
3784 static CURLcode ftp_do(struct connectdata *conn, bool *done)
3786 CURLcode retcode = CURLE_OK;
3788 *done = FALSE; /* default to false */
3791 Since connections can be re-used between SessionHandles, this might be a
3792 connection already existing but on a fresh SessionHandle struct so we must
3793 make sure we have a good 'struct FTP' to play with. For new connections,
3794 the struct FTP is allocated and setup in the ftp_connect() function.
3796 Curl_reset_reqproto(conn);
3797 retcode = ftp_init(conn);
3801 if(conn->data->set.wildcardmatch) {
3802 retcode = wc_statemach(conn);
3803 if(conn->data->wildcard.state == CURLWC_SKIP ||
3804 conn->data->wildcard.state == CURLWC_DONE) {
3805 /* do not call ftp_regular_transfer */
3808 if(retcode) /* error, loop or skipping the file */
3811 else { /* no wildcard FSM needed */
3812 retcode = ftp_parse_url_path(conn);
3817 retcode = ftp_regular_transfer(conn, done);
3823 CURLcode Curl_ftpsendf(struct connectdata *conn,
3824 const char *fmt, ...)
3826 ssize_t bytes_written;
3827 #define SBUF_SIZE 1024
3831 CURLcode res = CURLE_OK;
3832 #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
3833 enum protection_level data_sec = conn->data_prot;
3838 vsnprintf(s, SBUF_SIZE-3, fmt, ap);
3841 strcat(s, "\r\n"); /* append a trailing CRLF */
3844 write_len = strlen(s);
3846 res = Curl_convert_to_network(conn->data, s, write_len);
3847 /* Curl_convert_to_network calls failf if unsuccessful */
3852 #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
3853 conn->data_prot = PROT_CMD;
3855 res = Curl_write(conn, conn->sock[FIRSTSOCKET], sptr, write_len,
3857 #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
3858 DEBUGASSERT(data_sec > PROT_NONE && data_sec < PROT_LAST);
3859 conn->data_prot = data_sec;
3865 if(conn->data->set.verbose)
3866 Curl_debug(conn->data, CURLINFO_HEADER_OUT,
3867 sptr, (size_t)bytes_written, conn);
3869 if(bytes_written != (ssize_t)write_len) {
3870 write_len -= bytes_written;
3871 sptr += bytes_written;
3880 /***********************************************************************
3884 * This should be called before calling sclose() on an ftp control connection
3885 * (not data connections). We should then wait for the response from the
3886 * server before returning. The calling code should then try to close the
3890 static CURLcode ftp_quit(struct connectdata *conn)
3892 CURLcode result = CURLE_OK;
3894 if(conn->proto.ftpc.ctl_valid) {
3895 PPSENDF(&conn->proto.ftpc.pp, "QUIT", NULL);
3896 state(conn, FTP_QUIT);
3898 result = ftp_easy_statemach(conn);
3904 /***********************************************************************
3908 * Disconnect from an FTP server. Cleanup protocol-specific per-connection
3909 * resources. BLOCKING.
3911 static CURLcode ftp_disconnect(struct connectdata *conn, bool dead_connection)
3913 struct ftp_conn *ftpc= &conn->proto.ftpc;
3914 struct pingpong *pp = &ftpc->pp;
3916 /* We cannot send quit unconditionally. If this connection is stale or
3917 bad in any way, sending quit and waiting around here will make the
3918 disconnect wait in vain and cause more problems than we need to.
3920 ftp_quit() will check the state of ftp->ctl_valid. If it's ok it
3921 will try to send the QUIT command, otherwise it will just return.
3924 ftpc->ctl_valid = FALSE;
3926 /* The FTP session may or may not have been allocated/setup at this point! */
3927 (void)ftp_quit(conn); /* ignore errors on the QUIT */
3929 if(ftpc->entrypath) {
3930 struct SessionHandle *data = conn->data;
3931 if(data->state.most_recent_ftp_entrypath == ftpc->entrypath) {
3932 data->state.most_recent_ftp_entrypath = NULL;
3934 free(ftpc->entrypath);
3935 ftpc->entrypath = NULL;
3939 if(ftpc->prevpath) {
3940 free(ftpc->prevpath);
3941 ftpc->prevpath = NULL;
3943 if(ftpc->server_os) {
3944 free(ftpc->server_os);
3945 ftpc->server_os = NULL;
3948 Curl_pp_disconnect(pp);
3950 #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
3957 /***********************************************************************
3959 * ftp_parse_url_path()
3961 * Parse the URL path into separate path components.
3965 CURLcode ftp_parse_url_path(struct connectdata *conn)
3967 struct SessionHandle *data = conn->data;
3968 /* the ftp struct is already inited in ftp_connect() */
3969 struct FTP *ftp = data->state.proto.ftp;
3970 struct ftp_conn *ftpc = &conn->proto.ftpc;
3971 const char *slash_pos; /* position of the first '/' char in curpos */
3972 const char *path_to_use = data->state.path;
3973 const char *cur_pos;
3974 const char *filename = NULL;
3976 cur_pos = path_to_use; /* current position in path. point at the begin
3977 of next path component */
3979 ftpc->ctl_valid = FALSE;
3980 ftpc->cwdfail = FALSE;
3982 switch(data->set.ftp_filemethod) {
3984 /* fastest, but less standard-compliant */
3987 The best time to check whether the path is a file or directory is right
3990 the first condition in the if() right here, is there just in case
3991 someone decides to set path to NULL one day
3993 if(data->state.path &&
3994 data->state.path[0] &&
3995 (data->state.path[strlen(data->state.path) - 1] != '/') )
3996 filename = data->state.path; /* this is a full file path */
3998 ftpc->file is not used anywhere other than for operations on a file.
3999 In other words, never for directory operations.
4000 So we can safely leave filename as NULL here and use it as a
4001 argument in dir/file decisions.
4005 case FTPFILE_SINGLECWD:
4006 /* get the last slash */
4007 if(!path_to_use[0]) {
4008 /* no dir, no file */
4012 slash_pos=strrchr(cur_pos, '/');
4013 if(slash_pos || !*cur_pos) {
4014 ftpc->dirs = calloc(1, sizeof(ftpc->dirs[0]));
4016 return CURLE_OUT_OF_MEMORY;
4018 ftpc->dirs[0] = curl_easy_unescape(conn->data, slash_pos ? cur_pos : "/",
4019 slash_pos?(int)(slash_pos-cur_pos):1,
4021 if(!ftpc->dirs[0]) {
4023 return CURLE_OUT_OF_MEMORY;
4025 ftpc->dirdepth = 1; /* we consider it to be a single dir */
4026 filename = slash_pos ? slash_pos+1 : cur_pos; /* rest is file name */
4029 filename = cur_pos; /* this is a file name only */
4032 default: /* allow pretty much anything */
4033 case FTPFILE_MULTICWD:
4035 ftpc->diralloc = 5; /* default dir depth to allocate */
4036 ftpc->dirs = calloc(ftpc->diralloc, sizeof(ftpc->dirs[0]));
4038 return CURLE_OUT_OF_MEMORY;
4040 /* we have a special case for listing the root dir only */
4041 if(strequal(path_to_use, "/")) {
4042 cur_pos++; /* make it point to the zero byte */
4043 ftpc->dirs[0] = strdup("/");
4047 /* parse the URL path into separate path components */
4048 while((slash_pos = strchr(cur_pos, '/')) != NULL) {
4049 /* 1 or 0 pointer offset to indicate absolute directory */
4050 ssize_t absolute_dir = ((cur_pos - data->state.path > 0) &&
4051 (ftpc->dirdepth == 0))?1:0;
4053 /* seek out the next path component */
4054 if(slash_pos-cur_pos) {
4055 /* we skip empty path components, like "x//y" since the FTP command
4056 CWD requires a parameter and a non-existent parameter a) doesn't
4057 work on many servers and b) has no effect on the others. */
4058 int len = (int)(slash_pos - cur_pos + absolute_dir);
4059 ftpc->dirs[ftpc->dirdepth] =
4060 curl_easy_unescape(conn->data, cur_pos - absolute_dir, len, NULL);
4061 if(!ftpc->dirs[ftpc->dirdepth]) { /* run out of memory ... */
4062 failf(data, "no memory");
4064 return CURLE_OUT_OF_MEMORY;
4066 if(isBadFtpString(ftpc->dirs[ftpc->dirdepth])) {
4067 free(ftpc->dirs[ftpc->dirdepth]);
4069 return CURLE_URL_MALFORMAT;
4073 cur_pos = slash_pos + 1; /* jump to the rest of the string */
4077 cur_pos = slash_pos + 1; /* jump to the rest of the string */
4078 if(++ftpc->dirdepth >= ftpc->diralloc) {
4081 ftpc->diralloc *= 2; /* double the size each time */
4082 bigger = realloc(ftpc->dirs, ftpc->diralloc * sizeof(ftpc->dirs[0]));
4085 return CURLE_OUT_OF_MEMORY;
4087 ftpc->dirs = bigger;
4091 filename = cur_pos; /* the rest is the file name */
4095 if(filename && *filename) {
4096 ftpc->file = curl_easy_unescape(conn->data, filename, 0, NULL);
4097 if(NULL == ftpc->file) {
4099 failf(data, "no memory");
4100 return CURLE_OUT_OF_MEMORY;
4102 if(isBadFtpString(ftpc->file)) {
4104 return CURLE_URL_MALFORMAT;
4108 ftpc->file=NULL; /* instead of point to a zero byte, we make it a NULL
4111 if(data->set.upload && !ftpc->file && (ftp->transfer == FTPTRANSFER_BODY)) {
4112 /* We need a file name when uploading. Return error! */
4113 failf(data, "Uploading to a URL without a file name!");
4114 return CURLE_URL_MALFORMAT;
4117 ftpc->cwddone = FALSE; /* default to not done */
4119 if(ftpc->prevpath) {
4120 /* prevpath is "raw" so we convert the input path before we compare the
4123 char *path = curl_easy_unescape(conn->data, data->state.path, 0, &dlen);
4126 return CURLE_OUT_OF_MEMORY;
4129 dlen -= ftpc->file?(int)strlen(ftpc->file):0;
4130 if((dlen == (int)strlen(ftpc->prevpath)) &&
4131 strnequal(path, ftpc->prevpath, dlen)) {
4132 infof(data, "Request has same path as previous transfer\n");
4133 ftpc->cwddone = TRUE;
4141 /* call this when the DO phase has completed */
4142 static CURLcode ftp_dophase_done(struct connectdata *conn,
4145 CURLcode result = CURLE_OK;
4146 struct FTP *ftp = conn->data->state.proto.ftp;
4147 struct ftp_conn *ftpc = &conn->proto.ftpc;
4150 result = ftp_nextconnect(conn);
4152 if(result && (conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD)) {
4153 /* Failure detected, close the second socket if it was created already */
4154 Curl_closesocket(conn, conn->sock[SECONDARYSOCKET]);
4155 conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
4159 if(ftp->transfer != FTPTRANSFER_BODY)
4160 /* no data to transfer */
4161 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
4163 /* since we didn't connect now, we want do_more to get called */
4164 conn->bits.do_more = TRUE;
4166 ftpc->ctl_valid = TRUE; /* seems good */
4171 /* called from multi.c while DOing */
4172 static CURLcode ftp_doing(struct connectdata *conn,
4175 CURLcode result = ftp_multi_statemach(conn, dophase_done);
4178 DEBUGF(infof(conn->data, "DO phase failed\n"));
4179 else if(*dophase_done) {
4180 result = ftp_dophase_done(conn, FALSE /* not connected */);
4182 DEBUGF(infof(conn->data, "DO phase is complete\n"));
4187 /***********************************************************************
4189 * ftp_regular_transfer()
4191 * The input argument is already checked for validity.
4193 * Performs all commands done before a regular transfer between a local and a
4196 * ftp->ctl_valid starts out as FALSE, and gets set to TRUE if we reach the
4197 * ftp_done() function without finding any major problem.
4200 CURLcode ftp_regular_transfer(struct connectdata *conn,
4203 CURLcode result=CURLE_OK;
4204 bool connected=FALSE;
4205 struct SessionHandle *data = conn->data;
4206 struct ftp_conn *ftpc = &conn->proto.ftpc;
4207 data->req.size = -1; /* make sure this is unknown at this point */
4209 Curl_pgrsSetUploadCounter(data, 0);
4210 Curl_pgrsSetDownloadCounter(data, 0);
4211 Curl_pgrsSetUploadSize(data, 0);
4212 Curl_pgrsSetDownloadSize(data, 0);
4214 ftpc->ctl_valid = TRUE; /* starts good */
4216 result = ftp_perform(conn,
4217 &connected, /* have we connected after PASV/PORT */
4218 dophase_done); /* all commands in the DO-phase done? */
4220 if(CURLE_OK == result) {
4223 /* the DO phase has not completed yet */
4226 result = ftp_dophase_done(conn, connected);
4236 static CURLcode ftp_setup_connection(struct connectdata * conn)
4238 struct SessionHandle *data = conn->data;
4242 if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
4243 /* Unless we have asked to tunnel ftp operations through the proxy, we
4244 switch and use HTTP operations only */
4245 #ifndef CURL_DISABLE_HTTP
4246 if(conn->handler == &Curl_handler_ftp)
4247 conn->handler = &Curl_handler_ftp_proxy;
4250 conn->handler = &Curl_handler_ftps_proxy;
4252 failf(data, "FTPS not supported!");
4253 return CURLE_UNSUPPORTED_PROTOCOL;
4257 * We explicitly mark this connection as persistent here as we're doing
4258 * FTP over HTTP and thus we accidentally avoid setting this value
4261 conn->bits.close = FALSE;
4263 failf(data, "FTP over http proxy requires HTTP support built-in!");
4264 return CURLE_UNSUPPORTED_PROTOCOL;
4268 data->state.path++; /* don't include the initial slash */
4269 data->state.slash_removed = TRUE; /* we've skipped the slash */
4271 /* FTP URLs support an extension like ";type=<typecode>" that
4272 * we'll try to get now! */
4273 type = strstr(data->state.path, ";type=");
4276 type = strstr(conn->host.rawalloc, ";type=");
4279 *type = 0; /* it was in the middle of the hostname */
4280 command = Curl_raw_toupper(type[6]);
4281 conn->bits.type_set = TRUE;
4284 case 'A': /* ASCII mode */
4285 data->set.prefer_ascii = TRUE;
4288 case 'D': /* directory mode */
4289 data->set.ftp_list_only = TRUE;
4292 case 'I': /* binary mode */
4294 /* switch off ASCII */
4295 data->set.prefer_ascii = FALSE;
4303 #endif /* CURL_DISABLE_FTP */