Common subdirectories: libold/.deps and lib/.deps
Common subdirectories: libold/.libs and lib/.libs
diff -N -u libold/ftp.c lib/ftp.c
--- libold/ftp.c	Tue Jun  1 08:56:39 2004
+++ lib/ftp.c	Tue Jun  1 08:39:23 2004
@@ -119,6 +119,10 @@
 static CURLcode ftp_mkd(struct connectdata *conn, char *path);
 static CURLcode ftp_cwd_and_mkd(struct connectdata *conn, char *path);
 static CURLcode ftp_quit(struct connectdata *conn);
+static CURLcode ftp_3rd_party_pretransfer(struct connectdata *conn);
+static CURLcode ftp_3rd_party_transfer(struct connectdata *conn);
+static CURLcode Curl_ftp_regular_transfer(struct connectdata *conn);
+static CURLcode Curl_ftp_3rd_party_transfer(struct connectdata *conn);
 
 /* easy-to-use macro: */
 #define FTPSENDF(x,y,z) if((result = Curl_ftpsendf(x,y,z))) return result
@@ -369,7 +373,7 @@
 
             /* output debug output if that is requested */
             if(data->set.verbose)
-              Curl_debug(data, CURLINFO_HEADER_IN, line_start, perline);
+              Curl_debug(data, CURLINFO_HEADER_IN, line_start, perline, conn->host.dispname);
 
             /*
              * We pass all response-lines to the callback function registered
@@ -844,8 +848,13 @@
   ftp->no_transfer = FALSE;
   ftp->dont_check = FALSE; 
 
+  if (!result && data->set.source_host) {   /* 3rd party transfer */
+    /* "done" with the secondary connection */
+    result = Curl_ftp_done(conn->data->sec_conn, status);
+  }
+
   /* Send any post-transfer QUOTE strings? */
-  if(!result && data->set.postquote)
+  if(!status && !result && data->set.postquote)
     result = ftp_sendquote(conn, data->set.postquote);
 
   return result;
@@ -2334,117 +2343,17 @@
  * parts etc as a wrapper to the actual DO function (ftp_perform).
  *
  * The input argument is already checked for validity.
- *
- * ftp->ctl_valid starts out as FALSE, and gets set to TRUE if we reach the
- * Curl_ftp_done() function without finding any major problem.
  */
 CURLcode Curl_ftp(struct connectdata *conn)
 {
-  CURLcode retcode=CURLE_OK;
-  bool connected=0;
-  struct SessionHandle *data = conn->data;
-  struct FTP *ftp;
-
-  char *slash_pos;  /* position of the first '/' char in curpos */
-  char *cur_pos=conn->path; /* current position in ppath. point at the begin
-                               of next path component */
-
-  /* the ftp struct is already inited in ftp_connect() */
-  ftp = conn->proto.ftp;
-  ftp->ctl_valid = FALSE;
-  conn->size = -1; /* make sure this is unknown at this point */
-
-  Curl_pgrsSetUploadCounter(data, 0);
-  Curl_pgrsSetDownloadCounter(data, 0);
-  Curl_pgrsSetUploadSize(data, 0);
-  Curl_pgrsSetDownloadSize(data, 0);
-
-  ftp->dirdepth = 0;
-  ftp->diralloc = 5; /* default dir depth to allocate */
-  ftp->dirs = (char **)malloc(ftp->diralloc * sizeof(ftp->dirs[0]));
-  if(!ftp->dirs)
-    return CURLE_OUT_OF_MEMORY;
-  ftp->dirs[0] = NULL; /* to start with */
-  
-  /* 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. */
-      ftp->dirs[ftp->dirdepth] = curl_unescape(cur_pos - absolute_dir,
-                                               slash_pos - cur_pos +
-                                               absolute_dir);
-    
-      if (!ftp->dirs[ftp->dirdepth]) { /* run out of memory ... */
-        failf(data, "no memory");
-        freedirs(ftp);
-        return CURLE_OUT_OF_MEMORY;
-      }
-    }
-    else {
-      cur_pos = slash_pos + 1; /* jump to the rest of the string */
-      continue;
-    }
+  CURLcode retcode = CURLE_OK;
 
