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