]> granicus.if.org Git - curl/commitdiff
moved the PORT and PASV code into separate smaller functions for readability
authorDaniel Stenberg <daniel@haxx.se>
Fri, 28 Sep 2001 08:58:18 +0000 (08:58 +0000)
committerDaniel Stenberg <daniel@haxx.se>
Fri, 28 Sep 2001 08:58:18 +0000 (08:58 +0000)
renamed all static ^_ftp_* functions to ^ftp_, prefixing with underscore is
not nice

lib/ftp.c

index 200cddc4764164118e15f83e068e98ae55cb406b..0e8758c1d6b06bc272883d3054b33dd2a225f996 100644 (file)
--- a/lib/ftp.c
+++ b/lib/ftp.c
@@ -92,8 +92,8 @@
 #endif
 
 /* Local API functions */
-static CURLcode _ftp_sendquote(struct connectdata *conn, struct curl_slist *quote);
-static CURLcode _ftp_cwd(struct connectdata *conn, char *path);
+static CURLcode ftp_sendquote(struct connectdata *conn, struct curl_slist *quote);
+static CURLcode ftp_cwd(struct connectdata *conn, char *path);
 
 /* easy-to-use macro: */
 #define ftpsendf Curl_ftpsendf
@@ -590,7 +590,7 @@ CURLcode Curl_ftp_done(struct connectdata *conn)
 
   /* Send any post-transfer QUOTE strings? */
   if(data->set.postquote) {
-    CURLcode result = _ftp_sendquote(conn, data->set.postquote);
+    CURLcode result = ftp_sendquote(conn, data->set.postquote);
     return result;
   }
 
@@ -599,7 +599,7 @@ CURLcode Curl_ftp_done(struct connectdata *conn)
 
 
 static 
-CURLcode _ftp_sendquote(struct connectdata *conn, struct curl_slist *quote)
+CURLcode ftp_sendquote(struct connectdata *conn, struct curl_slist *quote)
 {
   struct curl_slist *item;
   ssize_t            nread;
@@ -628,7 +628,7 @@ CURLcode _ftp_sendquote(struct connectdata *conn, struct curl_slist *quote)
 }
 
 static 
-CURLcode _ftp_cwd(struct connectdata *conn, char *path)
+CURLcode ftp_cwd(struct connectdata *conn, char *path)
 {
   ssize_t nread;
   int     ftpcode;
@@ -648,7 +648,7 @@ CURLcode _ftp_cwd(struct connectdata *conn, char *path)
 }
 
 static
-CURLcode _ftp_getfiletime(struct connectdata *conn, char *file)
+CURLcode ftp_getfiletime(struct connectdata *conn, char *file)
 {
   CURLcode result=CURLE_OK;
   int ftpcode; /* for ftp status */
@@ -683,7 +683,7 @@ CURLcode _ftp_getfiletime(struct connectdata *conn, char *file)
   return  result;
 }
 