-    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) {
-          freedirs(ftp);
-          return CURLE_OUT_OF_MEMORY;
-        }
-        ftp->dirs = (char **)bigger;
-      }
-    }
+  if (conn->data->set.source_host) {   /* 3rd party transfer */
+    retcode = Curl_ftp_3rd_party_transfer(conn);
+  } else {
+    retcode = Curl_ftp_regular_transfer(conn);
   }
 
-  ftp->file = cur_pos;  /* the rest is the file name */
-
-  if(*ftp->file) {
-    ftp->file = curl_unescape(ftp->file, 0);
-    if(NULL == ftp->file) {
-      freedirs(ftp);
-      failf(data, "no memory");
-      return CURLE_OUT_OF_MEMORY;
-    }
-  }
-  else
-    ftp->file=NULL; /* instead of point to a zero byte, we make it a NULL
-                       pointer */
-  
-  retcode = ftp_perform(conn, &connected);
-
-  if(CURLE_OK == retcode) {
-    if(connected)
-      retcode = Curl_ftp_nextconnect(conn);
-
-    if(retcode && (conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD)) {
-      /* Failure detected, close the second socket if it was created already */
-      sclose(conn->sock[SECONDARYSOCKET]);
-      conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
-    }
-
-    if(ftp->no_transfer)
-      /* no data to transfer */
-      retcode=Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL);        
-    else if(!connected)
-      /* since we didn't connect now, we want do_more to get called */
-      conn->bits.do_more = TRUE;
-  }
-  else
-    freedirs(ftp);
-
-  ftp->ctl_valid = TRUE; /* seems good */
-
   return retcode;
 }
 
@@ -2484,7 +2393,7 @@
       break;
 
     if(conn->data->set.verbose)
-      Curl_debug(conn->data, CURLINFO_HEADER_OUT, sptr, bytes_written);
+      Curl_debug(conn->data, CURLINFO_HEADER_OUT, sptr, bytes_written, conn->host.dispname);
 
     if(bytes_written != (ssize_t)write_len) {
       write_len -= bytes_written;
@@ -2649,6 +2558,285 @@
       failf(conn->data, "Couldn't cd to %s", path);
   }
   return result;
