new experimental "ftp method" code
authorDaniel Stenberg <daniel@haxx.se>
Mon, 28 Nov 2005 23:06:00 +0000 (23:06 +0000)
committerDaniel Stenberg <daniel@haxx.se>
Mon, 28 Nov 2005 23:06:00 +0000 (23:06 +0000)
include/curl/curl.h
lib/ftp.c
lib/url.c
lib/urldata.h
src/main.c

index 48c8961..1034ffb 100644 (file)
@@ -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;
 
index fe86ba3..4af0bda 100644 (file)
--- 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);
index bff500f..ff52e45 100644 (file)
--- 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
index b5b2d80..3842952 100644 (file)
@@ -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. */
index ecc7800..4eba7bd 100644 (file)
@@ -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;