aboutsummaryrefslogtreecommitdiff
path: root/lib/ftp.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ftp.c')
-rw-r--r--lib/ftp.c561
1 files changed, 386 insertions, 175 deletions
diff --git a/lib/ftp.c b/lib/ftp.c
index 08b1cc3bd..7a7b82040 100644
--- a/lib/ftp.c
+++ b/lib/ftp.c
@@ -1,8 +1,8 @@
/***************************************************************************
- * _ _ ____ _
- * Project ___| | | | _ \| |
- * / __| | | | |_) | |
- * | (__| |_| | _ <| |___
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al.
@@ -10,7 +10,7 @@
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at http://curl.haxx.se/docs/copyright.html.
- *
+ *
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING 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_cwd_and_mkd(struct connectdata *conn, char *path);
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: */
#define FTPSENDF(x,y,z) if((result = Curl_ftpsendf(x,y,z))) return result
@@ -161,11 +165,11 @@ static CURLcode AllowServerConnect(struct connectdata *conn)
long timespent = Curl_tvdiff(Curl_tvnow(), now)/1000;
long timeout = data->set.connecttimeout?data->set.connecttimeout:
(data->set.timeout?data->set.timeout: 0);
-
+
FD_ZERO(&rdset);
FD_SET(sock, &rdset);
-
+
if(timeout) {
timeout -= timespent;
if(timeout<=0) {
@@ -369,7 +373,7 @@ CURLcode Curl_GetFTPResponse(ssize_t *nreadp, /* return number of bytes read */
/* 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
@@ -380,7 +384,7 @@ CURLcode Curl_GetFTPResponse(ssize_t *nreadp, /* return number of bytes read */
line_start, perline);
if(result)
return result;
-
+
#define lastline(line) (isdigit((int)line[0]) && isdigit((int)line[1]) && \
isdigit((int)line[2]) && (' ' == line[3]))
@@ -560,7 +564,7 @@ CURLcode Curl_ftp_connect(struct connectdata *conn)
}
}
}
-
+
/* send USER */
FTPSENDF(conn, "USER %s", ftp->user?ftp->user:"");
@@ -592,7 +596,7 @@ CURLcode Curl_ftp_connect(struct connectdata *conn)
else if(ftpcode == 230) {
/* 230 User ... logged in.
(user successfully logged in) */
-
+
infof(data, "We have successfully logged in\n");
}
else {
@@ -611,7 +615,7 @@ CURLcode Curl_ftp_connect(struct connectdata *conn)
*/
if(conn->sec_complete)
Curl_sec_set_protection_level(conn);
-
+
/* We may need to issue a KAUTH here to have access to the files
* do it if user supplied a password
*/
@@ -638,7 +642,7 @@ CURLcode Curl_ftp_connect(struct connectdata *conn)
exchange (the TLS negotiation in this case)
... (and on page 8):
-
+
Thus the PBSZ command must still be issued, but must have a parameter
of '0' to indicate that no buffering is taking place and the data
connection should not be encapsulated.
@@ -659,7 +663,7 @@ CURLcode Curl_ftp_connect(struct connectdata *conn)
result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
if(result)
return result;
-
+
if(ftpcode == 200)
/* We have enabled SSL for the data connection! */
conn->ssl[SECONDARYSOCKET].use = TRUE;
@@ -684,7 +688,7 @@ CURLcode Curl_ftp_connect(struct connectdata *conn)
if(!dir)
return CURLE_OUT_OF_MEMORY;
-
+
/* Reply format is like
257<space>"<directory-name>"<space><commentary> and the RFC959 says
@@ -822,7 +826,7 @@ CURLcode Curl_ftp_done(struct connectdata *conn, CURLcode status)
result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
ftp->response_time = 3600; /* set this back to one hour waits */
-
+
if(!nread && (CURLE_OPERATION_TIMEDOUT == result)) {
failf(data, "control connection looks dead");
return result;
@@ -842,10 +846,15 @@ CURLcode Curl_ftp_done(struct connectdata *conn, CURLcode status)
/* clear these for next connection */
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? */
- if(!result && data->set.postquote)
+ if(!status && !result && data->set.postquote)
result = ftp_sendquote(conn, data->set.postquote);
return result;
@@ -859,7 +868,7 @@ CURLcode Curl_ftp_done(struct connectdata *conn, CURLcode status)
* The quote list is passed as an argument.
*/
-static
+static
CURLcode ftp_sendquote(struct connectdata *conn, struct curl_slist *quote)
{
struct curl_slist *item;
@@ -958,7 +967,7 @@ static CURLcode ftp_transfertype(struct connectdata *conn,
result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
if(result)
return result;
-
+
if(ftpcode != 200) {
failf(data, "Couldn't set %s mode",
ascii?"ASCII":"binary");
@@ -1052,7 +1061,7 @@ ftp_pasv_verbose(struct connectdata *conn,
/* Bjorn Reese (November 28 2001):
The Tru64 man page on gethostbyaddr_r() says that
the hostent struct must be filled with zeroes before the call to
- gethostbyaddr_r().
+ gethostbyaddr_r().
... as must be struct hostent_data Craig Markwardt 19 Sep 2002. */
@@ -1065,7 +1074,7 @@ ftp_pasv_verbose(struct connectdata *conn,
answer=NULL;
else
answer=(struct hostent *)hostent_buf;
-
+
# endif
# ifdef HAVE_GETHOSTBYADDR_R_7
/* Solaris and IRIX */
@@ -1085,7 +1094,7 @@ ftp_pasv_verbose(struct connectdata *conn,
&h_errnop))
answer=NULL; /* error */
# endif
-
+
# else
(void)hostent_buf; /* avoid compiler warning */
answer = gethostbyaddr((char *) &address, sizeof(address), AF_INET);
@@ -1117,7 +1126,7 @@ ftp_pasv_verbose(struct connectdata *conn,
snprintf(nbuf, sizeof(nbuf), "?");
snprintf(sbuf, sizeof(sbuf), "?");
}
-
+
if (getnameinfo(addr->ai_addr, addr->ai_addrlen,
hbuf, sizeof(hbuf), NULL, 0, 0)) {
infof(conn->data, "Connecting to %s (%s) port %s\n", nbuf, newhost, sbuf);
@@ -1178,7 +1187,7 @@ CURLcode ftp_use_port(struct connectdata *conn)
failf(data, "getsockname() returned %d\n", rc);
return CURLE_FTP_PORT_FAILED;
}
-
+
rc = getnameinfo((struct sockaddr *)&ss, sslen, hbuf, sizeof(hbuf), NULL, 0,
NIFLAGS);
if(rc) {
@@ -1189,7 +1198,7 @@ CURLcode ftp_use_port(struct connectdata *conn)
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
+ 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;
@@ -1199,7 +1208,7 @@ CURLcode ftp_use_port(struct connectdata *conn)
failf(data, "getaddrinfo() returned %d\n", rc);
return CURLE_FTP_PORT_FAILED;
}
-
+
portsock = CURL_SOCKET_BAD;
error = 0;
for (ai = res; ai; ai = ai->ai_next) {
@@ -1221,14 +1230,14 @@ CURLcode ftp_use_port(struct connectdata *conn)
portsock = CURL_SOCKET_BAD;
continue;
}
-
+
if (listen(portsock, 1) < 0) {
error = Curl_ourerrno();
sclose(portsock);
portsock = CURL_SOCKET_BAD;
continue;
}
-
+
break;
}
freeaddrinfo(res);
@@ -1247,7 +1256,7 @@ CURLcode ftp_use_port(struct connectdata *conn)
modep && *modep; modep++) {
int lprtaf, eprtaf;
int alen=0, plen=0;
-
+
switch (sa->sa_family) {
case AF_INET:
ap = (unsigned char *)&((struct sockaddr_in *)&ss)->sin_addr;
@@ -1314,45 +1323,45 @@ CURLcode ftp_use_port(struct connectdata *conn)
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;
}
}
-
+
result = Curl_ftpsendf(conn, "%s %s", *modep, portmsgbuf);
if(result)
return result;
}
-
+
result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
if(result)
return result;
-
+
if (ftpcode != 200) {
continue;
}
else
break;
}
-
+
if (!*modep) {
sclose(portsock);
failf(data, "PORT command attempts failed");
@@ -1362,7 +1371,7 @@ CURLcode ftp_use_port(struct connectdata *conn)
is only so that the cleanup function will close it in case
we fail before the true secondary stuff is made */
conn->sock[SECONDARYSOCKET] = portsock;
-
+
#else
/******************************************************************
*
@@ -1403,7 +1412,7 @@ CURLcode ftp_use_port(struct connectdata *conn)
/* pick a suitable default here */
socklen_t sslen;
-
+
sslen = sizeof(sa);
if (getsockname(conn->sock[FIRSTSOCKET],
(struct sockaddr *)&sa, &sslen) < 0) {
@@ -1421,7 +1430,7 @@ CURLcode ftp_use_port(struct connectdata *conn)
if ( h || sa_filled_in) {
if( (portsock = socket(AF_INET, SOCK_STREAM, 0)) != CURL_SOCKET_BAD ) {
int size;
-
+
/* 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 */
@@ -1438,7 +1447,7 @@ CURLcode ftp_use_port(struct connectdata *conn)
sa.sin_port = 0;
size = sizeof(sa);
-
+
if(bind(portsock, (struct sockaddr *)&sa, size) >= 0) {
/* we succeeded to bind */
struct sockaddr_in add;
@@ -1450,7 +1459,7 @@ CURLcode ftp_use_port(struct connectdata *conn)
return CURLE_FTP_PORT_FAILED;
}
porttouse = ntohs(add.sin_port);
-
+
if ( listen(portsock, 1) < 0 ) {
failf(data, "listen(2) failed on socket");
return CURLE_FTP_PORT_FAILED;
@@ -1492,7 +1501,7 @@ CURLcode ftp_use_port(struct connectdata *conn)
#endif
infof(data, "Telling server to connect to %d.%d.%d.%d:%d\n",
ip[0], ip[1], ip[2], ip[3], porttouse);
-
+
result=Curl_ftpsendf(conn, "PORT %d,%d,%d,%d,%d,%d",
ip[0], ip[1], ip[2], ip[3],
porttouse >> 8,
@@ -1555,12 +1564,12 @@ CURLcode ftp_use_pasv(struct connectdata *conn,
int modeoff;
unsigned short connectport; /* the local port connect() should use! */
unsigned short newport=0; /* remote port, not necessary the local one */
-
+
/* newhost must be able to hold a full IP-style address in ASCII, which
in the IPv6 case means 5*8-1 = 39 letters */
char newhost[48];
char *newhostp=NULL;
-
+
for (modeoff = (data->set.ftp_use_epsv?0:1);
mode[modeoff]; modeoff++) {
result = Curl_ftpsendf(conn, "%s", mode[modeoff]);
@@ -1592,7 +1601,7 @@ CURLcode ftp_use_pasv(struct connectdata *conn,
* "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],
@@ -1639,7 +1648,7 @@ CURLcode ftp_use_pasv(struct connectdata *conn,
/* we should use the same host we already are connected to */
newhostp = conn->host.name;
}
- }
+ }
else
ptr=NULL;
}
@@ -1664,7 +1673,7 @@ CURLcode ftp_use_pasv(struct connectdata *conn,
rc = Curl_wait_for_resolv(conn, &addr);
connectport =
- (unsigned short)conn->port; /* we connect to the proxy's port */
+ (unsigned short)conn->port; /* we connect to the proxy's port */
}
else {
@@ -1679,7 +1688,7 @@ CURLcode ftp_use_pasv(struct connectdata *conn,
}
connectport = newport; /* we connect to the remote port */
}
-
+
result = Curl_connecthost(conn,
addr,
connectport,
@@ -1697,11 +1706,11 @@ CURLcode ftp_use_pasv(struct connectdata *conn,
* the 'connected' set to FALSE and thus we are now awaiting a non-blocking
* connect to connect and we should not be "hanging" here waiting.
*/
-
+
if(data->set.verbose)
/* this just dumps information about this second connection */
ftp_pasv_verbose(conn, conninfo, newhostp, connectport);
-
+
if(conn->bits.tunnel_proxy) {
/* We want "seamless" FTP operations through HTTP proxy tunnel */
result = Curl_ConnectHTTPProxyTunnel(conn, SECONDARYSOCKET,
@@ -1809,10 +1818,10 @@ CURLcode Curl_ftp_nextconnect(struct connectdata *conn)
/* no data to transfer */
result=Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
-
+
/* Set no_transfer so that we won't get any error in
* Curl_ftp_done() because we didn't transfer anything! */
- ftp->no_transfer = TRUE;
+ ftp->no_transfer = TRUE;
return CURLE_OK;
}
@@ -1867,7 +1876,7 @@ CURLcode Curl_ftp_nextconnect(struct connectdata *conn)
SECONDARYSOCKET, bytecountp);
if(result)
return result;
-
+
}
else if(!conn->bits.no_body) {
/* Retrieve file or directory */
@@ -2016,7 +2025,7 @@ CURLcode Curl_ftp_nextconnect(struct connectdata *conn)
ftp->no_transfer = TRUE;
return CURLE_OK;
}
-
+
/* Set resume file transfer offset */
infof(data, "Instructs server to resume from offset %" FORMAT_OFF_T
"\n",
@@ -2047,16 +2056,16 @@ CURLcode Curl_ftp_nextconnect(struct connectdata *conn)
A;
150 Opening BINARY mode data connection for /etc/passwd (2241
bytes). (ok, the file is being transfered)
-
+
B:
- 150 Opening ASCII mode data connection for /bin/ls
+ 150 Opening ASCII mode data connection for /bin/ls
C:
150 ASCII data connection for /bin/ls (137.167.104.91,37445) (0 bytes).
D:
150 Opening ASCII mode data connection for /linux/fisk/kpanelrc (0.0.0.0,0) (545 bytes).
-
+
E:
125 Data connection already open; Transfer starting. */
@@ -2104,7 +2113,7 @@ CURLcode Curl_ftp_nextconnect(struct connectdata *conn)
/* get the number! */
size = curlx_strtoofft(bytes, NULL, 0);
}
-
+
}
}
else if(downloadsize > -1)
@@ -2147,7 +2156,7 @@ CURLcode Curl_ftp_nextconnect(struct connectdata *conn)
return CURLE_FTP_COULDNT_RETR_FILE;
}
}
-
+
}
/* end of transfer */
@@ -2249,7 +2258,7 @@ CURLcode ftp_perform(struct connectdata *conn,
int ftpcode;
ftp->no_transfer = TRUE; /* this means no actual transfer is made */
-
+
/* 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);
@@ -2322,7 +2331,7 @@ CURLcode ftp_perform(struct connectdata *conn,
if(CURLE_OK == result && *connected)
infof(data, "Connected the data stream with PASV!\n");
}
-
+
return result;
}
@@ -2334,116 +2343,15 @@ CURLcode ftp_perform(struct connectdata *conn,
* 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;
- }
- }
- }
-
- 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;
- }
- }
+ if (conn->sec_conn) /* 3rd party transfer */
+ retcode = ftp_3rdparty(conn);
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 */
+ retcode = ftp_regular_transfer(conn);
return retcode;
}
@@ -2470,7 +2378,7 @@ CURLcode Curl_ftpsendf(struct connectdata *conn,
va_start(ap, fmt);
vsnprintf(s, 250, fmt, ap);
va_end(ap);
-
+
strcat(s, "\r\n"); /* append a trailing CRLF */
bytes_written=0;
@@ -2484,7 +2392,7 @@ CURLcode Curl_ftpsendf(struct connectdata *conn,
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;
@@ -2502,7 +2410,7 @@ CURLcode Curl_ftpsendf(struct connectdata *conn,
* ftp_quit()
*
* This should be called before calling sclose() on an ftp control connection
- * (not data connections). We should then wait for the response from the
+ * (not data connections). We should then wait for the response from the
* server before returning. The calling code should then try to close the
* connection.
*
@@ -2576,7 +2484,7 @@ static CURLcode ftp_mkd(struct connectdata *conn, char *path)
result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
if(result)
return result;
-
+
switch(ftpcode) {
case 257:
/* success! */
@@ -2604,13 +2512,13 @@ static CURLcode ftp_mkd(struct connectdata *conn, char *path)
*
* This function does NOT call failf().
*/
-static
+static
CURLcode ftp_cwd(struct connectdata *conn, char *path)
{
ssize_t nread;
int ftpcode;
CURLcode result;
-
+
FTPSENDF(conn, "CWD %s", path);
result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
if (!result) {
@@ -2635,7 +2543,7 @@ CURLcode ftp_cwd(struct connectdata *conn, char *path)
static CURLcode ftp_cwd_and_mkd(struct connectdata *conn, char *path)
{
CURLcode result;
-
+
result = ftp_cwd(conn, path);
if (result) {
if(conn->data->set.ftp_create_missing_dirs) {
@@ -2651,4 +2559,307 @@ static CURLcode ftp_cwd_and_mkd(struct connectdata *conn, char *path)
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 */