+}
+
+
+
+/***********************************************************************
+ *
+ * ftp_3rd_party_pretransfer()
+ *
+ * Preparation for 3rd party transfer.
+ *
+ */
+static CURLcode ftp_3rd_party_pretransfer(struct connectdata *conn)
+{
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data = conn->data;
+  struct connectdata *sec_conn = data->sec_conn;
+
+  /* sets transfer type */
+  result = ftp_transfertype(conn, data->set.ftp_ascii);
+  if (result)
+    return result;
+
+  result = ftp_transfertype(sec_conn, data->set.ftp_ascii);
+  if (result)
+    return result;
+
+  /* Send any PREQUOTE strings after transfer type is set? */
+  if (data->set.source_prequote) {
+    /* sends command(s) to source server before file transfer */
+    result = ftp_sendquote(sec_conn, data->set.source_prequote);
+  }
+  if (!result && data->set.prequote)
+    result = ftp_sendquote(conn, data->set.prequote);
+
+  return result;
+}
+
+
+
+/***********************************************************************
+ *
+ * ftp_3rd_party_transfer()
+ *
+ * Performs 3rd party transfer.
+ *
+ */
+static CURLcode ftp_3rd_party_transfer(struct connectdata *conn)
+{
+  CURLcode result = CURLE_OK;
+  ssize_t nread;
+  int ftpcode, ip[4], port[2];
+  struct SessionHandle *data = conn->data;
+  struct connectdata *sec_conn = data->sec_conn;
+  char *buf = NULL;   /* this is our buffer */
+  char *str, pasvPort[50] = "";
+  struct connectdata *pasv_conn, *port_conn;
+
+  if (data->set.pasvHost == CURL_TARGET_HOST) {
+    pasv_conn = conn;
+    port_conn = sec_conn;
+    buf = data->state.buffer;
+  } else {
+    pasv_conn = sec_conn;
+    port_conn = conn;
+    buf = sec_conn->data->state.buffer;
+  }
+  str = buf;
+
+  /* sets the passive mode */
+  result = Curl_ftpsendf(pasv_conn, "%s", "PASV");
+  if (result) return result;
+  result = Curl_GetFTPResponse(&nread, pasv_conn, &ftpcode);
+  if (result) return result;
+
+  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(pasv_conn->data, "Couldn't interpret this 227-reply: %s", buf);
+    return CURLE_FTP_WEIRD_227_FORMAT;
+  }
+
+  snprintf(pasvPort, sizeof(pasvPort), "%d,%d,%d,%d,%d,%d", ip[0], ip[1],
+                     ip[2], ip[3], port[0], port[1]);
+
+  /* sets data connection between remote hosts */
+  FTPSENDF(port_conn, "PORT %s", pasvPort);
+  result = Curl_GetFTPResponse(&nread, port_conn, &ftpcode);
+  if (result) return result;
+  if (ftpcode != 200) return CURLE_FTP_PORT_FAILED;
+
+  if (data->set.pasvHost == CURL_TARGET_HOST) {
+    /* transfers file between remote hosts */
+    FTPSENDF(sec_conn, "RETR %s", data->set.source_path);
+    result = Curl_GetFTPResponse(&nread, sec_conn, &ftpcode);
+    if (result) return result;
+    if (ftpcode != 150) return CURLE_FTP_COULDNT_RETR_FILE;
+
+    if(data->set.ftp_append) {
+      /* we append onto the file instead of rewriting it */
+      FTPSENDF(conn, "APPE %s", conn->path);
+    } else {
+      FTPSENDF(conn, "STOR %s", conn->path);
+    }
+    result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
+    if (result) return result;
+    if (ftpcode != 150) return CURLE_FTP_COULDNT_STOR_FILE;
+	
+  } else {
+  
+    /* transfers file between remote hosts */
+    FTPSENDF(sec_conn, "RETR %s", data->set.source_path);
+
+    if (data->set.ftp_append) {
+      /* we append onto the file instead of rewriting it */
+      FTPSENDF(conn, "APPE %s", conn->path);
+    } else {
+      FTPSENDF(conn, "STOR %s", conn->path);
+    }
+    result = Curl_GetFTPResponse(&nread, sec_conn, &ftpcode);
+    if (result) return result;
+    if (ftpcode != 150) return CURLE_FTP_COULDNT_RETR_FILE;
+
+    result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
+    if (result) return result;
+    if (ftpcode != 150) return CURLE_FTP_COULDNT_STOR_FILE;
+  }
+
+  return CURLE_OK;
+}
+
+
+
+/***********************************************************************
+ *
+ * Curl_ftp_regular_transfer()
+ *
+ * The input argument is already checked for validity.
+ * Performs a regular transfer between local and remote hosts.
+ *
+ * ftp->ctl_valid starts out as FALSE, and gets set to TRUE if we reach the
+ * Curl_ftp_done() function without finding any major problem.
+ */
+CURLcode Curl_ftp_regular_transfer(struct connectdata *conn)
+{
+  CURLcode retcode=CURLE_OK;
+  bool connected=0;
+  struct SessionHandle *data = conn->data;
+  struct FTP *ftp;
+
+  char *slash_pos;  /* position of the first '/' char in curpos */
+  char *cur_pos=conn->path; /* current position in ppath. point at the begin
+                               of next path component */
+
+  /* the ftp struct is already inited in ftp_connect() */
+  ftp = conn->proto.ftp;
+  ftp->ctl_valid = FALSE;
+  conn->size = -1; /* make sure this is unknown at this point */
+
+  Curl_pgrsSetUploadCounter(data, 0);
+  Curl_pgrsSetDownloadCounter(data, 0);
+  Curl_pgrsSetUploadSize(data, 0);
+  Curl_pgrsSetDownloadSize(data, 0);
+
+  ftp->dirdepth = 0;
+  ftp->diralloc = 5; /* default dir depth to allocate */
+  ftp->dirs = (char **)malloc(ftp->diralloc * sizeof(ftp->dirs[0]));
+  if(!ftp->dirs)
+    return CURLE_OUT_OF_MEMORY;
+  ftp->dirs[0] = NULL; /* to start with */
+  
+  /* 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. */
+      ftp->dirs[ftp->dirdepth] = curl_unescape(cur_pos - absolute_dir,
+                                               slash_pos - cur_pos +
+                                               absolute_dir);
+    
+      if (!ftp->dirs[ftp->dirdepth]) { /* run out of memory ... */
+        failf(data, "no memory");
+        freedirs(ftp);
+        return CURLE_OUT_OF_MEMORY;
+      }
+    }
+    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) {
+          freedirs(ftp);
+          return CURLE_OUT_OF_MEMORY;
+        }
+        ftp->dirs = (char **)bigger;
+      }
+    }
+  }
+
+  ftp->file = cur_pos;  /* the rest is the file name */
+
+  if(*ftp->file) {
+    ftp->file = curl_unescape(ftp->file, 0);
+    if(NULL == ftp->file) {
+      freedirs(ftp);
+      failf(data, "no memory");
+      return CURLE_OUT_OF_MEMORY;
+    }
+  }
+  else
+    ftp->file=NULL; /* instead of point to a zero byte, we make it a NULL
+                       pointer */
+  
+  retcode = ftp_perform(conn, &connected);
+
+  if(CURLE_OK == retcode) {
+    if(connected)
+      retcode = Curl_ftp_nextconnect(conn);
+
+    if(retcode && (conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD)) {
+      /* Failure detected, close the second socket if it was created already */
+      sclose(conn->sock[SECONDARYSOCKET]);
+      conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
+    }
+
+    if(ftp->no_transfer)
+      /* no data to transfer */
+      retcode=Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL);        
+    else if(!connected)
+      /* since we didn't connect now, we want do_more to get called */
+      conn->bits.do_more = TRUE;
+  }
+  else
+    freedirs(ftp);
+
+  ftp->ctl_valid = TRUE; /* seems good */
+
+  return retcode;
+}
+
+
+
+/***********************************************************************
+ *
+ * Curl_ftp_3rd_party_transfer()
+ *
+ * The input argument is already checked for validity.
+ * Performs a 3rd party transfer between two remote hosts.
+ */
+CURLcode Curl_ftp_3rd_party_transfer(struct connectdata *conn)
+{
+  CURLcode retcode = CURLE_OK;
+
+  conn->proto.ftp->ctl_valid = conn->data->sec_conn->proto.ftp->ctl_valid=TRUE;
+  conn->size = conn->data->sec_conn->size = -1;
+
+  retcode = ftp_3rd_party_pretransfer(conn);
+  if (!retcode)
+    retcode = ftp_3rd_party_transfer(conn);
+
+  return retcode;
 }
 
 #endif /* CURL_DISABLE_FTP */
