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