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