summaryrefslogtreecommitdiffstats
path: root/Utilities/cmcurl/lib/ftp.c
diff options
context:
space:
mode:
Diffstat (limited to 'Utilities/cmcurl/lib/ftp.c')
-rw-r--r--Utilities/cmcurl/lib/ftp.c493
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;