1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
9  *
10  * This software is licensed as described in the file COPYING, which
11  * you should have received as part of this distribution. The terms
12  * are also available at https://curl.se/docs/copyright.html.
13  *
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.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
21  ***************************************************************************/
22 
23 #include "curl_setup.h"
24 
25 #ifndef CURL_DISABLE_FTP
26 
27 #ifdef HAVE_NETINET_IN_H
28 #include <netinet/in.h>
29 #endif
30 #ifdef HAVE_ARPA_INET_H
31 #include <arpa/inet.h>
32 #endif
33 #ifdef HAVE_UTSNAME_H
34 #include <sys/utsname.h>
35 #endif
36 #ifdef HAVE_NETDB_H
37 #include <netdb.h>
38 #endif
39 #ifdef __VMS
40 #include <in.h>
41 #include <inet.h>
42 #endif
43 
44 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
45 #undef in_addr_t
46 #define in_addr_t unsigned long
47 #endif
48 
49 #include <curl/curl.h>
50 #include "urldata.h"
51 #include "sendf.h"
52 #include "if2ip.h"
53 #include "hostip.h"
54 #include "progress.h"
55 #include "transfer.h"
56 #include "escape.h"
57 #include "http.h" /* for HTTP proxy tunnel stuff */
58 #include "ftp.h"
59 #include "fileinfo.h"
60 #include "ftplistparser.h"
61 #include "curl_range.h"
62 #include "curl_krb5.h"
63 #include "strtoofft.h"
64 #include "strcase.h"
65 #include "vtls/vtls.h"
66 #include "connect.h"
67 #include "strerror.h"
68 #include "inet_ntop.h"
69 #include "inet_pton.h"
70 #include "select.h"
71 #include "parsedate.h" /* for the week day and month names */
72 #include "sockaddr.h" /* required for Curl_sockaddr_storage */
73 #include "multiif.h"
74 #include "url.h"
75 #include "strcase.h"
76 #include "speedcheck.h"
77 #include "warnless.h"
78 #include "http_proxy.h"
79 #include "non-ascii.h"
80 #include "socks.h"
81 /* The last 3 #include files should be in this order */
82 #include "curl_printf.h"
83 #include "curl_memory.h"
84 #include "memdebug.h"
85 
86 #ifndef NI_MAXHOST
87 #define NI_MAXHOST 1025
88 #endif
89 #ifndef INET_ADDRSTRLEN
90 #define INET_ADDRSTRLEN 16
91 #endif
92 
93 #ifdef CURL_DISABLE_VERBOSE_STRINGS
94 #define ftp_pasv_verbose(a,b,c,d)  Curl_nop_stmt
95 #endif
96 
97 /* Local API functions */
98 #ifndef DEBUGBUILD
99 static void _state(struct connectdata *conn,
100                    ftpstate newstate);
101 #define state(x,y) _state(x,y)
102 #else
103 static void _state(struct connectdata *conn,
104                    ftpstate newstate,
105                    int lineno);
106 #define state(x,y) _state(x,y,__LINE__)
107 #endif
108 
109 static CURLcode ftp_sendquote(struct connectdata *conn,
110                               struct curl_slist *quote);
111 static CURLcode ftp_quit(struct connectdata *conn);
112 static CURLcode ftp_parse_url_path(struct connectdata *conn);
113 static CURLcode ftp_regular_transfer(struct connectdata *conn, bool *done);
114 #ifndef CURL_DISABLE_VERBOSE_STRINGS
115 static void ftp_pasv_verbose(struct connectdata *conn,
116                              struct Curl_addrinfo *ai,
117                              char *newhost, /* ascii version */
118                              int port);
119 #endif
120 static CURLcode ftp_state_prepare_transfer(struct connectdata *conn);
121 static CURLcode ftp_state_mdtm(struct connectdata *conn);
122 static CURLcode ftp_state_quote(struct connectdata *conn,
123                                 bool init, ftpstate instate);
124 static CURLcode ftp_nb_type(struct connectdata *conn,
125                             bool ascii, ftpstate newstate);
126 static int ftp_need_type(struct connectdata *conn,
127                          bool ascii);
128 static CURLcode ftp_do(struct connectdata *conn, bool *done);
129 static CURLcode ftp_done(struct connectdata *conn,
130                          CURLcode, bool premature);
131 static CURLcode ftp_connect(struct connectdata *conn, bool *done);
132 static CURLcode ftp_disconnect(struct connectdata *conn, bool dead_connection);
133 static CURLcode ftp_do_more(struct connectdata *conn, int *completed);
134 static CURLcode ftp_multi_statemach(struct connectdata *conn, bool *done);
135 static int ftp_getsock(struct connectdata *conn, curl_socket_t *socks);
136 static int ftp_domore_getsock(struct connectdata *conn, curl_socket_t *socks);
137 static CURLcode ftp_doing(struct connectdata *conn,
138                           bool *dophase_done);
139 static CURLcode ftp_setup_connection(struct connectdata *conn);
140 static CURLcode init_wc_data(struct connectdata *conn);
141 static CURLcode wc_statemach(struct connectdata *conn);
142 static void wc_data_dtor(void *ptr);
143 static CURLcode ftp_state_retr(struct connectdata *conn, curl_off_t filesize);
144 static CURLcode ftp_readresp(curl_socket_t sockfd,
145                              struct pingpong *pp,
146                              int *ftpcode,
147                              size_t *size);
148 static CURLcode ftp_dophase_done(struct connectdata *conn,
149                                  bool connected);
150 
151 /*
152  * FTP protocol handler.
153  */
154 
155 const struct Curl_handler Curl_handler_ftp = {
156   "FTP",                           /* scheme */
157   ftp_setup_connection,            /* setup_connection */
158   ftp_do,                          /* do_it */
159   ftp_done,                        /* done */
160   ftp_do_more,                     /* do_more */
161   ftp_connect,                     /* connect_it */
162   ftp_multi_statemach,             /* connecting */
163   ftp_doing,                       /* doing */
164   ftp_getsock,                     /* proto_getsock */
165   ftp_getsock,                     /* doing_getsock */
166   ftp_domore_getsock,              /* domore_getsock */
167   ZERO_NULL,                       /* perform_getsock */
168   ftp_disconnect,                  /* disconnect */
169   ZERO_NULL,                       /* readwrite */
170   ZERO_NULL,                       /* connection_check */
171   PORT_FTP,                        /* defport */
172   CURLPROTO_FTP,                   /* protocol */
173   CURLPROTO_FTP,                   /* family */
174   PROTOPT_DUAL | PROTOPT_CLOSEACTION | PROTOPT_NEEDSPWD |
175   PROTOPT_NOURLQUERY | PROTOPT_PROXY_AS_HTTP |
176   PROTOPT_WILDCARD /* flags */
177 };
178 
179 
180 #ifdef USE_SSL
181 /*
182  * FTPS protocol handler.
183  */
184 
185 const struct Curl_handler Curl_handler_ftps = {
186   "FTPS",                          /* scheme */
187   ftp_setup_connection,            /* setup_connection */
188   ftp_do,                          /* do_it */
189   ftp_done,                        /* done */
190   ftp_do_more,                     /* do_more */
191   ftp_connect,                     /* connect_it */
192   ftp_multi_statemach,             /* connecting */
193   ftp_doing,                       /* doing */
194   ftp_getsock,                     /* proto_getsock */
195   ftp_getsock,                     /* doing_getsock */
196   ftp_domore_getsock,              /* domore_getsock */
197   ZERO_NULL,                       /* perform_getsock */
198   ftp_disconnect,                  /* disconnect */
199   ZERO_NULL,                       /* readwrite */
200   ZERO_NULL,                       /* connection_check */
201   PORT_FTPS,                       /* defport */
202   CURLPROTO_FTPS,                  /* protocol */
203   CURLPROTO_FTP,                   /* family */
204   PROTOPT_SSL | PROTOPT_DUAL | PROTOPT_CLOSEACTION |
205   PROTOPT_NEEDSPWD | PROTOPT_NOURLQUERY | PROTOPT_WILDCARD /* flags */
206 };
207 #endif
208 
close_secondarysocket(struct connectdata * conn)209 static void close_secondarysocket(struct connectdata *conn)
210 {
211   if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET]) {
212     Curl_closesocket(conn, conn->sock[SECONDARYSOCKET]);
213     conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
214   }
215   conn->bits.tcpconnect[SECONDARYSOCKET] = FALSE;
216 #ifndef CURL_DISABLE_PROXY
217   conn->bits.proxy_ssl_connected[SECONDARYSOCKET] = FALSE;
218 #endif
219 }
220 
221 /*
222  * NOTE: back in the old days, we added code in the FTP code that made NOBODY
223  * requests on files respond with headers passed to the client/stdout that
224  * looked like HTTP ones.
225  *
226  * This approach is not very elegant, it causes confusion and is error-prone.
227  * It is subject for removal at the next (or at least a future) soname bump.
228  * Until then you can test the effects of the removal by undefining the
229  * following define named CURL_FTP_HTTPSTYLE_HEAD.
230  */
231 #define CURL_FTP_HTTPSTYLE_HEAD 1
232 
freedirs(struct ftp_conn * ftpc)233 static void freedirs(struct ftp_conn *ftpc)
234 {
235   if(ftpc->dirs) {
236     int i;
237     for(i = 0; i < ftpc->dirdepth; i++) {
238       free(ftpc->dirs[i]);
239       ftpc->dirs[i] = NULL;
240     }
241     free(ftpc->dirs);
242     ftpc->dirs = NULL;
243     ftpc->dirdepth = 0;
244   }
245   Curl_safefree(ftpc->file);
246 
247   /* no longer of any use */
248   Curl_safefree(ftpc->newhost);
249 }
250 
251 /***********************************************************************
252  *
253  * AcceptServerConnect()
254  *
255  * After connection request is received from the server this function is
256  * called to accept the connection and close the listening socket
257  *
258  */
AcceptServerConnect(struct connectdata * conn)259 static CURLcode AcceptServerConnect(struct connectdata *conn)
260 {
261   struct Curl_easy *data = conn->data;
262   curl_socket_t sock = conn->sock[SECONDARYSOCKET];
263   curl_socket_t s = CURL_SOCKET_BAD;
264 #ifdef ENABLE_IPV6
265   struct Curl_sockaddr_storage add;
266 #else
267   struct sockaddr_in add;
268 #endif
269   curl_socklen_t size = (curl_socklen_t) sizeof(add);
270 
271   if(0 == getsockname(sock, (struct sockaddr *) &add, &size)) {
272     size = sizeof(add);
273 
274     s = accept(sock, (struct sockaddr *) &add, &size);
275   }
276   Curl_closesocket(conn, sock); /* close the first socket */
277 
278   if(CURL_SOCKET_BAD == s) {
279     failf(data, "Error accept()ing server connect");
280     return CURLE_FTP_PORT_FAILED;
281   }
282   infof(data, "Connection accepted from server\n");
283   /* when this happens within the DO state it is important that we mark us as
284      not needing DO_MORE anymore */
285   conn->bits.do_more = FALSE;
286 
287   conn->sock[SECONDARYSOCKET] = s;
288   (void)curlx_nonblock(s, TRUE); /* enable non-blocking */
289   conn->bits.sock_accepted = TRUE;
290 
291   if(data->set.fsockopt) {
292     int error = 0;
293 
294     /* activate callback for setting socket options */
295     Curl_set_in_callback(data, true);
296     error = data->set.fsockopt(data->set.sockopt_client,
297                                s,
298                                CURLSOCKTYPE_ACCEPT);
299     Curl_set_in_callback(data, false);
300 
301     if(error) {
302       close_secondarysocket(conn);
303       return CURLE_ABORTED_BY_CALLBACK;
304     }
305   }
306 
307   return CURLE_OK;
308 
309 }
310 
311 /*
312  * ftp_timeleft_accept() returns the amount of milliseconds left allowed for
313  * waiting server to connect. If the value is negative, the timeout time has
314  * already elapsed.
315  *
316  * The start time is stored in progress.t_acceptdata - as set with
317  * Curl_pgrsTime(..., TIMER_STARTACCEPT);
318  *
319  */
ftp_timeleft_accept(struct Curl_easy * data)320 static timediff_t ftp_timeleft_accept(struct Curl_easy *data)
321 {
322   timediff_t timeout_ms = DEFAULT_ACCEPT_TIMEOUT;
323   timediff_t other;
324   struct curltime now;
325 
326   if(data->set.accepttimeout > 0)
327     timeout_ms = data->set.accepttimeout;
328 
329   now = Curl_now();
330 
331   /* check if the generic timeout possibly is set shorter */
332   other = Curl_timeleft(data, &now, FALSE);
333   if(other && (other < timeout_ms))
334     /* note that this also works fine for when other happens to be negative
335        due to it already having elapsed */
336     timeout_ms = other;
337   else {
338     /* subtract elapsed time */
339     timeout_ms -= Curl_timediff(now, data->progress.t_acceptdata);
340     if(!timeout_ms)
341       /* avoid returning 0 as that means no timeout! */
342       return -1;
343   }
344 
345   return timeout_ms;
346 }
347 
348 
349 /***********************************************************************
350  *
351  * ReceivedServerConnect()
352  *
353  * After allowing server to connect to us from data port, this function
354  * checks both data connection for connection establishment and ctrl
355  * connection for a negative response regarding a failure in connecting
356  *
357  */
ReceivedServerConnect(struct connectdata * conn,bool * received)358 static CURLcode ReceivedServerConnect(struct connectdata *conn, bool *received)
359 {
360   struct Curl_easy *data = conn->data;
361   curl_socket_t ctrl_sock = conn->sock[FIRSTSOCKET];
362   curl_socket_t data_sock = conn->sock[SECONDARYSOCKET];
363   struct ftp_conn *ftpc = &conn->proto.ftpc;
364   struct pingpong *pp = &ftpc->pp;
365   int result;
366   timediff_t timeout_ms;
367   ssize_t nread;
368   int ftpcode;
369 
370   *received = FALSE;
371 
372   timeout_ms = ftp_timeleft_accept(data);
373   infof(data, "Checking for server connect\n");
374   if(timeout_ms < 0) {
375     /* if a timeout was already reached, bail out */
376     failf(data, "Accept timeout occurred while waiting server connect");
377     return CURLE_FTP_ACCEPT_TIMEOUT;
378   }
379 
380   /* First check whether there is a cached response from server */
381   if(pp->cache_size && pp->cache && pp->cache[0] > '3') {
382     /* Data connection could not be established, let's return */
383     infof(data, "There is negative response in cache while serv connect\n");
384     (void)Curl_GetFTPResponse(&nread, conn, &ftpcode);
385     return CURLE_FTP_ACCEPT_FAILED;
386   }
387 
388   result = Curl_socket_check(ctrl_sock, data_sock, CURL_SOCKET_BAD, 0);
389 
390   /* see if the connection request is already here */
391   switch(result) {
392   case -1: /* error */
393     /* let's die here */
394     failf(data, "Error while waiting for server connect");
395     return CURLE_FTP_ACCEPT_FAILED;
396   case 0:  /* Server connect is not received yet */
397     break; /* loop */
398   default:
399 
400     if(result & CURL_CSELECT_IN2) {
401       infof(data, "Ready to accept data connection from server\n");
402       *received = TRUE;
403     }
404     else if(result & CURL_CSELECT_IN) {
405       infof(data, "Ctrl conn has data while waiting for data conn\n");
406       (void)Curl_GetFTPResponse(&nread, conn, &ftpcode);
407 
408       if(ftpcode/100 > 3)
409         return CURLE_FTP_ACCEPT_FAILED;
410 
411       return CURLE_WEIRD_SERVER_REPLY;
412     }
413 
414     break;
415   } /* switch() */
416 
417   return CURLE_OK;
418 }
419 
420 
421 /***********************************************************************
422  *
423  * InitiateTransfer()
424  *
425  * After connection from server is accepted this function is called to
426  * setup transfer parameters and initiate the data transfer.
427  *
428  */
InitiateTransfer(struct connectdata * conn)429 static CURLcode InitiateTransfer(struct connectdata *conn)
430 {
431   struct Curl_easy *data = conn->data;
432   CURLcode result = CURLE_OK;
433 
434   if(conn->bits.ftp_use_data_ssl) {
435     /* since we only have a plaintext TCP connection here, we must now
436      * do the TLS stuff */
437     infof(data, "Doing the SSL/TLS handshake on the data stream\n");
438     result = Curl_ssl_connect(conn, SECONDARYSOCKET);
439     if(result)
440       return result;
441   }
442 
443   if(conn->proto.ftpc.state_saved == FTP_STOR) {
444     /* When we know we're uploading a specified file, we can get the file
445        size prior to the actual upload. */
446     Curl_pgrsSetUploadSize(data, data->state.infilesize);
447 
448     /* set the SO_SNDBUF for the secondary socket for those who need it */
449     Curl_sndbufset(conn->sock[SECONDARYSOCKET]);
450 
451     Curl_setup_transfer(data, -1, -1, FALSE, SECONDARYSOCKET);
452   }
453   else {
454     /* FTP download: */
455     Curl_setup_transfer(data, SECONDARYSOCKET,
456                         conn->proto.ftpc.retr_size_saved, FALSE, -1);
457   }
458 
459   conn->proto.ftpc.pp.pending_resp = TRUE; /* expect server response */
460   state(conn, FTP_STOP);
461 
462   return CURLE_OK;
463 }
464 
465 /***********************************************************************
466  *
467  * AllowServerConnect()
468  *
469  * When we've issue the PORT command, we have told the server to connect to
470  * us. This function checks whether data connection is established if so it is
471  * accepted.
472  *
473  */
AllowServerConnect(struct connectdata * conn,bool * connected)474 static CURLcode AllowServerConnect(struct connectdata *conn, bool *connected)
475 {
476   struct Curl_easy *data = conn->data;
477   timediff_t timeout_ms;
478   CURLcode result = CURLE_OK;
479 
480   *connected = FALSE;
481   infof(data, "Preparing for accepting server on data port\n");
482 
483   /* Save the time we start accepting server connect */
484   Curl_pgrsTime(data, TIMER_STARTACCEPT);
485 
486   timeout_ms = ftp_timeleft_accept(data);
487   if(timeout_ms < 0) {
488     /* if a timeout was already reached, bail out */
489     failf(data, "Accept timeout occurred while waiting server connect");
490     return CURLE_FTP_ACCEPT_TIMEOUT;
491   }
492 
493   /* see if the connection request is already here */
494   result = ReceivedServerConnect(conn, connected);
495   if(result)
496     return result;
497 
498   if(*connected) {
499     result = AcceptServerConnect(conn);
500     if(result)
501       return result;
502 
503     result = InitiateTransfer(conn);
504     if(result)
505       return result;
506   }
507   else {
508     /* Add timeout to multi handle and break out of the loop */
509     if(*connected == FALSE) {
510       Curl_expire(data, data->set.accepttimeout > 0 ?
511                   data->set.accepttimeout: DEFAULT_ACCEPT_TIMEOUT, 0);
512     }
513   }
514 
515   return result;
516 }
517 
518 /* macro to check for a three-digit ftp status code at the start of the
519    given string */
520 #define STATUSCODE(line) (ISDIGIT(line[0]) && ISDIGIT(line[1]) &&       \
521                           ISDIGIT(line[2]))
522 
523 /* macro to check for the last line in an FTP server response */
524 #define LASTLINE(line) (STATUSCODE(line) && (' ' == line[3]))
525 
ftp_endofresp(struct connectdata * conn,char * line,size_t len,int * code)526 static bool ftp_endofresp(struct connectdata *conn, char *line, size_t len,
527                           int *code)
528 {
529   (void)conn;
530 
531   if((len > 3) && LASTLINE(line)) {
532     *code = curlx_sltosi(strtol(line, NULL, 10));
533     return TRUE;
534   }
535 
536   return FALSE;
537 }
538 
ftp_readresp(curl_socket_t sockfd,struct pingpong * pp,int * ftpcode,size_t * size)539 static CURLcode ftp_readresp(curl_socket_t sockfd,
540                              struct pingpong *pp,
541                              int *ftpcode, /* return the ftp-code if done */
542                              size_t *size) /* size of the response */
543 {
544   struct connectdata *conn = pp->conn;
545   struct Curl_easy *data = conn->data;
546 #ifdef HAVE_GSSAPI
547   char * const buf = data->state.buffer;
548 #endif
549   int code;
550   CURLcode result = Curl_pp_readresp(sockfd, pp, &code, size);
551 
552 #if defined(HAVE_GSSAPI)
553   /* handle the security-oriented responses 6xx ***/
554   switch(code) {
555   case 631:
556     code = Curl_sec_read_msg(conn, buf, PROT_SAFE);
557     break;
558   case 632:
559     code = Curl_sec_read_msg(conn, buf, PROT_PRIVATE);
560     break;
561   case 633:
562     code = Curl_sec_read_msg(conn, buf, PROT_CONFIDENTIAL);
563     break;
564   default:
565     /* normal ftp stuff we pass through! */
566     break;
567   }
568 #endif
569 
570   /* store the latest code for later retrieval */
571   data->info.httpcode = code;
572 
573   if(ftpcode)
574     *ftpcode = code;
575 
576   if(421 == code) {
577     /* 421 means "Service not available, closing control connection." and FTP
578      * servers use it to signal that idle session timeout has been exceeded.
579      * If we ignored the response, it could end up hanging in some cases.
580      *
581      * This response code can come at any point so having it treated
582      * generically is a good idea.
583      */
584     infof(data, "We got a 421 - timeout!\n");
585     state(conn, FTP_STOP);
586     return CURLE_OPERATION_TIMEDOUT;
587   }
588 
589   return result;
590 }
591 
592 /* --- parse FTP server responses --- */
593 
594 /*
595  * Curl_GetFTPResponse() is a BLOCKING function to read the full response
596  * from a server after a command.
597  *
598  */
599 
Curl_GetFTPResponse(ssize_t * nreadp,struct connectdata * conn,int * ftpcode)600 CURLcode Curl_GetFTPResponse(ssize_t *nreadp, /* return number of bytes read */
601                              struct connectdata *conn,
602                              int *ftpcode) /* return the ftp-code */
603 {
604   /*
605    * We cannot read just one byte per read() and then go back to select() as
606    * the OpenSSL read() doesn't grok that properly.
607    *
608    * Alas, read as much as possible, split up into lines, use the ending
609    * line in a response or continue reading.  */
610 
611   curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
612   struct Curl_easy *data = conn->data;
613   CURLcode result = CURLE_OK;
614   struct ftp_conn *ftpc = &conn->proto.ftpc;
615   struct pingpong *pp = &ftpc->pp;
616   size_t nread;
617   int cache_skip = 0;
618   int value_to_be_ignored = 0;
619 
620   if(ftpcode)
621     *ftpcode = 0; /* 0 for errors */
622   else
623     /* make the pointer point to something for the rest of this function */
624     ftpcode = &value_to_be_ignored;
625 
626   *nreadp = 0;
627 
628   while(!*ftpcode && !result) {
629     /* check and reset timeout value every lap */
630     timediff_t timeout = Curl_pp_state_timeout(pp, FALSE);
631     timediff_t interval_ms;
632 
633     if(timeout <= 0) {
634       failf(data, "FTP response timeout");
635       return CURLE_OPERATION_TIMEDOUT; /* already too little time */
636     }
637 
638     interval_ms = 1000;  /* use 1 second timeout intervals */
639     if(timeout < interval_ms)
640       interval_ms = timeout;
641 
642     /*
643      * Since this function is blocking, we need to wait here for input on the
644      * connection and only then we call the response reading function. We do
645      * timeout at least every second to make the timeout check run.
646      *
647      * A caution here is that the ftp_readresp() function has a cache that may
648      * contain pieces of a response from the previous invoke and we need to
649      * make sure we don't just wait for input while there is unhandled data in
650      * that cache. But also, if the cache is there, we call ftp_readresp() and
651      * the cache wasn't good enough to continue we must not just busy-loop
652      * around this function.
653      *
654      */
655 
656     if(pp->cache && (cache_skip < 2)) {
657       /*
658        * There's a cache left since before. We then skipping the wait for
659        * socket action, unless this is the same cache like the previous round
660        * as then the cache was deemed not enough to act on and we then need to
661        * wait for more data anyway.
662        */
663     }
664     else if(!Curl_conn_data_pending(conn, FIRSTSOCKET)) {
665       switch(SOCKET_READABLE(sockfd, interval_ms)) {
666       case -1: /* select() error, stop reading */
667         failf(data, "FTP response aborted due to select/poll error: %d",
668               SOCKERRNO);
669         return CURLE_RECV_ERROR;
670 
671       case 0: /* timeout */
672         if(Curl_pgrsUpdate(conn))
673           return CURLE_ABORTED_BY_CALLBACK;
674         continue; /* just continue in our loop for the timeout duration */
675 
676       default: /* for clarity */
677         break;
678       }
679     }
680     result = ftp_readresp(sockfd, pp, ftpcode, &nread);
681     if(result)
682       break;
683 
684     if(!nread && pp->cache)
685       /* bump cache skip counter as on repeated skips we must wait for more
686          data */
687       cache_skip++;
688     else
689       /* when we got data or there is no cache left, we reset the cache skip
690          counter */
691       cache_skip = 0;
692 
693     *nreadp += nread;
694 
695   } /* while there's buffer left and loop is requested */
696 
697   pp->pending_resp = FALSE;
698 
699   return result;
700 }
701 
702 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
703   /* for debug purposes */
704 static const char * const ftp_state_names[]={
705   "STOP",
706   "WAIT220",
707   "AUTH",
708   "USER",
709   "PASS",
710   "ACCT",
711   "PBSZ",
712   "PROT",
713   "CCC",
714   "PWD",
715   "SYST",
716   "NAMEFMT",
717   "QUOTE",
718   "RETR_PREQUOTE",
719   "STOR_PREQUOTE",
720   "POSTQUOTE",
721   "CWD",
722   "MKD",
723   "MDTM",
724   "TYPE",
725   "LIST_TYPE",
726   "RETR_TYPE",
727   "STOR_TYPE",
728   "SIZE",
729   "RETR_SIZE",
730   "STOR_SIZE",
731   "REST",
732   "RETR_REST",
733   "PORT",
734   "PRET",
735   "PASV",
736   "LIST",
737   "RETR",
738   "STOR",
739   "QUIT"
740 };
741 #endif
742 
743 /* This is the ONLY way to change FTP state! */
_state(struct connectdata * conn,ftpstate newstate,int lineno)744 static void _state(struct connectdata *conn,
745                    ftpstate newstate
746 #ifdef DEBUGBUILD
747                    , int lineno
748 #endif
749   )
750 {
751   struct ftp_conn *ftpc = &conn->proto.ftpc;
752 
753 #if defined(DEBUGBUILD)
754 
755 #if defined(CURL_DISABLE_VERBOSE_STRINGS)
756   (void) lineno;
757 #else
758   if(ftpc->state != newstate)
759     infof(conn->data, "FTP %p (line %d) state change from %s to %s\n",
760           (void *)ftpc, lineno, ftp_state_names[ftpc->state],
761           ftp_state_names[newstate]);
762 #endif
763 #endif
764 
765   ftpc->state = newstate;
766 }
767 
ftp_state_user(struct connectdata * conn)768 static CURLcode ftp_state_user(struct connectdata *conn)
769 {
770   CURLcode result = Curl_pp_sendf(&conn->proto.ftpc.pp, "USER %s",
771                                   conn->user?conn->user:"");
772   if(!result) {
773     state(conn, FTP_USER);
774     conn->data->state.ftp_trying_alternative = FALSE;
775   }
776   return result;
777 }
778 
ftp_state_pwd(struct connectdata * conn)779 static CURLcode ftp_state_pwd(struct connectdata *conn)
780 {
781   CURLcode result = Curl_pp_sendf(&conn->proto.ftpc.pp, "%s", "PWD");
782   if(!result)
783     state(conn, FTP_PWD);
784 
785   return result;
786 }
787 
788 /* For the FTP "protocol connect" and "doing" phases only */
ftp_getsock(struct connectdata * conn,curl_socket_t * socks)789 static int ftp_getsock(struct connectdata *conn,
790                        curl_socket_t *socks)
791 {
792   return Curl_pp_getsock(&conn->proto.ftpc.pp, socks);
793 }
794 
795 /* For the FTP "DO_MORE" phase only */
ftp_domore_getsock(struct connectdata * conn,curl_socket_t * socks)796 static int ftp_domore_getsock(struct connectdata *conn, curl_socket_t *socks)
797 {
798   struct ftp_conn *ftpc = &conn->proto.ftpc;
799 
800   /* When in DO_MORE state, we could be either waiting for us to connect to a
801    * remote site, or we could wait for that site to connect to us. Or just
802    * handle ordinary commands.
803    */
804 
805   if(SOCKS_STATE(conn->cnnct.state))
806     return Curl_SOCKS_getsock(conn, socks, SECONDARYSOCKET);
807 
808   if(FTP_STOP == ftpc->state) {
809     int bits = GETSOCK_READSOCK(0);
810     bool any = FALSE;
811 
812     /* if stopped and still in this state, then we're also waiting for a
813        connect on the secondary connection */
814     socks[0] = conn->sock[FIRSTSOCKET];
815 
816     if(!conn->data->set.ftp_use_port) {
817       int s;
818       int i;
819       /* PORT is used to tell the server to connect to us, and during that we
820          don't do happy eyeballs, but we do if we connect to the server */
821       for(s = 1, i = 0; i<2; i++) {
822         if(conn->tempsock[i] != CURL_SOCKET_BAD) {
823           socks[s] = conn->tempsock[i];
824           bits |= GETSOCK_WRITESOCK(s++);
825           any = TRUE;
826         }
827       }
828     }
829     if(!any) {
830       socks[1] = conn->sock[SECONDARYSOCKET];
831       bits |= GETSOCK_WRITESOCK(1) | GETSOCK_READSOCK(1);
832     }
833 
834     return bits;
835   }
836   return Curl_pp_getsock(&conn->proto.ftpc.pp, socks);
837 }
838 
839 /* This is called after the FTP_QUOTE state is passed.
840 
841    ftp_state_cwd() sends the range of CWD commands to the server to change to
842    the correct directory. It may also need to send MKD commands to create
843    missing ones, if that option is enabled.
844 */
ftp_state_cwd(struct connectdata * conn)845 static CURLcode ftp_state_cwd(struct connectdata *conn)
846 {
847   CURLcode result = CURLE_OK;
848   struct ftp_conn *ftpc = &conn->proto.ftpc;
849 
850   if(ftpc->cwddone)
851     /* already done and fine */
852     result = ftp_state_mdtm(conn);
853   else {
854     /* FTPFILE_NOCWD with full path: expect ftpc->cwddone! */
855     DEBUGASSERT((conn->data->set.ftp_filemethod != FTPFILE_NOCWD) ||
856                 !(ftpc->dirdepth && ftpc->dirs[0][0] == '/'));
857 
858     ftpc->count2 = 0; /* count2 counts failed CWDs */
859 
860     /* count3 is set to allow a MKD to fail once. In the case when first CWD
861        fails and then MKD fails (due to another session raced it to create the
862        dir) this then allows for a second try to CWD to it */
863     ftpc->count3 = (conn->data->set.ftp_create_missing_dirs == 2)?1:0;
864 
865     if(conn->bits.reuse && ftpc->entrypath &&
866        /* no need to go to entrypath when we have an absolute path */
867        !(ftpc->dirdepth && ftpc->dirs[0][0] == '/')) {
868       /* This is a re-used connection. Since we change directory to where the
869          transfer is taking place, we must first get back to the original dir
870          where we ended up after login: */
871       ftpc->cwdcount = 0; /* we count this as the first path, then we add one
872                              for all upcoming ones in the ftp->dirs[] array */
873       result = Curl_pp_sendf(&ftpc->pp, "CWD %s", ftpc->entrypath);
874       if(!result)
875         state(conn, FTP_CWD);
876     }
877     else {
878       if(ftpc->dirdepth) {
879         ftpc->cwdcount = 1;
880         /* issue the first CWD, the rest is sent when the CWD responses are
881            received... */
882         result = Curl_pp_sendf(&ftpc->pp, "CWD %s",
883                                ftpc->dirs[ftpc->cwdcount -1]);
884         if(!result)
885           state(conn, FTP_CWD);
886       }
887       else {
888         /* No CWD necessary */
889         result = ftp_state_mdtm(conn);
890       }
891     }
892   }
893   return result;
894 }
895 
896 typedef enum {
897   EPRT,
898   PORT,
899   DONE
900 } ftpport;
901 
ftp_state_use_port(struct connectdata * conn,ftpport fcmd)902 static CURLcode ftp_state_use_port(struct connectdata *conn,
903                                    ftpport fcmd) /* start with this */
904 {
905   CURLcode result = CURLE_OK;
906   struct ftp_conn *ftpc = &conn->proto.ftpc;
907   struct Curl_easy *data = conn->data;
908   curl_socket_t portsock = CURL_SOCKET_BAD;
909   char myhost[MAX_IPADR_LEN + 1] = "";
910 
911   struct Curl_sockaddr_storage ss;
912   struct Curl_addrinfo *res, *ai;
913   curl_socklen_t sslen;
914   char hbuf[NI_MAXHOST];
915   struct sockaddr *sa = (struct sockaddr *)&ss;
916   struct sockaddr_in * const sa4 = (void *)sa;
917 #ifdef ENABLE_IPV6
918   struct sockaddr_in6 * const sa6 = (void *)sa;
919 #endif
920   static const char mode[][5] = { "EPRT", "PORT" };
921   enum resolve_t rc;
922   int error;
923   char *host = NULL;
924   char *string_ftpport = data->set.str[STRING_FTPPORT];
925   struct Curl_dns_entry *h = NULL;
926   unsigned short port_min = 0;
927   unsigned short port_max = 0;
928   unsigned short port;
929   bool possibly_non_local = TRUE;
930   char buffer[STRERROR_LEN];
931   char *addr = NULL;
932 
933   /* Step 1, figure out what is requested,
934    * accepted format :
935    * (ipv4|ipv6|domain|interface)?(:port(-range)?)?
936    */
937 
938   if(data->set.str[STRING_FTPPORT] &&
939      (strlen(data->set.str[STRING_FTPPORT]) > 1)) {
940 
941 #ifdef ENABLE_IPV6
942     size_t addrlen = INET6_ADDRSTRLEN > strlen(string_ftpport) ?
943       INET6_ADDRSTRLEN : strlen(string_ftpport);
944 #else
945     size_t addrlen = INET_ADDRSTRLEN > strlen(string_ftpport) ?
946       INET_ADDRSTRLEN : strlen(string_ftpport);
947 #endif
948     char *ip_start = string_ftpport;
949     char *ip_end = NULL;
950     char *port_start = NULL;
951     char *port_sep = NULL;
952 
953     addr = calloc(addrlen + 1, 1);
954     if(!addr)
955       return CURLE_OUT_OF_MEMORY;
956 
957 #ifdef ENABLE_IPV6
958     if(*string_ftpport == '[') {
959       /* [ipv6]:port(-range) */
960       ip_start = string_ftpport + 1;
961       ip_end = strchr(string_ftpport, ']');
962       if(ip_end)
963         strncpy(addr, ip_start, ip_end - ip_start);
964     }
965     else
966 #endif
967       if(*string_ftpport == ':') {
968         /* :port */
969         ip_end = string_ftpport;
970       }
971       else {
972         ip_end = strchr(string_ftpport, ':');
973         if(ip_end) {
974           /* either ipv6 or (ipv4|domain|interface):port(-range) */
975 #ifdef ENABLE_IPV6
976           if(Curl_inet_pton(AF_INET6, string_ftpport, sa6) == 1) {
977             /* ipv6 */
978             port_min = port_max = 0;
979             strcpy(addr, string_ftpport);
980             ip_end = NULL; /* this got no port ! */
981           }
982           else
983 #endif
984             /* (ipv4|domain|interface):port(-range) */
985             strncpy(addr, string_ftpport, ip_end - ip_start);
986         }
987         else
988           /* ipv4|interface */
989           strcpy(addr, string_ftpport);
990       }
991 
992     /* parse the port */
993     if(ip_end != NULL) {
994       port_start = strchr(ip_end, ':');
995       if(port_start) {
996         port_min = curlx_ultous(strtoul(port_start + 1, NULL, 10));
997         port_sep = strchr(port_start, '-');
998         if(port_sep) {
999           port_max = curlx_ultous(strtoul(port_sep + 1, NULL, 10));
1000         }
1001         else
1002           port_max = port_min;
1003       }
1004     }
1005 
1006     /* correct errors like:
1007      *  :1234-1230
1008      *  :-4711,  in this case port_min is (unsigned)-1,
1009      *           therefore port_min > port_max for all cases
1010      *           but port_max = (unsigned)-1
1011      */
1012     if(port_min > port_max)
1013       port_min = port_max = 0;
1014 
1015 
1016     if(*addr != '\0') {
1017       /* attempt to get the address of the given interface name */
1018       switch(Curl_if2ip(conn->ip_addr->ai_family,
1019                         Curl_ipv6_scope(conn->ip_addr->ai_addr),
1020                         conn->scope_id, addr, hbuf, sizeof(hbuf))) {
1021         case IF2IP_NOT_FOUND:
1022           /* not an interface, use the given string as host name instead */
1023           host = addr;
1024           break;
1025         case IF2IP_AF_NOT_SUPPORTED:
1026           return CURLE_FTP_PORT_FAILED;
1027         case IF2IP_FOUND:
1028           host = hbuf; /* use the hbuf for host name */
1029       }
1030     }
1031     else
1032       /* there was only a port(-range) given, default the host */
1033       host = NULL;
1034   } /* data->set.ftpport */
1035 
1036   if(!host) {
1037     const char *r;
1038     /* not an interface and not a host name, get default by extracting
1039        the IP from the control connection */
1040     sslen = sizeof(ss);
1041     if(getsockname(conn->sock[FIRSTSOCKET], sa, &sslen)) {
1042       failf(data, "getsockname() failed: %s",
1043             Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
1044       free(addr);
1045       return CURLE_FTP_PORT_FAILED;
1046     }
1047     switch(sa->sa_family) {
1048 #ifdef ENABLE_IPV6
1049     case AF_INET6:
1050       r = Curl_inet_ntop(sa->sa_family, &sa6->sin6_addr, hbuf, sizeof(hbuf));
1051       break;
1052 #endif
1053     default:
1054       r = Curl_inet_ntop(sa->sa_family, &sa4->sin_addr, hbuf, sizeof(hbuf));
1055       break;
1056     }
1057     if(!r)
1058       return CURLE_FTP_PORT_FAILED;
1059     host = hbuf; /* use this host name */
1060     possibly_non_local = FALSE; /* we know it is local now */
1061   }
1062 
1063   /* resolv ip/host to ip */
1064   rc = Curl_resolv(conn, host, 0, FALSE, &h);
1065   if(rc == CURLRESOLV_PENDING)
1066     (void)Curl_resolver_wait_resolv(conn, &h);
1067   if(h) {
1068     res = h->addr;
1069     /* when we return from this function, we can forget about this entry
1070        to we can unlock it now already */
1071     Curl_resolv_unlock(data, h);
1072   } /* (h) */
1073   else
1074     res = NULL; /* failure! */
1075 
1076   if(res == NULL) {
1077     failf(data, "failed to resolve the address provided to PORT: %s", host);
1078     free(addr);
1079     return CURLE_FTP_PORT_FAILED;
1080   }
1081 
1082   free(addr);
1083   host = NULL;
1084 
1085   /* step 2, create a socket for the requested address */
1086 
1087   portsock = CURL_SOCKET_BAD;
1088   error = 0;
1089   for(ai = res; ai; ai = ai->ai_next) {
1090     result = Curl_socket(conn, ai, NULL, &portsock);
1091     if(result) {
1092       error = SOCKERRNO;
1093       continue;
1094     }
1095     break;
1096   }
1097   if(!ai) {
1098     failf(data, "socket failure: %s",
1099           Curl_strerror(error, buffer, sizeof(buffer)));
1100     return CURLE_FTP_PORT_FAILED;
1101   }
1102 
1103   /* step 3, bind to a suitable local address */
1104 
1105   memcpy(sa, ai->ai_addr, ai->ai_addrlen);
1106   sslen = ai->ai_addrlen;
1107 
1108   for(port = port_min; port <= port_max;) {
1109     if(sa->sa_family == AF_INET)
1110       sa4->sin_port = htons(port);
1111 #ifdef ENABLE_IPV6
1112     else
1113       sa6->sin6_port = htons(port);
1114 #endif
1115     /* Try binding the given address. */
1116     if(bind(portsock, sa, sslen) ) {
1117       /* It failed. */
1118       error = SOCKERRNO;
1119       if(possibly_non_local && (error == EADDRNOTAVAIL)) {
1120         /* The requested bind address is not local.  Use the address used for
1121          * the control connection instead and restart the port loop
1122          */
1123         infof(data, "bind(port=%hu) on non-local address failed: %s\n", port,
1124               Curl_strerror(error, buffer, sizeof(buffer)));
1125 
1126         sslen = sizeof(ss);
1127         if(getsockname(conn->sock[FIRSTSOCKET], sa, &sslen)) {
1128           failf(data, "getsockname() failed: %s",
1129                 Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
1130           Curl_closesocket(conn, portsock);
1131           return CURLE_FTP_PORT_FAILED;
1132         }
1133         port = port_min;
1134         possibly_non_local = FALSE; /* don't try this again */
1135         continue;
1136       }
1137       if(error != EADDRINUSE && error != EACCES) {
1138         failf(data, "bind(port=%hu) failed: %s", port,
1139               Curl_strerror(error, buffer, sizeof(buffer)));
1140         Curl_closesocket(conn, portsock);
1141         return CURLE_FTP_PORT_FAILED;
1142       }
1143     }
1144     else
1145       break;
1146 
1147     port++;
1148   }
1149 
1150   /* maybe all ports were in use already*/
1151   if(port > port_max) {
1152     failf(data, "bind() failed, we ran out of ports!");
1153     Curl_closesocket(conn, portsock);
1154     return CURLE_FTP_PORT_FAILED;
1155   }
1156 
1157   /* get the name again after the bind() so that we can extract the
1158      port number it uses now */
1159   sslen = sizeof(ss);
1160   if(getsockname(portsock, (struct sockaddr *)sa, &sslen)) {
1161     failf(data, "getsockname() failed: %s",
1162           Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
1163     Curl_closesocket(conn, portsock);
1164     return CURLE_FTP_PORT_FAILED;
1165   }
1166 
1167   /* step 4, listen on the socket */
1168 
1169   if(listen(portsock, 1)) {
1170     failf(data, "socket failure: %s",
1171           Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
1172     Curl_closesocket(conn, portsock);
1173     return CURLE_FTP_PORT_FAILED;
1174   }
1175 
1176   /* step 5, send the proper FTP command */
1177 
1178   /* get a plain printable version of the numerical address to work with
1179      below */
1180   Curl_printable_address(ai, myhost, sizeof(myhost));
1181 
1182 #ifdef ENABLE_IPV6
1183   if(!conn->bits.ftp_use_eprt && conn->bits.ipv6)
1184     /* EPRT is disabled but we are connected to a IPv6 host, so we ignore the
1185        request and enable EPRT again! */
1186     conn->bits.ftp_use_eprt = TRUE;
1187 #endif
1188 
1189   for(; fcmd != DONE; fcmd++) {
1190 
1191     if(!conn->bits.ftp_use_eprt && (EPRT == fcmd))
1192       /* if disabled, goto next */
1193       continue;
1194 
1195     if((PORT == fcmd) && sa->sa_family != AF_INET)
1196       /* PORT is IPv4 only */
1197       continue;
1198 
1199     switch(sa->sa_family) {
1200     case AF_INET:
1201       port = ntohs(sa4->sin_port);
1202       break;
1203 #ifdef ENABLE_IPV6
1204     case AF_INET6:
1205       port = ntohs(sa6->sin6_port);
1206       break;
1207 #endif
1208     default:
1209       continue; /* might as well skip this */
1210     }
1211 
1212     if(EPRT == fcmd) {
1213       /*
1214        * Two fine examples from RFC2428;
1215        *
1216        * EPRT |1|132.235.1.2|6275|
1217        *
1218        * EPRT |2|1080::8:800:200C:417A|5282|
1219        */
1220 
1221       result = Curl_pp_sendf(&ftpc->pp, "%s |%d|%s|%hu|", mode[fcmd],
1222                              sa->sa_family == AF_INET?1:2,
1223                              myhost, port);
1224       if(result) {
1225         failf(data, "Failure sending EPRT command: %s",
1226               curl_easy_strerror(result));
1227         Curl_closesocket(conn, portsock);
1228         /* don't retry using PORT */
1229         ftpc->count1 = PORT;
1230         /* bail out */
1231         state(conn, FTP_STOP);
1232         return result;
1233       }
1234       break;
1235     }
1236     if(PORT == fcmd) {
1237       /* large enough for [IP address],[num],[num] */
1238       char target[sizeof(myhost) + 20];
1239       char *source = myhost;
1240       char *dest = target;
1241 
1242       /* translate x.x.x.x to x,x,x,x */
1243       while(source && *source) {
1244         if(*source == '.')
1245           *dest = ',';
1246         else
1247           *dest = *source;
1248         dest++;
1249         source++;
1250       }
1251       *dest = 0;
1252       msnprintf(dest, 20, ",%d,%d", (int)(port>>8), (int)(port&0xff));
1253 
1254       result = Curl_pp_sendf(&ftpc->pp, "%s %s", mode[fcmd], target);
1255       if(result) {
1256         failf(data, "Failure sending PORT command: %s",
1257               curl_easy_strerror(result));
1258         Curl_closesocket(conn, portsock);
1259         /* bail out */
1260         state(conn, FTP_STOP);
1261         return result;
1262       }
1263       break;
1264     }
1265   }
1266 
1267   /* store which command was sent */
1268   ftpc->count1 = fcmd;
1269 
1270   close_secondarysocket(conn);
1271 
1272   /* we set the secondary socket variable to this for now, it is only so that
1273      the cleanup function will close it in case we fail before the true
1274      secondary stuff is made */
1275   conn->sock[SECONDARYSOCKET] = portsock;
1276 
1277   /* this tcpconnect assignment below is a hackish work-around to make the
1278      multi interface with active FTP work - as it will not wait for a
1279      (passive) connect in Curl_is_connected().
1280 
1281      The *proper* fix is to make sure that the active connection from the
1282      server is done in a non-blocking way. Currently, it is still BLOCKING.
1283   */
1284   conn->bits.tcpconnect[SECONDARYSOCKET] = TRUE;
1285 
1286   state(conn, FTP_PORT);
1287   return result;
1288 }
1289 
ftp_state_use_pasv(struct connectdata * conn)1290 static CURLcode ftp_state_use_pasv(struct connectdata *conn)
1291 {
1292   struct ftp_conn *ftpc = &conn->proto.ftpc;
1293   CURLcode result = CURLE_OK;
1294   /*
1295     Here's the executive summary on what to do:
1296 
1297     PASV is RFC959, expect:
1298     227 Entering Passive Mode (a1,a2,a3,a4,p1,p2)
1299 
1300     LPSV is RFC1639, expect:
1301     228 Entering Long Passive Mode (4,4,a1,a2,a3,a4,2,p1,p2)
1302 
1303     EPSV is RFC2428, expect:
1304     229 Entering Extended Passive Mode (|||port|)
1305 
1306   */
1307 
1308   static const char mode[][5] = { "EPSV", "PASV" };
1309   int modeoff;
1310 
1311 #ifdef PF_INET6
1312   if(!conn->bits.ftp_use_epsv && conn->bits.ipv6)
1313     /* EPSV is disabled but we are connected to a IPv6 host, so we ignore the
1314        request and enable EPSV again! */
1315     conn->bits.ftp_use_epsv = TRUE;
1316 #endif
1317 
1318   modeoff = conn->bits.ftp_use_epsv?0:1;
1319 
1320   result = Curl_pp_sendf(&ftpc->pp, "%s", mode[modeoff]);
1321   if(!result) {
1322     ftpc->count1 = modeoff;
1323     state(conn, FTP_PASV);
1324     infof(conn->data, "Connect data stream passively\n");
1325   }
1326   return result;
1327 }
1328 
1329 /*
1330  * ftp_state_prepare_transfer() starts PORT, PASV or PRET etc.
1331  *
1332  * REST is the last command in the chain of commands when a "head"-like
1333  * request is made. Thus, if an actual transfer is to be made this is where we
1334  * take off for real.
1335  */
ftp_state_prepare_transfer(struct connectdata * conn)1336 static CURLcode ftp_state_prepare_transfer(struct connectdata *conn)
1337 {
1338   CURLcode result = CURLE_OK;
1339   struct FTP *ftp = conn->data->req.p.ftp;
1340   struct Curl_easy *data = conn->data;
1341 
1342   if(ftp->transfer != FTPTRANSFER_BODY) {
1343     /* doesn't transfer any data */
1344 
1345     /* still possibly do PRE QUOTE jobs */
1346     state(conn, FTP_RETR_PREQUOTE);
1347     result = ftp_state_quote(conn, TRUE, FTP_RETR_PREQUOTE);
1348   }
1349   else if(data->set.ftp_use_port) {
1350     /* We have chosen to use the PORT (or similar) command */
1351     result = ftp_state_use_port(conn, EPRT);
1352   }
1353   else {
1354     /* We have chosen (this is default) to use the PASV (or similar) command */
1355     if(data->set.ftp_use_pret) {
1356       /* The user has requested that we send a PRET command
1357          to prepare the server for the upcoming PASV */
1358       struct ftp_conn *ftpc = &conn->proto.ftpc;
1359       if(!conn->proto.ftpc.file)
1360         result = Curl_pp_sendf(&ftpc->pp, "PRET %s",
1361                                data->set.str[STRING_CUSTOMREQUEST]?
1362                                data->set.str[STRING_CUSTOMREQUEST]:
1363                                (data->set.ftp_list_only?"NLST":"LIST"));
1364       else if(data->set.upload)
1365         result = Curl_pp_sendf(&ftpc->pp, "PRET STOR %s",
1366                                conn->proto.ftpc.file);
1367       else
1368         result = Curl_pp_sendf(&ftpc->pp, "PRET RETR %s",
1369                                conn->proto.ftpc.file);
1370       if(!result)
1371         state(conn, FTP_PRET);
1372     }
1373     else
1374       result = ftp_state_use_pasv(conn);
1375   }
1376   return result;
1377 }
1378 
ftp_state_rest(struct connectdata * conn)1379 static CURLcode ftp_state_rest(struct connectdata *conn)
1380 {
1381   CURLcode result = CURLE_OK;
1382   struct FTP *ftp = conn->data->req.p.ftp;
1383   struct ftp_conn *ftpc = &conn->proto.ftpc;
1384 
1385   if((ftp->transfer != FTPTRANSFER_BODY) && ftpc->file) {
1386     /* if a "head"-like request is being made (on a file) */
1387 
1388     /* Determine if server can respond to REST command and therefore
1389        whether it supports range */
1390     result = Curl_pp_sendf(&ftpc->pp, "REST %d", 0);
1391     if(!result)
1392       state(conn, FTP_REST);
1393   }
1394   else
1395     result = ftp_state_prepare_transfer(conn);
1396 
1397   return result;
1398 }
1399 
ftp_state_size(struct connectdata * conn)1400 static CURLcode ftp_state_size(struct connectdata *conn)
1401 {
1402   CURLcode result = CURLE_OK;
1403   struct FTP *ftp = conn->data->req.p.ftp;
1404   struct ftp_conn *ftpc = &conn->proto.ftpc;
1405 
1406   if((ftp->transfer == FTPTRANSFER_INFO) && ftpc->file) {
1407     /* if a "head"-like request is being made (on a file) */
1408 
1409     /* we know ftpc->file is a valid pointer to a file name */
1410     result = Curl_pp_sendf(&ftpc->pp, "SIZE %s", ftpc->file);
1411     if(!result)
1412       state(conn, FTP_SIZE);
1413   }
1414   else
1415     result = ftp_state_rest(conn);
1416 
1417   return result;
1418 }
1419 
ftp_state_list(struct connectdata * conn)1420 static CURLcode ftp_state_list(struct connectdata *conn)
1421 {
1422   CURLcode result = CURLE_OK;
1423   struct Curl_easy *data = conn->data;
1424   struct FTP *ftp = data->req.p.ftp;
1425 
1426   /* If this output is to be machine-parsed, the NLST command might be better
1427      to use, since the LIST command output is not specified or standard in any
1428      way. It has turned out that the NLST list output is not the same on all
1429      servers either... */
1430 
1431   /*
1432      if FTPFILE_NOCWD was specified, we should add the path
1433      as argument for the LIST / NLST / or custom command.
1434      Whether the server will support this, is uncertain.
1435 
1436      The other ftp_filemethods will CWD into dir/dir/ first and
1437      then just do LIST (in that case: nothing to do here)
1438   */
1439   char *lstArg = NULL;
1440   char *cmd;
1441 
1442   if((data->set.ftp_filemethod == FTPFILE_NOCWD) && ftp->path) {
1443     /* url-decode before evaluation: e.g. paths starting/ending with %2f */
1444     const char *slashPos = NULL;
1445     char *rawPath = NULL;
1446     result = Curl_urldecode(data, ftp->path, 0, &rawPath, NULL, REJECT_CTRL);
1447     if(result)
1448       return result;
1449 
1450     slashPos = strrchr(rawPath, '/');
1451     if(slashPos) {
1452       /* chop off the file part if format is dir/file otherwise remove
1453          the trailing slash for dir/dir/ except for absolute path / */
1454       size_t n = slashPos - rawPath;
1455       if(n == 0)
1456         ++n;
1457 
1458       lstArg = rawPath;
1459       lstArg[n] = '\0';
1460     }
1461     else
1462       free(rawPath);
1463   }
1464 
1465   cmd = aprintf("%s%s%s",
1466                 data->set.str[STRING_CUSTOMREQUEST]?
1467                 data->set.str[STRING_CUSTOMREQUEST]:
1468                 (data->set.ftp_list_only?"NLST":"LIST"),
1469                 lstArg? " ": "",
1470                 lstArg? lstArg: "");
1471   free(lstArg);
1472 
1473   if(!cmd)
1474     return CURLE_OUT_OF_MEMORY;
1475 
1476   result = Curl_pp_sendf(&conn->proto.ftpc.pp, "%s", cmd);
1477   free(cmd);
1478 
1479   if(!result)
1480     state(conn, FTP_LIST);
1481 
1482   return result;
1483 }
1484 
ftp_state_retr_prequote(struct connectdata * conn)1485 static CURLcode ftp_state_retr_prequote(struct connectdata *conn)
1486 {
1487   /* We've sent the TYPE, now we must send the list of prequote strings */
1488   return ftp_state_quote(conn, TRUE, FTP_RETR_PREQUOTE);
1489 }
1490 
ftp_state_stor_prequote(struct connectdata * conn)1491 static CURLcode ftp_state_stor_prequote(struct connectdata *conn)
1492 {
1493   /* We've sent the TYPE, now we must send the list of prequote strings */
1494   return ftp_state_quote(conn, TRUE, FTP_STOR_PREQUOTE);
1495 }
1496 
ftp_state_type(struct connectdata * conn)1497 static CURLcode ftp_state_type(struct connectdata *conn)
1498 {
1499   CURLcode result = CURLE_OK;
1500   struct FTP *ftp = conn->data->req.p.ftp;
1501   struct Curl_easy *data = conn->data;
1502   struct ftp_conn *ftpc = &conn->proto.ftpc;
1503 
1504   /* If we have selected NOBODY and HEADER, it means that we only want file
1505      information. Which in FTP can't be much more than the file size and
1506      date. */
1507   if(data->set.opt_no_body && ftpc->file &&
1508      ftp_need_type(conn, data->set.prefer_ascii)) {
1509     /* The SIZE command is _not_ RFC 959 specified, and therefore many servers
1510        may not support it! It is however the only way we have to get a file's
1511        size! */
1512 
1513     ftp->transfer = FTPTRANSFER_INFO;
1514     /* this means no actual transfer will be made */
1515 
1516     /* Some servers return different sizes for different modes, and thus we
1517        must set the proper type before we check the size */
1518     result = ftp_nb_type(conn, data->set.prefer_ascii, FTP_TYPE);
1519     if(result)
1520       return result;
1521   }
1522   else
1523     result = ftp_state_size(conn);
1524 
1525   return result;
1526 }
1527 
1528 /* This is called after the CWD commands have been done in the beginning of
1529    the DO phase */
ftp_state_mdtm(struct connectdata * conn)1530 static CURLcode ftp_state_mdtm(struct connectdata *conn)
1531 {
1532   CURLcode result = CURLE_OK;
1533   struct Curl_easy *data = conn->data;
1534   struct ftp_conn *ftpc = &conn->proto.ftpc;
1535 
1536   /* Requested time of file or time-depended transfer? */
1537   if((data->set.get_filetime || data->set.timecondition) && ftpc->file) {
1538 
1539     /* we have requested to get the modified-time of the file, this is a white
1540        spot as the MDTM is not mentioned in RFC959 */
1541     result = Curl_pp_sendf(&ftpc->pp, "MDTM %s", ftpc->file);
1542 
1543     if(!result)
1544       state(conn, FTP_MDTM);
1545   }
1546   else
1547     result = ftp_state_type(conn);
1548 
1549   return result;
1550 }
1551 
1552 
1553 /* This is called after the TYPE and possible quote commands have been sent */
ftp_state_ul_setup(struct connectdata * conn,bool sizechecked)1554 static CURLcode ftp_state_ul_setup(struct connectdata *conn,
1555                                    bool sizechecked)
1556 {
1557   CURLcode result = CURLE_OK;
1558   struct FTP *ftp = conn->data->req.p.ftp;
1559   struct Curl_easy *data = conn->data;
1560   struct ftp_conn *ftpc = &conn->proto.ftpc;
1561 
1562   if((data->state.resume_from && !sizechecked) ||
1563      ((data->state.resume_from > 0) && sizechecked)) {
1564     /* we're about to continue the uploading of a file */
1565     /* 1. get already existing file's size. We use the SIZE command for this
1566        which may not exist in the server!  The SIZE command is not in
1567        RFC959. */
1568 
1569     /* 2. This used to set REST. But since we can do append, we
1570        don't another ftp command. We just skip the source file
1571        offset and then we APPEND the rest on the file instead */
1572 
1573     /* 3. pass file-size number of bytes in the source file */
1574     /* 4. lower the infilesize counter */
1575     /* => transfer as usual */
1576     int seekerr = CURL_SEEKFUNC_OK;
1577 
1578     if(data->state.resume_from < 0) {
1579       /* Got no given size to start from, figure it out */
1580       result = Curl_pp_sendf(&ftpc->pp, "SIZE %s", ftpc->file);
1581       if(!result)
1582         state(conn, FTP_STOR_SIZE);
1583       return result;
1584     }
1585 
1586     /* enable append */
1587     data->set.ftp_append = TRUE;
1588 
1589     /* Let's read off the proper amount of bytes from the input. */
1590     if(conn->seek_func) {
1591       Curl_set_in_callback(data, true);
1592       seekerr = conn->seek_func(conn->seek_client, data->state.resume_from,
1593                                 SEEK_SET);
1594       Curl_set_in_callback(data, false);
1595     }
1596 
1597     if(seekerr != CURL_SEEKFUNC_OK) {
1598       curl_off_t passed = 0;
1599       if(seekerr != CURL_SEEKFUNC_CANTSEEK) {
1600         failf(data, "Could not seek stream");
1601         return CURLE_FTP_COULDNT_USE_REST;
1602       }
1603       /* seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */
1604       do {
1605         size_t readthisamountnow =
1606           (data->state.resume_from - passed > data->set.buffer_size) ?
1607           (size_t)data->set.buffer_size :
1608           curlx_sotouz(data->state.resume_from - passed);
1609 
1610         size_t actuallyread =
1611           data->state.fread_func(data->state.buffer, 1, readthisamountnow,
1612                                  data->state.in);
1613 
1614         passed += actuallyread;
1615         if((actuallyread == 0) || (actuallyread > readthisamountnow)) {
1616           /* this checks for greater-than only to make sure that the
1617              CURL_READFUNC_ABORT return code still aborts */
1618           failf(data, "Failed to read data");
1619           return CURLE_FTP_COULDNT_USE_REST;
1620         }
1621       } while(passed < data->state.resume_from);
1622     }
1623     /* now, decrease the size of the read */
1624     if(data->state.infilesize>0) {
1625       data->state.infilesize -= data->state.resume_from;
1626 
1627       if(data->state.infilesize <= 0) {
1628         infof(data, "File already completely uploaded\n");
1629 
1630         /* no data to transfer */
1631         Curl_setup_transfer(data, -1, -1, FALSE, -1);
1632 
1633         /* Set ->transfer so that we won't get any error in
1634          * ftp_done() because we didn't transfer anything! */
1635         ftp->transfer = FTPTRANSFER_NONE;
1636 
1637         state(conn, FTP_STOP);
1638         return CURLE_OK;
1639       }
1640     }
1641     /* we've passed, proceed as normal */
1642   } /* resume_from */
1643 
1644   result = Curl_pp_sendf(&ftpc->pp, data->set.ftp_append?"APPE %s":"STOR %s",
1645                          ftpc->file);
1646   if(!result)
1647     state(conn, FTP_STOR);
1648 
1649   return result;
1650 }
1651 
ftp_state_quote(struct connectdata * conn,bool init,ftpstate instate)1652 static CURLcode ftp_state_quote(struct connectdata *conn,
1653                                 bool init,
1654                                 ftpstate instate)
1655 {
1656   CURLcode result = CURLE_OK;
1657   struct Curl_easy *data = conn->data;
1658   struct FTP *ftp = data->req.p.ftp;
1659   struct ftp_conn *ftpc = &conn->proto.ftpc;
1660   bool quote = FALSE;
1661   struct curl_slist *item;
1662 
1663   switch(instate) {
1664   case FTP_QUOTE:
1665   default:
1666     item = data->set.quote;
1667     break;
1668   case FTP_RETR_PREQUOTE:
1669   case FTP_STOR_PREQUOTE:
1670     item = data->set.prequote;
1671     break;
1672   case FTP_POSTQUOTE:
1673     item = data->set.postquote;
1674     break;
1675   }
1676 
1677   /*
1678    * This state uses:
1679    * 'count1' to iterate over the commands to send
1680    * 'count2' to store whether to allow commands to fail
1681    */
1682 
1683   if(init)
1684     ftpc->count1 = 0;
1685   else
1686     ftpc->count1++;
1687 
1688   if(item) {
1689     int i = 0;
1690 
1691     /* Skip count1 items in the linked list */
1692     while((i< ftpc->count1) && item) {
1693       item = item->next;
1694       i++;
1695     }
1696     if(item) {
1697       char *cmd = item->data;
1698       if(cmd[0] == '*') {
1699         cmd++;
1700         ftpc->count2 = 1; /* the sent command is allowed to fail */
1701       }
1702       else
1703         ftpc->count2 = 0; /* failure means cancel operation */
1704 
1705       result = Curl_pp_sendf(&ftpc->pp, "%s", cmd);
1706       if(result)
1707         return result;
1708       state(conn, instate);
1709       quote = TRUE;
1710     }
1711   }
1712 
1713   if(!quote) {
1714     /* No more quote to send, continue to ... */
1715     switch(instate) {
1716     case FTP_QUOTE:
1717     default:
1718       result = ftp_state_cwd(conn);
1719       break;
1720     case FTP_RETR_PREQUOTE:
1721       if(ftp->transfer != FTPTRANSFER_BODY)
1722         state(conn, FTP_STOP);
1723       else {
1724         if(ftpc->known_filesize != -1) {
1725           Curl_pgrsSetDownloadSize(data, ftpc->known_filesize);
1726           result = ftp_state_retr(conn, ftpc->known_filesize);
1727         }
1728         else {
1729           if(data->set.ignorecl) {
1730             /* This code is to support download of growing files.  It prevents
1731                the state machine from requesting the file size from the
1732                server.  With an unknown file size the download continues until
1733                the server terminates it, otherwise the client stops if the
1734                received byte count exceeds the reported file size.  Set option
1735                CURLOPT_IGNORE_CONTENT_LENGTH to 1 to enable this behavior.*/
1736             result = Curl_pp_sendf(&ftpc->pp, "RETR %s", ftpc->file);
1737             if(!result)
1738               state(conn, FTP_RETR);
1739           }
1740           else {
1741             result = Curl_pp_sendf(&ftpc->pp, "SIZE %s", ftpc->file);
1742             if(!result)
1743               state(conn, FTP_RETR_SIZE);
1744           }
1745         }
1746       }
1747       break;
1748     case FTP_STOR_PREQUOTE:
1749       result = ftp_state_ul_setup(conn, FALSE);
1750       break;
1751     case FTP_POSTQUOTE:
1752       break;
1753     }
1754   }
1755 
1756   return result;
1757 }
1758 
1759 /* called from ftp_state_pasv_resp to switch to PASV in case of EPSV
1760    problems */
ftp_epsv_disable(struct connectdata * conn)1761 static CURLcode ftp_epsv_disable(struct connectdata *conn)
1762 {
1763   CURLcode result = CURLE_OK;
1764 
1765   if(conn->bits.ipv6
1766 #ifndef CURL_DISABLE_PROXY
1767      && !(conn->bits.tunnel_proxy || conn->bits.socksproxy)
1768 #endif
1769     ) {
1770     /* We can't disable EPSV when doing IPv6, so this is instead a fail */
1771     failf(conn->data, "Failed EPSV attempt, exiting\n");
1772     return CURLE_WEIRD_SERVER_REPLY;
1773   }
1774 
1775   infof(conn->data, "Failed EPSV attempt. Disabling EPSV\n");
1776   /* disable it for next transfer */
1777   conn->bits.ftp_use_epsv = FALSE;
1778   conn->data->state.errorbuf = FALSE; /* allow error message to get
1779                                          rewritten */
1780   result = Curl_pp_sendf(&conn->proto.ftpc.pp, "%s", "PASV");
1781   if(!result) {
1782     conn->proto.ftpc.count1++;
1783     /* remain in/go to the FTP_PASV state */
1784     state(conn, FTP_PASV);
1785   }
1786   return result;
1787 }
1788 
1789 
control_address(struct connectdata * conn)1790 static char *control_address(struct connectdata *conn)
1791 {
1792   /* Returns the control connection IP address.
1793      If a proxy tunnel is used, returns the original host name instead, because
1794      the effective control connection address is the proxy address,
1795      not the ftp host. */
1796 #ifndef CURL_DISABLE_PROXY
1797   if(conn->bits.tunnel_proxy || conn->bits.socksproxy)
1798     return conn->host.name;
1799 #endif
1800   return conn->ip_addr_str;
1801 }
1802 
ftp_state_pasv_resp(struct connectdata * conn,int ftpcode)1803 static CURLcode ftp_state_pasv_resp(struct connectdata *conn,
1804                                     int ftpcode)
1805 {
1806   struct ftp_conn *ftpc = &conn->proto.ftpc;
1807   CURLcode result;
1808   struct Curl_easy *data = conn->data;
1809   struct Curl_dns_entry *addr = NULL;
1810   enum resolve_t rc;
1811   unsigned short connectport; /* the local port connect() should use! */
1812   char *str = &data->state.buffer[4];  /* start on the first letter */
1813 
1814   /* if we come here again, make sure the former name is cleared */
1815   Curl_safefree(ftpc->newhost);
1816 
1817   if((ftpc->count1 == 0) &&
1818      (ftpcode == 229)) {
1819     /* positive EPSV response */
1820     char *ptr = strchr(str, '(');
1821     if(ptr) {
1822       unsigned int num;
1823       char separator[4];
1824       ptr++;
1825       if(5 == sscanf(ptr, "%c%c%c%u%c",
1826                      &separator[0],
1827                      &separator[1],
1828                      &separator[2],
1829                      &num,
1830                      &separator[3])) {
1831         const char sep1 = separator[0];
1832         int i;
1833 
1834         /* The four separators should be identical, or else this is an oddly
1835            formatted reply and we bail out immediately. */
1836         for(i = 1; i<4; i++) {
1837           if(separator[i] != sep1) {
1838             ptr = NULL; /* set to NULL to signal error */
1839             break;
1840           }
1841         }
1842         if(num > 0xffff) {
1843           failf(data, "Illegal port number in EPSV reply");
1844           return CURLE_FTP_WEIRD_PASV_REPLY;
1845         }
1846         if(ptr) {
1847           ftpc->newport = (unsigned short)(num & 0xffff);
1848           ftpc->newhost = strdup(control_address(conn));
1849           if(!ftpc->newhost)
1850             return CURLE_OUT_OF_MEMORY;
1851         }
1852       }
1853       else
1854         ptr = NULL;
1855     }
1856     if(!ptr) {
1857       failf(data, "Weirdly formatted EPSV reply");
1858       return CURLE_FTP_WEIRD_PASV_REPLY;
1859     }
1860   }
1861   else if((ftpc->count1 == 1) &&
1862           (ftpcode == 227)) {
1863     /* positive PASV response */
1864     unsigned int ip[4] = {0, 0, 0, 0};
1865     unsigned int port[2] = {0, 0};
1866 
1867     /*
1868      * Scan for a sequence of six comma-separated numbers and use them as
1869      * IP+port indicators.
1870      *
1871      * Found reply-strings include:
1872      * "227 Entering Passive Mode (127,0,0,1,4,51)"
1873      * "227 Data transfer will passively listen to 127,0,0,1,4,51"
1874      * "227 Entering passive mode. 127,0,0,1,4,51"
1875      */
1876     while(*str) {
1877       if(6 == sscanf(str, "%u,%u,%u,%u,%u,%u",
1878                      &ip[0], &ip[1], &ip[2], &ip[3],
1879                      &port[0], &port[1]))
1880         break;
1881       str++;
1882     }
1883 
1884     if(!*str || (ip[0] > 255) || (ip[1] > 255)  || (ip[2] > 255)  ||
1885        (ip[3] > 255) || (port[0] > 255)  || (port[1] > 255) ) {
1886       failf(data, "Couldn't interpret the 227-response");
1887       return CURLE_FTP_WEIRD_227_FORMAT;
1888     }
1889 
1890     /* we got OK from server */
1891     if(data->set.ftp_skip_ip) {
1892       /* told to ignore the remotely given IP but instead use the host we used
1893          for the control connection */
1894       infof(data, "Skip %u.%u.%u.%u for data connection, re-use %s instead\n",
1895             ip[0], ip[1], ip[2], ip[3],
1896             conn->host.name);
1897       ftpc->newhost = strdup(control_address(conn));
1898     }
1899     else
1900       ftpc->newhost = aprintf("%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]);
1901 
1902     if(!ftpc->newhost)
1903       return CURLE_OUT_OF_MEMORY;
1904 
1905     ftpc->newport = (unsigned short)(((port[0]<<8) + port[1]) & 0xffff);
1906   }
1907   else if(ftpc->count1 == 0) {
1908     /* EPSV failed, move on to PASV */
1909     return ftp_epsv_disable(conn);
1910   }
1911   else {
1912     failf(data, "Bad PASV/EPSV response: %03d", ftpcode);
1913     return CURLE_FTP_WEIRD_PASV_REPLY;
1914   }
1915 
1916 #ifndef CURL_DISABLE_PROXY
1917   if(conn->bits.proxy) {
1918     /*
1919      * This connection uses a proxy and we need to connect to the proxy again
1920      * here. We don't want to rely on a former host lookup that might've
1921      * expired now, instead we remake the lookup here and now!
1922      */
1923     const char * const host_name = conn->bits.socksproxy ?
1924       conn->socks_proxy.host.name : conn->http_proxy.host.name;
1925     rc = Curl_resolv(conn, host_name, (int)conn->port, FALSE, &addr);
1926     if(rc == CURLRESOLV_PENDING)
1927       /* BLOCKING, ignores the return code but 'addr' will be NULL in
1928          case of failure */
1929       (void)Curl_resolver_wait_resolv(conn, &addr);
1930 
1931     connectport =
1932       (unsigned short)conn->port; /* we connect to the proxy's port */
1933 
1934     if(!addr) {
1935       failf(data, "Can't resolve proxy host %s:%hu", host_name, connectport);
1936       return CURLE_COULDNT_RESOLVE_PROXY;
1937     }
1938   }
1939   else
1940 #endif
1941   {
1942     /* normal, direct, ftp connection */
1943     DEBUGASSERT(ftpc->newhost);
1944 
1945     /* postponed address resolution in case of tcp fastopen */
1946     if(conn->bits.tcp_fastopen && !conn->bits.reuse && !ftpc->newhost[0]) {
1947       Curl_conninfo_remote(conn, conn->sock[FIRSTSOCKET]);
1948       Curl_safefree(ftpc->newhost);
1949       ftpc->newhost = strdup(control_address(conn));
1950       if(!ftpc->newhost)
1951         return CURLE_OUT_OF_MEMORY;
1952     }
1953 
1954     rc = Curl_resolv(conn, ftpc->newhost, ftpc->newport, FALSE, &addr);
1955     if(rc == CURLRESOLV_PENDING)
1956       /* BLOCKING */
1957       (void)Curl_resolver_wait_resolv(conn, &addr);
1958 
1959     connectport = ftpc->newport; /* we connect to the remote port */
1960 
1961     if(!addr) {
1962       failf(data, "Can't resolve new host %s:%hu", ftpc->newhost, connectport);
1963       return CURLE_FTP_CANT_GET_HOST;
1964     }
1965   }
1966 
1967   conn->bits.tcpconnect[SECONDARYSOCKET] = FALSE;
1968   result = Curl_connecthost(conn, addr);
1969 
1970   if(result) {
1971     Curl_resolv_unlock(data, addr); /* we're done using this address */
1972     if(ftpc->count1 == 0 && ftpcode == 229)
1973       return ftp_epsv_disable(conn);
1974 
1975     return result;
1976   }
1977 
1978 
1979   /*
1980    * When this is used from the multi interface, this might've returned with
1981    * the 'connected' set to FALSE and thus we are now awaiting a non-blocking
1982    * connect to connect.
1983    */
1984 
1985   if(data->set.verbose)
1986     /* this just dumps information about this second connection */
1987     ftp_pasv_verbose(conn, addr->addr, ftpc->newhost, connectport);
1988 
1989   Curl_resolv_unlock(data, addr); /* we're done using this address */
1990 
1991   Curl_safefree(conn->secondaryhostname);
1992   conn->secondary_port = ftpc->newport;
1993   conn->secondaryhostname = strdup(ftpc->newhost);
1994   if(!conn->secondaryhostname)
1995     return CURLE_OUT_OF_MEMORY;
1996 
1997   conn->bits.do_more = TRUE;
1998   state(conn, FTP_STOP); /* this phase is completed */
1999 
2000   return result;
2001 }
2002 
ftp_state_port_resp(struct connectdata * conn,int ftpcode)2003 static CURLcode ftp_state_port_resp(struct connectdata *conn,
2004                                     int ftpcode)
2005 {
2006   struct Curl_easy *data = conn->data;
2007   struct ftp_conn *ftpc = &conn->proto.ftpc;
2008   ftpport fcmd = (ftpport)ftpc->count1;
2009   CURLcode result = CURLE_OK;
2010 
2011   /* The FTP spec tells a positive response should have code 200.
2012      Be more permissive here to tolerate deviant servers. */
2013   if(ftpcode / 100 != 2) {
2014     /* the command failed */
2015 
2016     if(EPRT == fcmd) {
2017       infof(data, "disabling EPRT usage\n");
2018       conn->bits.ftp_use_eprt = FALSE;
2019     }
2020     fcmd++;
2021 
2022     if(fcmd == DONE) {
2023       failf(data, "Failed to do PORT");
2024       result = CURLE_FTP_PORT_FAILED;
2025     }
2026     else
2027       /* try next */
2028       result = ftp_state_use_port(conn, fcmd);
2029   }
2030   else {
2031     infof(data, "Connect data stream actively\n");
2032     state(conn, FTP_STOP); /* end of DO phase */
2033     result = ftp_dophase_done(conn, FALSE);
2034   }
2035 
2036   return result;
2037 }
2038 
ftp_state_mdtm_resp(struct connectdata * conn,int ftpcode)2039 static CURLcode ftp_state_mdtm_resp(struct connectdata *conn,
2040                                     int ftpcode)
2041 {
2042   CURLcode result = CURLE_OK;
2043   struct Curl_easy *data = conn->data;
2044   struct FTP *ftp = data->req.p.ftp;
2045   struct ftp_conn *ftpc = &conn->proto.ftpc;
2046 
2047   switch(ftpcode) {
2048   case 213:
2049     {
2050       /* we got a time. Format should be: "YYYYMMDDHHMMSS[.sss]" where the
2051          last .sss part is optional and means fractions of a second */
2052       int year, month, day, hour, minute, second;
2053       if(6 == sscanf(&data->state.buffer[4], "%04d%02d%02d%02d%02d%02d",
2054                      &year, &month, &day, &hour, &minute, &second)) {
2055         /* we have a time, reformat it */
2056         char timebuf[24];
2057         msnprintf(timebuf, sizeof(timebuf),
2058                   "%04d%02d%02d %02d:%02d:%02d GMT",
2059                   year, month, day, hour, minute, second);
2060         /* now, convert this into a time() value: */
2061         data->info.filetime = Curl_getdate_capped(timebuf);
2062       }
2063 
2064 #ifdef CURL_FTP_HTTPSTYLE_HEAD
2065       /* If we asked for a time of the file and we actually got one as well,
2066          we "emulate" a HTTP-style header in our output. */
2067 
2068       if(data->set.opt_no_body &&
2069          ftpc->file &&
2070          data->set.get_filetime &&
2071          (data->info.filetime >= 0) ) {
2072         char headerbuf[128];
2073         time_t filetime = data->info.filetime;
2074         struct tm buffer;
2075         const struct tm *tm = &buffer;
2076 
2077         result = Curl_gmtime(filetime, &buffer);
2078         if(result)
2079           return result;
2080 
2081         /* format: "Tue, 15 Nov 1994 12:45:26" */
2082         msnprintf(headerbuf, sizeof(headerbuf),
2083                   "Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n",
2084                   Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
2085                   tm->tm_mday,
2086                   Curl_month[tm->tm_mon],
2087                   tm->tm_year + 1900,
2088                   tm->tm_hour,
2089                   tm->tm_min,
2090                   tm->tm_sec);
2091         result = Curl_client_write(conn, CLIENTWRITE_BOTH, headerbuf, 0);
2092         if(result)
2093           return result;
2094       } /* end of a ridiculous amount of conditionals */
2095 #endif
2096     }
2097     break;
2098   default:
2099     infof(data, "unsupported MDTM reply format\n");
2100     break;
2101   case 550: /* "No such file or directory" */
2102     failf(data, "Given file does not exist");
2103     result = CURLE_REMOTE_FILE_NOT_FOUND;
2104     break;
2105   }
2106 
2107   if(data->set.timecondition) {
2108     if((data->info.filetime > 0) && (data->set.timevalue > 0)) {
2109       switch(data->set.timecondition) {
2110       case CURL_TIMECOND_IFMODSINCE:
2111       default:
2112         if(data->info.filetime <= data->set.timevalue) {
2113           infof(data, "The requested document is not new enough\n");
2114           ftp->transfer = FTPTRANSFER_NONE; /* mark to not transfer data */
2115           data->info.timecond = TRUE;
2116           state(conn, FTP_STOP);
2117           return CURLE_OK;
2118         }
2119         break;
2120       case CURL_TIMECOND_IFUNMODSINCE:
2121         if(data->info.filetime > data->set.timevalue) {
2122           infof(data, "The requested document is not old enough\n");
2123           ftp->transfer = FTPTRANSFER_NONE; /* mark to not transfer data */
2124           data->info.timecond = TRUE;
2125           state(conn, FTP_STOP);
2126           return CURLE_OK;
2127         }
2128         break;
2129       } /* switch */
2130     }
2131     else {
2132       infof(data, "Skipping time comparison\n");
2133     }
2134   }
2135 
2136   if(!result)
2137     result = ftp_state_type(conn);
2138 
2139   return result;
2140 }
2141 
ftp_state_type_resp(struct connectdata * conn,int ftpcode,ftpstate instate)2142 static CURLcode ftp_state_type_resp(struct connectdata *conn,
2143                                     int ftpcode,
2144                                     ftpstate instate)
2145 {
2146   CURLcode result = CURLE_OK;
2147   struct Curl_easy *data = conn->data;
2148 
2149   if(ftpcode/100 != 2) {
2150     /* "sasserftpd" and "(u)r(x)bot ftpd" both responds with 226 after a
2151        successful 'TYPE I'. While that is not as RFC959 says, it is still a
2152        positive response code and we allow that. */
2153     failf(data, "Couldn't set desired mode");
2154     return CURLE_FTP_COULDNT_SET_TYPE;
2155   }
2156   if(ftpcode != 200)
2157     infof(data, "Got a %03d response code instead of the assumed 200\n",
2158           ftpcode);
2159 
2160   if(instate == FTP_TYPE)
2161     result = ftp_state_size(conn);
2162   else if(instate == FTP_LIST_TYPE)
2163     result = ftp_state_list(conn);
2164   else if(instate == FTP_RETR_TYPE)
2165     result = ftp_state_retr_prequote(conn);
2166   else if(instate == FTP_STOR_TYPE)
2167     result = ftp_state_stor_prequote(conn);
2168 
2169   return result;
2170 }
2171 
ftp_state_retr(struct connectdata * conn,curl_off_t filesize)2172 static CURLcode ftp_state_retr(struct connectdata *conn,
2173                                          curl_off_t filesize)
2174 {
2175   CURLcode result = CURLE_OK;
2176   struct Curl_easy *data = conn->data;
2177   struct FTP *ftp = data->req.p.ftp;
2178   struct ftp_conn *ftpc = &conn->proto.ftpc;
2179 
2180   if(data->set.max_filesize && (filesize > data->set.max_filesize)) {
2181     failf(data, "Maximum file size exceeded");
2182     return CURLE_FILESIZE_EXCEEDED;
2183   }
2184   ftp->downloadsize = filesize;
2185 
2186   if(data->state.resume_from) {
2187     /* We always (attempt to) get the size of downloads, so it is done before
2188        this even when not doing resumes. */
2189     if(filesize == -1) {
2190       infof(data, "ftp server doesn't support SIZE\n");
2191       /* We couldn't get the size and therefore we can't know if there really
2192          is a part of the file left to get, although the server will just
2193          close the connection when we start the connection so it won't cause
2194          us any harm, just not make us exit as nicely. */
2195     }
2196     else {
2197       /* We got a file size report, so we check that there actually is a
2198          part of the file left to get, or else we go home.  */
2199       if(data->state.resume_from< 0) {
2200         /* We're supposed to download the last abs(from) bytes */
2201         if(filesize < -data->state.resume_from) {
2202           failf(data, "Offset (%" CURL_FORMAT_CURL_OFF_T
2203                 ") was beyond file size (%" CURL_FORMAT_CURL_OFF_T ")",
2204                 data->state.resume_from, filesize);
2205           return CURLE_BAD_DOWNLOAD_RESUME;
2206         }
2207         /* convert to size to download */
2208         ftp->downloadsize = -data->state.resume_from;
2209         /* download from where? */
2210         data->state.resume_from = filesize - ftp->downloadsize;
2211       }
2212       else {
2213         if(filesize < data->state.resume_from) {
2214           failf(data, "Offset (%" CURL_FORMAT_CURL_OFF_T
2215                 ") was beyond file size (%" CURL_FORMAT_CURL_OFF_T ")",
2216                 data->state.resume_from, filesize);
2217           return CURLE_BAD_DOWNLOAD_RESUME;
2218         }
2219         /* Now store the number of bytes we are expected to download */
2220         ftp->downloadsize = filesize-data->state.resume_from;
2221       }
2222     }
2223 
2224     if(ftp->downloadsize == 0) {
2225       /* no data to transfer */
2226       Curl_setup_transfer(data, -1, -1, FALSE, -1);
2227       infof(data, "File already completely downloaded\n");
2228 
2229       /* Set ->transfer so that we won't get any error in ftp_done()
2230        * because we didn't transfer the any file */
2231       ftp->transfer = FTPTRANSFER_NONE;
2232       state(conn, FTP_STOP);
2233       return CURLE_OK;
2234     }
2235 
2236     /* Set resume file transfer offset */
2237     infof(data, "Instructs server to resume from offset %"
2238           CURL_FORMAT_CURL_OFF_T "\n", data->state.resume_from);
2239 
2240     result = Curl_pp_sendf(&ftpc->pp, "REST %" CURL_FORMAT_CURL_OFF_T,
2241                            data->state.resume_from);
2242     if(!result)
2243       state(conn, FTP_RETR_REST);
2244   }
2245   else {
2246     /* no resume */
2247     result = Curl_pp_sendf(&ftpc->pp, "RETR %s", ftpc->file);
2248     if(!result)
2249       state(conn, FTP_RETR);
2250   }
2251 
2252   return result;
2253 }
2254 
ftp_state_size_resp(struct connectdata * conn,int ftpcode,ftpstate instate)2255 static CURLcode ftp_state_size_resp(struct connectdata *conn,
2256                                     int ftpcode,
2257                                     ftpstate instate)
2258 {
2259   CURLcode result = CURLE_OK;
2260   struct Curl_easy *data = conn->data;
2261   curl_off_t filesize = -1;
2262   char *buf = data->state.buffer;
2263 
2264   /* get the size from the ascii string: */
2265   if(ftpcode == 213) {
2266     /* To allow servers to prepend "rubbish" in the response string, we scan
2267        for all the digits at the end of the response and parse only those as a
2268        number. */
2269     char *start = &buf[4];
2270     char *fdigit = strchr(start, '\r');
2271     if(fdigit) {
2272       do
2273         fdigit--;
2274       while(ISDIGIT(*fdigit) && (fdigit > start));
2275       if(!ISDIGIT(*fdigit))
2276         fdigit++;
2277     }
2278     else
2279       fdigit = start;
2280     /* ignores parsing errors, which will make the size remain unknown */
2281     (void)curlx_strtoofft(fdigit, NULL, 0, &filesize);
2282 
2283   }
2284   else if(ftpcode == 550) { /* "No such file or directory" */
2285     failf(data, "The file does not exist");
2286     return CURLE_REMOTE_FILE_NOT_FOUND;
2287   }
2288 
2289   if(instate == FTP_SIZE) {
2290 #ifdef CURL_FTP_HTTPSTYLE_HEAD
2291     if(-1 != filesize) {
2292       char clbuf[128];
2293       msnprintf(clbuf, sizeof(clbuf),
2294                 "Content-Length: %" CURL_FORMAT_CURL_OFF_T "\r\n", filesize);
2295       result = Curl_client_write(conn, CLIENTWRITE_BOTH, clbuf, 0);
2296       if(result)
2297         return result;
2298     }
2299 #endif
2300     Curl_pgrsSetDownloadSize(data, filesize);
2301     result = ftp_state_rest(conn);
2302   }
2303   else if(instate == FTP_RETR_SIZE) {
2304     Curl_pgrsSetDownloadSize(data, filesize);
2305     result = ftp_state_retr(conn, filesize);
2306   }
2307   else if(instate == FTP_STOR_SIZE) {
2308     data->state.resume_from = filesize;
2309     result = ftp_state_ul_setup(conn, TRUE);
2310   }
2311 
2312   return result;
2313 }
2314 
ftp_state_rest_resp(struct connectdata * conn,int ftpcode,ftpstate instate)2315 static CURLcode ftp_state_rest_resp(struct connectdata *conn,
2316                                     int ftpcode,
2317                                     ftpstate instate)
2318 {
2319   CURLcode result = CURLE_OK;
2320   struct ftp_conn *ftpc = &conn->proto.ftpc;
2321 
2322   switch(instate) {
2323   case FTP_REST:
2324   default:
2325 #ifdef CURL_FTP_HTTPSTYLE_HEAD
2326     if(ftpcode == 350) {
2327       char buffer[24]= { "Accept-ranges: bytes\r\n" };
2328       result = Curl_client_write(conn, CLIENTWRITE_BOTH, buffer, 0);
2329       if(result)
2330         return result;
2331     }
2332 #endif
2333     result = ftp_state_prepare_transfer(conn);
2334     break;
2335 
2336   case FTP_RETR_REST:
2337     if(ftpcode != 350) {
2338       failf(conn->data, "Couldn't use REST");
2339       result = CURLE_FTP_COULDNT_USE_REST;
2340     }
2341     else {
2342       result = Curl_pp_sendf(&ftpc->pp, "RETR %s", ftpc->file);
2343       if(!result)
2344         state(conn, FTP_RETR);
2345     }
2346     break;
2347   }
2348 
2349   return result;
2350 }
2351 
ftp_state_stor_resp(struct connectdata * conn,int ftpcode,ftpstate instate)2352 static CURLcode ftp_state_stor_resp(struct connectdata *conn,
2353                                     int ftpcode, ftpstate instate)
2354 {
2355   CURLcode result = CURLE_OK;
2356   struct Curl_easy *data = conn->data;
2357 
2358   if(ftpcode >= 400) {
2359     failf(data, "Failed FTP upload: %0d", ftpcode);
2360     state(conn, FTP_STOP);
2361     /* oops, we never close the sockets! */
2362     return CURLE_UPLOAD_FAILED;
2363   }
2364 
2365   conn->proto.ftpc.state_saved = instate;
2366 
2367   /* PORT means we are now awaiting the server to connect to us. */
2368   if(data->set.ftp_use_port) {
2369     bool connected;
2370 
2371     state(conn, FTP_STOP); /* no longer in STOR state */
2372 
2373     result = AllowServerConnect(conn, &connected);
2374     if(result)
2375       return result;
2376 
2377     if(!connected) {
2378       struct ftp_conn *ftpc = &conn->proto.ftpc;
2379       infof(data, "Data conn was not available immediately\n");
2380       ftpc->wait_data_conn = TRUE;
2381     }
2382 
2383     return CURLE_OK;
2384   }
2385   return InitiateTransfer(conn);
2386 }
2387 
2388 /* for LIST and RETR responses */
ftp_state_get_resp(struct connectdata * conn,int ftpcode,ftpstate instate)2389 static CURLcode ftp_state_get_resp(struct connectdata *conn,
2390                                     int ftpcode,
2391                                     ftpstate instate)
2392 {
2393   CURLcode result = CURLE_OK;
2394   struct Curl_easy *data = conn->data;
2395   struct FTP *ftp = data->req.p.ftp;
2396 
2397   if((ftpcode == 150) || (ftpcode == 125)) {
2398 
2399     /*
2400       A;
2401       150 Opening BINARY mode data connection for /etc/passwd (2241
2402       bytes).  (ok, the file is being transferred)
2403 
2404       B:
2405       150 Opening ASCII mode data connection for /bin/ls
2406 
2407       C:
2408       150 ASCII data connection for /bin/ls (137.167.104.91,37445) (0 bytes).
2409 
2410       D:
2411       150 Opening ASCII mode data connection for [file] (0.0.0.0,0) (545 bytes)
2412 
2413       E:
2414       125 Data connection already open; Transfer starting. */
2415 
2416     curl_off_t size = -1; /* default unknown size */
2417 
2418 
2419     /*
2420      * It appears that there are FTP-servers that return size 0 for files when
2421      * SIZE is used on the file while being in BINARY mode. To work around
2422      * that (stupid) behavior, we attempt to parse the RETR response even if
2423      * the SIZE returned size zero.
2424      *
2425      * Debugging help from Salvatore Sorrentino on February 26, 2003.
2426      */
2427 
2428     if((instate != FTP_LIST) &&
2429        !data->set.prefer_ascii &&
2430        (ftp->downloadsize < 1)) {
2431       /*
2432        * It seems directory listings either don't show the size or very
2433        * often uses size 0 anyway. ASCII transfers may very well turn out
2434        * that the transferred amount of data is not the same as this line
2435        * tells, why using this number in those cases only confuses us.
2436        *
2437        * Example D above makes this parsing a little tricky */
2438       char *bytes;
2439       char *buf = data->state.buffer;
2440       bytes = strstr(buf, " bytes");
2441       if(bytes) {
2442         long in = (long)(--bytes-buf);
2443         /* this is a hint there is size information in there! ;-) */
2444         while(--in) {
2445           /* scan for the left parenthesis and break there */
2446           if('(' == *bytes)
2447             break;
2448           /* skip only digits */
2449           if(!ISDIGIT(*bytes)) {
2450             bytes = NULL;
2451             break;
2452           }
2453           /* one more estep backwards */
2454           bytes--;
2455         }
2456         /* if we have nothing but digits: */
2457         if(bytes++) {
2458           /* get the number! */
2459           (void)curlx_strtoofft(bytes, NULL, 0, &size);
2460         }
2461       }
2462     }
2463     else if(ftp->downloadsize > -1)
2464       size = ftp->downloadsize;
2465 
2466     if(size > data->req.maxdownload && data->req.maxdownload > 0)
2467       size = data->req.size = data->req.maxdownload;
2468     else if((instate != FTP_LIST) && (data->set.prefer_ascii))
2469       size = -1; /* kludge for servers that understate ASCII mode file size */
2470 
2471     infof(data, "Maxdownload = %" CURL_FORMAT_CURL_OFF_T "\n",
2472           data->req.maxdownload);
2473 
2474     if(instate != FTP_LIST)
2475       infof(data, "Getting file with size: %" CURL_FORMAT_CURL_OFF_T "\n",
2476             size);
2477 
2478     /* FTP download: */
2479     conn->proto.ftpc.state_saved = instate;
2480     conn->proto.ftpc.retr_size_saved = size;
2481 
2482     if(data->set.ftp_use_port) {
2483       bool connected;
2484 
2485       result = AllowServerConnect(conn, &connected);
2486       if(result)
2487         return result;
2488 
2489       if(!connected) {
2490         struct ftp_conn *ftpc = &conn->proto.ftpc;
2491         infof(data, "Data conn was not available immediately\n");
2492         state(conn, FTP_STOP);
2493         ftpc->wait_data_conn = TRUE;
2494       }
2495     }
2496     else
2497       return InitiateTransfer(conn);
2498   }
2499   else {
2500     if((instate == FTP_LIST) && (ftpcode == 450)) {
2501       /* simply no matching files in the dir listing */
2502       ftp->transfer = FTPTRANSFER_NONE; /* don't download anything */
2503       state(conn, FTP_STOP); /* this phase is over */
2504     }
2505     else {
2506       failf(data, "RETR response: %03d", ftpcode);
2507       return instate == FTP_RETR && ftpcode == 550?
2508         CURLE_REMOTE_FILE_NOT_FOUND:
2509         CURLE_FTP_COULDNT_RETR_FILE;
2510     }
2511   }
2512 
2513   return result;
2514 }
2515 
2516 /* after USER, PASS and ACCT */
ftp_state_loggedin(struct connectdata * conn)2517 static CURLcode ftp_state_loggedin(struct connectdata *conn)
2518 {
2519   CURLcode result = CURLE_OK;
2520 
2521   if(conn->bits.ftp_use_control_ssl) {
2522     /* PBSZ = PROTECTION BUFFER SIZE.
2523 
2524     The 'draft-murray-auth-ftp-ssl' (draft 12, page 7) says:
2525 
2526     Specifically, the PROT command MUST be preceded by a PBSZ
2527     command and a PBSZ command MUST be preceded by a successful
2528     security data exchange (the TLS negotiation in this case)
2529 
2530     ... (and on page 8):
2531 
2532     Thus the PBSZ command must still be issued, but must have a
2533     parameter of '0' to indicate that no buffering is taking place
2534     and the data connection should not be encapsulated.
2535     */
2536     result = Curl_pp_sendf(&conn->proto.ftpc.pp, "PBSZ %d", 0);
2537     if(!result)
2538       state(conn, FTP_PBSZ);
2539   }
2540   else {
2541     result = ftp_state_pwd(conn);
2542   }
2543   return result;
2544 }
2545 
2546 /* for USER and PASS responses */
ftp_state_user_resp(struct connectdata * conn,int ftpcode,ftpstate instate)2547 static CURLcode ftp_state_user_resp(struct connectdata *conn,
2548                                     int ftpcode,
2549                                     ftpstate instate)
2550 {
2551   CURLcode result = CURLE_OK;
2552   struct Curl_easy *data = conn->data;
2553   struct ftp_conn *ftpc = &conn->proto.ftpc;
2554   (void)instate; /* no use for this yet */
2555 
2556   /* some need password anyway, and others just return 2xx ignored */
2557   if((ftpcode == 331) && (ftpc->state == FTP_USER)) {
2558     /* 331 Password required for ...
2559        (the server requires to send the user's password too) */
2560     result = Curl_pp_sendf(&ftpc->pp, "PASS %s", conn->passwd?conn->passwd:"");
2561     if(!result)
2562       state(conn, FTP_PASS);
2563   }
2564   else if(ftpcode/100 == 2) {
2565     /* 230 User ... logged in.
2566        (the user logged in with or without password) */
2567     result = ftp_state_loggedin(conn);
2568   }
2569   else if(ftpcode == 332) {
2570     if(data->set.str[STRING_FTP_ACCOUNT]) {
2571       result = Curl_pp_sendf(&ftpc->pp, "ACCT %s",
2572                              data->set.str[STRING_FTP_ACCOUNT]);
2573       if(!result)
2574         state(conn, FTP_ACCT);
2575     }
2576     else {
2577       failf(data, "ACCT requested but none available");
2578       result = CURLE_LOGIN_DENIED;
2579     }
2580   }
2581   else {
2582     /* All other response codes, like:
2583 
2584     530 User ... access denied
2585     (the server denies to log the specified user) */
2586 
2587     if(conn->data->set.str[STRING_FTP_ALTERNATIVE_TO_USER] &&
2588         !conn->data->state.ftp_trying_alternative) {
2589       /* Ok, USER failed.  Let's try the supplied command. */
2590       result =
2591         Curl_pp_sendf(&ftpc->pp, "%s",
2592                       conn->data->set.str[STRING_FTP_ALTERNATIVE_TO_USER]);
2593       if(!result) {
2594         conn->data->state.ftp_trying_alternative = TRUE;
2595         state(conn, FTP_USER);
2596       }
2597     }
2598     else {
2599       failf(data, "Access denied: %03d", ftpcode);
2600       result = CURLE_LOGIN_DENIED;
2601     }
2602   }
2603   return result;
2604 }
2605 
2606 /* for ACCT response */
ftp_state_acct_resp(struct connectdata * conn,int ftpcode)2607 static CURLcode ftp_state_acct_resp(struct connectdata *conn,
2608                                     int ftpcode)
2609 {
2610   CURLcode result = CURLE_OK;
2611   struct Curl_easy *data = conn->data;
2612   if(ftpcode != 230) {
2613     failf(data, "ACCT rejected by server: %03d", ftpcode);
2614     result = CURLE_FTP_WEIRD_PASS_REPLY; /* FIX */
2615   }
2616   else
2617     result = ftp_state_loggedin(conn);
2618 
2619   return result;
2620 }
2621 
2622 
ftp_statemach_act(struct connectdata * conn)2623 static CURLcode ftp_statemach_act(struct connectdata *conn)
2624 {
2625   CURLcode result;
2626   curl_socket_t sock = conn->sock[FIRSTSOCKET];
2627   struct Curl_easy *data = conn->data;
2628   int ftpcode;
2629   struct ftp_conn *ftpc = &conn->proto.ftpc;
2630   struct pingpong *pp = &ftpc->pp;
2631   static const char ftpauth[][4]  = { "SSL", "TLS" };
2632   size_t nread = 0;
2633 
2634   if(pp->sendleft)
2635     return Curl_pp_flushsend(pp);
2636 
2637   result = ftp_readresp(sock, pp, &ftpcode, &nread);
2638   if(result)
2639     return result;
2640 
2641   if(ftpcode) {
2642     /* we have now received a full FTP server response */
2643     switch(ftpc->state) {
2644     case FTP_WAIT220:
2645       if(ftpcode == 230)
2646         /* 230 User logged in - already! */
2647         return ftp_state_user_resp(conn, ftpcode, ftpc->state);
2648       else if(ftpcode != 220) {
2649         failf(data, "Got a %03d ftp-server response when 220 was expected",
2650               ftpcode);
2651         return CURLE_WEIRD_SERVER_REPLY;
2652       }
2653 
2654       /* We have received a 220 response fine, now we proceed. */
2655 #ifdef HAVE_GSSAPI
2656       if(data->set.krb) {
2657         /* If not anonymous login, try a secure login. Note that this
2658            procedure is still BLOCKING. */
2659 
2660         Curl_sec_request_prot(conn, "private");
2661         /* We set private first as default, in case the line below fails to
2662            set a valid level */
2663         Curl_sec_request_prot(conn, data->set.str[STRING_KRB_LEVEL]);
2664 
2665         if(Curl_sec_login(conn))
2666           infof(data, "Logging in with password in cleartext!\n");
2667         else
2668           infof(data, "Authentication successful\n");
2669       }
2670 #endif
2671 
2672       if(data->set.use_ssl && !conn->bits.ftp_use_control_ssl) {
2673         /* We don't have a SSL/TLS control connection yet, but FTPS is
2674            requested. Try a FTPS connection now */
2675 
2676         ftpc->count3 = 0;
2677         switch(data->set.ftpsslauth) {
2678         case CURLFTPAUTH_DEFAULT:
2679         case CURLFTPAUTH_SSL:
2680           ftpc->count2 = 1; /* add one to get next */
2681           ftpc->count1 = 0;
2682           break;
2683         case CURLFTPAUTH_TLS:
2684           ftpc->count2 = -1; /* subtract one to get next */
2685           ftpc->count1 = 1;
2686           break;
2687         default:
2688           failf(data, "unsupported parameter to CURLOPT_FTPSSLAUTH: %d",
2689                 (int)data->set.ftpsslauth);
2690           return CURLE_UNKNOWN_OPTION; /* we don't know what to do */
2691         }
2692         result = Curl_pp_sendf(&ftpc->pp, "AUTH %s", ftpauth[ftpc->count1]);
2693         if(!result)
2694           state(conn, FTP_AUTH);
2695       }
2696       else
2697         result = ftp_state_user(conn);
2698       break;
2699 
2700     case FTP_AUTH:
2701       /* we have gotten the response to a previous AUTH command */
2702 
2703       /* RFC2228 (page 5) says:
2704        *
2705        * If the server is willing to accept the named security mechanism,
2706        * and does not require any security data, it must respond with
2707        * reply code 234/334.
2708        */
2709 
2710       if((ftpcode == 234) || (ftpcode == 334)) {
2711         /* Curl_ssl_connect is BLOCKING */
2712         result = Curl_ssl_connect(conn, FIRSTSOCKET);
2713         if(!result) {
2714           conn->bits.ftp_use_data_ssl = FALSE; /* clear-text data */
2715           conn->bits.ftp_use_control_ssl = TRUE; /* SSL on control */
2716           result = ftp_state_user(conn);
2717         }
2718       }
2719       else if(ftpc->count3 < 1) {
2720         ftpc->count3++;
2721         ftpc->count1 += ftpc->count2; /* get next attempt */
2722         result = Curl_pp_sendf(&ftpc->pp, "AUTH %s", ftpauth[ftpc->count1]);
2723         /* remain in this same state */
2724       }
2725       else {
2726         if(data->set.use_ssl > CURLUSESSL_TRY)
2727           /* we failed and CURLUSESSL_CONTROL or CURLUSESSL_ALL is set */
2728           result = CURLE_USE_SSL_FAILED;
2729         else
2730           /* ignore the failure and continue */
2731           result = ftp_state_user(conn);
2732       }
2733       break;
2734 
2735     case FTP_USER:
2736     case FTP_PASS:
2737       result = ftp_state_user_resp(conn, ftpcode, ftpc->state);
2738       break;
2739 
2740     case FTP_ACCT:
2741       result = ftp_state_acct_resp(conn, ftpcode);
2742       break;
2743 
2744     case FTP_PBSZ:
2745       result =
2746         Curl_pp_sendf(&ftpc->pp, "PROT %c",
2747                       data->set.use_ssl == CURLUSESSL_CONTROL ? 'C' : 'P');
2748       if(!result)
2749         state(conn, FTP_PROT);
2750       break;
2751 
2752     case FTP_PROT:
2753       if(ftpcode/100 == 2)
2754         /* We have enabled SSL for the data connection! */
2755         conn->bits.ftp_use_data_ssl =
2756           (data->set.use_ssl != CURLUSESSL_CONTROL) ? TRUE : FALSE;
2757       /* FTP servers typically responds with 500 if they decide to reject
2758          our 'P' request */
2759       else if(data->set.use_ssl > CURLUSESSL_CONTROL)
2760         /* we failed and bails out */
2761         return CURLE_USE_SSL_FAILED;
2762 
2763       if(data->set.ftp_ccc) {
2764         /* CCC - Clear Command Channel
2765          */
2766         result = Curl_pp_sendf(&ftpc->pp, "%s", "CCC");
2767         if(!result)
2768           state(conn, FTP_CCC);
2769       }
2770       else
2771         result = ftp_state_pwd(conn);
2772       break;
2773 
2774     case FTP_CCC:
2775       if(ftpcode < 500) {
2776         /* First shut down the SSL layer (note: this call will block) */
2777         result = Curl_ssl_shutdown(conn, FIRSTSOCKET);
2778 
2779         if(result)
2780           failf(conn->data, "Failed to clear the command channel (CCC)");
2781       }
2782       if(!result)
2783         /* Then continue as normal */
2784         result = ftp_state_pwd(conn);
2785       break;
2786 
2787     case FTP_PWD:
2788       if(ftpcode == 257) {
2789         char *ptr = &data->state.buffer[4];  /* start on the first letter */
2790         const size_t buf_size = data->set.buffer_size;
2791         char *dir;
2792         bool entry_extracted = FALSE;
2793 
2794         dir = malloc(nread + 1);
2795         if(!dir)
2796           return CURLE_OUT_OF_MEMORY;
2797 
2798         /* Reply format is like
2799            257<space>[rubbish]"<directory-name>"<space><commentary> and the
2800            RFC959 says
2801 
2802            The directory name can contain any character; embedded
2803            double-quotes should be escaped by double-quotes (the
2804            "quote-doubling" convention).
2805         */
2806 
2807         /* scan for the first double-quote for non-standard responses */
2808         while(ptr < &data->state.buffer[buf_size]
2809               && *ptr != '\n' && *ptr != '\0' && *ptr != '"')
2810           ptr++;
2811 
2812         if('\"' == *ptr) {
2813           /* it started good */
2814           char *store;
2815           ptr++;
2816           for(store = dir; *ptr;) {
2817             if('\"' == *ptr) {
2818               if('\"' == ptr[1]) {
2819                 /* "quote-doubling" */
2820                 *store = ptr[1];
2821                 ptr++;
2822               }
2823               else {
2824                 /* end of path */
2825                 entry_extracted = TRUE;
2826                 break; /* get out of this loop */
2827               }
2828             }
2829             else
2830               *store = *ptr;
2831             store++;
2832             ptr++;
2833           }
2834           *store = '\0'; /* null-terminate */
2835         }
2836         if(entry_extracted) {
2837           /* If the path name does not look like an absolute path (i.e.: it
2838              does not start with a '/'), we probably need some server-dependent
2839              adjustments. For example, this is the case when connecting to
2840              an OS400 FTP server: this server supports two name syntaxes,
2841              the default one being incompatible with standard paths. In
2842              addition, this server switches automatically to the regular path
2843              syntax when one is encountered in a command: this results in
2844              having an entrypath in the wrong syntax when later used in CWD.
2845                The method used here is to check the server OS: we do it only
2846              if the path name looks strange to minimize overhead on other
2847              systems. */
2848 
2849           if(!ftpc->server_os && dir[0] != '/') {
2850             result = Curl_pp_sendf(&ftpc->pp, "%s", "SYST");
2851             if(result) {
2852               free(dir);
2853               return result;
2854             }
2855             Curl_safefree(ftpc->entrypath);
2856             ftpc->entrypath = dir; /* remember this */
2857             infof(data, "Entry path is '%s'\n", ftpc->entrypath);
2858             /* also save it where getinfo can access it: */
2859             data->state.most_recent_ftp_entrypath = ftpc->entrypath;
2860             state(conn, FTP_SYST);
2861             break;
2862           }
2863 
2864           Curl_safefree(ftpc->entrypath);
2865           ftpc->entrypath = dir; /* remember this */
2866           infof(data, "Entry path is '%s'\n", ftpc->entrypath);
2867           /* also save it where getinfo can access it: */
2868           data->state.most_recent_ftp_entrypath = ftpc->entrypath;
2869         }
2870         else {
2871           /* couldn't get the path */
2872           free(dir);
2873           infof(data, "Failed to figure out path\n");
2874         }
2875       }
2876       state(conn, FTP_STOP); /* we are done with the CONNECT phase! */
2877       DEBUGF(infof(data, "protocol connect phase DONE\n"));
2878       break;
2879 
2880     case FTP_SYST:
2881       if(ftpcode == 215) {
2882         char *ptr = &data->state.buffer[4];  /* start on the first letter */
2883         char *os;
2884         char *store;
2885 
2886         os = malloc(nread + 1);
2887         if(!os)
2888           return CURLE_OUT_OF_MEMORY;
2889 
2890         /* Reply format is like
2891            215<space><OS-name><space><commentary>
2892         */
2893         while(*ptr == ' ')
2894           ptr++;
2895         for(store = os; *ptr && *ptr != ' ';)
2896           *store++ = *ptr++;
2897         *store = '\0'; /* null-terminate */
2898 
2899         /* Check for special servers here. */
2900 
2901         if(strcasecompare(os, "OS/400")) {
2902           /* Force OS400 name format 1. */
2903           result = Curl_pp_sendf(&ftpc->pp, "%s", "SITE NAMEFMT 1");
2904           if(result) {
2905             free(os);
2906             return result;
2907           }
2908           /* remember target server OS */
2909           Curl_safefree(ftpc->server_os);
2910           ftpc->server_os = os;
2911           state(conn, FTP_NAMEFMT);
2912           break;
2913         }
2914         /* Nothing special for the target server. */
2915         /* remember target server OS */
2916         Curl_safefree(ftpc->server_os);
2917         ftpc->server_os = os;
2918       }
2919       else {
2920         /* Cannot identify server OS. Continue anyway and cross fingers. */
2921       }
2922 
2923       state(conn, FTP_STOP); /* we are done with the CONNECT phase! */
2924       DEBUGF(infof(data, "protocol connect phase DONE\n"));
2925       break;
2926 
2927     case FTP_NAMEFMT:
2928       if(ftpcode == 250) {
2929         /* Name format change successful: reload initial path. */
2930         ftp_state_pwd(conn);
2931         break;
2932       }
2933 
2934       state(conn, FTP_STOP); /* we are done with the CONNECT phase! */
2935       DEBUGF(infof(data, "protocol connect phase DONE\n"));
2936       break;
2937 
2938     case FTP_QUOTE:
2939     case FTP_POSTQUOTE:
2940     case FTP_RETR_PREQUOTE:
2941     case FTP_STOR_PREQUOTE:
2942       if((ftpcode >= 400) && !ftpc->count2) {
2943         /* failure response code, and not allowed to fail */
2944         failf(conn->data, "QUOT command failed with %03d", ftpcode);
2945         result = CURLE_QUOTE_ERROR;
2946       }
2947       else
2948         result = ftp_state_quote(conn, FALSE, ftpc->state);
2949       break;
2950 
2951     case FTP_CWD:
2952       if(ftpcode/100 != 2) {
2953         /* failure to CWD there */
2954         if(conn->data->set.ftp_create_missing_dirs &&
2955            ftpc->cwdcount && !ftpc->count2) {
2956           /* try making it */
2957           ftpc->count2++; /* counter to prevent CWD-MKD loops */
2958           result = Curl_pp_sendf(&ftpc->pp, "MKD %s",
2959                                  ftpc->dirs[ftpc->cwdcount - 1]);
2960           if(!result)
2961             state(conn, FTP_MKD);
2962         }
2963         else {
2964           /* return failure */
2965           failf(data, "Server denied you to change to the given directory");
2966           ftpc->cwdfail = TRUE; /* don't remember this path as we failed
2967                                    to enter it */
2968           result = CURLE_REMOTE_ACCESS_DENIED;
2969         }
2970       }
2971       else {
2972         /* success */
2973         ftpc->count2 = 0;
2974         if(++ftpc->cwdcount <= ftpc->dirdepth)
2975           /* send next CWD */
2976           result = Curl_pp_sendf(&ftpc->pp, "CWD %s",
2977                                  ftpc->dirs[ftpc->cwdcount - 1]);
2978         else
2979           result = ftp_state_mdtm(conn);
2980       }
2981       break;
2982 
2983     case FTP_MKD:
2984       if((ftpcode/100 != 2) && !ftpc->count3--) {
2985         /* failure to MKD the dir */
2986         failf(data, "Failed to MKD dir: %03d", ftpcode);
2987         result = CURLE_REMOTE_ACCESS_DENIED;
2988       }
2989       else {
2990         state(conn, FTP_CWD);
2991         /* send CWD */
2992         result = Curl_pp_sendf(&ftpc->pp, "CWD %s",
2993                                ftpc->dirs[ftpc->cwdcount - 1]);
2994       }
2995       break;
2996 
2997     case FTP_MDTM:
2998       result = ftp_state_mdtm_resp(conn, ftpcode);
2999       break;
3000 
3001     case FTP_TYPE:
3002     case FTP_LIST_TYPE:
3003     case FTP_RETR_TYPE:
3004     case FTP_STOR_TYPE:
3005       result = ftp_state_type_resp(conn, ftpcode, ftpc->state);
3006       break;
3007 
3008     case FTP_SIZE:
3009     case FTP_RETR_SIZE:
3010     case FTP_STOR_SIZE:
3011       result = ftp_state_size_resp(conn, ftpcode, ftpc->state);
3012       break;
3013 
3014     case FTP_REST:
3015     case FTP_RETR_REST:
3016       result = ftp_state_rest_resp(conn, ftpcode, ftpc->state);
3017       break;
3018 
3019     case FTP_PRET:
3020       if(ftpcode != 200) {
3021         /* there only is this one standard OK return code. */
3022         failf(data, "PRET command not accepted: %03d", ftpcode);
3023         return CURLE_FTP_PRET_FAILED;
3024       }
3025       result = ftp_state_use_pasv(conn);
3026       break;
3027 
3028     case FTP_PASV:
3029       result = ftp_state_pasv_resp(conn, ftpcode);
3030       break;
3031 
3032     case FTP_PORT:
3033       result = ftp_state_port_resp(conn, ftpcode);
3034       break;
3035 
3036     case FTP_LIST:
3037     case FTP_RETR:
3038       result = ftp_state_get_resp(conn, ftpcode, ftpc->state);
3039       break;
3040 
3041     case FTP_STOR:
3042       result = ftp_state_stor_resp(conn, ftpcode, ftpc->state);
3043       break;
3044 
3045     case FTP_QUIT:
3046       /* fallthrough, just stop! */
3047     default:
3048       /* internal error */
3049       state(conn, FTP_STOP);
3050       break;
3051     }
3052   } /* if(ftpcode) */
3053 
3054   return result;
3055 }
3056 
3057 
3058 /* called repeatedly until done from multi.c */
ftp_multi_statemach(struct connectdata * conn,bool * done)3059 static CURLcode ftp_multi_statemach(struct connectdata *conn,
3060                                     bool *done)
3061 {
3062   struct ftp_conn *ftpc = &conn->proto.ftpc;
3063   CURLcode result = Curl_pp_statemach(&ftpc->pp, FALSE, FALSE);
3064 
3065   /* Check for the state outside of the Curl_socket_check() return code checks
3066      since at times we are in fact already in this state when this function
3067      gets called. */
3068   *done = (ftpc->state == FTP_STOP) ? TRUE : FALSE;
3069 
3070   return result;
3071 }
3072 
ftp_block_statemach(struct connectdata * conn)3073 static CURLcode ftp_block_statemach(struct connectdata *conn)
3074 {
3075   struct ftp_conn *ftpc = &conn->proto.ftpc;
3076   struct pingpong *pp = &ftpc->pp;
3077   CURLcode result = CURLE_OK;
3078 
3079   while(ftpc->state != FTP_STOP) {
3080     result = Curl_pp_statemach(pp, TRUE, TRUE /* disconnecting */);
3081     if(result)
3082       break;
3083   }
3084 
3085   return result;
3086 }
3087 
3088 /*
3089  * ftp_connect() should do everything that is to be considered a part of
3090  * the connection phase.
3091  *
3092  * The variable 'done' points to will be TRUE if the protocol-layer connect
3093  * phase is done when this function returns, or FALSE if not.
3094  *
3095  */
ftp_connect(struct connectdata * conn,bool * done)3096 static CURLcode ftp_connect(struct connectdata *conn,
3097                             bool *done) /* see description above */
3098 {
3099   CURLcode result;
3100   struct ftp_conn *ftpc = &conn->proto.ftpc;
3101   struct pingpong *pp = &ftpc->pp;
3102 
3103   *done = FALSE; /* default to not done yet */
3104 
3105   /* We always support persistent connections on ftp */
3106   connkeep(conn, "FTP default");
3107 
3108   pp->response_time = RESP_TIMEOUT; /* set default response time-out */
3109   pp->statemach_act = ftp_statemach_act;
3110   pp->endofresp = ftp_endofresp;
3111   pp->conn = conn;
3112 
3113   if(conn->handler->flags & PROTOPT_SSL) {
3114     /* BLOCKING */
3115     result = Curl_ssl_connect(conn, FIRSTSOCKET);
3116     if(result)
3117       return result;
3118     conn->bits.ftp_use_control_ssl = TRUE;
3119   }
3120 
3121   Curl_pp_setup(pp); /* once per transfer */
3122   Curl_pp_init(pp); /* init the generic pingpong data */
3123 
3124   /* When we connect, we start in the state where we await the 220
3125      response */
3126   state(conn, FTP_WAIT220);
3127 
3128   result = ftp_multi_statemach(conn, done);
3129 
3130   return result;
3131 }
3132 
3133 /***********************************************************************
3134  *
3135  * ftp_done()
3136  *
3137  * The DONE function. This does what needs to be done after a single DO has
3138  * performed.
3139  *
3140  * Input argument is already checked for validity.
3141  */
ftp_done(struct connectdata * conn,CURLcode status,bool premature)3142 static CURLcode ftp_done(struct connectdata *conn, CURLcode status,
3143                          bool premature)
3144 {
3145   struct Curl_easy *data = conn->data;
3146   struct FTP *ftp = data->req.p.ftp;
3147   struct ftp_conn *ftpc = &conn->proto.ftpc;
3148   struct pingpong *pp = &ftpc->pp;
3149   ssize_t nread;
3150   int ftpcode;
3151   CURLcode result = CURLE_OK;
3152   char *rawPath = NULL;
3153   size_t pathLen = 0;
3154 
3155   if(!ftp)
3156     return CURLE_OK;
3157 
3158   switch(status) {
3159   case CURLE_BAD_DOWNLOAD_RESUME:
3160   case CURLE_FTP_WEIRD_PASV_REPLY:
3161   case CURLE_FTP_PORT_FAILED:
3162   case CURLE_FTP_ACCEPT_FAILED:
3163   case CURLE_FTP_ACCEPT_TIMEOUT:
3164   case CURLE_FTP_COULDNT_SET_TYPE:
3165   case CURLE_FTP_COULDNT_RETR_FILE:
3166   case CURLE_PARTIAL_FILE:
3167   case CURLE_UPLOAD_FAILED:
3168   case CURLE_REMOTE_ACCESS_DENIED:
3169   case CURLE_FILESIZE_EXCEEDED:
3170   case CURLE_REMOTE_FILE_NOT_FOUND:
3171   case CURLE_WRITE_ERROR:
3172     /* the connection stays alive fine even though this happened */
3173     /* fall-through */
3174   case CURLE_OK: /* doesn't affect the control connection's status */
3175     if(!premature)
3176       break;
3177 
3178     /* until we cope better with prematurely ended requests, let them
3179      * fallback as if in complete failure */
3180     /* FALLTHROUGH */
3181   default:       /* by default, an error means the control connection is
3182                     wedged and should not be used anymore */
3183     ftpc->ctl_valid = FALSE;
3184     ftpc->cwdfail = TRUE; /* set this TRUE to prevent us to remember the
3185                              current path, as this connection is going */
3186     connclose(conn, "FTP ended with bad error code");
3187     result = status;      /* use the already set error code */
3188     break;
3189   }
3190 
3191   if(data->state.wildcardmatch) {
3192     if(data->set.chunk_end && ftpc->file) {
3193       Curl_set_in_callback(data, true);
3194       data->set.chunk_end(data->wildcard.customptr);
3195       Curl_set_in_callback(data, false);
3196     }
3197     ftpc->known_filesize = -1;
3198   }
3199 
3200   if(!result)
3201     /* get the url-decoded "raw" path */
3202     result = Curl_urldecode(data, ftp->path, 0, &rawPath, &pathLen,
3203                             REJECT_CTRL);
3204   if(result) {
3205     /* We can limp along anyway (and should try to since we may already be in
3206      * the error path) */
3207     ftpc->ctl_valid = FALSE; /* mark control connection as bad */
3208     connclose(conn, "FTP: out of memory!"); /* mark for connection closure */
3209     free(ftpc->prevpath);
3210     ftpc->prevpath = NULL; /* no path remembering */
3211   }
3212   else { /* remember working directory for connection reuse */
3213     if((data->set.ftp_filemethod == FTPFILE_NOCWD) && (rawPath[0] == '/'))
3214       free(rawPath); /* full path => no CWDs happened => keep ftpc->prevpath */
3215     else {
3216       free(ftpc->prevpath);
3217 
3218       if(!ftpc->cwdfail) {
3219         if(data->set.ftp_filemethod == FTPFILE_NOCWD)
3220           pathLen = 0; /* relative path => working directory is FTP home */
3221         else
3222           pathLen -= ftpc->file?strlen(ftpc->file):0; /* file is url-decoded */
3223 
3224         rawPath[pathLen] = '\0';
3225         ftpc->prevpath = rawPath;
3226       }
3227       else {
3228         free(rawPath);
3229         ftpc->prevpath = NULL; /* no path */
3230       }
3231     }
3232 
3233     if(ftpc->prevpath)
3234       infof(data, "Remembering we are in dir \"%s\"\n", ftpc->prevpath);
3235   }
3236 
3237   /* free the dir tree and file parts */
3238   freedirs(ftpc);
3239 
3240   /* shut down the socket to inform the server we're done */
3241 
3242 #ifdef _WIN32_WCE
3243   shutdown(conn->sock[SECONDARYSOCKET], 2);  /* SD_BOTH */
3244 #endif
3245 
3246   if(conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD) {
3247     if(!result && ftpc->dont_check && data->req.maxdownload > 0) {
3248       /* partial download completed */
3249       result = Curl_pp_sendf(pp, "%s", "ABOR");
3250       if(result) {
3251         failf(data, "Failure sending ABOR command: %s",
3252               curl_easy_strerror(result));
3253         ftpc->ctl_valid = FALSE; /* mark control connection as bad */
3254         connclose(conn, "ABOR command failed"); /* connection closure */
3255       }
3256     }
3257 
3258     if(conn->ssl[SECONDARYSOCKET].use) {
3259       /* The secondary socket is using SSL so we must close down that part
3260          first before we close the socket for real */
3261       Curl_ssl_close(conn, SECONDARYSOCKET);
3262 
3263       /* Note that we keep "use" set to TRUE since that (next) connection is
3264          still requested to use SSL */
3265     }
3266     close_secondarysocket(conn);
3267   }
3268 
3269   if(!result && (ftp->transfer == FTPTRANSFER_BODY) && ftpc->ctl_valid &&
3270      pp->pending_resp && !premature) {
3271     /*
3272      * Let's see what the server says about the transfer we just performed,
3273      * but lower the timeout as sometimes this connection has died while the
3274      * data has been transferred. This happens when doing through NATs etc that
3275      * abandon old silent connections.
3276      */
3277     timediff_t old_time = pp->response_time;
3278 
3279     pp->response_time = 60*1000; /* give it only a minute for now */
3280     pp->response = Curl_now(); /* timeout relative now */
3281 
3282     result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
3283 
3284     pp->response_time = old_time; /* set this back to previous value */
3285 
3286     if(!nread && (CURLE_OPERATION_TIMEDOUT == result)) {
3287       failf(data, "control connection looks dead");
3288       ftpc->ctl_valid = FALSE; /* mark control connection as bad */
3289       connclose(conn, "Timeout or similar in FTP DONE operation"); /* close */
3290     }
3291 
3292     if(result)
3293       return result;
3294 
3295     if(ftpc->dont_check && data->req.maxdownload > 0) {
3296       /* we have just sent ABOR and there is no reliable way to check if it was
3297        * successful or not; we have to close the connection now */
3298       infof(data, "partial download completed, closing connection\n");
3299       connclose(conn, "Partial download with no ability to check");
3300       return result;
3301     }
3302 
3303     if(!ftpc->dont_check) {
3304       /* 226 Transfer complete, 250 Requested file action okay, completed. */
3305       switch(ftpcode) {
3306       case 226:
3307       case 250:
3308         break;
3309       case 552:
3310         failf(data, "Exceeded storage allocation");
3311         result = CURLE_REMOTE_DISK_FULL;
3312         break;
3313       default:
3314         failf(data, "server did not report OK, got %d", ftpcode);
3315         result = CURLE_PARTIAL_FILE;
3316         break;
3317       }
3318     }
3319   }
3320 
3321   if(result || premature)
3322     /* the response code from the transfer showed an error already so no
3323        use checking further */
3324     ;
3325   else if(data->set.upload) {
3326     if((-1 != data->state.infilesize) &&
3327        (data->state.infilesize != data->req.writebytecount) &&
3328        !data->set.crlf &&
3329        (ftp->transfer == FTPTRANSFER_BODY)) {
3330       failf(data, "Uploaded unaligned file size (%" CURL_FORMAT_CURL_OFF_T
3331             " out of %" CURL_FORMAT_CURL_OFF_T " bytes)",
3332             data->req.bytecount, data->state.infilesize);
3333       result = CURLE_PARTIAL_FILE;
3334     }
3335   }
3336   else {
3337     if((-1 != data->req.size) &&
3338        (data->req.size != data->req.bytecount) &&
3339 #ifdef CURL_DO_LINEEND_CONV
3340        /* Most FTP servers don't adjust their file SIZE response for CRLFs, so
3341         * we'll check to see if the discrepancy can be explained by the number
3342         * of CRLFs we've changed to LFs.
3343         */
3344        ((data->req.size + data->state.crlf_conversions) !=
3345         data->req.bytecount) &&
3346 #endif /* CURL_DO_LINEEND_CONV */
3347        (data->req.maxdownload != data->req.bytecount)) {
3348       failf(data, "Received only partial file: %" CURL_FORMAT_CURL_OFF_T
3349             " bytes", data->req.bytecount);
3350       result = CURLE_PARTIAL_FILE;
3351     }
3352     else if(!ftpc->dont_check &&
3353             !data->req.bytecount &&
3354             (data->req.size>0)) {
3355       failf(data, "No data was received!");
3356       result = CURLE_FTP_COULDNT_RETR_FILE;
3357     }
3358   }
3359 
3360   /* clear these for next connection */
3361   ftp->transfer = FTPTRANSFER_BODY;
3362   ftpc->dont_check = FALSE;
3363 
3364   /* Send any post-transfer QUOTE strings? */
3365   if(!status && !result && !premature && data->set.postquote)
3366     result = ftp_sendquote(conn, data->set.postquote);
3367   Curl_safefree(ftp->pathalloc);
3368   return result;
3369 }
3370 
3371 /***********************************************************************
3372  *
3373  * ftp_sendquote()
3374  *
3375  * Where a 'quote' means a list of custom commands to send to the server.
3376  * The quote list is passed as an argument.
3377  *
3378  * BLOCKING
3379  */
3380 
3381 static
ftp_sendquote(struct connectdata * conn,struct curl_slist * quote)3382 CURLcode ftp_sendquote(struct connectdata *conn, struct curl_slist *quote)
3383 {
3384   struct curl_slist *item;
3385   struct ftp_conn *ftpc = &conn->proto.ftpc;
3386   struct pingpong *pp = &ftpc->pp;
3387 
3388   item = quote;
3389   while(item) {
3390     if(item->data) {
3391       ssize_t nread;
3392       char *cmd = item->data;
3393       bool acceptfail = FALSE;
3394       CURLcode result;
3395       int ftpcode = 0;
3396 
3397       /* if a command starts with an asterisk, which a legal FTP command never
3398          can, the command will be allowed to fail without it causing any
3399          aborts or cancels etc. It will cause libcurl to act as if the command
3400          is successful, whatever the server reponds. */
3401 
3402       if(cmd[0] == '*') {
3403         cmd++;
3404         acceptfail = TRUE;
3405       }
3406 
3407       result = Curl_pp_sendf(&ftpc->pp, "%s", cmd);
3408       if(!result) {
3409         pp->response = Curl_now(); /* timeout relative now */
3410         result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
3411       }
3412       if(result)
3413         return result;
3414 
3415       if(!acceptfail && (ftpcode >= 400)) {
3416         failf(conn->data, "QUOT string not accepted: %s", cmd);
3417         return CURLE_QUOTE_ERROR;
3418       }
3419     }
3420 
3421     item = item->next;
3422   }
3423 
3424   return CURLE_OK;
3425 }
3426 
3427 /***********************************************************************
3428  *
3429  * ftp_need_type()
3430  *
3431  * Returns TRUE if we in the current situation should send TYPE
3432  */
ftp_need_type(struct connectdata * conn,bool ascii_wanted)3433 static int ftp_need_type(struct connectdata *conn,
3434                          bool ascii_wanted)
3435 {
3436   return conn->proto.ftpc.transfertype != (ascii_wanted?'A':'I');
3437 }
3438 
3439 /***********************************************************************
3440  *
3441  * ftp_nb_type()
3442  *
3443  * Set TYPE. We only deal with ASCII or BINARY so this function
3444  * sets one of them.
3445  * If the transfer type is not sent, simulate on OK response in newstate
3446  */
ftp_nb_type(struct connectdata * conn,bool ascii,ftpstate newstate)3447 static CURLcode ftp_nb_type(struct connectdata *conn,
3448                             bool ascii, ftpstate newstate)
3449 {
3450   struct ftp_conn *ftpc = &conn->proto.ftpc;
3451   CURLcode result;
3452   char want = (char)(ascii?'A':'I');
3453 
3454   if(ftpc->transfertype == want) {
3455     state(conn, newstate);
3456     return ftp_state_type_resp(conn, 200, newstate);
3457   }
3458 
3459   result = Curl_pp_sendf(&ftpc->pp, "TYPE %c", want);
3460   if(!result) {
3461     state(conn, newstate);
3462 
3463     /* keep track of our current transfer type */
3464     ftpc->transfertype = want;
3465   }
3466   return result;
3467 }
3468 
3469 /***************************************************************************
3470  *
3471  * ftp_pasv_verbose()
3472  *
3473  * This function only outputs some informationals about this second connection
3474  * when we've issued a PASV command before and thus we have connected to a
3475  * possibly new IP address.
3476  *
3477  */
3478 #ifndef CURL_DISABLE_VERBOSE_STRINGS
3479 static void
ftp_pasv_verbose(struct connectdata * conn,struct Curl_addrinfo * ai,char * newhost,int port)3480 ftp_pasv_verbose(struct connectdata *conn,
3481                  struct Curl_addrinfo *ai,
3482                  char *newhost, /* ascii version */
3483                  int port)
3484 {
3485   char buf[256];
3486   Curl_printable_address(ai, buf, sizeof(buf));
3487   infof(conn->data, "Connecting to %s (%s) port %d\n", newhost, buf, port);
3488 }
3489 #endif
3490 
3491 /*
3492  * ftp_do_more()
3493  *
3494  * This function shall be called when the second FTP (data) connection is
3495  * connected.
3496  *
3497  * 'complete' can return 0 for incomplete, 1 for done and -1 for go back
3498  * (which basically is only for when PASV is being sent to retry a failed
3499  * EPSV).
3500  */
3501 
ftp_do_more(struct connectdata * conn,int * completep)3502 static CURLcode ftp_do_more(struct connectdata *conn, int *completep)
3503 {
3504   struct Curl_easy *data = conn->data;
3505   struct ftp_conn *ftpc = &conn->proto.ftpc;
3506   CURLcode result = CURLE_OK;
3507   bool connected = FALSE;
3508   bool complete = FALSE;
3509 
3510   /* the ftp struct is inited in ftp_connect() */
3511   struct FTP *ftp = data->req.p.ftp;
3512 
3513   /* if the second connection isn't done yet, wait for it */
3514   if(!conn->bits.tcpconnect[SECONDARYSOCKET]) {
3515     if(Curl_connect_ongoing(conn)) {
3516       /* As we're in TUNNEL_CONNECT state now, we know the proxy name and port
3517          aren't used so we blank their arguments. */
3518       result = Curl_proxyCONNECT(conn, SECONDARYSOCKET, NULL, 0);
3519 
3520       return result;
3521     }
3522 
3523     result = Curl_is_connected(conn, SECONDARYSOCKET, &connected);
3524 
3525     /* Ready to do more? */
3526     if(connected) {
3527       DEBUGF(infof(data, "DO-MORE connected phase starts\n"));
3528     }
3529     else {
3530       if(result && (ftpc->count1 == 0)) {
3531         *completep = -1; /* go back to DOING please */
3532         /* this is a EPSV connect failing, try PASV instead */
3533         return ftp_epsv_disable(conn);
3534       }
3535       return result;
3536     }
3537   }
3538 
3539 #ifndef CURL_DISABLE_PROXY
3540   result = Curl_proxy_connect(conn, SECONDARYSOCKET);
3541   if(result)
3542     return result;
3543 
3544   if(CONNECT_SECONDARYSOCKET_PROXY_SSL())
3545     return result;
3546 
3547   if(conn->bits.tunnel_proxy && conn->bits.httpproxy &&
3548      Curl_connect_ongoing(conn))
3549     return result;
3550 #endif
3551 
3552   if(ftpc->state) {
3553     /* already in a state so skip the initial commands.
3554        They are only done to kickstart the do_more state */
3555     result = ftp_multi_statemach(conn, &complete);
3556 
3557     *completep = (int)complete;
3558 
3559     /* if we got an error or if we don't wait for a data connection return
3560        immediately */
3561     if(result || !ftpc->wait_data_conn)
3562       return result;
3563 
3564     /* if we reach the end of the FTP state machine here, *complete will be
3565        TRUE but so is ftpc->wait_data_conn, which says we need to wait for the
3566        data connection and therefore we're not actually complete */
3567     *completep = 0;
3568   }
3569 
3570   if(ftp->transfer <= FTPTRANSFER_INFO) {
3571     /* a transfer is about to take place, or if not a file name was given
3572        so we'll do a SIZE on it later and then we need the right TYPE first */
3573 
3574     if(ftpc->wait_data_conn == TRUE) {
3575       bool serv_conned;
3576 
3577       result = ReceivedServerConnect(conn, &serv_conned);
3578       if(result)
3579         return result; /* Failed to accept data connection */
3580 
3581       if(serv_conned) {
3582         /* It looks data connection is established */
3583         result = AcceptServerConnect(conn);
3584         ftpc->wait_data_conn = FALSE;
3585         if(!result)
3586           result = InitiateTransfer(conn);
3587 
3588         if(result)
3589           return result;
3590 
3591         *completep = 1; /* this state is now complete when the server has
3592                            connected back to us */
3593       }
3594     }
3595     else if(data->set.upload) {
3596       result = ftp_nb_type(conn, data->set.prefer_ascii, FTP_STOR_TYPE);
3597       if(result)
3598         return result;
3599 
3600       result = ftp_multi_statemach(conn, &complete);
3601       /* ftpc->wait_data_conn is always false here */
3602       *completep = (int)complete;
3603     }
3604     else {
3605       /* download */
3606       ftp->downloadsize = -1; /* unknown as of yet */
3607 
3608       result = Curl_range(conn);
3609 
3610       if(result == CURLE_OK && data->req.maxdownload >= 0) {
3611         /* Don't check for successful transfer */
3612         ftpc->dont_check = TRUE;
3613       }
3614 
3615       if(result)
3616         ;
3617       else if(data->set.ftp_list_only || !ftpc->file) {
3618         /* The specified path ends with a slash, and therefore we think this
3619            is a directory that is requested, use LIST. But before that we
3620            need to set ASCII transfer mode. */
3621 
3622         /* But only if a body transfer was requested. */
3623         if(ftp->transfer == FTPTRANSFER_BODY) {
3624           result = ftp_nb_type(conn, TRUE, FTP_LIST_TYPE);
3625           if(result)
3626             return result;
3627         }
3628         /* otherwise just fall through */
3629       }
3630       else {
3631         result = ftp_nb_type(conn, data->set.prefer_ascii, FTP_RETR_TYPE);
3632         if(result)
3633           return result;
3634       }
3635 
3636       result = ftp_multi_statemach(conn, &complete);
3637       *completep = (int)complete;
3638     }
3639     return result;
3640   }
3641 
3642   /* no data to transfer */
3643   Curl_setup_transfer(data, -1, -1, FALSE, -1);
3644 
3645   if(!ftpc->wait_data_conn) {
3646     /* no waiting for the data connection so this is now complete */
3647     *completep = 1;
3648     DEBUGF(infof(data, "DO-MORE phase ends with %d\n", (int)result));
3649   }
3650 
3651   return result;
3652 }
3653 
3654 
3655 
3656 /***********************************************************************
3657  *
3658  * ftp_perform()
3659  *
3660  * This is the actual DO function for FTP. Get a file/directory according to
3661  * the options previously setup.
3662  */
3663 
3664 static
ftp_perform(struct connectdata * conn,bool * connected,bool * dophase_done)3665 CURLcode ftp_perform(struct connectdata *conn,
3666                      bool *connected,  /* connect status after PASV / PORT */
3667                      bool *dophase_done)
3668 {
3669   /* this is FTP and no proxy */
3670   CURLcode result = CURLE_OK;
3671 
3672   DEBUGF(infof(conn->data, "DO phase starts\n"));
3673 
3674   if(conn->data->set.opt_no_body) {
3675     /* requested no body means no transfer... */
3676     struct FTP *ftp = conn->data->req.p.ftp;
3677     ftp->transfer = FTPTRANSFER_INFO;
3678   }
3679 
3680   *dophase_done = FALSE; /* not done yet */
3681 
3682   /* start the first command in the DO phase */
3683   result = ftp_state_quote(conn, TRUE, FTP_QUOTE);
3684   if(result)
3685     return result;
3686 
3687   /* run the state-machine */
3688   result = ftp_multi_statemach(conn, dophase_done);
3689 
3690   *connected = conn->bits.tcpconnect[SECONDARYSOCKET];
3691 
3692   infof(conn->data, "ftp_perform ends with SECONDARY: %d\n", *connected);
3693 
3694   if(*dophase_done)
3695     DEBUGF(infof(conn->data, "DO phase is complete1\n"));
3696 
3697   return result;
3698 }
3699 
wc_data_dtor(void * ptr)3700 static void wc_data_dtor(void *ptr)
3701 {
3702   struct ftp_wc *ftpwc = ptr;
3703   if(ftpwc && ftpwc->parser)
3704     Curl_ftp_parselist_data_free(&ftpwc->parser);
3705   free(ftpwc);
3706 }
3707 
init_wc_data(struct connectdata * conn)3708 static CURLcode init_wc_data(struct connectdata *conn)
3709 {
3710   char *last_slash;
3711   struct FTP *ftp = conn->data->req.p.ftp;
3712   char *path = ftp->path;
3713   struct WildcardData *wildcard = &(conn->data->wildcard);
3714   CURLcode result = CURLE_OK;
3715   struct ftp_wc *ftpwc = NULL;
3716 
3717   last_slash = strrchr(ftp->path, '/');
3718   if(last_slash) {
3719     last_slash++;
3720     if(last_slash[0] == '\0') {
3721       wildcard->state = CURLWC_CLEAN;
3722       result = ftp_parse_url_path(conn);
3723       return result;
3724     }
3725     wildcard->pattern = strdup(last_slash);
3726     if(!wildcard->pattern)
3727       return CURLE_OUT_OF_MEMORY;
3728     last_slash[0] = '\0'; /* cut file from path */
3729   }
3730   else { /* there is only 'wildcard pattern' or nothing */
3731     if(path[0]) {
3732       wildcard->pattern = strdup(path);
3733       if(!wildcard->pattern)
3734         return CURLE_OUT_OF_MEMORY;
3735       path[0] = '\0';
3736     }
3737     else { /* only list */
3738       wildcard->state = CURLWC_CLEAN;
3739       result = ftp_parse_url_path(conn);
3740       return result;
3741     }
3742   }
3743 
3744   /* program continues only if URL is not ending with slash, allocate needed
3745      resources for wildcard transfer */
3746 
3747   /* allocate ftp protocol specific wildcard data */
3748   ftpwc = calloc(1, sizeof(struct ftp_wc));
3749   if(!ftpwc) {
3750     result = CURLE_OUT_OF_MEMORY;
3751     goto fail;
3752   }
3753 
3754   /* INITIALIZE parselist structure */
3755   ftpwc->parser = Curl_ftp_parselist_data_alloc();
3756   if(!ftpwc->parser) {
3757     result = CURLE_OUT_OF_MEMORY;
3758     goto fail;
3759   }
3760 
3761   wildcard->protdata = ftpwc; /* put it to the WildcardData tmp pointer */
3762   wildcard->dtor = wc_data_dtor;
3763 
3764   /* wildcard does not support NOCWD option (assert it?) */
3765   if(conn->data->set.ftp_filemethod == FTPFILE_NOCWD)
3766     conn->data->set.ftp_filemethod = FTPFILE_MULTICWD;
3767 
3768   /* try to parse ftp url */
3769   result = ftp_parse_url_path(conn);
3770   if(result) {
3771     goto fail;
3772   }
3773 
3774   wildcard->path = strdup(ftp->path);
3775   if(!wildcard->path) {
3776     result = CURLE_OUT_OF_MEMORY;
3777     goto fail;
3778   }
3779 
3780   /* backup old write_function */
3781   ftpwc->backup.write_function = conn->data->set.fwrite_func;
3782   /* parsing write function */
3783   conn->data->set.fwrite_func = Curl_ftp_parselist;
3784   /* backup old file descriptor */
3785   ftpwc->backup.file_descriptor = conn->data->set.out;
3786   /* let the writefunc callback know what curl pointer is working with */
3787   conn->data->set.out = conn;
3788 
3789   infof(conn->data, "Wildcard - Parsing started\n");
3790   return CURLE_OK;
3791 
3792   fail:
3793   if(ftpwc) {
3794     Curl_ftp_parselist_data_free(&ftpwc->parser);
3795     free(ftpwc);
3796   }
3797   Curl_safefree(wildcard->pattern);
3798   wildcard->dtor = ZERO_NULL;
3799   wildcard->protdata = NULL;
3800   return result;
3801 }
3802 
wc_statemach(struct connectdata * conn)3803 static CURLcode wc_statemach(struct connectdata *conn)
3804 {
3805   struct WildcardData * const wildcard = &(conn->data->wildcard);
3806   CURLcode result = CURLE_OK;
3807 
3808   for(;;) {
3809     switch(wildcard->state) {
3810     case CURLWC_INIT:
3811       result = init_wc_data(conn);
3812       if(wildcard->state == CURLWC_CLEAN)
3813         /* only listing! */
3814         return result;
3815       wildcard->state = result ? CURLWC_ERROR : CURLWC_MATCHING;
3816       return result;
3817 
3818     case CURLWC_MATCHING: {
3819       /* In this state is LIST response successfully parsed, so lets restore
3820          previous WRITEFUNCTION callback and WRITEDATA pointer */
3821       struct ftp_wc *ftpwc = wildcard->protdata;
3822       conn->data->set.fwrite_func = ftpwc->backup.write_function;
3823       conn->data->set.out = ftpwc->backup.file_descriptor;
3824       ftpwc->backup.write_function = ZERO_NULL;
3825       ftpwc->backup.file_descriptor = NULL;
3826       wildcard->state = CURLWC_DOWNLOADING;
3827 
3828       if(Curl_ftp_parselist_geterror(ftpwc->parser)) {
3829         /* error found in LIST parsing */
3830         wildcard->state = CURLWC_CLEAN;
3831         continue;
3832       }
3833       if(wildcard->filelist.size == 0) {
3834         /* no corresponding file */
3835         wildcard->state = CURLWC_CLEAN;
3836         return CURLE_REMOTE_FILE_NOT_FOUND;
3837       }
3838       continue;
3839     }
3840 
3841     case CURLWC_DOWNLOADING: {
3842       /* filelist has at least one file, lets get first one */
3843       struct ftp_conn *ftpc = &conn->proto.ftpc;
3844       struct curl_fileinfo *finfo = wildcard->filelist.head->ptr;
3845       struct FTP *ftp = conn->data->req.p.ftp;
3846 
3847       char *tmp_path = aprintf("%s%s", wildcard->path, finfo->filename);
3848       if(!tmp_path)
3849         return CURLE_OUT_OF_MEMORY;
3850 
3851       /* switch default ftp->path and tmp_path */
3852       free(ftp->pathalloc);
3853       ftp->pathalloc = ftp->path = tmp_path;
3854 
3855       infof(conn->data, "Wildcard - START of \"%s\"\n", finfo->filename);
3856       if(conn->data->set.chunk_bgn) {
3857         long userresponse;
3858         Curl_set_in_callback(conn->data, true);
3859         userresponse = conn->data->set.chunk_bgn(
3860           finfo, wildcard->customptr, (int)wildcard->filelist.size);
3861         Curl_set_in_callback(conn->data, false);
3862         switch(userresponse) {
3863         case CURL_CHUNK_BGN_FUNC_SKIP:
3864           infof(conn->data, "Wildcard - \"%s\" skipped by user\n",
3865                 finfo->filename);
3866           wildcard->state = CURLWC_SKIP;
3867           continue;
3868         case CURL_CHUNK_BGN_FUNC_FAIL:
3869           return CURLE_CHUNK_FAILED;
3870         }
3871       }
3872 
3873       if(finfo->filetype != CURLFILETYPE_FILE) {
3874         wildcard->state = CURLWC_SKIP;
3875         continue;
3876       }
3877 
3878       if(finfo->flags & CURLFINFOFLAG_KNOWN_SIZE)
3879         ftpc->known_filesize = finfo->size;
3880 
3881       result = ftp_parse_url_path(conn);
3882       if(result)
3883         return result;
3884 
3885       /* we don't need the Curl_fileinfo of first file anymore */
3886       Curl_llist_remove(&wildcard->filelist, wildcard->filelist.head, NULL);
3887 
3888       if(wildcard->filelist.size == 0) { /* remains only one file to down. */
3889         wildcard->state = CURLWC_CLEAN;
3890         /* after that will be ftp_do called once again and no transfer
3891            will be done because of CURLWC_CLEAN state */
3892         return CURLE_OK;
3893       }
3894       return result;
3895     }
3896 
3897     case CURLWC_SKIP: {
3898       if(conn->data->set.chunk_end) {
3899         Curl_set_in_callback(conn->data, true);
3900         conn->data->set.chunk_end(conn->data->wildcard.customptr);
3901         Curl_set_in_callback(conn->data, false);
3902       }
3903       Curl_llist_remove(&wildcard->filelist, wildcard->filelist.head, NULL);
3904       wildcard->state = (wildcard->filelist.size == 0) ?
3905         CURLWC_CLEAN : CURLWC_DOWNLOADING;
3906       continue;
3907     }
3908 
3909     case CURLWC_CLEAN: {
3910       struct ftp_wc *ftpwc = wildcard->protdata;
3911       result = CURLE_OK;
3912       if(ftpwc)
3913         result = Curl_ftp_parselist_geterror(ftpwc->parser);
3914 
3915       wildcard->state = result ? CURLWC_ERROR : CURLWC_DONE;
3916       return result;
3917     }
3918 
3919     case CURLWC_DONE:
3920     case CURLWC_ERROR:
3921     case CURLWC_CLEAR:
3922       if(wildcard->dtor)
3923         wildcard->dtor(wildcard->protdata);
3924       return result;
3925     }
3926   }
3927   /* UNREACHABLE */
3928 }
3929 
3930 /***********************************************************************
3931  *
3932  * ftp_do()
3933  *
3934  * This function is registered as 'curl_do' function. It decodes the path
3935  * parts etc as a wrapper to the actual DO function (ftp_perform).
3936  *
3937  * The input argument is already checked for validity.
3938  */
ftp_do(struct connectdata * conn,bool * done)3939 static CURLcode ftp_do(struct connectdata *conn, bool *done)
3940 {
3941   CURLcode result = CURLE_OK;
3942   struct ftp_conn *ftpc = &conn->proto.ftpc;
3943 
3944   *done = FALSE; /* default to false */
3945   ftpc->wait_data_conn = FALSE; /* default to no such wait */
3946 
3947   if(conn->data->state.wildcardmatch) {
3948     result = wc_statemach(conn);
3949     if(conn->data->wildcard.state == CURLWC_SKIP ||
3950       conn->data->wildcard.state == CURLWC_DONE) {
3951       /* do not call ftp_regular_transfer */
3952       return CURLE_OK;
3953     }
3954     if(result) /* error, loop or skipping the file */
3955       return result;
3956   }
3957   else { /* no wildcard FSM needed */
3958     result = ftp_parse_url_path(conn);
3959     if(result)
3960       return result;
3961   }
3962 
3963   result = ftp_regular_transfer(conn, done);
3964 
3965   return result;
3966 }
3967 
3968 /***********************************************************************
3969  *
3970  * ftp_quit()
3971  *
3972  * This should be called before calling sclose() on an ftp control connection
3973  * (not data connections). We should then wait for the response from the
3974  * server before returning. The calling code should then try to close the
3975  * connection.
3976  *
3977  */
ftp_quit(struct connectdata * conn)3978 static CURLcode ftp_quit(struct connectdata *conn)
3979 {
3980   CURLcode result = CURLE_OK;
3981 
3982   if(conn->proto.ftpc.ctl_valid) {
3983     result = Curl_pp_sendf(&conn->proto.ftpc.pp, "%s", "QUIT");
3984     if(result) {
3985       failf(conn->data, "Failure sending QUIT command: %s",
3986             curl_easy_strerror(result));
3987       conn->proto.ftpc.ctl_valid = FALSE; /* mark control connection as bad */
3988       connclose(conn, "QUIT command failed"); /* mark for connection closure */
3989       state(conn, FTP_STOP);
3990       return result;
3991     }
3992 
3993     state(conn, FTP_QUIT);
3994 
3995     result = ftp_block_statemach(conn);
3996   }
3997 
3998   return result;
3999 }
4000 
4001 /***********************************************************************
4002  *
4003  * ftp_disconnect()
4004  *
4005  * Disconnect from an FTP server. Cleanup protocol-specific per-connection
4006  * resources. BLOCKING.
4007  */
ftp_disconnect(struct connectdata * conn,bool dead_connection)4008 static CURLcode ftp_disconnect(struct connectdata *conn, bool dead_connection)
4009 {
4010   struct ftp_conn *ftpc = &conn->proto.ftpc;
4011   struct pingpong *pp = &ftpc->pp;
4012 
4013   /* We cannot send quit unconditionally. If this connection is stale or
4014      bad in any way, sending quit and waiting around here will make the
4015      disconnect wait in vain and cause more problems than we need to.
4016 
4017      ftp_quit() will check the state of ftp->ctl_valid. If it's ok it
4018      will try to send the QUIT command, otherwise it will just return.
4019   */
4020   if(dead_connection)
4021     ftpc->ctl_valid = FALSE;
4022 
4023   /* The FTP session may or may not have been allocated/setup at this point! */
4024   (void)ftp_quit(conn); /* ignore errors on the QUIT */
4025 
4026   if(ftpc->entrypath) {
4027     struct Curl_easy *data = conn->data;
4028     if(data->state.most_recent_ftp_entrypath == ftpc->entrypath) {
4029       data->state.most_recent_ftp_entrypath = NULL;
4030     }
4031     Curl_safefree(ftpc->entrypath);
4032   }
4033 
4034   freedirs(ftpc);
4035   Curl_safefree(ftpc->prevpath);
4036   Curl_safefree(ftpc->server_os);
4037   Curl_pp_disconnect(pp);
4038   Curl_sec_end(conn);
4039   return CURLE_OK;
4040 }
4041 
4042 /***********************************************************************
4043  *
4044  * ftp_parse_url_path()
4045  *
4046  * Parse the URL path into separate path components.
4047  *
4048  */
4049 static
ftp_parse_url_path(struct connectdata * conn)4050 CURLcode ftp_parse_url_path(struct connectdata *conn)
4051 {
4052   struct Curl_easy *data = conn->data;
4053   /* the ftp struct is already inited in ftp_connect() */
4054   struct FTP *ftp = data->req.p.ftp;
4055   struct ftp_conn *ftpc = &conn->proto.ftpc;
4056   const char *slashPos = NULL;
4057   const char *fileName = NULL;
4058   CURLcode result = CURLE_OK;
4059   char *rawPath = NULL; /* url-decoded "raw" path */
4060   size_t pathLen = 0;
4061 
4062   ftpc->ctl_valid = FALSE;
4063   ftpc->cwdfail = FALSE;
4064 
4065   /* url-decode ftp path before further evaluation */
4066   result = Curl_urldecode(data, ftp->path, 0, &rawPath, &pathLen, REJECT_CTRL);
4067   if(result)
4068     return result;
4069 
4070   switch(data->set.ftp_filemethod) {
4071     case FTPFILE_NOCWD: /* fastest, but less standard-compliant */
4072 
4073       if((pathLen > 0) && (rawPath[pathLen - 1] != '/'))
4074           fileName = rawPath;  /* this is a full file path */
4075       /*
4076         else: ftpc->file is not used anywhere other than for operations on
4077               a file. In other words, never for directory operations.
4078               So we can safely leave filename as NULL here and use it as a
4079               argument in dir/file decisions.
4080       */
4081       break;
4082 
4083     case FTPFILE_SINGLECWD:
4084       slashPos = strrchr(rawPath, '/');
4085       if(slashPos) {
4086         /* get path before last slash, except for / */
4087         size_t dirlen = slashPos - rawPath;
4088         if(dirlen == 0)
4089             dirlen++;
4090 
4091         ftpc->dirs = calloc(1, sizeof(ftpc->dirs[0]));
4092         if(!ftpc->dirs) {
4093           free(rawPath);
4094           return CURLE_OUT_OF_MEMORY;
4095         }
4096 
4097         ftpc->dirs[0] = calloc(1, dirlen + 1);
4098         if(!ftpc->dirs[0]) {
4099           free(rawPath);
4100           return CURLE_OUT_OF_MEMORY;
4101         }
4102 
4103         strncpy(ftpc->dirs[0], rawPath, dirlen);
4104         ftpc->dirdepth = 1; /* we consider it to be a single dir */
4105         fileName = slashPos + 1; /* rest is file name */
4106       }
4107       else
4108         fileName = rawPath; /* file name only (or empty) */
4109       break;
4110 
4111     default: /* allow pretty much anything */
4112     case FTPFILE_MULTICWD: {
4113       /* current position: begin of next path component */
4114       const char *curPos = rawPath;
4115 
4116       int dirAlloc = 0; /* number of entries allocated for the 'dirs' array */
4117       const char *str = rawPath;
4118       for(; *str != 0; ++str)
4119         if (*str == '/')
4120           ++dirAlloc;
4121 
4122       if(dirAlloc > 0) {
4123         ftpc->dirs = calloc(dirAlloc, sizeof(ftpc->dirs[0]));
4124         if(!ftpc->dirs) {
4125           free(rawPath);
4126           return CURLE_OUT_OF_MEMORY;
4127         }
4128 
4129         /* parse the URL path into separate path components */
4130         while((slashPos = strchr(curPos, '/')) != NULL) {
4131           size_t compLen = slashPos - curPos;
4132 
4133           /* path starts with a slash: add that as a directory */
4134           if((compLen == 0) && (ftpc->dirdepth == 0))
4135             ++compLen;
4136 
4137           /* we skip empty path components, like "x//y" since the FTP command
4138              CWD requires a parameter and a non-existent parameter a) doesn't
4139              work on many servers and b) has no effect on the others. */
4140           if(compLen > 0) {
4141             char *comp = calloc(1, compLen + 1);
4142             if(!comp) {
4143               free(rawPath);
4144               return CURLE_OUT_OF_MEMORY;
4145             }
4146             strncpy(comp, curPos, compLen);
4147             ftpc->dirs[ftpc->dirdepth++] = comp;
4148           }
4149           curPos = slashPos + 1;
4150         }
4151       }
4152       DEBUGASSERT(ftpc->dirdepth <= dirAlloc);
4153       fileName = curPos; /* the rest is the file name (or empty) */
4154     }
4155     break;
4156   } /* switch */
4157 
4158   if(fileName && *fileName)
4159     ftpc->file = strdup(fileName);
4160   else
4161     ftpc->file = NULL; /* instead of point to a zero byte,
4162                             we make it a NULL pointer */
4163 
4164   if(data->set.upload && !ftpc->file && (ftp->transfer == FTPTRANSFER_BODY)) {
4165     /* We need a file name when uploading. Return error! */
4166     failf(data, "Uploading to a URL without a file name!");
4167     free(rawPath);
4168     return CURLE_URL_MALFORMAT;
4169   }
4170 
4171   ftpc->cwddone = FALSE; /* default to not done */
4172 
4173   if((data->set.ftp_filemethod == FTPFILE_NOCWD) && (rawPath[0] == '/'))
4174     ftpc->cwddone = TRUE; /* skip CWD for absolute paths */
4175   else { /* newly created FTP connections are already in entry path */
4176     const char *oldPath = conn->bits.reuse ? ftpc->prevpath : "";
4177     if(oldPath) {
4178       size_t n = pathLen;
4179       if(data->set.ftp_filemethod == FTPFILE_NOCWD)
4180         n = 0; /* CWD to entry for relative paths */
4181       else
4182         n -= ftpc->file?strlen(ftpc->file):0;
4183 
4184       if((strlen(oldPath) == n) && !strncmp(rawPath, oldPath, n)) {
4185         infof(data, "Request has same path as previous transfer\n");
4186         ftpc->cwddone = TRUE;
4187       }
4188     }
4189   }
4190 
4191   free(rawPath);
4192   return CURLE_OK;
4193 }
4194 
4195 /* call this when the DO phase has completed */
ftp_dophase_done(struct connectdata * conn,bool connected)4196 static CURLcode ftp_dophase_done(struct connectdata *conn,
4197                                  bool connected)
4198 {
4199   struct FTP *ftp = conn->data->req.p.ftp;
4200   struct ftp_conn *ftpc = &conn->proto.ftpc;
4201 
4202   if(connected) {
4203     int completed;
4204     CURLcode result = ftp_do_more(conn, &completed);
4205 
4206     if(result) {
4207       close_secondarysocket(conn);
4208       return result;
4209     }
4210   }
4211 
4212   if(ftp->transfer != FTPTRANSFER_BODY)
4213     /* no data to transfer */
4214     Curl_setup_transfer(conn->data, -1, -1, FALSE, -1);
4215   else if(!connected)
4216     /* since we didn't connect now, we want do_more to get called */
4217     conn->bits.do_more = TRUE;
4218 
4219   ftpc->ctl_valid = TRUE; /* seems good */
4220 
4221   return CURLE_OK;
4222 }
4223 
4224 /* called from multi.c while DOing */
ftp_doing(struct connectdata * conn,bool * dophase_done)4225 static CURLcode ftp_doing(struct connectdata *conn,
4226                           bool *dophase_done)
4227 {
4228   CURLcode result = ftp_multi_statemach(conn, dophase_done);
4229 
4230   if(result)
4231     DEBUGF(infof(conn->data, "DO phase failed\n"));
4232   else if(*dophase_done) {
4233     result = ftp_dophase_done(conn, FALSE /* not connected */);
4234 
4235     DEBUGF(infof(conn->data, "DO phase is complete2\n"));
4236   }
4237   return result;
4238 }
4239 
4240 /***********************************************************************
4241  *
4242  * ftp_regular_transfer()
4243  *
4244  * The input argument is already checked for validity.
4245  *
4246  * Performs all commands done before a regular transfer between a local and a
4247  * remote host.
4248  *
4249  * ftp->ctl_valid starts out as FALSE, and gets set to TRUE if we reach the
4250  * ftp_done() function without finding any major problem.
4251  */
4252 static
ftp_regular_transfer(struct connectdata * conn,bool * dophase_done)4253 CURLcode ftp_regular_transfer(struct connectdata *conn,
4254                               bool *dophase_done)
4255 {
4256   CURLcode result = CURLE_OK;
4257   bool connected = FALSE;
4258   struct Curl_easy *data = conn->data;
4259   struct ftp_conn *ftpc = &conn->proto.ftpc;
4260   data->req.size = -1; /* make sure this is unknown at this point */
4261 
4262   Curl_pgrsSetUploadCounter(data, 0);
4263   Curl_pgrsSetDownloadCounter(data, 0);
4264   Curl_pgrsSetUploadSize(data, -1);
4265   Curl_pgrsSetDownloadSize(data, -1);
4266 
4267   ftpc->ctl_valid = TRUE; /* starts good */
4268 
4269   result = ftp_perform(conn,
4270                        &connected, /* have we connected after PASV/PORT */
4271                        dophase_done); /* all commands in the DO-phase done? */
4272 
4273   if(!result) {
4274 
4275     if(!*dophase_done)
4276       /* the DO phase has not completed yet */
4277       return CURLE_OK;
4278 
4279     result = ftp_dophase_done(conn, connected);
4280 
4281     if(result)
4282       return result;
4283   }
4284   else
4285     freedirs(ftpc);
4286 
4287   return result;
4288 }
4289 
ftp_setup_connection(struct connectdata * conn)4290 static CURLcode ftp_setup_connection(struct connectdata *conn)
4291 {
4292   struct Curl_easy *data = conn->data;
4293   char *type;
4294   struct FTP *ftp;
4295 
4296   conn->data->req.p.ftp = ftp = calloc(sizeof(struct FTP), 1);
4297   if(NULL == ftp)
4298     return CURLE_OUT_OF_MEMORY;
4299 
4300   ftp->path = &data->state.up.path[1]; /* don't include the initial slash */
4301 
4302   /* FTP URLs support an extension like ";type=<typecode>" that
4303    * we'll try to get now! */
4304   type = strstr(ftp->path, ";type=");
4305 
4306   if(!type)
4307     type = strstr(conn->host.rawalloc, ";type=");
4308 
4309   if(type) {
4310     char command;
4311     *type = 0;                     /* it was in the middle of the hostname */
4312     command = Curl_raw_toupper(type[6]);
4313 
4314     switch(command) {
4315     case 'A': /* ASCII mode */
4316       data->set.prefer_ascii = TRUE;
4317       break;
4318 
4319     case 'D': /* directory mode */
4320       data->set.ftp_list_only = TRUE;
4321       break;
4322 
4323     case 'I': /* binary mode */
4324     default:
4325       /* switch off ASCII */
4326       data->set.prefer_ascii = FALSE;
4327       break;
4328     }
4329   }
4330 
4331   /* get some initial data into the ftp struct */
4332   ftp->transfer = FTPTRANSFER_BODY;
4333   ftp->downloadsize = 0;
4334   conn->proto.ftpc.known_filesize = -1; /* unknown size for now */
4335 
4336   return CURLE_OK;
4337 }
4338 
4339 #endif /* CURL_DISABLE_FTP */
4340