summaryrefslogtreecommitdiff
path: root/libs/libcurl/src/ftp.c
diff options
context:
space:
mode:
Diffstat (limited to 'libs/libcurl/src/ftp.c')
-rw-r--r--libs/libcurl/src/ftp.c396
1 files changed, 182 insertions, 214 deletions
diff --git a/libs/libcurl/src/ftp.c b/libs/libcurl/src/ftp.c
index e807a2acde..8072a33d5d 100644
--- a/libs/libcurl/src/ftp.c
+++ b/libs/libcurl/src/ftp.c
@@ -523,7 +523,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);
}
@@ -867,6 +867,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
@@ -874,10 +878,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: */
@@ -1436,31 +1439,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",
@@ -1469,15 +1478,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)
@@ -2242,9 +2248,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
@@ -3115,7 +3137,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;
@@ -3153,9 +3176,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);
@@ -3166,41 +3186,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);
@@ -3513,14 +3533,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) {
@@ -3554,13 +3573,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 */
@@ -3600,10 +3614,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 */
@@ -4080,186 +4092,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;
}