113498266Sopenharmony_ci/***************************************************************************
213498266Sopenharmony_ci *                                  _   _ ____  _
313498266Sopenharmony_ci *  Project                     ___| | | |  _ \| |
413498266Sopenharmony_ci *                             / __| | | | |_) | |
513498266Sopenharmony_ci *                            | (__| |_| |  _ <| |___
613498266Sopenharmony_ci *                             \___|\___/|_| \_\_____|
713498266Sopenharmony_ci *
813498266Sopenharmony_ci * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
913498266Sopenharmony_ci *
1013498266Sopenharmony_ci * This software is licensed as described in the file COPYING, which
1113498266Sopenharmony_ci * you should have received as part of this distribution. The terms
1213498266Sopenharmony_ci * are also available at https://curl.se/docs/copyright.html.
1313498266Sopenharmony_ci *
1413498266Sopenharmony_ci * You may opt to use, copy, modify, merge, publish, distribute and/or sell
1513498266Sopenharmony_ci * copies of the Software, and permit persons to whom the Software is
1613498266Sopenharmony_ci * furnished to do so, under the terms of the COPYING file.
1713498266Sopenharmony_ci *
1813498266Sopenharmony_ci * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
1913498266Sopenharmony_ci * KIND, either express or implied.
2013498266Sopenharmony_ci *
2113498266Sopenharmony_ci * SPDX-License-Identifier: curl
2213498266Sopenharmony_ci *
2313498266Sopenharmony_ci ***************************************************************************/
2413498266Sopenharmony_ci
2513498266Sopenharmony_ci#include "curl_setup.h"
2613498266Sopenharmony_ci
2713498266Sopenharmony_ci#ifndef CURL_DISABLE_FTP
2813498266Sopenharmony_ci
2913498266Sopenharmony_ci#ifdef HAVE_NETINET_IN_H
3013498266Sopenharmony_ci#include <netinet/in.h>
3113498266Sopenharmony_ci#endif
3213498266Sopenharmony_ci#ifdef HAVE_ARPA_INET_H
3313498266Sopenharmony_ci#include <arpa/inet.h>
3413498266Sopenharmony_ci#endif
3513498266Sopenharmony_ci#ifdef HAVE_NETDB_H
3613498266Sopenharmony_ci#include <netdb.h>
3713498266Sopenharmony_ci#endif
3813498266Sopenharmony_ci#ifdef __VMS
3913498266Sopenharmony_ci#include <in.h>
4013498266Sopenharmony_ci#include <inet.h>
4113498266Sopenharmony_ci#endif
4213498266Sopenharmony_ci
4313498266Sopenharmony_ci#include <curl/curl.h>
4413498266Sopenharmony_ci#include "urldata.h"
4513498266Sopenharmony_ci#include "sendf.h"
4613498266Sopenharmony_ci#include "if2ip.h"
4713498266Sopenharmony_ci#include "hostip.h"
4813498266Sopenharmony_ci#include "progress.h"
4913498266Sopenharmony_ci#include "transfer.h"
5013498266Sopenharmony_ci#include "escape.h"
5113498266Sopenharmony_ci#include "http.h" /* for HTTP proxy tunnel stuff */
5213498266Sopenharmony_ci#include "ftp.h"
5313498266Sopenharmony_ci#include "fileinfo.h"
5413498266Sopenharmony_ci#include "ftplistparser.h"
5513498266Sopenharmony_ci#include "curl_range.h"
5613498266Sopenharmony_ci#include "curl_krb5.h"
5713498266Sopenharmony_ci#include "strtoofft.h"
5813498266Sopenharmony_ci#include "strcase.h"
5913498266Sopenharmony_ci#include "vtls/vtls.h"
6013498266Sopenharmony_ci#include "cfilters.h"
6113498266Sopenharmony_ci#include "cf-socket.h"
6213498266Sopenharmony_ci#include "connect.h"
6313498266Sopenharmony_ci#include "strerror.h"
6413498266Sopenharmony_ci#include "inet_ntop.h"
6513498266Sopenharmony_ci#include "inet_pton.h"
6613498266Sopenharmony_ci#include "select.h"
6713498266Sopenharmony_ci#include "parsedate.h" /* for the week day and month names */
6813498266Sopenharmony_ci#include "sockaddr.h" /* required for Curl_sockaddr_storage */
6913498266Sopenharmony_ci#include "multiif.h"
7013498266Sopenharmony_ci#include "url.h"
7113498266Sopenharmony_ci#include "speedcheck.h"
7213498266Sopenharmony_ci#include "warnless.h"
7313498266Sopenharmony_ci#include "http_proxy.h"
7413498266Sopenharmony_ci#include "socks.h"
7513498266Sopenharmony_ci#include "strdup.h"
7613498266Sopenharmony_ci/* The last 3 #include files should be in this order */
7713498266Sopenharmony_ci#include "curl_printf.h"
7813498266Sopenharmony_ci#include "curl_memory.h"
7913498266Sopenharmony_ci#include "memdebug.h"
8013498266Sopenharmony_ci
8113498266Sopenharmony_ci#ifndef NI_MAXHOST
8213498266Sopenharmony_ci#define NI_MAXHOST 1025
8313498266Sopenharmony_ci#endif
8413498266Sopenharmony_ci#ifndef INET_ADDRSTRLEN
8513498266Sopenharmony_ci#define INET_ADDRSTRLEN 16
8613498266Sopenharmony_ci#endif
8713498266Sopenharmony_ci
8813498266Sopenharmony_ci#ifdef CURL_DISABLE_VERBOSE_STRINGS
8913498266Sopenharmony_ci#define ftp_pasv_verbose(a,b,c,d)  Curl_nop_stmt
9013498266Sopenharmony_ci#endif
9113498266Sopenharmony_ci
9213498266Sopenharmony_ci/* Local API functions */
9313498266Sopenharmony_ci#ifndef DEBUGBUILD
9413498266Sopenharmony_cistatic void _ftp_state(struct Curl_easy *data,
9513498266Sopenharmony_ci                       ftpstate newstate);
9613498266Sopenharmony_ci#define ftp_state(x,y) _ftp_state(x,y)
9713498266Sopenharmony_ci#else
9813498266Sopenharmony_cistatic void _ftp_state(struct Curl_easy *data,
9913498266Sopenharmony_ci                       ftpstate newstate,
10013498266Sopenharmony_ci                       int lineno);
10113498266Sopenharmony_ci#define ftp_state(x,y) _ftp_state(x,y,__LINE__)
10213498266Sopenharmony_ci#endif
10313498266Sopenharmony_ci
10413498266Sopenharmony_cistatic CURLcode ftp_sendquote(struct Curl_easy *data,
10513498266Sopenharmony_ci                              struct connectdata *conn,
10613498266Sopenharmony_ci                              struct curl_slist *quote);
10713498266Sopenharmony_cistatic CURLcode ftp_quit(struct Curl_easy *data, struct connectdata *conn);
10813498266Sopenharmony_cistatic CURLcode ftp_parse_url_path(struct Curl_easy *data);
10913498266Sopenharmony_cistatic CURLcode ftp_regular_transfer(struct Curl_easy *data, bool *done);
11013498266Sopenharmony_ci#ifndef CURL_DISABLE_VERBOSE_STRINGS
11113498266Sopenharmony_cistatic void ftp_pasv_verbose(struct Curl_easy *data,
11213498266Sopenharmony_ci                             struct Curl_addrinfo *ai,
11313498266Sopenharmony_ci                             char *newhost, /* ascii version */
11413498266Sopenharmony_ci                             int port);
11513498266Sopenharmony_ci#endif
11613498266Sopenharmony_cistatic CURLcode ftp_state_prepare_transfer(struct Curl_easy *data);
11713498266Sopenharmony_cistatic CURLcode ftp_state_mdtm(struct Curl_easy *data);
11813498266Sopenharmony_cistatic CURLcode ftp_state_quote(struct Curl_easy *data,
11913498266Sopenharmony_ci                                bool init, ftpstate instate);
12013498266Sopenharmony_cistatic CURLcode ftp_nb_type(struct Curl_easy *data,
12113498266Sopenharmony_ci                            struct connectdata *conn,
12213498266Sopenharmony_ci                            bool ascii, ftpstate newstate);
12313498266Sopenharmony_cistatic int ftp_need_type(struct connectdata *conn,
12413498266Sopenharmony_ci                         bool ascii);
12513498266Sopenharmony_cistatic CURLcode ftp_do(struct Curl_easy *data, bool *done);
12613498266Sopenharmony_cistatic CURLcode ftp_done(struct Curl_easy *data,
12713498266Sopenharmony_ci                         CURLcode, bool premature);
12813498266Sopenharmony_cistatic CURLcode ftp_connect(struct Curl_easy *data, bool *done);
12913498266Sopenharmony_cistatic CURLcode ftp_disconnect(struct Curl_easy *data,
13013498266Sopenharmony_ci                               struct connectdata *conn, bool dead_connection);
13113498266Sopenharmony_cistatic CURLcode ftp_do_more(struct Curl_easy *data, int *completed);
13213498266Sopenharmony_cistatic CURLcode ftp_multi_statemach(struct Curl_easy *data, bool *done);
13313498266Sopenharmony_cistatic int ftp_getsock(struct Curl_easy *data, struct connectdata *conn,
13413498266Sopenharmony_ci                       curl_socket_t *socks);
13513498266Sopenharmony_cistatic int ftp_domore_getsock(struct Curl_easy *data,
13613498266Sopenharmony_ci                              struct connectdata *conn, curl_socket_t *socks);
13713498266Sopenharmony_cistatic CURLcode ftp_doing(struct Curl_easy *data,
13813498266Sopenharmony_ci                          bool *dophase_done);
13913498266Sopenharmony_cistatic CURLcode ftp_setup_connection(struct Curl_easy *data,
14013498266Sopenharmony_ci                                     struct connectdata *conn);
14113498266Sopenharmony_cistatic CURLcode init_wc_data(struct Curl_easy *data);
14213498266Sopenharmony_cistatic CURLcode wc_statemach(struct Curl_easy *data);
14313498266Sopenharmony_cistatic void wc_data_dtor(void *ptr);
14413498266Sopenharmony_cistatic CURLcode ftp_state_retr(struct Curl_easy *data, curl_off_t filesize);
14513498266Sopenharmony_cistatic CURLcode ftp_readresp(struct Curl_easy *data,
14613498266Sopenharmony_ci                             curl_socket_t sockfd,
14713498266Sopenharmony_ci                             struct pingpong *pp,
14813498266Sopenharmony_ci                             int *ftpcode,
14913498266Sopenharmony_ci                             size_t *size);
15013498266Sopenharmony_cistatic CURLcode ftp_dophase_done(struct Curl_easy *data,
15113498266Sopenharmony_ci                                 bool connected);
15213498266Sopenharmony_ci
15313498266Sopenharmony_ci/*
15413498266Sopenharmony_ci * FTP protocol handler.
15513498266Sopenharmony_ci */
15613498266Sopenharmony_ci
15713498266Sopenharmony_ciconst struct Curl_handler Curl_handler_ftp = {
15813498266Sopenharmony_ci  "FTP",                           /* scheme */
15913498266Sopenharmony_ci  ftp_setup_connection,            /* setup_connection */
16013498266Sopenharmony_ci  ftp_do,                          /* do_it */
16113498266Sopenharmony_ci  ftp_done,                        /* done */
16213498266Sopenharmony_ci  ftp_do_more,                     /* do_more */
16313498266Sopenharmony_ci  ftp_connect,                     /* connect_it */
16413498266Sopenharmony_ci  ftp_multi_statemach,             /* connecting */
16513498266Sopenharmony_ci  ftp_doing,                       /* doing */
16613498266Sopenharmony_ci  ftp_getsock,                     /* proto_getsock */
16713498266Sopenharmony_ci  ftp_getsock,                     /* doing_getsock */
16813498266Sopenharmony_ci  ftp_domore_getsock,              /* domore_getsock */
16913498266Sopenharmony_ci  ZERO_NULL,                       /* perform_getsock */
17013498266Sopenharmony_ci  ftp_disconnect,                  /* disconnect */
17113498266Sopenharmony_ci  ZERO_NULL,                       /* write_resp */
17213498266Sopenharmony_ci  ZERO_NULL,                       /* connection_check */
17313498266Sopenharmony_ci  ZERO_NULL,                       /* attach connection */
17413498266Sopenharmony_ci  PORT_FTP,                        /* defport */
17513498266Sopenharmony_ci  CURLPROTO_FTP,                   /* protocol */
17613498266Sopenharmony_ci  CURLPROTO_FTP,                   /* family */
17713498266Sopenharmony_ci  PROTOPT_DUAL | PROTOPT_CLOSEACTION | PROTOPT_NEEDSPWD |
17813498266Sopenharmony_ci  PROTOPT_NOURLQUERY | PROTOPT_PROXY_AS_HTTP |
17913498266Sopenharmony_ci  PROTOPT_WILDCARD /* flags */
18013498266Sopenharmony_ci};
18113498266Sopenharmony_ci
18213498266Sopenharmony_ci
18313498266Sopenharmony_ci#ifdef USE_SSL
18413498266Sopenharmony_ci/*
18513498266Sopenharmony_ci * FTPS protocol handler.
18613498266Sopenharmony_ci */
18713498266Sopenharmony_ci
18813498266Sopenharmony_ciconst struct Curl_handler Curl_handler_ftps = {
18913498266Sopenharmony_ci  "FTPS",                          /* scheme */
19013498266Sopenharmony_ci  ftp_setup_connection,            /* setup_connection */
19113498266Sopenharmony_ci  ftp_do,                          /* do_it */
19213498266Sopenharmony_ci  ftp_done,                        /* done */
19313498266Sopenharmony_ci  ftp_do_more,                     /* do_more */
19413498266Sopenharmony_ci  ftp_connect,                     /* connect_it */
19513498266Sopenharmony_ci  ftp_multi_statemach,             /* connecting */
19613498266Sopenharmony_ci  ftp_doing,                       /* doing */
19713498266Sopenharmony_ci  ftp_getsock,                     /* proto_getsock */
19813498266Sopenharmony_ci  ftp_getsock,                     /* doing_getsock */
19913498266Sopenharmony_ci  ftp_domore_getsock,              /* domore_getsock */
20013498266Sopenharmony_ci  ZERO_NULL,                       /* perform_getsock */
20113498266Sopenharmony_ci  ftp_disconnect,                  /* disconnect */
20213498266Sopenharmony_ci  ZERO_NULL,                       /* write_resp */
20313498266Sopenharmony_ci  ZERO_NULL,                       /* connection_check */
20413498266Sopenharmony_ci  ZERO_NULL,                       /* attach connection */
20513498266Sopenharmony_ci  PORT_FTPS,                       /* defport */
20613498266Sopenharmony_ci  CURLPROTO_FTPS,                  /* protocol */
20713498266Sopenharmony_ci  CURLPROTO_FTP,                   /* family */
20813498266Sopenharmony_ci  PROTOPT_SSL | PROTOPT_DUAL | PROTOPT_CLOSEACTION |
20913498266Sopenharmony_ci  PROTOPT_NEEDSPWD | PROTOPT_NOURLQUERY | PROTOPT_WILDCARD /* flags */
21013498266Sopenharmony_ci};
21113498266Sopenharmony_ci#endif
21213498266Sopenharmony_ci
21313498266Sopenharmony_cistatic void close_secondarysocket(struct Curl_easy *data,
21413498266Sopenharmony_ci                                  struct connectdata *conn)
21513498266Sopenharmony_ci{
21613498266Sopenharmony_ci  Curl_conn_close(data, SECONDARYSOCKET);
21713498266Sopenharmony_ci  Curl_conn_cf_discard_all(data, conn, SECONDARYSOCKET);
21813498266Sopenharmony_ci}
21913498266Sopenharmony_ci
22013498266Sopenharmony_ci/*
22113498266Sopenharmony_ci * NOTE: back in the old days, we added code in the FTP code that made NOBODY
22213498266Sopenharmony_ci * requests on files respond with headers passed to the client/stdout that
22313498266Sopenharmony_ci * looked like HTTP ones.
22413498266Sopenharmony_ci *
22513498266Sopenharmony_ci * This approach is not very elegant, it causes confusion and is error-prone.
22613498266Sopenharmony_ci * It is subject for removal at the next (or at least a future) soname bump.
22713498266Sopenharmony_ci * Until then you can test the effects of the removal by undefining the
22813498266Sopenharmony_ci * following define named CURL_FTP_HTTPSTYLE_HEAD.
22913498266Sopenharmony_ci */
23013498266Sopenharmony_ci#define CURL_FTP_HTTPSTYLE_HEAD 1
23113498266Sopenharmony_ci
23213498266Sopenharmony_cistatic void freedirs(struct ftp_conn *ftpc)
23313498266Sopenharmony_ci{
23413498266Sopenharmony_ci  if(ftpc->dirs) {
23513498266Sopenharmony_ci    int i;
23613498266Sopenharmony_ci    for(i = 0; i < ftpc->dirdepth; i++) {
23713498266Sopenharmony_ci      free(ftpc->dirs[i]);
23813498266Sopenharmony_ci      ftpc->dirs[i] = NULL;
23913498266Sopenharmony_ci    }
24013498266Sopenharmony_ci    free(ftpc->dirs);
24113498266Sopenharmony_ci    ftpc->dirs = NULL;
24213498266Sopenharmony_ci    ftpc->dirdepth = 0;
24313498266Sopenharmony_ci  }
24413498266Sopenharmony_ci  Curl_safefree(ftpc->file);
24513498266Sopenharmony_ci
24613498266Sopenharmony_ci  /* no longer of any use */
24713498266Sopenharmony_ci  Curl_safefree(ftpc->newhost);
24813498266Sopenharmony_ci}
24913498266Sopenharmony_ci
25013498266Sopenharmony_ci/***********************************************************************
25113498266Sopenharmony_ci *
25213498266Sopenharmony_ci * AcceptServerConnect()
25313498266Sopenharmony_ci *
25413498266Sopenharmony_ci * After connection request is received from the server this function is
25513498266Sopenharmony_ci * called to accept the connection and close the listening socket
25613498266Sopenharmony_ci *
25713498266Sopenharmony_ci */
25813498266Sopenharmony_cistatic CURLcode AcceptServerConnect(struct Curl_easy *data)
25913498266Sopenharmony_ci{
26013498266Sopenharmony_ci  struct connectdata *conn = data->conn;
26113498266Sopenharmony_ci  curl_socket_t sock = conn->sock[SECONDARYSOCKET];
26213498266Sopenharmony_ci  curl_socket_t s = CURL_SOCKET_BAD;
26313498266Sopenharmony_ci#ifdef ENABLE_IPV6
26413498266Sopenharmony_ci  struct Curl_sockaddr_storage add;
26513498266Sopenharmony_ci#else
26613498266Sopenharmony_ci  struct sockaddr_in add;
26713498266Sopenharmony_ci#endif
26813498266Sopenharmony_ci  curl_socklen_t size = (curl_socklen_t) sizeof(add);
26913498266Sopenharmony_ci  CURLcode result;
27013498266Sopenharmony_ci
27113498266Sopenharmony_ci  if(0 == getsockname(sock, (struct sockaddr *) &add, &size)) {
27213498266Sopenharmony_ci    size = sizeof(add);
27313498266Sopenharmony_ci
27413498266Sopenharmony_ci    s = accept(sock, (struct sockaddr *) &add, &size);
27513498266Sopenharmony_ci  }
27613498266Sopenharmony_ci
27713498266Sopenharmony_ci  if(CURL_SOCKET_BAD == s) {
27813498266Sopenharmony_ci    failf(data, "Error accept()ing server connect");
27913498266Sopenharmony_ci    return CURLE_FTP_PORT_FAILED;
28013498266Sopenharmony_ci  }
28113498266Sopenharmony_ci  infof(data, "Connection accepted from server");
28213498266Sopenharmony_ci  /* when this happens within the DO state it is important that we mark us as
28313498266Sopenharmony_ci     not needing DO_MORE anymore */
28413498266Sopenharmony_ci  conn->bits.do_more = FALSE;
28513498266Sopenharmony_ci
28613498266Sopenharmony_ci  (void)curlx_nonblock(s, TRUE); /* enable non-blocking */
28713498266Sopenharmony_ci  /* Replace any filter on SECONDARY with one listening on this socket */
28813498266Sopenharmony_ci  result = Curl_conn_tcp_accepted_set(data, conn, SECONDARYSOCKET, &s);
28913498266Sopenharmony_ci  if(result)
29013498266Sopenharmony_ci    return result;
29113498266Sopenharmony_ci
29213498266Sopenharmony_ci  if(data->set.fsockopt) {
29313498266Sopenharmony_ci    int error = 0;
29413498266Sopenharmony_ci
29513498266Sopenharmony_ci    /* activate callback for setting socket options */
29613498266Sopenharmony_ci    Curl_set_in_callback(data, true);
29713498266Sopenharmony_ci    error = data->set.fsockopt(data->set.sockopt_client,
29813498266Sopenharmony_ci                               s,
29913498266Sopenharmony_ci                               CURLSOCKTYPE_ACCEPT);
30013498266Sopenharmony_ci    Curl_set_in_callback(data, false);
30113498266Sopenharmony_ci
30213498266Sopenharmony_ci    if(error) {
30313498266Sopenharmony_ci      close_secondarysocket(data, conn);
30413498266Sopenharmony_ci      return CURLE_ABORTED_BY_CALLBACK;
30513498266Sopenharmony_ci    }
30613498266Sopenharmony_ci  }
30713498266Sopenharmony_ci
30813498266Sopenharmony_ci  return CURLE_OK;
30913498266Sopenharmony_ci
31013498266Sopenharmony_ci}
31113498266Sopenharmony_ci
31213498266Sopenharmony_ci/*
31313498266Sopenharmony_ci * ftp_timeleft_accept() returns the amount of milliseconds left allowed for
31413498266Sopenharmony_ci * waiting server to connect. If the value is negative, the timeout time has
31513498266Sopenharmony_ci * already elapsed.
31613498266Sopenharmony_ci *
31713498266Sopenharmony_ci * The start time is stored in progress.t_acceptdata - as set with
31813498266Sopenharmony_ci * Curl_pgrsTime(..., TIMER_STARTACCEPT);
31913498266Sopenharmony_ci *
32013498266Sopenharmony_ci */
32113498266Sopenharmony_cistatic timediff_t ftp_timeleft_accept(struct Curl_easy *data)
32213498266Sopenharmony_ci{
32313498266Sopenharmony_ci  timediff_t timeout_ms = DEFAULT_ACCEPT_TIMEOUT;
32413498266Sopenharmony_ci  timediff_t other;
32513498266Sopenharmony_ci  struct curltime now;
32613498266Sopenharmony_ci
32713498266Sopenharmony_ci  if(data->set.accepttimeout > 0)
32813498266Sopenharmony_ci    timeout_ms = data->set.accepttimeout;
32913498266Sopenharmony_ci
33013498266Sopenharmony_ci  now = Curl_now();
33113498266Sopenharmony_ci
33213498266Sopenharmony_ci  /* check if the generic timeout possibly is set shorter */
33313498266Sopenharmony_ci  other = Curl_timeleft(data, &now, FALSE);
33413498266Sopenharmony_ci  if(other && (other < timeout_ms))
33513498266Sopenharmony_ci    /* note that this also works fine for when other happens to be negative
33613498266Sopenharmony_ci       due to it already having elapsed */
33713498266Sopenharmony_ci    timeout_ms = other;
33813498266Sopenharmony_ci  else {
33913498266Sopenharmony_ci    /* subtract elapsed time */
34013498266Sopenharmony_ci    timeout_ms -= Curl_timediff(now, data->progress.t_acceptdata);
34113498266Sopenharmony_ci    if(!timeout_ms)
34213498266Sopenharmony_ci      /* avoid returning 0 as that means no timeout! */
34313498266Sopenharmony_ci      return -1;
34413498266Sopenharmony_ci  }
34513498266Sopenharmony_ci
34613498266Sopenharmony_ci  return timeout_ms;
34713498266Sopenharmony_ci}
34813498266Sopenharmony_ci
34913498266Sopenharmony_ci
35013498266Sopenharmony_ci/***********************************************************************
35113498266Sopenharmony_ci *
35213498266Sopenharmony_ci * ReceivedServerConnect()
35313498266Sopenharmony_ci *
35413498266Sopenharmony_ci * After allowing server to connect to us from data port, this function
35513498266Sopenharmony_ci * checks both data connection for connection establishment and ctrl
35613498266Sopenharmony_ci * connection for a negative response regarding a failure in connecting
35713498266Sopenharmony_ci *
35813498266Sopenharmony_ci */
35913498266Sopenharmony_cistatic CURLcode ReceivedServerConnect(struct Curl_easy *data, bool *received)
36013498266Sopenharmony_ci{
36113498266Sopenharmony_ci  struct connectdata *conn = data->conn;
36213498266Sopenharmony_ci  curl_socket_t ctrl_sock = conn->sock[FIRSTSOCKET];
36313498266Sopenharmony_ci  curl_socket_t data_sock = conn->sock[SECONDARYSOCKET];
36413498266Sopenharmony_ci  struct ftp_conn *ftpc = &conn->proto.ftpc;
36513498266Sopenharmony_ci  struct pingpong *pp = &ftpc->pp;
36613498266Sopenharmony_ci  int socketstate = 0;
36713498266Sopenharmony_ci  timediff_t timeout_ms;
36813498266Sopenharmony_ci  ssize_t nread;
36913498266Sopenharmony_ci  int ftpcode;
37013498266Sopenharmony_ci  bool response = FALSE;
37113498266Sopenharmony_ci
37213498266Sopenharmony_ci  *received = FALSE;
37313498266Sopenharmony_ci
37413498266Sopenharmony_ci  timeout_ms = ftp_timeleft_accept(data);
37513498266Sopenharmony_ci  infof(data, "Checking for server connect");
37613498266Sopenharmony_ci  if(timeout_ms < 0) {
37713498266Sopenharmony_ci    /* if a timeout was already reached, bail out */
37813498266Sopenharmony_ci    failf(data, "Accept timeout occurred while waiting server connect");
37913498266Sopenharmony_ci    return CURLE_FTP_ACCEPT_TIMEOUT;
38013498266Sopenharmony_ci  }
38113498266Sopenharmony_ci
38213498266Sopenharmony_ci  /* First check whether there is a cached response from server */
38313498266Sopenharmony_ci  if(Curl_dyn_len(&pp->recvbuf) && (*Curl_dyn_ptr(&pp->recvbuf) > '3')) {
38413498266Sopenharmony_ci    /* Data connection could not be established, let's return */
38513498266Sopenharmony_ci    infof(data, "There is negative response in cache while serv connect");
38613498266Sopenharmony_ci    (void)Curl_GetFTPResponse(data, &nread, &ftpcode);
38713498266Sopenharmony_ci    return CURLE_FTP_ACCEPT_FAILED;
38813498266Sopenharmony_ci  }
38913498266Sopenharmony_ci
39013498266Sopenharmony_ci  if(pp->overflow)
39113498266Sopenharmony_ci    /* there is pending control data still in the buffer to read */
39213498266Sopenharmony_ci    response = TRUE;
39313498266Sopenharmony_ci  else
39413498266Sopenharmony_ci    socketstate = Curl_socket_check(ctrl_sock, data_sock, CURL_SOCKET_BAD, 0);
39513498266Sopenharmony_ci
39613498266Sopenharmony_ci  /* see if the connection request is already here */
39713498266Sopenharmony_ci  switch(socketstate) {
39813498266Sopenharmony_ci  case -1: /* error */
39913498266Sopenharmony_ci    /* let's die here */
40013498266Sopenharmony_ci    failf(data, "Error while waiting for server connect");
40113498266Sopenharmony_ci    return CURLE_FTP_ACCEPT_FAILED;
40213498266Sopenharmony_ci  case 0:  /* Server connect is not received yet */
40313498266Sopenharmony_ci    break; /* loop */
40413498266Sopenharmony_ci  default:
40513498266Sopenharmony_ci    if(socketstate & CURL_CSELECT_IN2) {
40613498266Sopenharmony_ci      infof(data, "Ready to accept data connection from server");
40713498266Sopenharmony_ci      *received = TRUE;
40813498266Sopenharmony_ci    }
40913498266Sopenharmony_ci    else if(socketstate & CURL_CSELECT_IN)
41013498266Sopenharmony_ci      response = TRUE;
41113498266Sopenharmony_ci    break;
41213498266Sopenharmony_ci  }
41313498266Sopenharmony_ci  if(response) {
41413498266Sopenharmony_ci    infof(data, "Ctrl conn has data while waiting for data conn");
41513498266Sopenharmony_ci    (void)Curl_GetFTPResponse(data, &nread, &ftpcode);
41613498266Sopenharmony_ci
41713498266Sopenharmony_ci    if(ftpcode/100 > 3)
41813498266Sopenharmony_ci      return CURLE_FTP_ACCEPT_FAILED;
41913498266Sopenharmony_ci
42013498266Sopenharmony_ci    return CURLE_WEIRD_SERVER_REPLY;
42113498266Sopenharmony_ci  }
42213498266Sopenharmony_ci
42313498266Sopenharmony_ci  return CURLE_OK;
42413498266Sopenharmony_ci}
42513498266Sopenharmony_ci
42613498266Sopenharmony_ci
42713498266Sopenharmony_ci/***********************************************************************
42813498266Sopenharmony_ci *
42913498266Sopenharmony_ci * InitiateTransfer()
43013498266Sopenharmony_ci *
43113498266Sopenharmony_ci * After connection from server is accepted this function is called to
43213498266Sopenharmony_ci * setup transfer parameters and initiate the data transfer.
43313498266Sopenharmony_ci *
43413498266Sopenharmony_ci */
43513498266Sopenharmony_cistatic CURLcode InitiateTransfer(struct Curl_easy *data)
43613498266Sopenharmony_ci{
43713498266Sopenharmony_ci  CURLcode result = CURLE_OK;
43813498266Sopenharmony_ci  struct connectdata *conn = data->conn;
43913498266Sopenharmony_ci  bool connected;
44013498266Sopenharmony_ci
44113498266Sopenharmony_ci  DEBUGF(infof(data, "ftp InitiateTransfer()"));
44213498266Sopenharmony_ci  if(conn->bits.ftp_use_data_ssl && data->set.ftp_use_port &&
44313498266Sopenharmony_ci     !Curl_conn_is_ssl(conn, SECONDARYSOCKET)) {
44413498266Sopenharmony_ci    result = Curl_ssl_cfilter_add(data, conn, SECONDARYSOCKET);
44513498266Sopenharmony_ci    if(result)
44613498266Sopenharmony_ci      return result;
44713498266Sopenharmony_ci  }
44813498266Sopenharmony_ci  result = Curl_conn_connect(data, SECONDARYSOCKET, TRUE, &connected);
44913498266Sopenharmony_ci  if(result || !connected)
45013498266Sopenharmony_ci    return result;
45113498266Sopenharmony_ci
45213498266Sopenharmony_ci  if(conn->proto.ftpc.state_saved == FTP_STOR) {
45313498266Sopenharmony_ci    /* When we know we're uploading a specified file, we can get the file
45413498266Sopenharmony_ci       size prior to the actual upload. */
45513498266Sopenharmony_ci    Curl_pgrsSetUploadSize(data, data->state.infilesize);
45613498266Sopenharmony_ci
45713498266Sopenharmony_ci    /* set the SO_SNDBUF for the secondary socket for those who need it */
45813498266Sopenharmony_ci    Curl_sndbufset(conn->sock[SECONDARYSOCKET]);
45913498266Sopenharmony_ci
46013498266Sopenharmony_ci    Curl_setup_transfer(data, -1, -1, FALSE, SECONDARYSOCKET);
46113498266Sopenharmony_ci  }
46213498266Sopenharmony_ci  else {
46313498266Sopenharmony_ci    /* FTP download: */
46413498266Sopenharmony_ci    Curl_setup_transfer(data, SECONDARYSOCKET,
46513498266Sopenharmony_ci                        conn->proto.ftpc.retr_size_saved, FALSE, -1);
46613498266Sopenharmony_ci  }
46713498266Sopenharmony_ci
46813498266Sopenharmony_ci  conn->proto.ftpc.pp.pending_resp = TRUE; /* expect server response */
46913498266Sopenharmony_ci  ftp_state(data, FTP_STOP);
47013498266Sopenharmony_ci
47113498266Sopenharmony_ci  return CURLE_OK;
47213498266Sopenharmony_ci}
47313498266Sopenharmony_ci
47413498266Sopenharmony_ci/***********************************************************************
47513498266Sopenharmony_ci *
47613498266Sopenharmony_ci * AllowServerConnect()
47713498266Sopenharmony_ci *
47813498266Sopenharmony_ci * When we've issue the PORT command, we have told the server to connect to
47913498266Sopenharmony_ci * us. This function checks whether data connection is established if so it is
48013498266Sopenharmony_ci * accepted.
48113498266Sopenharmony_ci *
48213498266Sopenharmony_ci */
48313498266Sopenharmony_cistatic CURLcode AllowServerConnect(struct Curl_easy *data, bool *connected)
48413498266Sopenharmony_ci{
48513498266Sopenharmony_ci  timediff_t timeout_ms;
48613498266Sopenharmony_ci  CURLcode result = CURLE_OK;
48713498266Sopenharmony_ci
48813498266Sopenharmony_ci  *connected = FALSE;
48913498266Sopenharmony_ci  infof(data, "Preparing for accepting server on data port");
49013498266Sopenharmony_ci
49113498266Sopenharmony_ci  /* Save the time we start accepting server connect */
49213498266Sopenharmony_ci  Curl_pgrsTime(data, TIMER_STARTACCEPT);
49313498266Sopenharmony_ci
49413498266Sopenharmony_ci  timeout_ms = ftp_timeleft_accept(data);
49513498266Sopenharmony_ci  if(timeout_ms < 0) {
49613498266Sopenharmony_ci    /* if a timeout was already reached, bail out */
49713498266Sopenharmony_ci    failf(data, "Accept timeout occurred while waiting server connect");
49813498266Sopenharmony_ci    result = CURLE_FTP_ACCEPT_TIMEOUT;
49913498266Sopenharmony_ci    goto out;
50013498266Sopenharmony_ci  }
50113498266Sopenharmony_ci
50213498266Sopenharmony_ci  /* see if the connection request is already here */
50313498266Sopenharmony_ci  result = ReceivedServerConnect(data, connected);
50413498266Sopenharmony_ci  if(result)
50513498266Sopenharmony_ci    goto out;
50613498266Sopenharmony_ci
50713498266Sopenharmony_ci  if(*connected) {
50813498266Sopenharmony_ci    result = AcceptServerConnect(data);
50913498266Sopenharmony_ci    if(result)
51013498266Sopenharmony_ci      goto out;
51113498266Sopenharmony_ci
51213498266Sopenharmony_ci    result = InitiateTransfer(data);
51313498266Sopenharmony_ci    if(result)
51413498266Sopenharmony_ci      goto out;
51513498266Sopenharmony_ci  }
51613498266Sopenharmony_ci  else {
51713498266Sopenharmony_ci    /* Add timeout to multi handle and break out of the loop */
51813498266Sopenharmony_ci    Curl_expire(data, data->set.accepttimeout ?
51913498266Sopenharmony_ci                data->set.accepttimeout: DEFAULT_ACCEPT_TIMEOUT,
52013498266Sopenharmony_ci                EXPIRE_FTP_ACCEPT);
52113498266Sopenharmony_ci  }
52213498266Sopenharmony_ci
52313498266Sopenharmony_ciout:
52413498266Sopenharmony_ci  DEBUGF(infof(data, "ftp AllowServerConnect() -> %d", result));
52513498266Sopenharmony_ci  return result;
52613498266Sopenharmony_ci}
52713498266Sopenharmony_ci
52813498266Sopenharmony_ci/* macro to check for a three-digit ftp status code at the start of the
52913498266Sopenharmony_ci   given string */
53013498266Sopenharmony_ci#define STATUSCODE(line) (ISDIGIT(line[0]) && ISDIGIT(line[1]) &&       \
53113498266Sopenharmony_ci                          ISDIGIT(line[2]))
53213498266Sopenharmony_ci
53313498266Sopenharmony_ci/* macro to check for the last line in an FTP server response */
53413498266Sopenharmony_ci#define LASTLINE(line) (STATUSCODE(line) && (' ' == line[3]))
53513498266Sopenharmony_ci
53613498266Sopenharmony_cistatic bool ftp_endofresp(struct Curl_easy *data, struct connectdata *conn,
53713498266Sopenharmony_ci                          char *line, size_t len, int *code)
53813498266Sopenharmony_ci{
53913498266Sopenharmony_ci  (void)data;
54013498266Sopenharmony_ci  (void)conn;
54113498266Sopenharmony_ci
54213498266Sopenharmony_ci  if((len > 3) && LASTLINE(line)) {
54313498266Sopenharmony_ci    *code = curlx_sltosi(strtol(line, NULL, 10));
54413498266Sopenharmony_ci    return TRUE;
54513498266Sopenharmony_ci  }
54613498266Sopenharmony_ci
54713498266Sopenharmony_ci  return FALSE;
54813498266Sopenharmony_ci}
54913498266Sopenharmony_ci
55013498266Sopenharmony_cistatic CURLcode ftp_readresp(struct Curl_easy *data,
55113498266Sopenharmony_ci                             curl_socket_t sockfd,
55213498266Sopenharmony_ci                             struct pingpong *pp,
55313498266Sopenharmony_ci                             int *ftpcode, /* return the ftp-code if done */
55413498266Sopenharmony_ci                             size_t *size) /* size of the response */
55513498266Sopenharmony_ci{
55613498266Sopenharmony_ci  int code;
55713498266Sopenharmony_ci  CURLcode result = Curl_pp_readresp(data, sockfd, pp, &code, size);
55813498266Sopenharmony_ci
55913498266Sopenharmony_ci#ifdef HAVE_GSSAPI
56013498266Sopenharmony_ci  {
56113498266Sopenharmony_ci    struct connectdata *conn = data->conn;
56213498266Sopenharmony_ci    char * const buf = Curl_dyn_ptr(&data->conn->proto.ftpc.pp.recvbuf);
56313498266Sopenharmony_ci
56413498266Sopenharmony_ci    /* handle the security-oriented responses 6xx ***/
56513498266Sopenharmony_ci    switch(code) {
56613498266Sopenharmony_ci    case 631:
56713498266Sopenharmony_ci      code = Curl_sec_read_msg(data, conn, buf, PROT_SAFE);
56813498266Sopenharmony_ci      break;
56913498266Sopenharmony_ci    case 632:
57013498266Sopenharmony_ci      code = Curl_sec_read_msg(data, conn, buf, PROT_PRIVATE);
57113498266Sopenharmony_ci      break;
57213498266Sopenharmony_ci    case 633:
57313498266Sopenharmony_ci      code = Curl_sec_read_msg(data, conn, buf, PROT_CONFIDENTIAL);
57413498266Sopenharmony_ci      break;
57513498266Sopenharmony_ci    default:
57613498266Sopenharmony_ci      /* normal ftp stuff we pass through! */
57713498266Sopenharmony_ci      break;
57813498266Sopenharmony_ci    }
57913498266Sopenharmony_ci  }
58013498266Sopenharmony_ci#endif
58113498266Sopenharmony_ci
58213498266Sopenharmony_ci  /* store the latest code for later retrieval */
58313498266Sopenharmony_ci  data->info.httpcode = code;
58413498266Sopenharmony_ci
58513498266Sopenharmony_ci  if(ftpcode)
58613498266Sopenharmony_ci    *ftpcode = code;
58713498266Sopenharmony_ci
58813498266Sopenharmony_ci  if(421 == code) {
58913498266Sopenharmony_ci    /* 421 means "Service not available, closing control connection." and FTP
59013498266Sopenharmony_ci     * servers use it to signal that idle session timeout has been exceeded.
59113498266Sopenharmony_ci     * If we ignored the response, it could end up hanging in some cases.
59213498266Sopenharmony_ci     *
59313498266Sopenharmony_ci     * This response code can come at any point so having it treated
59413498266Sopenharmony_ci     * generically is a good idea.
59513498266Sopenharmony_ci     */
59613498266Sopenharmony_ci    infof(data, "We got a 421 - timeout");
59713498266Sopenharmony_ci    ftp_state(data, FTP_STOP);
59813498266Sopenharmony_ci    return CURLE_OPERATION_TIMEDOUT;
59913498266Sopenharmony_ci  }
60013498266Sopenharmony_ci
60113498266Sopenharmony_ci  return result;
60213498266Sopenharmony_ci}
60313498266Sopenharmony_ci
60413498266Sopenharmony_ci/* --- parse FTP server responses --- */
60513498266Sopenharmony_ci
60613498266Sopenharmony_ci/*
60713498266Sopenharmony_ci * Curl_GetFTPResponse() is a BLOCKING function to read the full response
60813498266Sopenharmony_ci * from a server after a command.
60913498266Sopenharmony_ci *
61013498266Sopenharmony_ci */
61113498266Sopenharmony_ci
61213498266Sopenharmony_ciCURLcode Curl_GetFTPResponse(struct Curl_easy *data,
61313498266Sopenharmony_ci                             ssize_t *nreadp, /* return number of bytes read */
61413498266Sopenharmony_ci                             int *ftpcode) /* return the ftp-code */
61513498266Sopenharmony_ci{
61613498266Sopenharmony_ci  /*
61713498266Sopenharmony_ci   * We cannot read just one byte per read() and then go back to select() as
61813498266Sopenharmony_ci   * the OpenSSL read() doesn't grok that properly.
61913498266Sopenharmony_ci   *
62013498266Sopenharmony_ci   * Alas, read as much as possible, split up into lines, use the ending
62113498266Sopenharmony_ci   * line in a response or continue reading.  */
62213498266Sopenharmony_ci
62313498266Sopenharmony_ci  struct connectdata *conn = data->conn;
62413498266Sopenharmony_ci  curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
62513498266Sopenharmony_ci  CURLcode result = CURLE_OK;
62613498266Sopenharmony_ci  struct ftp_conn *ftpc = &conn->proto.ftpc;
62713498266Sopenharmony_ci  struct pingpong *pp = &ftpc->pp;
62813498266Sopenharmony_ci  size_t nread;
62913498266Sopenharmony_ci  int cache_skip = 0;
63013498266Sopenharmony_ci  int value_to_be_ignored = 0;
63113498266Sopenharmony_ci
63213498266Sopenharmony_ci  if(ftpcode)
63313498266Sopenharmony_ci    *ftpcode = 0; /* 0 for errors */
63413498266Sopenharmony_ci  else
63513498266Sopenharmony_ci    /* make the pointer point to something for the rest of this function */
63613498266Sopenharmony_ci    ftpcode = &value_to_be_ignored;
63713498266Sopenharmony_ci
63813498266Sopenharmony_ci  *nreadp = 0;
63913498266Sopenharmony_ci
64013498266Sopenharmony_ci  while(!*ftpcode && !result) {
64113498266Sopenharmony_ci    /* check and reset timeout value every lap */
64213498266Sopenharmony_ci    timediff_t timeout = Curl_pp_state_timeout(data, pp, FALSE);
64313498266Sopenharmony_ci    timediff_t interval_ms;
64413498266Sopenharmony_ci
64513498266Sopenharmony_ci    if(timeout <= 0) {
64613498266Sopenharmony_ci      failf(data, "FTP response timeout");
64713498266Sopenharmony_ci      return CURLE_OPERATION_TIMEDOUT; /* already too little time */
64813498266Sopenharmony_ci    }
64913498266Sopenharmony_ci
65013498266Sopenharmony_ci    interval_ms = 1000;  /* use 1 second timeout intervals */
65113498266Sopenharmony_ci    if(timeout < interval_ms)
65213498266Sopenharmony_ci      interval_ms = timeout;
65313498266Sopenharmony_ci
65413498266Sopenharmony_ci    /*
65513498266Sopenharmony_ci     * Since this function is blocking, we need to wait here for input on the
65613498266Sopenharmony_ci     * connection and only then we call the response reading function. We do
65713498266Sopenharmony_ci     * timeout at least every second to make the timeout check run.
65813498266Sopenharmony_ci     *
65913498266Sopenharmony_ci     * A caution here is that the ftp_readresp() function has a cache that may
66013498266Sopenharmony_ci     * contain pieces of a response from the previous invoke and we need to
66113498266Sopenharmony_ci     * make sure we don't just wait for input while there is unhandled data in
66213498266Sopenharmony_ci     * that cache. But also, if the cache is there, we call ftp_readresp() and
66313498266Sopenharmony_ci     * the cache wasn't good enough to continue we must not just busy-loop
66413498266Sopenharmony_ci     * around this function.
66513498266Sopenharmony_ci     *
66613498266Sopenharmony_ci     */
66713498266Sopenharmony_ci
66813498266Sopenharmony_ci    if(Curl_dyn_len(&pp->recvbuf) && (cache_skip < 2)) {
66913498266Sopenharmony_ci      /*
67013498266Sopenharmony_ci       * There's a cache left since before. We then skipping the wait for
67113498266Sopenharmony_ci       * socket action, unless this is the same cache like the previous round
67213498266Sopenharmony_ci       * as then the cache was deemed not enough to act on and we then need to
67313498266Sopenharmony_ci       * wait for more data anyway.
67413498266Sopenharmony_ci       */
67513498266Sopenharmony_ci    }
67613498266Sopenharmony_ci    else if(!Curl_conn_data_pending(data, FIRSTSOCKET)) {
67713498266Sopenharmony_ci      switch(SOCKET_READABLE(sockfd, interval_ms)) {
67813498266Sopenharmony_ci      case -1: /* select() error, stop reading */
67913498266Sopenharmony_ci        failf(data, "FTP response aborted due to select/poll error: %d",
68013498266Sopenharmony_ci              SOCKERRNO);
68113498266Sopenharmony_ci        return CURLE_RECV_ERROR;
68213498266Sopenharmony_ci
68313498266Sopenharmony_ci      case 0: /* timeout */
68413498266Sopenharmony_ci        if(Curl_pgrsUpdate(data))
68513498266Sopenharmony_ci          return CURLE_ABORTED_BY_CALLBACK;
68613498266Sopenharmony_ci        continue; /* just continue in our loop for the timeout duration */
68713498266Sopenharmony_ci
68813498266Sopenharmony_ci      default: /* for clarity */
68913498266Sopenharmony_ci        break;
69013498266Sopenharmony_ci      }
69113498266Sopenharmony_ci    }
69213498266Sopenharmony_ci    result = ftp_readresp(data, sockfd, pp, ftpcode, &nread);
69313498266Sopenharmony_ci    if(result)
69413498266Sopenharmony_ci      break;
69513498266Sopenharmony_ci
69613498266Sopenharmony_ci    if(!nread && Curl_dyn_len(&pp->recvbuf))
69713498266Sopenharmony_ci      /* bump cache skip counter as on repeated skips we must wait for more
69813498266Sopenharmony_ci         data */
69913498266Sopenharmony_ci      cache_skip++;
70013498266Sopenharmony_ci    else
70113498266Sopenharmony_ci      /* when we got data or there is no cache left, we reset the cache skip
70213498266Sopenharmony_ci         counter */
70313498266Sopenharmony_ci      cache_skip = 0;
70413498266Sopenharmony_ci
70513498266Sopenharmony_ci    *nreadp += nread;
70613498266Sopenharmony_ci
70713498266Sopenharmony_ci  } /* while there's buffer left and loop is requested */
70813498266Sopenharmony_ci
70913498266Sopenharmony_ci  pp->pending_resp = FALSE;
71013498266Sopenharmony_ci
71113498266Sopenharmony_ci  return result;
71213498266Sopenharmony_ci}
71313498266Sopenharmony_ci
71413498266Sopenharmony_ci#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
71513498266Sopenharmony_ci  /* for debug purposes */
71613498266Sopenharmony_cistatic const char * const ftp_state_names[]={
71713498266Sopenharmony_ci  "STOP",
71813498266Sopenharmony_ci  "WAIT220",
71913498266Sopenharmony_ci  "AUTH",
72013498266Sopenharmony_ci  "USER",
72113498266Sopenharmony_ci  "PASS",
72213498266Sopenharmony_ci  "ACCT",
72313498266Sopenharmony_ci  "PBSZ",
72413498266Sopenharmony_ci  "PROT",
72513498266Sopenharmony_ci  "CCC",
72613498266Sopenharmony_ci  "PWD",
72713498266Sopenharmony_ci  "SYST",
72813498266Sopenharmony_ci  "NAMEFMT",
72913498266Sopenharmony_ci  "QUOTE",
73013498266Sopenharmony_ci  "RETR_PREQUOTE",
73113498266Sopenharmony_ci  "STOR_PREQUOTE",
73213498266Sopenharmony_ci  "POSTQUOTE",
73313498266Sopenharmony_ci  "CWD",
73413498266Sopenharmony_ci  "MKD",
73513498266Sopenharmony_ci  "MDTM",
73613498266Sopenharmony_ci  "TYPE",
73713498266Sopenharmony_ci  "LIST_TYPE",
73813498266Sopenharmony_ci  "RETR_TYPE",
73913498266Sopenharmony_ci  "STOR_TYPE",
74013498266Sopenharmony_ci  "SIZE",
74113498266Sopenharmony_ci  "RETR_SIZE",
74213498266Sopenharmony_ci  "STOR_SIZE",
74313498266Sopenharmony_ci  "REST",
74413498266Sopenharmony_ci  "RETR_REST",
74513498266Sopenharmony_ci  "PORT",
74613498266Sopenharmony_ci  "PRET",
74713498266Sopenharmony_ci  "PASV",
74813498266Sopenharmony_ci  "LIST",
74913498266Sopenharmony_ci  "RETR",
75013498266Sopenharmony_ci  "STOR",
75113498266Sopenharmony_ci  "QUIT"
75213498266Sopenharmony_ci};
75313498266Sopenharmony_ci#endif
75413498266Sopenharmony_ci
75513498266Sopenharmony_ci/* This is the ONLY way to change FTP state! */
75613498266Sopenharmony_cistatic void _ftp_state(struct Curl_easy *data,
75713498266Sopenharmony_ci                       ftpstate newstate
75813498266Sopenharmony_ci#ifdef DEBUGBUILD
75913498266Sopenharmony_ci                       , int lineno
76013498266Sopenharmony_ci#endif
76113498266Sopenharmony_ci  )
76213498266Sopenharmony_ci{
76313498266Sopenharmony_ci  struct connectdata *conn = data->conn;
76413498266Sopenharmony_ci  struct ftp_conn *ftpc = &conn->proto.ftpc;
76513498266Sopenharmony_ci
76613498266Sopenharmony_ci#if defined(DEBUGBUILD)
76713498266Sopenharmony_ci
76813498266Sopenharmony_ci#if defined(CURL_DISABLE_VERBOSE_STRINGS)
76913498266Sopenharmony_ci  (void) lineno;
77013498266Sopenharmony_ci#else
77113498266Sopenharmony_ci  if(ftpc->state != newstate)
77213498266Sopenharmony_ci    infof(data, "FTP %p (line %d) state change from %s to %s",
77313498266Sopenharmony_ci          (void *)ftpc, lineno, ftp_state_names[ftpc->state],
77413498266Sopenharmony_ci          ftp_state_names[newstate]);
77513498266Sopenharmony_ci#endif
77613498266Sopenharmony_ci#endif
77713498266Sopenharmony_ci
77813498266Sopenharmony_ci  ftpc->state = newstate;
77913498266Sopenharmony_ci}
78013498266Sopenharmony_ci
78113498266Sopenharmony_cistatic CURLcode ftp_state_user(struct Curl_easy *data,
78213498266Sopenharmony_ci                               struct connectdata *conn)
78313498266Sopenharmony_ci{
78413498266Sopenharmony_ci  CURLcode result = Curl_pp_sendf(data,
78513498266Sopenharmony_ci                                  &conn->proto.ftpc.pp, "USER %s",
78613498266Sopenharmony_ci                                  conn->user?conn->user:"");
78713498266Sopenharmony_ci  if(!result) {
78813498266Sopenharmony_ci    struct ftp_conn *ftpc = &conn->proto.ftpc;
78913498266Sopenharmony_ci    ftpc->ftp_trying_alternative = FALSE;
79013498266Sopenharmony_ci    ftp_state(data, FTP_USER);
79113498266Sopenharmony_ci  }
79213498266Sopenharmony_ci  return result;
79313498266Sopenharmony_ci}
79413498266Sopenharmony_ci
79513498266Sopenharmony_cistatic CURLcode ftp_state_pwd(struct Curl_easy *data,
79613498266Sopenharmony_ci                              struct connectdata *conn)
79713498266Sopenharmony_ci{
79813498266Sopenharmony_ci  CURLcode result = Curl_pp_sendf(data, &conn->proto.ftpc.pp, "%s", "PWD");
79913498266Sopenharmony_ci  if(!result)
80013498266Sopenharmony_ci    ftp_state(data, FTP_PWD);
80113498266Sopenharmony_ci
80213498266Sopenharmony_ci  return result;
80313498266Sopenharmony_ci}
80413498266Sopenharmony_ci
80513498266Sopenharmony_ci/* For the FTP "protocol connect" and "doing" phases only */
80613498266Sopenharmony_cistatic int ftp_getsock(struct Curl_easy *data,
80713498266Sopenharmony_ci                       struct connectdata *conn,
80813498266Sopenharmony_ci                       curl_socket_t *socks)
80913498266Sopenharmony_ci{
81013498266Sopenharmony_ci  return Curl_pp_getsock(data, &conn->proto.ftpc.pp, socks);
81113498266Sopenharmony_ci}
81213498266Sopenharmony_ci
81313498266Sopenharmony_ci/* For the FTP "DO_MORE" phase only */
81413498266Sopenharmony_cistatic int ftp_domore_getsock(struct Curl_easy *data,
81513498266Sopenharmony_ci                              struct connectdata *conn, curl_socket_t *socks)
81613498266Sopenharmony_ci{
81713498266Sopenharmony_ci  struct ftp_conn *ftpc = &conn->proto.ftpc;
81813498266Sopenharmony_ci  (void)data;
81913498266Sopenharmony_ci
82013498266Sopenharmony_ci  /* When in DO_MORE state, we could be either waiting for us to connect to a
82113498266Sopenharmony_ci   * remote site, or we could wait for that site to connect to us. Or just
82213498266Sopenharmony_ci   * handle ordinary commands.
82313498266Sopenharmony_ci   */
82413498266Sopenharmony_ci
82513498266Sopenharmony_ci  DEBUGF(infof(data, "ftp_domore_getsock()"));
82613498266Sopenharmony_ci  if(conn->cfilter[SECONDARYSOCKET]
82713498266Sopenharmony_ci     && !Curl_conn_is_connected(conn, SECONDARYSOCKET))
82813498266Sopenharmony_ci    return 0;
82913498266Sopenharmony_ci
83013498266Sopenharmony_ci  if(FTP_STOP == ftpc->state) {
83113498266Sopenharmony_ci    int bits = GETSOCK_READSOCK(0);
83213498266Sopenharmony_ci
83313498266Sopenharmony_ci    /* if stopped and still in this state, then we're also waiting for a
83413498266Sopenharmony_ci       connect on the secondary connection */
83513498266Sopenharmony_ci    socks[0] = conn->sock[FIRSTSOCKET];
83613498266Sopenharmony_ci    if(conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD) {
83713498266Sopenharmony_ci      socks[1] = conn->sock[SECONDARYSOCKET];
83813498266Sopenharmony_ci      bits |= GETSOCK_WRITESOCK(1) | GETSOCK_READSOCK(1);
83913498266Sopenharmony_ci    }
84013498266Sopenharmony_ci
84113498266Sopenharmony_ci    return bits;
84213498266Sopenharmony_ci  }
84313498266Sopenharmony_ci  return Curl_pp_getsock(data, &conn->proto.ftpc.pp, socks);
84413498266Sopenharmony_ci}
84513498266Sopenharmony_ci
84613498266Sopenharmony_ci/* This is called after the FTP_QUOTE state is passed.
84713498266Sopenharmony_ci
84813498266Sopenharmony_ci   ftp_state_cwd() sends the range of CWD commands to the server to change to
84913498266Sopenharmony_ci   the correct directory. It may also need to send MKD commands to create
85013498266Sopenharmony_ci   missing ones, if that option is enabled.
85113498266Sopenharmony_ci*/
85213498266Sopenharmony_cistatic CURLcode ftp_state_cwd(struct Curl_easy *data,
85313498266Sopenharmony_ci                              struct connectdata *conn)
85413498266Sopenharmony_ci{
85513498266Sopenharmony_ci  CURLcode result = CURLE_OK;
85613498266Sopenharmony_ci  struct ftp_conn *ftpc = &conn->proto.ftpc;
85713498266Sopenharmony_ci
85813498266Sopenharmony_ci  if(ftpc->cwddone)
85913498266Sopenharmony_ci    /* already done and fine */
86013498266Sopenharmony_ci    result = ftp_state_mdtm(data);
86113498266Sopenharmony_ci  else {
86213498266Sopenharmony_ci    /* FTPFILE_NOCWD with full path: expect ftpc->cwddone! */
86313498266Sopenharmony_ci    DEBUGASSERT((data->set.ftp_filemethod != FTPFILE_NOCWD) ||
86413498266Sopenharmony_ci                !(ftpc->dirdepth && ftpc->dirs[0][0] == '/'));
86513498266Sopenharmony_ci
86613498266Sopenharmony_ci    ftpc->count2 = 0; /* count2 counts failed CWDs */
86713498266Sopenharmony_ci
86813498266Sopenharmony_ci    if(conn->bits.reuse && ftpc->entrypath &&
86913498266Sopenharmony_ci       /* no need to go to entrypath when we have an absolute path */
87013498266Sopenharmony_ci       !(ftpc->dirdepth && ftpc->dirs[0][0] == '/')) {
87113498266Sopenharmony_ci      /* This is a reused connection. Since we change directory to where the
87213498266Sopenharmony_ci         transfer is taking place, we must first get back to the original dir
87313498266Sopenharmony_ci         where we ended up after login: */
87413498266Sopenharmony_ci      ftpc->cwdcount = 0; /* we count this as the first path, then we add one
87513498266Sopenharmony_ci                             for all upcoming ones in the ftp->dirs[] array */
87613498266Sopenharmony_ci      result = Curl_pp_sendf(data, &ftpc->pp, "CWD %s", ftpc->entrypath);
87713498266Sopenharmony_ci      if(!result)
87813498266Sopenharmony_ci        ftp_state(data, FTP_CWD);
87913498266Sopenharmony_ci    }
88013498266Sopenharmony_ci    else {
88113498266Sopenharmony_ci      if(ftpc->dirdepth) {
88213498266Sopenharmony_ci        ftpc->cwdcount = 1;
88313498266Sopenharmony_ci        /* issue the first CWD, the rest is sent when the CWD responses are
88413498266Sopenharmony_ci           received... */
88513498266Sopenharmony_ci        result = Curl_pp_sendf(data, &ftpc->pp, "CWD %s",
88613498266Sopenharmony_ci                               ftpc->dirs[ftpc->cwdcount -1]);
88713498266Sopenharmony_ci        if(!result)
88813498266Sopenharmony_ci          ftp_state(data, FTP_CWD);
88913498266Sopenharmony_ci      }
89013498266Sopenharmony_ci      else {
89113498266Sopenharmony_ci        /* No CWD necessary */
89213498266Sopenharmony_ci        result = ftp_state_mdtm(data);
89313498266Sopenharmony_ci      }
89413498266Sopenharmony_ci    }
89513498266Sopenharmony_ci  }
89613498266Sopenharmony_ci  return result;
89713498266Sopenharmony_ci}
89813498266Sopenharmony_ci
89913498266Sopenharmony_citypedef enum {
90013498266Sopenharmony_ci  EPRT,
90113498266Sopenharmony_ci  PORT,
90213498266Sopenharmony_ci  DONE
90313498266Sopenharmony_ci} ftpport;
90413498266Sopenharmony_ci
90513498266Sopenharmony_cistatic CURLcode ftp_state_use_port(struct Curl_easy *data,
90613498266Sopenharmony_ci                                   ftpport fcmd) /* start with this */
90713498266Sopenharmony_ci{
90813498266Sopenharmony_ci  CURLcode result = CURLE_FTP_PORT_FAILED;
90913498266Sopenharmony_ci  struct connectdata *conn = data->conn;
91013498266Sopenharmony_ci  struct ftp_conn *ftpc = &conn->proto.ftpc;
91113498266Sopenharmony_ci  curl_socket_t portsock = CURL_SOCKET_BAD;
91213498266Sopenharmony_ci  char myhost[MAX_IPADR_LEN + 1] = "";
91313498266Sopenharmony_ci
91413498266Sopenharmony_ci  struct Curl_sockaddr_storage ss;
91513498266Sopenharmony_ci  struct Curl_addrinfo *res, *ai;
91613498266Sopenharmony_ci  curl_socklen_t sslen;
91713498266Sopenharmony_ci  char hbuf[NI_MAXHOST];
91813498266Sopenharmony_ci  struct sockaddr *sa = (struct sockaddr *)&ss;
91913498266Sopenharmony_ci  struct sockaddr_in * const sa4 = (void *)sa;
92013498266Sopenharmony_ci#ifdef ENABLE_IPV6
92113498266Sopenharmony_ci  struct sockaddr_in6 * const sa6 = (void *)sa;
92213498266Sopenharmony_ci#endif
92313498266Sopenharmony_ci  static const char mode[][5] = { "EPRT", "PORT" };
92413498266Sopenharmony_ci  enum resolve_t rc;
92513498266Sopenharmony_ci  int error;
92613498266Sopenharmony_ci  char *host = NULL;
92713498266Sopenharmony_ci  char *string_ftpport = data->set.str[STRING_FTPPORT];
92813498266Sopenharmony_ci  struct Curl_dns_entry *h = NULL;
92913498266Sopenharmony_ci  unsigned short port_min = 0;
93013498266Sopenharmony_ci  unsigned short port_max = 0;
93113498266Sopenharmony_ci  unsigned short port;
93213498266Sopenharmony_ci  bool possibly_non_local = TRUE;
93313498266Sopenharmony_ci  char buffer[STRERROR_LEN];
93413498266Sopenharmony_ci  char *addr = NULL;
93513498266Sopenharmony_ci  size_t addrlen = 0;
93613498266Sopenharmony_ci  char ipstr[50];
93713498266Sopenharmony_ci
93813498266Sopenharmony_ci  /* Step 1, figure out what is requested,
93913498266Sopenharmony_ci   * accepted format :
94013498266Sopenharmony_ci   * (ipv4|ipv6|domain|interface)?(:port(-range)?)?
94113498266Sopenharmony_ci   */
94213498266Sopenharmony_ci
94313498266Sopenharmony_ci  if(data->set.str[STRING_FTPPORT] &&
94413498266Sopenharmony_ci     (strlen(data->set.str[STRING_FTPPORT]) > 1)) {
94513498266Sopenharmony_ci    char *ip_end = NULL;
94613498266Sopenharmony_ci
94713498266Sopenharmony_ci#ifdef ENABLE_IPV6
94813498266Sopenharmony_ci    if(*string_ftpport == '[') {
94913498266Sopenharmony_ci      /* [ipv6]:port(-range) */
95013498266Sopenharmony_ci      char *ip_start = string_ftpport + 1;
95113498266Sopenharmony_ci      ip_end = strchr(ip_start, ']');
95213498266Sopenharmony_ci      if(ip_end) {
95313498266Sopenharmony_ci        addrlen = ip_end - ip_start;
95413498266Sopenharmony_ci        addr = ip_start;
95513498266Sopenharmony_ci      }
95613498266Sopenharmony_ci    }
95713498266Sopenharmony_ci    else
95813498266Sopenharmony_ci#endif
95913498266Sopenharmony_ci      if(*string_ftpport == ':') {
96013498266Sopenharmony_ci        /* :port */
96113498266Sopenharmony_ci        ip_end = string_ftpport;
96213498266Sopenharmony_ci      }
96313498266Sopenharmony_ci      else {
96413498266Sopenharmony_ci        ip_end = strchr(string_ftpport, ':');
96513498266Sopenharmony_ci        addr = string_ftpport;
96613498266Sopenharmony_ci        if(ip_end) {
96713498266Sopenharmony_ci          /* either ipv6 or (ipv4|domain|interface):port(-range) */
96813498266Sopenharmony_ci          addrlen = ip_end - string_ftpport;
96913498266Sopenharmony_ci#ifdef ENABLE_IPV6
97013498266Sopenharmony_ci          if(Curl_inet_pton(AF_INET6, string_ftpport, &sa6->sin6_addr) == 1) {
97113498266Sopenharmony_ci            /* ipv6 */
97213498266Sopenharmony_ci            port_min = port_max = 0;
97313498266Sopenharmony_ci            ip_end = NULL; /* this got no port ! */
97413498266Sopenharmony_ci          }
97513498266Sopenharmony_ci#endif
97613498266Sopenharmony_ci        }
97713498266Sopenharmony_ci        else
97813498266Sopenharmony_ci          /* ipv4|interface */
97913498266Sopenharmony_ci          addrlen = strlen(string_ftpport);
98013498266Sopenharmony_ci      }
98113498266Sopenharmony_ci
98213498266Sopenharmony_ci    /* parse the port */
98313498266Sopenharmony_ci    if(ip_end) {
98413498266Sopenharmony_ci      char *port_sep = NULL;
98513498266Sopenharmony_ci      char *port_start = strchr(ip_end, ':');
98613498266Sopenharmony_ci      if(port_start) {
98713498266Sopenharmony_ci        port_min = curlx_ultous(strtoul(port_start + 1, NULL, 10));
98813498266Sopenharmony_ci        port_sep = strchr(port_start, '-');
98913498266Sopenharmony_ci        if(port_sep) {
99013498266Sopenharmony_ci          port_max = curlx_ultous(strtoul(port_sep + 1, NULL, 10));
99113498266Sopenharmony_ci        }
99213498266Sopenharmony_ci        else
99313498266Sopenharmony_ci          port_max = port_min;
99413498266Sopenharmony_ci      }
99513498266Sopenharmony_ci    }
99613498266Sopenharmony_ci
99713498266Sopenharmony_ci    /* correct errors like:
99813498266Sopenharmony_ci     *  :1234-1230
99913498266Sopenharmony_ci     *  :-4711,  in this case port_min is (unsigned)-1,
100013498266Sopenharmony_ci     *           therefore port_min > port_max for all cases
100113498266Sopenharmony_ci     *           but port_max = (unsigned)-1
100213498266Sopenharmony_ci     */
100313498266Sopenharmony_ci    if(port_min > port_max)
100413498266Sopenharmony_ci      port_min = port_max = 0;
100513498266Sopenharmony_ci
100613498266Sopenharmony_ci    if(addrlen) {
100713498266Sopenharmony_ci      DEBUGASSERT(addr);
100813498266Sopenharmony_ci      if(addrlen >= sizeof(ipstr))
100913498266Sopenharmony_ci        goto out;
101013498266Sopenharmony_ci      memcpy(ipstr, addr, addrlen);
101113498266Sopenharmony_ci      ipstr[addrlen] = 0;
101213498266Sopenharmony_ci
101313498266Sopenharmony_ci      /* attempt to get the address of the given interface name */
101413498266Sopenharmony_ci      switch(Curl_if2ip(conn->remote_addr->family,
101513498266Sopenharmony_ci#ifdef ENABLE_IPV6
101613498266Sopenharmony_ci                        Curl_ipv6_scope(&conn->remote_addr->sa_addr),
101713498266Sopenharmony_ci                        conn->scope_id,
101813498266Sopenharmony_ci#endif
101913498266Sopenharmony_ci                        ipstr, hbuf, sizeof(hbuf))) {
102013498266Sopenharmony_ci        case IF2IP_NOT_FOUND:
102113498266Sopenharmony_ci          /* not an interface, use the given string as host name instead */
102213498266Sopenharmony_ci          host = ipstr;
102313498266Sopenharmony_ci          break;
102413498266Sopenharmony_ci        case IF2IP_AF_NOT_SUPPORTED:
102513498266Sopenharmony_ci          goto out;
102613498266Sopenharmony_ci        case IF2IP_FOUND:
102713498266Sopenharmony_ci          host = hbuf; /* use the hbuf for host name */
102813498266Sopenharmony_ci          break;
102913498266Sopenharmony_ci      }
103013498266Sopenharmony_ci    }
103113498266Sopenharmony_ci    else
103213498266Sopenharmony_ci      /* there was only a port(-range) given, default the host */
103313498266Sopenharmony_ci      host = NULL;
103413498266Sopenharmony_ci  } /* data->set.ftpport */
103513498266Sopenharmony_ci
103613498266Sopenharmony_ci  if(!host) {
103713498266Sopenharmony_ci    const char *r;
103813498266Sopenharmony_ci    /* not an interface and not a host name, get default by extracting
103913498266Sopenharmony_ci       the IP from the control connection */
104013498266Sopenharmony_ci    sslen = sizeof(ss);
104113498266Sopenharmony_ci    if(getsockname(conn->sock[FIRSTSOCKET], sa, &sslen)) {
104213498266Sopenharmony_ci      failf(data, "getsockname() failed: %s",
104313498266Sopenharmony_ci            Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
104413498266Sopenharmony_ci      goto out;
104513498266Sopenharmony_ci    }
104613498266Sopenharmony_ci    switch(sa->sa_family) {
104713498266Sopenharmony_ci#ifdef ENABLE_IPV6
104813498266Sopenharmony_ci    case AF_INET6:
104913498266Sopenharmony_ci      r = Curl_inet_ntop(sa->sa_family, &sa6->sin6_addr, hbuf, sizeof(hbuf));
105013498266Sopenharmony_ci      break;
105113498266Sopenharmony_ci#endif
105213498266Sopenharmony_ci    default:
105313498266Sopenharmony_ci      r = Curl_inet_ntop(sa->sa_family, &sa4->sin_addr, hbuf, sizeof(hbuf));
105413498266Sopenharmony_ci      break;
105513498266Sopenharmony_ci    }
105613498266Sopenharmony_ci    if(!r) {
105713498266Sopenharmony_ci      goto out;
105813498266Sopenharmony_ci    }
105913498266Sopenharmony_ci    host = hbuf; /* use this host name */
106013498266Sopenharmony_ci    possibly_non_local = FALSE; /* we know it is local now */
106113498266Sopenharmony_ci  }
106213498266Sopenharmony_ci
106313498266Sopenharmony_ci  /* resolv ip/host to ip */
106413498266Sopenharmony_ci  rc = Curl_resolv(data, host, 0, FALSE, &h);
106513498266Sopenharmony_ci  if(rc == CURLRESOLV_PENDING)
106613498266Sopenharmony_ci    (void)Curl_resolver_wait_resolv(data, &h);
106713498266Sopenharmony_ci  if(h) {
106813498266Sopenharmony_ci    res = h->addr;
106913498266Sopenharmony_ci    /* when we return from this function, we can forget about this entry
107013498266Sopenharmony_ci       to we can unlock it now already */
107113498266Sopenharmony_ci    Curl_resolv_unlock(data, h);
107213498266Sopenharmony_ci  } /* (h) */
107313498266Sopenharmony_ci  else
107413498266Sopenharmony_ci    res = NULL; /* failure! */
107513498266Sopenharmony_ci
107613498266Sopenharmony_ci  if(!res) {
107713498266Sopenharmony_ci    failf(data, "failed to resolve the address provided to PORT: %s", host);
107813498266Sopenharmony_ci    goto out;
107913498266Sopenharmony_ci  }
108013498266Sopenharmony_ci
108113498266Sopenharmony_ci  host = NULL;
108213498266Sopenharmony_ci
108313498266Sopenharmony_ci  /* step 2, create a socket for the requested address */
108413498266Sopenharmony_ci  error = 0;
108513498266Sopenharmony_ci  for(ai = res; ai; ai = ai->ai_next) {
108613498266Sopenharmony_ci    if(Curl_socket_open(data, ai, NULL, conn->transport, &portsock)) {
108713498266Sopenharmony_ci      error = SOCKERRNO;
108813498266Sopenharmony_ci      continue;
108913498266Sopenharmony_ci    }
109013498266Sopenharmony_ci    break;
109113498266Sopenharmony_ci  }
109213498266Sopenharmony_ci  if(!ai) {
109313498266Sopenharmony_ci    failf(data, "socket failure: %s",
109413498266Sopenharmony_ci          Curl_strerror(error, buffer, sizeof(buffer)));
109513498266Sopenharmony_ci    goto out;
109613498266Sopenharmony_ci  }
109713498266Sopenharmony_ci  DEBUGF(infof(data, "ftp_state_use_port(), opened socket"));
109813498266Sopenharmony_ci
109913498266Sopenharmony_ci  /* step 3, bind to a suitable local address */
110013498266Sopenharmony_ci
110113498266Sopenharmony_ci  memcpy(sa, ai->ai_addr, ai->ai_addrlen);
110213498266Sopenharmony_ci  sslen = ai->ai_addrlen;
110313498266Sopenharmony_ci
110413498266Sopenharmony_ci  for(port = port_min; port <= port_max;) {
110513498266Sopenharmony_ci    if(sa->sa_family == AF_INET)
110613498266Sopenharmony_ci      sa4->sin_port = htons(port);
110713498266Sopenharmony_ci#ifdef ENABLE_IPV6
110813498266Sopenharmony_ci    else
110913498266Sopenharmony_ci      sa6->sin6_port = htons(port);
111013498266Sopenharmony_ci#endif
111113498266Sopenharmony_ci    /* Try binding the given address. */
111213498266Sopenharmony_ci    if(bind(portsock, sa, sslen) ) {
111313498266Sopenharmony_ci      /* It failed. */
111413498266Sopenharmony_ci      error = SOCKERRNO;
111513498266Sopenharmony_ci      if(possibly_non_local && (error == EADDRNOTAVAIL)) {
111613498266Sopenharmony_ci        /* The requested bind address is not local.  Use the address used for
111713498266Sopenharmony_ci         * the control connection instead and restart the port loop
111813498266Sopenharmony_ci         */
111913498266Sopenharmony_ci        infof(data, "bind(port=%hu) on non-local address failed: %s", port,
112013498266Sopenharmony_ci              Curl_strerror(error, buffer, sizeof(buffer)));
112113498266Sopenharmony_ci
112213498266Sopenharmony_ci        sslen = sizeof(ss);
112313498266Sopenharmony_ci        if(getsockname(conn->sock[FIRSTSOCKET], sa, &sslen)) {
112413498266Sopenharmony_ci          failf(data, "getsockname() failed: %s",
112513498266Sopenharmony_ci                Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
112613498266Sopenharmony_ci          goto out;
112713498266Sopenharmony_ci        }
112813498266Sopenharmony_ci        port = port_min;
112913498266Sopenharmony_ci        possibly_non_local = FALSE; /* don't try this again */
113013498266Sopenharmony_ci        continue;
113113498266Sopenharmony_ci      }
113213498266Sopenharmony_ci      if(error != EADDRINUSE && error != EACCES) {
113313498266Sopenharmony_ci        failf(data, "bind(port=%hu) failed: %s", port,
113413498266Sopenharmony_ci              Curl_strerror(error, buffer, sizeof(buffer)));
113513498266Sopenharmony_ci        goto out;
113613498266Sopenharmony_ci      }
113713498266Sopenharmony_ci    }
113813498266Sopenharmony_ci    else
113913498266Sopenharmony_ci      break;
114013498266Sopenharmony_ci
114113498266Sopenharmony_ci    port++;
114213498266Sopenharmony_ci  }
114313498266Sopenharmony_ci
114413498266Sopenharmony_ci  /* maybe all ports were in use already */
114513498266Sopenharmony_ci  if(port > port_max) {
114613498266Sopenharmony_ci    failf(data, "bind() failed, we ran out of ports");
114713498266Sopenharmony_ci    goto out;
114813498266Sopenharmony_ci  }
114913498266Sopenharmony_ci
115013498266Sopenharmony_ci  /* get the name again after the bind() so that we can extract the
115113498266Sopenharmony_ci     port number it uses now */
115213498266Sopenharmony_ci  sslen = sizeof(ss);
115313498266Sopenharmony_ci  if(getsockname(portsock, sa, &sslen)) {
115413498266Sopenharmony_ci    failf(data, "getsockname() failed: %s",
115513498266Sopenharmony_ci          Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
115613498266Sopenharmony_ci    goto out;
115713498266Sopenharmony_ci  }
115813498266Sopenharmony_ci  DEBUGF(infof(data, "ftp_state_use_port(), socket bound to port %d", port));
115913498266Sopenharmony_ci
116013498266Sopenharmony_ci  /* step 4, listen on the socket */
116113498266Sopenharmony_ci
116213498266Sopenharmony_ci  if(listen(portsock, 1)) {
116313498266Sopenharmony_ci    failf(data, "socket failure: %s",
116413498266Sopenharmony_ci          Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
116513498266Sopenharmony_ci    goto out;
116613498266Sopenharmony_ci  }
116713498266Sopenharmony_ci  DEBUGF(infof(data, "ftp_state_use_port(), listening on %d", port));
116813498266Sopenharmony_ci
116913498266Sopenharmony_ci  /* step 5, send the proper FTP command */
117013498266Sopenharmony_ci
117113498266Sopenharmony_ci  /* get a plain printable version of the numerical address to work with
117213498266Sopenharmony_ci     below */
117313498266Sopenharmony_ci  Curl_printable_address(ai, myhost, sizeof(myhost));
117413498266Sopenharmony_ci
117513498266Sopenharmony_ci#ifdef ENABLE_IPV6
117613498266Sopenharmony_ci  if(!conn->bits.ftp_use_eprt && conn->bits.ipv6)
117713498266Sopenharmony_ci    /* EPRT is disabled but we are connected to a IPv6 host, so we ignore the
117813498266Sopenharmony_ci       request and enable EPRT again! */
117913498266Sopenharmony_ci    conn->bits.ftp_use_eprt = TRUE;
118013498266Sopenharmony_ci#endif
118113498266Sopenharmony_ci
118213498266Sopenharmony_ci  for(; fcmd != DONE; fcmd++) {
118313498266Sopenharmony_ci
118413498266Sopenharmony_ci    if(!conn->bits.ftp_use_eprt && (EPRT == fcmd))
118513498266Sopenharmony_ci      /* if disabled, goto next */
118613498266Sopenharmony_ci      continue;
118713498266Sopenharmony_ci
118813498266Sopenharmony_ci    if((PORT == fcmd) && sa->sa_family != AF_INET)
118913498266Sopenharmony_ci      /* PORT is IPv4 only */
119013498266Sopenharmony_ci      continue;
119113498266Sopenharmony_ci
119213498266Sopenharmony_ci    switch(sa->sa_family) {
119313498266Sopenharmony_ci    case AF_INET:
119413498266Sopenharmony_ci      port = ntohs(sa4->sin_port);
119513498266Sopenharmony_ci      break;
119613498266Sopenharmony_ci#ifdef ENABLE_IPV6
119713498266Sopenharmony_ci    case AF_INET6:
119813498266Sopenharmony_ci      port = ntohs(sa6->sin6_port);
119913498266Sopenharmony_ci      break;
120013498266Sopenharmony_ci#endif
120113498266Sopenharmony_ci    default:
120213498266Sopenharmony_ci      continue; /* might as well skip this */
120313498266Sopenharmony_ci    }
120413498266Sopenharmony_ci
120513498266Sopenharmony_ci    if(EPRT == fcmd) {
120613498266Sopenharmony_ci      /*
120713498266Sopenharmony_ci       * Two fine examples from RFC2428;
120813498266Sopenharmony_ci       *
120913498266Sopenharmony_ci       * EPRT |1|132.235.1.2|6275|
121013498266Sopenharmony_ci       *
121113498266Sopenharmony_ci       * EPRT |2|1080::8:800:200C:417A|5282|
121213498266Sopenharmony_ci       */
121313498266Sopenharmony_ci
121413498266Sopenharmony_ci      result = Curl_pp_sendf(data, &ftpc->pp, "%s |%d|%s|%hu|", mode[fcmd],
121513498266Sopenharmony_ci                             sa->sa_family == AF_INET?1:2,
121613498266Sopenharmony_ci                             myhost, port);
121713498266Sopenharmony_ci      if(result) {
121813498266Sopenharmony_ci        failf(data, "Failure sending EPRT command: %s",
121913498266Sopenharmony_ci              curl_easy_strerror(result));
122013498266Sopenharmony_ci        goto out;
122113498266Sopenharmony_ci      }
122213498266Sopenharmony_ci      break;
122313498266Sopenharmony_ci    }
122413498266Sopenharmony_ci    if(PORT == fcmd) {
122513498266Sopenharmony_ci      /* large enough for [IP address],[num],[num] */
122613498266Sopenharmony_ci      char target[sizeof(myhost) + 20];
122713498266Sopenharmony_ci      char *source = myhost;
122813498266Sopenharmony_ci      char *dest = target;
122913498266Sopenharmony_ci
123013498266Sopenharmony_ci      /* translate x.x.x.x to x,x,x,x */
123113498266Sopenharmony_ci      while(source && *source) {
123213498266Sopenharmony_ci        if(*source == '.')
123313498266Sopenharmony_ci          *dest = ',';
123413498266Sopenharmony_ci        else
123513498266Sopenharmony_ci          *dest = *source;
123613498266Sopenharmony_ci        dest++;
123713498266Sopenharmony_ci        source++;
123813498266Sopenharmony_ci      }
123913498266Sopenharmony_ci      *dest = 0;
124013498266Sopenharmony_ci      msnprintf(dest, 20, ",%d,%d", (int)(port>>8), (int)(port&0xff));
124113498266Sopenharmony_ci
124213498266Sopenharmony_ci      result = Curl_pp_sendf(data, &ftpc->pp, "%s %s", mode[fcmd], target);
124313498266Sopenharmony_ci      if(result) {
124413498266Sopenharmony_ci        failf(data, "Failure sending PORT command: %s",
124513498266Sopenharmony_ci              curl_easy_strerror(result));
124613498266Sopenharmony_ci        goto out;
124713498266Sopenharmony_ci      }
124813498266Sopenharmony_ci      break;
124913498266Sopenharmony_ci    }
125013498266Sopenharmony_ci  }
125113498266Sopenharmony_ci
125213498266Sopenharmony_ci  /* store which command was sent */
125313498266Sopenharmony_ci  ftpc->count1 = fcmd;
125413498266Sopenharmony_ci
125513498266Sopenharmony_ci  /* Replace any filter on SECONDARY with one listening on this socket */
125613498266Sopenharmony_ci  result = Curl_conn_tcp_listen_set(data, conn, SECONDARYSOCKET, &portsock);
125713498266Sopenharmony_ci  if(result)
125813498266Sopenharmony_ci    goto out;
125913498266Sopenharmony_ci  portsock = CURL_SOCKET_BAD; /* now held in filter */
126013498266Sopenharmony_ci  ftp_state(data, FTP_PORT);
126113498266Sopenharmony_ci
126213498266Sopenharmony_ciout:
126313498266Sopenharmony_ci  if(result) {
126413498266Sopenharmony_ci    ftp_state(data, FTP_STOP);
126513498266Sopenharmony_ci  }
126613498266Sopenharmony_ci  if(portsock != CURL_SOCKET_BAD)
126713498266Sopenharmony_ci    Curl_socket_close(data, conn, portsock);
126813498266Sopenharmony_ci  return result;
126913498266Sopenharmony_ci}
127013498266Sopenharmony_ci
127113498266Sopenharmony_cistatic CURLcode ftp_state_use_pasv(struct Curl_easy *data,
127213498266Sopenharmony_ci                                   struct connectdata *conn)
127313498266Sopenharmony_ci{
127413498266Sopenharmony_ci  struct ftp_conn *ftpc = &conn->proto.ftpc;
127513498266Sopenharmony_ci  CURLcode result = CURLE_OK;
127613498266Sopenharmony_ci  /*
127713498266Sopenharmony_ci    Here's the executive summary on what to do:
127813498266Sopenharmony_ci
127913498266Sopenharmony_ci    PASV is RFC959, expect:
128013498266Sopenharmony_ci    227 Entering Passive Mode (a1,a2,a3,a4,p1,p2)
128113498266Sopenharmony_ci
128213498266Sopenharmony_ci    LPSV is RFC1639, expect:
128313498266Sopenharmony_ci    228 Entering Long Passive Mode (4,4,a1,a2,a3,a4,2,p1,p2)
128413498266Sopenharmony_ci
128513498266Sopenharmony_ci    EPSV is RFC2428, expect:
128613498266Sopenharmony_ci    229 Entering Extended Passive Mode (|||port|)
128713498266Sopenharmony_ci
128813498266Sopenharmony_ci  */
128913498266Sopenharmony_ci
129013498266Sopenharmony_ci  static const char mode[][5] = { "EPSV", "PASV" };
129113498266Sopenharmony_ci  int modeoff;
129213498266Sopenharmony_ci
129313498266Sopenharmony_ci#ifdef PF_INET6
129413498266Sopenharmony_ci  if(!conn->bits.ftp_use_epsv && conn->bits.ipv6)
129513498266Sopenharmony_ci    /* EPSV is disabled but we are connected to a IPv6 host, so we ignore the
129613498266Sopenharmony_ci       request and enable EPSV again! */
129713498266Sopenharmony_ci    conn->bits.ftp_use_epsv = TRUE;
129813498266Sopenharmony_ci#endif
129913498266Sopenharmony_ci
130013498266Sopenharmony_ci  modeoff = conn->bits.ftp_use_epsv?0:1;
130113498266Sopenharmony_ci
130213498266Sopenharmony_ci  result = Curl_pp_sendf(data, &ftpc->pp, "%s", mode[modeoff]);
130313498266Sopenharmony_ci  if(!result) {
130413498266Sopenharmony_ci    ftpc->count1 = modeoff;
130513498266Sopenharmony_ci    ftp_state(data, FTP_PASV);
130613498266Sopenharmony_ci    infof(data, "Connect data stream passively");
130713498266Sopenharmony_ci  }
130813498266Sopenharmony_ci  return result;
130913498266Sopenharmony_ci}
131013498266Sopenharmony_ci
131113498266Sopenharmony_ci/*
131213498266Sopenharmony_ci * ftp_state_prepare_transfer() starts PORT, PASV or PRET etc.
131313498266Sopenharmony_ci *
131413498266Sopenharmony_ci * REST is the last command in the chain of commands when a "head"-like
131513498266Sopenharmony_ci * request is made. Thus, if an actual transfer is to be made this is where we
131613498266Sopenharmony_ci * take off for real.
131713498266Sopenharmony_ci */
131813498266Sopenharmony_cistatic CURLcode ftp_state_prepare_transfer(struct Curl_easy *data)
131913498266Sopenharmony_ci{
132013498266Sopenharmony_ci  CURLcode result = CURLE_OK;
132113498266Sopenharmony_ci  struct FTP *ftp = data->req.p.ftp;
132213498266Sopenharmony_ci  struct connectdata *conn = data->conn;
132313498266Sopenharmony_ci
132413498266Sopenharmony_ci  if(ftp->transfer != PPTRANSFER_BODY) {
132513498266Sopenharmony_ci    /* doesn't transfer any data */
132613498266Sopenharmony_ci
132713498266Sopenharmony_ci    /* still possibly do PRE QUOTE jobs */
132813498266Sopenharmony_ci    ftp_state(data, FTP_RETR_PREQUOTE);
132913498266Sopenharmony_ci    result = ftp_state_quote(data, TRUE, FTP_RETR_PREQUOTE);
133013498266Sopenharmony_ci  }
133113498266Sopenharmony_ci  else if(data->set.ftp_use_port) {
133213498266Sopenharmony_ci    /* We have chosen to use the PORT (or similar) command */
133313498266Sopenharmony_ci    result = ftp_state_use_port(data, EPRT);
133413498266Sopenharmony_ci  }
133513498266Sopenharmony_ci  else {
133613498266Sopenharmony_ci    /* We have chosen (this is default) to use the PASV (or similar) command */
133713498266Sopenharmony_ci    if(data->set.ftp_use_pret) {
133813498266Sopenharmony_ci      /* The user has requested that we send a PRET command
133913498266Sopenharmony_ci         to prepare the server for the upcoming PASV */
134013498266Sopenharmony_ci      struct ftp_conn *ftpc = &conn->proto.ftpc;
134113498266Sopenharmony_ci      if(!conn->proto.ftpc.file)
134213498266Sopenharmony_ci        result = Curl_pp_sendf(data, &ftpc->pp, "PRET %s",
134313498266Sopenharmony_ci                               data->set.str[STRING_CUSTOMREQUEST]?
134413498266Sopenharmony_ci                               data->set.str[STRING_CUSTOMREQUEST]:
134513498266Sopenharmony_ci                               (data->state.list_only?"NLST":"LIST"));
134613498266Sopenharmony_ci      else if(data->state.upload)
134713498266Sopenharmony_ci        result = Curl_pp_sendf(data, &ftpc->pp, "PRET STOR %s",
134813498266Sopenharmony_ci                               conn->proto.ftpc.file);
134913498266Sopenharmony_ci      else
135013498266Sopenharmony_ci        result = Curl_pp_sendf(data, &ftpc->pp, "PRET RETR %s",
135113498266Sopenharmony_ci                               conn->proto.ftpc.file);
135213498266Sopenharmony_ci      if(!result)
135313498266Sopenharmony_ci        ftp_state(data, FTP_PRET);
135413498266Sopenharmony_ci    }
135513498266Sopenharmony_ci    else
135613498266Sopenharmony_ci      result = ftp_state_use_pasv(data, conn);
135713498266Sopenharmony_ci  }
135813498266Sopenharmony_ci  return result;
135913498266Sopenharmony_ci}
136013498266Sopenharmony_ci
136113498266Sopenharmony_cistatic CURLcode ftp_state_rest(struct Curl_easy *data,
136213498266Sopenharmony_ci                               struct connectdata *conn)
136313498266Sopenharmony_ci{
136413498266Sopenharmony_ci  CURLcode result = CURLE_OK;
136513498266Sopenharmony_ci  struct FTP *ftp = data->req.p.ftp;
136613498266Sopenharmony_ci  struct ftp_conn *ftpc = &conn->proto.ftpc;
136713498266Sopenharmony_ci
136813498266Sopenharmony_ci  if((ftp->transfer != PPTRANSFER_BODY) && ftpc->file) {
136913498266Sopenharmony_ci    /* if a "head"-like request is being made (on a file) */
137013498266Sopenharmony_ci
137113498266Sopenharmony_ci    /* Determine if server can respond to REST command and therefore
137213498266Sopenharmony_ci       whether it supports range */
137313498266Sopenharmony_ci    result = Curl_pp_sendf(data, &ftpc->pp, "REST %d", 0);
137413498266Sopenharmony_ci    if(!result)
137513498266Sopenharmony_ci      ftp_state(data, FTP_REST);
137613498266Sopenharmony_ci  }
137713498266Sopenharmony_ci  else
137813498266Sopenharmony_ci    result = ftp_state_prepare_transfer(data);
137913498266Sopenharmony_ci
138013498266Sopenharmony_ci  return result;
138113498266Sopenharmony_ci}
138213498266Sopenharmony_ci
138313498266Sopenharmony_cistatic CURLcode ftp_state_size(struct Curl_easy *data,
138413498266Sopenharmony_ci                               struct connectdata *conn)
138513498266Sopenharmony_ci{
138613498266Sopenharmony_ci  CURLcode result = CURLE_OK;
138713498266Sopenharmony_ci  struct FTP *ftp = data->req.p.ftp;
138813498266Sopenharmony_ci  struct ftp_conn *ftpc = &conn->proto.ftpc;
138913498266Sopenharmony_ci
139013498266Sopenharmony_ci  if((ftp->transfer == PPTRANSFER_INFO) && ftpc->file) {
139113498266Sopenharmony_ci    /* if a "head"-like request is being made (on a file) */
139213498266Sopenharmony_ci
139313498266Sopenharmony_ci    /* we know ftpc->file is a valid pointer to a file name */
139413498266Sopenharmony_ci    result = Curl_pp_sendf(data, &ftpc->pp, "SIZE %s", ftpc->file);
139513498266Sopenharmony_ci    if(!result)
139613498266Sopenharmony_ci      ftp_state(data, FTP_SIZE);
139713498266Sopenharmony_ci  }
139813498266Sopenharmony_ci  else
139913498266Sopenharmony_ci    result = ftp_state_rest(data, conn);
140013498266Sopenharmony_ci
140113498266Sopenharmony_ci  return result;
140213498266Sopenharmony_ci}
140313498266Sopenharmony_ci
140413498266Sopenharmony_cistatic CURLcode ftp_state_list(struct Curl_easy *data)
140513498266Sopenharmony_ci{
140613498266Sopenharmony_ci  CURLcode result = CURLE_OK;
140713498266Sopenharmony_ci  struct FTP *ftp = data->req.p.ftp;
140813498266Sopenharmony_ci  struct connectdata *conn = data->conn;
140913498266Sopenharmony_ci
141013498266Sopenharmony_ci  /* If this output is to be machine-parsed, the NLST command might be better
141113498266Sopenharmony_ci     to use, since the LIST command output is not specified or standard in any
141213498266Sopenharmony_ci     way. It has turned out that the NLST list output is not the same on all
141313498266Sopenharmony_ci     servers either... */
141413498266Sopenharmony_ci
141513498266Sopenharmony_ci  /*
141613498266Sopenharmony_ci     if FTPFILE_NOCWD was specified, we should add the path
141713498266Sopenharmony_ci     as argument for the LIST / NLST / or custom command.
141813498266Sopenharmony_ci     Whether the server will support this, is uncertain.
141913498266Sopenharmony_ci
142013498266Sopenharmony_ci     The other ftp_filemethods will CWD into dir/dir/ first and
142113498266Sopenharmony_ci     then just do LIST (in that case: nothing to do here)
142213498266Sopenharmony_ci  */
142313498266Sopenharmony_ci  char *lstArg = NULL;
142413498266Sopenharmony_ci  char *cmd;
142513498266Sopenharmony_ci
142613498266Sopenharmony_ci  if((data->set.ftp_filemethod == FTPFILE_NOCWD) && ftp->path) {
142713498266Sopenharmony_ci    /* url-decode before evaluation: e.g. paths starting/ending with %2f */
142813498266Sopenharmony_ci    const char *slashPos = NULL;
142913498266Sopenharmony_ci    char *rawPath = NULL;
143013498266Sopenharmony_ci    result = Curl_urldecode(ftp->path, 0, &rawPath, NULL, REJECT_CTRL);
143113498266Sopenharmony_ci    if(result)
143213498266Sopenharmony_ci      return result;
143313498266Sopenharmony_ci
143413498266Sopenharmony_ci    slashPos = strrchr(rawPath, '/');
143513498266Sopenharmony_ci    if(slashPos) {
143613498266Sopenharmony_ci      /* chop off the file part if format is dir/file otherwise remove
143713498266Sopenharmony_ci         the trailing slash for dir/dir/ except for absolute path / */
143813498266Sopenharmony_ci      size_t n = slashPos - rawPath;
143913498266Sopenharmony_ci      if(n == 0)
144013498266Sopenharmony_ci        ++n;
144113498266Sopenharmony_ci
144213498266Sopenharmony_ci      lstArg = rawPath;
144313498266Sopenharmony_ci      lstArg[n] = '\0';
144413498266Sopenharmony_ci    }
144513498266Sopenharmony_ci    else
144613498266Sopenharmony_ci      free(rawPath);
144713498266Sopenharmony_ci  }
144813498266Sopenharmony_ci
144913498266Sopenharmony_ci  cmd = aprintf("%s%s%s",
145013498266Sopenharmony_ci                data->set.str[STRING_CUSTOMREQUEST]?
145113498266Sopenharmony_ci                data->set.str[STRING_CUSTOMREQUEST]:
145213498266Sopenharmony_ci                (data->state.list_only?"NLST":"LIST"),
145313498266Sopenharmony_ci                lstArg? " ": "",
145413498266Sopenharmony_ci                lstArg? lstArg: "");
145513498266Sopenharmony_ci  free(lstArg);
145613498266Sopenharmony_ci
145713498266Sopenharmony_ci  if(!cmd)
145813498266Sopenharmony_ci    return CURLE_OUT_OF_MEMORY;
145913498266Sopenharmony_ci
146013498266Sopenharmony_ci  result = Curl_pp_sendf(data, &conn->proto.ftpc.pp, "%s", cmd);
146113498266Sopenharmony_ci  free(cmd);
146213498266Sopenharmony_ci
146313498266Sopenharmony_ci  if(!result)
146413498266Sopenharmony_ci    ftp_state(data, FTP_LIST);
146513498266Sopenharmony_ci
146613498266Sopenharmony_ci  return result;
146713498266Sopenharmony_ci}
146813498266Sopenharmony_ci
146913498266Sopenharmony_cistatic CURLcode ftp_state_retr_prequote(struct Curl_easy *data)
147013498266Sopenharmony_ci{
147113498266Sopenharmony_ci  /* We've sent the TYPE, now we must send the list of prequote strings */
147213498266Sopenharmony_ci  return ftp_state_quote(data, TRUE, FTP_RETR_PREQUOTE);
147313498266Sopenharmony_ci}
147413498266Sopenharmony_ci
147513498266Sopenharmony_cistatic CURLcode ftp_state_stor_prequote(struct Curl_easy *data)
147613498266Sopenharmony_ci{
147713498266Sopenharmony_ci  /* We've sent the TYPE, now we must send the list of prequote strings */
147813498266Sopenharmony_ci  return ftp_state_quote(data, TRUE, FTP_STOR_PREQUOTE);
147913498266Sopenharmony_ci}
148013498266Sopenharmony_ci
148113498266Sopenharmony_cistatic CURLcode ftp_state_type(struct Curl_easy *data)
148213498266Sopenharmony_ci{
148313498266Sopenharmony_ci  CURLcode result = CURLE_OK;
148413498266Sopenharmony_ci  struct FTP *ftp = data->req.p.ftp;
148513498266Sopenharmony_ci  struct connectdata *conn = data->conn;
148613498266Sopenharmony_ci  struct ftp_conn *ftpc = &conn->proto.ftpc;
148713498266Sopenharmony_ci
148813498266Sopenharmony_ci  /* If we have selected NOBODY and HEADER, it means that we only want file
148913498266Sopenharmony_ci     information. Which in FTP can't be much more than the file size and
149013498266Sopenharmony_ci     date. */
149113498266Sopenharmony_ci  if(data->req.no_body && ftpc->file &&
149213498266Sopenharmony_ci     ftp_need_type(conn, data->state.prefer_ascii)) {
149313498266Sopenharmony_ci    /* The SIZE command is _not_ RFC 959 specified, and therefore many servers
149413498266Sopenharmony_ci       may not support it! It is however the only way we have to get a file's
149513498266Sopenharmony_ci       size! */
149613498266Sopenharmony_ci
149713498266Sopenharmony_ci    ftp->transfer = PPTRANSFER_INFO;
149813498266Sopenharmony_ci    /* this means no actual transfer will be made */
149913498266Sopenharmony_ci
150013498266Sopenharmony_ci    /* Some servers return different sizes for different modes, and thus we
150113498266Sopenharmony_ci       must set the proper type before we check the size */
150213498266Sopenharmony_ci    result = ftp_nb_type(data, conn, data->state.prefer_ascii, FTP_TYPE);
150313498266Sopenharmony_ci    if(result)
150413498266Sopenharmony_ci      return result;
150513498266Sopenharmony_ci  }
150613498266Sopenharmony_ci  else
150713498266Sopenharmony_ci    result = ftp_state_size(data, conn);
150813498266Sopenharmony_ci
150913498266Sopenharmony_ci  return result;
151013498266Sopenharmony_ci}
151113498266Sopenharmony_ci
151213498266Sopenharmony_ci/* This is called after the CWD commands have been done in the beginning of
151313498266Sopenharmony_ci   the DO phase */
151413498266Sopenharmony_cistatic CURLcode ftp_state_mdtm(struct Curl_easy *data)
151513498266Sopenharmony_ci{
151613498266Sopenharmony_ci  CURLcode result = CURLE_OK;
151713498266Sopenharmony_ci  struct connectdata *conn = data->conn;
151813498266Sopenharmony_ci  struct ftp_conn *ftpc = &conn->proto.ftpc;
151913498266Sopenharmony_ci
152013498266Sopenharmony_ci  /* Requested time of file or time-depended transfer? */
152113498266Sopenharmony_ci  if((data->set.get_filetime || data->set.timecondition) && ftpc->file) {
152213498266Sopenharmony_ci
152313498266Sopenharmony_ci    /* we have requested to get the modified-time of the file, this is a white
152413498266Sopenharmony_ci       spot as the MDTM is not mentioned in RFC959 */
152513498266Sopenharmony_ci    result = Curl_pp_sendf(data, &ftpc->pp, "MDTM %s", ftpc->file);
152613498266Sopenharmony_ci
152713498266Sopenharmony_ci    if(!result)
152813498266Sopenharmony_ci      ftp_state(data, FTP_MDTM);
152913498266Sopenharmony_ci  }
153013498266Sopenharmony_ci  else
153113498266Sopenharmony_ci    result = ftp_state_type(data);
153213498266Sopenharmony_ci
153313498266Sopenharmony_ci  return result;
153413498266Sopenharmony_ci}
153513498266Sopenharmony_ci
153613498266Sopenharmony_ci
153713498266Sopenharmony_ci/* This is called after the TYPE and possible quote commands have been sent */
153813498266Sopenharmony_cistatic CURLcode ftp_state_ul_setup(struct Curl_easy *data,
153913498266Sopenharmony_ci                                   bool sizechecked)
154013498266Sopenharmony_ci{
154113498266Sopenharmony_ci  CURLcode result = CURLE_OK;
154213498266Sopenharmony_ci  struct connectdata *conn = data->conn;
154313498266Sopenharmony_ci  struct FTP *ftp = data->req.p.ftp;
154413498266Sopenharmony_ci  struct ftp_conn *ftpc = &conn->proto.ftpc;
154513498266Sopenharmony_ci  bool append = data->set.remote_append;
154613498266Sopenharmony_ci
154713498266Sopenharmony_ci  if((data->state.resume_from && !sizechecked) ||
154813498266Sopenharmony_ci     ((data->state.resume_from > 0) && sizechecked)) {
154913498266Sopenharmony_ci    /* we're about to continue the uploading of a file */
155013498266Sopenharmony_ci    /* 1. get already existing file's size. We use the SIZE command for this
155113498266Sopenharmony_ci       which may not exist in the server!  The SIZE command is not in
155213498266Sopenharmony_ci       RFC959. */
155313498266Sopenharmony_ci
155413498266Sopenharmony_ci    /* 2. This used to set REST. But since we can do append, we
155513498266Sopenharmony_ci       don't another ftp command. We just skip the source file
155613498266Sopenharmony_ci       offset and then we APPEND the rest on the file instead */
155713498266Sopenharmony_ci
155813498266Sopenharmony_ci    /* 3. pass file-size number of bytes in the source file */
155913498266Sopenharmony_ci    /* 4. lower the infilesize counter */
156013498266Sopenharmony_ci    /* => transfer as usual */
156113498266Sopenharmony_ci    int seekerr = CURL_SEEKFUNC_OK;
156213498266Sopenharmony_ci
156313498266Sopenharmony_ci    if(data->state.resume_from < 0) {
156413498266Sopenharmony_ci      /* Got no given size to start from, figure it out */
156513498266Sopenharmony_ci      result = Curl_pp_sendf(data, &ftpc->pp, "SIZE %s", ftpc->file);
156613498266Sopenharmony_ci      if(!result)
156713498266Sopenharmony_ci        ftp_state(data, FTP_STOR_SIZE);
156813498266Sopenharmony_ci      return result;
156913498266Sopenharmony_ci    }
157013498266Sopenharmony_ci
157113498266Sopenharmony_ci    /* enable append */
157213498266Sopenharmony_ci    append = TRUE;
157313498266Sopenharmony_ci
157413498266Sopenharmony_ci    /* Let's read off the proper amount of bytes from the input. */
157513498266Sopenharmony_ci    if(conn->seek_func) {
157613498266Sopenharmony_ci      Curl_set_in_callback(data, true);
157713498266Sopenharmony_ci      seekerr = conn->seek_func(conn->seek_client, data->state.resume_from,
157813498266Sopenharmony_ci                                SEEK_SET);
157913498266Sopenharmony_ci      Curl_set_in_callback(data, false);
158013498266Sopenharmony_ci    }
158113498266Sopenharmony_ci
158213498266Sopenharmony_ci    if(seekerr != CURL_SEEKFUNC_OK) {
158313498266Sopenharmony_ci      curl_off_t passed = 0;
158413498266Sopenharmony_ci      if(seekerr != CURL_SEEKFUNC_CANTSEEK) {
158513498266Sopenharmony_ci        failf(data, "Could not seek stream");
158613498266Sopenharmony_ci        return CURLE_FTP_COULDNT_USE_REST;
158713498266Sopenharmony_ci      }
158813498266Sopenharmony_ci      /* seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */
158913498266Sopenharmony_ci      do {
159013498266Sopenharmony_ci        char scratch[4*1024];
159113498266Sopenharmony_ci        size_t readthisamountnow =
159213498266Sopenharmony_ci          (data->state.resume_from - passed > (curl_off_t)sizeof(scratch)) ?
159313498266Sopenharmony_ci          sizeof(scratch) :
159413498266Sopenharmony_ci          curlx_sotouz(data->state.resume_from - passed);
159513498266Sopenharmony_ci
159613498266Sopenharmony_ci        size_t actuallyread =
159713498266Sopenharmony_ci          data->state.fread_func(scratch, 1, readthisamountnow,
159813498266Sopenharmony_ci                                 data->state.in);
159913498266Sopenharmony_ci
160013498266Sopenharmony_ci        passed += actuallyread;
160113498266Sopenharmony_ci        if((actuallyread == 0) || (actuallyread > readthisamountnow)) {
160213498266Sopenharmony_ci          /* this checks for greater-than only to make sure that the
160313498266Sopenharmony_ci             CURL_READFUNC_ABORT return code still aborts */
160413498266Sopenharmony_ci          failf(data, "Failed to read data");
160513498266Sopenharmony_ci          return CURLE_FTP_COULDNT_USE_REST;
160613498266Sopenharmony_ci        }
160713498266Sopenharmony_ci      } while(passed < data->state.resume_from);
160813498266Sopenharmony_ci    }
160913498266Sopenharmony_ci    /* now, decrease the size of the read */
161013498266Sopenharmony_ci    if(data->state.infilesize>0) {
161113498266Sopenharmony_ci      data->state.infilesize -= data->state.resume_from;
161213498266Sopenharmony_ci
161313498266Sopenharmony_ci      if(data->state.infilesize <= 0) {
161413498266Sopenharmony_ci        infof(data, "File already completely uploaded");
161513498266Sopenharmony_ci
161613498266Sopenharmony_ci        /* no data to transfer */
161713498266Sopenharmony_ci        Curl_setup_transfer(data, -1, -1, FALSE, -1);
161813498266Sopenharmony_ci
161913498266Sopenharmony_ci        /* Set ->transfer so that we won't get any error in
162013498266Sopenharmony_ci         * ftp_done() because we didn't transfer anything! */
162113498266Sopenharmony_ci        ftp->transfer = PPTRANSFER_NONE;
162213498266Sopenharmony_ci
162313498266Sopenharmony_ci        ftp_state(data, FTP_STOP);
162413498266Sopenharmony_ci        return CURLE_OK;
162513498266Sopenharmony_ci      }
162613498266Sopenharmony_ci    }
162713498266Sopenharmony_ci    /* we've passed, proceed as normal */
162813498266Sopenharmony_ci  } /* resume_from */
162913498266Sopenharmony_ci
163013498266Sopenharmony_ci  result = Curl_pp_sendf(data, &ftpc->pp, append?"APPE %s":"STOR %s",
163113498266Sopenharmony_ci                         ftpc->file);
163213498266Sopenharmony_ci  if(!result)
163313498266Sopenharmony_ci    ftp_state(data, FTP_STOR);
163413498266Sopenharmony_ci
163513498266Sopenharmony_ci  return result;
163613498266Sopenharmony_ci}
163713498266Sopenharmony_ci
163813498266Sopenharmony_cistatic CURLcode ftp_state_quote(struct Curl_easy *data,
163913498266Sopenharmony_ci                                bool init,
164013498266Sopenharmony_ci                                ftpstate instate)
164113498266Sopenharmony_ci{
164213498266Sopenharmony_ci  CURLcode result = CURLE_OK;
164313498266Sopenharmony_ci  struct FTP *ftp = data->req.p.ftp;
164413498266Sopenharmony_ci  struct connectdata *conn = data->conn;
164513498266Sopenharmony_ci  struct ftp_conn *ftpc = &conn->proto.ftpc;
164613498266Sopenharmony_ci  bool quote = FALSE;
164713498266Sopenharmony_ci  struct curl_slist *item;
164813498266Sopenharmony_ci
164913498266Sopenharmony_ci  switch(instate) {
165013498266Sopenharmony_ci  case FTP_QUOTE:
165113498266Sopenharmony_ci  default:
165213498266Sopenharmony_ci    item = data->set.quote;
165313498266Sopenharmony_ci    break;
165413498266Sopenharmony_ci  case FTP_RETR_PREQUOTE:
165513498266Sopenharmony_ci  case FTP_STOR_PREQUOTE:
165613498266Sopenharmony_ci    item = data->set.prequote;
165713498266Sopenharmony_ci    break;
165813498266Sopenharmony_ci  case FTP_POSTQUOTE:
165913498266Sopenharmony_ci    item = data->set.postquote;
166013498266Sopenharmony_ci    break;
166113498266Sopenharmony_ci  }
166213498266Sopenharmony_ci
166313498266Sopenharmony_ci  /*
166413498266Sopenharmony_ci   * This state uses:
166513498266Sopenharmony_ci   * 'count1' to iterate over the commands to send
166613498266Sopenharmony_ci   * 'count2' to store whether to allow commands to fail
166713498266Sopenharmony_ci   */
166813498266Sopenharmony_ci
166913498266Sopenharmony_ci  if(init)
167013498266Sopenharmony_ci    ftpc->count1 = 0;
167113498266Sopenharmony_ci  else
167213498266Sopenharmony_ci    ftpc->count1++;
167313498266Sopenharmony_ci
167413498266Sopenharmony_ci  if(item) {
167513498266Sopenharmony_ci    int i = 0;
167613498266Sopenharmony_ci
167713498266Sopenharmony_ci    /* Skip count1 items in the linked list */
167813498266Sopenharmony_ci    while((i< ftpc->count1) && item) {
167913498266Sopenharmony_ci      item = item->next;
168013498266Sopenharmony_ci      i++;
168113498266Sopenharmony_ci    }
168213498266Sopenharmony_ci    if(item) {
168313498266Sopenharmony_ci      char *cmd = item->data;
168413498266Sopenharmony_ci      if(cmd[0] == '*') {
168513498266Sopenharmony_ci        cmd++;
168613498266Sopenharmony_ci        ftpc->count2 = 1; /* the sent command is allowed to fail */
168713498266Sopenharmony_ci      }
168813498266Sopenharmony_ci      else
168913498266Sopenharmony_ci        ftpc->count2 = 0; /* failure means cancel operation */
169013498266Sopenharmony_ci
169113498266Sopenharmony_ci      result = Curl_pp_sendf(data, &ftpc->pp, "%s", cmd);
169213498266Sopenharmony_ci      if(result)
169313498266Sopenharmony_ci        return result;
169413498266Sopenharmony_ci      ftp_state(data, instate);
169513498266Sopenharmony_ci      quote = TRUE;
169613498266Sopenharmony_ci    }
169713498266Sopenharmony_ci  }
169813498266Sopenharmony_ci
169913498266Sopenharmony_ci  if(!quote) {
170013498266Sopenharmony_ci    /* No more quote to send, continue to ... */
170113498266Sopenharmony_ci    switch(instate) {
170213498266Sopenharmony_ci    case FTP_QUOTE:
170313498266Sopenharmony_ci    default:
170413498266Sopenharmony_ci      result = ftp_state_cwd(data, conn);
170513498266Sopenharmony_ci      break;
170613498266Sopenharmony_ci    case FTP_RETR_PREQUOTE:
170713498266Sopenharmony_ci      if(ftp->transfer != PPTRANSFER_BODY)
170813498266Sopenharmony_ci        ftp_state(data, FTP_STOP);
170913498266Sopenharmony_ci      else {
171013498266Sopenharmony_ci        if(ftpc->known_filesize != -1) {
171113498266Sopenharmony_ci          Curl_pgrsSetDownloadSize(data, ftpc->known_filesize);
171213498266Sopenharmony_ci          result = ftp_state_retr(data, ftpc->known_filesize);
171313498266Sopenharmony_ci        }
171413498266Sopenharmony_ci        else {
171513498266Sopenharmony_ci          if(data->set.ignorecl || data->state.prefer_ascii) {
171613498266Sopenharmony_ci            /* 'ignorecl' is used to support download of growing files.  It
171713498266Sopenharmony_ci               prevents the state machine from requesting the file size from
171813498266Sopenharmony_ci               the server.  With an unknown file size the download continues
171913498266Sopenharmony_ci               until the server terminates it, otherwise the client stops if
172013498266Sopenharmony_ci               the received byte count exceeds the reported file size.  Set
172113498266Sopenharmony_ci               option CURLOPT_IGNORE_CONTENT_LENGTH to 1 to enable this
172213498266Sopenharmony_ci               behavior.
172313498266Sopenharmony_ci
172413498266Sopenharmony_ci               In addition: asking for the size for 'TYPE A' transfers is not
172513498266Sopenharmony_ci               constructive since servers don't report the converted size. So
172613498266Sopenharmony_ci               skip it.
172713498266Sopenharmony_ci            */
172813498266Sopenharmony_ci            result = Curl_pp_sendf(data, &ftpc->pp, "RETR %s", ftpc->file);
172913498266Sopenharmony_ci            if(!result)
173013498266Sopenharmony_ci              ftp_state(data, FTP_RETR);
173113498266Sopenharmony_ci          }
173213498266Sopenharmony_ci          else {
173313498266Sopenharmony_ci            result = Curl_pp_sendf(data, &ftpc->pp, "SIZE %s", ftpc->file);
173413498266Sopenharmony_ci            if(!result)
173513498266Sopenharmony_ci              ftp_state(data, FTP_RETR_SIZE);
173613498266Sopenharmony_ci          }
173713498266Sopenharmony_ci        }
173813498266Sopenharmony_ci      }
173913498266Sopenharmony_ci      break;
174013498266Sopenharmony_ci    case FTP_STOR_PREQUOTE:
174113498266Sopenharmony_ci      result = ftp_state_ul_setup(data, FALSE);
174213498266Sopenharmony_ci      break;
174313498266Sopenharmony_ci    case FTP_POSTQUOTE:
174413498266Sopenharmony_ci      break;
174513498266Sopenharmony_ci    }
174613498266Sopenharmony_ci  }
174713498266Sopenharmony_ci
174813498266Sopenharmony_ci  return result;
174913498266Sopenharmony_ci}
175013498266Sopenharmony_ci
175113498266Sopenharmony_ci/* called from ftp_state_pasv_resp to switch to PASV in case of EPSV
175213498266Sopenharmony_ci   problems */
175313498266Sopenharmony_cistatic CURLcode ftp_epsv_disable(struct Curl_easy *data,
175413498266Sopenharmony_ci                                 struct connectdata *conn)
175513498266Sopenharmony_ci{
175613498266Sopenharmony_ci  CURLcode result = CURLE_OK;
175713498266Sopenharmony_ci
175813498266Sopenharmony_ci  if(conn->bits.ipv6
175913498266Sopenharmony_ci#ifndef CURL_DISABLE_PROXY
176013498266Sopenharmony_ci     && !(conn->bits.tunnel_proxy || conn->bits.socksproxy)
176113498266Sopenharmony_ci#endif
176213498266Sopenharmony_ci    ) {
176313498266Sopenharmony_ci    /* We can't disable EPSV when doing IPv6, so this is instead a fail */
176413498266Sopenharmony_ci    failf(data, "Failed EPSV attempt, exiting");
176513498266Sopenharmony_ci    return CURLE_WEIRD_SERVER_REPLY;
176613498266Sopenharmony_ci  }
176713498266Sopenharmony_ci
176813498266Sopenharmony_ci  infof(data, "Failed EPSV attempt. Disabling EPSV");
176913498266Sopenharmony_ci  /* disable it for next transfer */
177013498266Sopenharmony_ci  conn->bits.ftp_use_epsv = FALSE;
177113498266Sopenharmony_ci  Curl_conn_close(data, SECONDARYSOCKET);
177213498266Sopenharmony_ci  Curl_conn_cf_discard_all(data, conn, SECONDARYSOCKET);
177313498266Sopenharmony_ci  data->state.errorbuf = FALSE; /* allow error message to get
177413498266Sopenharmony_ci                                         rewritten */
177513498266Sopenharmony_ci  result = Curl_pp_sendf(data, &conn->proto.ftpc.pp, "%s", "PASV");
177613498266Sopenharmony_ci  if(!result) {
177713498266Sopenharmony_ci    conn->proto.ftpc.count1++;
177813498266Sopenharmony_ci    /* remain in/go to the FTP_PASV state */
177913498266Sopenharmony_ci    ftp_state(data, FTP_PASV);
178013498266Sopenharmony_ci  }
178113498266Sopenharmony_ci  return result;
178213498266Sopenharmony_ci}
178313498266Sopenharmony_ci
178413498266Sopenharmony_ci
178513498266Sopenharmony_cistatic char *control_address(struct connectdata *conn)
178613498266Sopenharmony_ci{
178713498266Sopenharmony_ci  /* Returns the control connection IP address.
178813498266Sopenharmony_ci     If a proxy tunnel is used, returns the original host name instead, because
178913498266Sopenharmony_ci     the effective control connection address is the proxy address,
179013498266Sopenharmony_ci     not the ftp host. */
179113498266Sopenharmony_ci#ifndef CURL_DISABLE_PROXY
179213498266Sopenharmony_ci  if(conn->bits.tunnel_proxy || conn->bits.socksproxy)
179313498266Sopenharmony_ci    return conn->host.name;
179413498266Sopenharmony_ci#endif
179513498266Sopenharmony_ci  return conn->primary_ip;
179613498266Sopenharmony_ci}
179713498266Sopenharmony_ci
179813498266Sopenharmony_cistatic bool match_pasv_6nums(const char *p,
179913498266Sopenharmony_ci                             unsigned int *array) /* 6 numbers */
180013498266Sopenharmony_ci{
180113498266Sopenharmony_ci  int i;
180213498266Sopenharmony_ci  for(i = 0; i < 6; i++) {
180313498266Sopenharmony_ci    unsigned long num;
180413498266Sopenharmony_ci    char *endp;
180513498266Sopenharmony_ci    if(i) {
180613498266Sopenharmony_ci      if(*p != ',')
180713498266Sopenharmony_ci        return FALSE;
180813498266Sopenharmony_ci      p++;
180913498266Sopenharmony_ci    }
181013498266Sopenharmony_ci    if(!ISDIGIT(*p))
181113498266Sopenharmony_ci      return FALSE;
181213498266Sopenharmony_ci    num = strtoul(p, &endp, 10);
181313498266Sopenharmony_ci    if(num > 255)
181413498266Sopenharmony_ci      return FALSE;
181513498266Sopenharmony_ci    array[i] = (unsigned int)num;
181613498266Sopenharmony_ci    p = endp;
181713498266Sopenharmony_ci  }
181813498266Sopenharmony_ci  return TRUE;
181913498266Sopenharmony_ci}
182013498266Sopenharmony_ci
182113498266Sopenharmony_cistatic CURLcode ftp_state_pasv_resp(struct Curl_easy *data,
182213498266Sopenharmony_ci                                    int ftpcode)
182313498266Sopenharmony_ci{
182413498266Sopenharmony_ci  struct connectdata *conn = data->conn;
182513498266Sopenharmony_ci  struct ftp_conn *ftpc = &conn->proto.ftpc;
182613498266Sopenharmony_ci  CURLcode result;
182713498266Sopenharmony_ci  struct Curl_dns_entry *addr = NULL;
182813498266Sopenharmony_ci  enum resolve_t rc;
182913498266Sopenharmony_ci  unsigned short connectport; /* the local port connect() should use! */
183013498266Sopenharmony_ci  struct pingpong *pp = &ftpc->pp;
183113498266Sopenharmony_ci  char *str =
183213498266Sopenharmony_ci    Curl_dyn_ptr(&pp->recvbuf) + 4; /* start on the first letter */
183313498266Sopenharmony_ci
183413498266Sopenharmony_ci  /* if we come here again, make sure the former name is cleared */
183513498266Sopenharmony_ci  Curl_safefree(ftpc->newhost);
183613498266Sopenharmony_ci
183713498266Sopenharmony_ci  if((ftpc->count1 == 0) &&
183813498266Sopenharmony_ci     (ftpcode == 229)) {
183913498266Sopenharmony_ci    /* positive EPSV response */
184013498266Sopenharmony_ci    char *ptr = strchr(str, '(');
184113498266Sopenharmony_ci    if(ptr) {
184213498266Sopenharmony_ci      char sep;
184313498266Sopenharmony_ci      ptr++;
184413498266Sopenharmony_ci      /* |||12345| */
184513498266Sopenharmony_ci      sep = ptr[0];
184613498266Sopenharmony_ci      /* the ISDIGIT() check here is because strtoul() accepts leading minus
184713498266Sopenharmony_ci         etc */
184813498266Sopenharmony_ci      if((ptr[1] == sep) && (ptr[2] == sep) && ISDIGIT(ptr[3])) {
184913498266Sopenharmony_ci        char *endp;
185013498266Sopenharmony_ci        unsigned long num = strtoul(&ptr[3], &endp, 10);
185113498266Sopenharmony_ci        if(*endp != sep)
185213498266Sopenharmony_ci          ptr = NULL;
185313498266Sopenharmony_ci        else if(num > 0xffff) {
185413498266Sopenharmony_ci          failf(data, "Illegal port number in EPSV reply");
185513498266Sopenharmony_ci          return CURLE_FTP_WEIRD_PASV_REPLY;
185613498266Sopenharmony_ci        }
185713498266Sopenharmony_ci        if(ptr) {
185813498266Sopenharmony_ci          ftpc->newport = (unsigned short)(num & 0xffff);
185913498266Sopenharmony_ci          ftpc->newhost = strdup(control_address(conn));
186013498266Sopenharmony_ci          if(!ftpc->newhost)
186113498266Sopenharmony_ci            return CURLE_OUT_OF_MEMORY;
186213498266Sopenharmony_ci        }
186313498266Sopenharmony_ci      }
186413498266Sopenharmony_ci      else
186513498266Sopenharmony_ci        ptr = NULL;
186613498266Sopenharmony_ci    }
186713498266Sopenharmony_ci    if(!ptr) {
186813498266Sopenharmony_ci      failf(data, "Weirdly formatted EPSV reply");
186913498266Sopenharmony_ci      return CURLE_FTP_WEIRD_PASV_REPLY;
187013498266Sopenharmony_ci    }
187113498266Sopenharmony_ci  }
187213498266Sopenharmony_ci  else if((ftpc->count1 == 1) &&
187313498266Sopenharmony_ci          (ftpcode == 227)) {
187413498266Sopenharmony_ci    /* positive PASV response */
187513498266Sopenharmony_ci    unsigned int ip[6];
187613498266Sopenharmony_ci
187713498266Sopenharmony_ci    /*
187813498266Sopenharmony_ci     * Scan for a sequence of six comma-separated numbers and use them as
187913498266Sopenharmony_ci     * IP+port indicators.
188013498266Sopenharmony_ci     *
188113498266Sopenharmony_ci     * Found reply-strings include:
188213498266Sopenharmony_ci     * "227 Entering Passive Mode (127,0,0,1,4,51)"
188313498266Sopenharmony_ci     * "227 Data transfer will passively listen to 127,0,0,1,4,51"
188413498266Sopenharmony_ci     * "227 Entering passive mode. 127,0,0,1,4,51"
188513498266Sopenharmony_ci     */
188613498266Sopenharmony_ci    while(*str) {
188713498266Sopenharmony_ci      if(match_pasv_6nums(str, ip))
188813498266Sopenharmony_ci        break;
188913498266Sopenharmony_ci      str++;
189013498266Sopenharmony_ci    }
189113498266Sopenharmony_ci
189213498266Sopenharmony_ci    if(!*str) {
189313498266Sopenharmony_ci      failf(data, "Couldn't interpret the 227-response");
189413498266Sopenharmony_ci      return CURLE_FTP_WEIRD_227_FORMAT;
189513498266Sopenharmony_ci    }
189613498266Sopenharmony_ci
189713498266Sopenharmony_ci    /* we got OK from server */
189813498266Sopenharmony_ci    if(data->set.ftp_skip_ip) {
189913498266Sopenharmony_ci      /* told to ignore the remotely given IP but instead use the host we used
190013498266Sopenharmony_ci         for the control connection */
190113498266Sopenharmony_ci      infof(data, "Skip %u.%u.%u.%u for data connection, reuse %s instead",
190213498266Sopenharmony_ci            ip[0], ip[1], ip[2], ip[3],
190313498266Sopenharmony_ci            conn->host.name);
190413498266Sopenharmony_ci      ftpc->newhost = strdup(control_address(conn));
190513498266Sopenharmony_ci    }
190613498266Sopenharmony_ci    else
190713498266Sopenharmony_ci      ftpc->newhost = aprintf("%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]);
190813498266Sopenharmony_ci
190913498266Sopenharmony_ci    if(!ftpc->newhost)
191013498266Sopenharmony_ci      return CURLE_OUT_OF_MEMORY;
191113498266Sopenharmony_ci
191213498266Sopenharmony_ci    ftpc->newport = (unsigned short)(((ip[4]<<8) + ip[5]) & 0xffff);
191313498266Sopenharmony_ci  }
191413498266Sopenharmony_ci  else if(ftpc->count1 == 0) {
191513498266Sopenharmony_ci    /* EPSV failed, move on to PASV */
191613498266Sopenharmony_ci    return ftp_epsv_disable(data, conn);
191713498266Sopenharmony_ci  }
191813498266Sopenharmony_ci  else {
191913498266Sopenharmony_ci    failf(data, "Bad PASV/EPSV response: %03d", ftpcode);
192013498266Sopenharmony_ci    return CURLE_FTP_WEIRD_PASV_REPLY;
192113498266Sopenharmony_ci  }
192213498266Sopenharmony_ci
192313498266Sopenharmony_ci#ifndef CURL_DISABLE_PROXY
192413498266Sopenharmony_ci  if(conn->bits.proxy) {
192513498266Sopenharmony_ci    /*
192613498266Sopenharmony_ci     * This connection uses a proxy and we need to connect to the proxy again
192713498266Sopenharmony_ci     * here. We don't want to rely on a former host lookup that might've
192813498266Sopenharmony_ci     * expired now, instead we remake the lookup here and now!
192913498266Sopenharmony_ci     */
193013498266Sopenharmony_ci    const char * const host_name = conn->bits.socksproxy ?
193113498266Sopenharmony_ci      conn->socks_proxy.host.name : conn->http_proxy.host.name;
193213498266Sopenharmony_ci    rc = Curl_resolv(data, host_name, conn->port, FALSE, &addr);
193313498266Sopenharmony_ci    if(rc == CURLRESOLV_PENDING)
193413498266Sopenharmony_ci      /* BLOCKING, ignores the return code but 'addr' will be NULL in
193513498266Sopenharmony_ci         case of failure */
193613498266Sopenharmony_ci      (void)Curl_resolver_wait_resolv(data, &addr);
193713498266Sopenharmony_ci
193813498266Sopenharmony_ci    connectport =
193913498266Sopenharmony_ci      (unsigned short)conn->port; /* we connect to the proxy's port */
194013498266Sopenharmony_ci
194113498266Sopenharmony_ci    if(!addr) {
194213498266Sopenharmony_ci      failf(data, "Can't resolve proxy host %s:%hu", host_name, connectport);
194313498266Sopenharmony_ci      return CURLE_COULDNT_RESOLVE_PROXY;
194413498266Sopenharmony_ci    }
194513498266Sopenharmony_ci  }
194613498266Sopenharmony_ci  else
194713498266Sopenharmony_ci#endif
194813498266Sopenharmony_ci  {
194913498266Sopenharmony_ci    /* normal, direct, ftp connection */
195013498266Sopenharmony_ci    DEBUGASSERT(ftpc->newhost);
195113498266Sopenharmony_ci
195213498266Sopenharmony_ci    /* postponed address resolution in case of tcp fastopen */
195313498266Sopenharmony_ci    if(conn->bits.tcp_fastopen && !conn->bits.reuse && !ftpc->newhost[0]) {
195413498266Sopenharmony_ci      Curl_conn_ev_update_info(data, conn);
195513498266Sopenharmony_ci      Curl_safefree(ftpc->newhost);
195613498266Sopenharmony_ci      ftpc->newhost = strdup(control_address(conn));
195713498266Sopenharmony_ci      if(!ftpc->newhost)
195813498266Sopenharmony_ci        return CURLE_OUT_OF_MEMORY;
195913498266Sopenharmony_ci    }
196013498266Sopenharmony_ci
196113498266Sopenharmony_ci    rc = Curl_resolv(data, ftpc->newhost, ftpc->newport, FALSE, &addr);
196213498266Sopenharmony_ci    if(rc == CURLRESOLV_PENDING)
196313498266Sopenharmony_ci      /* BLOCKING */
196413498266Sopenharmony_ci      (void)Curl_resolver_wait_resolv(data, &addr);
196513498266Sopenharmony_ci
196613498266Sopenharmony_ci    connectport = ftpc->newport; /* we connect to the remote port */
196713498266Sopenharmony_ci
196813498266Sopenharmony_ci    if(!addr) {
196913498266Sopenharmony_ci      failf(data, "Can't resolve new host %s:%hu", ftpc->newhost, connectport);
197013498266Sopenharmony_ci      return CURLE_FTP_CANT_GET_HOST;
197113498266Sopenharmony_ci    }
197213498266Sopenharmony_ci  }
197313498266Sopenharmony_ci
197413498266Sopenharmony_ci  result = Curl_conn_setup(data, conn, SECONDARYSOCKET, addr,
197513498266Sopenharmony_ci                           conn->bits.ftp_use_data_ssl?
197613498266Sopenharmony_ci                           CURL_CF_SSL_ENABLE : CURL_CF_SSL_DISABLE);
197713498266Sopenharmony_ci
197813498266Sopenharmony_ci  if(result) {
197913498266Sopenharmony_ci    Curl_resolv_unlock(data, addr); /* we're done using this address */
198013498266Sopenharmony_ci    if(ftpc->count1 == 0 && ftpcode == 229)
198113498266Sopenharmony_ci      return ftp_epsv_disable(data, conn);
198213498266Sopenharmony_ci
198313498266Sopenharmony_ci    return result;
198413498266Sopenharmony_ci  }
198513498266Sopenharmony_ci
198613498266Sopenharmony_ci
198713498266Sopenharmony_ci  /*
198813498266Sopenharmony_ci   * When this is used from the multi interface, this might've returned with
198913498266Sopenharmony_ci   * the 'connected' set to FALSE and thus we are now awaiting a non-blocking
199013498266Sopenharmony_ci   * connect to connect.
199113498266Sopenharmony_ci   */
199213498266Sopenharmony_ci
199313498266Sopenharmony_ci  if(data->set.verbose)
199413498266Sopenharmony_ci    /* this just dumps information about this second connection */
199513498266Sopenharmony_ci    ftp_pasv_verbose(data, addr->addr, ftpc->newhost, connectport);
199613498266Sopenharmony_ci
199713498266Sopenharmony_ci  Curl_resolv_unlock(data, addr); /* we're done using this address */
199813498266Sopenharmony_ci
199913498266Sopenharmony_ci  Curl_safefree(conn->secondaryhostname);
200013498266Sopenharmony_ci  conn->secondary_port = ftpc->newport;
200113498266Sopenharmony_ci  conn->secondaryhostname = strdup(ftpc->newhost);
200213498266Sopenharmony_ci  if(!conn->secondaryhostname)
200313498266Sopenharmony_ci    return CURLE_OUT_OF_MEMORY;
200413498266Sopenharmony_ci
200513498266Sopenharmony_ci  conn->bits.do_more = TRUE;
200613498266Sopenharmony_ci  ftp_state(data, FTP_STOP); /* this phase is completed */
200713498266Sopenharmony_ci
200813498266Sopenharmony_ci  return result;
200913498266Sopenharmony_ci}
201013498266Sopenharmony_ci
201113498266Sopenharmony_cistatic CURLcode ftp_state_port_resp(struct Curl_easy *data,
201213498266Sopenharmony_ci                                    int ftpcode)
201313498266Sopenharmony_ci{
201413498266Sopenharmony_ci  struct connectdata *conn = data->conn;
201513498266Sopenharmony_ci  struct ftp_conn *ftpc = &conn->proto.ftpc;
201613498266Sopenharmony_ci  ftpport fcmd = (ftpport)ftpc->count1;
201713498266Sopenharmony_ci  CURLcode result = CURLE_OK;
201813498266Sopenharmony_ci
201913498266Sopenharmony_ci  /* The FTP spec tells a positive response should have code 200.
202013498266Sopenharmony_ci     Be more permissive here to tolerate deviant servers. */
202113498266Sopenharmony_ci  if(ftpcode / 100 != 2) {
202213498266Sopenharmony_ci    /* the command failed */
202313498266Sopenharmony_ci
202413498266Sopenharmony_ci    if(EPRT == fcmd) {
202513498266Sopenharmony_ci      infof(data, "disabling EPRT usage");
202613498266Sopenharmony_ci      conn->bits.ftp_use_eprt = FALSE;
202713498266Sopenharmony_ci    }
202813498266Sopenharmony_ci    fcmd++;
202913498266Sopenharmony_ci
203013498266Sopenharmony_ci    if(fcmd == DONE) {
203113498266Sopenharmony_ci      failf(data, "Failed to do PORT");
203213498266Sopenharmony_ci      result = CURLE_FTP_PORT_FAILED;
203313498266Sopenharmony_ci    }
203413498266Sopenharmony_ci    else
203513498266Sopenharmony_ci      /* try next */
203613498266Sopenharmony_ci      result = ftp_state_use_port(data, fcmd);
203713498266Sopenharmony_ci  }
203813498266Sopenharmony_ci  else {
203913498266Sopenharmony_ci    infof(data, "Connect data stream actively");
204013498266Sopenharmony_ci    ftp_state(data, FTP_STOP); /* end of DO phase */
204113498266Sopenharmony_ci    result = ftp_dophase_done(data, FALSE);
204213498266Sopenharmony_ci  }
204313498266Sopenharmony_ci
204413498266Sopenharmony_ci  return result;
204513498266Sopenharmony_ci}
204613498266Sopenharmony_ci
204713498266Sopenharmony_cistatic int twodigit(const char *p)
204813498266Sopenharmony_ci{
204913498266Sopenharmony_ci  return (p[0]-'0') * 10 + (p[1]-'0');
205013498266Sopenharmony_ci}
205113498266Sopenharmony_ci
205213498266Sopenharmony_cistatic bool ftp_213_date(const char *p, int *year, int *month, int *day,
205313498266Sopenharmony_ci                         int *hour, int *minute, int *second)
205413498266Sopenharmony_ci{
205513498266Sopenharmony_ci  size_t len = strlen(p);
205613498266Sopenharmony_ci  if(len < 14)
205713498266Sopenharmony_ci    return FALSE;
205813498266Sopenharmony_ci  *year = twodigit(&p[0]) * 100 + twodigit(&p[2]);
205913498266Sopenharmony_ci  *month = twodigit(&p[4]);
206013498266Sopenharmony_ci  *day = twodigit(&p[6]);
206113498266Sopenharmony_ci  *hour = twodigit(&p[8]);
206213498266Sopenharmony_ci  *minute = twodigit(&p[10]);
206313498266Sopenharmony_ci  *second = twodigit(&p[12]);
206413498266Sopenharmony_ci
206513498266Sopenharmony_ci  if((*month > 12) || (*day > 31) || (*hour > 23) || (*minute > 59) ||
206613498266Sopenharmony_ci     (*second > 60))
206713498266Sopenharmony_ci    return FALSE;
206813498266Sopenharmony_ci  return TRUE;
206913498266Sopenharmony_ci}
207013498266Sopenharmony_ci
207113498266Sopenharmony_cistatic CURLcode client_write_header(struct Curl_easy *data,
207213498266Sopenharmony_ci                                    char *buf, size_t blen)
207313498266Sopenharmony_ci{
207413498266Sopenharmony_ci  /* Some replies from an FTP server are written to the client
207513498266Sopenharmony_ci   * as CLIENTWRITE_HEADER, formatted as if they came from a
207613498266Sopenharmony_ci   * HTTP conversation.
207713498266Sopenharmony_ci   * In all protocols, CLIENTWRITE_HEADER data is only passed to
207813498266Sopenharmony_ci   * the body write callback when data->set.include_header is set
207913498266Sopenharmony_ci   * via CURLOPT_HEADER.
208013498266Sopenharmony_ci   * For historic reasons, FTP never played this game and expects
208113498266Sopenharmony_ci   * all its HEADERs to do that always. Set that flag during the
208213498266Sopenharmony_ci   * call to Curl_client_write() so it does the right thing.
208313498266Sopenharmony_ci   *
208413498266Sopenharmony_ci   * Notice that we cannot enable this flag for FTP in general,
208513498266Sopenharmony_ci   * as an FTP transfer might involve a HTTP proxy connection and
208613498266Sopenharmony_ci   * headers from CONNECT should not automatically be part of the
208713498266Sopenharmony_ci   * output. */
208813498266Sopenharmony_ci  CURLcode result;
208913498266Sopenharmony_ci  int save = data->set.include_header;
209013498266Sopenharmony_ci  data->set.include_header = TRUE;
209113498266Sopenharmony_ci  result = Curl_client_write(data, CLIENTWRITE_HEADER, buf, blen);
209213498266Sopenharmony_ci  data->set.include_header = save? TRUE:FALSE;
209313498266Sopenharmony_ci  return result;
209413498266Sopenharmony_ci}
209513498266Sopenharmony_ci
209613498266Sopenharmony_cistatic CURLcode ftp_state_mdtm_resp(struct Curl_easy *data,
209713498266Sopenharmony_ci                                    int ftpcode)
209813498266Sopenharmony_ci{
209913498266Sopenharmony_ci  CURLcode result = CURLE_OK;
210013498266Sopenharmony_ci  struct FTP *ftp = data->req.p.ftp;
210113498266Sopenharmony_ci  struct connectdata *conn = data->conn;
210213498266Sopenharmony_ci  struct ftp_conn *ftpc = &conn->proto.ftpc;
210313498266Sopenharmony_ci
210413498266Sopenharmony_ci  switch(ftpcode) {
210513498266Sopenharmony_ci  case 213:
210613498266Sopenharmony_ci    {
210713498266Sopenharmony_ci      /* we got a time. Format should be: "YYYYMMDDHHMMSS[.sss]" where the
210813498266Sopenharmony_ci         last .sss part is optional and means fractions of a second */
210913498266Sopenharmony_ci      int year, month, day, hour, minute, second;
211013498266Sopenharmony_ci      struct pingpong *pp = &ftpc->pp;
211113498266Sopenharmony_ci      char *resp = Curl_dyn_ptr(&pp->recvbuf) + 4;
211213498266Sopenharmony_ci      if(ftp_213_date(resp, &year, &month, &day, &hour, &minute, &second)) {
211313498266Sopenharmony_ci        /* we have a time, reformat it */
211413498266Sopenharmony_ci        char timebuf[24];
211513498266Sopenharmony_ci        msnprintf(timebuf, sizeof(timebuf),
211613498266Sopenharmony_ci                  "%04d%02d%02d %02d:%02d:%02d GMT",
211713498266Sopenharmony_ci                  year, month, day, hour, minute, second);
211813498266Sopenharmony_ci        /* now, convert this into a time() value: */
211913498266Sopenharmony_ci        data->info.filetime = Curl_getdate_capped(timebuf);
212013498266Sopenharmony_ci      }
212113498266Sopenharmony_ci
212213498266Sopenharmony_ci#ifdef CURL_FTP_HTTPSTYLE_HEAD
212313498266Sopenharmony_ci      /* If we asked for a time of the file and we actually got one as well,
212413498266Sopenharmony_ci         we "emulate" an HTTP-style header in our output. */
212513498266Sopenharmony_ci
212613498266Sopenharmony_ci      if(data->req.no_body &&
212713498266Sopenharmony_ci         ftpc->file &&
212813498266Sopenharmony_ci         data->set.get_filetime &&
212913498266Sopenharmony_ci         (data->info.filetime >= 0) ) {
213013498266Sopenharmony_ci        char headerbuf[128];
213113498266Sopenharmony_ci        int headerbuflen;
213213498266Sopenharmony_ci        time_t filetime = data->info.filetime;
213313498266Sopenharmony_ci        struct tm buffer;
213413498266Sopenharmony_ci        const struct tm *tm = &buffer;
213513498266Sopenharmony_ci
213613498266Sopenharmony_ci        result = Curl_gmtime(filetime, &buffer);
213713498266Sopenharmony_ci        if(result)
213813498266Sopenharmony_ci          return result;
213913498266Sopenharmony_ci
214013498266Sopenharmony_ci        /* format: "Tue, 15 Nov 1994 12:45:26" */
214113498266Sopenharmony_ci        headerbuflen = msnprintf(headerbuf, sizeof(headerbuf),
214213498266Sopenharmony_ci                  "Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n",
214313498266Sopenharmony_ci                  Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
214413498266Sopenharmony_ci                  tm->tm_mday,
214513498266Sopenharmony_ci                  Curl_month[tm->tm_mon],
214613498266Sopenharmony_ci                  tm->tm_year + 1900,
214713498266Sopenharmony_ci                  tm->tm_hour,
214813498266Sopenharmony_ci                  tm->tm_min,
214913498266Sopenharmony_ci                  tm->tm_sec);
215013498266Sopenharmony_ci        result = client_write_header(data, headerbuf, headerbuflen);
215113498266Sopenharmony_ci        if(result)
215213498266Sopenharmony_ci          return result;
215313498266Sopenharmony_ci      } /* end of a ridiculous amount of conditionals */
215413498266Sopenharmony_ci#endif
215513498266Sopenharmony_ci    }
215613498266Sopenharmony_ci    break;
215713498266Sopenharmony_ci  default:
215813498266Sopenharmony_ci    infof(data, "unsupported MDTM reply format");
215913498266Sopenharmony_ci    break;
216013498266Sopenharmony_ci  case 550: /* 550 is used for several different problems, e.g.
216113498266Sopenharmony_ci               "No such file or directory" or "Permission denied".
216213498266Sopenharmony_ci               It does not mean that the file does not exist at all. */
216313498266Sopenharmony_ci    infof(data, "MDTM failed: file does not exist or permission problem,"
216413498266Sopenharmony_ci          " continuing");
216513498266Sopenharmony_ci    break;
216613498266Sopenharmony_ci  }
216713498266Sopenharmony_ci
216813498266Sopenharmony_ci  if(data->set.timecondition) {
216913498266Sopenharmony_ci    if((data->info.filetime > 0) && (data->set.timevalue > 0)) {
217013498266Sopenharmony_ci      switch(data->set.timecondition) {
217113498266Sopenharmony_ci      case CURL_TIMECOND_IFMODSINCE:
217213498266Sopenharmony_ci      default:
217313498266Sopenharmony_ci        if(data->info.filetime <= data->set.timevalue) {
217413498266Sopenharmony_ci          infof(data, "The requested document is not new enough");
217513498266Sopenharmony_ci          ftp->transfer = PPTRANSFER_NONE; /* mark to not transfer data */
217613498266Sopenharmony_ci          data->info.timecond = TRUE;
217713498266Sopenharmony_ci          ftp_state(data, FTP_STOP);
217813498266Sopenharmony_ci          return CURLE_OK;
217913498266Sopenharmony_ci        }
218013498266Sopenharmony_ci        break;
218113498266Sopenharmony_ci      case CURL_TIMECOND_IFUNMODSINCE:
218213498266Sopenharmony_ci        if(data->info.filetime > data->set.timevalue) {
218313498266Sopenharmony_ci          infof(data, "The requested document is not old enough");
218413498266Sopenharmony_ci          ftp->transfer = PPTRANSFER_NONE; /* mark to not transfer data */
218513498266Sopenharmony_ci          data->info.timecond = TRUE;
218613498266Sopenharmony_ci          ftp_state(data, FTP_STOP);
218713498266Sopenharmony_ci          return CURLE_OK;
218813498266Sopenharmony_ci        }
218913498266Sopenharmony_ci        break;
219013498266Sopenharmony_ci      } /* switch */
219113498266Sopenharmony_ci    }
219213498266Sopenharmony_ci    else {
219313498266Sopenharmony_ci      infof(data, "Skipping time comparison");
219413498266Sopenharmony_ci    }
219513498266Sopenharmony_ci  }
219613498266Sopenharmony_ci
219713498266Sopenharmony_ci  if(!result)
219813498266Sopenharmony_ci    result = ftp_state_type(data);
219913498266Sopenharmony_ci
220013498266Sopenharmony_ci  return result;
220113498266Sopenharmony_ci}
220213498266Sopenharmony_ci
220313498266Sopenharmony_cistatic CURLcode ftp_state_type_resp(struct Curl_easy *data,
220413498266Sopenharmony_ci                                    int ftpcode,
220513498266Sopenharmony_ci                                    ftpstate instate)
220613498266Sopenharmony_ci{
220713498266Sopenharmony_ci  CURLcode result = CURLE_OK;
220813498266Sopenharmony_ci  struct connectdata *conn = data->conn;
220913498266Sopenharmony_ci
221013498266Sopenharmony_ci  if(ftpcode/100 != 2) {
221113498266Sopenharmony_ci    /* "sasserftpd" and "(u)r(x)bot ftpd" both responds with 226 after a
221213498266Sopenharmony_ci       successful 'TYPE I'. While that is not as RFC959 says, it is still a
221313498266Sopenharmony_ci       positive response code and we allow that. */
221413498266Sopenharmony_ci    failf(data, "Couldn't set desired mode");
221513498266Sopenharmony_ci    return CURLE_FTP_COULDNT_SET_TYPE;
221613498266Sopenharmony_ci  }
221713498266Sopenharmony_ci  if(ftpcode != 200)
221813498266Sopenharmony_ci    infof(data, "Got a %03d response code instead of the assumed 200",
221913498266Sopenharmony_ci          ftpcode);
222013498266Sopenharmony_ci
222113498266Sopenharmony_ci  if(instate == FTP_TYPE)
222213498266Sopenharmony_ci    result = ftp_state_size(data, conn);
222313498266Sopenharmony_ci  else if(instate == FTP_LIST_TYPE)
222413498266Sopenharmony_ci    result = ftp_state_list(data);
222513498266Sopenharmony_ci  else if(instate == FTP_RETR_TYPE)
222613498266Sopenharmony_ci    result = ftp_state_retr_prequote(data);
222713498266Sopenharmony_ci  else if(instate == FTP_STOR_TYPE)
222813498266Sopenharmony_ci    result = ftp_state_stor_prequote(data);
222913498266Sopenharmony_ci
223013498266Sopenharmony_ci  return result;
223113498266Sopenharmony_ci}
223213498266Sopenharmony_ci
223313498266Sopenharmony_cistatic CURLcode ftp_state_retr(struct Curl_easy *data,
223413498266Sopenharmony_ci                               curl_off_t filesize)
223513498266Sopenharmony_ci{
223613498266Sopenharmony_ci  CURLcode result = CURLE_OK;
223713498266Sopenharmony_ci  struct FTP *ftp = data->req.p.ftp;
223813498266Sopenharmony_ci  struct connectdata *conn = data->conn;
223913498266Sopenharmony_ci  struct ftp_conn *ftpc = &conn->proto.ftpc;
224013498266Sopenharmony_ci
224113498266Sopenharmony_ci  DEBUGF(infof(data, "ftp_state_retr()"));
224213498266Sopenharmony_ci  if(data->set.max_filesize && (filesize > data->set.max_filesize)) {
224313498266Sopenharmony_ci    failf(data, "Maximum file size exceeded");
224413498266Sopenharmony_ci    return CURLE_FILESIZE_EXCEEDED;
224513498266Sopenharmony_ci  }
224613498266Sopenharmony_ci  ftp->downloadsize = filesize;
224713498266Sopenharmony_ci
224813498266Sopenharmony_ci  if(data->state.resume_from) {
224913498266Sopenharmony_ci    /* We always (attempt to) get the size of downloads, so it is done before
225013498266Sopenharmony_ci       this even when not doing resumes. */
225113498266Sopenharmony_ci    if(filesize == -1) {
225213498266Sopenharmony_ci      infof(data, "ftp server doesn't support SIZE");
225313498266Sopenharmony_ci      /* We couldn't get the size and therefore we can't know if there really
225413498266Sopenharmony_ci         is a part of the file left to get, although the server will just
225513498266Sopenharmony_ci         close the connection when we start the connection so it won't cause
225613498266Sopenharmony_ci         us any harm, just not make us exit as nicely. */
225713498266Sopenharmony_ci    }
225813498266Sopenharmony_ci    else {
225913498266Sopenharmony_ci      /* We got a file size report, so we check that there actually is a
226013498266Sopenharmony_ci         part of the file left to get, or else we go home.  */
226113498266Sopenharmony_ci      if(data->state.resume_from< 0) {
226213498266Sopenharmony_ci        /* We're supposed to download the last abs(from) bytes */
226313498266Sopenharmony_ci        if(filesize < -data->state.resume_from) {
226413498266Sopenharmony_ci          failf(data, "Offset (%" CURL_FORMAT_CURL_OFF_T
226513498266Sopenharmony_ci                ") was beyond file size (%" CURL_FORMAT_CURL_OFF_T ")",
226613498266Sopenharmony_ci                data->state.resume_from, filesize);
226713498266Sopenharmony_ci          return CURLE_BAD_DOWNLOAD_RESUME;
226813498266Sopenharmony_ci        }
226913498266Sopenharmony_ci        /* convert to size to download */
227013498266Sopenharmony_ci        ftp->downloadsize = -data->state.resume_from;
227113498266Sopenharmony_ci        /* download from where? */
227213498266Sopenharmony_ci        data->state.resume_from = filesize - ftp->downloadsize;
227313498266Sopenharmony_ci      }
227413498266Sopenharmony_ci      else {
227513498266Sopenharmony_ci        if(filesize < data->state.resume_from) {
227613498266Sopenharmony_ci          failf(data, "Offset (%" CURL_FORMAT_CURL_OFF_T
227713498266Sopenharmony_ci                ") was beyond file size (%" CURL_FORMAT_CURL_OFF_T ")",
227813498266Sopenharmony_ci                data->state.resume_from, filesize);
227913498266Sopenharmony_ci          return CURLE_BAD_DOWNLOAD_RESUME;
228013498266Sopenharmony_ci        }
228113498266Sopenharmony_ci        /* Now store the number of bytes we are expected to download */
228213498266Sopenharmony_ci        ftp->downloadsize = filesize-data->state.resume_from;
228313498266Sopenharmony_ci      }
228413498266Sopenharmony_ci    }
228513498266Sopenharmony_ci
228613498266Sopenharmony_ci    if(ftp->downloadsize == 0) {
228713498266Sopenharmony_ci      /* no data to transfer */
228813498266Sopenharmony_ci      Curl_setup_transfer(data, -1, -1, FALSE, -1);
228913498266Sopenharmony_ci      infof(data, "File already completely downloaded");
229013498266Sopenharmony_ci
229113498266Sopenharmony_ci      /* Set ->transfer so that we won't get any error in ftp_done()
229213498266Sopenharmony_ci       * because we didn't transfer the any file */
229313498266Sopenharmony_ci      ftp->transfer = PPTRANSFER_NONE;
229413498266Sopenharmony_ci      ftp_state(data, FTP_STOP);
229513498266Sopenharmony_ci      return CURLE_OK;
229613498266Sopenharmony_ci    }
229713498266Sopenharmony_ci
229813498266Sopenharmony_ci    /* Set resume file transfer offset */
229913498266Sopenharmony_ci    infof(data, "Instructs server to resume from offset %"
230013498266Sopenharmony_ci          CURL_FORMAT_CURL_OFF_T, data->state.resume_from);
230113498266Sopenharmony_ci
230213498266Sopenharmony_ci    result = Curl_pp_sendf(data, &ftpc->pp, "REST %" CURL_FORMAT_CURL_OFF_T,
230313498266Sopenharmony_ci                           data->state.resume_from);
230413498266Sopenharmony_ci    if(!result)
230513498266Sopenharmony_ci      ftp_state(data, FTP_RETR_REST);
230613498266Sopenharmony_ci  }
230713498266Sopenharmony_ci  else {
230813498266Sopenharmony_ci    /* no resume */
230913498266Sopenharmony_ci    result = Curl_pp_sendf(data, &ftpc->pp, "RETR %s", ftpc->file);
231013498266Sopenharmony_ci    if(!result)
231113498266Sopenharmony_ci      ftp_state(data, FTP_RETR);
231213498266Sopenharmony_ci  }
231313498266Sopenharmony_ci
231413498266Sopenharmony_ci  return result;
231513498266Sopenharmony_ci}
231613498266Sopenharmony_ci
231713498266Sopenharmony_cistatic CURLcode ftp_state_size_resp(struct Curl_easy *data,
231813498266Sopenharmony_ci                                    int ftpcode,
231913498266Sopenharmony_ci                                    ftpstate instate)
232013498266Sopenharmony_ci{
232113498266Sopenharmony_ci  CURLcode result = CURLE_OK;
232213498266Sopenharmony_ci  curl_off_t filesize = -1;
232313498266Sopenharmony_ci  char *buf = Curl_dyn_ptr(&data->conn->proto.ftpc.pp.recvbuf);
232413498266Sopenharmony_ci  size_t len = data->conn->proto.ftpc.pp.nfinal;
232513498266Sopenharmony_ci
232613498266Sopenharmony_ci  /* get the size from the ascii string: */
232713498266Sopenharmony_ci  if(ftpcode == 213) {
232813498266Sopenharmony_ci    /* To allow servers to prepend "rubbish" in the response string, we scan
232913498266Sopenharmony_ci       for all the digits at the end of the response and parse only those as a
233013498266Sopenharmony_ci       number. */
233113498266Sopenharmony_ci    char *start = &buf[4];
233213498266Sopenharmony_ci    char *fdigit = memchr(start, '\r', len);
233313498266Sopenharmony_ci    if(fdigit) {
233413498266Sopenharmony_ci      fdigit--;
233513498266Sopenharmony_ci      if(*fdigit == '\n')
233613498266Sopenharmony_ci        fdigit--;
233713498266Sopenharmony_ci      while(ISDIGIT(fdigit[-1]) && (fdigit > start))
233813498266Sopenharmony_ci        fdigit--;
233913498266Sopenharmony_ci    }
234013498266Sopenharmony_ci    else
234113498266Sopenharmony_ci      fdigit = start;
234213498266Sopenharmony_ci    /* ignores parsing errors, which will make the size remain unknown */
234313498266Sopenharmony_ci    (void)curlx_strtoofft(fdigit, NULL, 10, &filesize);
234413498266Sopenharmony_ci
234513498266Sopenharmony_ci  }
234613498266Sopenharmony_ci  else if(ftpcode == 550) { /* "No such file or directory" */
234713498266Sopenharmony_ci    /* allow a SIZE failure for (resumed) uploads, when probing what command
234813498266Sopenharmony_ci       to use */
234913498266Sopenharmony_ci    if(instate != FTP_STOR_SIZE) {
235013498266Sopenharmony_ci      failf(data, "The file does not exist");
235113498266Sopenharmony_ci      return CURLE_REMOTE_FILE_NOT_FOUND;
235213498266Sopenharmony_ci    }
235313498266Sopenharmony_ci  }
235413498266Sopenharmony_ci
235513498266Sopenharmony_ci  if(instate == FTP_SIZE) {
235613498266Sopenharmony_ci#ifdef CURL_FTP_HTTPSTYLE_HEAD
235713498266Sopenharmony_ci    if(-1 != filesize) {
235813498266Sopenharmony_ci      char clbuf[128];
235913498266Sopenharmony_ci      int clbuflen = msnprintf(clbuf, sizeof(clbuf),
236013498266Sopenharmony_ci                "Content-Length: %" CURL_FORMAT_CURL_OFF_T "\r\n", filesize);
236113498266Sopenharmony_ci      result = client_write_header(data, clbuf, clbuflen);
236213498266Sopenharmony_ci      if(result)
236313498266Sopenharmony_ci        return result;
236413498266Sopenharmony_ci    }
236513498266Sopenharmony_ci#endif
236613498266Sopenharmony_ci    Curl_pgrsSetDownloadSize(data, filesize);
236713498266Sopenharmony_ci    result = ftp_state_rest(data, data->conn);
236813498266Sopenharmony_ci  }
236913498266Sopenharmony_ci  else if(instate == FTP_RETR_SIZE) {
237013498266Sopenharmony_ci    Curl_pgrsSetDownloadSize(data, filesize);
237113498266Sopenharmony_ci    result = ftp_state_retr(data, filesize);
237213498266Sopenharmony_ci  }
237313498266Sopenharmony_ci  else if(instate == FTP_STOR_SIZE) {
237413498266Sopenharmony_ci    data->state.resume_from = filesize;
237513498266Sopenharmony_ci    result = ftp_state_ul_setup(data, TRUE);
237613498266Sopenharmony_ci  }
237713498266Sopenharmony_ci
237813498266Sopenharmony_ci  return result;
237913498266Sopenharmony_ci}
238013498266Sopenharmony_ci
238113498266Sopenharmony_cistatic CURLcode ftp_state_rest_resp(struct Curl_easy *data,
238213498266Sopenharmony_ci                                    struct connectdata *conn,
238313498266Sopenharmony_ci                                    int ftpcode,
238413498266Sopenharmony_ci                                    ftpstate instate)
238513498266Sopenharmony_ci{
238613498266Sopenharmony_ci  CURLcode result = CURLE_OK;
238713498266Sopenharmony_ci  struct ftp_conn *ftpc = &conn->proto.ftpc;
238813498266Sopenharmony_ci
238913498266Sopenharmony_ci  switch(instate) {
239013498266Sopenharmony_ci  case FTP_REST:
239113498266Sopenharmony_ci  default:
239213498266Sopenharmony_ci#ifdef CURL_FTP_HTTPSTYLE_HEAD
239313498266Sopenharmony_ci    if(ftpcode == 350) {
239413498266Sopenharmony_ci      char buffer[24]= { "Accept-ranges: bytes\r\n" };
239513498266Sopenharmony_ci      result = client_write_header(data, buffer, strlen(buffer));
239613498266Sopenharmony_ci      if(result)
239713498266Sopenharmony_ci        return result;
239813498266Sopenharmony_ci    }
239913498266Sopenharmony_ci#endif
240013498266Sopenharmony_ci    result = ftp_state_prepare_transfer(data);
240113498266Sopenharmony_ci    break;
240213498266Sopenharmony_ci
240313498266Sopenharmony_ci  case FTP_RETR_REST:
240413498266Sopenharmony_ci    if(ftpcode != 350) {
240513498266Sopenharmony_ci      failf(data, "Couldn't use REST");
240613498266Sopenharmony_ci      result = CURLE_FTP_COULDNT_USE_REST;
240713498266Sopenharmony_ci    }
240813498266Sopenharmony_ci    else {
240913498266Sopenharmony_ci      result = Curl_pp_sendf(data, &ftpc->pp, "RETR %s", ftpc->file);
241013498266Sopenharmony_ci      if(!result)
241113498266Sopenharmony_ci        ftp_state(data, FTP_RETR);
241213498266Sopenharmony_ci    }
241313498266Sopenharmony_ci    break;
241413498266Sopenharmony_ci  }
241513498266Sopenharmony_ci
241613498266Sopenharmony_ci  return result;
241713498266Sopenharmony_ci}
241813498266Sopenharmony_ci
241913498266Sopenharmony_cistatic CURLcode ftp_state_stor_resp(struct Curl_easy *data,
242013498266Sopenharmony_ci                                    int ftpcode, ftpstate instate)
242113498266Sopenharmony_ci{
242213498266Sopenharmony_ci  CURLcode result = CURLE_OK;
242313498266Sopenharmony_ci  struct connectdata *conn = data->conn;
242413498266Sopenharmony_ci
242513498266Sopenharmony_ci  if(ftpcode >= 400) {
242613498266Sopenharmony_ci    failf(data, "Failed FTP upload: %0d", ftpcode);
242713498266Sopenharmony_ci    ftp_state(data, FTP_STOP);
242813498266Sopenharmony_ci    /* oops, we never close the sockets! */
242913498266Sopenharmony_ci    return CURLE_UPLOAD_FAILED;
243013498266Sopenharmony_ci  }
243113498266Sopenharmony_ci
243213498266Sopenharmony_ci  conn->proto.ftpc.state_saved = instate;
243313498266Sopenharmony_ci
243413498266Sopenharmony_ci  /* PORT means we are now awaiting the server to connect to us. */
243513498266Sopenharmony_ci  if(data->set.ftp_use_port) {
243613498266Sopenharmony_ci    bool connected;
243713498266Sopenharmony_ci
243813498266Sopenharmony_ci    ftp_state(data, FTP_STOP); /* no longer in STOR state */
243913498266Sopenharmony_ci
244013498266Sopenharmony_ci    result = AllowServerConnect(data, &connected);
244113498266Sopenharmony_ci    if(result)
244213498266Sopenharmony_ci      return result;
244313498266Sopenharmony_ci
244413498266Sopenharmony_ci    if(!connected) {
244513498266Sopenharmony_ci      struct ftp_conn *ftpc = &conn->proto.ftpc;
244613498266Sopenharmony_ci      infof(data, "Data conn was not available immediately");
244713498266Sopenharmony_ci      ftpc->wait_data_conn = TRUE;
244813498266Sopenharmony_ci    }
244913498266Sopenharmony_ci
245013498266Sopenharmony_ci    return CURLE_OK;
245113498266Sopenharmony_ci  }
245213498266Sopenharmony_ci  return InitiateTransfer(data);
245313498266Sopenharmony_ci}
245413498266Sopenharmony_ci
245513498266Sopenharmony_ci/* for LIST and RETR responses */
245613498266Sopenharmony_cistatic CURLcode ftp_state_get_resp(struct Curl_easy *data,
245713498266Sopenharmony_ci                                   int ftpcode,
245813498266Sopenharmony_ci                                   ftpstate instate)
245913498266Sopenharmony_ci{
246013498266Sopenharmony_ci  CURLcode result = CURLE_OK;
246113498266Sopenharmony_ci  struct FTP *ftp = data->req.p.ftp;
246213498266Sopenharmony_ci  struct connectdata *conn = data->conn;
246313498266Sopenharmony_ci
246413498266Sopenharmony_ci  if((ftpcode == 150) || (ftpcode == 125)) {
246513498266Sopenharmony_ci
246613498266Sopenharmony_ci    /*
246713498266Sopenharmony_ci      A;
246813498266Sopenharmony_ci      150 Opening BINARY mode data connection for /etc/passwd (2241
246913498266Sopenharmony_ci      bytes).  (ok, the file is being transferred)
247013498266Sopenharmony_ci
247113498266Sopenharmony_ci      B:
247213498266Sopenharmony_ci      150 Opening ASCII mode data connection for /bin/ls
247313498266Sopenharmony_ci
247413498266Sopenharmony_ci      C:
247513498266Sopenharmony_ci      150 ASCII data connection for /bin/ls (137.167.104.91,37445) (0 bytes).
247613498266Sopenharmony_ci
247713498266Sopenharmony_ci      D:
247813498266Sopenharmony_ci      150 Opening ASCII mode data connection for [file] (0.0.0.0,0) (545 bytes)
247913498266Sopenharmony_ci
248013498266Sopenharmony_ci      E:
248113498266Sopenharmony_ci      125 Data connection already open; Transfer starting. */
248213498266Sopenharmony_ci
248313498266Sopenharmony_ci    curl_off_t size = -1; /* default unknown size */
248413498266Sopenharmony_ci
248513498266Sopenharmony_ci
248613498266Sopenharmony_ci    /*
248713498266Sopenharmony_ci     * It appears that there are FTP-servers that return size 0 for files when
248813498266Sopenharmony_ci     * SIZE is used on the file while being in BINARY mode. To work around
248913498266Sopenharmony_ci     * that (stupid) behavior, we attempt to parse the RETR response even if
249013498266Sopenharmony_ci     * the SIZE returned size zero.
249113498266Sopenharmony_ci     *
249213498266Sopenharmony_ci     * Debugging help from Salvatore Sorrentino on February 26, 2003.
249313498266Sopenharmony_ci     */
249413498266Sopenharmony_ci
249513498266Sopenharmony_ci    if((instate != FTP_LIST) &&
249613498266Sopenharmony_ci       !data->state.prefer_ascii &&
249713498266Sopenharmony_ci       !data->set.ignorecl &&
249813498266Sopenharmony_ci       (ftp->downloadsize < 1)) {
249913498266Sopenharmony_ci      /*
250013498266Sopenharmony_ci       * It seems directory listings either don't show the size or very
250113498266Sopenharmony_ci       * often uses size 0 anyway. ASCII transfers may very well turn out
250213498266Sopenharmony_ci       * that the transferred amount of data is not the same as this line
250313498266Sopenharmony_ci       * tells, why using this number in those cases only confuses us.
250413498266Sopenharmony_ci       *
250513498266Sopenharmony_ci       * Example D above makes this parsing a little tricky */
250613498266Sopenharmony_ci      char *bytes;
250713498266Sopenharmony_ci      char *buf = Curl_dyn_ptr(&conn->proto.ftpc.pp.recvbuf);
250813498266Sopenharmony_ci      bytes = strstr(buf, " bytes");
250913498266Sopenharmony_ci      if(bytes) {
251013498266Sopenharmony_ci        long in = (long)(--bytes-buf);
251113498266Sopenharmony_ci        /* this is a hint there is size information in there! ;-) */
251213498266Sopenharmony_ci        while(--in) {
251313498266Sopenharmony_ci          /* scan for the left parenthesis and break there */
251413498266Sopenharmony_ci          if('(' == *bytes)
251513498266Sopenharmony_ci            break;
251613498266Sopenharmony_ci          /* skip only digits */
251713498266Sopenharmony_ci          if(!ISDIGIT(*bytes)) {
251813498266Sopenharmony_ci            bytes = NULL;
251913498266Sopenharmony_ci            break;
252013498266Sopenharmony_ci          }
252113498266Sopenharmony_ci          /* one more estep backwards */
252213498266Sopenharmony_ci          bytes--;
252313498266Sopenharmony_ci        }
252413498266Sopenharmony_ci        /* if we have nothing but digits: */
252513498266Sopenharmony_ci        if(bytes) {
252613498266Sopenharmony_ci          ++bytes;
252713498266Sopenharmony_ci          /* get the number! */
252813498266Sopenharmony_ci          (void)curlx_strtoofft(bytes, NULL, 10, &size);
252913498266Sopenharmony_ci        }
253013498266Sopenharmony_ci      }
253113498266Sopenharmony_ci    }
253213498266Sopenharmony_ci    else if(ftp->downloadsize > -1)
253313498266Sopenharmony_ci      size = ftp->downloadsize;
253413498266Sopenharmony_ci
253513498266Sopenharmony_ci    if(size > data->req.maxdownload && data->req.maxdownload > 0)
253613498266Sopenharmony_ci      size = data->req.size = data->req.maxdownload;
253713498266Sopenharmony_ci    else if((instate != FTP_LIST) && (data->state.prefer_ascii))
253813498266Sopenharmony_ci      size = -1; /* kludge for servers that understate ASCII mode file size */
253913498266Sopenharmony_ci
254013498266Sopenharmony_ci    infof(data, "Maxdownload = %" CURL_FORMAT_CURL_OFF_T,
254113498266Sopenharmony_ci          data->req.maxdownload);
254213498266Sopenharmony_ci
254313498266Sopenharmony_ci    if(instate != FTP_LIST)
254413498266Sopenharmony_ci      infof(data, "Getting file with size: %" CURL_FORMAT_CURL_OFF_T,
254513498266Sopenharmony_ci            size);
254613498266Sopenharmony_ci
254713498266Sopenharmony_ci    /* FTP download: */
254813498266Sopenharmony_ci    conn->proto.ftpc.state_saved = instate;
254913498266Sopenharmony_ci    conn->proto.ftpc.retr_size_saved = size;
255013498266Sopenharmony_ci
255113498266Sopenharmony_ci    if(data->set.ftp_use_port) {
255213498266Sopenharmony_ci      bool connected;
255313498266Sopenharmony_ci
255413498266Sopenharmony_ci      result = AllowServerConnect(data, &connected);
255513498266Sopenharmony_ci      if(result)
255613498266Sopenharmony_ci        return result;
255713498266Sopenharmony_ci
255813498266Sopenharmony_ci      if(!connected) {
255913498266Sopenharmony_ci        struct ftp_conn *ftpc = &conn->proto.ftpc;
256013498266Sopenharmony_ci        infof(data, "Data conn was not available immediately");
256113498266Sopenharmony_ci        ftp_state(data, FTP_STOP);
256213498266Sopenharmony_ci        ftpc->wait_data_conn = TRUE;
256313498266Sopenharmony_ci      }
256413498266Sopenharmony_ci    }
256513498266Sopenharmony_ci    else
256613498266Sopenharmony_ci      return InitiateTransfer(data);
256713498266Sopenharmony_ci  }
256813498266Sopenharmony_ci  else {
256913498266Sopenharmony_ci    if((instate == FTP_LIST) && (ftpcode == 450)) {
257013498266Sopenharmony_ci      /* simply no matching files in the dir listing */
257113498266Sopenharmony_ci      ftp->transfer = PPTRANSFER_NONE; /* don't download anything */
257213498266Sopenharmony_ci      ftp_state(data, FTP_STOP); /* this phase is over */
257313498266Sopenharmony_ci    }
257413498266Sopenharmony_ci    else {
257513498266Sopenharmony_ci      failf(data, "RETR response: %03d", ftpcode);
257613498266Sopenharmony_ci      return instate == FTP_RETR && ftpcode == 550?
257713498266Sopenharmony_ci        CURLE_REMOTE_FILE_NOT_FOUND:
257813498266Sopenharmony_ci        CURLE_FTP_COULDNT_RETR_FILE;
257913498266Sopenharmony_ci    }
258013498266Sopenharmony_ci  }
258113498266Sopenharmony_ci
258213498266Sopenharmony_ci  return result;
258313498266Sopenharmony_ci}
258413498266Sopenharmony_ci
258513498266Sopenharmony_ci/* after USER, PASS and ACCT */
258613498266Sopenharmony_cistatic CURLcode ftp_state_loggedin(struct Curl_easy *data)
258713498266Sopenharmony_ci{
258813498266Sopenharmony_ci  CURLcode result = CURLE_OK;
258913498266Sopenharmony_ci  struct connectdata *conn = data->conn;
259013498266Sopenharmony_ci
259113498266Sopenharmony_ci  if(conn->bits.ftp_use_control_ssl) {
259213498266Sopenharmony_ci    /* PBSZ = PROTECTION BUFFER SIZE.
259313498266Sopenharmony_ci
259413498266Sopenharmony_ci    The 'draft-murray-auth-ftp-ssl' (draft 12, page 7) says:
259513498266Sopenharmony_ci
259613498266Sopenharmony_ci    Specifically, the PROT command MUST be preceded by a PBSZ
259713498266Sopenharmony_ci    command and a PBSZ command MUST be preceded by a successful
259813498266Sopenharmony_ci    security data exchange (the TLS negotiation in this case)
259913498266Sopenharmony_ci
260013498266Sopenharmony_ci    ... (and on page 8):
260113498266Sopenharmony_ci
260213498266Sopenharmony_ci    Thus the PBSZ command must still be issued, but must have a
260313498266Sopenharmony_ci    parameter of '0' to indicate that no buffering is taking place
260413498266Sopenharmony_ci    and the data connection should not be encapsulated.
260513498266Sopenharmony_ci    */
260613498266Sopenharmony_ci    result = Curl_pp_sendf(data, &conn->proto.ftpc.pp, "PBSZ %d", 0);
260713498266Sopenharmony_ci    if(!result)
260813498266Sopenharmony_ci      ftp_state(data, FTP_PBSZ);
260913498266Sopenharmony_ci  }
261013498266Sopenharmony_ci  else {
261113498266Sopenharmony_ci    result = ftp_state_pwd(data, conn);
261213498266Sopenharmony_ci  }
261313498266Sopenharmony_ci  return result;
261413498266Sopenharmony_ci}
261513498266Sopenharmony_ci
261613498266Sopenharmony_ci/* for USER and PASS responses */
261713498266Sopenharmony_cistatic CURLcode ftp_state_user_resp(struct Curl_easy *data,
261813498266Sopenharmony_ci                                    int ftpcode)
261913498266Sopenharmony_ci{
262013498266Sopenharmony_ci  CURLcode result = CURLE_OK;
262113498266Sopenharmony_ci  struct connectdata *conn = data->conn;
262213498266Sopenharmony_ci  struct ftp_conn *ftpc = &conn->proto.ftpc;
262313498266Sopenharmony_ci
262413498266Sopenharmony_ci  /* some need password anyway, and others just return 2xx ignored */
262513498266Sopenharmony_ci  if((ftpcode == 331) && (ftpc->state == FTP_USER)) {
262613498266Sopenharmony_ci    /* 331 Password required for ...
262713498266Sopenharmony_ci       (the server requires to send the user's password too) */
262813498266Sopenharmony_ci    result = Curl_pp_sendf(data, &ftpc->pp, "PASS %s",
262913498266Sopenharmony_ci                           conn->passwd?conn->passwd:"");
263013498266Sopenharmony_ci    if(!result)
263113498266Sopenharmony_ci      ftp_state(data, FTP_PASS);
263213498266Sopenharmony_ci  }
263313498266Sopenharmony_ci  else if(ftpcode/100 == 2) {
263413498266Sopenharmony_ci    /* 230 User ... logged in.
263513498266Sopenharmony_ci       (the user logged in with or without password) */
263613498266Sopenharmony_ci    result = ftp_state_loggedin(data);
263713498266Sopenharmony_ci  }
263813498266Sopenharmony_ci  else if(ftpcode == 332) {
263913498266Sopenharmony_ci    if(data->set.str[STRING_FTP_ACCOUNT]) {
264013498266Sopenharmony_ci      result = Curl_pp_sendf(data, &ftpc->pp, "ACCT %s",
264113498266Sopenharmony_ci                             data->set.str[STRING_FTP_ACCOUNT]);
264213498266Sopenharmony_ci      if(!result)
264313498266Sopenharmony_ci        ftp_state(data, FTP_ACCT);
264413498266Sopenharmony_ci    }
264513498266Sopenharmony_ci    else {
264613498266Sopenharmony_ci      failf(data, "ACCT requested but none available");
264713498266Sopenharmony_ci      result = CURLE_LOGIN_DENIED;
264813498266Sopenharmony_ci    }
264913498266Sopenharmony_ci  }
265013498266Sopenharmony_ci  else {
265113498266Sopenharmony_ci    /* All other response codes, like:
265213498266Sopenharmony_ci
265313498266Sopenharmony_ci    530 User ... access denied
265413498266Sopenharmony_ci    (the server denies to log the specified user) */
265513498266Sopenharmony_ci
265613498266Sopenharmony_ci    if(data->set.str[STRING_FTP_ALTERNATIVE_TO_USER] &&
265713498266Sopenharmony_ci       !ftpc->ftp_trying_alternative) {
265813498266Sopenharmony_ci      /* Ok, USER failed.  Let's try the supplied command. */
265913498266Sopenharmony_ci      result =
266013498266Sopenharmony_ci        Curl_pp_sendf(data, &ftpc->pp, "%s",
266113498266Sopenharmony_ci                      data->set.str[STRING_FTP_ALTERNATIVE_TO_USER]);
266213498266Sopenharmony_ci      if(!result) {
266313498266Sopenharmony_ci        ftpc->ftp_trying_alternative = TRUE;
266413498266Sopenharmony_ci        ftp_state(data, FTP_USER);
266513498266Sopenharmony_ci      }
266613498266Sopenharmony_ci    }
266713498266Sopenharmony_ci    else {
266813498266Sopenharmony_ci      failf(data, "Access denied: %03d", ftpcode);
266913498266Sopenharmony_ci      result = CURLE_LOGIN_DENIED;
267013498266Sopenharmony_ci    }
267113498266Sopenharmony_ci  }
267213498266Sopenharmony_ci  return result;
267313498266Sopenharmony_ci}
267413498266Sopenharmony_ci
267513498266Sopenharmony_ci/* for ACCT response */
267613498266Sopenharmony_cistatic CURLcode ftp_state_acct_resp(struct Curl_easy *data,
267713498266Sopenharmony_ci                                    int ftpcode)
267813498266Sopenharmony_ci{
267913498266Sopenharmony_ci  CURLcode result = CURLE_OK;
268013498266Sopenharmony_ci  if(ftpcode != 230) {
268113498266Sopenharmony_ci    failf(data, "ACCT rejected by server: %03d", ftpcode);
268213498266Sopenharmony_ci    result = CURLE_FTP_WEIRD_PASS_REPLY; /* FIX */
268313498266Sopenharmony_ci  }
268413498266Sopenharmony_ci  else
268513498266Sopenharmony_ci    result = ftp_state_loggedin(data);
268613498266Sopenharmony_ci
268713498266Sopenharmony_ci  return result;
268813498266Sopenharmony_ci}
268913498266Sopenharmony_ci
269013498266Sopenharmony_ci
269113498266Sopenharmony_cistatic CURLcode ftp_statemachine(struct Curl_easy *data,
269213498266Sopenharmony_ci                                 struct connectdata *conn)
269313498266Sopenharmony_ci{
269413498266Sopenharmony_ci  CURLcode result;
269513498266Sopenharmony_ci  curl_socket_t sock = conn->sock[FIRSTSOCKET];
269613498266Sopenharmony_ci  int ftpcode;
269713498266Sopenharmony_ci  struct ftp_conn *ftpc = &conn->proto.ftpc;
269813498266Sopenharmony_ci  struct pingpong *pp = &ftpc->pp;
269913498266Sopenharmony_ci  static const char * const ftpauth[] = { "SSL", "TLS" };
270013498266Sopenharmony_ci  size_t nread = 0;
270113498266Sopenharmony_ci
270213498266Sopenharmony_ci  if(pp->sendleft)
270313498266Sopenharmony_ci    return Curl_pp_flushsend(data, pp);
270413498266Sopenharmony_ci
270513498266Sopenharmony_ci  result = ftp_readresp(data, sock, pp, &ftpcode, &nread);
270613498266Sopenharmony_ci  if(result)
270713498266Sopenharmony_ci    return result;
270813498266Sopenharmony_ci
270913498266Sopenharmony_ci  if(ftpcode) {
271013498266Sopenharmony_ci    /* we have now received a full FTP server response */
271113498266Sopenharmony_ci    switch(ftpc->state) {
271213498266Sopenharmony_ci    case FTP_WAIT220:
271313498266Sopenharmony_ci      if(ftpcode == 230) {
271413498266Sopenharmony_ci        /* 230 User logged in - already! Take as 220 if TLS required. */
271513498266Sopenharmony_ci        if(data->set.use_ssl <= CURLUSESSL_TRY ||
271613498266Sopenharmony_ci           conn->bits.ftp_use_control_ssl)
271713498266Sopenharmony_ci          return ftp_state_user_resp(data, ftpcode);
271813498266Sopenharmony_ci      }
271913498266Sopenharmony_ci      else if(ftpcode != 220) {
272013498266Sopenharmony_ci        failf(data, "Got a %03d ftp-server response when 220 was expected",
272113498266Sopenharmony_ci              ftpcode);
272213498266Sopenharmony_ci        return CURLE_WEIRD_SERVER_REPLY;
272313498266Sopenharmony_ci      }
272413498266Sopenharmony_ci
272513498266Sopenharmony_ci      /* We have received a 220 response fine, now we proceed. */
272613498266Sopenharmony_ci#ifdef HAVE_GSSAPI
272713498266Sopenharmony_ci      if(data->set.krb) {
272813498266Sopenharmony_ci        /* If not anonymous login, try a secure login. Note that this
272913498266Sopenharmony_ci           procedure is still BLOCKING. */
273013498266Sopenharmony_ci
273113498266Sopenharmony_ci        Curl_sec_request_prot(conn, "private");
273213498266Sopenharmony_ci        /* We set private first as default, in case the line below fails to
273313498266Sopenharmony_ci           set a valid level */
273413498266Sopenharmony_ci        Curl_sec_request_prot(conn, data->set.str[STRING_KRB_LEVEL]);
273513498266Sopenharmony_ci
273613498266Sopenharmony_ci        if(Curl_sec_login(data, conn)) {
273713498266Sopenharmony_ci          failf(data, "secure login failed");
273813498266Sopenharmony_ci          return CURLE_WEIRD_SERVER_REPLY;
273913498266Sopenharmony_ci        }
274013498266Sopenharmony_ci        infof(data, "Authentication successful");
274113498266Sopenharmony_ci      }
274213498266Sopenharmony_ci#endif
274313498266Sopenharmony_ci
274413498266Sopenharmony_ci      if(data->set.use_ssl && !conn->bits.ftp_use_control_ssl) {
274513498266Sopenharmony_ci        /* We don't have a SSL/TLS control connection yet, but FTPS is
274613498266Sopenharmony_ci           requested. Try a FTPS connection now */
274713498266Sopenharmony_ci
274813498266Sopenharmony_ci        ftpc->count3 = 0;
274913498266Sopenharmony_ci        switch(data->set.ftpsslauth) {
275013498266Sopenharmony_ci        case CURLFTPAUTH_DEFAULT:
275113498266Sopenharmony_ci        case CURLFTPAUTH_SSL:
275213498266Sopenharmony_ci          ftpc->count2 = 1; /* add one to get next */
275313498266Sopenharmony_ci          ftpc->count1 = 0;
275413498266Sopenharmony_ci          break;
275513498266Sopenharmony_ci        case CURLFTPAUTH_TLS:
275613498266Sopenharmony_ci          ftpc->count2 = -1; /* subtract one to get next */
275713498266Sopenharmony_ci          ftpc->count1 = 1;
275813498266Sopenharmony_ci          break;
275913498266Sopenharmony_ci        default:
276013498266Sopenharmony_ci          failf(data, "unsupported parameter to CURLOPT_FTPSSLAUTH: %d",
276113498266Sopenharmony_ci                (int)data->set.ftpsslauth);
276213498266Sopenharmony_ci          return CURLE_UNKNOWN_OPTION; /* we don't know what to do */
276313498266Sopenharmony_ci        }
276413498266Sopenharmony_ci        result = Curl_pp_sendf(data, &ftpc->pp, "AUTH %s",
276513498266Sopenharmony_ci                               ftpauth[ftpc->count1]);
276613498266Sopenharmony_ci        if(!result)
276713498266Sopenharmony_ci          ftp_state(data, FTP_AUTH);
276813498266Sopenharmony_ci      }
276913498266Sopenharmony_ci      else
277013498266Sopenharmony_ci        result = ftp_state_user(data, conn);
277113498266Sopenharmony_ci      break;
277213498266Sopenharmony_ci
277313498266Sopenharmony_ci    case FTP_AUTH:
277413498266Sopenharmony_ci      /* we have gotten the response to a previous AUTH command */
277513498266Sopenharmony_ci
277613498266Sopenharmony_ci      if(pp->overflow)
277713498266Sopenharmony_ci        return CURLE_WEIRD_SERVER_REPLY; /* Forbid pipelining in response. */
277813498266Sopenharmony_ci
277913498266Sopenharmony_ci      /* RFC2228 (page 5) says:
278013498266Sopenharmony_ci       *
278113498266Sopenharmony_ci       * If the server is willing to accept the named security mechanism,
278213498266Sopenharmony_ci       * and does not require any security data, it must respond with
278313498266Sopenharmony_ci       * reply code 234/334.
278413498266Sopenharmony_ci       */
278513498266Sopenharmony_ci
278613498266Sopenharmony_ci      if((ftpcode == 234) || (ftpcode == 334)) {
278713498266Sopenharmony_ci        /* this was BLOCKING, keep it so for now */
278813498266Sopenharmony_ci        bool done;
278913498266Sopenharmony_ci        if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
279013498266Sopenharmony_ci          result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET);
279113498266Sopenharmony_ci          if(result) {
279213498266Sopenharmony_ci            /* we failed and bail out */
279313498266Sopenharmony_ci            return CURLE_USE_SSL_FAILED;
279413498266Sopenharmony_ci          }
279513498266Sopenharmony_ci        }
279613498266Sopenharmony_ci        result = Curl_conn_connect(data, FIRSTSOCKET, TRUE, &done);
279713498266Sopenharmony_ci        if(!result) {
279813498266Sopenharmony_ci          conn->bits.ftp_use_data_ssl = FALSE; /* clear-text data */
279913498266Sopenharmony_ci          conn->bits.ftp_use_control_ssl = TRUE; /* SSL on control */
280013498266Sopenharmony_ci          result = ftp_state_user(data, conn);
280113498266Sopenharmony_ci        }
280213498266Sopenharmony_ci      }
280313498266Sopenharmony_ci      else if(ftpc->count3 < 1) {
280413498266Sopenharmony_ci        ftpc->count3++;
280513498266Sopenharmony_ci        ftpc->count1 += ftpc->count2; /* get next attempt */
280613498266Sopenharmony_ci        result = Curl_pp_sendf(data, &ftpc->pp, "AUTH %s",
280713498266Sopenharmony_ci                               ftpauth[ftpc->count1]);
280813498266Sopenharmony_ci        /* remain in this same state */
280913498266Sopenharmony_ci      }
281013498266Sopenharmony_ci      else {
281113498266Sopenharmony_ci        if(data->set.use_ssl > CURLUSESSL_TRY)
281213498266Sopenharmony_ci          /* we failed and CURLUSESSL_CONTROL or CURLUSESSL_ALL is set */
281313498266Sopenharmony_ci          result = CURLE_USE_SSL_FAILED;
281413498266Sopenharmony_ci        else
281513498266Sopenharmony_ci          /* ignore the failure and continue */
281613498266Sopenharmony_ci          result = ftp_state_user(data, conn);
281713498266Sopenharmony_ci      }
281813498266Sopenharmony_ci      break;
281913498266Sopenharmony_ci
282013498266Sopenharmony_ci    case FTP_USER:
282113498266Sopenharmony_ci    case FTP_PASS:
282213498266Sopenharmony_ci      result = ftp_state_user_resp(data, ftpcode);
282313498266Sopenharmony_ci      break;
282413498266Sopenharmony_ci
282513498266Sopenharmony_ci    case FTP_ACCT:
282613498266Sopenharmony_ci      result = ftp_state_acct_resp(data, ftpcode);
282713498266Sopenharmony_ci      break;
282813498266Sopenharmony_ci
282913498266Sopenharmony_ci    case FTP_PBSZ:
283013498266Sopenharmony_ci      result =
283113498266Sopenharmony_ci        Curl_pp_sendf(data, &ftpc->pp, "PROT %c",
283213498266Sopenharmony_ci                      data->set.use_ssl == CURLUSESSL_CONTROL ? 'C' : 'P');
283313498266Sopenharmony_ci      if(!result)
283413498266Sopenharmony_ci        ftp_state(data, FTP_PROT);
283513498266Sopenharmony_ci      break;
283613498266Sopenharmony_ci
283713498266Sopenharmony_ci    case FTP_PROT:
283813498266Sopenharmony_ci      if(ftpcode/100 == 2)
283913498266Sopenharmony_ci        /* We have enabled SSL for the data connection! */
284013498266Sopenharmony_ci        conn->bits.ftp_use_data_ssl =
284113498266Sopenharmony_ci          (data->set.use_ssl != CURLUSESSL_CONTROL) ? TRUE : FALSE;
284213498266Sopenharmony_ci      /* FTP servers typically responds with 500 if they decide to reject
284313498266Sopenharmony_ci         our 'P' request */
284413498266Sopenharmony_ci      else if(data->set.use_ssl > CURLUSESSL_CONTROL)
284513498266Sopenharmony_ci        /* we failed and bails out */
284613498266Sopenharmony_ci        return CURLE_USE_SSL_FAILED;
284713498266Sopenharmony_ci
284813498266Sopenharmony_ci      if(data->set.ftp_ccc) {
284913498266Sopenharmony_ci        /* CCC - Clear Command Channel
285013498266Sopenharmony_ci         */
285113498266Sopenharmony_ci        result = Curl_pp_sendf(data, &ftpc->pp, "%s", "CCC");
285213498266Sopenharmony_ci        if(!result)
285313498266Sopenharmony_ci          ftp_state(data, FTP_CCC);
285413498266Sopenharmony_ci      }
285513498266Sopenharmony_ci      else
285613498266Sopenharmony_ci        result = ftp_state_pwd(data, conn);
285713498266Sopenharmony_ci      break;
285813498266Sopenharmony_ci
285913498266Sopenharmony_ci    case FTP_CCC:
286013498266Sopenharmony_ci      if(ftpcode < 500) {
286113498266Sopenharmony_ci        /* First shut down the SSL layer (note: this call will block) */
286213498266Sopenharmony_ci        result = Curl_ssl_cfilter_remove(data, FIRSTSOCKET);
286313498266Sopenharmony_ci
286413498266Sopenharmony_ci        if(result)
286513498266Sopenharmony_ci          failf(data, "Failed to clear the command channel (CCC)");
286613498266Sopenharmony_ci      }
286713498266Sopenharmony_ci      if(!result)
286813498266Sopenharmony_ci        /* Then continue as normal */
286913498266Sopenharmony_ci        result = ftp_state_pwd(data, conn);
287013498266Sopenharmony_ci      break;
287113498266Sopenharmony_ci
287213498266Sopenharmony_ci    case FTP_PWD:
287313498266Sopenharmony_ci      if(ftpcode == 257) {
287413498266Sopenharmony_ci        char *ptr = Curl_dyn_ptr(&pp->recvbuf) + 4; /* start on the first
287513498266Sopenharmony_ci                                                       letter */
287613498266Sopenharmony_ci        bool entry_extracted = FALSE;
287713498266Sopenharmony_ci        struct dynbuf out;
287813498266Sopenharmony_ci        Curl_dyn_init(&out, 1000);
287913498266Sopenharmony_ci
288013498266Sopenharmony_ci        /* Reply format is like
288113498266Sopenharmony_ci           257<space>[rubbish]"<directory-name>"<space><commentary> and the
288213498266Sopenharmony_ci           RFC959 says
288313498266Sopenharmony_ci
288413498266Sopenharmony_ci           The directory name can contain any character; embedded
288513498266Sopenharmony_ci           double-quotes should be escaped by double-quotes (the
288613498266Sopenharmony_ci           "quote-doubling" convention).
288713498266Sopenharmony_ci        */
288813498266Sopenharmony_ci
288913498266Sopenharmony_ci        /* scan for the first double-quote for non-standard responses */
289013498266Sopenharmony_ci        while(*ptr != '\n' && *ptr != '\0' && *ptr != '"')
289113498266Sopenharmony_ci          ptr++;
289213498266Sopenharmony_ci
289313498266Sopenharmony_ci        if('\"' == *ptr) {
289413498266Sopenharmony_ci          /* it started good */
289513498266Sopenharmony_ci          for(ptr++; *ptr; ptr++) {
289613498266Sopenharmony_ci            if('\"' == *ptr) {
289713498266Sopenharmony_ci              if('\"' == ptr[1]) {
289813498266Sopenharmony_ci                /* "quote-doubling" */
289913498266Sopenharmony_ci                result = Curl_dyn_addn(&out, &ptr[1], 1);
290013498266Sopenharmony_ci                ptr++;
290113498266Sopenharmony_ci              }
290213498266Sopenharmony_ci              else {
290313498266Sopenharmony_ci                /* end of path */
290413498266Sopenharmony_ci                if(Curl_dyn_len(&out))
290513498266Sopenharmony_ci                  entry_extracted = TRUE;
290613498266Sopenharmony_ci                break; /* get out of this loop */
290713498266Sopenharmony_ci              }
290813498266Sopenharmony_ci            }
290913498266Sopenharmony_ci            else
291013498266Sopenharmony_ci              result = Curl_dyn_addn(&out, ptr, 1);
291113498266Sopenharmony_ci            if(result)
291213498266Sopenharmony_ci              return result;
291313498266Sopenharmony_ci          }
291413498266Sopenharmony_ci        }
291513498266Sopenharmony_ci        if(entry_extracted) {
291613498266Sopenharmony_ci          /* If the path name does not look like an absolute path (i.e.: it
291713498266Sopenharmony_ci             does not start with a '/'), we probably need some server-dependent
291813498266Sopenharmony_ci             adjustments. For example, this is the case when connecting to
291913498266Sopenharmony_ci             an OS400 FTP server: this server supports two name syntaxes,
292013498266Sopenharmony_ci             the default one being incompatible with standard paths. In
292113498266Sopenharmony_ci             addition, this server switches automatically to the regular path
292213498266Sopenharmony_ci             syntax when one is encountered in a command: this results in
292313498266Sopenharmony_ci             having an entrypath in the wrong syntax when later used in CWD.
292413498266Sopenharmony_ci               The method used here is to check the server OS: we do it only
292513498266Sopenharmony_ci             if the path name looks strange to minimize overhead on other
292613498266Sopenharmony_ci             systems. */
292713498266Sopenharmony_ci          char *dir = Curl_dyn_ptr(&out);
292813498266Sopenharmony_ci
292913498266Sopenharmony_ci          if(!ftpc->server_os && dir[0] != '/') {
293013498266Sopenharmony_ci            result = Curl_pp_sendf(data, &ftpc->pp, "%s", "SYST");
293113498266Sopenharmony_ci            if(result) {
293213498266Sopenharmony_ci              free(dir);
293313498266Sopenharmony_ci              return result;
293413498266Sopenharmony_ci            }
293513498266Sopenharmony_ci            Curl_safefree(ftpc->entrypath);
293613498266Sopenharmony_ci            ftpc->entrypath = dir; /* remember this */
293713498266Sopenharmony_ci            infof(data, "Entry path is '%s'", ftpc->entrypath);
293813498266Sopenharmony_ci            /* also save it where getinfo can access it: */
293913498266Sopenharmony_ci            data->state.most_recent_ftp_entrypath = ftpc->entrypath;
294013498266Sopenharmony_ci            ftp_state(data, FTP_SYST);
294113498266Sopenharmony_ci            break;
294213498266Sopenharmony_ci          }
294313498266Sopenharmony_ci
294413498266Sopenharmony_ci          Curl_safefree(ftpc->entrypath);
294513498266Sopenharmony_ci          ftpc->entrypath = dir; /* remember this */
294613498266Sopenharmony_ci          infof(data, "Entry path is '%s'", ftpc->entrypath);
294713498266Sopenharmony_ci          /* also save it where getinfo can access it: */
294813498266Sopenharmony_ci          data->state.most_recent_ftp_entrypath = ftpc->entrypath;
294913498266Sopenharmony_ci        }
295013498266Sopenharmony_ci        else {
295113498266Sopenharmony_ci          /* couldn't get the path */
295213498266Sopenharmony_ci          Curl_dyn_free(&out);
295313498266Sopenharmony_ci          infof(data, "Failed to figure out path");
295413498266Sopenharmony_ci        }
295513498266Sopenharmony_ci      }
295613498266Sopenharmony_ci      ftp_state(data, FTP_STOP); /* we are done with the CONNECT phase! */
295713498266Sopenharmony_ci      DEBUGF(infof(data, "protocol connect phase DONE"));
295813498266Sopenharmony_ci      break;
295913498266Sopenharmony_ci
296013498266Sopenharmony_ci    case FTP_SYST:
296113498266Sopenharmony_ci      if(ftpcode == 215) {
296213498266Sopenharmony_ci        char *ptr = Curl_dyn_ptr(&pp->recvbuf) + 4; /* start on the first
296313498266Sopenharmony_ci                                                       letter */
296413498266Sopenharmony_ci        char *os;
296513498266Sopenharmony_ci        char *start;
296613498266Sopenharmony_ci
296713498266Sopenharmony_ci        /* Reply format is like
296813498266Sopenharmony_ci           215<space><OS-name><space><commentary>
296913498266Sopenharmony_ci        */
297013498266Sopenharmony_ci        while(*ptr == ' ')
297113498266Sopenharmony_ci          ptr++;
297213498266Sopenharmony_ci        for(start = ptr; *ptr && *ptr != ' '; ptr++)
297313498266Sopenharmony_ci          ;
297413498266Sopenharmony_ci        os = Curl_memdup0(start, ptr - start);
297513498266Sopenharmony_ci        if(!os)
297613498266Sopenharmony_ci          return CURLE_OUT_OF_MEMORY;
297713498266Sopenharmony_ci
297813498266Sopenharmony_ci        /* Check for special servers here. */
297913498266Sopenharmony_ci        if(strcasecompare(os, "OS/400")) {
298013498266Sopenharmony_ci          /* Force OS400 name format 1. */
298113498266Sopenharmony_ci          result = Curl_pp_sendf(data, &ftpc->pp, "%s", "SITE NAMEFMT 1");
298213498266Sopenharmony_ci          if(result) {
298313498266Sopenharmony_ci            free(os);
298413498266Sopenharmony_ci            return result;
298513498266Sopenharmony_ci          }
298613498266Sopenharmony_ci          /* remember target server OS */
298713498266Sopenharmony_ci          Curl_safefree(ftpc->server_os);
298813498266Sopenharmony_ci          ftpc->server_os = os;
298913498266Sopenharmony_ci          ftp_state(data, FTP_NAMEFMT);
299013498266Sopenharmony_ci          break;
299113498266Sopenharmony_ci        }
299213498266Sopenharmony_ci        /* Nothing special for the target server. */
299313498266Sopenharmony_ci        /* remember target server OS */
299413498266Sopenharmony_ci        Curl_safefree(ftpc->server_os);
299513498266Sopenharmony_ci        ftpc->server_os = os;
299613498266Sopenharmony_ci      }
299713498266Sopenharmony_ci      else {
299813498266Sopenharmony_ci        /* Cannot identify server OS. Continue anyway and cross fingers. */
299913498266Sopenharmony_ci      }
300013498266Sopenharmony_ci
300113498266Sopenharmony_ci      ftp_state(data, FTP_STOP); /* we are done with the CONNECT phase! */
300213498266Sopenharmony_ci      DEBUGF(infof(data, "protocol connect phase DONE"));
300313498266Sopenharmony_ci      break;
300413498266Sopenharmony_ci
300513498266Sopenharmony_ci    case FTP_NAMEFMT:
300613498266Sopenharmony_ci      if(ftpcode == 250) {
300713498266Sopenharmony_ci        /* Name format change successful: reload initial path. */
300813498266Sopenharmony_ci        ftp_state_pwd(data, conn);
300913498266Sopenharmony_ci        break;
301013498266Sopenharmony_ci      }
301113498266Sopenharmony_ci
301213498266Sopenharmony_ci      ftp_state(data, FTP_STOP); /* we are done with the CONNECT phase! */
301313498266Sopenharmony_ci      DEBUGF(infof(data, "protocol connect phase DONE"));
301413498266Sopenharmony_ci      break;
301513498266Sopenharmony_ci
301613498266Sopenharmony_ci    case FTP_QUOTE:
301713498266Sopenharmony_ci    case FTP_POSTQUOTE:
301813498266Sopenharmony_ci    case FTP_RETR_PREQUOTE:
301913498266Sopenharmony_ci    case FTP_STOR_PREQUOTE:
302013498266Sopenharmony_ci      if((ftpcode >= 400) && !ftpc->count2) {
302113498266Sopenharmony_ci        /* failure response code, and not allowed to fail */
302213498266Sopenharmony_ci        failf(data, "QUOT command failed with %03d", ftpcode);
302313498266Sopenharmony_ci        result = CURLE_QUOTE_ERROR;
302413498266Sopenharmony_ci      }
302513498266Sopenharmony_ci      else
302613498266Sopenharmony_ci        result = ftp_state_quote(data, FALSE, ftpc->state);
302713498266Sopenharmony_ci      break;
302813498266Sopenharmony_ci
302913498266Sopenharmony_ci    case FTP_CWD:
303013498266Sopenharmony_ci      if(ftpcode/100 != 2) {
303113498266Sopenharmony_ci        /* failure to CWD there */
303213498266Sopenharmony_ci        if(data->set.ftp_create_missing_dirs &&
303313498266Sopenharmony_ci           ftpc->cwdcount && !ftpc->count2) {
303413498266Sopenharmony_ci          /* try making it */
303513498266Sopenharmony_ci          ftpc->count2++; /* counter to prevent CWD-MKD loops */
303613498266Sopenharmony_ci
303713498266Sopenharmony_ci          /* count3 is set to allow MKD to fail once per dir. In the case when
303813498266Sopenharmony_ci          CWD fails and then MKD fails (due to another session raced it to
303913498266Sopenharmony_ci          create the dir) this then allows for a second try to CWD to it. */
304013498266Sopenharmony_ci          ftpc->count3 = (data->set.ftp_create_missing_dirs == 2) ? 1 : 0;
304113498266Sopenharmony_ci
304213498266Sopenharmony_ci          result = Curl_pp_sendf(data, &ftpc->pp, "MKD %s",
304313498266Sopenharmony_ci                                 ftpc->dirs[ftpc->cwdcount - 1]);
304413498266Sopenharmony_ci          if(!result)
304513498266Sopenharmony_ci            ftp_state(data, FTP_MKD);
304613498266Sopenharmony_ci        }
304713498266Sopenharmony_ci        else {
304813498266Sopenharmony_ci          /* return failure */
304913498266Sopenharmony_ci          failf(data, "Server denied you to change to the given directory");
305013498266Sopenharmony_ci          ftpc->cwdfail = TRUE; /* don't remember this path as we failed
305113498266Sopenharmony_ci                                   to enter it */
305213498266Sopenharmony_ci          result = CURLE_REMOTE_ACCESS_DENIED;
305313498266Sopenharmony_ci        }
305413498266Sopenharmony_ci      }
305513498266Sopenharmony_ci      else {
305613498266Sopenharmony_ci        /* success */
305713498266Sopenharmony_ci        ftpc->count2 = 0;
305813498266Sopenharmony_ci        if(++ftpc->cwdcount <= ftpc->dirdepth)
305913498266Sopenharmony_ci          /* send next CWD */
306013498266Sopenharmony_ci          result = Curl_pp_sendf(data, &ftpc->pp, "CWD %s",
306113498266Sopenharmony_ci                                 ftpc->dirs[ftpc->cwdcount - 1]);
306213498266Sopenharmony_ci        else
306313498266Sopenharmony_ci          result = ftp_state_mdtm(data);
306413498266Sopenharmony_ci      }
306513498266Sopenharmony_ci      break;
306613498266Sopenharmony_ci
306713498266Sopenharmony_ci    case FTP_MKD:
306813498266Sopenharmony_ci      if((ftpcode/100 != 2) && !ftpc->count3--) {
306913498266Sopenharmony_ci        /* failure to MKD the dir */
307013498266Sopenharmony_ci        failf(data, "Failed to MKD dir: %03d", ftpcode);
307113498266Sopenharmony_ci        result = CURLE_REMOTE_ACCESS_DENIED;
307213498266Sopenharmony_ci      }
307313498266Sopenharmony_ci      else {
307413498266Sopenharmony_ci        ftp_state(data, FTP_CWD);
307513498266Sopenharmony_ci        /* send CWD */
307613498266Sopenharmony_ci        result = Curl_pp_sendf(data, &ftpc->pp, "CWD %s",
307713498266Sopenharmony_ci                               ftpc->dirs[ftpc->cwdcount - 1]);
307813498266Sopenharmony_ci      }
307913498266Sopenharmony_ci      break;
308013498266Sopenharmony_ci
308113498266Sopenharmony_ci    case FTP_MDTM:
308213498266Sopenharmony_ci      result = ftp_state_mdtm_resp(data, ftpcode);
308313498266Sopenharmony_ci      break;
308413498266Sopenharmony_ci
308513498266Sopenharmony_ci    case FTP_TYPE:
308613498266Sopenharmony_ci    case FTP_LIST_TYPE:
308713498266Sopenharmony_ci    case FTP_RETR_TYPE:
308813498266Sopenharmony_ci    case FTP_STOR_TYPE:
308913498266Sopenharmony_ci      result = ftp_state_type_resp(data, ftpcode, ftpc->state);
309013498266Sopenharmony_ci      break;
309113498266Sopenharmony_ci
309213498266Sopenharmony_ci    case FTP_SIZE:
309313498266Sopenharmony_ci    case FTP_RETR_SIZE:
309413498266Sopenharmony_ci    case FTP_STOR_SIZE:
309513498266Sopenharmony_ci      result = ftp_state_size_resp(data, ftpcode, ftpc->state);
309613498266Sopenharmony_ci      break;
309713498266Sopenharmony_ci
309813498266Sopenharmony_ci    case FTP_REST:
309913498266Sopenharmony_ci    case FTP_RETR_REST:
310013498266Sopenharmony_ci      result = ftp_state_rest_resp(data, conn, ftpcode, ftpc->state);
310113498266Sopenharmony_ci      break;
310213498266Sopenharmony_ci
310313498266Sopenharmony_ci    case FTP_PRET:
310413498266Sopenharmony_ci      if(ftpcode != 200) {
310513498266Sopenharmony_ci        /* there only is this one standard OK return code. */
310613498266Sopenharmony_ci        failf(data, "PRET command not accepted: %03d", ftpcode);
310713498266Sopenharmony_ci        return CURLE_FTP_PRET_FAILED;
310813498266Sopenharmony_ci      }
310913498266Sopenharmony_ci      result = ftp_state_use_pasv(data, conn);
311013498266Sopenharmony_ci      break;
311113498266Sopenharmony_ci
311213498266Sopenharmony_ci    case FTP_PASV:
311313498266Sopenharmony_ci      result = ftp_state_pasv_resp(data, ftpcode);
311413498266Sopenharmony_ci      break;
311513498266Sopenharmony_ci
311613498266Sopenharmony_ci    case FTP_PORT:
311713498266Sopenharmony_ci      result = ftp_state_port_resp(data, ftpcode);
311813498266Sopenharmony_ci      break;
311913498266Sopenharmony_ci
312013498266Sopenharmony_ci    case FTP_LIST:
312113498266Sopenharmony_ci    case FTP_RETR:
312213498266Sopenharmony_ci      result = ftp_state_get_resp(data, ftpcode, ftpc->state);
312313498266Sopenharmony_ci      break;
312413498266Sopenharmony_ci
312513498266Sopenharmony_ci    case FTP_STOR:
312613498266Sopenharmony_ci      result = ftp_state_stor_resp(data, ftpcode, ftpc->state);
312713498266Sopenharmony_ci      break;
312813498266Sopenharmony_ci
312913498266Sopenharmony_ci    case FTP_QUIT:
313013498266Sopenharmony_ci    default:
313113498266Sopenharmony_ci      /* internal error */
313213498266Sopenharmony_ci      ftp_state(data, FTP_STOP);
313313498266Sopenharmony_ci      break;
313413498266Sopenharmony_ci    }
313513498266Sopenharmony_ci  } /* if(ftpcode) */
313613498266Sopenharmony_ci
313713498266Sopenharmony_ci  return result;
313813498266Sopenharmony_ci}
313913498266Sopenharmony_ci
314013498266Sopenharmony_ci
314113498266Sopenharmony_ci/* called repeatedly until done from multi.c */
314213498266Sopenharmony_cistatic CURLcode ftp_multi_statemach(struct Curl_easy *data,
314313498266Sopenharmony_ci                                    bool *done)
314413498266Sopenharmony_ci{
314513498266Sopenharmony_ci  struct connectdata *conn = data->conn;
314613498266Sopenharmony_ci  struct ftp_conn *ftpc = &conn->proto.ftpc;
314713498266Sopenharmony_ci  CURLcode result = Curl_pp_statemach(data, &ftpc->pp, FALSE, FALSE);
314813498266Sopenharmony_ci
314913498266Sopenharmony_ci  /* Check for the state outside of the Curl_socket_check() return code checks
315013498266Sopenharmony_ci     since at times we are in fact already in this state when this function
315113498266Sopenharmony_ci     gets called. */
315213498266Sopenharmony_ci  *done = (ftpc->state == FTP_STOP) ? TRUE : FALSE;
315313498266Sopenharmony_ci
315413498266Sopenharmony_ci  return result;
315513498266Sopenharmony_ci}
315613498266Sopenharmony_ci
315713498266Sopenharmony_cistatic CURLcode ftp_block_statemach(struct Curl_easy *data,
315813498266Sopenharmony_ci                                    struct connectdata *conn)
315913498266Sopenharmony_ci{
316013498266Sopenharmony_ci  struct ftp_conn *ftpc = &conn->proto.ftpc;
316113498266Sopenharmony_ci  struct pingpong *pp = &ftpc->pp;
316213498266Sopenharmony_ci  CURLcode result = CURLE_OK;
316313498266Sopenharmony_ci
316413498266Sopenharmony_ci  while(ftpc->state != FTP_STOP) {
316513498266Sopenharmony_ci    result = Curl_pp_statemach(data, pp, TRUE, TRUE /* disconnecting */);
316613498266Sopenharmony_ci    if(result)
316713498266Sopenharmony_ci      break;
316813498266Sopenharmony_ci  }
316913498266Sopenharmony_ci
317013498266Sopenharmony_ci  return result;
317113498266Sopenharmony_ci}
317213498266Sopenharmony_ci
317313498266Sopenharmony_ci/*
317413498266Sopenharmony_ci * ftp_connect() should do everything that is to be considered a part of
317513498266Sopenharmony_ci * the connection phase.
317613498266Sopenharmony_ci *
317713498266Sopenharmony_ci * The variable 'done' points to will be TRUE if the protocol-layer connect
317813498266Sopenharmony_ci * phase is done when this function returns, or FALSE if not.
317913498266Sopenharmony_ci *
318013498266Sopenharmony_ci */
318113498266Sopenharmony_cistatic CURLcode ftp_connect(struct Curl_easy *data,
318213498266Sopenharmony_ci                            bool *done) /* see description above */
318313498266Sopenharmony_ci{
318413498266Sopenharmony_ci  CURLcode result;
318513498266Sopenharmony_ci  struct connectdata *conn = data->conn;
318613498266Sopenharmony_ci  struct ftp_conn *ftpc = &conn->proto.ftpc;
318713498266Sopenharmony_ci  struct pingpong *pp = &ftpc->pp;
318813498266Sopenharmony_ci
318913498266Sopenharmony_ci  *done = FALSE; /* default to not done yet */
319013498266Sopenharmony_ci
319113498266Sopenharmony_ci  /* We always support persistent connections on ftp */
319213498266Sopenharmony_ci  connkeep(conn, "FTP default");
319313498266Sopenharmony_ci
319413498266Sopenharmony_ci  PINGPONG_SETUP(pp, ftp_statemachine, ftp_endofresp);
319513498266Sopenharmony_ci
319613498266Sopenharmony_ci  if(conn->handler->flags & PROTOPT_SSL) {
319713498266Sopenharmony_ci    /* BLOCKING */
319813498266Sopenharmony_ci    result = Curl_conn_connect(data, FIRSTSOCKET, TRUE, done);
319913498266Sopenharmony_ci    if(result)
320013498266Sopenharmony_ci      return result;
320113498266Sopenharmony_ci    conn->bits.ftp_use_control_ssl = TRUE;
320213498266Sopenharmony_ci  }
320313498266Sopenharmony_ci
320413498266Sopenharmony_ci  Curl_pp_init(pp); /* once per transfer */
320513498266Sopenharmony_ci
320613498266Sopenharmony_ci  /* When we connect, we start in the state where we await the 220
320713498266Sopenharmony_ci     response */
320813498266Sopenharmony_ci  ftp_state(data, FTP_WAIT220);
320913498266Sopenharmony_ci
321013498266Sopenharmony_ci  result = ftp_multi_statemach(data, done);
321113498266Sopenharmony_ci
321213498266Sopenharmony_ci  return result;
321313498266Sopenharmony_ci}
321413498266Sopenharmony_ci
321513498266Sopenharmony_ci/***********************************************************************
321613498266Sopenharmony_ci *
321713498266Sopenharmony_ci * ftp_done()
321813498266Sopenharmony_ci *
321913498266Sopenharmony_ci * The DONE function. This does what needs to be done after a single DO has
322013498266Sopenharmony_ci * performed.
322113498266Sopenharmony_ci *
322213498266Sopenharmony_ci * Input argument is already checked for validity.
322313498266Sopenharmony_ci */
322413498266Sopenharmony_cistatic CURLcode ftp_done(struct Curl_easy *data, CURLcode status,
322513498266Sopenharmony_ci                         bool premature)
322613498266Sopenharmony_ci{
322713498266Sopenharmony_ci  struct connectdata *conn = data->conn;
322813498266Sopenharmony_ci  struct FTP *ftp = data->req.p.ftp;
322913498266Sopenharmony_ci  struct ftp_conn *ftpc = &conn->proto.ftpc;
323013498266Sopenharmony_ci  struct pingpong *pp = &ftpc->pp;
323113498266Sopenharmony_ci  ssize_t nread;
323213498266Sopenharmony_ci  int ftpcode;
323313498266Sopenharmony_ci  CURLcode result = CURLE_OK;
323413498266Sopenharmony_ci  char *rawPath = NULL;
323513498266Sopenharmony_ci  size_t pathLen = 0;
323613498266Sopenharmony_ci
323713498266Sopenharmony_ci  if(!ftp)
323813498266Sopenharmony_ci    return CURLE_OK;
323913498266Sopenharmony_ci
324013498266Sopenharmony_ci  switch(status) {
324113498266Sopenharmony_ci  case CURLE_BAD_DOWNLOAD_RESUME:
324213498266Sopenharmony_ci  case CURLE_FTP_WEIRD_PASV_REPLY:
324313498266Sopenharmony_ci  case CURLE_FTP_PORT_FAILED:
324413498266Sopenharmony_ci  case CURLE_FTP_ACCEPT_FAILED:
324513498266Sopenharmony_ci  case CURLE_FTP_ACCEPT_TIMEOUT:
324613498266Sopenharmony_ci  case CURLE_FTP_COULDNT_SET_TYPE:
324713498266Sopenharmony_ci  case CURLE_FTP_COULDNT_RETR_FILE:
324813498266Sopenharmony_ci  case CURLE_PARTIAL_FILE:
324913498266Sopenharmony_ci  case CURLE_UPLOAD_FAILED:
325013498266Sopenharmony_ci  case CURLE_REMOTE_ACCESS_DENIED:
325113498266Sopenharmony_ci  case CURLE_FILESIZE_EXCEEDED:
325213498266Sopenharmony_ci  case CURLE_REMOTE_FILE_NOT_FOUND:
325313498266Sopenharmony_ci  case CURLE_WRITE_ERROR:
325413498266Sopenharmony_ci    /* the connection stays alive fine even though this happened */
325513498266Sopenharmony_ci  case CURLE_OK: /* doesn't affect the control connection's status */
325613498266Sopenharmony_ci    if(!premature)
325713498266Sopenharmony_ci      break;
325813498266Sopenharmony_ci
325913498266Sopenharmony_ci    /* until we cope better with prematurely ended requests, let them
326013498266Sopenharmony_ci     * fallback as if in complete failure */
326113498266Sopenharmony_ci    FALLTHROUGH();
326213498266Sopenharmony_ci  default:       /* by default, an error means the control connection is
326313498266Sopenharmony_ci                    wedged and should not be used anymore */
326413498266Sopenharmony_ci    ftpc->ctl_valid = FALSE;
326513498266Sopenharmony_ci    ftpc->cwdfail = TRUE; /* set this TRUE to prevent us to remember the
326613498266Sopenharmony_ci                             current path, as this connection is going */
326713498266Sopenharmony_ci    connclose(conn, "FTP ended with bad error code");
326813498266Sopenharmony_ci    result = status;      /* use the already set error code */
326913498266Sopenharmony_ci    break;
327013498266Sopenharmony_ci  }
327113498266Sopenharmony_ci
327213498266Sopenharmony_ci  if(data->state.wildcardmatch) {
327313498266Sopenharmony_ci    if(data->set.chunk_end && ftpc->file) {
327413498266Sopenharmony_ci      Curl_set_in_callback(data, true);
327513498266Sopenharmony_ci      data->set.chunk_end(data->set.wildcardptr);
327613498266Sopenharmony_ci      Curl_set_in_callback(data, false);
327713498266Sopenharmony_ci    }
327813498266Sopenharmony_ci    ftpc->known_filesize = -1;
327913498266Sopenharmony_ci  }
328013498266Sopenharmony_ci
328113498266Sopenharmony_ci  if(!result)
328213498266Sopenharmony_ci    /* get the url-decoded "raw" path */
328313498266Sopenharmony_ci    result = Curl_urldecode(ftp->path, 0, &rawPath, &pathLen,
328413498266Sopenharmony_ci                            REJECT_CTRL);
328513498266Sopenharmony_ci  if(result) {
328613498266Sopenharmony_ci    /* We can limp along anyway (and should try to since we may already be in
328713498266Sopenharmony_ci     * the error path) */
328813498266Sopenharmony_ci    ftpc->ctl_valid = FALSE; /* mark control connection as bad */
328913498266Sopenharmony_ci    connclose(conn, "FTP: out of memory!"); /* mark for connection closure */
329013498266Sopenharmony_ci    free(ftpc->prevpath);
329113498266Sopenharmony_ci    ftpc->prevpath = NULL; /* no path remembering */
329213498266Sopenharmony_ci  }
329313498266Sopenharmony_ci  else { /* remember working directory for connection reuse */
329413498266Sopenharmony_ci    if((data->set.ftp_filemethod == FTPFILE_NOCWD) && (rawPath[0] == '/'))
329513498266Sopenharmony_ci      free(rawPath); /* full path => no CWDs happened => keep ftpc->prevpath */
329613498266Sopenharmony_ci    else {
329713498266Sopenharmony_ci      free(ftpc->prevpath);
329813498266Sopenharmony_ci
329913498266Sopenharmony_ci      if(!ftpc->cwdfail) {
330013498266Sopenharmony_ci        if(data->set.ftp_filemethod == FTPFILE_NOCWD)
330113498266Sopenharmony_ci          pathLen = 0; /* relative path => working directory is FTP home */
330213498266Sopenharmony_ci        else
330313498266Sopenharmony_ci          pathLen -= ftpc->file?strlen(ftpc->file):0; /* file is url-decoded */
330413498266Sopenharmony_ci
330513498266Sopenharmony_ci        rawPath[pathLen] = '\0';
330613498266Sopenharmony_ci        ftpc->prevpath = rawPath;
330713498266Sopenharmony_ci      }
330813498266Sopenharmony_ci      else {
330913498266Sopenharmony_ci        free(rawPath);
331013498266Sopenharmony_ci        ftpc->prevpath = NULL; /* no path */
331113498266Sopenharmony_ci      }
331213498266Sopenharmony_ci    }
331313498266Sopenharmony_ci
331413498266Sopenharmony_ci    if(ftpc->prevpath)
331513498266Sopenharmony_ci      infof(data, "Remembering we are in dir \"%s\"", ftpc->prevpath);
331613498266Sopenharmony_ci  }
331713498266Sopenharmony_ci
331813498266Sopenharmony_ci  /* free the dir tree and file parts */
331913498266Sopenharmony_ci  freedirs(ftpc);
332013498266Sopenharmony_ci
332113498266Sopenharmony_ci  /* shut down the socket to inform the server we're done */
332213498266Sopenharmony_ci
332313498266Sopenharmony_ci#ifdef _WIN32_WCE
332413498266Sopenharmony_ci  shutdown(conn->sock[SECONDARYSOCKET], 2);  /* SD_BOTH */
332513498266Sopenharmony_ci#endif
332613498266Sopenharmony_ci
332713498266Sopenharmony_ci  if(conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD) {
332813498266Sopenharmony_ci    if(!result && ftpc->dont_check && data->req.maxdownload > 0) {
332913498266Sopenharmony_ci      /* partial download completed */
333013498266Sopenharmony_ci      result = Curl_pp_sendf(data, pp, "%s", "ABOR");
333113498266Sopenharmony_ci      if(result) {
333213498266Sopenharmony_ci        failf(data, "Failure sending ABOR command: %s",
333313498266Sopenharmony_ci              curl_easy_strerror(result));
333413498266Sopenharmony_ci        ftpc->ctl_valid = FALSE; /* mark control connection as bad */
333513498266Sopenharmony_ci        connclose(conn, "ABOR command failed"); /* connection closure */
333613498266Sopenharmony_ci      }
333713498266Sopenharmony_ci    }
333813498266Sopenharmony_ci
333913498266Sopenharmony_ci    close_secondarysocket(data, conn);
334013498266Sopenharmony_ci  }
334113498266Sopenharmony_ci
334213498266Sopenharmony_ci  if(!result && (ftp->transfer == PPTRANSFER_BODY) && ftpc->ctl_valid &&
334313498266Sopenharmony_ci     pp->pending_resp && !premature) {
334413498266Sopenharmony_ci    /*
334513498266Sopenharmony_ci     * Let's see what the server says about the transfer we just performed,
334613498266Sopenharmony_ci     * but lower the timeout as sometimes this connection has died while the
334713498266Sopenharmony_ci     * data has been transferred. This happens when doing through NATs etc that
334813498266Sopenharmony_ci     * abandon old silent connections.
334913498266Sopenharmony_ci     */
335013498266Sopenharmony_ci    timediff_t old_time = pp->response_time;
335113498266Sopenharmony_ci
335213498266Sopenharmony_ci    pp->response_time = 60*1000; /* give it only a minute for now */
335313498266Sopenharmony_ci    pp->response = Curl_now(); /* timeout relative now */
335413498266Sopenharmony_ci
335513498266Sopenharmony_ci    result = Curl_GetFTPResponse(data, &nread, &ftpcode);
335613498266Sopenharmony_ci
335713498266Sopenharmony_ci    pp->response_time = old_time; /* set this back to previous value */
335813498266Sopenharmony_ci
335913498266Sopenharmony_ci    if(!nread && (CURLE_OPERATION_TIMEDOUT == result)) {
336013498266Sopenharmony_ci      failf(data, "control connection looks dead");
336113498266Sopenharmony_ci      ftpc->ctl_valid = FALSE; /* mark control connection as bad */
336213498266Sopenharmony_ci      connclose(conn, "Timeout or similar in FTP DONE operation"); /* close */
336313498266Sopenharmony_ci    }
336413498266Sopenharmony_ci
336513498266Sopenharmony_ci    if(result) {
336613498266Sopenharmony_ci      Curl_safefree(ftp->pathalloc);
336713498266Sopenharmony_ci      return result;
336813498266Sopenharmony_ci    }
336913498266Sopenharmony_ci
337013498266Sopenharmony_ci    if(ftpc->dont_check && data->req.maxdownload > 0) {
337113498266Sopenharmony_ci      /* we have just sent ABOR and there is no reliable way to check if it was
337213498266Sopenharmony_ci       * successful or not; we have to close the connection now */
337313498266Sopenharmony_ci      infof(data, "partial download completed, closing connection");
337413498266Sopenharmony_ci      connclose(conn, "Partial download with no ability to check");
337513498266Sopenharmony_ci      return result;
337613498266Sopenharmony_ci    }
337713498266Sopenharmony_ci
337813498266Sopenharmony_ci    if(!ftpc->dont_check) {
337913498266Sopenharmony_ci      /* 226 Transfer complete, 250 Requested file action okay, completed. */
338013498266Sopenharmony_ci      switch(ftpcode) {
338113498266Sopenharmony_ci      case 226:
338213498266Sopenharmony_ci      case 250:
338313498266Sopenharmony_ci        break;
338413498266Sopenharmony_ci      case 552:
338513498266Sopenharmony_ci        failf(data, "Exceeded storage allocation");
338613498266Sopenharmony_ci        result = CURLE_REMOTE_DISK_FULL;
338713498266Sopenharmony_ci        break;
338813498266Sopenharmony_ci      default:
338913498266Sopenharmony_ci        failf(data, "server did not report OK, got %d", ftpcode);
339013498266Sopenharmony_ci        result = CURLE_PARTIAL_FILE;
339113498266Sopenharmony_ci        break;
339213498266Sopenharmony_ci      }
339313498266Sopenharmony_ci    }
339413498266Sopenharmony_ci  }
339513498266Sopenharmony_ci
339613498266Sopenharmony_ci  if(result || premature)
339713498266Sopenharmony_ci    /* the response code from the transfer showed an error already so no
339813498266Sopenharmony_ci       use checking further */
339913498266Sopenharmony_ci    ;
340013498266Sopenharmony_ci  else if(data->state.upload) {
340113498266Sopenharmony_ci    if((-1 != data->state.infilesize) &&
340213498266Sopenharmony_ci       (data->state.infilesize != data->req.writebytecount) &&
340313498266Sopenharmony_ci       !data->set.crlf &&
340413498266Sopenharmony_ci       (ftp->transfer == PPTRANSFER_BODY)) {
340513498266Sopenharmony_ci      failf(data, "Uploaded unaligned file size (%" CURL_FORMAT_CURL_OFF_T
340613498266Sopenharmony_ci            " out of %" CURL_FORMAT_CURL_OFF_T " bytes)",
340713498266Sopenharmony_ci            data->req.writebytecount, data->state.infilesize);
340813498266Sopenharmony_ci      result = CURLE_PARTIAL_FILE;
340913498266Sopenharmony_ci    }
341013498266Sopenharmony_ci  }
341113498266Sopenharmony_ci  else {
341213498266Sopenharmony_ci    if((-1 != data->req.size) &&
341313498266Sopenharmony_ci       (data->req.size != data->req.bytecount) &&
341413498266Sopenharmony_ci#ifdef CURL_DO_LINEEND_CONV
341513498266Sopenharmony_ci       /* Most FTP servers don't adjust their file SIZE response for CRLFs, so
341613498266Sopenharmony_ci        * we'll check to see if the discrepancy can be explained by the number
341713498266Sopenharmony_ci        * of CRLFs we've changed to LFs.
341813498266Sopenharmony_ci        */
341913498266Sopenharmony_ci       ((data->req.size + data->state.crlf_conversions) !=
342013498266Sopenharmony_ci        data->req.bytecount) &&
342113498266Sopenharmony_ci#endif /* CURL_DO_LINEEND_CONV */
342213498266Sopenharmony_ci       (data->req.maxdownload != data->req.bytecount)) {
342313498266Sopenharmony_ci      failf(data, "Received only partial file: %" CURL_FORMAT_CURL_OFF_T
342413498266Sopenharmony_ci            " bytes", data->req.bytecount);
342513498266Sopenharmony_ci      result = CURLE_PARTIAL_FILE;
342613498266Sopenharmony_ci    }
342713498266Sopenharmony_ci    else if(!ftpc->dont_check &&
342813498266Sopenharmony_ci            !data->req.bytecount &&
342913498266Sopenharmony_ci            (data->req.size>0)) {
343013498266Sopenharmony_ci      failf(data, "No data was received");
343113498266Sopenharmony_ci      result = CURLE_FTP_COULDNT_RETR_FILE;
343213498266Sopenharmony_ci    }
343313498266Sopenharmony_ci  }
343413498266Sopenharmony_ci
343513498266Sopenharmony_ci  /* clear these for next connection */
343613498266Sopenharmony_ci  ftp->transfer = PPTRANSFER_BODY;
343713498266Sopenharmony_ci  ftpc->dont_check = FALSE;
343813498266Sopenharmony_ci
343913498266Sopenharmony_ci  /* Send any post-transfer QUOTE strings? */
344013498266Sopenharmony_ci  if(!status && !result && !premature && data->set.postquote)
344113498266Sopenharmony_ci    result = ftp_sendquote(data, conn, data->set.postquote);
344213498266Sopenharmony_ci  Curl_safefree(ftp->pathalloc);
344313498266Sopenharmony_ci  return result;
344413498266Sopenharmony_ci}
344513498266Sopenharmony_ci
344613498266Sopenharmony_ci/***********************************************************************
344713498266Sopenharmony_ci *
344813498266Sopenharmony_ci * ftp_sendquote()
344913498266Sopenharmony_ci *
345013498266Sopenharmony_ci * Where a 'quote' means a list of custom commands to send to the server.
345113498266Sopenharmony_ci * The quote list is passed as an argument.
345213498266Sopenharmony_ci *
345313498266Sopenharmony_ci * BLOCKING
345413498266Sopenharmony_ci */
345513498266Sopenharmony_ci
345613498266Sopenharmony_cistatic
345713498266Sopenharmony_ciCURLcode ftp_sendquote(struct Curl_easy *data,
345813498266Sopenharmony_ci                       struct connectdata *conn, struct curl_slist *quote)
345913498266Sopenharmony_ci{
346013498266Sopenharmony_ci  struct curl_slist *item;
346113498266Sopenharmony_ci  struct ftp_conn *ftpc = &conn->proto.ftpc;
346213498266Sopenharmony_ci  struct pingpong *pp = &ftpc->pp;
346313498266Sopenharmony_ci
346413498266Sopenharmony_ci  item = quote;
346513498266Sopenharmony_ci  while(item) {
346613498266Sopenharmony_ci    if(item->data) {
346713498266Sopenharmony_ci      ssize_t nread;
346813498266Sopenharmony_ci      char *cmd = item->data;
346913498266Sopenharmony_ci      bool acceptfail = FALSE;
347013498266Sopenharmony_ci      CURLcode result;
347113498266Sopenharmony_ci      int ftpcode = 0;
347213498266Sopenharmony_ci
347313498266Sopenharmony_ci      /* if a command starts with an asterisk, which a legal FTP command never
347413498266Sopenharmony_ci         can, the command will be allowed to fail without it causing any
347513498266Sopenharmony_ci         aborts or cancels etc. It will cause libcurl to act as if the command
347613498266Sopenharmony_ci         is successful, whatever the server responds. */
347713498266Sopenharmony_ci
347813498266Sopenharmony_ci      if(cmd[0] == '*') {
347913498266Sopenharmony_ci        cmd++;
348013498266Sopenharmony_ci        acceptfail = TRUE;
348113498266Sopenharmony_ci      }
348213498266Sopenharmony_ci
348313498266Sopenharmony_ci      result = Curl_pp_sendf(data, &ftpc->pp, "%s", cmd);
348413498266Sopenharmony_ci      if(!result) {
348513498266Sopenharmony_ci        pp->response = Curl_now(); /* timeout relative now */
348613498266Sopenharmony_ci        result = Curl_GetFTPResponse(data, &nread, &ftpcode);
348713498266Sopenharmony_ci      }
348813498266Sopenharmony_ci      if(result)
348913498266Sopenharmony_ci        return result;
349013498266Sopenharmony_ci
349113498266Sopenharmony_ci      if(!acceptfail && (ftpcode >= 400)) {
349213498266Sopenharmony_ci        failf(data, "QUOT string not accepted: %s", cmd);
349313498266Sopenharmony_ci        return CURLE_QUOTE_ERROR;
349413498266Sopenharmony_ci      }
349513498266Sopenharmony_ci    }
349613498266Sopenharmony_ci
349713498266Sopenharmony_ci    item = item->next;
349813498266Sopenharmony_ci  }
349913498266Sopenharmony_ci
350013498266Sopenharmony_ci  return CURLE_OK;
350113498266Sopenharmony_ci}
350213498266Sopenharmony_ci
350313498266Sopenharmony_ci/***********************************************************************
350413498266Sopenharmony_ci *
350513498266Sopenharmony_ci * ftp_need_type()
350613498266Sopenharmony_ci *
350713498266Sopenharmony_ci * Returns TRUE if we in the current situation should send TYPE
350813498266Sopenharmony_ci */
350913498266Sopenharmony_cistatic int ftp_need_type(struct connectdata *conn,
351013498266Sopenharmony_ci                         bool ascii_wanted)
351113498266Sopenharmony_ci{
351213498266Sopenharmony_ci  return conn->proto.ftpc.transfertype != (ascii_wanted?'A':'I');
351313498266Sopenharmony_ci}
351413498266Sopenharmony_ci
351513498266Sopenharmony_ci/***********************************************************************
351613498266Sopenharmony_ci *
351713498266Sopenharmony_ci * ftp_nb_type()
351813498266Sopenharmony_ci *
351913498266Sopenharmony_ci * Set TYPE. We only deal with ASCII or BINARY so this function
352013498266Sopenharmony_ci * sets one of them.
352113498266Sopenharmony_ci * If the transfer type is not sent, simulate on OK response in newstate
352213498266Sopenharmony_ci */
352313498266Sopenharmony_cistatic CURLcode ftp_nb_type(struct Curl_easy *data,
352413498266Sopenharmony_ci                            struct connectdata *conn,
352513498266Sopenharmony_ci                            bool ascii, ftpstate newstate)
352613498266Sopenharmony_ci{
352713498266Sopenharmony_ci  struct ftp_conn *ftpc = &conn->proto.ftpc;
352813498266Sopenharmony_ci  CURLcode result;
352913498266Sopenharmony_ci  char want = (char)(ascii?'A':'I');
353013498266Sopenharmony_ci
353113498266Sopenharmony_ci  if(ftpc->transfertype == want) {
353213498266Sopenharmony_ci    ftp_state(data, newstate);
353313498266Sopenharmony_ci    return ftp_state_type_resp(data, 200, newstate);
353413498266Sopenharmony_ci  }
353513498266Sopenharmony_ci
353613498266Sopenharmony_ci  result = Curl_pp_sendf(data, &ftpc->pp, "TYPE %c", want);
353713498266Sopenharmony_ci  if(!result) {
353813498266Sopenharmony_ci    ftp_state(data, newstate);
353913498266Sopenharmony_ci
354013498266Sopenharmony_ci    /* keep track of our current transfer type */
354113498266Sopenharmony_ci    ftpc->transfertype = want;
354213498266Sopenharmony_ci  }
354313498266Sopenharmony_ci  return result;
354413498266Sopenharmony_ci}
354513498266Sopenharmony_ci
354613498266Sopenharmony_ci/***************************************************************************
354713498266Sopenharmony_ci *
354813498266Sopenharmony_ci * ftp_pasv_verbose()
354913498266Sopenharmony_ci *
355013498266Sopenharmony_ci * This function only outputs some informationals about this second connection
355113498266Sopenharmony_ci * when we've issued a PASV command before and thus we have connected to a
355213498266Sopenharmony_ci * possibly new IP address.
355313498266Sopenharmony_ci *
355413498266Sopenharmony_ci */
355513498266Sopenharmony_ci#ifndef CURL_DISABLE_VERBOSE_STRINGS
355613498266Sopenharmony_cistatic void
355713498266Sopenharmony_ciftp_pasv_verbose(struct Curl_easy *data,
355813498266Sopenharmony_ci                 struct Curl_addrinfo *ai,
355913498266Sopenharmony_ci                 char *newhost, /* ascii version */
356013498266Sopenharmony_ci                 int port)
356113498266Sopenharmony_ci{
356213498266Sopenharmony_ci  char buf[256];
356313498266Sopenharmony_ci  Curl_printable_address(ai, buf, sizeof(buf));
356413498266Sopenharmony_ci  infof(data, "Connecting to %s (%s) port %d", newhost, buf, port);
356513498266Sopenharmony_ci}
356613498266Sopenharmony_ci#endif
356713498266Sopenharmony_ci
356813498266Sopenharmony_ci/*
356913498266Sopenharmony_ci * ftp_do_more()
357013498266Sopenharmony_ci *
357113498266Sopenharmony_ci * This function shall be called when the second FTP (data) connection is
357213498266Sopenharmony_ci * connected.
357313498266Sopenharmony_ci *
357413498266Sopenharmony_ci * 'complete' can return 0 for incomplete, 1 for done and -1 for go back
357513498266Sopenharmony_ci * (which basically is only for when PASV is being sent to retry a failed
357613498266Sopenharmony_ci * EPSV).
357713498266Sopenharmony_ci */
357813498266Sopenharmony_ci
357913498266Sopenharmony_cistatic CURLcode ftp_do_more(struct Curl_easy *data, int *completep)
358013498266Sopenharmony_ci{
358113498266Sopenharmony_ci  struct connectdata *conn = data->conn;
358213498266Sopenharmony_ci  struct ftp_conn *ftpc = &conn->proto.ftpc;
358313498266Sopenharmony_ci  CURLcode result = CURLE_OK;
358413498266Sopenharmony_ci  bool connected = FALSE;
358513498266Sopenharmony_ci  bool complete = FALSE;
358613498266Sopenharmony_ci
358713498266Sopenharmony_ci  /* the ftp struct is inited in ftp_connect(). If we are connecting to an HTTP
358813498266Sopenharmony_ci   * proxy then the state will not be valid until after that connection is
358913498266Sopenharmony_ci   * complete */
359013498266Sopenharmony_ci  struct FTP *ftp = NULL;
359113498266Sopenharmony_ci
359213498266Sopenharmony_ci  /* if the second connection isn't done yet, wait for it to have
359313498266Sopenharmony_ci   * connected to the remote host. When using proxy tunneling, this
359413498266Sopenharmony_ci   * means the tunnel needs to have been establish. However, we
359513498266Sopenharmony_ci   * can not expect the remote host to talk to us in any way yet.
359613498266Sopenharmony_ci   * So, when using ftps: the SSL handshake will not start until we
359713498266Sopenharmony_ci   * tell the remote server that we are there. */
359813498266Sopenharmony_ci  if(conn->cfilter[SECONDARYSOCKET]) {
359913498266Sopenharmony_ci    result = Curl_conn_connect(data, SECONDARYSOCKET, FALSE, &connected);
360013498266Sopenharmony_ci    if(result || !Curl_conn_is_ip_connected(data, SECONDARYSOCKET)) {
360113498266Sopenharmony_ci      if(result && (ftpc->count1 == 0)) {
360213498266Sopenharmony_ci        *completep = -1; /* go back to DOING please */
360313498266Sopenharmony_ci        /* this is a EPSV connect failing, try PASV instead */
360413498266Sopenharmony_ci        return ftp_epsv_disable(data, conn);
360513498266Sopenharmony_ci      }
360613498266Sopenharmony_ci      return result;
360713498266Sopenharmony_ci    }
360813498266Sopenharmony_ci  }
360913498266Sopenharmony_ci
361013498266Sopenharmony_ci  /* Curl_proxy_connect might have moved the protocol state */
361113498266Sopenharmony_ci  ftp = data->req.p.ftp;
361213498266Sopenharmony_ci
361313498266Sopenharmony_ci  if(ftpc->state) {
361413498266Sopenharmony_ci    /* already in a state so skip the initial commands.
361513498266Sopenharmony_ci       They are only done to kickstart the do_more state */
361613498266Sopenharmony_ci    result = ftp_multi_statemach(data, &complete);
361713498266Sopenharmony_ci
361813498266Sopenharmony_ci    *completep = (int)complete;
361913498266Sopenharmony_ci
362013498266Sopenharmony_ci    /* if we got an error or if we don't wait for a data connection return
362113498266Sopenharmony_ci       immediately */
362213498266Sopenharmony_ci    if(result || !ftpc->wait_data_conn)
362313498266Sopenharmony_ci      return result;
362413498266Sopenharmony_ci
362513498266Sopenharmony_ci    /* if we reach the end of the FTP state machine here, *complete will be
362613498266Sopenharmony_ci       TRUE but so is ftpc->wait_data_conn, which says we need to wait for the
362713498266Sopenharmony_ci       data connection and therefore we're not actually complete */
362813498266Sopenharmony_ci    *completep = 0;
362913498266Sopenharmony_ci  }
363013498266Sopenharmony_ci
363113498266Sopenharmony_ci  if(ftp->transfer <= PPTRANSFER_INFO) {
363213498266Sopenharmony_ci    /* a transfer is about to take place, or if not a file name was given
363313498266Sopenharmony_ci       so we'll do a SIZE on it later and then we need the right TYPE first */
363413498266Sopenharmony_ci
363513498266Sopenharmony_ci    if(ftpc->wait_data_conn) {
363613498266Sopenharmony_ci      bool serv_conned;
363713498266Sopenharmony_ci
363813498266Sopenharmony_ci      result = ReceivedServerConnect(data, &serv_conned);
363913498266Sopenharmony_ci      if(result)
364013498266Sopenharmony_ci        return result; /* Failed to accept data connection */
364113498266Sopenharmony_ci
364213498266Sopenharmony_ci      if(serv_conned) {
364313498266Sopenharmony_ci        /* It looks data connection is established */
364413498266Sopenharmony_ci        result = AcceptServerConnect(data);
364513498266Sopenharmony_ci        ftpc->wait_data_conn = FALSE;
364613498266Sopenharmony_ci        if(!result)
364713498266Sopenharmony_ci          result = InitiateTransfer(data);
364813498266Sopenharmony_ci
364913498266Sopenharmony_ci        if(result)
365013498266Sopenharmony_ci          return result;
365113498266Sopenharmony_ci
365213498266Sopenharmony_ci        *completep = 1; /* this state is now complete when the server has
365313498266Sopenharmony_ci                           connected back to us */
365413498266Sopenharmony_ci      }
365513498266Sopenharmony_ci    }
365613498266Sopenharmony_ci    else if(data->state.upload) {
365713498266Sopenharmony_ci      result = ftp_nb_type(data, conn, data->state.prefer_ascii,
365813498266Sopenharmony_ci                           FTP_STOR_TYPE);
365913498266Sopenharmony_ci      if(result)
366013498266Sopenharmony_ci        return result;
366113498266Sopenharmony_ci
366213498266Sopenharmony_ci      result = ftp_multi_statemach(data, &complete);
366313498266Sopenharmony_ci      *completep = (int)complete;
366413498266Sopenharmony_ci    }
366513498266Sopenharmony_ci    else {
366613498266Sopenharmony_ci      /* download */
366713498266Sopenharmony_ci      ftp->downloadsize = -1; /* unknown as of yet */
366813498266Sopenharmony_ci
366913498266Sopenharmony_ci      result = Curl_range(data);
367013498266Sopenharmony_ci
367113498266Sopenharmony_ci      if(result == CURLE_OK && data->req.maxdownload >= 0) {
367213498266Sopenharmony_ci        /* Don't check for successful transfer */
367313498266Sopenharmony_ci        ftpc->dont_check = TRUE;
367413498266Sopenharmony_ci      }
367513498266Sopenharmony_ci
367613498266Sopenharmony_ci      if(result)
367713498266Sopenharmony_ci        ;
367813498266Sopenharmony_ci      else if(data->state.list_only || !ftpc->file) {
367913498266Sopenharmony_ci        /* The specified path ends with a slash, and therefore we think this
368013498266Sopenharmony_ci           is a directory that is requested, use LIST. But before that we
368113498266Sopenharmony_ci           need to set ASCII transfer mode. */
368213498266Sopenharmony_ci
368313498266Sopenharmony_ci        /* But only if a body transfer was requested. */
368413498266Sopenharmony_ci        if(ftp->transfer == PPTRANSFER_BODY) {
368513498266Sopenharmony_ci          result = ftp_nb_type(data, conn, TRUE, FTP_LIST_TYPE);
368613498266Sopenharmony_ci          if(result)
368713498266Sopenharmony_ci            return result;
368813498266Sopenharmony_ci        }
368913498266Sopenharmony_ci        /* otherwise just fall through */
369013498266Sopenharmony_ci      }
369113498266Sopenharmony_ci      else {
369213498266Sopenharmony_ci        result = ftp_nb_type(data, conn, data->state.prefer_ascii,
369313498266Sopenharmony_ci                             FTP_RETR_TYPE);
369413498266Sopenharmony_ci        if(result)
369513498266Sopenharmony_ci          return result;
369613498266Sopenharmony_ci      }
369713498266Sopenharmony_ci
369813498266Sopenharmony_ci      result = ftp_multi_statemach(data, &complete);
369913498266Sopenharmony_ci      *completep = (int)complete;
370013498266Sopenharmony_ci    }
370113498266Sopenharmony_ci    return result;
370213498266Sopenharmony_ci  }
370313498266Sopenharmony_ci
370413498266Sopenharmony_ci  /* no data to transfer */
370513498266Sopenharmony_ci  Curl_setup_transfer(data, -1, -1, FALSE, -1);
370613498266Sopenharmony_ci
370713498266Sopenharmony_ci  if(!ftpc->wait_data_conn) {
370813498266Sopenharmony_ci    /* no waiting for the data connection so this is now complete */
370913498266Sopenharmony_ci    *completep = 1;
371013498266Sopenharmony_ci    DEBUGF(infof(data, "DO-MORE phase ends with %d", (int)result));
371113498266Sopenharmony_ci  }
371213498266Sopenharmony_ci
371313498266Sopenharmony_ci  return result;
371413498266Sopenharmony_ci}
371513498266Sopenharmony_ci
371613498266Sopenharmony_ci
371713498266Sopenharmony_ci
371813498266Sopenharmony_ci/***********************************************************************
371913498266Sopenharmony_ci *
372013498266Sopenharmony_ci * ftp_perform()
372113498266Sopenharmony_ci *
372213498266Sopenharmony_ci * This is the actual DO function for FTP. Get a file/directory according to
372313498266Sopenharmony_ci * the options previously setup.
372413498266Sopenharmony_ci */
372513498266Sopenharmony_ci
372613498266Sopenharmony_cistatic
372713498266Sopenharmony_ciCURLcode ftp_perform(struct Curl_easy *data,
372813498266Sopenharmony_ci                     bool *connected,  /* connect status after PASV / PORT */
372913498266Sopenharmony_ci                     bool *dophase_done)
373013498266Sopenharmony_ci{
373113498266Sopenharmony_ci  /* this is FTP and no proxy */
373213498266Sopenharmony_ci  CURLcode result = CURLE_OK;
373313498266Sopenharmony_ci
373413498266Sopenharmony_ci  DEBUGF(infof(data, "DO phase starts"));
373513498266Sopenharmony_ci
373613498266Sopenharmony_ci  if(data->req.no_body) {
373713498266Sopenharmony_ci    /* requested no body means no transfer... */
373813498266Sopenharmony_ci    struct FTP *ftp = data->req.p.ftp;
373913498266Sopenharmony_ci    ftp->transfer = PPTRANSFER_INFO;
374013498266Sopenharmony_ci  }
374113498266Sopenharmony_ci
374213498266Sopenharmony_ci  *dophase_done = FALSE; /* not done yet */
374313498266Sopenharmony_ci
374413498266Sopenharmony_ci  /* start the first command in the DO phase */
374513498266Sopenharmony_ci  result = ftp_state_quote(data, TRUE, FTP_QUOTE);
374613498266Sopenharmony_ci  if(result)
374713498266Sopenharmony_ci    return result;
374813498266Sopenharmony_ci
374913498266Sopenharmony_ci  /* run the state-machine */
375013498266Sopenharmony_ci  result = ftp_multi_statemach(data, dophase_done);
375113498266Sopenharmony_ci
375213498266Sopenharmony_ci  *connected = Curl_conn_is_connected(data->conn, SECONDARYSOCKET);
375313498266Sopenharmony_ci
375413498266Sopenharmony_ci  infof(data, "ftp_perform ends with SECONDARY: %d", *connected);
375513498266Sopenharmony_ci
375613498266Sopenharmony_ci  if(*dophase_done)
375713498266Sopenharmony_ci    DEBUGF(infof(data, "DO phase is complete1"));
375813498266Sopenharmony_ci
375913498266Sopenharmony_ci  return result;
376013498266Sopenharmony_ci}
376113498266Sopenharmony_ci
376213498266Sopenharmony_cistatic void wc_data_dtor(void *ptr)
376313498266Sopenharmony_ci{
376413498266Sopenharmony_ci  struct ftp_wc *ftpwc = ptr;
376513498266Sopenharmony_ci  if(ftpwc && ftpwc->parser)
376613498266Sopenharmony_ci    Curl_ftp_parselist_data_free(&ftpwc->parser);
376713498266Sopenharmony_ci  free(ftpwc);
376813498266Sopenharmony_ci}
376913498266Sopenharmony_ci
377013498266Sopenharmony_cistatic CURLcode init_wc_data(struct Curl_easy *data)
377113498266Sopenharmony_ci{
377213498266Sopenharmony_ci  char *last_slash;
377313498266Sopenharmony_ci  struct FTP *ftp = data->req.p.ftp;
377413498266Sopenharmony_ci  char *path = ftp->path;
377513498266Sopenharmony_ci  struct WildcardData *wildcard = data->wildcard;
377613498266Sopenharmony_ci  CURLcode result = CURLE_OK;
377713498266Sopenharmony_ci  struct ftp_wc *ftpwc = NULL;
377813498266Sopenharmony_ci
377913498266Sopenharmony_ci  last_slash = strrchr(ftp->path, '/');
378013498266Sopenharmony_ci  if(last_slash) {
378113498266Sopenharmony_ci    last_slash++;
378213498266Sopenharmony_ci    if(last_slash[0] == '\0') {
378313498266Sopenharmony_ci      wildcard->state = CURLWC_CLEAN;
378413498266Sopenharmony_ci      result = ftp_parse_url_path(data);
378513498266Sopenharmony_ci      return result;
378613498266Sopenharmony_ci    }
378713498266Sopenharmony_ci    wildcard->pattern = strdup(last_slash);
378813498266Sopenharmony_ci    if(!wildcard->pattern)
378913498266Sopenharmony_ci      return CURLE_OUT_OF_MEMORY;
379013498266Sopenharmony_ci    last_slash[0] = '\0'; /* cut file from path */
379113498266Sopenharmony_ci  }
379213498266Sopenharmony_ci  else { /* there is only 'wildcard pattern' or nothing */
379313498266Sopenharmony_ci    if(path[0]) {
379413498266Sopenharmony_ci      wildcard->pattern = strdup(path);
379513498266Sopenharmony_ci      if(!wildcard->pattern)
379613498266Sopenharmony_ci        return CURLE_OUT_OF_MEMORY;
379713498266Sopenharmony_ci      path[0] = '\0';
379813498266Sopenharmony_ci    }
379913498266Sopenharmony_ci    else { /* only list */
380013498266Sopenharmony_ci      wildcard->state = CURLWC_CLEAN;
380113498266Sopenharmony_ci      result = ftp_parse_url_path(data);
380213498266Sopenharmony_ci      return result;
380313498266Sopenharmony_ci    }
380413498266Sopenharmony_ci  }
380513498266Sopenharmony_ci
380613498266Sopenharmony_ci  /* program continues only if URL is not ending with slash, allocate needed
380713498266Sopenharmony_ci     resources for wildcard transfer */
380813498266Sopenharmony_ci
380913498266Sopenharmony_ci  /* allocate ftp protocol specific wildcard data */
381013498266Sopenharmony_ci  ftpwc = calloc(1, sizeof(struct ftp_wc));
381113498266Sopenharmony_ci  if(!ftpwc) {
381213498266Sopenharmony_ci    result = CURLE_OUT_OF_MEMORY;
381313498266Sopenharmony_ci    goto fail;
381413498266Sopenharmony_ci  }
381513498266Sopenharmony_ci
381613498266Sopenharmony_ci  /* INITIALIZE parselist structure */
381713498266Sopenharmony_ci  ftpwc->parser = Curl_ftp_parselist_data_alloc();
381813498266Sopenharmony_ci  if(!ftpwc->parser) {
381913498266Sopenharmony_ci    result = CURLE_OUT_OF_MEMORY;
382013498266Sopenharmony_ci    goto fail;
382113498266Sopenharmony_ci  }
382213498266Sopenharmony_ci
382313498266Sopenharmony_ci  wildcard->ftpwc = ftpwc; /* put it to the WildcardData tmp pointer */
382413498266Sopenharmony_ci  wildcard->dtor = wc_data_dtor;
382513498266Sopenharmony_ci
382613498266Sopenharmony_ci  /* wildcard does not support NOCWD option (assert it?) */
382713498266Sopenharmony_ci  if(data->set.ftp_filemethod == FTPFILE_NOCWD)
382813498266Sopenharmony_ci    data->set.ftp_filemethod = FTPFILE_MULTICWD;
382913498266Sopenharmony_ci
383013498266Sopenharmony_ci  /* try to parse ftp url */
383113498266Sopenharmony_ci  result = ftp_parse_url_path(data);
383213498266Sopenharmony_ci  if(result) {
383313498266Sopenharmony_ci    goto fail;
383413498266Sopenharmony_ci  }
383513498266Sopenharmony_ci
383613498266Sopenharmony_ci  wildcard->path = strdup(ftp->path);
383713498266Sopenharmony_ci  if(!wildcard->path) {
383813498266Sopenharmony_ci    result = CURLE_OUT_OF_MEMORY;
383913498266Sopenharmony_ci    goto fail;
384013498266Sopenharmony_ci  }
384113498266Sopenharmony_ci
384213498266Sopenharmony_ci  /* backup old write_function */
384313498266Sopenharmony_ci  ftpwc->backup.write_function = data->set.fwrite_func;
384413498266Sopenharmony_ci  /* parsing write function */
384513498266Sopenharmony_ci  data->set.fwrite_func = Curl_ftp_parselist;
384613498266Sopenharmony_ci  /* backup old file descriptor */
384713498266Sopenharmony_ci  ftpwc->backup.file_descriptor = data->set.out;
384813498266Sopenharmony_ci  /* let the writefunc callback know the transfer */
384913498266Sopenharmony_ci  data->set.out = data;
385013498266Sopenharmony_ci
385113498266Sopenharmony_ci  infof(data, "Wildcard - Parsing started");
385213498266Sopenharmony_ci  return CURLE_OK;
385313498266Sopenharmony_ci
385413498266Sopenharmony_cifail:
385513498266Sopenharmony_ci  if(ftpwc) {
385613498266Sopenharmony_ci    Curl_ftp_parselist_data_free(&ftpwc->parser);
385713498266Sopenharmony_ci    free(ftpwc);
385813498266Sopenharmony_ci  }
385913498266Sopenharmony_ci  Curl_safefree(wildcard->pattern);
386013498266Sopenharmony_ci  wildcard->dtor = ZERO_NULL;
386113498266Sopenharmony_ci  wildcard->ftpwc = NULL;
386213498266Sopenharmony_ci  return result;
386313498266Sopenharmony_ci}
386413498266Sopenharmony_ci
386513498266Sopenharmony_cistatic CURLcode wc_statemach(struct Curl_easy *data)
386613498266Sopenharmony_ci{
386713498266Sopenharmony_ci  struct WildcardData * const wildcard = data->wildcard;
386813498266Sopenharmony_ci  struct connectdata *conn = data->conn;
386913498266Sopenharmony_ci  CURLcode result = CURLE_OK;
387013498266Sopenharmony_ci
387113498266Sopenharmony_ci  for(;;) {
387213498266Sopenharmony_ci    switch(wildcard->state) {
387313498266Sopenharmony_ci    case CURLWC_INIT:
387413498266Sopenharmony_ci      result = init_wc_data(data);
387513498266Sopenharmony_ci      if(wildcard->state == CURLWC_CLEAN)
387613498266Sopenharmony_ci        /* only listing! */
387713498266Sopenharmony_ci        return result;
387813498266Sopenharmony_ci      wildcard->state = result ? CURLWC_ERROR : CURLWC_MATCHING;
387913498266Sopenharmony_ci      return result;
388013498266Sopenharmony_ci
388113498266Sopenharmony_ci    case CURLWC_MATCHING: {
388213498266Sopenharmony_ci      /* In this state is LIST response successfully parsed, so lets restore
388313498266Sopenharmony_ci         previous WRITEFUNCTION callback and WRITEDATA pointer */
388413498266Sopenharmony_ci      struct ftp_wc *ftpwc = wildcard->ftpwc;
388513498266Sopenharmony_ci      data->set.fwrite_func = ftpwc->backup.write_function;
388613498266Sopenharmony_ci      data->set.out = ftpwc->backup.file_descriptor;
388713498266Sopenharmony_ci      ftpwc->backup.write_function = ZERO_NULL;
388813498266Sopenharmony_ci      ftpwc->backup.file_descriptor = NULL;
388913498266Sopenharmony_ci      wildcard->state = CURLWC_DOWNLOADING;
389013498266Sopenharmony_ci
389113498266Sopenharmony_ci      if(Curl_ftp_parselist_geterror(ftpwc->parser)) {
389213498266Sopenharmony_ci        /* error found in LIST parsing */
389313498266Sopenharmony_ci        wildcard->state = CURLWC_CLEAN;
389413498266Sopenharmony_ci        continue;
389513498266Sopenharmony_ci      }
389613498266Sopenharmony_ci      if(wildcard->filelist.size == 0) {
389713498266Sopenharmony_ci        /* no corresponding file */
389813498266Sopenharmony_ci        wildcard->state = CURLWC_CLEAN;
389913498266Sopenharmony_ci        return CURLE_REMOTE_FILE_NOT_FOUND;
390013498266Sopenharmony_ci      }
390113498266Sopenharmony_ci      continue;
390213498266Sopenharmony_ci    }
390313498266Sopenharmony_ci
390413498266Sopenharmony_ci    case CURLWC_DOWNLOADING: {
390513498266Sopenharmony_ci      /* filelist has at least one file, lets get first one */
390613498266Sopenharmony_ci      struct ftp_conn *ftpc = &conn->proto.ftpc;
390713498266Sopenharmony_ci      struct curl_fileinfo *finfo = wildcard->filelist.head->ptr;
390813498266Sopenharmony_ci      struct FTP *ftp = data->req.p.ftp;
390913498266Sopenharmony_ci
391013498266Sopenharmony_ci      char *tmp_path = aprintf("%s%s", wildcard->path, finfo->filename);
391113498266Sopenharmony_ci      if(!tmp_path)
391213498266Sopenharmony_ci        return CURLE_OUT_OF_MEMORY;
391313498266Sopenharmony_ci
391413498266Sopenharmony_ci      /* switch default ftp->path and tmp_path */
391513498266Sopenharmony_ci      free(ftp->pathalloc);
391613498266Sopenharmony_ci      ftp->pathalloc = ftp->path = tmp_path;
391713498266Sopenharmony_ci
391813498266Sopenharmony_ci      infof(data, "Wildcard - START of \"%s\"", finfo->filename);
391913498266Sopenharmony_ci      if(data->set.chunk_bgn) {
392013498266Sopenharmony_ci        long userresponse;
392113498266Sopenharmony_ci        Curl_set_in_callback(data, true);
392213498266Sopenharmony_ci        userresponse = data->set.chunk_bgn(
392313498266Sopenharmony_ci          finfo, data->set.wildcardptr, (int)wildcard->filelist.size);
392413498266Sopenharmony_ci        Curl_set_in_callback(data, false);
392513498266Sopenharmony_ci        switch(userresponse) {
392613498266Sopenharmony_ci        case CURL_CHUNK_BGN_FUNC_SKIP:
392713498266Sopenharmony_ci          infof(data, "Wildcard - \"%s\" skipped by user",
392813498266Sopenharmony_ci                finfo->filename);
392913498266Sopenharmony_ci          wildcard->state = CURLWC_SKIP;
393013498266Sopenharmony_ci          continue;
393113498266Sopenharmony_ci        case CURL_CHUNK_BGN_FUNC_FAIL:
393213498266Sopenharmony_ci          return CURLE_CHUNK_FAILED;
393313498266Sopenharmony_ci        }
393413498266Sopenharmony_ci      }
393513498266Sopenharmony_ci
393613498266Sopenharmony_ci      if(finfo->filetype != CURLFILETYPE_FILE) {
393713498266Sopenharmony_ci        wildcard->state = CURLWC_SKIP;
393813498266Sopenharmony_ci        continue;
393913498266Sopenharmony_ci      }
394013498266Sopenharmony_ci
394113498266Sopenharmony_ci      if(finfo->flags & CURLFINFOFLAG_KNOWN_SIZE)
394213498266Sopenharmony_ci        ftpc->known_filesize = finfo->size;
394313498266Sopenharmony_ci
394413498266Sopenharmony_ci      result = ftp_parse_url_path(data);
394513498266Sopenharmony_ci      if(result)
394613498266Sopenharmony_ci        return result;
394713498266Sopenharmony_ci
394813498266Sopenharmony_ci      /* we don't need the Curl_fileinfo of first file anymore */
394913498266Sopenharmony_ci      Curl_llist_remove(&wildcard->filelist, wildcard->filelist.head, NULL);
395013498266Sopenharmony_ci
395113498266Sopenharmony_ci      if(wildcard->filelist.size == 0) { /* remains only one file to down. */
395213498266Sopenharmony_ci        wildcard->state = CURLWC_CLEAN;
395313498266Sopenharmony_ci        /* after that will be ftp_do called once again and no transfer
395413498266Sopenharmony_ci           will be done because of CURLWC_CLEAN state */
395513498266Sopenharmony_ci        return CURLE_OK;
395613498266Sopenharmony_ci      }
395713498266Sopenharmony_ci      return result;
395813498266Sopenharmony_ci    }
395913498266Sopenharmony_ci
396013498266Sopenharmony_ci    case CURLWC_SKIP: {
396113498266Sopenharmony_ci      if(data->set.chunk_end) {
396213498266Sopenharmony_ci        Curl_set_in_callback(data, true);
396313498266Sopenharmony_ci        data->set.chunk_end(data->set.wildcardptr);
396413498266Sopenharmony_ci        Curl_set_in_callback(data, false);
396513498266Sopenharmony_ci      }
396613498266Sopenharmony_ci      Curl_llist_remove(&wildcard->filelist, wildcard->filelist.head, NULL);
396713498266Sopenharmony_ci      wildcard->state = (wildcard->filelist.size == 0) ?
396813498266Sopenharmony_ci        CURLWC_CLEAN : CURLWC_DOWNLOADING;
396913498266Sopenharmony_ci      continue;
397013498266Sopenharmony_ci    }
397113498266Sopenharmony_ci
397213498266Sopenharmony_ci    case CURLWC_CLEAN: {
397313498266Sopenharmony_ci      struct ftp_wc *ftpwc = wildcard->ftpwc;
397413498266Sopenharmony_ci      result = CURLE_OK;
397513498266Sopenharmony_ci      if(ftpwc)
397613498266Sopenharmony_ci        result = Curl_ftp_parselist_geterror(ftpwc->parser);
397713498266Sopenharmony_ci
397813498266Sopenharmony_ci      wildcard->state = result ? CURLWC_ERROR : CURLWC_DONE;
397913498266Sopenharmony_ci      return result;
398013498266Sopenharmony_ci    }
398113498266Sopenharmony_ci
398213498266Sopenharmony_ci    case CURLWC_DONE:
398313498266Sopenharmony_ci    case CURLWC_ERROR:
398413498266Sopenharmony_ci    case CURLWC_CLEAR:
398513498266Sopenharmony_ci      if(wildcard->dtor) {
398613498266Sopenharmony_ci        wildcard->dtor(wildcard->ftpwc);
398713498266Sopenharmony_ci        wildcard->ftpwc = NULL;
398813498266Sopenharmony_ci      }
398913498266Sopenharmony_ci      return result;
399013498266Sopenharmony_ci    }
399113498266Sopenharmony_ci  }
399213498266Sopenharmony_ci  /* UNREACHABLE */
399313498266Sopenharmony_ci}
399413498266Sopenharmony_ci
399513498266Sopenharmony_ci/***********************************************************************
399613498266Sopenharmony_ci *
399713498266Sopenharmony_ci * ftp_do()
399813498266Sopenharmony_ci *
399913498266Sopenharmony_ci * This function is registered as 'curl_do' function. It decodes the path
400013498266Sopenharmony_ci * parts etc as a wrapper to the actual DO function (ftp_perform).
400113498266Sopenharmony_ci *
400213498266Sopenharmony_ci * The input argument is already checked for validity.
400313498266Sopenharmony_ci */
400413498266Sopenharmony_cistatic CURLcode ftp_do(struct Curl_easy *data, bool *done)
400513498266Sopenharmony_ci{
400613498266Sopenharmony_ci  CURLcode result = CURLE_OK;
400713498266Sopenharmony_ci  struct connectdata *conn = data->conn;
400813498266Sopenharmony_ci  struct ftp_conn *ftpc = &conn->proto.ftpc;
400913498266Sopenharmony_ci
401013498266Sopenharmony_ci  *done = FALSE; /* default to false */
401113498266Sopenharmony_ci  ftpc->wait_data_conn = FALSE; /* default to no such wait */
401213498266Sopenharmony_ci
401313498266Sopenharmony_ci  if(data->state.wildcardmatch) {
401413498266Sopenharmony_ci    result = wc_statemach(data);
401513498266Sopenharmony_ci    if(data->wildcard->state == CURLWC_SKIP ||
401613498266Sopenharmony_ci       data->wildcard->state == CURLWC_DONE) {
401713498266Sopenharmony_ci      /* do not call ftp_regular_transfer */
401813498266Sopenharmony_ci      return CURLE_OK;
401913498266Sopenharmony_ci    }
402013498266Sopenharmony_ci    if(result) /* error, loop or skipping the file */
402113498266Sopenharmony_ci      return result;
402213498266Sopenharmony_ci  }
402313498266Sopenharmony_ci  else { /* no wildcard FSM needed */
402413498266Sopenharmony_ci    result = ftp_parse_url_path(data);
402513498266Sopenharmony_ci    if(result)
402613498266Sopenharmony_ci      return result;
402713498266Sopenharmony_ci  }
402813498266Sopenharmony_ci
402913498266Sopenharmony_ci  result = ftp_regular_transfer(data, done);
403013498266Sopenharmony_ci
403113498266Sopenharmony_ci  return result;
403213498266Sopenharmony_ci}
403313498266Sopenharmony_ci
403413498266Sopenharmony_ci/***********************************************************************
403513498266Sopenharmony_ci *
403613498266Sopenharmony_ci * ftp_quit()
403713498266Sopenharmony_ci *
403813498266Sopenharmony_ci * This should be called before calling sclose() on an ftp control connection
403913498266Sopenharmony_ci * (not data connections). We should then wait for the response from the
404013498266Sopenharmony_ci * server before returning. The calling code should then try to close the
404113498266Sopenharmony_ci * connection.
404213498266Sopenharmony_ci *
404313498266Sopenharmony_ci */
404413498266Sopenharmony_cistatic CURLcode ftp_quit(struct Curl_easy *data, struct connectdata *conn)
404513498266Sopenharmony_ci{
404613498266Sopenharmony_ci  CURLcode result = CURLE_OK;
404713498266Sopenharmony_ci
404813498266Sopenharmony_ci  if(conn->proto.ftpc.ctl_valid) {
404913498266Sopenharmony_ci    result = Curl_pp_sendf(data, &conn->proto.ftpc.pp, "%s", "QUIT");
405013498266Sopenharmony_ci    if(result) {
405113498266Sopenharmony_ci      failf(data, "Failure sending QUIT command: %s",
405213498266Sopenharmony_ci            curl_easy_strerror(result));
405313498266Sopenharmony_ci      conn->proto.ftpc.ctl_valid = FALSE; /* mark control connection as bad */
405413498266Sopenharmony_ci      connclose(conn, "QUIT command failed"); /* mark for connection closure */
405513498266Sopenharmony_ci      ftp_state(data, FTP_STOP);
405613498266Sopenharmony_ci      return result;
405713498266Sopenharmony_ci    }
405813498266Sopenharmony_ci
405913498266Sopenharmony_ci    ftp_state(data, FTP_QUIT);
406013498266Sopenharmony_ci
406113498266Sopenharmony_ci    result = ftp_block_statemach(data, conn);
406213498266Sopenharmony_ci  }
406313498266Sopenharmony_ci
406413498266Sopenharmony_ci  return result;
406513498266Sopenharmony_ci}
406613498266Sopenharmony_ci
406713498266Sopenharmony_ci/***********************************************************************
406813498266Sopenharmony_ci *
406913498266Sopenharmony_ci * ftp_disconnect()
407013498266Sopenharmony_ci *
407113498266Sopenharmony_ci * Disconnect from an FTP server. Cleanup protocol-specific per-connection
407213498266Sopenharmony_ci * resources. BLOCKING.
407313498266Sopenharmony_ci */
407413498266Sopenharmony_cistatic CURLcode ftp_disconnect(struct Curl_easy *data,
407513498266Sopenharmony_ci                               struct connectdata *conn,
407613498266Sopenharmony_ci                               bool dead_connection)
407713498266Sopenharmony_ci{
407813498266Sopenharmony_ci  struct ftp_conn *ftpc = &conn->proto.ftpc;
407913498266Sopenharmony_ci  struct pingpong *pp = &ftpc->pp;
408013498266Sopenharmony_ci
408113498266Sopenharmony_ci  /* We cannot send quit unconditionally. If this connection is stale or
408213498266Sopenharmony_ci     bad in any way, sending quit and waiting around here will make the
408313498266Sopenharmony_ci     disconnect wait in vain and cause more problems than we need to.
408413498266Sopenharmony_ci
408513498266Sopenharmony_ci     ftp_quit() will check the state of ftp->ctl_valid. If it's ok it
408613498266Sopenharmony_ci     will try to send the QUIT command, otherwise it will just return.
408713498266Sopenharmony_ci  */
408813498266Sopenharmony_ci  if(dead_connection)
408913498266Sopenharmony_ci    ftpc->ctl_valid = FALSE;
409013498266Sopenharmony_ci
409113498266Sopenharmony_ci  /* The FTP session may or may not have been allocated/setup at this point! */
409213498266Sopenharmony_ci  (void)ftp_quit(data, conn); /* ignore errors on the QUIT */
409313498266Sopenharmony_ci
409413498266Sopenharmony_ci  if(ftpc->entrypath) {
409513498266Sopenharmony_ci    if(data->state.most_recent_ftp_entrypath == ftpc->entrypath) {
409613498266Sopenharmony_ci      data->state.most_recent_ftp_entrypath = NULL;
409713498266Sopenharmony_ci    }
409813498266Sopenharmony_ci    Curl_safefree(ftpc->entrypath);
409913498266Sopenharmony_ci  }
410013498266Sopenharmony_ci
410113498266Sopenharmony_ci  freedirs(ftpc);
410213498266Sopenharmony_ci  Curl_safefree(ftpc->account);
410313498266Sopenharmony_ci  Curl_safefree(ftpc->alternative_to_user);
410413498266Sopenharmony_ci  Curl_safefree(ftpc->prevpath);
410513498266Sopenharmony_ci  Curl_safefree(ftpc->server_os);
410613498266Sopenharmony_ci  Curl_pp_disconnect(pp);
410713498266Sopenharmony_ci  Curl_sec_end(conn);
410813498266Sopenharmony_ci  return CURLE_OK;
410913498266Sopenharmony_ci}
411013498266Sopenharmony_ci
411113498266Sopenharmony_ci#ifdef _MSC_VER
411213498266Sopenharmony_ci/* warning C4706: assignment within conditional expression */
411313498266Sopenharmony_ci#pragma warning(disable:4706)
411413498266Sopenharmony_ci#endif
411513498266Sopenharmony_ci
411613498266Sopenharmony_ci/***********************************************************************
411713498266Sopenharmony_ci *
411813498266Sopenharmony_ci * ftp_parse_url_path()
411913498266Sopenharmony_ci *
412013498266Sopenharmony_ci * Parse the URL path into separate path components.
412113498266Sopenharmony_ci *
412213498266Sopenharmony_ci */
412313498266Sopenharmony_cistatic
412413498266Sopenharmony_ciCURLcode ftp_parse_url_path(struct Curl_easy *data)
412513498266Sopenharmony_ci{
412613498266Sopenharmony_ci  /* the ftp struct is already inited in ftp_connect() */
412713498266Sopenharmony_ci  struct FTP *ftp = data->req.p.ftp;
412813498266Sopenharmony_ci  struct connectdata *conn = data->conn;
412913498266Sopenharmony_ci  struct ftp_conn *ftpc = &conn->proto.ftpc;
413013498266Sopenharmony_ci  const char *slashPos = NULL;
413113498266Sopenharmony_ci  const char *fileName = NULL;
413213498266Sopenharmony_ci  CURLcode result = CURLE_OK;
413313498266Sopenharmony_ci  char *rawPath = NULL; /* url-decoded "raw" path */
413413498266Sopenharmony_ci  size_t pathLen = 0;
413513498266Sopenharmony_ci
413613498266Sopenharmony_ci  ftpc->ctl_valid = FALSE;
413713498266Sopenharmony_ci  ftpc->cwdfail = FALSE;
413813498266Sopenharmony_ci
413913498266Sopenharmony_ci  /* url-decode ftp path before further evaluation */
414013498266Sopenharmony_ci  result = Curl_urldecode(ftp->path, 0, &rawPath, &pathLen, REJECT_CTRL);
414113498266Sopenharmony_ci  if(result) {
414213498266Sopenharmony_ci    failf(data, "path contains control characters");
414313498266Sopenharmony_ci    return result;
414413498266Sopenharmony_ci  }
414513498266Sopenharmony_ci
414613498266Sopenharmony_ci  switch(data->set.ftp_filemethod) {
414713498266Sopenharmony_ci    case FTPFILE_NOCWD: /* fastest, but less standard-compliant */
414813498266Sopenharmony_ci
414913498266Sopenharmony_ci      if((pathLen > 0) && (rawPath[pathLen - 1] != '/'))
415013498266Sopenharmony_ci        fileName = rawPath;  /* this is a full file path */
415113498266Sopenharmony_ci      /*
415213498266Sopenharmony_ci        else: ftpc->file is not used anywhere other than for operations on
415313498266Sopenharmony_ci              a file. In other words, never for directory operations.
415413498266Sopenharmony_ci              So we can safely leave filename as NULL here and use it as a
415513498266Sopenharmony_ci              argument in dir/file decisions.
415613498266Sopenharmony_ci      */
415713498266Sopenharmony_ci      break;
415813498266Sopenharmony_ci
415913498266Sopenharmony_ci    case FTPFILE_SINGLECWD:
416013498266Sopenharmony_ci      slashPos = strrchr(rawPath, '/');
416113498266Sopenharmony_ci      if(slashPos) {
416213498266Sopenharmony_ci        /* get path before last slash, except for / */
416313498266Sopenharmony_ci        size_t dirlen = slashPos - rawPath;
416413498266Sopenharmony_ci        if(dirlen == 0)
416513498266Sopenharmony_ci          dirlen = 1;
416613498266Sopenharmony_ci
416713498266Sopenharmony_ci        ftpc->dirs = calloc(1, sizeof(ftpc->dirs[0]));
416813498266Sopenharmony_ci        if(!ftpc->dirs) {
416913498266Sopenharmony_ci          free(rawPath);
417013498266Sopenharmony_ci          return CURLE_OUT_OF_MEMORY;
417113498266Sopenharmony_ci        }
417213498266Sopenharmony_ci
417313498266Sopenharmony_ci        ftpc->dirs[0] = Curl_memdup0(rawPath, dirlen);
417413498266Sopenharmony_ci        if(!ftpc->dirs[0]) {
417513498266Sopenharmony_ci          free(rawPath);
417613498266Sopenharmony_ci          return CURLE_OUT_OF_MEMORY;
417713498266Sopenharmony_ci        }
417813498266Sopenharmony_ci
417913498266Sopenharmony_ci        ftpc->dirdepth = 1; /* we consider it to be a single dir */
418013498266Sopenharmony_ci        fileName = slashPos + 1; /* rest is file name */
418113498266Sopenharmony_ci      }
418213498266Sopenharmony_ci      else
418313498266Sopenharmony_ci        fileName = rawPath; /* file name only (or empty) */
418413498266Sopenharmony_ci      break;
418513498266Sopenharmony_ci
418613498266Sopenharmony_ci    default: /* allow pretty much anything */
418713498266Sopenharmony_ci    case FTPFILE_MULTICWD: {
418813498266Sopenharmony_ci      /* current position: begin of next path component */
418913498266Sopenharmony_ci      const char *curPos = rawPath;
419013498266Sopenharmony_ci
419113498266Sopenharmony_ci      /* number of entries allocated for the 'dirs' array */
419213498266Sopenharmony_ci      size_t dirAlloc = 0;
419313498266Sopenharmony_ci      const char *str = rawPath;
419413498266Sopenharmony_ci      for(; *str != 0; ++str)
419513498266Sopenharmony_ci        if(*str == '/')
419613498266Sopenharmony_ci          ++dirAlloc;
419713498266Sopenharmony_ci
419813498266Sopenharmony_ci      if(dirAlloc) {
419913498266Sopenharmony_ci        ftpc->dirs = calloc(dirAlloc, sizeof(ftpc->dirs[0]));
420013498266Sopenharmony_ci        if(!ftpc->dirs) {
420113498266Sopenharmony_ci          free(rawPath);
420213498266Sopenharmony_ci          return CURLE_OUT_OF_MEMORY;
420313498266Sopenharmony_ci        }
420413498266Sopenharmony_ci
420513498266Sopenharmony_ci        /* parse the URL path into separate path components */
420613498266Sopenharmony_ci        while((slashPos = strchr(curPos, '/'))) {
420713498266Sopenharmony_ci          size_t compLen = slashPos - curPos;
420813498266Sopenharmony_ci
420913498266Sopenharmony_ci          /* path starts with a slash: add that as a directory */
421013498266Sopenharmony_ci          if((compLen == 0) && (ftpc->dirdepth == 0))
421113498266Sopenharmony_ci            ++compLen;
421213498266Sopenharmony_ci
421313498266Sopenharmony_ci          /* we skip empty path components, like "x//y" since the FTP command
421413498266Sopenharmony_ci             CWD requires a parameter and a non-existent parameter a) doesn't
421513498266Sopenharmony_ci             work on many servers and b) has no effect on the others. */
421613498266Sopenharmony_ci          if(compLen > 0) {
421713498266Sopenharmony_ci            char *comp = Curl_memdup0(curPos, compLen);
421813498266Sopenharmony_ci            if(!comp) {
421913498266Sopenharmony_ci              free(rawPath);
422013498266Sopenharmony_ci              return CURLE_OUT_OF_MEMORY;
422113498266Sopenharmony_ci            }
422213498266Sopenharmony_ci            ftpc->dirs[ftpc->dirdepth++] = comp;
422313498266Sopenharmony_ci          }
422413498266Sopenharmony_ci          curPos = slashPos + 1;
422513498266Sopenharmony_ci        }
422613498266Sopenharmony_ci      }
422713498266Sopenharmony_ci      DEBUGASSERT((size_t)ftpc->dirdepth <= dirAlloc);
422813498266Sopenharmony_ci      fileName = curPos; /* the rest is the file name (or empty) */
422913498266Sopenharmony_ci    }
423013498266Sopenharmony_ci    break;
423113498266Sopenharmony_ci  } /* switch */
423213498266Sopenharmony_ci
423313498266Sopenharmony_ci  if(fileName && *fileName)
423413498266Sopenharmony_ci    ftpc->file = strdup(fileName);
423513498266Sopenharmony_ci  else
423613498266Sopenharmony_ci    ftpc->file = NULL; /* instead of point to a zero byte,
423713498266Sopenharmony_ci                            we make it a NULL pointer */
423813498266Sopenharmony_ci
423913498266Sopenharmony_ci  if(data->state.upload && !ftpc->file && (ftp->transfer == PPTRANSFER_BODY)) {
424013498266Sopenharmony_ci    /* We need a file name when uploading. Return error! */
424113498266Sopenharmony_ci    failf(data, "Uploading to a URL without a file name");
424213498266Sopenharmony_ci    free(rawPath);
424313498266Sopenharmony_ci    return CURLE_URL_MALFORMAT;
424413498266Sopenharmony_ci  }
424513498266Sopenharmony_ci
424613498266Sopenharmony_ci  ftpc->cwddone = FALSE; /* default to not done */
424713498266Sopenharmony_ci
424813498266Sopenharmony_ci  if((data->set.ftp_filemethod == FTPFILE_NOCWD) && (rawPath[0] == '/'))
424913498266Sopenharmony_ci    ftpc->cwddone = TRUE; /* skip CWD for absolute paths */
425013498266Sopenharmony_ci  else { /* newly created FTP connections are already in entry path */
425113498266Sopenharmony_ci    const char *oldPath = conn->bits.reuse ? ftpc->prevpath : "";
425213498266Sopenharmony_ci    if(oldPath) {
425313498266Sopenharmony_ci      size_t n = pathLen;
425413498266Sopenharmony_ci      if(data->set.ftp_filemethod == FTPFILE_NOCWD)
425513498266Sopenharmony_ci        n = 0; /* CWD to entry for relative paths */
425613498266Sopenharmony_ci      else
425713498266Sopenharmony_ci        n -= ftpc->file?strlen(ftpc->file):0;
425813498266Sopenharmony_ci
425913498266Sopenharmony_ci      if((strlen(oldPath) == n) && !strncmp(rawPath, oldPath, n)) {
426013498266Sopenharmony_ci        infof(data, "Request has same path as previous transfer");
426113498266Sopenharmony_ci        ftpc->cwddone = TRUE;
426213498266Sopenharmony_ci      }
426313498266Sopenharmony_ci    }
426413498266Sopenharmony_ci  }
426513498266Sopenharmony_ci
426613498266Sopenharmony_ci  free(rawPath);
426713498266Sopenharmony_ci  return CURLE_OK;
426813498266Sopenharmony_ci}
426913498266Sopenharmony_ci
427013498266Sopenharmony_ci/* call this when the DO phase has completed */
427113498266Sopenharmony_cistatic CURLcode ftp_dophase_done(struct Curl_easy *data, bool connected)
427213498266Sopenharmony_ci{
427313498266Sopenharmony_ci  struct connectdata *conn = data->conn;
427413498266Sopenharmony_ci  struct FTP *ftp = data->req.p.ftp;
427513498266Sopenharmony_ci  struct ftp_conn *ftpc = &conn->proto.ftpc;
427613498266Sopenharmony_ci
427713498266Sopenharmony_ci  if(connected) {
427813498266Sopenharmony_ci    int completed;
427913498266Sopenharmony_ci    CURLcode result = ftp_do_more(data, &completed);
428013498266Sopenharmony_ci
428113498266Sopenharmony_ci    if(result) {
428213498266Sopenharmony_ci      close_secondarysocket(data, conn);
428313498266Sopenharmony_ci      return result;
428413498266Sopenharmony_ci    }
428513498266Sopenharmony_ci  }
428613498266Sopenharmony_ci
428713498266Sopenharmony_ci  if(ftp->transfer != PPTRANSFER_BODY)
428813498266Sopenharmony_ci    /* no data to transfer */
428913498266Sopenharmony_ci    Curl_setup_transfer(data, -1, -1, FALSE, -1);
429013498266Sopenharmony_ci  else if(!connected)
429113498266Sopenharmony_ci    /* since we didn't connect now, we want do_more to get called */
429213498266Sopenharmony_ci    conn->bits.do_more = TRUE;
429313498266Sopenharmony_ci
429413498266Sopenharmony_ci  ftpc->ctl_valid = TRUE; /* seems good */
429513498266Sopenharmony_ci
429613498266Sopenharmony_ci  return CURLE_OK;
429713498266Sopenharmony_ci}
429813498266Sopenharmony_ci
429913498266Sopenharmony_ci/* called from multi.c while DOing */
430013498266Sopenharmony_cistatic CURLcode ftp_doing(struct Curl_easy *data,
430113498266Sopenharmony_ci                          bool *dophase_done)
430213498266Sopenharmony_ci{
430313498266Sopenharmony_ci  CURLcode result = ftp_multi_statemach(data, dophase_done);
430413498266Sopenharmony_ci
430513498266Sopenharmony_ci  if(result)
430613498266Sopenharmony_ci    DEBUGF(infof(data, "DO phase failed"));
430713498266Sopenharmony_ci  else if(*dophase_done) {
430813498266Sopenharmony_ci    result = ftp_dophase_done(data, FALSE /* not connected */);
430913498266Sopenharmony_ci
431013498266Sopenharmony_ci    DEBUGF(infof(data, "DO phase is complete2"));
431113498266Sopenharmony_ci  }
431213498266Sopenharmony_ci  return result;
431313498266Sopenharmony_ci}
431413498266Sopenharmony_ci
431513498266Sopenharmony_ci/***********************************************************************
431613498266Sopenharmony_ci *
431713498266Sopenharmony_ci * ftp_regular_transfer()
431813498266Sopenharmony_ci *
431913498266Sopenharmony_ci * The input argument is already checked for validity.
432013498266Sopenharmony_ci *
432113498266Sopenharmony_ci * Performs all commands done before a regular transfer between a local and a
432213498266Sopenharmony_ci * remote host.
432313498266Sopenharmony_ci *
432413498266Sopenharmony_ci * ftp->ctl_valid starts out as FALSE, and gets set to TRUE if we reach the
432513498266Sopenharmony_ci * ftp_done() function without finding any major problem.
432613498266Sopenharmony_ci */
432713498266Sopenharmony_cistatic
432813498266Sopenharmony_ciCURLcode ftp_regular_transfer(struct Curl_easy *data,
432913498266Sopenharmony_ci                              bool *dophase_done)
433013498266Sopenharmony_ci{
433113498266Sopenharmony_ci  CURLcode result = CURLE_OK;
433213498266Sopenharmony_ci  bool connected = FALSE;
433313498266Sopenharmony_ci  struct connectdata *conn = data->conn;
433413498266Sopenharmony_ci  struct ftp_conn *ftpc = &conn->proto.ftpc;
433513498266Sopenharmony_ci  data->req.size = -1; /* make sure this is unknown at this point */
433613498266Sopenharmony_ci
433713498266Sopenharmony_ci  Curl_pgrsSetUploadCounter(data, 0);
433813498266Sopenharmony_ci  Curl_pgrsSetDownloadCounter(data, 0);
433913498266Sopenharmony_ci  Curl_pgrsSetUploadSize(data, -1);
434013498266Sopenharmony_ci  Curl_pgrsSetDownloadSize(data, -1);
434113498266Sopenharmony_ci
434213498266Sopenharmony_ci  ftpc->ctl_valid = TRUE; /* starts good */
434313498266Sopenharmony_ci
434413498266Sopenharmony_ci  result = ftp_perform(data,
434513498266Sopenharmony_ci                       &connected, /* have we connected after PASV/PORT */
434613498266Sopenharmony_ci                       dophase_done); /* all commands in the DO-phase done? */
434713498266Sopenharmony_ci
434813498266Sopenharmony_ci  if(!result) {
434913498266Sopenharmony_ci
435013498266Sopenharmony_ci    if(!*dophase_done)
435113498266Sopenharmony_ci      /* the DO phase has not completed yet */
435213498266Sopenharmony_ci      return CURLE_OK;
435313498266Sopenharmony_ci
435413498266Sopenharmony_ci    result = ftp_dophase_done(data, connected);
435513498266Sopenharmony_ci
435613498266Sopenharmony_ci    if(result)
435713498266Sopenharmony_ci      return result;
435813498266Sopenharmony_ci  }
435913498266Sopenharmony_ci  else
436013498266Sopenharmony_ci    freedirs(ftpc);
436113498266Sopenharmony_ci
436213498266Sopenharmony_ci  return result;
436313498266Sopenharmony_ci}
436413498266Sopenharmony_ci
436513498266Sopenharmony_cistatic CURLcode ftp_setup_connection(struct Curl_easy *data,
436613498266Sopenharmony_ci                                     struct connectdata *conn)
436713498266Sopenharmony_ci{
436813498266Sopenharmony_ci  char *type;
436913498266Sopenharmony_ci  struct FTP *ftp;
437013498266Sopenharmony_ci  CURLcode result = CURLE_OK;
437113498266Sopenharmony_ci  struct ftp_conn *ftpc = &conn->proto.ftpc;
437213498266Sopenharmony_ci
437313498266Sopenharmony_ci  ftp = calloc(1, sizeof(struct FTP));
437413498266Sopenharmony_ci  if(!ftp)
437513498266Sopenharmony_ci    return CURLE_OUT_OF_MEMORY;
437613498266Sopenharmony_ci
437713498266Sopenharmony_ci  /* clone connection related data that is FTP specific */
437813498266Sopenharmony_ci  if(data->set.str[STRING_FTP_ACCOUNT]) {
437913498266Sopenharmony_ci    ftpc->account = strdup(data->set.str[STRING_FTP_ACCOUNT]);
438013498266Sopenharmony_ci    if(!ftpc->account) {
438113498266Sopenharmony_ci      free(ftp);
438213498266Sopenharmony_ci      return CURLE_OUT_OF_MEMORY;
438313498266Sopenharmony_ci    }
438413498266Sopenharmony_ci  }
438513498266Sopenharmony_ci  if(data->set.str[STRING_FTP_ALTERNATIVE_TO_USER]) {
438613498266Sopenharmony_ci    ftpc->alternative_to_user =
438713498266Sopenharmony_ci      strdup(data->set.str[STRING_FTP_ALTERNATIVE_TO_USER]);
438813498266Sopenharmony_ci    if(!ftpc->alternative_to_user) {
438913498266Sopenharmony_ci      Curl_safefree(ftpc->account);
439013498266Sopenharmony_ci      free(ftp);
439113498266Sopenharmony_ci      return CURLE_OUT_OF_MEMORY;
439213498266Sopenharmony_ci    }
439313498266Sopenharmony_ci  }
439413498266Sopenharmony_ci  data->req.p.ftp = ftp;
439513498266Sopenharmony_ci
439613498266Sopenharmony_ci  ftp->path = &data->state.up.path[1]; /* don't include the initial slash */
439713498266Sopenharmony_ci
439813498266Sopenharmony_ci  /* FTP URLs support an extension like ";type=<typecode>" that
439913498266Sopenharmony_ci   * we'll try to get now! */
440013498266Sopenharmony_ci  type = strstr(ftp->path, ";type=");
440113498266Sopenharmony_ci
440213498266Sopenharmony_ci  if(!type)
440313498266Sopenharmony_ci    type = strstr(conn->host.rawalloc, ";type=");
440413498266Sopenharmony_ci
440513498266Sopenharmony_ci  if(type) {
440613498266Sopenharmony_ci    char command;
440713498266Sopenharmony_ci    *type = 0;                     /* it was in the middle of the hostname */
440813498266Sopenharmony_ci    command = Curl_raw_toupper(type[6]);
440913498266Sopenharmony_ci
441013498266Sopenharmony_ci    switch(command) {
441113498266Sopenharmony_ci    case 'A': /* ASCII mode */
441213498266Sopenharmony_ci      data->state.prefer_ascii = TRUE;
441313498266Sopenharmony_ci      break;
441413498266Sopenharmony_ci
441513498266Sopenharmony_ci    case 'D': /* directory mode */
441613498266Sopenharmony_ci      data->state.list_only = TRUE;
441713498266Sopenharmony_ci      break;
441813498266Sopenharmony_ci
441913498266Sopenharmony_ci    case 'I': /* binary mode */
442013498266Sopenharmony_ci    default:
442113498266Sopenharmony_ci      /* switch off ASCII */
442213498266Sopenharmony_ci      data->state.prefer_ascii = FALSE;
442313498266Sopenharmony_ci      break;
442413498266Sopenharmony_ci    }
442513498266Sopenharmony_ci  }
442613498266Sopenharmony_ci
442713498266Sopenharmony_ci  /* get some initial data into the ftp struct */
442813498266Sopenharmony_ci  ftp->transfer = PPTRANSFER_BODY;
442913498266Sopenharmony_ci  ftp->downloadsize = 0;
443013498266Sopenharmony_ci  ftpc->known_filesize = -1; /* unknown size for now */
443113498266Sopenharmony_ci  ftpc->use_ssl = data->set.use_ssl;
443213498266Sopenharmony_ci  ftpc->ccc = data->set.ftp_ccc;
443313498266Sopenharmony_ci
443413498266Sopenharmony_ci  return result;
443513498266Sopenharmony_ci}
443613498266Sopenharmony_ci
443713498266Sopenharmony_ci#endif /* CURL_DISABLE_FTP */
4438