diff -N -u libold/http.c lib/http.c
--- libold/http.c	Tue Jun  1 08:56:45 2004
+++ lib/http.c	Mon May 31 15:50:20 2004
@@ -736,7 +736,8 @@
 
     if(conn->data->set.verbose)
       /* this data _may_ contain binary stuff */
-      Curl_debug(conn->data, CURLINFO_HEADER_OUT, ptr, amount);
+      Curl_debug(conn->data, CURLINFO_HEADER_OUT, ptr, amount,
+                 conn->host.dispname);
 
     *bytes_written += amount;
     
@@ -1040,7 +1041,8 @@
             
               /* output debug output if that is requested */
               if(data->set.verbose)
-                Curl_debug(data, CURLINFO_HEADER_IN, line_start, perline);
+                Curl_debug(data, CURLINFO_HEADER_IN, line_start, perline,
+                           conn->host.dispname);
 
               /* send the header to the callback */
               writetype = CLIENTWRITE_HEADER;
diff -N -u libold/sendf.c lib/sendf.c
--- libold/sendf.c	Tue Jun  1 08:56:52 2004
+++ lib/sendf.c	Mon May 31 15:50:20 2004
@@ -142,7 +142,7 @@
     va_start(ap, fmt);
     vsnprintf(print_buffer, 1024, fmt, ap);
     va_end(ap);
