diff options
Diffstat (limited to 'plugins/FTPFileYM/curl/lib/ftp.c')
-rw-r--r-- | plugins/FTPFileYM/curl/lib/ftp.c | 551 |
1 files changed, 257 insertions, 294 deletions
diff --git a/plugins/FTPFileYM/curl/lib/ftp.c b/plugins/FTPFileYM/curl/lib/ftp.c index 469b887496..e818af6c58 100644 --- a/plugins/FTPFileYM/curl/lib/ftp.c +++ b/plugins/FTPFileYM/curl/lib/ftp.c @@ -59,11 +59,7 @@ #include "ftp.h" #include "fileinfo.h" #include "ftplistparser.h" - -#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI) -#include "krb4.h" -#endif - +#include "curl_sec.h" #include "strtoofft.h" #include "strequal.h" #include "sslgen.h" @@ -123,8 +119,8 @@ static void ftp_pasv_verbose(struct connectdata *conn, char *newhost, /* ascii version */ int port); #endif -static CURLcode ftp_state_post_rest(struct connectdata *conn); -static CURLcode ftp_state_post_cwd(struct connectdata *conn); +static CURLcode ftp_state_prepare_transfer(struct connectdata *conn); +static CURLcode ftp_state_mdtm(struct connectdata *conn); static CURLcode ftp_state_quote(struct connectdata *conn, bool init, ftpstate instate); static CURLcode ftp_nb_type(struct connectdata *conn, @@ -136,7 +132,7 @@ static CURLcode ftp_done(struct connectdata *conn, CURLcode, bool premature); static CURLcode ftp_connect(struct connectdata *conn, bool *done); static CURLcode ftp_disconnect(struct connectdata *conn, bool dead_connection); -static CURLcode ftp_do_more(struct connectdata *conn, bool *completed); +static CURLcode ftp_do_more(struct connectdata *conn, int *completed); static CURLcode ftp_multi_statemach(struct connectdata *conn, bool *done); static int ftp_getsock(struct connectdata *conn, curl_socket_t *socks, int numsocks); @@ -151,8 +147,7 @@ static CURLcode wc_statemach(struct connectdata *conn); static void wc_data_dtor(void *ptr); -static CURLcode ftp_state_post_retr_size(struct connectdata *conn, - curl_off_t filesize); +static CURLcode ftp_state_retr(struct connectdata *conn, curl_off_t filesize); static CURLcode ftp_readresp(curl_socket_t sockfd, struct pingpong *pp, @@ -162,8 +157,6 @@ static CURLcode ftp_dophase_done(struct connectdata *conn, bool connected); /* easy-to-use macro: */ -#define FTPSENDF(x,y,z) if((result = Curl_ftpsendf(x,y,z)) != CURLE_OK) \ - return result #define PPSENDF(x,y,z) if((result = Curl_pp_sendf(x,y,z)) != CURLE_OK) \ return result @@ -228,7 +221,7 @@ const struct Curl_handler Curl_handler_ftps = { static const struct Curl_handler Curl_handler_ftp_proxy = { "FTP", /* scheme */ - ZERO_NULL, /* setup_connection */ + Curl_http_setup_conn, /* setup_connection */ Curl_http, /* do_it */ Curl_http_done, /* done */ ZERO_NULL, /* do_more */ @@ -254,7 +247,7 @@ static const struct Curl_handler Curl_handler_ftp_proxy = { static const struct Curl_handler Curl_handler_ftps_proxy = { "FTPS", /* scheme */ - ZERO_NULL, /* setup_connection */ + Curl_http_setup_conn, /* setup_connection */ Curl_http, /* do_it */ Curl_http_done, /* done */ ZERO_NULL, /* do_more */ @@ -496,7 +489,7 @@ static CURLcode ReceivedServerConnect(struct connectdata *conn, bool *received) static CURLcode InitiateTransfer(struct connectdata *conn) { struct SessionHandle *data = conn->data; - struct FTP *ftp = data->state.proto.ftp; + struct FTP *ftp = data->req.protop; CURLcode result = CURLE_OK; if(conn->ssl[SECONDARYSOCKET].use) { @@ -598,17 +591,17 @@ static CURLcode AllowServerConnect(struct connectdata *conn, bool *connected) /* macro to check for the last line in an FTP server response */ #define LASTLINE(line) (STATUSCODE(line) && (' ' == line[3])) -static int ftp_endofresp(struct pingpong *pp, - int *code) +static bool ftp_endofresp(struct connectdata *conn, char *line, size_t len, + int *code) { - char *line = pp->linestart_resp; - size_t len = pp->nread_resp; + (void)conn; if((len > 3) && LASTLINE(line)) { *code = curlx_sltosi(strtol(line, NULL, 10)); - return 1; + return TRUE; } - return 0; + + return FALSE; } static CURLcode ftp_readresp(curl_socket_t sockfd, @@ -618,7 +611,7 @@ static CURLcode ftp_readresp(curl_socket_t sockfd, { struct connectdata *conn = pp->conn; struct SessionHandle *data = conn->data; -#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI) +#ifdef HAVE_GSSAPI char * const buf = data->state.buffer; #endif CURLcode result = CURLE_OK; @@ -626,7 +619,7 @@ static CURLcode ftp_readresp(curl_socket_t sockfd, result = Curl_pp_readresp(sockfd, pp, &code, size); -#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI) +#if defined(HAVE_GSSAPI) /* handle the security-oriented responses 6xx ***/ /* FIXME: some errorchecking perhaps... ***/ switch(code) { @@ -778,6 +771,47 @@ CURLcode Curl_GetFTPResponse(ssize_t *nreadp, /* return number of bytes read */ return result; } +#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) + /* for debug purposes */ +static const char * const ftp_state_names[]={ + "STOP", + "WAIT220", + "AUTH", + "USER", + "PASS", + "ACCT", + "PBSZ", + "PROT", + "CCC", + "PWD", + "SYST", + "NAMEFMT", + "QUOTE", + "RETR_PREQUOTE", + "STOR_PREQUOTE", + "POSTQUOTE", + "CWD", + "MKD", + "MDTM", + "TYPE", + "LIST_TYPE", + "RETR_TYPE", + "STOR_TYPE", + "SIZE", + "RETR_SIZE", + "STOR_SIZE", + "REST", + "RETR_REST", + "PORT", + "PRET", + "PASV", + "LIST", + "RETR", + "STOR", + "QUIT" +}; +#endif + /* This is the ONLY way to change FTP state! */ static void _state(struct connectdata *conn, ftpstate newstate @@ -786,51 +820,12 @@ static void _state(struct connectdata *conn, #endif ) { -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) - /* for debug purposes */ - static const char * const names[]={ - "STOP", - "WAIT220", - "AUTH", - "USER", - "PASS", - "ACCT", - "PBSZ", - "PROT", - "CCC", - "PWD", - "SYST", - "NAMEFMT", - "QUOTE", - "RETR_PREQUOTE", - "STOR_PREQUOTE", - "POSTQUOTE", - "CWD", - "MKD", - "MDTM", - "TYPE", - "LIST_TYPE", - "RETR_TYPE", - "STOR_TYPE", - "SIZE", - "RETR_SIZE", - "STOR_SIZE", - "REST", - "RETR_REST", - "PORT", - "PRET", - "PASV", - "LIST", - "RETR", - "STOR", - "QUIT" - }; -#endif struct ftp_conn *ftpc = &conn->proto.ftpc; #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) if(ftpc->state != newstate) infof(conn->data, "FTP %p (line %d) state change from %s to %s\n", - ftpc, lineno, names[ftpc->state], names[newstate]); + (void *)ftpc, lineno, ftp_state_names[ftpc->state], + ftp_state_names[newstate]); #endif ftpc->state = newstate; } @@ -838,7 +833,7 @@ static void _state(struct connectdata *conn, static CURLcode ftp_state_user(struct connectdata *conn) { CURLcode result; - struct FTP *ftp = conn->data->state.proto.ftp; + struct FTP *ftp = conn->data->req.protop; /* send USER */ PPSENDF(&conn->proto.ftpc.pp, "USER %s", ftp->user?ftp->user:""); @@ -853,7 +848,7 @@ static CURLcode ftp_state_pwd(struct connectdata *conn) CURLcode result; /* send PWD to discover our entry point */ - PPSENDF(&conn->proto.ftpc.pp, "PWD", NULL); + PPSENDF(&conn->proto.ftpc.pp, "%s", "PWD"); state(conn, FTP_PWD); return CURLE_OK; @@ -877,29 +872,21 @@ static int ftp_domore_getsock(struct connectdata *conn, curl_socket_t *socks, return GETSOCK_BLANK; /* When in DO_MORE state, we could be either waiting for us to connect to a - remote site, or we could wait for that site to connect to us. Or just - handle ordinary commands. - - When waiting for a connect, we will be in FTP_STOP state and then we wait - for the secondary socket to become writeable. If we're in another state, - we're still handling commands on the control (primary) connection. + * remote site, or we could wait for that site to connect to us. Or just + * handle ordinary commands. + */ - */ + if(FTP_STOP == ftpc->state) { + /* if stopped and still in this state, then we're also waiting for a + connect on the secondary connection */ + socks[0] = conn->sock[FIRSTSOCKET]; + socks[1] = conn->sock[SECONDARYSOCKET]; - switch(ftpc->state) { - case FTP_STOP: - break; - default: - return Curl_pp_getsock(&conn->proto.ftpc.pp, socks, numsocks); - } - - socks[0] = conn->sock[SECONDARYSOCKET]; - if(ftpc->wait_data_conn) { - socks[1] = conn->sock[FIRSTSOCKET]; - return GETSOCK_READSOCK(0) | GETSOCK_READSOCK(1); + return GETSOCK_READSOCK(FIRSTSOCKET) | + GETSOCK_WRITESOCK(SECONDARYSOCKET); } - - return GETSOCK_READSOCK(0); + else + return Curl_pp_getsock(&conn->proto.ftpc.pp, socks, numsocks); } /* This is called after the FTP_QUOTE state is passed. @@ -915,7 +902,7 @@ static CURLcode ftp_state_cwd(struct connectdata *conn) if(ftpc->cwddone) /* already done and fine */ - result = ftp_state_post_cwd(conn); + result = ftp_state_mdtm(conn); else { ftpc->count2 = 0; /* count2 counts failed CWDs */ @@ -943,7 +930,7 @@ static CURLcode ftp_state_cwd(struct connectdata *conn) } else { /* No CWD necessary */ - result = ftp_state_post_cwd(conn); + result = ftp_state_mdtm(conn); } } } @@ -1068,12 +1055,17 @@ static CURLcode ftp_state_use_port(struct connectdata *conn, if(*addr != '\0') { /* attempt to get the address of the given interface name */ - if(!Curl_if2ip(conn->ip_addr->ai_family, addr, - hbuf, sizeof(hbuf))) - /* not an interface, use the given string as host name instead */ - host = addr; - else - host = hbuf; /* use the hbuf for host name */ + switch(Curl_if2ip(conn->ip_addr->ai_family, conn->scope, addr, + hbuf, sizeof(hbuf))) { + case IF2IP_NOT_FOUND: + /* not an interface, use the given string as host name instead */ + host = addr; + break; + case IF2IP_AF_NOT_SUPPORTED: + return CURLE_FTP_PORT_FAILED; + case IF2IP_FOUND: + host = hbuf; /* use the hbuf for host name */ + } } else /* there was only a port(-range) given, default the host */ @@ -1368,13 +1360,17 @@ static CURLcode ftp_state_use_pasv(struct connectdata *conn) return result; } -/* REST is the last command in the chain of commands when a "head"-like - request is made. Thus, if an actual transfer is to be made this is where - we take off for real. */ -static CURLcode ftp_state_post_rest(struct connectdata *conn) +/* + * ftp_state_prepare_transfer() starts PORT, PASV or PRET etc. + * + * REST is the last command in the chain of commands when a "head"-like + * request is made. Thus, if an actual transfer is to be made this is where we + * take off for real. + */ +static CURLcode ftp_state_prepare_transfer(struct connectdata *conn) { CURLcode result = CURLE_OK; - struct FTP *ftp = conn->data->state.proto.ftp; + struct FTP *ftp = conn->data->req.protop; struct SessionHandle *data = conn->data; if(ftp->transfer != FTPTRANSFER_BODY) { @@ -1414,10 +1410,10 @@ static CURLcode ftp_state_post_rest(struct connectdata *conn) return result; } -static CURLcode ftp_state_post_size(struct connectdata *conn) +static CURLcode ftp_state_rest(struct connectdata *conn) { CURLcode result = CURLE_OK; - struct FTP *ftp = conn->data->state.proto.ftp; + struct FTP *ftp = conn->data->req.protop; struct ftp_conn *ftpc = &conn->proto.ftpc; if((ftp->transfer != FTPTRANSFER_BODY) && ftpc->file) { @@ -1430,15 +1426,15 @@ static CURLcode ftp_state_post_size(struct connectdata *conn) state(conn, FTP_REST); } else - result = ftp_state_post_rest(conn); + result = ftp_state_prepare_transfer(conn); return result; } -static CURLcode ftp_state_post_type(struct connectdata *conn) +static CURLcode ftp_state_size(struct connectdata *conn) { CURLcode result = CURLE_OK; - struct FTP *ftp = conn->data->state.proto.ftp; + struct FTP *ftp = conn->data->req.protop; struct ftp_conn *ftpc = &conn->proto.ftpc; if((ftp->transfer == FTPTRANSFER_INFO) && ftpc->file) { @@ -1450,12 +1446,12 @@ static CURLcode ftp_state_post_type(struct connectdata *conn) state(conn, FTP_SIZE); } else - result = ftp_state_post_size(conn); + result = ftp_state_rest(conn); return result; } -static CURLcode ftp_state_post_listtype(struct connectdata *conn) +static CURLcode ftp_state_list(struct connectdata *conn) { CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; @@ -1524,7 +1520,7 @@ static CURLcode ftp_state_post_listtype(struct connectdata *conn) return result; } -static CURLcode ftp_state_post_retrtype(struct connectdata *conn) +static CURLcode ftp_state_retr_prequote(struct connectdata *conn) { CURLcode result = CURLE_OK; @@ -1535,7 +1531,7 @@ static CURLcode ftp_state_post_retrtype(struct connectdata *conn) return result; } -static CURLcode ftp_state_post_stortype(struct connectdata *conn) +static CURLcode ftp_state_stor_prequote(struct connectdata *conn) { CURLcode result = CURLE_OK; @@ -1546,10 +1542,10 @@ static CURLcode ftp_state_post_stortype(struct connectdata *conn) return result; } -static CURLcode ftp_state_post_mdtm(struct connectdata *conn) +static CURLcode ftp_state_type(struct connectdata *conn) { CURLcode result = CURLE_OK; - struct FTP *ftp = conn->data->state.proto.ftp; + struct FTP *ftp = conn->data->req.protop; struct SessionHandle *data = conn->data; struct ftp_conn *ftpc = &conn->proto.ftpc; @@ -1572,14 +1568,14 @@ static CURLcode ftp_state_post_mdtm(struct connectdata *conn) return result; } else - result = ftp_state_post_type(conn); + result = ftp_state_size(conn); return result; } /* This is called after the CWD commands have been done in the beginning of the DO phase */ -static CURLcode ftp_state_post_cwd(struct connectdata *conn) +static CURLcode ftp_state_mdtm(struct connectdata *conn) { CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; @@ -1595,7 +1591,7 @@ static CURLcode ftp_state_post_cwd(struct connectdata *conn) state(conn, FTP_MDTM); } else - result = ftp_state_post_mdtm(conn); + result = ftp_state_type(conn); return result; } @@ -1606,7 +1602,7 @@ static CURLcode ftp_state_ul_setup(struct connectdata *conn, bool sizechecked) { CURLcode result = CURLE_OK; - struct FTP *ftp = conn->data->state.proto.ftp; + struct FTP *ftp = conn->data->req.protop; struct SessionHandle *data = conn->data; struct ftp_conn *ftpc = &conn->proto.ftpc; int seekerr = CURL_SEEKFUNC_OK; @@ -1704,7 +1700,7 @@ static CURLcode ftp_state_quote(struct connectdata *conn, { CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; - struct FTP *ftp = data->state.proto.ftp; + struct FTP *ftp = data->req.protop; struct ftp_conn *ftpc = &conn->proto.ftpc; bool quote=FALSE; struct curl_slist *item; @@ -1770,7 +1766,7 @@ static CURLcode ftp_state_quote(struct connectdata *conn, else { if(ftpc->known_filesize != -1) { Curl_pgrsSetDownloadSize(data, ftpc->known_filesize); - result = ftp_state_post_retr_size(conn, ftpc->known_filesize); + result = ftp_state_retr(conn, ftpc->known_filesize); } else { PPSENDF(&ftpc->pp, "SIZE %s", ftpc->file); @@ -1794,15 +1790,15 @@ static CURLcode ftp_state_quote(struct connectdata *conn, static CURLcode ftp_epsv_disable(struct connectdata *conn) { CURLcode result = CURLE_OK; - infof(conn->data, "got positive EPSV response, but can't connect. " - "Disabling EPSV\n"); + infof(conn->data, "Failed EPSV attempt. Disabling EPSV\n"); /* disable it for next transfer */ conn->bits.ftp_use_epsv = FALSE; conn->data->state.errorbuf = FALSE; /* allow error message to get rewritten */ - PPSENDF(&conn->proto.ftpc.pp, "PASV", NULL); + PPSENDF(&conn->proto.ftpc.pp, "%s", "PASV"); conn->proto.ftpc.count1++; - /* remain in the FTP_PASV state */ + /* remain in/go to the FTP_PASV state */ + state(conn, FTP_PASV); return result; } @@ -1931,28 +1927,18 @@ static CURLcode ftp_state_pasv_resp(struct connectdata *conn, } else if(ftpc->count1 == 0) { /* EPSV failed, move on to PASV */ - - /* disable it for next transfer */ - conn->bits.ftp_use_epsv = FALSE; - infof(data, "disabling EPSV usage\n"); - - PPSENDF(&ftpc->pp, "PASV", NULL); - ftpc->count1++; - /* remain in the FTP_PASV state */ - return result; + return ftp_epsv_disable(conn); } else { failf(data, "Bad PASV/EPSV response: %03d", ftpcode); return CURLE_FTP_WEIRD_PASV_REPLY; } - if(data->set.str[STRING_PROXY] && *data->set.str[STRING_PROXY]) { + if(conn->bits.proxy) { /* - * This is a tunnel through a http proxy and we need to connect to the - * proxy again here. - * - * We don't want to rely on a former host lookup that might've expired - * now, instead we remake the lookup here and now! + * This connection uses a proxy and we need to connect to the proxy again + * here. We don't want to rely on a former host lookup that might've + * expired now, instead we remake the lookup here and now! */ rc = Curl_resolv(conn, conn->proxy.name, (int)conn->port, &addr); if(rc == CURLRESOLV_PENDING) @@ -2018,14 +2004,17 @@ static CURLcode ftp_state_pasv_resp(struct connectdata *conn, case CURLPROXY_SOCKS5_HOSTNAME: result = Curl_SOCKS5(conn->proxyuser, conn->proxypasswd, newhost, newport, SECONDARYSOCKET, conn); + connected = TRUE; break; case CURLPROXY_SOCKS4: result = Curl_SOCKS4(conn->proxyuser, newhost, newport, SECONDARYSOCKET, conn, FALSE); + connected = TRUE; break; case CURLPROXY_SOCKS4A: result = Curl_SOCKS4(conn->proxyuser, newhost, newport, SECONDARYSOCKET, conn, TRUE); + connected = TRUE; break; case CURLPROXY_HTTP: case CURLPROXY_HTTP_1_0: @@ -2057,13 +2046,13 @@ static CURLcode ftp_state_pasv_resp(struct connectdata *conn, * FTP pointer */ struct HTTP http_proxy; - struct FTP *ftp_save = data->state.proto.ftp; + struct FTP *ftp_save = data->req.protop; memset(&http_proxy, 0, sizeof(http_proxy)); - data->state.proto.http = &http_proxy; + data->req.protop = &http_proxy; result = Curl_proxyCONNECT(conn, SECONDARYSOCKET, newhost, newport); - data->state.proto.ftp = ftp_save; + data->req.protop = ftp_save; if(result) return result; @@ -2077,8 +2066,7 @@ static CURLcode ftp_state_pasv_resp(struct connectdata *conn, } } - conn->bits.tcpconnect[SECONDARYSOCKET] = TRUE; - + conn->bits.tcpconnect[SECONDARYSOCKET] = connected; conn->bits.do_more = TRUE; state(conn, FTP_STOP); /* this phase is completed */ @@ -2124,7 +2112,7 @@ static CURLcode ftp_state_mdtm_resp(struct connectdata *conn, { CURLcode result = CURLE_OK; struct SessionHandle *data=conn->data; - struct FTP *ftp = data->state.proto.ftp; + struct FTP *ftp = data->req.protop; struct ftp_conn *ftpc = &conn->proto.ftpc; switch(ftpcode) { @@ -2218,7 +2206,7 @@ static CURLcode ftp_state_mdtm_resp(struct connectdata *conn, } if(!result) - result = ftp_state_post_mdtm(conn); + result = ftp_state_type(conn); return result; } @@ -2242,23 +2230,23 @@ static CURLcode ftp_state_type_resp(struct connectdata *conn, ftpcode); if(instate == FTP_TYPE) - result = ftp_state_post_type(conn); + result = ftp_state_size(conn); else if(instate == FTP_LIST_TYPE) - result = ftp_state_post_listtype(conn); + result = ftp_state_list(conn); else if(instate == FTP_RETR_TYPE) - result = ftp_state_post_retrtype(conn); + result = ftp_state_retr_prequote(conn); else if(instate == FTP_STOR_TYPE) - result = ftp_state_post_stortype(conn); + result = ftp_state_stor_prequote(conn); return result; } -static CURLcode ftp_state_post_retr_size(struct connectdata *conn, +static CURLcode ftp_state_retr(struct connectdata *conn, curl_off_t filesize) { CURLcode result = CURLE_OK; struct SessionHandle *data=conn->data; - struct FTP *ftp = data->state.proto.ftp; + struct FTP *ftp = data->req.protop; struct ftp_conn *ftpc = &conn->proto.ftpc; if(data->set.max_filesize && (filesize > data->set.max_filesize)) { @@ -2358,11 +2346,11 @@ static CURLcode ftp_state_size_resp(struct connectdata *conn, } #endif Curl_pgrsSetDownloadSize(data, filesize); - result = ftp_state_post_size(conn); + result = ftp_state_rest(conn); } else if(instate == FTP_RETR_SIZE) { Curl_pgrsSetDownloadSize(data, filesize); - result = ftp_state_post_retr_size(conn, filesize); + result = ftp_state_retr(conn, filesize); } else if(instate == FTP_STOR_SIZE) { data->state.resume_from = filesize; @@ -2390,7 +2378,7 @@ static CURLcode ftp_state_rest_resp(struct connectdata *conn, return result; } #endif - result = ftp_state_post_rest(conn); + result = ftp_state_prepare_transfer(conn); break; case FTP_RETR_REST: @@ -2427,6 +2415,8 @@ static CURLcode ftp_state_stor_resp(struct connectdata *conn, if(data->set.ftp_use_port) { bool connected; + state(conn, FTP_STOP); /* no longer in STOR state */ + result = AllowServerConnect(conn, &connected); if(result) return result; @@ -2450,7 +2440,7 @@ static CURLcode ftp_state_get_resp(struct connectdata *conn, { CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; - struct FTP *ftp = data->state.proto.ftp; + struct FTP *ftp = data->req.protop; char *buf = data->state.buffer; if((ftpcode == 150) || (ftpcode == 125)) { @@ -2574,19 +2564,6 @@ static CURLcode ftp_state_loggedin(struct connectdata *conn) { CURLcode result = CURLE_OK; -#ifdef HAVE_KRB4 - if(conn->data->set.krb) { - /* We may need to issue a KAUTH here to have access to the files - * do it if user supplied a password - */ - if(conn->passwd && *conn->passwd) { - /* BLOCKING */ - result = Curl_krb_kauth(conn); - if(result) - return result; - } - } -#endif if(conn->ssl[FIRSTSOCKET].use) { /* PBSZ = PROTECTION BUFFER SIZE. @@ -2618,7 +2595,7 @@ static CURLcode ftp_state_user_resp(struct connectdata *conn, { CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; - struct FTP *ftp = data->state.proto.ftp; + struct FTP *ftp = data->req.protop; struct ftp_conn *ftpc = &conn->proto.ftpc; (void)instate; /* no use for this yet */ @@ -2706,14 +2683,17 @@ static CURLcode ftp_statemach_act(struct connectdata *conn) /* we have now received a full FTP server response */ switch(ftpc->state) { case FTP_WAIT220: - if(ftpcode != 220) { + if(ftpcode == 230) + /* 230 User logged in - already! */ + return ftp_state_user_resp(conn, ftpcode, ftpc->state); + else if(ftpcode != 220) { failf(data, "Got a %03d ftp-server response when 220 was expected", ftpcode); return CURLE_FTP_WEIRD_SERVER_REPLY; } /* We have received a 220 response fine, now we proceed. */ -#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI) +#ifdef HAVE_GSSAPI if(data->set.krb) { /* If not anonymous login, try a secure login. Note that this procedure is still BLOCKING. */ @@ -2828,7 +2808,7 @@ static CURLcode ftp_statemach_act(struct connectdata *conn) if(data->set.ftp_ccc) { /* CCC - Clear Command Channel */ - PPSENDF(&ftpc->pp, "CCC", NULL); + PPSENDF(&ftpc->pp, "%s", "CCC"); state(conn, FTP_CCC); } else { @@ -2866,13 +2846,19 @@ static CURLcode ftp_statemach_act(struct connectdata *conn) return CURLE_OUT_OF_MEMORY; /* Reply format is like - 257<space>"<directory-name>"<space><commentary> and the RFC959 - says + 257<space>[rubbish]"<directory-name>"<space><commentary> and the + RFC959 says The directory name can contain any character; embedded double-quotes should be escaped by double-quotes (the "quote-doubling" convention). */ + + /* scan for the first double-quote for non-standard responses */ + while(ptr < &data->state.buffer[sizeof(data->state.buffer)] + && *ptr != '\n' && *ptr != '\0' && *ptr != '"') + ptr++; + if('\"' == *ptr) { /* it started good */ ptr++; @@ -2909,7 +2895,7 @@ static CURLcode ftp_statemach_act(struct connectdata *conn) if(!ftpc->server_os && dir[0] != '/') { - result = Curl_pp_sendf(&ftpc->pp, "SYST", NULL); + result = Curl_pp_sendf(&ftpc->pp, "%s", "SYST"); if(result != CURLE_OK) { free(dir); return result; @@ -2962,7 +2948,7 @@ static CURLcode ftp_statemach_act(struct connectdata *conn) if(strequal(os, "OS/400")) { /* Force OS400 name format 1. */ - result = Curl_pp_sendf(&ftpc->pp, "SITE NAMEFMT 1", NULL); + result = Curl_pp_sendf(&ftpc->pp, "%s", "SITE NAMEFMT 1"); if(result != CURLE_OK) { free(os); return result; @@ -3040,7 +3026,7 @@ static CURLcode ftp_statemach_act(struct connectdata *conn) PPSENDF(&ftpc->pp, "CWD %s", ftpc->dirs[ftpc->count1 - 1]); } else { - result = ftp_state_post_cwd(conn); + result = ftp_state_mdtm(conn); if(result) return result; } @@ -3124,7 +3110,7 @@ static CURLcode ftp_multi_statemach(struct connectdata *conn, bool *done) { struct ftp_conn *ftpc = &conn->proto.ftpc; - CURLcode result = Curl_pp_multi_statemach(&ftpc->pp); + CURLcode result = Curl_pp_statemach(&ftpc->pp, FALSE); /* Check for the state outside of the Curl_socket_ready() return code checks since at times we are in fact already in this state when this function @@ -3134,14 +3120,14 @@ static CURLcode ftp_multi_statemach(struct connectdata *conn, return result; } -static CURLcode ftp_easy_statemach(struct connectdata *conn) +static CURLcode ftp_block_statemach(struct connectdata *conn) { struct ftp_conn *ftpc = &conn->proto.ftpc; struct pingpong *pp = &ftpc->pp; CURLcode result = CURLE_OK; while(ftpc->state != FTP_STOP) { - result = Curl_pp_easy_statemach(pp); + result = Curl_pp_statemach(pp, TRUE); if(result) break; } @@ -3150,62 +3136,12 @@ static CURLcode ftp_easy_statemach(struct connectdata *conn) } /* - * Allocate and initialize the struct FTP for the current SessionHandle. If - * need be. - */ - -#if defined(__INTEL_COMPILER) && (__INTEL_COMPILER == 910) && \ - defined(__OPTIMIZE__) && defined(__unix__) && defined(__i386__) - /* workaround icc 9.1 optimizer issue */ -#pragma optimize("", off) -#endif - -static CURLcode ftp_init(struct connectdata *conn) -{ - struct FTP *ftp; - - if(NULL == conn->data->state.proto.ftp) { - conn->data->state.proto.ftp = malloc(sizeof(struct FTP)); - if(NULL == conn->data->state.proto.ftp) - return CURLE_OUT_OF_MEMORY; - } - - ftp = conn->data->state.proto.ftp; - - /* get some initial data into the ftp struct */ - ftp->bytecountp = &conn->data->req.bytecount; - ftp->transfer = FTPTRANSFER_BODY; - ftp->downloadsize = 0; - - /* No need to duplicate user+password, the connectdata struct won't change - during a session, but we re-init them here since on subsequent inits - since the conn struct may have changed or been replaced. - */ - ftp->user = conn->user; - ftp->passwd = conn->passwd; - if(isBadFtpString(ftp->user)) - return CURLE_URL_MALFORMAT; - if(isBadFtpString(ftp->passwd)) - return CURLE_URL_MALFORMAT; - - conn->proto.ftpc.known_filesize = -1; /* unknown size for now */ - - return CURLE_OK; -} - -#if defined(__INTEL_COMPILER) && (__INTEL_COMPILER == 910) && \ - defined(__OPTIMIZE__) && defined(__unix__) && defined(__i386__) - /* workaround icc 9.1 optimizer issue */ -#pragma optimize("", on) -#endif - -/* * ftp_connect() should do everything that is to be considered a part of * the connection phase. * * The variable 'done' points to will be TRUE if the protocol-layer connect - * phase is done when this function returns, or FALSE is not. When called as - * a part of the easy interface, it will always be TRUE. + * phase is done when this function returns, or FALSE if not. + * */ static CURLcode ftp_connect(struct connectdata *conn, bool *done) /* see description above */ @@ -3216,14 +3152,6 @@ static CURLcode ftp_connect(struct connectdata *conn, *done = FALSE; /* default to not done yet */ - /* If there already is a protocol-specific struct allocated for this - sessionhandle, deal with it */ - Curl_reset_reqproto(conn); - - result = ftp_init(conn); - if(CURLE_OK != result) - return result; - /* We always support persistent connections on ftp */ conn->bits.close = FALSE; @@ -3263,7 +3191,7 @@ static CURLcode ftp_done(struct connectdata *conn, CURLcode status, bool premature) { struct SessionHandle *data = conn->data; - struct FTP *ftp = data->state.proto.ftp; + struct FTP *ftp = data->req.protop; struct ftp_conn *ftpc = &conn->proto.ftpc; struct pingpong *pp = &ftpc->pp; ssize_t nread; @@ -3369,15 +3297,16 @@ static CURLcode ftp_done(struct connectdata *conn, CURLcode status, #endif if(conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD) { - if(!result && ftpc->dont_check && data->req.maxdownload > 0) + if(!result && ftpc->dont_check && data->req.maxdownload > 0) { /* partial download completed */ - result = Curl_pp_sendf(pp, "ABOR"); + result = Curl_pp_sendf(pp, "%s", "ABOR"); if(result) { failf(data, "Failure sending ABOR command: %s", curl_easy_strerror(result)); ftpc->ctl_valid = FALSE; /* mark control connection as bad */ conn->bits.close = TRUE; /* mark for connection closure */ } + } if(conn->ssl[SECONDARYSOCKET].use) { /* The secondary socket is using SSL so we must close down that part @@ -3523,7 +3452,7 @@ CURLcode ftp_sendquote(struct connectdata *conn, struct curl_slist *quote) acceptfail = TRUE; } - FTPSENDF(conn, "%s", cmd); + PPSENDF(&conn->proto.ftpc.pp, "%s", cmd); pp->response = Curl_tvnow(); /* timeout relative now */ @@ -3664,19 +3593,22 @@ static CURLcode ftp_range(struct connectdata *conn) * * This function shall be called when the second FTP (data) connection is * connected. + * + * 'complete' can return 0 for incomplete, 1 for done and -1 for go back + * (which basically is only for when PASV is being sent to retry a failed + * EPSV). */ -static CURLcode ftp_do_more(struct connectdata *conn, bool *complete) +static CURLcode ftp_do_more(struct connectdata *conn, int *completep) { struct SessionHandle *data=conn->data; struct ftp_conn *ftpc = &conn->proto.ftpc; CURLcode result = CURLE_OK; bool connected = FALSE; + bool complete = FALSE; /* the ftp struct is inited in ftp_connect() */ - struct FTP *ftp = data->state.proto.ftp; - - *complete = FALSE; + struct FTP *ftp = data->req.protop; /* if the second connection isn't done yet, wait for it */ if(!conn->bits.tcpconnect[SECONDARYSOCKET]) { @@ -3694,14 +3626,22 @@ static CURLcode ftp_do_more(struct connectdata *conn, bool *complete) if(connected) { DEBUGF(infof(data, "DO-MORE connected phase starts\n")); } - else + else { + if(result && (ftpc->count1 == 0)) { + *completep = -1; /* go back to DOING please */ + /* this is a EPSV connect failing, try PASV instead */ + return ftp_epsv_disable(conn); + } return result; + } } if(ftpc->state) { /* already in a state so skip the intial commands. They are only done to kickstart the do_more state */ - result = ftp_multi_statemach(conn, complete); + result = ftp_multi_statemach(conn, &complete); + + *completep = (int)complete; /* if we got an error or if we don't wait for a data connection return immediately */ @@ -3712,7 +3652,7 @@ static CURLcode ftp_do_more(struct connectdata *conn, bool *complete) /* if we reach the end of the FTP state machine here, *complete will be TRUE but so is ftpc->wait_data_conn, which says we need to wait for the data connection and therefore we're not actually complete */ - *complete = FALSE; + *completep = 0; } if(ftp->transfer <= FTPTRANSFER_INFO) { @@ -3735,6 +3675,9 @@ static CURLcode ftp_do_more(struct connectdata *conn, bool *complete) if(result) return result; + + *completep = 1; /* this state is now complete when the server has + connected back to us */ } } else if(data->set.upload) { @@ -3742,7 +3685,8 @@ static CURLcode ftp_do_more(struct connectdata *conn, bool *complete) if(result) return result; - result = ftp_multi_statemach(conn, complete); + result = ftp_multi_statemach(conn, &complete); + *completep = (int)complete; } else { /* download */ @@ -3770,7 +3714,8 @@ static CURLcode ftp_do_more(struct connectdata *conn, bool *complete) return result; } - result = ftp_multi_statemach(conn, complete); + result = ftp_multi_statemach(conn, &complete); + *completep = (int)complete; } return result; } @@ -3782,7 +3727,7 @@ static CURLcode ftp_do_more(struct connectdata *conn, bool *complete) if(!ftpc->wait_data_conn) { /* no waiting for the data connection so this is now complete */ - *complete = TRUE; + *completep = 1; DEBUGF(infof(data, "DO-MORE phase ends with %d\n", (int)result)); } @@ -3811,7 +3756,7 @@ CURLcode ftp_perform(struct connectdata *conn, if(conn->data->set.opt_no_body) { /* requested no body means no transfer... */ - struct FTP *ftp = conn->data->state.proto.ftp; + struct FTP *ftp = conn->data->req.protop; ftp->transfer = FTPTRANSFER_INFO; } @@ -3825,7 +3770,9 @@ CURLcode ftp_perform(struct connectdata *conn, /* run the state-machine */ result = ftp_multi_statemach(conn, dophase_done); - *connected = conn->bits.tcpconnect[FIRSTSOCKET]; + *connected = conn->bits.tcpconnect[SECONDARYSOCKET]; + + infof(conn->data, "ftp_perform ends with SECONDARY: %d\n", *connected); if(*dophase_done) DEBUGF(infof(conn->data, "DO phase is complete1\n")); @@ -3978,16 +3925,11 @@ static CURLcode wc_statemach(struct connectdata *conn) /* filelist has at least one file, lets get first one */ struct ftp_conn *ftpc = &conn->proto.ftpc; struct curl_fileinfo *finfo = wildcard->filelist->head->ptr; - char *tmp_path = malloc(strlen(conn->data->state.path) + - strlen(finfo->filename) + 1); - if(!tmp_path) { + + char *tmp_path = aprintf("%s%s", wildcard->path, finfo->filename); + if(!tmp_path) return CURLE_OUT_OF_MEMORY; - } - tmp_path[0] = 0; - /* make full path to matched file */ - strcat(tmp_path, wildcard->path); - strcat(tmp_path, finfo->filename); /* switch default "state.pathbuffer" and tmp_path, good to see ftp_parse_url_path function to understand this trick */ Curl_safefree(conn->data->state.pathbuffer); @@ -4076,17 +4018,6 @@ static CURLcode ftp_do(struct connectdata *conn, bool *done) *done = FALSE; /* default to false */ ftpc->wait_data_conn = FALSE; /* default to no such wait */ - /* - Since connections can be re-used between SessionHandles, this might be a - connection already existing but on a fresh SessionHandle struct so we must - make sure we have a good 'struct FTP' to play with. For new connections, - the struct FTP is allocated and setup in the ftp_connect() function. - */ - Curl_reset_reqproto(conn); - retcode = ftp_init(conn); - if(retcode) - return retcode; - if(conn->data->set.wildcardmatch) { retcode = wc_statemach(conn); if(conn->data->wildcard.state == CURLWC_SKIP || @@ -4118,19 +4049,19 @@ CURLcode Curl_ftpsendf(struct connectdata *conn, size_t write_len; char *sptr=s; CURLcode res = CURLE_OK; -#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI) +#ifdef HAVE_GSSAPI enum protection_level data_sec = conn->data_prot; #endif va_list ap; va_start(ap, fmt); - vsnprintf(s, SBUF_SIZE-3, fmt, ap); + write_len = vsnprintf(s, SBUF_SIZE-3, fmt, ap); va_end(ap); - strcat(s, "\r\n"); /* append a trailing CRLF */ + strcpy(&s[write_len], "\r\n"); /* append a trailing CRLF */ + write_len +=2; bytes_written=0; - write_len = strlen(s); res = Curl_convert_to_network(conn->data, s, write_len); /* Curl_convert_to_network calls failf if unsuccessful */ @@ -4138,12 +4069,12 @@ CURLcode Curl_ftpsendf(struct connectdata *conn, return(res); for(;;) { -#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI) +#ifdef HAVE_GSSAPI conn->data_prot = PROT_CMD; #endif res = Curl_write(conn, conn->sock[FIRSTSOCKET], sptr, write_len, &bytes_written); -#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI) +#ifdef HAVE_GSSAPI DEBUGASSERT(data_sec > PROT_NONE && data_sec < PROT_LAST); conn->data_prot = data_sec; #endif @@ -4181,7 +4112,7 @@ static CURLcode ftp_quit(struct connectdata *conn) CURLcode result = CURLE_OK; if(conn->proto.ftpc.ctl_valid) { - result = Curl_pp_sendf(&conn->proto.ftpc.pp, "QUIT", NULL); + result = Curl_pp_sendf(&conn->proto.ftpc.pp, "%s", "QUIT"); if(result) { failf(conn->data, "Failure sending QUIT command: %s", curl_easy_strerror(result)); @@ -4193,7 +4124,7 @@ static CURLcode ftp_quit(struct connectdata *conn) state(conn, FTP_QUIT); - result = ftp_easy_statemach(conn); + result = ftp_block_statemach(conn); } return result; @@ -4245,7 +4176,7 @@ static CURLcode ftp_disconnect(struct connectdata *conn, bool dead_connection) Curl_pp_disconnect(pp); -#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI) +#ifdef HAVE_GSSAPI Curl_sec_end(conn); #endif @@ -4264,7 +4195,7 @@ CURLcode ftp_parse_url_path(struct connectdata *conn) { struct SessionHandle *data = conn->data; /* the ftp struct is already inited in ftp_connect() */ - struct FTP *ftp = data->state.proto.ftp; + struct FTP *ftp = data->req.protop; struct ftp_conn *ftpc = &conn->proto.ftpc; const char *slash_pos; /* position of the first '/' char in curpos */ const char *path_to_use = data->state.path; @@ -4309,13 +4240,17 @@ CURLcode ftp_parse_url_path(struct connectdata *conn) } slash_pos=strrchr(cur_pos, '/'); if(slash_pos || !*cur_pos) { + size_t dirlen = slash_pos-cur_pos; + ftpc->dirs = calloc(1, sizeof(ftpc->dirs[0])); if(!ftpc->dirs) return CURLE_OUT_OF_MEMORY; + if(!dirlen) + dirlen++; + ftpc->dirs[0] = curl_easy_unescape(conn->data, slash_pos ? cur_pos : "/", - slash_pos ? - curlx_sztosi(slash_pos-cur_pos) : 1, + slash_pos ? curlx_uztosi(dirlen) : 1, NULL); if(!ftpc->dirs[0]) { freedirs(ftpc); @@ -4370,6 +4305,15 @@ CURLcode ftp_parse_url_path(struct connectdata *conn) } else { cur_pos = slash_pos + 1; /* jump to the rest of the string */ + if(!ftpc->dirdepth) { + /* path starts with a slash, add that as a directory */ + ftpc->dirs[ftpc->dirdepth] = strdup("/"); + if(!ftpc->dirs[ftpc->dirdepth++]) { /* run out of memory ... */ + failf(data, "no memory"); + freedirs(ftpc); + return CURLE_OUT_OF_MEMORY; + } + } continue; } @@ -4441,11 +4385,11 @@ CURLcode ftp_parse_url_path(struct connectdata *conn) static CURLcode ftp_dophase_done(struct connectdata *conn, bool connected) { - struct FTP *ftp = conn->data->state.proto.ftp; + struct FTP *ftp = conn->data->req.protop; struct ftp_conn *ftpc = &conn->proto.ftpc; if(connected) { - bool completed; + int completed; CURLcode result = ftp_do_more(conn, &completed); if(result) { @@ -4536,11 +4480,12 @@ CURLcode ftp_regular_transfer(struct connectdata *conn, return result; } -static CURLcode ftp_setup_connection(struct connectdata * conn) +static CURLcode ftp_setup_connection(struct connectdata *conn) { struct SessionHandle *data = conn->data; - char * type; + char *type; char command; + struct FTP *ftp; if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) { /* Unless we have asked to tunnel ftp operations through the proxy, we @@ -4556,18 +4501,18 @@ static CURLcode ftp_setup_connection(struct connectdata * conn) return CURLE_UNSUPPORTED_PROTOCOL; #endif } - /* - * We explicitly mark this connection as persistent here as we're doing - * FTP over HTTP and thus we accidentally avoid setting this value - * otherwise. - */ - conn->bits.close = FALSE; + /* set it up as a HTTP connection instead */ + return conn->handler->setup_connection(conn); #else failf(data, "FTP over http proxy requires HTTP support built-in!"); return CURLE_UNSUPPORTED_PROTOCOL; #endif } + conn->data->req.protop = ftp = malloc(sizeof(struct FTP)); + if(NULL == ftp) + return CURLE_OUT_OF_MEMORY; + data->state.path++; /* don't include the initial slash */ data->state.slash_removed = TRUE; /* we've skipped the slash */ @@ -4600,6 +4545,24 @@ static CURLcode ftp_setup_connection(struct connectdata * conn) } } + /* get some initial data into the ftp struct */ + ftp->bytecountp = &conn->data->req.bytecount; + ftp->transfer = FTPTRANSFER_BODY; + ftp->downloadsize = 0; + + /* No need to duplicate user+password, the connectdata struct won't change + during a session, but we re-init them here since on subsequent inits + since the conn struct may have changed or been replaced. + */ + ftp->user = conn->user; + ftp->passwd = conn->passwd; + if(isBadFtpString(ftp->user)) + return CURLE_URL_MALFORMAT; + if(isBadFtpString(ftp->passwd)) + return CURLE_URL_MALFORMAT; + + conn->proto.ftpc.known_filesize = -1; /* unknown size for now */ + return CURLE_OK; } |