ftp: keep the "raw" URL decoded version of the path around

Instead of doing temporary decodes in three different places.
This commit is contained in:
Daniel Stenberg 2025-08-18 14:11:33 +02:00
parent 7dafe10db2
commit ecdaf4c0d8
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
2 changed files with 21 additions and 42 deletions

View file

@ -333,17 +333,13 @@ static void freedirs(struct ftp_conn *ftpc)
{
if(ftpc->dirs) {
int i;
for(i = 0; i < ftpc->dirdepth; i++) {
for(i = 0; i < ftpc->dirdepth; i++)
free(ftpc->dirs[i]);
ftpc->dirs[i] = NULL;
}
free(ftpc->dirs);
ftpc->dirs = NULL;
Curl_safefree(ftpc->dirs);
ftpc->dirdepth = 0;
}
Curl_safefree(ftpc->rawpath);
Curl_safefree(ftpc->file);
/* no longer of any use */
Curl_safefree(ftpc->newhost);
}
@ -1415,18 +1411,14 @@ static CURLcode ftp_state_list(struct Curl_easy *data,
The other ftp_filemethods will CWD into dir/dir/ first and
then just do LIST (in that case: nothing to do here)
*/
char *lstArg = NULL;
const char *lstArg = NULL;
int lstArglen = 0;
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(ftp->path, 0, &rawPath, NULL, REJECT_CTRL);
if(result)
return result;
slashPos = strrchr(rawPath, '/');
const char *rawPath = ftpc->rawpath;
const char *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 / */
@ -1435,19 +1427,16 @@ static CURLcode ftp_state_list(struct Curl_easy *data,
++n;
lstArg = rawPath;
lstArg[n] = '\0';
lstArglen = (int)n;
}
else
free(rawPath);
}
cmd = aprintf("%s%s%s",
cmd = aprintf("%s%s%.*s",
data->set.str[STRING_CUSTOMREQUEST] ?
data->set.str[STRING_CUSTOMREQUEST] :
(data->state.list_only ? "NLST" : "LIST"),
lstArg ? " " : "",
lstArg ? lstArg : "");
free(lstArg);
lstArglen, lstArg ? lstArg : "");
if(!cmd)
return CURLE_OUT_OF_MEMORY;
@ -3269,8 +3258,7 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status,
ssize_t nread;
int ftpcode;
CURLcode result = CURLE_OK;
char *rawPath = NULL;
size_t pathLen = 0;
const char *rawPath = NULL;
if(!ftp || !ftpc)
return CURLE_OK;
@ -3317,10 +3305,6 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status,
ftpc->known_filesize = -1;
}
if(!result)
/* get the url-decoded "raw" path */
result = Curl_urldecode(ftp->path, 0, &rawPath, &pathLen,
REJECT_CTRL);
if(result) {
/* We can limp along anyway (and should try to since we may already be in
* the error path) */
@ -3330,9 +3314,12 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status,
ftpc->prevpath = NULL; /* no path remembering */
}
else { /* remember working directory for connection reuse */
rawPath = ftpc->rawpath;
if((data->set.ftp_filemethod == FTPFILE_NOCWD) && (rawPath[0] == '/'))
free(rawPath); /* full path => no CWDs happened => keep ftpc->prevpath */
; /* full path => no CWDs happened => keep ftpc->prevpath */
else {
size_t pathLen = strlen(ftpc->rawpath);
free(ftpc->prevpath);
if(!ftpc->cwdfail) {
@ -3341,14 +3328,10 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status,
else
/* file is url-decoded */
pathLen -= ftpc->file ? strlen(ftpc->file) : 0;
rawPath[pathLen] = '\0';
ftpc->prevpath = rawPath;
ftpc->prevpath = Curl_memdup0(rawPath, pathLen);
}
else {
free(rawPath);
else
ftpc->prevpath = NULL; /* no path */
}
}
if(ftpc->prevpath)
@ -4190,18 +4173,19 @@ CURLcode ftp_parse_url_path(struct Curl_easy *data,
const char *slashPos = NULL;
const char *fileName = NULL;
CURLcode result = CURLE_OK;
char *rawPath = NULL; /* url-decoded "raw" path */
const char *rawPath = NULL; /* url-decoded "raw" path */
size_t pathLen = 0;
ftpc->ctl_valid = FALSE;
ftpc->cwdfail = FALSE;
/* url-decode ftp path before further evaluation */
result = Curl_urldecode(ftp->path, 0, &rawPath, &pathLen, REJECT_CTRL);
result = Curl_urldecode(ftp->path, 0, &ftpc->rawpath, &pathLen, REJECT_CTRL);
if(result) {
failf(data, "path contains control characters");
return result;
}
rawPath = ftpc->rawpath;
switch(data->set.ftp_filemethod) {
case FTPFILE_NOCWD: /* fastest, but less standard-compliant */
@ -4226,13 +4210,11 @@ CURLcode ftp_parse_url_path(struct Curl_easy *data,
ftpc->dirs = calloc(1, sizeof(ftpc->dirs[0]));
if(!ftpc->dirs) {
free(rawPath);
return CURLE_OUT_OF_MEMORY;
}
ftpc->dirs[0] = Curl_memdup0(rawPath, dirlen);
if(!ftpc->dirs[0]) {
free(rawPath);
return CURLE_OUT_OF_MEMORY;
}
@ -4258,7 +4240,6 @@ CURLcode ftp_parse_url_path(struct Curl_easy *data,
if(dirAlloc) {
ftpc->dirs = calloc(dirAlloc, sizeof(ftpc->dirs[0]));
if(!ftpc->dirs) {
free(rawPath);
return CURLE_OUT_OF_MEMORY;
}
@ -4277,7 +4258,6 @@ CURLcode ftp_parse_url_path(struct Curl_easy *data,
if(compLen > 0) {
char *comp = Curl_memdup0(curPos, compLen);
if(!comp) {
free(rawPath);
return CURLE_OUT_OF_MEMORY;
}
ftpc->dirs[ftpc->dirdepth++] = comp;
@ -4300,7 +4280,6 @@ CURLcode ftp_parse_url_path(struct Curl_easy *data,
if(data->state.upload && !ftpc->file && (ftp->transfer == PPTRANSFER_BODY)) {
/* We need a filename when uploading. Return error! */
failf(data, "Uploading to a URL without a filename");
free(rawPath);
return CURLE_URL_MALFORMAT;
}
@ -4324,7 +4303,6 @@ CURLcode ftp_parse_url_path(struct Curl_easy *data,
}
}
free(rawPath);
return CURLE_OK;
}

View file

@ -129,6 +129,7 @@ struct ftp_conn {
char *alternative_to_user;
char *entrypath; /* the PWD reply when we logged on */
char *file; /* url-decoded filename (or path) */
char *rawpath; /* URL decoded, allocated, version of the path */
char **dirs; /* realloc()ed array for path components */
char *newhost; /* the (allocated) IP addr or hostname to connect the data
connection to */