/***************************************************************************
- * _ _ ____ _
- * Project ___| | | | _ \| |
- * / __| | | | |_) | |
- * | (__| |_| | _ <| |___
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al.
* 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.
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
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) {
/* 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
line_start, perline);
if(result)
return result;
-
+
#define lastline(line) (isdigit((int)line[0]) && isdigit((int)line[1]) && \
isdigit((int)line[2]) && (' ' == line[3]))
}
}
}
-
+
/* send USER */
FTPSENDF(conn, "USER %s", ftp->user?ftp->user:"");
else if(ftpcode == 230) {
/* 230 User ... logged in.
(user successfully logged in) */
-
+
infof(data, "We have successfully logged in\n");
}
else {
*/
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
*/
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.
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;
if(!dir)
return CURLE_OUT_OF_MEMORY;
-
+
/* Reply format is like
257<space>"<directory-name>"<space><commentary> and the RFC959 says
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;
/* 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;
* The quote list is passed as an argument.
*/
-static
+static
CURLcode ftp_sendquote(struct connectdata *conn, struct curl_slist *quote)
{
struct curl_slist *item;
result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
if(result)
return result;
-
+
if(ftpcode != 200) {
failf(data, "Couldn't set %s mode",
ascii?"ASCII":"binary");
/* 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. */
answer=NULL;
else
answer=(struct hostent *)hostent_buf;
-
+
# endif
# ifdef HAVE_GETHOSTBYADDR_R_7
/* Solaris and IRIX */
&h_errnop))
answer=NULL; /* error */
# endif
-
+
# else
(void)hostent_buf; /* avoid compiler warning */
answer = gethostbyaddr((char *) &address, sizeof(address), AF_INET);
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);
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) {
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;
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) {
portsock = CURL_SOCKET_BAD;
continue;
}
-
+
if (listen(portsock, 1) < 0) {
error = Curl_ourerrno();
sclose(portsock);
portsock = CURL_SOCKET_BAD;
continue;
}
-
+
break;
}
freeaddrinfo(res);
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;
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");
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
/******************************************************************
*
/* pick a suitable default here */
socklen_t sslen;
-
+
sslen = sizeof(sa);
if (getsockname(conn->sock[FIRSTSOCKET],
(struct sockaddr *)&sa, &sslen) < 0) {
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 */
sa.sin_port = 0;
size = sizeof(sa);
-
+
if(bind(portsock, (struct sockaddr *)&sa, size) >= 0) {
/* we succeeded to bind */
struct sockaddr_in add;
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;
#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,
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]);
* "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],
/* we should use the same host we already are connected to */
newhostp = conn->host.name;
}
- }
+ }
else
ptr=NULL;
}
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 {
}
connectport = newport; /* we connect to the remote port */
}
-
+
result = Curl_connecthost(conn,
addr,
connectport,
* 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,
/* 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;
}
SECONDARYSOCKET, bytecountp);
if(result)
return result;
-
+
}
else if(!conn->bits.no_body) {
/* Retrieve file or directory */
ftp->no_transfer = TRUE;
return CURLE_OK;
}
-
+
/* Set resume file transfer offset */
infof(data, "Instructs server to resume from offset %" FORMAT_OFF_T
"\n",
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. */
/* get the number! */
size = curlx_strtoofft(bytes, NULL, 0);
}
-
+
}
}
else if(downloadsize > -1)
return CURLE_FTP_COULDNT_RETR_FILE;
}
}
-
+
}
/* end of transfer */
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);
if(CURLE_OK == result && *connected)
infof(data, "Connected the data stream with PASV!\n");
}
-
+
return result;
}
* 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;
}
va_start(ap, fmt);
vsnprintf(s, 250, fmt, ap);
va_end(ap);
-
+
strcat(s, "\r\n"); /* append a trailing CRLF */
bytes_written=0;
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;
* 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.
*
result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
if(result)
return result;
-
+
switch(ftpcode) {
case 257:
/* success! */
*
* 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) {
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) {
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 */