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