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