aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/curl/curl.h3
-rw-r--r--lib/ftp.c113
-rw-r--r--lib/url.c8
-rw-r--r--lib/urldata.h14
-rw-r--r--src/main.c23
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;
diff --git a/lib/ftp.c b/lib/ftp.c
index fe86ba392..4af0bda76 100644
--- a/lib/ftp.c
+++ b/lib/ftp.c
@@ -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);
diff --git a/lib/url.c b/lib/url.c
index bff500f57..ff52e459f 100644
--- a/lib/url.c
+++ b/lib/url.c
@@ -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;