-    Curl_debug(data, CURLINFO_TEXT, print_buffer, strlen(print_buffer));
+    Curl_debug(data, CURLINFO_TEXT, print_buffer, strlen(print_buffer), NULL);
   }
 }
 
@@ -166,7 +166,7 @@
         data->set.errorbuffer[len] = '\n';
         data->set.errorbuffer[++len] = '\0';
       }
-      Curl_debug(data, CURLINFO_TEXT, data->set.errorbuffer, len);
+      Curl_debug(data, CURLINFO_TEXT, data->set.errorbuffer, len, NULL);
       if(doneit)
         /* cut off the newline again */
         data->set.errorbuffer[--len]=0;
@@ -204,7 +204,7 @@
       break;
 
     if(data->set.verbose)
-      Curl_debug(data, CURLINFO_DATA_OUT, sptr, bytes_written);
+      Curl_debug(data, CURLINFO_DATA_OUT, sptr, bytes_written, conn->host.name);
 
     if((size_t)bytes_written != write_len) {
       /* if not all was written at once, we must advance the pointer, decrease
@@ -441,24 +441,49 @@
 
 /* return 0 on success */
 int Curl_debug(struct SessionHandle *data, curl_infotype type,
-               char *ptr, size_t size)
+               char *ptr, size_t size, char *host)
 {
   static const char * const s_infotype[CURLINFO_END] = {
     "* ", "< ", "> ", "{ ", "} " };
 
-  if(data->set.fdebug)
-    return (*data->set.fdebug)(data, type, ptr, size,
-                               data->set.debugdata);
+  char *info = NULL;
+  size_t info_size;
+  
+  /* 3rd party transfer - print host to know which host responds */
+  if (data->set.isPrintHost && host) {
+    info_size = size + strlen(host) + 5;
+    info = (char *)malloc(info_size);
+    if (!info)
+      return CURLE_OUT_OF_MEMORY;
+
+    snprintf(info, info_size, "%s: %s%s", host, s_infotype[type], ptr);
+  } else {
+    info_size = size + 3;
+    info = (char *)malloc(info_size);
+    if (!info)
+      return CURLE_OUT_OF_MEMORY;
+
+    snprintf(info, info_size, "%s%s", s_infotype[type], ptr);
+  }
+
+  if(data->set.fdebug) {
+    int status;
+
+    status = (*data->set.fdebug)(data, type, info, info_size,
+                                 data->set.debugdata);
+    Curl_safefree(info);
+	return status;
+  }
 
   switch(type) {
   case CURLINFO_TEXT:
   case CURLINFO_HEADER_OUT:
   case CURLINFO_HEADER_IN:
-    fwrite(s_infotype[type], 2, 1, data->set.err);
-    fwrite(ptr, size, 1, data->set.err);
+    fwrite(info, info_size, 1, data->set.err);
     break;
   default: /* nada */
     break;
   }
+  Curl_safefree(info);
   return 0;
 }
diff -N -u libold/sendf.h lib/sendf.h
--- libold/sendf.h	Tue Jun  1 08:56:57 2004
+++ lib/sendf.h	Sun May 30 10:48:28 2004
@@ -50,7 +50,7 @@
 
 /* the function used to output verbose information */
 int Curl_debug(struct SessionHandle *handle, curl_infotype type,
-               char *data, size_t size);
+               char *data, size_t size, char *host);
 
 
 #endif
diff -N -u libold/transfer.c lib/transfer.c
--- libold/transfer.c	Tue Jun  1 08:57:02 2004
+++ lib/transfer.c	Mon May 31 15:50:20 2004
@@ -876,7 +876,7 @@
 
             if(data->set.verbose)
               Curl_debug(data, CURLINFO_HEADER_IN,
-                         k->p, k->hbuflen);
+                         k->p, k->hbuflen, conn->host.dispname);
 
             result = Curl_client_write(data, writetype, k->p, k->hbuflen);
             if(result)
