1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at http://curl.haxx.se/docs/copyright.html.
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ***************************************************************************/
25 #ifndef CURL_DISABLE_FTP
31 #ifdef HAVE_SYS_SOCKET_H
32 #include <sys/socket.h>
34 #ifdef HAVE_NETINET_IN_H
35 #include <netinet/in.h>
37 #ifdef HAVE_ARPA_INET_H
38 #include <arpa/inet.h>
41 #include <sys/utsname.h>
51 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
53 #define in_addr_t unsigned long
56 #include <curl/curl.h>
64 #include "http.h" /* for HTTP proxy tunnel stuff */
68 #include "ftplistparser.h"
70 #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
74 #include "strtoofft.h"
79 #include "inet_ntop.h"
80 #include "inet_pton.h"
82 #include "parsedate.h" /* for the week day and month names */
83 #include "sockaddr.h" /* required for Curl_sockaddr_storage */
87 #include "speedcheck.h"
89 #include "http_proxy.h"
90 #include "non-ascii.h"
92 #define _MPRINTF_REPLACE /* use our functions only */
93 #include <curl/mprintf.h>
95 #include "curl_memory.h"
96 /* The last #include file should be: */
100 #define NI_MAXHOST 1025
102 #ifndef INET_ADDRSTRLEN
103 #define INET_ADDRSTRLEN 16
106 #ifdef CURL_DISABLE_VERBOSE_STRINGS
107 #define ftp_pasv_verbose(a,b,c,d) Curl_nop_stmt
110 /* Local API functions */
111 static void state(struct connectdata *conn,
113 static CURLcode ftp_sendquote(struct connectdata *conn,
114 struct curl_slist *quote);
115 static CURLcode ftp_quit(struct connectdata *conn);
116 static CURLcode ftp_parse_url_path(struct connectdata *conn);
117 static CURLcode ftp_regular_transfer(struct connectdata *conn, bool *done);
118 #ifndef CURL_DISABLE_VERBOSE_STRINGS
119 static void ftp_pasv_verbose(struct connectdata *conn,
121 char *newhost, /* ascii version */
124 static CURLcode ftp_state_post_rest(struct connectdata *conn);
125 static CURLcode ftp_state_post_cwd(struct connectdata *conn);
126 static CURLcode ftp_state_quote(struct connectdata *conn,
127 bool init, ftpstate instate);
128 static CURLcode ftp_nb_type(struct connectdata *conn,
129 bool ascii, ftpstate newstate);
130 static int ftp_need_type(struct connectdata *conn,
132 static CURLcode ftp_do(struct connectdata *conn, bool *done);
133 static CURLcode ftp_done(struct connectdata *conn,
134 CURLcode, bool premature);
135 static CURLcode ftp_connect(struct connectdata *conn, bool *done);
136 static CURLcode ftp_disconnect(struct connectdata *conn, bool dead_connection);
137 static CURLcode ftp_nextconnect(struct connectdata *conn);
138 static CURLcode ftp_multi_statemach(struct connectdata *conn, bool *done);
139 static int ftp_getsock(struct connectdata *conn, curl_socket_t *socks,
141 static int ftp_domore_getsock(struct connectdata *conn, curl_socket_t *socks,
143 static CURLcode ftp_doing(struct connectdata *conn,
145 static CURLcode ftp_setup_connection(struct connectdata * conn);
147 static CURLcode init_wc_data(struct connectdata *conn);
148 static CURLcode wc_statemach(struct connectdata *conn);
150 static void wc_data_dtor(void *ptr);
152 static CURLcode ftp_state_post_retr_size(struct connectdata *conn,
153 curl_off_t filesize);
155 static CURLcode ftp_readresp(curl_socket_t sockfd,
160 /* easy-to-use macro: */
161 #define FTPSENDF(x,y,z) if((result = Curl_ftpsendf(x,y,z)) != CURLE_OK) \
163 #define PPSENDF(x,y,z) if((result = Curl_pp_sendf(x,y,z)) != CURLE_OK) \
168 * FTP protocol handler.
171 const struct Curl_handler Curl_handler_ftp = {
173 ftp_setup_connection, /* setup_connection */
176 ftp_nextconnect, /* do_more */
177 ftp_connect, /* connect_it */
178 ftp_multi_statemach, /* connecting */
179 ftp_doing, /* doing */
180 ftp_getsock, /* proto_getsock */
181 ftp_getsock, /* doing_getsock */
182 ftp_domore_getsock, /* domore_getsock */
183 ZERO_NULL, /* perform_getsock */
184 ftp_disconnect, /* disconnect */
185 ZERO_NULL, /* readwrite */
186 PORT_FTP, /* defport */
187 CURLPROTO_FTP, /* protocol */
188 PROTOPT_DUAL | PROTOPT_CLOSEACTION | PROTOPT_NEEDSPWD
189 | PROTOPT_NOURLQUERY /* flags */
195 * FTPS protocol handler.
198 const struct Curl_handler Curl_handler_ftps = {
200 ftp_setup_connection, /* setup_connection */
203 ftp_nextconnect, /* do_more */
204 ftp_connect, /* connect_it */
205 ftp_multi_statemach, /* connecting */
206 ftp_doing, /* doing */
207 ftp_getsock, /* proto_getsock */
208 ftp_getsock, /* doing_getsock */
209 ftp_domore_getsock, /* domore_getsock */
210 ZERO_NULL, /* perform_getsock */
211 ftp_disconnect, /* disconnect */
212 ZERO_NULL, /* readwrite */
213 PORT_FTPS, /* defport */
214 CURLPROTO_FTP | CURLPROTO_FTPS, /* protocol */
215 PROTOPT_SSL | PROTOPT_DUAL | PROTOPT_CLOSEACTION |
216 PROTOPT_NEEDSPWD | PROTOPT_NOURLQUERY /* flags */
220 #ifndef CURL_DISABLE_HTTP
222 * HTTP-proxyed FTP protocol handler.
225 static const struct Curl_handler Curl_handler_ftp_proxy = {
227 ZERO_NULL, /* setup_connection */
228 Curl_http, /* do_it */
229 Curl_http_done, /* done */
230 ZERO_NULL, /* do_more */
231 ZERO_NULL, /* connect_it */
232 ZERO_NULL, /* connecting */
233 ZERO_NULL, /* doing */
234 ZERO_NULL, /* proto_getsock */
235 ZERO_NULL, /* doing_getsock */
236 ZERO_NULL, /* domore_getsock */
237 ZERO_NULL, /* perform_getsock */
238 ZERO_NULL, /* disconnect */
239 ZERO_NULL, /* readwrite */
240 PORT_FTP, /* defport */
241 CURLPROTO_HTTP, /* protocol */
242 PROTOPT_NONE /* flags */
248 * HTTP-proxyed FTPS protocol handler.
251 static const struct Curl_handler Curl_handler_ftps_proxy = {
253 ZERO_NULL, /* setup_connection */
254 Curl_http, /* do_it */
255 Curl_http_done, /* done */
256 ZERO_NULL, /* do_more */
257 ZERO_NULL, /* connect_it */
258 ZERO_NULL, /* connecting */
259 ZERO_NULL, /* doing */
260 ZERO_NULL, /* proto_getsock */
261 ZERO_NULL, /* doing_getsock */
262 ZERO_NULL, /* domore_getsock */
263 ZERO_NULL, /* perform_getsock */
264 ZERO_NULL, /* disconnect */
265 ZERO_NULL, /* readwrite */
266 PORT_FTPS, /* defport */
267 CURLPROTO_HTTP, /* protocol */
268 PROTOPT_NONE /* flags */
275 * NOTE: back in the old days, we added code in the FTP code that made NOBODY
276 * requests on files respond with headers passed to the client/stdout that
277 * looked like HTTP ones.
279 * This approach is not very elegant, it causes confusion and is error-prone.
280 * It is subject for removal at the next (or at least a future) soname bump.
281 * Until then you can test the effects of the removal by undefining the
282 * following define named CURL_FTP_HTTPSTYLE_HEAD.
284 #define CURL_FTP_HTTPSTYLE_HEAD 1
286 static void freedirs(struct ftp_conn *ftpc)
290 for(i=0; i < ftpc->dirdepth; i++) {
306 /* Returns non-zero if the given string contains CR (\r) or LF (\n),
307 which are not allowed within RFC 959 <string>.
308 Note: The input string is in the client's encoding which might
309 not be ASCII, so escape sequences \r & \n must be used instead
310 of hex values 0x0d & 0x0a.
312 static bool isBadFtpString(const char *string)
314 return ((NULL != strchr(string, '\r')) ||
315 (NULL != strchr(string, '\n'))) ? TRUE : FALSE;
318 /***********************************************************************
320 * AcceptServerConnect()
322 * After connection request is received from the server this function is
323 * called to accept the connection and close the listening socket
326 static CURLcode AcceptServerConnect(struct connectdata *conn)
328 struct SessionHandle *data = conn->data;
329 curl_socket_t sock = conn->sock[SECONDARYSOCKET];
330 curl_socket_t s = CURL_SOCKET_BAD;
332 struct Curl_sockaddr_storage add;
334 struct sockaddr_in add;
336 curl_socklen_t size = (curl_socklen_t) sizeof(add);
338 if(0 == getsockname(sock, (struct sockaddr *) &add, &size)) {
341 s=accept(sock, (struct sockaddr *) &add, &size);
343 Curl_closesocket(conn, sock); /* close the first socket */
345 if(CURL_SOCKET_BAD == s) {
346 failf(data, "Error accept()ing server connect");
347 return CURLE_FTP_PORT_FAILED;
349 infof(data, "Connection accepted from server\n");
351 conn->sock[SECONDARYSOCKET] = s;
352 curlx_nonblock(s, TRUE); /* enable non-blocking */
353 conn->sock_accepted[SECONDARYSOCKET] = TRUE;
358 /***********************************************************************
360 * ReceivedServerConnect()
362 * After allowing server to connect to us from data port, this function
363 * checks both data connection for connection establishment and ctrl
364 * connection for a negative response regarding a failure in connecting
367 static CURLcode ReceivedServerConnect(struct connectdata* conn, bool* received)
369 struct SessionHandle *data = conn->data;
370 curl_socket_t ctrl_sock = conn->sock[FIRSTSOCKET];
371 curl_socket_t data_sock = conn->sock[SECONDARYSOCKET];
372 struct ftp_conn *ftpc = &conn->proto.ftpc;
373 struct pingpong *pp = &ftpc->pp;
381 timeout_ms = Curl_timeleft_accept(data);
382 infof(data, "Checking for server connect\n");
384 /* if a timeout was already reached, bail out */
385 failf(data, "Accept timeout occurred while waiting server connect");
386 return CURLE_FTP_ACCEPT_TIMEOUT;
389 /* First check whether there is a cached response from server */
390 if(pp->cache_size && pp->cache && pp->cache[0] > '3') {
391 /* Data connection could not be established, let's return */
392 infof(data, "There is negative response in cache while serv connect");
393 Curl_GetFTPResponse(&nread, conn, &ftpcode);
394 return CURLE_FTP_ACCEPT_FAILED;
397 result = Curl_socket_check(ctrl_sock, data_sock, CURL_SOCKET_BAD, 0);
399 /* see if the connection request is already here */
403 failf(data, "Error while waiting for server connect");
404 return CURLE_FTP_ACCEPT_FAILED;
405 case 0: /* Server connect is not received yet */
409 if(result & CURL_CSELECT_IN2) {
410 infof(data, "Ready to accept data connection from server\n");
413 else if(result & CURL_CSELECT_IN) {
414 infof(data, "Ctrl conn has data while waiting for data conn\n");
415 Curl_GetFTPResponse(&nread, conn, &ftpcode);
418 return CURLE_FTP_ACCEPT_FAILED;
420 return CURLE_FTP_WEIRD_SERVER_REPLY;
430 /***********************************************************************
434 * After connection from server is accepted this function is called to
435 * setup transfer parameters and initiate the data transfer.
438 static CURLcode InitiateTransfer(struct connectdata *conn)
440 struct SessionHandle *data = conn->data;
441 struct FTP *ftp = data->state.proto.ftp;
442 CURLcode result = CURLE_OK;
444 if(conn->ssl[SECONDARYSOCKET].use) {
445 /* since we only have a plaintext TCP connection here, we must now
446 * do the TLS stuff */
447 infof(data, "Doing the SSL/TLS handshake on the data stream\n");
448 result = Curl_ssl_connect(conn, SECONDARYSOCKET);
453 if(conn->proto.ftpc.state_saved == FTP_STOR) {
454 *(ftp->bytecountp)=0;
456 /* When we know we're uploading a specified file, we can get the file
457 size prior to the actual upload. */
459 Curl_pgrsSetUploadSize(data, data->set.infilesize);
461 /* set the SO_SNDBUF for the secondary socket for those who need it */
462 Curl_sndbufset(conn->sock[SECONDARYSOCKET]);
464 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, /* no download */
465 SECONDARYSOCKET, ftp->bytecountp);
469 Curl_setup_transfer(conn, SECONDARYSOCKET,
470 conn->proto.ftpc.retr_size_saved, FALSE,
471 ftp->bytecountp, -1, NULL); /* no upload here */
474 conn->proto.ftpc.pp.pending_resp = TRUE; /* expect server response */
475 state(conn, FTP_STOP);
480 /***********************************************************************
482 * AllowServerConnect()
484 * When we've issue the PORT command, we have told the server to connect
485 * to us. This function
486 * - will sit and wait here until the server has connected for easy interface
487 * - will check whether data connection is established if so it is accepted
488 * for multi interface
491 static CURLcode AllowServerConnect(struct connectdata *conn, bool *connected)
493 struct SessionHandle *data = conn->data;
496 CURLcode ret = CURLE_OK;
499 infof(data, "Preparing for accepting server on data port\n");
501 /* Save the time we start accepting server connect */
502 Curl_pgrsTime(data, TIMER_STARTACCEPT);
505 timeout_ms = Curl_timeleft_accept(data);
507 /* if a timeout was already reached, bail out */
508 failf(data, "Accept timeout occurred while waiting server connect");
509 return CURLE_FTP_ACCEPT_TIMEOUT;
512 /* see if the connection request is already here */
513 ret = ReceivedServerConnect(conn, connected);
518 ret = AcceptServerConnect(conn);
522 ret = InitiateTransfer(conn);
526 break; /* connection is accepted, break the loop */
529 if(data->state.used_interface == Curl_if_easy) {
531 if(timeout_ms < interval_ms)
532 interval_ms = timeout_ms;
534 /* sleep for 1 second and then continue */
535 Curl_socket_ready(CURL_SOCKET_BAD, CURL_SOCKET_BAD, interval_ms);
538 /* Add timeout to multi handle and break out of the loop */
539 if(ret == CURLE_OK && *connected == FALSE) {
540 if(data->set.accepttimeout > 0)
541 Curl_expire(data, data->set.accepttimeout);
543 Curl_expire(data, DEFAULT_ACCEPT_TIMEOUT);
546 break; /* connection was not accepted immediately */
554 /* macro to check for a three-digit ftp status code at the start of the
556 #define STATUSCODE(line) (ISDIGIT(line[0]) && ISDIGIT(line[1]) && \
559 /* macro to check for the last line in an FTP server response */
560 #define LASTLINE(line) (STATUSCODE(line) && (' ' == line[3]))
562 static int ftp_endofresp(struct pingpong *pp,
565 char *line = pp->linestart_resp;
566 size_t len = pp->nread_resp;
568 if((len > 3) && LASTLINE(line)) {
569 *code = curlx_sltosi(strtol(line, NULL, 10));
575 static CURLcode ftp_readresp(curl_socket_t sockfd,
577 int *ftpcode, /* return the ftp-code if done */
578 size_t *size) /* size of the response */
580 struct connectdata *conn = pp->conn;
581 #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
582 struct SessionHandle *data = conn->data;
583 char * const buf = data->state.buffer;
585 CURLcode result = CURLE_OK;
588 result = Curl_pp_readresp(sockfd, pp, &code, size);
590 #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
591 /* handle the security-oriented responses 6xx ***/
592 /* FIXME: some errorchecking perhaps... ***/
595 code = Curl_sec_read_msg(conn, buf, PROT_SAFE);
598 code = Curl_sec_read_msg(conn, buf, PROT_PRIVATE);
601 code = Curl_sec_read_msg(conn, buf, PROT_CONFIDENTIAL);
604 /* normal ftp stuff we pass through! */
609 /* store the latest code for later retrieval */
610 conn->data->info.httpcode=code;
616 /* 421 means "Service not available, closing control connection." and FTP
617 * servers use it to signal that idle session timeout has been exceeded.
618 * If we ignored the response, it could end up hanging in some cases. */
619 return CURLE_OPERATION_TIMEDOUT;
624 /* --- parse FTP server responses --- */
627 * Curl_GetFTPResponse() is a BLOCKING function to read the full response
628 * from a server after a command.
632 CURLcode Curl_GetFTPResponse(ssize_t *nreadp, /* return number of bytes read */
633 struct connectdata *conn,
634 int *ftpcode) /* return the ftp-code */
637 * We cannot read just one byte per read() and then go back to select() as
638 * the OpenSSL read() doesn't grok that properly.
640 * Alas, read as much as possible, split up into lines, use the ending
641 * line in a response or continue reading. */
643 curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
644 long timeout; /* timeout in milliseconds */
646 struct SessionHandle *data = conn->data;
647 CURLcode result = CURLE_OK;
648 struct ftp_conn *ftpc = &conn->proto.ftpc;
649 struct pingpong *pp = &ftpc->pp;
652 int value_to_be_ignored=0;
655 *ftpcode = 0; /* 0 for errors */
657 /* make the pointer point to something for the rest of this function */
658 ftpcode = &value_to_be_ignored;
662 while(!*ftpcode && !result) {
663 /* check and reset timeout value every lap */
664 timeout = Curl_pp_state_timeout(pp);
667 failf(data, "FTP response timeout");
668 return CURLE_OPERATION_TIMEDOUT; /* already too little time */
671 interval_ms = 1000; /* use 1 second timeout intervals */
672 if(timeout < interval_ms)
673 interval_ms = timeout;
676 * Since this function is blocking, we need to wait here for input on the
677 * connection and only then we call the response reading function. We do
678 * timeout at least every second to make the timeout check run.
680 * A caution here is that the ftp_readresp() function has a cache that may
681 * contain pieces of a response from the previous invoke and we need to
682 * make sure we don't just wait for input while there is unhandled data in
683 * that cache. But also, if the cache is there, we call ftp_readresp() and
684 * the cache wasn't good enough to continue we must not just busy-loop
685 * around this function.
689 if(pp->cache && (cache_skip < 2)) {
691 * There's a cache left since before. We then skipping the wait for
692 * socket action, unless this is the same cache like the previous round
693 * as then the cache was deemed not enough to act on and we then need to
694 * wait for more data anyway.
698 switch (Curl_socket_ready(sockfd, CURL_SOCKET_BAD, interval_ms)) {
699 case -1: /* select() error, stop reading */
700 failf(data, "FTP response aborted due to select/poll error: %d",
702 return CURLE_RECV_ERROR;
704 case 0: /* timeout */
705 if(Curl_pgrsUpdate(conn))
706 return CURLE_ABORTED_BY_CALLBACK;
707 continue; /* just continue in our loop for the timeout duration */
709 default: /* for clarity */
713 result = ftp_readresp(sockfd, pp, ftpcode, &nread);
717 if(!nread && pp->cache)
718 /* bump cache skip counter as on repeated skips we must wait for more
722 /* when we got data or there is no cache left, we reset the cache skip
728 } /* while there's buffer left and loop is requested */
730 pp->pending_resp = FALSE;
735 /* This is the ONLY way to change FTP state! */
736 static void state(struct connectdata *conn,
739 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
740 /* for debug purposes */
741 static const char * const names[]={
779 struct ftp_conn *ftpc = &conn->proto.ftpc;
780 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
781 if(ftpc->state != newstate)
782 infof(conn->data, "FTP %p state change from %s to %s\n",
783 ftpc, names[ftpc->state], names[newstate]);
785 ftpc->state = newstate;
788 static CURLcode ftp_state_user(struct connectdata *conn)
791 struct FTP *ftp = conn->data->state.proto.ftp;
793 PPSENDF(&conn->proto.ftpc.pp, "USER %s", ftp->user?ftp->user:"");
795 state(conn, FTP_USER);
796 conn->data->state.ftp_trying_alternative = FALSE;
801 static CURLcode ftp_state_pwd(struct connectdata *conn)
805 /* send PWD to discover our entry point */
806 PPSENDF(&conn->proto.ftpc.pp, "PWD", NULL);
807 state(conn, FTP_PWD);
812 /* For the FTP "protocol connect" and "doing" phases only */
813 static int ftp_getsock(struct connectdata *conn,
814 curl_socket_t *socks,
817 return Curl_pp_getsock(&conn->proto.ftpc.pp, socks, numsocks);
820 /* For the FTP "DO_MORE" phase only */
821 static int ftp_domore_getsock(struct connectdata *conn, curl_socket_t *socks,
824 struct ftp_conn *ftpc = &conn->proto.ftpc;
827 return GETSOCK_BLANK;
829 /* When in DO_MORE state, we could be either waiting for us to connect to a
830 remote site, or we could wait for that site to connect to us. Or just
831 handle ordinary commands.
833 When waiting for a connect, we will be in FTP_STOP state and then we wait
834 for the secondary socket to become writeable. If we're in another state,
835 we're still handling commands on the control (primary) connection.
839 switch(ftpc->state) {
843 return Curl_pp_getsock(&conn->proto.ftpc.pp, socks, numsocks);
846 socks[0] = conn->sock[SECONDARYSOCKET];
847 if(conn->bits.wait_data_conn) {
848 socks[1] = conn->sock[FIRSTSOCKET];
849 return GETSOCK_READSOCK(0) | GETSOCK_READSOCK(1);
852 return GETSOCK_READSOCK(0);
855 /* This is called after the FTP_QUOTE state is passed.
857 ftp_state_cwd() sends the range of CWD commands to the server to change to
858 the correct directory. It may also need to send MKD commands to create
859 missing ones, if that option is enabled.
861 static CURLcode ftp_state_cwd(struct connectdata *conn)
863 CURLcode result = CURLE_OK;
864 struct ftp_conn *ftpc = &conn->proto.ftpc;
867 /* already done and fine */
868 result = ftp_state_post_cwd(conn);
870 ftpc->count2 = 0; /* count2 counts failed CWDs */
872 /* count3 is set to allow a MKD to fail once. In the case when first CWD
873 fails and then MKD fails (due to another session raced it to create the
874 dir) this then allows for a second try to CWD to it */
875 ftpc->count3 = (conn->data->set.ftp_create_missing_dirs==2)?1:0;
877 if(conn->bits.reuse && ftpc->entrypath) {
878 /* This is a re-used connection. Since we change directory to where the
879 transfer is taking place, we must first get back to the original dir
880 where we ended up after login: */
881 ftpc->count1 = 0; /* we count this as the first path, then we add one
882 for all upcoming ones in the ftp->dirs[] array */
883 PPSENDF(&conn->proto.ftpc.pp, "CWD %s", ftpc->entrypath);
884 state(conn, FTP_CWD);
889 /* issue the first CWD, the rest is sent when the CWD responses are
891 PPSENDF(&conn->proto.ftpc.pp, "CWD %s", ftpc->dirs[ftpc->count1 -1]);
892 state(conn, FTP_CWD);
895 /* No CWD necessary */
896 result = ftp_state_post_cwd(conn);
909 static CURLcode ftp_state_use_port(struct connectdata *conn,
910 ftpport fcmd) /* start with this */
913 CURLcode result = CURLE_OK;
914 struct ftp_conn *ftpc = &conn->proto.ftpc;
915 struct SessionHandle *data=conn->data;
916 curl_socket_t portsock= CURL_SOCKET_BAD;
917 char myhost[256] = "";
919 struct Curl_sockaddr_storage ss;
920 Curl_addrinfo *res, *ai;
921 curl_socklen_t sslen;
922 char hbuf[NI_MAXHOST];
923 struct sockaddr *sa=(struct sockaddr *)&ss;
924 struct sockaddr_in * const sa4 = (void *)sa;
926 struct sockaddr_in6 * const sa6 = (void *)sa;
929 static const char mode[][5] = { "EPRT", "PORT" };
933 char *string_ftpport = data->set.str[STRING_FTPPORT];
934 struct Curl_dns_entry *h=NULL;
935 unsigned short port_min = 0;
936 unsigned short port_max = 0;
938 bool possibly_non_local = TRUE;
942 /* Step 1, figure out what is requested,
944 * (ipv4|ipv6|domain|interface)?(:port(-range)?)?
947 if(data->set.str[STRING_FTPPORT] &&
948 (strlen(data->set.str[STRING_FTPPORT]) > 1)) {
951 size_t addrlen = INET6_ADDRSTRLEN > strlen(string_ftpport) ?
952 INET6_ADDRSTRLEN : strlen(string_ftpport);
954 size_t addrlen = INET_ADDRSTRLEN > strlen(string_ftpport) ?
955 INET_ADDRSTRLEN : strlen(string_ftpport);
957 char *ip_start = string_ftpport;
959 char *port_start = NULL;
960 char *port_sep = NULL;
962 addr = calloc(addrlen+1, 1);
964 return CURLE_OUT_OF_MEMORY;
967 if(*string_ftpport == '[') {
968 /* [ipv6]:port(-range) */
969 ip_start = string_ftpport + 1;
970 if((ip_end = strchr(string_ftpport, ']')) != NULL )
971 strncpy(addr, ip_start, ip_end - ip_start);
975 if(*string_ftpport == ':') {
977 ip_end = string_ftpport;
979 else if((ip_end = strchr(string_ftpport, ':')) != NULL) {
980 /* either ipv6 or (ipv4|domain|interface):port(-range) */
982 if(Curl_inet_pton(AF_INET6, string_ftpport, sa6) == 1) {
984 port_min = port_max = 0;
985 strcpy(addr, string_ftpport);
986 ip_end = NULL; /* this got no port ! */
990 /* (ipv4|domain|interface):port(-range) */
991 strncpy(addr, string_ftpport, ip_end - ip_start );
995 strcpy(addr, string_ftpport);
999 if((port_start = strchr(ip_end, ':')) != NULL) {
1000 port_min = curlx_ultous(strtoul(port_start+1, NULL, 10));
1001 if((port_sep = strchr(port_start, '-')) != NULL) {
1002 port_max = curlx_ultous(strtoul(port_sep + 1, NULL, 10));
1005 port_max = port_min;
1009 /* correct errors like:
1011 * :-4711 , in this case port_min is (unsigned)-1,
1012 * therefore port_min > port_max for all cases
1013 * but port_max = (unsigned)-1
1015 if(port_min > port_max )
1016 port_min = port_max = 0;
1020 /* attempt to get the address of the given interface name */
1021 if(!Curl_if2ip(conn->ip_addr->ai_family, addr,
1022 hbuf, sizeof(hbuf)))
1023 /* not an interface, use the given string as host name instead */
1026 host = hbuf; /* use the hbuf for host name */
1029 /* there was only a port(-range) given, default the host */
1031 } /* data->set.ftpport */
1034 /* not an interface and not a host name, get default by extracting
1035 the IP from the control connection */
1038 if(getsockname(conn->sock[FIRSTSOCKET], sa, &sslen)) {
1039 failf(data, "getsockname() failed: %s",
1040 Curl_strerror(conn, SOCKERRNO) );
1041 Curl_safefree(addr);
1042 return CURLE_FTP_PORT_FAILED;
1044 switch(sa->sa_family) {
1047 Curl_inet_ntop(sa->sa_family, &sa6->sin6_addr, hbuf, sizeof(hbuf));
1051 Curl_inet_ntop(sa->sa_family, &sa4->sin_addr, hbuf, sizeof(hbuf));
1054 host = hbuf; /* use this host name */
1055 possibly_non_local = FALSE; /* we know it is local now */
1058 /* resolv ip/host to ip */
1059 rc = Curl_resolv(conn, host, 0, &h);
1060 if(rc == CURLRESOLV_PENDING)
1061 (void)Curl_resolver_wait_resolv(conn, &h);
1064 /* when we return from this function, we can forget about this entry
1065 to we can unlock it now already */
1066 Curl_resolv_unlock(data, h);
1069 res = NULL; /* failure! */
1072 failf(data, "failed to resolve the address provided to PORT: %s", host);
1073 Curl_safefree(addr);
1074 return CURLE_FTP_PORT_FAILED;
1077 Curl_safefree(addr);
1080 /* step 2, create a socket for the requested address */
1082 portsock = CURL_SOCKET_BAD;
1084 for(ai = res; ai; ai = ai->ai_next) {
1085 result = Curl_socket(conn, ai, NULL, &portsock);
1093 failf(data, "socket failure: %s", Curl_strerror(conn, error));
1094 return CURLE_FTP_PORT_FAILED;
1097 /* step 3, bind to a suitable local address */
1099 memcpy(sa, ai->ai_addr, ai->ai_addrlen);
1100 sslen = ai->ai_addrlen;
1102 for(port = port_min; port <= port_max;) {
1103 if(sa->sa_family == AF_INET)
1104 sa4->sin_port = htons(port);
1107 sa6->sin6_port = htons(port);
1109 /* Try binding the given address. */
1110 if(bind(portsock, sa, sslen) ) {
1113 if(possibly_non_local && (error == EADDRNOTAVAIL)) {
1114 /* The requested bind address is not local. Use the address used for
1115 * the control connection instead and restart the port loop
1118 infof(data, "bind(port=%hu) on non-local address failed: %s", port,
1119 Curl_strerror(conn, error) );
1122 if(getsockname(conn->sock[FIRSTSOCKET], sa, &sslen)) {
1123 failf(data, "getsockname() failed: %s",
1124 Curl_strerror(conn, SOCKERRNO) );
1125 Curl_closesocket(conn, portsock);
1126 return CURLE_FTP_PORT_FAILED;
1129 possibly_non_local = FALSE; /* don't try this again */
1132 else if(error != EADDRINUSE && error != EACCES) {
1133 failf(data, "bind(port=%hu) failed: %s", port,
1134 Curl_strerror(conn, error) );
1135 Curl_closesocket(conn, portsock);
1136 return CURLE_FTP_PORT_FAILED;
1145 /* maybe all ports were in use already*/
1146 if(port > port_max) {
1147 failf(data, "bind() failed, we ran out of ports!");
1148 Curl_closesocket(conn, portsock);
1149 return CURLE_FTP_PORT_FAILED;
1152 /* get the name again after the bind() so that we can extract the
1153 port number it uses now */
1155 if(getsockname(portsock, (struct sockaddr *)sa, &sslen)) {
1156 failf(data, "getsockname() failed: %s",
1157 Curl_strerror(conn, SOCKERRNO) );
1158 Curl_closesocket(conn, portsock);
1159 return CURLE_FTP_PORT_FAILED;
1162 /* step 4, listen on the socket */
1164 if(listen(portsock, 1)) {
1165 failf(data, "socket failure: %s", Curl_strerror(conn, SOCKERRNO));
1166 Curl_closesocket(conn, portsock);
1167 return CURLE_FTP_PORT_FAILED;
1170 /* step 5, send the proper FTP command */
1172 /* get a plain printable version of the numerical address to work with
1174 Curl_printable_address(ai, myhost, sizeof(myhost));
1177 if(!conn->bits.ftp_use_eprt && conn->bits.ipv6)
1178 /* EPRT is disabled but we are connected to a IPv6 host, so we ignore the
1179 request and enable EPRT again! */
1180 conn->bits.ftp_use_eprt = TRUE;
1183 for(; fcmd != DONE; fcmd++) {
1185 if(!conn->bits.ftp_use_eprt && (EPRT == fcmd))
1186 /* if disabled, goto next */
1189 if((PORT == fcmd) && sa->sa_family != AF_INET)
1190 /* PORT is ipv4 only */
1193 switch (sa->sa_family) {
1195 port = ntohs(sa4->sin_port);
1199 port = ntohs(sa6->sin6_port);
1203 continue; /* might as well skip this */
1208 * Two fine examples from RFC2428;
1210 * EPRT |1|132.235.1.2|6275|
1212 * EPRT |2|1080::8:800:200C:417A|5282|
1215 result = Curl_pp_sendf(&ftpc->pp, "%s |%d|%s|%hu|", mode[fcmd],
1216 sa->sa_family == AF_INET?1:2,
1219 failf(data, "Failure sending EPRT command: %s",
1220 curl_easy_strerror(result));
1221 Curl_closesocket(conn, portsock);
1222 /* don't retry using PORT */
1223 ftpc->count1 = PORT;
1225 state(conn, FTP_STOP);
1230 else if(PORT == fcmd) {
1231 char *source = myhost;
1234 /* translate x.x.x.x to x,x,x,x */
1235 while(source && *source) {
1244 snprintf(dest, 20, ",%d,%d", (int)(port>>8), (int)(port&0xff));
1246 result = Curl_pp_sendf(&ftpc->pp, "%s %s", mode[fcmd], tmp);
1248 failf(data, "Failure sending PORT command: %s",
1249 curl_easy_strerror(result));
1250 Curl_closesocket(conn, portsock);
1252 state(conn, FTP_STOP);
1259 /* store which command was sent */
1260 ftpc->count1 = fcmd;
1262 /* we set the secondary socket variable to this for now, it is only so that
1263 the cleanup function will close it in case we fail before the true
1264 secondary stuff is made */
1265 if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET])
1266 Curl_closesocket(conn, conn->sock[SECONDARYSOCKET]);
1267 conn->sock[SECONDARYSOCKET] = portsock;
1269 /* this tcpconnect assignment below is a hackish work-around to make the
1270 multi interface with active FTP work - as it will not wait for a
1271 (passive) connect in Curl_is_connected().
1273 The *proper* fix is to make sure that the active connection from the
1274 server is done in a non-blocking way. Currently, it is still BLOCKING.
1276 conn->bits.tcpconnect[SECONDARYSOCKET] = TRUE;
1278 state(conn, FTP_PORT);
1282 static CURLcode ftp_state_use_pasv(struct connectdata *conn)
1284 struct ftp_conn *ftpc = &conn->proto.ftpc;
1285 CURLcode result = CURLE_OK;
1287 Here's the excecutive summary on what to do:
1289 PASV is RFC959, expect:
1290 227 Entering Passive Mode (a1,a2,a3,a4,p1,p2)
1292 LPSV is RFC1639, expect:
1293 228 Entering Long Passive Mode (4,4,a1,a2,a3,a4,2,p1,p2)
1295 EPSV is RFC2428, expect:
1296 229 Entering Extended Passive Mode (|||port|)
1300 static const char mode[][5] = { "EPSV", "PASV" };
1304 if(!conn->bits.ftp_use_epsv && conn->bits.ipv6)
1305 /* EPSV is disabled but we are connected to a IPv6 host, so we ignore the
1306 request and enable EPSV again! */
1307 conn->bits.ftp_use_epsv = TRUE;
1310 modeoff = conn->bits.ftp_use_epsv?0:1;
1312 PPSENDF(&ftpc->pp, "%s", mode[modeoff]);
1314 ftpc->count1 = modeoff;
1315 state(conn, FTP_PASV);
1316 infof(conn->data, "Connect data stream passively\n");
1321 /* REST is the last command in the chain of commands when a "head"-like
1322 request is made. Thus, if an actual transfer is to be made this is where
1323 we take off for real. */
1324 static CURLcode ftp_state_post_rest(struct connectdata *conn)
1326 CURLcode result = CURLE_OK;
1327 struct FTP *ftp = conn->data->state.proto.ftp;
1328 struct SessionHandle *data = conn->data;
1330 if(ftp->transfer != FTPTRANSFER_BODY) {
1331 /* doesn't transfer any data */
1333 /* still possibly do PRE QUOTE jobs */
1334 state(conn, FTP_RETR_PREQUOTE);
1335 result = ftp_state_quote(conn, TRUE, FTP_RETR_PREQUOTE);
1337 else if(data->set.ftp_use_port) {
1338 /* We have chosen to use the PORT (or similar) command */
1339 result = ftp_state_use_port(conn, EPRT);
1342 /* We have chosen (this is default) to use the PASV (or similar) command */
1343 if(data->set.ftp_use_pret) {
1344 /* The user has requested that we send a PRET command
1345 to prepare the server for the upcoming PASV */
1346 if(!conn->proto.ftpc.file) {
1347 PPSENDF(&conn->proto.ftpc.pp, "PRET %s",
1348 data->set.str[STRING_CUSTOMREQUEST]?
1349 data->set.str[STRING_CUSTOMREQUEST]:
1350 (data->set.ftp_list_only?"NLST":"LIST"));
1352 else if(data->set.upload) {
1353 PPSENDF(&conn->proto.ftpc.pp, "PRET STOR %s", conn->proto.ftpc.file);
1356 PPSENDF(&conn->proto.ftpc.pp, "PRET RETR %s", conn->proto.ftpc.file);
1358 state(conn, FTP_PRET);
1361 result = ftp_state_use_pasv(conn);
1367 static CURLcode ftp_state_post_size(struct connectdata *conn)
1369 CURLcode result = CURLE_OK;
1370 struct FTP *ftp = conn->data->state.proto.ftp;
1371 struct ftp_conn *ftpc = &conn->proto.ftpc;
1373 if((ftp->transfer != FTPTRANSFER_BODY) && ftpc->file) {
1374 /* if a "head"-like request is being made (on a file) */
1376 /* Determine if server can respond to REST command and therefore
1377 whether it supports range */
1378 PPSENDF(&conn->proto.ftpc.pp, "REST %d", 0);
1380 state(conn, FTP_REST);
1383 result = ftp_state_post_rest(conn);
1388 static CURLcode ftp_state_post_type(struct connectdata *conn)
1390 CURLcode result = CURLE_OK;
1391 struct FTP *ftp = conn->data->state.proto.ftp;
1392 struct ftp_conn *ftpc = &conn->proto.ftpc;
1394 if((ftp->transfer == FTPTRANSFER_INFO) && ftpc->file) {
1395 /* if a "head"-like request is being made (on a file) */
1397 /* we know ftpc->file is a valid pointer to a file name */
1398 PPSENDF(&ftpc->pp, "SIZE %s", ftpc->file);
1400 state(conn, FTP_SIZE);
1403 result = ftp_state_post_size(conn);
1408 static CURLcode ftp_state_post_listtype(struct connectdata *conn)
1410 CURLcode result = CURLE_OK;
1411 struct SessionHandle *data = conn->data;
1413 /* If this output is to be machine-parsed, the NLST command might be better
1414 to use, since the LIST command output is not specified or standard in any
1415 way. It has turned out that the NLST list output is not the same on all
1416 servers either... */
1419 if FTPFILE_NOCWD was specified, we are currently in
1420 the user's home directory, so we should add the path
1421 as argument for the LIST / NLST / or custom command.
1422 Whether the server will support this, is uncertain.
1424 The other ftp_filemethods will CWD into dir/dir/ first and
1425 then just do LIST (in that case: nothing to do here)
1427 char *cmd,*lstArg,*slashPos;
1430 if((data->set.ftp_filemethod == FTPFILE_NOCWD) &&
1432 data->state.path[0] &&
1433 strchr(data->state.path,'/')) {
1435 lstArg = strdup(data->state.path);
1437 return CURLE_OUT_OF_MEMORY;
1439 /* Check if path does not end with /, as then we cut off the file part */
1440 if(lstArg[strlen(lstArg) - 1] != '/') {
1442 /* chop off the file part if format is dir/dir/file */
1443 slashPos = strrchr(lstArg,'/');
1445 *(slashPos+1) = '\0';
1449 cmd = aprintf( "%s%s%s",
1450 data->set.str[STRING_CUSTOMREQUEST]?
1451 data->set.str[STRING_CUSTOMREQUEST]:
1452 (data->set.ftp_list_only?"NLST":"LIST"),
1454 lstArg? lstArg: "" );
1459 return CURLE_OUT_OF_MEMORY;
1462 result = Curl_pp_sendf(&conn->proto.ftpc.pp, "%s", cmd);
1469 if(result != CURLE_OK)
1472 state(conn, FTP_LIST);
1477 static CURLcode ftp_state_post_retrtype(struct connectdata *conn)
1479 CURLcode result = CURLE_OK;
1481 /* We've sent the TYPE, now we must send the list of prequote strings */
1483 result = ftp_state_quote(conn, TRUE, FTP_RETR_PREQUOTE);
1488 static CURLcode ftp_state_post_stortype(struct connectdata *conn)
1490 CURLcode result = CURLE_OK;
1492 /* We've sent the TYPE, now we must send the list of prequote strings */
1494 result = ftp_state_quote(conn, TRUE, FTP_STOR_PREQUOTE);
1499 static CURLcode ftp_state_post_mdtm(struct connectdata *conn)
1501 CURLcode result = CURLE_OK;
1502 struct FTP *ftp = conn->data->state.proto.ftp;
1503 struct SessionHandle *data = conn->data;
1504 struct ftp_conn *ftpc = &conn->proto.ftpc;
1506 /* If we have selected NOBODY and HEADER, it means that we only want file
1507 information. Which in FTP can't be much more than the file size and
1509 if(data->set.opt_no_body && ftpc->file &&
1510 ftp_need_type(conn, data->set.prefer_ascii)) {
1511 /* The SIZE command is _not_ RFC 959 specified, and therefor many servers
1512 may not support it! It is however the only way we have to get a file's
1515 ftp->transfer = FTPTRANSFER_INFO;
1516 /* this means no actual transfer will be made */
1518 /* Some servers return different sizes for different modes, and thus we
1519 must set the proper type before we check the size */
1520 result = ftp_nb_type(conn, data->set.prefer_ascii, FTP_TYPE);
1525 result = ftp_state_post_type(conn);
1530 /* This is called after the CWD commands have been done in the beginning of
1532 static CURLcode ftp_state_post_cwd(struct connectdata *conn)
1534 CURLcode result = CURLE_OK;
1535 struct SessionHandle *data = conn->data;
1536 struct ftp_conn *ftpc = &conn->proto.ftpc;
1538 /* Requested time of file or time-depended transfer? */
1539 if((data->set.get_filetime || data->set.timecondition) && ftpc->file) {
1541 /* we have requested to get the modified-time of the file, this is a white
1542 spot as the MDTM is not mentioned in RFC959 */
1543 PPSENDF(&ftpc->pp, "MDTM %s", ftpc->file);
1545 state(conn, FTP_MDTM);
1548 result = ftp_state_post_mdtm(conn);
1554 /* This is called after the TYPE and possible quote commands have been sent */
1555 static CURLcode ftp_state_ul_setup(struct connectdata *conn,
1558 CURLcode result = CURLE_OK;
1559 struct FTP *ftp = conn->data->state.proto.ftp;
1560 struct SessionHandle *data = conn->data;
1561 struct ftp_conn *ftpc = &conn->proto.ftpc;
1562 int seekerr = CURL_SEEKFUNC_OK;
1564 if((data->state.resume_from && !sizechecked) ||
1565 ((data->state.resume_from > 0) && sizechecked)) {
1566 /* we're about to continue the uploading of a file */
1567 /* 1. get already existing file's size. We use the SIZE command for this
1568 which may not exist in the server! The SIZE command is not in
1571 /* 2. This used to set REST. But since we can do append, we
1572 don't another ftp command. We just skip the source file
1573 offset and then we APPEND the rest on the file instead */
1575 /* 3. pass file-size number of bytes in the source file */
1576 /* 4. lower the infilesize counter */
1577 /* => transfer as usual */
1579 if(data->state.resume_from < 0 ) {
1580 /* Got no given size to start from, figure it out */
1581 PPSENDF(&ftpc->pp, "SIZE %s", ftpc->file);
1582 state(conn, FTP_STOR_SIZE);
1587 data->set.ftp_append = TRUE;
1589 /* Let's read off the proper amount of bytes from the input. */
1590 if(conn->seek_func) {
1591 seekerr = conn->seek_func(conn->seek_client, data->state.resume_from,
1595 if(seekerr != CURL_SEEKFUNC_OK) {
1596 if(seekerr != CURL_SEEKFUNC_CANTSEEK) {
1597 failf(data, "Could not seek stream");
1598 return CURLE_FTP_COULDNT_USE_REST;
1600 /* seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */
1602 curl_off_t passed=0;
1604 size_t readthisamountnow =
1605 (data->state.resume_from - passed > CURL_OFF_T_C(BUFSIZE)) ?
1606 BUFSIZE : curlx_sotouz(data->state.resume_from - passed);
1608 size_t actuallyread =
1609 conn->fread_func(data->state.buffer, 1, readthisamountnow,
1612 passed += actuallyread;
1613 if((actuallyread == 0) || (actuallyread > readthisamountnow)) {
1614 /* this checks for greater-than only to make sure that the
1615 CURL_READFUNC_ABORT return code still aborts */
1616 failf(data, "Failed to read data");
1617 return CURLE_FTP_COULDNT_USE_REST;
1619 } while(passed < data->state.resume_from);
1622 /* now, decrease the size of the read */
1623 if(data->set.infilesize>0) {
1624 data->set.infilesize -= data->state.resume_from;
1626 if(data->set.infilesize <= 0) {
1627 infof(data, "File already completely uploaded\n");
1629 /* no data to transfer */
1630 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1632 /* Set ->transfer so that we won't get any error in
1633 * ftp_done() because we didn't transfer anything! */
1634 ftp->transfer = FTPTRANSFER_NONE;
1636 state(conn, FTP_STOP);
1640 /* we've passed, proceed as normal */
1643 PPSENDF(&ftpc->pp, data->set.ftp_append?"APPE %s":"STOR %s",
1646 state(conn, FTP_STOR);
1651 static CURLcode ftp_state_quote(struct connectdata *conn,
1655 CURLcode result = CURLE_OK;
1656 struct SessionHandle *data = conn->data;
1657 struct FTP *ftp = data->state.proto.ftp;
1658 struct ftp_conn *ftpc = &conn->proto.ftpc;
1660 struct curl_slist *item;
1665 item = data->set.quote;
1667 case FTP_RETR_PREQUOTE:
1668 case FTP_STOR_PREQUOTE:
1669 item = data->set.prequote;
1672 item = data->set.postquote;
1678 * 'count1' to iterate over the commands to send
1679 * 'count2' to store wether to allow commands to fail
1690 /* Skip count1 items in the linked list */
1691 while((i< ftpc->count1) && item) {
1696 char *cmd = item->data;
1699 ftpc->count2 = 1; /* the sent command is allowed to fail */
1702 ftpc->count2 = 0; /* failure means cancel operation */
1704 PPSENDF(&ftpc->pp, "%s", cmd);
1705 state(conn, instate);
1711 /* No more quote to send, continue to ... */
1715 result = ftp_state_cwd(conn);
1717 case FTP_RETR_PREQUOTE:
1718 if(ftp->transfer != FTPTRANSFER_BODY)
1719 state(conn, FTP_STOP);
1721 if(ftpc->known_filesize != -1) {
1722 Curl_pgrsSetDownloadSize(data, ftpc->known_filesize);
1723 result = ftp_state_post_retr_size(conn, ftpc->known_filesize);
1726 PPSENDF(&ftpc->pp, "SIZE %s", ftpc->file);
1727 state(conn, FTP_RETR_SIZE);
1731 case FTP_STOR_PREQUOTE:
1732 result = ftp_state_ul_setup(conn, FALSE);
1742 static CURLcode ftp_state_pasv_resp(struct connectdata *conn,
1745 struct ftp_conn *ftpc = &conn->proto.ftpc;
1747 struct SessionHandle *data=conn->data;
1748 Curl_addrinfo *conninfo;
1749 struct Curl_dns_entry *addr=NULL;
1751 unsigned short connectport; /* the local port connect() should use! */
1752 unsigned short newport=0; /* remote port */
1755 /* newhost must be able to hold a full IP-style address in ASCII, which
1756 in the IPv6 case means 5*8-1 = 39 letters */
1757 #define NEWHOST_BUFSIZE 48
1758 char newhost[NEWHOST_BUFSIZE];
1759 char *str=&data->state.buffer[4]; /* start on the first letter */
1761 if((ftpc->count1 == 0) &&
1763 /* positive EPSV response */
1764 char *ptr = strchr(str, '(');
1769 if(5 == sscanf(ptr, "%c%c%c%u%c",
1775 const char sep1 = separator[0];
1778 /* The four separators should be identical, or else this is an oddly
1779 formatted reply and we bail out immediately. */
1780 for(i=1; i<4; i++) {
1781 if(separator[i] != sep1) {
1782 ptr=NULL; /* set to NULL to signal error */
1787 newport = (unsigned short)(num & 0xffff);
1789 if(conn->bits.tunnel_proxy ||
1790 conn->proxytype == CURLPROXY_SOCKS5 ||
1791 conn->proxytype == CURLPROXY_SOCKS5_HOSTNAME ||
1792 conn->proxytype == CURLPROXY_SOCKS4 ||
1793 conn->proxytype == CURLPROXY_SOCKS4A)
1794 /* proxy tunnel -> use other host info because ip_addr_str is the
1795 proxy address not the ftp host */
1796 snprintf(newhost, sizeof(newhost), "%s", conn->host.name);
1798 /* use the same IP we are already connected to */
1799 snprintf(newhost, NEWHOST_BUFSIZE, "%s", conn->ip_addr_str);
1806 failf(data, "Weirdly formatted EPSV reply");
1807 return CURLE_FTP_WEIRD_PASV_REPLY;
1810 else if((ftpc->count1 == 1) &&
1812 /* positive PASV response */
1817 * Scan for a sequence of six comma-separated numbers and use them as
1818 * IP+port indicators.
1820 * Found reply-strings include:
1821 * "227 Entering Passive Mode (127,0,0,1,4,51)"
1822 * "227 Data transfer will passively listen to 127,0,0,1,4,51"
1823 * "227 Entering passive mode. 127,0,0,1,4,51"
1826 if(6 == sscanf(str, "%d,%d,%d,%d,%d,%d",
1827 &ip[0], &ip[1], &ip[2], &ip[3],
1828 &port[0], &port[1]))
1834 failf(data, "Couldn't interpret the 227-response");
1835 return CURLE_FTP_WEIRD_227_FORMAT;
1838 /* we got OK from server */
1839 if(data->set.ftp_skip_ip) {
1840 /* told to ignore the remotely given IP but instead use the one we used
1841 for the control connection */
1842 infof(data, "Skips %d.%d.%d.%d for data connection, uses %s instead\n",
1843 ip[0], ip[1], ip[2], ip[3],
1845 if(conn->bits.tunnel_proxy ||
1846 conn->proxytype == CURLPROXY_SOCKS5 ||
1847 conn->proxytype == CURLPROXY_SOCKS5_HOSTNAME ||
1848 conn->proxytype == CURLPROXY_SOCKS4 ||
1849 conn->proxytype == CURLPROXY_SOCKS4A)
1850 /* proxy tunnel -> use other host info because ip_addr_str is the
1851 proxy address not the ftp host */
1852 snprintf(newhost, sizeof(newhost), "%s", conn->host.name);
1854 snprintf(newhost, sizeof(newhost), "%s", conn->ip_addr_str);
1857 snprintf(newhost, sizeof(newhost),
1858 "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
1859 newport = (unsigned short)(((port[0]<<8) + port[1]) & 0xffff);
1861 else if(ftpc->count1 == 0) {
1862 /* EPSV failed, move on to PASV */
1864 /* disable it for next transfer */
1865 conn->bits.ftp_use_epsv = FALSE;
1866 infof(data, "disabling EPSV usage\n");
1868 PPSENDF(&ftpc->pp, "PASV", NULL);
1870 /* remain in the FTP_PASV state */
1874 failf(data, "Bad PASV/EPSV response: %03d", ftpcode);
1875 return CURLE_FTP_WEIRD_PASV_REPLY;
1878 if(data->set.str[STRING_PROXY] && *data->set.str[STRING_PROXY]) {
1880 * This is a tunnel through a http proxy and we need to connect to the
1883 * We don't want to rely on a former host lookup that might've expired
1884 * now, instead we remake the lookup here and now!
1886 rc = Curl_resolv(conn, conn->proxy.name, (int)conn->port, &addr);
1887 if(rc == CURLRESOLV_PENDING)
1888 /* BLOCKING, ignores the return code but 'addr' will be NULL in
1890 (void)Curl_resolver_wait_resolv(conn, &addr);
1893 (unsigned short)conn->port; /* we connect to the proxy's port */
1896 failf(data, "Can't resolve proxy host %s:%hu",
1897 conn->proxy.name, connectport);
1898 return CURLE_FTP_CANT_GET_HOST;
1902 /* normal, direct, ftp connection */
1903 rc = Curl_resolv(conn, newhost, newport, &addr);
1904 if(rc == CURLRESOLV_PENDING)
1906 (void)Curl_resolver_wait_resolv(conn, &addr);
1908 connectport = newport; /* we connect to the remote port */
1911 failf(data, "Can't resolve new host %s:%hu", newhost, connectport);
1912 return CURLE_FTP_CANT_GET_HOST;
1916 result = Curl_connecthost(conn,
1918 &conn->sock[SECONDARYSOCKET],
1922 Curl_resolv_unlock(data, addr); /* we're done using this address */
1924 if(result && ftpc->count1 == 0 && ftpcode == 229) {
1925 infof(data, "got positive EPSV response, but can't connect. "
1926 "Disabling EPSV\n");
1927 /* disable it for next transfer */
1928 conn->bits.ftp_use_epsv = FALSE;
1929 data->state.errorbuf = FALSE; /* allow error message to get rewritten */
1930 PPSENDF(&ftpc->pp, "PASV", NULL);
1932 /* remain in the FTP_PASV state */
1939 conn->bits.tcpconnect[SECONDARYSOCKET] = connected;
1942 * When this is used from the multi interface, this might've returned with
1943 * the 'connected' set to FALSE and thus we are now awaiting a non-blocking
1944 * connect to connect and we should not be "hanging" here waiting.
1947 if(data->set.verbose)
1948 /* this just dumps information about this second connection */
1949 ftp_pasv_verbose(conn, conninfo, newhost, connectport);
1951 switch(conn->proxytype) {
1952 /* FIX: this MUST wait for a proper connect first if 'connected' is
1954 case CURLPROXY_SOCKS5:
1955 case CURLPROXY_SOCKS5_HOSTNAME:
1956 result = Curl_SOCKS5(conn->proxyuser, conn->proxypasswd, newhost, newport,
1957 SECONDARYSOCKET, conn);
1959 case CURLPROXY_SOCKS4:
1960 result = Curl_SOCKS4(conn->proxyuser, newhost, newport,
1961 SECONDARYSOCKET, conn, FALSE);
1963 case CURLPROXY_SOCKS4A:
1964 result = Curl_SOCKS4(conn->proxyuser, newhost, newport,
1965 SECONDARYSOCKET, conn, TRUE);
1967 case CURLPROXY_HTTP:
1968 case CURLPROXY_HTTP_1_0:
1969 /* do nothing here. handled later. */
1972 failf(data, "unknown proxytype option given");
1973 result = CURLE_COULDNT_CONNECT;
1980 if(conn->bits.tunnel_proxy && conn->bits.httpproxy) {
1981 /* FIX: this MUST wait for a proper connect first if 'connected' is
1985 /* We want "seamless" FTP operations through HTTP proxy tunnel */
1987 /* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the member
1988 * conn->proto.http; we want FTP through HTTP and we have to change the
1989 * member temporarily for connecting to the HTTP proxy. After
1990 * Curl_proxyCONNECT we have to set back the member to the original struct
1993 struct HTTP http_proxy;
1994 struct FTP *ftp_save = data->state.proto.ftp;
1995 memset(&http_proxy, 0, sizeof(http_proxy));
1996 data->state.proto.http = &http_proxy;
1998 result = Curl_proxyCONNECT(conn, SECONDARYSOCKET, newhost, newport);
2000 data->state.proto.ftp = ftp_save;
2006 conn->bits.tcpconnect[SECONDARYSOCKET] = TRUE;
2008 state(conn, FTP_STOP); /* this phase is completed */
2013 static CURLcode ftp_state_port_resp(struct connectdata *conn,
2016 struct SessionHandle *data = conn->data;
2017 struct ftp_conn *ftpc = &conn->proto.ftpc;
2018 ftpport fcmd = (ftpport)ftpc->count1;
2019 CURLcode result = CURLE_OK;
2021 if(ftpcode != 200) {
2022 /* the command failed */
2025 infof(data, "disabling EPRT usage\n");
2026 conn->bits.ftp_use_eprt = FALSE;
2031 failf(data, "Failed to do PORT");
2032 result = CURLE_FTP_PORT_FAILED;
2036 result = ftp_state_use_port(conn, fcmd);
2039 infof(data, "Connect data stream actively\n");
2040 state(conn, FTP_STOP); /* end of DO phase */
2046 static CURLcode ftp_state_mdtm_resp(struct connectdata *conn,
2049 CURLcode result = CURLE_OK;
2050 struct SessionHandle *data=conn->data;
2051 struct FTP *ftp = data->state.proto.ftp;
2052 struct ftp_conn *ftpc = &conn->proto.ftpc;
2057 /* we got a time. Format should be: "YYYYMMDDHHMMSS[.sss]" where the
2058 last .sss part is optional and means fractions of a second */
2059 int year, month, day, hour, minute, second;
2060 char *buf = data->state.buffer;
2061 if(6 == sscanf(buf+4, "%04d%02d%02d%02d%02d%02d",
2062 &year, &month, &day, &hour, &minute, &second)) {
2063 /* we have a time, reformat it */
2064 time_t secs=time(NULL);
2065 /* using the good old yacc/bison yuck */
2066 snprintf(buf, sizeof(conn->data->state.buffer),
2067 "%04d%02d%02d %02d:%02d:%02d GMT",
2068 year, month, day, hour, minute, second);
2069 /* now, convert this into a time() value: */
2070 data->info.filetime = (long)curl_getdate(buf, &secs);
2073 #ifdef CURL_FTP_HTTPSTYLE_HEAD
2074 /* If we asked for a time of the file and we actually got one as well,
2075 we "emulate" a HTTP-style header in our output. */
2077 if(data->set.opt_no_body &&
2079 data->set.get_filetime &&
2080 (data->info.filetime>=0) ) {
2081 time_t filetime = (time_t)data->info.filetime;
2083 const struct tm *tm = &buffer;
2085 result = Curl_gmtime(filetime, &buffer);
2089 /* format: "Tue, 15 Nov 1994 12:45:26" */
2090 snprintf(buf, BUFSIZE-1,
2091 "Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n",
2092 Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
2094 Curl_month[tm->tm_mon],
2099 result = Curl_client_write(conn, CLIENTWRITE_BOTH, buf, 0);
2102 } /* end of a ridiculous amount of conditionals */
2107 infof(data, "unsupported MDTM reply format\n");
2109 case 550: /* "No such file or directory" */
2110 failf(data, "Given file does not exist");
2111 result = CURLE_FTP_COULDNT_RETR_FILE;
2115 if(data->set.timecondition) {
2116 if((data->info.filetime > 0) && (data->set.timevalue > 0)) {
2117 switch(data->set.timecondition) {
2118 case CURL_TIMECOND_IFMODSINCE:
2120 if(data->info.filetime <= data->set.timevalue) {
2121 infof(data, "The requested document is not new enough\n");
2122 ftp->transfer = FTPTRANSFER_NONE; /* mark to not transfer data */
2123 data->info.timecond = TRUE;
2124 state(conn, FTP_STOP);
2128 case CURL_TIMECOND_IFUNMODSINCE:
2129 if(data->info.filetime > data->set.timevalue) {
2130 infof(data, "The requested document is not old enough\n");
2131 ftp->transfer = FTPTRANSFER_NONE; /* mark to not transfer data */
2132 data->info.timecond = TRUE;
2133 state(conn, FTP_STOP);
2140 infof(data, "Skipping time comparison\n");
2145 result = ftp_state_post_mdtm(conn);
2150 static CURLcode ftp_state_type_resp(struct connectdata *conn,
2154 CURLcode result = CURLE_OK;
2155 struct SessionHandle *data=conn->data;
2157 if(ftpcode/100 != 2) {
2158 /* "sasserftpd" and "(u)r(x)bot ftpd" both responds with 226 after a
2159 successful 'TYPE I'. While that is not as RFC959 says, it is still a
2160 positive response code and we allow that. */
2161 failf(data, "Couldn't set desired mode");
2162 return CURLE_FTP_COULDNT_SET_TYPE;
2165 infof(data, "Got a %03d response code instead of the assumed 200\n",
2168 if(instate == FTP_TYPE)
2169 result = ftp_state_post_type(conn);
2170 else if(instate == FTP_LIST_TYPE)
2171 result = ftp_state_post_listtype(conn);
2172 else if(instate == FTP_RETR_TYPE)
2173 result = ftp_state_post_retrtype(conn);
2174 else if(instate == FTP_STOR_TYPE)
2175 result = ftp_state_post_stortype(conn);
2180 static CURLcode ftp_state_post_retr_size(struct connectdata *conn,
2181 curl_off_t filesize)
2183 CURLcode result = CURLE_OK;
2184 struct SessionHandle *data=conn->data;
2185 struct FTP *ftp = data->state.proto.ftp;
2186 struct ftp_conn *ftpc = &conn->proto.ftpc;
2188 if(data->set.max_filesize && (filesize > data->set.max_filesize)) {
2189 failf(data, "Maximum file size exceeded");
2190 return CURLE_FILESIZE_EXCEEDED;
2192 ftp->downloadsize = filesize;
2194 if(data->state.resume_from) {
2195 /* We always (attempt to) get the size of downloads, so it is done before
2196 this even when not doing resumes. */
2197 if(filesize == -1) {
2198 infof(data, "ftp server doesn't support SIZE\n");
2199 /* We couldn't get the size and therefore we can't know if there really
2200 is a part of the file left to get, although the server will just
2201 close the connection when we start the connection so it won't cause
2202 us any harm, just not make us exit as nicely. */
2205 /* We got a file size report, so we check that there actually is a
2206 part of the file left to get, or else we go home. */
2207 if(data->state.resume_from< 0) {
2208 /* We're supposed to download the last abs(from) bytes */
2209 if(filesize < -data->state.resume_from) {
2210 failf(data, "Offset (%" FORMAT_OFF_T
2211 ") was beyond file size (%" FORMAT_OFF_T ")",
2212 data->state.resume_from, filesize);
2213 return CURLE_BAD_DOWNLOAD_RESUME;
2215 /* convert to size to download */
2216 ftp->downloadsize = -data->state.resume_from;
2217 /* download from where? */
2218 data->state.resume_from = filesize - ftp->downloadsize;
2221 if(filesize < data->state.resume_from) {
2222 failf(data, "Offset (%" FORMAT_OFF_T
2223 ") was beyond file size (%" FORMAT_OFF_T ")",
2224 data->state.resume_from, filesize);
2225 return CURLE_BAD_DOWNLOAD_RESUME;
2227 /* Now store the number of bytes we are expected to download */
2228 ftp->downloadsize = filesize-data->state.resume_from;
2232 if(ftp->downloadsize == 0) {
2233 /* no data to transfer */
2234 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
2235 infof(data, "File already completely downloaded\n");
2237 /* Set ->transfer so that we won't get any error in ftp_done()
2238 * because we didn't transfer the any file */
2239 ftp->transfer = FTPTRANSFER_NONE;
2240 state(conn, FTP_STOP);
2244 /* Set resume file transfer offset */
2245 infof(data, "Instructs server to resume from offset %" FORMAT_OFF_T
2246 "\n", data->state.resume_from);
2248 PPSENDF(&ftpc->pp, "REST %" FORMAT_OFF_T, data->state.resume_from);
2250 state(conn, FTP_RETR_REST);
2255 PPSENDF(&ftpc->pp, "RETR %s", ftpc->file);
2256 state(conn, FTP_RETR);
2262 static CURLcode ftp_state_size_resp(struct connectdata *conn,
2266 CURLcode result = CURLE_OK;
2267 struct SessionHandle *data=conn->data;
2268 curl_off_t filesize;
2269 char *buf = data->state.buffer;
2271 /* get the size from the ascii string: */
2272 filesize = (ftpcode == 213)?curlx_strtoofft(buf+4, NULL, 0):-1;
2274 if(instate == FTP_SIZE) {
2275 #ifdef CURL_FTP_HTTPSTYLE_HEAD
2276 if(-1 != filesize) {
2277 snprintf(buf, sizeof(data->state.buffer),
2278 "Content-Length: %" FORMAT_OFF_T "\r\n", filesize);
2279 result = Curl_client_write(conn, CLIENTWRITE_BOTH, buf, 0);
2284 Curl_pgrsSetDownloadSize(data, filesize);
2285 result = ftp_state_post_size(conn);
2287 else if(instate == FTP_RETR_SIZE) {
2288 Curl_pgrsSetDownloadSize(data, filesize);
2289 result = ftp_state_post_retr_size(conn, filesize);
2291 else if(instate == FTP_STOR_SIZE) {
2292 data->state.resume_from = filesize;
2293 result = ftp_state_ul_setup(conn, TRUE);
2299 static CURLcode ftp_state_rest_resp(struct connectdata *conn,
2303 CURLcode result = CURLE_OK;
2304 struct ftp_conn *ftpc = &conn->proto.ftpc;
2309 #ifdef CURL_FTP_HTTPSTYLE_HEAD
2310 if(ftpcode == 350) {
2311 char buffer[24]= { "Accept-ranges: bytes\r\n" };
2312 result = Curl_client_write(conn, CLIENTWRITE_BOTH, buffer, 0);
2317 result = ftp_state_post_rest(conn);
2321 if(ftpcode != 350) {
2322 failf(conn->data, "Couldn't use REST");
2323 result = CURLE_FTP_COULDNT_USE_REST;
2326 PPSENDF(&ftpc->pp, "RETR %s", ftpc->file);
2327 state(conn, FTP_RETR);
2335 static CURLcode ftp_state_stor_resp(struct connectdata *conn,
2336 int ftpcode, ftpstate instate)
2338 CURLcode result = CURLE_OK;
2339 struct SessionHandle *data = conn->data;
2342 failf(data, "Failed FTP upload: %0d", ftpcode);
2343 /* oops, we never close the sockets! */
2344 return CURLE_UPLOAD_FAILED;
2347 conn->proto.ftpc.state_saved = instate;
2349 /* PORT means we are now awaiting the server to connect to us. */
2350 if(data->set.ftp_use_port) {
2353 result = AllowServerConnect(conn, &connected);
2358 infof(data, "Data conn was not available immediately\n");
2359 state(conn, FTP_STOP);
2360 conn->bits.wait_data_conn = TRUE;
2366 return InitiateTransfer(conn);
2369 /* for LIST and RETR responses */
2370 static CURLcode ftp_state_get_resp(struct connectdata *conn,
2374 CURLcode result = CURLE_OK;
2375 struct SessionHandle *data = conn->data;
2376 struct FTP *ftp = data->state.proto.ftp;
2377 char *buf = data->state.buffer;
2379 if((ftpcode == 150) || (ftpcode == 125)) {
2383 150 Opening BINARY mode data connection for /etc/passwd (2241
2384 bytes). (ok, the file is being transferred)
2387 150 Opening ASCII mode data connection for /bin/ls
2390 150 ASCII data connection for /bin/ls (137.167.104.91,37445) (0 bytes).
2393 150 Opening ASCII mode data connection for [file] (0.0.0.0,0) (545 bytes)
2396 125 Data connection already open; Transfer starting. */
2398 curl_off_t size=-1; /* default unknown size */
2402 * It appears that there are FTP-servers that return size 0 for files when
2403 * SIZE is used on the file while being in BINARY mode. To work around
2404 * that (stupid) behavior, we attempt to parse the RETR response even if
2405 * the SIZE returned size zero.
2407 * Debugging help from Salvatore Sorrentino on February 26, 2003.
2410 if((instate != FTP_LIST) &&
2411 !data->set.prefer_ascii &&
2412 (ftp->downloadsize < 1)) {
2414 * It seems directory listings either don't show the size or very
2415 * often uses size 0 anyway. ASCII transfers may very well turn out
2416 * that the transferred amount of data is not the same as this line
2417 * tells, why using this number in those cases only confuses us.
2419 * Example D above makes this parsing a little tricky */
2421 bytes=strstr(buf, " bytes");
2423 long in=(long)(bytes-buf);
2424 /* this is a hint there is size information in there! ;-) */
2426 /* scan for the left parenthesis and break there */
2429 /* skip only digits */
2430 if(!ISDIGIT(*bytes)) {
2434 /* one more estep backwards */
2437 /* if we have nothing but digits: */
2439 /* get the number! */
2440 size = curlx_strtoofft(bytes, NULL, 0);
2444 else if(ftp->downloadsize > -1)
2445 size = ftp->downloadsize;
2447 if(size > data->req.maxdownload && data->req.maxdownload > 0)
2448 size = data->req.size = data->req.maxdownload;
2449 else if((instate != FTP_LIST) && (data->set.prefer_ascii))
2450 size = -1; /* kludge for servers that understate ASCII mode file size */
2452 infof(data, "Maxdownload = %" FORMAT_OFF_T "\n", data->req.maxdownload);
2454 if(instate != FTP_LIST)
2455 infof(data, "Getting file with size: %" FORMAT_OFF_T "\n", size);
2458 conn->proto.ftpc.state_saved = instate;
2459 conn->proto.ftpc.retr_size_saved = size;
2461 if(data->set.ftp_use_port) {
2464 result = AllowServerConnect(conn, &connected);
2469 infof(data, "Data conn was not available immediately\n");
2470 state(conn, FTP_STOP);
2471 conn->bits.wait_data_conn = TRUE;
2475 return InitiateTransfer(conn);
2478 if((instate == FTP_LIST) && (ftpcode == 450)) {
2479 /* simply no matching files in the dir listing */
2480 ftp->transfer = FTPTRANSFER_NONE; /* don't download anything */
2481 state(conn, FTP_STOP); /* this phase is over */
2484 failf(data, "RETR response: %03d", ftpcode);
2485 return instate == FTP_RETR && ftpcode == 550?
2486 CURLE_REMOTE_FILE_NOT_FOUND:
2487 CURLE_FTP_COULDNT_RETR_FILE;
2494 /* after USER, PASS and ACCT */
2495 static CURLcode ftp_state_loggedin(struct connectdata *conn)
2497 CURLcode result = CURLE_OK;
2500 if(conn->data->set.krb) {
2501 /* We may need to issue a KAUTH here to have access to the files
2502 * do it if user supplied a password
2504 if(conn->passwd && *conn->passwd) {
2506 result = Curl_krb_kauth(conn);
2512 if(conn->ssl[FIRSTSOCKET].use) {
2513 /* PBSZ = PROTECTION BUFFER SIZE.
2515 The 'draft-murray-auth-ftp-ssl' (draft 12, page 7) says:
2517 Specifically, the PROT command MUST be preceded by a PBSZ
2518 command and a PBSZ command MUST be preceded by a successful
2519 security data exchange (the TLS negotiation in this case)
2521 ... (and on page 8):
2523 Thus the PBSZ command must still be issued, but must have a
2524 parameter of '0' to indicate that no buffering is taking place
2525 and the data connection should not be encapsulated.
2527 PPSENDF(&conn->proto.ftpc.pp, "PBSZ %d", 0);
2528 state(conn, FTP_PBSZ);
2531 result = ftp_state_pwd(conn);
2536 /* for USER and PASS responses */
2537 static CURLcode ftp_state_user_resp(struct connectdata *conn,
2541 CURLcode result = CURLE_OK;
2542 struct SessionHandle *data = conn->data;
2543 struct FTP *ftp = data->state.proto.ftp;
2544 struct ftp_conn *ftpc = &conn->proto.ftpc;
2545 (void)instate; /* no use for this yet */
2547 /* some need password anyway, and others just return 2xx ignored */
2548 if((ftpcode == 331) && (ftpc->state == FTP_USER)) {
2549 /* 331 Password required for ...
2550 (the server requires to send the user's password too) */
2551 PPSENDF(&ftpc->pp, "PASS %s", ftp->passwd?ftp->passwd:"");
2552 state(conn, FTP_PASS);
2554 else if(ftpcode/100 == 2) {
2555 /* 230 User ... logged in.
2556 (the user logged in with or without password) */
2557 result = ftp_state_loggedin(conn);
2559 else if(ftpcode == 332) {
2560 if(data->set.str[STRING_FTP_ACCOUNT]) {
2561 PPSENDF(&ftpc->pp, "ACCT %s", data->set.str[STRING_FTP_ACCOUNT]);
2562 state(conn, FTP_ACCT);
2565 failf(data, "ACCT requested but none available");
2566 result = CURLE_LOGIN_DENIED;
2570 /* All other response codes, like:
2572 530 User ... access denied
2573 (the server denies to log the specified user) */
2575 if(conn->data->set.str[STRING_FTP_ALTERNATIVE_TO_USER] &&
2576 !conn->data->state.ftp_trying_alternative) {
2577 /* Ok, USER failed. Let's try the supplied command. */
2578 PPSENDF(&conn->proto.ftpc.pp, "%s",
2579 conn->data->set.str[STRING_FTP_ALTERNATIVE_TO_USER]);
2580 conn->data->state.ftp_trying_alternative = TRUE;
2581 state(conn, FTP_USER);
2585 failf(data, "Access denied: %03d", ftpcode);
2586 result = CURLE_LOGIN_DENIED;
2592 /* for ACCT response */
2593 static CURLcode ftp_state_acct_resp(struct connectdata *conn,
2596 CURLcode result = CURLE_OK;
2597 struct SessionHandle *data = conn->data;
2598 if(ftpcode != 230) {
2599 failf(data, "ACCT rejected by server: %03d", ftpcode);
2600 result = CURLE_FTP_WEIRD_PASS_REPLY; /* FIX */
2603 result = ftp_state_loggedin(conn);
2609 static CURLcode ftp_statemach_act(struct connectdata *conn)
2612 curl_socket_t sock = conn->sock[FIRSTSOCKET];
2613 struct SessionHandle *data=conn->data;
2615 struct ftp_conn *ftpc = &conn->proto.ftpc;
2616 struct pingpong *pp = &ftpc->pp;
2617 static const char ftpauth[][4] = { "SSL", "TLS" };
2621 return Curl_pp_flushsend(pp);
2623 result = ftp_readresp(sock, pp, &ftpcode, &nread);
2628 /* we have now received a full FTP server response */
2629 switch(ftpc->state) {
2631 if(ftpcode != 220) {
2632 failf(data, "Got a %03d ftp-server response when 220 was expected",
2634 return CURLE_FTP_WEIRD_SERVER_REPLY;
2637 /* We have received a 220 response fine, now we proceed. */
2638 #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
2640 /* If not anonymous login, try a secure login. Note that this
2641 procedure is still BLOCKING. */
2643 Curl_sec_request_prot(conn, "private");
2644 /* We set private first as default, in case the line below fails to
2645 set a valid level */
2646 Curl_sec_request_prot(conn, data->set.str[STRING_KRB_LEVEL]);
2648 if(Curl_sec_login(conn) != CURLE_OK)
2649 infof(data, "Logging in with password in cleartext!\n");
2651 infof(data, "Authentication successful\n");
2655 if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
2656 /* We don't have a SSL/TLS connection yet, but FTPS is
2657 requested. Try a FTPS connection now */
2660 switch(data->set.ftpsslauth) {
2661 case CURLFTPAUTH_DEFAULT:
2662 case CURLFTPAUTH_SSL:
2663 ftpc->count2 = 1; /* add one to get next */
2666 case CURLFTPAUTH_TLS:
2667 ftpc->count2 = -1; /* subtract one to get next */
2671 failf(data, "unsupported parameter to CURLOPT_FTPSSLAUTH: %d",
2672 (int)data->set.ftpsslauth);
2673 return CURLE_UNKNOWN_OPTION; /* we don't know what to do */
2675 PPSENDF(&ftpc->pp, "AUTH %s", ftpauth[ftpc->count1]);
2676 state(conn, FTP_AUTH);
2679 result = ftp_state_user(conn);
2687 /* we have gotten the response to a previous AUTH command */
2689 /* RFC2228 (page 5) says:
2691 * If the server is willing to accept the named security mechanism,
2692 * and does not require any security data, it must respond with
2693 * reply code 234/334.
2696 if((ftpcode == 234) || (ftpcode == 334)) {
2697 /* Curl_ssl_connect is BLOCKING */
2698 result = Curl_ssl_connect(conn, FIRSTSOCKET);
2699 if(CURLE_OK == result) {
2700 conn->ssl[SECONDARYSOCKET].use = FALSE; /* clear-text data */
2701 result = ftp_state_user(conn);
2704 else if(ftpc->count3 < 1) {
2706 ftpc->count1 += ftpc->count2; /* get next attempt */
2707 result = Curl_pp_sendf(&ftpc->pp, "AUTH %s", ftpauth[ftpc->count1]);
2708 /* remain in this same state */
2711 if(data->set.use_ssl > CURLUSESSL_TRY)
2712 /* we failed and CURLUSESSL_CONTROL or CURLUSESSL_ALL is set */
2713 result = CURLE_USE_SSL_FAILED;
2715 /* ignore the failure and continue */
2716 result = ftp_state_user(conn);
2725 result = ftp_state_user_resp(conn, ftpcode, ftpc->state);
2729 result = ftp_state_acct_resp(conn, ftpcode);
2733 PPSENDF(&ftpc->pp, "PROT %c",
2734 data->set.use_ssl == CURLUSESSL_CONTROL ? 'C' : 'P');
2735 state(conn, FTP_PROT);
2740 if(ftpcode/100 == 2)
2741 /* We have enabled SSL for the data connection! */
2742 conn->ssl[SECONDARYSOCKET].use =
2743 (data->set.use_ssl != CURLUSESSL_CONTROL) ? TRUE : FALSE;
2744 /* FTP servers typically responds with 500 if they decide to reject
2746 else if(data->set.use_ssl > CURLUSESSL_CONTROL)
2747 /* we failed and bails out */
2748 return CURLE_USE_SSL_FAILED;
2750 if(data->set.ftp_ccc) {
2751 /* CCC - Clear Command Channel
2753 PPSENDF(&ftpc->pp, "CCC", NULL);
2754 state(conn, FTP_CCC);
2757 result = ftp_state_pwd(conn);
2765 /* First shut down the SSL layer (note: this call will block) */
2766 result = Curl_ssl_shutdown(conn, FIRSTSOCKET);
2769 failf(conn->data, "Failed to clear the command channel (CCC)");
2774 /* Then continue as normal */
2775 result = ftp_state_pwd(conn);
2781 if(ftpcode == 257) {
2782 char *ptr=&data->state.buffer[4]; /* start on the first letter */
2786 dir = malloc(nread + 1);
2788 return CURLE_OUT_OF_MEMORY;
2790 /* Reply format is like
2791 257<space>"<directory-name>"<space><commentary> and the RFC959
2794 The directory name can contain any character; embedded
2795 double-quotes should be escaped by double-quotes (the
2796 "quote-doubling" convention).
2799 /* it started good */
2801 for(store = dir; *ptr;) {
2803 if('\"' == ptr[1]) {
2804 /* "quote-doubling" */
2810 *store = '\0'; /* zero terminate */
2811 break; /* get out of this loop */
2820 /* If the path name does not look like an absolute path (i.e.: it
2821 does not start with a '/'), we probably need some server-dependent
2822 adjustments. For example, this is the case when connecting to
2823 an OS400 FTP server: this server supports two name syntaxes,
2824 the default one being incompatible with standard pathes. In
2825 addition, this server switches automatically to the regular path
2826 syntax when one is encountered in a command: this results in
2827 having an entrypath in the wrong syntax when later used in CWD.
2828 The method used here is to check the server OS: we do it only
2829 if the path name looks strange to minimize overhead on other
2832 if(!ftpc->server_os && dir[0] != '/') {
2834 result = Curl_pp_sendf(&ftpc->pp, "SYST", NULL);
2835 if(result != CURLE_OK) {
2839 Curl_safefree(ftpc->entrypath);
2840 ftpc->entrypath = dir; /* remember this */
2841 infof(data, "Entry path is '%s'\n", ftpc->entrypath);
2842 /* also save it where getinfo can access it: */
2843 data->state.most_recent_ftp_entrypath = ftpc->entrypath;
2844 state(conn, FTP_SYST);
2848 Curl_safefree(ftpc->entrypath);
2849 ftpc->entrypath = dir; /* remember this */
2850 infof(data, "Entry path is '%s'\n", ftpc->entrypath);
2851 /* also save it where getinfo can access it: */
2852 data->state.most_recent_ftp_entrypath = ftpc->entrypath;
2855 /* couldn't get the path */
2857 infof(data, "Failed to figure out path\n");
2860 state(conn, FTP_STOP); /* we are done with the CONNECT phase! */
2861 DEBUGF(infof(data, "protocol connect phase DONE\n"));
2865 if(ftpcode == 215) {
2866 char *ptr=&data->state.buffer[4]; /* start on the first letter */
2870 os = malloc(nread + 1);
2872 return CURLE_OUT_OF_MEMORY;
2874 /* Reply format is like
2875 215<space><OS-name><space><commentary>
2879 for(store = os; *ptr && *ptr != ' ';)
2881 *store = '\0'; /* zero terminate */
2883 /* Check for special servers here. */
2885 if(strequal(os, "OS/400")) {
2886 /* Force OS400 name format 1. */
2887 result = Curl_pp_sendf(&ftpc->pp, "SITE NAMEFMT 1", NULL);
2888 if(result != CURLE_OK) {
2892 /* remember target server OS */
2893 Curl_safefree(ftpc->server_os);
2894 ftpc->server_os = os;
2895 state(conn, FTP_NAMEFMT);
2899 /* Nothing special for the target server. */
2900 /* remember target server OS */
2901 Curl_safefree(ftpc->server_os);
2902 ftpc->server_os = os;
2906 /* Cannot identify server OS. Continue anyway and cross fingers. */
2909 state(conn, FTP_STOP); /* we are done with the CONNECT phase! */
2910 DEBUGF(infof(data, "protocol connect phase DONE\n"));
2914 if(ftpcode == 250) {
2915 /* Name format change successful: reload initial path. */
2916 ftp_state_pwd(conn);
2920 state(conn, FTP_STOP); /* we are done with the CONNECT phase! */
2921 DEBUGF(infof(data, "protocol connect phase DONE\n"));
2926 case FTP_RETR_PREQUOTE:
2927 case FTP_STOR_PREQUOTE:
2928 if((ftpcode >= 400) && !ftpc->count2) {
2929 /* failure response code, and not allowed to fail */
2930 failf(conn->data, "QUOT command failed with %03d", ftpcode);
2931 return CURLE_QUOTE_ERROR;
2933 result = ftp_state_quote(conn, FALSE, ftpc->state);
2940 if(ftpcode/100 != 2) {
2941 /* failure to CWD there */
2942 if(conn->data->set.ftp_create_missing_dirs &&
2943 ftpc->count1 && !ftpc->count2) {
2945 ftpc->count2++; /* counter to prevent CWD-MKD loops */
2946 PPSENDF(&ftpc->pp, "MKD %s", ftpc->dirs[ftpc->count1 - 1]);
2947 state(conn, FTP_MKD);
2950 /* return failure */
2951 failf(data, "Server denied you to change to the given directory");
2952 ftpc->cwdfail = TRUE; /* don't remember this path as we failed
2954 return CURLE_REMOTE_ACCESS_DENIED;
2960 if(++ftpc->count1 <= ftpc->dirdepth) {
2962 PPSENDF(&ftpc->pp, "CWD %s", ftpc->dirs[ftpc->count1 - 1]);
2965 result = ftp_state_post_cwd(conn);
2973 if((ftpcode/100 != 2) && !ftpc->count3--) {
2974 /* failure to MKD the dir */
2975 failf(data, "Failed to MKD dir: %03d", ftpcode);
2976 return CURLE_REMOTE_ACCESS_DENIED;
2978 state(conn, FTP_CWD);
2980 PPSENDF(&ftpc->pp, "CWD %s", ftpc->dirs[ftpc->count1 - 1]);
2984 result = ftp_state_mdtm_resp(conn, ftpcode);
2991 result = ftp_state_type_resp(conn, ftpcode, ftpc->state);
2997 result = ftp_state_size_resp(conn, ftpcode, ftpc->state);
3002 result = ftp_state_rest_resp(conn, ftpcode, ftpc->state);
3006 if(ftpcode != 200) {
3007 /* there only is this one standard OK return code. */
3008 failf(data, "PRET command not accepted: %03d", ftpcode);
3009 return CURLE_FTP_PRET_FAILED;
3011 result = ftp_state_use_pasv(conn);
3015 result = ftp_state_pasv_resp(conn, ftpcode);
3019 result = ftp_state_port_resp(conn, ftpcode);
3024 result = ftp_state_get_resp(conn, ftpcode, ftpc->state);
3028 result = ftp_state_stor_resp(conn, ftpcode, ftpc->state);
3032 /* fallthrough, just stop! */
3034 /* internal error */
3035 state(conn, FTP_STOP);
3044 /* called repeatedly until done from multi.c */
3045 static CURLcode ftp_multi_statemach(struct connectdata *conn,
3048 struct ftp_conn *ftpc = &conn->proto.ftpc;
3049 CURLcode result = Curl_pp_multi_statemach(&ftpc->pp);
3051 /* Check for the state outside of the Curl_socket_ready() return code checks
3052 since at times we are in fact already in this state when this function
3054 *done = (ftpc->state == FTP_STOP) ? TRUE : FALSE;
3059 static CURLcode ftp_easy_statemach(struct connectdata *conn)
3061 struct ftp_conn *ftpc = &conn->proto.ftpc;
3062 struct pingpong *pp = &ftpc->pp;
3063 CURLcode result = CURLE_OK;
3065 while(ftpc->state != FTP_STOP) {
3066 result = Curl_pp_easy_statemach(pp);
3075 * Allocate and initialize the struct FTP for the current SessionHandle. If
3079 #if defined(__INTEL_COMPILER) && (__INTEL_COMPILER == 910) && \
3080 defined(__OPTIMIZE__) && defined(__unix__) && defined(__i386__)
3081 /* workaround icc 9.1 optimizer issue */
3082 #pragma optimize("", off)
3085 static CURLcode ftp_init(struct connectdata *conn)
3089 if(NULL == conn->data->state.proto.ftp) {
3090 conn->data->state.proto.ftp = malloc(sizeof(struct FTP));
3091 if(NULL == conn->data->state.proto.ftp)
3092 return CURLE_OUT_OF_MEMORY;
3095 ftp = conn->data->state.proto.ftp;
3097 /* get some initial data into the ftp struct */
3098 ftp->bytecountp = &conn->data->req.bytecount;
3099 ftp->transfer = FTPTRANSFER_BODY;
3100 ftp->downloadsize = 0;
3102 /* No need to duplicate user+password, the connectdata struct won't change
3103 during a session, but we re-init them here since on subsequent inits
3104 since the conn struct may have changed or been replaced.
3106 ftp->user = conn->user;
3107 ftp->passwd = conn->passwd;
3108 if(isBadFtpString(ftp->user))
3109 return CURLE_URL_MALFORMAT;
3110 if(isBadFtpString(ftp->passwd))
3111 return CURLE_URL_MALFORMAT;
3113 conn->proto.ftpc.known_filesize = -1; /* unknown size for now */
3118 #if defined(__INTEL_COMPILER) && (__INTEL_COMPILER == 910) && \
3119 defined(__OPTIMIZE__) && defined(__unix__) && defined(__i386__)
3120 /* workaround icc 9.1 optimizer issue */
3121 #pragma optimize("", on)
3125 * ftp_connect() should do everything that is to be considered a part of
3126 * the connection phase.
3128 * The variable 'done' points to will be TRUE if the protocol-layer connect
3129 * phase is done when this function returns, or FALSE is not. When called as
3130 * a part of the easy interface, it will always be TRUE.
3132 static CURLcode ftp_connect(struct connectdata *conn,
3133 bool *done) /* see description above */
3136 struct ftp_conn *ftpc = &conn->proto.ftpc;
3137 struct SessionHandle *data=conn->data;
3138 struct pingpong *pp = &ftpc->pp;
3140 *done = FALSE; /* default to not done yet */
3142 /* If there already is a protocol-specific struct allocated for this
3143 sessionhandle, deal with it */
3144 Curl_reset_reqproto(conn);
3146 result = ftp_init(conn);
3147 if(CURLE_OK != result)
3150 /* We always support persistent connections on ftp */
3151 conn->bits.close = FALSE;
3153 pp->response_time = RESP_TIMEOUT; /* set default response time-out */
3154 pp->statemach_act = ftp_statemach_act;
3155 pp->endofresp = ftp_endofresp;
3158 if(conn->bits.tunnel_proxy && conn->bits.httpproxy) {
3159 /* for FTP over HTTP proxy */
3160 struct HTTP http_proxy;
3161 struct FTP *ftp_save;
3164 /* We want "seamless" FTP operations through HTTP proxy tunnel */
3166 /* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the member
3167 * conn->proto.http; we want FTP through HTTP and we have to change the
3168 * member temporarily for connecting to the HTTP proxy. After
3169 * Curl_proxyCONNECT we have to set back the member to the original struct
3172 ftp_save = data->state.proto.ftp;
3173 memset(&http_proxy, 0, sizeof(http_proxy));
3174 data->state.proto.http = &http_proxy;
3176 result = Curl_proxyCONNECT(conn, FIRSTSOCKET,
3177 conn->host.name, conn->remote_port);
3179 data->state.proto.ftp = ftp_save;
3181 if(CURLE_OK != result)
3185 if(conn->handler->flags & PROTOPT_SSL) {
3187 result = Curl_ssl_connect(conn, FIRSTSOCKET);
3192 Curl_pp_init(pp); /* init the generic pingpong data */
3194 /* When we connect, we start in the state where we await the 220
3196 state(conn, FTP_WAIT220);
3198 if(data->state.used_interface == Curl_if_multi)
3199 result = ftp_multi_statemach(conn, done);
3201 result = ftp_easy_statemach(conn);
3209 /***********************************************************************
3213 * The DONE function. This does what needs to be done after a single DO has
3216 * Input argument is already checked for validity.
3218 static CURLcode ftp_done(struct connectdata *conn, CURLcode status,
3221 struct SessionHandle *data = conn->data;
3222 struct FTP *ftp = data->state.proto.ftp;
3223 struct ftp_conn *ftpc = &conn->proto.ftpc;
3224 struct pingpong *pp = &ftpc->pp;
3227 CURLcode result = CURLE_OK;
3228 bool was_ctl_valid = ftpc->ctl_valid;
3230 const char *path_to_use = data->state.path;
3233 /* When the easy handle is removed from the multi while libcurl is still
3234 * trying to resolve the host name, it seems that the ftp struct is not
3235 * yet initialized, but the removal action calls Curl_done() which calls
3236 * this function. So we simply return success if no ftp pointer is set.
3241 case CURLE_BAD_DOWNLOAD_RESUME:
3242 case CURLE_FTP_WEIRD_PASV_REPLY:
3243 case CURLE_FTP_PORT_FAILED:
3244 case CURLE_FTP_ACCEPT_FAILED:
3245 case CURLE_FTP_ACCEPT_TIMEOUT:
3246 case CURLE_FTP_COULDNT_SET_TYPE:
3247 case CURLE_FTP_COULDNT_RETR_FILE:
3248 case CURLE_UPLOAD_FAILED:
3249 case CURLE_REMOTE_ACCESS_DENIED:
3250 case CURLE_FILESIZE_EXCEEDED:
3251 case CURLE_REMOTE_FILE_NOT_FOUND:
3252 case CURLE_WRITE_ERROR:
3253 /* the connection stays alive fine even though this happened */
3255 case CURLE_OK: /* doesn't affect the control connection's status */
3257 ftpc->ctl_valid = was_ctl_valid;
3260 /* until we cope better with prematurely ended requests, let them
3261 * fallback as if in complete failure */
3262 default: /* by default, an error means the control connection is
3263 wedged and should not be used anymore */
3264 ftpc->ctl_valid = FALSE;
3265 ftpc->cwdfail = TRUE; /* set this TRUE to prevent us to remember the
3266 current path, as this connection is going */
3267 conn->bits.close = TRUE; /* marked for closure */
3268 result = status; /* use the already set error code */
3272 /* now store a copy of the directory we are in */
3274 free(ftpc->prevpath);
3276 if(data->set.wildcardmatch) {
3277 if(data->set.chunk_end && ftpc->file) {
3278 data->set.chunk_end(data->wildcard.customptr);
3280 ftpc->known_filesize = -1;
3283 /* get the "raw" path */
3284 path = curl_easy_unescape(data, path_to_use, 0, NULL);
3286 /* out of memory, but we can limp along anyway (and should try to
3287 * since we may already be in the out of memory cleanup path) */
3289 result = CURLE_OUT_OF_MEMORY;
3290 ftpc->ctl_valid = FALSE; /* mark control connection as bad */
3291 conn->bits.close = TRUE; /* mark for connection closure */
3292 ftpc->prevpath = NULL; /* no path remembering */
3295 size_t flen = ftpc->file?strlen(ftpc->file):0; /* file is "raw" already */
3296 size_t dlen = strlen(path)-flen;
3297 if(!ftpc->cwdfail) {
3298 if(dlen && (data->set.ftp_filemethod != FTPFILE_NOCWD)) {
3299 ftpc->prevpath = path;
3301 /* if 'path' is not the whole string */
3302 ftpc->prevpath[dlen]=0; /* terminate */
3305 /* we never changed dir */
3306 ftpc->prevpath=strdup("");
3310 infof(data, "Remembering we are in dir \"%s\"\n", ftpc->prevpath);
3313 ftpc->prevpath = NULL; /* no path */
3317 /* free the dir tree and file parts */
3320 /* shut down the socket to inform the server we're done */
3323 shutdown(conn->sock[SECONDARYSOCKET],2); /* SD_BOTH */
3326 if(conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD) {
3327 if(!result && ftpc->dont_check && data->req.maxdownload > 0)
3328 /* partial download completed */
3329 result = Curl_pp_sendf(pp, "ABOR");
3331 failf(data, "Failure sending ABOR command: %s",
3332 curl_easy_strerror(result));
3333 ftpc->ctl_valid = FALSE; /* mark control connection as bad */
3334 conn->bits.close = TRUE; /* mark for connection closure */
3337 if(conn->ssl[SECONDARYSOCKET].use) {
3338 /* The secondary socket is using SSL so we must close down that part
3339 first before we close the socket for real */
3340 Curl_ssl_close(conn, SECONDARYSOCKET);
3342 /* Note that we keep "use" set to TRUE since that (next) connection is
3343 still requested to use SSL */
3345 if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET]) {
3346 Curl_closesocket(conn, conn->sock[SECONDARYSOCKET]);
3347 conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
3348 conn->bits.tcpconnect[SECONDARYSOCKET] = FALSE;
3352 if(!result && (ftp->transfer == FTPTRANSFER_BODY) && ftpc->ctl_valid &&
3353 pp->pending_resp && !premature) {
3355 * Let's see what the server says about the transfer we just performed,
3356 * but lower the timeout as sometimes this connection has died while the
3357 * data has been transferred. This happens when doing through NATs etc that
3358 * abandon old silent connections.
3360 long old_time = pp->response_time;
3362 pp->response_time = 60*1000; /* give it only a minute for now */
3363 pp->response = Curl_tvnow(); /* timeout relative now */
3365 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
3367 pp->response_time = old_time; /* set this back to previous value */
3369 if(!nread && (CURLE_OPERATION_TIMEDOUT == result)) {
3370 failf(data, "control connection looks dead");
3371 ftpc->ctl_valid = FALSE; /* mark control connection as bad */
3372 conn->bits.close = TRUE; /* mark for closure */
3378 if(ftpc->dont_check && data->req.maxdownload > 0) {
3379 /* we have just sent ABOR and there is no reliable way to check if it was
3380 * successful or not; we have to close the connection now */
3381 infof(data, "partial download completed, closing connection\n");
3382 conn->bits.close = TRUE; /* mark for closure */
3386 if(!ftpc->dont_check) {
3387 /* 226 Transfer complete, 250 Requested file action okay, completed. */
3388 if((ftpcode != 226) && (ftpcode != 250)) {
3389 failf(data, "server did not report OK, got %d", ftpcode);
3390 result = CURLE_PARTIAL_FILE;
3395 if(result || premature)
3396 /* the response code from the transfer showed an error already so no
3397 use checking further */
3399 else if(data->set.upload) {
3400 if((-1 != data->set.infilesize) &&
3401 (data->set.infilesize != *ftp->bytecountp) &&
3403 (ftp->transfer == FTPTRANSFER_BODY)) {
3404 failf(data, "Uploaded unaligned file size (%" FORMAT_OFF_T
3405 " out of %" FORMAT_OFF_T " bytes)",
3406 *ftp->bytecountp, data->set.infilesize);
3407 result = CURLE_PARTIAL_FILE;
3411 if((-1 != data->req.size) &&
3412 (data->req.size != *ftp->bytecountp) &&
3413 #ifdef CURL_DO_LINEEND_CONV
3414 /* Most FTP servers don't adjust their file SIZE response for CRLFs, so
3415 * we'll check to see if the discrepancy can be explained by the number
3416 * of CRLFs we've changed to LFs.
3418 ((data->req.size + data->state.crlf_conversions) !=
3419 *ftp->bytecountp) &&
3420 #endif /* CURL_DO_LINEEND_CONV */
3421 (data->req.maxdownload != *ftp->bytecountp)) {
3422 failf(data, "Received only partial file: %" FORMAT_OFF_T " bytes",
3424 result = CURLE_PARTIAL_FILE;
3426 else if(!ftpc->dont_check &&
3427 !*ftp->bytecountp &&
3428 (data->req.size>0)) {
3429 failf(data, "No data was received!");
3430 result = CURLE_FTP_COULDNT_RETR_FILE;
3434 /* clear these for next connection */
3435 ftp->transfer = FTPTRANSFER_BODY;
3436 ftpc->dont_check = FALSE;
3438 /* Send any post-transfer QUOTE strings? */
3439 if(!status && !result && !premature && data->set.postquote)
3440 result = ftp_sendquote(conn, data->set.postquote);
3445 /***********************************************************************
3449 * Where a 'quote' means a list of custom commands to send to the server.
3450 * The quote list is passed as an argument.
3456 CURLcode ftp_sendquote(struct connectdata *conn, struct curl_slist *quote)
3458 struct curl_slist *item;
3462 struct ftp_conn *ftpc = &conn->proto.ftpc;
3463 struct pingpong *pp = &ftpc->pp;
3468 char *cmd = item->data;
3469 bool acceptfail = FALSE;
3471 /* if a command starts with an asterisk, which a legal FTP command never
3472 can, the command will be allowed to fail without it causing any
3473 aborts or cancels etc. It will cause libcurl to act as if the command
3474 is successful, whatever the server reponds. */
3481 FTPSENDF(conn, "%s", cmd);
3483 pp->response = Curl_tvnow(); /* timeout relative now */
3485 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
3489 if(!acceptfail && (ftpcode >= 400)) {
3490 failf(conn->data, "QUOT string not accepted: %s", cmd);
3491 return CURLE_QUOTE_ERROR;
3501 /***********************************************************************
3505 * Returns TRUE if we in the current situation should send TYPE
3507 static int ftp_need_type(struct connectdata *conn,
3510 return conn->proto.ftpc.transfertype != (ascii_wanted?'A':'I');
3513 /***********************************************************************
3517 * Set TYPE. We only deal with ASCII or BINARY so this function
3519 * If the transfer type is not sent, simulate on OK response in newstate
3521 static CURLcode ftp_nb_type(struct connectdata *conn,
3522 bool ascii, ftpstate newstate)
3524 struct ftp_conn *ftpc = &conn->proto.ftpc;
3526 char want = (char)(ascii?'A':'I');
3528 if(ftpc->transfertype == want) {
3529 state(conn, newstate);
3530 return ftp_state_type_resp(conn, 200, newstate);
3533 PPSENDF(&ftpc->pp, "TYPE %c", want);
3534 state(conn, newstate);
3536 /* keep track of our current transfer type */
3537 ftpc->transfertype = want;
3541 /***************************************************************************
3543 * ftp_pasv_verbose()
3545 * This function only outputs some informationals about this second connection
3546 * when we've issued a PASV command before and thus we have connected to a
3547 * possibly new IP address.
3550 #ifndef CURL_DISABLE_VERBOSE_STRINGS
3552 ftp_pasv_verbose(struct connectdata *conn,
3554 char *newhost, /* ascii version */
3558 Curl_printable_address(ai, buf, sizeof(buf));
3559 infof(conn->data, "Connecting to %s (%s) port %d\n", newhost, buf, port);
3564 Check if this is a range download, and if so, set the internal variables
3568 static CURLcode ftp_range(struct connectdata *conn)
3570 curl_off_t from, to;
3573 struct SessionHandle *data = conn->data;
3574 struct ftp_conn *ftpc = &conn->proto.ftpc;
3576 if(data->state.use_range && data->state.range) {
3577 from=curlx_strtoofft(data->state.range, &ptr, 0);
3578 while(*ptr && (ISSPACE(*ptr) || (*ptr=='-')))
3580 to=curlx_strtoofft(ptr, &ptr2, 0);
3582 /* we didn't get any digit */
3585 if((-1 == to) && (from>=0)) {
3587 data->state.resume_from = from;
3588 DEBUGF(infof(conn->data, "FTP RANGE %" FORMAT_OFF_T " to end of file\n",
3593 data->req.maxdownload = -from;
3594 data->state.resume_from = from;
3595 DEBUGF(infof(conn->data, "FTP RANGE the last %" FORMAT_OFF_T " bytes\n",
3600 data->req.maxdownload = (to-from)+1; /* include last byte */
3601 data->state.resume_from = from;
3602 DEBUGF(infof(conn->data, "FTP RANGE from %" FORMAT_OFF_T
3603 " getting %" FORMAT_OFF_T " bytes\n",
3604 from, data->req.maxdownload));
3606 DEBUGF(infof(conn->data, "range-download from %" FORMAT_OFF_T
3607 " to %" FORMAT_OFF_T ", totally %" FORMAT_OFF_T " bytes\n",
3608 from, to, data->req.maxdownload));
3609 ftpc->dont_check = TRUE; /* dont check for successful transfer */
3612 data->req.maxdownload = -1;
3620 * This function shall be called when the second FTP (data) connection is
3624 static CURLcode ftp_nextconnect(struct connectdata *conn)
3626 struct SessionHandle *data=conn->data;
3627 struct ftp_conn *ftpc = &conn->proto.ftpc;
3628 CURLcode result = CURLE_OK;
3630 /* the ftp struct is inited in ftp_connect() */
3631 struct FTP *ftp = data->state.proto.ftp;
3633 DEBUGF(infof(data, "DO-MORE phase starts\n"));
3635 if(ftp->transfer <= FTPTRANSFER_INFO) {
3636 /* a transfer is about to take place, or if not a file name was given
3637 so we'll do a SIZE on it later and then we need the right TYPE first */
3639 if(conn->bits.wait_data_conn == TRUE) {
3642 result = ReceivedServerConnect(conn, &serv_conned);
3644 return result; /* Failed to accept data connection */
3647 /* It looks data connection is established */
3648 result = AcceptServerConnect(conn);
3649 conn->bits.wait_data_conn = FALSE;
3650 if(result == CURLE_OK)
3651 result = InitiateTransfer(conn);
3656 else if(data->set.upload) {
3657 result = ftp_nb_type(conn, data->set.prefer_ascii, FTP_STOR_TYPE);
3663 ftp->downloadsize = -1; /* unknown as of yet */
3665 result = ftp_range(conn);
3668 else if(data->set.ftp_list_only || !ftpc->file) {
3669 /* The specified path ends with a slash, and therefore we think this
3670 is a directory that is requested, use LIST. But before that we
3671 need to set ASCII transfer mode. */
3673 /* But only if a body transfer was requested. */
3674 if(ftp->transfer == FTPTRANSFER_BODY) {
3675 result = ftp_nb_type(conn, TRUE, FTP_LIST_TYPE);
3679 /* otherwise just fall through */
3682 result = ftp_nb_type(conn, data->set.prefer_ascii, FTP_RETR_TYPE);
3687 result = ftp_easy_statemach(conn);
3690 if((result == CURLE_OK) && (ftp->transfer != FTPTRANSFER_BODY))
3691 /* no data to transfer. FIX: it feels like a kludge to have this here
3693 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
3695 DEBUGF(infof(data, "DO-MORE phase ends with %d\n", (int)result));
3702 /***********************************************************************
3706 * This is the actual DO function for FTP. Get a file/directory according to
3707 * the options previously setup.
3711 CURLcode ftp_perform(struct connectdata *conn,
3712 bool *connected, /* connect status after PASV / PORT */
3715 /* this is FTP and no proxy */
3716 CURLcode result=CURLE_OK;
3718 DEBUGF(infof(conn->data, "DO phase starts\n"));
3720 if(conn->data->set.opt_no_body) {
3721 /* requested no body means no transfer... */
3722 struct FTP *ftp = conn->data->state.proto.ftp;
3723 ftp->transfer = FTPTRANSFER_INFO;
3727 *dophase_done = FALSE; /* not done yet */
3729 /* start the first command in the DO phase */
3730 result = ftp_state_quote(conn, TRUE, FTP_QUOTE);
3734 /* run the state-machine */
3735 if(conn->data->state.used_interface == Curl_if_multi)
3736 result = ftp_multi_statemach(conn, dophase_done);
3738 result = ftp_easy_statemach(conn);
3739 *dophase_done = TRUE; /* with the easy interface we are done here */
3741 *connected = conn->bits.tcpconnect[FIRSTSOCKET];
3744 DEBUGF(infof(conn->data, "DO phase is complete\n"));
3749 static void wc_data_dtor(void *ptr)
3751 struct ftp_wc_tmpdata *tmp = ptr;
3753 Curl_ftp_parselist_data_free(&tmp->parser);
3757 static CURLcode init_wc_data(struct connectdata *conn)
3760 char *path = conn->data->state.path;
3761 struct WildcardData *wildcard = &(conn->data->wildcard);
3762 CURLcode ret = CURLE_OK;
3763 struct ftp_wc_tmpdata *ftp_tmp;
3765 last_slash = strrchr(conn->data->state.path, '/');
3768 if(last_slash[0] == '\0') {
3769 wildcard->state = CURLWC_CLEAN;
3770 ret = ftp_parse_url_path(conn);
3774 wildcard->pattern = strdup(last_slash);
3775 if(!wildcard->pattern)
3776 return CURLE_OUT_OF_MEMORY;
3777 last_slash[0] = '\0'; /* cut file from path */
3780 else { /* there is only 'wildcard pattern' or nothing */
3782 wildcard->pattern = strdup(path);
3783 if(!wildcard->pattern)
3784 return CURLE_OUT_OF_MEMORY;
3787 else { /* only list */
3788 wildcard->state = CURLWC_CLEAN;
3789 ret = ftp_parse_url_path(conn);
3794 /* program continues only if URL is not ending with slash, allocate needed
3795 resources for wildcard transfer */
3797 /* allocate ftp protocol specific temporary wildcard data */
3798 ftp_tmp = malloc(sizeof(struct ftp_wc_tmpdata));
3800 return CURLE_OUT_OF_MEMORY;
3803 /* INITIALIZE parselist structure */
3804 ftp_tmp->parser = Curl_ftp_parselist_data_alloc();
3805 if(!ftp_tmp->parser) {
3807 return CURLE_OUT_OF_MEMORY;
3810 wildcard->tmp = ftp_tmp; /* put it to the WildcardData tmp pointer */
3811 wildcard->tmp_dtor = wc_data_dtor;
3813 /* wildcard does not support NOCWD option (assert it?) */
3814 if(conn->data->set.ftp_filemethod == FTPFILE_NOCWD)
3815 conn->data->set.ftp_filemethod = FTPFILE_MULTICWD;
3817 /* try to parse ftp url */
3818 ret = ftp_parse_url_path(conn);
3823 /* backup old write_function */
3824 ftp_tmp->backup.write_function = conn->data->set.fwrite_func;
3825 /* parsing write function */
3826 conn->data->set.fwrite_func = Curl_ftp_parselist;
3827 /* backup old file descriptor */
3828 ftp_tmp->backup.file_descriptor = conn->data->set.out;
3829 /* let the writefunc callback know what curl pointer is working with */
3830 conn->data->set.out = conn;
3832 wildcard->path = strdup(conn->data->state.path);
3833 if(!wildcard->path) {
3834 return CURLE_OUT_OF_MEMORY;
3837 infof(conn->data, "Wildcard - Parsing started\n");
3841 /* This is called recursively */
3842 static CURLcode wc_statemach(struct connectdata *conn)
3844 struct WildcardData * const wildcard = &(conn->data->wildcard);
3845 CURLcode ret = CURLE_OK;
3847 switch (wildcard->state) {
3849 ret = init_wc_data(conn);
3850 if(wildcard->state == CURLWC_CLEAN)
3854 wildcard->state = ret ? CURLWC_ERROR : CURLWC_MATCHING;
3857 case CURLWC_MATCHING: {
3858 /* In this state is LIST response successfully parsed, so lets restore
3859 previous WRITEFUNCTION callback and WRITEDATA pointer */
3860 struct ftp_wc_tmpdata *ftp_tmp = wildcard->tmp;
3861 conn->data->set.fwrite_func = ftp_tmp->backup.write_function;
3862 conn->data->set.out = ftp_tmp->backup.file_descriptor;
3863 wildcard->state = CURLWC_DOWNLOADING;
3865 if(Curl_ftp_parselist_geterror(ftp_tmp->parser)) {
3866 /* error found in LIST parsing */
3867 wildcard->state = CURLWC_CLEAN;
3868 return wc_statemach(conn);
3870 else if(wildcard->filelist->size == 0) {
3871 /* no corresponding file */
3872 wildcard->state = CURLWC_CLEAN;
3873 return CURLE_REMOTE_FILE_NOT_FOUND;
3875 return wc_statemach(conn);
3878 case CURLWC_DOWNLOADING: {
3879 /* filelist has at least one file, lets get first one */
3880 struct ftp_conn *ftpc = &conn->proto.ftpc;
3881 struct curl_fileinfo *finfo = wildcard->filelist->head->ptr;
3882 char *tmp_path = malloc(strlen(conn->data->state.path) +
3883 strlen(finfo->filename) + 1);
3885 return CURLE_OUT_OF_MEMORY;
3889 /* make full path to matched file */
3890 strcat(tmp_path, wildcard->path);
3891 strcat(tmp_path, finfo->filename);
3892 /* switch default "state.pathbuffer" and tmp_path, good to see
3893 ftp_parse_url_path function to understand this trick */
3894 Curl_safefree(conn->data->state.pathbuffer);
3895 conn->data->state.pathbuffer = tmp_path;
3896 conn->data->state.path = tmp_path;
3898 infof(conn->data, "Wildcard - START of \"%s\"\n", finfo->filename);
3899 if(conn->data->set.chunk_bgn) {
3900 long userresponse = conn->data->set.chunk_bgn(
3901 finfo, wildcard->customptr, (int)wildcard->filelist->size);
3902 switch(userresponse) {
3903 case CURL_CHUNK_BGN_FUNC_SKIP:
3904 infof(conn->data, "Wildcard - \"%s\" skipped by user\n",
3906 wildcard->state = CURLWC_SKIP;
3907 return wc_statemach(conn);
3908 case CURL_CHUNK_BGN_FUNC_FAIL:
3909 return CURLE_CHUNK_FAILED;
3913 if(finfo->filetype != CURLFILETYPE_FILE) {
3914 wildcard->state = CURLWC_SKIP;
3915 return wc_statemach(conn);
3918 if(finfo->flags & CURLFINFOFLAG_KNOWN_SIZE)
3919 ftpc->known_filesize = finfo->size;
3921 ret = ftp_parse_url_path(conn);
3926 /* we don't need the Curl_fileinfo of first file anymore */
3927 Curl_llist_remove(wildcard->filelist, wildcard->filelist->head, NULL);
3929 if(wildcard->filelist->size == 0) { /* remains only one file to down. */
3930 wildcard->state = CURLWC_CLEAN;
3931 /* after that will be ftp_do called once again and no transfer
3932 will be done because of CURLWC_CLEAN state */
3938 if(conn->data->set.chunk_end)
3939 conn->data->set.chunk_end(conn->data->wildcard.customptr);
3940 Curl_llist_remove(wildcard->filelist, wildcard->filelist->head, NULL);
3941 wildcard->state = (wildcard->filelist->size == 0) ?
3942 CURLWC_CLEAN : CURLWC_DOWNLOADING;
3943 return wc_statemach(conn);
3946 case CURLWC_CLEAN: {
3947 struct ftp_wc_tmpdata *ftp_tmp = wildcard->tmp;
3950 ret = Curl_ftp_parselist_geterror(ftp_tmp->parser);
3952 wildcard->state = ret ? CURLWC_ERROR : CURLWC_DONE;
3963 /***********************************************************************
3967 * This function is registered as 'curl_do' function. It decodes the path
3968 * parts etc as a wrapper to the actual DO function (ftp_perform).
3970 * The input argument is already checked for validity.
3972 static CURLcode ftp_do(struct connectdata *conn, bool *done)
3974 CURLcode retcode = CURLE_OK;
3976 *done = FALSE; /* default to false */
3979 Since connections can be re-used between SessionHandles, this might be a
3980 connection already existing but on a fresh SessionHandle struct so we must
3981 make sure we have a good 'struct FTP' to play with. For new connections,
3982 the struct FTP is allocated and setup in the ftp_connect() function.
3984 Curl_reset_reqproto(conn);
3985 retcode = ftp_init(conn);
3989 if(conn->data->set.wildcardmatch) {
3990 retcode = wc_statemach(conn);
3991 if(conn->data->wildcard.state == CURLWC_SKIP ||
3992 conn->data->wildcard.state == CURLWC_DONE) {
3993 /* do not call ftp_regular_transfer */
3996 if(retcode) /* error, loop or skipping the file */
3999 else { /* no wildcard FSM needed */
4000 retcode = ftp_parse_url_path(conn);
4005 retcode = ftp_regular_transfer(conn, done);
4011 CURLcode Curl_ftpsendf(struct connectdata *conn,
4012 const char *fmt, ...)
4014 ssize_t bytes_written;
4015 #define SBUF_SIZE 1024
4019 CURLcode res = CURLE_OK;
4020 #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
4021 enum protection_level data_sec = conn->data_prot;
4026 vsnprintf(s, SBUF_SIZE-3, fmt, ap);
4029 strcat(s, "\r\n"); /* append a trailing CRLF */
4032 write_len = strlen(s);
4034 res = Curl_convert_to_network(conn->data, s, write_len);
4035 /* Curl_convert_to_network calls failf if unsuccessful */
4040 #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
4041 conn->data_prot = PROT_CMD;
4043 res = Curl_write(conn, conn->sock[FIRSTSOCKET], sptr, write_len,
4045 #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
4046 DEBUGASSERT(data_sec > PROT_NONE && data_sec < PROT_LAST);
4047 conn->data_prot = data_sec;
4053 if(conn->data->set.verbose)
4054 Curl_debug(conn->data, CURLINFO_HEADER_OUT,
4055 sptr, (size_t)bytes_written, conn);
4057 if(bytes_written != (ssize_t)write_len) {
4058 write_len -= bytes_written;
4059 sptr += bytes_written;
4068 /***********************************************************************
4072 * This should be called before calling sclose() on an ftp control connection
4073 * (not data connections). We should then wait for the response from the
4074 * server before returning. The calling code should then try to close the
4078 static CURLcode ftp_quit(struct connectdata *conn)
4080 CURLcode result = CURLE_OK;
4082 if(conn->proto.ftpc.ctl_valid) {
4083 result = Curl_pp_sendf(&conn->proto.ftpc.pp, "QUIT", NULL);
4085 failf(conn->data, "Failure sending QUIT command: %s",
4086 curl_easy_strerror(result));
4087 conn->proto.ftpc.ctl_valid = FALSE; /* mark control connection as bad */
4088 conn->bits.close = TRUE; /* mark for connection closure */
4089 state(conn, FTP_STOP);
4093 state(conn, FTP_QUIT);
4095 result = ftp_easy_statemach(conn);
4101 /***********************************************************************
4105 * Disconnect from an FTP server. Cleanup protocol-specific per-connection
4106 * resources. BLOCKING.
4108 static CURLcode ftp_disconnect(struct connectdata *conn, bool dead_connection)
4110 struct ftp_conn *ftpc= &conn->proto.ftpc;
4111 struct pingpong *pp = &ftpc->pp;
4113 /* We cannot send quit unconditionally. If this connection is stale or
4114 bad in any way, sending quit and waiting around here will make the
4115 disconnect wait in vain and cause more problems than we need to.
4117 ftp_quit() will check the state of ftp->ctl_valid. If it's ok it
4118 will try to send the QUIT command, otherwise it will just return.
4121 ftpc->ctl_valid = FALSE;
4123 /* The FTP session may or may not have been allocated/setup at this point! */
4124 (void)ftp_quit(conn); /* ignore errors on the QUIT */
4126 if(ftpc->entrypath) {
4127 struct SessionHandle *data = conn->data;
4128 if(data->state.most_recent_ftp_entrypath == ftpc->entrypath) {
4129 data->state.most_recent_ftp_entrypath = NULL;
4131 free(ftpc->entrypath);
4132 ftpc->entrypath = NULL;
4136 if(ftpc->prevpath) {
4137 free(ftpc->prevpath);
4138 ftpc->prevpath = NULL;
4140 if(ftpc->server_os) {
4141 free(ftpc->server_os);
4142 ftpc->server_os = NULL;
4145 Curl_pp_disconnect(pp);
4147 #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
4154 /***********************************************************************
4156 * ftp_parse_url_path()
4158 * Parse the URL path into separate path components.
4162 CURLcode ftp_parse_url_path(struct connectdata *conn)
4164 struct SessionHandle *data = conn->data;
4165 /* the ftp struct is already inited in ftp_connect() */
4166 struct FTP *ftp = data->state.proto.ftp;
4167 struct ftp_conn *ftpc = &conn->proto.ftpc;
4168 const char *slash_pos; /* position of the first '/' char in curpos */
4169 const char *path_to_use = data->state.path;
4170 const char *cur_pos;
4171 const char *filename = NULL;
4173 cur_pos = path_to_use; /* current position in path. point at the begin
4174 of next path component */
4176 ftpc->ctl_valid = FALSE;
4177 ftpc->cwdfail = FALSE;
4179 switch(data->set.ftp_filemethod) {
4181 /* fastest, but less standard-compliant */
4184 The best time to check whether the path is a file or directory is right
4187 the first condition in the if() right here, is there just in case
4188 someone decides to set path to NULL one day
4190 if(data->state.path &&
4191 data->state.path[0] &&
4192 (data->state.path[strlen(data->state.path) - 1] != '/') )
4193 filename = data->state.path; /* this is a full file path */
4195 ftpc->file is not used anywhere other than for operations on a file.
4196 In other words, never for directory operations.
4197 So we can safely leave filename as NULL here and use it as a
4198 argument in dir/file decisions.
4202 case FTPFILE_SINGLECWD:
4203 /* get the last slash */
4204 if(!path_to_use[0]) {
4205 /* no dir, no file */
4209 slash_pos=strrchr(cur_pos, '/');
4210 if(slash_pos || !*cur_pos) {
4211 ftpc->dirs = calloc(1, sizeof(ftpc->dirs[0]));
4213 return CURLE_OUT_OF_MEMORY;
4215 ftpc->dirs[0] = curl_easy_unescape(conn->data, slash_pos ? cur_pos : "/",
4216 slash_pos?(int)(slash_pos-cur_pos):1,
4218 if(!ftpc->dirs[0]) {
4220 return CURLE_OUT_OF_MEMORY;
4222 ftpc->dirdepth = 1; /* we consider it to be a single dir */
4223 filename = slash_pos ? slash_pos+1 : cur_pos; /* rest is file name */
4226 filename = cur_pos; /* this is a file name only */
4229 default: /* allow pretty much anything */
4230 case FTPFILE_MULTICWD:
4232 ftpc->diralloc = 5; /* default dir depth to allocate */
4233 ftpc->dirs = calloc(ftpc->diralloc, sizeof(ftpc->dirs[0]));
4235 return CURLE_OUT_OF_MEMORY;
4237 /* we have a special case for listing the root dir only */
4238 if(strequal(path_to_use, "/")) {
4239 cur_pos++; /* make it point to the zero byte */
4240 ftpc->dirs[0] = strdup("/");
4244 /* parse the URL path into separate path components */
4245 while((slash_pos = strchr(cur_pos, '/')) != NULL) {
4246 /* 1 or 0 pointer offset to indicate absolute directory */
4247 ssize_t absolute_dir = ((cur_pos - data->state.path > 0) &&
4248 (ftpc->dirdepth == 0))?1:0;
4250 /* seek out the next path component */
4251 if(slash_pos-cur_pos) {
4252 /* we skip empty path components, like "x//y" since the FTP command
4253 CWD requires a parameter and a non-existent parameter a) doesn't
4254 work on many servers and b) has no effect on the others. */
4255 int len = (int)(slash_pos - cur_pos + absolute_dir);
4256 ftpc->dirs[ftpc->dirdepth] =
4257 curl_easy_unescape(conn->data, cur_pos - absolute_dir, len, NULL);
4258 if(!ftpc->dirs[ftpc->dirdepth]) { /* run out of memory ... */
4259 failf(data, "no memory");
4261 return CURLE_OUT_OF_MEMORY;
4263 if(isBadFtpString(ftpc->dirs[ftpc->dirdepth])) {
4264 free(ftpc->dirs[ftpc->dirdepth]);
4266 return CURLE_URL_MALFORMAT;
4270 cur_pos = slash_pos + 1; /* jump to the rest of the string */
4274 cur_pos = slash_pos + 1; /* jump to the rest of the string */
4275 if(++ftpc->dirdepth >= ftpc->diralloc) {
4278 ftpc->diralloc *= 2; /* double the size each time */
4279 bigger = realloc(ftpc->dirs, ftpc->diralloc * sizeof(ftpc->dirs[0]));
4282 return CURLE_OUT_OF_MEMORY;
4284 ftpc->dirs = bigger;
4288 filename = cur_pos; /* the rest is the file name */
4292 if(filename && *filename) {
4293 ftpc->file = curl_easy_unescape(conn->data, filename, 0, NULL);
4294 if(NULL == ftpc->file) {
4296 failf(data, "no memory");
4297 return CURLE_OUT_OF_MEMORY;
4299 if(isBadFtpString(ftpc->file)) {
4301 return CURLE_URL_MALFORMAT;
4305 ftpc->file=NULL; /* instead of point to a zero byte, we make it a NULL
4308 if(data->set.upload && !ftpc->file && (ftp->transfer == FTPTRANSFER_BODY)) {
4309 /* We need a file name when uploading. Return error! */
4310 failf(data, "Uploading to a URL without a file name!");
4311 return CURLE_URL_MALFORMAT;
4314 ftpc->cwddone = FALSE; /* default to not done */
4316 if(ftpc->prevpath) {
4317 /* prevpath is "raw" so we convert the input path before we compare the
4320 char *path = curl_easy_unescape(conn->data, data->state.path, 0, &dlen);
4323 return CURLE_OUT_OF_MEMORY;
4326 dlen -= ftpc->file?(int)strlen(ftpc->file):0;
4327 if((dlen == (int)strlen(ftpc->prevpath)) &&
4328 strnequal(path, ftpc->prevpath, dlen)) {
4329 infof(data, "Request has same path as previous transfer\n");
4330 ftpc->cwddone = TRUE;
4338 /* call this when the DO phase has completed */
4339 static CURLcode ftp_dophase_done(struct connectdata *conn,
4342 CURLcode result = CURLE_OK;
4343 struct FTP *ftp = conn->data->state.proto.ftp;
4344 struct ftp_conn *ftpc = &conn->proto.ftpc;
4347 result = ftp_nextconnect(conn);
4349 if(result && (conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD)) {
4350 /* Failure detected, close the second socket if it was created already */
4351 Curl_closesocket(conn, conn->sock[SECONDARYSOCKET]);
4352 conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
4356 if(ftp->transfer != FTPTRANSFER_BODY)
4357 /* no data to transfer */
4358 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
4360 /* since we didn't connect now, we want do_more to get called */
4361 conn->bits.do_more = TRUE;
4363 ftpc->ctl_valid = TRUE; /* seems good */
4368 /* called from multi.c while DOing */
4369 static CURLcode ftp_doing(struct connectdata *conn,
4372 CURLcode result = ftp_multi_statemach(conn, dophase_done);
4375 DEBUGF(infof(conn->data, "DO phase failed\n"));
4376 else if(*dophase_done) {
4377 result = ftp_dophase_done(conn, FALSE /* not connected */);
4379 DEBUGF(infof(conn->data, "DO phase is complete\n"));
4384 /***********************************************************************
4386 * ftp_regular_transfer()
4388 * The input argument is already checked for validity.
4390 * Performs all commands done before a regular transfer between a local and a
4393 * ftp->ctl_valid starts out as FALSE, and gets set to TRUE if we reach the
4394 * ftp_done() function without finding any major problem.
4397 CURLcode ftp_regular_transfer(struct connectdata *conn,
4400 CURLcode result=CURLE_OK;
4401 bool connected=FALSE;
4402 struct SessionHandle *data = conn->data;
4403 struct ftp_conn *ftpc = &conn->proto.ftpc;
4404 data->req.size = -1; /* make sure this is unknown at this point */
4406 Curl_pgrsSetUploadCounter(data, 0);
4407 Curl_pgrsSetDownloadCounter(data, 0);
4408 Curl_pgrsSetUploadSize(data, 0);
4409 Curl_pgrsSetDownloadSize(data, 0);
4411 ftpc->ctl_valid = TRUE; /* starts good */
4413 result = ftp_perform(conn,
4414 &connected, /* have we connected after PASV/PORT */
4415 dophase_done); /* all commands in the DO-phase done? */
4417 if(CURLE_OK == result) {
4420 /* the DO phase has not completed yet */
4423 result = ftp_dophase_done(conn, connected);
4433 static CURLcode ftp_setup_connection(struct connectdata * conn)
4435 struct SessionHandle *data = conn->data;
4439 if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
4440 /* Unless we have asked to tunnel ftp operations through the proxy, we
4441 switch and use HTTP operations only */
4442 #ifndef CURL_DISABLE_HTTP
4443 if(conn->handler == &Curl_handler_ftp)
4444 conn->handler = &Curl_handler_ftp_proxy;
4447 conn->handler = &Curl_handler_ftps_proxy;
4449 failf(data, "FTPS not supported!");
4450 return CURLE_UNSUPPORTED_PROTOCOL;
4454 * We explicitly mark this connection as persistent here as we're doing
4455 * FTP over HTTP and thus we accidentally avoid setting this value
4458 conn->bits.close = FALSE;
4460 failf(data, "FTP over http proxy requires HTTP support built-in!");
4461 return CURLE_UNSUPPORTED_PROTOCOL;
4465 data->state.path++; /* don't include the initial slash */
4466 data->state.slash_removed = TRUE; /* we've skipped the slash */
4468 /* FTP URLs support an extension like ";type=<typecode>" that
4469 * we'll try to get now! */
4470 type = strstr(data->state.path, ";type=");
4473 type = strstr(conn->host.rawalloc, ";type=");
4476 *type = 0; /* it was in the middle of the hostname */
4477 command = Curl_raw_toupper(type[6]);
4478 conn->bits.type_set = TRUE;
4481 case 'A': /* ASCII mode */
4482 data->set.prefer_ascii = TRUE;
4485 case 'D': /* directory mode */
4486 data->set.ftp_list_only = TRUE;
4489 case 'I': /* binary mode */
4491 /* switch off ASCII */
4492 data->set.prefer_ascii = FALSE;
4500 #endif /* CURL_DISABLE_FTP */