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