@@ -963,12 +963,12 @@
           if(data->set.verbose) {
             if(k->badheader) {
               Curl_debug(data, CURLINFO_DATA_IN, data->state.headerbuff,
-                         k->hbuflen);
+                         k->hbuflen, conn->host.dispname);
               if(k->badheader == HEADER_PARTHEADER)
-                Curl_debug(data, CURLINFO_DATA_IN, k->str, nread);
+                Curl_debug(data, CURLINFO_DATA_IN, k->str, nread, conn->host.dispname);
             }
             else
-              Curl_debug(data, CURLINFO_DATA_IN, k->str, nread);
+              Curl_debug(data, CURLINFO_DATA_IN, k->str, nread, conn->host.dispname);
           }
 
           if(conn->bits.chunk) {
@@ -1188,7 +1188,7 @@
         if(data->set.verbose)
           /* show the data before we change the pointer upload_fromhere */
           Curl_debug(data, CURLINFO_DATA_OUT, conn->upload_fromhere,
-                     bytes_written);
+                     bytes_written, conn->host.dispname);
 
         if(conn->upload_present != bytes_written) {
           /* we only wrote a part of the buffer (if anything), deal with it! */
@@ -1920,6 +1920,51 @@
   return CURLE_OK;
 }
 
+
+
+CURLcode Curl_connect_host(struct SessionHandle *data,
+                           struct connectdata   **conn)
+{
+  CURLcode res = CURLE_OK;
+  int urlchanged = FALSE;
+
+  do {
+    bool async;
+    Curl_pgrsTime(data, TIMER_STARTSINGLE);
+    data->change.url_changed = FALSE;
+    res = Curl_connect(data, conn, &async);
+
+    if((CURLE_OK == res) && async) {
+      /* Now, if async is TRUE here, we need to wait for the name
+         to resolve */
+      res = Curl_wait_for_resolv(*conn, NULL);
+      if(CURLE_OK == res)
+        /* Resolved, continue with the connection */
+        res = Curl_async_resolved(*conn);
+    }
+    if(res)
+      break;
+
+    /* If a callback (or something) has altered the URL we should use within
+       the Curl_connect(), we detect it here and act as if we are redirected
+       to the new URL */
+    urlchanged = data->change.url_changed;
+    if ((CURLE_OK == res) && urlchanged) {
+      res = Curl_done(conn, res);
+      if(CURLE_OK == res) {
+        char *gotourl = strdup(data->change.url);
+        res = Curl_follow(data, gotourl);
+        if(res)
+          free(gotourl);
+      }
+    }
+  } while (urlchanged && res == CURLE_OK);
+
+  return res;
+}
+
+
+
 /*
  * Curl_perform() is the internal high-level function that gets called by the
  * external curl_easy_perform() function. It inits, performs and cleans up a
@@ -1946,43 +1991,17 @@
    */
 
   do {
-    int urlchanged = FALSE;
-    do {
-      bool async;
-      Curl_pgrsTime(data, TIMER_STARTSINGLE);
-      data->change.url_changed = FALSE;
-      res = Curl_connect(data, &conn, &async);
-
-      if((CURLE_OK == res) && async) {
-        /* Now, if async is TRUE here, we need to wait for the name
-           to resolve */
-        res = Curl_wait_for_resolv(conn, NULL);
-        if(CURLE_OK == res)
-          /* Resolved, continue with the connection */
-          res = Curl_async_resolved(conn);
-      }
-      if(res)
-        break;
+    res = Curl_connect_host(data, &conn);   /* primary connection */
 
-      /* If a callback (or something) has altered the URL we should use within
-         the Curl_connect(), we detect it here and act as if we are redirected
-         to the new URL */
-      urlchanged = data->change.url_changed;
-      if ((CURLE_OK == res) && urlchanged) {
-        res = Curl_done(&conn, res);
-        if(CURLE_OK == res) {
-          char *gotourl = strdup(data->change.url);
-          res = Curl_follow(data, gotourl);
-          if(res)
-            free(gotourl);
-        }
-      }
-    } while (urlchanged && res == CURLE_OK);
+    if (data->set.source_host) {   /* 3rd party transfer */
+      res = Curl_pretransfersec(conn);
+    }
 
     if(res == CURLE_OK) {
       res = Curl_do(&conn);
 
-      if(res == CURLE_OK) {
+      /* for non 3rd party transfer only */
+      if(res == CURLE_OK && !data->set.source_host) {
         res = Transfer(conn); /* now fetch that URL please */
         if(res == CURLE_OK) {
 
@@ -2099,4 +2118,40 @@
 
   return CURLE_OK;
 
+}
+
+
+
+/*
+ * Curl_pretransfersec() prepares secondary connection.
+ */
+CURLcode Curl_pretransfersec(struct connectdata *conn)
+{
+  CURLcode status = CURLE_OK;
+  struct SessionHandle *data = conn->data;
+  struct connectdata *sec_conn = NULL;   /* secondary connection */
+  struct SessionHandle *sec_data;
+  char url[500];
+
+  sec_data = (struct SessionHandle *)malloc(sizeof(struct SessionHandle));
+  if(!sec_data)   /* this is a very serious error */
+    return CURLE_OUT_OF_MEMORY;
+
+  /* clones the primary connection to preserve FTP options */
+  memcpy(sec_data, data, sizeof(struct SessionHandle));
+
+  snprintf(url, sizeof(url), "%s://%s/", conn->protostr,
+                data->set.source_host);
+  sec_data->change.url = url;
+  sec_data->set.ftpport = data->set.source_port;
+  sec_data->set.userpwd = data->set.source_userpwd;
+  sec_data->set.source_host = NULL;  /* prevents recursive definition */
+  sec_data->set.prequote = data->set.source_prequote;
+  sec_data->set.postquote = data->set.source_postquote;
+
+  status = Curl_connect_host(sec_data, &sec_conn); /* secondary connection */
+  conn->data->sec_conn = sec_conn;
+  conn->data->sec_data = sec_data;
+
+  return status;
 }
diff -N -u libold/transfer.h lib/transfer.h
--- libold/transfer.h	Tue Jun  1 08:57:07 2004
+++ lib/transfer.h	Mon May 31 14:41:03 2004
@@ -24,6 +24,7 @@
  ***************************************************************************/
 CURLcode Curl_perform(struct SessionHandle *data);
 CURLcode Curl_pretransfer(struct SessionHandle *data);
+CURLcode Curl_pretransfersec(struct connectdata *conn);
 CURLcode Curl_posttransfer(struct SessionHandle *data);
 CURLcode Curl_follow(struct SessionHandle *data, char *newurl);
 CURLcode Curl_readwrite(struct connectdata *conn, bool *done);
diff -N -u libold/url.c lib/url.c
--- libold/url.c	Tue Jun  1 08:57:11 2004
+++ lib/url.c	Mon May 31 16:19:01 2004
@@ -245,6 +245,9 @@
   if (data->share)
     data->share->dirty--;
 
+  if (data->sec_data) {   /* free secondary connection data */
+    Curl_safefree(data->sec_data);
+  }
   free(data);
   return CURLE_OK;
 }
@@ -1332,6 +1335,57 @@
     data->set.tcp_nodelay = (bool)va_arg(param, long);
     break;
 
+  /*********** 3rd party transfer options ***********/
+  case CURLOPT_SOURCE_HOST:
+    /*
+     * Use SOURCE HOST
+     */
+    data->set.source_host = va_arg(param, char *);
+    data->set.isPrintHost = (data->set.source_host != NULL);
+    break;
+
+  case CURLOPT_SOURCE_PORT:
+    /*
+     * Use SOURCE PORT
+     */
+    data->set.source_port = va_arg(param, char *);
+    break;
+
+  case CURLOPT_SOURCE_USERPWD:
+    /*
+     * Use SOURCE USER[:PASSWORD]
+     */
+    data->set.source_userpwd = va_arg(param, char *);
+    break;
+
+  case CURLOPT_SOURCE_PATH:
+    /*
+     * Use SOURCE PATH
+     */
+    data->set.source_path = va_arg(param, char *);
+    break;
+
+  case CURLOPT_PASV_HOST:
+    /*
+     * Indicates whether source or target host is passive
+     */
+    data->set.pasvHost = va_arg(param, long)?CURL_SOURCE_HOST:CURL_TARGET_HOST;
+    break;
+
+  case CURLOPT_SOURCE_PREQUOTE:
+    /*
+     * List of RAW FTP commands to use before a transfer on the source host
+     */
+    data->set.source_prequote = va_arg(param, struct curl_slist *);
+    break;
+
+  case CURLOPT_SOURCE_POSTQUOTE:
+    /*
+     * List of RAW FTP commands to use after a transfer on the source host
+     */
+    data->set.source_postquote = va_arg(param, struct curl_slist *);
+    break;
+
   default:
     /* unknown tag and its companion, just ignore: */
     return CURLE_FAILED_INIT; /* correct this */
@@ -3009,6 +3063,9 @@
     conn->bits.chunk = FALSE; /* always assume not chunked unless told
                                  otherwise */
     conn->maxdownload = -1;  /* might have been used previously! */
+
+    /* it is important to update user's options */
+    memcpy(&(conn->data->set), &(old_conn->data->set), sizeof(struct UserDefined));
 
     Curl_safefree(old_conn->user);
     Curl_safefree(old_conn->passwd);
diff -N -u libold/urldata.h lib/urldata.h
--- libold/urldata.h	Tue Jun  1 08:57:16 2004
+++ lib/urldata.h	Mon May 31 15:50:13 2004
@@ -185,6 +185,11 @@
   NTLMSTATE_LAST
 } curlntlm;
 
+typedef enum {
+  CURL_TARGET_HOST,
+  CURL_SOURCE_HOST
+} curl_host_part;
+
 /* Struct used for NTLM challenge-response authentication */
 struct ntlmdata {
   curlntlm state;
@@ -466,7 +471,7 @@
 
   char *proxyuser;    /* proxy user name string, allocated */
   char *proxypasswd;  /* proxy password string, allocated */
-
+  
   struct timeval now;     /* "current" time */
   struct timeval created; /* creation time */
   curl_socket_t sock[2]; /* two sockets, the second is used for the data
@@ -840,6 +845,10 @@
   struct curl_slist *quote;     /* after connection is established */
   struct curl_slist *postquote; /* after the transfer */
   struct curl_slist *prequote; /* before the transfer, after type (Wesley Laxton)*/
+  struct curl_slist *source_prequote;  /* in 3rd party transfer mode - before
+                                          the transfer on source host */
+  struct curl_slist *source_postquote; /* in 3rd party transfer mode - after
+                                          the transfer on source host */
   struct curl_slist *telnet_options; /* linked list of telnet options */
   curl_TimeCond timecondition; /* kind of time/date comparison */
   time_t timevalue;       /* what time to compare with */
@@ -867,6 +876,13 @@
   int ip_version;
 
   curl_off_t max_filesize; /* Maximum file size to download */
+  
+  char *source_host;     /* for 3rd party transfer */
+  char *source_port;     /* for 3rd party transfer */
+  char *source_userpwd;  /* for 3rd party transfer */
+  char *source_path;     /* for 3rd party transfer */
+  curl_host_part pasvHost;   /* for 3rd party transfer indicates passive host */
+  bool isPrintHost;   /* indicates printing host name in debug info */
 
 /* Here follows boolean settings that define how to behave during
    this session. They are STATIC, set by libcurl users or at least initially
@@ -904,7 +920,7 @@
   bool no_signal;        /* do not use any signal/alarm handler */
   bool global_dns_cache; /* subject for future removal */
   bool tcp_nodelay;      /* whether to enable TCP_NODELAY or not */
-
+  
 };
 
 /*
@@ -929,6 +945,9 @@
   struct UrlState state;       /* struct for fields used for state info and
                                   other dynamic purposes */
   struct PureInfo info;        /* stats, reports and info data */
+  struct connectdata *sec_conn;   /* secondary connection for 3rd party
+                                     transfer */
+  struct SessionHandle *sec_data;
 #if defined(USE_SSLEAY) && defined(HAVE_OPENSSL_ENGINE_H)
   ENGINE*  engine;
 #endif /* USE_SSLEAY */
