1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2016, 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 https://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 #include "strtoofft.h"
65 #include "vtls/vtls.h"
68 #include "inet_ntop.h"
69 #include "inet_pton.h"
71 #include "parsedate.h" /* for the week day and month names */
72 #include "sockaddr.h" /* required for Curl_sockaddr_storage */
76 #include "speedcheck.h"
78 #include "http_proxy.h"
79 #include "non-ascii.h"
80 #include "curl_printf.h"
82 #include "curl_memory.h"
83 /* The last #include file should be: */
87 #define NI_MAXHOST 1025
89 #ifndef INET_ADDRSTRLEN
90 #define INET_ADDRSTRLEN 16
93 #ifdef CURL_DISABLE_VERBOSE_STRINGS
94 #define ftp_pasv_verbose(a,b,c,d) Curl_nop_stmt
97 /* Local API functions */
99 static void _state(struct connectdata *conn,
101 #define state(x,y) _state(x,y)
103 static void _state(struct connectdata *conn,
106 #define state(x,y) _state(x,y,__LINE__)
109 static CURLcode ftp_sendquote(struct connectdata *conn,
110 struct curl_slist *quote);
111 static CURLcode ftp_quit(struct connectdata *conn);
112 static CURLcode ftp_parse_url_path(struct connectdata *conn);
113 static CURLcode ftp_regular_transfer(struct connectdata *conn, bool *done);
114 #ifndef CURL_DISABLE_VERBOSE_STRINGS
115 static void ftp_pasv_verbose(struct connectdata *conn,
117 char *newhost, /* ascii version */
120 static CURLcode ftp_state_prepare_transfer(struct connectdata *conn);
121 static CURLcode ftp_state_mdtm(struct connectdata *conn);
122 static CURLcode ftp_state_quote(struct connectdata *conn,
123 bool init, ftpstate instate);
124 static CURLcode ftp_nb_type(struct connectdata *conn,
125 bool ascii, ftpstate newstate);
126 static int ftp_need_type(struct connectdata *conn,
128 static CURLcode ftp_do(struct connectdata *conn, bool *done);
129 static CURLcode ftp_done(struct connectdata *conn,
130 CURLcode, bool premature);
131 static CURLcode ftp_connect(struct connectdata *conn, bool *done);
132 static CURLcode ftp_disconnect(struct connectdata *conn, bool dead_connection);
133 static CURLcode ftp_do_more(struct connectdata *conn, int *completed);
134 static CURLcode ftp_multi_statemach(struct connectdata *conn, bool *done);
135 static int ftp_getsock(struct connectdata *conn, curl_socket_t *socks,
137 static int ftp_domore_getsock(struct connectdata *conn, curl_socket_t *socks,
139 static CURLcode ftp_doing(struct connectdata *conn,
141 static CURLcode ftp_setup_connection(struct connectdata * conn);
143 static CURLcode init_wc_data(struct connectdata *conn);
144 static CURLcode wc_statemach(struct connectdata *conn);
146 static void wc_data_dtor(void *ptr);
148 static CURLcode ftp_state_retr(struct connectdata *conn, curl_off_t filesize);
150 static CURLcode ftp_readresp(curl_socket_t sockfd,
154 static CURLcode ftp_dophase_done(struct connectdata *conn,
157 /* easy-to-use macro: */
158 #define PPSENDF(x,y,z) result = Curl_pp_sendf(x,y,z); \
164 * FTP protocol handler.
167 const struct Curl_handler Curl_handler_ftp = {
169 ftp_setup_connection, /* setup_connection */
172 ftp_do_more, /* do_more */
173 ftp_connect, /* connect_it */
174 ftp_multi_statemach, /* connecting */
175 ftp_doing, /* doing */
176 ftp_getsock, /* proto_getsock */
177 ftp_getsock, /* doing_getsock */
178 ftp_domore_getsock, /* domore_getsock */
179 ZERO_NULL, /* perform_getsock */
180 ftp_disconnect, /* disconnect */
181 ZERO_NULL, /* readwrite */
182 PORT_FTP, /* defport */
183 CURLPROTO_FTP, /* protocol */
184 PROTOPT_DUAL | PROTOPT_CLOSEACTION | PROTOPT_NEEDSPWD
185 | PROTOPT_NOURLQUERY /* flags */
191 * FTPS protocol handler.
194 const struct Curl_handler Curl_handler_ftps = {
196 ftp_setup_connection, /* setup_connection */
199 ftp_do_more, /* do_more */
200 ftp_connect, /* connect_it */
201 ftp_multi_statemach, /* connecting */
202 ftp_doing, /* doing */
203 ftp_getsock, /* proto_getsock */
204 ftp_getsock, /* doing_getsock */
205 ftp_domore_getsock, /* domore_getsock */
206 ZERO_NULL, /* perform_getsock */
207 ftp_disconnect, /* disconnect */
208 ZERO_NULL, /* readwrite */
209 PORT_FTPS, /* defport */
210 CURLPROTO_FTPS, /* protocol */
211 PROTOPT_SSL | PROTOPT_DUAL | PROTOPT_CLOSEACTION |
212 PROTOPT_NEEDSPWD | PROTOPT_NOURLQUERY /* flags */
216 #ifndef CURL_DISABLE_HTTP
218 * HTTP-proxyed FTP protocol handler.
221 static const struct Curl_handler Curl_handler_ftp_proxy = {
223 Curl_http_setup_conn, /* setup_connection */
224 Curl_http, /* do_it */
225 Curl_http_done, /* done */
226 ZERO_NULL, /* do_more */
227 ZERO_NULL, /* connect_it */
228 ZERO_NULL, /* connecting */
229 ZERO_NULL, /* doing */
230 ZERO_NULL, /* proto_getsock */
231 ZERO_NULL, /* doing_getsock */
232 ZERO_NULL, /* domore_getsock */
233 ZERO_NULL, /* perform_getsock */
234 ZERO_NULL, /* disconnect */
235 ZERO_NULL, /* readwrite */
236 PORT_FTP, /* defport */
237 CURLPROTO_HTTP, /* protocol */
238 PROTOPT_NONE /* flags */
244 * HTTP-proxyed FTPS protocol handler.
247 static const struct Curl_handler Curl_handler_ftps_proxy = {
249 Curl_http_setup_conn, /* setup_connection */
250 Curl_http, /* do_it */
251 Curl_http_done, /* done */
252 ZERO_NULL, /* do_more */
253 ZERO_NULL, /* connect_it */
254 ZERO_NULL, /* connecting */
255 ZERO_NULL, /* doing */
256 ZERO_NULL, /* proto_getsock */
257 ZERO_NULL, /* doing_getsock */
258 ZERO_NULL, /* domore_getsock */
259 ZERO_NULL, /* perform_getsock */
260 ZERO_NULL, /* disconnect */
261 ZERO_NULL, /* readwrite */
262 PORT_FTPS, /* defport */
263 CURLPROTO_HTTP, /* protocol */
264 PROTOPT_NONE /* flags */
269 static void close_secondarysocket(struct connectdata *conn)
271 if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET]) {
272 Curl_closesocket(conn, conn->sock[SECONDARYSOCKET]);
273 conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
275 conn->bits.tcpconnect[SECONDARYSOCKET] = FALSE;
276 conn->tunnel_state[SECONDARYSOCKET] = TUNNEL_INIT;
280 * NOTE: back in the old days, we added code in the FTP code that made NOBODY
281 * requests on files respond with headers passed to the client/stdout that
282 * looked like HTTP ones.
284 * This approach is not very elegant, it causes confusion and is error-prone.
285 * It is subject for removal at the next (or at least a future) soname bump.
286 * Until then you can test the effects of the removal by undefining the
287 * following define named CURL_FTP_HTTPSTYLE_HEAD.
289 #define CURL_FTP_HTTPSTYLE_HEAD 1
291 static void freedirs(struct ftp_conn *ftpc)
295 for(i=0; i < ftpc->dirdepth; i++) {
303 Curl_safefree(ftpc->file);
305 /* no longer of any use */
306 Curl_safefree(ftpc->newhost);
309 /* Returns non-zero if the given string contains CR (\r) or LF (\n),
310 which are not allowed within RFC 959 <string>.
311 Note: The input string is in the client's encoding which might
312 not be ASCII, so escape sequences \r & \n must be used instead
313 of hex values 0x0d & 0x0a.
315 static bool isBadFtpString(const char *string)
317 return ((NULL != strchr(string, '\r')) ||
318 (NULL != strchr(string, '\n'))) ? TRUE : FALSE;
321 /***********************************************************************
323 * AcceptServerConnect()
325 * After connection request is received from the server this function is
326 * called to accept the connection and close the listening socket
329 static CURLcode AcceptServerConnect(struct connectdata *conn)
331 struct SessionHandle *data = conn->data;
332 curl_socket_t sock = conn->sock[SECONDARYSOCKET];
333 curl_socket_t s = CURL_SOCKET_BAD;
335 struct Curl_sockaddr_storage add;
337 struct sockaddr_in add;
339 curl_socklen_t size = (curl_socklen_t) sizeof(add);
341 if(0 == getsockname(sock, (struct sockaddr *) &add, &size)) {
344 s=accept(sock, (struct sockaddr *) &add, &size);
346 Curl_closesocket(conn, sock); /* close the first socket */
348 if(CURL_SOCKET_BAD == s) {
349 failf(data, "Error accept()ing server connect");
350 return CURLE_FTP_PORT_FAILED;
352 infof(data, "Connection accepted from server\n");
353 /* when this happens within the DO state it is important that we mark us as
354 not needing DO_MORE anymore */
355 conn->bits.do_more = FALSE;
357 conn->sock[SECONDARYSOCKET] = s;
358 (void)curlx_nonblock(s, TRUE); /* enable non-blocking */
359 conn->sock_accepted[SECONDARYSOCKET] = TRUE;
361 if(data->set.fsockopt) {
364 /* activate callback for setting socket options */
365 error = data->set.fsockopt(data->set.sockopt_client,
367 CURLSOCKTYPE_ACCEPT);
370 close_secondarysocket(conn);
371 return CURLE_ABORTED_BY_CALLBACK;
380 * ftp_timeleft_accept() returns the amount of milliseconds left allowed for
381 * waiting server to connect. If the value is negative, the timeout time has
384 * The start time is stored in progress.t_acceptdata - as set with
385 * Curl_pgrsTime(..., TIMER_STARTACCEPT);
388 static long ftp_timeleft_accept(struct SessionHandle *data)
390 long timeout_ms = DEFAULT_ACCEPT_TIMEOUT;
394 if(data->set.accepttimeout > 0)
395 timeout_ms = data->set.accepttimeout;
399 /* check if the generic timeout possibly is set shorter */
400 other = Curl_timeleft(data, &now, FALSE);
401 if(other && (other < timeout_ms))
402 /* note that this also works fine for when other happens to be negative
403 due to it already having elapsed */
406 /* subtract elapsed time */
407 timeout_ms -= Curl_tvdiff(now, data->progress.t_acceptdata);
409 /* avoid returning 0 as that means no timeout! */
417 /***********************************************************************
419 * ReceivedServerConnect()
421 * After allowing server to connect to us from data port, this function
422 * checks both data connection for connection establishment and ctrl
423 * connection for a negative response regarding a failure in connecting
426 static CURLcode ReceivedServerConnect(struct connectdata *conn, bool *received)
428 struct SessionHandle *data = conn->data;
429 curl_socket_t ctrl_sock = conn->sock[FIRSTSOCKET];
430 curl_socket_t data_sock = conn->sock[SECONDARYSOCKET];
431 struct ftp_conn *ftpc = &conn->proto.ftpc;
432 struct pingpong *pp = &ftpc->pp;
440 timeout_ms = ftp_timeleft_accept(data);
441 infof(data, "Checking for server connect\n");
443 /* if a timeout was already reached, bail out */
444 failf(data, "Accept timeout occurred while waiting server connect");
445 return CURLE_FTP_ACCEPT_TIMEOUT;
448 /* First check whether there is a cached response from server */
449 if(pp->cache_size && pp->cache && pp->cache[0] > '3') {
450 /* Data connection could not be established, let's return */
451 infof(data, "There is negative response in cache while serv connect\n");
452 Curl_GetFTPResponse(&nread, conn, &ftpcode);
453 return CURLE_FTP_ACCEPT_FAILED;
456 result = Curl_socket_check(ctrl_sock, data_sock, CURL_SOCKET_BAD, 0);
458 /* see if the connection request is already here */
462 failf(data, "Error while waiting for server connect");
463 return CURLE_FTP_ACCEPT_FAILED;
464 case 0: /* Server connect is not received yet */
468 if(result & CURL_CSELECT_IN2) {
469 infof(data, "Ready to accept data connection from server\n");
472 else if(result & CURL_CSELECT_IN) {
473 infof(data, "Ctrl conn has data while waiting for data conn\n");
474 Curl_GetFTPResponse(&nread, conn, &ftpcode);
477 return CURLE_FTP_ACCEPT_FAILED;
479 return CURLE_FTP_WEIRD_SERVER_REPLY;
489 /***********************************************************************
493 * After connection from server is accepted this function is called to
494 * setup transfer parameters and initiate the data transfer.
497 static CURLcode InitiateTransfer(struct connectdata *conn)
499 struct SessionHandle *data = conn->data;
500 struct FTP *ftp = data->req.protop;
501 CURLcode result = CURLE_OK;
503 if(conn->ssl[SECONDARYSOCKET].use) {
504 /* since we only have a plaintext TCP connection here, we must now
505 * do the TLS stuff */
506 infof(data, "Doing the SSL/TLS handshake on the data stream\n");
507 result = Curl_ssl_connect(conn, SECONDARYSOCKET);
512 if(conn->proto.ftpc.state_saved == FTP_STOR) {
513 *(ftp->bytecountp)=0;
515 /* When we know we're uploading a specified file, we can get the file
516 size prior to the actual upload. */
518 Curl_pgrsSetUploadSize(data, data->state.infilesize);
520 /* set the SO_SNDBUF for the secondary socket for those who need it */
521 Curl_sndbufset(conn->sock[SECONDARYSOCKET]);
523 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, /* no download */
524 SECONDARYSOCKET, ftp->bytecountp);
528 Curl_setup_transfer(conn, SECONDARYSOCKET,
529 conn->proto.ftpc.retr_size_saved, FALSE,
530 ftp->bytecountp, -1, NULL); /* no upload here */
533 conn->proto.ftpc.pp.pending_resp = TRUE; /* expect server response */
534 state(conn, FTP_STOP);
539 /***********************************************************************
541 * AllowServerConnect()
543 * When we've issue the PORT command, we have told the server to connect to
544 * us. This function checks whether data connection is established if so it is
548 static CURLcode AllowServerConnect(struct connectdata *conn, bool *connected)
550 struct SessionHandle *data = conn->data;
552 CURLcode result = CURLE_OK;
555 infof(data, "Preparing for accepting server on data port\n");
557 /* Save the time we start accepting server connect */
558 Curl_pgrsTime(data, TIMER_STARTACCEPT);
560 timeout_ms = ftp_timeleft_accept(data);
562 /* if a timeout was already reached, bail out */
563 failf(data, "Accept timeout occurred while waiting server connect");
564 return CURLE_FTP_ACCEPT_TIMEOUT;
567 /* see if the connection request is already here */
568 result = ReceivedServerConnect(conn, connected);
573 result = AcceptServerConnect(conn);
577 result = InitiateTransfer(conn);
582 /* Add timeout to multi handle and break out of the loop */
583 if(!result && *connected == FALSE) {
584 if(data->set.accepttimeout > 0)
585 Curl_expire(data, data->set.accepttimeout);
587 Curl_expire(data, DEFAULT_ACCEPT_TIMEOUT);
594 /* macro to check for a three-digit ftp status code at the start of the
596 #define STATUSCODE(line) (ISDIGIT(line[0]) && ISDIGIT(line[1]) && \
599 /* macro to check for the last line in an FTP server response */
600 #define LASTLINE(line) (STATUSCODE(line) && (' ' == line[3]))
602 static bool ftp_endofresp(struct connectdata *conn, char *line, size_t len,
607 if((len > 3) && LASTLINE(line)) {
608 *code = curlx_sltosi(strtol(line, NULL, 10));
615 static CURLcode ftp_readresp(curl_socket_t sockfd,
617 int *ftpcode, /* return the ftp-code if done */
618 size_t *size) /* size of the response */
620 struct connectdata *conn = pp->conn;
621 struct SessionHandle *data = conn->data;
623 char * const buf = data->state.buffer;
625 CURLcode result = CURLE_OK;
628 result = Curl_pp_readresp(sockfd, pp, &code, size);
630 #if defined(HAVE_GSSAPI)
631 /* handle the security-oriented responses 6xx ***/
632 /* FIXME: some errorchecking perhaps... ***/
635 code = Curl_sec_read_msg(conn, buf, PROT_SAFE);
638 code = Curl_sec_read_msg(conn, buf, PROT_PRIVATE);
641 code = Curl_sec_read_msg(conn, buf, PROT_CONFIDENTIAL);
644 /* normal ftp stuff we pass through! */
649 /* store the latest code for later retrieval */
650 data->info.httpcode=code;
656 /* 421 means "Service not available, closing control connection." and FTP
657 * servers use it to signal that idle session timeout has been exceeded.
658 * If we ignored the response, it could end up hanging in some cases.
660 * This response code can come at any point so having it treated
661 * generically is a good idea.
663 infof(data, "We got a 421 - timeout!\n");
664 state(conn, FTP_STOP);
665 return CURLE_OPERATION_TIMEDOUT;
671 /* --- parse FTP server responses --- */
674 * Curl_GetFTPResponse() is a BLOCKING function to read the full response
675 * from a server after a command.
679 CURLcode Curl_GetFTPResponse(ssize_t *nreadp, /* return number of bytes read */
680 struct connectdata *conn,
681 int *ftpcode) /* return the ftp-code */
684 * We cannot read just one byte per read() and then go back to select() as
685 * the OpenSSL read() doesn't grok that properly.
687 * Alas, read as much as possible, split up into lines, use the ending
688 * line in a response or continue reading. */
690 curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
691 long timeout; /* timeout in milliseconds */
693 struct SessionHandle *data = conn->data;
694 CURLcode result = CURLE_OK;
695 struct ftp_conn *ftpc = &conn->proto.ftpc;
696 struct pingpong *pp = &ftpc->pp;
699 int value_to_be_ignored=0;
702 *ftpcode = 0; /* 0 for errors */
704 /* make the pointer point to something for the rest of this function */
705 ftpcode = &value_to_be_ignored;
709 while(!*ftpcode && !result) {
710 /* check and reset timeout value every lap */
711 timeout = Curl_pp_state_timeout(pp);
714 failf(data, "FTP response timeout");
715 return CURLE_OPERATION_TIMEDOUT; /* already too little time */
718 interval_ms = 1000; /* use 1 second timeout intervals */
719 if(timeout < interval_ms)
720 interval_ms = timeout;
723 * Since this function is blocking, we need to wait here for input on the
724 * connection and only then we call the response reading function. We do
725 * timeout at least every second to make the timeout check run.
727 * A caution here is that the ftp_readresp() function has a cache that may
728 * contain pieces of a response from the previous invoke and we need to
729 * make sure we don't just wait for input while there is unhandled data in
730 * that cache. But also, if the cache is there, we call ftp_readresp() and
731 * the cache wasn't good enough to continue we must not just busy-loop
732 * around this function.
736 if(pp->cache && (cache_skip < 2)) {
738 * There's a cache left since before. We then skipping the wait for
739 * socket action, unless this is the same cache like the previous round
740 * as then the cache was deemed not enough to act on and we then need to
741 * wait for more data anyway.
745 switch (Curl_socket_ready(sockfd, CURL_SOCKET_BAD, interval_ms)) {
746 case -1: /* select() error, stop reading */
747 failf(data, "FTP response aborted due to select/poll error: %d",
749 return CURLE_RECV_ERROR;
751 case 0: /* timeout */
752 if(Curl_pgrsUpdate(conn))
753 return CURLE_ABORTED_BY_CALLBACK;
754 continue; /* just continue in our loop for the timeout duration */
756 default: /* for clarity */
760 result = ftp_readresp(sockfd, pp, ftpcode, &nread);
764 if(!nread && pp->cache)
765 /* bump cache skip counter as on repeated skips we must wait for more
769 /* when we got data or there is no cache left, we reset the cache skip
775 } /* while there's buffer left and loop is requested */
777 pp->pending_resp = FALSE;
782 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
783 /* for debug purposes */
784 static const char * const ftp_state_names[]={
823 /* This is the ONLY way to change FTP state! */
824 static void _state(struct connectdata *conn,
831 struct ftp_conn *ftpc = &conn->proto.ftpc;
833 #if defined(DEBUGBUILD)
835 #if defined(CURL_DISABLE_VERBOSE_STRINGS)
838 if(ftpc->state != newstate)
839 infof(conn->data, "FTP %p (line %d) state change from %s to %s\n",
840 (void *)ftpc, lineno, ftp_state_names[ftpc->state],
841 ftp_state_names[newstate]);
845 ftpc->state = newstate;
848 static CURLcode ftp_state_user(struct connectdata *conn)
851 struct FTP *ftp = conn->data->req.protop;
853 PPSENDF(&conn->proto.ftpc.pp, "USER %s", ftp->user?ftp->user:"");
855 state(conn, FTP_USER);
856 conn->data->state.ftp_trying_alternative = FALSE;
861 static CURLcode ftp_state_pwd(struct connectdata *conn)
865 /* send PWD to discover our entry point */
866 PPSENDF(&conn->proto.ftpc.pp, "%s", "PWD");
867 state(conn, FTP_PWD);
872 /* For the FTP "protocol connect" and "doing" phases only */
873 static int ftp_getsock(struct connectdata *conn,
874 curl_socket_t *socks,
877 return Curl_pp_getsock(&conn->proto.ftpc.pp, socks, numsocks);
880 /* For the FTP "DO_MORE" phase only */
881 static int ftp_domore_getsock(struct connectdata *conn, curl_socket_t *socks,
884 struct ftp_conn *ftpc = &conn->proto.ftpc;
887 return GETSOCK_BLANK;
889 /* When in DO_MORE state, we could be either waiting for us to connect to a
890 * remote site, or we could wait for that site to connect to us. Or just
891 * handle ordinary commands.
894 if(FTP_STOP == ftpc->state) {
895 int bits = GETSOCK_READSOCK(0);
897 /* if stopped and still in this state, then we're also waiting for a
898 connect on the secondary connection */
899 socks[0] = conn->sock[FIRSTSOCKET];
901 if(!conn->data->set.ftp_use_port) {
904 /* PORT is used to tell the server to connect to us, and during that we
905 don't do happy eyeballs, but we do if we connect to the server */
906 for(s=1, i=0; i<2; i++) {
907 if(conn->tempsock[i] != CURL_SOCKET_BAD) {
908 socks[s] = conn->tempsock[i];
909 bits |= GETSOCK_WRITESOCK(s++);
914 socks[1] = conn->sock[SECONDARYSOCKET];
915 bits |= GETSOCK_WRITESOCK(1);
921 return Curl_pp_getsock(&conn->proto.ftpc.pp, socks, numsocks);
924 /* This is called after the FTP_QUOTE state is passed.
926 ftp_state_cwd() sends the range of CWD commands to the server to change to
927 the correct directory. It may also need to send MKD commands to create
928 missing ones, if that option is enabled.
930 static CURLcode ftp_state_cwd(struct connectdata *conn)
932 CURLcode result = CURLE_OK;
933 struct ftp_conn *ftpc = &conn->proto.ftpc;
936 /* already done and fine */
937 result = ftp_state_mdtm(conn);
939 ftpc->count2 = 0; /* count2 counts failed CWDs */
941 /* count3 is set to allow a MKD to fail once. In the case when first CWD
942 fails and then MKD fails (due to another session raced it to create the
943 dir) this then allows for a second try to CWD to it */
944 ftpc->count3 = (conn->data->set.ftp_create_missing_dirs==2)?1:0;
946 if(conn->bits.reuse && ftpc->entrypath) {
947 /* This is a re-used connection. Since we change directory to where the
948 transfer is taking place, we must first get back to the original dir
949 where we ended up after login: */
950 ftpc->count1 = 0; /* we count this as the first path, then we add one
951 for all upcoming ones in the ftp->dirs[] array */
952 PPSENDF(&conn->proto.ftpc.pp, "CWD %s", ftpc->entrypath);
953 state(conn, FTP_CWD);
958 /* issue the first CWD, the rest is sent when the CWD responses are
960 PPSENDF(&conn->proto.ftpc.pp, "CWD %s", ftpc->dirs[ftpc->count1 -1]);
961 state(conn, FTP_CWD);
964 /* No CWD necessary */
965 result = ftp_state_mdtm(conn);
978 static CURLcode ftp_state_use_port(struct connectdata *conn,
979 ftpport fcmd) /* start with this */
982 CURLcode result = CURLE_OK;
983 struct ftp_conn *ftpc = &conn->proto.ftpc;
984 struct SessionHandle *data=conn->data;
985 curl_socket_t portsock= CURL_SOCKET_BAD;
986 char myhost[256] = "";
988 struct Curl_sockaddr_storage ss;
989 Curl_addrinfo *res, *ai;
990 curl_socklen_t sslen;
991 char hbuf[NI_MAXHOST];
992 struct sockaddr *sa=(struct sockaddr *)&ss;
993 struct sockaddr_in * const sa4 = (void *)sa;
995 struct sockaddr_in6 * const sa6 = (void *)sa;
998 static const char mode[][5] = { "EPRT", "PORT" };
1002 char *string_ftpport = data->set.str[STRING_FTPPORT];
1003 struct Curl_dns_entry *h=NULL;
1004 unsigned short port_min = 0;
1005 unsigned short port_max = 0;
1006 unsigned short port;
1007 bool possibly_non_local = TRUE;
1011 /* Step 1, figure out what is requested,
1013 * (ipv4|ipv6|domain|interface)?(:port(-range)?)?
1016 if(data->set.str[STRING_FTPPORT] &&
1017 (strlen(data->set.str[STRING_FTPPORT]) > 1)) {
1020 size_t addrlen = INET6_ADDRSTRLEN > strlen(string_ftpport) ?
1021 INET6_ADDRSTRLEN : strlen(string_ftpport);
1023 size_t addrlen = INET_ADDRSTRLEN > strlen(string_ftpport) ?
1024 INET_ADDRSTRLEN : strlen(string_ftpport);
1026 char *ip_start = string_ftpport;
1027 char *ip_end = NULL;
1028 char *port_start = NULL;
1029 char *port_sep = NULL;
1031 addr = calloc(addrlen+1, 1);
1033 return CURLE_OUT_OF_MEMORY;
1036 if(*string_ftpport == '[') {
1037 /* [ipv6]:port(-range) */
1038 ip_start = string_ftpport + 1;
1039 if((ip_end = strchr(string_ftpport, ']')) != NULL )
1040 strncpy(addr, ip_start, ip_end - ip_start);
1044 if(*string_ftpport == ':') {
1046 ip_end = string_ftpport;
1048 else if((ip_end = strchr(string_ftpport, ':')) != NULL) {
1049 /* either ipv6 or (ipv4|domain|interface):port(-range) */
1051 if(Curl_inet_pton(AF_INET6, string_ftpport, sa6) == 1) {
1053 port_min = port_max = 0;
1054 strcpy(addr, string_ftpport);
1055 ip_end = NULL; /* this got no port ! */
1059 /* (ipv4|domain|interface):port(-range) */
1060 strncpy(addr, string_ftpport, ip_end - ip_start );
1063 /* ipv4|interface */
1064 strcpy(addr, string_ftpport);
1066 /* parse the port */
1067 if(ip_end != NULL) {
1068 if((port_start = strchr(ip_end, ':')) != NULL) {
1069 port_min = curlx_ultous(strtoul(port_start+1, NULL, 10));
1070 if((port_sep = strchr(port_start, '-')) != NULL) {
1071 port_max = curlx_ultous(strtoul(port_sep + 1, NULL, 10));
1074 port_max = port_min;
1078 /* correct errors like:
1080 * :-4711 , in this case port_min is (unsigned)-1,
1081 * therefore port_min > port_max for all cases
1082 * but port_max = (unsigned)-1
1084 if(port_min > port_max )
1085 port_min = port_max = 0;
1089 /* attempt to get the address of the given interface name */
1090 switch(Curl_if2ip(conn->ip_addr->ai_family,
1091 Curl_ipv6_scope(conn->ip_addr->ai_addr),
1092 conn->scope_id, addr, hbuf, sizeof(hbuf))) {
1093 case IF2IP_NOT_FOUND:
1094 /* not an interface, use the given string as host name instead */
1097 case IF2IP_AF_NOT_SUPPORTED:
1098 return CURLE_FTP_PORT_FAILED;
1100 host = hbuf; /* use the hbuf for host name */
1104 /* there was only a port(-range) given, default the host */
1106 } /* data->set.ftpport */
1109 /* not an interface and not a host name, get default by extracting
1110 the IP from the control connection */
1113 if(getsockname(conn->sock[FIRSTSOCKET], sa, &sslen)) {
1114 failf(data, "getsockname() failed: %s",
1115 Curl_strerror(conn, SOCKERRNO) );
1117 return CURLE_FTP_PORT_FAILED;
1119 switch(sa->sa_family) {
1122 Curl_inet_ntop(sa->sa_family, &sa6->sin6_addr, hbuf, sizeof(hbuf));
1126 Curl_inet_ntop(sa->sa_family, &sa4->sin_addr, hbuf, sizeof(hbuf));
1129 host = hbuf; /* use this host name */
1130 possibly_non_local = FALSE; /* we know it is local now */
1133 /* resolv ip/host to ip */
1134 rc = Curl_resolv(conn, host, 0, &h);
1135 if(rc == CURLRESOLV_PENDING)
1136 (void)Curl_resolver_wait_resolv(conn, &h);
1139 /* when we return from this function, we can forget about this entry
1140 to we can unlock it now already */
1141 Curl_resolv_unlock(data, h);
1144 res = NULL; /* failure! */
1147 failf(data, "failed to resolve the address provided to PORT: %s", host);
1149 return CURLE_FTP_PORT_FAILED;
1155 /* step 2, create a socket for the requested address */
1157 portsock = CURL_SOCKET_BAD;
1159 for(ai = res; ai; ai = ai->ai_next) {
1160 result = Curl_socket(conn, ai, NULL, &portsock);
1168 failf(data, "socket failure: %s", Curl_strerror(conn, error));
1169 return CURLE_FTP_PORT_FAILED;
1172 /* step 3, bind to a suitable local address */
1174 memcpy(sa, ai->ai_addr, ai->ai_addrlen);
1175 sslen = ai->ai_addrlen;
1177 for(port = port_min; port <= port_max;) {
1178 if(sa->sa_family == AF_INET)
1179 sa4->sin_port = htons(port);
1182 sa6->sin6_port = htons(port);
1184 /* Try binding the given address. */
1185 if(bind(portsock, sa, sslen) ) {
1188 if(possibly_non_local && (error == EADDRNOTAVAIL)) {
1189 /* The requested bind address is not local. Use the address used for
1190 * the control connection instead and restart the port loop
1193 infof(data, "bind(port=%hu) on non-local address failed: %s\n", port,
1194 Curl_strerror(conn, error) );
1197 if(getsockname(conn->sock[FIRSTSOCKET], sa, &sslen)) {
1198 failf(data, "getsockname() failed: %s",
1199 Curl_strerror(conn, SOCKERRNO) );
1200 Curl_closesocket(conn, portsock);
1201 return CURLE_FTP_PORT_FAILED;
1204 possibly_non_local = FALSE; /* don't try this again */
1207 else if(error != EADDRINUSE && error != EACCES) {
1208 failf(data, "bind(port=%hu) failed: %s", port,
1209 Curl_strerror(conn, error) );
1210 Curl_closesocket(conn, portsock);
1211 return CURLE_FTP_PORT_FAILED;
1220 /* maybe all ports were in use already*/
1221 if(port > port_max) {
1222 failf(data, "bind() failed, we ran out of ports!");
1223 Curl_closesocket(conn, portsock);
1224 return CURLE_FTP_PORT_FAILED;
1227 /* get the name again after the bind() so that we can extract the
1228 port number it uses now */
1230 if(getsockname(portsock, (struct sockaddr *)sa, &sslen)) {
1231 failf(data, "getsockname() failed: %s",
1232 Curl_strerror(conn, SOCKERRNO) );
1233 Curl_closesocket(conn, portsock);
1234 return CURLE_FTP_PORT_FAILED;
1237 /* step 4, listen on the socket */
1239 if(listen(portsock, 1)) {
1240 failf(data, "socket failure: %s", Curl_strerror(conn, SOCKERRNO));
1241 Curl_closesocket(conn, portsock);
1242 return CURLE_FTP_PORT_FAILED;
1245 /* step 5, send the proper FTP command */
1247 /* get a plain printable version of the numerical address to work with
1249 Curl_printable_address(ai, myhost, sizeof(myhost));
1252 if(!conn->bits.ftp_use_eprt && conn->bits.ipv6)
1253 /* EPRT is disabled but we are connected to a IPv6 host, so we ignore the
1254 request and enable EPRT again! */
1255 conn->bits.ftp_use_eprt = TRUE;
1258 for(; fcmd != DONE; fcmd++) {
1260 if(!conn->bits.ftp_use_eprt && (EPRT == fcmd))
1261 /* if disabled, goto next */
1264 if((PORT == fcmd) && sa->sa_family != AF_INET)
1265 /* PORT is IPv4 only */
1268 switch(sa->sa_family) {
1270 port = ntohs(sa4->sin_port);
1274 port = ntohs(sa6->sin6_port);
1278 continue; /* might as well skip this */
1283 * Two fine examples from RFC2428;
1285 * EPRT |1|132.235.1.2|6275|
1287 * EPRT |2|1080::8:800:200C:417A|5282|
1290 result = Curl_pp_sendf(&ftpc->pp, "%s |%d|%s|%hu|", mode[fcmd],
1291 sa->sa_family == AF_INET?1:2,
1294 failf(data, "Failure sending EPRT command: %s",
1295 curl_easy_strerror(result));
1296 Curl_closesocket(conn, portsock);
1297 /* don't retry using PORT */
1298 ftpc->count1 = PORT;
1300 state(conn, FTP_STOP);
1305 else if(PORT == fcmd) {
1306 char *source = myhost;
1309 /* translate x.x.x.x to x,x,x,x */
1310 while(source && *source) {
1319 snprintf(dest, 20, ",%d,%d", (int)(port>>8), (int)(port&0xff));
1321 result = Curl_pp_sendf(&ftpc->pp, "%s %s", mode[fcmd], tmp);
1323 failf(data, "Failure sending PORT command: %s",
1324 curl_easy_strerror(result));
1325 Curl_closesocket(conn, portsock);
1327 state(conn, FTP_STOP);
1334 /* store which command was sent */
1335 ftpc->count1 = fcmd;
1337 close_secondarysocket(conn);
1339 /* we set the secondary socket variable to this for now, it is only so that
1340 the cleanup function will close it in case we fail before the true
1341 secondary stuff is made */
1342 conn->sock[SECONDARYSOCKET] = portsock;
1344 /* this tcpconnect assignment below is a hackish work-around to make the
1345 multi interface with active FTP work - as it will not wait for a
1346 (passive) connect in Curl_is_connected().
1348 The *proper* fix is to make sure that the active connection from the
1349 server is done in a non-blocking way. Currently, it is still BLOCKING.
1351 conn->bits.tcpconnect[SECONDARYSOCKET] = TRUE;
1353 state(conn, FTP_PORT);
1357 static CURLcode ftp_state_use_pasv(struct connectdata *conn)
1359 struct ftp_conn *ftpc = &conn->proto.ftpc;
1360 CURLcode result = CURLE_OK;
1362 Here's the excecutive summary on what to do:
1364 PASV is RFC959, expect:
1365 227 Entering Passive Mode (a1,a2,a3,a4,p1,p2)
1367 LPSV is RFC1639, expect:
1368 228 Entering Long Passive Mode (4,4,a1,a2,a3,a4,2,p1,p2)
1370 EPSV is RFC2428, expect:
1371 229 Entering Extended Passive Mode (|||port|)
1375 static const char mode[][5] = { "EPSV", "PASV" };
1379 if(!conn->bits.ftp_use_epsv && conn->bits.ipv6)
1380 /* EPSV is disabled but we are connected to a IPv6 host, so we ignore the
1381 request and enable EPSV again! */
1382 conn->bits.ftp_use_epsv = TRUE;
1385 modeoff = conn->bits.ftp_use_epsv?0:1;
1387 PPSENDF(&ftpc->pp, "%s", mode[modeoff]);
1389 ftpc->count1 = modeoff;
1390 state(conn, FTP_PASV);
1391 infof(conn->data, "Connect data stream passively\n");
1397 * ftp_state_prepare_transfer() starts PORT, PASV or PRET etc.
1399 * REST is the last command in the chain of commands when a "head"-like
1400 * request is made. Thus, if an actual transfer is to be made this is where we
1401 * take off for real.
1403 static CURLcode ftp_state_prepare_transfer(struct connectdata *conn)
1405 CURLcode result = CURLE_OK;
1406 struct FTP *ftp = conn->data->req.protop;
1407 struct SessionHandle *data = conn->data;
1409 if(ftp->transfer != FTPTRANSFER_BODY) {
1410 /* doesn't transfer any data */
1412 /* still possibly do PRE QUOTE jobs */
1413 state(conn, FTP_RETR_PREQUOTE);
1414 result = ftp_state_quote(conn, TRUE, FTP_RETR_PREQUOTE);
1416 else if(data->set.ftp_use_port) {
1417 /* We have chosen to use the PORT (or similar) command */
1418 result = ftp_state_use_port(conn, EPRT);
1421 /* We have chosen (this is default) to use the PASV (or similar) command */
1422 if(data->set.ftp_use_pret) {
1423 /* The user has requested that we send a PRET command
1424 to prepare the server for the upcoming PASV */
1425 if(!conn->proto.ftpc.file) {
1426 PPSENDF(&conn->proto.ftpc.pp, "PRET %s",
1427 data->set.str[STRING_CUSTOMREQUEST]?
1428 data->set.str[STRING_CUSTOMREQUEST]:
1429 (data->set.ftp_list_only?"NLST":"LIST"));
1431 else if(data->set.upload) {
1432 PPSENDF(&conn->proto.ftpc.pp, "PRET STOR %s", conn->proto.ftpc.file);
1435 PPSENDF(&conn->proto.ftpc.pp, "PRET RETR %s", conn->proto.ftpc.file);
1437 state(conn, FTP_PRET);
1440 result = ftp_state_use_pasv(conn);
1446 static CURLcode ftp_state_rest(struct connectdata *conn)
1448 CURLcode result = CURLE_OK;
1449 struct FTP *ftp = conn->data->req.protop;
1450 struct ftp_conn *ftpc = &conn->proto.ftpc;
1452 if((ftp->transfer != FTPTRANSFER_BODY) && ftpc->file) {
1453 /* if a "head"-like request is being made (on a file) */
1455 /* Determine if server can respond to REST command and therefore
1456 whether it supports range */
1457 PPSENDF(&conn->proto.ftpc.pp, "REST %d", 0);
1459 state(conn, FTP_REST);
1462 result = ftp_state_prepare_transfer(conn);
1467 static CURLcode ftp_state_size(struct connectdata *conn)
1469 CURLcode result = CURLE_OK;
1470 struct FTP *ftp = conn->data->req.protop;
1471 struct ftp_conn *ftpc = &conn->proto.ftpc;
1473 if((ftp->transfer == FTPTRANSFER_INFO) && ftpc->file) {
1474 /* if a "head"-like request is being made (on a file) */
1476 /* we know ftpc->file is a valid pointer to a file name */
1477 PPSENDF(&ftpc->pp, "SIZE %s", ftpc->file);
1479 state(conn, FTP_SIZE);
1482 result = ftp_state_rest(conn);
1487 static CURLcode ftp_state_list(struct connectdata *conn)
1489 CURLcode result = CURLE_OK;
1490 struct SessionHandle *data = conn->data;
1492 /* If this output is to be machine-parsed, the NLST command might be better
1493 to use, since the LIST command output is not specified or standard in any
1494 way. It has turned out that the NLST list output is not the same on all
1495 servers either... */
1498 if FTPFILE_NOCWD was specified, we are currently in
1499 the user's home directory, so we should add the path
1500 as argument for the LIST / NLST / or custom command.
1501 Whether the server will support this, is uncertain.
1503 The other ftp_filemethods will CWD into dir/dir/ first and
1504 then just do LIST (in that case: nothing to do here)
1506 char *cmd, *lstArg, *slashPos;
1509 if((data->set.ftp_filemethod == FTPFILE_NOCWD) &&
1511 data->state.path[0] &&
1512 strchr(data->state.path, '/')) {
1514 lstArg = strdup(data->state.path);
1516 return CURLE_OUT_OF_MEMORY;
1518 /* Check if path does not end with /, as then we cut off the file part */
1519 if(lstArg[strlen(lstArg) - 1] != '/') {
1521 /* chop off the file part if format is dir/dir/file */
1522 slashPos = strrchr(lstArg, '/');
1524 *(slashPos+1) = '\0';
1528 cmd = aprintf( "%s%s%s",
1529 data->set.str[STRING_CUSTOMREQUEST]?
1530 data->set.str[STRING_CUSTOMREQUEST]:
1531 (data->set.ftp_list_only?"NLST":"LIST"),
1533 lstArg? lstArg: "" );
1537 return CURLE_OUT_OF_MEMORY;
1540 result = Curl_pp_sendf(&conn->proto.ftpc.pp, "%s", cmd);
1548 state(conn, FTP_LIST);
1553 static CURLcode ftp_state_retr_prequote(struct connectdata *conn)
1555 CURLcode result = CURLE_OK;
1557 /* We've sent the TYPE, now we must send the list of prequote strings */
1559 result = ftp_state_quote(conn, TRUE, FTP_RETR_PREQUOTE);
1564 static CURLcode ftp_state_stor_prequote(struct connectdata *conn)
1566 CURLcode result = CURLE_OK;
1568 /* We've sent the TYPE, now we must send the list of prequote strings */
1570 result = ftp_state_quote(conn, TRUE, FTP_STOR_PREQUOTE);
1575 static CURLcode ftp_state_type(struct connectdata *conn)
1577 CURLcode result = CURLE_OK;
1578 struct FTP *ftp = conn->data->req.protop;
1579 struct SessionHandle *data = conn->data;
1580 struct ftp_conn *ftpc = &conn->proto.ftpc;
1582 /* If we have selected NOBODY and HEADER, it means that we only want file
1583 information. Which in FTP can't be much more than the file size and
1585 if(data->set.opt_no_body && ftpc->file &&
1586 ftp_need_type(conn, data->set.prefer_ascii)) {
1587 /* The SIZE command is _not_ RFC 959 specified, and therefor many servers
1588 may not support it! It is however the only way we have to get a file's
1591 ftp->transfer = FTPTRANSFER_INFO;
1592 /* this means no actual transfer will be made */
1594 /* Some servers return different sizes for different modes, and thus we
1595 must set the proper type before we check the size */
1596 result = ftp_nb_type(conn, data->set.prefer_ascii, FTP_TYPE);
1601 result = ftp_state_size(conn);
1606 /* This is called after the CWD commands have been done in the beginning of
1608 static CURLcode ftp_state_mdtm(struct connectdata *conn)
1610 CURLcode result = CURLE_OK;
1611 struct SessionHandle *data = conn->data;
1612 struct ftp_conn *ftpc = &conn->proto.ftpc;
1614 /* Requested time of file or time-depended transfer? */
1615 if((data->set.get_filetime || data->set.timecondition) && ftpc->file) {
1617 /* we have requested to get the modified-time of the file, this is a white
1618 spot as the MDTM is not mentioned in RFC959 */
1619 PPSENDF(&ftpc->pp, "MDTM %s", ftpc->file);
1621 state(conn, FTP_MDTM);
1624 result = ftp_state_type(conn);
1630 /* This is called after the TYPE and possible quote commands have been sent */
1631 static CURLcode ftp_state_ul_setup(struct connectdata *conn,
1634 CURLcode result = CURLE_OK;
1635 struct FTP *ftp = conn->data->req.protop;
1636 struct SessionHandle *data = conn->data;
1637 struct ftp_conn *ftpc = &conn->proto.ftpc;
1638 int seekerr = CURL_SEEKFUNC_OK;
1640 if((data->state.resume_from && !sizechecked) ||
1641 ((data->state.resume_from > 0) && sizechecked)) {
1642 /* we're about to continue the uploading of a file */
1643 /* 1. get already existing file's size. We use the SIZE command for this
1644 which may not exist in the server! The SIZE command is not in
1647 /* 2. This used to set REST. But since we can do append, we
1648 don't another ftp command. We just skip the source file
1649 offset and then we APPEND the rest on the file instead */
1651 /* 3. pass file-size number of bytes in the source file */
1652 /* 4. lower the infilesize counter */
1653 /* => transfer as usual */
1655 if(data->state.resume_from < 0 ) {
1656 /* Got no given size to start from, figure it out */
1657 PPSENDF(&ftpc->pp, "SIZE %s", ftpc->file);
1658 state(conn, FTP_STOR_SIZE);
1663 data->set.ftp_append = TRUE;
1665 /* Let's read off the proper amount of bytes from the input. */
1666 if(conn->seek_func) {
1667 seekerr = conn->seek_func(conn->seek_client, data->state.resume_from,
1671 if(seekerr != CURL_SEEKFUNC_OK) {
1672 if(seekerr != CURL_SEEKFUNC_CANTSEEK) {
1673 failf(data, "Could not seek stream");
1674 return CURLE_FTP_COULDNT_USE_REST;
1676 /* seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */
1678 curl_off_t passed=0;
1680 size_t readthisamountnow =
1681 (data->state.resume_from - passed > CURL_OFF_T_C(BUFSIZE)) ?
1682 BUFSIZE : curlx_sotouz(data->state.resume_from - passed);
1684 size_t actuallyread =
1685 data->state.fread_func(data->state.buffer, 1, readthisamountnow,
1688 passed += actuallyread;
1689 if((actuallyread == 0) || (actuallyread > readthisamountnow)) {
1690 /* this checks for greater-than only to make sure that the
1691 CURL_READFUNC_ABORT return code still aborts */
1692 failf(data, "Failed to read data");
1693 return CURLE_FTP_COULDNT_USE_REST;
1695 } while(passed < data->state.resume_from);
1698 /* now, decrease the size of the read */
1699 if(data->state.infilesize>0) {
1700 data->state.infilesize -= data->state.resume_from;
1702 if(data->state.infilesize <= 0) {
1703 infof(data, "File already completely uploaded\n");
1705 /* no data to transfer */
1706 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1708 /* Set ->transfer so that we won't get any error in
1709 * ftp_done() because we didn't transfer anything! */
1710 ftp->transfer = FTPTRANSFER_NONE;
1712 state(conn, FTP_STOP);
1716 /* we've passed, proceed as normal */
1719 PPSENDF(&ftpc->pp, data->set.ftp_append?"APPE %s":"STOR %s",
1722 state(conn, FTP_STOR);
1727 static CURLcode ftp_state_quote(struct connectdata *conn,
1731 CURLcode result = CURLE_OK;
1732 struct SessionHandle *data = conn->data;
1733 struct FTP *ftp = data->req.protop;
1734 struct ftp_conn *ftpc = &conn->proto.ftpc;
1736 struct curl_slist *item;
1741 item = data->set.quote;
1743 case FTP_RETR_PREQUOTE:
1744 case FTP_STOR_PREQUOTE:
1745 item = data->set.prequote;
1748 item = data->set.postquote;
1754 * 'count1' to iterate over the commands to send
1755 * 'count2' to store wether to allow commands to fail
1766 /* Skip count1 items in the linked list */
1767 while((i< ftpc->count1) && item) {
1772 char *cmd = item->data;
1775 ftpc->count2 = 1; /* the sent command is allowed to fail */
1778 ftpc->count2 = 0; /* failure means cancel operation */
1780 PPSENDF(&ftpc->pp, "%s", cmd);
1781 state(conn, instate);
1787 /* No more quote to send, continue to ... */
1791 result = ftp_state_cwd(conn);
1793 case FTP_RETR_PREQUOTE:
1794 if(ftp->transfer != FTPTRANSFER_BODY)
1795 state(conn, FTP_STOP);
1797 if(ftpc->known_filesize != -1) {
1798 Curl_pgrsSetDownloadSize(data, ftpc->known_filesize);
1799 result = ftp_state_retr(conn, ftpc->known_filesize);
1802 if(data->set.ignorecl) {
1803 /* This code is to support download of growing files. It prevents
1804 the state machine from requesting the file size from the
1805 server. With an unknown file size the download continues until
1806 the server terminates it, otherwise the client stops if the
1807 received byte count exceeds the reported file size. Set option
1808 CURLOPT_IGNORE_CONTENT_LENGTH to 1 to enable this behavior.*/
1809 PPSENDF(&ftpc->pp, "RETR %s", ftpc->file);
1810 state(conn, FTP_RETR);
1813 PPSENDF(&ftpc->pp, "SIZE %s", ftpc->file);
1814 state(conn, FTP_RETR_SIZE);
1819 case FTP_STOR_PREQUOTE:
1820 result = ftp_state_ul_setup(conn, FALSE);
1830 /* called from ftp_state_pasv_resp to switch to PASV in case of EPSV
1832 static CURLcode ftp_epsv_disable(struct connectdata *conn)
1834 CURLcode result = CURLE_OK;
1836 if(conn->bits.ipv6) {
1837 /* We can't disable EPSV when doing IPv6, so this is instead a fail */
1838 failf(conn->data, "Failed EPSV attempt, exiting\n");
1839 return CURLE_FTP_WEIRD_SERVER_REPLY;
1842 infof(conn->data, "Failed EPSV attempt. Disabling EPSV\n");
1843 /* disable it for next transfer */
1844 conn->bits.ftp_use_epsv = FALSE;
1845 conn->data->state.errorbuf = FALSE; /* allow error message to get
1847 PPSENDF(&conn->proto.ftpc.pp, "%s", "PASV");
1848 conn->proto.ftpc.count1++;
1849 /* remain in/go to the FTP_PASV state */
1850 state(conn, FTP_PASV);
1855 * Perform the necessary magic that needs to be done once the TCP connection
1856 * to the proxy has completed.
1858 static CURLcode proxy_magic(struct connectdata *conn,
1859 char *newhost, unsigned short newport,
1862 CURLcode result = CURLE_OK;
1863 struct SessionHandle *data = conn->data;
1865 #if defined(CURL_DISABLE_PROXY)
1872 switch(conn->proxytype) {
1873 case CURLPROXY_SOCKS5:
1874 case CURLPROXY_SOCKS5_HOSTNAME:
1875 result = Curl_SOCKS5(conn->proxyuser, conn->proxypasswd, newhost,
1876 newport, SECONDARYSOCKET, conn);
1879 case CURLPROXY_SOCKS4:
1880 result = Curl_SOCKS4(conn->proxyuser, newhost, newport,
1881 SECONDARYSOCKET, conn, FALSE);
1884 case CURLPROXY_SOCKS4A:
1885 result = Curl_SOCKS4(conn->proxyuser, newhost, newport,
1886 SECONDARYSOCKET, conn, TRUE);
1889 case CURLPROXY_HTTP:
1890 case CURLPROXY_HTTP_1_0:
1891 /* do nothing here. handled later. */
1894 failf(data, "unknown proxytype option given");
1895 result = CURLE_COULDNT_CONNECT;
1899 if(conn->bits.tunnel_proxy && conn->bits.httpproxy) {
1901 /* We want "seamless" FTP operations through HTTP proxy tunnel */
1903 /* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the
1904 * member conn->proto.http; we want FTP through HTTP and we have to
1905 * change the member temporarily for connecting to the HTTP proxy. After
1906 * Curl_proxyCONNECT we have to set back the member to the original
1907 * struct FTP pointer
1909 struct HTTP http_proxy;
1910 struct FTP *ftp_save = data->req.protop;
1911 memset(&http_proxy, 0, sizeof(http_proxy));
1912 data->req.protop = &http_proxy;
1914 result = Curl_proxyCONNECT(conn, SECONDARYSOCKET, newhost, newport, TRUE);
1916 data->req.protop = ftp_save;
1921 if(conn->tunnel_state[SECONDARYSOCKET] != TUNNEL_COMPLETE) {
1922 /* the CONNECT procedure is not complete, the tunnel is not yet up */
1923 state(conn, FTP_STOP); /* this phase is completed */
1933 static char *control_address(struct connectdata *conn)
1935 /* Returns the control connection IP address.
1936 If a proxy tunnel is used, returns the original host name instead, because
1937 the effective control connection address is the proxy address,
1938 not the ftp host. */
1939 if(conn->bits.tunnel_proxy ||
1940 conn->proxytype == CURLPROXY_SOCKS5 ||
1941 conn->proxytype == CURLPROXY_SOCKS5_HOSTNAME ||
1942 conn->proxytype == CURLPROXY_SOCKS4 ||
1943 conn->proxytype == CURLPROXY_SOCKS4A)
1944 return conn->host.name;
1946 return conn->ip_addr_str;
1949 static CURLcode ftp_state_pasv_resp(struct connectdata *conn,
1952 struct ftp_conn *ftpc = &conn->proto.ftpc;
1954 struct SessionHandle *data=conn->data;
1955 struct Curl_dns_entry *addr=NULL;
1957 unsigned short connectport; /* the local port connect() should use! */
1958 char *str=&data->state.buffer[4]; /* start on the first letter */
1960 /* if we come here again, make sure the former name is cleared */
1961 Curl_safefree(ftpc->newhost);
1963 if((ftpc->count1 == 0) &&
1965 /* positive EPSV response */
1966 char *ptr = strchr(str, '(');
1971 if(5 == sscanf(ptr, "%c%c%c%u%c",
1977 const char sep1 = separator[0];
1980 /* The four separators should be identical, or else this is an oddly
1981 formatted reply and we bail out immediately. */
1982 for(i=1; i<4; i++) {
1983 if(separator[i] != sep1) {
1984 ptr=NULL; /* set to NULL to signal error */
1989 failf(data, "Illegal port number in EPSV reply");
1990 return CURLE_FTP_WEIRD_PASV_REPLY;
1993 ftpc->newport = (unsigned short)(num & 0xffff);
1994 ftpc->newhost = strdup(control_address(conn));
1996 return CURLE_OUT_OF_MEMORY;
2003 failf(data, "Weirdly formatted EPSV reply");
2004 return CURLE_FTP_WEIRD_PASV_REPLY;
2007 else if((ftpc->count1 == 1) &&
2009 /* positive PASV response */
2014 * Scan for a sequence of six comma-separated numbers and use them as
2015 * IP+port indicators.
2017 * Found reply-strings include:
2018 * "227 Entering Passive Mode (127,0,0,1,4,51)"
2019 * "227 Data transfer will passively listen to 127,0,0,1,4,51"
2020 * "227 Entering passive mode. 127,0,0,1,4,51"
2023 if(6 == sscanf(str, "%d,%d,%d,%d,%d,%d",
2024 &ip[0], &ip[1], &ip[2], &ip[3],
2025 &port[0], &port[1]))
2031 failf(data, "Couldn't interpret the 227-response");
2032 return CURLE_FTP_WEIRD_227_FORMAT;
2035 /* we got OK from server */
2036 if(data->set.ftp_skip_ip) {
2037 /* told to ignore the remotely given IP but instead use the host we used
2038 for the control connection */
2039 infof(data, "Skip %d.%d.%d.%d for data connection, re-use %s instead\n",
2040 ip[0], ip[1], ip[2], ip[3],
2042 ftpc->newhost = strdup(control_address(conn));
2045 ftpc->newhost = aprintf("%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
2048 return CURLE_OUT_OF_MEMORY;
2050 ftpc->newport = (unsigned short)(((port[0]<<8) + port[1]) & 0xffff);
2052 else if(ftpc->count1 == 0) {
2053 /* EPSV failed, move on to PASV */
2054 return ftp_epsv_disable(conn);
2057 failf(data, "Bad PASV/EPSV response: %03d", ftpcode);
2058 return CURLE_FTP_WEIRD_PASV_REPLY;
2061 if(conn->bits.proxy) {
2063 * This connection uses a proxy and we need to connect to the proxy again
2064 * here. We don't want to rely on a former host lookup that might've
2065 * expired now, instead we remake the lookup here and now!
2067 rc = Curl_resolv(conn, conn->proxy.name, (int)conn->port, &addr);
2068 if(rc == CURLRESOLV_PENDING)
2069 /* BLOCKING, ignores the return code but 'addr' will be NULL in
2071 (void)Curl_resolver_wait_resolv(conn, &addr);
2074 (unsigned short)conn->port; /* we connect to the proxy's port */
2077 failf(data, "Can't resolve proxy host %s:%hu",
2078 conn->proxy.name, connectport);
2079 return CURLE_FTP_CANT_GET_HOST;
2083 /* normal, direct, ftp connection */
2084 rc = Curl_resolv(conn, ftpc->newhost, ftpc->newport, &addr);
2085 if(rc == CURLRESOLV_PENDING)
2087 (void)Curl_resolver_wait_resolv(conn, &addr);
2089 connectport = ftpc->newport; /* we connect to the remote port */
2092 failf(data, "Can't resolve new host %s:%hu", ftpc->newhost, connectport);
2093 return CURLE_FTP_CANT_GET_HOST;
2097 conn->bits.tcpconnect[SECONDARYSOCKET] = FALSE;
2098 result = Curl_connecthost(conn, addr);
2101 Curl_resolv_unlock(data, addr); /* we're done using this address */
2102 if(ftpc->count1 == 0 && ftpcode == 229)
2103 return ftp_epsv_disable(conn);
2110 * When this is used from the multi interface, this might've returned with
2111 * the 'connected' set to FALSE and thus we are now awaiting a non-blocking
2112 * connect to connect.
2115 if(data->set.verbose)
2116 /* this just dumps information about this second connection */
2117 ftp_pasv_verbose(conn, addr->addr, ftpc->newhost, connectport);
2119 Curl_resolv_unlock(data, addr); /* we're done using this address */
2120 conn->bits.do_more = TRUE;
2121 state(conn, FTP_STOP); /* this phase is completed */
2126 static CURLcode ftp_state_port_resp(struct connectdata *conn,
2129 struct SessionHandle *data = conn->data;
2130 struct ftp_conn *ftpc = &conn->proto.ftpc;
2131 ftpport fcmd = (ftpport)ftpc->count1;
2132 CURLcode result = CURLE_OK;
2134 /* The FTP spec tells a positive response should have code 200.
2135 Be more permissive here to tolerate deviant servers. */
2136 if(ftpcode / 100 != 2) {
2137 /* the command failed */
2140 infof(data, "disabling EPRT usage\n");
2141 conn->bits.ftp_use_eprt = FALSE;
2146 failf(data, "Failed to do PORT");
2147 result = CURLE_FTP_PORT_FAILED;
2151 result = ftp_state_use_port(conn, fcmd);
2154 infof(data, "Connect data stream actively\n");
2155 state(conn, FTP_STOP); /* end of DO phase */
2156 result = ftp_dophase_done(conn, FALSE);
2162 static CURLcode ftp_state_mdtm_resp(struct connectdata *conn,
2165 CURLcode result = CURLE_OK;
2166 struct SessionHandle *data=conn->data;
2167 struct FTP *ftp = data->req.protop;
2168 struct ftp_conn *ftpc = &conn->proto.ftpc;
2173 /* we got a time. Format should be: "YYYYMMDDHHMMSS[.sss]" where the
2174 last .sss part is optional and means fractions of a second */
2175 int year, month, day, hour, minute, second;
2176 char *buf = data->state.buffer;
2177 if(6 == sscanf(buf+4, "%04d%02d%02d%02d%02d%02d",
2178 &year, &month, &day, &hour, &minute, &second)) {
2179 /* we have a time, reformat it */
2180 time_t secs=time(NULL);
2181 /* using the good old yacc/bison yuck */
2182 snprintf(buf, sizeof(conn->data->state.buffer),
2183 "%04d%02d%02d %02d:%02d:%02d GMT",
2184 year, month, day, hour, minute, second);
2185 /* now, convert this into a time() value: */
2186 data->info.filetime = (long)curl_getdate(buf, &secs);
2189 #ifdef CURL_FTP_HTTPSTYLE_HEAD
2190 /* If we asked for a time of the file and we actually got one as well,
2191 we "emulate" a HTTP-style header in our output. */
2193 if(data->set.opt_no_body &&
2195 data->set.get_filetime &&
2196 (data->info.filetime>=0) ) {
2197 time_t filetime = (time_t)data->info.filetime;
2199 const struct tm *tm = &buffer;
2201 result = Curl_gmtime(filetime, &buffer);
2205 /* format: "Tue, 15 Nov 1994 12:45:26" */
2206 snprintf(buf, BUFSIZE-1,
2207 "Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n",
2208 Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
2210 Curl_month[tm->tm_mon],
2215 result = Curl_client_write(conn, CLIENTWRITE_BOTH, buf, 0);
2218 } /* end of a ridiculous amount of conditionals */
2223 infof(data, "unsupported MDTM reply format\n");
2225 case 550: /* "No such file or directory" */
2226 failf(data, "Given file does not exist");
2227 result = CURLE_FTP_COULDNT_RETR_FILE;
2231 if(data->set.timecondition) {
2232 if((data->info.filetime > 0) && (data->set.timevalue > 0)) {
2233 switch(data->set.timecondition) {
2234 case CURL_TIMECOND_IFMODSINCE:
2236 if(data->info.filetime <= data->set.timevalue) {
2237 infof(data, "The requested document is not new enough\n");
2238 ftp->transfer = FTPTRANSFER_NONE; /* mark to not transfer data */
2239 data->info.timecond = TRUE;
2240 state(conn, FTP_STOP);
2244 case CURL_TIMECOND_IFUNMODSINCE:
2245 if(data->info.filetime > data->set.timevalue) {
2246 infof(data, "The requested document is not old enough\n");
2247 ftp->transfer = FTPTRANSFER_NONE; /* mark to not transfer data */
2248 data->info.timecond = TRUE;
2249 state(conn, FTP_STOP);
2256 infof(data, "Skipping time comparison\n");
2261 result = ftp_state_type(conn);
2266 static CURLcode ftp_state_type_resp(struct connectdata *conn,
2270 CURLcode result = CURLE_OK;
2271 struct SessionHandle *data=conn->data;
2273 if(ftpcode/100 != 2) {
2274 /* "sasserftpd" and "(u)r(x)bot ftpd" both responds with 226 after a
2275 successful 'TYPE I'. While that is not as RFC959 says, it is still a
2276 positive response code and we allow that. */
2277 failf(data, "Couldn't set desired mode");
2278 return CURLE_FTP_COULDNT_SET_TYPE;
2281 infof(data, "Got a %03d response code instead of the assumed 200\n",
2284 if(instate == FTP_TYPE)
2285 result = ftp_state_size(conn);
2286 else if(instate == FTP_LIST_TYPE)
2287 result = ftp_state_list(conn);
2288 else if(instate == FTP_RETR_TYPE)
2289 result = ftp_state_retr_prequote(conn);
2290 else if(instate == FTP_STOR_TYPE)
2291 result = ftp_state_stor_prequote(conn);
2296 static CURLcode ftp_state_retr(struct connectdata *conn,
2297 curl_off_t filesize)
2299 CURLcode result = CURLE_OK;
2300 struct SessionHandle *data=conn->data;
2301 struct FTP *ftp = data->req.protop;
2302 struct ftp_conn *ftpc = &conn->proto.ftpc;
2304 if(data->set.max_filesize && (filesize > data->set.max_filesize)) {
2305 failf(data, "Maximum file size exceeded");
2306 return CURLE_FILESIZE_EXCEEDED;
2308 ftp->downloadsize = filesize;
2310 if(data->state.resume_from) {
2311 /* We always (attempt to) get the size of downloads, so it is done before
2312 this even when not doing resumes. */
2313 if(filesize == -1) {
2314 infof(data, "ftp server doesn't support SIZE\n");
2315 /* We couldn't get the size and therefore we can't know if there really
2316 is a part of the file left to get, although the server will just
2317 close the connection when we start the connection so it won't cause
2318 us any harm, just not make us exit as nicely. */
2321 /* We got a file size report, so we check that there actually is a
2322 part of the file left to get, or else we go home. */
2323 if(data->state.resume_from< 0) {
2324 /* We're supposed to download the last abs(from) bytes */
2325 if(filesize < -data->state.resume_from) {
2326 failf(data, "Offset (%" CURL_FORMAT_CURL_OFF_T
2327 ") was beyond file size (%" CURL_FORMAT_CURL_OFF_T ")",
2328 data->state.resume_from, filesize);
2329 return CURLE_BAD_DOWNLOAD_RESUME;
2331 /* convert to size to download */
2332 ftp->downloadsize = -data->state.resume_from;
2333 /* download from where? */
2334 data->state.resume_from = filesize - ftp->downloadsize;
2337 if(filesize < data->state.resume_from) {
2338 failf(data, "Offset (%" CURL_FORMAT_CURL_OFF_T
2339 ") was beyond file size (%" CURL_FORMAT_CURL_OFF_T ")",
2340 data->state.resume_from, filesize);
2341 return CURLE_BAD_DOWNLOAD_RESUME;
2343 /* Now store the number of bytes we are expected to download */
2344 ftp->downloadsize = filesize-data->state.resume_from;
2348 if(ftp->downloadsize == 0) {
2349 /* no data to transfer */
2350 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
2351 infof(data, "File already completely downloaded\n");
2353 /* Set ->transfer so that we won't get any error in ftp_done()
2354 * because we didn't transfer the any file */
2355 ftp->transfer = FTPTRANSFER_NONE;
2356 state(conn, FTP_STOP);
2360 /* Set resume file transfer offset */
2361 infof(data, "Instructs server to resume from offset %"
2362 CURL_FORMAT_CURL_OFF_T "\n", data->state.resume_from);
2364 PPSENDF(&ftpc->pp, "REST %" CURL_FORMAT_CURL_OFF_T,
2365 data->state.resume_from);
2367 state(conn, FTP_RETR_REST);
2371 PPSENDF(&ftpc->pp, "RETR %s", ftpc->file);
2372 state(conn, FTP_RETR);
2378 static CURLcode ftp_state_size_resp(struct connectdata *conn,
2382 CURLcode result = CURLE_OK;
2383 struct SessionHandle *data=conn->data;
2384 curl_off_t filesize;
2385 char *buf = data->state.buffer;
2387 /* get the size from the ascii string: */
2388 filesize = (ftpcode == 213)?curlx_strtoofft(buf+4, NULL, 0):-1;
2390 if(instate == FTP_SIZE) {
2391 #ifdef CURL_FTP_HTTPSTYLE_HEAD
2392 if(-1 != filesize) {
2393 snprintf(buf, sizeof(data->state.buffer),
2394 "Content-Length: %" CURL_FORMAT_CURL_OFF_T "\r\n", filesize);
2395 result = Curl_client_write(conn, CLIENTWRITE_BOTH, buf, 0);
2400 Curl_pgrsSetDownloadSize(data, filesize);
2401 result = ftp_state_rest(conn);
2403 else if(instate == FTP_RETR_SIZE) {
2404 Curl_pgrsSetDownloadSize(data, filesize);
2405 result = ftp_state_retr(conn, filesize);
2407 else if(instate == FTP_STOR_SIZE) {
2408 data->state.resume_from = filesize;
2409 result = ftp_state_ul_setup(conn, TRUE);
2415 static CURLcode ftp_state_rest_resp(struct connectdata *conn,
2419 CURLcode result = CURLE_OK;
2420 struct ftp_conn *ftpc = &conn->proto.ftpc;
2425 #ifdef CURL_FTP_HTTPSTYLE_HEAD
2426 if(ftpcode == 350) {
2427 char buffer[24]= { "Accept-ranges: bytes\r\n" };
2428 result = Curl_client_write(conn, CLIENTWRITE_BOTH, buffer, 0);
2433 result = ftp_state_prepare_transfer(conn);
2437 if(ftpcode != 350) {
2438 failf(conn->data, "Couldn't use REST");
2439 result = CURLE_FTP_COULDNT_USE_REST;
2442 PPSENDF(&ftpc->pp, "RETR %s", ftpc->file);
2443 state(conn, FTP_RETR);
2451 static CURLcode ftp_state_stor_resp(struct connectdata *conn,
2452 int ftpcode, ftpstate instate)
2454 CURLcode result = CURLE_OK;
2455 struct SessionHandle *data = conn->data;
2458 failf(data, "Failed FTP upload: %0d", ftpcode);
2459 state(conn, FTP_STOP);
2460 /* oops, we never close the sockets! */
2461 return CURLE_UPLOAD_FAILED;
2464 conn->proto.ftpc.state_saved = instate;
2466 /* PORT means we are now awaiting the server to connect to us. */
2467 if(data->set.ftp_use_port) {
2470 state(conn, FTP_STOP); /* no longer in STOR state */
2472 result = AllowServerConnect(conn, &connected);
2477 struct ftp_conn *ftpc = &conn->proto.ftpc;
2478 infof(data, "Data conn was not available immediately\n");
2479 ftpc->wait_data_conn = TRUE;
2485 return InitiateTransfer(conn);
2488 /* for LIST and RETR responses */
2489 static CURLcode ftp_state_get_resp(struct connectdata *conn,
2493 CURLcode result = CURLE_OK;
2494 struct SessionHandle *data = conn->data;
2495 struct FTP *ftp = data->req.protop;
2496 char *buf = data->state.buffer;
2498 if((ftpcode == 150) || (ftpcode == 125)) {
2502 150 Opening BINARY mode data connection for /etc/passwd (2241
2503 bytes). (ok, the file is being transferred)
2506 150 Opening ASCII mode data connection for /bin/ls
2509 150 ASCII data connection for /bin/ls (137.167.104.91,37445) (0 bytes).
2512 150 Opening ASCII mode data connection for [file] (0.0.0.0,0) (545 bytes)
2515 125 Data connection already open; Transfer starting. */
2517 curl_off_t size=-1; /* default unknown size */
2521 * It appears that there are FTP-servers that return size 0 for files when
2522 * SIZE is used on the file while being in BINARY mode. To work around
2523 * that (stupid) behavior, we attempt to parse the RETR response even if
2524 * the SIZE returned size zero.
2526 * Debugging help from Salvatore Sorrentino on February 26, 2003.
2529 if((instate != FTP_LIST) &&
2530 !data->set.prefer_ascii &&
2531 (ftp->downloadsize < 1)) {
2533 * It seems directory listings either don't show the size or very
2534 * often uses size 0 anyway. ASCII transfers may very well turn out
2535 * that the transferred amount of data is not the same as this line
2536 * tells, why using this number in those cases only confuses us.
2538 * Example D above makes this parsing a little tricky */
2540 bytes=strstr(buf, " bytes");
2542 long in=(long)(bytes-buf);
2543 /* this is a hint there is size information in there! ;-) */
2545 /* scan for the left parenthesis and break there */
2548 /* skip only digits */
2549 if(!ISDIGIT(*bytes)) {
2553 /* one more estep backwards */
2556 /* if we have nothing but digits: */
2558 /* get the number! */
2559 size = curlx_strtoofft(bytes, NULL, 0);
2563 else if(ftp->downloadsize > -1)
2564 size = ftp->downloadsize;
2566 if(size > data->req.maxdownload && data->req.maxdownload > 0)
2567 size = data->req.size = data->req.maxdownload;
2568 else if((instate != FTP_LIST) && (data->set.prefer_ascii))
2569 size = -1; /* kludge for servers that understate ASCII mode file size */
2571 infof(data, "Maxdownload = %" CURL_FORMAT_CURL_OFF_T "\n",
2572 data->req.maxdownload);
2574 if(instate != FTP_LIST)
2575 infof(data, "Getting file with size: %" CURL_FORMAT_CURL_OFF_T "\n",
2579 conn->proto.ftpc.state_saved = instate;
2580 conn->proto.ftpc.retr_size_saved = size;
2582 if(data->set.ftp_use_port) {
2585 result = AllowServerConnect(conn, &connected);
2590 struct ftp_conn *ftpc = &conn->proto.ftpc;
2591 infof(data, "Data conn was not available immediately\n");
2592 state(conn, FTP_STOP);
2593 ftpc->wait_data_conn = TRUE;
2597 return InitiateTransfer(conn);
2600 if((instate == FTP_LIST) && (ftpcode == 450)) {
2601 /* simply no matching files in the dir listing */
2602 ftp->transfer = FTPTRANSFER_NONE; /* don't download anything */
2603 state(conn, FTP_STOP); /* this phase is over */
2606 failf(data, "RETR response: %03d", ftpcode);
2607 return instate == FTP_RETR && ftpcode == 550?
2608 CURLE_REMOTE_FILE_NOT_FOUND:
2609 CURLE_FTP_COULDNT_RETR_FILE;
2616 /* after USER, PASS and ACCT */
2617 static CURLcode ftp_state_loggedin(struct connectdata *conn)
2619 CURLcode result = CURLE_OK;
2621 if(conn->ssl[FIRSTSOCKET].use) {
2622 /* PBSZ = PROTECTION BUFFER SIZE.
2624 The 'draft-murray-auth-ftp-ssl' (draft 12, page 7) says:
2626 Specifically, the PROT command MUST be preceded by a PBSZ
2627 command and a PBSZ command MUST be preceded by a successful
2628 security data exchange (the TLS negotiation in this case)
2630 ... (and on page 8):
2632 Thus the PBSZ command must still be issued, but must have a
2633 parameter of '0' to indicate that no buffering is taking place
2634 and the data connection should not be encapsulated.
2636 PPSENDF(&conn->proto.ftpc.pp, "PBSZ %d", 0);
2637 state(conn, FTP_PBSZ);
2640 result = ftp_state_pwd(conn);
2645 /* for USER and PASS responses */
2646 static CURLcode ftp_state_user_resp(struct connectdata *conn,
2650 CURLcode result = CURLE_OK;
2651 struct SessionHandle *data = conn->data;
2652 struct FTP *ftp = data->req.protop;
2653 struct ftp_conn *ftpc = &conn->proto.ftpc;
2654 (void)instate; /* no use for this yet */
2656 /* some need password anyway, and others just return 2xx ignored */
2657 if((ftpcode == 331) && (ftpc->state == FTP_USER)) {
2658 /* 331 Password required for ...
2659 (the server requires to send the user's password too) */
2660 PPSENDF(&ftpc->pp, "PASS %s", ftp->passwd?ftp->passwd:"");
2661 state(conn, FTP_PASS);
2663 else if(ftpcode/100 == 2) {
2664 /* 230 User ... logged in.
2665 (the user logged in with or without password) */
2666 result = ftp_state_loggedin(conn);
2668 else if(ftpcode == 332) {
2669 if(data->set.str[STRING_FTP_ACCOUNT]) {
2670 PPSENDF(&ftpc->pp, "ACCT %s", data->set.str[STRING_FTP_ACCOUNT]);
2671 state(conn, FTP_ACCT);
2674 failf(data, "ACCT requested but none available");
2675 result = CURLE_LOGIN_DENIED;
2679 /* All other response codes, like:
2681 530 User ... access denied
2682 (the server denies to log the specified user) */
2684 if(conn->data->set.str[STRING_FTP_ALTERNATIVE_TO_USER] &&
2685 !conn->data->state.ftp_trying_alternative) {
2686 /* Ok, USER failed. Let's try the supplied command. */
2687 PPSENDF(&conn->proto.ftpc.pp, "%s",
2688 conn->data->set.str[STRING_FTP_ALTERNATIVE_TO_USER]);
2689 conn->data->state.ftp_trying_alternative = TRUE;
2690 state(conn, FTP_USER);
2694 failf(data, "Access denied: %03d", ftpcode);
2695 result = CURLE_LOGIN_DENIED;
2701 /* for ACCT response */
2702 static CURLcode ftp_state_acct_resp(struct connectdata *conn,
2705 CURLcode result = CURLE_OK;
2706 struct SessionHandle *data = conn->data;
2707 if(ftpcode != 230) {
2708 failf(data, "ACCT rejected by server: %03d", ftpcode);
2709 result = CURLE_FTP_WEIRD_PASS_REPLY; /* FIX */
2712 result = ftp_state_loggedin(conn);
2718 static CURLcode ftp_statemach_act(struct connectdata *conn)
2721 curl_socket_t sock = conn->sock[FIRSTSOCKET];
2722 struct SessionHandle *data=conn->data;
2724 struct ftp_conn *ftpc = &conn->proto.ftpc;
2725 struct pingpong *pp = &ftpc->pp;
2726 static const char ftpauth[][4] = { "SSL", "TLS" };
2730 return Curl_pp_flushsend(pp);
2732 result = ftp_readresp(sock, pp, &ftpcode, &nread);
2737 /* we have now received a full FTP server response */
2738 switch(ftpc->state) {
2741 /* 230 User logged in - already! */
2742 return ftp_state_user_resp(conn, ftpcode, ftpc->state);
2743 else if(ftpcode != 220) {
2744 failf(data, "Got a %03d ftp-server response when 220 was expected",
2746 return CURLE_FTP_WEIRD_SERVER_REPLY;
2749 /* We have received a 220 response fine, now we proceed. */
2752 /* If not anonymous login, try a secure login. Note that this
2753 procedure is still BLOCKING. */
2755 Curl_sec_request_prot(conn, "private");
2756 /* We set private first as default, in case the line below fails to
2757 set a valid level */
2758 Curl_sec_request_prot(conn, data->set.str[STRING_KRB_LEVEL]);
2760 if(Curl_sec_login(conn))
2761 infof(data, "Logging in with password in cleartext!\n");
2763 infof(data, "Authentication successful\n");
2767 if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
2768 /* We don't have a SSL/TLS connection yet, but FTPS is
2769 requested. Try a FTPS connection now */
2772 switch(data->set.ftpsslauth) {
2773 case CURLFTPAUTH_DEFAULT:
2774 case CURLFTPAUTH_SSL:
2775 ftpc->count2 = 1; /* add one to get next */
2778 case CURLFTPAUTH_TLS:
2779 ftpc->count2 = -1; /* subtract one to get next */
2783 failf(data, "unsupported parameter to CURLOPT_FTPSSLAUTH: %d",
2784 (int)data->set.ftpsslauth);
2785 return CURLE_UNKNOWN_OPTION; /* we don't know what to do */
2787 PPSENDF(&ftpc->pp, "AUTH %s", ftpauth[ftpc->count1]);
2788 state(conn, FTP_AUTH);
2791 result = ftp_state_user(conn);
2799 /* we have gotten the response to a previous AUTH command */
2801 /* RFC2228 (page 5) says:
2803 * If the server is willing to accept the named security mechanism,
2804 * and does not require any security data, it must respond with
2805 * reply code 234/334.
2808 if((ftpcode == 234) || (ftpcode == 334)) {
2809 /* Curl_ssl_connect is BLOCKING */
2810 result = Curl_ssl_connect(conn, FIRSTSOCKET);
2812 conn->ssl[SECONDARYSOCKET].use = FALSE; /* clear-text data */
2813 result = ftp_state_user(conn);
2816 else if(ftpc->count3 < 1) {
2818 ftpc->count1 += ftpc->count2; /* get next attempt */
2819 result = Curl_pp_sendf(&ftpc->pp, "AUTH %s", ftpauth[ftpc->count1]);
2820 /* remain in this same state */
2823 if(data->set.use_ssl > CURLUSESSL_TRY)
2824 /* we failed and CURLUSESSL_CONTROL or CURLUSESSL_ALL is set */
2825 result = CURLE_USE_SSL_FAILED;
2827 /* ignore the failure and continue */
2828 result = ftp_state_user(conn);
2837 result = ftp_state_user_resp(conn, ftpcode, ftpc->state);
2841 result = ftp_state_acct_resp(conn, ftpcode);
2845 PPSENDF(&ftpc->pp, "PROT %c",
2846 data->set.use_ssl == CURLUSESSL_CONTROL ? 'C' : 'P');
2847 state(conn, FTP_PROT);
2852 if(ftpcode/100 == 2)
2853 /* We have enabled SSL for the data connection! */
2854 conn->ssl[SECONDARYSOCKET].use =
2855 (data->set.use_ssl != CURLUSESSL_CONTROL) ? TRUE : FALSE;
2856 /* FTP servers typically responds with 500 if they decide to reject
2858 else if(data->set.use_ssl > CURLUSESSL_CONTROL)
2859 /* we failed and bails out */
2860 return CURLE_USE_SSL_FAILED;
2862 if(data->set.ftp_ccc) {
2863 /* CCC - Clear Command Channel
2865 PPSENDF(&ftpc->pp, "%s", "CCC");
2866 state(conn, FTP_CCC);
2869 result = ftp_state_pwd(conn);
2877 /* First shut down the SSL layer (note: this call will block) */
2878 result = Curl_ssl_shutdown(conn, FIRSTSOCKET);
2881 failf(conn->data, "Failed to clear the command channel (CCC)");
2886 /* Then continue as normal */
2887 result = ftp_state_pwd(conn);
2893 if(ftpcode == 257) {
2894 char *ptr=&data->state.buffer[4]; /* start on the first letter */
2898 dir = malloc(nread + 1);
2900 return CURLE_OUT_OF_MEMORY;
2902 /* Reply format is like
2903 257<space>[rubbish]"<directory-name>"<space><commentary> and the
2906 The directory name can contain any character; embedded
2907 double-quotes should be escaped by double-quotes (the
2908 "quote-doubling" convention).
2911 /* scan for the first double-quote for non-standard responses */
2912 while(ptr < &data->state.buffer[sizeof(data->state.buffer)]
2913 && *ptr != '\n' && *ptr != '\0' && *ptr != '"')
2917 /* it started good */
2919 for(store = dir; *ptr;) {
2921 if('\"' == ptr[1]) {
2922 /* "quote-doubling" */
2928 *store = '\0'; /* zero terminate */
2929 break; /* get out of this loop */
2938 /* If the path name does not look like an absolute path (i.e.: it
2939 does not start with a '/'), we probably need some server-dependent
2940 adjustments. For example, this is the case when connecting to
2941 an OS400 FTP server: this server supports two name syntaxes,
2942 the default one being incompatible with standard pathes. In
2943 addition, this server switches automatically to the regular path
2944 syntax when one is encountered in a command: this results in
2945 having an entrypath in the wrong syntax when later used in CWD.
2946 The method used here is to check the server OS: we do it only
2947 if the path name looks strange to minimize overhead on other
2950 if(!ftpc->server_os && dir[0] != '/') {
2952 result = Curl_pp_sendf(&ftpc->pp, "%s", "SYST");
2957 Curl_safefree(ftpc->entrypath);
2958 ftpc->entrypath = dir; /* remember this */
2959 infof(data, "Entry path is '%s'\n", ftpc->entrypath);
2960 /* also save it where getinfo can access it: */
2961 data->state.most_recent_ftp_entrypath = ftpc->entrypath;
2962 state(conn, FTP_SYST);
2966 Curl_safefree(ftpc->entrypath);
2967 ftpc->entrypath = dir; /* remember this */
2968 infof(data, "Entry path is '%s'\n", ftpc->entrypath);
2969 /* also save it where getinfo can access it: */
2970 data->state.most_recent_ftp_entrypath = ftpc->entrypath;
2973 /* couldn't get the path */
2975 infof(data, "Failed to figure out path\n");
2978 state(conn, FTP_STOP); /* we are done with the CONNECT phase! */
2979 DEBUGF(infof(data, "protocol connect phase DONE\n"));
2983 if(ftpcode == 215) {
2984 char *ptr=&data->state.buffer[4]; /* start on the first letter */
2988 os = malloc(nread + 1);
2990 return CURLE_OUT_OF_MEMORY;
2992 /* Reply format is like
2993 215<space><OS-name><space><commentary>
2997 for(store = os; *ptr && *ptr != ' ';)
2999 *store = '\0'; /* zero terminate */
3001 /* Check for special servers here. */
3003 if(strequal(os, "OS/400")) {
3004 /* Force OS400 name format 1. */
3005 result = Curl_pp_sendf(&ftpc->pp, "%s", "SITE NAMEFMT 1");
3010 /* remember target server OS */
3011 Curl_safefree(ftpc->server_os);
3012 ftpc->server_os = os;
3013 state(conn, FTP_NAMEFMT);
3017 /* Nothing special for the target server. */
3018 /* remember target server OS */
3019 Curl_safefree(ftpc->server_os);
3020 ftpc->server_os = os;
3024 /* Cannot identify server OS. Continue anyway and cross fingers. */
3027 state(conn, FTP_STOP); /* we are done with the CONNECT phase! */
3028 DEBUGF(infof(data, "protocol connect phase DONE\n"));
3032 if(ftpcode == 250) {
3033 /* Name format change successful: reload initial path. */
3034 ftp_state_pwd(conn);
3038 state(conn, FTP_STOP); /* we are done with the CONNECT phase! */
3039 DEBUGF(infof(data, "protocol connect phase DONE\n"));
3044 case FTP_RETR_PREQUOTE:
3045 case FTP_STOR_PREQUOTE:
3046 if((ftpcode >= 400) && !ftpc->count2) {
3047 /* failure response code, and not allowed to fail */
3048 failf(conn->data, "QUOT command failed with %03d", ftpcode);
3049 return CURLE_QUOTE_ERROR;
3051 result = ftp_state_quote(conn, FALSE, ftpc->state);
3058 if(ftpcode/100 != 2) {
3059 /* failure to CWD there */
3060 if(conn->data->set.ftp_create_missing_dirs &&
3061 ftpc->count1 && !ftpc->count2) {
3063 ftpc->count2++; /* counter to prevent CWD-MKD loops */
3064 PPSENDF(&ftpc->pp, "MKD %s", ftpc->dirs[ftpc->count1 - 1]);
3065 state(conn, FTP_MKD);
3068 /* return failure */
3069 failf(data, "Server denied you to change to the given directory");
3070 ftpc->cwdfail = TRUE; /* don't remember this path as we failed
3072 return CURLE_REMOTE_ACCESS_DENIED;
3078 if(++ftpc->count1 <= ftpc->dirdepth) {
3080 PPSENDF(&ftpc->pp, "CWD %s", ftpc->dirs[ftpc->count1 - 1]);
3083 result = ftp_state_mdtm(conn);
3091 if((ftpcode/100 != 2) && !ftpc->count3--) {
3092 /* failure to MKD the dir */
3093 failf(data, "Failed to MKD dir: %03d", ftpcode);
3094 return CURLE_REMOTE_ACCESS_DENIED;
3096 state(conn, FTP_CWD);
3098 PPSENDF(&ftpc->pp, "CWD %s", ftpc->dirs[ftpc->count1 - 1]);
3102 result = ftp_state_mdtm_resp(conn, ftpcode);
3109 result = ftp_state_type_resp(conn, ftpcode, ftpc->state);
3115 result = ftp_state_size_resp(conn, ftpcode, ftpc->state);
3120 result = ftp_state_rest_resp(conn, ftpcode, ftpc->state);
3124 if(ftpcode != 200) {
3125 /* there only is this one standard OK return code. */
3126 failf(data, "PRET command not accepted: %03d", ftpcode);
3127 return CURLE_FTP_PRET_FAILED;
3129 result = ftp_state_use_pasv(conn);
3133 result = ftp_state_pasv_resp(conn, ftpcode);
3137 result = ftp_state_port_resp(conn, ftpcode);
3142 result = ftp_state_get_resp(conn, ftpcode, ftpc->state);
3146 result = ftp_state_stor_resp(conn, ftpcode, ftpc->state);
3150 /* fallthrough, just stop! */
3152 /* internal error */
3153 state(conn, FTP_STOP);
3162 /* called repeatedly until done from multi.c */
3163 static CURLcode ftp_multi_statemach(struct connectdata *conn,
3166 struct ftp_conn *ftpc = &conn->proto.ftpc;
3167 CURLcode result = Curl_pp_statemach(&ftpc->pp, FALSE);
3169 /* Check for the state outside of the Curl_socket_ready() return code checks
3170 since at times we are in fact already in this state when this function
3172 *done = (ftpc->state == FTP_STOP) ? TRUE : FALSE;
3177 static CURLcode ftp_block_statemach(struct connectdata *conn)
3179 struct ftp_conn *ftpc = &conn->proto.ftpc;
3180 struct pingpong *pp = &ftpc->pp;
3181 CURLcode result = CURLE_OK;
3183 while(ftpc->state != FTP_STOP) {
3184 result = Curl_pp_statemach(pp, TRUE);
3193 * ftp_connect() should do everything that is to be considered a part of
3194 * the connection phase.
3196 * The variable 'done' points to will be TRUE if the protocol-layer connect
3197 * phase is done when this function returns, or FALSE if not.
3200 static CURLcode ftp_connect(struct connectdata *conn,
3201 bool *done) /* see description above */
3204 struct ftp_conn *ftpc = &conn->proto.ftpc;
3205 struct pingpong *pp = &ftpc->pp;
3207 *done = FALSE; /* default to not done yet */
3209 /* We always support persistent connections on ftp */
3210 connkeep(conn, "FTP default");
3212 pp->response_time = RESP_TIMEOUT; /* set default response time-out */
3213 pp->statemach_act = ftp_statemach_act;
3214 pp->endofresp = ftp_endofresp;
3217 if(conn->handler->flags & PROTOPT_SSL) {
3219 result = Curl_ssl_connect(conn, FIRSTSOCKET);
3224 Curl_pp_init(pp); /* init the generic pingpong data */
3226 /* When we connect, we start in the state where we await the 220
3228 state(conn, FTP_WAIT220);
3230 result = ftp_multi_statemach(conn, done);
3235 /***********************************************************************
3239 * The DONE function. This does what needs to be done after a single DO has
3242 * Input argument is already checked for validity.
3244 static CURLcode ftp_done(struct connectdata *conn, CURLcode status,
3247 struct SessionHandle *data = conn->data;
3248 struct FTP *ftp = data->req.protop;
3249 struct ftp_conn *ftpc = &conn->proto.ftpc;
3250 struct pingpong *pp = &ftpc->pp;
3253 CURLcode result = CURLE_OK;
3254 bool was_ctl_valid = ftpc->ctl_valid;
3256 const char *path_to_use = data->state.path;
3259 /* When the easy handle is removed from the multi while libcurl is still
3260 * trying to resolve the host name, it seems that the ftp struct is not
3261 * yet initialized, but the removal action calls Curl_done() which calls
3262 * this function. So we simply return success if no ftp pointer is set.
3267 case CURLE_BAD_DOWNLOAD_RESUME:
3268 case CURLE_FTP_WEIRD_PASV_REPLY:
3269 case CURLE_FTP_PORT_FAILED:
3270 case CURLE_FTP_ACCEPT_FAILED:
3271 case CURLE_FTP_ACCEPT_TIMEOUT:
3272 case CURLE_FTP_COULDNT_SET_TYPE:
3273 case CURLE_FTP_COULDNT_RETR_FILE:
3274 case CURLE_PARTIAL_FILE:
3275 case CURLE_UPLOAD_FAILED:
3276 case CURLE_REMOTE_ACCESS_DENIED:
3277 case CURLE_FILESIZE_EXCEEDED:
3278 case CURLE_REMOTE_FILE_NOT_FOUND:
3279 case CURLE_WRITE_ERROR:
3280 /* the connection stays alive fine even though this happened */
3282 case CURLE_OK: /* doesn't affect the control connection's status */
3284 ftpc->ctl_valid = was_ctl_valid;
3287 /* until we cope better with prematurely ended requests, let them
3288 * fallback as if in complete failure */
3289 default: /* by default, an error means the control connection is
3290 wedged and should not be used anymore */
3291 ftpc->ctl_valid = FALSE;
3292 ftpc->cwdfail = TRUE; /* set this TRUE to prevent us to remember the
3293 current path, as this connection is going */
3294 connclose(conn, "FTP ended with bad error code");
3295 result = status; /* use the already set error code */
3299 /* now store a copy of the directory we are in */
3300 free(ftpc->prevpath);
3302 if(data->set.wildcardmatch) {
3303 if(data->set.chunk_end && ftpc->file) {
3304 data->set.chunk_end(data->wildcard.customptr);
3306 ftpc->known_filesize = -1;
3309 /* get the "raw" path */
3310 path = curl_easy_unescape(data, path_to_use, 0, NULL);
3312 /* out of memory, but we can limp along anyway (and should try to
3313 * since we may already be in the out of memory cleanup path) */
3315 result = CURLE_OUT_OF_MEMORY;
3316 ftpc->ctl_valid = FALSE; /* mark control connection as bad */
3317 connclose(conn, "FTP: out of memory!"); /* mark for connection closure */
3318 ftpc->prevpath = NULL; /* no path remembering */
3321 size_t flen = ftpc->file?strlen(ftpc->file):0; /* file is "raw" already */
3322 size_t dlen = strlen(path)-flen;
3323 if(!ftpc->cwdfail) {
3324 if(dlen && (data->set.ftp_filemethod != FTPFILE_NOCWD)) {
3325 ftpc->prevpath = path;
3327 /* if 'path' is not the whole string */
3328 ftpc->prevpath[dlen]=0; /* terminate */
3331 /* we never changed dir */
3332 ftpc->prevpath=strdup("");
3336 infof(data, "Remembering we are in dir \"%s\"\n", ftpc->prevpath);
3339 ftpc->prevpath = NULL; /* no path */
3343 /* free the dir tree and file parts */
3346 /* shut down the socket to inform the server we're done */
3349 shutdown(conn->sock[SECONDARYSOCKET], 2); /* SD_BOTH */
3352 if(conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD) {
3353 if(!result && ftpc->dont_check && data->req.maxdownload > 0) {
3354 /* partial download completed */
3355 result = Curl_pp_sendf(pp, "%s", "ABOR");
3357 failf(data, "Failure sending ABOR command: %s",
3358 curl_easy_strerror(result));
3359 ftpc->ctl_valid = FALSE; /* mark control connection as bad */
3360 connclose(conn, "ABOR command failed"); /* connection closure */
3364 if(conn->ssl[SECONDARYSOCKET].use) {
3365 /* The secondary socket is using SSL so we must close down that part
3366 first before we close the socket for real */
3367 Curl_ssl_close(conn, SECONDARYSOCKET);
3369 /* Note that we keep "use" set to TRUE since that (next) connection is
3370 still requested to use SSL */
3372 close_secondarysocket(conn);
3375 if(!result && (ftp->transfer == FTPTRANSFER_BODY) && ftpc->ctl_valid &&
3376 pp->pending_resp && !premature) {
3378 * Let's see what the server says about the transfer we just performed,
3379 * but lower the timeout as sometimes this connection has died while the
3380 * data has been transferred. This happens when doing through NATs etc that
3381 * abandon old silent connections.
3383 long old_time = pp->response_time;
3385 pp->response_time = 60*1000; /* give it only a minute for now */
3386 pp->response = Curl_tvnow(); /* timeout relative now */
3388 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
3390 pp->response_time = old_time; /* set this back to previous value */
3392 if(!nread && (CURLE_OPERATION_TIMEDOUT == result)) {
3393 failf(data, "control connection looks dead");
3394 ftpc->ctl_valid = FALSE; /* mark control connection as bad */
3395 connclose(conn, "Timeout or similar in FTP DONE operation"); /* close */
3401 if(ftpc->dont_check && data->req.maxdownload > 0) {
3402 /* we have just sent ABOR and there is no reliable way to check if it was
3403 * successful or not; we have to close the connection now */
3404 infof(data, "partial download completed, closing connection\n");
3405 connclose(conn, "Partial download with no ability to check");
3409 if(!ftpc->dont_check) {
3410 /* 226 Transfer complete, 250 Requested file action okay, completed. */
3411 if((ftpcode != 226) && (ftpcode != 250)) {
3412 failf(data, "server did not report OK, got %d", ftpcode);
3413 result = CURLE_PARTIAL_FILE;
3418 if(result || premature)
3419 /* the response code from the transfer showed an error already so no
3420 use checking further */
3422 else if(data->set.upload) {
3423 if((-1 != data->state.infilesize) &&
3424 (data->state.infilesize != *ftp->bytecountp) &&
3426 (ftp->transfer == FTPTRANSFER_BODY)) {
3427 failf(data, "Uploaded unaligned file size (%" CURL_FORMAT_CURL_OFF_T
3428 " out of %" CURL_FORMAT_CURL_OFF_T " bytes)",
3429 *ftp->bytecountp, data->state.infilesize);
3430 result = CURLE_PARTIAL_FILE;
3434 if((-1 != data->req.size) &&
3435 (data->req.size != *ftp->bytecountp) &&
3436 #ifdef CURL_DO_LINEEND_CONV
3437 /* Most FTP servers don't adjust their file SIZE response for CRLFs, so
3438 * we'll check to see if the discrepancy can be explained by the number
3439 * of CRLFs we've changed to LFs.
3441 ((data->req.size + data->state.crlf_conversions) !=
3442 *ftp->bytecountp) &&
3443 #endif /* CURL_DO_LINEEND_CONV */
3444 (data->req.maxdownload != *ftp->bytecountp)) {
3445 failf(data, "Received only partial file: %" CURL_FORMAT_CURL_OFF_T
3446 " bytes", *ftp->bytecountp);
3447 result = CURLE_PARTIAL_FILE;
3449 else if(!ftpc->dont_check &&
3450 !*ftp->bytecountp &&
3451 (data->req.size>0)) {
3452 failf(data, "No data was received!");
3453 result = CURLE_FTP_COULDNT_RETR_FILE;
3457 /* clear these for next connection */
3458 ftp->transfer = FTPTRANSFER_BODY;
3459 ftpc->dont_check = FALSE;
3461 /* Send any post-transfer QUOTE strings? */
3462 if(!status && !result && !premature && data->set.postquote)
3463 result = ftp_sendquote(conn, data->set.postquote);
3468 /***********************************************************************
3472 * Where a 'quote' means a list of custom commands to send to the server.
3473 * The quote list is passed as an argument.
3479 CURLcode ftp_sendquote(struct connectdata *conn, struct curl_slist *quote)
3481 struct curl_slist *item;
3485 struct ftp_conn *ftpc = &conn->proto.ftpc;
3486 struct pingpong *pp = &ftpc->pp;
3491 char *cmd = item->data;
3492 bool acceptfail = FALSE;
3494 /* if a command starts with an asterisk, which a legal FTP command never
3495 can, the command will be allowed to fail without it causing any
3496 aborts or cancels etc. It will cause libcurl to act as if the command
3497 is successful, whatever the server reponds. */
3504 PPSENDF(&conn->proto.ftpc.pp, "%s", cmd);
3506 pp->response = Curl_tvnow(); /* timeout relative now */
3508 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
3512 if(!acceptfail && (ftpcode >= 400)) {
3513 failf(conn->data, "QUOT string not accepted: %s", cmd);
3514 return CURLE_QUOTE_ERROR;
3524 /***********************************************************************
3528 * Returns TRUE if we in the current situation should send TYPE
3530 static int ftp_need_type(struct connectdata *conn,
3533 return conn->proto.ftpc.transfertype != (ascii_wanted?'A':'I');
3536 /***********************************************************************
3540 * Set TYPE. We only deal with ASCII or BINARY so this function
3542 * If the transfer type is not sent, simulate on OK response in newstate
3544 static CURLcode ftp_nb_type(struct connectdata *conn,
3545 bool ascii, ftpstate newstate)
3547 struct ftp_conn *ftpc = &conn->proto.ftpc;
3549 char want = (char)(ascii?'A':'I');
3551 if(ftpc->transfertype == want) {
3552 state(conn, newstate);
3553 return ftp_state_type_resp(conn, 200, newstate);
3556 PPSENDF(&ftpc->pp, "TYPE %c", want);
3557 state(conn, newstate);
3559 /* keep track of our current transfer type */
3560 ftpc->transfertype = want;
3564 /***************************************************************************
3566 * ftp_pasv_verbose()
3568 * This function only outputs some informationals about this second connection
3569 * when we've issued a PASV command before and thus we have connected to a
3570 * possibly new IP address.
3573 #ifndef CURL_DISABLE_VERBOSE_STRINGS
3575 ftp_pasv_verbose(struct connectdata *conn,
3577 char *newhost, /* ascii version */
3581 Curl_printable_address(ai, buf, sizeof(buf));
3582 infof(conn->data, "Connecting to %s (%s) port %d\n", newhost, buf, port);
3587 Check if this is a range download, and if so, set the internal variables
3591 static CURLcode ftp_range(struct connectdata *conn)
3593 curl_off_t from, to;
3596 struct SessionHandle *data = conn->data;
3597 struct ftp_conn *ftpc = &conn->proto.ftpc;
3599 if(data->state.use_range && data->state.range) {
3600 from=curlx_strtoofft(data->state.range, &ptr, 0);
3601 while(*ptr && (ISSPACE(*ptr) || (*ptr=='-')))
3603 to=curlx_strtoofft(ptr, &ptr2, 0);
3605 /* we didn't get any digit */
3608 if((-1 == to) && (from>=0)) {
3610 data->state.resume_from = from;
3611 DEBUGF(infof(conn->data, "FTP RANGE %" CURL_FORMAT_CURL_OFF_T
3612 " to end of file\n", from));
3616 data->req.maxdownload = -from;
3617 data->state.resume_from = from;
3618 DEBUGF(infof(conn->data, "FTP RANGE the last %" CURL_FORMAT_CURL_OFF_T
3619 " bytes\n", -from));
3623 data->req.maxdownload = (to-from)+1; /* include last byte */
3624 data->state.resume_from = from;
3625 DEBUGF(infof(conn->data, "FTP RANGE from %" CURL_FORMAT_CURL_OFF_T
3626 " getting %" CURL_FORMAT_CURL_OFF_T " bytes\n",
3627 from, data->req.maxdownload));
3629 DEBUGF(infof(conn->data, "range-download from %" CURL_FORMAT_CURL_OFF_T
3630 " to %" CURL_FORMAT_CURL_OFF_T ", totally %"
3631 CURL_FORMAT_CURL_OFF_T " bytes\n",
3632 from, to, data->req.maxdownload));
3633 ftpc->dont_check = TRUE; /* dont check for successful transfer */
3636 data->req.maxdownload = -1;
3644 * This function shall be called when the second FTP (data) connection is
3647 * 'complete' can return 0 for incomplete, 1 for done and -1 for go back
3648 * (which basically is only for when PASV is being sent to retry a failed
3652 static CURLcode ftp_do_more(struct connectdata *conn, int *completep)
3654 struct SessionHandle *data=conn->data;
3655 struct ftp_conn *ftpc = &conn->proto.ftpc;
3656 CURLcode result = CURLE_OK;
3657 bool connected = FALSE;
3658 bool complete = FALSE;
3660 /* the ftp struct is inited in ftp_connect() */
3661 struct FTP *ftp = data->req.protop;
3663 /* if the second connection isn't done yet, wait for it */
3664 if(!conn->bits.tcpconnect[SECONDARYSOCKET]) {
3665 if(conn->tunnel_state[SECONDARYSOCKET] == TUNNEL_CONNECT) {
3666 /* As we're in TUNNEL_CONNECT state now, we know the proxy name and port
3667 aren't used so we blank their arguments. TODO: make this nicer */
3668 result = Curl_proxyCONNECT(conn, SECONDARYSOCKET, NULL, 0, FALSE);
3673 result = Curl_is_connected(conn, SECONDARYSOCKET, &connected);
3675 /* Ready to do more? */
3677 DEBUGF(infof(data, "DO-MORE connected phase starts\n"));
3678 if(conn->bits.proxy) {
3679 infof(data, "Connection to proxy confirmed\n");
3680 result = proxy_magic(conn, ftpc->newhost, ftpc->newport, &connected);
3684 if(result && (ftpc->count1 == 0)) {
3685 *completep = -1; /* go back to DOING please */
3686 /* this is a EPSV connect failing, try PASV instead */
3687 return ftp_epsv_disable(conn);
3694 /* already in a state so skip the intial commands.
3695 They are only done to kickstart the do_more state */
3696 result = ftp_multi_statemach(conn, &complete);
3698 *completep = (int)complete;
3700 /* if we got an error or if we don't wait for a data connection return
3702 if(result || (ftpc->wait_data_conn != TRUE))
3705 if(ftpc->wait_data_conn)
3706 /* if we reach the end of the FTP state machine here, *complete will be
3707 TRUE but so is ftpc->wait_data_conn, which says we need to wait for
3708 the data connection and therefore we're not actually complete */
3712 if(ftp->transfer <= FTPTRANSFER_INFO) {
3713 /* a transfer is about to take place, or if not a file name was given
3714 so we'll do a SIZE on it later and then we need the right TYPE first */
3716 if(ftpc->wait_data_conn == TRUE) {
3719 result = ReceivedServerConnect(conn, &serv_conned);
3721 return result; /* Failed to accept data connection */
3724 /* It looks data connection is established */
3725 result = AcceptServerConnect(conn);
3726 ftpc->wait_data_conn = FALSE;
3728 result = InitiateTransfer(conn);
3733 *completep = 1; /* this state is now complete when the server has
3734 connected back to us */
3737 else if(data->set.upload) {
3738 result = ftp_nb_type(conn, data->set.prefer_ascii, FTP_STOR_TYPE);
3742 result = ftp_multi_statemach(conn, &complete);
3743 if(ftpc->wait_data_conn)
3744 /* if we reach the end of the FTP state machine here, *complete will be
3745 TRUE but so is ftpc->wait_data_conn, which says we need to wait for
3746 the data connection and therefore we're not actually complete */
3749 *completep = (int)complete;
3753 ftp->downloadsize = -1; /* unknown as of yet */
3755 result = ftp_range(conn);
3758 else if(data->set.ftp_list_only || !ftpc->file) {
3759 /* The specified path ends with a slash, and therefore we think this
3760 is a directory that is requested, use LIST. But before that we
3761 need to set ASCII transfer mode. */
3763 /* But only if a body transfer was requested. */
3764 if(ftp->transfer == FTPTRANSFER_BODY) {
3765 result = ftp_nb_type(conn, TRUE, FTP_LIST_TYPE);
3769 /* otherwise just fall through */
3772 result = ftp_nb_type(conn, data->set.prefer_ascii, FTP_RETR_TYPE);
3777 result = ftp_multi_statemach(conn, &complete);
3778 *completep = (int)complete;
3783 if(!result && (ftp->transfer != FTPTRANSFER_BODY))
3784 /* no data to transfer. FIX: it feels like a kludge to have this here
3786 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
3788 if(!ftpc->wait_data_conn) {
3789 /* no waiting for the data connection so this is now complete */
3791 DEBUGF(infof(data, "DO-MORE phase ends with %d\n", (int)result));
3799 /***********************************************************************
3803 * This is the actual DO function for FTP. Get a file/directory according to
3804 * the options previously setup.
3808 CURLcode ftp_perform(struct connectdata *conn,
3809 bool *connected, /* connect status after PASV / PORT */
3812 /* this is FTP and no proxy */
3813 CURLcode result=CURLE_OK;
3815 DEBUGF(infof(conn->data, "DO phase starts\n"));
3817 if(conn->data->set.opt_no_body) {
3818 /* requested no body means no transfer... */
3819 struct FTP *ftp = conn->data->req.protop;
3820 ftp->transfer = FTPTRANSFER_INFO;
3823 *dophase_done = FALSE; /* not done yet */
3825 /* start the first command in the DO phase */
3826 result = ftp_state_quote(conn, TRUE, FTP_QUOTE);
3830 /* run the state-machine */
3831 result = ftp_multi_statemach(conn, dophase_done);
3833 *connected = conn->bits.tcpconnect[SECONDARYSOCKET];
3835 infof(conn->data, "ftp_perform ends with SECONDARY: %d\n", *connected);
3838 DEBUGF(infof(conn->data, "DO phase is complete1\n"));
3843 static void wc_data_dtor(void *ptr)
3845 struct ftp_wc_tmpdata *tmp = ptr;
3847 Curl_ftp_parselist_data_free(&tmp->parser);
3851 static CURLcode init_wc_data(struct connectdata *conn)
3854 char *path = conn->data->state.path;
3855 struct WildcardData *wildcard = &(conn->data->wildcard);
3856 CURLcode result = CURLE_OK;
3857 struct ftp_wc_tmpdata *ftp_tmp;
3859 last_slash = strrchr(conn->data->state.path, '/');
3862 if(last_slash[0] == '\0') {
3863 wildcard->state = CURLWC_CLEAN;
3864 result = ftp_parse_url_path(conn);
3868 wildcard->pattern = strdup(last_slash);
3869 if(!wildcard->pattern)
3870 return CURLE_OUT_OF_MEMORY;
3871 last_slash[0] = '\0'; /* cut file from path */
3874 else { /* there is only 'wildcard pattern' or nothing */
3876 wildcard->pattern = strdup(path);
3877 if(!wildcard->pattern)
3878 return CURLE_OUT_OF_MEMORY;
3881 else { /* only list */
3882 wildcard->state = CURLWC_CLEAN;
3883 result = ftp_parse_url_path(conn);
3888 /* program continues only if URL is not ending with slash, allocate needed
3889 resources for wildcard transfer */
3891 /* allocate ftp protocol specific temporary wildcard data */
3892 ftp_tmp = calloc(1, sizeof(struct ftp_wc_tmpdata));
3894 Curl_safefree(wildcard->pattern);
3895 return CURLE_OUT_OF_MEMORY;
3898 /* INITIALIZE parselist structure */
3899 ftp_tmp->parser = Curl_ftp_parselist_data_alloc();
3900 if(!ftp_tmp->parser) {
3901 Curl_safefree(wildcard->pattern);
3903 return CURLE_OUT_OF_MEMORY;
3906 wildcard->tmp = ftp_tmp; /* put it to the WildcardData tmp pointer */
3907 wildcard->tmp_dtor = wc_data_dtor;
3909 /* wildcard does not support NOCWD option (assert it?) */
3910 if(conn->data->set.ftp_filemethod == FTPFILE_NOCWD)
3911 conn->data->set.ftp_filemethod = FTPFILE_MULTICWD;
3913 /* try to parse ftp url */
3914 result = ftp_parse_url_path(conn);
3916 Curl_safefree(wildcard->pattern);
3917 wildcard->tmp_dtor(wildcard->tmp);
3918 wildcard->tmp_dtor = ZERO_NULL;
3919 wildcard->tmp = NULL;
3923 wildcard->path = strdup(conn->data->state.path);
3924 if(!wildcard->path) {
3925 Curl_safefree(wildcard->pattern);
3926 wildcard->tmp_dtor(wildcard->tmp);
3927 wildcard->tmp_dtor = ZERO_NULL;
3928 wildcard->tmp = NULL;
3929 return CURLE_OUT_OF_MEMORY;
3932 /* backup old write_function */
3933 ftp_tmp->backup.write_function = conn->data->set.fwrite_func;
3934 /* parsing write function */
3935 conn->data->set.fwrite_func = Curl_ftp_parselist;
3936 /* backup old file descriptor */
3937 ftp_tmp->backup.file_descriptor = conn->data->set.out;
3938 /* let the writefunc callback know what curl pointer is working with */
3939 conn->data->set.out = conn;
3941 infof(conn->data, "Wildcard - Parsing started\n");
3945 /* This is called recursively */
3946 static CURLcode wc_statemach(struct connectdata *conn)
3948 struct WildcardData * const wildcard = &(conn->data->wildcard);
3949 CURLcode result = CURLE_OK;
3951 switch (wildcard->state) {
3953 result = init_wc_data(conn);
3954 if(wildcard->state == CURLWC_CLEAN)
3958 wildcard->state = result ? CURLWC_ERROR : CURLWC_MATCHING;
3961 case CURLWC_MATCHING: {
3962 /* In this state is LIST response successfully parsed, so lets restore
3963 previous WRITEFUNCTION callback and WRITEDATA pointer */
3964 struct ftp_wc_tmpdata *ftp_tmp = wildcard->tmp;
3965 conn->data->set.fwrite_func = ftp_tmp->backup.write_function;
3966 conn->data->set.out = ftp_tmp->backup.file_descriptor;
3967 ftp_tmp->backup.write_function = ZERO_NULL;
3968 ftp_tmp->backup.file_descriptor = NULL;
3969 wildcard->state = CURLWC_DOWNLOADING;
3971 if(Curl_ftp_parselist_geterror(ftp_tmp->parser)) {
3972 /* error found in LIST parsing */
3973 wildcard->state = CURLWC_CLEAN;
3974 return wc_statemach(conn);
3976 else if(wildcard->filelist->size == 0) {
3977 /* no corresponding file */
3978 wildcard->state = CURLWC_CLEAN;
3979 return CURLE_REMOTE_FILE_NOT_FOUND;
3981 return wc_statemach(conn);
3984 case CURLWC_DOWNLOADING: {
3985 /* filelist has at least one file, lets get first one */
3986 struct ftp_conn *ftpc = &conn->proto.ftpc;
3987 struct curl_fileinfo *finfo = wildcard->filelist->head->ptr;
3989 char *tmp_path = aprintf("%s%s", wildcard->path, finfo->filename);
3991 return CURLE_OUT_OF_MEMORY;
3993 /* switch default "state.pathbuffer" and tmp_path, good to see
3994 ftp_parse_url_path function to understand this trick */
3995 Curl_safefree(conn->data->state.pathbuffer);
3996 conn->data->state.pathbuffer = tmp_path;
3997 conn->data->state.path = tmp_path;
3999 infof(conn->data, "Wildcard - START of \"%s\"\n", finfo->filename);
4000 if(conn->data->set.chunk_bgn) {
4001 long userresponse = conn->data->set.chunk_bgn(
4002 finfo, wildcard->customptr, (int)wildcard->filelist->size);
4003 switch(userresponse) {
4004 case CURL_CHUNK_BGN_FUNC_SKIP:
4005 infof(conn->data, "Wildcard - \"%s\" skipped by user\n",
4007 wildcard->state = CURLWC_SKIP;
4008 return wc_statemach(conn);
4009 case CURL_CHUNK_BGN_FUNC_FAIL:
4010 return CURLE_CHUNK_FAILED;
4014 if(finfo->filetype != CURLFILETYPE_FILE) {
4015 wildcard->state = CURLWC_SKIP;
4016 return wc_statemach(conn);
4019 if(finfo->flags & CURLFINFOFLAG_KNOWN_SIZE)
4020 ftpc->known_filesize = finfo->size;
4022 result = ftp_parse_url_path(conn);
4026 /* we don't need the Curl_fileinfo of first file anymore */
4027 Curl_llist_remove(wildcard->filelist, wildcard->filelist->head, NULL);
4029 if(wildcard->filelist->size == 0) { /* remains only one file to down. */
4030 wildcard->state = CURLWC_CLEAN;
4031 /* after that will be ftp_do called once again and no transfer
4032 will be done because of CURLWC_CLEAN state */
4038 if(conn->data->set.chunk_end)
4039 conn->data->set.chunk_end(conn->data->wildcard.customptr);
4040 Curl_llist_remove(wildcard->filelist, wildcard->filelist->head, NULL);
4041 wildcard->state = (wildcard->filelist->size == 0) ?
4042 CURLWC_CLEAN : CURLWC_DOWNLOADING;
4043 return wc_statemach(conn);
4046 case CURLWC_CLEAN: {
4047 struct ftp_wc_tmpdata *ftp_tmp = wildcard->tmp;
4050 result = Curl_ftp_parselist_geterror(ftp_tmp->parser);
4052 wildcard->state = result ? CURLWC_ERROR : CURLWC_DONE;
4063 /***********************************************************************
4067 * This function is registered as 'curl_do' function. It decodes the path
4068 * parts etc as a wrapper to the actual DO function (ftp_perform).
4070 * The input argument is already checked for validity.
4072 static CURLcode ftp_do(struct connectdata *conn, bool *done)
4074 CURLcode result = CURLE_OK;
4075 struct ftp_conn *ftpc = &conn->proto.ftpc;
4077 *done = FALSE; /* default to false */
4078 ftpc->wait_data_conn = FALSE; /* default to no such wait */
4080 if(conn->data->set.wildcardmatch) {
4081 result = wc_statemach(conn);
4082 if(conn->data->wildcard.state == CURLWC_SKIP ||
4083 conn->data->wildcard.state == CURLWC_DONE) {
4084 /* do not call ftp_regular_transfer */
4087 if(result) /* error, loop or skipping the file */
4090 else { /* no wildcard FSM needed */
4091 result = ftp_parse_url_path(conn);
4096 result = ftp_regular_transfer(conn, done);
4102 CURLcode Curl_ftpsendf(struct connectdata *conn,
4103 const char *fmt, ...)
4105 ssize_t bytes_written;
4106 #define SBUF_SIZE 1024
4110 CURLcode result = CURLE_OK;
4112 enum protection_level data_sec = conn->data_prot;
4117 write_len = vsnprintf(s, SBUF_SIZE-3, fmt, ap);
4120 strcpy(&s[write_len], "\r\n"); /* append a trailing CRLF */
4125 result = Curl_convert_to_network(conn->data, s, write_len);
4126 /* Curl_convert_to_network calls failf if unsuccessful */
4132 conn->data_prot = PROT_CMD;
4134 result = Curl_write(conn, conn->sock[FIRSTSOCKET], sptr, write_len,
4137 DEBUGASSERT(data_sec > PROT_NONE && data_sec < PROT_LAST);
4138 conn->data_prot = data_sec;
4144 if(conn->data->set.verbose)
4145 Curl_debug(conn->data, CURLINFO_HEADER_OUT,
4146 sptr, (size_t)bytes_written, conn);
4148 if(bytes_written != (ssize_t)write_len) {
4149 write_len -= bytes_written;
4150 sptr += bytes_written;
4159 /***********************************************************************
4163 * This should be called before calling sclose() on an ftp control connection
4164 * (not data connections). We should then wait for the response from the
4165 * server before returning. The calling code should then try to close the
4169 static CURLcode ftp_quit(struct connectdata *conn)
4171 CURLcode result = CURLE_OK;
4173 if(conn->proto.ftpc.ctl_valid) {
4174 result = Curl_pp_sendf(&conn->proto.ftpc.pp, "%s", "QUIT");
4176 failf(conn->data, "Failure sending QUIT command: %s",
4177 curl_easy_strerror(result));
4178 conn->proto.ftpc.ctl_valid = FALSE; /* mark control connection as bad */
4179 connclose(conn, "QUIT command failed"); /* mark for connection closure */
4180 state(conn, FTP_STOP);
4184 state(conn, FTP_QUIT);
4186 result = ftp_block_statemach(conn);
4192 /***********************************************************************
4196 * Disconnect from an FTP server. Cleanup protocol-specific per-connection
4197 * resources. BLOCKING.
4199 static CURLcode ftp_disconnect(struct connectdata *conn, bool dead_connection)
4201 struct ftp_conn *ftpc= &conn->proto.ftpc;
4202 struct pingpong *pp = &ftpc->pp;
4204 /* We cannot send quit unconditionally. If this connection is stale or
4205 bad in any way, sending quit and waiting around here will make the
4206 disconnect wait in vain and cause more problems than we need to.
4208 ftp_quit() will check the state of ftp->ctl_valid. If it's ok it
4209 will try to send the QUIT command, otherwise it will just return.
4212 ftpc->ctl_valid = FALSE;
4214 /* The FTP session may or may not have been allocated/setup at this point! */
4215 (void)ftp_quit(conn); /* ignore errors on the QUIT */
4217 if(ftpc->entrypath) {
4218 struct SessionHandle *data = conn->data;
4219 if(data->state.most_recent_ftp_entrypath == ftpc->entrypath) {
4220 data->state.most_recent_ftp_entrypath = NULL;
4222 free(ftpc->entrypath);
4223 ftpc->entrypath = NULL;
4227 free(ftpc->prevpath);
4228 ftpc->prevpath = NULL;
4229 free(ftpc->server_os);
4230 ftpc->server_os = NULL;
4232 Curl_pp_disconnect(pp);
4241 /***********************************************************************
4243 * ftp_parse_url_path()
4245 * Parse the URL path into separate path components.
4249 CURLcode ftp_parse_url_path(struct connectdata *conn)
4251 struct SessionHandle *data = conn->data;
4252 /* the ftp struct is already inited in ftp_connect() */
4253 struct FTP *ftp = data->req.protop;
4254 struct ftp_conn *ftpc = &conn->proto.ftpc;
4255 const char *slash_pos; /* position of the first '/' char in curpos */
4256 const char *path_to_use = data->state.path;
4257 const char *cur_pos;
4258 const char *filename = NULL;
4260 cur_pos = path_to_use; /* current position in path. point at the begin
4261 of next path component */
4263 ftpc->ctl_valid = FALSE;
4264 ftpc->cwdfail = FALSE;
4266 switch(data->set.ftp_filemethod) {
4268 /* fastest, but less standard-compliant */
4271 The best time to check whether the path is a file or directory is right
4274 the first condition in the if() right here, is there just in case
4275 someone decides to set path to NULL one day
4277 if(path_to_use[0] &&
4278 (path_to_use[strlen(path_to_use) - 1] != '/') )
4279 filename = path_to_use; /* this is a full file path */
4282 ftpc->file is not used anywhere other than for operations on a file.
4283 In other words, never for directory operations.
4284 So we can safely leave filename as NULL here and use it as a
4285 argument in dir/file decisions.
4290 case FTPFILE_SINGLECWD:
4291 /* get the last slash */
4292 if(!path_to_use[0]) {
4293 /* no dir, no file */
4297 slash_pos=strrchr(cur_pos, '/');
4298 if(slash_pos || !*cur_pos) {
4299 size_t dirlen = slash_pos-cur_pos;
4301 ftpc->dirs = calloc(1, sizeof(ftpc->dirs[0]));
4303 return CURLE_OUT_OF_MEMORY;
4308 ftpc->dirs[0] = curl_easy_unescape(conn->data, slash_pos ? cur_pos : "/",
4309 slash_pos ? curlx_uztosi(dirlen) : 1,
4311 if(!ftpc->dirs[0]) {
4313 return CURLE_OUT_OF_MEMORY;
4315 ftpc->dirdepth = 1; /* we consider it to be a single dir */
4316 filename = slash_pos ? slash_pos+1 : cur_pos; /* rest is file name */
4319 filename = cur_pos; /* this is a file name only */
4322 default: /* allow pretty much anything */
4323 case FTPFILE_MULTICWD:
4325 ftpc->diralloc = 5; /* default dir depth to allocate */
4326 ftpc->dirs = calloc(ftpc->diralloc, sizeof(ftpc->dirs[0]));
4328 return CURLE_OUT_OF_MEMORY;
4330 /* we have a special case for listing the root dir only */
4331 if(strequal(path_to_use, "/")) {
4332 cur_pos++; /* make it point to the zero byte */
4333 ftpc->dirs[0] = strdup("/");
4337 /* parse the URL path into separate path components */
4338 while((slash_pos = strchr(cur_pos, '/')) != NULL) {
4339 /* 1 or 0 pointer offset to indicate absolute directory */
4340 ssize_t absolute_dir = ((cur_pos - data->state.path > 0) &&
4341 (ftpc->dirdepth == 0))?1:0;
4343 /* seek out the next path component */
4344 if(slash_pos-cur_pos) {
4345 /* we skip empty path components, like "x//y" since the FTP command
4346 CWD requires a parameter and a non-existent parameter a) doesn't
4347 work on many servers and b) has no effect on the others. */
4348 int len = curlx_sztosi(slash_pos - cur_pos + absolute_dir);
4349 ftpc->dirs[ftpc->dirdepth] =
4350 curl_easy_unescape(conn->data, cur_pos - absolute_dir, len, NULL);
4351 if(!ftpc->dirs[ftpc->dirdepth]) { /* run out of memory ... */
4352 failf(data, "no memory");
4354 return CURLE_OUT_OF_MEMORY;
4356 if(isBadFtpString(ftpc->dirs[ftpc->dirdepth])) {
4357 free(ftpc->dirs[ftpc->dirdepth]);
4359 return CURLE_URL_MALFORMAT;
4363 cur_pos = slash_pos + 1; /* jump to the rest of the string */
4364 if(!ftpc->dirdepth) {
4365 /* path starts with a slash, add that as a directory */
4366 ftpc->dirs[ftpc->dirdepth] = strdup("/");
4367 if(!ftpc->dirs[ftpc->dirdepth++]) { /* run out of memory ... */
4368 failf(data, "no memory");
4370 return CURLE_OUT_OF_MEMORY;
4376 cur_pos = slash_pos + 1; /* jump to the rest of the string */
4377 if(++ftpc->dirdepth >= ftpc->diralloc) {
4380 ftpc->diralloc *= 2; /* double the size each time */
4381 bigger = realloc(ftpc->dirs, ftpc->diralloc * sizeof(ftpc->dirs[0]));
4384 return CURLE_OUT_OF_MEMORY;
4386 ftpc->dirs = bigger;
4390 filename = cur_pos; /* the rest is the file name */
4394 if(filename && *filename) {
4395 ftpc->file = curl_easy_unescape(conn->data, filename, 0, NULL);
4396 if(NULL == ftpc->file) {
4398 failf(data, "no memory");
4399 return CURLE_OUT_OF_MEMORY;
4401 if(isBadFtpString(ftpc->file)) {
4403 return CURLE_URL_MALFORMAT;
4407 ftpc->file=NULL; /* instead of point to a zero byte, we make it a NULL
4410 if(data->set.upload && !ftpc->file && (ftp->transfer == FTPTRANSFER_BODY)) {
4411 /* We need a file name when uploading. Return error! */
4412 failf(data, "Uploading to a URL without a file name!");
4413 return CURLE_URL_MALFORMAT;
4416 ftpc->cwddone = FALSE; /* default to not done */
4418 if(ftpc->prevpath) {
4419 /* prevpath is "raw" so we convert the input path before we compare the
4422 char *path = curl_easy_unescape(conn->data, data->state.path, 0, &dlen);
4425 return CURLE_OUT_OF_MEMORY;
4428 dlen -= ftpc->file?curlx_uztosi(strlen(ftpc->file)):0;
4429 if((dlen == curlx_uztosi(strlen(ftpc->prevpath))) &&
4430 strnequal(path, ftpc->prevpath, dlen)) {
4431 infof(data, "Request has same path as previous transfer\n");
4432 ftpc->cwddone = TRUE;
4440 /* call this when the DO phase has completed */
4441 static CURLcode ftp_dophase_done(struct connectdata *conn,
4444 struct FTP *ftp = conn->data->req.protop;
4445 struct ftp_conn *ftpc = &conn->proto.ftpc;
4449 CURLcode result = ftp_do_more(conn, &completed);
4452 close_secondarysocket(conn);
4457 if(ftp->transfer != FTPTRANSFER_BODY)
4458 /* no data to transfer */
4459 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
4461 /* since we didn't connect now, we want do_more to get called */
4462 conn->bits.do_more = TRUE;
4464 ftpc->ctl_valid = TRUE; /* seems good */
4469 /* called from multi.c while DOing */
4470 static CURLcode ftp_doing(struct connectdata *conn,
4473 CURLcode result = ftp_multi_statemach(conn, dophase_done);
4476 DEBUGF(infof(conn->data, "DO phase failed\n"));
4477 else if(*dophase_done) {
4478 result = ftp_dophase_done(conn, FALSE /* not connected */);
4480 DEBUGF(infof(conn->data, "DO phase is complete2\n"));
4485 /***********************************************************************
4487 * ftp_regular_transfer()
4489 * The input argument is already checked for validity.
4491 * Performs all commands done before a regular transfer between a local and a
4494 * ftp->ctl_valid starts out as FALSE, and gets set to TRUE if we reach the
4495 * ftp_done() function without finding any major problem.
4498 CURLcode ftp_regular_transfer(struct connectdata *conn,
4501 CURLcode result=CURLE_OK;
4502 bool connected=FALSE;
4503 struct SessionHandle *data = conn->data;
4504 struct ftp_conn *ftpc = &conn->proto.ftpc;
4505 data->req.size = -1; /* make sure this is unknown at this point */
4507 Curl_pgrsSetUploadCounter(data, 0);
4508 Curl_pgrsSetDownloadCounter(data, 0);
4509 Curl_pgrsSetUploadSize(data, -1);
4510 Curl_pgrsSetDownloadSize(data, -1);
4512 ftpc->ctl_valid = TRUE; /* starts good */
4514 result = ftp_perform(conn,
4515 &connected, /* have we connected after PASV/PORT */
4516 dophase_done); /* all commands in the DO-phase done? */
4521 /* the DO phase has not completed yet */
4524 result = ftp_dophase_done(conn, connected);
4535 static CURLcode ftp_setup_connection(struct connectdata *conn)
4537 struct SessionHandle *data = conn->data;
4542 if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
4543 /* Unless we have asked to tunnel ftp operations through the proxy, we
4544 switch and use HTTP operations only */
4545 #ifndef CURL_DISABLE_HTTP
4546 if(conn->handler == &Curl_handler_ftp)
4547 conn->handler = &Curl_handler_ftp_proxy;
4550 conn->handler = &Curl_handler_ftps_proxy;
4552 failf(data, "FTPS not supported!");
4553 return CURLE_UNSUPPORTED_PROTOCOL;
4556 /* set it up as a HTTP connection instead */
4557 return conn->handler->setup_connection(conn);
4559 failf(data, "FTP over http proxy requires HTTP support built-in!");
4560 return CURLE_UNSUPPORTED_PROTOCOL;
4564 conn->data->req.protop = ftp = malloc(sizeof(struct FTP));
4566 return CURLE_OUT_OF_MEMORY;
4568 data->state.path++; /* don't include the initial slash */
4569 data->state.slash_removed = TRUE; /* we've skipped the slash */
4571 /* FTP URLs support an extension like ";type=<typecode>" that
4572 * we'll try to get now! */
4573 type = strstr(data->state.path, ";type=");
4576 type = strstr(conn->host.rawalloc, ";type=");
4579 *type = 0; /* it was in the middle of the hostname */
4580 command = Curl_raw_toupper(type[6]);
4581 conn->bits.type_set = TRUE;
4584 case 'A': /* ASCII mode */
4585 data->set.prefer_ascii = TRUE;
4588 case 'D': /* directory mode */
4589 data->set.ftp_list_only = TRUE;
4592 case 'I': /* binary mode */
4594 /* switch off ASCII */
4595 data->set.prefer_ascii = FALSE;
4600 /* get some initial data into the ftp struct */
4601 ftp->bytecountp = &conn->data->req.bytecount;
4602 ftp->transfer = FTPTRANSFER_BODY;
4603 ftp->downloadsize = 0;
4605 /* No need to duplicate user+password, the connectdata struct won't change
4606 during a session, but we re-init them here since on subsequent inits
4607 since the conn struct may have changed or been replaced.
4609 ftp->user = conn->user;
4610 ftp->passwd = conn->passwd;
4611 if(isBadFtpString(ftp->user))
4612 return CURLE_URL_MALFORMAT;
4613 if(isBadFtpString(ftp->passwd))
4614 return CURLE_URL_MALFORMAT;
4616 conn->proto.ftpc.known_filesize = -1; /* unknown size for now */
4621 #endif /* CURL_DISABLE_FTP */