diff options
-rw-r--r-- | include/curl/curl.h | 3 | ||||
-rw-r--r-- | lib/ftp.c | 113 | ||||
-rw-r--r-- | lib/url.c | 8 | ||||
-rw-r--r-- | lib/urldata.h | 14 | ||||
-rw-r--r-- | src/main.c | 23 |
5 files changed, 112 insertions, 49 deletions
diff --git a/include/curl/curl.h b/include/curl/curl.h index 48c896119..1034ffb95 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -909,6 +909,9 @@ typedef enum { control connection. */ CINIT(FTP_SKIP_PASV_IP, LONG, 137), + /* Select "file method" to use when doing FTP */ + CINIT(FTP_FILEMETHOD, LONG, 138), + CURLOPT_LASTENTRY /* the last unused */ } CURLoption; @@ -3642,8 +3642,6 @@ static CURLcode ftp_3rdparty_transfer(struct connectdata *conn) return CURLE_OK; } - - /*********************************************************************** * * ftp_parse_url_path() @@ -3667,58 +3665,87 @@ CURLcode ftp_parse_url_path(struct connectdata *conn) ftp = conn->proto.ftp; ftp->ctl_valid = FALSE; - ftp->dirdepth = 0; - ftp->diralloc = 5; /* default dir depth to allocate */ - ftp->dirs = (char **)calloc(ftp->diralloc, sizeof(ftp->dirs[0])); - if(!ftp->dirs) - return CURLE_OUT_OF_MEMORY; + switch(data->set.ftp_filemethod) { + case FTPFILE_NOCWD: + /* fastest, but less standard-compliant */ + ftp->file = conn->path; /* this is a full file path */ + break; - /* parse the URL path into separate path components */ - while((slash_pos=strchr(cur_pos, '/'))) { - /* 1 or 0 to indicate absolute directory */ - bool absolute_dir = (cur_pos - conn->path > 0) && (ftp->dirdepth == 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-existant parameter a) doesn't work on - many servers and b) has no effect on the others. */ - int len = (int)(slash_pos - cur_pos + absolute_dir); - ftp->dirs[ftp->dirdepth] = curl_unescape(cur_pos - absolute_dir, len); - - if (!ftp->dirs[ftp->dirdepth]) { /* run out of memory ... */ - failf(data, "no memory"); - freedirs(ftp); + case FTPFILE_SINGLECWD: + /* get the last slash */ + slash_pos=strrchr(cur_pos, '/'); + if(slash_pos) { + ftp->dirdepth = 1; /* we consider it to be a single dir */ + ftp->dirs = (char **)calloc(1, sizeof(ftp->dirs[0])); + if(!ftp->dirs) + return CURLE_OUT_OF_MEMORY; + + ftp->dirs[0] = curl_unescape(cur_pos, slash_pos-cur_pos); + if(!ftp->dirs[0]) { + free(ftp->dirs); return CURLE_OUT_OF_MEMORY; } - if (isBadFtpString(ftp->dirs[ftp->dirdepth])) { - freedirs(ftp); - return CURLE_URL_MALFORMAT; - } - } - else { - cur_pos = slash_pos + 1; /* jump to the rest of the string */ - continue; + ftp->file = slash_pos+1; /* the rest is the file name */ } + else + ftp->file = cur_pos; /* this is a file name only */ + break; - if(!retcode) { - cur_pos = slash_pos + 1; /* jump to the rest of the string */ - if(++ftp->dirdepth >= ftp->diralloc) { - /* enlarge array */ - char *bigger; - ftp->diralloc *= 2; /* double the size each time */ - bigger = realloc(ftp->dirs, ftp->diralloc * sizeof(ftp->dirs[0])); - if(!bigger) { - ftp->dirdepth--; + default: /* allow pretty much anything */ + case FTPFILE_MULTICWD: + ftp->dirdepth = 0; + ftp->diralloc = 5; /* default dir depth to allocate */ + ftp->dirs = (char **)calloc(ftp->diralloc, sizeof(ftp->dirs[0])); + if(!ftp->dirs) + return CURLE_OUT_OF_MEMORY; + + /* parse the URL path into separate path components */ + while((slash_pos=strchr(cur_pos, '/'))) { + /* 1 or 0 to indicate absolute directory */ + bool absolute_dir = (cur_pos - conn->path > 0) && (ftp->dirdepth == 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-existant parameter a) doesn't work on + many servers and b) has no effect on the others. */ + int len = (int)(slash_pos - cur_pos + absolute_dir); + ftp->dirs[ftp->dirdepth] = curl_unescape(cur_pos - absolute_dir, len); + + if (!ftp->dirs[ftp->dirdepth]) { /* run out of memory ... */ + failf(data, "no memory"); freedirs(ftp); return CURLE_OUT_OF_MEMORY; } - ftp->dirs = (char **)bigger; + if (isBadFtpString(ftp->dirs[ftp->dirdepth])) { + freedirs(ftp); + return CURLE_URL_MALFORMAT; + } + } + else { + cur_pos = slash_pos + 1; /* jump to the rest of the string */ + continue; + } + + if(!retcode) { + cur_pos = slash_pos + 1; /* jump to the rest of the string */ + if(++ftp->dirdepth >= ftp->diralloc) { + /* enlarge array */ + char *bigger; + ftp->diralloc *= 2; /* double the size each time */ + bigger = realloc(ftp->dirs, ftp->diralloc * sizeof(ftp->dirs[0])); + if(!bigger) { + ftp->dirdepth--; + freedirs(ftp); + return CURLE_OUT_OF_MEMORY; + } + ftp->dirs = (char **)bigger; + } } } - } - ftp->file = cur_pos; /* the rest is the file name */ + ftp->file = cur_pos; /* the rest is the file name */ + } if(*ftp->file) { ftp->file = curl_unescape(ftp->file, 0); @@ -327,7 +327,7 @@ CURLcode Curl_open(struct SessionHandle **curl) data->set.ftp_use_epsv = TRUE; /* FTP defaults to EPSV operations */ data->set.ftp_use_eprt = TRUE; /* FTP defaults to EPRT operations */ data->set.ftp_use_lprt = TRUE; /* FTP defaults to EPRT operations */ - + data->set.ftp_filemethod = FTPFILE_MULTICWD; data->set.dns_cache_timeout = 60; /* Timeout every 60 seconds by default */ /* make libcurl quiet by default: */ @@ -557,6 +557,12 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, */ data->set.ftp_append = va_arg(param, long)?TRUE:FALSE; break; + case CURLOPT_FTP_FILEMETHOD: + /* + * How do access files over FTP. + */ + data->set.ftp_filemethod = va_arg(param, long); + break; case CURLOPT_NETRC: /* * Parse the $HOME/.netrc file diff --git a/lib/urldata.h b/lib/urldata.h index b5b2d8040..3842952fe 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -310,6 +310,12 @@ typedef enum { FTP_LAST /* never used */ } ftpstate; +typedef enum { + FTPFILE_MULTICWD = 1, /* as defined by RFC1738 */ + FTPFILE_NOCWD = 2, /* use SIZE / RETR / STOR on the full path */ + FTPFILE_SINGLECWD = 3 /* make one CWD, then SIZE / RETR / STOR on the file */ +} curl_ftpfile; + struct FTP { curl_off_t *bytecountp; char *user; /* user name string */ @@ -424,10 +430,10 @@ struct ConnectBits { LPRT doesn't work we disable it for the forthcoming requests */ bool netrc; /* name+password provided by netrc */ - + bool trailerHdrPresent; /* Set when Trailer: header found in HTTP response. - Required to determine whether to look for trailers - in case of Transfer-Encoding: chunking */ + Required to determine whether to look for trailers + in case of Transfer-Encoding: chunking */ }; struct hostname { @@ -1035,6 +1041,8 @@ struct UserDefined { char *source_url; /* for 3rd party transfer */ char *source_userpwd; /* for 3rd party transfer */ + curl_ftpfile ftp_filemethod; /* how to get to a file when FTP is used */ + /* Here follows boolean settings that define how to behave during this session. They are STATIC, set by libcurl users or at least initially and they don't change during operations. */ diff --git a/src/main.c b/src/main.c index ecc78001d..4eba7bd7b 100644 --- a/src/main.c +++ b/src/main.c @@ -357,6 +357,7 @@ struct Configurable { struct curl_slist *tp_postquote; struct curl_slist *tp_prequote; char *ftp_account; /* for ACCT */ + int ftp_filemethod; bool ignorecl; /* --ignore-content-length */ }; @@ -1244,6 +1245,18 @@ static ParameterError add2list(struct curl_slist **list, return PARAM_OK; } +static int ftpfilemethod(struct Configurable *config, char *str) +{ + if(strequal("singlecwd", str)) + return 3; + if(strequal("nocwd", str)) + return 2; + if(strequal("multicwd", str)) + return 1; + warnf(config, "unrecognized ftp file method '%s', using default\n", str); + return 1; +} + static ParameterError getparameter(char *flag, /* f or -long-flag */ char *nextarg, /* NULL if unset */ bool *usedarg, /* set to TRUE if the arg @@ -1316,6 +1329,7 @@ static ParameterError getparameter(char *flag, /* f or -long-flag */ {"$o", "trace-time", FALSE}, {"$p", "ignore-content-length", FALSE}, {"$q", "ftp-skip-pasv-ip", FALSE}, + {"$r", "ftp-method", TRUE}, {"0", "http1.0", FALSE}, {"1", "tlsv1", FALSE}, @@ -1726,6 +1740,9 @@ static ParameterError getparameter(char *flag, /* f or -long-flag */ case 'q': /* --ftp-skip-pasv-ip */ config->ftp_skip_ip ^= TRUE; break; + case 'r': /* --ftp-method (undocumented at this point) */ + config->ftp_filemethod = ftpfilemethod(config, nextarg); + break; } break; case '#': /* --progress-bar */ @@ -3944,8 +3961,10 @@ operate(struct Configurable *config, int argc, char *argv[]) curl_easy_setopt(curl, CURLOPT_IGNORE_CONTENT_LENGTH, config->ignorecl); /* curl 7.14.2 */ - curl_easy_setopt(curl, CURLOPT_FTP_SKIP_PASV_IP, - config->ftp_skip_ip); + curl_easy_setopt(curl, CURLOPT_FTP_SKIP_PASV_IP, config->ftp_skip_ip); + + /* curl 7.15.1 */ + curl_easy_setopt(curl, CURLOPT_FTP_FILEMETHOD, config->ftp_filemethod); retry_numretries = config->req_retry; |