-static CURLcode _ftp_transfertype(struct connectdata *conn,
+static CURLcode ftp_transfertype(struct connectdata *conn,
                                   bool ascii)
 {
   struct SessionHandle *data = conn->data;
@@ -707,7 +707,7 @@ static CURLcode _ftp_transfertype(struct connectdata *conn,
 }
 
 static
-CURLcode _ftp_getsize(struct connectdata *conn, char *file,
+CURLcode ftp_getsize(struct connectdata *conn, char *file,
                       ssize_t *size)
 {
   struct SessionHandle *data = conn->data;
@@ -741,12 +741,9 @@ CURLcode _ftp_getsize(struct connectdata *conn, char *file,
  */
 static void
 ftp_pasv_verbose(struct connectdata *conn,
-#ifdef ENABLE_IPV6
-                 struct addrinfo *newhost
-#else
-                 char *newhost /* ipv4 */
-#endif
-)
+                 Curl_addrinfo *addr,
+                 char *newhost, /* ascii version */
+                 int port)
 {
 #ifndef ENABLE_IPV6
   /*****************************************************************
@@ -757,11 +754,10 @@ ftp_pasv_verbose(struct connectdata *conn,
   struct in_addr in;
   struct hostent * answer;
 
-#if defined (HAVE_INET_NTOA_R)
+#ifdef HAVE_INET_NTOA_R
   char ntoa_buf[64];
 #endif
 #ifndef ENABLE_IPV6
-  struct sockaddr_in serv_addr;
   char hostent_buf[8192];
 #endif
 
@@ -817,11 +813,11 @@ ftp_pasv_verbose(struct connectdata *conn,
   infof(conn->data, "Connecting to %s (%s) port %u\n",
         answer?answer->h_name:newhost,
 #if defined(HAVE_INET_NTOA_R)
-        inet_ntoa_r(in, ip_addr=ntoa_buf, sizeof(ntoa_buf)),
+        inet_ntoa_r(in, ntoa_buf, sizeof(ntoa_buf)),
 #else
-        ip_addr = inet_ntoa(in),
+        inet_ntoa(in),
 #endif
-        connectport);
+        port);
 
 #else
   /*****************************************************************
@@ -836,13 +832,13 @@ ftp_pasv_verbose(struct connectdata *conn,
 #else
   const int niflags = NI_NUMERICHOST | NI_NUMERICSERV;
 #endif
-  if (getnameinfo(newhost->ai_addr, newhost->ai_addrlen,
+  if (getnameinfo(addr->ai_addr, addr->ai_addrlen,
                   nbuf, sizeof(nbuf), sbuf, sizeof(sbuf), niflags)) {
     snprintf(nbuf, sizeof(nbuf), "?");
     snprintf(sbuf, sizeof(sbuf), "?");
   }
         
-  if (getnameinfo(newhost->ai_addr, newhost->ai_addrlen,
+  if (getnameinfo(addr->ai_addr, addr->ai_addrlen,
                   hbuf, sizeof(hbuf), NULL, 0, 0)) {
     infof(conn->data, "Connecting to %s port %s\n", nbuf, sbuf);
   } 
@@ -850,570 +846,630 @@ ftp_pasv_verbose(struct connectdata *conn,
     infof(conn->data, "Connecting to %s (%s) port %s\n", hbuf, nbuf, sbuf);
   }
 #endif
-
 }
 
+/**********
+ * PORT is the ftp client's way of telling the server that *WE* open a port
+ * that we listen on an awaits the server to connect to. This is the opposite
+ * of PASV.
+ */
 
 static
-CURLcode _ftp(struct connectdata *conn)
+CURLcode ftp_use_port(struct connectdata *conn)
 {
-  /* this is FTP and no proxy */
-  ssize_t nread;
-  CURLcode result;
   struct SessionHandle *data=conn->data;
-  char *buf = data->state.buffer; /* this is our buffer */
-  /* for the ftp PORT mode */
   int portsock=-1;
-  /* the ftp struct is already inited in ftp_connect() */
-  struct FTP *ftp = conn->proto.ftp;
-
-  long *bytecountp = ftp->bytecountp;
-  int ftpcode; /* for ftp status */
-
-  /* Send any QUOTE strings? */
-  if(data->set.quote) {
-    if ((result = _ftp_sendquote(conn, data->set.quote)) != CURLE_OK)
-      return result;
-  }
-    
-  /* This is a re-used connection. Since we change directory to where the
-     transfer is taking place, we must now get back to the original dir
-     where we ended up after login: */
-  if (conn->bits.reuse) {
-    if ((result = _ftp_cwd(conn, ftp->entrypath)) != CURLE_OK)
-      return result;
-  }
-
-  /* change directory first! */
-  if(ftp->dir && ftp->dir[0]) {
-    if ((result = _ftp_cwd(conn, ftp->dir)) != CURLE_OK)
-        return result;
-  }
-
-  /* Requested time of file? */
-  if(data->set.get_filetime && ftp->file) {
-    result = _ftp_getfiletime(conn, ftp->file);
-    if(result)
-      return result;
-  }
-
-  /* If we have selected NOBODY, it means that we only want file information.
-     Which in FTP can't be much more than the file size! */
-  if(data->set.no_body) {
-    /* The SIZE command is _not_ RFC 959 specified, and therefor many servers
-       may not support it! It is however the only way we have to get a file's
-       size! */
-    ssize_t filesize;
-
-    /* Some servers return different sizes for different modes, and thus we
-       must set the proper type before we check the size */
-    result = _ftp_transfertype(conn, data->set.ftp_ascii);
-    if(result)
-      return result;
-
-    /* failing to get size is not a serious error */
-    result = _ftp_getsize(conn, ftp->file, &filesize);
-
-    if(CURLE_OK == result) {
-      sprintf(buf, "Content-Length: %d\r\n", filesize);
-      result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0);
-      if(result)
-        return result;
-    }
-
-    /* If we asked for a time of the file and we actually got one as
-       well, we "emulate" a HTTP-style header in our output. */
-
-#ifdef HAVE_STRFTIME
-    if(data->set.get_filetime && data->info.filetime) {
-      struct tm *tm;
-#ifdef HAVE_LOCALTIME_R
-      struct tm buffer;
-      tm = (struct tm *)localtime_r(&data->info.filetime, &buffer);
-#else
-      tm = localtime(&data->info.filetime);
-#endif
-      /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */
-      strftime(buf, BUFSIZE-1, "Last-Modified: %a, %d %b %Y %H:%M:%S %Z\r\n",
-               tm);
-      result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0);
-      if(result)
-        return result;
-    }
-#endif
-
-    return CURLE_OK;
-  }
+  ssize_t nread;
+  char *buf = data->state.buffer; /* this is our buffer */
+  int ftpcode; /* receive FTP response codes in this */
 
-  /* We have chosen to use the PORT command */
-  if(data->set.ftp_use_port) {
 #ifdef ENABLE_IPV6
-    struct addrinfo hints, *res, *ai;
-    struct sockaddr_storage ss;
-    socklen_t sslen;
-    char hbuf[NI_MAXHOST];
+  /******************************************************************
+   *
+   * Here's a piece of IPv6-specific code coming up
+   *
+   */
 
-    struct sockaddr *sa=(struct sockaddr *)&ss;
+  struct addrinfo hints, *res, *ai;
+  struct sockaddr_storage ss;
+  socklen_t sslen;
+  char hbuf[NI_MAXHOST];
+
+  struct sockaddr *sa=(struct sockaddr *)&ss;
 #ifdef NI_WITHSCOPEID
-    const int niflags = NI_NUMERICHOST | NI_NUMERICSERV | NI_WITHSCOPEID;
+  const int niflags = NI_NUMERICHOST | NI_NUMERICSERV | NI_WITHSCOPEID;
 #else
-    const int niflags = NI_NUMERICHOST | NI_NUMERICSERV;
+  const int niflags = NI_NUMERICHOST | NI_NUMERICSERV;
 #endif
-    unsigned char *ap;
-    unsigned char *pp;
-    int alen, plen;
-    char portmsgbuf[4096], tmp[4096];
+  unsigned char *ap;
+  unsigned char *pp;
+  int alen, plen;
+  char portmsgbuf[4096], tmp[4096];
 
-    const char *mode[] = { "EPRT", "LPRT", "PORT", NULL };
-    char **modep;
+  const char *mode[] = { "EPRT", "LPRT", "PORT", NULL };
+  char **modep;
 
-    /*
-     * we should use Curl_if2ip?  given pickiness of recent ftpd,
-     * I believe we should use the same address as the control connection.
-     */
-    sslen = sizeof(ss);
-    if (getsockname(conn->firstsocket, (struct sockaddr *)&ss, &sslen) < 0)
-      return CURLE_FTP_PORT_FAILED;
-
-    if (getnameinfo((struct sockaddr *)&ss, sslen, hbuf, sizeof(hbuf), NULL, 0,
-                    niflags))
-      return CURLE_FTP_PORT_FAILED;
-
-    memset(&hints, 0, sizeof(hints));
-    hints.ai_family = sa->sa_family;
-    /*hints.ai_family = ss.ss_family;
-      this way can be used if sockaddr_storage is properly defined, as glibc 
-      2.1.X doesn't do*/
-    hints.ai_socktype = SOCK_STREAM;
-    hints.ai_flags = AI_PASSIVE;
-    if (getaddrinfo(hbuf, "0", &hints, &res))
-      return CURLE_FTP_PORT_FAILED;
-
-    portsock = -1;
-    for (ai = res; ai; ai = ai->ai_next) {
-      portsock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
-      if (portsock < 0)
-        continue;
-
-      if (bind(portsock, ai->ai_addr, ai->ai_addrlen) < 0) {
-        sclose(portsock);
-        portsock = -1;
-        continue;
-      }
+  /*
+   * we should use Curl_if2ip?  given pickiness of recent ftpd,
+   * I believe we should use the same address as the control connection.
+   */
+  sslen = sizeof(ss);
+  if (getsockname(conn->firstsocket, (struct sockaddr *)&ss, &sslen) < 0)
+    return CURLE_FTP_PORT_FAILED;
+  
+  if (getnameinfo((struct sockaddr *)&ss, sslen, hbuf, sizeof(hbuf), NULL, 0,
+                  niflags))
+    return CURLE_FTP_PORT_FAILED;
 
-      if (listen(portsock, 1) < 0) {
-        sclose(portsock);
-        portsock = -1;
-        continue;
-      }
+  memset(&hints, 0, sizeof(hints));
+  hints.ai_family = sa->sa_family;
+  /*hints.ai_family = ss.ss_family;
+    this way can be used if sockaddr_storage is properly defined, as glibc 
+    2.1.X doesn't do*/
+  hints.ai_socktype = SOCK_STREAM;
+  hints.ai_flags = AI_PASSIVE;
+  if (getaddrinfo(hbuf, "0", &hints, &res))
+    return CURLE_FTP_PORT_FAILED;
+  
+  portsock = -1;
+  for (ai = res; ai; ai = ai->ai_next) {
+    portsock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+    if (portsock < 0)
+      continue;
 
-      break;
+    if (bind(portsock, ai->ai_addr, ai->ai_addrlen) < 0) {
+      sclose(portsock);
+      portsock = -1;
+      continue;
     }
-    if (portsock < 0) {
-      failf(data, strerror(errno));
-      freeaddrinfo(res);
-      return CURLE_FTP_PORT_FAILED;
+      
+    if (listen(portsock, 1) < 0) {
+      sclose(portsock);
+      portsock = -1;
+      continue;
     }
+    
+    break;
+  }
+  if (portsock < 0) {
+    failf(data, strerror(errno));
+    freeaddrinfo(res);
+    return CURLE_FTP_PORT_FAILED;
+  }
 
-    sslen = sizeof(ss);
-    if (getsockname(portsock, sa, &sslen) < 0) {
-      failf(data, strerror(errno));
-      freeaddrinfo(res);
-      return CURLE_FTP_PORT_FAILED;
-    }
+  sslen = sizeof(ss);
+  if (getsockname(portsock, sa, &sslen) < 0) {
+    failf(data, strerror(errno));
+    freeaddrinfo(res);
+    return CURLE_FTP_PORT_FAILED;
+  }
 
-    for (modep = (char **)mode; modep && *modep; modep++) {
-      int lprtaf, eprtaf;
-
-      switch (sa->sa_family) {
-      case AF_INET:
-        ap = (unsigned char *)&((struct sockaddr_in *)&ss)->sin_addr;
-        alen = sizeof(((struct sockaddr_in *)&ss)->sin_addr);
-        pp = (unsigned char *)&((struct sockaddr_in *)&ss)->sin_port;
-        plen = sizeof(((struct sockaddr_in *)&ss)->sin_port);
-        lprtaf = 4;
-        eprtaf = 1;
-        break;
-      case AF_INET6:
-        ap = (unsigned char *)&((struct sockaddr_in6 *)&ss)->sin6_addr;
-        alen = sizeof(((struct sockaddr_in6 *)&ss)->sin6_addr);
-        pp = (unsigned char *)&((struct sockaddr_in6 *)&ss)->sin6_port;
-        plen = sizeof(((struct sockaddr_in6 *)&ss)->sin6_port);
-        lprtaf = 6;
-        eprtaf = 2;
-        break;
-      default:
-        ap = pp = NULL;
-        lprtaf = eprtaf = -1;
-        break;
-      }
+  for (modep = (char **)mode; modep && *modep; modep++) {
+    int lprtaf, eprtaf;
+    
+    switch (sa->sa_family) {
+    case AF_INET:
+      ap = (unsigned char *)&((struct sockaddr_in *)&ss)->sin_addr;
+      alen = sizeof(((struct sockaddr_in *)&ss)->sin_addr);
+      pp = (unsigned char *)&((struct sockaddr_in *)&ss)->sin_port;
+      plen = sizeof(((struct sockaddr_in *)&ss)->sin_port);
+      lprtaf = 4;
+      eprtaf = 1;
+      break;
+    case AF_INET6:
+      ap = (unsigned char *)&((struct sockaddr_in6 *)&ss)->sin6_addr;
+      alen = sizeof(((struct sockaddr_in6 *)&ss)->sin6_addr);
+      pp = (unsigned char *)&((struct sockaddr_in6 *)&ss)->sin6_port;
+      plen = sizeof(((struct sockaddr_in6 *)&ss)->sin6_port);
+      lprtaf = 6;
+      eprtaf = 2;
+      break;
+    default:
+      ap = pp = NULL;
+      lprtaf = eprtaf = -1;
+      break;
+    }
 
-      if (strcmp(*modep, "EPRT") == 0) {
-        if (eprtaf < 0)
-          continue;
-        if (getnameinfo((struct sockaddr *)&ss, sslen,
-                         portmsgbuf, sizeof(portmsgbuf), tmp, sizeof(tmp), niflags))
-          continue;
+    if (strcmp(*modep, "EPRT") == 0) {
+      if (eprtaf < 0)
+        continue;
+      if (getnameinfo((struct sockaddr *)&ss, sslen,
+                      portmsgbuf, sizeof(portmsgbuf), tmp, sizeof(tmp), niflags))
+        continue;
 
-        /* do not transmit IPv6 scope identifier to the wire */
-       if (sa->sa_family == AF_INET6) {
-          char *q = strchr(portmsgbuf, '%');
+      /* do not transmit IPv6 scope identifier to the wire */
+      if (sa->sa_family == AF_INET6) {
+        char *q = strchr(portmsgbuf, '%');
           if (q)
             *q = '\0';
-        }
+      }
 
-        ftpsendf(conn->firstsocket, conn, "%s |%d|%s|%s|", *modep, eprtaf,
-                 portmsgbuf, tmp);
-      } else if (strcmp(*modep, "LPRT") == 0 ||
-                 strcmp(*modep, "PORT") == 0) {
-        int i;
+      ftpsendf(conn->firstsocket, conn, "%s |%d|%s|%s|", *modep, eprtaf,
+               portmsgbuf, tmp);
+    } else if (strcmp(*modep, "LPRT") == 0 ||
+               strcmp(*modep, "PORT") == 0) {
+      int i;
+      
+      if (strcmp(*modep, "LPRT") == 0 && lprtaf < 0)
+        continue;
+      if (strcmp(*modep, "PORT") == 0 && sa->sa_family != AF_INET)
+        continue;
 
-        if (strcmp(*modep, "LPRT") == 0 && lprtaf < 0)
+      portmsgbuf[0] = '\0';
+      if (strcmp(*modep, "LPRT") == 0) {
+        snprintf(tmp, sizeof(tmp), "%d,%d", lprtaf, alen);
+        if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >=
+            sizeof(portmsgbuf)) {
           continue;
-        if (strcmp(*modep, "PORT") == 0 && sa->sa_family != AF_INET)
-          continue;
-
-        portmsgbuf[0] = '\0';
-        if (strcmp(*modep, "LPRT") == 0) {
-          snprintf(tmp, sizeof(tmp), "%d,%d", lprtaf, alen);
-          if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >= sizeof(portmsgbuf)) {
-            continue;
-          }
-        }
-
-        for (i = 0; i < alen; i++) {
-          if (portmsgbuf[0])
-            snprintf(tmp, sizeof(tmp), ",%u", ap[i]);
-          else
-            snprintf(tmp, sizeof(tmp), "%u", ap[i]);
-
-          if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >= sizeof(portmsgbuf)) {
-            continue;
-          }
         }
+      }
 
-        if (strcmp(*modep, "LPRT") == 0) {
-          snprintf(tmp, sizeof(tmp), ",%d", plen);
-
-          if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >= sizeof(portmsgbuf))
-            continue;
+      for (i = 0; i < alen; i++) {
+        if (portmsgbuf[0])
+          snprintf(tmp, sizeof(tmp), ",%u", ap[i]);
+        else
+          snprintf(tmp, sizeof(tmp), "%u", ap[i]);
+        
+        if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >=
+            sizeof(portmsgbuf)) {
+          continue;
         }
+      }
+      
+      if (strcmp(*modep, "LPRT") == 0) {
+        snprintf(tmp, sizeof(tmp), ",%d", plen);
+        
+        if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >= sizeof(portmsgbuf))
+          continue;
+      }
 
-        for (i = 0; i < plen; i++) {
-          snprintf(tmp, sizeof(tmp), ",%u", pp[i]);
-
-          if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >= sizeof(portmsgbuf)) {
-              continue;
-          }
+      for (i = 0; i < plen; i++) {
+        snprintf(tmp, sizeof(tmp), ",%u", pp[i]);
+        
+        if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >=
+            sizeof(portmsgbuf)) {
+          continue;
         }
-
-        ftpsendf(conn->firstsocket, conn, "%s %s", *modep, portmsgbuf);
       }
-
-      nread = Curl_GetFTPResponse(conn->firstsocket, buf, conn, &ftpcode);
-      if(nread < 0)
-        return CURLE_OPERATION_TIMEOUTED;
-
-      if (ftpcode != 200) {
-        failf(data, "Server does not grok %s", *modep);
-        continue;
-      } else
-        break;
+      
+      ftpsendf(conn->firstsocket, conn, "%s %s", *modep, portmsgbuf);
     }
-
-    if (!*modep) {
-      sclose(portsock);
-      freeaddrinfo(res);
-      return CURLE_FTP_PORT_FAILED;
+    
+    nread = Curl_GetFTPResponse(conn->firstsocket, buf, conn, &ftpcode);
+    if(nread < 0)
+      return CURLE_OPERATION_TIMEOUTED;
+    
+    if (ftpcode != 200) {
+      failf(data, "Server does not grok %s", *modep);
+      continue;
     }
-    /* we set the secondary socket variable to this for now, it
-       is only so that the cleanup function will close it in case
-       we fail before the true secondary stuff is made */
-    conn->secondarysocket = portsock;
-
+    else
+      break;
+  }
+  
+  if (!*modep) {
+    sclose(portsock);
+    freeaddrinfo(res);
+    return CURLE_FTP_PORT_FAILED;
+  }
+  /* we set the secondary socket variable to this for now, it
+     is only so that the cleanup function will close it in case
+     we fail before the true secondary stuff is made */
+  conn->secondarysocket = portsock;
+  
 #else
-    struct sockaddr_in sa;
-    struct hostent *h=NULL;
-    char *hostdataptr=NULL;
-    size_t size;
-    unsigned short porttouse;
-    char myhost[256] = "";
-
-    if(data->set.ftpport) {
-      if(Curl_if2ip(data->set.ftpport, myhost, sizeof(myhost))) {
-        h = Curl_getaddrinfo(data, myhost, 0, &hostdataptr);
-      }
-      else {
-        if(strlen(data->set.ftpport)>1)
-          h = Curl_getaddrinfo(data, data->set.ftpport, 0, &hostdataptr);
-        if(h)
-          strcpy(myhost, data->set.ftpport); /* buffer overflow risk */
-      }
+  /******************************************************************
+   *
+   * Here's a piece of IPv4-specific code coming up
+   *
+   */
+  struct sockaddr_in sa;
+  struct hostent *h=NULL;
+  char *hostdataptr=NULL;
+  size_t size;
+  unsigned short porttouse;
+  char myhost[256] = "";
+
+  if(data->set.ftpport) {
+    if(Curl_if2ip(data->set.ftpport, myhost, sizeof(myhost))) {
+      h = Curl_getaddrinfo(data, myhost, 0, &hostdataptr);
     }
-    if(! *myhost) {
-      h=Curl_getaddrinfo(data,
-                         getmyhost(myhost, sizeof(myhost)),
-                         0, &hostdataptr);
+    else {
+      if(strlen(data->set.ftpport)>1)
+        h = Curl_getaddrinfo(data, data->set.ftpport, 0, &hostdataptr);
+      if(h)
+        strcpy(myhost, data->set.ftpport); /* buffer overflow risk */
     }
-    infof(data, "We connect from %s\n", myhost);
-
-    if ( h ) {
-      if( (portsock = socket(AF_INET, SOCK_STREAM, 0)) >= 0 ) {
-
-        /* we set the secondary socket variable to this for now, it
-           is only so that the cleanup function will close it in case
-           we fail before the true secondary stuff is made */
-        conn->secondarysocket = portsock;
-
-        memset((char *)&sa, 0, sizeof(sa));
-        memcpy((char *)&sa.sin_addr,
-               h->h_addr,
-               h->h_length);
-        sa.sin_family = AF_INET;
-        sa.sin_addr.s_addr = INADDR_ANY;
-        sa.sin_port = 0;
-        size = sizeof(sa);
-
-        if(bind(portsock, (struct sockaddr *)&sa, size) >= 0) {
-          /* we succeeded to bind */
-          struct sockaddr_in add;
-          size = sizeof(add);
-
-          if(getsockname(portsock, (struct sockaddr *) &add,
-                         (socklen_t *)&size)<0) {
-            failf(data, "getsockname() failed");
-            return CURLE_FTP_PORT_FAILED;
-          }
-          porttouse = ntohs(add.sin_port);
-
-          if ( listen(portsock, 1) < 0 ) {
-            failf(data, "listen(2) failed on socket");
-            free(hostdataptr);
-            return CURLE_FTP_PORT_FAILED;
-          }
+  }
+  if(! *myhost) {
+    h=Curl_getaddrinfo(data,
+                       getmyhost(myhost, sizeof(myhost)),
+                       0, &hostdataptr);
+  }
+  infof(data, "We connect from %s\n", myhost);
+  
+  if ( h ) {
+    if( (portsock = socket(AF_INET, SOCK_STREAM, 0)) >= 0 ) {
+      
+      /* we set the secondary socket variable to this for now, it
+         is only so that the cleanup function will close it in case
+         we fail before the true secondary stuff is made */
+      conn->secondarysocket = portsock;
+
+      memset((char *)&sa, 0, sizeof(sa));
+      memcpy((char *)&sa.sin_addr,
+             h->h_addr,
+             h->h_length);
+      sa.sin_family = AF_INET;
+      sa.sin_addr.s_addr = INADDR_ANY;
+      sa.sin_port = 0;
+      size = sizeof(sa);
+      
+      if(bind(portsock, (struct sockaddr *)&sa, size) >= 0) {
+        /* we succeeded to bind */
+        struct sockaddr_in add;
+        size = sizeof(add);
+
+        if(getsockname(portsock, (struct sockaddr *) &add,
+                       (socklen_t *)&size)<0) {
+          failf(data, "getsockname() failed");
+          return CURLE_FTP_PORT_FAILED;
         }
-        else {
-          failf(data, "bind(2) failed on socket");
+        porttouse = ntohs(add.sin_port);
+        
+        if ( listen(portsock, 1) < 0 ) {
+          failf(data, "listen(2) failed on socket");
           free(hostdataptr);
           return CURLE_FTP_PORT_FAILED;
         }
       }
       else {
-        failf(data, "socket(2) failed (%s)");
+        failf(data, "bind(2) failed on socket");
         free(hostdataptr);
         return CURLE_FTP_PORT_FAILED;
       }
-      if(hostdataptr)
-        /* free the memory used for name lookup */
-        free(hostdataptr);
     }
     else {
-      failf(data, "could't find my own IP address (%s)", myhost);
+      failf(data, "socket(2) failed (%s)");
+      free(hostdataptr);
       return CURLE_FTP_PORT_FAILED;
     }
-    {
-      struct in_addr in;
-      unsigned short ip[5];
-      (void) memcpy(&in.s_addr, *h->h_addr_list, sizeof (in.s_addr));
-#if defined (HAVE_INET_NTOA_R)
-      /* ignore the return code from inet_ntoa_r() as it is int or
-         char * depending on system */
-      inet_ntoa_r(in, ntoa_buf, sizeof(ntoa_buf));
-      sscanf( ntoa_buf, "%hu.%hu.%hu.%hu",
-              &ip[0], &ip[1], &ip[2], &ip[3]);
+    if(hostdataptr)
+      /* free the memory used for name lookup */
+      free(hostdataptr);
+  }
+  else {
+    failf(data, "could't find my own IP address (%s)", myhost);
+    return CURLE_FTP_PORT_FAILED;
+  }
+  {
+#ifdef HAVE_INET_NTOA_R
+    char ntoa_buf[64];
+#endif
+    struct in_addr in;
+    unsigned short ip[5];
+    (void) memcpy(&in.s_addr, *h->h_addr_list, sizeof (in.s_addr));
+#ifdef HAVE_INET_NTOA_R
+    /* ignore the return code from inet_ntoa_r() as it is int or
+       char * depending on system */
+    inet_ntoa_r(in, ntoa_buf, sizeof(ntoa_buf));
+    sscanf( ntoa_buf, "%hu.%hu.%hu.%hu",
+            &ip[0], &ip[1], &ip[2], &ip[3]);
 #else
-      sscanf( inet_ntoa(in), "%hu.%hu.%hu.%hu",
-              &ip[0], &ip[1], &ip[2], &ip[3]);
+    sscanf( inet_ntoa(in), "%hu.%hu.%hu.%hu",
+            &ip[0], &ip[1], &ip[2], &ip[3]);
 #endif
-      ftpsendf(conn->firstsocket, conn, "PORT %d,%d,%d,%d,%d,%d",
-            ip[0], ip[1], ip[2], ip[3],
-            porttouse >> 8,
-            porttouse & 255);
-    }
+    ftpsendf(conn->firstsocket, conn, "PORT %d,%d,%d,%d,%d,%d",
+             ip[0], ip[1], ip[2], ip[3],
+             porttouse >> 8,
+             porttouse & 255);
+  }
 
-    nread = Curl_GetFTPResponse(conn->firstsocket, buf, conn, &ftpcode);
-    if(nread < 0)
-      return CURLE_OPERATION_TIMEOUTED;
+  nread = Curl_GetFTPResponse(conn->firstsocket, buf, conn, &ftpcode);
+  if(nread < 0)
+    return CURLE_OPERATION_TIMEOUTED;
 
-    if(ftpcode != 200) {
-      failf(data, "Server does not grok PORT, try without it!");
-      return CURLE_FTP_PORT_FAILED;
-    }     
-#endif /* ENABLE_IPV6 */
+  if(ftpcode != 200) {
+    failf(data, "Server does not grok PORT, try without it!");
+    return CURLE_FTP_PORT_FAILED;
   }
-  else { /* we use the PASV command */
+#endif /* end of ipv4-specific code */
+
+  return CURLE_OK;
+}
+
+/**********
+ * PASV is the ftp client's way of asking the server to open a second port
+ * that we can connect to (for the data transfer). This is the opposite of
+ * PORT.
+ */
+
+static
+CURLcode ftp_use_pasv(struct connectdata *conn)
+{
+  struct SessionHandle *data = conn->data;
+  ssize_t nread;
+  char *buf = data->state.buffer; /* this is our buffer */
+  int ftpcode; /* receive FTP response codes in this */
+  CURLcode result;
+
 #if 0
-    /* no support for IPv6 passive mode yet */
-    char *mode[] = { "EPSV", "LPSV", "PASV", NULL };
-    int results[] = { 229, 228, 227, 0 };
+  /* no support for IPv6 passive mode yet */
+  char *mode[] = { "EPSV", "LPSV", "PASV", NULL };
+  int results[] = { 229, 228, 227, 0 };
 #else
-    const char *mode[] = { "PASV", NULL };
-    int results[] = { 227, 0 };
+  const char *mode[] = { "PASV", NULL };
+  int results[] = { 227, 0 };
 #endif
-    int modeoff;
-
-    for (modeoff = 0; mode[modeoff]; modeoff++) {
-      ftpsendf(conn->firstsocket, conn, mode[modeoff]);
-      nread = Curl_GetFTPResponse(conn->firstsocket, buf, conn, &ftpcode);
-      if(nread < 0)
-        return CURLE_OPERATION_TIMEOUTED;
-
+  int modeoff;
+  
+  for (modeoff = 0; mode[modeoff]; modeoff++) {
+    ftpsendf(conn->firstsocket, conn, mode[modeoff]);
+    nread = Curl_GetFTPResponse(conn->firstsocket, buf, conn, &ftpcode);
+    if(nread < 0)
+      return CURLE_OPERATION_TIMEOUTED;
+    
       if (ftpcode == results[modeoff])
         break;
-    }
-
-    if (!mode[modeoff]) {
-      failf(data, "Odd return code after PASV");
-      return CURLE_FTP_WEIRD_PASV_REPLY;
-    }
-    else if (strcmp(mode[modeoff], "PASV") == 0) {
-      int ip[4];
-      int port[2];
-      unsigned short newport; /* remote port, not necessary the local one */
-      unsigned short connectport; /* the local port connect() should use! */
-      char newhost[32];
+  }
 
-      Curl_addrinfo *addr;
-      char *hostdataptr=NULL;
+  if (!mode[modeoff]) {
+    failf(data, "Odd return code after PASV");
+    return CURLE_FTP_WEIRD_PASV_REPLY;
+  }
+  else if (strcmp(mode[modeoff], "PASV") == 0) {
+    int ip[4];
+    int port[2];
+    unsigned short newport; /* remote port, not necessary the local one */
+    unsigned short connectport; /* the local port connect() should use! */
+    char newhost[32];
+    
+    Curl_addrinfo *addr;
+    char *hostdataptr=NULL;
 
-#ifndef ENABLE_IPV6
-      char *ip_addr;
+#ifdef ENABLE_IPV6
+    struct addrinfo *ai;
 #else
-      struct addrinfo *ai;
+    struct sockaddr_in serv_addr;
 #endif
-      char *str=buf;
+    char *str=buf;
 
-      /*
-       * New 227-parser June 3rd 1999.
-       * It now scans for a sequence of six comma-separated numbers and
-       * will take them as IP+port indicators.
-       *
-       * Found reply-strings include:
-       * "227 Entering Passive Mode (127,0,0,1,4,51)"
-       * "227 Data transfer will passively listen to 127,0,0,1,4,51"
-       * "227 Entering passive mode. 127,0,0,1,4,51"
-       */
+    /*
+     * New 227-parser June 3rd 1999.
+     * It now scans for a sequence of six comma-separated numbers and
+     * will take them as IP+port indicators.
+     *
+     * Found reply-strings include:
+     * "227 Entering Passive Mode (127,0,0,1,4,51)"
+     * "227 Data transfer will passively listen to 127,0,0,1,4,51"
+     * "227 Entering passive mode. 127,0,0,1,4,51"
+     */
       
-      while(*str) {
-             if (6 == sscanf(str, "%d,%d,%d,%d,%d,%d",
-                        &ip[0], &ip[1], &ip[2], &ip[3],
-                        &port[0], &port[1]))
-           break;
-        str++;
-      }
+    while(*str) {
+      if (6 == sscanf(str, "%d,%d,%d,%d,%d,%d",
+                      &ip[0], &ip[1], &ip[2], &ip[3],
+                      &port[0], &port[1]))
+        break;
+      str++;
+    }
 
-      if(!*str) {
-        failf(data, "Couldn't interpret this 227-reply: %s", buf);
-        return CURLE_FTP_WEIRD_227_FORMAT;
-      }
+    if(!*str) {
+      failf(data, "Couldn't interpret this 227-reply: %s", buf);
+      return CURLE_FTP_WEIRD_227_FORMAT;
+    }
 
-      sprintf(newhost, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
-      newport = (port[0]<<8) + port[1];
-      if(data->change.proxy) {
-        /*
-         * This is a tunnel through a http proxy and we need to connect to the
-         * proxy again here. We already have the name info for it since the
-         * previous lookup.
-         */
-        addr = conn->hp;
-        connectport =
-          (unsigned short)conn->port; /* we connect to the proxy's port */
-      }
-      else {
-        /* normal, direct, ftp connection */
-        addr = Curl_getaddrinfo(data, newhost, newport, &hostdataptr);
-        if(!addr) {
-          failf(data, "Can't resolve new host %s", newhost);
-          return CURLE_FTP_CANT_GET_HOST;
-        }
-        connectport = newport; /* we connect to the remote port */
+    sprintf(newhost, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
+    newport = (port[0]<<8) + port[1];
+    if(data->change.proxy) {
+      /*
+       * This is a tunnel through a http proxy and we need to connect to the
+       * proxy again here. We already have the name info for it since the
+       * previous lookup.
+       */
+      addr = conn->hp;
+      connectport =
+        (unsigned short)conn->port; /* we connect to the proxy's port */
+    }
+    else {
+      /* normal, direct, ftp connection */
+      addr = Curl_getaddrinfo(data, newhost, newport, &hostdataptr);
+      if(!addr) {
+        failf(data, "Can't resolve new host %s", newhost);
+        return CURLE_FTP_CANT_GET_HOST;
       }
-       
+      connectport = newport; /* we connect to the remote port */
+    }
+    
 #ifdef ENABLE_IPV6
-      conn->secondarysocket = -1;
-      for (ai = addr; ai; ai = ai->ai_next) {
-        /* XXX for now, we can do IPv4 only */
-        if (ai->ai_family != AF_INET)
-          continue;
-
-        conn->secondarysocket = socket(ai->ai_family, ai->ai_socktype,
-                                       ai->ai_protocol);
-        if (conn->secondarysocket < 0)
-          continue;
-
-        if (connect(conn->secondarysocket, ai->ai_addr, ai->ai_addrlen) < 0) {
-          close(conn->secondarysocket);
-          conn->secondarysocket = -1;
-          continue;
-        }
-
-       if(data->set.verbose)
-          /* this just dumps information about this second connection */
-          ftp_pasv_verbose(conn, ai);
-       break;
+    conn->secondarysocket = -1;
+    for (ai = addr; ai; ai = ai->ai_next) {
+      /* XXX for now, we can do IPv4 only */
+      if (ai->ai_family != AF_INET)
+        continue;
+      
+      conn->secondarysocket = socket(ai->ai_family, ai->ai_socktype,
+                                     ai->ai_protocol);
+      if (conn->secondarysocket < 0)
+        continue;
+      
+      if (connect(conn->secondarysocket, ai->ai_addr, ai->ai_addrlen) < 0) {
+        close(conn->secondarysocket);
+        conn->secondarysocket = -1;
+        continue;
       }
 
-      if (conn->secondarysocket < 0) {
-        failf(data, strerror(errno));
-        return CURLE_FTP_CANT_RECONNECT;
-      }
+      if(data->set.verbose)
+        /* this just dumps information about this second connection */
+        ftp_pasv_verbose(conn, ai, newhost, 0 /* port not really known */);
+      break;
+    }
+
+    if (conn->secondarysocket < 0) {
+      failf(data, strerror(errno));
+      return CURLE_FTP_CANT_RECONNECT;
+    }
 #else
-      /* IPv4 code */
-      conn->secondarysocket = socket(AF_INET, SOCK_STREAM, 0);
+    /* IPv4 code */
+    conn->secondarysocket = socket(AF_INET, SOCK_STREAM, 0);
 
-      memset((char *) &serv_addr, '\0', sizeof(serv_addr));
-      memcpy((char *)&(serv_addr.sin_addr), addr->h_addr, addr->h_length);
-      serv_addr.sin_family = addr->h_addrtype;
+    memset((char *) &serv_addr, '\0', sizeof(serv_addr));
+    memcpy((char *)&(serv_addr.sin_addr), addr->h_addr, addr->h_length);
+    serv_addr.sin_family = addr->h_addrtype;
 
-      serv_addr.sin_port = htons(connectport);
+    serv_addr.sin_port = htons(connectport);
 
-      if(data->set.verbose)
-        /* this just dumps information about this second connection */
-        ftp_pasv_verbose(conn, newhost);
+    if(data->set.verbose)
+      /* this just dumps information about this second connection */
+      ftp_pasv_verbose(conn, addr, newhost, connectport);
        
-      if(hostdataptr)
-        free(hostdataptr);
-
-      if (connect(conn->secondarysocket, (struct sockaddr *) &serv_addr,
-                  sizeof(serv_addr)) < 0) {
-        switch(errno) {
+    if(hostdataptr)
+      free(hostdataptr);
+    
+    if (connect(conn->secondarysocket, (struct sockaddr *) &serv_addr,
+                sizeof(serv_addr)) < 0) {
+      switch(errno) {
 #ifdef ECONNREFUSED
-          /* this should be made nicer */
-        case ECONNREFUSED:
-          failf(data, "Connection refused by ftp server");
-          break;
+        /* this should be made nicer */
+      case ECONNREFUSED:
+        failf(data, "Connection refused by ftp server");
+        break;
 #endif
 #ifdef EINTR
-        case EINTR:
-          failf(data, "Connection timed out to ftp server");
-          break;
+      case EINTR:
+        failf(data, "Connection timed out to ftp server");
+        break;
 #endif
-        default:
-          failf(data, "Can't connect to ftp server");
-          break;
-        }
-        return CURLE_FTP_CANT_RECONNECT;
+      default:
+        failf(data, "Can't connect to ftp server");
+        break;
       }
+      return CURLE_FTP_CANT_RECONNECT;
+    }
 #endif /* end of IPv4-specific code*/
 
-      if (data->set.tunnel_thru_httpproxy) {
-        /* We want "seamless" FTP operations through HTTP proxy tunnel */
-        result = Curl_ConnectHTTPProxyTunnel(conn, conn->secondarysocket,
-                                             newhost, newport);
-        if(CURLE_OK != result)
-          return result;
-      }
-    } else {
-      return CURLE_FTP_CANT_RECONNECT;
+    if (data->set.tunnel_thru_httpproxy) {
+      /* We want "seamless" FTP operations through HTTP proxy tunnel */
+      result = Curl_ConnectHTTPProxyTunnel(conn, conn->secondarysocket,
+                                           newhost, newport);
+      if(CURLE_OK != result)
+        return result;
+    }
+  }
+  else
+    return CURLE_FTP_CANT_RECONNECT;
+
+  return CURLE_OK;
+}
+
+
+static
+CURLcode ftp_perform(struct connectdata *conn)
+{
+  /* this is FTP and no proxy */
+  ssize_t nread;
+  CURLcode result;
+  struct SessionHandle *data=conn->data;
+  char *buf = data->state.buffer; /* this is our buffer */
+
+  /* the ftp struct is already inited in ftp_connect() */
+  struct FTP *ftp = conn->proto.ftp;
+
+  long *bytecountp = ftp->bytecountp;
+  int ftpcode; /* for ftp status */
+
+  /* Send any QUOTE strings? */
+  if(data->set.quote) {
+    if ((result = ftp_sendquote(conn, data->set.quote)) != CURLE_OK)
+      return result;
+  }
+    
+  /* This is a re-used connection. Since we change directory to where the
+     transfer is taking place, we must now get back to the original dir
+     where we ended up after login: */
+  if (conn->bits.reuse) {
+    if ((result = ftp_cwd(conn, ftp->entrypath)) != CURLE_OK)
+      return result;
+  }
+
+  /* change directory first! */
+  if(ftp->dir && ftp->dir[0]) {
+    if ((result = ftp_cwd(conn, ftp->dir)) != CURLE_OK)
+        return result;
+  }
+
+  /* Requested time of file? */
+  if(data->set.get_filetime && ftp->file) {
+    result = ftp_getfiletime(conn, ftp->file);
+    if(result)
+      return result;
+  }
+
+  /* If we have selected NOBODY, it means that we only want file information.
+     Which in FTP can't be much more than the file size! */
+  if(data->set.no_body) {
+    /* The SIZE command is _not_ RFC 959 specified, and therefor many servers
+       may not support it! It is however the only way we have to get a file's
+       size! */
+    ssize_t filesize;
+
+    /* Some servers return different sizes for different modes, and thus we
+       must set the proper type before we check the size */
+    result = ftp_transfertype(conn, data->set.ftp_ascii);
+    if(result)
+      return result;
+
+    /* failing to get size is not a serious error */
+    result = ftp_getsize(conn, ftp->file, &filesize);
+
+    if(CURLE_OK == result) {
+      sprintf(buf, "Content-Length: %d\r\n", filesize);
+      result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0);
+      if(result)
+        return result;
     }
+
+    /* If we asked for a time of the file and we actually got one as
+       well, we "emulate" a HTTP-style header in our output. */
+
+#ifdef HAVE_STRFTIME
+    if(data->set.get_filetime && data->info.filetime) {
+      struct tm *tm;
+#ifdef HAVE_LOCALTIME_R
+      struct tm buffer;
+      tm = (struct tm *)localtime_r(&data->info.filetime, &buffer);
+#else
+      tm = localtime(&data->info.filetime);
+#endif
+      /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */
+      strftime(buf, BUFSIZE-1, "Last-Modified: %a, %d %b %Y %H:%M:%S %Z\r\n",
+               tm);
+      result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0);
+      if(result)
+        return result;
+    }
+#endif
+
+    return CURLE_OK;
   }
-  /* we have the (new) data connection ready */
+
+  /* Get us a second connection up and connected */
+  if(data->set.ftp_use_port)
+    /* We have chosen to use the PORT command */
+    result = ftp_use_port(conn);
+  else
+    /* We have chosen (this is default) to use the PASV command */
+    result = ftp_use_pasv(conn);
+
+  if(result)
+    return result;
+
+  /* we have the data connection ready */
   infof(data, "Connected the data stream!\n");
 
   if(data->set.upload) {
 
     /* Set type to binary (unless specified ASCII) */
-    result = _ftp_transfertype(conn, data->set.ftp_ascii);
+    result = ftp_transfertype(conn, data->set.ftp_ascii);
     if(result)
       return result;
 
@@ -1435,7 +1491,7 @@ CURLcode _ftp(struct connectdata *conn)
         /* we could've got a specified offset from the command line,
            but now we know we didn't */
 
-        if(CURLE_OK != _ftp_getsize(conn, ftp->file, &conn->resume_from)) {
+        if(CURLE_OK != ftp_getsize(conn, ftp->file, &conn->resume_from)) {
           failf(data, "Couldn't get remote file size");
           return CURLE_FTP_COULDNT_GET_SIZE;
         }
@@ -1510,7 +1566,7 @@ CURLcode _ftp(struct connectdata *conn)
 
     if(data->set.ftp_use_port) {
       /* PORT means we are now awaiting the server to connect to us. */
-      result = AllowServerConnect(data, conn, portsock);
+      result = AllowServerConnect(data, conn, conn->secondarysocket);
       if( result )
         return result;
     }
@@ -1578,7 +1634,7 @@ CURLcode _ftp(struct connectdata *conn)
       dirlist = TRUE;
 
       /* Set type to ASCII */
-      result = _ftp_transfertype(conn, TRUE /* ASCII enforced */);
+      result = ftp_transfertype(conn, TRUE /* ASCII enforced */);
       if(result)
         return result;
 
@@ -1592,7 +1648,7 @@ CURLcode _ftp(struct connectdata *conn)
     }
     else {
       /* Set type to binary (unless specified ASCII) */
-      result = _ftp_transfertype(conn, data->set.ftp_ascii);
+      result = ftp_transfertype(conn, data->set.ftp_ascii);
       if(result)
         return result;
 
@@ -1605,7 +1661,7 @@ CURLcode _ftp(struct connectdata *conn)
          * the best way to know if we're trying to resume beyond the EOF.  */
         int foundsize=-1;
         
-        result = _ftp_getsize(conn, ftp->file, &foundsize);
+        result = ftp_getsize(conn, ftp->file, &foundsize);
 
         if(CURLE_OK != result) {
           infof(data, "ftp server doesn't support SIZE\n");
@@ -1736,7 +1792,7 @@ CURLcode _ftp(struct connectdata *conn)
         size = downloadsize;
 
       if(data->set.ftp_use_port) {
-        result = AllowServerConnect(data, conn, portsock);
+        result = AllowServerConnect(data, conn, conn->secondarysocket);
         if( result )
           return result;
       }
@@ -1813,7 +1869,7 @@ CURLcode Curl_ftp(struct connectdata *conn)
   else
     ftp->dir = NULL;
 
-  retcode = _ftp(conn);
+  retcode = ftp_perform(conn);
 
   /* clean up here, success or error doesn't matter */
   if(ftp->file)