1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at http://curl.haxx.se/docs/copyright.html.
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ***************************************************************************/
25 #ifndef CURL_DISABLE_FTP
27 #ifdef HAVE_NETINET_IN_H
28 #include <netinet/in.h>
30 #ifdef HAVE_ARPA_INET_H
31 #include <arpa/inet.h>
34 #include <sys/utsname.h>
44 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
46 #define in_addr_t unsigned long
49 #include <curl/curl.h>
57 #include "http.h" /* for HTTP proxy tunnel stuff */
61 #include "ftplistparser.h"
63 #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
67 #include "strtoofft.h"
72 #include "inet_ntop.h"
73 #include "inet_pton.h"
75 #include "parsedate.h" /* for the week day and month names */
76 #include "sockaddr.h" /* required for Curl_sockaddr_storage */
80 #include "speedcheck.h"
82 #include "http_proxy.h"
83 #include "non-ascii.h"
85 #define _MPRINTF_REPLACE /* use our functions only */
86 #include <curl/mprintf.h>
88 #include "curl_memory.h"
89 /* The last #include file should be: */
93 #define NI_MAXHOST 1025
95 #ifndef INET_ADDRSTRLEN
96 #define INET_ADDRSTRLEN 16
99 #ifdef CURL_DISABLE_VERBOSE_STRINGS
100 #define ftp_pasv_verbose(a,b,c,d) Curl_nop_stmt
103 /* Local API functions */
104 static void state(struct connectdata *conn,
106 static CURLcode ftp_sendquote(struct connectdata *conn,
107 struct curl_slist *quote);
108 static CURLcode ftp_quit(struct connectdata *conn);
109 static CURLcode ftp_parse_url_path(struct connectdata *conn);
110 static CURLcode ftp_regular_transfer(struct connectdata *conn, bool *done);
111 #ifndef CURL_DISABLE_VERBOSE_STRINGS
112 static void ftp_pasv_verbose(struct connectdata *conn,
114 char *newhost, /* ascii version */
117 static CURLcode ftp_state_post_rest(struct connectdata *conn);
118 static CURLcode ftp_state_post_cwd(struct connectdata *conn);
119 static CURLcode ftp_state_quote(struct connectdata *conn,
120 bool init, ftpstate instate);
121 static CURLcode ftp_nb_type(struct connectdata *conn,
122 bool ascii, ftpstate newstate);
123 static int ftp_need_type(struct connectdata *conn,
125 static CURLcode ftp_do(struct connectdata *conn, bool *done);
126 static CURLcode ftp_done(struct connectdata *conn,
127 CURLcode, bool premature);
128 static CURLcode ftp_connect(struct connectdata *conn, bool *done);
129 static CURLcode ftp_disconnect(struct connectdata *conn, bool dead_connection);
130 static CURLcode ftp_do_more(struct connectdata *conn, bool *completed);
131 static CURLcode ftp_multi_statemach(struct connectdata *conn, bool *done);
132 static int ftp_getsock(struct connectdata *conn, curl_socket_t *socks,
134 static int ftp_domore_getsock(struct connectdata *conn, curl_socket_t *socks,
136 static CURLcode ftp_doing(struct connectdata *conn,
138 static CURLcode ftp_setup_connection(struct connectdata * conn);
140 static CURLcode init_wc_data(struct connectdata *conn);
141 static CURLcode wc_statemach(struct connectdata *conn);
143 static void wc_data_dtor(void *ptr);
145 static CURLcode ftp_state_post_retr_size(struct connectdata *conn,
146 curl_off_t filesize);
148 static CURLcode ftp_readresp(curl_socket_t sockfd,
153 /* easy-to-use macro: */
154 #define FTPSENDF(x,y,z) if((result = Curl_ftpsendf(x,y,z)) != CURLE_OK) \
156 #define PPSENDF(x,y,z) if((result = Curl_pp_sendf(x,y,z)) != CURLE_OK) \
161 * FTP protocol handler.
164 const struct Curl_handler Curl_handler_ftp = {
166 ftp_setup_connection, /* setup_connection */
169 ftp_do_more, /* do_more */
170 ftp_connect, /* connect_it */
171 ftp_multi_statemach, /* connecting */
172 ftp_doing, /* doing */
173 ftp_getsock, /* proto_getsock */
174 ftp_getsock, /* doing_getsock */
175 ftp_domore_getsock, /* domore_getsock */
176 ZERO_NULL, /* perform_getsock */
177 ftp_disconnect, /* disconnect */
178 ZERO_NULL, /* readwrite */
179 PORT_FTP, /* defport */
180 CURLPROTO_FTP, /* protocol */
181 PROTOPT_DUAL | PROTOPT_CLOSEACTION | PROTOPT_NEEDSPWD
182 | PROTOPT_NOURLQUERY /* flags */
188 * FTPS protocol handler.
191 const struct Curl_handler Curl_handler_ftps = {
193 ftp_setup_connection, /* setup_connection */
196 ftp_do_more, /* do_more */
197 ftp_connect, /* connect_it */
198 ftp_multi_statemach, /* connecting */
199 ftp_doing, /* doing */
200 ftp_getsock, /* proto_getsock */
201 ftp_getsock, /* doing_getsock */
202 ftp_domore_getsock, /* domore_getsock */
203 ZERO_NULL, /* perform_getsock */
204 ftp_disconnect, /* disconnect */
205 ZERO_NULL, /* readwrite */
206 PORT_FTPS, /* defport */
207 CURLPROTO_FTP | CURLPROTO_FTPS, /* protocol */
208 PROTOPT_SSL | PROTOPT_DUAL | PROTOPT_CLOSEACTION |
209 PROTOPT_NEEDSPWD | PROTOPT_NOURLQUERY /* flags */
213 #ifndef CURL_DISABLE_HTTP
215 * HTTP-proxyed FTP protocol handler.
218 static const struct Curl_handler Curl_handler_ftp_proxy = {
220 ZERO_NULL, /* setup_connection */
221 Curl_http, /* do_it */
222 Curl_http_done, /* done */
223 ZERO_NULL, /* do_more */
224 ZERO_NULL, /* connect_it */
225 ZERO_NULL, /* connecting */
226 ZERO_NULL, /* doing */
227 ZERO_NULL, /* proto_getsock */
228 ZERO_NULL, /* doing_getsock */
229 ZERO_NULL, /* domore_getsock */
230 ZERO_NULL, /* perform_getsock */
231 ZERO_NULL, /* disconnect */
232 ZERO_NULL, /* readwrite */
233 PORT_FTP, /* defport */
234 CURLPROTO_HTTP, /* protocol */
235 PROTOPT_NONE /* flags */
241 * HTTP-proxyed FTPS protocol handler.
244 static const struct Curl_handler Curl_handler_ftps_proxy = {
246 ZERO_NULL, /* setup_connection */
247 Curl_http, /* do_it */
248 Curl_http_done, /* done */
249 ZERO_NULL, /* do_more */
250 ZERO_NULL, /* connect_it */
251 ZERO_NULL, /* connecting */
252 ZERO_NULL, /* doing */
253 ZERO_NULL, /* proto_getsock */
254 ZERO_NULL, /* doing_getsock */
255 ZERO_NULL, /* domore_getsock */
256 ZERO_NULL, /* perform_getsock */
257 ZERO_NULL, /* disconnect */
258 ZERO_NULL, /* readwrite */
259 PORT_FTPS, /* defport */
260 CURLPROTO_HTTP, /* protocol */
261 PROTOPT_NONE /* flags */
268 * NOTE: back in the old days, we added code in the FTP code that made NOBODY
269 * requests on files respond with headers passed to the client/stdout that
270 * looked like HTTP ones.
272 * This approach is not very elegant, it causes confusion and is error-prone.
273 * It is subject for removal at the next (or at least a future) soname bump.
274 * Until then you can test the effects of the removal by undefining the
275 * following define named CURL_FTP_HTTPSTYLE_HEAD.
277 #define CURL_FTP_HTTPSTYLE_HEAD 1
279 static void freedirs(struct ftp_conn *ftpc)
283 for(i=0; i < ftpc->dirdepth; i++) {
299 /* Returns non-zero if the given string contains CR (\r) or LF (\n),
300 which are not allowed within RFC 959 <string>.
301 Note: The input string is in the client's encoding which might
302 not be ASCII, so escape sequences \r & \n must be used instead
303 of hex values 0x0d & 0x0a.
305 static bool isBadFtpString(const char *string)
307 return ((NULL != strchr(string, '\r')) ||
308 (NULL != strchr(string, '\n'))) ? TRUE : FALSE;
311 /***********************************************************************
313 * AcceptServerConnect()
315 * After connection request is received from the server this function is
316 * called to accept the connection and close the listening socket
319 static CURLcode AcceptServerConnect(struct connectdata *conn)
321 struct SessionHandle *data = conn->data;
322 curl_socket_t sock = conn->sock[SECONDARYSOCKET];
323 curl_socket_t s = CURL_SOCKET_BAD;
325 struct Curl_sockaddr_storage add;
327 struct sockaddr_in add;
329 curl_socklen_t size = (curl_socklen_t) sizeof(add);
331 if(0 == getsockname(sock, (struct sockaddr *) &add, &size)) {
334 s=accept(sock, (struct sockaddr *) &add, &size);
336 Curl_closesocket(conn, sock); /* close the first socket */
338 if(CURL_SOCKET_BAD == s) {
339 failf(data, "Error accept()ing server connect");
340 return CURLE_FTP_PORT_FAILED;
342 infof(data, "Connection accepted from server\n");
344 conn->sock[SECONDARYSOCKET] = s;
345 curlx_nonblock(s, TRUE); /* enable non-blocking */
346 conn->sock_accepted[SECONDARYSOCKET] = TRUE;
348 if(data->set.fsockopt) {
351 /* activate callback for setting socket options */
352 error = data->set.fsockopt(data->set.sockopt_client,
354 CURLSOCKTYPE_ACCEPT);
357 Curl_closesocket(conn, s); /* close the socket and bail out */
358 conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
359 return CURLE_ABORTED_BY_CALLBACK;
368 * ftp_timeleft_accept() returns the amount of milliseconds left allowed for
369 * waiting server to connect. If the value is negative, the timeout time has
372 * The start time is stored in progress.t_acceptdata - as set with
373 * Curl_pgrsTime(..., TIMER_STARTACCEPT);
376 static long ftp_timeleft_accept(struct SessionHandle *data)
378 long timeout_ms = DEFAULT_ACCEPT_TIMEOUT;
382 if(data->set.accepttimeout > 0)
383 timeout_ms = data->set.accepttimeout;
387 /* check if the generic timeout possibly is set shorter */
388 other = Curl_timeleft(data, &now, FALSE);
389 if(other && (other < timeout_ms))
390 /* note that this also works fine for when other happens to be negative
391 due to it already having elapsed */
394 /* subtract elapsed time */
395 timeout_ms -= Curl_tvdiff(now, data->progress.t_acceptdata);
397 /* avoid returning 0 as that means no timeout! */
405 /***********************************************************************
407 * ReceivedServerConnect()
409 * After allowing server to connect to us from data port, this function
410 * checks both data connection for connection establishment and ctrl
411 * connection for a negative response regarding a failure in connecting
414 static CURLcode ReceivedServerConnect(struct connectdata* conn, bool* received)
416 struct SessionHandle *data = conn->data;
417 curl_socket_t ctrl_sock = conn->sock[FIRSTSOCKET];
418 curl_socket_t data_sock = conn->sock[SECONDARYSOCKET];
419 struct ftp_conn *ftpc = &conn->proto.ftpc;
420 struct pingpong *pp = &ftpc->pp;
428 timeout_ms = ftp_timeleft_accept(data);
429 infof(data, "Checking for server connect\n");
431 /* if a timeout was already reached, bail out */
432 failf(data, "Accept timeout occurred while waiting server connect");
433 return CURLE_FTP_ACCEPT_TIMEOUT;
436 /* First check whether there is a cached response from server */
437 if(pp->cache_size && pp->cache && pp->cache[0] > '3') {
438 /* Data connection could not be established, let's return */
439 infof(data, "There is negative response in cache while serv connect\n");
440 Curl_GetFTPResponse(&nread, conn, &ftpcode);
441 return CURLE_FTP_ACCEPT_FAILED;
444 result = Curl_socket_check(ctrl_sock, data_sock, CURL_SOCKET_BAD, 0);
446 /* see if the connection request is already here */
450 failf(data, "Error while waiting for server connect");
451 return CURLE_FTP_ACCEPT_FAILED;
452 case 0: /* Server connect is not received yet */
456 if(result & CURL_CSELECT_IN2) {
457 infof(data, "Ready to accept data connection from server\n");
460 else if(result & CURL_CSELECT_IN) {
461 infof(data, "Ctrl conn has data while waiting for data conn\n");
462 Curl_GetFTPResponse(&nread, conn, &ftpcode);
465 return CURLE_FTP_ACCEPT_FAILED;
467 return CURLE_FTP_WEIRD_SERVER_REPLY;
477 /***********************************************************************
481 * After connection from server is accepted this function is called to
482 * setup transfer parameters and initiate the data transfer.
485 static CURLcode InitiateTransfer(struct connectdata *conn)
487 struct SessionHandle *data = conn->data;
488 struct FTP *ftp = data->state.proto.ftp;
489 CURLcode result = CURLE_OK;
491 if(conn->ssl[SECONDARYSOCKET].use) {
492 /* since we only have a plaintext TCP connection here, we must now
493 * do the TLS stuff */
494 infof(data, "Doing the SSL/TLS handshake on the data stream\n");
495 result = Curl_ssl_connect(conn, SECONDARYSOCKET);
500 if(conn->proto.ftpc.state_saved == FTP_STOR) {
501 *(ftp->bytecountp)=0;
503 /* When we know we're uploading a specified file, we can get the file
504 size prior to the actual upload. */
506 Curl_pgrsSetUploadSize(data, data->set.infilesize);
508 /* set the SO_SNDBUF for the secondary socket for those who need it */
509 Curl_sndbufset(conn->sock[SECONDARYSOCKET]);
511 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, /* no download */
512 SECONDARYSOCKET, ftp->bytecountp);
516 Curl_setup_transfer(conn, SECONDARYSOCKET,
517 conn->proto.ftpc.retr_size_saved, FALSE,
518 ftp->bytecountp, -1, NULL); /* no upload here */
521 conn->proto.ftpc.pp.pending_resp = TRUE; /* expect server response */
522 state(conn, FTP_STOP);
527 /***********************************************************************
529 * AllowServerConnect()
531 * When we've issue the PORT command, we have told the server to connect
532 * to us. This function
533 * - will sit and wait here until the server has connected for easy interface
534 * - will check whether data connection is established if so it is accepted
535 * for multi interface
538 static CURLcode AllowServerConnect(struct connectdata *conn, bool *connected)
540 struct SessionHandle *data = conn->data;
543 CURLcode ret = CURLE_OK;
546 infof(data, "Preparing for accepting server on data port\n");
548 /* Save the time we start accepting server connect */
549 Curl_pgrsTime(data, TIMER_STARTACCEPT);
552 timeout_ms = ftp_timeleft_accept(data);
554 /* if a timeout was already reached, bail out */
555 failf(data, "Accept timeout occurred while waiting server connect");
556 return CURLE_FTP_ACCEPT_TIMEOUT;
559 /* see if the connection request is already here */
560 ret = ReceivedServerConnect(conn, connected);
565 ret = AcceptServerConnect(conn);
569 ret = InitiateTransfer(conn);
573 break; /* connection is accepted, break the loop */
576 if(data->state.used_interface == Curl_if_easy) {
578 if(timeout_ms < interval_ms)
579 interval_ms = timeout_ms;
581 /* sleep for 1 second and then continue */
582 Curl_socket_ready(CURL_SOCKET_BAD, CURL_SOCKET_BAD, interval_ms);
585 /* Add timeout to multi handle and break out of the loop */
586 if(ret == CURLE_OK && *connected == FALSE) {
587 if(data->set.accepttimeout > 0)
588 Curl_expire(data, data->set.accepttimeout);
590 Curl_expire(data, DEFAULT_ACCEPT_TIMEOUT);
593 break; /* connection was not accepted immediately */
601 /* macro to check for a three-digit ftp status code at the start of the
603 #define STATUSCODE(line) (ISDIGIT(line[0]) && ISDIGIT(line[1]) && \
606 /* macro to check for the last line in an FTP server response */
607 #define LASTLINE(line) (STATUSCODE(line) && (' ' == line[3]))
609 static int ftp_endofresp(struct pingpong *pp,
612 char *line = pp->linestart_resp;
613 size_t len = pp->nread_resp;
615 if((len > 3) && LASTLINE(line)) {
616 *code = curlx_sltosi(strtol(line, NULL, 10));
622 static CURLcode ftp_readresp(curl_socket_t sockfd,
624 int *ftpcode, /* return the ftp-code if done */
625 size_t *size) /* size of the response */
627 struct connectdata *conn = pp->conn;
628 struct SessionHandle *data = conn->data;
629 #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
630 char * const buf = data->state.buffer;
632 CURLcode result = CURLE_OK;
635 result = Curl_pp_readresp(sockfd, pp, &code, size);
637 #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
638 /* handle the security-oriented responses 6xx ***/
639 /* FIXME: some errorchecking perhaps... ***/
642 code = Curl_sec_read_msg(conn, buf, PROT_SAFE);
645 code = Curl_sec_read_msg(conn, buf, PROT_PRIVATE);
648 code = Curl_sec_read_msg(conn, buf, PROT_CONFIDENTIAL);
651 /* normal ftp stuff we pass through! */
656 /* store the latest code for later retrieval */
657 data->info.httpcode=code;
663 /* 421 means "Service not available, closing control connection." and FTP
664 * servers use it to signal that idle session timeout has been exceeded.
665 * If we ignored the response, it could end up hanging in some cases.
667 * This response code can come at any point so having it treated
668 * generically is a good idea.
670 infof(data, "We got a 421 - timeout!\n");
671 state(conn, FTP_STOP);
672 return CURLE_OPERATION_TIMEDOUT;
678 /* --- parse FTP server responses --- */
681 * Curl_GetFTPResponse() is a BLOCKING function to read the full response
682 * from a server after a command.
686 CURLcode Curl_GetFTPResponse(ssize_t *nreadp, /* return number of bytes read */
687 struct connectdata *conn,
688 int *ftpcode) /* return the ftp-code */
691 * We cannot read just one byte per read() and then go back to select() as
692 * the OpenSSL read() doesn't grok that properly.
694 * Alas, read as much as possible, split up into lines, use the ending
695 * line in a response or continue reading. */
697 curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
698 long timeout; /* timeout in milliseconds */
700 struct SessionHandle *data = conn->data;
701 CURLcode result = CURLE_OK;
702 struct ftp_conn *ftpc = &conn->proto.ftpc;
703 struct pingpong *pp = &ftpc->pp;
706 int value_to_be_ignored=0;
709 *ftpcode = 0; /* 0 for errors */
711 /* make the pointer point to something for the rest of this function */
712 ftpcode = &value_to_be_ignored;
716 while(!*ftpcode && !result) {
717 /* check and reset timeout value every lap */
718 timeout = Curl_pp_state_timeout(pp);
721 failf(data, "FTP response timeout");
722 return CURLE_OPERATION_TIMEDOUT; /* already too little time */
725 interval_ms = 1000; /* use 1 second timeout intervals */
726 if(timeout < interval_ms)
727 interval_ms = timeout;
730 * Since this function is blocking, we need to wait here for input on the
731 * connection and only then we call the response reading function. We do
732 * timeout at least every second to make the timeout check run.
734 * A caution here is that the ftp_readresp() function has a cache that may
735 * contain pieces of a response from the previous invoke and we need to
736 * make sure we don't just wait for input while there is unhandled data in
737 * that cache. But also, if the cache is there, we call ftp_readresp() and
738 * the cache wasn't good enough to continue we must not just busy-loop
739 * around this function.
743 if(pp->cache && (cache_skip < 2)) {
745 * There's a cache left since before. We then skipping the wait for
746 * socket action, unless this is the same cache like the previous round
747 * as then the cache was deemed not enough to act on and we then need to
748 * wait for more data anyway.
752 switch (Curl_socket_ready(sockfd, CURL_SOCKET_BAD, interval_ms)) {
753 case -1: /* select() error, stop reading */
754 failf(data, "FTP response aborted due to select/poll error: %d",
756 return CURLE_RECV_ERROR;
758 case 0: /* timeout */
759 if(Curl_pgrsUpdate(conn))
760 return CURLE_ABORTED_BY_CALLBACK;
761 continue; /* just continue in our loop for the timeout duration */
763 default: /* for clarity */
767 result = ftp_readresp(sockfd, pp, ftpcode, &nread);
771 if(!nread && pp->cache)
772 /* bump cache skip counter as on repeated skips we must wait for more
776 /* when we got data or there is no cache left, we reset the cache skip
782 } /* while there's buffer left and loop is requested */
784 pp->pending_resp = FALSE;
789 /* This is the ONLY way to change FTP state! */
790 static void state(struct connectdata *conn,
793 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
794 /* for debug purposes */
795 static const char * const names[]={
833 struct ftp_conn *ftpc = &conn->proto.ftpc;
834 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
835 if(ftpc->state != newstate)
836 infof(conn->data, "FTP %p state change from %s to %s\n",
837 ftpc, names[ftpc->state], names[newstate]);
839 ftpc->state = newstate;
842 static CURLcode ftp_state_user(struct connectdata *conn)
845 struct FTP *ftp = conn->data->state.proto.ftp;
847 PPSENDF(&conn->proto.ftpc.pp, "USER %s", ftp->user?ftp->user:"");
849 state(conn, FTP_USER);
850 conn->data->state.ftp_trying_alternative = FALSE;
855 static CURLcode ftp_state_pwd(struct connectdata *conn)
859 /* send PWD to discover our entry point */
860 PPSENDF(&conn->proto.ftpc.pp, "PWD", NULL);
861 state(conn, FTP_PWD);
866 /* For the FTP "protocol connect" and "doing" phases only */
867 static int ftp_getsock(struct connectdata *conn,
868 curl_socket_t *socks,
871 return Curl_pp_getsock(&conn->proto.ftpc.pp, socks, numsocks);
874 /* For the FTP "DO_MORE" phase only */
875 static int ftp_domore_getsock(struct connectdata *conn, curl_socket_t *socks,
878 struct ftp_conn *ftpc = &conn->proto.ftpc;
881 return GETSOCK_BLANK;
883 /* When in DO_MORE state, we could be either waiting for us to connect to a
884 remote site, or we could wait for that site to connect to us. Or just
885 handle ordinary commands.
887 When waiting for a connect, we will be in FTP_STOP state and then we wait
888 for the secondary socket to become writeable. If we're in another state,
889 we're still handling commands on the control (primary) connection.
893 switch(ftpc->state) {
897 return Curl_pp_getsock(&conn->proto.ftpc.pp, socks, numsocks);
900 socks[0] = conn->sock[SECONDARYSOCKET];
901 if(ftpc->wait_data_conn) {
902 socks[1] = conn->sock[FIRSTSOCKET];
903 return GETSOCK_READSOCK(0) | GETSOCK_READSOCK(1);
906 return GETSOCK_READSOCK(0);
909 /* This is called after the FTP_QUOTE state is passed.
911 ftp_state_cwd() sends the range of CWD commands to the server to change to
912 the correct directory. It may also need to send MKD commands to create
913 missing ones, if that option is enabled.
915 static CURLcode ftp_state_cwd(struct connectdata *conn)
917 CURLcode result = CURLE_OK;
918 struct ftp_conn *ftpc = &conn->proto.ftpc;
921 /* already done and fine */
922 result = ftp_state_post_cwd(conn);
924 ftpc->count2 = 0; /* count2 counts failed CWDs */
926 /* count3 is set to allow a MKD to fail once. In the case when first CWD
927 fails and then MKD fails (due to another session raced it to create the
928 dir) this then allows for a second try to CWD to it */
929 ftpc->count3 = (conn->data->set.ftp_create_missing_dirs==2)?1:0;
931 if(conn->bits.reuse && ftpc->entrypath) {
932 /* This is a re-used connection. Since we change directory to where the
933 transfer is taking place, we must first get back to the original dir
934 where we ended up after login: */
935 ftpc->count1 = 0; /* we count this as the first path, then we add one
936 for all upcoming ones in the ftp->dirs[] array */
937 PPSENDF(&conn->proto.ftpc.pp, "CWD %s", ftpc->entrypath);
938 state(conn, FTP_CWD);
943 /* issue the first CWD, the rest is sent when the CWD responses are
945 PPSENDF(&conn->proto.ftpc.pp, "CWD %s", ftpc->dirs[ftpc->count1 -1]);
946 state(conn, FTP_CWD);
949 /* No CWD necessary */
950 result = ftp_state_post_cwd(conn);
963 static CURLcode ftp_state_use_port(struct connectdata *conn,
964 ftpport fcmd) /* start with this */
967 CURLcode result = CURLE_OK;
968 struct ftp_conn *ftpc = &conn->proto.ftpc;
969 struct SessionHandle *data=conn->data;
970 curl_socket_t portsock= CURL_SOCKET_BAD;
971 char myhost[256] = "";
973 struct Curl_sockaddr_storage ss;
974 Curl_addrinfo *res, *ai;
975 curl_socklen_t sslen;
976 char hbuf[NI_MAXHOST];
977 struct sockaddr *sa=(struct sockaddr *)&ss;
978 struct sockaddr_in * const sa4 = (void *)sa;
980 struct sockaddr_in6 * const sa6 = (void *)sa;
983 static const char mode[][5] = { "EPRT", "PORT" };
987 char *string_ftpport = data->set.str[STRING_FTPPORT];
988 struct Curl_dns_entry *h=NULL;
989 unsigned short port_min = 0;
990 unsigned short port_max = 0;
992 bool possibly_non_local = TRUE;
996 /* Step 1, figure out what is requested,
998 * (ipv4|ipv6|domain|interface)?(:port(-range)?)?
1001 if(data->set.str[STRING_FTPPORT] &&
1002 (strlen(data->set.str[STRING_FTPPORT]) > 1)) {
1005 size_t addrlen = INET6_ADDRSTRLEN > strlen(string_ftpport) ?
1006 INET6_ADDRSTRLEN : strlen(string_ftpport);
1008 size_t addrlen = INET_ADDRSTRLEN > strlen(string_ftpport) ?
1009 INET_ADDRSTRLEN : strlen(string_ftpport);
1011 char *ip_start = string_ftpport;
1012 char *ip_end = NULL;
1013 char *port_start = NULL;
1014 char *port_sep = NULL;
1016 addr = calloc(addrlen+1, 1);
1018 return CURLE_OUT_OF_MEMORY;
1021 if(*string_ftpport == '[') {
1022 /* [ipv6]:port(-range) */
1023 ip_start = string_ftpport + 1;
1024 if((ip_end = strchr(string_ftpport, ']')) != NULL )
1025 strncpy(addr, ip_start, ip_end - ip_start);
1029 if(*string_ftpport == ':') {
1031 ip_end = string_ftpport;
1033 else if((ip_end = strchr(string_ftpport, ':')) != NULL) {
1034 /* either ipv6 or (ipv4|domain|interface):port(-range) */
1036 if(Curl_inet_pton(AF_INET6, string_ftpport, sa6) == 1) {
1038 port_min = port_max = 0;
1039 strcpy(addr, string_ftpport);
1040 ip_end = NULL; /* this got no port ! */
1044 /* (ipv4|domain|interface):port(-range) */
1045 strncpy(addr, string_ftpport, ip_end - ip_start );
1048 /* ipv4|interface */
1049 strcpy(addr, string_ftpport);
1051 /* parse the port */
1052 if(ip_end != NULL) {
1053 if((port_start = strchr(ip_end, ':')) != NULL) {
1054 port_min = curlx_ultous(strtoul(port_start+1, NULL, 10));
1055 if((port_sep = strchr(port_start, '-')) != NULL) {
1056 port_max = curlx_ultous(strtoul(port_sep + 1, NULL, 10));
1059 port_max = port_min;
1063 /* correct errors like:
1065 * :-4711 , in this case port_min is (unsigned)-1,
1066 * therefore port_min > port_max for all cases
1067 * but port_max = (unsigned)-1
1069 if(port_min > port_max )
1070 port_min = port_max = 0;
1074 /* attempt to get the address of the given interface name */
1075 if(!Curl_if2ip(conn->ip_addr->ai_family, addr,
1076 hbuf, sizeof(hbuf)))
1077 /* not an interface, use the given string as host name instead */
1080 host = hbuf; /* use the hbuf for host name */
1083 /* there was only a port(-range) given, default the host */
1085 } /* data->set.ftpport */
1088 /* not an interface and not a host name, get default by extracting
1089 the IP from the control connection */
1092 if(getsockname(conn->sock[FIRSTSOCKET], sa, &sslen)) {
1093 failf(data, "getsockname() failed: %s",
1094 Curl_strerror(conn, SOCKERRNO) );
1095 Curl_safefree(addr);
1096 return CURLE_FTP_PORT_FAILED;
1098 switch(sa->sa_family) {
1101 Curl_inet_ntop(sa->sa_family, &sa6->sin6_addr, hbuf, sizeof(hbuf));
1105 Curl_inet_ntop(sa->sa_family, &sa4->sin_addr, hbuf, sizeof(hbuf));
1108 host = hbuf; /* use this host name */
1109 possibly_non_local = FALSE; /* we know it is local now */
1112 /* resolv ip/host to ip */
1113 rc = Curl_resolv(conn, host, 0, &h);
1114 if(rc == CURLRESOLV_PENDING)
1115 (void)Curl_resolver_wait_resolv(conn, &h);
1118 /* when we return from this function, we can forget about this entry
1119 to we can unlock it now already */
1120 Curl_resolv_unlock(data, h);
1123 res = NULL; /* failure! */
1126 failf(data, "failed to resolve the address provided to PORT: %s", host);
1127 Curl_safefree(addr);
1128 return CURLE_FTP_PORT_FAILED;
1131 Curl_safefree(addr);
1134 /* step 2, create a socket for the requested address */
1136 portsock = CURL_SOCKET_BAD;
1138 for(ai = res; ai; ai = ai->ai_next) {
1139 result = Curl_socket(conn, ai, NULL, &portsock);
1147 failf(data, "socket failure: %s", Curl_strerror(conn, error));
1148 return CURLE_FTP_PORT_FAILED;
1151 /* step 3, bind to a suitable local address */
1153 memcpy(sa, ai->ai_addr, ai->ai_addrlen);
1154 sslen = ai->ai_addrlen;
1156 for(port = port_min; port <= port_max;) {
1157 if(sa->sa_family == AF_INET)
1158 sa4->sin_port = htons(port);
1161 sa6->sin6_port = htons(port);
1163 /* Try binding the given address. */
1164 if(bind(portsock, sa, sslen) ) {
1167 if(possibly_non_local && (error == EADDRNOTAVAIL)) {
1168 /* The requested bind address is not local. Use the address used for
1169 * the control connection instead and restart the port loop
1172 infof(data, "bind(port=%hu) on non-local address failed: %s\n", port,
1173 Curl_strerror(conn, error) );
1176 if(getsockname(conn->sock[FIRSTSOCKET], sa, &sslen)) {
1177 failf(data, "getsockname() failed: %s",
1178 Curl_strerror(conn, SOCKERRNO) );
1179 Curl_closesocket(conn, portsock);
1180 return CURLE_FTP_PORT_FAILED;
1183 possibly_non_local = FALSE; /* don't try this again */
1186 else if(error != EADDRINUSE && error != EACCES) {
1187 failf(data, "bind(port=%hu) failed: %s", port,
1188 Curl_strerror(conn, error) );
1189 Curl_closesocket(conn, portsock);
1190 return CURLE_FTP_PORT_FAILED;
1199 /* maybe all ports were in use already*/
1200 if(port > port_max) {
1201 failf(data, "bind() failed, we ran out of ports!");
1202 Curl_closesocket(conn, portsock);
1203 return CURLE_FTP_PORT_FAILED;
1206 /* get the name again after the bind() so that we can extract the
1207 port number it uses now */
1209 if(getsockname(portsock, (struct sockaddr *)sa, &sslen)) {
1210 failf(data, "getsockname() failed: %s",
1211 Curl_strerror(conn, SOCKERRNO) );
1212 Curl_closesocket(conn, portsock);
1213 return CURLE_FTP_PORT_FAILED;
1216 /* step 4, listen on the socket */
1218 if(listen(portsock, 1)) {
1219 failf(data, "socket failure: %s", Curl_strerror(conn, SOCKERRNO));
1220 Curl_closesocket(conn, portsock);
1221 return CURLE_FTP_PORT_FAILED;
1224 /* step 5, send the proper FTP command */
1226 /* get a plain printable version of the numerical address to work with
1228 Curl_printable_address(ai, myhost, sizeof(myhost));
1231 if(!conn->bits.ftp_use_eprt && conn->bits.ipv6)
1232 /* EPRT is disabled but we are connected to a IPv6 host, so we ignore the
1233 request and enable EPRT again! */
1234 conn->bits.ftp_use_eprt = TRUE;
1237 for(; fcmd != DONE; fcmd++) {
1239 if(!conn->bits.ftp_use_eprt && (EPRT == fcmd))
1240 /* if disabled, goto next */
1243 if((PORT == fcmd) && sa->sa_family != AF_INET)
1244 /* PORT is ipv4 only */
1247 switch (sa->sa_family) {
1249 port = ntohs(sa4->sin_port);
1253 port = ntohs(sa6->sin6_port);
1257 continue; /* might as well skip this */
1262 * Two fine examples from RFC2428;
1264 * EPRT |1|132.235.1.2|6275|
1266 * EPRT |2|1080::8:800:200C:417A|5282|
1269 result = Curl_pp_sendf(&ftpc->pp, "%s |%d|%s|%hu|", mode[fcmd],
1270 sa->sa_family == AF_INET?1:2,
1273 failf(data, "Failure sending EPRT command: %s",
1274 curl_easy_strerror(result));
1275 Curl_closesocket(conn, portsock);
1276 /* don't retry using PORT */
1277 ftpc->count1 = PORT;
1279 state(conn, FTP_STOP);
1284 else if(PORT == fcmd) {
1285 char *source = myhost;
1288 /* translate x.x.x.x to x,x,x,x */
1289 while(source && *source) {
1298 snprintf(dest, 20, ",%d,%d", (int)(port>>8), (int)(port&0xff));
1300 result = Curl_pp_sendf(&ftpc->pp, "%s %s", mode[fcmd], tmp);
1302 failf(data, "Failure sending PORT command: %s",
1303 curl_easy_strerror(result));
1304 Curl_closesocket(conn, portsock);
1306 state(conn, FTP_STOP);
1313 /* store which command was sent */
1314 ftpc->count1 = fcmd;
1316 /* we set the secondary socket variable to this for now, it is only so that
1317 the cleanup function will close it in case we fail before the true
1318 secondary stuff is made */
1319 if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET])
1320 Curl_closesocket(conn, conn->sock[SECONDARYSOCKET]);
1321 conn->sock[SECONDARYSOCKET] = portsock;
1323 /* this tcpconnect assignment below is a hackish work-around to make the
1324 multi interface with active FTP work - as it will not wait for a
1325 (passive) connect in Curl_is_connected().
1327 The *proper* fix is to make sure that the active connection from the
1328 server is done in a non-blocking way. Currently, it is still BLOCKING.
1330 conn->bits.tcpconnect[SECONDARYSOCKET] = TRUE;
1332 state(conn, FTP_PORT);
1336 static CURLcode ftp_state_use_pasv(struct connectdata *conn)
1338 struct ftp_conn *ftpc = &conn->proto.ftpc;
1339 CURLcode result = CURLE_OK;
1341 Here's the excecutive summary on what to do:
1343 PASV is RFC959, expect:
1344 227 Entering Passive Mode (a1,a2,a3,a4,p1,p2)
1346 LPSV is RFC1639, expect:
1347 228 Entering Long Passive Mode (4,4,a1,a2,a3,a4,2,p1,p2)
1349 EPSV is RFC2428, expect:
1350 229 Entering Extended Passive Mode (|||port|)
1354 static const char mode[][5] = { "EPSV", "PASV" };
1358 if(!conn->bits.ftp_use_epsv && conn->bits.ipv6)
1359 /* EPSV is disabled but we are connected to a IPv6 host, so we ignore the
1360 request and enable EPSV again! */
1361 conn->bits.ftp_use_epsv = TRUE;
1364 modeoff = conn->bits.ftp_use_epsv?0:1;
1366 PPSENDF(&ftpc->pp, "%s", mode[modeoff]);
1368 ftpc->count1 = modeoff;
1369 state(conn, FTP_PASV);
1370 infof(conn->data, "Connect data stream passively\n");
1375 /* REST is the last command in the chain of commands when a "head"-like
1376 request is made. Thus, if an actual transfer is to be made this is where
1377 we take off for real. */
1378 static CURLcode ftp_state_post_rest(struct connectdata *conn)
1380 CURLcode result = CURLE_OK;
1381 struct FTP *ftp = conn->data->state.proto.ftp;
1382 struct SessionHandle *data = conn->data;
1384 if(ftp->transfer != FTPTRANSFER_BODY) {
1385 /* doesn't transfer any data */
1387 /* still possibly do PRE QUOTE jobs */
1388 state(conn, FTP_RETR_PREQUOTE);
1389 result = ftp_state_quote(conn, TRUE, FTP_RETR_PREQUOTE);
1391 else if(data->set.ftp_use_port) {
1392 /* We have chosen to use the PORT (or similar) command */
1393 result = ftp_state_use_port(conn, EPRT);
1396 /* We have chosen (this is default) to use the PASV (or similar) command */
1397 if(data->set.ftp_use_pret) {
1398 /* The user has requested that we send a PRET command
1399 to prepare the server for the upcoming PASV */
1400 if(!conn->proto.ftpc.file) {
1401 PPSENDF(&conn->proto.ftpc.pp, "PRET %s",
1402 data->set.str[STRING_CUSTOMREQUEST]?
1403 data->set.str[STRING_CUSTOMREQUEST]:
1404 (data->set.ftp_list_only?"NLST":"LIST"));
1406 else if(data->set.upload) {
1407 PPSENDF(&conn->proto.ftpc.pp, "PRET STOR %s", conn->proto.ftpc.file);
1410 PPSENDF(&conn->proto.ftpc.pp, "PRET RETR %s", conn->proto.ftpc.file);
1412 state(conn, FTP_PRET);
1415 result = ftp_state_use_pasv(conn);
1421 static CURLcode ftp_state_post_size(struct connectdata *conn)
1423 CURLcode result = CURLE_OK;
1424 struct FTP *ftp = conn->data->state.proto.ftp;
1425 struct ftp_conn *ftpc = &conn->proto.ftpc;
1427 if((ftp->transfer != FTPTRANSFER_BODY) && ftpc->file) {
1428 /* if a "head"-like request is being made (on a file) */
1430 /* Determine if server can respond to REST command and therefore
1431 whether it supports range */
1432 PPSENDF(&conn->proto.ftpc.pp, "REST %d", 0);
1434 state(conn, FTP_REST);
1437 result = ftp_state_post_rest(conn);
1442 static CURLcode ftp_state_post_type(struct connectdata *conn)
1444 CURLcode result = CURLE_OK;
1445 struct FTP *ftp = conn->data->state.proto.ftp;
1446 struct ftp_conn *ftpc = &conn->proto.ftpc;
1448 if((ftp->transfer == FTPTRANSFER_INFO) && ftpc->file) {
1449 /* if a "head"-like request is being made (on a file) */
1451 /* we know ftpc->file is a valid pointer to a file name */
1452 PPSENDF(&ftpc->pp, "SIZE %s", ftpc->file);
1454 state(conn, FTP_SIZE);
1457 result = ftp_state_post_size(conn);
1462 static CURLcode ftp_state_post_listtype(struct connectdata *conn)
1464 CURLcode result = CURLE_OK;
1465 struct SessionHandle *data = conn->data;
1467 /* If this output is to be machine-parsed, the NLST command might be better
1468 to use, since the LIST command output is not specified or standard in any
1469 way. It has turned out that the NLST list output is not the same on all
1470 servers either... */
1473 if FTPFILE_NOCWD was specified, we are currently in
1474 the user's home directory, so we should add the path
1475 as argument for the LIST / NLST / or custom command.
1476 Whether the server will support this, is uncertain.
1478 The other ftp_filemethods will CWD into dir/dir/ first and
1479 then just do LIST (in that case: nothing to do here)
1481 char *cmd,*lstArg,*slashPos;
1484 if((data->set.ftp_filemethod == FTPFILE_NOCWD) &&
1486 data->state.path[0] &&
1487 strchr(data->state.path,'/')) {
1489 lstArg = strdup(data->state.path);
1491 return CURLE_OUT_OF_MEMORY;
1493 /* Check if path does not end with /, as then we cut off the file part */
1494 if(lstArg[strlen(lstArg) - 1] != '/') {
1496 /* chop off the file part if format is dir/dir/file */
1497 slashPos = strrchr(lstArg,'/');
1499 *(slashPos+1) = '\0';
1503 cmd = aprintf( "%s%s%s",
1504 data->set.str[STRING_CUSTOMREQUEST]?
1505 data->set.str[STRING_CUSTOMREQUEST]:
1506 (data->set.ftp_list_only?"NLST":"LIST"),
1508 lstArg? lstArg: "" );
1513 return CURLE_OUT_OF_MEMORY;
1516 result = Curl_pp_sendf(&conn->proto.ftpc.pp, "%s", cmd);
1523 if(result != CURLE_OK)
1526 state(conn, FTP_LIST);
1531 static CURLcode ftp_state_post_retrtype(struct connectdata *conn)
1533 CURLcode result = CURLE_OK;
1535 /* We've sent the TYPE, now we must send the list of prequote strings */
1537 result = ftp_state_quote(conn, TRUE, FTP_RETR_PREQUOTE);
1542 static CURLcode ftp_state_post_stortype(struct connectdata *conn)
1544 CURLcode result = CURLE_OK;
1546 /* We've sent the TYPE, now we must send the list of prequote strings */
1548 result = ftp_state_quote(conn, TRUE, FTP_STOR_PREQUOTE);
1553 static CURLcode ftp_state_post_mdtm(struct connectdata *conn)
1555 CURLcode result = CURLE_OK;
1556 struct FTP *ftp = conn->data->state.proto.ftp;
1557 struct SessionHandle *data = conn->data;
1558 struct ftp_conn *ftpc = &conn->proto.ftpc;
1560 /* If we have selected NOBODY and HEADER, it means that we only want file
1561 information. Which in FTP can't be much more than the file size and
1563 if(data->set.opt_no_body && ftpc->file &&
1564 ftp_need_type(conn, data->set.prefer_ascii)) {
1565 /* The SIZE command is _not_ RFC 959 specified, and therefor many servers
1566 may not support it! It is however the only way we have to get a file's
1569 ftp->transfer = FTPTRANSFER_INFO;
1570 /* this means no actual transfer will be made */
1572 /* Some servers return different sizes for different modes, and thus we
1573 must set the proper type before we check the size */
1574 result = ftp_nb_type(conn, data->set.prefer_ascii, FTP_TYPE);
1579 result = ftp_state_post_type(conn);
1584 /* This is called after the CWD commands have been done in the beginning of
1586 static CURLcode ftp_state_post_cwd(struct connectdata *conn)
1588 CURLcode result = CURLE_OK;
1589 struct SessionHandle *data = conn->data;
1590 struct ftp_conn *ftpc = &conn->proto.ftpc;
1592 /* Requested time of file or time-depended transfer? */
1593 if((data->set.get_filetime || data->set.timecondition) && ftpc->file) {
1595 /* we have requested to get the modified-time of the file, this is a white
1596 spot as the MDTM is not mentioned in RFC959 */
1597 PPSENDF(&ftpc->pp, "MDTM %s", ftpc->file);
1599 state(conn, FTP_MDTM);
1602 result = ftp_state_post_mdtm(conn);
1608 /* This is called after the TYPE and possible quote commands have been sent */
1609 static CURLcode ftp_state_ul_setup(struct connectdata *conn,
1612 CURLcode result = CURLE_OK;
1613 struct FTP *ftp = conn->data->state.proto.ftp;
1614 struct SessionHandle *data = conn->data;
1615 struct ftp_conn *ftpc = &conn->proto.ftpc;
1616 int seekerr = CURL_SEEKFUNC_OK;
1618 if((data->state.resume_from && !sizechecked) ||
1619 ((data->state.resume_from > 0) && sizechecked)) {
1620 /* we're about to continue the uploading of a file */
1621 /* 1. get already existing file's size. We use the SIZE command for this
1622 which may not exist in the server! The SIZE command is not in
1625 /* 2. This used to set REST. But since we can do append, we
1626 don't another ftp command. We just skip the source file
1627 offset and then we APPEND the rest on the file instead */
1629 /* 3. pass file-size number of bytes in the source file */
1630 /* 4. lower the infilesize counter */
1631 /* => transfer as usual */
1633 if(data->state.resume_from < 0 ) {
1634 /* Got no given size to start from, figure it out */
1635 PPSENDF(&ftpc->pp, "SIZE %s", ftpc->file);
1636 state(conn, FTP_STOR_SIZE);
1641 data->set.ftp_append = TRUE;
1643 /* Let's read off the proper amount of bytes from the input. */
1644 if(conn->seek_func) {
1645 seekerr = conn->seek_func(conn->seek_client, data->state.resume_from,
1649 if(seekerr != CURL_SEEKFUNC_OK) {
1650 if(seekerr != CURL_SEEKFUNC_CANTSEEK) {
1651 failf(data, "Could not seek stream");
1652 return CURLE_FTP_COULDNT_USE_REST;
1654 /* seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */
1656 curl_off_t passed=0;
1658 size_t readthisamountnow =
1659 (data->state.resume_from - passed > CURL_OFF_T_C(BUFSIZE)) ?
1660 BUFSIZE : curlx_sotouz(data->state.resume_from - passed);
1662 size_t actuallyread =
1663 conn->fread_func(data->state.buffer, 1, readthisamountnow,
1666 passed += actuallyread;
1667 if((actuallyread == 0) || (actuallyread > readthisamountnow)) {
1668 /* this checks for greater-than only to make sure that the
1669 CURL_READFUNC_ABORT return code still aborts */
1670 failf(data, "Failed to read data");
1671 return CURLE_FTP_COULDNT_USE_REST;
1673 } while(passed < data->state.resume_from);
1676 /* now, decrease the size of the read */
1677 if(data->set.infilesize>0) {
1678 data->set.infilesize -= data->state.resume_from;
1680 if(data->set.infilesize <= 0) {
1681 infof(data, "File already completely uploaded\n");
1683 /* no data to transfer */
1684 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1686 /* Set ->transfer so that we won't get any error in
1687 * ftp_done() because we didn't transfer anything! */
1688 ftp->transfer = FTPTRANSFER_NONE;
1690 state(conn, FTP_STOP);
1694 /* we've passed, proceed as normal */
1697 PPSENDF(&ftpc->pp, data->set.ftp_append?"APPE %s":"STOR %s",
1700 state(conn, FTP_STOR);
1705 static CURLcode ftp_state_quote(struct connectdata *conn,
1709 CURLcode result = CURLE_OK;
1710 struct SessionHandle *data = conn->data;
1711 struct FTP *ftp = data->state.proto.ftp;
1712 struct ftp_conn *ftpc = &conn->proto.ftpc;
1714 struct curl_slist *item;
1719 item = data->set.quote;
1721 case FTP_RETR_PREQUOTE:
1722 case FTP_STOR_PREQUOTE:
1723 item = data->set.prequote;
1726 item = data->set.postquote;
1732 * 'count1' to iterate over the commands to send
1733 * 'count2' to store wether to allow commands to fail
1744 /* Skip count1 items in the linked list */
1745 while((i< ftpc->count1) && item) {
1750 char *cmd = item->data;
1753 ftpc->count2 = 1; /* the sent command is allowed to fail */
1756 ftpc->count2 = 0; /* failure means cancel operation */
1758 PPSENDF(&ftpc->pp, "%s", cmd);
1759 state(conn, instate);
1765 /* No more quote to send, continue to ... */
1769 result = ftp_state_cwd(conn);
1771 case FTP_RETR_PREQUOTE:
1772 if(ftp->transfer != FTPTRANSFER_BODY)
1773 state(conn, FTP_STOP);
1775 if(ftpc->known_filesize != -1) {
1776 Curl_pgrsSetDownloadSize(data, ftpc->known_filesize);
1777 result = ftp_state_post_retr_size(conn, ftpc->known_filesize);
1780 PPSENDF(&ftpc->pp, "SIZE %s", ftpc->file);
1781 state(conn, FTP_RETR_SIZE);
1785 case FTP_STOR_PREQUOTE:
1786 result = ftp_state_ul_setup(conn, FALSE);
1796 /* called from ftp_state_pasv_resp to switch to PASV in case of EPSV
1798 static CURLcode ftp_epsv_disable(struct connectdata *conn)
1800 CURLcode result = CURLE_OK;
1801 infof(conn->data, "got positive EPSV response, but can't connect. "
1802 "Disabling EPSV\n");
1803 /* disable it for next transfer */
1804 conn->bits.ftp_use_epsv = FALSE;
1805 conn->data->state.errorbuf = FALSE; /* allow error message to get
1807 PPSENDF(&conn->proto.ftpc.pp, "PASV", NULL);
1808 conn->proto.ftpc.count1++;
1809 /* remain in the FTP_PASV state */
1813 static CURLcode ftp_state_pasv_resp(struct connectdata *conn,
1816 struct ftp_conn *ftpc = &conn->proto.ftpc;
1818 struct SessionHandle *data=conn->data;
1819 Curl_addrinfo *conninfo;
1820 struct Curl_dns_entry *addr=NULL;
1822 unsigned short connectport; /* the local port connect() should use! */
1823 unsigned short newport=0; /* remote port */
1826 /* newhost must be able to hold a full IP-style address in ASCII, which
1827 in the IPv6 case means 5*8-1 = 39 letters */
1828 #define NEWHOST_BUFSIZE 48
1829 char newhost[NEWHOST_BUFSIZE];
1830 char *str=&data->state.buffer[4]; /* start on the first letter */
1832 if((ftpc->count1 == 0) &&
1834 /* positive EPSV response */
1835 char *ptr = strchr(str, '(');
1840 if(5 == sscanf(ptr, "%c%c%c%u%c",
1846 const char sep1 = separator[0];
1849 /* The four separators should be identical, or else this is an oddly
1850 formatted reply and we bail out immediately. */
1851 for(i=1; i<4; i++) {
1852 if(separator[i] != sep1) {
1853 ptr=NULL; /* set to NULL to signal error */
1858 newport = (unsigned short)(num & 0xffff);
1860 if(conn->bits.tunnel_proxy ||
1861 conn->proxytype == CURLPROXY_SOCKS5 ||
1862 conn->proxytype == CURLPROXY_SOCKS5_HOSTNAME ||
1863 conn->proxytype == CURLPROXY_SOCKS4 ||
1864 conn->proxytype == CURLPROXY_SOCKS4A)
1865 /* proxy tunnel -> use other host info because ip_addr_str is the
1866 proxy address not the ftp host */
1867 snprintf(newhost, sizeof(newhost), "%s", conn->host.name);
1869 /* use the same IP we are already connected to */
1870 snprintf(newhost, NEWHOST_BUFSIZE, "%s", conn->ip_addr_str);
1877 failf(data, "Weirdly formatted EPSV reply");
1878 return CURLE_FTP_WEIRD_PASV_REPLY;
1881 else if((ftpc->count1 == 1) &&
1883 /* positive PASV response */
1888 * Scan for a sequence of six comma-separated numbers and use them as
1889 * IP+port indicators.
1891 * Found reply-strings include:
1892 * "227 Entering Passive Mode (127,0,0,1,4,51)"
1893 * "227 Data transfer will passively listen to 127,0,0,1,4,51"
1894 * "227 Entering passive mode. 127,0,0,1,4,51"
1897 if(6 == sscanf(str, "%d,%d,%d,%d,%d,%d",
1898 &ip[0], &ip[1], &ip[2], &ip[3],
1899 &port[0], &port[1]))
1905 failf(data, "Couldn't interpret the 227-response");
1906 return CURLE_FTP_WEIRD_227_FORMAT;
1909 /* we got OK from server */
1910 if(data->set.ftp_skip_ip) {
1911 /* told to ignore the remotely given IP but instead use the one we used
1912 for the control connection */
1913 infof(data, "Skips %d.%d.%d.%d for data connection, uses %s instead\n",
1914 ip[0], ip[1], ip[2], ip[3],
1916 if(conn->bits.tunnel_proxy ||
1917 conn->proxytype == CURLPROXY_SOCKS5 ||
1918 conn->proxytype == CURLPROXY_SOCKS5_HOSTNAME ||
1919 conn->proxytype == CURLPROXY_SOCKS4 ||
1920 conn->proxytype == CURLPROXY_SOCKS4A)
1921 /* proxy tunnel -> use other host info because ip_addr_str is the
1922 proxy address not the ftp host */
1923 snprintf(newhost, sizeof(newhost), "%s", conn->host.name);
1925 snprintf(newhost, sizeof(newhost), "%s", conn->ip_addr_str);
1928 snprintf(newhost, sizeof(newhost),
1929 "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
1930 newport = (unsigned short)(((port[0]<<8) + port[1]) & 0xffff);
1932 else if(ftpc->count1 == 0) {
1933 /* EPSV failed, move on to PASV */
1935 /* disable it for next transfer */
1936 conn->bits.ftp_use_epsv = FALSE;
1937 infof(data, "disabling EPSV usage\n");
1939 PPSENDF(&ftpc->pp, "PASV", NULL);
1941 /* remain in the FTP_PASV state */
1945 failf(data, "Bad PASV/EPSV response: %03d", ftpcode);
1946 return CURLE_FTP_WEIRD_PASV_REPLY;
1949 if(data->set.str[STRING_PROXY] && *data->set.str[STRING_PROXY]) {
1951 * This is a tunnel through a http proxy and we need to connect to the
1954 * We don't want to rely on a former host lookup that might've expired
1955 * now, instead we remake the lookup here and now!
1957 rc = Curl_resolv(conn, conn->proxy.name, (int)conn->port, &addr);
1958 if(rc == CURLRESOLV_PENDING)
1959 /* BLOCKING, ignores the return code but 'addr' will be NULL in
1961 (void)Curl_resolver_wait_resolv(conn, &addr);
1964 (unsigned short)conn->port; /* we connect to the proxy's port */
1967 failf(data, "Can't resolve proxy host %s:%hu",
1968 conn->proxy.name, connectport);
1969 return CURLE_FTP_CANT_GET_HOST;
1973 /* normal, direct, ftp connection */
1974 rc = Curl_resolv(conn, newhost, newport, &addr);
1975 if(rc == CURLRESOLV_PENDING)
1977 (void)Curl_resolver_wait_resolv(conn, &addr);
1979 connectport = newport; /* we connect to the remote port */
1982 failf(data, "Can't resolve new host %s:%hu", newhost, connectport);
1983 return CURLE_FTP_CANT_GET_HOST;
1987 result = Curl_connecthost(conn,
1989 &conn->sock[SECONDARYSOCKET],
1993 Curl_resolv_unlock(data, addr); /* we're done using this address */
1996 if(ftpc->count1 == 0 && ftpcode == 229)
1997 return ftp_epsv_disable(conn);
2002 conn->bits.tcpconnect[SECONDARYSOCKET] = connected;
2005 * When this is used from the multi interface, this might've returned with
2006 * the 'connected' set to FALSE and thus we are now awaiting a non-blocking
2007 * connect to connect and we should not be "hanging" here waiting.
2010 if(data->set.verbose)
2011 /* this just dumps information about this second connection */
2012 ftp_pasv_verbose(conn, conninfo, newhost, connectport);
2014 switch(conn->proxytype) {
2015 /* FIX: this MUST wait for a proper connect first if 'connected' is
2017 case CURLPROXY_SOCKS5:
2018 case CURLPROXY_SOCKS5_HOSTNAME:
2019 result = Curl_SOCKS5(conn->proxyuser, conn->proxypasswd, newhost, newport,
2020 SECONDARYSOCKET, conn);
2022 case CURLPROXY_SOCKS4:
2023 result = Curl_SOCKS4(conn->proxyuser, newhost, newport,
2024 SECONDARYSOCKET, conn, FALSE);
2026 case CURLPROXY_SOCKS4A:
2027 result = Curl_SOCKS4(conn->proxyuser, newhost, newport,
2028 SECONDARYSOCKET, conn, TRUE);
2030 case CURLPROXY_HTTP:
2031 case CURLPROXY_HTTP_1_0:
2032 /* do nothing here. handled later. */
2035 failf(data, "unknown proxytype option given");
2036 result = CURLE_COULDNT_CONNECT;
2041 if(ftpc->count1 == 0 && ftpcode == 229)
2042 return ftp_epsv_disable(conn);
2046 if(conn->bits.tunnel_proxy && conn->bits.httpproxy) {
2047 /* FIX: this MUST wait for a proper connect first if 'connected' is
2051 /* We want "seamless" FTP operations through HTTP proxy tunnel */
2053 /* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the member
2054 * conn->proto.http; we want FTP through HTTP and we have to change the
2055 * member temporarily for connecting to the HTTP proxy. After
2056 * Curl_proxyCONNECT we have to set back the member to the original struct
2059 struct HTTP http_proxy;
2060 struct FTP *ftp_save = data->state.proto.ftp;
2061 memset(&http_proxy, 0, sizeof(http_proxy));
2062 data->state.proto.http = &http_proxy;
2064 result = Curl_proxyCONNECT(conn, SECONDARYSOCKET, newhost, newport);
2066 data->state.proto.ftp = ftp_save;
2072 conn->bits.tcpconnect[SECONDARYSOCKET] = TRUE;
2074 state(conn, FTP_STOP); /* this phase is completed */
2079 static CURLcode ftp_state_port_resp(struct connectdata *conn,
2082 struct SessionHandle *data = conn->data;
2083 struct ftp_conn *ftpc = &conn->proto.ftpc;
2084 ftpport fcmd = (ftpport)ftpc->count1;
2085 CURLcode result = CURLE_OK;
2087 if(ftpcode != 200) {
2088 /* the command failed */
2091 infof(data, "disabling EPRT usage\n");
2092 conn->bits.ftp_use_eprt = FALSE;
2097 failf(data, "Failed to do PORT");
2098 result = CURLE_FTP_PORT_FAILED;
2102 result = ftp_state_use_port(conn, fcmd);
2105 infof(data, "Connect data stream actively\n");
2106 state(conn, FTP_STOP); /* end of DO phase */
2112 static CURLcode ftp_state_mdtm_resp(struct connectdata *conn,
2115 CURLcode result = CURLE_OK;
2116 struct SessionHandle *data=conn->data;
2117 struct FTP *ftp = data->state.proto.ftp;
2118 struct ftp_conn *ftpc = &conn->proto.ftpc;
2123 /* we got a time. Format should be: "YYYYMMDDHHMMSS[.sss]" where the
2124 last .sss part is optional and means fractions of a second */
2125 int year, month, day, hour, minute, second;
2126 char *buf = data->state.buffer;
2127 if(6 == sscanf(buf+4, "%04d%02d%02d%02d%02d%02d",
2128 &year, &month, &day, &hour, &minute, &second)) {
2129 /* we have a time, reformat it */
2130 time_t secs=time(NULL);
2131 /* using the good old yacc/bison yuck */
2132 snprintf(buf, sizeof(conn->data->state.buffer),
2133 "%04d%02d%02d %02d:%02d:%02d GMT",
2134 year, month, day, hour, minute, second);
2135 /* now, convert this into a time() value: */
2136 data->info.filetime = (long)curl_getdate(buf, &secs);
2139 #ifdef CURL_FTP_HTTPSTYLE_HEAD
2140 /* If we asked for a time of the file and we actually got one as well,
2141 we "emulate" a HTTP-style header in our output. */
2143 if(data->set.opt_no_body &&
2145 data->set.get_filetime &&
2146 (data->info.filetime>=0) ) {
2147 time_t filetime = (time_t)data->info.filetime;
2149 const struct tm *tm = &buffer;
2151 result = Curl_gmtime(filetime, &buffer);
2155 /* format: "Tue, 15 Nov 1994 12:45:26" */
2156 snprintf(buf, BUFSIZE-1,
2157 "Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n",
2158 Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
2160 Curl_month[tm->tm_mon],
2165 result = Curl_client_write(conn, CLIENTWRITE_BOTH, buf, 0);
2168 } /* end of a ridiculous amount of conditionals */
2173 infof(data, "unsupported MDTM reply format\n");
2175 case 550: /* "No such file or directory" */
2176 failf(data, "Given file does not exist");
2177 result = CURLE_FTP_COULDNT_RETR_FILE;
2181 if(data->set.timecondition) {
2182 if((data->info.filetime > 0) && (data->set.timevalue > 0)) {
2183 switch(data->set.timecondition) {
2184 case CURL_TIMECOND_IFMODSINCE:
2186 if(data->info.filetime <= data->set.timevalue) {
2187 infof(data, "The requested document is not new enough\n");
2188 ftp->transfer = FTPTRANSFER_NONE; /* mark to not transfer data */
2189 data->info.timecond = TRUE;
2190 state(conn, FTP_STOP);
2194 case CURL_TIMECOND_IFUNMODSINCE:
2195 if(data->info.filetime > data->set.timevalue) {
2196 infof(data, "The requested document is not old enough\n");
2197 ftp->transfer = FTPTRANSFER_NONE; /* mark to not transfer data */
2198 data->info.timecond = TRUE;
2199 state(conn, FTP_STOP);
2206 infof(data, "Skipping time comparison\n");
2211 result = ftp_state_post_mdtm(conn);
2216 static CURLcode ftp_state_type_resp(struct connectdata *conn,
2220 CURLcode result = CURLE_OK;
2221 struct SessionHandle *data=conn->data;
2223 if(ftpcode/100 != 2) {
2224 /* "sasserftpd" and "(u)r(x)bot ftpd" both responds with 226 after a
2225 successful 'TYPE I'. While that is not as RFC959 says, it is still a
2226 positive response code and we allow that. */
2227 failf(data, "Couldn't set desired mode");
2228 return CURLE_FTP_COULDNT_SET_TYPE;
2231 infof(data, "Got a %03d response code instead of the assumed 200\n",
2234 if(instate == FTP_TYPE)
2235 result = ftp_state_post_type(conn);
2236 else if(instate == FTP_LIST_TYPE)
2237 result = ftp_state_post_listtype(conn);
2238 else if(instate == FTP_RETR_TYPE)
2239 result = ftp_state_post_retrtype(conn);
2240 else if(instate == FTP_STOR_TYPE)
2241 result = ftp_state_post_stortype(conn);
2246 static CURLcode ftp_state_post_retr_size(struct connectdata *conn,
2247 curl_off_t filesize)
2249 CURLcode result = CURLE_OK;
2250 struct SessionHandle *data=conn->data;
2251 struct FTP *ftp = data->state.proto.ftp;
2252 struct ftp_conn *ftpc = &conn->proto.ftpc;
2254 if(data->set.max_filesize && (filesize > data->set.max_filesize)) {
2255 failf(data, "Maximum file size exceeded");
2256 return CURLE_FILESIZE_EXCEEDED;
2258 ftp->downloadsize = filesize;
2260 if(data->state.resume_from) {
2261 /* We always (attempt to) get the size of downloads, so it is done before
2262 this even when not doing resumes. */
2263 if(filesize == -1) {
2264 infof(data, "ftp server doesn't support SIZE\n");
2265 /* We couldn't get the size and therefore we can't know if there really
2266 is a part of the file left to get, although the server will just
2267 close the connection when we start the connection so it won't cause
2268 us any harm, just not make us exit as nicely. */
2271 /* We got a file size report, so we check that there actually is a
2272 part of the file left to get, or else we go home. */
2273 if(data->state.resume_from< 0) {
2274 /* We're supposed to download the last abs(from) bytes */
2275 if(filesize < -data->state.resume_from) {
2276 failf(data, "Offset (%" FORMAT_OFF_T
2277 ") was beyond file size (%" FORMAT_OFF_T ")",
2278 data->state.resume_from, filesize);
2279 return CURLE_BAD_DOWNLOAD_RESUME;
2281 /* convert to size to download */
2282 ftp->downloadsize = -data->state.resume_from;
2283 /* download from where? */
2284 data->state.resume_from = filesize - ftp->downloadsize;
2287 if(filesize < data->state.resume_from) {
2288 failf(data, "Offset (%" FORMAT_OFF_T
2289 ") was beyond file size (%" FORMAT_OFF_T ")",
2290 data->state.resume_from, filesize);
2291 return CURLE_BAD_DOWNLOAD_RESUME;
2293 /* Now store the number of bytes we are expected to download */
2294 ftp->downloadsize = filesize-data->state.resume_from;
2298 if(ftp->downloadsize == 0) {
2299 /* no data to transfer */
2300 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
2301 infof(data, "File already completely downloaded\n");
2303 /* Set ->transfer so that we won't get any error in ftp_done()
2304 * because we didn't transfer the any file */
2305 ftp->transfer = FTPTRANSFER_NONE;
2306 state(conn, FTP_STOP);
2310 /* Set resume file transfer offset */
2311 infof(data, "Instructs server to resume from offset %" FORMAT_OFF_T
2312 "\n", data->state.resume_from);
2314 PPSENDF(&ftpc->pp, "REST %" FORMAT_OFF_T, data->state.resume_from);
2316 state(conn, FTP_RETR_REST);
2321 PPSENDF(&ftpc->pp, "RETR %s", ftpc->file);
2322 state(conn, FTP_RETR);
2328 static CURLcode ftp_state_size_resp(struct connectdata *conn,
2332 CURLcode result = CURLE_OK;
2333 struct SessionHandle *data=conn->data;
2334 curl_off_t filesize;
2335 char *buf = data->state.buffer;
2337 /* get the size from the ascii string: */
2338 filesize = (ftpcode == 213)?curlx_strtoofft(buf+4, NULL, 0):-1;
2340 if(instate == FTP_SIZE) {
2341 #ifdef CURL_FTP_HTTPSTYLE_HEAD
2342 if(-1 != filesize) {
2343 snprintf(buf, sizeof(data->state.buffer),
2344 "Content-Length: %" FORMAT_OFF_T "\r\n", filesize);
2345 result = Curl_client_write(conn, CLIENTWRITE_BOTH, buf, 0);
2350 Curl_pgrsSetDownloadSize(data, filesize);
2351 result = ftp_state_post_size(conn);
2353 else if(instate == FTP_RETR_SIZE) {
2354 Curl_pgrsSetDownloadSize(data, filesize);
2355 result = ftp_state_post_retr_size(conn, filesize);
2357 else if(instate == FTP_STOR_SIZE) {
2358 data->state.resume_from = filesize;
2359 result = ftp_state_ul_setup(conn, TRUE);
2365 static CURLcode ftp_state_rest_resp(struct connectdata *conn,
2369 CURLcode result = CURLE_OK;
2370 struct ftp_conn *ftpc = &conn->proto.ftpc;
2375 #ifdef CURL_FTP_HTTPSTYLE_HEAD
2376 if(ftpcode == 350) {
2377 char buffer[24]= { "Accept-ranges: bytes\r\n" };
2378 result = Curl_client_write(conn, CLIENTWRITE_BOTH, buffer, 0);
2383 result = ftp_state_post_rest(conn);
2387 if(ftpcode != 350) {
2388 failf(conn->data, "Couldn't use REST");
2389 result = CURLE_FTP_COULDNT_USE_REST;
2392 PPSENDF(&ftpc->pp, "RETR %s", ftpc->file);
2393 state(conn, FTP_RETR);
2401 static CURLcode ftp_state_stor_resp(struct connectdata *conn,
2402 int ftpcode, ftpstate instate)
2404 CURLcode result = CURLE_OK;
2405 struct SessionHandle *data = conn->data;
2408 failf(data, "Failed FTP upload: %0d", ftpcode);
2409 state(conn, FTP_STOP);
2410 /* oops, we never close the sockets! */
2411 return CURLE_UPLOAD_FAILED;
2414 conn->proto.ftpc.state_saved = instate;
2416 /* PORT means we are now awaiting the server to connect to us. */
2417 if(data->set.ftp_use_port) {
2420 result = AllowServerConnect(conn, &connected);
2425 struct ftp_conn *ftpc = &conn->proto.ftpc;
2426 infof(data, "Data conn was not available immediately\n");
2427 ftpc->wait_data_conn = TRUE;
2433 return InitiateTransfer(conn);
2436 /* for LIST and RETR responses */
2437 static CURLcode ftp_state_get_resp(struct connectdata *conn,
2441 CURLcode result = CURLE_OK;
2442 struct SessionHandle *data = conn->data;
2443 struct FTP *ftp = data->state.proto.ftp;
2444 char *buf = data->state.buffer;
2446 if((ftpcode == 150) || (ftpcode == 125)) {
2450 150 Opening BINARY mode data connection for /etc/passwd (2241
2451 bytes). (ok, the file is being transferred)
2454 150 Opening ASCII mode data connection for /bin/ls
2457 150 ASCII data connection for /bin/ls (137.167.104.91,37445) (0 bytes).
2460 150 Opening ASCII mode data connection for [file] (0.0.0.0,0) (545 bytes)
2463 125 Data connection already open; Transfer starting. */
2465 curl_off_t size=-1; /* default unknown size */
2469 * It appears that there are FTP-servers that return size 0 for files when
2470 * SIZE is used on the file while being in BINARY mode. To work around
2471 * that (stupid) behavior, we attempt to parse the RETR response even if
2472 * the SIZE returned size zero.
2474 * Debugging help from Salvatore Sorrentino on February 26, 2003.
2477 if((instate != FTP_LIST) &&
2478 !data->set.prefer_ascii &&
2479 (ftp->downloadsize < 1)) {
2481 * It seems directory listings either don't show the size or very
2482 * often uses size 0 anyway. ASCII transfers may very well turn out
2483 * that the transferred amount of data is not the same as this line
2484 * tells, why using this number in those cases only confuses us.
2486 * Example D above makes this parsing a little tricky */
2488 bytes=strstr(buf, " bytes");
2490 long in=(long)(bytes-buf);
2491 /* this is a hint there is size information in there! ;-) */
2493 /* scan for the left parenthesis and break there */
2496 /* skip only digits */
2497 if(!ISDIGIT(*bytes)) {
2501 /* one more estep backwards */
2504 /* if we have nothing but digits: */
2506 /* get the number! */
2507 size = curlx_strtoofft(bytes, NULL, 0);
2511 else if(ftp->downloadsize > -1)
2512 size = ftp->downloadsize;
2514 if(size > data->req.maxdownload && data->req.maxdownload > 0)
2515 size = data->req.size = data->req.maxdownload;
2516 else if((instate != FTP_LIST) && (data->set.prefer_ascii))
2517 size = -1; /* kludge for servers that understate ASCII mode file size */
2519 infof(data, "Maxdownload = %" FORMAT_OFF_T "\n", data->req.maxdownload);
2521 if(instate != FTP_LIST)
2522 infof(data, "Getting file with size: %" FORMAT_OFF_T "\n", size);
2525 conn->proto.ftpc.state_saved = instate;
2526 conn->proto.ftpc.retr_size_saved = size;
2528 if(data->set.ftp_use_port) {
2531 result = AllowServerConnect(conn, &connected);
2536 struct ftp_conn *ftpc = &conn->proto.ftpc;
2537 infof(data, "Data conn was not available immediately\n");
2538 state(conn, FTP_STOP);
2539 ftpc->wait_data_conn = TRUE;
2543 return InitiateTransfer(conn);
2546 if((instate == FTP_LIST) && (ftpcode == 450)) {
2547 /* simply no matching files in the dir listing */
2548 ftp->transfer = FTPTRANSFER_NONE; /* don't download anything */
2549 state(conn, FTP_STOP); /* this phase is over */
2552 failf(data, "RETR response: %03d", ftpcode);
2553 return instate == FTP_RETR && ftpcode == 550?
2554 CURLE_REMOTE_FILE_NOT_FOUND:
2555 CURLE_FTP_COULDNT_RETR_FILE;
2562 /* after USER, PASS and ACCT */
2563 static CURLcode ftp_state_loggedin(struct connectdata *conn)
2565 CURLcode result = CURLE_OK;
2568 if(conn->data->set.krb) {
2569 /* We may need to issue a KAUTH here to have access to the files
2570 * do it if user supplied a password
2572 if(conn->passwd && *conn->passwd) {
2574 result = Curl_krb_kauth(conn);
2580 if(conn->ssl[FIRSTSOCKET].use) {
2581 /* PBSZ = PROTECTION BUFFER SIZE.
2583 The 'draft-murray-auth-ftp-ssl' (draft 12, page 7) says:
2585 Specifically, the PROT command MUST be preceded by a PBSZ
2586 command and a PBSZ command MUST be preceded by a successful
2587 security data exchange (the TLS negotiation in this case)
2589 ... (and on page 8):
2591 Thus the PBSZ command must still be issued, but must have a
2592 parameter of '0' to indicate that no buffering is taking place
2593 and the data connection should not be encapsulated.
2595 PPSENDF(&conn->proto.ftpc.pp, "PBSZ %d", 0);
2596 state(conn, FTP_PBSZ);
2599 result = ftp_state_pwd(conn);
2604 /* for USER and PASS responses */
2605 static CURLcode ftp_state_user_resp(struct connectdata *conn,
2609 CURLcode result = CURLE_OK;
2610 struct SessionHandle *data = conn->data;
2611 struct FTP *ftp = data->state.proto.ftp;
2612 struct ftp_conn *ftpc = &conn->proto.ftpc;
2613 (void)instate; /* no use for this yet */
2615 /* some need password anyway, and others just return 2xx ignored */
2616 if((ftpcode == 331) && (ftpc->state == FTP_USER)) {
2617 /* 331 Password required for ...
2618 (the server requires to send the user's password too) */
2619 PPSENDF(&ftpc->pp, "PASS %s", ftp->passwd?ftp->passwd:"");
2620 state(conn, FTP_PASS);
2622 else if(ftpcode/100 == 2) {
2623 /* 230 User ... logged in.
2624 (the user logged in with or without password) */
2625 result = ftp_state_loggedin(conn);
2627 else if(ftpcode == 332) {
2628 if(data->set.str[STRING_FTP_ACCOUNT]) {
2629 PPSENDF(&ftpc->pp, "ACCT %s", data->set.str[STRING_FTP_ACCOUNT]);
2630 state(conn, FTP_ACCT);
2633 failf(data, "ACCT requested but none available");
2634 result = CURLE_LOGIN_DENIED;
2638 /* All other response codes, like:
2640 530 User ... access denied
2641 (the server denies to log the specified user) */
2643 if(conn->data->set.str[STRING_FTP_ALTERNATIVE_TO_USER] &&
2644 !conn->data->state.ftp_trying_alternative) {
2645 /* Ok, USER failed. Let's try the supplied command. */
2646 PPSENDF(&conn->proto.ftpc.pp, "%s",
2647 conn->data->set.str[STRING_FTP_ALTERNATIVE_TO_USER]);
2648 conn->data->state.ftp_trying_alternative = TRUE;
2649 state(conn, FTP_USER);
2653 failf(data, "Access denied: %03d", ftpcode);
2654 result = CURLE_LOGIN_DENIED;
2660 /* for ACCT response */
2661 static CURLcode ftp_state_acct_resp(struct connectdata *conn,
2664 CURLcode result = CURLE_OK;
2665 struct SessionHandle *data = conn->data;
2666 if(ftpcode != 230) {
2667 failf(data, "ACCT rejected by server: %03d", ftpcode);
2668 result = CURLE_FTP_WEIRD_PASS_REPLY; /* FIX */
2671 result = ftp_state_loggedin(conn);
2677 static CURLcode ftp_statemach_act(struct connectdata *conn)
2680 curl_socket_t sock = conn->sock[FIRSTSOCKET];
2681 struct SessionHandle *data=conn->data;
2683 struct ftp_conn *ftpc = &conn->proto.ftpc;
2684 struct pingpong *pp = &ftpc->pp;
2685 static const char ftpauth[][4] = { "SSL", "TLS" };
2689 return Curl_pp_flushsend(pp);
2691 result = ftp_readresp(sock, pp, &ftpcode, &nread);
2696 /* we have now received a full FTP server response */
2697 switch(ftpc->state) {
2699 if(ftpcode != 220) {
2700 failf(data, "Got a %03d ftp-server response when 220 was expected",
2702 return CURLE_FTP_WEIRD_SERVER_REPLY;
2705 /* We have received a 220 response fine, now we proceed. */
2706 #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
2708 /* If not anonymous login, try a secure login. Note that this
2709 procedure is still BLOCKING. */
2711 Curl_sec_request_prot(conn, "private");
2712 /* We set private first as default, in case the line below fails to
2713 set a valid level */
2714 Curl_sec_request_prot(conn, data->set.str[STRING_KRB_LEVEL]);
2716 if(Curl_sec_login(conn) != CURLE_OK)
2717 infof(data, "Logging in with password in cleartext!\n");
2719 infof(data, "Authentication successful\n");
2723 if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
2724 /* We don't have a SSL/TLS connection yet, but FTPS is
2725 requested. Try a FTPS connection now */
2728 switch(data->set.ftpsslauth) {
2729 case CURLFTPAUTH_DEFAULT:
2730 case CURLFTPAUTH_SSL:
2731 ftpc->count2 = 1; /* add one to get next */
2734 case CURLFTPAUTH_TLS:
2735 ftpc->count2 = -1; /* subtract one to get next */
2739 failf(data, "unsupported parameter to CURLOPT_FTPSSLAUTH: %d",
2740 (int)data->set.ftpsslauth);
2741 return CURLE_UNKNOWN_OPTION; /* we don't know what to do */
2743 PPSENDF(&ftpc->pp, "AUTH %s", ftpauth[ftpc->count1]);
2744 state(conn, FTP_AUTH);
2747 result = ftp_state_user(conn);
2755 /* we have gotten the response to a previous AUTH command */
2757 /* RFC2228 (page 5) says:
2759 * If the server is willing to accept the named security mechanism,
2760 * and does not require any security data, it must respond with
2761 * reply code 234/334.
2764 if((ftpcode == 234) || (ftpcode == 334)) {
2765 /* Curl_ssl_connect is BLOCKING */
2766 result = Curl_ssl_connect(conn, FIRSTSOCKET);
2767 if(CURLE_OK == result) {
2768 conn->ssl[SECONDARYSOCKET].use = FALSE; /* clear-text data */
2769 result = ftp_state_user(conn);
2772 else if(ftpc->count3 < 1) {
2774 ftpc->count1 += ftpc->count2; /* get next attempt */
2775 result = Curl_pp_sendf(&ftpc->pp, "AUTH %s", ftpauth[ftpc->count1]);
2776 /* remain in this same state */
2779 if(data->set.use_ssl > CURLUSESSL_TRY)
2780 /* we failed and CURLUSESSL_CONTROL or CURLUSESSL_ALL is set */
2781 result = CURLE_USE_SSL_FAILED;
2783 /* ignore the failure and continue */
2784 result = ftp_state_user(conn);
2793 result = ftp_state_user_resp(conn, ftpcode, ftpc->state);
2797 result = ftp_state_acct_resp(conn, ftpcode);
2801 PPSENDF(&ftpc->pp, "PROT %c",
2802 data->set.use_ssl == CURLUSESSL_CONTROL ? 'C' : 'P');
2803 state(conn, FTP_PROT);
2808 if(ftpcode/100 == 2)
2809 /* We have enabled SSL for the data connection! */
2810 conn->ssl[SECONDARYSOCKET].use =
2811 (data->set.use_ssl != CURLUSESSL_CONTROL) ? TRUE : FALSE;
2812 /* FTP servers typically responds with 500 if they decide to reject
2814 else if(data->set.use_ssl > CURLUSESSL_CONTROL)
2815 /* we failed and bails out */
2816 return CURLE_USE_SSL_FAILED;
2818 if(data->set.ftp_ccc) {
2819 /* CCC - Clear Command Channel
2821 PPSENDF(&ftpc->pp, "CCC", NULL);
2822 state(conn, FTP_CCC);
2825 result = ftp_state_pwd(conn);
2833 /* First shut down the SSL layer (note: this call will block) */
2834 result = Curl_ssl_shutdown(conn, FIRSTSOCKET);
2837 failf(conn->data, "Failed to clear the command channel (CCC)");
2842 /* Then continue as normal */
2843 result = ftp_state_pwd(conn);
2849 if(ftpcode == 257) {
2850 char *ptr=&data->state.buffer[4]; /* start on the first letter */
2854 dir = malloc(nread + 1);
2856 return CURLE_OUT_OF_MEMORY;
2858 /* Reply format is like
2859 257<space>"<directory-name>"<space><commentary> and the RFC959
2862 The directory name can contain any character; embedded
2863 double-quotes should be escaped by double-quotes (the
2864 "quote-doubling" convention).
2867 /* it started good */
2869 for(store = dir; *ptr;) {
2871 if('\"' == ptr[1]) {
2872 /* "quote-doubling" */
2878 *store = '\0'; /* zero terminate */
2879 break; /* get out of this loop */
2888 /* If the path name does not look like an absolute path (i.e.: it
2889 does not start with a '/'), we probably need some server-dependent
2890 adjustments. For example, this is the case when connecting to
2891 an OS400 FTP server: this server supports two name syntaxes,
2892 the default one being incompatible with standard pathes. In
2893 addition, this server switches automatically to the regular path
2894 syntax when one is encountered in a command: this results in
2895 having an entrypath in the wrong syntax when later used in CWD.
2896 The method used here is to check the server OS: we do it only
2897 if the path name looks strange to minimize overhead on other
2900 if(!ftpc->server_os && dir[0] != '/') {
2902 result = Curl_pp_sendf(&ftpc->pp, "SYST", NULL);
2903 if(result != CURLE_OK) {
2907 Curl_safefree(ftpc->entrypath);
2908 ftpc->entrypath = dir; /* remember this */
2909 infof(data, "Entry path is '%s'\n", ftpc->entrypath);
2910 /* also save it where getinfo can access it: */
2911 data->state.most_recent_ftp_entrypath = ftpc->entrypath;
2912 state(conn, FTP_SYST);
2916 Curl_safefree(ftpc->entrypath);
2917 ftpc->entrypath = dir; /* remember this */
2918 infof(data, "Entry path is '%s'\n", ftpc->entrypath);
2919 /* also save it where getinfo can access it: */
2920 data->state.most_recent_ftp_entrypath = ftpc->entrypath;
2923 /* couldn't get the path */
2925 infof(data, "Failed to figure out path\n");
2928 state(conn, FTP_STOP); /* we are done with the CONNECT phase! */
2929 DEBUGF(infof(data, "protocol connect phase DONE\n"));
2933 if(ftpcode == 215) {
2934 char *ptr=&data->state.buffer[4]; /* start on the first letter */
2938 os = malloc(nread + 1);
2940 return CURLE_OUT_OF_MEMORY;
2942 /* Reply format is like
2943 215<space><OS-name><space><commentary>
2947 for(store = os; *ptr && *ptr != ' ';)
2949 *store = '\0'; /* zero terminate */
2951 /* Check for special servers here. */
2953 if(strequal(os, "OS/400")) {
2954 /* Force OS400 name format 1. */
2955 result = Curl_pp_sendf(&ftpc->pp, "SITE NAMEFMT 1", NULL);
2956 if(result != CURLE_OK) {
2960 /* remember target server OS */
2961 Curl_safefree(ftpc->server_os);
2962 ftpc->server_os = os;
2963 state(conn, FTP_NAMEFMT);
2967 /* Nothing special for the target server. */
2968 /* remember target server OS */
2969 Curl_safefree(ftpc->server_os);
2970 ftpc->server_os = os;
2974 /* Cannot identify server OS. Continue anyway and cross fingers. */
2977 state(conn, FTP_STOP); /* we are done with the CONNECT phase! */
2978 DEBUGF(infof(data, "protocol connect phase DONE\n"));
2982 if(ftpcode == 250) {
2983 /* Name format change successful: reload initial path. */
2984 ftp_state_pwd(conn);
2988 state(conn, FTP_STOP); /* we are done with the CONNECT phase! */
2989 DEBUGF(infof(data, "protocol connect phase DONE\n"));
2994 case FTP_RETR_PREQUOTE:
2995 case FTP_STOR_PREQUOTE:
2996 if((ftpcode >= 400) && !ftpc->count2) {
2997 /* failure response code, and not allowed to fail */
2998 failf(conn->data, "QUOT command failed with %03d", ftpcode);
2999 return CURLE_QUOTE_ERROR;
3001 result = ftp_state_quote(conn, FALSE, ftpc->state);
3008 if(ftpcode/100 != 2) {
3009 /* failure to CWD there */
3010 if(conn->data->set.ftp_create_missing_dirs &&
3011 ftpc->count1 && !ftpc->count2) {
3013 ftpc->count2++; /* counter to prevent CWD-MKD loops */
3014 PPSENDF(&ftpc->pp, "MKD %s", ftpc->dirs[ftpc->count1 - 1]);
3015 state(conn, FTP_MKD);
3018 /* return failure */
3019 failf(data, "Server denied you to change to the given directory");
3020 ftpc->cwdfail = TRUE; /* don't remember this path as we failed
3022 return CURLE_REMOTE_ACCESS_DENIED;
3028 if(++ftpc->count1 <= ftpc->dirdepth) {
3030 PPSENDF(&ftpc->pp, "CWD %s", ftpc->dirs[ftpc->count1 - 1]);
3033 result = ftp_state_post_cwd(conn);
3041 if((ftpcode/100 != 2) && !ftpc->count3--) {
3042 /* failure to MKD the dir */
3043 failf(data, "Failed to MKD dir: %03d", ftpcode);
3044 return CURLE_REMOTE_ACCESS_DENIED;
3046 state(conn, FTP_CWD);
3048 PPSENDF(&ftpc->pp, "CWD %s", ftpc->dirs[ftpc->count1 - 1]);
3052 result = ftp_state_mdtm_resp(conn, ftpcode);
3059 result = ftp_state_type_resp(conn, ftpcode, ftpc->state);
3065 result = ftp_state_size_resp(conn, ftpcode, ftpc->state);
3070 result = ftp_state_rest_resp(conn, ftpcode, ftpc->state);
3074 if(ftpcode != 200) {
3075 /* there only is this one standard OK return code. */
3076 failf(data, "PRET command not accepted: %03d", ftpcode);
3077 return CURLE_FTP_PRET_FAILED;
3079 result = ftp_state_use_pasv(conn);
3083 result = ftp_state_pasv_resp(conn, ftpcode);
3087 result = ftp_state_port_resp(conn, ftpcode);
3092 result = ftp_state_get_resp(conn, ftpcode, ftpc->state);
3096 result = ftp_state_stor_resp(conn, ftpcode, ftpc->state);
3100 /* fallthrough, just stop! */
3102 /* internal error */
3103 state(conn, FTP_STOP);
3112 /* called repeatedly until done from multi.c */
3113 static CURLcode ftp_multi_statemach(struct connectdata *conn,
3116 struct ftp_conn *ftpc = &conn->proto.ftpc;
3117 CURLcode result = Curl_pp_multi_statemach(&ftpc->pp);
3119 /* Check for the state outside of the Curl_socket_ready() return code checks
3120 since at times we are in fact already in this state when this function
3122 *done = (ftpc->state == FTP_STOP) ? TRUE : FALSE;
3127 static CURLcode ftp_easy_statemach(struct connectdata *conn)
3129 struct ftp_conn *ftpc = &conn->proto.ftpc;
3130 struct pingpong *pp = &ftpc->pp;
3131 CURLcode result = CURLE_OK;
3133 while(ftpc->state != FTP_STOP) {
3134 result = Curl_pp_easy_statemach(pp);
3143 * Allocate and initialize the struct FTP for the current SessionHandle. If
3147 #if defined(__INTEL_COMPILER) && (__INTEL_COMPILER == 910) && \
3148 defined(__OPTIMIZE__) && defined(__unix__) && defined(__i386__)
3149 /* workaround icc 9.1 optimizer issue */
3150 #pragma optimize("", off)
3153 static CURLcode ftp_init(struct connectdata *conn)
3157 if(NULL == conn->data->state.proto.ftp) {
3158 conn->data->state.proto.ftp = malloc(sizeof(struct FTP));
3159 if(NULL == conn->data->state.proto.ftp)
3160 return CURLE_OUT_OF_MEMORY;
3163 ftp = conn->data->state.proto.ftp;
3165 /* get some initial data into the ftp struct */
3166 ftp->bytecountp = &conn->data->req.bytecount;
3167 ftp->transfer = FTPTRANSFER_BODY;
3168 ftp->downloadsize = 0;
3170 /* No need to duplicate user+password, the connectdata struct won't change
3171 during a session, but we re-init them here since on subsequent inits
3172 since the conn struct may have changed or been replaced.
3174 ftp->user = conn->user;
3175 ftp->passwd = conn->passwd;
3176 if(isBadFtpString(ftp->user))
3177 return CURLE_URL_MALFORMAT;
3178 if(isBadFtpString(ftp->passwd))
3179 return CURLE_URL_MALFORMAT;
3181 conn->proto.ftpc.known_filesize = -1; /* unknown size for now */
3186 #if defined(__INTEL_COMPILER) && (__INTEL_COMPILER == 910) && \
3187 defined(__OPTIMIZE__) && defined(__unix__) && defined(__i386__)
3188 /* workaround icc 9.1 optimizer issue */
3189 #pragma optimize("", on)
3193 * ftp_connect() should do everything that is to be considered a part of
3194 * the connection phase.
3196 * The variable 'done' points to will be TRUE if the protocol-layer connect
3197 * phase is done when this function returns, or FALSE is not. When called as
3198 * a part of the easy interface, it will always be TRUE.
3200 static CURLcode ftp_connect(struct connectdata *conn,
3201 bool *done) /* see description above */
3204 struct ftp_conn *ftpc = &conn->proto.ftpc;
3205 struct SessionHandle *data=conn->data;
3206 struct pingpong *pp = &ftpc->pp;
3208 *done = FALSE; /* default to not done yet */
3210 /* If there already is a protocol-specific struct allocated for this
3211 sessionhandle, deal with it */
3212 Curl_reset_reqproto(conn);
3214 result = ftp_init(conn);
3215 if(CURLE_OK != result)
3218 /* We always support persistent connections on ftp */
3219 conn->bits.close = FALSE;
3221 pp->response_time = RESP_TIMEOUT; /* set default response time-out */
3222 pp->statemach_act = ftp_statemach_act;
3223 pp->endofresp = ftp_endofresp;
3226 if(conn->handler->flags & PROTOPT_SSL) {
3228 result = Curl_ssl_connect(conn, FIRSTSOCKET);
3233 Curl_pp_init(pp); /* init the generic pingpong data */
3235 /* When we connect, we start in the state where we await the 220
3237 state(conn, FTP_WAIT220);
3239 if(data->state.used_interface == Curl_if_multi)
3240 result = ftp_multi_statemach(conn, done);
3242 result = ftp_easy_statemach(conn);
3250 /***********************************************************************
3254 * The DONE function. This does what needs to be done after a single DO has
3257 * Input argument is already checked for validity.
3259 static CURLcode ftp_done(struct connectdata *conn, CURLcode status,
3262 struct SessionHandle *data = conn->data;
3263 struct FTP *ftp = data->state.proto.ftp;
3264 struct ftp_conn *ftpc = &conn->proto.ftpc;
3265 struct pingpong *pp = &ftpc->pp;
3268 CURLcode result = CURLE_OK;
3269 bool was_ctl_valid = ftpc->ctl_valid;
3271 const char *path_to_use = data->state.path;
3274 /* When the easy handle is removed from the multi while libcurl is still
3275 * trying to resolve the host name, it seems that the ftp struct is not
3276 * yet initialized, but the removal action calls Curl_done() which calls
3277 * this function. So we simply return success if no ftp pointer is set.
3282 case CURLE_BAD_DOWNLOAD_RESUME:
3283 case CURLE_FTP_WEIRD_PASV_REPLY:
3284 case CURLE_FTP_PORT_FAILED:
3285 case CURLE_FTP_ACCEPT_FAILED:
3286 case CURLE_FTP_ACCEPT_TIMEOUT:
3287 case CURLE_FTP_COULDNT_SET_TYPE:
3288 case CURLE_FTP_COULDNT_RETR_FILE:
3289 case CURLE_PARTIAL_FILE:
3290 case CURLE_UPLOAD_FAILED:
3291 case CURLE_REMOTE_ACCESS_DENIED:
3292 case CURLE_FILESIZE_EXCEEDED:
3293 case CURLE_REMOTE_FILE_NOT_FOUND:
3294 case CURLE_WRITE_ERROR:
3295 /* the connection stays alive fine even though this happened */
3297 case CURLE_OK: /* doesn't affect the control connection's status */
3299 ftpc->ctl_valid = was_ctl_valid;
3302 /* until we cope better with prematurely ended requests, let them
3303 * fallback as if in complete failure */
3304 default: /* by default, an error means the control connection is
3305 wedged and should not be used anymore */
3306 ftpc->ctl_valid = FALSE;
3307 ftpc->cwdfail = TRUE; /* set this TRUE to prevent us to remember the
3308 current path, as this connection is going */
3309 conn->bits.close = TRUE; /* marked for closure */
3310 result = status; /* use the already set error code */
3314 /* now store a copy of the directory we are in */
3316 free(ftpc->prevpath);
3318 if(data->set.wildcardmatch) {
3319 if(data->set.chunk_end && ftpc->file) {
3320 data->set.chunk_end(data->wildcard.customptr);
3322 ftpc->known_filesize = -1;
3325 /* get the "raw" path */
3326 path = curl_easy_unescape(data, path_to_use, 0, NULL);
3328 /* out of memory, but we can limp along anyway (and should try to
3329 * since we may already be in the out of memory cleanup path) */
3331 result = CURLE_OUT_OF_MEMORY;
3332 ftpc->ctl_valid = FALSE; /* mark control connection as bad */
3333 conn->bits.close = TRUE; /* mark for connection closure */
3334 ftpc->prevpath = NULL; /* no path remembering */
3337 size_t flen = ftpc->file?strlen(ftpc->file):0; /* file is "raw" already */
3338 size_t dlen = strlen(path)-flen;
3339 if(!ftpc->cwdfail) {
3340 if(dlen && (data->set.ftp_filemethod != FTPFILE_NOCWD)) {
3341 ftpc->prevpath = path;
3343 /* if 'path' is not the whole string */
3344 ftpc->prevpath[dlen]=0; /* terminate */
3347 /* we never changed dir */
3348 ftpc->prevpath=strdup("");
3352 infof(data, "Remembering we are in dir \"%s\"\n", ftpc->prevpath);
3355 ftpc->prevpath = NULL; /* no path */
3359 /* free the dir tree and file parts */
3362 /* shut down the socket to inform the server we're done */
3365 shutdown(conn->sock[SECONDARYSOCKET],2); /* SD_BOTH */
3368 if(conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD) {
3369 if(!result && ftpc->dont_check && data->req.maxdownload > 0)
3370 /* partial download completed */
3371 result = Curl_pp_sendf(pp, "ABOR");
3373 failf(data, "Failure sending ABOR command: %s",
3374 curl_easy_strerror(result));
3375 ftpc->ctl_valid = FALSE; /* mark control connection as bad */
3376 conn->bits.close = TRUE; /* mark for connection closure */
3379 if(conn->ssl[SECONDARYSOCKET].use) {
3380 /* The secondary socket is using SSL so we must close down that part
3381 first before we close the socket for real */
3382 Curl_ssl_close(conn, SECONDARYSOCKET);
3384 /* Note that we keep "use" set to TRUE since that (next) connection is
3385 still requested to use SSL */
3387 if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET]) {
3388 Curl_closesocket(conn, conn->sock[SECONDARYSOCKET]);
3389 conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
3390 conn->bits.tcpconnect[SECONDARYSOCKET] = FALSE;
3394 if(!result && (ftp->transfer == FTPTRANSFER_BODY) && ftpc->ctl_valid &&
3395 pp->pending_resp && !premature) {
3397 * Let's see what the server says about the transfer we just performed,
3398 * but lower the timeout as sometimes this connection has died while the
3399 * data has been transferred. This happens when doing through NATs etc that
3400 * abandon old silent connections.
3402 long old_time = pp->response_time;
3404 pp->response_time = 60*1000; /* give it only a minute for now */
3405 pp->response = Curl_tvnow(); /* timeout relative now */
3407 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
3409 pp->response_time = old_time; /* set this back to previous value */
3411 if(!nread && (CURLE_OPERATION_TIMEDOUT == result)) {
3412 failf(data, "control connection looks dead");
3413 ftpc->ctl_valid = FALSE; /* mark control connection as bad */
3414 conn->bits.close = TRUE; /* mark for closure */
3420 if(ftpc->dont_check && data->req.maxdownload > 0) {
3421 /* we have just sent ABOR and there is no reliable way to check if it was
3422 * successful or not; we have to close the connection now */
3423 infof(data, "partial download completed, closing connection\n");
3424 conn->bits.close = TRUE; /* mark for closure */
3428 if(!ftpc->dont_check) {
3429 /* 226 Transfer complete, 250 Requested file action okay, completed. */
3430 if((ftpcode != 226) && (ftpcode != 250)) {
3431 failf(data, "server did not report OK, got %d", ftpcode);
3432 result = CURLE_PARTIAL_FILE;
3437 if(result || premature)
3438 /* the response code from the transfer showed an error already so no
3439 use checking further */
3441 else if(data->set.upload) {
3442 if((-1 != data->set.infilesize) &&
3443 (data->set.infilesize != *ftp->bytecountp) &&
3445 (ftp->transfer == FTPTRANSFER_BODY)) {
3446 failf(data, "Uploaded unaligned file size (%" FORMAT_OFF_T
3447 " out of %" FORMAT_OFF_T " bytes)",
3448 *ftp->bytecountp, data->set.infilesize);
3449 result = CURLE_PARTIAL_FILE;
3453 if((-1 != data->req.size) &&
3454 (data->req.size != *ftp->bytecountp) &&
3455 #ifdef CURL_DO_LINEEND_CONV
3456 /* Most FTP servers don't adjust their file SIZE response for CRLFs, so
3457 * we'll check to see if the discrepancy can be explained by the number
3458 * of CRLFs we've changed to LFs.
3460 ((data->req.size + data->state.crlf_conversions) !=
3461 *ftp->bytecountp) &&
3462 #endif /* CURL_DO_LINEEND_CONV */
3463 (data->req.maxdownload != *ftp->bytecountp)) {
3464 failf(data, "Received only partial file: %" FORMAT_OFF_T " bytes",
3466 result = CURLE_PARTIAL_FILE;
3468 else if(!ftpc->dont_check &&
3469 !*ftp->bytecountp &&
3470 (data->req.size>0)) {
3471 failf(data, "No data was received!");
3472 result = CURLE_FTP_COULDNT_RETR_FILE;
3476 /* clear these for next connection */
3477 ftp->transfer = FTPTRANSFER_BODY;
3478 ftpc->dont_check = FALSE;
3480 /* Send any post-transfer QUOTE strings? */
3481 if(!status && !result && !premature && data->set.postquote)
3482 result = ftp_sendquote(conn, data->set.postquote);
3487 /***********************************************************************
3491 * Where a 'quote' means a list of custom commands to send to the server.
3492 * The quote list is passed as an argument.
3498 CURLcode ftp_sendquote(struct connectdata *conn, struct curl_slist *quote)
3500 struct curl_slist *item;
3504 struct ftp_conn *ftpc = &conn->proto.ftpc;
3505 struct pingpong *pp = &ftpc->pp;
3510 char *cmd = item->data;
3511 bool acceptfail = FALSE;
3513 /* if a command starts with an asterisk, which a legal FTP command never
3514 can, the command will be allowed to fail without it causing any
3515 aborts or cancels etc. It will cause libcurl to act as if the command
3516 is successful, whatever the server reponds. */
3523 FTPSENDF(conn, "%s", cmd);
3525 pp->response = Curl_tvnow(); /* timeout relative now */
3527 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
3531 if(!acceptfail && (ftpcode >= 400)) {
3532 failf(conn->data, "QUOT string not accepted: %s", cmd);
3533 return CURLE_QUOTE_ERROR;
3543 /***********************************************************************
3547 * Returns TRUE if we in the current situation should send TYPE
3549 static int ftp_need_type(struct connectdata *conn,
3552 return conn->proto.ftpc.transfertype != (ascii_wanted?'A':'I');
3555 /***********************************************************************
3559 * Set TYPE. We only deal with ASCII or BINARY so this function
3561 * If the transfer type is not sent, simulate on OK response in newstate
3563 static CURLcode ftp_nb_type(struct connectdata *conn,
3564 bool ascii, ftpstate newstate)
3566 struct ftp_conn *ftpc = &conn->proto.ftpc;
3568 char want = (char)(ascii?'A':'I');
3570 if(ftpc->transfertype == want) {
3571 state(conn, newstate);
3572 return ftp_state_type_resp(conn, 200, newstate);
3575 PPSENDF(&ftpc->pp, "TYPE %c", want);
3576 state(conn, newstate);
3578 /* keep track of our current transfer type */
3579 ftpc->transfertype = want;
3583 /***************************************************************************
3585 * ftp_pasv_verbose()
3587 * This function only outputs some informationals about this second connection
3588 * when we've issued a PASV command before and thus we have connected to a
3589 * possibly new IP address.
3592 #ifndef CURL_DISABLE_VERBOSE_STRINGS
3594 ftp_pasv_verbose(struct connectdata *conn,
3596 char *newhost, /* ascii version */
3600 Curl_printable_address(ai, buf, sizeof(buf));
3601 infof(conn->data, "Connecting to %s (%s) port %d\n", newhost, buf, port);
3606 Check if this is a range download, and if so, set the internal variables
3610 static CURLcode ftp_range(struct connectdata *conn)
3612 curl_off_t from, to;
3615 struct SessionHandle *data = conn->data;
3616 struct ftp_conn *ftpc = &conn->proto.ftpc;
3618 if(data->state.use_range && data->state.range) {
3619 from=curlx_strtoofft(data->state.range, &ptr, 0);
3620 while(*ptr && (ISSPACE(*ptr) || (*ptr=='-')))
3622 to=curlx_strtoofft(ptr, &ptr2, 0);
3624 /* we didn't get any digit */
3627 if((-1 == to) && (from>=0)) {
3629 data->state.resume_from = from;
3630 DEBUGF(infof(conn->data, "FTP RANGE %" FORMAT_OFF_T " to end of file\n",
3635 data->req.maxdownload = -from;
3636 data->state.resume_from = from;
3637 DEBUGF(infof(conn->data, "FTP RANGE the last %" FORMAT_OFF_T " bytes\n",
3642 data->req.maxdownload = (to-from)+1; /* include last byte */
3643 data->state.resume_from = from;
3644 DEBUGF(infof(conn->data, "FTP RANGE from %" FORMAT_OFF_T
3645 " getting %" FORMAT_OFF_T " bytes\n",
3646 from, data->req.maxdownload));
3648 DEBUGF(infof(conn->data, "range-download from %" FORMAT_OFF_T
3649 " to %" FORMAT_OFF_T ", totally %" FORMAT_OFF_T " bytes\n",
3650 from, to, data->req.maxdownload));
3651 ftpc->dont_check = TRUE; /* dont check for successful transfer */
3654 data->req.maxdownload = -1;
3662 * This function shall be called when the second FTP (data) connection is
3666 static CURLcode ftp_do_more(struct connectdata *conn, bool *complete)
3668 struct SessionHandle *data=conn->data;
3669 struct ftp_conn *ftpc = &conn->proto.ftpc;
3670 CURLcode result = CURLE_OK;
3671 bool connected = FALSE;
3673 /* the ftp struct is inited in ftp_connect() */
3674 struct FTP *ftp = data->state.proto.ftp;
3678 /* if the second connection isn't done yet, wait for it */
3679 if(!conn->bits.tcpconnect[SECONDARYSOCKET]) {
3680 result = Curl_is_connected(conn, SECONDARYSOCKET, &connected);
3682 /* Ready to do more? */
3684 DEBUGF(infof(data, "DO-MORE connected phase starts\n"));
3690 if((data->state.used_interface == Curl_if_multi) &&
3692 /* multi interface and already in a state so skip the intial commands.
3693 They are only done to kickstart the do_more state */
3694 result = ftp_multi_statemach(conn, complete);
3696 /* if we got an error or if we don't wait for a data connection return
3698 if(result || (ftpc->wait_data_conn != TRUE))
3702 if(ftp->transfer <= FTPTRANSFER_INFO) {
3703 /* a transfer is about to take place, or if not a file name was given
3704 so we'll do a SIZE on it later and then we need the right TYPE first */
3706 if(ftpc->wait_data_conn == TRUE) {
3709 result = ReceivedServerConnect(conn, &serv_conned);
3711 return result; /* Failed to accept data connection */
3714 /* It looks data connection is established */
3715 result = AcceptServerConnect(conn);
3716 ftpc->wait_data_conn = FALSE;
3718 result = InitiateTransfer(conn);
3724 else if(data->set.upload) {
3725 result = ftp_nb_type(conn, data->set.prefer_ascii, FTP_STOR_TYPE);
3731 ftp->downloadsize = -1; /* unknown as of yet */
3733 result = ftp_range(conn);
3736 else if(data->set.ftp_list_only || !ftpc->file) {
3737 /* The specified path ends with a slash, and therefore we think this
3738 is a directory that is requested, use LIST. But before that we
3739 need to set ASCII transfer mode. */
3741 /* But only if a body transfer was requested. */
3742 if(ftp->transfer == FTPTRANSFER_BODY) {
3743 result = ftp_nb_type(conn, TRUE, FTP_LIST_TYPE);
3747 /* otherwise just fall through */
3750 result = ftp_nb_type(conn, data->set.prefer_ascii, FTP_RETR_TYPE);
3755 if(data->state.used_interface == Curl_if_multi) {
3756 result = ftp_multi_statemach(conn, complete);
3761 result = ftp_easy_statemach(conn);
3764 if((result == CURLE_OK) && (ftp->transfer != FTPTRANSFER_BODY))
3765 /* no data to transfer. FIX: it feels like a kludge to have this here
3767 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
3769 if(!ftpc->wait_data_conn) {
3770 /* no waiting for the data connection so this is now complete */
3772 DEBUGF(infof(data, "DO-MORE phase ends with %d\n", (int)result));
3780 /***********************************************************************
3784 * This is the actual DO function for FTP. Get a file/directory according to
3785 * the options previously setup.
3789 CURLcode ftp_perform(struct connectdata *conn,
3790 bool *connected, /* connect status after PASV / PORT */
3793 /* this is FTP and no proxy */
3794 CURLcode result=CURLE_OK;
3796 DEBUGF(infof(conn->data, "DO phase starts\n"));
3798 if(conn->data->set.opt_no_body) {
3799 /* requested no body means no transfer... */
3800 struct FTP *ftp = conn->data->state.proto.ftp;
3801 ftp->transfer = FTPTRANSFER_INFO;
3805 *dophase_done = FALSE; /* not done yet */
3807 /* start the first command in the DO phase */
3808 result = ftp_state_quote(conn, TRUE, FTP_QUOTE);
3812 /* run the state-machine */
3813 if(conn->data->state.used_interface == Curl_if_multi)
3814 result = ftp_multi_statemach(conn, dophase_done);
3816 result = ftp_easy_statemach(conn);
3817 *dophase_done = TRUE; /* with the easy interface we are done here */
3819 *connected = conn->bits.tcpconnect[FIRSTSOCKET];
3822 DEBUGF(infof(conn->data, "DO phase is complete\n"));
3827 static void wc_data_dtor(void *ptr)
3829 struct ftp_wc_tmpdata *tmp = ptr;
3831 Curl_ftp_parselist_data_free(&tmp->parser);
3835 static CURLcode init_wc_data(struct connectdata *conn)
3838 char *path = conn->data->state.path;
3839 struct WildcardData *wildcard = &(conn->data->wildcard);
3840 CURLcode ret = CURLE_OK;
3841 struct ftp_wc_tmpdata *ftp_tmp;
3843 last_slash = strrchr(conn->data->state.path, '/');
3846 if(last_slash[0] == '\0') {
3847 wildcard->state = CURLWC_CLEAN;
3848 ret = ftp_parse_url_path(conn);
3852 wildcard->pattern = strdup(last_slash);
3853 if(!wildcard->pattern)
3854 return CURLE_OUT_OF_MEMORY;
3855 last_slash[0] = '\0'; /* cut file from path */
3858 else { /* there is only 'wildcard pattern' or nothing */
3860 wildcard->pattern = strdup(path);
3861 if(!wildcard->pattern)
3862 return CURLE_OUT_OF_MEMORY;
3865 else { /* only list */
3866 wildcard->state = CURLWC_CLEAN;
3867 ret = ftp_parse_url_path(conn);
3872 /* program continues only if URL is not ending with slash, allocate needed
3873 resources for wildcard transfer */
3875 /* allocate ftp protocol specific temporary wildcard data */
3876 ftp_tmp = calloc(1, sizeof(struct ftp_wc_tmpdata));
3878 Curl_safefree(wildcard->pattern);
3879 return CURLE_OUT_OF_MEMORY;
3882 /* INITIALIZE parselist structure */
3883 ftp_tmp->parser = Curl_ftp_parselist_data_alloc();
3884 if(!ftp_tmp->parser) {
3885 Curl_safefree(wildcard->pattern);
3886 Curl_safefree(ftp_tmp);
3887 return CURLE_OUT_OF_MEMORY;
3890 wildcard->tmp = ftp_tmp; /* put it to the WildcardData tmp pointer */
3891 wildcard->tmp_dtor = wc_data_dtor;
3893 /* wildcard does not support NOCWD option (assert it?) */
3894 if(conn->data->set.ftp_filemethod == FTPFILE_NOCWD)
3895 conn->data->set.ftp_filemethod = FTPFILE_MULTICWD;
3897 /* try to parse ftp url */
3898 ret = ftp_parse_url_path(conn);
3900 Curl_safefree(wildcard->pattern);
3901 wildcard->tmp_dtor(wildcard->tmp);
3902 wildcard->tmp_dtor = ZERO_NULL;
3903 wildcard->tmp = NULL;
3907 wildcard->path = strdup(conn->data->state.path);
3908 if(!wildcard->path) {
3909 Curl_safefree(wildcard->pattern);
3910 wildcard->tmp_dtor(wildcard->tmp);
3911 wildcard->tmp_dtor = ZERO_NULL;
3912 wildcard->tmp = NULL;
3913 return CURLE_OUT_OF_MEMORY;
3916 /* backup old write_function */
3917 ftp_tmp->backup.write_function = conn->data->set.fwrite_func;
3918 /* parsing write function */
3919 conn->data->set.fwrite_func = Curl_ftp_parselist;
3920 /* backup old file descriptor */
3921 ftp_tmp->backup.file_descriptor = conn->data->set.out;
3922 /* let the writefunc callback know what curl pointer is working with */
3923 conn->data->set.out = conn;
3925 infof(conn->data, "Wildcard - Parsing started\n");
3929 /* This is called recursively */
3930 static CURLcode wc_statemach(struct connectdata *conn)
3932 struct WildcardData * const wildcard = &(conn->data->wildcard);
3933 CURLcode ret = CURLE_OK;
3935 switch (wildcard->state) {
3937 ret = init_wc_data(conn);
3938 if(wildcard->state == CURLWC_CLEAN)
3942 wildcard->state = ret ? CURLWC_ERROR : CURLWC_MATCHING;
3945 case CURLWC_MATCHING: {
3946 /* In this state is LIST response successfully parsed, so lets restore
3947 previous WRITEFUNCTION callback and WRITEDATA pointer */
3948 struct ftp_wc_tmpdata *ftp_tmp = wildcard->tmp;
3949 conn->data->set.fwrite_func = ftp_tmp->backup.write_function;
3950 conn->data->set.out = ftp_tmp->backup.file_descriptor;
3951 ftp_tmp->backup.write_function = ZERO_NULL;
3952 ftp_tmp->backup.file_descriptor = NULL;
3953 wildcard->state = CURLWC_DOWNLOADING;
3955 if(Curl_ftp_parselist_geterror(ftp_tmp->parser)) {
3956 /* error found in LIST parsing */
3957 wildcard->state = CURLWC_CLEAN;
3958 return wc_statemach(conn);
3960 else if(wildcard->filelist->size == 0) {
3961 /* no corresponding file */
3962 wildcard->state = CURLWC_CLEAN;
3963 return CURLE_REMOTE_FILE_NOT_FOUND;
3965 return wc_statemach(conn);
3968 case CURLWC_DOWNLOADING: {
3969 /* filelist has at least one file, lets get first one */
3970 struct ftp_conn *ftpc = &conn->proto.ftpc;
3971 struct curl_fileinfo *finfo = wildcard->filelist->head->ptr;
3972 char *tmp_path = malloc(strlen(conn->data->state.path) +
3973 strlen(finfo->filename) + 1);
3975 return CURLE_OUT_OF_MEMORY;
3979 /* make full path to matched file */
3980 strcat(tmp_path, wildcard->path);
3981 strcat(tmp_path, finfo->filename);
3982 /* switch default "state.pathbuffer" and tmp_path, good to see
3983 ftp_parse_url_path function to understand this trick */
3984 Curl_safefree(conn->data->state.pathbuffer);
3985 conn->data->state.pathbuffer = tmp_path;
3986 conn->data->state.path = tmp_path;
3988 infof(conn->data, "Wildcard - START of \"%s\"\n", finfo->filename);
3989 if(conn->data->set.chunk_bgn) {
3990 long userresponse = conn->data->set.chunk_bgn(
3991 finfo, wildcard->customptr, (int)wildcard->filelist->size);
3992 switch(userresponse) {
3993 case CURL_CHUNK_BGN_FUNC_SKIP:
3994 infof(conn->data, "Wildcard - \"%s\" skipped by user\n",
3996 wildcard->state = CURLWC_SKIP;
3997 return wc_statemach(conn);
3998 case CURL_CHUNK_BGN_FUNC_FAIL:
3999 return CURLE_CHUNK_FAILED;
4003 if(finfo->filetype != CURLFILETYPE_FILE) {
4004 wildcard->state = CURLWC_SKIP;
4005 return wc_statemach(conn);
4008 if(finfo->flags & CURLFINFOFLAG_KNOWN_SIZE)
4009 ftpc->known_filesize = finfo->size;
4011 ret = ftp_parse_url_path(conn);
4016 /* we don't need the Curl_fileinfo of first file anymore */
4017 Curl_llist_remove(wildcard->filelist, wildcard->filelist->head, NULL);
4019 if(wildcard->filelist->size == 0) { /* remains only one file to down. */
4020 wildcard->state = CURLWC_CLEAN;
4021 /* after that will be ftp_do called once again and no transfer
4022 will be done because of CURLWC_CLEAN state */
4028 if(conn->data->set.chunk_end)
4029 conn->data->set.chunk_end(conn->data->wildcard.customptr);
4030 Curl_llist_remove(wildcard->filelist, wildcard->filelist->head, NULL);
4031 wildcard->state = (wildcard->filelist->size == 0) ?
4032 CURLWC_CLEAN : CURLWC_DOWNLOADING;
4033 return wc_statemach(conn);
4036 case CURLWC_CLEAN: {
4037 struct ftp_wc_tmpdata *ftp_tmp = wildcard->tmp;
4040 ret = Curl_ftp_parselist_geterror(ftp_tmp->parser);
4042 wildcard->state = ret ? CURLWC_ERROR : CURLWC_DONE;
4053 /***********************************************************************
4057 * This function is registered as 'curl_do' function. It decodes the path
4058 * parts etc as a wrapper to the actual DO function (ftp_perform).
4060 * The input argument is already checked for validity.
4062 static CURLcode ftp_do(struct connectdata *conn, bool *done)
4064 CURLcode retcode = CURLE_OK;
4065 struct ftp_conn *ftpc = &conn->proto.ftpc;
4067 *done = FALSE; /* default to false */
4068 ftpc->wait_data_conn = FALSE; /* default to no such wait */
4071 Since connections can be re-used between SessionHandles, this might be a
4072 connection already existing but on a fresh SessionHandle struct so we must
4073 make sure we have a good 'struct FTP' to play with. For new connections,
4074 the struct FTP is allocated and setup in the ftp_connect() function.
4076 Curl_reset_reqproto(conn);
4077 retcode = ftp_init(conn);
4081 if(conn->data->set.wildcardmatch) {
4082 retcode = wc_statemach(conn);
4083 if(conn->data->wildcard.state == CURLWC_SKIP ||
4084 conn->data->wildcard.state == CURLWC_DONE) {
4085 /* do not call ftp_regular_transfer */
4088 if(retcode) /* error, loop or skipping the file */
4091 else { /* no wildcard FSM needed */
4092 retcode = ftp_parse_url_path(conn);
4097 retcode = ftp_regular_transfer(conn, done);
4103 CURLcode Curl_ftpsendf(struct connectdata *conn,
4104 const char *fmt, ...)
4106 ssize_t bytes_written;
4107 #define SBUF_SIZE 1024
4111 CURLcode res = CURLE_OK;
4112 #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
4113 enum protection_level data_sec = conn->data_prot;
4118 vsnprintf(s, SBUF_SIZE-3, fmt, ap);
4121 strcat(s, "\r\n"); /* append a trailing CRLF */
4124 write_len = strlen(s);
4126 res = Curl_convert_to_network(conn->data, s, write_len);
4127 /* Curl_convert_to_network calls failf if unsuccessful */
4132 #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
4133 conn->data_prot = PROT_CMD;
4135 res = Curl_write(conn, conn->sock[FIRSTSOCKET], sptr, write_len,
4137 #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
4138 DEBUGASSERT(data_sec > PROT_NONE && data_sec < PROT_LAST);
4139 conn->data_prot = data_sec;
4145 if(conn->data->set.verbose)
4146 Curl_debug(conn->data, CURLINFO_HEADER_OUT,
4147 sptr, (size_t)bytes_written, conn);
4149 if(bytes_written != (ssize_t)write_len) {
4150 write_len -= bytes_written;
4151 sptr += bytes_written;
4160 /***********************************************************************
4164 * This should be called before calling sclose() on an ftp control connection
4165 * (not data connections). We should then wait for the response from the
4166 * server before returning. The calling code should then try to close the
4170 static CURLcode ftp_quit(struct connectdata *conn)
4172 CURLcode result = CURLE_OK;
4174 if(conn->proto.ftpc.ctl_valid) {
4175 result = Curl_pp_sendf(&conn->proto.ftpc.pp, "QUIT", NULL);
4177 failf(conn->data, "Failure sending QUIT command: %s",
4178 curl_easy_strerror(result));
4179 conn->proto.ftpc.ctl_valid = FALSE; /* mark control connection as bad */
4180 conn->bits.close = TRUE; /* mark for connection closure */
4181 state(conn, FTP_STOP);
4185 state(conn, FTP_QUIT);
4187 result = ftp_easy_statemach(conn);
4193 /***********************************************************************
4197 * Disconnect from an FTP server. Cleanup protocol-specific per-connection
4198 * resources. BLOCKING.
4200 static CURLcode ftp_disconnect(struct connectdata *conn, bool dead_connection)
4202 struct ftp_conn *ftpc= &conn->proto.ftpc;
4203 struct pingpong *pp = &ftpc->pp;
4205 /* We cannot send quit unconditionally. If this connection is stale or
4206 bad in any way, sending quit and waiting around here will make the
4207 disconnect wait in vain and cause more problems than we need to.
4209 ftp_quit() will check the state of ftp->ctl_valid. If it's ok it
4210 will try to send the QUIT command, otherwise it will just return.
4213 ftpc->ctl_valid = FALSE;
4215 /* The FTP session may or may not have been allocated/setup at this point! */
4216 (void)ftp_quit(conn); /* ignore errors on the QUIT */
4218 if(ftpc->entrypath) {
4219 struct SessionHandle *data = conn->data;
4220 if(data->state.most_recent_ftp_entrypath == ftpc->entrypath) {
4221 data->state.most_recent_ftp_entrypath = NULL;
4223 free(ftpc->entrypath);
4224 ftpc->entrypath = NULL;
4228 if(ftpc->prevpath) {
4229 free(ftpc->prevpath);
4230 ftpc->prevpath = NULL;
4232 if(ftpc->server_os) {
4233 free(ftpc->server_os);
4234 ftpc->server_os = NULL;
4237 Curl_pp_disconnect(pp);
4239 #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
4246 /***********************************************************************
4248 * ftp_parse_url_path()
4250 * Parse the URL path into separate path components.
4254 CURLcode ftp_parse_url_path(struct connectdata *conn)
4256 struct SessionHandle *data = conn->data;
4257 /* the ftp struct is already inited in ftp_connect() */
4258 struct FTP *ftp = data->state.proto.ftp;
4259 struct ftp_conn *ftpc = &conn->proto.ftpc;
4260 const char *slash_pos; /* position of the first '/' char in curpos */
4261 const char *path_to_use = data->state.path;
4262 const char *cur_pos;
4263 const char *filename = NULL;
4265 cur_pos = path_to_use; /* current position in path. point at the begin
4266 of next path component */
4268 ftpc->ctl_valid = FALSE;
4269 ftpc->cwdfail = FALSE;
4271 switch(data->set.ftp_filemethod) {
4273 /* fastest, but less standard-compliant */
4276 The best time to check whether the path is a file or directory is right
4279 the first condition in the if() right here, is there just in case
4280 someone decides to set path to NULL one day
4282 if(data->state.path &&
4283 data->state.path[0] &&
4284 (data->state.path[strlen(data->state.path) - 1] != '/') )
4285 filename = data->state.path; /* this is a full file path */
4287 ftpc->file is not used anywhere other than for operations on a file.
4288 In other words, never for directory operations.
4289 So we can safely leave filename as NULL here and use it as a
4290 argument in dir/file decisions.
4294 case FTPFILE_SINGLECWD:
4295 /* get the last slash */
4296 if(!path_to_use[0]) {
4297 /* no dir, no file */
4301 slash_pos=strrchr(cur_pos, '/');
4302 if(slash_pos || !*cur_pos) {
4303 ftpc->dirs = calloc(1, sizeof(ftpc->dirs[0]));
4305 return CURLE_OUT_OF_MEMORY;
4307 ftpc->dirs[0] = curl_easy_unescape(conn->data, slash_pos ? cur_pos : "/",
4309 curlx_sztosi(slash_pos-cur_pos) : 1,
4311 if(!ftpc->dirs[0]) {
4313 return CURLE_OUT_OF_MEMORY;
4315 ftpc->dirdepth = 1; /* we consider it to be a single dir */
4316 filename = slash_pos ? slash_pos+1 : cur_pos; /* rest is file name */
4319 filename = cur_pos; /* this is a file name only */
4322 default: /* allow pretty much anything */
4323 case FTPFILE_MULTICWD:
4325 ftpc->diralloc = 5; /* default dir depth to allocate */
4326 ftpc->dirs = calloc(ftpc->diralloc, sizeof(ftpc->dirs[0]));
4328 return CURLE_OUT_OF_MEMORY;
4330 /* we have a special case for listing the root dir only */
4331 if(strequal(path_to_use, "/")) {
4332 cur_pos++; /* make it point to the zero byte */
4333 ftpc->dirs[0] = strdup("/");
4337 /* parse the URL path into separate path components */
4338 while((slash_pos = strchr(cur_pos, '/')) != NULL) {
4339 /* 1 or 0 pointer offset to indicate absolute directory */
4340 ssize_t absolute_dir = ((cur_pos - data->state.path > 0) &&
4341 (ftpc->dirdepth == 0))?1:0;
4343 /* seek out the next path component */
4344 if(slash_pos-cur_pos) {
4345 /* we skip empty path components, like "x//y" since the FTP command
4346 CWD requires a parameter and a non-existent parameter a) doesn't
4347 work on many servers and b) has no effect on the others. */
4348 int len = curlx_sztosi(slash_pos - cur_pos + absolute_dir);
4349 ftpc->dirs[ftpc->dirdepth] =
4350 curl_easy_unescape(conn->data, cur_pos - absolute_dir, len, NULL);
4351 if(!ftpc->dirs[ftpc->dirdepth]) { /* run out of memory ... */
4352 failf(data, "no memory");
4354 return CURLE_OUT_OF_MEMORY;
4356 if(isBadFtpString(ftpc->dirs[ftpc->dirdepth])) {
4357 free(ftpc->dirs[ftpc->dirdepth]);
4359 return CURLE_URL_MALFORMAT;
4363 cur_pos = slash_pos + 1; /* jump to the rest of the string */
4367 cur_pos = slash_pos + 1; /* jump to the rest of the string */
4368 if(++ftpc->dirdepth >= ftpc->diralloc) {
4371 ftpc->diralloc *= 2; /* double the size each time */
4372 bigger = realloc(ftpc->dirs, ftpc->diralloc * sizeof(ftpc->dirs[0]));
4375 return CURLE_OUT_OF_MEMORY;
4377 ftpc->dirs = bigger;
4381 filename = cur_pos; /* the rest is the file name */
4385 if(filename && *filename) {
4386 ftpc->file = curl_easy_unescape(conn->data, filename, 0, NULL);
4387 if(NULL == ftpc->file) {
4389 failf(data, "no memory");
4390 return CURLE_OUT_OF_MEMORY;
4392 if(isBadFtpString(ftpc->file)) {
4394 return CURLE_URL_MALFORMAT;
4398 ftpc->file=NULL; /* instead of point to a zero byte, we make it a NULL
4401 if(data->set.upload && !ftpc->file && (ftp->transfer == FTPTRANSFER_BODY)) {
4402 /* We need a file name when uploading. Return error! */
4403 failf(data, "Uploading to a URL without a file name!");
4404 return CURLE_URL_MALFORMAT;
4407 ftpc->cwddone = FALSE; /* default to not done */
4409 if(ftpc->prevpath) {
4410 /* prevpath is "raw" so we convert the input path before we compare the
4413 char *path = curl_easy_unescape(conn->data, data->state.path, 0, &dlen);
4416 return CURLE_OUT_OF_MEMORY;
4419 dlen -= ftpc->file?curlx_uztosi(strlen(ftpc->file)):0;
4420 if((dlen == curlx_uztosi(strlen(ftpc->prevpath))) &&
4421 strnequal(path, ftpc->prevpath, dlen)) {
4422 infof(data, "Request has same path as previous transfer\n");
4423 ftpc->cwddone = TRUE;
4431 /* call this when the DO phase has completed */
4432 static CURLcode ftp_dophase_done(struct connectdata *conn,
4435 struct FTP *ftp = conn->data->state.proto.ftp;
4436 struct ftp_conn *ftpc = &conn->proto.ftpc;
4440 CURLcode result = ftp_do_more(conn, &completed);
4443 if(conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD) {
4444 /* close the second socket if it was created already */
4445 Curl_closesocket(conn, conn->sock[SECONDARYSOCKET]);
4446 conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
4452 if(ftp->transfer != FTPTRANSFER_BODY)
4453 /* no data to transfer */
4454 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
4456 /* since we didn't connect now, we want do_more to get called */
4457 conn->bits.do_more = TRUE;
4459 ftpc->ctl_valid = TRUE; /* seems good */
4464 /* called from multi.c while DOing */
4465 static CURLcode ftp_doing(struct connectdata *conn,
4468 CURLcode result = ftp_multi_statemach(conn, dophase_done);
4471 DEBUGF(infof(conn->data, "DO phase failed\n"));
4472 else if(*dophase_done) {
4473 result = ftp_dophase_done(conn, FALSE /* not connected */);
4475 DEBUGF(infof(conn->data, "DO phase is complete\n"));
4480 /***********************************************************************
4482 * ftp_regular_transfer()
4484 * The input argument is already checked for validity.
4486 * Performs all commands done before a regular transfer between a local and a
4489 * ftp->ctl_valid starts out as FALSE, and gets set to TRUE if we reach the
4490 * ftp_done() function without finding any major problem.
4493 CURLcode ftp_regular_transfer(struct connectdata *conn,
4496 CURLcode result=CURLE_OK;
4497 bool connected=FALSE;
4498 struct SessionHandle *data = conn->data;
4499 struct ftp_conn *ftpc = &conn->proto.ftpc;
4500 data->req.size = -1; /* make sure this is unknown at this point */
4502 Curl_pgrsSetUploadCounter(data, 0);
4503 Curl_pgrsSetDownloadCounter(data, 0);
4504 Curl_pgrsSetUploadSize(data, 0);
4505 Curl_pgrsSetDownloadSize(data, 0);
4507 ftpc->ctl_valid = TRUE; /* starts good */
4509 result = ftp_perform(conn,
4510 &connected, /* have we connected after PASV/PORT */
4511 dophase_done); /* all commands in the DO-phase done? */
4513 if(CURLE_OK == result) {
4516 /* the DO phase has not completed yet */
4519 result = ftp_dophase_done(conn, connected);
4529 static CURLcode ftp_setup_connection(struct connectdata * conn)
4531 struct SessionHandle *data = conn->data;
4535 if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
4536 /* Unless we have asked to tunnel ftp operations through the proxy, we
4537 switch and use HTTP operations only */
4538 #ifndef CURL_DISABLE_HTTP
4539 if(conn->handler == &Curl_handler_ftp)
4540 conn->handler = &Curl_handler_ftp_proxy;
4543 conn->handler = &Curl_handler_ftps_proxy;
4545 failf(data, "FTPS not supported!");
4546 return CURLE_UNSUPPORTED_PROTOCOL;
4550 * We explicitly mark this connection as persistent here as we're doing
4551 * FTP over HTTP and thus we accidentally avoid setting this value
4554 conn->bits.close = FALSE;
4556 failf(data, "FTP over http proxy requires HTTP support built-in!");
4557 return CURLE_UNSUPPORTED_PROTOCOL;
4561 data->state.path++; /* don't include the initial slash */
4562 data->state.slash_removed = TRUE; /* we've skipped the slash */
4564 /* FTP URLs support an extension like ";type=<typecode>" that
4565 * we'll try to get now! */
4566 type = strstr(data->state.path, ";type=");
4569 type = strstr(conn->host.rawalloc, ";type=");
4572 *type = 0; /* it was in the middle of the hostname */
4573 command = Curl_raw_toupper(type[6]);
4574 conn->bits.type_set = TRUE;
4577 case 'A': /* ASCII mode */
4578 data->set.prefer_ascii = TRUE;
4581 case 'D': /* directory mode */
4582 data->set.ftp_list_only = TRUE;
4585 case 'I': /* binary mode */
4587 /* switch off ASCII */
4588 data->set.prefer_ascii = FALSE;
4596 #endif /* CURL_DISABLE_FTP */