diff options
Diffstat (limited to 'Utilities/cmcurl/lib/ftp.c')
-rw-r--r-- | Utilities/cmcurl/lib/ftp.c | 493 |
1 files changed, 209 insertions, 284 deletions
diff --git a/Utilities/cmcurl/lib/ftp.c b/Utilities/cmcurl/lib/ftp.c index 53510f8..7292e01 100644 --- a/Utilities/cmcurl/lib/ftp.c +++ b/Utilities/cmcurl/lib/ftp.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -55,7 +55,6 @@ #include "transfer.h" #include "escape.h" #include "http.h" /* for HTTP proxy tunnel stuff */ -#include "socks.h" #include "ftp.h" #include "fileinfo.h" #include "ftplistparser.h" @@ -78,6 +77,7 @@ #include "warnless.h" #include "http_proxy.h" #include "non-ascii.h" +#include "socks.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" #include "curl_memory.h" @@ -132,10 +132,8 @@ 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, 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); -static int ftp_domore_getsock(struct connectdata *conn, curl_socket_t *socks, - int numsocks); +static int ftp_getsock(struct connectdata *conn, curl_socket_t *socks); +static int ftp_domore_getsock(struct connectdata *conn, curl_socket_t *socks); static CURLcode ftp_doing(struct connectdata *conn, bool *dophase_done); static CURLcode ftp_setup_connection(struct connectdata * conn); @@ -255,18 +253,6 @@ static void freedirs(struct ftp_conn *ftpc) Curl_safefree(ftpc->newhost); } -/* Returns non-zero if the given string contains CR (\r) or LF (\n), - which are not allowed within RFC 959 <string>. - Note: The input string is in the client's encoding which might - not be ASCII, so escape sequences \r & \n must be used instead - of hex values 0x0d & 0x0a. -*/ -static bool isBadFtpString(const char *string) -{ - return ((NULL != strchr(string, '\r')) || - (NULL != strchr(string, '\n'))) ? TRUE : FALSE; -} - /*********************************************************************** * * AcceptServerConnect() @@ -305,7 +291,7 @@ static CURLcode AcceptServerConnect(struct connectdata *conn) conn->sock[SECONDARYSOCKET] = s; (void)curlx_nonblock(s, TRUE); /* enable non-blocking */ - conn->sock_accepted[SECONDARYSOCKET] = TRUE; + conn->sock_accepted = TRUE; if(data->set.fsockopt) { int error = 0; @@ -382,7 +368,7 @@ static CURLcode ReceivedServerConnect(struct connectdata *conn, bool *received) struct ftp_conn *ftpc = &conn->proto.ftpc; struct pingpong *pp = &ftpc->pp; int result; - time_t timeout_ms; + timediff_t timeout_ms; ssize_t nread; int ftpcode; @@ -493,7 +479,7 @@ static CURLcode InitiateTransfer(struct connectdata *conn) static CURLcode AllowServerConnect(struct connectdata *conn, bool *connected) { struct Curl_easy *data = conn->data; - time_t timeout_ms; + timediff_t timeout_ms; CURLcode result = CURLE_OK; *connected = FALSE; @@ -525,7 +511,7 @@ static CURLcode AllowServerConnect(struct connectdata *conn, bool *connected) } else { /* Add timeout to multi handle and break out of the loop */ - if(!result && *connected == FALSE) { + if(*connected == FALSE) { Curl_expire(data, data->set.accepttimeout > 0 ? data->set.accepttimeout: DEFAULT_ACCEPT_TIMEOUT, 0); } @@ -565,10 +551,8 @@ static CURLcode ftp_readresp(curl_socket_t sockfd, #ifdef HAVE_GSSAPI char * const buf = data->state.buffer; #endif - CURLcode result = CURLE_OK; int code; - - result = Curl_pp_readresp(sockfd, pp, &code, size); + CURLcode result = Curl_pp_readresp(sockfd, pp, &code, size); #if defined(HAVE_GSSAPI) /* handle the security-oriented responses 6xx ***/ @@ -789,9 +773,8 @@ static void _state(struct connectdata *conn, static CURLcode ftp_state_user(struct connectdata *conn) { CURLcode result; - struct FTP *ftp = conn->data->req.protop; /* send USER */ - PPSENDF(&conn->proto.ftpc.pp, "USER %s", ftp->user?ftp->user:""); + PPSENDF(&conn->proto.ftpc.pp, "USER %s", conn->user?conn->user:""); state(conn, FTP_USER); conn->data->state.ftp_trying_alternative = FALSE; @@ -812,26 +795,24 @@ static CURLcode ftp_state_pwd(struct connectdata *conn) /* For the FTP "protocol connect" and "doing" phases only */ static int ftp_getsock(struct connectdata *conn, - curl_socket_t *socks, - int numsocks) + curl_socket_t *socks) { - return Curl_pp_getsock(&conn->proto.ftpc.pp, socks, numsocks); + return Curl_pp_getsock(&conn->proto.ftpc.pp, socks); } /* For the FTP "DO_MORE" phase only */ -static int ftp_domore_getsock(struct connectdata *conn, curl_socket_t *socks, - int numsocks) +static int ftp_domore_getsock(struct connectdata *conn, curl_socket_t *socks) { struct ftp_conn *ftpc = &conn->proto.ftpc; - if(!numsocks) - 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. */ + if(SOCKS_STATE(conn->cnnct.state)) + return Curl_SOCKS_getsock(conn, socks, SECONDARYSOCKET); + if(FTP_STOP == ftpc->state) { int bits = GETSOCK_READSOCK(0); @@ -858,7 +839,7 @@ static int ftp_domore_getsock(struct connectdata *conn, curl_socket_t *socks, return bits; } - return Curl_pp_getsock(&conn->proto.ftpc.pp, socks, numsocks); + return Curl_pp_getsock(&conn->proto.ftpc.pp, socks); } /* This is called after the FTP_QUOTE state is passed. @@ -876,6 +857,10 @@ static CURLcode ftp_state_cwd(struct connectdata *conn) /* already done and fine */ result = ftp_state_mdtm(conn); else { + /* FTPFILE_NOCWD with full path: expect ftpc->cwddone! */ + DEBUGASSERT((conn->data->set.ftp_filemethod != FTPFILE_NOCWD) || + !(ftpc->dirdepth && ftpc->dirs[0][0] == '/')); + ftpc->count2 = 0; /* count2 counts failed CWDs */ /* count3 is set to allow a MKD to fail once. In the case when first CWD @@ -883,10 +868,9 @@ static CURLcode ftp_state_cwd(struct connectdata *conn) dir) this then allows for a second try to CWD to it */ ftpc->count3 = (conn->data->set.ftp_create_missing_dirs == 2)?1:0; - if((conn->data->set.ftp_filemethod == FTPFILE_NOCWD) && !ftpc->cwdcount) - /* No CWD necessary */ - result = ftp_state_mdtm(conn); - else if(conn->bits.reuse && ftpc->entrypath) { + if(conn->bits.reuse && ftpc->entrypath && + /* no need to go to entrypath when we have an absolute path */ + !(ftpc->dirdepth && ftpc->dirs[0][0] == '/')) { /* This is a re-used connection. Since we change directory to where the transfer is taking place, we must first get back to the original dir where we ended up after login: */ @@ -926,7 +910,7 @@ static CURLcode ftp_state_use_port(struct connectdata *conn, struct ftp_conn *ftpc = &conn->proto.ftpc; struct Curl_easy *data = conn->data; curl_socket_t portsock = CURL_SOCKET_BAD; - char myhost[256] = ""; + char myhost[MAX_IPADR_LEN + 1] = ""; struct Curl_sockaddr_storage ss; Curl_addrinfo *res, *ai; @@ -937,9 +921,8 @@ static CURLcode ftp_state_use_port(struct connectdata *conn, #ifdef ENABLE_IPV6 struct sockaddr_in6 * const sa6 = (void *)sa; #endif - char tmp[1024]; static const char mode[][5] = { "EPRT", "PORT" }; - int rc; + enum resolve_t rc; int error; char *host = NULL; char *string_ftpport = data->set.str[STRING_FTPPORT]; @@ -1252,8 +1235,10 @@ static CURLcode ftp_state_use_port(struct connectdata *conn, break; } if(PORT == fcmd) { + /* large enough for [IP address],[num],[num] */ + char target[sizeof(myhost) + 20]; char *source = myhost; - char *dest = tmp; + char *dest = target; /* translate x.x.x.x to x,x,x,x */ while(source && *source) { @@ -1267,7 +1252,7 @@ static CURLcode ftp_state_use_port(struct connectdata *conn, *dest = 0; msnprintf(dest, 20, ",%d,%d", (int)(port>>8), (int)(port&0xff)); - result = Curl_pp_sendf(&ftpc->pp, "%s %s", mode[fcmd], tmp); + result = Curl_pp_sendf(&ftpc->pp, "%s %s", mode[fcmd], target); if(result) { failf(data, "Failure sending PORT command: %s", curl_easy_strerror(result)); @@ -1445,31 +1430,37 @@ static CURLcode ftp_state_list(struct connectdata *conn) servers either... */ /* - if FTPFILE_NOCWD was specified, we are currently in - the user's home directory, so we should add the path + if FTPFILE_NOCWD was specified, we should add the path as argument for the LIST / NLST / or custom command. Whether the server will support this, is uncertain. The other ftp_filemethods will CWD into dir/dir/ first and then just do LIST (in that case: nothing to do here) */ - char *cmd, *lstArg, *slashPos; - const char *inpath = ftp->path; - - lstArg = NULL; - if((data->set.ftp_filemethod == FTPFILE_NOCWD) && - inpath && inpath[0] && strchr(inpath, '/')) { - size_t n = strlen(inpath); - - /* Check if path does not end with /, as then we cut off the file part */ - if(inpath[n - 1] != '/') { - /* chop off the file part if format is dir/dir/file */ - slashPos = strrchr(inpath, '/'); - n = slashPos - inpath; - } - result = Curl_urldecode(data, inpath, n, &lstArg, NULL, TRUE); + char *lstArg = NULL; + char *cmd; + + if((data->set.ftp_filemethod == FTPFILE_NOCWD) && ftp->path) { + /* url-decode before evaluation: e.g. paths starting/ending with %2f */ + const char *slashPos = NULL; + char *rawPath = NULL; + result = Curl_urldecode(data, ftp->path, 0, &rawPath, NULL, TRUE); if(result) return result; + + slashPos = strrchr(rawPath, '/'); + if(slashPos) { + /* chop off the file part if format is dir/file otherwise remove + the trailing slash for dir/dir/ except for absolute path / */ + size_t n = slashPos - rawPath; + if(n == 0) + ++n; + + lstArg = rawPath; + lstArg[n] = '\0'; + } + else + free(rawPath); } cmd = aprintf("%s%s%s", @@ -1478,15 +1469,12 @@ static CURLcode ftp_state_list(struct connectdata *conn) (data->set.ftp_list_only?"NLST":"LIST"), lstArg? " ": "", lstArg? lstArg: ""); + free(lstArg); - if(!cmd) { - free(lstArg); + if(!cmd) return CURLE_OUT_OF_MEMORY; - } result = Curl_pp_sendf(&conn->proto.ftpc.pp, "%s", cmd); - - free(lstArg); free(cmd); if(result) @@ -1499,24 +1487,14 @@ static CURLcode ftp_state_list(struct connectdata *conn) static CURLcode ftp_state_retr_prequote(struct connectdata *conn) { - CURLcode result = CURLE_OK; - /* We've sent the TYPE, now we must send the list of prequote strings */ - - result = ftp_state_quote(conn, TRUE, FTP_RETR_PREQUOTE); - - return result; + return ftp_state_quote(conn, TRUE, FTP_RETR_PREQUOTE); } static CURLcode ftp_state_stor_prequote(struct connectdata *conn) { - CURLcode result = CURLE_OK; - /* We've sent the TYPE, now we must send the list of prequote strings */ - - result = ftp_state_quote(conn, TRUE, FTP_STOR_PREQUOTE); - - return result; + return ftp_state_quote(conn, TRUE, FTP_STOR_PREQUOTE); } static CURLcode ftp_state_type(struct connectdata *conn) @@ -1819,7 +1797,7 @@ static CURLcode ftp_state_pasv_resp(struct connectdata *conn, CURLcode result; struct Curl_easy *data = conn->data; struct Curl_dns_entry *addr = NULL; - int rc; + enum resolve_t rc; unsigned short connectport; /* the local port connect() should use! */ char *str = &data->state.buffer[4]; /* start on the first letter */ @@ -2052,13 +2030,11 @@ static CURLcode ftp_state_mdtm_resp(struct connectdata *conn, &year, &month, &day, &hour, &minute, &second)) { /* we have a time, reformat it */ char timebuf[24]; - time_t secs = time(NULL); - msnprintf(timebuf, sizeof(timebuf), "%04d%02d%02d %02d:%02d:%02d GMT", year, month, day, hour, minute, second); /* now, convert this into a time() value: */ - data->info.filetime = curl_getdate(timebuf, &secs); + data->info.filetime = Curl_getdate_capped(timebuf); } #ifdef CURL_FTP_HTTPSTYLE_HEAD @@ -2261,9 +2237,25 @@ static CURLcode ftp_state_size_resp(struct connectdata *conn, char *buf = data->state.buffer; /* get the size from the ascii string: */ - if(ftpcode == 213) + if(ftpcode == 213) { + /* To allow servers to prepend "rubbish" in the response string, we scan + for all the digits at the end of the response and parse only those as a + number. */ + char *start = &buf[4]; + char *fdigit = strchr(start, '\r'); + if(fdigit) { + do + fdigit--; + while(ISDIGIT(*fdigit) && (fdigit > start)); + if(!ISDIGIT(*fdigit)) + fdigit++; + } + else + fdigit = start; /* ignores parsing errors, which will make the size remain unknown */ - (void)curlx_strtoofft(buf + 4, NULL, 0, &filesize); + (void)curlx_strtoofft(fdigit, NULL, 0, &filesize); + + } if(instate == FTP_SIZE) { #ifdef CURL_FTP_HTTPSTYLE_HEAD @@ -2527,7 +2519,6 @@ static CURLcode ftp_state_user_resp(struct connectdata *conn, { CURLcode result = CURLE_OK; struct Curl_easy *data = conn->data; - struct FTP *ftp = data->req.protop; struct ftp_conn *ftpc = &conn->proto.ftpc; (void)instate; /* no use for this yet */ @@ -2535,7 +2526,7 @@ static CURLcode ftp_state_user_resp(struct connectdata *conn, if((ftpcode == 331) && (ftpc->state == FTP_USER)) { /* 331 Password required for ... (the server requires to send the user's password too) */ - PPSENDF(&ftpc->pp, "PASS %s", ftp->passwd?ftp->passwd:""); + PPSENDF(&ftpc->pp, "PASS %s", conn->passwd?conn->passwd:""); state(conn, FTP_PASS); } else if(ftpcode/100 == 2) { @@ -3134,7 +3125,8 @@ static CURLcode ftp_done(struct connectdata *conn, CURLcode status, ssize_t nread; int ftpcode; CURLcode result = CURLE_OK; - char *path = NULL; + char *rawPath = NULL; + size_t pathLen = 0; if(!ftp) return CURLE_OK; @@ -3172,9 +3164,6 @@ static CURLcode ftp_done(struct connectdata *conn, CURLcode status, break; } - /* now store a copy of the directory we are in */ - free(ftpc->prevpath); - if(data->state.wildcardmatch) { if(data->set.chunk_end && ftpc->file) { Curl_set_in_callback(data, true); @@ -3185,41 +3174,41 @@ static CURLcode ftp_done(struct connectdata *conn, CURLcode status, } if(!result) - /* get the "raw" path */ - result = Curl_urldecode(data, ftp->path, 0, &path, NULL, TRUE); + /* get the url-decoded "raw" path */ + result = Curl_urldecode(data, ftp->path, 0, &rawPath, &pathLen, TRUE); if(result) { /* We can limp along anyway (and should try to since we may already be in * the error path) */ ftpc->ctl_valid = FALSE; /* mark control connection as bad */ connclose(conn, "FTP: out of memory!"); /* mark for connection closure */ + free(ftpc->prevpath); ftpc->prevpath = NULL; /* no path remembering */ } - else { - size_t flen = ftpc->file?strlen(ftpc->file):0; /* file is "raw" already */ - size_t dlen = strlen(path)-flen; - if(!ftpc->cwdfail) { - ftpc->prevmethod = data->set.ftp_filemethod; - if(dlen && (data->set.ftp_filemethod != FTPFILE_NOCWD)) { - ftpc->prevpath = path; - if(flen) - /* if 'path' is not the whole string */ - ftpc->prevpath[dlen] = 0; /* terminate */ + else { /* remember working directory for connection reuse */ + if((data->set.ftp_filemethod == FTPFILE_NOCWD) && (rawPath[0] == '/')) + free(rawPath); /* full path => no CWDs happened => keep ftpc->prevpath */ + else { + free(ftpc->prevpath); + + if(!ftpc->cwdfail) { + if(data->set.ftp_filemethod == FTPFILE_NOCWD) + pathLen = 0; /* relative path => working directory is FTP home */ + else + pathLen -= ftpc->file?strlen(ftpc->file):0; /* file is url-decoded */ + + rawPath[pathLen] = '\0'; + ftpc->prevpath = rawPath; } else { - free(path); - /* we never changed dir */ - ftpc->prevpath = strdup(""); - if(!ftpc->prevpath) - return CURLE_OUT_OF_MEMORY; + free(rawPath); + ftpc->prevpath = NULL; /* no path */ } - if(ftpc->prevpath) - infof(data, "Remembering we are in dir \"%s\"\n", ftpc->prevpath); - } - else { - ftpc->prevpath = NULL; /* no path */ - free(path); } + + if(ftpc->prevpath) + infof(data, "Remembering we are in dir \"%s\"\n", ftpc->prevpath); } + /* free the dir tree and file parts */ freedirs(ftpc); @@ -3532,14 +3521,13 @@ static CURLcode ftp_do_more(struct connectdata *conn, int *completep) /* if we got an error or if we don't wait for a data connection return immediately */ - if(result || (ftpc->wait_data_conn != TRUE)) + if(result || !ftpc->wait_data_conn) return result; - if(ftpc->wait_data_conn) - /* 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 */ - *completep = 0; + /* 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 */ + *completep = 0; } if(ftp->transfer <= FTPTRANSFER_INFO) { @@ -3573,13 +3561,8 @@ static CURLcode ftp_do_more(struct connectdata *conn, int *completep) return result; result = ftp_multi_statemach(conn, &complete); - if(ftpc->wait_data_conn) - /* 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 */ - *completep = 0; - else - *completep = (int)complete; + /* ftpc->wait_data_conn is always false here */ + *completep = (int)complete; } else { /* download */ @@ -3619,10 +3602,8 @@ static CURLcode ftp_do_more(struct connectdata *conn, int *completep) return result; } - if(!result && (ftp->transfer != FTPTRANSFER_BODY)) - /* no data to transfer. FIX: it feels like a kludge to have this here - too! */ - Curl_setup_transfer(data, -1, -1, FALSE, -1); + /* no data to transfer */ + Curl_setup_transfer(data, -1, -1, FALSE, -1); if(!ftpc->wait_data_conn) { /* no waiting for the data connection so this is now complete */ @@ -4100,186 +4081,142 @@ CURLcode ftp_parse_url_path(struct connectdata *conn) /* the ftp struct is already inited in ftp_connect() */ 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 = ftp->path; - const char *cur_pos; - const char *filename = NULL; - - cur_pos = path_to_use; /* current position in path. point at the begin of - next path component */ + const char *slashPos = NULL; + const char *fileName = NULL; + CURLcode result = CURLE_OK; + char *rawPath = NULL; /* url-decoded "raw" path */ + size_t pathLen = 0; ftpc->ctl_valid = FALSE; ftpc->cwdfail = FALSE; - switch(data->set.ftp_filemethod) { - case FTPFILE_NOCWD: - /* fastest, but less standard-compliant */ - - /* - The best time to check whether the path is a file or directory is right - here. so: + /* url-decode ftp path before further evaluation */ + result = Curl_urldecode(data, ftp->path, 0, &rawPath, &pathLen, TRUE); + if(result) + return result; - the first condition in the if() right here, is there just in case - someone decides to set path to NULL one day - */ - if(path_to_use[0] && - (path_to_use[strlen(path_to_use) - 1] != '/') ) - filename = path_to_use; /* this is a full file path */ - /* - else { - ftpc->file is not used anywhere other than for operations on a file. - In other words, never for directory operations. - So we can safely leave filename as NULL here and use it as a - argument in dir/file decisions. - } - */ - break; + switch(data->set.ftp_filemethod) { + case FTPFILE_NOCWD: /* fastest, but less standard-compliant */ - case FTPFILE_SINGLECWD: - /* get the last slash */ - if(!path_to_use[0]) { - /* no dir, no file */ - ftpc->dirdepth = 0; + if((pathLen > 0) && (rawPath[pathLen - 1] != '/')) + fileName = rawPath; /* this is a full file path */ + /* + else: ftpc->file is not used anywhere other than for operations on + a file. In other words, never for directory operations. + So we can safely leave filename as NULL here and use it as a + argument in dir/file decisions. + */ break; - } - slash_pos = strrchr(cur_pos, '/'); - if(slash_pos || !*cur_pos) { - size_t dirlen = slash_pos-cur_pos; - CURLcode result; - ftpc->dirs = calloc(1, sizeof(ftpc->dirs[0])); - if(!ftpc->dirs) - return CURLE_OUT_OF_MEMORY; + case FTPFILE_SINGLECWD: + slashPos = strrchr(rawPath, '/'); + if(slashPos) { + /* get path before last slash, except for / */ + size_t dirlen = slashPos - rawPath; + if(dirlen == 0) + dirlen++; + + ftpc->dirs = calloc(1, sizeof(ftpc->dirs[0])); + if(!ftpc->dirs) { + free(rawPath); + return CURLE_OUT_OF_MEMORY; + } - if(!dirlen) - dirlen++; + ftpc->dirs[0] = calloc(1, dirlen + 1); + if(!ftpc->dirs[0]) { + free(rawPath); + return CURLE_OUT_OF_MEMORY; + } - result = Curl_urldecode(conn->data, slash_pos ? cur_pos : "/", - slash_pos ? dirlen : 1, - &ftpc->dirs[0], NULL, - TRUE); - if(result) { - freedirs(ftpc); - return result; + strncpy(ftpc->dirs[0], rawPath, dirlen); + ftpc->dirdepth = 1; /* we consider it to be a single dir */ + fileName = slashPos + 1; /* rest is file name */ } - ftpc->dirdepth = 1; /* we consider it to be a single dir */ - filename = slash_pos ? slash_pos + 1 : cur_pos; /* rest is file name */ - } - else - filename = cur_pos; /* this is a file name only */ - break; + else + fileName = rawPath; /* file name only (or empty) */ + break; - default: /* allow pretty much anything */ - case FTPFILE_MULTICWD: - ftpc->dirdepth = 0; - ftpc->diralloc = 5; /* default dir depth to allocate */ - ftpc->dirs = calloc(ftpc->diralloc, sizeof(ftpc->dirs[0])); - if(!ftpc->dirs) - return CURLE_OUT_OF_MEMORY; + default: /* allow pretty much anything */ + case FTPFILE_MULTICWD: { + /* current position: begin of next path component */ + const char *curPos = rawPath; + + int dirAlloc = 0; /* number of entries allocated for the 'dirs' array */ + const char *str = rawPath; + for(; *str != 0; ++str) + if (*str == '/') + ++dirAlloc; + + if(dirAlloc > 0) { + ftpc->dirs = calloc(dirAlloc, sizeof(ftpc->dirs[0])); + if(!ftpc->dirs) { + free(rawPath); + return CURLE_OUT_OF_MEMORY; + } + + /* parse the URL path into separate path components */ + while((slashPos = strchr(curPos, '/')) != NULL) { + size_t compLen = slashPos - curPos; + + /* path starts with a slash: add that as a directory */ + if((compLen == 0) && (ftpc->dirdepth == 0)) + ++compLen; - /* we have a special case for listing the root dir only */ - if(!strcmp(path_to_use, "/")) { - cur_pos++; /* make it point to the zero byte */ - ftpc->dirs[0] = strdup("/"); - ftpc->dirdepth++; - } - else { - /* parse the URL path into separate path components */ - while((slash_pos = strchr(cur_pos, '/')) != NULL) { - /* 1 or 0 pointer offset to indicate absolute directory */ - ssize_t absolute_dir = ((cur_pos - ftp->path > 0) && - (ftpc->dirdepth == 0))?1:0; - - /* seek out the next path component */ - if(slash_pos-cur_pos) { /* we skip empty path components, like "x//y" since the FTP command CWD requires a parameter and a non-existent parameter a) doesn't work on many servers and b) has no effect on the others. */ - size_t len = slash_pos - cur_pos + absolute_dir; - CURLcode result = - Curl_urldecode(conn->data, cur_pos - absolute_dir, len, - &ftpc->dirs[ftpc->dirdepth], NULL, - TRUE); - if(result) { - freedirs(ftpc); - return result; - } - } - 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); + if(compLen > 0) { + char *comp = calloc(1, compLen + 1); + if(!comp) { + free(rawPath); return CURLE_OUT_OF_MEMORY; } + strncpy(comp, curPos, compLen); + ftpc->dirs[ftpc->dirdepth++] = comp; } - continue; - } - - cur_pos = slash_pos + 1; /* jump to the rest of the string */ - if(++ftpc->dirdepth >= ftpc->diralloc) { - /* enlarge array */ - char **bigger; - ftpc->diralloc *= 2; /* double the size each time */ - bigger = realloc(ftpc->dirs, ftpc->diralloc * sizeof(ftpc->dirs[0])); - if(!bigger) { - freedirs(ftpc); - return CURLE_OUT_OF_MEMORY; - } - ftpc->dirs = bigger; + curPos = slashPos + 1; } } + DEBUGASSERT(ftpc->dirdepth <= dirAlloc); + fileName = curPos; /* the rest is the file name (or empty) */ } - filename = cur_pos; /* the rest is the file name */ break; } /* switch */ - if(filename && *filename) { - CURLcode result = - Curl_urldecode(conn->data, filename, 0, &ftpc->file, NULL, TRUE); - - if(result) { - freedirs(ftpc); - return result; - } - } + if(fileName && *fileName) + ftpc->file = strdup(fileName); else - ftpc->file = NULL; /* instead of point to a zero byte, we make it a NULL - pointer */ + ftpc->file = NULL; /* instead of point to a zero byte, + we make it a NULL pointer */ if(data->set.upload && !ftpc->file && (ftp->transfer == FTPTRANSFER_BODY)) { /* We need a file name when uploading. Return error! */ failf(data, "Uploading to a URL without a file name!"); + free(rawPath); return CURLE_URL_MALFORMAT; } ftpc->cwddone = FALSE; /* default to not done */ - if(ftpc->prevpath) { - /* prevpath is "raw" so we convert the input path before we compare the - strings */ - size_t dlen; - char *path; - CURLcode result = - Curl_urldecode(conn->data, ftp->path, 0, &path, &dlen, TRUE); - if(result) { - freedirs(ftpc); - return result; - } + if((data->set.ftp_filemethod == FTPFILE_NOCWD) && (rawPath[0] == '/')) + ftpc->cwddone = TRUE; /* skip CWD for absolute paths */ + else { /* newly created FTP connections are already in entry path */ + const char *oldPath = conn->bits.reuse ? ftpc->prevpath : ""; + if(oldPath) { + size_t n = pathLen; + if(data->set.ftp_filemethod == FTPFILE_NOCWD) + n = 0; /* CWD to entry for relative paths */ + else + n -= ftpc->file?strlen(ftpc->file):0; - dlen -= ftpc->file?strlen(ftpc->file):0; - if((dlen == strlen(ftpc->prevpath)) && - !strncmp(path, ftpc->prevpath, dlen) && - (ftpc->prevmethod == data->set.ftp_filemethod)) { - infof(data, "Request has same path as previous transfer\n"); - ftpc->cwddone = TRUE; + if((strlen(oldPath) == n) && !strncmp(rawPath, oldPath, n)) { + infof(data, "Request has same path as previous transfer\n"); + ftpc->cwddone = TRUE; + } } - free(path); } + free(rawPath); return CURLE_OK; } @@ -4423,18 +4360,6 @@ static CURLcode ftp_setup_connection(struct connectdata *conn) /* get some initial data into the ftp struct */ 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; |