1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2013, 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 ***************************************************************************/
23 #include "curl_setup.h"
25 #ifndef CURL_DISABLE_FTP
27 #ifdef HAVE_NETINET_IN_H
28 #include <netinet/in.h>
30 #ifdef HAVE_ARPA_INET_H
31 #include <arpa/inet.h>
34 #include <sys/utsname.h>
44 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
46 #define in_addr_t unsigned long
49 #include <curl/curl.h>
57 #include "http.h" /* for HTTP proxy tunnel stuff */
61 #include "ftplistparser.h"
63 #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
67 #include "strtoofft.h"
72 #include "inet_ntop.h"
73 #include "inet_pton.h"
75 #include "parsedate.h" /* for the week day and month names */
76 #include "sockaddr.h" /* required for Curl_sockaddr_storage */
80 #include "speedcheck.h"
82 #include "http_proxy.h"
83 #include "non-ascii.h"
85 #define _MPRINTF_REPLACE /* use our functions only */
86 #include <curl/mprintf.h>
88 #include "curl_memory.h"
89 /* The last #include file should be: */
93 #define NI_MAXHOST 1025
95 #ifndef INET_ADDRSTRLEN
96 #define INET_ADDRSTRLEN 16
99 #ifdef CURL_DISABLE_VERBOSE_STRINGS
100 #define ftp_pasv_verbose(a,b,c,d) Curl_nop_stmt
103 /* Local API functions */
105 static void _state(struct connectdata *conn,
107 #define state(x,y) _state(x,y)
109 static void _state(struct connectdata *conn,
112 #define state(x,y) _state(x,y,__LINE__)
115 static CURLcode ftp_sendquote(struct connectdata *conn,
116 struct curl_slist *quote);
117 static CURLcode ftp_quit(struct connectdata *conn);
118 static CURLcode ftp_parse_url_path(struct connectdata *conn);
119 static CURLcode ftp_regular_transfer(struct connectdata *conn, bool *done);
120 #ifndef CURL_DISABLE_VERBOSE_STRINGS
121 static void ftp_pasv_verbose(struct connectdata *conn,
123 char *newhost, /* ascii version */
126 static CURLcode ftp_state_prepare_transfer(struct connectdata *conn);
127 static CURLcode ftp_state_mdtm(struct connectdata *conn);
128 static CURLcode ftp_state_quote(struct connectdata *conn,
129 bool init, ftpstate instate);
130 static CURLcode ftp_nb_type(struct connectdata *conn,
131 bool ascii, ftpstate newstate);
132 static int ftp_need_type(struct connectdata *conn,
134 static CURLcode ftp_do(struct connectdata *conn, bool *done);
135 static CURLcode ftp_done(struct connectdata *conn,
136 CURLcode, bool premature);
137 static CURLcode ftp_connect(struct connectdata *conn, bool *done);
138 static CURLcode ftp_disconnect(struct connectdata *conn, bool dead_connection);
139 static CURLcode ftp_do_more(struct connectdata *conn, int *completed);
140 static CURLcode ftp_multi_statemach(struct connectdata *conn, bool *done);
141 static int ftp_getsock(struct connectdata *conn, curl_socket_t *socks,
143 static int ftp_domore_getsock(struct connectdata *conn, curl_socket_t *socks,
145 static CURLcode ftp_doing(struct connectdata *conn,
147 static CURLcode ftp_setup_connection(struct connectdata * conn);
149 static CURLcode init_wc_data(struct connectdata *conn);
150 static CURLcode wc_statemach(struct connectdata *conn);
152 static void wc_data_dtor(void *ptr);
154 static CURLcode ftp_state_retr(struct connectdata *conn, curl_off_t filesize);
156 static CURLcode ftp_readresp(curl_socket_t sockfd,
160 static CURLcode ftp_dophase_done(struct connectdata *conn,
163 /* easy-to-use macro: */
164 #define PPSENDF(x,y,z) if((result = Curl_pp_sendf(x,y,z)) != CURLE_OK) \
169 * FTP protocol handler.
172 const struct Curl_handler Curl_handler_ftp = {
174 ftp_setup_connection, /* setup_connection */
177 ftp_do_more, /* do_more */
178 ftp_connect, /* connect_it */
179 ftp_multi_statemach, /* connecting */
180 ftp_doing, /* doing */
181 ftp_getsock, /* proto_getsock */
182 ftp_getsock, /* doing_getsock */
183 ftp_domore_getsock, /* domore_getsock */
184 ZERO_NULL, /* perform_getsock */
185 ftp_disconnect, /* disconnect */
186 ZERO_NULL, /* readwrite */
187 PORT_FTP, /* defport */
188 CURLPROTO_FTP, /* protocol */
189 PROTOPT_DUAL | PROTOPT_CLOSEACTION | PROTOPT_NEEDSPWD
190 | PROTOPT_NOURLQUERY /* flags */
196 * FTPS protocol handler.
199 const struct Curl_handler Curl_handler_ftps = {
201 ftp_setup_connection, /* setup_connection */
204 ftp_do_more, /* do_more */
205 ftp_connect, /* connect_it */
206 ftp_multi_statemach, /* connecting */
207 ftp_doing, /* doing */
208 ftp_getsock, /* proto_getsock */
209 ftp_getsock, /* doing_getsock */
210 ftp_domore_getsock, /* domore_getsock */
211 ZERO_NULL, /* perform_getsock */
212 ftp_disconnect, /* disconnect */
213 ZERO_NULL, /* readwrite */
214 PORT_FTPS, /* defport */
215 CURLPROTO_FTP | CURLPROTO_FTPS, /* protocol */
216 PROTOPT_SSL | PROTOPT_DUAL | PROTOPT_CLOSEACTION |
217 PROTOPT_NEEDSPWD | PROTOPT_NOURLQUERY /* flags */
221 #ifndef CURL_DISABLE_HTTP
223 * HTTP-proxyed FTP protocol handler.
226 static const struct Curl_handler Curl_handler_ftp_proxy = {
228 ZERO_NULL, /* setup_connection */
229 Curl_http, /* do_it */
230 Curl_http_done, /* done */
231 ZERO_NULL, /* do_more */
232 ZERO_NULL, /* connect_it */
233 ZERO_NULL, /* connecting */
234 ZERO_NULL, /* doing */
235 ZERO_NULL, /* proto_getsock */
236 ZERO_NULL, /* doing_getsock */
237 ZERO_NULL, /* domore_getsock */
238 ZERO_NULL, /* perform_getsock */
239 ZERO_NULL, /* disconnect */
240 ZERO_NULL, /* readwrite */
241 PORT_FTP, /* defport */
242 CURLPROTO_HTTP, /* protocol */
243 PROTOPT_NONE /* flags */
249 * HTTP-proxyed FTPS protocol handler.
252 static const struct Curl_handler Curl_handler_ftps_proxy = {
254 ZERO_NULL, /* setup_connection */
255 Curl_http, /* do_it */
256 Curl_http_done, /* done */
257 ZERO_NULL, /* do_more */
258 ZERO_NULL, /* connect_it */
259 ZERO_NULL, /* connecting */
260 ZERO_NULL, /* doing */
261 ZERO_NULL, /* proto_getsock */
262 ZERO_NULL, /* doing_getsock */
263 ZERO_NULL, /* domore_getsock */
264 ZERO_NULL, /* perform_getsock */
265 ZERO_NULL, /* disconnect */
266 ZERO_NULL, /* readwrite */
267 PORT_FTPS, /* defport */
268 CURLPROTO_HTTP, /* protocol */
269 PROTOPT_NONE /* flags */
276 * NOTE: back in the old days, we added code in the FTP code that made NOBODY
277 * requests on files respond with headers passed to the client/stdout that
278 * looked like HTTP ones.
280 * This approach is not very elegant, it causes confusion and is error-prone.
281 * It is subject for removal at the next (or at least a future) soname bump.
282 * Until then you can test the effects of the removal by undefining the
283 * following define named CURL_FTP_HTTPSTYLE_HEAD.
285 #define CURL_FTP_HTTPSTYLE_HEAD 1
287 static void freedirs(struct ftp_conn *ftpc)
291 for(i=0; i < ftpc->dirdepth; i++) {
307 /* Returns non-zero if the given string contains CR (\r) or LF (\n),
308 which are not allowed within RFC 959 <string>.
309 Note: The input string is in the client's encoding which might
310 not be ASCII, so escape sequences \r & \n must be used instead
311 of hex values 0x0d & 0x0a.
313 static bool isBadFtpString(const char *string)
315 return ((NULL != strchr(string, '\r')) ||
316 (NULL != strchr(string, '\n'))) ? TRUE : FALSE;
319 /***********************************************************************
321 * AcceptServerConnect()
323 * After connection request is received from the server this function is
324 * called to accept the connection and close the listening socket
327 static CURLcode AcceptServerConnect(struct connectdata *conn)
329 struct SessionHandle *data = conn->data;
330 curl_socket_t sock = conn->sock[SECONDARYSOCKET];
331 curl_socket_t s = CURL_SOCKET_BAD;
333 struct Curl_sockaddr_storage add;
335 struct sockaddr_in add;
337 curl_socklen_t size = (curl_socklen_t) sizeof(add);
339 if(0 == getsockname(sock, (struct sockaddr *) &add, &size)) {
342 s=accept(sock, (struct sockaddr *) &add, &size);
344 Curl_closesocket(conn, sock); /* close the first socket */
346 if(CURL_SOCKET_BAD == s) {
347 failf(data, "Error accept()ing server connect");
348 return CURLE_FTP_PORT_FAILED;
350 infof(data, "Connection accepted from server\n");
352 conn->sock[SECONDARYSOCKET] = s;
353 curlx_nonblock(s, TRUE); /* enable non-blocking */
354 conn->sock_accepted[SECONDARYSOCKET] = TRUE;
356 if(data->set.fsockopt) {
359 /* activate callback for setting socket options */
360 error = data->set.fsockopt(data->set.sockopt_client,
362 CURLSOCKTYPE_ACCEPT);
365 Curl_closesocket(conn, s); /* close the socket and bail out */
366 conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
367 return CURLE_ABORTED_BY_CALLBACK;
376 * ftp_timeleft_accept() returns the amount of milliseconds left allowed for
377 * waiting server to connect. If the value is negative, the timeout time has
380 * The start time is stored in progress.t_acceptdata - as set with
381 * Curl_pgrsTime(..., TIMER_STARTACCEPT);
384 static long ftp_timeleft_accept(struct SessionHandle *data)
386 long timeout_ms = DEFAULT_ACCEPT_TIMEOUT;
390 if(data->set.accepttimeout > 0)
391 timeout_ms = data->set.accepttimeout;
395 /* check if the generic timeout possibly is set shorter */
396 other = Curl_timeleft(data, &now, FALSE);
397 if(other && (other < timeout_ms))
398 /* note that this also works fine for when other happens to be negative
399 due to it already having elapsed */
402 /* subtract elapsed time */
403 timeout_ms -= Curl_tvdiff(now, data->progress.t_acceptdata);
405 /* avoid returning 0 as that means no timeout! */
413 /***********************************************************************
415 * ReceivedServerConnect()
417 * After allowing server to connect to us from data port, this function
418 * checks both data connection for connection establishment and ctrl
419 * connection for a negative response regarding a failure in connecting
422 static CURLcode ReceivedServerConnect(struct connectdata *conn, bool *received)
424 struct SessionHandle *data = conn->data;
425 curl_socket_t ctrl_sock = conn->sock[FIRSTSOCKET];
426 curl_socket_t data_sock = conn->sock[SECONDARYSOCKET];
427 struct ftp_conn *ftpc = &conn->proto.ftpc;
428 struct pingpong *pp = &ftpc->pp;
436 timeout_ms = ftp_timeleft_accept(data);
437 infof(data, "Checking for server connect\n");
439 /* if a timeout was already reached, bail out */
440 failf(data, "Accept timeout occurred while waiting server connect");
441 return CURLE_FTP_ACCEPT_TIMEOUT;
444 /* First check whether there is a cached response from server */
445 if(pp->cache_size && pp->cache && pp->cache[0] > '3') {
446 /* Data connection could not be established, let's return */
447 infof(data, "There is negative response in cache while serv connect\n");
448 Curl_GetFTPResponse(&nread, conn, &ftpcode);
449 return CURLE_FTP_ACCEPT_FAILED;
452 result = Curl_socket_check(ctrl_sock, data_sock, CURL_SOCKET_BAD, 0);
454 /* see if the connection request is already here */
458 failf(data, "Error while waiting for server connect");
459 return CURLE_FTP_ACCEPT_FAILED;
460 case 0: /* Server connect is not received yet */
464 if(result & CURL_CSELECT_IN2) {
465 infof(data, "Ready to accept data connection from server\n");
468 else if(result & CURL_CSELECT_IN) {
469 infof(data, "Ctrl conn has data while waiting for data conn\n");
470 Curl_GetFTPResponse(&nread, conn, &ftpcode);
473 return CURLE_FTP_ACCEPT_FAILED;
475 return CURLE_FTP_WEIRD_SERVER_REPLY;
485 /***********************************************************************
489 * After connection from server is accepted this function is called to
490 * setup transfer parameters and initiate the data transfer.
493 static CURLcode InitiateTransfer(struct connectdata *conn)
495 struct SessionHandle *data = conn->data;
496 struct FTP *ftp = data->state.proto.ftp;
497 CURLcode result = CURLE_OK;
499 if(conn->ssl[SECONDARYSOCKET].use) {
500 /* since we only have a plaintext TCP connection here, we must now
501 * do the TLS stuff */
502 infof(data, "Doing the SSL/TLS handshake on the data stream\n");
503 result = Curl_ssl_connect(conn, SECONDARYSOCKET);
508 if(conn->proto.ftpc.state_saved == FTP_STOR) {
509 *(ftp->bytecountp)=0;
511 /* When we know we're uploading a specified file, we can get the file
512 size prior to the actual upload. */
514 Curl_pgrsSetUploadSize(data, data->set.infilesize);
516 /* set the SO_SNDBUF for the secondary socket for those who need it */
517 Curl_sndbufset(conn->sock[SECONDARYSOCKET]);
519 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, /* no download */
520 SECONDARYSOCKET, ftp->bytecountp);
524 Curl_setup_transfer(conn, SECONDARYSOCKET,
525 conn->proto.ftpc.retr_size_saved, FALSE,
526 ftp->bytecountp, -1, NULL); /* no upload here */
529 conn->proto.ftpc.pp.pending_resp = TRUE; /* expect server response */
530 state(conn, FTP_STOP);
535 /***********************************************************************
537 * AllowServerConnect()
539 * When we've issue the PORT command, we have told the server to connect to
540 * us. This function checks whether data connection is established if so it is
544 static CURLcode AllowServerConnect(struct connectdata *conn, bool *connected)
546 struct SessionHandle *data = conn->data;
548 CURLcode ret = CURLE_OK;
551 infof(data, "Preparing for accepting server on data port\n");
553 /* Save the time we start accepting server connect */
554 Curl_pgrsTime(data, TIMER_STARTACCEPT);
556 timeout_ms = ftp_timeleft_accept(data);
558 /* if a timeout was already reached, bail out */
559 failf(data, "Accept timeout occurred while waiting server connect");
560 return CURLE_FTP_ACCEPT_TIMEOUT;
563 /* see if the connection request is already here */
564 ret = ReceivedServerConnect(conn, connected);
569 ret = AcceptServerConnect(conn);
573 ret = InitiateTransfer(conn);
578 /* Add timeout to multi handle and break out of the loop */
579 if(ret == CURLE_OK && *connected == FALSE) {
580 if(data->set.accepttimeout > 0)
581 Curl_expire(data, data->set.accepttimeout);
583 Curl_expire(data, DEFAULT_ACCEPT_TIMEOUT);
590 /* macro to check for a three-digit ftp status code at the start of the
592 #define STATUSCODE(line) (ISDIGIT(line[0]) && ISDIGIT(line[1]) && \
595 /* macro to check for the last line in an FTP server response */
596 #define LASTLINE(line) (STATUSCODE(line) && (' ' == line[3]))
598 static bool ftp_endofresp(struct connectdata *conn, char *line, size_t len,
603 if((len > 3) && LASTLINE(line)) {
604 *code = curlx_sltosi(strtol(line, NULL, 10));
611 static CURLcode ftp_readresp(curl_socket_t sockfd,
613 int *ftpcode, /* return the ftp-code if done */
614 size_t *size) /* size of the response */
616 struct connectdata *conn = pp->conn;
617 struct SessionHandle *data = conn->data;
618 #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
619 char * const buf = data->state.buffer;
621 CURLcode result = CURLE_OK;
624 result = Curl_pp_readresp(sockfd, pp, &code, size);
626 #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
627 /* handle the security-oriented responses 6xx ***/
628 /* FIXME: some errorchecking perhaps... ***/
631 code = Curl_sec_read_msg(conn, buf, PROT_SAFE);
634 code = Curl_sec_read_msg(conn, buf, PROT_PRIVATE);
637 code = Curl_sec_read_msg(conn, buf, PROT_CONFIDENTIAL);
640 /* normal ftp stuff we pass through! */
645 /* store the latest code for later retrieval */
646 data->info.httpcode=code;
652 /* 421 means "Service not available, closing control connection." and FTP
653 * servers use it to signal that idle session timeout has been exceeded.
654 * If we ignored the response, it could end up hanging in some cases.
656 * This response code can come at any point so having it treated
657 * generically is a good idea.
659 infof(data, "We got a 421 - timeout!\n");
660 state(conn, FTP_STOP);
661 return CURLE_OPERATION_TIMEDOUT;
667 /* --- parse FTP server responses --- */
670 * Curl_GetFTPResponse() is a BLOCKING function to read the full response
671 * from a server after a command.
675 CURLcode Curl_GetFTPResponse(ssize_t *nreadp, /* return number of bytes read */
676 struct connectdata *conn,
677 int *ftpcode) /* return the ftp-code */
680 * We cannot read just one byte per read() and then go back to select() as
681 * the OpenSSL read() doesn't grok that properly.
683 * Alas, read as much as possible, split up into lines, use the ending
684 * line in a response or continue reading. */
686 curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
687 long timeout; /* timeout in milliseconds */
689 struct SessionHandle *data = conn->data;
690 CURLcode result = CURLE_OK;
691 struct ftp_conn *ftpc = &conn->proto.ftpc;
692 struct pingpong *pp = &ftpc->pp;
695 int value_to_be_ignored=0;
698 *ftpcode = 0; /* 0 for errors */
700 /* make the pointer point to something for the rest of this function */
701 ftpcode = &value_to_be_ignored;
705 while(!*ftpcode && !result) {
706 /* check and reset timeout value every lap */
707 timeout = Curl_pp_state_timeout(pp);
710 failf(data, "FTP response timeout");
711 return CURLE_OPERATION_TIMEDOUT; /* already too little time */
714 interval_ms = 1000; /* use 1 second timeout intervals */
715 if(timeout < interval_ms)
716 interval_ms = timeout;
719 * Since this function is blocking, we need to wait here for input on the
720 * connection and only then we call the response reading function. We do
721 * timeout at least every second to make the timeout check run.
723 * A caution here is that the ftp_readresp() function has a cache that may
724 * contain pieces of a response from the previous invoke and we need to
725 * make sure we don't just wait for input while there is unhandled data in
726 * that cache. But also, if the cache is there, we call ftp_readresp() and
727 * the cache wasn't good enough to continue we must not just busy-loop
728 * around this function.
732 if(pp->cache && (cache_skip < 2)) {
734 * There's a cache left since before. We then skipping the wait for
735 * socket action, unless this is the same cache like the previous round
736 * as then the cache was deemed not enough to act on and we then need to
737 * wait for more data anyway.
741 switch (Curl_socket_ready(sockfd, CURL_SOCKET_BAD, interval_ms)) {
742 case -1: /* select() error, stop reading */
743 failf(data, "FTP response aborted due to select/poll error: %d",
745 return CURLE_RECV_ERROR;
747 case 0: /* timeout */
748 if(Curl_pgrsUpdate(conn))
749 return CURLE_ABORTED_BY_CALLBACK;
750 continue; /* just continue in our loop for the timeout duration */
752 default: /* for clarity */
756 result = ftp_readresp(sockfd, pp, ftpcode, &nread);
760 if(!nread && pp->cache)
761 /* bump cache skip counter as on repeated skips we must wait for more
765 /* when we got data or there is no cache left, we reset the cache skip
771 } /* while there's buffer left and loop is requested */
773 pp->pending_resp = FALSE;
778 /* This is the ONLY way to change FTP state! */
779 static void _state(struct connectdata *conn,
786 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
787 /* for debug purposes */
788 static const char * const names[]={
826 struct ftp_conn *ftpc = &conn->proto.ftpc;
827 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
828 if(ftpc->state != newstate)
829 infof(conn->data, "FTP %p (line %d) state change from %s to %s\n",
830 (void *)ftpc, lineno, names[ftpc->state], names[newstate]);
832 ftpc->state = newstate;
835 static CURLcode ftp_state_user(struct connectdata *conn)
838 struct FTP *ftp = conn->data->state.proto.ftp;
840 PPSENDF(&conn->proto.ftpc.pp, "USER %s", ftp->user?ftp->user:"");
842 state(conn, FTP_USER);
843 conn->data->state.ftp_trying_alternative = FALSE;
848 static CURLcode ftp_state_pwd(struct connectdata *conn)
852 /* send PWD to discover our entry point */
853 PPSENDF(&conn->proto.ftpc.pp, "%s", "PWD");
854 state(conn, FTP_PWD);
859 /* For the FTP "protocol connect" and "doing" phases only */
860 static int ftp_getsock(struct connectdata *conn,
861 curl_socket_t *socks,
864 return Curl_pp_getsock(&conn->proto.ftpc.pp, socks, numsocks);
867 /* For the FTP "DO_MORE" phase only */
868 static int ftp_domore_getsock(struct connectdata *conn, curl_socket_t *socks,
871 struct ftp_conn *ftpc = &conn->proto.ftpc;
874 return GETSOCK_BLANK;
876 /* When in DO_MORE state, we could be either waiting for us to connect to a
877 remote site, or we could wait for that site to connect to us. Or just
878 handle ordinary commands.
880 When waiting for a connect, we can be in FTP_STOP state (or we're in
881 FTP_STOR when we do an upload) and then we wait for the secondary socket
882 to become writeable. . If we're in another state, we're still handling
883 commands on the control (primary) connection.
887 switch(ftpc->state) {
892 return Curl_pp_getsock(&conn->proto.ftpc.pp, socks, numsocks);
895 socks[0] = conn->sock[SECONDARYSOCKET];
896 if(ftpc->wait_data_conn) {
897 socks[1] = conn->sock[FIRSTSOCKET];
898 return GETSOCK_READSOCK(0) | GETSOCK_READSOCK(1);
901 return GETSOCK_READSOCK(0);
904 /* This is called after the FTP_QUOTE state is passed.
906 ftp_state_cwd() sends the range of CWD commands to the server to change to
907 the correct directory. It may also need to send MKD commands to create
908 missing ones, if that option is enabled.
910 static CURLcode ftp_state_cwd(struct connectdata *conn)
912 CURLcode result = CURLE_OK;
913 struct ftp_conn *ftpc = &conn->proto.ftpc;
916 /* already done and fine */
917 result = ftp_state_mdtm(conn);
919 ftpc->count2 = 0; /* count2 counts failed CWDs */
921 /* count3 is set to allow a MKD to fail once. In the case when first CWD
922 fails and then MKD fails (due to another session raced it to create the
923 dir) this then allows for a second try to CWD to it */
924 ftpc->count3 = (conn->data->set.ftp_create_missing_dirs==2)?1:0;
926 if(conn->bits.reuse && ftpc->entrypath) {
927 /* This is a re-used connection. Since we change directory to where the
928 transfer is taking place, we must first get back to the original dir
929 where we ended up after login: */
930 ftpc->count1 = 0; /* we count this as the first path, then we add one
931 for all upcoming ones in the ftp->dirs[] array */
932 PPSENDF(&conn->proto.ftpc.pp, "CWD %s", ftpc->entrypath);
933 state(conn, FTP_CWD);
938 /* issue the first CWD, the rest is sent when the CWD responses are
940 PPSENDF(&conn->proto.ftpc.pp, "CWD %s", ftpc->dirs[ftpc->count1 -1]);
941 state(conn, FTP_CWD);
944 /* No CWD necessary */
945 result = ftp_state_mdtm(conn);
958 static CURLcode ftp_state_use_port(struct connectdata *conn,
959 ftpport fcmd) /* start with this */
962 CURLcode result = CURLE_OK;
963 struct ftp_conn *ftpc = &conn->proto.ftpc;
964 struct SessionHandle *data=conn->data;
965 curl_socket_t portsock= CURL_SOCKET_BAD;
966 char myhost[256] = "";
968 struct Curl_sockaddr_storage ss;
969 Curl_addrinfo *res, *ai;
970 curl_socklen_t sslen;
971 char hbuf[NI_MAXHOST];
972 struct sockaddr *sa=(struct sockaddr *)&ss;
973 struct sockaddr_in * const sa4 = (void *)sa;
975 struct sockaddr_in6 * const sa6 = (void *)sa;
978 static const char mode[][5] = { "EPRT", "PORT" };
982 char *string_ftpport = data->set.str[STRING_FTPPORT];
983 struct Curl_dns_entry *h=NULL;
984 unsigned short port_min = 0;
985 unsigned short port_max = 0;
987 bool possibly_non_local = TRUE;
991 /* Step 1, figure out what is requested,
993 * (ipv4|ipv6|domain|interface)?(:port(-range)?)?
996 if(data->set.str[STRING_FTPPORT] &&
997 (strlen(data->set.str[STRING_FTPPORT]) > 1)) {
1000 size_t addrlen = INET6_ADDRSTRLEN > strlen(string_ftpport) ?
1001 INET6_ADDRSTRLEN : strlen(string_ftpport);
1003 size_t addrlen = INET_ADDRSTRLEN > strlen(string_ftpport) ?
1004 INET_ADDRSTRLEN : strlen(string_ftpport);
1006 char *ip_start = string_ftpport;
1007 char *ip_end = NULL;
1008 char *port_start = NULL;
1009 char *port_sep = NULL;
1011 addr = calloc(addrlen+1, 1);
1013 return CURLE_OUT_OF_MEMORY;
1016 if(*string_ftpport == '[') {
1017 /* [ipv6]:port(-range) */
1018 ip_start = string_ftpport + 1;
1019 if((ip_end = strchr(string_ftpport, ']')) != NULL )
1020 strncpy(addr, ip_start, ip_end - ip_start);
1024 if(*string_ftpport == ':') {
1026 ip_end = string_ftpport;
1028 else if((ip_end = strchr(string_ftpport, ':')) != NULL) {
1029 /* either ipv6 or (ipv4|domain|interface):port(-range) */
1031 if(Curl_inet_pton(AF_INET6, string_ftpport, sa6) == 1) {
1033 port_min = port_max = 0;
1034 strcpy(addr, string_ftpport);
1035 ip_end = NULL; /* this got no port ! */
1039 /* (ipv4|domain|interface):port(-range) */
1040 strncpy(addr, string_ftpport, ip_end - ip_start );
1043 /* ipv4|interface */
1044 strcpy(addr, string_ftpport);
1046 /* parse the port */
1047 if(ip_end != NULL) {
1048 if((port_start = strchr(ip_end, ':')) != NULL) {
1049 port_min = curlx_ultous(strtoul(port_start+1, NULL, 10));
1050 if((port_sep = strchr(port_start, '-')) != NULL) {
1051 port_max = curlx_ultous(strtoul(port_sep + 1, NULL, 10));
1054 port_max = port_min;
1058 /* correct errors like:
1060 * :-4711 , in this case port_min is (unsigned)-1,
1061 * therefore port_min > port_max for all cases
1062 * but port_max = (unsigned)-1
1064 if(port_min > port_max )
1065 port_min = port_max = 0;
1069 /* attempt to get the address of the given interface name */
1070 switch(Curl_if2ip(conn->ip_addr->ai_family, conn->scope, addr,
1071 hbuf, sizeof(hbuf))) {
1072 case IF2IP_NOT_FOUND:
1073 /* not an interface, use the given string as host name instead */
1076 case IF2IP_AF_NOT_SUPPORTED:
1077 return CURLE_FTP_PORT_FAILED;
1079 host = hbuf; /* use the hbuf for host name */
1083 /* there was only a port(-range) given, default the host */
1085 } /* data->set.ftpport */
1088 /* not an interface and not a host name, get default by extracting
1089 the IP from the control connection */
1092 if(getsockname(conn->sock[FIRSTSOCKET], sa, &sslen)) {
1093 failf(data, "getsockname() failed: %s",
1094 Curl_strerror(conn, SOCKERRNO) );
1095 Curl_safefree(addr);
1096 return CURLE_FTP_PORT_FAILED;
1098 switch(sa->sa_family) {
1101 Curl_inet_ntop(sa->sa_family, &sa6->sin6_addr, hbuf, sizeof(hbuf));
1105 Curl_inet_ntop(sa->sa_family, &sa4->sin_addr, hbuf, sizeof(hbuf));
1108 host = hbuf; /* use this host name */
1109 possibly_non_local = FALSE; /* we know it is local now */
1112 /* resolv ip/host to ip */
1113 rc = Curl_resolv(conn, host, 0, &h);
1114 if(rc == CURLRESOLV_PENDING)
1115 (void)Curl_resolver_wait_resolv(conn, &h);
1118 /* when we return from this function, we can forget about this entry
1119 to we can unlock it now already */
1120 Curl_resolv_unlock(data, h);
1123 res = NULL; /* failure! */
1126 failf(data, "failed to resolve the address provided to PORT: %s", host);
1127 Curl_safefree(addr);
1128 return CURLE_FTP_PORT_FAILED;
1131 Curl_safefree(addr);
1134 /* step 2, create a socket for the requested address */
1136 portsock = CURL_SOCKET_BAD;
1138 for(ai = res; ai; ai = ai->ai_next) {
1139 result = Curl_socket(conn, ai, NULL, &portsock);
1147 failf(data, "socket failure: %s", Curl_strerror(conn, error));
1148 return CURLE_FTP_PORT_FAILED;
1151 /* step 3, bind to a suitable local address */
1153 memcpy(sa, ai->ai_addr, ai->ai_addrlen);
1154 sslen = ai->ai_addrlen;
1156 for(port = port_min; port <= port_max;) {
1157 if(sa->sa_family == AF_INET)
1158 sa4->sin_port = htons(port);
1161 sa6->sin6_port = htons(port);
1163 /* Try binding the given address. */
1164 if(bind(portsock, sa, sslen) ) {
1167 if(possibly_non_local && (error == EADDRNOTAVAIL)) {
1168 /* The requested bind address is not local. Use the address used for
1169 * the control connection instead and restart the port loop
1172 infof(data, "bind(port=%hu) on non-local address failed: %s\n", port,
1173 Curl_strerror(conn, error) );
1176 if(getsockname(conn->sock[FIRSTSOCKET], sa, &sslen)) {
1177 failf(data, "getsockname() failed: %s",
1178 Curl_strerror(conn, SOCKERRNO) );
1179 Curl_closesocket(conn, portsock);
1180 return CURLE_FTP_PORT_FAILED;
1183 possibly_non_local = FALSE; /* don't try this again */
1186 else if(error != EADDRINUSE && error != EACCES) {
1187 failf(data, "bind(port=%hu) failed: %s", port,
1188 Curl_strerror(conn, error) );
1189 Curl_closesocket(conn, portsock);
1190 return CURLE_FTP_PORT_FAILED;
1199 /* maybe all ports were in use already*/
1200 if(port > port_max) {
1201 failf(data, "bind() failed, we ran out of ports!");
1202 Curl_closesocket(conn, portsock);
1203 return CURLE_FTP_PORT_FAILED;
1206 /* get the name again after the bind() so that we can extract the
1207 port number it uses now */
1209 if(getsockname(portsock, (struct sockaddr *)sa, &sslen)) {
1210 failf(data, "getsockname() failed: %s",
1211 Curl_strerror(conn, SOCKERRNO) );
1212 Curl_closesocket(conn, portsock);
1213 return CURLE_FTP_PORT_FAILED;
1216 /* step 4, listen on the socket */
1218 if(listen(portsock, 1)) {
1219 failf(data, "socket failure: %s", Curl_strerror(conn, SOCKERRNO));
1220 Curl_closesocket(conn, portsock);
1221 return CURLE_FTP_PORT_FAILED;
1224 /* step 5, send the proper FTP command */
1226 /* get a plain printable version of the numerical address to work with
1228 Curl_printable_address(ai, myhost, sizeof(myhost));
1231 if(!conn->bits.ftp_use_eprt && conn->bits.ipv6)
1232 /* EPRT is disabled but we are connected to a IPv6 host, so we ignore the
1233 request and enable EPRT again! */
1234 conn->bits.ftp_use_eprt = TRUE;
1237 for(; fcmd != DONE; fcmd++) {
1239 if(!conn->bits.ftp_use_eprt && (EPRT == fcmd))
1240 /* if disabled, goto next */
1243 if((PORT == fcmd) && sa->sa_family != AF_INET)
1244 /* PORT is ipv4 only */
1247 switch (sa->sa_family) {
1249 port = ntohs(sa4->sin_port);
1253 port = ntohs(sa6->sin6_port);
1257 continue; /* might as well skip this */
1262 * Two fine examples from RFC2428;
1264 * EPRT |1|132.235.1.2|6275|
1266 * EPRT |2|1080::8:800:200C:417A|5282|
1269 result = Curl_pp_sendf(&ftpc->pp, "%s |%d|%s|%hu|", mode[fcmd],
1270 sa->sa_family == AF_INET?1:2,
1273 failf(data, "Failure sending EPRT command: %s",
1274 curl_easy_strerror(result));
1275 Curl_closesocket(conn, portsock);
1276 /* don't retry using PORT */
1277 ftpc->count1 = PORT;
1279 state(conn, FTP_STOP);
1284 else if(PORT == fcmd) {
1285 char *source = myhost;
1288 /* translate x.x.x.x to x,x,x,x */
1289 while(source && *source) {
1298 snprintf(dest, 20, ",%d,%d", (int)(port>>8), (int)(port&0xff));
1300 result = Curl_pp_sendf(&ftpc->pp, "%s %s", mode[fcmd], tmp);
1302 failf(data, "Failure sending PORT command: %s",
1303 curl_easy_strerror(result));
1304 Curl_closesocket(conn, portsock);
1306 state(conn, FTP_STOP);
1313 /* store which command was sent */
1314 ftpc->count1 = fcmd;
1316 /* we set the secondary socket variable to this for now, it is only so that
1317 the cleanup function will close it in case we fail before the true
1318 secondary stuff is made */
1319 if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET])
1320 Curl_closesocket(conn, conn->sock[SECONDARYSOCKET]);
1321 conn->sock[SECONDARYSOCKET] = portsock;
1323 /* this tcpconnect assignment below is a hackish work-around to make the
1324 multi interface with active FTP work - as it will not wait for a
1325 (passive) connect in Curl_is_connected().
1327 The *proper* fix is to make sure that the active connection from the
1328 server is done in a non-blocking way. Currently, it is still BLOCKING.
1330 conn->bits.tcpconnect[SECONDARYSOCKET] = TRUE;
1332 state(conn, FTP_PORT);
1336 static CURLcode ftp_state_use_pasv(struct connectdata *conn)
1338 struct ftp_conn *ftpc = &conn->proto.ftpc;
1339 CURLcode result = CURLE_OK;
1341 Here's the excecutive summary on what to do:
1343 PASV is RFC959, expect:
1344 227 Entering Passive Mode (a1,a2,a3,a4,p1,p2)
1346 LPSV is RFC1639, expect:
1347 228 Entering Long Passive Mode (4,4,a1,a2,a3,a4,2,p1,p2)
1349 EPSV is RFC2428, expect:
1350 229 Entering Extended Passive Mode (|||port|)
1354 static const char mode[][5] = { "EPSV", "PASV" };
1358 if(!conn->bits.ftp_use_epsv && conn->bits.ipv6)
1359 /* EPSV is disabled but we are connected to a IPv6 host, so we ignore the
1360 request and enable EPSV again! */
1361 conn->bits.ftp_use_epsv = TRUE;
1364 modeoff = conn->bits.ftp_use_epsv?0:1;
1366 PPSENDF(&ftpc->pp, "%s", mode[modeoff]);
1368 ftpc->count1 = modeoff;
1369 state(conn, FTP_PASV);
1370 infof(conn->data, "Connect data stream passively\n");
1376 * ftp_state_prepare_transfer() starts PORT, PASV or PRET etc.
1378 * REST is the last command in the chain of commands when a "head"-like
1379 * request is made. Thus, if an actual transfer is to be made this is where we
1380 * take off for real.
1382 static CURLcode ftp_state_prepare_transfer(struct connectdata *conn)
1384 CURLcode result = CURLE_OK;
1385 struct FTP *ftp = conn->data->state.proto.ftp;
1386 struct SessionHandle *data = conn->data;
1388 if(ftp->transfer != FTPTRANSFER_BODY) {
1389 /* doesn't transfer any data */
1391 /* still possibly do PRE QUOTE jobs */
1392 state(conn, FTP_RETR_PREQUOTE);
1393 result = ftp_state_quote(conn, TRUE, FTP_RETR_PREQUOTE);
1395 else if(data->set.ftp_use_port) {
1396 /* We have chosen to use the PORT (or similar) command */
1397 result = ftp_state_use_port(conn, EPRT);
1400 /* We have chosen (this is default) to use the PASV (or similar) command */
1401 if(data->set.ftp_use_pret) {
1402 /* The user has requested that we send a PRET command
1403 to prepare the server for the upcoming PASV */
1404 if(!conn->proto.ftpc.file) {
1405 PPSENDF(&conn->proto.ftpc.pp, "PRET %s",
1406 data->set.str[STRING_CUSTOMREQUEST]?
1407 data->set.str[STRING_CUSTOMREQUEST]:
1408 (data->set.ftp_list_only?"NLST":"LIST"));
1410 else if(data->set.upload) {
1411 PPSENDF(&conn->proto.ftpc.pp, "PRET STOR %s", conn->proto.ftpc.file);
1414 PPSENDF(&conn->proto.ftpc.pp, "PRET RETR %s", conn->proto.ftpc.file);
1416 state(conn, FTP_PRET);
1419 result = ftp_state_use_pasv(conn);
1425 static CURLcode ftp_state_rest(struct connectdata *conn)
1427 CURLcode result = CURLE_OK;
1428 struct FTP *ftp = conn->data->state.proto.ftp;
1429 struct ftp_conn *ftpc = &conn->proto.ftpc;
1431 if((ftp->transfer != FTPTRANSFER_BODY) && ftpc->file) {
1432 /* if a "head"-like request is being made (on a file) */
1434 /* Determine if server can respond to REST command and therefore
1435 whether it supports range */
1436 PPSENDF(&conn->proto.ftpc.pp, "REST %d", 0);
1438 state(conn, FTP_REST);
1441 result = ftp_state_prepare_transfer(conn);
1446 static CURLcode ftp_state_size(struct connectdata *conn)
1448 CURLcode result = CURLE_OK;
1449 struct FTP *ftp = conn->data->state.proto.ftp;
1450 struct ftp_conn *ftpc = &conn->proto.ftpc;
1452 if((ftp->transfer == FTPTRANSFER_INFO) && ftpc->file) {
1453 /* if a "head"-like request is being made (on a file) */
1455 /* we know ftpc->file is a valid pointer to a file name */
1456 PPSENDF(&ftpc->pp, "SIZE %s", ftpc->file);
1458 state(conn, FTP_SIZE);
1461 result = ftp_state_rest(conn);
1466 static CURLcode ftp_state_list(struct connectdata *conn)
1468 CURLcode result = CURLE_OK;
1469 struct SessionHandle *data = conn->data;
1471 /* If this output is to be machine-parsed, the NLST command might be better
1472 to use, since the LIST command output is not specified or standard in any
1473 way. It has turned out that the NLST list output is not the same on all
1474 servers either... */
1477 if FTPFILE_NOCWD was specified, we are currently in
1478 the user's home directory, so we should add the path
1479 as argument for the LIST / NLST / or custom command.
1480 Whether the server will support this, is uncertain.
1482 The other ftp_filemethods will CWD into dir/dir/ first and
1483 then just do LIST (in that case: nothing to do here)
1485 char *cmd,*lstArg,*slashPos;
1488 if((data->set.ftp_filemethod == FTPFILE_NOCWD) &&
1490 data->state.path[0] &&
1491 strchr(data->state.path,'/')) {
1493 lstArg = strdup(data->state.path);
1495 return CURLE_OUT_OF_MEMORY;
1497 /* Check if path does not end with /, as then we cut off the file part */
1498 if(lstArg[strlen(lstArg) - 1] != '/') {
1500 /* chop off the file part if format is dir/dir/file */
1501 slashPos = strrchr(lstArg,'/');
1503 *(slashPos+1) = '\0';
1507 cmd = aprintf( "%s%s%s",
1508 data->set.str[STRING_CUSTOMREQUEST]?
1509 data->set.str[STRING_CUSTOMREQUEST]:
1510 (data->set.ftp_list_only?"NLST":"LIST"),
1512 lstArg? lstArg: "" );
1517 return CURLE_OUT_OF_MEMORY;
1520 result = Curl_pp_sendf(&conn->proto.ftpc.pp, "%s", cmd);
1527 if(result != CURLE_OK)
1530 state(conn, FTP_LIST);
1535 static CURLcode ftp_state_retr_prequote(struct connectdata *conn)
1537 CURLcode result = CURLE_OK;
1539 /* We've sent the TYPE, now we must send the list of prequote strings */
1541 result = ftp_state_quote(conn, TRUE, FTP_RETR_PREQUOTE);
1546 static CURLcode ftp_state_stor_prequote(struct connectdata *conn)
1548 CURLcode result = CURLE_OK;
1550 /* We've sent the TYPE, now we must send the list of prequote strings */
1552 result = ftp_state_quote(conn, TRUE, FTP_STOR_PREQUOTE);
1557 static CURLcode ftp_state_type(struct connectdata *conn)
1559 CURLcode result = CURLE_OK;
1560 struct FTP *ftp = conn->data->state.proto.ftp;
1561 struct SessionHandle *data = conn->data;
1562 struct ftp_conn *ftpc = &conn->proto.ftpc;
1564 /* If we have selected NOBODY and HEADER, it means that we only want file
1565 information. Which in FTP can't be much more than the file size and
1567 if(data->set.opt_no_body && ftpc->file &&
1568 ftp_need_type(conn, data->set.prefer_ascii)) {
1569 /* The SIZE command is _not_ RFC 959 specified, and therefor many servers
1570 may not support it! It is however the only way we have to get a file's
1573 ftp->transfer = FTPTRANSFER_INFO;
1574 /* this means no actual transfer will be made */
1576 /* Some servers return different sizes for different modes, and thus we
1577 must set the proper type before we check the size */
1578 result = ftp_nb_type(conn, data->set.prefer_ascii, FTP_TYPE);
1583 result = ftp_state_size(conn);
1588 /* This is called after the CWD commands have been done in the beginning of
1590 static CURLcode ftp_state_mdtm(struct connectdata *conn)
1592 CURLcode result = CURLE_OK;
1593 struct SessionHandle *data = conn->data;
1594 struct ftp_conn *ftpc = &conn->proto.ftpc;
1596 /* Requested time of file or time-depended transfer? */
1597 if((data->set.get_filetime || data->set.timecondition) && ftpc->file) {
1599 /* we have requested to get the modified-time of the file, this is a white
1600 spot as the MDTM is not mentioned in RFC959 */
1601 PPSENDF(&ftpc->pp, "MDTM %s", ftpc->file);
1603 state(conn, FTP_MDTM);
1606 result = ftp_state_type(conn);
1612 /* This is called after the TYPE and possible quote commands have been sent */
1613 static CURLcode ftp_state_ul_setup(struct connectdata *conn,
1616 CURLcode result = CURLE_OK;
1617 struct FTP *ftp = conn->data->state.proto.ftp;
1618 struct SessionHandle *data = conn->data;
1619 struct ftp_conn *ftpc = &conn->proto.ftpc;
1620 int seekerr = CURL_SEEKFUNC_OK;
1622 if((data->state.resume_from && !sizechecked) ||
1623 ((data->state.resume_from > 0) && sizechecked)) {
1624 /* we're about to continue the uploading of a file */
1625 /* 1. get already existing file's size. We use the SIZE command for this
1626 which may not exist in the server! The SIZE command is not in
1629 /* 2. This used to set REST. But since we can do append, we
1630 don't another ftp command. We just skip the source file
1631 offset and then we APPEND the rest on the file instead */
1633 /* 3. pass file-size number of bytes in the source file */
1634 /* 4. lower the infilesize counter */
1635 /* => transfer as usual */
1637 if(data->state.resume_from < 0 ) {
1638 /* Got no given size to start from, figure it out */
1639 PPSENDF(&ftpc->pp, "SIZE %s", ftpc->file);
1640 state(conn, FTP_STOR_SIZE);
1645 data->set.ftp_append = TRUE;
1647 /* Let's read off the proper amount of bytes from the input. */
1648 if(conn->seek_func) {
1649 seekerr = conn->seek_func(conn->seek_client, data->state.resume_from,
1653 if(seekerr != CURL_SEEKFUNC_OK) {
1654 if(seekerr != CURL_SEEKFUNC_CANTSEEK) {
1655 failf(data, "Could not seek stream");
1656 return CURLE_FTP_COULDNT_USE_REST;
1658 /* seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */
1660 curl_off_t passed=0;
1662 size_t readthisamountnow =
1663 (data->state.resume_from - passed > CURL_OFF_T_C(BUFSIZE)) ?
1664 BUFSIZE : curlx_sotouz(data->state.resume_from - passed);
1666 size_t actuallyread =
1667 conn->fread_func(data->state.buffer, 1, readthisamountnow,
1670 passed += actuallyread;
1671 if((actuallyread == 0) || (actuallyread > readthisamountnow)) {
1672 /* this checks for greater-than only to make sure that the
1673 CURL_READFUNC_ABORT return code still aborts */
1674 failf(data, "Failed to read data");
1675 return CURLE_FTP_COULDNT_USE_REST;
1677 } while(passed < data->state.resume_from);
1680 /* now, decrease the size of the read */
1681 if(data->set.infilesize>0) {
1682 data->set.infilesize -= data->state.resume_from;
1684 if(data->set.infilesize <= 0) {
1685 infof(data, "File already completely uploaded\n");
1687 /* no data to transfer */
1688 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1690 /* Set ->transfer so that we won't get any error in
1691 * ftp_done() because we didn't transfer anything! */
1692 ftp->transfer = FTPTRANSFER_NONE;
1694 state(conn, FTP_STOP);
1698 /* we've passed, proceed as normal */
1701 PPSENDF(&ftpc->pp, data->set.ftp_append?"APPE %s":"STOR %s",
1704 state(conn, FTP_STOR);
1709 static CURLcode ftp_state_quote(struct connectdata *conn,
1713 CURLcode result = CURLE_OK;
1714 struct SessionHandle *data = conn->data;
1715 struct FTP *ftp = data->state.proto.ftp;
1716 struct ftp_conn *ftpc = &conn->proto.ftpc;
1718 struct curl_slist *item;
1723 item = data->set.quote;
1725 case FTP_RETR_PREQUOTE:
1726 case FTP_STOR_PREQUOTE:
1727 item = data->set.prequote;
1730 item = data->set.postquote;
1736 * 'count1' to iterate over the commands to send
1737 * 'count2' to store wether to allow commands to fail
1748 /* Skip count1 items in the linked list */
1749 while((i< ftpc->count1) && item) {
1754 char *cmd = item->data;
1757 ftpc->count2 = 1; /* the sent command is allowed to fail */
1760 ftpc->count2 = 0; /* failure means cancel operation */
1762 PPSENDF(&ftpc->pp, "%s", cmd);
1763 state(conn, instate);
1769 /* No more quote to send, continue to ... */
1773 result = ftp_state_cwd(conn);
1775 case FTP_RETR_PREQUOTE:
1776 if(ftp->transfer != FTPTRANSFER_BODY)
1777 state(conn, FTP_STOP);
1779 if(ftpc->known_filesize != -1) {
1780 Curl_pgrsSetDownloadSize(data, ftpc->known_filesize);
1781 result = ftp_state_retr(conn, ftpc->known_filesize);
1784 PPSENDF(&ftpc->pp, "SIZE %s", ftpc->file);
1785 state(conn, FTP_RETR_SIZE);
1789 case FTP_STOR_PREQUOTE:
1790 result = ftp_state_ul_setup(conn, FALSE);
1800 /* called from ftp_state_pasv_resp to switch to PASV in case of EPSV
1802 static CURLcode ftp_epsv_disable(struct connectdata *conn)
1804 CURLcode result = CURLE_OK;
1805 infof(conn->data, "Failed EPSV attempt. Disabling EPSV\n");
1806 /* disable it for next transfer */
1807 conn->bits.ftp_use_epsv = FALSE;
1808 conn->data->state.errorbuf = FALSE; /* allow error message to get
1810 PPSENDF(&conn->proto.ftpc.pp, "%s", "PASV");
1811 conn->proto.ftpc.count1++;
1812 /* remain in/go to the FTP_PASV state */
1813 state(conn, FTP_PASV);
1817 static CURLcode ftp_state_pasv_resp(struct connectdata *conn,
1820 struct ftp_conn *ftpc = &conn->proto.ftpc;
1822 struct SessionHandle *data=conn->data;
1823 Curl_addrinfo *conninfo;
1824 struct Curl_dns_entry *addr=NULL;
1826 unsigned short connectport; /* the local port connect() should use! */
1827 unsigned short newport=0; /* remote port */
1830 /* newhost must be able to hold a full IP-style address in ASCII, which
1831 in the IPv6 case means 5*8-1 = 39 letters */
1832 #define NEWHOST_BUFSIZE 48
1833 char newhost[NEWHOST_BUFSIZE];
1834 char *str=&data->state.buffer[4]; /* start on the first letter */
1836 if((ftpc->count1 == 0) &&
1838 /* positive EPSV response */
1839 char *ptr = strchr(str, '(');
1844 if(5 == sscanf(ptr, "%c%c%c%u%c",
1850 const char sep1 = separator[0];
1853 /* The four separators should be identical, or else this is an oddly
1854 formatted reply and we bail out immediately. */
1855 for(i=1; i<4; i++) {
1856 if(separator[i] != sep1) {
1857 ptr=NULL; /* set to NULL to signal error */
1862 failf(data, "Illegal port number in EPSV reply");
1863 return CURLE_FTP_WEIRD_PASV_REPLY;
1866 newport = (unsigned short)(num & 0xffff);
1868 if(conn->bits.tunnel_proxy ||
1869 conn->proxytype == CURLPROXY_SOCKS5 ||
1870 conn->proxytype == CURLPROXY_SOCKS5_HOSTNAME ||
1871 conn->proxytype == CURLPROXY_SOCKS4 ||
1872 conn->proxytype == CURLPROXY_SOCKS4A)
1873 /* proxy tunnel -> use other host info because ip_addr_str is the
1874 proxy address not the ftp host */
1875 snprintf(newhost, sizeof(newhost), "%s", conn->host.name);
1877 /* use the same IP we are already connected to */
1878 snprintf(newhost, NEWHOST_BUFSIZE, "%s", conn->ip_addr_str);
1885 failf(data, "Weirdly formatted EPSV reply");
1886 return CURLE_FTP_WEIRD_PASV_REPLY;
1889 else if((ftpc->count1 == 1) &&
1891 /* positive PASV response */
1896 * Scan for a sequence of six comma-separated numbers and use them as
1897 * IP+port indicators.
1899 * Found reply-strings include:
1900 * "227 Entering Passive Mode (127,0,0,1,4,51)"
1901 * "227 Data transfer will passively listen to 127,0,0,1,4,51"
1902 * "227 Entering passive mode. 127,0,0,1,4,51"
1905 if(6 == sscanf(str, "%d,%d,%d,%d,%d,%d",
1906 &ip[0], &ip[1], &ip[2], &ip[3],
1907 &port[0], &port[1]))
1913 failf(data, "Couldn't interpret the 227-response");
1914 return CURLE_FTP_WEIRD_227_FORMAT;
1917 /* we got OK from server */
1918 if(data->set.ftp_skip_ip) {
1919 /* told to ignore the remotely given IP but instead use the one we used
1920 for the control connection */
1921 infof(data, "Skips %d.%d.%d.%d for data connection, uses %s instead\n",
1922 ip[0], ip[1], ip[2], ip[3],
1924 if(conn->bits.tunnel_proxy ||
1925 conn->proxytype == CURLPROXY_SOCKS5 ||
1926 conn->proxytype == CURLPROXY_SOCKS5_HOSTNAME ||
1927 conn->proxytype == CURLPROXY_SOCKS4 ||
1928 conn->proxytype == CURLPROXY_SOCKS4A)
1929 /* proxy tunnel -> use other host info because ip_addr_str is the
1930 proxy address not the ftp host */
1931 snprintf(newhost, sizeof(newhost), "%s", conn->host.name);
1933 snprintf(newhost, sizeof(newhost), "%s", conn->ip_addr_str);
1936 snprintf(newhost, sizeof(newhost),
1937 "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
1938 newport = (unsigned short)(((port[0]<<8) + port[1]) & 0xffff);
1940 else if(ftpc->count1 == 0) {
1941 /* EPSV failed, move on to PASV */
1942 return ftp_epsv_disable(conn);
1945 failf(data, "Bad PASV/EPSV response: %03d", ftpcode);
1946 return CURLE_FTP_WEIRD_PASV_REPLY;
1949 if(conn->bits.proxy) {
1951 * This connection uses a proxy and we need to connect to the proxy again
1952 * here. We don't want to rely on a former host lookup that might've
1953 * expired now, instead we remake the lookup here and now!
1955 rc = Curl_resolv(conn, conn->proxy.name, (int)conn->port, &addr);
1956 if(rc == CURLRESOLV_PENDING)
1957 /* BLOCKING, ignores the return code but 'addr' will be NULL in
1959 (void)Curl_resolver_wait_resolv(conn, &addr);
1962 (unsigned short)conn->port; /* we connect to the proxy's port */
1965 failf(data, "Can't resolve proxy host %s:%hu",
1966 conn->proxy.name, connectport);
1967 return CURLE_FTP_CANT_GET_HOST;
1971 /* normal, direct, ftp connection */
1972 rc = Curl_resolv(conn, newhost, newport, &addr);
1973 if(rc == CURLRESOLV_PENDING)
1975 (void)Curl_resolver_wait_resolv(conn, &addr);
1977 connectport = newport; /* we connect to the remote port */
1980 failf(data, "Can't resolve new host %s:%hu", newhost, connectport);
1981 return CURLE_FTP_CANT_GET_HOST;
1985 result = Curl_connecthost(conn,
1987 &conn->sock[SECONDARYSOCKET],
1991 Curl_resolv_unlock(data, addr); /* we're done using this address */
1994 if(ftpc->count1 == 0 && ftpcode == 229)
1995 return ftp_epsv_disable(conn);
2000 conn->bits.tcpconnect[SECONDARYSOCKET] = connected;
2003 * When this is used from the multi interface, this might've returned with
2004 * the 'connected' set to FALSE and thus we are now awaiting a non-blocking
2005 * connect to connect and we should not be "hanging" here waiting.
2008 if(data->set.verbose)
2009 /* this just dumps information about this second connection */
2010 ftp_pasv_verbose(conn, conninfo, newhost, connectport);
2012 switch(conn->proxytype) {
2013 /* FIX: this MUST wait for a proper connect first if 'connected' is
2015 case CURLPROXY_SOCKS5:
2016 case CURLPROXY_SOCKS5_HOSTNAME:
2017 result = Curl_SOCKS5(conn->proxyuser, conn->proxypasswd, newhost, newport,
2018 SECONDARYSOCKET, conn);
2021 case CURLPROXY_SOCKS4:
2022 result = Curl_SOCKS4(conn->proxyuser, newhost, newport,
2023 SECONDARYSOCKET, conn, FALSE);
2026 case CURLPROXY_SOCKS4A:
2027 result = Curl_SOCKS4(conn->proxyuser, newhost, newport,
2028 SECONDARYSOCKET, conn, TRUE);
2031 case CURLPROXY_HTTP:
2032 case CURLPROXY_HTTP_1_0:
2033 /* do nothing here. handled later. */
2036 failf(data, "unknown proxytype option given");
2037 result = CURLE_COULDNT_CONNECT;
2042 if(ftpc->count1 == 0 && ftpcode == 229)
2043 return ftp_epsv_disable(conn);
2047 if(conn->bits.tunnel_proxy && conn->bits.httpproxy) {
2048 /* FIX: this MUST wait for a proper connect first if 'connected' is
2052 /* We want "seamless" FTP operations through HTTP proxy tunnel */
2054 /* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the member
2055 * conn->proto.http; we want FTP through HTTP and we have to change the
2056 * member temporarily for connecting to the HTTP proxy. After
2057 * Curl_proxyCONNECT we have to set back the member to the original struct
2060 struct HTTP http_proxy;
2061 struct FTP *ftp_save = data->state.proto.ftp;
2062 memset(&http_proxy, 0, sizeof(http_proxy));
2063 data->state.proto.http = &http_proxy;
2065 result = Curl_proxyCONNECT(conn, SECONDARYSOCKET, newhost, newport);
2067 data->state.proto.ftp = ftp_save;
2072 if(conn->tunnel_state[SECONDARYSOCKET] != TUNNEL_COMPLETE) {
2073 /* the CONNECT procedure is not complete, the tunnel is not yet up */
2074 state(conn, FTP_STOP); /* this phase is completed */
2075 conn->bits.tcpconnect[SECONDARYSOCKET] = FALSE;
2081 conn->bits.tcpconnect[SECONDARYSOCKET] = connected;
2082 conn->bits.do_more = TRUE;
2083 state(conn, FTP_STOP); /* this phase is completed */
2088 static CURLcode ftp_state_port_resp(struct connectdata *conn,
2091 struct SessionHandle *data = conn->data;
2092 struct ftp_conn *ftpc = &conn->proto.ftpc;
2093 ftpport fcmd = (ftpport)ftpc->count1;
2094 CURLcode result = CURLE_OK;
2096 if(ftpcode != 200) {
2097 /* the command failed */
2100 infof(data, "disabling EPRT usage\n");
2101 conn->bits.ftp_use_eprt = FALSE;
2106 failf(data, "Failed to do PORT");
2107 result = CURLE_FTP_PORT_FAILED;
2111 result = ftp_state_use_port(conn, fcmd);
2114 infof(data, "Connect data stream actively\n");
2115 state(conn, FTP_STOP); /* end of DO phase */
2116 result = ftp_dophase_done(conn, FALSE);
2122 static CURLcode ftp_state_mdtm_resp(struct connectdata *conn,
2125 CURLcode result = CURLE_OK;
2126 struct SessionHandle *data=conn->data;
2127 struct FTP *ftp = data->state.proto.ftp;
2128 struct ftp_conn *ftpc = &conn->proto.ftpc;
2133 /* we got a time. Format should be: "YYYYMMDDHHMMSS[.sss]" where the
2134 last .sss part is optional and means fractions of a second */
2135 int year, month, day, hour, minute, second;
2136 char *buf = data->state.buffer;
2137 if(6 == sscanf(buf+4, "%04d%02d%02d%02d%02d%02d",
2138 &year, &month, &day, &hour, &minute, &second)) {
2139 /* we have a time, reformat it */
2140 time_t secs=time(NULL);
2141 /* using the good old yacc/bison yuck */
2142 snprintf(buf, sizeof(conn->data->state.buffer),
2143 "%04d%02d%02d %02d:%02d:%02d GMT",
2144 year, month, day, hour, minute, second);
2145 /* now, convert this into a time() value: */
2146 data->info.filetime = (long)curl_getdate(buf, &secs);
2149 #ifdef CURL_FTP_HTTPSTYLE_HEAD
2150 /* If we asked for a time of the file and we actually got one as well,
2151 we "emulate" a HTTP-style header in our output. */
2153 if(data->set.opt_no_body &&
2155 data->set.get_filetime &&
2156 (data->info.filetime>=0) ) {
2157 time_t filetime = (time_t)data->info.filetime;
2159 const struct tm *tm = &buffer;
2161 result = Curl_gmtime(filetime, &buffer);
2165 /* format: "Tue, 15 Nov 1994 12:45:26" */
2166 snprintf(buf, BUFSIZE-1,
2167 "Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n",
2168 Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
2170 Curl_month[tm->tm_mon],
2175 result = Curl_client_write(conn, CLIENTWRITE_BOTH, buf, 0);
2178 } /* end of a ridiculous amount of conditionals */
2183 infof(data, "unsupported MDTM reply format\n");
2185 case 550: /* "No such file or directory" */
2186 failf(data, "Given file does not exist");
2187 result = CURLE_FTP_COULDNT_RETR_FILE;
2191 if(data->set.timecondition) {
2192 if((data->info.filetime > 0) && (data->set.timevalue > 0)) {
2193 switch(data->set.timecondition) {
2194 case CURL_TIMECOND_IFMODSINCE:
2196 if(data->info.filetime <= data->set.timevalue) {
2197 infof(data, "The requested document is not new enough\n");
2198 ftp->transfer = FTPTRANSFER_NONE; /* mark to not transfer data */
2199 data->info.timecond = TRUE;
2200 state(conn, FTP_STOP);
2204 case CURL_TIMECOND_IFUNMODSINCE:
2205 if(data->info.filetime > data->set.timevalue) {
2206 infof(data, "The requested document is not old enough\n");
2207 ftp->transfer = FTPTRANSFER_NONE; /* mark to not transfer data */
2208 data->info.timecond = TRUE;
2209 state(conn, FTP_STOP);
2216 infof(data, "Skipping time comparison\n");
2221 result = ftp_state_type(conn);
2226 static CURLcode ftp_state_type_resp(struct connectdata *conn,
2230 CURLcode result = CURLE_OK;
2231 struct SessionHandle *data=conn->data;
2233 if(ftpcode/100 != 2) {
2234 /* "sasserftpd" and "(u)r(x)bot ftpd" both responds with 226 after a
2235 successful 'TYPE I'. While that is not as RFC959 says, it is still a
2236 positive response code and we allow that. */
2237 failf(data, "Couldn't set desired mode");
2238 return CURLE_FTP_COULDNT_SET_TYPE;
2241 infof(data, "Got a %03d response code instead of the assumed 200\n",
2244 if(instate == FTP_TYPE)
2245 result = ftp_state_size(conn);
2246 else if(instate == FTP_LIST_TYPE)
2247 result = ftp_state_list(conn);
2248 else if(instate == FTP_RETR_TYPE)
2249 result = ftp_state_retr_prequote(conn);
2250 else if(instate == FTP_STOR_TYPE)
2251 result = ftp_state_stor_prequote(conn);
2256 static CURLcode ftp_state_retr(struct connectdata *conn,
2257 curl_off_t filesize)
2259 CURLcode result = CURLE_OK;
2260 struct SessionHandle *data=conn->data;
2261 struct FTP *ftp = data->state.proto.ftp;
2262 struct ftp_conn *ftpc = &conn->proto.ftpc;
2264 if(data->set.max_filesize && (filesize > data->set.max_filesize)) {
2265 failf(data, "Maximum file size exceeded");
2266 return CURLE_FILESIZE_EXCEEDED;
2268 ftp->downloadsize = filesize;
2270 if(data->state.resume_from) {
2271 /* We always (attempt to) get the size of downloads, so it is done before
2272 this even when not doing resumes. */
2273 if(filesize == -1) {
2274 infof(data, "ftp server doesn't support SIZE\n");
2275 /* We couldn't get the size and therefore we can't know if there really
2276 is a part of the file left to get, although the server will just
2277 close the connection when we start the connection so it won't cause
2278 us any harm, just not make us exit as nicely. */
2281 /* We got a file size report, so we check that there actually is a
2282 part of the file left to get, or else we go home. */
2283 if(data->state.resume_from< 0) {
2284 /* We're supposed to download the last abs(from) bytes */
2285 if(filesize < -data->state.resume_from) {
2286 failf(data, "Offset (%" FORMAT_OFF_T
2287 ") was beyond file size (%" FORMAT_OFF_T ")",
2288 data->state.resume_from, filesize);
2289 return CURLE_BAD_DOWNLOAD_RESUME;
2291 /* convert to size to download */
2292 ftp->downloadsize = -data->state.resume_from;
2293 /* download from where? */
2294 data->state.resume_from = filesize - ftp->downloadsize;
2297 if(filesize < data->state.resume_from) {
2298 failf(data, "Offset (%" FORMAT_OFF_T
2299 ") was beyond file size (%" FORMAT_OFF_T ")",
2300 data->state.resume_from, filesize);
2301 return CURLE_BAD_DOWNLOAD_RESUME;
2303 /* Now store the number of bytes we are expected to download */
2304 ftp->downloadsize = filesize-data->state.resume_from;
2308 if(ftp->downloadsize == 0) {
2309 /* no data to transfer */
2310 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
2311 infof(data, "File already completely downloaded\n");
2313 /* Set ->transfer so that we won't get any error in ftp_done()
2314 * because we didn't transfer the any file */
2315 ftp->transfer = FTPTRANSFER_NONE;
2316 state(conn, FTP_STOP);
2320 /* Set resume file transfer offset */
2321 infof(data, "Instructs server to resume from offset %" FORMAT_OFF_T
2322 "\n", data->state.resume_from);
2324 PPSENDF(&ftpc->pp, "REST %" FORMAT_OFF_T, data->state.resume_from);
2326 state(conn, FTP_RETR_REST);
2331 PPSENDF(&ftpc->pp, "RETR %s", ftpc->file);
2332 state(conn, FTP_RETR);
2338 static CURLcode ftp_state_size_resp(struct connectdata *conn,
2342 CURLcode result = CURLE_OK;
2343 struct SessionHandle *data=conn->data;
2344 curl_off_t filesize;
2345 char *buf = data->state.buffer;
2347 /* get the size from the ascii string: */
2348 filesize = (ftpcode == 213)?curlx_strtoofft(buf+4, NULL, 0):-1;
2350 if(instate == FTP_SIZE) {
2351 #ifdef CURL_FTP_HTTPSTYLE_HEAD
2352 if(-1 != filesize) {
2353 snprintf(buf, sizeof(data->state.buffer),
2354 "Content-Length: %" FORMAT_OFF_T "\r\n", filesize);
2355 result = Curl_client_write(conn, CLIENTWRITE_BOTH, buf, 0);
2360 Curl_pgrsSetDownloadSize(data, filesize);
2361 result = ftp_state_rest(conn);
2363 else if(instate == FTP_RETR_SIZE) {
2364 Curl_pgrsSetDownloadSize(data, filesize);
2365 result = ftp_state_retr(conn, filesize);
2367 else if(instate == FTP_STOR_SIZE) {
2368 data->state.resume_from = filesize;
2369 result = ftp_state_ul_setup(conn, TRUE);
2375 static CURLcode ftp_state_rest_resp(struct connectdata *conn,
2379 CURLcode result = CURLE_OK;
2380 struct ftp_conn *ftpc = &conn->proto.ftpc;
2385 #ifdef CURL_FTP_HTTPSTYLE_HEAD
2386 if(ftpcode == 350) {
2387 char buffer[24]= { "Accept-ranges: bytes\r\n" };
2388 result = Curl_client_write(conn, CLIENTWRITE_BOTH, buffer, 0);
2393 result = ftp_state_prepare_transfer(conn);
2397 if(ftpcode != 350) {
2398 failf(conn->data, "Couldn't use REST");
2399 result = CURLE_FTP_COULDNT_USE_REST;
2402 PPSENDF(&ftpc->pp, "RETR %s", ftpc->file);
2403 state(conn, FTP_RETR);
2411 static CURLcode ftp_state_stor_resp(struct connectdata *conn,
2412 int ftpcode, ftpstate instate)
2414 CURLcode result = CURLE_OK;
2415 struct SessionHandle *data = conn->data;
2418 failf(data, "Failed FTP upload: %0d", ftpcode);
2419 state(conn, FTP_STOP);
2420 /* oops, we never close the sockets! */
2421 return CURLE_UPLOAD_FAILED;
2424 conn->proto.ftpc.state_saved = instate;
2426 /* PORT means we are now awaiting the server to connect to us. */
2427 if(data->set.ftp_use_port) {
2430 result = AllowServerConnect(conn, &connected);
2435 struct ftp_conn *ftpc = &conn->proto.ftpc;
2436 infof(data, "Data conn was not available immediately\n");
2437 ftpc->wait_data_conn = TRUE;
2443 return InitiateTransfer(conn);
2446 /* for LIST and RETR responses */
2447 static CURLcode ftp_state_get_resp(struct connectdata *conn,
2451 CURLcode result = CURLE_OK;
2452 struct SessionHandle *data = conn->data;
2453 struct FTP *ftp = data->state.proto.ftp;
2454 char *buf = data->state.buffer;
2456 if((ftpcode == 150) || (ftpcode == 125)) {
2460 150 Opening BINARY mode data connection for /etc/passwd (2241
2461 bytes). (ok, the file is being transferred)
2464 150 Opening ASCII mode data connection for /bin/ls
2467 150 ASCII data connection for /bin/ls (137.167.104.91,37445) (0 bytes).
2470 150 Opening ASCII mode data connection for [file] (0.0.0.0,0) (545 bytes)
2473 125 Data connection already open; Transfer starting. */
2475 curl_off_t size=-1; /* default unknown size */
2479 * It appears that there are FTP-servers that return size 0 for files when
2480 * SIZE is used on the file while being in BINARY mode. To work around
2481 * that (stupid) behavior, we attempt to parse the RETR response even if
2482 * the SIZE returned size zero.
2484 * Debugging help from Salvatore Sorrentino on February 26, 2003.
2487 if((instate != FTP_LIST) &&
2488 !data->set.prefer_ascii &&
2489 (ftp->downloadsize < 1)) {
2491 * It seems directory listings either don't show the size or very
2492 * often uses size 0 anyway. ASCII transfers may very well turn out
2493 * that the transferred amount of data is not the same as this line
2494 * tells, why using this number in those cases only confuses us.
2496 * Example D above makes this parsing a little tricky */
2498 bytes=strstr(buf, " bytes");
2500 long in=(long)(bytes-buf);
2501 /* this is a hint there is size information in there! ;-) */
2503 /* scan for the left parenthesis and break there */
2506 /* skip only digits */
2507 if(!ISDIGIT(*bytes)) {
2511 /* one more estep backwards */
2514 /* if we have nothing but digits: */
2516 /* get the number! */
2517 size = curlx_strtoofft(bytes, NULL, 0);
2521 else if(ftp->downloadsize > -1)
2522 size = ftp->downloadsize;
2524 if(size > data->req.maxdownload && data->req.maxdownload > 0)
2525 size = data->req.size = data->req.maxdownload;
2526 else if((instate != FTP_LIST) && (data->set.prefer_ascii))
2527 size = -1; /* kludge for servers that understate ASCII mode file size */
2529 infof(data, "Maxdownload = %" FORMAT_OFF_T "\n", data->req.maxdownload);
2531 if(instate != FTP_LIST)
2532 infof(data, "Getting file with size: %" FORMAT_OFF_T "\n", size);
2535 conn->proto.ftpc.state_saved = instate;
2536 conn->proto.ftpc.retr_size_saved = size;
2538 if(data->set.ftp_use_port) {
2541 result = AllowServerConnect(conn, &connected);
2546 struct ftp_conn *ftpc = &conn->proto.ftpc;
2547 infof(data, "Data conn was not available immediately\n");
2548 state(conn, FTP_STOP);
2549 ftpc->wait_data_conn = TRUE;
2553 return InitiateTransfer(conn);
2556 if((instate == FTP_LIST) && (ftpcode == 450)) {
2557 /* simply no matching files in the dir listing */
2558 ftp->transfer = FTPTRANSFER_NONE; /* don't download anything */
2559 state(conn, FTP_STOP); /* this phase is over */
2562 failf(data, "RETR response: %03d", ftpcode);
2563 return instate == FTP_RETR && ftpcode == 550?
2564 CURLE_REMOTE_FILE_NOT_FOUND:
2565 CURLE_FTP_COULDNT_RETR_FILE;
2572 /* after USER, PASS and ACCT */
2573 static CURLcode ftp_state_loggedin(struct connectdata *conn)
2575 CURLcode result = CURLE_OK;
2578 if(conn->data->set.krb) {
2579 /* We may need to issue a KAUTH here to have access to the files
2580 * do it if user supplied a password
2582 if(conn->passwd && *conn->passwd) {
2584 result = Curl_krb_kauth(conn);
2590 if(conn->ssl[FIRSTSOCKET].use) {
2591 /* PBSZ = PROTECTION BUFFER SIZE.
2593 The 'draft-murray-auth-ftp-ssl' (draft 12, page 7) says:
2595 Specifically, the PROT command MUST be preceded by a PBSZ
2596 command and a PBSZ command MUST be preceded by a successful
2597 security data exchange (the TLS negotiation in this case)
2599 ... (and on page 8):
2601 Thus the PBSZ command must still be issued, but must have a
2602 parameter of '0' to indicate that no buffering is taking place
2603 and the data connection should not be encapsulated.
2605 PPSENDF(&conn->proto.ftpc.pp, "PBSZ %d", 0);
2606 state(conn, FTP_PBSZ);
2609 result = ftp_state_pwd(conn);
2614 /* for USER and PASS responses */
2615 static CURLcode ftp_state_user_resp(struct connectdata *conn,
2619 CURLcode result = CURLE_OK;
2620 struct SessionHandle *data = conn->data;
2621 struct FTP *ftp = data->state.proto.ftp;
2622 struct ftp_conn *ftpc = &conn->proto.ftpc;
2623 (void)instate; /* no use for this yet */
2625 /* some need password anyway, and others just return 2xx ignored */
2626 if((ftpcode == 331) && (ftpc->state == FTP_USER)) {
2627 /* 331 Password required for ...
2628 (the server requires to send the user's password too) */
2629 PPSENDF(&ftpc->pp, "PASS %s", ftp->passwd?ftp->passwd:"");
2630 state(conn, FTP_PASS);
2632 else if(ftpcode/100 == 2) {
2633 /* 230 User ... logged in.
2634 (the user logged in with or without password) */
2635 result = ftp_state_loggedin(conn);
2637 else if(ftpcode == 332) {
2638 if(data->set.str[STRING_FTP_ACCOUNT]) {
2639 PPSENDF(&ftpc->pp, "ACCT %s", data->set.str[STRING_FTP_ACCOUNT]);
2640 state(conn, FTP_ACCT);
2643 failf(data, "ACCT requested but none available");
2644 result = CURLE_LOGIN_DENIED;
2648 /* All other response codes, like:
2650 530 User ... access denied
2651 (the server denies to log the specified user) */
2653 if(conn->data->set.str[STRING_FTP_ALTERNATIVE_TO_USER] &&
2654 !conn->data->state.ftp_trying_alternative) {
2655 /* Ok, USER failed. Let's try the supplied command. */
2656 PPSENDF(&conn->proto.ftpc.pp, "%s",
2657 conn->data->set.str[STRING_FTP_ALTERNATIVE_TO_USER]);
2658 conn->data->state.ftp_trying_alternative = TRUE;
2659 state(conn, FTP_USER);
2663 failf(data, "Access denied: %03d", ftpcode);
2664 result = CURLE_LOGIN_DENIED;
2670 /* for ACCT response */
2671 static CURLcode ftp_state_acct_resp(struct connectdata *conn,
2674 CURLcode result = CURLE_OK;
2675 struct SessionHandle *data = conn->data;
2676 if(ftpcode != 230) {
2677 failf(data, "ACCT rejected by server: %03d", ftpcode);
2678 result = CURLE_FTP_WEIRD_PASS_REPLY; /* FIX */
2681 result = ftp_state_loggedin(conn);
2687 static CURLcode ftp_statemach_act(struct connectdata *conn)
2690 curl_socket_t sock = conn->sock[FIRSTSOCKET];
2691 struct SessionHandle *data=conn->data;
2693 struct ftp_conn *ftpc = &conn->proto.ftpc;
2694 struct pingpong *pp = &ftpc->pp;
2695 static const char ftpauth[][4] = { "SSL", "TLS" };
2699 return Curl_pp_flushsend(pp);
2701 result = ftp_readresp(sock, pp, &ftpcode, &nread);
2706 /* we have now received a full FTP server response */
2707 switch(ftpc->state) {
2710 /* 230 User logged in - already! */
2711 return ftp_state_user_resp(conn, ftpcode, ftpc->state);
2712 else if(ftpcode != 220) {
2713 failf(data, "Got a %03d ftp-server response when 220 was expected",
2715 return CURLE_FTP_WEIRD_SERVER_REPLY;
2718 /* We have received a 220 response fine, now we proceed. */
2719 #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
2721 /* If not anonymous login, try a secure login. Note that this
2722 procedure is still BLOCKING. */
2724 Curl_sec_request_prot(conn, "private");
2725 /* We set private first as default, in case the line below fails to
2726 set a valid level */
2727 Curl_sec_request_prot(conn, data->set.str[STRING_KRB_LEVEL]);
2729 if(Curl_sec_login(conn) != CURLE_OK)
2730 infof(data, "Logging in with password in cleartext!\n");
2732 infof(data, "Authentication successful\n");
2736 if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
2737 /* We don't have a SSL/TLS connection yet, but FTPS is
2738 requested. Try a FTPS connection now */
2741 switch(data->set.ftpsslauth) {
2742 case CURLFTPAUTH_DEFAULT:
2743 case CURLFTPAUTH_SSL:
2744 ftpc->count2 = 1; /* add one to get next */
2747 case CURLFTPAUTH_TLS:
2748 ftpc->count2 = -1; /* subtract one to get next */
2752 failf(data, "unsupported parameter to CURLOPT_FTPSSLAUTH: %d",
2753 (int)data->set.ftpsslauth);
2754 return CURLE_UNKNOWN_OPTION; /* we don't know what to do */
2756 PPSENDF(&ftpc->pp, "AUTH %s", ftpauth[ftpc->count1]);
2757 state(conn, FTP_AUTH);
2760 result = ftp_state_user(conn);
2768 /* we have gotten the response to a previous AUTH command */
2770 /* RFC2228 (page 5) says:
2772 * If the server is willing to accept the named security mechanism,
2773 * and does not require any security data, it must respond with
2774 * reply code 234/334.
2777 if((ftpcode == 234) || (ftpcode == 334)) {
2778 /* Curl_ssl_connect is BLOCKING */
2779 result = Curl_ssl_connect(conn, FIRSTSOCKET);
2780 if(CURLE_OK == result) {
2781 conn->ssl[SECONDARYSOCKET].use = FALSE; /* clear-text data */
2782 result = ftp_state_user(conn);
2785 else if(ftpc->count3 < 1) {
2787 ftpc->count1 += ftpc->count2; /* get next attempt */
2788 result = Curl_pp_sendf(&ftpc->pp, "AUTH %s", ftpauth[ftpc->count1]);
2789 /* remain in this same state */
2792 if(data->set.use_ssl > CURLUSESSL_TRY)
2793 /* we failed and CURLUSESSL_CONTROL or CURLUSESSL_ALL is set */
2794 result = CURLE_USE_SSL_FAILED;
2796 /* ignore the failure and continue */
2797 result = ftp_state_user(conn);
2806 result = ftp_state_user_resp(conn, ftpcode, ftpc->state);
2810 result = ftp_state_acct_resp(conn, ftpcode);
2814 PPSENDF(&ftpc->pp, "PROT %c",
2815 data->set.use_ssl == CURLUSESSL_CONTROL ? 'C' : 'P');
2816 state(conn, FTP_PROT);
2821 if(ftpcode/100 == 2)
2822 /* We have enabled SSL for the data connection! */
2823 conn->ssl[SECONDARYSOCKET].use =
2824 (data->set.use_ssl != CURLUSESSL_CONTROL) ? TRUE : FALSE;
2825 /* FTP servers typically responds with 500 if they decide to reject
2827 else if(data->set.use_ssl > CURLUSESSL_CONTROL)
2828 /* we failed and bails out */
2829 return CURLE_USE_SSL_FAILED;
2831 if(data->set.ftp_ccc) {
2832 /* CCC - Clear Command Channel
2834 PPSENDF(&ftpc->pp, "%s", "CCC");
2835 state(conn, FTP_CCC);
2838 result = ftp_state_pwd(conn);
2846 /* First shut down the SSL layer (note: this call will block) */
2847 result = Curl_ssl_shutdown(conn, FIRSTSOCKET);
2850 failf(conn->data, "Failed to clear the command channel (CCC)");
2855 /* Then continue as normal */
2856 result = ftp_state_pwd(conn);
2862 if(ftpcode == 257) {
2863 char *ptr=&data->state.buffer[4]; /* start on the first letter */
2867 dir = malloc(nread + 1);
2869 return CURLE_OUT_OF_MEMORY;
2871 /* Reply format is like
2872 257<space>[rubbish]"<directory-name>"<space><commentary> and the
2875 The directory name can contain any character; embedded
2876 double-quotes should be escaped by double-quotes (the
2877 "quote-doubling" convention).
2880 /* scan for the first double-quote for non-standard responses */
2881 while(ptr < &data->state.buffer[sizeof(data->state.buffer)]
2882 && *ptr != '\n' && *ptr != '\0' && *ptr != '"')
2886 /* it started good */
2888 for(store = dir; *ptr;) {
2890 if('\"' == ptr[1]) {
2891 /* "quote-doubling" */
2897 *store = '\0'; /* zero terminate */
2898 break; /* get out of this loop */
2907 /* If the path name does not look like an absolute path (i.e.: it
2908 does not start with a '/'), we probably need some server-dependent
2909 adjustments. For example, this is the case when connecting to
2910 an OS400 FTP server: this server supports two name syntaxes,
2911 the default one being incompatible with standard pathes. In
2912 addition, this server switches automatically to the regular path
2913 syntax when one is encountered in a command: this results in
2914 having an entrypath in the wrong syntax when later used in CWD.
2915 The method used here is to check the server OS: we do it only
2916 if the path name looks strange to minimize overhead on other
2919 if(!ftpc->server_os && dir[0] != '/') {
2921 result = Curl_pp_sendf(&ftpc->pp, "%s", "SYST");
2922 if(result != CURLE_OK) {
2926 Curl_safefree(ftpc->entrypath);
2927 ftpc->entrypath = dir; /* remember this */
2928 infof(data, "Entry path is '%s'\n", ftpc->entrypath);
2929 /* also save it where getinfo can access it: */
2930 data->state.most_recent_ftp_entrypath = ftpc->entrypath;
2931 state(conn, FTP_SYST);
2935 Curl_safefree(ftpc->entrypath);
2936 ftpc->entrypath = dir; /* remember this */
2937 infof(data, "Entry path is '%s'\n", ftpc->entrypath);
2938 /* also save it where getinfo can access it: */
2939 data->state.most_recent_ftp_entrypath = ftpc->entrypath;
2942 /* couldn't get the path */
2944 infof(data, "Failed to figure out path\n");
2947 state(conn, FTP_STOP); /* we are done with the CONNECT phase! */
2948 DEBUGF(infof(data, "protocol connect phase DONE\n"));
2952 if(ftpcode == 215) {
2953 char *ptr=&data->state.buffer[4]; /* start on the first letter */
2957 os = malloc(nread + 1);
2959 return CURLE_OUT_OF_MEMORY;
2961 /* Reply format is like
2962 215<space><OS-name><space><commentary>
2966 for(store = os; *ptr && *ptr != ' ';)
2968 *store = '\0'; /* zero terminate */
2970 /* Check for special servers here. */
2972 if(strequal(os, "OS/400")) {
2973 /* Force OS400 name format 1. */
2974 result = Curl_pp_sendf(&ftpc->pp, "%s", "SITE NAMEFMT 1");
2975 if(result != CURLE_OK) {
2979 /* remember target server OS */
2980 Curl_safefree(ftpc->server_os);
2981 ftpc->server_os = os;
2982 state(conn, FTP_NAMEFMT);
2986 /* Nothing special for the target server. */
2987 /* remember target server OS */
2988 Curl_safefree(ftpc->server_os);
2989 ftpc->server_os = os;
2993 /* Cannot identify server OS. Continue anyway and cross fingers. */
2996 state(conn, FTP_STOP); /* we are done with the CONNECT phase! */
2997 DEBUGF(infof(data, "protocol connect phase DONE\n"));
3001 if(ftpcode == 250) {
3002 /* Name format change successful: reload initial path. */
3003 ftp_state_pwd(conn);
3007 state(conn, FTP_STOP); /* we are done with the CONNECT phase! */
3008 DEBUGF(infof(data, "protocol connect phase DONE\n"));
3013 case FTP_RETR_PREQUOTE:
3014 case FTP_STOR_PREQUOTE:
3015 if((ftpcode >= 400) && !ftpc->count2) {
3016 /* failure response code, and not allowed to fail */
3017 failf(conn->data, "QUOT command failed with %03d", ftpcode);
3018 return CURLE_QUOTE_ERROR;
3020 result = ftp_state_quote(conn, FALSE, ftpc->state);
3027 if(ftpcode/100 != 2) {
3028 /* failure to CWD there */
3029 if(conn->data->set.ftp_create_missing_dirs &&
3030 ftpc->count1 && !ftpc->count2) {
3032 ftpc->count2++; /* counter to prevent CWD-MKD loops */
3033 PPSENDF(&ftpc->pp, "MKD %s", ftpc->dirs[ftpc->count1 - 1]);
3034 state(conn, FTP_MKD);
3037 /* return failure */
3038 failf(data, "Server denied you to change to the given directory");
3039 ftpc->cwdfail = TRUE; /* don't remember this path as we failed
3041 return CURLE_REMOTE_ACCESS_DENIED;
3047 if(++ftpc->count1 <= ftpc->dirdepth) {
3049 PPSENDF(&ftpc->pp, "CWD %s", ftpc->dirs[ftpc->count1 - 1]);
3052 result = ftp_state_mdtm(conn);
3060 if((ftpcode/100 != 2) && !ftpc->count3--) {
3061 /* failure to MKD the dir */
3062 failf(data, "Failed to MKD dir: %03d", ftpcode);
3063 return CURLE_REMOTE_ACCESS_DENIED;
3065 state(conn, FTP_CWD);
3067 PPSENDF(&ftpc->pp, "CWD %s", ftpc->dirs[ftpc->count1 - 1]);
3071 result = ftp_state_mdtm_resp(conn, ftpcode);
3078 result = ftp_state_type_resp(conn, ftpcode, ftpc->state);
3084 result = ftp_state_size_resp(conn, ftpcode, ftpc->state);
3089 result = ftp_state_rest_resp(conn, ftpcode, ftpc->state);
3093 if(ftpcode != 200) {
3094 /* there only is this one standard OK return code. */
3095 failf(data, "PRET command not accepted: %03d", ftpcode);
3096 return CURLE_FTP_PRET_FAILED;
3098 result = ftp_state_use_pasv(conn);
3102 result = ftp_state_pasv_resp(conn, ftpcode);
3106 result = ftp_state_port_resp(conn, ftpcode);
3111 result = ftp_state_get_resp(conn, ftpcode, ftpc->state);
3115 result = ftp_state_stor_resp(conn, ftpcode, ftpc->state);
3119 /* fallthrough, just stop! */
3121 /* internal error */
3122 state(conn, FTP_STOP);
3131 /* called repeatedly until done from multi.c */
3132 static CURLcode ftp_multi_statemach(struct connectdata *conn,
3135 struct ftp_conn *ftpc = &conn->proto.ftpc;
3136 CURLcode result = Curl_pp_statemach(&ftpc->pp, FALSE);
3138 /* Check for the state outside of the Curl_socket_ready() return code checks
3139 since at times we are in fact already in this state when this function
3141 *done = (ftpc->state == FTP_STOP) ? TRUE : FALSE;
3146 static CURLcode ftp_block_statemach(struct connectdata *conn)
3148 struct ftp_conn *ftpc = &conn->proto.ftpc;
3149 struct pingpong *pp = &ftpc->pp;
3150 CURLcode result = CURLE_OK;
3152 while(ftpc->state != FTP_STOP) {
3153 result = Curl_pp_statemach(pp, TRUE);
3162 * Allocate and initialize the struct FTP for the current SessionHandle. If
3166 #if defined(__INTEL_COMPILER) && (__INTEL_COMPILER == 910) && \
3167 defined(__OPTIMIZE__) && defined(__unix__) && defined(__i386__)
3168 /* workaround icc 9.1 optimizer issue */
3169 #pragma optimize("", off)
3172 static CURLcode ftp_init(struct connectdata *conn)
3176 if(NULL == conn->data->state.proto.ftp) {
3177 conn->data->state.proto.ftp = malloc(sizeof(struct FTP));
3178 if(NULL == conn->data->state.proto.ftp)
3179 return CURLE_OUT_OF_MEMORY;
3182 ftp = conn->data->state.proto.ftp;
3184 /* get some initial data into the ftp struct */
3185 ftp->bytecountp = &conn->data->req.bytecount;
3186 ftp->transfer = FTPTRANSFER_BODY;
3187 ftp->downloadsize = 0;
3189 /* No need to duplicate user+password, the connectdata struct won't change
3190 during a session, but we re-init them here since on subsequent inits
3191 since the conn struct may have changed or been replaced.
3193 ftp->user = conn->user;
3194 ftp->passwd = conn->passwd;
3195 if(isBadFtpString(ftp->user))
3196 return CURLE_URL_MALFORMAT;
3197 if(isBadFtpString(ftp->passwd))
3198 return CURLE_URL_MALFORMAT;
3200 conn->proto.ftpc.known_filesize = -1; /* unknown size for now */
3205 #if defined(__INTEL_COMPILER) && (__INTEL_COMPILER == 910) && \
3206 defined(__OPTIMIZE__) && defined(__unix__) && defined(__i386__)
3207 /* workaround icc 9.1 optimizer issue */
3208 #pragma optimize("", on)
3212 * ftp_connect() should do everything that is to be considered a part of
3213 * the connection phase.
3215 * The variable 'done' points to will be TRUE if the protocol-layer connect
3216 * phase is done when this function returns, or FALSE if not.
3219 static CURLcode ftp_connect(struct connectdata *conn,
3220 bool *done) /* see description above */
3223 struct ftp_conn *ftpc = &conn->proto.ftpc;
3224 struct pingpong *pp = &ftpc->pp;
3226 *done = FALSE; /* default to not done yet */
3228 /* If there already is a protocol-specific struct allocated for this
3229 sessionhandle, deal with it */
3230 Curl_reset_reqproto(conn);
3232 result = ftp_init(conn);
3233 if(CURLE_OK != result)
3236 /* We always support persistent connections on ftp */
3237 conn->bits.close = FALSE;
3239 pp->response_time = RESP_TIMEOUT; /* set default response time-out */
3240 pp->statemach_act = ftp_statemach_act;
3241 pp->endofresp = ftp_endofresp;
3244 if(conn->handler->flags & PROTOPT_SSL) {
3246 result = Curl_ssl_connect(conn, FIRSTSOCKET);
3251 Curl_pp_init(pp); /* init the generic pingpong data */
3253 /* When we connect, we start in the state where we await the 220
3255 state(conn, FTP_WAIT220);
3257 result = ftp_multi_statemach(conn, done);
3262 /***********************************************************************
3266 * The DONE function. This does what needs to be done after a single DO has
3269 * Input argument is already checked for validity.
3271 static CURLcode ftp_done(struct connectdata *conn, CURLcode status,
3274 struct SessionHandle *data = conn->data;
3275 struct FTP *ftp = data->state.proto.ftp;
3276 struct ftp_conn *ftpc = &conn->proto.ftpc;
3277 struct pingpong *pp = &ftpc->pp;
3280 CURLcode result = CURLE_OK;
3281 bool was_ctl_valid = ftpc->ctl_valid;
3283 const char *path_to_use = data->state.path;
3286 /* When the easy handle is removed from the multi while libcurl is still
3287 * trying to resolve the host name, it seems that the ftp struct is not
3288 * yet initialized, but the removal action calls Curl_done() which calls
3289 * this function. So we simply return success if no ftp pointer is set.
3294 case CURLE_BAD_DOWNLOAD_RESUME:
3295 case CURLE_FTP_WEIRD_PASV_REPLY:
3296 case CURLE_FTP_PORT_FAILED:
3297 case CURLE_FTP_ACCEPT_FAILED:
3298 case CURLE_FTP_ACCEPT_TIMEOUT:
3299 case CURLE_FTP_COULDNT_SET_TYPE:
3300 case CURLE_FTP_COULDNT_RETR_FILE:
3301 case CURLE_PARTIAL_FILE:
3302 case CURLE_UPLOAD_FAILED:
3303 case CURLE_REMOTE_ACCESS_DENIED:
3304 case CURLE_FILESIZE_EXCEEDED:
3305 case CURLE_REMOTE_FILE_NOT_FOUND:
3306 case CURLE_WRITE_ERROR:
3307 /* the connection stays alive fine even though this happened */
3309 case CURLE_OK: /* doesn't affect the control connection's status */
3311 ftpc->ctl_valid = was_ctl_valid;
3314 /* until we cope better with prematurely ended requests, let them
3315 * fallback as if in complete failure */
3316 default: /* by default, an error means the control connection is
3317 wedged and should not be used anymore */
3318 ftpc->ctl_valid = FALSE;
3319 ftpc->cwdfail = TRUE; /* set this TRUE to prevent us to remember the
3320 current path, as this connection is going */
3321 conn->bits.close = TRUE; /* marked for closure */
3322 result = status; /* use the already set error code */
3326 /* now store a copy of the directory we are in */
3328 free(ftpc->prevpath);
3330 if(data->set.wildcardmatch) {
3331 if(data->set.chunk_end && ftpc->file) {
3332 data->set.chunk_end(data->wildcard.customptr);
3334 ftpc->known_filesize = -1;
3337 /* get the "raw" path */
3338 path = curl_easy_unescape(data, path_to_use, 0, NULL);
3340 /* out of memory, but we can limp along anyway (and should try to
3341 * since we may already be in the out of memory cleanup path) */
3343 result = CURLE_OUT_OF_MEMORY;
3344 ftpc->ctl_valid = FALSE; /* mark control connection as bad */
3345 conn->bits.close = TRUE; /* mark for connection closure */
3346 ftpc->prevpath = NULL; /* no path remembering */
3349 size_t flen = ftpc->file?strlen(ftpc->file):0; /* file is "raw" already */
3350 size_t dlen = strlen(path)-flen;
3351 if(!ftpc->cwdfail) {
3352 if(dlen && (data->set.ftp_filemethod != FTPFILE_NOCWD)) {
3353 ftpc->prevpath = path;
3355 /* if 'path' is not the whole string */
3356 ftpc->prevpath[dlen]=0; /* terminate */
3359 /* we never changed dir */
3360 ftpc->prevpath=strdup("");
3364 infof(data, "Remembering we are in dir \"%s\"\n", ftpc->prevpath);
3367 ftpc->prevpath = NULL; /* no path */
3371 /* free the dir tree and file parts */
3374 /* shut down the socket to inform the server we're done */
3377 shutdown(conn->sock[SECONDARYSOCKET],2); /* SD_BOTH */
3380 if(conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD) {
3381 if(!result && ftpc->dont_check && data->req.maxdownload > 0) {
3382 /* partial download completed */
3383 result = Curl_pp_sendf(pp, "%s", "ABOR");
3385 failf(data, "Failure sending ABOR command: %s",
3386 curl_easy_strerror(result));
3387 ftpc->ctl_valid = FALSE; /* mark control connection as bad */
3388 conn->bits.close = TRUE; /* mark for connection closure */
3392 if(conn->ssl[SECONDARYSOCKET].use) {
3393 /* The secondary socket is using SSL so we must close down that part
3394 first before we close the socket for real */
3395 Curl_ssl_close(conn, SECONDARYSOCKET);
3397 /* Note that we keep "use" set to TRUE since that (next) connection is
3398 still requested to use SSL */
3400 if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET]) {
3401 Curl_closesocket(conn, conn->sock[SECONDARYSOCKET]);
3402 conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
3403 conn->bits.tcpconnect[SECONDARYSOCKET] = FALSE;
3407 if(!result && (ftp->transfer == FTPTRANSFER_BODY) && ftpc->ctl_valid &&
3408 pp->pending_resp && !premature) {
3410 * Let's see what the server says about the transfer we just performed,
3411 * but lower the timeout as sometimes this connection has died while the
3412 * data has been transferred. This happens when doing through NATs etc that
3413 * abandon old silent connections.
3415 long old_time = pp->response_time;
3417 pp->response_time = 60*1000; /* give it only a minute for now */
3418 pp->response = Curl_tvnow(); /* timeout relative now */
3420 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
3422 pp->response_time = old_time; /* set this back to previous value */
3424 if(!nread && (CURLE_OPERATION_TIMEDOUT == result)) {
3425 failf(data, "control connection looks dead");
3426 ftpc->ctl_valid = FALSE; /* mark control connection as bad */
3427 conn->bits.close = TRUE; /* mark for closure */
3433 if(ftpc->dont_check && data->req.maxdownload > 0) {
3434 /* we have just sent ABOR and there is no reliable way to check if it was
3435 * successful or not; we have to close the connection now */
3436 infof(data, "partial download completed, closing connection\n");
3437 conn->bits.close = TRUE; /* mark for closure */
3441 if(!ftpc->dont_check) {
3442 /* 226 Transfer complete, 250 Requested file action okay, completed. */
3443 if((ftpcode != 226) && (ftpcode != 250)) {
3444 failf(data, "server did not report OK, got %d", ftpcode);
3445 result = CURLE_PARTIAL_FILE;
3450 if(result || premature)
3451 /* the response code from the transfer showed an error already so no
3452 use checking further */
3454 else if(data->set.upload) {
3455 if((-1 != data->set.infilesize) &&
3456 (data->set.infilesize != *ftp->bytecountp) &&
3458 (ftp->transfer == FTPTRANSFER_BODY)) {
3459 failf(data, "Uploaded unaligned file size (%" FORMAT_OFF_T
3460 " out of %" FORMAT_OFF_T " bytes)",
3461 *ftp->bytecountp, data->set.infilesize);
3462 result = CURLE_PARTIAL_FILE;
3466 if((-1 != data->req.size) &&
3467 (data->req.size != *ftp->bytecountp) &&
3468 #ifdef CURL_DO_LINEEND_CONV
3469 /* Most FTP servers don't adjust their file SIZE response for CRLFs, so
3470 * we'll check to see if the discrepancy can be explained by the number
3471 * of CRLFs we've changed to LFs.
3473 ((data->req.size + data->state.crlf_conversions) !=
3474 *ftp->bytecountp) &&
3475 #endif /* CURL_DO_LINEEND_CONV */
3476 (data->req.maxdownload != *ftp->bytecountp)) {
3477 failf(data, "Received only partial file: %" FORMAT_OFF_T " bytes",
3479 result = CURLE_PARTIAL_FILE;
3481 else if(!ftpc->dont_check &&
3482 !*ftp->bytecountp &&
3483 (data->req.size>0)) {
3484 failf(data, "No data was received!");
3485 result = CURLE_FTP_COULDNT_RETR_FILE;
3489 /* clear these for next connection */
3490 ftp->transfer = FTPTRANSFER_BODY;
3491 ftpc->dont_check = FALSE;
3493 /* Send any post-transfer QUOTE strings? */
3494 if(!status && !result && !premature && data->set.postquote)
3495 result = ftp_sendquote(conn, data->set.postquote);
3500 /***********************************************************************
3504 * Where a 'quote' means a list of custom commands to send to the server.
3505 * The quote list is passed as an argument.
3511 CURLcode ftp_sendquote(struct connectdata *conn, struct curl_slist *quote)
3513 struct curl_slist *item;
3517 struct ftp_conn *ftpc = &conn->proto.ftpc;
3518 struct pingpong *pp = &ftpc->pp;
3523 char *cmd = item->data;
3524 bool acceptfail = FALSE;
3526 /* if a command starts with an asterisk, which a legal FTP command never
3527 can, the command will be allowed to fail without it causing any
3528 aborts or cancels etc. It will cause libcurl to act as if the command
3529 is successful, whatever the server reponds. */
3536 PPSENDF(&conn->proto.ftpc.pp, "%s", cmd);
3538 pp->response = Curl_tvnow(); /* timeout relative now */
3540 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
3544 if(!acceptfail && (ftpcode >= 400)) {
3545 failf(conn->data, "QUOT string not accepted: %s", cmd);
3546 return CURLE_QUOTE_ERROR;
3556 /***********************************************************************
3560 * Returns TRUE if we in the current situation should send TYPE
3562 static int ftp_need_type(struct connectdata *conn,
3565 return conn->proto.ftpc.transfertype != (ascii_wanted?'A':'I');
3568 /***********************************************************************
3572 * Set TYPE. We only deal with ASCII or BINARY so this function
3574 * If the transfer type is not sent, simulate on OK response in newstate
3576 static CURLcode ftp_nb_type(struct connectdata *conn,
3577 bool ascii, ftpstate newstate)
3579 struct ftp_conn *ftpc = &conn->proto.ftpc;
3581 char want = (char)(ascii?'A':'I');
3583 if(ftpc->transfertype == want) {
3584 state(conn, newstate);
3585 return ftp_state_type_resp(conn, 200, newstate);
3588 PPSENDF(&ftpc->pp, "TYPE %c", want);
3589 state(conn, newstate);
3591 /* keep track of our current transfer type */
3592 ftpc->transfertype = want;
3596 /***************************************************************************
3598 * ftp_pasv_verbose()
3600 * This function only outputs some informationals about this second connection
3601 * when we've issued a PASV command before and thus we have connected to a
3602 * possibly new IP address.
3605 #ifndef CURL_DISABLE_VERBOSE_STRINGS
3607 ftp_pasv_verbose(struct connectdata *conn,
3609 char *newhost, /* ascii version */
3613 Curl_printable_address(ai, buf, sizeof(buf));
3614 infof(conn->data, "Connecting to %s (%s) port %d\n", newhost, buf, port);
3619 Check if this is a range download, and if so, set the internal variables
3623 static CURLcode ftp_range(struct connectdata *conn)
3625 curl_off_t from, to;
3628 struct SessionHandle *data = conn->data;
3629 struct ftp_conn *ftpc = &conn->proto.ftpc;
3631 if(data->state.use_range && data->state.range) {
3632 from=curlx_strtoofft(data->state.range, &ptr, 0);
3633 while(*ptr && (ISSPACE(*ptr) || (*ptr=='-')))
3635 to=curlx_strtoofft(ptr, &ptr2, 0);
3637 /* we didn't get any digit */
3640 if((-1 == to) && (from>=0)) {
3642 data->state.resume_from = from;
3643 DEBUGF(infof(conn->data, "FTP RANGE %" FORMAT_OFF_T " to end of file\n",
3648 data->req.maxdownload = -from;
3649 data->state.resume_from = from;
3650 DEBUGF(infof(conn->data, "FTP RANGE the last %" FORMAT_OFF_T " bytes\n",
3655 data->req.maxdownload = (to-from)+1; /* include last byte */
3656 data->state.resume_from = from;
3657 DEBUGF(infof(conn->data, "FTP RANGE from %" FORMAT_OFF_T
3658 " getting %" FORMAT_OFF_T " bytes\n",
3659 from, data->req.maxdownload));
3661 DEBUGF(infof(conn->data, "range-download from %" FORMAT_OFF_T
3662 " to %" FORMAT_OFF_T ", totally %" FORMAT_OFF_T " bytes\n",
3663 from, to, data->req.maxdownload));
3664 ftpc->dont_check = TRUE; /* dont check for successful transfer */
3667 data->req.maxdownload = -1;
3675 * This function shall be called when the second FTP (data) connection is
3678 * 'complete' can return 0 for incomplete, 1 for done and -1 for go back
3679 * (which basically is only for when PASV is being sent to retry a failed
3683 static CURLcode ftp_do_more(struct connectdata *conn, int *completep)
3685 struct SessionHandle *data=conn->data;
3686 struct ftp_conn *ftpc = &conn->proto.ftpc;
3687 CURLcode result = CURLE_OK;
3688 bool connected = FALSE;
3689 bool complete = FALSE;
3691 /* the ftp struct is inited in ftp_connect() */
3692 struct FTP *ftp = data->state.proto.ftp;
3694 /* if the second connection isn't done yet, wait for it */
3695 if(!conn->bits.tcpconnect[SECONDARYSOCKET]) {
3696 if(conn->tunnel_state[SECONDARYSOCKET] == TUNNEL_CONNECT) {
3697 /* As we're in TUNNEL_CONNECT state now, we know the proxy name and port
3698 aren't used so we blank their arguments. TODO: make this nicer */
3699 result = Curl_proxyCONNECT(conn, SECONDARYSOCKET, NULL, 0);
3704 result = Curl_is_connected(conn, SECONDARYSOCKET, &connected);
3706 /* Ready to do more? */
3708 DEBUGF(infof(data, "DO-MORE connected phase starts\n"));
3711 if(result && (ftpc->count1 == 0)) {
3712 *completep = -1; /* go back to DOING please */
3713 /* this is a EPSV connect failing, try PASV instead */
3714 return ftp_epsv_disable(conn);
3721 /* already in a state so skip the intial commands.
3722 They are only done to kickstart the do_more state */
3723 result = ftp_multi_statemach(conn, &complete);
3725 *completep = (int)complete;
3727 /* if we got an error or if we don't wait for a data connection return
3729 if(result || (ftpc->wait_data_conn != TRUE))
3732 if(ftpc->wait_data_conn)
3733 /* if we reach the end of the FTP state machine here, *complete will be
3734 TRUE but so is ftpc->wait_data_conn, which says we need to wait for
3735 the data connection and therefore we're not actually complete */
3739 if(ftp->transfer <= FTPTRANSFER_INFO) {
3740 /* a transfer is about to take place, or if not a file name was given
3741 so we'll do a SIZE on it later and then we need the right TYPE first */
3743 if(ftpc->wait_data_conn == TRUE) {
3746 result = ReceivedServerConnect(conn, &serv_conned);
3748 return result; /* Failed to accept data connection */
3751 /* It looks data connection is established */
3752 result = AcceptServerConnect(conn);
3753 ftpc->wait_data_conn = FALSE;
3755 result = InitiateTransfer(conn);
3760 *completep = 1; /* this state is now complete when the server has
3761 connected back to us */
3764 else if(data->set.upload) {
3765 result = ftp_nb_type(conn, data->set.prefer_ascii, FTP_STOR_TYPE);
3769 result = ftp_multi_statemach(conn, &complete);
3770 *completep = (int)complete;
3774 ftp->downloadsize = -1; /* unknown as of yet */
3776 result = ftp_range(conn);
3779 else if(data->set.ftp_list_only || !ftpc->file) {
3780 /* The specified path ends with a slash, and therefore we think this
3781 is a directory that is requested, use LIST. But before that we
3782 need to set ASCII transfer mode. */
3784 /* But only if a body transfer was requested. */
3785 if(ftp->transfer == FTPTRANSFER_BODY) {
3786 result = ftp_nb_type(conn, TRUE, FTP_LIST_TYPE);
3790 /* otherwise just fall through */
3793 result = ftp_nb_type(conn, data->set.prefer_ascii, FTP_RETR_TYPE);
3798 result = ftp_multi_statemach(conn, &complete);
3799 *completep = (int)complete;
3804 if((result == CURLE_OK) && (ftp->transfer != FTPTRANSFER_BODY))
3805 /* no data to transfer. FIX: it feels like a kludge to have this here
3807 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
3809 if(!ftpc->wait_data_conn) {
3810 /* no waiting for the data connection so this is now complete */
3812 DEBUGF(infof(data, "DO-MORE phase ends with %d\n", (int)result));
3820 /***********************************************************************
3824 * This is the actual DO function for FTP. Get a file/directory according to
3825 * the options previously setup.
3829 CURLcode ftp_perform(struct connectdata *conn,
3830 bool *connected, /* connect status after PASV / PORT */
3833 /* this is FTP and no proxy */
3834 CURLcode result=CURLE_OK;
3836 DEBUGF(infof(conn->data, "DO phase starts\n"));
3838 if(conn->data->set.opt_no_body) {
3839 /* requested no body means no transfer... */
3840 struct FTP *ftp = conn->data->state.proto.ftp;
3841 ftp->transfer = FTPTRANSFER_INFO;
3844 *dophase_done = FALSE; /* not done yet */
3846 /* start the first command in the DO phase */
3847 result = ftp_state_quote(conn, TRUE, FTP_QUOTE);
3851 /* run the state-machine */
3852 result = ftp_multi_statemach(conn, dophase_done);
3854 *connected = conn->bits.tcpconnect[SECONDARYSOCKET];
3856 infof(conn->data, "ftp_perform ends with SECONDARY: %d\n", *connected);
3859 DEBUGF(infof(conn->data, "DO phase is complete1\n"));
3864 static void wc_data_dtor(void *ptr)
3866 struct ftp_wc_tmpdata *tmp = ptr;
3868 Curl_ftp_parselist_data_free(&tmp->parser);
3872 static CURLcode init_wc_data(struct connectdata *conn)
3875 char *path = conn->data->state.path;
3876 struct WildcardData *wildcard = &(conn->data->wildcard);
3877 CURLcode ret = CURLE_OK;
3878 struct ftp_wc_tmpdata *ftp_tmp;
3880 last_slash = strrchr(conn->data->state.path, '/');
3883 if(last_slash[0] == '\0') {
3884 wildcard->state = CURLWC_CLEAN;
3885 ret = ftp_parse_url_path(conn);
3889 wildcard->pattern = strdup(last_slash);
3890 if(!wildcard->pattern)
3891 return CURLE_OUT_OF_MEMORY;
3892 last_slash[0] = '\0'; /* cut file from path */
3895 else { /* there is only 'wildcard pattern' or nothing */
3897 wildcard->pattern = strdup(path);
3898 if(!wildcard->pattern)
3899 return CURLE_OUT_OF_MEMORY;
3902 else { /* only list */
3903 wildcard->state = CURLWC_CLEAN;
3904 ret = ftp_parse_url_path(conn);
3909 /* program continues only if URL is not ending with slash, allocate needed
3910 resources for wildcard transfer */
3912 /* allocate ftp protocol specific temporary wildcard data */
3913 ftp_tmp = calloc(1, sizeof(struct ftp_wc_tmpdata));
3915 Curl_safefree(wildcard->pattern);
3916 return CURLE_OUT_OF_MEMORY;
3919 /* INITIALIZE parselist structure */
3920 ftp_tmp->parser = Curl_ftp_parselist_data_alloc();
3921 if(!ftp_tmp->parser) {
3922 Curl_safefree(wildcard->pattern);
3923 Curl_safefree(ftp_tmp);
3924 return CURLE_OUT_OF_MEMORY;
3927 wildcard->tmp = ftp_tmp; /* put it to the WildcardData tmp pointer */
3928 wildcard->tmp_dtor = wc_data_dtor;
3930 /* wildcard does not support NOCWD option (assert it?) */
3931 if(conn->data->set.ftp_filemethod == FTPFILE_NOCWD)
3932 conn->data->set.ftp_filemethod = FTPFILE_MULTICWD;
3934 /* try to parse ftp url */
3935 ret = ftp_parse_url_path(conn);
3937 Curl_safefree(wildcard->pattern);
3938 wildcard->tmp_dtor(wildcard->tmp);
3939 wildcard->tmp_dtor = ZERO_NULL;
3940 wildcard->tmp = NULL;
3944 wildcard->path = strdup(conn->data->state.path);
3945 if(!wildcard->path) {
3946 Curl_safefree(wildcard->pattern);
3947 wildcard->tmp_dtor(wildcard->tmp);
3948 wildcard->tmp_dtor = ZERO_NULL;
3949 wildcard->tmp = NULL;
3950 return CURLE_OUT_OF_MEMORY;
3953 /* backup old write_function */
3954 ftp_tmp->backup.write_function = conn->data->set.fwrite_func;
3955 /* parsing write function */
3956 conn->data->set.fwrite_func = Curl_ftp_parselist;
3957 /* backup old file descriptor */
3958 ftp_tmp->backup.file_descriptor = conn->data->set.out;
3959 /* let the writefunc callback know what curl pointer is working with */
3960 conn->data->set.out = conn;
3962 infof(conn->data, "Wildcard - Parsing started\n");
3966 /* This is called recursively */
3967 static CURLcode wc_statemach(struct connectdata *conn)
3969 struct WildcardData * const wildcard = &(conn->data->wildcard);
3970 CURLcode ret = CURLE_OK;
3972 switch (wildcard->state) {
3974 ret = init_wc_data(conn);
3975 if(wildcard->state == CURLWC_CLEAN)
3979 wildcard->state = ret ? CURLWC_ERROR : CURLWC_MATCHING;
3982 case CURLWC_MATCHING: {
3983 /* In this state is LIST response successfully parsed, so lets restore
3984 previous WRITEFUNCTION callback and WRITEDATA pointer */
3985 struct ftp_wc_tmpdata *ftp_tmp = wildcard->tmp;
3986 conn->data->set.fwrite_func = ftp_tmp->backup.write_function;
3987 conn->data->set.out = ftp_tmp->backup.file_descriptor;
3988 ftp_tmp->backup.write_function = ZERO_NULL;
3989 ftp_tmp->backup.file_descriptor = NULL;
3990 wildcard->state = CURLWC_DOWNLOADING;
3992 if(Curl_ftp_parselist_geterror(ftp_tmp->parser)) {
3993 /* error found in LIST parsing */
3994 wildcard->state = CURLWC_CLEAN;
3995 return wc_statemach(conn);
3997 else if(wildcard->filelist->size == 0) {
3998 /* no corresponding file */
3999 wildcard->state = CURLWC_CLEAN;
4000 return CURLE_REMOTE_FILE_NOT_FOUND;
4002 return wc_statemach(conn);
4005 case CURLWC_DOWNLOADING: {
4006 /* filelist has at least one file, lets get first one */
4007 struct ftp_conn *ftpc = &conn->proto.ftpc;
4008 struct curl_fileinfo *finfo = wildcard->filelist->head->ptr;
4010 char *tmp_path = aprintf("%s%s", wildcard->path, finfo->filename);
4012 return CURLE_OUT_OF_MEMORY;
4014 /* switch default "state.pathbuffer" and tmp_path, good to see
4015 ftp_parse_url_path function to understand this trick */
4016 Curl_safefree(conn->data->state.pathbuffer);
4017 conn->data->state.pathbuffer = tmp_path;
4018 conn->data->state.path = tmp_path;
4020 infof(conn->data, "Wildcard - START of \"%s\"\n", finfo->filename);
4021 if(conn->data->set.chunk_bgn) {
4022 long userresponse = conn->data->set.chunk_bgn(
4023 finfo, wildcard->customptr, (int)wildcard->filelist->size);
4024 switch(userresponse) {
4025 case CURL_CHUNK_BGN_FUNC_SKIP:
4026 infof(conn->data, "Wildcard - \"%s\" skipped by user\n",
4028 wildcard->state = CURLWC_SKIP;
4029 return wc_statemach(conn);
4030 case CURL_CHUNK_BGN_FUNC_FAIL:
4031 return CURLE_CHUNK_FAILED;
4035 if(finfo->filetype != CURLFILETYPE_FILE) {
4036 wildcard->state = CURLWC_SKIP;
4037 return wc_statemach(conn);
4040 if(finfo->flags & CURLFINFOFLAG_KNOWN_SIZE)
4041 ftpc->known_filesize = finfo->size;
4043 ret = ftp_parse_url_path(conn);
4048 /* we don't need the Curl_fileinfo of first file anymore */
4049 Curl_llist_remove(wildcard->filelist, wildcard->filelist->head, NULL);
4051 if(wildcard->filelist->size == 0) { /* remains only one file to down. */
4052 wildcard->state = CURLWC_CLEAN;
4053 /* after that will be ftp_do called once again and no transfer
4054 will be done because of CURLWC_CLEAN state */
4060 if(conn->data->set.chunk_end)
4061 conn->data->set.chunk_end(conn->data->wildcard.customptr);
4062 Curl_llist_remove(wildcard->filelist, wildcard->filelist->head, NULL);
4063 wildcard->state = (wildcard->filelist->size == 0) ?
4064 CURLWC_CLEAN : CURLWC_DOWNLOADING;
4065 return wc_statemach(conn);
4068 case CURLWC_CLEAN: {
4069 struct ftp_wc_tmpdata *ftp_tmp = wildcard->tmp;
4072 ret = Curl_ftp_parselist_geterror(ftp_tmp->parser);
4074 wildcard->state = ret ? CURLWC_ERROR : CURLWC_DONE;
4085 /***********************************************************************
4089 * This function is registered as 'curl_do' function. It decodes the path
4090 * parts etc as a wrapper to the actual DO function (ftp_perform).
4092 * The input argument is already checked for validity.
4094 static CURLcode ftp_do(struct connectdata *conn, bool *done)
4096 CURLcode retcode = CURLE_OK;
4097 struct ftp_conn *ftpc = &conn->proto.ftpc;
4099 *done = FALSE; /* default to false */
4100 ftpc->wait_data_conn = FALSE; /* default to no such wait */
4103 Since connections can be re-used between SessionHandles, this might be a
4104 connection already existing but on a fresh SessionHandle struct so we must
4105 make sure we have a good 'struct FTP' to play with. For new connections,
4106 the struct FTP is allocated and setup in the ftp_connect() function.
4108 Curl_reset_reqproto(conn);
4109 retcode = ftp_init(conn);
4113 if(conn->data->set.wildcardmatch) {
4114 retcode = wc_statemach(conn);
4115 if(conn->data->wildcard.state == CURLWC_SKIP ||
4116 conn->data->wildcard.state == CURLWC_DONE) {
4117 /* do not call ftp_regular_transfer */
4120 if(retcode) /* error, loop or skipping the file */
4123 else { /* no wildcard FSM needed */
4124 retcode = ftp_parse_url_path(conn);
4129 retcode = ftp_regular_transfer(conn, done);
4135 CURLcode Curl_ftpsendf(struct connectdata *conn,
4136 const char *fmt, ...)
4138 ssize_t bytes_written;
4139 #define SBUF_SIZE 1024
4143 CURLcode res = CURLE_OK;
4144 #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
4145 enum protection_level data_sec = conn->data_prot;
4150 write_len = vsnprintf(s, SBUF_SIZE-3, fmt, ap);
4153 strcpy(&s[write_len], "\r\n"); /* append a trailing CRLF */
4158 res = Curl_convert_to_network(conn->data, s, write_len);
4159 /* Curl_convert_to_network calls failf if unsuccessful */
4164 #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
4165 conn->data_prot = PROT_CMD;
4167 res = Curl_write(conn, conn->sock[FIRSTSOCKET], sptr, write_len,
4169 #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
4170 DEBUGASSERT(data_sec > PROT_NONE && data_sec < PROT_LAST);
4171 conn->data_prot = data_sec;
4177 if(conn->data->set.verbose)
4178 Curl_debug(conn->data, CURLINFO_HEADER_OUT,
4179 sptr, (size_t)bytes_written, conn);
4181 if(bytes_written != (ssize_t)write_len) {
4182 write_len -= bytes_written;
4183 sptr += bytes_written;
4192 /***********************************************************************
4196 * This should be called before calling sclose() on an ftp control connection
4197 * (not data connections). We should then wait for the response from the
4198 * server before returning. The calling code should then try to close the
4202 static CURLcode ftp_quit(struct connectdata *conn)
4204 CURLcode result = CURLE_OK;
4206 if(conn->proto.ftpc.ctl_valid) {
4207 result = Curl_pp_sendf(&conn->proto.ftpc.pp, "%s", "QUIT");
4209 failf(conn->data, "Failure sending QUIT command: %s",
4210 curl_easy_strerror(result));
4211 conn->proto.ftpc.ctl_valid = FALSE; /* mark control connection as bad */
4212 conn->bits.close = TRUE; /* mark for connection closure */
4213 state(conn, FTP_STOP);
4217 state(conn, FTP_QUIT);
4219 result = ftp_block_statemach(conn);
4225 /***********************************************************************
4229 * Disconnect from an FTP server. Cleanup protocol-specific per-connection
4230 * resources. BLOCKING.
4232 static CURLcode ftp_disconnect(struct connectdata *conn, bool dead_connection)
4234 struct ftp_conn *ftpc= &conn->proto.ftpc;
4235 struct pingpong *pp = &ftpc->pp;
4237 /* We cannot send quit unconditionally. If this connection is stale or
4238 bad in any way, sending quit and waiting around here will make the
4239 disconnect wait in vain and cause more problems than we need to.
4241 ftp_quit() will check the state of ftp->ctl_valid. If it's ok it
4242 will try to send the QUIT command, otherwise it will just return.
4245 ftpc->ctl_valid = FALSE;
4247 /* The FTP session may or may not have been allocated/setup at this point! */
4248 (void)ftp_quit(conn); /* ignore errors on the QUIT */
4250 if(ftpc->entrypath) {
4251 struct SessionHandle *data = conn->data;
4252 if(data->state.most_recent_ftp_entrypath == ftpc->entrypath) {
4253 data->state.most_recent_ftp_entrypath = NULL;
4255 free(ftpc->entrypath);
4256 ftpc->entrypath = NULL;
4260 if(ftpc->prevpath) {
4261 free(ftpc->prevpath);
4262 ftpc->prevpath = NULL;
4264 if(ftpc->server_os) {
4265 free(ftpc->server_os);
4266 ftpc->server_os = NULL;
4269 Curl_pp_disconnect(pp);
4271 #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
4278 /***********************************************************************
4280 * ftp_parse_url_path()
4282 * Parse the URL path into separate path components.
4286 CURLcode ftp_parse_url_path(struct connectdata *conn)
4288 struct SessionHandle *data = conn->data;
4289 /* the ftp struct is already inited in ftp_connect() */
4290 struct FTP *ftp = data->state.proto.ftp;
4291 struct ftp_conn *ftpc = &conn->proto.ftpc;
4292 const char *slash_pos; /* position of the first '/' char in curpos */
4293 const char *path_to_use = data->state.path;
4294 const char *cur_pos;
4295 const char *filename = NULL;
4297 cur_pos = path_to_use; /* current position in path. point at the begin
4298 of next path component */
4300 ftpc->ctl_valid = FALSE;
4301 ftpc->cwdfail = FALSE;
4303 switch(data->set.ftp_filemethod) {
4305 /* fastest, but less standard-compliant */
4308 The best time to check whether the path is a file or directory is right
4311 the first condition in the if() right here, is there just in case
4312 someone decides to set path to NULL one day
4314 if(data->state.path &&
4315 data->state.path[0] &&
4316 (data->state.path[strlen(data->state.path) - 1] != '/') )
4317 filename = data->state.path; /* this is a full file path */
4319 ftpc->file is not used anywhere other than for operations on a file.
4320 In other words, never for directory operations.
4321 So we can safely leave filename as NULL here and use it as a
4322 argument in dir/file decisions.
4326 case FTPFILE_SINGLECWD:
4327 /* get the last slash */
4328 if(!path_to_use[0]) {
4329 /* no dir, no file */
4333 slash_pos=strrchr(cur_pos, '/');
4334 if(slash_pos || !*cur_pos) {
4335 size_t dirlen = slash_pos-cur_pos;
4337 ftpc->dirs = calloc(1, sizeof(ftpc->dirs[0]));
4339 return CURLE_OUT_OF_MEMORY;
4344 ftpc->dirs[0] = curl_easy_unescape(conn->data, slash_pos ? cur_pos : "/",
4345 slash_pos ? curlx_sztosi(dirlen) : 1,
4347 if(!ftpc->dirs[0]) {
4349 return CURLE_OUT_OF_MEMORY;
4351 ftpc->dirdepth = 1; /* we consider it to be a single dir */
4352 filename = slash_pos ? slash_pos+1 : cur_pos; /* rest is file name */
4355 filename = cur_pos; /* this is a file name only */
4358 default: /* allow pretty much anything */
4359 case FTPFILE_MULTICWD:
4361 ftpc->diralloc = 5; /* default dir depth to allocate */
4362 ftpc->dirs = calloc(ftpc->diralloc, sizeof(ftpc->dirs[0]));
4364 return CURLE_OUT_OF_MEMORY;
4366 /* we have a special case for listing the root dir only */
4367 if(strequal(path_to_use, "/")) {
4368 cur_pos++; /* make it point to the zero byte */
4369 ftpc->dirs[0] = strdup("/");
4373 /* parse the URL path into separate path components */
4374 while((slash_pos = strchr(cur_pos, '/')) != NULL) {
4375 /* 1 or 0 pointer offset to indicate absolute directory */
4376 ssize_t absolute_dir = ((cur_pos - data->state.path > 0) &&
4377 (ftpc->dirdepth == 0))?1:0;
4379 /* seek out the next path component */
4380 if(slash_pos-cur_pos) {
4381 /* we skip empty path components, like "x//y" since the FTP command
4382 CWD requires a parameter and a non-existent parameter a) doesn't
4383 work on many servers and b) has no effect on the others. */
4384 int len = curlx_sztosi(slash_pos - cur_pos + absolute_dir);
4385 ftpc->dirs[ftpc->dirdepth] =
4386 curl_easy_unescape(conn->data, cur_pos - absolute_dir, len, NULL);
4387 if(!ftpc->dirs[ftpc->dirdepth]) { /* run out of memory ... */
4388 failf(data, "no memory");
4390 return CURLE_OUT_OF_MEMORY;
4392 if(isBadFtpString(ftpc->dirs[ftpc->dirdepth])) {
4393 free(ftpc->dirs[ftpc->dirdepth]);
4395 return CURLE_URL_MALFORMAT;
4399 cur_pos = slash_pos + 1; /* jump to the rest of the string */
4400 if(!ftpc->dirdepth) {
4401 /* path starts with a slash, add that as a directory */
4402 ftpc->dirs[ftpc->dirdepth] = strdup("/");
4403 if(!ftpc->dirs[ftpc->dirdepth++]) { /* run out of memory ... */
4404 failf(data, "no memory");
4406 return CURLE_OUT_OF_MEMORY;
4412 cur_pos = slash_pos + 1; /* jump to the rest of the string */
4413 if(++ftpc->dirdepth >= ftpc->diralloc) {
4416 ftpc->diralloc *= 2; /* double the size each time */
4417 bigger = realloc(ftpc->dirs, ftpc->diralloc * sizeof(ftpc->dirs[0]));
4420 return CURLE_OUT_OF_MEMORY;
4422 ftpc->dirs = bigger;
4426 filename = cur_pos; /* the rest is the file name */
4430 if(filename && *filename) {
4431 ftpc->file = curl_easy_unescape(conn->data, filename, 0, NULL);
4432 if(NULL == ftpc->file) {
4434 failf(data, "no memory");
4435 return CURLE_OUT_OF_MEMORY;
4437 if(isBadFtpString(ftpc->file)) {
4439 return CURLE_URL_MALFORMAT;
4443 ftpc->file=NULL; /* instead of point to a zero byte, we make it a NULL
4446 if(data->set.upload && !ftpc->file && (ftp->transfer == FTPTRANSFER_BODY)) {
4447 /* We need a file name when uploading. Return error! */
4448 failf(data, "Uploading to a URL without a file name!");
4449 return CURLE_URL_MALFORMAT;
4452 ftpc->cwddone = FALSE; /* default to not done */
4454 if(ftpc->prevpath) {
4455 /* prevpath is "raw" so we convert the input path before we compare the
4458 char *path = curl_easy_unescape(conn->data, data->state.path, 0, &dlen);
4461 return CURLE_OUT_OF_MEMORY;
4464 dlen -= ftpc->file?curlx_uztosi(strlen(ftpc->file)):0;
4465 if((dlen == curlx_uztosi(strlen(ftpc->prevpath))) &&
4466 strnequal(path, ftpc->prevpath, dlen)) {
4467 infof(data, "Request has same path as previous transfer\n");
4468 ftpc->cwddone = TRUE;
4476 /* call this when the DO phase has completed */
4477 static CURLcode ftp_dophase_done(struct connectdata *conn,
4480 struct FTP *ftp = conn->data->state.proto.ftp;
4481 struct ftp_conn *ftpc = &conn->proto.ftpc;
4485 CURLcode result = ftp_do_more(conn, &completed);
4488 if(conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD) {
4489 /* close the second socket if it was created already */
4490 Curl_closesocket(conn, conn->sock[SECONDARYSOCKET]);
4491 conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
4497 if(ftp->transfer != FTPTRANSFER_BODY)
4498 /* no data to transfer */
4499 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
4501 /* since we didn't connect now, we want do_more to get called */
4502 conn->bits.do_more = TRUE;
4504 ftpc->ctl_valid = TRUE; /* seems good */
4509 /* called from multi.c while DOing */
4510 static CURLcode ftp_doing(struct connectdata *conn,
4513 CURLcode result = ftp_multi_statemach(conn, dophase_done);
4516 DEBUGF(infof(conn->data, "DO phase failed\n"));
4517 else if(*dophase_done) {
4518 result = ftp_dophase_done(conn, FALSE /* not connected */);
4520 DEBUGF(infof(conn->data, "DO phase is complete2\n"));
4525 /***********************************************************************
4527 * ftp_regular_transfer()
4529 * The input argument is already checked for validity.
4531 * Performs all commands done before a regular transfer between a local and a
4534 * ftp->ctl_valid starts out as FALSE, and gets set to TRUE if we reach the
4535 * ftp_done() function without finding any major problem.
4538 CURLcode ftp_regular_transfer(struct connectdata *conn,
4541 CURLcode result=CURLE_OK;
4542 bool connected=FALSE;
4543 struct SessionHandle *data = conn->data;
4544 struct ftp_conn *ftpc = &conn->proto.ftpc;
4545 data->req.size = -1; /* make sure this is unknown at this point */
4547 Curl_pgrsSetUploadCounter(data, 0);
4548 Curl_pgrsSetDownloadCounter(data, 0);
4549 Curl_pgrsSetUploadSize(data, 0);
4550 Curl_pgrsSetDownloadSize(data, 0);
4552 ftpc->ctl_valid = TRUE; /* starts good */
4554 result = ftp_perform(conn,
4555 &connected, /* have we connected after PASV/PORT */
4556 dophase_done); /* all commands in the DO-phase done? */
4558 if(CURLE_OK == result) {
4561 /* the DO phase has not completed yet */
4564 result = ftp_dophase_done(conn, connected);
4575 static CURLcode ftp_setup_connection(struct connectdata * conn)
4577 struct SessionHandle *data = conn->data;
4581 if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
4582 /* Unless we have asked to tunnel ftp operations through the proxy, we
4583 switch and use HTTP operations only */
4584 #ifndef CURL_DISABLE_HTTP
4585 if(conn->handler == &Curl_handler_ftp)
4586 conn->handler = &Curl_handler_ftp_proxy;
4589 conn->handler = &Curl_handler_ftps_proxy;
4591 failf(data, "FTPS not supported!");
4592 return CURLE_UNSUPPORTED_PROTOCOL;
4596 * We explicitly mark this connection as persistent here as we're doing
4597 * FTP over HTTP and thus we accidentally avoid setting this value
4600 conn->bits.close = FALSE;
4602 failf(data, "FTP over http proxy requires HTTP support built-in!");
4603 return CURLE_UNSUPPORTED_PROTOCOL;
4607 data->state.path++; /* don't include the initial slash */
4608 data->state.slash_removed = TRUE; /* we've skipped the slash */
4610 /* FTP URLs support an extension like ";type=<typecode>" that
4611 * we'll try to get now! */
4612 type = strstr(data->state.path, ";type=");
4615 type = strstr(conn->host.rawalloc, ";type=");
4618 *type = 0; /* it was in the middle of the hostname */
4619 command = Curl_raw_toupper(type[6]);
4620 conn->bits.type_set = TRUE;
4623 case 'A': /* ASCII mode */
4624 data->set.prefer_ascii = TRUE;
4627 case 'D': /* directory mode */
4628 data->set.ftp_list_only = TRUE;
4631 case 'I': /* binary mode */
4633 /* switch off ASCII */
4634 data->set.prefer_ascii = FALSE;
4642 #endif /* CURL_DISABLE_FTP */