Alexander Krasnostavsky's FTP third party transfer (proxy) support

This commit is contained in:
Daniel Stenberg 2004-06-03 11:41:05 +00:00
parent 7dcb102733
commit ea81dd9e2e
8 changed files with 583 additions and 230 deletions

427
lib/ftp.c
View File

@ -119,6 +119,10 @@ static CURLcode ftp_cwd(struct connectdata *conn, char *path);
static CURLcode ftp_mkd(struct connectdata *conn, char *path); static CURLcode ftp_mkd(struct connectdata *conn, char *path);
static CURLcode ftp_cwd_and_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_quit(struct connectdata *conn);
static CURLcode ftp_3rdparty_pretransfer(struct connectdata *conn);
static CURLcode ftp_3rdparty_transfer(struct connectdata *conn);
static CURLcode ftp_regular_transfer(struct connectdata *conn);
static CURLcode ftp_3rdparty(struct connectdata *conn);
/* easy-to-use macro: */ /* easy-to-use macro: */
#define FTPSENDF(x,y,z) if((result = Curl_ftpsendf(x,y,z))) return result #define FTPSENDF(x,y,z) if((result = Curl_ftpsendf(x,y,z))) return result
@ -369,7 +373,7 @@ CURLcode Curl_GetFTPResponse(ssize_t *nreadp, /* return number of bytes read */
/* output debug output if that is requested */ /* output debug output if that is requested */
if(data->set.verbose) 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 * We pass all response-lines to the callback function registered
@ -844,8 +848,13 @@ CURLcode Curl_ftp_done(struct connectdata *conn, CURLcode status)
ftp->no_transfer = FALSE; ftp->no_transfer = FALSE;
ftp->dont_check = FALSE; ftp->dont_check = FALSE;
if (!result && conn->sec_conn) { /* 3rd party transfer */
/* "done" with the secondary connection */
result = Curl_ftp_done(conn->sec_conn, status);
}
/* Send any post-transfer QUOTE strings? */ /* Send any post-transfer QUOTE strings? */
if(!result && data->set.postquote) if(!status && !result && data->set.postquote)
result = ftp_sendquote(conn, data->set.postquote); result = ftp_sendquote(conn, data->set.postquote);
return result; return result;
@ -2334,116 +2343,15 @@ CURLcode ftp_perform(struct connectdata *conn,
* parts etc as a wrapper to the actual DO function (ftp_perform). * parts etc as a wrapper to the actual DO function (ftp_perform).
* *
* The input argument is already checked for validity. * 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 Curl_ftp(struct connectdata *conn)
{ {
CURLcode retcode=CURLE_OK; 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 */ if (conn->sec_conn) /* 3rd party transfer */
char *cur_pos=conn->path; /* current position in ppath. point at the begin retcode = ftp_3rdparty(conn);
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 else
ftp->file=NULL; /* instead of point to a zero byte, we make it a NULL retcode = ftp_regular_transfer(conn);
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; return retcode;
} }
@ -2484,7 +2392,7 @@ CURLcode Curl_ftpsendf(struct connectdata *conn,
break; break;
if(conn->data->set.verbose) 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) { if(bytes_written != (ssize_t)write_len) {
write_len -= bytes_written; write_len -= bytes_written;
@ -2651,4 +2559,307 @@ static CURLcode ftp_cwd_and_mkd(struct connectdata *conn, char *path)
return result; return result;
} }
/***********************************************************************
*
* ftp_3rdparty_pretransfer()
*
* Preparation for 3rd party transfer.
*
*/
static CURLcode ftp_3rdparty_pretransfer(struct connectdata *conn)
{
CURLcode result = CURLE_OK;
struct SessionHandle *data = conn->data;
struct connectdata *sec_conn = conn->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_3rdparty_transfer()
*
* Performs 3rd party transfer.
*
*/
static CURLcode ftp_3rdparty_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 = conn->sec_conn;
char *buf = data->state.buffer; /* this is our buffer */
char *str = buf;
char pasv_port[50];
const char *stor_cmd;
struct connectdata *pasv_conn;
struct connectdata *port_conn;
if (data->set.pasvHost == CURL_TARGET_PASV) {
pasv_conn = conn;
port_conn = sec_conn;
}
else {
pasv_conn = sec_conn;
port_conn = conn;
}
/* sets the passive mode */
FTPSENDF(pasv_conn, "%s", "PASV");
result = Curl_GetFTPResponse(&nread, pasv_conn, &ftpcode);
if (result) return result;
if (ftpcode != 227) {
failf(data, "Odd return code after PASV:%s", buf + 3);
return CURLE_FTP_WEIRD_PASV_REPLY;
}
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(pasv_port, sizeof(pasv_port), "%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", pasv_port);
result = Curl_GetFTPResponse(&nread, port_conn, &ftpcode);
if (result)
return result;
if (ftpcode != 200) {
failf(data, "PORT command attempts failed:%s", buf + 3);
return CURLE_FTP_PORT_FAILED;
}
/* we might append onto the file instead of overwriting it */
stor_cmd = data->set.ftp_append?"APPE":"STOR";
/* transfers file between remote hosts */
FTPSENDF(sec_conn, "RETR %s", data->set.source_path);
if(data->set.pasvHost == CURL_TARGET_PASV) {
result = Curl_GetFTPResponse(&nread, sec_conn, &ftpcode);
if (result)
return result;
if (ftpcode != 150) {
failf(data, "Failed RETR: %s", buf + 4);
return CURLE_FTP_COULDNT_RETR_FILE;
}
result = Curl_ftpsendf(conn, "%s %s", stor_cmd, conn->path);
if(CURLE_OK == result)
result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
if (result)
return result;
if (ftpcode != 150) {
failf(data, "Failed FTP upload: %s", buf + 4);
return CURLE_FTP_COULDNT_STOR_FILE;
}
}
else {
result = Curl_ftpsendf(conn, "%s %s", stor_cmd, conn->path);
if(CURLE_OK == result)
result = Curl_GetFTPResponse(&nread, sec_conn, &ftpcode);
if (result)
return result;
if (ftpcode != 150) {
failf(data, "Failed FTP upload: %s", buf + 4);
return CURLE_FTP_COULDNT_STOR_FILE;
}
result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
if (result)
return result;
if (ftpcode != 150) {
failf(data, "Failed FTP upload: %s", buf + 4);
return CURLE_FTP_COULDNT_STOR_FILE;
}
}
return CURLE_OK;
}
/***********************************************************************
*
* 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.
*/
static
CURLcode 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;
}
/***********************************************************************
*
* ftp_3rdparty()
*
* The input argument is already checked for validity.
* Performs a 3rd party transfer between two remote hosts.
*/
static CURLcode ftp_3rdparty(struct connectdata *conn)
{
CURLcode retcode = CURLE_OK;
conn->proto.ftp->ctl_valid = conn->sec_conn->proto.ftp->ctl_valid = TRUE;
conn->size = conn->sec_conn->size = -1;
retcode = ftp_3rdparty_pretransfer(conn);
if (!retcode)
retcode = ftp_3rdparty_transfer(conn);
return retcode;
}
#endif /* CURL_DISABLE_FTP */ #endif /* CURL_DISABLE_FTP */

View File

@ -740,7 +740,8 @@ CURLcode add_buffer_send(send_buffer *in,
if(conn->data->set.verbose) if(conn->data->set.verbose)
/* this data _may_ contain binary stuff */ /* 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; *bytes_written += amount;
@ -1044,7 +1045,8 @@ CURLcode Curl_ConnectHTTPProxyTunnel(struct connectdata *conn,
/* output debug output if that is requested */ /* output debug output if that is requested */
if(data->set.verbose) 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 */ /* send the header to the callback */
writetype = CLIENTWRITE_HEADER; writetype = CLIENTWRITE_HEADER;

View File

@ -142,7 +142,7 @@ void Curl_infof(struct SessionHandle *data, const char *fmt, ...)
va_start(ap, fmt); va_start(ap, fmt);
vsnprintf(print_buffer, 1024, fmt, ap); vsnprintf(print_buffer, 1024, fmt, ap);
va_end(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 @@ void Curl_failf(struct SessionHandle *data, const char *fmt, ...)
data->set.errorbuffer[len] = '\n'; data->set.errorbuffer[len] = '\n';
data->set.errorbuffer[++len] = '\0'; 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) if(doneit)
/* cut off the newline again */ /* cut off the newline again */
data->set.errorbuffer[--len]=0; data->set.errorbuffer[--len]=0;
@ -204,7 +204,7 @@ CURLcode Curl_sendf(curl_socket_t sockfd, struct connectdata *conn,
break; break;
if(data->set.verbose) if(data->set.verbose)
Curl_debug(data, CURLINFO_DATA_OUT, sptr, bytes_written); Curl_debug(data, CURLINFO_DATA_OUT, sptr, bytes_written, conn->host.dispname);
if((size_t)bytes_written != write_len) { if((size_t)bytes_written != write_len) {
/* if not all was written at once, we must advance the pointer, decrease /* if not all was written at once, we must advance the pointer, decrease
@ -440,7 +440,7 @@ int Curl_read(struct connectdata *conn, /* connection data */
} }
/* return 0 on success */ /* return 0 on success */
int Curl_debug(struct SessionHandle *data, curl_infotype type, static int showit(struct SessionHandle *data, curl_infotype type,
char *ptr, size_t size) char *ptr, size_t size)
{ {
static const char * const s_infotype[CURLINFO_END] = { static const char * const s_infotype[CURLINFO_END] = {
@ -462,3 +462,18 @@ int Curl_debug(struct SessionHandle *data, curl_infotype type,
} }
return 0; return 0;
} }
int Curl_debug(struct SessionHandle *data, curl_infotype type,
char *ptr, size_t size, char *host)
{
int rc;
if(data->set.printhost && host) {
char buffer[160];
snprintf(buffer, sizeof(buffer), "[Chunk to/from %s]", host);
rc = showit(data, CURLINFO_TEXT, buffer, strlen(buffer));
if(rc)
return rc;
}
rc = showit(data, type, ptr, size);
return rc;
}

View File

@ -50,7 +50,7 @@ CURLcode Curl_write(struct connectdata *conn,
/* the function used to output verbose information */ /* the function used to output verbose information */
int Curl_debug(struct SessionHandle *handle, curl_infotype type, int Curl_debug(struct SessionHandle *handle, curl_infotype type,
char *data, size_t size); char *data, size_t size, char *host);
#endif #endif

View File

@ -875,7 +875,7 @@ CURLcode Curl_readwrite(struct connectdata *conn,
if(data->set.verbose) if(data->set.verbose)
Curl_debug(data, CURLINFO_HEADER_IN, 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); result = Curl_client_write(data, writetype, k->p, k->hbuflen);
if(result) if(result)
@ -962,12 +962,12 @@ CURLcode Curl_readwrite(struct connectdata *conn,
if(data->set.verbose) { if(data->set.verbose) {
if(k->badheader) { if(k->badheader) {
Curl_debug(data, CURLINFO_DATA_IN, data->state.headerbuff, Curl_debug(data, CURLINFO_DATA_IN, data->state.headerbuff,
k->hbuflen); k->hbuflen, conn->host.dispname);
if(k->badheader == HEADER_PARTHEADER) 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 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) { if(conn->bits.chunk) {
@ -1187,7 +1187,7 @@ CURLcode Curl_readwrite(struct connectdata *conn,
if(data->set.verbose) if(data->set.verbose)
/* show the data before we change the pointer upload_fromhere */ /* show the data before we change the pointer upload_fromhere */
Curl_debug(data, CURLINFO_DATA_OUT, conn->upload_fromhere, Curl_debug(data, CURLINFO_DATA_OUT, conn->upload_fromhere,
bytes_written); bytes_written, conn->host.dispname);
if(conn->upload_present != bytes_written) { if(conn->upload_present != bytes_written) {
/* we only wrote a part of the buffer (if anything), deal with it! */ /* we only wrote a part of the buffer (if anything), deal with it! */
@ -1919,6 +1919,50 @@ CURLcode Curl_follow(struct SessionHandle *data,
return CURLE_OK; return CURLE_OK;
} }
static 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 * 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 * external curl_easy_perform() function. It inits, performs and cleans up a
@ -1945,43 +1989,21 @@ CURLcode Curl_perform(struct SessionHandle *data)
*/ */
do { do {
int urlchanged = FALSE; res = Curl_connect_host(data, &conn); /* primary connection */
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);
if(res == CURLE_OK) { if(res == CURLE_OK) {
if (data->set.source_host) /* 3rd party transfer */
res = Curl_pretransfersec(conn);
else
conn->sec_conn = NULL;
}
if(res == CURLE_OK) {
res = Curl_do(&conn); 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 */ res = Transfer(conn); /* now fetch that URL please */
if(res == CURLE_OK) { if(res == CURLE_OK) {
@ -2099,3 +2121,35 @@ Curl_Transfer(struct connectdata *c_conn, /* connection data */
return CURLE_OK; return CURLE_OK;
} }
/*
* Curl_pretransfersec() prepares the secondary connection (used for 3rd party
* FTP transfers).
*/
CURLcode Curl_pretransfersec(struct connectdata *conn)
{
CURLcode status = CURLE_OK;
struct SessionHandle *data = conn->data;
struct connectdata *sec_conn = NULL; /* secondary connection */
/* update data with source host options */
char *url = aprintf( "%s://%s/", conn->protostr, data->set.source_host);
if(!url)
return CURLE_OUT_OF_MEMORY;
if(data->change.url_alloc)
free(data->change.url);
data->change.url_alloc = TRUE;
data->change.url = url;
data->set.ftpport = data->set.source_port;
data->set.userpwd = data->set.source_userpwd;
/* secondary connection */
status = Curl_connect_host(data, &sec_conn);
sec_conn->data = data;
conn->sec_conn = sec_conn;
return status;
}

View File

@ -24,6 +24,7 @@
***************************************************************************/ ***************************************************************************/
CURLcode Curl_perform(struct SessionHandle *data); CURLcode Curl_perform(struct SessionHandle *data);
CURLcode Curl_pretransfer(struct SessionHandle *data); CURLcode Curl_pretransfer(struct SessionHandle *data);
CURLcode Curl_pretransfersec(struct connectdata *conn);
CURLcode Curl_posttransfer(struct SessionHandle *data); CURLcode Curl_posttransfer(struct SessionHandle *data);
CURLcode Curl_follow(struct SessionHandle *data, char *newurl); CURLcode Curl_follow(struct SessionHandle *data, char *newurl);
CURLcode Curl_readwrite(struct connectdata *conn, bool *done); CURLcode Curl_readwrite(struct connectdata *conn, bool *done);

View File

@ -1337,6 +1337,57 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...)
data->set.tcp_nodelay = (bool)va_arg(param, long); data->set.tcp_nodelay = (bool)va_arg(param, long);
break; break;
/*********** 3rd party transfer options ***********/
case CURLOPT_SOURCE_HOST:
/*
* Use SOURCE HOST
*/
data->set.source_host = va_arg(param, char *);
data->set.printhost = (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_PASV:CURL_TARGET_PASV;
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: default:
/* unknown tag and its companion, just ignore: */ /* unknown tag and its companion, just ignore: */
return CURLE_FAILED_INIT; /* correct this */ return CURLE_FAILED_INIT; /* correct this */

View File

@ -185,6 +185,12 @@ typedef enum {
NTLMSTATE_LAST NTLMSTATE_LAST
} curlntlm; } curlntlm;
/* for 3rd party transfers to decide which side that issues PASV */
typedef enum {
CURL_TARGET_PASV,
CURL_SOURCE_PASV
} curl_pasv_side;
/* Struct used for NTLM challenge-response authentication */ /* Struct used for NTLM challenge-response authentication */
struct ntlmdata { struct ntlmdata {
curlntlm state; curlntlm state;
@ -601,6 +607,8 @@ struct connectdata {
/* data used for the asynch name resolve callback */ /* data used for the asynch name resolve callback */
struct Curl_async async; struct Curl_async async;
#endif #endif
struct connectdata *sec_conn; /* secondary connection for 3rd party
transfer */
}; };
/* The end of connectdata. */ /* The end of connectdata. */
@ -845,7 +853,11 @@ struct UserDefined {
bool crlf; /* convert crlf on ftp upload(?) */ bool crlf; /* convert crlf on ftp upload(?) */
struct curl_slist *quote; /* after connection is established */ struct curl_slist *quote; /* after connection is established */
struct curl_slist *postquote; /* after the transfer */ struct curl_slist *postquote; /* after the transfer */
struct curl_slist *prequote; /* before the transfer, after type (Wesley Laxton)*/ struct curl_slist *prequote; /* before the transfer, after type */
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 */ struct curl_slist *telnet_options; /* linked list of telnet options */
curl_TimeCond timecondition; /* kind of time/date comparison */ curl_TimeCond timecondition; /* kind of time/date comparison */
time_t timevalue; /* what time to compare with */ time_t timevalue; /* what time to compare with */
@ -874,10 +886,17 @@ struct UserDefined {
curl_off_t max_filesize; /* Maximum file size to download */ 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_pasv_side pasvHost; /* for 3rd party transfer indicates passive host */
/* Here follows boolean settings that define how to behave during /* Here follows boolean settings that define how to behave during
this session. They are STATIC, set by libcurl users or at least initially this session. They are STATIC, set by libcurl users or at least initially
and they don't change during operations. */ and they don't change during operations. */
bool printhost; /* printing host name in debug info */
bool get_filetime; bool get_filetime;
bool tunnel_thru_httpproxy; bool tunnel_thru_httpproxy;
bool ftp_append; bool ftp_append;