1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2022, 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.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 * SPDX-License-Identifier: curl
23 ***************************************************************************/
25 #include "curl_setup.h"
27 #ifndef CURL_DISABLE_FTP
29 #ifdef HAVE_NETINET_IN_H
30 #include <netinet/in.h>
32 #ifdef HAVE_ARPA_INET_H
33 #include <arpa/inet.h>
36 #include <sys/utsname.h>
46 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
48 #define in_addr_t unsigned long
51 #include <curl/curl.h>
59 #include "http.h" /* for HTTP proxy tunnel stuff */
62 #include "ftplistparser.h"
63 #include "curl_range.h"
64 #include "curl_krb5.h"
65 #include "strtoofft.h"
67 #include "vtls/vtls.h"
70 #include "inet_ntop.h"
71 #include "inet_pton.h"
73 #include "parsedate.h" /* for the week day and month names */
74 #include "sockaddr.h" /* required for Curl_sockaddr_storage */
78 #include "speedcheck.h"
80 #include "http_proxy.h"
82 /* The last 3 #include files should be in this order */
83 #include "curl_printf.h"
84 #include "curl_memory.h"
88 #define NI_MAXHOST 1025
90 #ifndef INET_ADDRSTRLEN
91 #define INET_ADDRSTRLEN 16
94 #ifdef CURL_DISABLE_VERBOSE_STRINGS
95 #define ftp_pasv_verbose(a,b,c,d) Curl_nop_stmt
98 /* Local API functions */
100 static void _state(struct Curl_easy *data,
102 #define state(x,y) _state(x,y)
104 static void _state(struct Curl_easy *data,
107 #define state(x,y) _state(x,y,__LINE__)
110 static CURLcode ftp_sendquote(struct Curl_easy *data,
111 struct connectdata *conn,
112 struct curl_slist *quote);
113 static CURLcode ftp_quit(struct Curl_easy *data, struct connectdata *conn);
114 static CURLcode ftp_parse_url_path(struct Curl_easy *data);
115 static CURLcode ftp_regular_transfer(struct Curl_easy *data, bool *done);
116 #ifndef CURL_DISABLE_VERBOSE_STRINGS
117 static void ftp_pasv_verbose(struct Curl_easy *data,
118 struct Curl_addrinfo *ai,
119 char *newhost, /* ascii version */
122 static CURLcode ftp_state_prepare_transfer(struct Curl_easy *data);
123 static CURLcode ftp_state_mdtm(struct Curl_easy *data);
124 static CURLcode ftp_state_quote(struct Curl_easy *data,
125 bool init, ftpstate instate);
126 static CURLcode ftp_nb_type(struct Curl_easy *data,
127 struct connectdata *conn,
128 bool ascii, ftpstate newstate);
129 static int ftp_need_type(struct connectdata *conn,
131 static CURLcode ftp_do(struct Curl_easy *data, bool *done);
132 static CURLcode ftp_done(struct Curl_easy *data,
133 CURLcode, bool premature);
134 static CURLcode ftp_connect(struct Curl_easy *data, bool *done);
135 static CURLcode ftp_disconnect(struct Curl_easy *data,
136 struct connectdata *conn, bool dead_connection);
137 static CURLcode ftp_do_more(struct Curl_easy *data, int *completed);
138 static CURLcode ftp_multi_statemach(struct Curl_easy *data, bool *done);
139 static int ftp_getsock(struct Curl_easy *data, struct connectdata *conn,
140 curl_socket_t *socks);
141 static int ftp_domore_getsock(struct Curl_easy *data,
142 struct connectdata *conn, curl_socket_t *socks);
143 static CURLcode ftp_doing(struct Curl_easy *data,
145 static CURLcode ftp_setup_connection(struct Curl_easy *data,
146 struct connectdata *conn);
147 static CURLcode init_wc_data(struct Curl_easy *data);
148 static CURLcode wc_statemach(struct Curl_easy *data);
149 static void wc_data_dtor(void *ptr);
150 static CURLcode ftp_state_retr(struct Curl_easy *data, curl_off_t filesize);
151 static CURLcode ftp_readresp(struct Curl_easy *data,
152 curl_socket_t sockfd,
156 static CURLcode ftp_dophase_done(struct Curl_easy *data,
160 * FTP protocol handler.
163 const struct Curl_handler Curl_handler_ftp = {
165 ftp_setup_connection, /* setup_connection */
168 ftp_do_more, /* do_more */
169 ftp_connect, /* connect_it */
170 ftp_multi_statemach, /* connecting */
171 ftp_doing, /* doing */
172 ftp_getsock, /* proto_getsock */
173 ftp_getsock, /* doing_getsock */
174 ftp_domore_getsock, /* domore_getsock */
175 ZERO_NULL, /* perform_getsock */
176 ftp_disconnect, /* disconnect */
177 ZERO_NULL, /* readwrite */
178 ZERO_NULL, /* connection_check */
179 ZERO_NULL, /* attach connection */
180 PORT_FTP, /* defport */
181 CURLPROTO_FTP, /* protocol */
182 CURLPROTO_FTP, /* family */
183 PROTOPT_DUAL | PROTOPT_CLOSEACTION | PROTOPT_NEEDSPWD |
184 PROTOPT_NOURLQUERY | PROTOPT_PROXY_AS_HTTP |
185 PROTOPT_WILDCARD /* 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 ZERO_NULL, /* connection_check */
210 ZERO_NULL, /* attach connection */
211 PORT_FTPS, /* defport */
212 CURLPROTO_FTPS, /* protocol */
213 CURLPROTO_FTP, /* family */
214 PROTOPT_SSL | PROTOPT_DUAL | PROTOPT_CLOSEACTION |
215 PROTOPT_NEEDSPWD | PROTOPT_NOURLQUERY | PROTOPT_WILDCARD /* flags */
219 static void close_secondarysocket(struct Curl_easy *data,
220 struct connectdata *conn)
222 if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET]) {
223 Curl_closesocket(data, conn, conn->sock[SECONDARYSOCKET]);
224 conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
226 conn->bits.tcpconnect[SECONDARYSOCKET] = FALSE;
227 #ifndef CURL_DISABLE_PROXY
228 conn->bits.proxy_ssl_connected[SECONDARYSOCKET] = FALSE;
233 * NOTE: back in the old days, we added code in the FTP code that made NOBODY
234 * requests on files respond with headers passed to the client/stdout that
235 * looked like HTTP ones.
237 * This approach is not very elegant, it causes confusion and is error-prone.
238 * It is subject for removal at the next (or at least a future) soname bump.
239 * Until then you can test the effects of the removal by undefining the
240 * following define named CURL_FTP_HTTPSTYLE_HEAD.
242 #define CURL_FTP_HTTPSTYLE_HEAD 1
244 static void freedirs(struct ftp_conn *ftpc)
248 for(i = 0; i < ftpc->dirdepth; i++) {
250 ftpc->dirs[i] = NULL;
256 Curl_safefree(ftpc->file);
258 /* no longer of any use */
259 Curl_safefree(ftpc->newhost);
262 /***********************************************************************
264 * AcceptServerConnect()
266 * After connection request is received from the server this function is
267 * called to accept the connection and close the listening socket
270 static CURLcode AcceptServerConnect(struct Curl_easy *data)
272 struct connectdata *conn = data->conn;
273 curl_socket_t sock = conn->sock[SECONDARYSOCKET];
274 curl_socket_t s = CURL_SOCKET_BAD;
276 struct Curl_sockaddr_storage add;
278 struct sockaddr_in add;
280 curl_socklen_t size = (curl_socklen_t) sizeof(add);
282 if(0 == getsockname(sock, (struct sockaddr *) &add, &size)) {
285 s = accept(sock, (struct sockaddr *) &add, &size);
287 Curl_closesocket(data, conn, sock); /* close the first socket */
289 if(CURL_SOCKET_BAD == s) {
290 failf(data, "Error accept()ing server connect");
291 return CURLE_FTP_PORT_FAILED;
293 infof(data, "Connection accepted from server");
294 /* when this happens within the DO state it is important that we mark us as
295 not needing DO_MORE anymore */
296 conn->bits.do_more = FALSE;
298 conn->sock[SECONDARYSOCKET] = s;
299 (void)curlx_nonblock(s, TRUE); /* enable non-blocking */
300 conn->bits.sock_accepted = TRUE;
302 if(data->set.fsockopt) {
305 /* activate callback for setting socket options */
306 Curl_set_in_callback(data, true);
307 error = data->set.fsockopt(data->set.sockopt_client,
309 CURLSOCKTYPE_ACCEPT);
310 Curl_set_in_callback(data, false);
313 close_secondarysocket(data, conn);
314 return CURLE_ABORTED_BY_CALLBACK;
323 * ftp_timeleft_accept() returns the amount of milliseconds left allowed for
324 * waiting server to connect. If the value is negative, the timeout time has
327 * The start time is stored in progress.t_acceptdata - as set with
328 * Curl_pgrsTime(..., TIMER_STARTACCEPT);
331 static timediff_t ftp_timeleft_accept(struct Curl_easy *data)
333 timediff_t timeout_ms = DEFAULT_ACCEPT_TIMEOUT;
337 if(data->set.accepttimeout > 0)
338 timeout_ms = data->set.accepttimeout;
342 /* check if the generic timeout possibly is set shorter */
343 other = Curl_timeleft(data, &now, FALSE);
344 if(other && (other < timeout_ms))
345 /* note that this also works fine for when other happens to be negative
346 due to it already having elapsed */
349 /* subtract elapsed time */
350 timeout_ms -= Curl_timediff(now, data->progress.t_acceptdata);
352 /* avoid returning 0 as that means no timeout! */
360 /***********************************************************************
362 * ReceivedServerConnect()
364 * After allowing server to connect to us from data port, this function
365 * checks both data connection for connection establishment and ctrl
366 * connection for a negative response regarding a failure in connecting
369 static CURLcode ReceivedServerConnect(struct Curl_easy *data, bool *received)
371 struct connectdata *conn = data->conn;
372 curl_socket_t ctrl_sock = conn->sock[FIRSTSOCKET];
373 curl_socket_t data_sock = conn->sock[SECONDARYSOCKET];
374 struct ftp_conn *ftpc = &conn->proto.ftpc;
375 struct pingpong *pp = &ftpc->pp;
377 timediff_t timeout_ms;
383 timeout_ms = ftp_timeleft_accept(data);
384 infof(data, "Checking for server connect");
386 /* if a timeout was already reached, bail out */
387 failf(data, "Accept timeout occurred while waiting server connect");
388 return CURLE_FTP_ACCEPT_TIMEOUT;
391 /* First check whether there is a cached response from server */
392 if(pp->cache_size && pp->cache && pp->cache[0] > '3') {
393 /* Data connection could not be established, let's return */
394 infof(data, "There is negative response in cache while serv connect");
395 (void)Curl_GetFTPResponse(data, &nread, &ftpcode);
396 return CURLE_FTP_ACCEPT_FAILED;
399 result = Curl_socket_check(ctrl_sock, data_sock, CURL_SOCKET_BAD, 0);
401 /* see if the connection request is already here */
405 failf(data, "Error while waiting for server connect");
406 return CURLE_FTP_ACCEPT_FAILED;
407 case 0: /* Server connect is not received yet */
411 if(result & CURL_CSELECT_IN2) {
412 infof(data, "Ready to accept data connection from server");
415 else if(result & CURL_CSELECT_IN) {
416 infof(data, "Ctrl conn has data while waiting for data conn");
417 (void)Curl_GetFTPResponse(data, &nread, &ftpcode);
420 return CURLE_FTP_ACCEPT_FAILED;
422 return CURLE_WEIRD_SERVER_REPLY;
432 /***********************************************************************
436 * After connection from server is accepted this function is called to
437 * setup transfer parameters and initiate the data transfer.
440 static CURLcode InitiateTransfer(struct Curl_easy *data)
442 CURLcode result = CURLE_OK;
443 struct connectdata *conn = data->conn;
445 if(conn->bits.ftp_use_data_ssl) {
446 /* since we only have a plaintext TCP connection here, we must now
447 * do the TLS stuff */
448 infof(data, "Doing the SSL/TLS handshake on the data stream");
449 result = Curl_ssl_connect(data, conn, SECONDARYSOCKET);
454 if(conn->proto.ftpc.state_saved == FTP_STOR) {
455 /* When we know we're uploading a specified file, we can get the file
456 size prior to the actual upload. */
457 Curl_pgrsSetUploadSize(data, data->state.infilesize);
459 /* set the SO_SNDBUF for the secondary socket for those who need it */
460 Curl_sndbufset(conn->sock[SECONDARYSOCKET]);
462 Curl_setup_transfer(data, -1, -1, FALSE, SECONDARYSOCKET);
466 Curl_setup_transfer(data, SECONDARYSOCKET,
467 conn->proto.ftpc.retr_size_saved, FALSE, -1);
470 conn->proto.ftpc.pp.pending_resp = TRUE; /* expect server response */
471 state(data, FTP_STOP);
476 /***********************************************************************
478 * AllowServerConnect()
480 * When we've issue the PORT command, we have told the server to connect to
481 * us. This function checks whether data connection is established if so it is
485 static CURLcode AllowServerConnect(struct Curl_easy *data, bool *connected)
487 timediff_t timeout_ms;
488 CURLcode result = CURLE_OK;
491 infof(data, "Preparing for accepting server on data port");
493 /* Save the time we start accepting server connect */
494 Curl_pgrsTime(data, TIMER_STARTACCEPT);
496 timeout_ms = ftp_timeleft_accept(data);
498 /* if a timeout was already reached, bail out */
499 failf(data, "Accept timeout occurred while waiting server connect");
500 return CURLE_FTP_ACCEPT_TIMEOUT;
503 /* see if the connection request is already here */
504 result = ReceivedServerConnect(data, connected);
509 result = AcceptServerConnect(data);
513 result = InitiateTransfer(data);
518 /* Add timeout to multi handle and break out of the loop */
519 Curl_expire(data, data->set.accepttimeout ?
520 data->set.accepttimeout: DEFAULT_ACCEPT_TIMEOUT,
527 /* macro to check for a three-digit ftp status code at the start of the
529 #define STATUSCODE(line) (ISDIGIT(line[0]) && ISDIGIT(line[1]) && \
532 /* macro to check for the last line in an FTP server response */
533 #define LASTLINE(line) (STATUSCODE(line) && (' ' == line[3]))
535 static bool ftp_endofresp(struct Curl_easy *data, struct connectdata *conn,
536 char *line, size_t len, int *code)
541 if((len > 3) && LASTLINE(line)) {
542 *code = curlx_sltosi(strtol(line, NULL, 10));
549 static CURLcode ftp_readresp(struct Curl_easy *data,
550 curl_socket_t sockfd,
552 int *ftpcode, /* return the ftp-code if done */
553 size_t *size) /* size of the response */
556 CURLcode result = Curl_pp_readresp(data, sockfd, pp, &code, size);
560 struct connectdata *conn = data->conn;
561 char * const buf = data->state.buffer;
563 /* handle the security-oriented responses 6xx ***/
566 code = Curl_sec_read_msg(data, conn, buf, PROT_SAFE);
569 code = Curl_sec_read_msg(data, conn, buf, PROT_PRIVATE);
572 code = Curl_sec_read_msg(data, conn, buf, PROT_CONFIDENTIAL);
575 /* normal ftp stuff we pass through! */
581 /* store the latest code for later retrieval */
582 data->info.httpcode = code;
588 /* 421 means "Service not available, closing control connection." and FTP
589 * servers use it to signal that idle session timeout has been exceeded.
590 * If we ignored the response, it could end up hanging in some cases.
592 * This response code can come at any point so having it treated
593 * generically is a good idea.
595 infof(data, "We got a 421 - timeout");
596 state(data, FTP_STOP);
597 return CURLE_OPERATION_TIMEDOUT;
603 /* --- parse FTP server responses --- */
606 * Curl_GetFTPResponse() is a BLOCKING function to read the full response
607 * from a server after a command.
611 CURLcode Curl_GetFTPResponse(struct Curl_easy *data,
612 ssize_t *nreadp, /* return number of bytes read */
613 int *ftpcode) /* return the ftp-code */
616 * We cannot read just one byte per read() and then go back to select() as
617 * the OpenSSL read() doesn't grok that properly.
619 * Alas, read as much as possible, split up into lines, use the ending
620 * line in a response or continue reading. */
622 struct connectdata *conn = data->conn;
623 curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
624 CURLcode result = CURLE_OK;
625 struct ftp_conn *ftpc = &conn->proto.ftpc;
626 struct pingpong *pp = &ftpc->pp;
629 int value_to_be_ignored = 0;
632 *ftpcode = 0; /* 0 for errors */
634 /* make the pointer point to something for the rest of this function */
635 ftpcode = &value_to_be_ignored;
639 while(!*ftpcode && !result) {
640 /* check and reset timeout value every lap */
641 timediff_t timeout = Curl_pp_state_timeout(data, pp, FALSE);
642 timediff_t interval_ms;
645 failf(data, "FTP response timeout");
646 return CURLE_OPERATION_TIMEDOUT; /* already too little time */
649 interval_ms = 1000; /* use 1 second timeout intervals */
650 if(timeout < interval_ms)
651 interval_ms = timeout;
654 * Since this function is blocking, we need to wait here for input on the
655 * connection and only then we call the response reading function. We do
656 * timeout at least every second to make the timeout check run.
658 * A caution here is that the ftp_readresp() function has a cache that may
659 * contain pieces of a response from the previous invoke and we need to
660 * make sure we don't just wait for input while there is unhandled data in
661 * that cache. But also, if the cache is there, we call ftp_readresp() and
662 * the cache wasn't good enough to continue we must not just busy-loop
663 * around this function.
667 if(pp->cache && (cache_skip < 2)) {
669 * There's a cache left since before. We then skipping the wait for
670 * socket action, unless this is the same cache like the previous round
671 * as then the cache was deemed not enough to act on and we then need to
672 * wait for more data anyway.
675 else if(!Curl_conn_data_pending(conn, FIRSTSOCKET)) {
676 switch(SOCKET_READABLE(sockfd, interval_ms)) {
677 case -1: /* select() error, stop reading */
678 failf(data, "FTP response aborted due to select/poll error: %d",
680 return CURLE_RECV_ERROR;
682 case 0: /* timeout */
683 if(Curl_pgrsUpdate(data))
684 return CURLE_ABORTED_BY_CALLBACK;
685 continue; /* just continue in our loop for the timeout duration */
687 default: /* for clarity */
691 result = ftp_readresp(data, sockfd, pp, ftpcode, &nread);
695 if(!nread && pp->cache)
696 /* bump cache skip counter as on repeated skips we must wait for more
700 /* when we got data or there is no cache left, we reset the cache skip
706 } /* while there's buffer left and loop is requested */
708 pp->pending_resp = FALSE;
713 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
714 /* for debug purposes */
715 static const char * const ftp_state_names[]={
754 /* This is the ONLY way to change FTP state! */
755 static void _state(struct Curl_easy *data,
762 struct connectdata *conn = data->conn;
763 struct ftp_conn *ftpc = &conn->proto.ftpc;
765 #if defined(DEBUGBUILD)
767 #if defined(CURL_DISABLE_VERBOSE_STRINGS)
770 if(ftpc->state != newstate)
771 infof(data, "FTP %p (line %d) state change from %s to %s",
772 (void *)ftpc, lineno, ftp_state_names[ftpc->state],
773 ftp_state_names[newstate]);
777 ftpc->state = newstate;
780 static CURLcode ftp_state_user(struct Curl_easy *data,
781 struct connectdata *conn)
783 CURLcode result = Curl_pp_sendf(data,
784 &conn->proto.ftpc.pp, "USER %s",
785 conn->user?conn->user:"");
787 struct ftp_conn *ftpc = &conn->proto.ftpc;
788 ftpc->ftp_trying_alternative = FALSE;
789 state(data, FTP_USER);
794 static CURLcode ftp_state_pwd(struct Curl_easy *data,
795 struct connectdata *conn)
797 CURLcode result = Curl_pp_sendf(data, &conn->proto.ftpc.pp, "%s", "PWD");
799 state(data, FTP_PWD);
804 /* For the FTP "protocol connect" and "doing" phases only */
805 static int ftp_getsock(struct Curl_easy *data,
806 struct connectdata *conn,
807 curl_socket_t *socks)
809 return Curl_pp_getsock(data, &conn->proto.ftpc.pp, socks);
812 /* For the FTP "DO_MORE" phase only */
813 static int ftp_domore_getsock(struct Curl_easy *data,
814 struct connectdata *conn, curl_socket_t *socks)
816 struct ftp_conn *ftpc = &conn->proto.ftpc;
819 /* When in DO_MORE state, we could be either waiting for us to connect to a
820 * remote site, or we could wait for that site to connect to us. Or just
821 * handle ordinary commands.
824 if(SOCKS_STATE(conn->cnnct.state))
825 return Curl_SOCKS_getsock(conn, socks, SECONDARYSOCKET);
827 if(FTP_STOP == ftpc->state) {
828 int bits = GETSOCK_READSOCK(0);
831 /* if stopped and still in this state, then we're also waiting for a
832 connect on the secondary connection */
833 socks[0] = conn->sock[FIRSTSOCKET];
835 if(!data->set.ftp_use_port) {
838 /* PORT is used to tell the server to connect to us, and during that we
839 don't do happy eyeballs, but we do if we connect to the server */
840 for(s = 1, i = 0; i<2; i++) {
841 if(conn->tempsock[i] != CURL_SOCKET_BAD) {
842 socks[s] = conn->tempsock[i];
843 bits |= GETSOCK_WRITESOCK(s++);
849 socks[1] = conn->sock[SECONDARYSOCKET];
850 bits |= GETSOCK_WRITESOCK(1) | GETSOCK_READSOCK(1);
855 return Curl_pp_getsock(data, &conn->proto.ftpc.pp, socks);
858 /* This is called after the FTP_QUOTE state is passed.
860 ftp_state_cwd() sends the range of CWD commands to the server to change to
861 the correct directory. It may also need to send MKD commands to create
862 missing ones, if that option is enabled.
864 static CURLcode ftp_state_cwd(struct Curl_easy *data,
865 struct connectdata *conn)
867 CURLcode result = CURLE_OK;
868 struct ftp_conn *ftpc = &conn->proto.ftpc;
871 /* already done and fine */
872 result = ftp_state_mdtm(data);
874 /* FTPFILE_NOCWD with full path: expect ftpc->cwddone! */
875 DEBUGASSERT((data->set.ftp_filemethod != FTPFILE_NOCWD) ||
876 !(ftpc->dirdepth && ftpc->dirs[0][0] == '/'));
878 ftpc->count2 = 0; /* count2 counts failed CWDs */
880 if(conn->bits.reuse && ftpc->entrypath &&
881 /* no need to go to entrypath when we have an absolute path */
882 !(ftpc->dirdepth && ftpc->dirs[0][0] == '/')) {
883 /* This is a re-used connection. Since we change directory to where the
884 transfer is taking place, we must first get back to the original dir
885 where we ended up after login: */
886 ftpc->cwdcount = 0; /* we count this as the first path, then we add one
887 for all upcoming ones in the ftp->dirs[] array */
888 result = Curl_pp_sendf(data, &ftpc->pp, "CWD %s", ftpc->entrypath);
890 state(data, FTP_CWD);
895 /* issue the first CWD, the rest is sent when the CWD responses are
897 result = Curl_pp_sendf(data, &ftpc->pp, "CWD %s",
898 ftpc->dirs[ftpc->cwdcount -1]);
900 state(data, FTP_CWD);
903 /* No CWD necessary */
904 result = ftp_state_mdtm(data);
917 static CURLcode ftp_state_use_port(struct Curl_easy *data,
918 ftpport fcmd) /* start with this */
920 CURLcode result = CURLE_OK;
921 struct connectdata *conn = data->conn;
922 struct ftp_conn *ftpc = &conn->proto.ftpc;
923 curl_socket_t portsock = CURL_SOCKET_BAD;
924 char myhost[MAX_IPADR_LEN + 1] = "";
926 struct Curl_sockaddr_storage ss;
927 struct Curl_addrinfo *res, *ai;
928 curl_socklen_t sslen;
929 char hbuf[NI_MAXHOST];
930 struct sockaddr *sa = (struct sockaddr *)&ss;
931 struct sockaddr_in * const sa4 = (void *)sa;
933 struct sockaddr_in6 * const sa6 = (void *)sa;
935 static const char mode[][5] = { "EPRT", "PORT" };
939 char *string_ftpport = data->set.str[STRING_FTPPORT];
940 struct Curl_dns_entry *h = NULL;
941 unsigned short port_min = 0;
942 unsigned short port_max = 0;
944 bool possibly_non_local = TRUE;
945 char buffer[STRERROR_LEN];
948 /* Step 1, figure out what is requested,
950 * (ipv4|ipv6|domain|interface)?(:port(-range)?)?
953 if(data->set.str[STRING_FTPPORT] &&
954 (strlen(data->set.str[STRING_FTPPORT]) > 1)) {
957 size_t addrlen = INET6_ADDRSTRLEN > strlen(string_ftpport) ?
958 INET6_ADDRSTRLEN : strlen(string_ftpport);
960 size_t addrlen = INET_ADDRSTRLEN > strlen(string_ftpport) ?
961 INET_ADDRSTRLEN : strlen(string_ftpport);
963 char *ip_start = string_ftpport;
965 char *port_start = NULL;
966 char *port_sep = NULL;
968 addr = calloc(addrlen + 1, 1);
970 return CURLE_OUT_OF_MEMORY;
973 if(*string_ftpport == '[') {
974 /* [ipv6]:port(-range) */
975 ip_start = string_ftpport + 1;
976 ip_end = strchr(string_ftpport, ']');
978 strncpy(addr, ip_start, ip_end - ip_start);
982 if(*string_ftpport == ':') {
984 ip_end = string_ftpport;
987 ip_end = strchr(string_ftpport, ':');
989 /* either ipv6 or (ipv4|domain|interface):port(-range) */
991 if(Curl_inet_pton(AF_INET6, string_ftpport, sa6) == 1) {
993 port_min = port_max = 0;
994 strcpy(addr, string_ftpport);
995 ip_end = NULL; /* this got no port ! */
999 /* (ipv4|domain|interface):port(-range) */
1000 strncpy(addr, string_ftpport, ip_end - ip_start);
1003 /* ipv4|interface */
1004 strcpy(addr, string_ftpport);
1007 /* parse the port */
1009 port_start = strchr(ip_end, ':');
1011 port_min = curlx_ultous(strtoul(port_start + 1, NULL, 10));
1012 port_sep = strchr(port_start, '-');
1014 port_max = curlx_ultous(strtoul(port_sep + 1, NULL, 10));
1017 port_max = port_min;
1021 /* correct errors like:
1023 * :-4711, in this case port_min is (unsigned)-1,
1024 * therefore port_min > port_max for all cases
1025 * but port_max = (unsigned)-1
1027 if(port_min > port_max)
1028 port_min = port_max = 0;
1032 /* attempt to get the address of the given interface name */
1033 switch(Curl_if2ip(conn->ip_addr->ai_family,
1035 Curl_ipv6_scope(conn->ip_addr->ai_addr),
1038 addr, hbuf, sizeof(hbuf))) {
1039 case IF2IP_NOT_FOUND:
1040 /* not an interface, use the given string as host name instead */
1043 case IF2IP_AF_NOT_SUPPORTED:
1044 return CURLE_FTP_PORT_FAILED;
1046 host = hbuf; /* use the hbuf for host name */
1050 /* there was only a port(-range) given, default the host */
1052 } /* data->set.ftpport */
1056 /* not an interface and not a host name, get default by extracting
1057 the IP from the control connection */
1059 if(getsockname(conn->sock[FIRSTSOCKET], sa, &sslen)) {
1060 failf(data, "getsockname() failed: %s",
1061 Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
1063 return CURLE_FTP_PORT_FAILED;
1065 switch(sa->sa_family) {
1068 r = Curl_inet_ntop(sa->sa_family, &sa6->sin6_addr, hbuf, sizeof(hbuf));
1072 r = Curl_inet_ntop(sa->sa_family, &sa4->sin_addr, hbuf, sizeof(hbuf));
1076 return CURLE_FTP_PORT_FAILED;
1077 host = hbuf; /* use this host name */
1078 possibly_non_local = FALSE; /* we know it is local now */
1081 /* resolv ip/host to ip */
1082 rc = Curl_resolv(data, host, 0, FALSE, &h);
1083 if(rc == CURLRESOLV_PENDING)
1084 (void)Curl_resolver_wait_resolv(data, &h);
1087 /* when we return from this function, we can forget about this entry
1088 to we can unlock it now already */
1089 Curl_resolv_unlock(data, h);
1092 res = NULL; /* failure! */
1095 failf(data, "failed to resolve the address provided to PORT: %s", host);
1097 return CURLE_FTP_PORT_FAILED;
1103 /* step 2, create a socket for the requested address */
1105 portsock = CURL_SOCKET_BAD;
1107 for(ai = res; ai; ai = ai->ai_next) {
1108 result = Curl_socket(data, ai, NULL, &portsock);
1116 failf(data, "socket failure: %s",
1117 Curl_strerror(error, buffer, sizeof(buffer)));
1118 return CURLE_FTP_PORT_FAILED;
1121 /* step 3, bind to a suitable local address */
1123 memcpy(sa, ai->ai_addr, ai->ai_addrlen);
1124 sslen = ai->ai_addrlen;
1126 for(port = port_min; port <= port_max;) {
1127 if(sa->sa_family == AF_INET)
1128 sa4->sin_port = htons(port);
1131 sa6->sin6_port = htons(port);
1133 /* Try binding the given address. */
1134 if(bind(portsock, sa, sslen) ) {
1137 if(possibly_non_local && (error == EADDRNOTAVAIL)) {
1138 /* The requested bind address is not local. Use the address used for
1139 * the control connection instead and restart the port loop
1141 infof(data, "bind(port=%hu) on non-local address failed: %s", port,
1142 Curl_strerror(error, buffer, sizeof(buffer)));
1145 if(getsockname(conn->sock[FIRSTSOCKET], sa, &sslen)) {
1146 failf(data, "getsockname() failed: %s",
1147 Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
1148 Curl_closesocket(data, conn, portsock);
1149 return CURLE_FTP_PORT_FAILED;
1152 possibly_non_local = FALSE; /* don't try this again */
1155 if(error != EADDRINUSE && error != EACCES) {
1156 failf(data, "bind(port=%hu) failed: %s", port,
1157 Curl_strerror(error, buffer, sizeof(buffer)));
1158 Curl_closesocket(data, conn, portsock);
1159 return CURLE_FTP_PORT_FAILED;
1168 /* maybe all ports were in use already*/
1169 if(port > port_max) {
1170 failf(data, "bind() failed, we ran out of ports");
1171 Curl_closesocket(data, conn, portsock);
1172 return CURLE_FTP_PORT_FAILED;
1175 /* get the name again after the bind() so that we can extract the
1176 port number it uses now */
1178 if(getsockname(portsock, (struct sockaddr *)sa, &sslen)) {
1179 failf(data, "getsockname() failed: %s",
1180 Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
1181 Curl_closesocket(data, conn, portsock);
1182 return CURLE_FTP_PORT_FAILED;
1185 /* step 4, listen on the socket */
1187 if(listen(portsock, 1)) {
1188 failf(data, "socket failure: %s",
1189 Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
1190 Curl_closesocket(data, conn, portsock);
1191 return CURLE_FTP_PORT_FAILED;
1194 /* step 5, send the proper FTP command */
1196 /* get a plain printable version of the numerical address to work with
1198 Curl_printable_address(ai, myhost, sizeof(myhost));
1201 if(!conn->bits.ftp_use_eprt && conn->bits.ipv6)
1202 /* EPRT is disabled but we are connected to a IPv6 host, so we ignore the
1203 request and enable EPRT again! */
1204 conn->bits.ftp_use_eprt = TRUE;
1207 for(; fcmd != DONE; fcmd++) {
1209 if(!conn->bits.ftp_use_eprt && (EPRT == fcmd))
1210 /* if disabled, goto next */
1213 if((PORT == fcmd) && sa->sa_family != AF_INET)
1214 /* PORT is IPv4 only */
1217 switch(sa->sa_family) {
1219 port = ntohs(sa4->sin_port);
1223 port = ntohs(sa6->sin6_port);
1227 continue; /* might as well skip this */
1232 * Two fine examples from RFC2428;
1234 * EPRT |1|132.235.1.2|6275|
1236 * EPRT |2|1080::8:800:200C:417A|5282|
1239 result = Curl_pp_sendf(data, &ftpc->pp, "%s |%d|%s|%hu|", mode[fcmd],
1240 sa->sa_family == AF_INET?1:2,
1243 failf(data, "Failure sending EPRT command: %s",
1244 curl_easy_strerror(result));
1245 Curl_closesocket(data, conn, portsock);
1246 /* don't retry using PORT */
1247 ftpc->count1 = PORT;
1249 state(data, FTP_STOP);
1255 /* large enough for [IP address],[num],[num] */
1256 char target[sizeof(myhost) + 20];
1257 char *source = myhost;
1258 char *dest = target;
1260 /* translate x.x.x.x to x,x,x,x */
1261 while(source && *source) {
1270 msnprintf(dest, 20, ",%d,%d", (int)(port>>8), (int)(port&0xff));
1272 result = Curl_pp_sendf(data, &ftpc->pp, "%s %s", mode[fcmd], target);
1274 failf(data, "Failure sending PORT command: %s",
1275 curl_easy_strerror(result));
1276 Curl_closesocket(data, conn, portsock);
1278 state(data, FTP_STOP);
1285 /* store which command was sent */
1286 ftpc->count1 = fcmd;
1288 close_secondarysocket(data, conn);
1290 /* we set the secondary socket variable to this for now, it is only so that
1291 the cleanup function will close it in case we fail before the true
1292 secondary stuff is made */
1293 conn->sock[SECONDARYSOCKET] = portsock;
1295 /* this tcpconnect assignment below is a hackish work-around to make the
1296 multi interface with active FTP work - as it will not wait for a
1297 (passive) connect in Curl_is_connected().
1299 The *proper* fix is to make sure that the active connection from the
1300 server is done in a non-blocking way. Currently, it is still BLOCKING.
1302 conn->bits.tcpconnect[SECONDARYSOCKET] = TRUE;
1304 state(data, FTP_PORT);
1308 static CURLcode ftp_state_use_pasv(struct Curl_easy *data,
1309 struct connectdata *conn)
1311 struct ftp_conn *ftpc = &conn->proto.ftpc;
1312 CURLcode result = CURLE_OK;
1314 Here's the executive summary on what to do:
1316 PASV is RFC959, expect:
1317 227 Entering Passive Mode (a1,a2,a3,a4,p1,p2)
1319 LPSV is RFC1639, expect:
1320 228 Entering Long Passive Mode (4,4,a1,a2,a3,a4,2,p1,p2)
1322 EPSV is RFC2428, expect:
1323 229 Entering Extended Passive Mode (|||port|)
1327 static const char mode[][5] = { "EPSV", "PASV" };
1331 if(!conn->bits.ftp_use_epsv && conn->bits.ipv6)
1332 /* EPSV is disabled but we are connected to a IPv6 host, so we ignore the
1333 request and enable EPSV again! */
1334 conn->bits.ftp_use_epsv = TRUE;
1337 modeoff = conn->bits.ftp_use_epsv?0:1;
1339 result = Curl_pp_sendf(data, &ftpc->pp, "%s", mode[modeoff]);
1341 ftpc->count1 = modeoff;
1342 state(data, FTP_PASV);
1343 infof(data, "Connect data stream passively");
1349 * ftp_state_prepare_transfer() starts PORT, PASV or PRET etc.
1351 * REST is the last command in the chain of commands when a "head"-like
1352 * request is made. Thus, if an actual transfer is to be made this is where we
1353 * take off for real.
1355 static CURLcode ftp_state_prepare_transfer(struct Curl_easy *data)
1357 CURLcode result = CURLE_OK;
1358 struct FTP *ftp = data->req.p.ftp;
1359 struct connectdata *conn = data->conn;
1361 if(ftp->transfer != PPTRANSFER_BODY) {
1362 /* doesn't transfer any data */
1364 /* still possibly do PRE QUOTE jobs */
1365 state(data, FTP_RETR_PREQUOTE);
1366 result = ftp_state_quote(data, TRUE, FTP_RETR_PREQUOTE);
1368 else if(data->set.ftp_use_port) {
1369 /* We have chosen to use the PORT (or similar) command */
1370 result = ftp_state_use_port(data, EPRT);
1373 /* We have chosen (this is default) to use the PASV (or similar) command */
1374 if(data->set.ftp_use_pret) {
1375 /* The user has requested that we send a PRET command
1376 to prepare the server for the upcoming PASV */
1377 struct ftp_conn *ftpc = &conn->proto.ftpc;
1378 if(!conn->proto.ftpc.file)
1379 result = Curl_pp_sendf(data, &ftpc->pp, "PRET %s",
1380 data->set.str[STRING_CUSTOMREQUEST]?
1381 data->set.str[STRING_CUSTOMREQUEST]:
1382 (data->state.list_only?"NLST":"LIST"));
1383 else if(data->set.upload)
1384 result = Curl_pp_sendf(data, &ftpc->pp, "PRET STOR %s",
1385 conn->proto.ftpc.file);
1387 result = Curl_pp_sendf(data, &ftpc->pp, "PRET RETR %s",
1388 conn->proto.ftpc.file);
1390 state(data, FTP_PRET);
1393 result = ftp_state_use_pasv(data, conn);
1398 static CURLcode ftp_state_rest(struct Curl_easy *data,
1399 struct connectdata *conn)
1401 CURLcode result = CURLE_OK;
1402 struct FTP *ftp = data->req.p.ftp;
1403 struct ftp_conn *ftpc = &conn->proto.ftpc;
1405 if((ftp->transfer != PPTRANSFER_BODY) && ftpc->file) {
1406 /* if a "head"-like request is being made (on a file) */
1408 /* Determine if server can respond to REST command and therefore
1409 whether it supports range */
1410 result = Curl_pp_sendf(data, &ftpc->pp, "REST %d", 0);
1412 state(data, FTP_REST);
1415 result = ftp_state_prepare_transfer(data);
1420 static CURLcode ftp_state_size(struct Curl_easy *data,
1421 struct connectdata *conn)
1423 CURLcode result = CURLE_OK;
1424 struct FTP *ftp = data->req.p.ftp;
1425 struct ftp_conn *ftpc = &conn->proto.ftpc;
1427 if((ftp->transfer == PPTRANSFER_INFO) && ftpc->file) {
1428 /* if a "head"-like request is being made (on a file) */
1430 /* we know ftpc->file is a valid pointer to a file name */
1431 result = Curl_pp_sendf(data, &ftpc->pp, "SIZE %s", ftpc->file);
1433 state(data, FTP_SIZE);
1436 result = ftp_state_rest(data, conn);
1441 static CURLcode ftp_state_list(struct Curl_easy *data)
1443 CURLcode result = CURLE_OK;
1444 struct FTP *ftp = data->req.p.ftp;
1445 struct connectdata *conn = data->conn;
1447 /* If this output is to be machine-parsed, the NLST command might be better
1448 to use, since the LIST command output is not specified or standard in any
1449 way. It has turned out that the NLST list output is not the same on all
1450 servers either... */
1453 if FTPFILE_NOCWD was specified, we should add the path
1454 as argument for the LIST / NLST / or custom command.
1455 Whether the server will support this, is uncertain.
1457 The other ftp_filemethods will CWD into dir/dir/ first and
1458 then just do LIST (in that case: nothing to do here)
1460 char *lstArg = NULL;
1463 if((data->set.ftp_filemethod == FTPFILE_NOCWD) && ftp->path) {
1464 /* url-decode before evaluation: e.g. paths starting/ending with %2f */
1465 const char *slashPos = NULL;
1466 char *rawPath = NULL;
1467 result = Curl_urldecode(ftp->path, 0, &rawPath, NULL, REJECT_CTRL);
1471 slashPos = strrchr(rawPath, '/');
1473 /* chop off the file part if format is dir/file otherwise remove
1474 the trailing slash for dir/dir/ except for absolute path / */
1475 size_t n = slashPos - rawPath;
1486 cmd = aprintf("%s%s%s",
1487 data->set.str[STRING_CUSTOMREQUEST]?
1488 data->set.str[STRING_CUSTOMREQUEST]:
1489 (data->state.list_only?"NLST":"LIST"),
1491 lstArg? lstArg: "");
1495 return CURLE_OUT_OF_MEMORY;
1497 result = Curl_pp_sendf(data, &conn->proto.ftpc.pp, "%s", cmd);
1501 state(data, FTP_LIST);
1506 static CURLcode ftp_state_retr_prequote(struct Curl_easy *data)
1508 /* We've sent the TYPE, now we must send the list of prequote strings */
1509 return ftp_state_quote(data, TRUE, FTP_RETR_PREQUOTE);
1512 static CURLcode ftp_state_stor_prequote(struct Curl_easy *data)
1514 /* We've sent the TYPE, now we must send the list of prequote strings */
1515 return ftp_state_quote(data, TRUE, FTP_STOR_PREQUOTE);
1518 static CURLcode ftp_state_type(struct Curl_easy *data)
1520 CURLcode result = CURLE_OK;
1521 struct FTP *ftp = data->req.p.ftp;
1522 struct connectdata *conn = data->conn;
1523 struct ftp_conn *ftpc = &conn->proto.ftpc;
1525 /* If we have selected NOBODY and HEADER, it means that we only want file
1526 information. Which in FTP can't be much more than the file size and
1528 if(data->set.opt_no_body && ftpc->file &&
1529 ftp_need_type(conn, data->state.prefer_ascii)) {
1530 /* The SIZE command is _not_ RFC 959 specified, and therefore many servers
1531 may not support it! It is however the only way we have to get a file's
1534 ftp->transfer = PPTRANSFER_INFO;
1535 /* this means no actual transfer will be made */
1537 /* Some servers return different sizes for different modes, and thus we
1538 must set the proper type before we check the size */
1539 result = ftp_nb_type(data, conn, data->state.prefer_ascii, FTP_TYPE);
1544 result = ftp_state_size(data, conn);
1549 /* This is called after the CWD commands have been done in the beginning of
1551 static CURLcode ftp_state_mdtm(struct Curl_easy *data)
1553 CURLcode result = CURLE_OK;
1554 struct connectdata *conn = data->conn;
1555 struct ftp_conn *ftpc = &conn->proto.ftpc;
1557 /* Requested time of file or time-depended transfer? */
1558 if((data->set.get_filetime || data->set.timecondition) && ftpc->file) {
1560 /* we have requested to get the modified-time of the file, this is a white
1561 spot as the MDTM is not mentioned in RFC959 */
1562 result = Curl_pp_sendf(data, &ftpc->pp, "MDTM %s", ftpc->file);
1565 state(data, FTP_MDTM);
1568 result = ftp_state_type(data);
1574 /* This is called after the TYPE and possible quote commands have been sent */
1575 static CURLcode ftp_state_ul_setup(struct Curl_easy *data,
1578 CURLcode result = CURLE_OK;
1579 struct connectdata *conn = data->conn;
1580 struct FTP *ftp = data->req.p.ftp;
1581 struct ftp_conn *ftpc = &conn->proto.ftpc;
1582 bool append = data->set.remote_append;
1584 if((data->state.resume_from && !sizechecked) ||
1585 ((data->state.resume_from > 0) && sizechecked)) {
1586 /* we're about to continue the uploading of a file */
1587 /* 1. get already existing file's size. We use the SIZE command for this
1588 which may not exist in the server! The SIZE command is not in
1591 /* 2. This used to set REST. But since we can do append, we
1592 don't another ftp command. We just skip the source file
1593 offset and then we APPEND the rest on the file instead */
1595 /* 3. pass file-size number of bytes in the source file */
1596 /* 4. lower the infilesize counter */
1597 /* => transfer as usual */
1598 int seekerr = CURL_SEEKFUNC_OK;
1600 if(data->state.resume_from < 0) {
1601 /* Got no given size to start from, figure it out */
1602 result = Curl_pp_sendf(data, &ftpc->pp, "SIZE %s", ftpc->file);
1604 state(data, FTP_STOR_SIZE);
1611 /* Let's read off the proper amount of bytes from the input. */
1612 if(conn->seek_func) {
1613 Curl_set_in_callback(data, true);
1614 seekerr = conn->seek_func(conn->seek_client, data->state.resume_from,
1616 Curl_set_in_callback(data, false);
1619 if(seekerr != CURL_SEEKFUNC_OK) {
1620 curl_off_t passed = 0;
1621 if(seekerr != CURL_SEEKFUNC_CANTSEEK) {
1622 failf(data, "Could not seek stream");
1623 return CURLE_FTP_COULDNT_USE_REST;
1625 /* seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */
1627 size_t readthisamountnow =
1628 (data->state.resume_from - passed > data->set.buffer_size) ?
1629 (size_t)data->set.buffer_size :
1630 curlx_sotouz(data->state.resume_from - passed);
1632 size_t actuallyread =
1633 data->state.fread_func(data->state.buffer, 1, readthisamountnow,
1636 passed += actuallyread;
1637 if((actuallyread == 0) || (actuallyread > readthisamountnow)) {
1638 /* this checks for greater-than only to make sure that the
1639 CURL_READFUNC_ABORT return code still aborts */
1640 failf(data, "Failed to read data");
1641 return CURLE_FTP_COULDNT_USE_REST;
1643 } while(passed < data->state.resume_from);
1645 /* now, decrease the size of the read */
1646 if(data->state.infilesize>0) {
1647 data->state.infilesize -= data->state.resume_from;
1649 if(data->state.infilesize <= 0) {
1650 infof(data, "File already completely uploaded");
1652 /* no data to transfer */
1653 Curl_setup_transfer(data, -1, -1, FALSE, -1);
1655 /* Set ->transfer so that we won't get any error in
1656 * ftp_done() because we didn't transfer anything! */
1657 ftp->transfer = PPTRANSFER_NONE;
1659 state(data, FTP_STOP);
1663 /* we've passed, proceed as normal */
1666 result = Curl_pp_sendf(data, &ftpc->pp, append?"APPE %s":"STOR %s",
1669 state(data, FTP_STOR);
1674 static CURLcode ftp_state_quote(struct Curl_easy *data,
1678 CURLcode result = CURLE_OK;
1679 struct FTP *ftp = data->req.p.ftp;
1680 struct connectdata *conn = data->conn;
1681 struct ftp_conn *ftpc = &conn->proto.ftpc;
1683 struct curl_slist *item;
1688 item = data->set.quote;
1690 case FTP_RETR_PREQUOTE:
1691 case FTP_STOR_PREQUOTE:
1692 item = data->set.prequote;
1695 item = data->set.postquote;
1701 * 'count1' to iterate over the commands to send
1702 * 'count2' to store whether to allow commands to fail
1713 /* Skip count1 items in the linked list */
1714 while((i< ftpc->count1) && item) {
1719 char *cmd = item->data;
1722 ftpc->count2 = 1; /* the sent command is allowed to fail */
1725 ftpc->count2 = 0; /* failure means cancel operation */
1727 result = Curl_pp_sendf(data, &ftpc->pp, "%s", cmd);
1730 state(data, instate);
1736 /* No more quote to send, continue to ... */
1740 result = ftp_state_cwd(data, conn);
1742 case FTP_RETR_PREQUOTE:
1743 if(ftp->transfer != PPTRANSFER_BODY)
1744 state(data, FTP_STOP);
1746 if(ftpc->known_filesize != -1) {
1747 Curl_pgrsSetDownloadSize(data, ftpc->known_filesize);
1748 result = ftp_state_retr(data, ftpc->known_filesize);
1751 if(data->set.ignorecl || data->state.prefer_ascii) {
1752 /* 'ignorecl' is used to support download of growing files. It
1753 prevents the state machine from requesting the file size from
1754 the server. With an unknown file size the download continues
1755 until the server terminates it, otherwise the client stops if
1756 the received byte count exceeds the reported file size. Set
1757 option CURLOPT_IGNORE_CONTENT_LENGTH to 1 to enable this
1760 In addition: asking for the size for 'TYPE A' transfers is not
1761 constructive since servers don't report the converted size. So
1764 result = Curl_pp_sendf(data, &ftpc->pp, "RETR %s", ftpc->file);
1766 state(data, FTP_RETR);
1769 result = Curl_pp_sendf(data, &ftpc->pp, "SIZE %s", ftpc->file);
1771 state(data, FTP_RETR_SIZE);
1776 case FTP_STOR_PREQUOTE:
1777 result = ftp_state_ul_setup(data, FALSE);
1787 /* called from ftp_state_pasv_resp to switch to PASV in case of EPSV
1789 static CURLcode ftp_epsv_disable(struct Curl_easy *data,
1790 struct connectdata *conn)
1792 CURLcode result = CURLE_OK;
1795 #ifndef CURL_DISABLE_PROXY
1796 && !(conn->bits.tunnel_proxy || conn->bits.socksproxy)
1799 /* We can't disable EPSV when doing IPv6, so this is instead a fail */
1800 failf(data, "Failed EPSV attempt, exiting");
1801 return CURLE_WEIRD_SERVER_REPLY;
1804 infof(data, "Failed EPSV attempt. Disabling EPSV");
1805 /* disable it for next transfer */
1806 conn->bits.ftp_use_epsv = FALSE;
1807 data->state.errorbuf = FALSE; /* allow error message to get
1809 result = Curl_pp_sendf(data, &conn->proto.ftpc.pp, "%s", "PASV");
1811 conn->proto.ftpc.count1++;
1812 /* remain in/go to the FTP_PASV state */
1813 state(data, FTP_PASV);
1819 static char *control_address(struct connectdata *conn)
1821 /* Returns the control connection IP address.
1822 If a proxy tunnel is used, returns the original host name instead, because
1823 the effective control connection address is the proxy address,
1824 not the ftp host. */
1825 #ifndef CURL_DISABLE_PROXY
1826 if(conn->bits.tunnel_proxy || conn->bits.socksproxy)
1827 return conn->host.name;
1829 return conn->primary_ip;
1832 static CURLcode ftp_state_pasv_resp(struct Curl_easy *data,
1835 struct connectdata *conn = data->conn;
1836 struct ftp_conn *ftpc = &conn->proto.ftpc;
1838 struct Curl_dns_entry *addr = NULL;
1840 unsigned short connectport; /* the local port connect() should use! */
1841 char *str = &data->state.buffer[4]; /* start on the first letter */
1843 /* if we come here again, make sure the former name is cleared */
1844 Curl_safefree(ftpc->newhost);
1846 if((ftpc->count1 == 0) &&
1848 /* positive EPSV response */
1849 char *ptr = strchr(str, '(');
1854 if(5 == sscanf(ptr, "%c%c%c%u%c",
1860 const char sep1 = separator[0];
1863 /* The four separators should be identical, or else this is an oddly
1864 formatted reply and we bail out immediately. */
1865 for(i = 1; i<4; i++) {
1866 if(separator[i] != sep1) {
1867 ptr = NULL; /* set to NULL to signal error */
1872 failf(data, "Illegal port number in EPSV reply");
1873 return CURLE_FTP_WEIRD_PASV_REPLY;
1876 ftpc->newport = (unsigned short)(num & 0xffff);
1877 ftpc->newhost = strdup(control_address(conn));
1879 return CURLE_OUT_OF_MEMORY;
1886 failf(data, "Weirdly formatted EPSV reply");
1887 return CURLE_FTP_WEIRD_PASV_REPLY;
1890 else if((ftpc->count1 == 1) &&
1892 /* positive PASV response */
1893 unsigned int ip[4] = {0, 0, 0, 0};
1894 unsigned int port[2] = {0, 0};
1897 * Scan for a sequence of six comma-separated numbers and use them as
1898 * IP+port indicators.
1900 * Found reply-strings include:
1901 * "227 Entering Passive Mode (127,0,0,1,4,51)"
1902 * "227 Data transfer will passively listen to 127,0,0,1,4,51"
1903 * "227 Entering passive mode. 127,0,0,1,4,51"
1906 if(6 == sscanf(str, "%u,%u,%u,%u,%u,%u",
1907 &ip[0], &ip[1], &ip[2], &ip[3],
1908 &port[0], &port[1]))
1913 if(!*str || (ip[0] > 255) || (ip[1] > 255) || (ip[2] > 255) ||
1914 (ip[3] > 255) || (port[0] > 255) || (port[1] > 255) ) {
1915 failf(data, "Couldn't interpret the 227-response");
1916 return CURLE_FTP_WEIRD_227_FORMAT;
1919 /* we got OK from server */
1920 if(data->set.ftp_skip_ip) {
1921 /* told to ignore the remotely given IP but instead use the host we used
1922 for the control connection */
1923 infof(data, "Skip %u.%u.%u.%u for data connection, re-use %s instead",
1924 ip[0], ip[1], ip[2], ip[3],
1926 ftpc->newhost = strdup(control_address(conn));
1929 ftpc->newhost = aprintf("%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]);
1932 return CURLE_OUT_OF_MEMORY;
1934 ftpc->newport = (unsigned short)(((port[0]<<8) + port[1]) & 0xffff);
1936 else if(ftpc->count1 == 0) {
1937 /* EPSV failed, move on to PASV */
1938 return ftp_epsv_disable(data, conn);
1941 failf(data, "Bad PASV/EPSV response: %03d", ftpcode);
1942 return CURLE_FTP_WEIRD_PASV_REPLY;
1945 #ifndef CURL_DISABLE_PROXY
1946 if(conn->bits.proxy) {
1948 * This connection uses a proxy and we need to connect to the proxy again
1949 * here. We don't want to rely on a former host lookup that might've
1950 * expired now, instead we remake the lookup here and now!
1952 const char * const host_name = conn->bits.socksproxy ?
1953 conn->socks_proxy.host.name : conn->http_proxy.host.name;
1954 rc = Curl_resolv(data, host_name, (int)conn->port, FALSE, &addr);
1955 if(rc == CURLRESOLV_PENDING)
1956 /* BLOCKING, ignores the return code but 'addr' will be NULL in
1958 (void)Curl_resolver_wait_resolv(data, &addr);
1961 (unsigned short)conn->port; /* we connect to the proxy's port */
1964 failf(data, "Can't resolve proxy host %s:%hu", host_name, connectport);
1965 return CURLE_COULDNT_RESOLVE_PROXY;
1971 /* normal, direct, ftp connection */
1972 DEBUGASSERT(ftpc->newhost);
1974 /* postponed address resolution in case of tcp fastopen */
1975 if(conn->bits.tcp_fastopen && !conn->bits.reuse && !ftpc->newhost[0]) {
1976 Curl_conninfo_remote(data, conn, conn->sock[FIRSTSOCKET]);
1977 Curl_safefree(ftpc->newhost);
1978 ftpc->newhost = strdup(control_address(conn));
1980 return CURLE_OUT_OF_MEMORY;
1983 rc = Curl_resolv(data, ftpc->newhost, ftpc->newport, FALSE, &addr);
1984 if(rc == CURLRESOLV_PENDING)
1986 (void)Curl_resolver_wait_resolv(data, &addr);
1988 connectport = ftpc->newport; /* we connect to the remote port */
1991 failf(data, "Can't resolve new host %s:%hu", ftpc->newhost, connectport);
1992 return CURLE_FTP_CANT_GET_HOST;
1996 conn->bits.tcpconnect[SECONDARYSOCKET] = FALSE;
1997 result = Curl_connecthost(data, conn, addr);
2000 Curl_resolv_unlock(data, addr); /* we're done using this address */
2001 if(ftpc->count1 == 0 && ftpcode == 229)
2002 return ftp_epsv_disable(data, conn);
2009 * When this is used from the multi interface, this might've returned with
2010 * the 'connected' set to FALSE and thus we are now awaiting a non-blocking
2011 * connect to connect.
2014 if(data->set.verbose)
2015 /* this just dumps information about this second connection */
2016 ftp_pasv_verbose(data, addr->addr, ftpc->newhost, connectport);
2018 Curl_resolv_unlock(data, addr); /* we're done using this address */
2020 Curl_safefree(conn->secondaryhostname);
2021 conn->secondary_port = ftpc->newport;
2022 conn->secondaryhostname = strdup(ftpc->newhost);
2023 if(!conn->secondaryhostname)
2024 return CURLE_OUT_OF_MEMORY;
2026 conn->bits.do_more = TRUE;
2027 state(data, FTP_STOP); /* this phase is completed */
2032 static CURLcode ftp_state_port_resp(struct Curl_easy *data,
2035 struct connectdata *conn = data->conn;
2036 struct ftp_conn *ftpc = &conn->proto.ftpc;
2037 ftpport fcmd = (ftpport)ftpc->count1;
2038 CURLcode result = CURLE_OK;
2040 /* The FTP spec tells a positive response should have code 200.
2041 Be more permissive here to tolerate deviant servers. */
2042 if(ftpcode / 100 != 2) {
2043 /* the command failed */
2046 infof(data, "disabling EPRT usage");
2047 conn->bits.ftp_use_eprt = FALSE;
2052 failf(data, "Failed to do PORT");
2053 result = CURLE_FTP_PORT_FAILED;
2057 result = ftp_state_use_port(data, fcmd);
2060 infof(data, "Connect data stream actively");
2061 state(data, FTP_STOP); /* end of DO phase */
2062 result = ftp_dophase_done(data, FALSE);
2068 static CURLcode ftp_state_mdtm_resp(struct Curl_easy *data,
2071 CURLcode result = CURLE_OK;
2072 struct FTP *ftp = data->req.p.ftp;
2073 struct connectdata *conn = data->conn;
2074 struct ftp_conn *ftpc = &conn->proto.ftpc;
2079 /* we got a time. Format should be: "YYYYMMDDHHMMSS[.sss]" where the
2080 last .sss part is optional and means fractions of a second */
2081 int year, month, day, hour, minute, second;
2082 if(6 == sscanf(&data->state.buffer[4], "%04d%02d%02d%02d%02d%02d",
2083 &year, &month, &day, &hour, &minute, &second)) {
2084 /* we have a time, reformat it */
2086 msnprintf(timebuf, sizeof(timebuf),
2087 "%04d%02d%02d %02d:%02d:%02d GMT",
2088 year, month, day, hour, minute, second);
2089 /* now, convert this into a time() value: */
2090 data->info.filetime = Curl_getdate_capped(timebuf);
2093 #ifdef CURL_FTP_HTTPSTYLE_HEAD
2094 /* If we asked for a time of the file and we actually got one as well,
2095 we "emulate" a HTTP-style header in our output. */
2097 if(data->set.opt_no_body &&
2099 data->set.get_filetime &&
2100 (data->info.filetime >= 0) ) {
2101 char headerbuf[128];
2103 time_t filetime = data->info.filetime;
2105 const struct tm *tm = &buffer;
2107 result = Curl_gmtime(filetime, &buffer);
2111 /* format: "Tue, 15 Nov 1994 12:45:26" */
2112 headerbuflen = msnprintf(headerbuf, sizeof(headerbuf),
2113 "Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n",
2114 Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
2116 Curl_month[tm->tm_mon],
2121 result = Curl_client_write(data, CLIENTWRITE_BOTH, headerbuf,
2125 } /* end of a ridiculous amount of conditionals */
2130 infof(data, "unsupported MDTM reply format");
2132 case 550: /* 550 is used for several different problems, e.g.
2133 "No such file or directory" or "Permission denied".
2134 It does not mean that the file does not exist at all. */
2135 infof(data, "MDTM failed: file does not exist or permission problem,"
2140 if(data->set.timecondition) {
2141 if((data->info.filetime > 0) && (data->set.timevalue > 0)) {
2142 switch(data->set.timecondition) {
2143 case CURL_TIMECOND_IFMODSINCE:
2145 if(data->info.filetime <= data->set.timevalue) {
2146 infof(data, "The requested document is not new enough");
2147 ftp->transfer = PPTRANSFER_NONE; /* mark to not transfer data */
2148 data->info.timecond = TRUE;
2149 state(data, FTP_STOP);
2153 case CURL_TIMECOND_IFUNMODSINCE:
2154 if(data->info.filetime > data->set.timevalue) {
2155 infof(data, "The requested document is not old enough");
2156 ftp->transfer = PPTRANSFER_NONE; /* mark to not transfer data */
2157 data->info.timecond = TRUE;
2158 state(data, FTP_STOP);
2165 infof(data, "Skipping time comparison");
2170 result = ftp_state_type(data);
2175 static CURLcode ftp_state_type_resp(struct Curl_easy *data,
2179 CURLcode result = CURLE_OK;
2180 struct connectdata *conn = data->conn;
2182 if(ftpcode/100 != 2) {
2183 /* "sasserftpd" and "(u)r(x)bot ftpd" both responds with 226 after a
2184 successful 'TYPE I'. While that is not as RFC959 says, it is still a
2185 positive response code and we allow that. */
2186 failf(data, "Couldn't set desired mode");
2187 return CURLE_FTP_COULDNT_SET_TYPE;
2190 infof(data, "Got a %03d response code instead of the assumed 200",
2193 if(instate == FTP_TYPE)
2194 result = ftp_state_size(data, conn);
2195 else if(instate == FTP_LIST_TYPE)
2196 result = ftp_state_list(data);
2197 else if(instate == FTP_RETR_TYPE)
2198 result = ftp_state_retr_prequote(data);
2199 else if(instate == FTP_STOR_TYPE)
2200 result = ftp_state_stor_prequote(data);
2205 static CURLcode ftp_state_retr(struct Curl_easy *data,
2206 curl_off_t filesize)
2208 CURLcode result = CURLE_OK;
2209 struct FTP *ftp = data->req.p.ftp;
2210 struct connectdata *conn = data->conn;
2211 struct ftp_conn *ftpc = &conn->proto.ftpc;
2213 if(data->set.max_filesize && (filesize > data->set.max_filesize)) {
2214 failf(data, "Maximum file size exceeded");
2215 return CURLE_FILESIZE_EXCEEDED;
2217 ftp->downloadsize = filesize;
2219 if(data->state.resume_from) {
2220 /* We always (attempt to) get the size of downloads, so it is done before
2221 this even when not doing resumes. */
2222 if(filesize == -1) {
2223 infof(data, "ftp server doesn't support SIZE");
2224 /* We couldn't get the size and therefore we can't know if there really
2225 is a part of the file left to get, although the server will just
2226 close the connection when we start the connection so it won't cause
2227 us any harm, just not make us exit as nicely. */
2230 /* We got a file size report, so we check that there actually is a
2231 part of the file left to get, or else we go home. */
2232 if(data->state.resume_from< 0) {
2233 /* We're supposed to download the last abs(from) bytes */
2234 if(filesize < -data->state.resume_from) {
2235 failf(data, "Offset (%" CURL_FORMAT_CURL_OFF_T
2236 ") was beyond file size (%" CURL_FORMAT_CURL_OFF_T ")",
2237 data->state.resume_from, filesize);
2238 return CURLE_BAD_DOWNLOAD_RESUME;
2240 /* convert to size to download */
2241 ftp->downloadsize = -data->state.resume_from;
2242 /* download from where? */
2243 data->state.resume_from = filesize - ftp->downloadsize;
2246 if(filesize < data->state.resume_from) {
2247 failf(data, "Offset (%" CURL_FORMAT_CURL_OFF_T
2248 ") was beyond file size (%" CURL_FORMAT_CURL_OFF_T ")",
2249 data->state.resume_from, filesize);
2250 return CURLE_BAD_DOWNLOAD_RESUME;
2252 /* Now store the number of bytes we are expected to download */
2253 ftp->downloadsize = filesize-data->state.resume_from;
2257 if(ftp->downloadsize == 0) {
2258 /* no data to transfer */
2259 Curl_setup_transfer(data, -1, -1, FALSE, -1);
2260 infof(data, "File already completely downloaded");
2262 /* Set ->transfer so that we won't get any error in ftp_done()
2263 * because we didn't transfer the any file */
2264 ftp->transfer = PPTRANSFER_NONE;
2265 state(data, FTP_STOP);
2269 /* Set resume file transfer offset */
2270 infof(data, "Instructs server to resume from offset %"
2271 CURL_FORMAT_CURL_OFF_T, data->state.resume_from);
2273 result = Curl_pp_sendf(data, &ftpc->pp, "REST %" CURL_FORMAT_CURL_OFF_T,
2274 data->state.resume_from);
2276 state(data, FTP_RETR_REST);
2280 result = Curl_pp_sendf(data, &ftpc->pp, "RETR %s", ftpc->file);
2282 state(data, FTP_RETR);
2288 static CURLcode ftp_state_size_resp(struct Curl_easy *data,
2292 CURLcode result = CURLE_OK;
2293 curl_off_t filesize = -1;
2294 char *buf = data->state.buffer;
2296 /* get the size from the ascii string: */
2297 if(ftpcode == 213) {
2298 /* To allow servers to prepend "rubbish" in the response string, we scan
2299 for all the digits at the end of the response and parse only those as a
2301 char *start = &buf[4];
2302 char *fdigit = strchr(start, '\r');
2306 while(ISDIGIT(*fdigit) && (fdigit > start));
2307 if(!ISDIGIT(*fdigit))
2312 /* ignores parsing errors, which will make the size remain unknown */
2313 (void)curlx_strtoofft(fdigit, NULL, 0, &filesize);
2316 else if(ftpcode == 550) { /* "No such file or directory" */
2317 /* allow a SIZE failure for (resumed) uploads, when probing what command
2319 if(instate != FTP_STOR_SIZE) {
2320 failf(data, "The file does not exist");
2321 return CURLE_REMOTE_FILE_NOT_FOUND;
2325 if(instate == FTP_SIZE) {
2326 #ifdef CURL_FTP_HTTPSTYLE_HEAD
2327 if(-1 != filesize) {
2329 int clbuflen = msnprintf(clbuf, sizeof(clbuf),
2330 "Content-Length: %" CURL_FORMAT_CURL_OFF_T "\r\n", filesize);
2331 result = Curl_client_write(data, CLIENTWRITE_BOTH, clbuf, clbuflen);
2336 Curl_pgrsSetDownloadSize(data, filesize);
2337 result = ftp_state_rest(data, data->conn);
2339 else if(instate == FTP_RETR_SIZE) {
2340 Curl_pgrsSetDownloadSize(data, filesize);
2341 result = ftp_state_retr(data, filesize);
2343 else if(instate == FTP_STOR_SIZE) {
2344 data->state.resume_from = filesize;
2345 result = ftp_state_ul_setup(data, TRUE);
2351 static CURLcode ftp_state_rest_resp(struct Curl_easy *data,
2352 struct connectdata *conn,
2356 CURLcode result = CURLE_OK;
2357 struct ftp_conn *ftpc = &conn->proto.ftpc;
2362 #ifdef CURL_FTP_HTTPSTYLE_HEAD
2363 if(ftpcode == 350) {
2364 char buffer[24]= { "Accept-ranges: bytes\r\n" };
2365 result = Curl_client_write(data, CLIENTWRITE_BOTH, buffer,
2371 result = ftp_state_prepare_transfer(data);
2375 if(ftpcode != 350) {
2376 failf(data, "Couldn't use REST");
2377 result = CURLE_FTP_COULDNT_USE_REST;
2380 result = Curl_pp_sendf(data, &ftpc->pp, "RETR %s", ftpc->file);
2382 state(data, FTP_RETR);
2390 static CURLcode ftp_state_stor_resp(struct Curl_easy *data,
2391 int ftpcode, ftpstate instate)
2393 CURLcode result = CURLE_OK;
2394 struct connectdata *conn = data->conn;
2396 if(ftpcode >= 400) {
2397 failf(data, "Failed FTP upload: %0d", ftpcode);
2398 state(data, FTP_STOP);
2399 /* oops, we never close the sockets! */
2400 return CURLE_UPLOAD_FAILED;
2403 conn->proto.ftpc.state_saved = instate;
2405 /* PORT means we are now awaiting the server to connect to us. */
2406 if(data->set.ftp_use_port) {
2409 state(data, FTP_STOP); /* no longer in STOR state */
2411 result = AllowServerConnect(data, &connected);
2416 struct ftp_conn *ftpc = &conn->proto.ftpc;
2417 infof(data, "Data conn was not available immediately");
2418 ftpc->wait_data_conn = TRUE;
2423 return InitiateTransfer(data);
2426 /* for LIST and RETR responses */
2427 static CURLcode ftp_state_get_resp(struct Curl_easy *data,
2431 CURLcode result = CURLE_OK;
2432 struct FTP *ftp = data->req.p.ftp;
2433 struct connectdata *conn = data->conn;
2435 if((ftpcode == 150) || (ftpcode == 125)) {
2439 150 Opening BINARY mode data connection for /etc/passwd (2241
2440 bytes). (ok, the file is being transferred)
2443 150 Opening ASCII mode data connection for /bin/ls
2446 150 ASCII data connection for /bin/ls (137.167.104.91,37445) (0 bytes).
2449 150 Opening ASCII mode data connection for [file] (0.0.0.0,0) (545 bytes)
2452 125 Data connection already open; Transfer starting. */
2454 curl_off_t size = -1; /* default unknown size */
2458 * It appears that there are FTP-servers that return size 0 for files when
2459 * SIZE is used on the file while being in BINARY mode. To work around
2460 * that (stupid) behavior, we attempt to parse the RETR response even if
2461 * the SIZE returned size zero.
2463 * Debugging help from Salvatore Sorrentino on February 26, 2003.
2466 if((instate != FTP_LIST) &&
2467 !data->state.prefer_ascii &&
2468 (ftp->downloadsize < 1)) {
2470 * It seems directory listings either don't show the size or very
2471 * often uses size 0 anyway. ASCII transfers may very well turn out
2472 * that the transferred amount of data is not the same as this line
2473 * tells, why using this number in those cases only confuses us.
2475 * Example D above makes this parsing a little tricky */
2477 char *buf = data->state.buffer;
2478 bytes = strstr(buf, " bytes");
2480 long in = (long)(--bytes-buf);
2481 /* this is a hint there is size information in there! ;-) */
2483 /* scan for the left parenthesis and break there */
2486 /* skip only digits */
2487 if(!ISDIGIT(*bytes)) {
2491 /* one more estep backwards */
2494 /* if we have nothing but digits: */
2497 /* get the number! */
2498 (void)curlx_strtoofft(bytes, NULL, 0, &size);
2502 else if(ftp->downloadsize > -1)
2503 size = ftp->downloadsize;
2505 if(size > data->req.maxdownload && data->req.maxdownload > 0)
2506 size = data->req.size = data->req.maxdownload;
2507 else if((instate != FTP_LIST) && (data->state.prefer_ascii))
2508 size = -1; /* kludge for servers that understate ASCII mode file size */
2510 infof(data, "Maxdownload = %" CURL_FORMAT_CURL_OFF_T,
2511 data->req.maxdownload);
2513 if(instate != FTP_LIST)
2514 infof(data, "Getting file with size: %" CURL_FORMAT_CURL_OFF_T,
2518 conn->proto.ftpc.state_saved = instate;
2519 conn->proto.ftpc.retr_size_saved = size;
2521 if(data->set.ftp_use_port) {
2524 result = AllowServerConnect(data, &connected);
2529 struct ftp_conn *ftpc = &conn->proto.ftpc;
2530 infof(data, "Data conn was not available immediately");
2531 state(data, FTP_STOP);
2532 ftpc->wait_data_conn = TRUE;
2536 return InitiateTransfer(data);
2539 if((instate == FTP_LIST) && (ftpcode == 450)) {
2540 /* simply no matching files in the dir listing */
2541 ftp->transfer = PPTRANSFER_NONE; /* don't download anything */
2542 state(data, FTP_STOP); /* this phase is over */
2545 failf(data, "RETR response: %03d", ftpcode);
2546 return instate == FTP_RETR && ftpcode == 550?
2547 CURLE_REMOTE_FILE_NOT_FOUND:
2548 CURLE_FTP_COULDNT_RETR_FILE;
2555 /* after USER, PASS and ACCT */
2556 static CURLcode ftp_state_loggedin(struct Curl_easy *data)
2558 CURLcode result = CURLE_OK;
2559 struct connectdata *conn = data->conn;
2561 if(conn->bits.ftp_use_control_ssl) {
2562 /* PBSZ = PROTECTION BUFFER SIZE.
2564 The 'draft-murray-auth-ftp-ssl' (draft 12, page 7) says:
2566 Specifically, the PROT command MUST be preceded by a PBSZ
2567 command and a PBSZ command MUST be preceded by a successful
2568 security data exchange (the TLS negotiation in this case)
2570 ... (and on page 8):
2572 Thus the PBSZ command must still be issued, but must have a
2573 parameter of '0' to indicate that no buffering is taking place
2574 and the data connection should not be encapsulated.
2576 result = Curl_pp_sendf(data, &conn->proto.ftpc.pp, "PBSZ %d", 0);
2578 state(data, FTP_PBSZ);
2581 result = ftp_state_pwd(data, conn);
2586 /* for USER and PASS responses */
2587 static CURLcode ftp_state_user_resp(struct Curl_easy *data,
2591 CURLcode result = CURLE_OK;
2592 struct connectdata *conn = data->conn;
2593 struct ftp_conn *ftpc = &conn->proto.ftpc;
2594 (void)instate; /* no use for this yet */
2596 /* some need password anyway, and others just return 2xx ignored */
2597 if((ftpcode == 331) && (ftpc->state == FTP_USER)) {
2598 /* 331 Password required for ...
2599 (the server requires to send the user's password too) */
2600 result = Curl_pp_sendf(data, &ftpc->pp, "PASS %s",
2601 conn->passwd?conn->passwd:"");
2603 state(data, FTP_PASS);
2605 else if(ftpcode/100 == 2) {
2606 /* 230 User ... logged in.
2607 (the user logged in with or without password) */
2608 result = ftp_state_loggedin(data);
2610 else if(ftpcode == 332) {
2611 if(data->set.str[STRING_FTP_ACCOUNT]) {
2612 result = Curl_pp_sendf(data, &ftpc->pp, "ACCT %s",
2613 data->set.str[STRING_FTP_ACCOUNT]);
2615 state(data, FTP_ACCT);
2618 failf(data, "ACCT requested but none available");
2619 result = CURLE_LOGIN_DENIED;
2623 /* All other response codes, like:
2625 530 User ... access denied
2626 (the server denies to log the specified user) */
2628 if(data->set.str[STRING_FTP_ALTERNATIVE_TO_USER] &&
2629 !ftpc->ftp_trying_alternative) {
2630 /* Ok, USER failed. Let's try the supplied command. */
2632 Curl_pp_sendf(data, &ftpc->pp, "%s",
2633 data->set.str[STRING_FTP_ALTERNATIVE_TO_USER]);
2635 ftpc->ftp_trying_alternative = TRUE;
2636 state(data, FTP_USER);
2640 failf(data, "Access denied: %03d", ftpcode);
2641 result = CURLE_LOGIN_DENIED;
2647 /* for ACCT response */
2648 static CURLcode ftp_state_acct_resp(struct Curl_easy *data,
2651 CURLcode result = CURLE_OK;
2652 if(ftpcode != 230) {
2653 failf(data, "ACCT rejected by server: %03d", ftpcode);
2654 result = CURLE_FTP_WEIRD_PASS_REPLY; /* FIX */
2657 result = ftp_state_loggedin(data);
2663 static CURLcode ftp_statemachine(struct Curl_easy *data,
2664 struct connectdata *conn)
2667 curl_socket_t sock = conn->sock[FIRSTSOCKET];
2669 struct ftp_conn *ftpc = &conn->proto.ftpc;
2670 struct pingpong *pp = &ftpc->pp;
2671 static const char ftpauth[][4] = { "SSL", "TLS" };
2675 return Curl_pp_flushsend(data, pp);
2677 result = ftp_readresp(data, sock, pp, &ftpcode, &nread);
2682 /* we have now received a full FTP server response */
2683 switch(ftpc->state) {
2685 if(ftpcode == 230) {
2686 /* 230 User logged in - already! Take as 220 if TLS required. */
2687 if(data->set.use_ssl <= CURLUSESSL_TRY ||
2688 conn->bits.ftp_use_control_ssl)
2689 return ftp_state_user_resp(data, ftpcode, ftpc->state);
2691 else if(ftpcode != 220) {
2692 failf(data, "Got a %03d ftp-server response when 220 was expected",
2694 return CURLE_WEIRD_SERVER_REPLY;
2697 /* We have received a 220 response fine, now we proceed. */
2700 /* If not anonymous login, try a secure login. Note that this
2701 procedure is still BLOCKING. */
2703 Curl_sec_request_prot(conn, "private");
2704 /* We set private first as default, in case the line below fails to
2705 set a valid level */
2706 Curl_sec_request_prot(conn, data->set.str[STRING_KRB_LEVEL]);
2708 if(Curl_sec_login(data, conn)) {
2709 failf(data, "secure login failed");
2710 return CURLE_WEIRD_SERVER_REPLY;
2712 infof(data, "Authentication successful");
2716 if(data->set.use_ssl && !conn->bits.ftp_use_control_ssl) {
2717 /* We don't have a SSL/TLS control connection yet, but FTPS is
2718 requested. Try a FTPS connection now */
2721 switch(data->set.ftpsslauth) {
2722 case CURLFTPAUTH_DEFAULT:
2723 case CURLFTPAUTH_SSL:
2724 ftpc->count2 = 1; /* add one to get next */
2727 case CURLFTPAUTH_TLS:
2728 ftpc->count2 = -1; /* subtract one to get next */
2732 failf(data, "unsupported parameter to CURLOPT_FTPSSLAUTH: %d",
2733 (int)data->set.ftpsslauth);
2734 return CURLE_UNKNOWN_OPTION; /* we don't know what to do */
2736 result = Curl_pp_sendf(data, &ftpc->pp, "AUTH %s",
2737 ftpauth[ftpc->count1]);
2739 state(data, FTP_AUTH);
2742 result = ftp_state_user(data, conn);
2746 /* we have gotten the response to a previous AUTH command */
2749 return CURLE_WEIRD_SERVER_REPLY; /* Forbid pipelining in response. */
2751 /* RFC2228 (page 5) says:
2753 * If the server is willing to accept the named security mechanism,
2754 * and does not require any security data, it must respond with
2755 * reply code 234/334.
2758 if((ftpcode == 234) || (ftpcode == 334)) {
2759 /* Curl_ssl_connect is BLOCKING */
2760 result = Curl_ssl_connect(data, conn, FIRSTSOCKET);
2762 conn->bits.ftp_use_data_ssl = FALSE; /* clear-text data */
2763 conn->bits.ftp_use_control_ssl = TRUE; /* SSL on control */
2764 result = ftp_state_user(data, conn);
2767 else if(ftpc->count3 < 1) {
2769 ftpc->count1 += ftpc->count2; /* get next attempt */
2770 result = Curl_pp_sendf(data, &ftpc->pp, "AUTH %s",
2771 ftpauth[ftpc->count1]);
2772 /* remain in this same state */
2775 if(data->set.use_ssl > CURLUSESSL_TRY)
2776 /* we failed and CURLUSESSL_CONTROL or CURLUSESSL_ALL is set */
2777 result = CURLE_USE_SSL_FAILED;
2779 /* ignore the failure and continue */
2780 result = ftp_state_user(data, conn);
2786 result = ftp_state_user_resp(data, ftpcode, ftpc->state);
2790 result = ftp_state_acct_resp(data, ftpcode);
2795 Curl_pp_sendf(data, &ftpc->pp, "PROT %c",
2796 data->set.use_ssl == CURLUSESSL_CONTROL ? 'C' : 'P');
2798 state(data, FTP_PROT);
2802 if(ftpcode/100 == 2)
2803 /* We have enabled SSL for the data connection! */
2804 conn->bits.ftp_use_data_ssl =
2805 (data->set.use_ssl != CURLUSESSL_CONTROL) ? TRUE : FALSE;
2806 /* FTP servers typically responds with 500 if they decide to reject
2808 else if(data->set.use_ssl > CURLUSESSL_CONTROL)
2809 /* we failed and bails out */
2810 return CURLE_USE_SSL_FAILED;
2812 if(data->set.ftp_ccc) {
2813 /* CCC - Clear Command Channel
2815 result = Curl_pp_sendf(data, &ftpc->pp, "%s", "CCC");
2817 state(data, FTP_CCC);
2820 result = ftp_state_pwd(data, conn);
2825 /* First shut down the SSL layer (note: this call will block) */
2826 result = Curl_ssl_shutdown(data, conn, FIRSTSOCKET);
2829 failf(data, "Failed to clear the command channel (CCC)");
2832 /* Then continue as normal */
2833 result = ftp_state_pwd(data, conn);
2837 if(ftpcode == 257) {
2838 char *ptr = &data->state.buffer[4]; /* start on the first letter */
2839 const size_t buf_size = data->set.buffer_size;
2841 bool entry_extracted = FALSE;
2843 dir = malloc(nread + 1);
2845 return CURLE_OUT_OF_MEMORY;
2847 /* Reply format is like
2848 257<space>[rubbish]"<directory-name>"<space><commentary> and the
2851 The directory name can contain any character; embedded
2852 double-quotes should be escaped by double-quotes (the
2853 "quote-doubling" convention).
2856 /* scan for the first double-quote for non-standard responses */
2857 while(ptr < &data->state.buffer[buf_size]
2858 && *ptr != '\n' && *ptr != '\0' && *ptr != '"')
2862 /* it started good */
2865 for(store = dir; *ptr;) {
2867 if('\"' == ptr[1]) {
2868 /* "quote-doubling" */
2874 entry_extracted = TRUE;
2875 break; /* get out of this loop */
2883 *store = '\0'; /* null-terminate */
2885 if(entry_extracted) {
2886 /* If the path name does not look like an absolute path (i.e.: it
2887 does not start with a '/'), we probably need some server-dependent
2888 adjustments. For example, this is the case when connecting to
2889 an OS400 FTP server: this server supports two name syntaxes,
2890 the default one being incompatible with standard paths. In
2891 addition, this server switches automatically to the regular path
2892 syntax when one is encountered in a command: this results in
2893 having an entrypath in the wrong syntax when later used in CWD.
2894 The method used here is to check the server OS: we do it only
2895 if the path name looks strange to minimize overhead on other
2898 if(!ftpc->server_os && dir[0] != '/') {
2899 result = Curl_pp_sendf(data, &ftpc->pp, "%s", "SYST");
2904 Curl_safefree(ftpc->entrypath);
2905 ftpc->entrypath = dir; /* remember this */
2906 infof(data, "Entry path is '%s'", ftpc->entrypath);
2907 /* also save it where getinfo can access it: */
2908 data->state.most_recent_ftp_entrypath = ftpc->entrypath;
2909 state(data, FTP_SYST);
2913 Curl_safefree(ftpc->entrypath);
2914 ftpc->entrypath = dir; /* remember this */
2915 infof(data, "Entry path is '%s'", ftpc->entrypath);
2916 /* also save it where getinfo can access it: */
2917 data->state.most_recent_ftp_entrypath = ftpc->entrypath;
2920 /* couldn't get the path */
2922 infof(data, "Failed to figure out path");
2925 state(data, FTP_STOP); /* we are done with the CONNECT phase! */
2926 DEBUGF(infof(data, "protocol connect phase DONE"));
2930 if(ftpcode == 215) {
2931 char *ptr = &data->state.buffer[4]; /* start on the first letter */
2935 os = malloc(nread + 1);
2937 return CURLE_OUT_OF_MEMORY;
2939 /* Reply format is like
2940 215<space><OS-name><space><commentary>
2944 for(store = os; *ptr && *ptr != ' ';)
2946 *store = '\0'; /* null-terminate */
2948 /* Check for special servers here. */
2950 if(strcasecompare(os, "OS/400")) {
2951 /* Force OS400 name format 1. */
2952 result = Curl_pp_sendf(data, &ftpc->pp, "%s", "SITE NAMEFMT 1");
2957 /* remember target server OS */
2958 Curl_safefree(ftpc->server_os);
2959 ftpc->server_os = os;
2960 state(data, FTP_NAMEFMT);
2963 /* Nothing special for the target server. */
2964 /* remember target server OS */
2965 Curl_safefree(ftpc->server_os);
2966 ftpc->server_os = os;
2969 /* Cannot identify server OS. Continue anyway and cross fingers. */
2972 state(data, FTP_STOP); /* we are done with the CONNECT phase! */
2973 DEBUGF(infof(data, "protocol connect phase DONE"));
2977 if(ftpcode == 250) {
2978 /* Name format change successful: reload initial path. */
2979 ftp_state_pwd(data, conn);
2983 state(data, FTP_STOP); /* we are done with the CONNECT phase! */
2984 DEBUGF(infof(data, "protocol connect phase DONE"));
2989 case FTP_RETR_PREQUOTE:
2990 case FTP_STOR_PREQUOTE:
2991 if((ftpcode >= 400) && !ftpc->count2) {
2992 /* failure response code, and not allowed to fail */
2993 failf(data, "QUOT command failed with %03d", ftpcode);
2994 result = CURLE_QUOTE_ERROR;
2997 result = ftp_state_quote(data, FALSE, ftpc->state);
3001 if(ftpcode/100 != 2) {
3002 /* failure to CWD there */
3003 if(data->set.ftp_create_missing_dirs &&
3004 ftpc->cwdcount && !ftpc->count2) {
3006 ftpc->count2++; /* counter to prevent CWD-MKD loops */
3008 /* count3 is set to allow MKD to fail once per dir. In the case when
3009 CWD fails and then MKD fails (due to another session raced it to
3010 create the dir) this then allows for a second try to CWD to it. */
3011 ftpc->count3 = (data->set.ftp_create_missing_dirs == 2) ? 1 : 0;
3013 result = Curl_pp_sendf(data, &ftpc->pp, "MKD %s",
3014 ftpc->dirs[ftpc->cwdcount - 1]);
3016 state(data, FTP_MKD);
3019 /* return failure */
3020 failf(data, "Server denied you to change to the given directory");
3021 ftpc->cwdfail = TRUE; /* don't remember this path as we failed
3023 result = CURLE_REMOTE_ACCESS_DENIED;
3029 if(++ftpc->cwdcount <= ftpc->dirdepth)
3031 result = Curl_pp_sendf(data, &ftpc->pp, "CWD %s",
3032 ftpc->dirs[ftpc->cwdcount - 1]);
3034 result = ftp_state_mdtm(data);
3039 if((ftpcode/100 != 2) && !ftpc->count3--) {
3040 /* failure to MKD the dir */
3041 failf(data, "Failed to MKD dir: %03d", ftpcode);
3042 result = CURLE_REMOTE_ACCESS_DENIED;
3045 state(data, FTP_CWD);
3047 result = Curl_pp_sendf(data, &ftpc->pp, "CWD %s",
3048 ftpc->dirs[ftpc->cwdcount - 1]);
3053 result = ftp_state_mdtm_resp(data, ftpcode);
3060 result = ftp_state_type_resp(data, ftpcode, ftpc->state);
3066 result = ftp_state_size_resp(data, ftpcode, ftpc->state);
3071 result = ftp_state_rest_resp(data, conn, ftpcode, ftpc->state);
3075 if(ftpcode != 200) {
3076 /* there only is this one standard OK return code. */
3077 failf(data, "PRET command not accepted: %03d", ftpcode);
3078 return CURLE_FTP_PRET_FAILED;
3080 result = ftp_state_use_pasv(data, conn);
3084 result = ftp_state_pasv_resp(data, ftpcode);
3088 result = ftp_state_port_resp(data, ftpcode);
3093 result = ftp_state_get_resp(data, ftpcode, ftpc->state);
3097 result = ftp_state_stor_resp(data, ftpcode, ftpc->state);
3101 /* fallthrough, just stop! */
3103 /* internal error */
3104 state(data, FTP_STOP);
3113 /* called repeatedly until done from multi.c */
3114 static CURLcode ftp_multi_statemach(struct Curl_easy *data,
3117 struct connectdata *conn = data->conn;
3118 struct ftp_conn *ftpc = &conn->proto.ftpc;
3119 CURLcode result = Curl_pp_statemach(data, &ftpc->pp, FALSE, FALSE);
3121 /* Check for the state outside of the Curl_socket_check() return code checks
3122 since at times we are in fact already in this state when this function
3124 *done = (ftpc->state == FTP_STOP) ? TRUE : FALSE;
3129 static CURLcode ftp_block_statemach(struct Curl_easy *data,
3130 struct connectdata *conn)
3132 struct ftp_conn *ftpc = &conn->proto.ftpc;
3133 struct pingpong *pp = &ftpc->pp;
3134 CURLcode result = CURLE_OK;
3136 while(ftpc->state != FTP_STOP) {
3137 result = Curl_pp_statemach(data, pp, TRUE, TRUE /* disconnecting */);
3146 * ftp_connect() should do everything that is to be considered a part of
3147 * the connection phase.
3149 * The variable 'done' points to will be TRUE if the protocol-layer connect
3150 * phase is done when this function returns, or FALSE if not.
3153 static CURLcode ftp_connect(struct Curl_easy *data,
3154 bool *done) /* see description above */
3157 struct connectdata *conn = data->conn;
3158 struct ftp_conn *ftpc = &conn->proto.ftpc;
3159 struct pingpong *pp = &ftpc->pp;
3161 *done = FALSE; /* default to not done yet */
3163 /* We always support persistent connections on ftp */
3164 connkeep(conn, "FTP default");
3166 PINGPONG_SETUP(pp, ftp_statemachine, ftp_endofresp);
3168 if(conn->handler->flags & PROTOPT_SSL) {
3170 result = Curl_ssl_connect(data, conn, FIRSTSOCKET);
3173 conn->bits.ftp_use_control_ssl = TRUE;
3176 Curl_pp_setup(pp); /* once per transfer */
3177 Curl_pp_init(data, pp); /* init the generic pingpong data */
3179 /* When we connect, we start in the state where we await the 220
3181 state(data, FTP_WAIT220);
3183 result = ftp_multi_statemach(data, done);
3188 /***********************************************************************
3192 * The DONE function. This does what needs to be done after a single DO has
3195 * Input argument is already checked for validity.
3197 static CURLcode ftp_done(struct Curl_easy *data, CURLcode status,
3200 struct connectdata *conn = data->conn;
3201 struct FTP *ftp = data->req.p.ftp;
3202 struct ftp_conn *ftpc = &conn->proto.ftpc;
3203 struct pingpong *pp = &ftpc->pp;
3206 CURLcode result = CURLE_OK;
3207 char *rawPath = NULL;
3214 case CURLE_BAD_DOWNLOAD_RESUME:
3215 case CURLE_FTP_WEIRD_PASV_REPLY:
3216 case CURLE_FTP_PORT_FAILED:
3217 case CURLE_FTP_ACCEPT_FAILED:
3218 case CURLE_FTP_ACCEPT_TIMEOUT:
3219 case CURLE_FTP_COULDNT_SET_TYPE:
3220 case CURLE_FTP_COULDNT_RETR_FILE:
3221 case CURLE_PARTIAL_FILE:
3222 case CURLE_UPLOAD_FAILED:
3223 case CURLE_REMOTE_ACCESS_DENIED:
3224 case CURLE_FILESIZE_EXCEEDED:
3225 case CURLE_REMOTE_FILE_NOT_FOUND:
3226 case CURLE_WRITE_ERROR:
3227 /* the connection stays alive fine even though this happened */
3229 case CURLE_OK: /* doesn't affect the control connection's status */
3233 /* until we cope better with prematurely ended requests, let them
3234 * fallback as if in complete failure */
3236 default: /* by default, an error means the control connection is
3237 wedged and should not be used anymore */
3238 ftpc->ctl_valid = FALSE;
3239 ftpc->cwdfail = TRUE; /* set this TRUE to prevent us to remember the
3240 current path, as this connection is going */
3241 connclose(conn, "FTP ended with bad error code");
3242 result = status; /* use the already set error code */
3246 if(data->state.wildcardmatch) {
3247 if(data->set.chunk_end && ftpc->file) {
3248 Curl_set_in_callback(data, true);
3249 data->set.chunk_end(data->wildcard.customptr);
3250 Curl_set_in_callback(data, false);
3252 ftpc->known_filesize = -1;
3256 /* get the url-decoded "raw" path */
3257 result = Curl_urldecode(ftp->path, 0, &rawPath, &pathLen,
3260 /* We can limp along anyway (and should try to since we may already be in
3261 * the error path) */
3262 ftpc->ctl_valid = FALSE; /* mark control connection as bad */
3263 connclose(conn, "FTP: out of memory!"); /* mark for connection closure */
3264 free(ftpc->prevpath);
3265 ftpc->prevpath = NULL; /* no path remembering */
3267 else { /* remember working directory for connection reuse */
3268 if((data->set.ftp_filemethod == FTPFILE_NOCWD) && (rawPath[0] == '/'))
3269 free(rawPath); /* full path => no CWDs happened => keep ftpc->prevpath */
3271 free(ftpc->prevpath);
3273 if(!ftpc->cwdfail) {
3274 if(data->set.ftp_filemethod == FTPFILE_NOCWD)
3275 pathLen = 0; /* relative path => working directory is FTP home */
3277 pathLen -= ftpc->file?strlen(ftpc->file):0; /* file is url-decoded */
3279 rawPath[pathLen] = '\0';
3280 ftpc->prevpath = rawPath;
3284 ftpc->prevpath = NULL; /* no path */
3289 infof(data, "Remembering we are in dir \"%s\"", ftpc->prevpath);
3292 /* free the dir tree and file parts */
3295 /* shut down the socket to inform the server we're done */
3298 shutdown(conn->sock[SECONDARYSOCKET], 2); /* SD_BOTH */
3301 if(conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD) {
3302 if(!result && ftpc->dont_check && data->req.maxdownload > 0) {
3303 /* partial download completed */
3304 result = Curl_pp_sendf(data, pp, "%s", "ABOR");
3306 failf(data, "Failure sending ABOR command: %s",
3307 curl_easy_strerror(result));
3308 ftpc->ctl_valid = FALSE; /* mark control connection as bad */
3309 connclose(conn, "ABOR command failed"); /* connection closure */
3313 if(conn->ssl[SECONDARYSOCKET].use) {
3314 /* The secondary socket is using SSL so we must close down that part
3315 first before we close the socket for real */
3316 Curl_ssl_close(data, conn, SECONDARYSOCKET);
3318 /* Note that we keep "use" set to TRUE since that (next) connection is
3319 still requested to use SSL */
3321 close_secondarysocket(data, conn);
3324 if(!result && (ftp->transfer == PPTRANSFER_BODY) && ftpc->ctl_valid &&
3325 pp->pending_resp && !premature) {
3327 * Let's see what the server says about the transfer we just performed,
3328 * but lower the timeout as sometimes this connection has died while the
3329 * data has been transferred. This happens when doing through NATs etc that
3330 * abandon old silent connections.
3332 timediff_t old_time = pp->response_time;
3334 pp->response_time = 60*1000; /* give it only a minute for now */
3335 pp->response = Curl_now(); /* timeout relative now */
3337 result = Curl_GetFTPResponse(data, &nread, &ftpcode);
3339 pp->response_time = old_time; /* set this back to previous value */
3341 if(!nread && (CURLE_OPERATION_TIMEDOUT == result)) {
3342 failf(data, "control connection looks dead");
3343 ftpc->ctl_valid = FALSE; /* mark control connection as bad */
3344 connclose(conn, "Timeout or similar in FTP DONE operation"); /* close */
3348 Curl_safefree(ftp->pathalloc);
3352 if(ftpc->dont_check && data->req.maxdownload > 0) {
3353 /* we have just sent ABOR and there is no reliable way to check if it was
3354 * successful or not; we have to close the connection now */
3355 infof(data, "partial download completed, closing connection");
3356 connclose(conn, "Partial download with no ability to check");
3360 if(!ftpc->dont_check) {
3361 /* 226 Transfer complete, 250 Requested file action okay, completed. */
3367 failf(data, "Exceeded storage allocation");
3368 result = CURLE_REMOTE_DISK_FULL;
3371 failf(data, "server did not report OK, got %d", ftpcode);
3372 result = CURLE_PARTIAL_FILE;
3378 if(result || premature)
3379 /* the response code from the transfer showed an error already so no
3380 use checking further */
3382 else if(data->set.upload) {
3383 if((-1 != data->state.infilesize) &&
3384 (data->state.infilesize != data->req.writebytecount) &&
3386 (ftp->transfer == PPTRANSFER_BODY)) {
3387 failf(data, "Uploaded unaligned file size (%" CURL_FORMAT_CURL_OFF_T
3388 " out of %" CURL_FORMAT_CURL_OFF_T " bytes)",
3389 data->req.writebytecount, data->state.infilesize);
3390 result = CURLE_PARTIAL_FILE;
3394 if((-1 != data->req.size) &&
3395 (data->req.size != data->req.bytecount) &&
3396 #ifdef CURL_DO_LINEEND_CONV
3397 /* Most FTP servers don't adjust their file SIZE response for CRLFs, so
3398 * we'll check to see if the discrepancy can be explained by the number
3399 * of CRLFs we've changed to LFs.
3401 ((data->req.size + data->state.crlf_conversions) !=
3402 data->req.bytecount) &&
3403 #endif /* CURL_DO_LINEEND_CONV */
3404 (data->req.maxdownload != data->req.bytecount)) {
3405 failf(data, "Received only partial file: %" CURL_FORMAT_CURL_OFF_T
3406 " bytes", data->req.bytecount);
3407 result = CURLE_PARTIAL_FILE;
3409 else if(!ftpc->dont_check &&
3410 !data->req.bytecount &&
3411 (data->req.size>0)) {
3412 failf(data, "No data was received");
3413 result = CURLE_FTP_COULDNT_RETR_FILE;
3417 /* clear these for next connection */
3418 ftp->transfer = PPTRANSFER_BODY;
3419 ftpc->dont_check = FALSE;
3421 /* Send any post-transfer QUOTE strings? */
3422 if(!status && !result && !premature && data->set.postquote)
3423 result = ftp_sendquote(data, conn, data->set.postquote);
3424 Curl_safefree(ftp->pathalloc);
3428 /***********************************************************************
3432 * Where a 'quote' means a list of custom commands to send to the server.
3433 * The quote list is passed as an argument.
3439 CURLcode ftp_sendquote(struct Curl_easy *data,
3440 struct connectdata *conn, struct curl_slist *quote)
3442 struct curl_slist *item;
3443 struct ftp_conn *ftpc = &conn->proto.ftpc;
3444 struct pingpong *pp = &ftpc->pp;
3450 char *cmd = item->data;
3451 bool acceptfail = FALSE;
3455 /* if a command starts with an asterisk, which a legal FTP command never
3456 can, the command will be allowed to fail without it causing any
3457 aborts or cancels etc. It will cause libcurl to act as if the command
3458 is successful, whatever the server reponds. */
3465 result = Curl_pp_sendf(data, &ftpc->pp, "%s", cmd);
3467 pp->response = Curl_now(); /* timeout relative now */
3468 result = Curl_GetFTPResponse(data, &nread, &ftpcode);
3473 if(!acceptfail && (ftpcode >= 400)) {
3474 failf(data, "QUOT string not accepted: %s", cmd);
3475 return CURLE_QUOTE_ERROR;
3485 /***********************************************************************
3489 * Returns TRUE if we in the current situation should send TYPE
3491 static int ftp_need_type(struct connectdata *conn,
3494 return conn->proto.ftpc.transfertype != (ascii_wanted?'A':'I');
3497 /***********************************************************************
3501 * Set TYPE. We only deal with ASCII or BINARY so this function
3503 * If the transfer type is not sent, simulate on OK response in newstate
3505 static CURLcode ftp_nb_type(struct Curl_easy *data,
3506 struct connectdata *conn,
3507 bool ascii, ftpstate newstate)
3509 struct ftp_conn *ftpc = &conn->proto.ftpc;
3511 char want = (char)(ascii?'A':'I');
3513 if(ftpc->transfertype == want) {
3514 state(data, newstate);
3515 return ftp_state_type_resp(data, 200, newstate);
3518 result = Curl_pp_sendf(data, &ftpc->pp, "TYPE %c", want);
3520 state(data, newstate);
3522 /* keep track of our current transfer type */
3523 ftpc->transfertype = want;
3528 /***************************************************************************
3530 * ftp_pasv_verbose()
3532 * This function only outputs some informationals about this second connection
3533 * when we've issued a PASV command before and thus we have connected to a
3534 * possibly new IP address.
3537 #ifndef CURL_DISABLE_VERBOSE_STRINGS
3539 ftp_pasv_verbose(struct Curl_easy *data,
3540 struct Curl_addrinfo *ai,
3541 char *newhost, /* ascii version */
3545 Curl_printable_address(ai, buf, sizeof(buf));
3546 infof(data, "Connecting to %s (%s) port %d", newhost, buf, port);
3553 * This function shall be called when the second FTP (data) connection is
3556 * 'complete' can return 0 for incomplete, 1 for done and -1 for go back
3557 * (which basically is only for when PASV is being sent to retry a failed
3561 static CURLcode ftp_do_more(struct Curl_easy *data, int *completep)
3563 struct connectdata *conn = data->conn;
3564 struct ftp_conn *ftpc = &conn->proto.ftpc;
3565 CURLcode result = CURLE_OK;
3566 bool connected = FALSE;
3567 bool complete = FALSE;
3569 /* the ftp struct is inited in ftp_connect(). If we are connecting to an HTTP
3570 * proxy then the state will not be valid until after that connection is
3572 struct FTP *ftp = NULL;
3574 /* if the second connection isn't done yet, wait for it */
3575 if(!conn->bits.tcpconnect[SECONDARYSOCKET]) {
3576 if(Curl_connect_ongoing(conn)) {
3577 /* As we're in TUNNEL_CONNECT state now, we know the proxy name and port
3578 aren't used so we blank their arguments. */
3579 result = Curl_proxyCONNECT(data, SECONDARYSOCKET, NULL, 0);
3584 result = Curl_is_connected(data, conn, SECONDARYSOCKET, &connected);
3586 /* Ready to do more? */
3588 DEBUGF(infof(data, "DO-MORE connected phase starts"));
3591 if(result && (ftpc->count1 == 0)) {
3592 *completep = -1; /* go back to DOING please */
3593 /* this is a EPSV connect failing, try PASV instead */
3594 return ftp_epsv_disable(data, conn);
3600 #ifndef CURL_DISABLE_PROXY
3601 result = Curl_proxy_connect(data, SECONDARYSOCKET);
3605 if(CONNECT_SECONDARYSOCKET_PROXY_SSL())
3608 if(conn->bits.tunnel_proxy && conn->bits.httpproxy &&
3609 Curl_connect_ongoing(conn))
3613 /* Curl_proxy_connect might have moved the protocol state */
3614 ftp = data->req.p.ftp;
3617 /* already in a state so skip the initial commands.
3618 They are only done to kickstart the do_more state */
3619 result = ftp_multi_statemach(data, &complete);
3621 *completep = (int)complete;
3623 /* if we got an error or if we don't wait for a data connection return
3625 if(result || !ftpc->wait_data_conn)
3628 /* if we reach the end of the FTP state machine here, *complete will be
3629 TRUE but so is ftpc->wait_data_conn, which says we need to wait for the
3630 data connection and therefore we're not actually complete */
3634 if(ftp->transfer <= PPTRANSFER_INFO) {
3635 /* a transfer is about to take place, or if not a file name was given
3636 so we'll do a SIZE on it later and then we need the right TYPE first */
3638 if(ftpc->wait_data_conn == TRUE) {
3641 result = ReceivedServerConnect(data, &serv_conned);
3643 return result; /* Failed to accept data connection */
3646 /* It looks data connection is established */
3647 result = AcceptServerConnect(data);
3648 ftpc->wait_data_conn = FALSE;
3650 result = InitiateTransfer(data);
3655 *completep = 1; /* this state is now complete when the server has
3656 connected back to us */
3659 else if(data->set.upload) {
3660 result = ftp_nb_type(data, conn, data->state.prefer_ascii,
3665 result = ftp_multi_statemach(data, &complete);
3666 if(ftpc->wait_data_conn)
3667 /* if we reach the end of the FTP state machine here, *complete will be
3668 TRUE but so is ftpc->wait_data_conn, which says we need to wait for
3669 the data connection and therefore we're not actually complete */
3672 *completep = (int)complete;
3676 ftp->downloadsize = -1; /* unknown as of yet */
3678 result = Curl_range(data);
3680 if(result == CURLE_OK && data->req.maxdownload >= 0) {
3681 /* Don't check for successful transfer */
3682 ftpc->dont_check = TRUE;
3687 else if(data->state.list_only || !ftpc->file) {
3688 /* The specified path ends with a slash, and therefore we think this
3689 is a directory that is requested, use LIST. But before that we
3690 need to set ASCII transfer mode. */
3692 /* But only if a body transfer was requested. */
3693 if(ftp->transfer == PPTRANSFER_BODY) {
3694 result = ftp_nb_type(data, conn, TRUE, FTP_LIST_TYPE);
3698 /* otherwise just fall through */
3701 result = ftp_nb_type(data, conn, data->state.prefer_ascii,
3707 result = ftp_multi_statemach(data, &complete);
3708 *completep = (int)complete;
3713 /* no data to transfer */
3714 Curl_setup_transfer(data, -1, -1, FALSE, -1);
3716 if(!ftpc->wait_data_conn) {
3717 /* no waiting for the data connection so this is now complete */
3719 DEBUGF(infof(data, "DO-MORE phase ends with %d", (int)result));
3727 /***********************************************************************
3731 * This is the actual DO function for FTP. Get a file/directory according to
3732 * the options previously setup.
3736 CURLcode ftp_perform(struct Curl_easy *data,
3737 bool *connected, /* connect status after PASV / PORT */
3740 /* this is FTP and no proxy */
3741 CURLcode result = CURLE_OK;
3742 struct connectdata *conn = data->conn;
3744 DEBUGF(infof(data, "DO phase starts"));
3746 if(data->set.opt_no_body) {
3747 /* requested no body means no transfer... */
3748 struct FTP *ftp = data->req.p.ftp;
3749 ftp->transfer = PPTRANSFER_INFO;
3752 *dophase_done = FALSE; /* not done yet */
3754 /* start the first command in the DO phase */
3755 result = ftp_state_quote(data, TRUE, FTP_QUOTE);
3759 /* run the state-machine */
3760 result = ftp_multi_statemach(data, dophase_done);
3762 *connected = conn->bits.tcpconnect[SECONDARYSOCKET];
3764 infof(data, "ftp_perform ends with SECONDARY: %d", *connected);
3767 DEBUGF(infof(data, "DO phase is complete1"));
3773 static void wc_data_dtor(void *ptr)
3775 struct ftp_wc *ftpwc = ptr;
3776 if(ftpwc && ftpwc->parser)
3777 Curl_ftp_parselist_data_free(&ftpwc->parser);
3781 static CURLcode init_wc_data(struct Curl_easy *data)
3784 struct FTP *ftp = data->req.p.ftp;
3785 char *path = ftp->path;
3786 struct WildcardData *wildcard = &(data->wildcard);
3787 CURLcode result = CURLE_OK;
3788 struct ftp_wc *ftpwc = NULL;
3790 last_slash = strrchr(ftp->path, '/');
3793 if(last_slash[0] == '\0') {
3794 wildcard->state = CURLWC_CLEAN;
3795 result = ftp_parse_url_path(data);
3798 wildcard->pattern = strdup(last_slash);
3799 if(!wildcard->pattern)
3800 return CURLE_OUT_OF_MEMORY;
3801 last_slash[0] = '\0'; /* cut file from path */
3803 else { /* there is only 'wildcard pattern' or nothing */
3805 wildcard->pattern = strdup(path);
3806 if(!wildcard->pattern)
3807 return CURLE_OUT_OF_MEMORY;
3810 else { /* only list */
3811 wildcard->state = CURLWC_CLEAN;
3812 result = ftp_parse_url_path(data);
3817 /* program continues only if URL is not ending with slash, allocate needed
3818 resources for wildcard transfer */
3820 /* allocate ftp protocol specific wildcard data */
3821 ftpwc = calloc(1, sizeof(struct ftp_wc));
3823 result = CURLE_OUT_OF_MEMORY;
3827 /* INITIALIZE parselist structure */
3828 ftpwc->parser = Curl_ftp_parselist_data_alloc();
3829 if(!ftpwc->parser) {
3830 result = CURLE_OUT_OF_MEMORY;
3834 wildcard->protdata = ftpwc; /* put it to the WildcardData tmp pointer */
3835 wildcard->dtor = wc_data_dtor;
3837 /* wildcard does not support NOCWD option (assert it?) */
3838 if(data->set.ftp_filemethod == FTPFILE_NOCWD)
3839 data->set.ftp_filemethod = FTPFILE_MULTICWD;
3841 /* try to parse ftp url */
3842 result = ftp_parse_url_path(data);
3847 wildcard->path = strdup(ftp->path);
3848 if(!wildcard->path) {
3849 result = CURLE_OUT_OF_MEMORY;
3853 /* backup old write_function */
3854 ftpwc->backup.write_function = data->set.fwrite_func;
3855 /* parsing write function */
3856 data->set.fwrite_func = Curl_ftp_parselist;
3857 /* backup old file descriptor */
3858 ftpwc->backup.file_descriptor = data->set.out;
3859 /* let the writefunc callback know the transfer */
3860 data->set.out = data;
3862 infof(data, "Wildcard - Parsing started");
3867 Curl_ftp_parselist_data_free(&ftpwc->parser);
3870 Curl_safefree(wildcard->pattern);
3871 wildcard->dtor = ZERO_NULL;
3872 wildcard->protdata = NULL;
3876 static CURLcode wc_statemach(struct Curl_easy *data)
3878 struct WildcardData * const wildcard = &(data->wildcard);
3879 struct connectdata *conn = data->conn;
3880 CURLcode result = CURLE_OK;
3883 switch(wildcard->state) {
3885 result = init_wc_data(data);
3886 if(wildcard->state == CURLWC_CLEAN)
3889 wildcard->state = result ? CURLWC_ERROR : CURLWC_MATCHING;
3892 case CURLWC_MATCHING: {
3893 /* In this state is LIST response successfully parsed, so lets restore
3894 previous WRITEFUNCTION callback and WRITEDATA pointer */
3895 struct ftp_wc *ftpwc = wildcard->protdata;
3896 data->set.fwrite_func = ftpwc->backup.write_function;
3897 data->set.out = ftpwc->backup.file_descriptor;
3898 ftpwc->backup.write_function = ZERO_NULL;
3899 ftpwc->backup.file_descriptor = NULL;
3900 wildcard->state = CURLWC_DOWNLOADING;
3902 if(Curl_ftp_parselist_geterror(ftpwc->parser)) {
3903 /* error found in LIST parsing */
3904 wildcard->state = CURLWC_CLEAN;
3907 if(wildcard->filelist.size == 0) {
3908 /* no corresponding file */
3909 wildcard->state = CURLWC_CLEAN;
3910 return CURLE_REMOTE_FILE_NOT_FOUND;
3915 case CURLWC_DOWNLOADING: {
3916 /* filelist has at least one file, lets get first one */
3917 struct ftp_conn *ftpc = &conn->proto.ftpc;
3918 struct curl_fileinfo *finfo = wildcard->filelist.head->ptr;
3919 struct FTP *ftp = data->req.p.ftp;
3921 char *tmp_path = aprintf("%s%s", wildcard->path, finfo->filename);
3923 return CURLE_OUT_OF_MEMORY;
3925 /* switch default ftp->path and tmp_path */
3926 free(ftp->pathalloc);
3927 ftp->pathalloc = ftp->path = tmp_path;
3929 infof(data, "Wildcard - START of \"%s\"", finfo->filename);
3930 if(data->set.chunk_bgn) {
3932 Curl_set_in_callback(data, true);
3933 userresponse = data->set.chunk_bgn(
3934 finfo, wildcard->customptr, (int)wildcard->filelist.size);
3935 Curl_set_in_callback(data, false);
3936 switch(userresponse) {
3937 case CURL_CHUNK_BGN_FUNC_SKIP:
3938 infof(data, "Wildcard - \"%s\" skipped by user",
3940 wildcard->state = CURLWC_SKIP;
3942 case CURL_CHUNK_BGN_FUNC_FAIL:
3943 return CURLE_CHUNK_FAILED;
3947 if(finfo->filetype != CURLFILETYPE_FILE) {
3948 wildcard->state = CURLWC_SKIP;
3952 if(finfo->flags & CURLFINFOFLAG_KNOWN_SIZE)
3953 ftpc->known_filesize = finfo->size;
3955 result = ftp_parse_url_path(data);
3959 /* we don't need the Curl_fileinfo of first file anymore */
3960 Curl_llist_remove(&wildcard->filelist, wildcard->filelist.head, NULL);
3962 if(wildcard->filelist.size == 0) { /* remains only one file to down. */
3963 wildcard->state = CURLWC_CLEAN;
3964 /* after that will be ftp_do called once again and no transfer
3965 will be done because of CURLWC_CLEAN state */
3972 if(data->set.chunk_end) {
3973 Curl_set_in_callback(data, true);
3974 data->set.chunk_end(data->wildcard.customptr);
3975 Curl_set_in_callback(data, false);
3977 Curl_llist_remove(&wildcard->filelist, wildcard->filelist.head, NULL);
3978 wildcard->state = (wildcard->filelist.size == 0) ?
3979 CURLWC_CLEAN : CURLWC_DOWNLOADING;
3983 case CURLWC_CLEAN: {
3984 struct ftp_wc *ftpwc = wildcard->protdata;
3987 result = Curl_ftp_parselist_geterror(ftpwc->parser);
3989 wildcard->state = result ? CURLWC_ERROR : CURLWC_DONE;
3997 wildcard->dtor(wildcard->protdata);
4004 /***********************************************************************
4008 * This function is registered as 'curl_do' function. It decodes the path
4009 * parts etc as a wrapper to the actual DO function (ftp_perform).
4011 * The input argument is already checked for validity.
4013 static CURLcode ftp_do(struct Curl_easy *data, bool *done)
4015 CURLcode result = CURLE_OK;
4016 struct connectdata *conn = data->conn;
4017 struct ftp_conn *ftpc = &conn->proto.ftpc;
4019 *done = FALSE; /* default to false */
4020 ftpc->wait_data_conn = FALSE; /* default to no such wait */
4022 if(data->state.wildcardmatch) {
4023 result = wc_statemach(data);
4024 if(data->wildcard.state == CURLWC_SKIP ||
4025 data->wildcard.state == CURLWC_DONE) {
4026 /* do not call ftp_regular_transfer */
4029 if(result) /* error, loop or skipping the file */
4032 else { /* no wildcard FSM needed */
4033 result = ftp_parse_url_path(data);
4038 result = ftp_regular_transfer(data, done);
4043 /***********************************************************************
4047 * This should be called before calling sclose() on an ftp control connection
4048 * (not data connections). We should then wait for the response from the
4049 * server before returning. The calling code should then try to close the
4053 static CURLcode ftp_quit(struct Curl_easy *data, struct connectdata *conn)
4055 CURLcode result = CURLE_OK;
4057 if(conn->proto.ftpc.ctl_valid) {
4058 result = Curl_pp_sendf(data, &conn->proto.ftpc.pp, "%s", "QUIT");
4060 failf(data, "Failure sending QUIT command: %s",
4061 curl_easy_strerror(result));
4062 conn->proto.ftpc.ctl_valid = FALSE; /* mark control connection as bad */
4063 connclose(conn, "QUIT command failed"); /* mark for connection closure */
4064 state(data, FTP_STOP);
4068 state(data, FTP_QUIT);
4070 result = ftp_block_statemach(data, conn);
4076 /***********************************************************************
4080 * Disconnect from an FTP server. Cleanup protocol-specific per-connection
4081 * resources. BLOCKING.
4083 static CURLcode ftp_disconnect(struct Curl_easy *data,
4084 struct connectdata *conn,
4085 bool dead_connection)
4087 struct ftp_conn *ftpc = &conn->proto.ftpc;
4088 struct pingpong *pp = &ftpc->pp;
4090 /* We cannot send quit unconditionally. If this connection is stale or
4091 bad in any way, sending quit and waiting around here will make the
4092 disconnect wait in vain and cause more problems than we need to.
4094 ftp_quit() will check the state of ftp->ctl_valid. If it's ok it
4095 will try to send the QUIT command, otherwise it will just return.
4098 ftpc->ctl_valid = FALSE;
4100 /* The FTP session may or may not have been allocated/setup at this point! */
4101 (void)ftp_quit(data, conn); /* ignore errors on the QUIT */
4103 if(ftpc->entrypath) {
4104 if(data->state.most_recent_ftp_entrypath == ftpc->entrypath) {
4105 data->state.most_recent_ftp_entrypath = NULL;
4107 Curl_safefree(ftpc->entrypath);
4111 Curl_safefree(ftpc->prevpath);
4112 Curl_safefree(ftpc->server_os);
4113 Curl_pp_disconnect(pp);
4119 /* warning C4706: assignment within conditional expression */
4120 #pragma warning(disable:4706)
4123 /***********************************************************************
4125 * ftp_parse_url_path()
4127 * Parse the URL path into separate path components.
4131 CURLcode ftp_parse_url_path(struct Curl_easy *data)
4133 /* the ftp struct is already inited in ftp_connect() */
4134 struct FTP *ftp = data->req.p.ftp;
4135 struct connectdata *conn = data->conn;
4136 struct ftp_conn *ftpc = &conn->proto.ftpc;
4137 const char *slashPos = NULL;
4138 const char *fileName = NULL;
4139 CURLcode result = CURLE_OK;
4140 char *rawPath = NULL; /* url-decoded "raw" path */
4143 ftpc->ctl_valid = FALSE;
4144 ftpc->cwdfail = FALSE;
4146 /* url-decode ftp path before further evaluation */
4147 result = Curl_urldecode(ftp->path, 0, &rawPath, &pathLen, REJECT_CTRL);
4149 failf(data, "path contains control characters");
4153 switch(data->set.ftp_filemethod) {
4154 case FTPFILE_NOCWD: /* fastest, but less standard-compliant */
4156 if((pathLen > 0) && (rawPath[pathLen - 1] != '/'))
4157 fileName = rawPath; /* this is a full file path */
4159 else: ftpc->file is not used anywhere other than for operations on
4160 a file. In other words, never for directory operations.
4161 So we can safely leave filename as NULL here and use it as a
4162 argument in dir/file decisions.
4166 case FTPFILE_SINGLECWD:
4167 slashPos = strrchr(rawPath, '/');
4169 /* get path before last slash, except for / */
4170 size_t dirlen = slashPos - rawPath;
4174 ftpc->dirs = calloc(1, sizeof(ftpc->dirs[0]));
4177 return CURLE_OUT_OF_MEMORY;
4180 ftpc->dirs[0] = calloc(1, dirlen + 1);
4181 if(!ftpc->dirs[0]) {
4183 return CURLE_OUT_OF_MEMORY;
4186 strncpy(ftpc->dirs[0], rawPath, dirlen);
4187 ftpc->dirdepth = 1; /* we consider it to be a single dir */
4188 fileName = slashPos + 1; /* rest is file name */
4191 fileName = rawPath; /* file name only (or empty) */
4194 default: /* allow pretty much anything */
4195 case FTPFILE_MULTICWD: {
4196 /* current position: begin of next path component */
4197 const char *curPos = rawPath;
4199 int dirAlloc = 0; /* number of entries allocated for the 'dirs' array */
4200 const char *str = rawPath;
4201 for(; *str != 0; ++str)
4206 ftpc->dirs = calloc(dirAlloc, sizeof(ftpc->dirs[0]));
4209 return CURLE_OUT_OF_MEMORY;
4212 /* parse the URL path into separate path components */
4213 while((slashPos = strchr(curPos, '/'))) {
4214 size_t compLen = slashPos - curPos;
4216 /* path starts with a slash: add that as a directory */
4217 if((compLen == 0) && (ftpc->dirdepth == 0))
4220 /* we skip empty path components, like "x//y" since the FTP command
4221 CWD requires a parameter and a non-existent parameter a) doesn't
4222 work on many servers and b) has no effect on the others. */
4224 char *comp = calloc(1, compLen + 1);
4227 return CURLE_OUT_OF_MEMORY;
4229 strncpy(comp, curPos, compLen);
4230 ftpc->dirs[ftpc->dirdepth++] = comp;
4232 curPos = slashPos + 1;
4235 DEBUGASSERT(ftpc->dirdepth <= dirAlloc);
4236 fileName = curPos; /* the rest is the file name (or empty) */
4241 if(fileName && *fileName)
4242 ftpc->file = strdup(fileName);
4244 ftpc->file = NULL; /* instead of point to a zero byte,
4245 we make it a NULL pointer */
4247 if(data->set.upload && !ftpc->file && (ftp->transfer == PPTRANSFER_BODY)) {
4248 /* We need a file name when uploading. Return error! */
4249 failf(data, "Uploading to a URL without a file name");
4251 return CURLE_URL_MALFORMAT;
4254 ftpc->cwddone = FALSE; /* default to not done */
4256 if((data->set.ftp_filemethod == FTPFILE_NOCWD) && (rawPath[0] == '/'))
4257 ftpc->cwddone = TRUE; /* skip CWD for absolute paths */
4258 else { /* newly created FTP connections are already in entry path */
4259 const char *oldPath = conn->bits.reuse ? ftpc->prevpath : "";
4262 if(data->set.ftp_filemethod == FTPFILE_NOCWD)
4263 n = 0; /* CWD to entry for relative paths */
4265 n -= ftpc->file?strlen(ftpc->file):0;
4267 if((strlen(oldPath) == n) && !strncmp(rawPath, oldPath, n)) {
4268 infof(data, "Request has same path as previous transfer");
4269 ftpc->cwddone = TRUE;
4278 /* call this when the DO phase has completed */
4279 static CURLcode ftp_dophase_done(struct Curl_easy *data, bool connected)
4281 struct connectdata *conn = data->conn;
4282 struct FTP *ftp = data->req.p.ftp;
4283 struct ftp_conn *ftpc = &conn->proto.ftpc;
4287 CURLcode result = ftp_do_more(data, &completed);
4290 close_secondarysocket(data, conn);
4295 if(ftp->transfer != PPTRANSFER_BODY)
4296 /* no data to transfer */
4297 Curl_setup_transfer(data, -1, -1, FALSE, -1);
4299 /* since we didn't connect now, we want do_more to get called */
4300 conn->bits.do_more = TRUE;
4302 ftpc->ctl_valid = TRUE; /* seems good */
4307 /* called from multi.c while DOing */
4308 static CURLcode ftp_doing(struct Curl_easy *data,
4311 CURLcode result = ftp_multi_statemach(data, dophase_done);
4314 DEBUGF(infof(data, "DO phase failed"));
4315 else if(*dophase_done) {
4316 result = ftp_dophase_done(data, FALSE /* not connected */);
4318 DEBUGF(infof(data, "DO phase is complete2"));
4323 /***********************************************************************
4325 * ftp_regular_transfer()
4327 * The input argument is already checked for validity.
4329 * Performs all commands done before a regular transfer between a local and a
4332 * ftp->ctl_valid starts out as FALSE, and gets set to TRUE if we reach the
4333 * ftp_done() function without finding any major problem.
4336 CURLcode ftp_regular_transfer(struct Curl_easy *data,
4339 CURLcode result = CURLE_OK;
4340 bool connected = FALSE;
4341 struct connectdata *conn = data->conn;
4342 struct ftp_conn *ftpc = &conn->proto.ftpc;
4343 data->req.size = -1; /* make sure this is unknown at this point */
4345 Curl_pgrsSetUploadCounter(data, 0);
4346 Curl_pgrsSetDownloadCounter(data, 0);
4347 Curl_pgrsSetUploadSize(data, -1);
4348 Curl_pgrsSetDownloadSize(data, -1);
4350 ftpc->ctl_valid = TRUE; /* starts good */
4352 result = ftp_perform(data,
4353 &connected, /* have we connected after PASV/PORT */
4354 dophase_done); /* all commands in the DO-phase done? */
4359 /* the DO phase has not completed yet */
4362 result = ftp_dophase_done(data, connected);
4373 static CURLcode ftp_setup_connection(struct Curl_easy *data,
4374 struct connectdata *conn)
4379 data->req.p.ftp = ftp = calloc(sizeof(struct FTP), 1);
4381 return CURLE_OUT_OF_MEMORY;
4383 ftp->path = &data->state.up.path[1]; /* don't include the initial slash */
4385 /* FTP URLs support an extension like ";type=<typecode>" that
4386 * we'll try to get now! */
4387 type = strstr(ftp->path, ";type=");
4390 type = strstr(conn->host.rawalloc, ";type=");
4394 *type = 0; /* it was in the middle of the hostname */
4395 command = Curl_raw_toupper(type[6]);
4398 case 'A': /* ASCII mode */
4399 data->state.prefer_ascii = TRUE;
4402 case 'D': /* directory mode */
4403 data->state.list_only = TRUE;
4406 case 'I': /* binary mode */
4408 /* switch off ASCII */
4409 data->state.prefer_ascii = FALSE;
4414 /* get some initial data into the ftp struct */
4415 ftp->transfer = PPTRANSFER_BODY;
4416 ftp->downloadsize = 0;
4417 conn->proto.ftpc.known_filesize = -1; /* unknown size for now */
4422 #endif /* CURL_DISABLE_FTP */