* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2010, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2015, 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
*
***************************************************************************/
-#include "setup.h"
+#include "curl_setup.h"
#ifndef CURL_DISABLE_FTP
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <ctype.h>
-
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#ifdef HAVE_SYS_SOCKET_H
-#include <sys/socket.h>
-#endif
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#include <curl/curl.h>
#include "urldata.h"
#include "sendf.h"
-#include "easyif.h" /* for Curl_convert_... prototypes */
-
#include "if2ip.h"
#include "hostip.h"
#include "progress.h"
#include "ftp.h"
#include "fileinfo.h"
#include "ftplistparser.h"
-
-#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
-#include "krb4.h"
-#endif
-
+#include "curl_sec.h"
#include "strtoofft.h"
#include "strequal.h"
-#include "sslgen.h"
+#include "vtls/vtls.h"
#include "connect.h"
#include "strerror.h"
#include "inet_ntop.h"
#include "rawstr.h"
#include "speedcheck.h"
#include "warnless.h"
+#include "http_proxy.h"
+#include "non-ascii.h"
#define _MPRINTF_REPLACE /* use our functions only */
#include <curl/mprintf.h>
#endif
#ifdef CURL_DISABLE_VERBOSE_STRINGS
-#define ftp_pasv_verbose(a,b,c,d) do { } while(0)
+#define ftp_pasv_verbose(a,b,c,d) Curl_nop_stmt
#endif
/* Local API functions */
+#ifndef DEBUGBUILD
+static void _state(struct connectdata *conn,
+ ftpstate newstate);
+#define state(x,y) _state(x,y)
+#else
+static void _state(struct connectdata *conn,
+ ftpstate newstate,
+ int lineno);
+#define state(x,y) _state(x,y,__LINE__)
+#endif
+
static CURLcode ftp_sendquote(struct connectdata *conn,
struct curl_slist *quote);
static CURLcode ftp_quit(struct connectdata *conn);
char *newhost, /* ascii version */
int port);
#endif
-static CURLcode ftp_state_post_rest(struct connectdata *conn);
-static CURLcode ftp_state_post_cwd(struct connectdata *conn);
+static CURLcode ftp_state_prepare_transfer(struct connectdata *conn);
+static CURLcode ftp_state_mdtm(struct connectdata *conn);
static CURLcode ftp_state_quote(struct connectdata *conn,
bool init, ftpstate instate);
static CURLcode ftp_nb_type(struct connectdata *conn,
CURLcode, bool premature);
static CURLcode ftp_connect(struct connectdata *conn, bool *done);
static CURLcode ftp_disconnect(struct connectdata *conn, bool dead_connection);
-static CURLcode ftp_nextconnect(struct connectdata *conn);
+static CURLcode ftp_do_more(struct connectdata *conn, int *completed);
static CURLcode ftp_multi_statemach(struct connectdata *conn, bool *done);
-static int ftp_getsock(struct connectdata *conn,
- curl_socket_t *socks,
+static int ftp_getsock(struct connectdata *conn, curl_socket_t *socks,
int numsocks);
+static int ftp_domore_getsock(struct connectdata *conn, curl_socket_t *socks,
+ int numsocks);
static CURLcode ftp_doing(struct connectdata *conn,
bool *dophase_done);
static CURLcode ftp_setup_connection(struct connectdata * conn);
static void wc_data_dtor(void *ptr);
-static CURLcode ftp_state_post_retr_size(struct connectdata *conn,
- curl_off_t filesize);
+static CURLcode ftp_state_retr(struct connectdata *conn, curl_off_t filesize);
+
+static CURLcode ftp_readresp(curl_socket_t sockfd,
+ struct pingpong *pp,
+ int *ftpcode,
+ size_t *size);
+static CURLcode ftp_dophase_done(struct connectdata *conn,
+ bool connected);
/* easy-to-use macro: */
-#define FTPSENDF(x,y,z) if((result = Curl_ftpsendf(x,y,z)) != CURLE_OK) \
- return result
-#define PPSENDF(x,y,z) if((result = Curl_pp_sendf(x,y,z)) != CURLE_OK) \
+#define PPSENDF(x,y,z) if((result = Curl_pp_sendf(x,y,z))) \
return result
ftp_setup_connection, /* setup_connection */
ftp_do, /* do_it */
ftp_done, /* done */
- ftp_nextconnect, /* do_more */
+ ftp_do_more, /* do_more */
ftp_connect, /* connect_it */
ftp_multi_statemach, /* connecting */
ftp_doing, /* doing */
ftp_getsock, /* proto_getsock */
ftp_getsock, /* doing_getsock */
+ ftp_domore_getsock, /* domore_getsock */
ZERO_NULL, /* perform_getsock */
ftp_disconnect, /* disconnect */
+ ZERO_NULL, /* readwrite */
PORT_FTP, /* defport */
- PROT_FTP /* protocol */
+ CURLPROTO_FTP, /* protocol */
+ PROTOPT_DUAL | PROTOPT_CLOSEACTION | PROTOPT_NEEDSPWD
+ | PROTOPT_NOURLQUERY /* flags */
};
ftp_setup_connection, /* setup_connection */
ftp_do, /* do_it */
ftp_done, /* done */
- ftp_nextconnect, /* do_more */
+ ftp_do_more, /* do_more */
ftp_connect, /* connect_it */
ftp_multi_statemach, /* connecting */
ftp_doing, /* doing */
ftp_getsock, /* proto_getsock */
ftp_getsock, /* doing_getsock */
+ ftp_domore_getsock, /* domore_getsock */
ZERO_NULL, /* perform_getsock */
ftp_disconnect, /* disconnect */
+ ZERO_NULL, /* readwrite */
PORT_FTPS, /* defport */
- PROT_FTP | PROT_FTPS | PROT_SSL /* protocol */
+ CURLPROTO_FTPS, /* protocol */
+ PROTOPT_SSL | PROTOPT_DUAL | PROTOPT_CLOSEACTION |
+ PROTOPT_NEEDSPWD | PROTOPT_NOURLQUERY /* flags */
};
#endif
static const struct Curl_handler Curl_handler_ftp_proxy = {
"FTP", /* scheme */
- ZERO_NULL, /* setup_connection */
+ Curl_http_setup_conn, /* setup_connection */
Curl_http, /* do_it */
Curl_http_done, /* done */
ZERO_NULL, /* do_more */
ZERO_NULL, /* doing */
ZERO_NULL, /* proto_getsock */
ZERO_NULL, /* doing_getsock */
+ ZERO_NULL, /* domore_getsock */
ZERO_NULL, /* perform_getsock */
ZERO_NULL, /* disconnect */
+ ZERO_NULL, /* readwrite */
PORT_FTP, /* defport */
- PROT_HTTP /* protocol */
+ CURLPROTO_HTTP, /* protocol */
+ PROTOPT_NONE /* flags */
};
static const struct Curl_handler Curl_handler_ftps_proxy = {
"FTPS", /* scheme */
- ZERO_NULL, /* setup_connection */
+ Curl_http_setup_conn, /* setup_connection */
Curl_http, /* do_it */
Curl_http_done, /* done */
ZERO_NULL, /* do_more */
ZERO_NULL, /* doing */
ZERO_NULL, /* proto_getsock */
ZERO_NULL, /* doing_getsock */
+ ZERO_NULL, /* domore_getsock */
ZERO_NULL, /* perform_getsock */
ZERO_NULL, /* disconnect */
+ ZERO_NULL, /* readwrite */
PORT_FTPS, /* defport */
- PROT_HTTP /* protocol */
+ CURLPROTO_HTTP, /* protocol */
+ PROTOPT_NONE /* flags */
};
#endif
#endif
{
int i;
if(ftpc->dirs) {
- for (i=0; i < ftpc->dirdepth; i++){
+ for(i=0; i < ftpc->dirdepth; i++) {
if(ftpc->dirs[i]) {
free(ftpc->dirs[i]);
ftpc->dirs[i]=NULL;
ftpc->dirs = NULL;
ftpc->dirdepth = 0;
}
- if(ftpc->file) {
- free(ftpc->file);
- ftpc->file = NULL;
- }
+ Curl_safefree(ftpc->file);
+
+ /* no longer of any use */
+ Curl_safefree(ftpc->newhost);
}
/* Returns non-zero if the given string contains CR (\r) or LF (\n),
*/
static bool isBadFtpString(const char *string)
{
- return (bool)((NULL != strchr(string, '\r')) ||
- (NULL != strchr(string, '\n')));
+ return ((NULL != strchr(string, '\r')) ||
+ (NULL != strchr(string, '\n'))) ? TRUE : FALSE;
}
/***********************************************************************
*
- * AllowServerConnect()
+ * AcceptServerConnect()
*
- * When we've issue the PORT command, we have told the server to connect
- * to us. This function will sit and wait here until the server has
- * connected.
+ * After connection request is received from the server this function is
+ * called to accept the connection and close the listening socket
*
*/
-static CURLcode AllowServerConnect(struct connectdata *conn)
+static CURLcode AcceptServerConnect(struct connectdata *conn)
{
struct SessionHandle *data = conn->data;
curl_socket_t sock = conn->sock[SECONDARYSOCKET];
- long timeout_ms;
- long interval_ms;
curl_socket_t s = CURL_SOCKET_BAD;
#ifdef ENABLE_IPV6
struct Curl_sockaddr_storage add;
#endif
curl_socklen_t size = (curl_socklen_t) sizeof(add);
- for(;;) {
- timeout_ms = Curl_timeleft(conn, NULL, TRUE);
+ if(0 == getsockname(sock, (struct sockaddr *) &add, &size)) {
+ size = sizeof(add);
+
+ s=accept(sock, (struct sockaddr *) &add, &size);
+ }
+ Curl_closesocket(conn, sock); /* close the first socket */
- if(timeout_ms < 0) {
- /* if a timeout was already reached, bail out */
- failf(data, "Timeout while waiting for server connect");
- return CURLE_OPERATION_TIMEDOUT;
+ if(CURL_SOCKET_BAD == s) {
+ failf(data, "Error accept()ing server connect");
+ return CURLE_FTP_PORT_FAILED;
+ }
+ infof(data, "Connection accepted from server\n");
+
+ conn->sock[SECONDARYSOCKET] = s;
+ (void)curlx_nonblock(s, TRUE); /* enable non-blocking */
+ conn->sock_accepted[SECONDARYSOCKET] = TRUE;
+
+ if(data->set.fsockopt) {
+ int error = 0;
+
+ /* activate callback for setting socket options */
+ error = data->set.fsockopt(data->set.sockopt_client,
+ s,
+ CURLSOCKTYPE_ACCEPT);
+
+ if(error) {
+ Curl_closesocket(conn, s); /* close the socket and bail out */
+ conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
+ return CURLE_ABORTED_BY_CALLBACK;
}
+ }
- interval_ms = 1000; /* use 1 second timeout intervals */
- if(timeout_ms < interval_ms)
- interval_ms = timeout_ms;
+ return CURLE_OK;
- switch (Curl_socket_ready(sock, CURL_SOCKET_BAD, (int)interval_ms)) {
- case -1: /* error */
- /* let's die here */
- failf(data, "Error while waiting for server connect");
- return CURLE_FTP_PORT_FAILED;
- case 0: /* timeout */
- break; /* loop */
- default:
- /* we have received data here */
- if(0 == getsockname(sock, (struct sockaddr *) &add, &size)) {
- size = sizeof(add);
+}
- s=accept(sock, (struct sockaddr *) &add, &size);
- }
- sclose(sock); /* close the first socket */
+/*
+ * ftp_timeleft_accept() returns the amount of milliseconds left allowed for
+ * waiting server to connect. If the value is negative, the timeout time has
+ * already elapsed.
+ *
+ * The start time is stored in progress.t_acceptdata - as set with
+ * Curl_pgrsTime(..., TIMER_STARTACCEPT);
+ *
+ */
+static long ftp_timeleft_accept(struct SessionHandle *data)
+{
+ long timeout_ms = DEFAULT_ACCEPT_TIMEOUT;
+ long other;
+ struct timeval now;
- if(CURL_SOCKET_BAD == s) {
- failf(data, "Error accept()ing server connect");
- return CURLE_FTP_PORT_FAILED;
- }
- infof(data, "Connection accepted from server\n");
+ if(data->set.accepttimeout > 0)
+ timeout_ms = data->set.accepttimeout;
- conn->sock[SECONDARYSOCKET] = s;
- curlx_nonblock(s, TRUE); /* enable non-blocking */
- return CURLE_OK;
- } /* switch() */
+ now = Curl_tvnow();
+
+ /* check if the generic timeout possibly is set shorter */
+ other = Curl_timeleft(data, &now, FALSE);
+ if(other && (other < timeout_ms))
+ /* note that this also works fine for when other happens to be negative
+ due to it already having elapsed */
+ timeout_ms = other;
+ else {
+ /* subtract elapsed time */
+ timeout_ms -= Curl_tvdiff(now, data->progress.t_acceptdata);
+ if(!timeout_ms)
+ /* avoid returning 0 as that means no timeout! */
+ return -1;
+ }
+
+ return timeout_ms;
+}
+
+
+/***********************************************************************
+ *
+ * ReceivedServerConnect()
+ *
+ * After allowing server to connect to us from data port, this function
+ * checks both data connection for connection establishment and ctrl
+ * connection for a negative response regarding a failure in connecting
+ *
+ */
+static CURLcode ReceivedServerConnect(struct connectdata *conn, bool *received)
+{
+ struct SessionHandle *data = conn->data;
+ curl_socket_t ctrl_sock = conn->sock[FIRSTSOCKET];
+ curl_socket_t data_sock = conn->sock[SECONDARYSOCKET];
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+ struct pingpong *pp = &ftpc->pp;
+ int result;
+ long timeout_ms;
+ ssize_t nread;
+ int ftpcode;
+
+ *received = FALSE;
+
+ timeout_ms = ftp_timeleft_accept(data);
+ infof(data, "Checking for server connect\n");
+ if(timeout_ms < 0) {
+ /* if a timeout was already reached, bail out */
+ failf(data, "Accept timeout occurred while waiting server connect");
+ return CURLE_FTP_ACCEPT_TIMEOUT;
}
- /* never reaches this point */
+
+ /* First check whether there is a cached response from server */
+ if(pp->cache_size && pp->cache && pp->cache[0] > '3') {
+ /* Data connection could not be established, let's return */
+ infof(data, "There is negative response in cache while serv connect\n");
+ Curl_GetFTPResponse(&nread, conn, &ftpcode);
+ return CURLE_FTP_ACCEPT_FAILED;
+ }
+
+ result = Curl_socket_check(ctrl_sock, data_sock, CURL_SOCKET_BAD, 0);
+
+ /* see if the connection request is already here */
+ switch (result) {
+ case -1: /* error */
+ /* let's die here */
+ failf(data, "Error while waiting for server connect");
+ return CURLE_FTP_ACCEPT_FAILED;
+ case 0: /* Server connect is not received yet */
+ break; /* loop */
+ default:
+
+ if(result & CURL_CSELECT_IN2) {
+ infof(data, "Ready to accept data connection from server\n");
+ *received = TRUE;
+ }
+ else if(result & CURL_CSELECT_IN) {
+ infof(data, "Ctrl conn has data while waiting for data conn\n");
+ Curl_GetFTPResponse(&nread, conn, &ftpcode);
+
+ if(ftpcode/100 > 3)
+ return CURLE_FTP_ACCEPT_FAILED;
+
+ return CURLE_FTP_WEIRD_SERVER_REPLY;
+ }
+
+ break;
+ } /* switch() */
+
+ return CURLE_OK;
+}
+
+
+/***********************************************************************
+ *
+ * InitiateTransfer()
+ *
+ * After connection from server is accepted this function is called to
+ * setup transfer parameters and initiate the data transfer.
+ *
+ */
+static CURLcode InitiateTransfer(struct connectdata *conn)
+{
+ struct SessionHandle *data = conn->data;
+ struct FTP *ftp = data->req.protop;
+ CURLcode result = CURLE_OK;
+
+ if(conn->ssl[SECONDARYSOCKET].use) {
+ /* since we only have a plaintext TCP connection here, we must now
+ * do the TLS stuff */
+ infof(data, "Doing the SSL/TLS handshake on the data stream\n");
+ result = Curl_ssl_connect(conn, SECONDARYSOCKET);
+ if(result)
+ return result;
+ }
+
+ if(conn->proto.ftpc.state_saved == FTP_STOR) {
+ *(ftp->bytecountp)=0;
+
+ /* When we know we're uploading a specified file, we can get the file
+ size prior to the actual upload. */
+
+ Curl_pgrsSetUploadSize(data, data->state.infilesize);
+
+ /* set the SO_SNDBUF for the secondary socket for those who need it */
+ Curl_sndbufset(conn->sock[SECONDARYSOCKET]);
+
+ Curl_setup_transfer(conn, -1, -1, FALSE, NULL, /* no download */
+ SECONDARYSOCKET, ftp->bytecountp);
+ }
+ else {
+ /* FTP download: */
+ Curl_setup_transfer(conn, SECONDARYSOCKET,
+ conn->proto.ftpc.retr_size_saved, FALSE,
+ ftp->bytecountp, -1, NULL); /* no upload here */
+ }
+
+ conn->proto.ftpc.pp.pending_resp = TRUE; /* expect server response */
+ state(conn, FTP_STOP);
+
+ return CURLE_OK;
+}
+
+/***********************************************************************
+ *
+ * AllowServerConnect()
+ *
+ * When we've issue the PORT command, we have told the server to connect to
+ * us. This function checks whether data connection is established if so it is
+ * accepted.
+ *
+ */
+static CURLcode AllowServerConnect(struct connectdata *conn, bool *connected)
+{
+ struct SessionHandle *data = conn->data;
+ long timeout_ms;
+ CURLcode result = CURLE_OK;
+
+ *connected = FALSE;
+ infof(data, "Preparing for accepting server on data port\n");
+
+ /* Save the time we start accepting server connect */
+ Curl_pgrsTime(data, TIMER_STARTACCEPT);
+
+ timeout_ms = ftp_timeleft_accept(data);
+ if(timeout_ms < 0) {
+ /* if a timeout was already reached, bail out */
+ failf(data, "Accept timeout occurred while waiting server connect");
+ return CURLE_FTP_ACCEPT_TIMEOUT;
+ }
+
+ /* see if the connection request is already here */
+ result = ReceivedServerConnect(conn, connected);
+ if(result)
+ return result;
+
+ if(*connected) {
+ result = AcceptServerConnect(conn);
+ if(result)
+ return result;
+
+ result = InitiateTransfer(conn);
+ if(result)
+ return result;
+ }
+ else {
+ /* Add timeout to multi handle and break out of the loop */
+ if(!result && *connected == FALSE) {
+ if(data->set.accepttimeout > 0)
+ Curl_expire(data, data->set.accepttimeout);
+ else
+ Curl_expire(data, DEFAULT_ACCEPT_TIMEOUT);
+ }
+ }
+
+ return result;
}
/* macro to check for a three-digit ftp status code at the start of the
/* macro to check for the last line in an FTP server response */
#define LASTLINE(line) (STATUSCODE(line) && (' ' == line[3]))
-static int ftp_endofresp(struct pingpong *pp,
- int *code)
+static bool ftp_endofresp(struct connectdata *conn, char *line, size_t len,
+ int *code)
{
- char *line = pp->linestart_resp;
- size_t len = pp->nread_resp;
+ (void)conn;
if((len > 3) && LASTLINE(line)) {
*code = curlx_sltosi(strtol(line, NULL, 10));
- return 1;
+ return TRUE;
}
- return 0;
+
+ return FALSE;
}
static CURLcode ftp_readresp(curl_socket_t sockfd,
size_t *size) /* size of the response */
{
struct connectdata *conn = pp->conn;
-#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
struct SessionHandle *data = conn->data;
+#ifdef HAVE_GSSAPI
char * const buf = data->state.buffer;
#endif
CURLcode result = CURLE_OK;
result = Curl_pp_readresp(sockfd, pp, &code, size);
-#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
+#if defined(HAVE_GSSAPI)
/* handle the security-oriented responses 6xx ***/
/* FIXME: some errorchecking perhaps... ***/
switch(code) {
#endif
/* store the latest code for later retrieval */
- conn->data->info.httpcode=code;
+ data->info.httpcode=code;
if(ftpcode)
*ftpcode = code;
- if(421 == code)
+ if(421 == code) {
/* 421 means "Service not available, closing control connection." and FTP
* servers use it to signal that idle session timeout has been exceeded.
- * If we ignored the response, it could end up hanging in some cases. */
+ * If we ignored the response, it could end up hanging in some cases.
+ *
+ * This response code can come at any point so having it treated
+ * generically is a good idea.
+ */
+ infof(data, "We got a 421 - timeout!\n");
+ state(conn, FTP_STOP);
return CURLE_OPERATION_TIMEDOUT;
+ }
return result;
}
*/
}
else {
- switch (Curl_socket_ready(sockfd, CURL_SOCKET_BAD, (int)interval_ms)) {
+ switch (Curl_socket_ready(sockfd, CURL_SOCKET_BAD, interval_ms)) {
case -1: /* select() error, stop reading */
failf(data, "FTP response aborted due to select/poll error: %d",
SOCKERRNO);
return result;
}
-/* This is the ONLY way to change FTP state! */
-static void state(struct connectdata *conn,
- ftpstate newstate)
-{
#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
/* for debug purposes */
- static const char * const names[]={
- "STOP",
- "WAIT220",
- "AUTH",
- "USER",
- "PASS",
- "ACCT",
- "PBSZ",
- "PROT",
- "CCC",
- "PWD",
- "SYST",
- "NAMEFMT",
- "QUOTE",
- "RETR_PREQUOTE",
- "STOR_PREQUOTE",
- "POSTQUOTE",
- "CWD",
- "MKD",
- "MDTM",
- "TYPE",
- "LIST_TYPE",
- "RETR_TYPE",
- "STOR_TYPE",
- "SIZE",
- "RETR_SIZE",
- "STOR_SIZE",
- "REST",
- "RETR_REST",
- "PORT",
- "PRET",
- "PASV",
- "LIST",
- "RETR",
- "STOR",
- "QUIT"
- };
+static const char * const ftp_state_names[]={
+ "STOP",
+ "WAIT220",
+ "AUTH",
+ "USER",
+ "PASS",
+ "ACCT",
+ "PBSZ",
+ "PROT",
+ "CCC",
+ "PWD",
+ "SYST",
+ "NAMEFMT",
+ "QUOTE",
+ "RETR_PREQUOTE",
+ "STOR_PREQUOTE",
+ "POSTQUOTE",
+ "CWD",
+ "MKD",
+ "MDTM",
+ "TYPE",
+ "LIST_TYPE",
+ "RETR_TYPE",
+ "STOR_TYPE",
+ "SIZE",
+ "RETR_SIZE",
+ "STOR_SIZE",
+ "REST",
+ "RETR_REST",
+ "PORT",
+ "PRET",
+ "PASV",
+ "LIST",
+ "RETR",
+ "STOR",
+ "QUIT"
+};
+#endif
+
+/* This is the ONLY way to change FTP state! */
+static void _state(struct connectdata *conn,
+ ftpstate newstate
+#ifdef DEBUGBUILD
+ , int lineno
#endif
+ )
+{
struct ftp_conn *ftpc = &conn->proto.ftpc;
-#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
+
+#if defined(DEBUGBUILD)
+
+#if defined(CURL_DISABLE_VERBOSE_STRINGS)
+ (void) lineno;
+#else
if(ftpc->state != newstate)
- infof(conn->data, "FTP %p state change from %s to %s\n",
- ftpc, names[ftpc->state], names[newstate]);
+ infof(conn->data, "FTP %p (line %d) state change from %s to %s\n",
+ (void *)ftpc, lineno, ftp_state_names[ftpc->state],
+ ftp_state_names[newstate]);
+#endif
#endif
+
ftpc->state = newstate;
}
static CURLcode ftp_state_user(struct connectdata *conn)
{
CURLcode result;
- struct FTP *ftp = conn->data->state.proto.ftp;
+ struct FTP *ftp = conn->data->req.protop;
/* send USER */
PPSENDF(&conn->proto.ftpc.pp, "USER %s", ftp->user?ftp->user:"");
CURLcode result;
/* send PWD to discover our entry point */
- PPSENDF(&conn->proto.ftpc.pp, "PWD", NULL);
+ PPSENDF(&conn->proto.ftpc.pp, "%s", "PWD");
state(conn, FTP_PWD);
return CURLE_OK;
return Curl_pp_getsock(&conn->proto.ftpc.pp, socks, numsocks);
}
+/* For the FTP "DO_MORE" phase only */
+static int ftp_domore_getsock(struct connectdata *conn, curl_socket_t *socks,
+ int numsocks)
+{
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+
+ if(!numsocks)
+ return GETSOCK_BLANK;
+
+ /* When in DO_MORE state, we could be either waiting for us to connect to a
+ * remote site, or we could wait for that site to connect to us. Or just
+ * handle ordinary commands.
+ */
+
+ if(FTP_STOP == ftpc->state) {
+ int bits = GETSOCK_READSOCK(0);
+
+ /* if stopped and still in this state, then we're also waiting for a
+ connect on the secondary connection */
+ socks[0] = conn->sock[FIRSTSOCKET];
+
+ if(!conn->data->set.ftp_use_port) {
+ int s;
+ int i;
+ /* PORT is used to tell the server to connect to us, and during that we
+ don't do happy eyeballs, but we do if we connect to the server */
+ for(s=1, i=0; i<2; i++) {
+ if(conn->tempsock[i] != CURL_SOCKET_BAD) {
+ socks[s] = conn->tempsock[i];
+ bits |= GETSOCK_WRITESOCK(s++);
+ }
+ }
+ }
+ else {
+ socks[1] = conn->sock[SECONDARYSOCKET];
+ bits |= GETSOCK_WRITESOCK(1);
+ }
+
+ return bits;
+ }
+ else
+ return Curl_pp_getsock(&conn->proto.ftpc.pp, socks, numsocks);
+}
+
/* This is called after the FTP_QUOTE state is passed.
ftp_state_cwd() sends the range of CWD commands to the server to change to
if(ftpc->cwddone)
/* already done and fine */
- result = ftp_state_post_cwd(conn);
+ result = ftp_state_mdtm(conn);
else {
ftpc->count2 = 0; /* count2 counts failed CWDs */
}
else {
/* No CWD necessary */
- result = ftp_state_post_cwd(conn);
+ result = ftp_state_mdtm(conn);
}
}
}
static const char mode[][5] = { "EPRT", "PORT" };
int rc;
int error;
- char *host=NULL;
+ char *host = NULL;
char *string_ftpport = data->set.str[STRING_FTPPORT];
struct Curl_dns_entry *h=NULL;
unsigned short port_min = 0;
unsigned short port_max = 0;
unsigned short port;
+ bool possibly_non_local = TRUE;
char *addr = NULL;
char *port_sep = NULL;
addr = calloc(addrlen+1, 1);
- if (!addr)
+ if(!addr)
return CURLE_OUT_OF_MEMORY;
#ifdef ENABLE_IPV6
}
else
#endif
- if( *string_ftpport == ':') {
+ if(*string_ftpport == ':') {
/* :port */
ip_end = string_ftpport;
}
- else if( (ip_end = strchr(string_ftpport, ':')) != NULL) {
+ else if((ip_end = strchr(string_ftpport, ':')) != NULL) {
/* either ipv6 or (ipv4|domain|interface):port(-range) */
#ifdef ENABLE_IPV6
if(Curl_inet_pton(AF_INET6, string_ftpport, sa6) == 1) {
port_min = port_max = 0;
strcpy(addr, string_ftpport);
ip_end = NULL; /* this got no port ! */
- } else
+ }
+ else
#endif
- {
/* (ipv4|domain|interface):port(-range) */
strncpy(addr, string_ftpport, ip_end - ip_start );
- }
}
else
/* ipv4|interface */
strcpy(addr, string_ftpport);
/* parse the port */
- if( ip_end != NULL ) {
+ if(ip_end != NULL) {
if((port_start = strchr(ip_end, ':')) != NULL) {
port_min = curlx_ultous(strtoul(port_start+1, NULL, 10));
if((port_sep = strchr(port_start, '-')) != NULL) {
if(*addr != '\0') {
/* attempt to get the address of the given interface name */
- if(!Curl_if2ip(conn->ip_addr->ai_family, addr,
- hbuf, sizeof(hbuf)))
- /* not an interface, use the given string as host name instead */
- host = addr;
- else
- host = hbuf; /* use the hbuf for host name */
- }else
+ switch(Curl_if2ip(conn->ip_addr->ai_family,
+ Curl_ipv6_scope(conn->ip_addr->ai_addr),
+ conn->scope_id, addr, hbuf, sizeof(hbuf))) {
+ case IF2IP_NOT_FOUND:
+ /* not an interface, use the given string as host name instead */
+ host = addr;
+ break;
+ case IF2IP_AF_NOT_SUPPORTED:
+ return CURLE_FTP_PORT_FAILED;
+ case IF2IP_FOUND:
+ host = hbuf; /* use the hbuf for host name */
+ }
+ }
+ else
/* there was only a port(-range) given, default the host */
host = NULL;
} /* data->set.ftpport */
if(getsockname(conn->sock[FIRSTSOCKET], sa, &sslen)) {
failf(data, "getsockname() failed: %s",
Curl_strerror(conn, SOCKERRNO) );
- if (addr)
- free(addr);
+ Curl_safefree(addr);
return CURLE_FTP_PORT_FAILED;
}
- switch(sa->sa_family)
- {
+ switch(sa->sa_family) {
#ifdef ENABLE_IPV6
- case AF_INET6:
- Curl_inet_ntop(sa->sa_family, &sa6->sin6_addr, hbuf, sizeof(hbuf));
- break;
+ case AF_INET6:
+ Curl_inet_ntop(sa->sa_family, &sa6->sin6_addr, hbuf, sizeof(hbuf));
+ break;
#endif
- default:
- Curl_inet_ntop(sa->sa_family, &sa4->sin_addr, hbuf, sizeof(hbuf));
- break;
+ default:
+ Curl_inet_ntop(sa->sa_family, &sa4->sin_addr, hbuf, sizeof(hbuf));
+ break;
}
host = hbuf; /* use this host name */
+ possibly_non_local = FALSE; /* we know it is local now */
}
/* resolv ip/host to ip */
rc = Curl_resolv(conn, host, 0, &h);
if(rc == CURLRESOLV_PENDING)
- (void)Curl_wait_for_resolv(conn, &h);
+ (void)Curl_resolver_wait_resolv(conn, &h);
if(h) {
res = h->addr;
/* when we return from this function, we can forget about this entry
else
res = NULL; /* failure! */
- if (addr)
- free(addr);
-
- if (res == NULL) {
- failf(data, "Curl_resolv failed, we can not recover!");
+ if(res == NULL) {
+ failf(data, "failed to resolve the address provided to PORT: %s", host);
+ Curl_safefree(addr);
return CURLE_FTP_PORT_FAILED;
}
+ Curl_safefree(addr);
+ host = NULL;
+
/* step 2, create a socket for the requested address */
portsock = CURL_SOCKET_BAD;
error = 0;
- for (ai = res; ai; ai = ai->ai_next) {
- /*
- * Workaround for AIX5 getaddrinfo() problem (it doesn't set ai_socktype):
- */
- if(ai->ai_socktype == 0)
- ai->ai_socktype = conn->socktype;
-
- portsock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
- if(portsock == CURL_SOCKET_BAD) {
+ for(ai = res; ai; ai = ai->ai_next) {
+ result = Curl_socket(conn, ai, NULL, &portsock);
+ if(result) {
error = SOCKERRNO;
continue;
}
memcpy(sa, ai->ai_addr, ai->ai_addrlen);
sslen = ai->ai_addrlen;
- for( port = port_min; port <= port_max; ) {
- if( sa->sa_family == AF_INET )
+ for(port = port_min; port <= port_max;) {
+ if(sa->sa_family == AF_INET)
sa4->sin_port = htons(port);
#ifdef ENABLE_IPV6
else
if(bind(portsock, sa, sslen) ) {
/* It failed. */
error = SOCKERRNO;
- if(error == EADDRNOTAVAIL) {
-
+ if(possibly_non_local && (error == EADDRNOTAVAIL)) {
/* The requested bind address is not local. Use the address used for
* the control connection instead and restart the port loop
*/
- failf(data, "bind(port=%hu) failed: %s", port,
+
+ infof(data, "bind(port=%hu) on non-local address failed: %s\n", port,
Curl_strerror(conn, error) );
sslen = sizeof(ss);
if(getsockname(conn->sock[FIRSTSOCKET], sa, &sslen)) {
failf(data, "getsockname() failed: %s",
Curl_strerror(conn, SOCKERRNO) );
- sclose(portsock);
+ Curl_closesocket(conn, portsock);
return CURLE_FTP_PORT_FAILED;
}
port = port_min;
+ possibly_non_local = FALSE; /* don't try this again */
continue;
}
else if(error != EADDRINUSE && error != EACCES) {
failf(data, "bind(port=%hu) failed: %s", port,
Curl_strerror(conn, error) );
- sclose(portsock);
+ Curl_closesocket(conn, portsock);
return CURLE_FTP_PORT_FAILED;
}
}
}
/* maybe all ports were in use already*/
- if (port > port_max) {
+ if(port > port_max) {
failf(data, "bind() failed, we ran out of ports!");
- sclose(portsock);
+ Curl_closesocket(conn, portsock);
return CURLE_FTP_PORT_FAILED;
}
if(getsockname(portsock, (struct sockaddr *)sa, &sslen)) {
failf(data, "getsockname() failed: %s",
Curl_strerror(conn, SOCKERRNO) );
- sclose(portsock);
+ Curl_closesocket(conn, portsock);
return CURLE_FTP_PORT_FAILED;
}
if(listen(portsock, 1)) {
failf(data, "socket failure: %s", Curl_strerror(conn, SOCKERRNO));
- sclose(portsock);
+ Curl_closesocket(conn, portsock);
return CURLE_FTP_PORT_FAILED;
}
conn->bits.ftp_use_eprt = TRUE;
#endif
- for (; fcmd != DONE; fcmd++) {
+ for(; fcmd != DONE; fcmd++) {
if(!conn->bits.ftp_use_eprt && (EPRT == fcmd))
/* if disabled, goto next */
continue;
if((PORT == fcmd) && sa->sa_family != AF_INET)
- /* PORT is ipv4 only */
+ /* PORT is IPv4 only */
continue;
- switch (sa->sa_family) {
+ switch(sa->sa_family) {
case AF_INET:
port = ntohs(sa4->sin_port);
break;
result = Curl_pp_sendf(&ftpc->pp, "%s |%d|%s|%hu|", mode[fcmd],
sa->sa_family == AF_INET?1:2,
myhost, port);
- if(result)
+ if(result) {
+ failf(data, "Failure sending EPRT command: %s",
+ curl_easy_strerror(result));
+ Curl_closesocket(conn, portsock);
+ /* don't retry using PORT */
+ ftpc->count1 = PORT;
+ /* bail out */
+ state(conn, FTP_STOP);
return result;
+ }
break;
}
else if(PORT == fcmd) {
snprintf(dest, 20, ",%d,%d", (int)(port>>8), (int)(port&0xff));
result = Curl_pp_sendf(&ftpc->pp, "%s %s", mode[fcmd], tmp);
- if(result)
+ if(result) {
+ failf(data, "Failure sending PORT command: %s",
+ curl_easy_strerror(result));
+ Curl_closesocket(conn, portsock);
+ /* bail out */
+ state(conn, FTP_STOP);
return result;
+ }
break;
}
}
the cleanup function will close it in case we fail before the true
secondary stuff is made */
if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET])
- sclose(conn->sock[SECONDARYSOCKET]);
+ Curl_closesocket(conn, conn->sock[SECONDARYSOCKET]);
conn->sock[SECONDARYSOCKET] = portsock;
/* this tcpconnect assignment below is a hackish work-around to make the
The *proper* fix is to make sure that the active connection from the
server is done in a non-blocking way. Currently, it is still BLOCKING.
*/
- conn->bits.tcpconnect = TRUE;
+ conn->bits.tcpconnect[SECONDARYSOCKET] = TRUE;
state(conn, FTP_PORT);
return result;
return result;
}
-/* REST is the last command in the chain of commands when a "head"-like
- request is made. Thus, if an actual transfer is to be made this is where
- we take off for real. */
-static CURLcode ftp_state_post_rest(struct connectdata *conn)
+/*
+ * ftp_state_prepare_transfer() starts PORT, PASV or PRET etc.
+ *
+ * REST is the last command in the chain of commands when a "head"-like
+ * request is made. Thus, if an actual transfer is to be made this is where we
+ * take off for real.
+ */
+static CURLcode ftp_state_prepare_transfer(struct connectdata *conn)
{
CURLcode result = CURLE_OK;
- struct FTP *ftp = conn->data->state.proto.ftp;
+ struct FTP *ftp = conn->data->req.protop;
struct SessionHandle *data = conn->data;
if(ftp->transfer != FTPTRANSFER_BODY) {
return result;
}
-static CURLcode ftp_state_post_size(struct connectdata *conn)
+static CURLcode ftp_state_rest(struct connectdata *conn)
{
CURLcode result = CURLE_OK;
- struct FTP *ftp = conn->data->state.proto.ftp;
+ struct FTP *ftp = conn->data->req.protop;
struct ftp_conn *ftpc = &conn->proto.ftpc;
if((ftp->transfer != FTPTRANSFER_BODY) && ftpc->file) {
state(conn, FTP_REST);
}
else
- result = ftp_state_post_rest(conn);
+ result = ftp_state_prepare_transfer(conn);
return result;
}
-static CURLcode ftp_state_post_type(struct connectdata *conn)
+static CURLcode ftp_state_size(struct connectdata *conn)
{
CURLcode result = CURLE_OK;
- struct FTP *ftp = conn->data->state.proto.ftp;
+ struct FTP *ftp = conn->data->req.protop;
struct ftp_conn *ftpc = &conn->proto.ftpc;
if((ftp->transfer == FTPTRANSFER_INFO) && ftpc->file) {
state(conn, FTP_SIZE);
}
else
- result = ftp_state_post_size(conn);
+ result = ftp_state_rest(conn);
return result;
}
-static CURLcode ftp_state_post_listtype(struct connectdata *conn)
+static CURLcode ftp_state_list(struct connectdata *conn)
{
CURLcode result = CURLE_OK;
struct SessionHandle *data = conn->data;
return CURLE_OUT_OF_MEMORY;
}
- PPSENDF(&conn->proto.ftpc.pp, "%s",cmd);
+ result = Curl_pp_sendf(&conn->proto.ftpc.pp, "%s", cmd);
if(lstArg)
free(lstArg);
free(cmd);
+ if(result)
+ return result;
+
state(conn, FTP_LIST);
return result;
}
-static CURLcode ftp_state_post_retrtype(struct connectdata *conn)
+static CURLcode ftp_state_retr_prequote(struct connectdata *conn)
{
CURLcode result = CURLE_OK;
return result;
}
-static CURLcode ftp_state_post_stortype(struct connectdata *conn)
+static CURLcode ftp_state_stor_prequote(struct connectdata *conn)
{
CURLcode result = CURLE_OK;
return result;
}
-static CURLcode ftp_state_post_mdtm(struct connectdata *conn)
+static CURLcode ftp_state_type(struct connectdata *conn)
{
CURLcode result = CURLE_OK;
- struct FTP *ftp = conn->data->state.proto.ftp;
+ struct FTP *ftp = conn->data->req.protop;
struct SessionHandle *data = conn->data;
struct ftp_conn *ftpc = &conn->proto.ftpc;
return result;
}
else
- result = ftp_state_post_type(conn);
+ result = ftp_state_size(conn);
return result;
}
/* This is called after the CWD commands have been done in the beginning of
the DO phase */
-static CURLcode ftp_state_post_cwd(struct connectdata *conn)
+static CURLcode ftp_state_mdtm(struct connectdata *conn)
{
CURLcode result = CURLE_OK;
struct SessionHandle *data = conn->data;
state(conn, FTP_MDTM);
}
else
- result = ftp_state_post_mdtm(conn);
+ result = ftp_state_type(conn);
return result;
}
bool sizechecked)
{
CURLcode result = CURLE_OK;
- struct FTP *ftp = conn->data->state.proto.ftp;
+ struct FTP *ftp = conn->data->req.protop;
struct SessionHandle *data = conn->data;
struct ftp_conn *ftpc = &conn->proto.ftpc;
int seekerr = CURL_SEEKFUNC_OK;
}
}
/* now, decrease the size of the read */
- if(data->set.infilesize>0) {
- data->set.infilesize -= data->state.resume_from;
+ if(data->state.infilesize>0) {
+ data->state.infilesize -= data->state.resume_from;
- if(data->set.infilesize <= 0) {
+ if(data->state.infilesize <= 0) {
infof(data, "File already completely uploaded\n");
/* no data to transfer */
{
CURLcode result = CURLE_OK;
struct SessionHandle *data = conn->data;
- struct FTP *ftp = data->state.proto.ftp;
+ struct FTP *ftp = data->req.protop;
struct ftp_conn *ftpc = &conn->proto.ftpc;
bool quote=FALSE;
struct curl_slist *item;
else {
if(ftpc->known_filesize != -1) {
Curl_pgrsSetDownloadSize(data, ftpc->known_filesize);
- result = ftp_state_post_retr_size(conn, ftpc->known_filesize);
+ result = ftp_state_retr(conn, ftpc->known_filesize);
}
else {
PPSENDF(&ftpc->pp, "SIZE %s", ftpc->file);
return result;
}
+/* called from ftp_state_pasv_resp to switch to PASV in case of EPSV
+ problems */
+static CURLcode ftp_epsv_disable(struct connectdata *conn)
+{
+ CURLcode result = CURLE_OK;
+
+ if(conn->bits.ipv6) {
+ /* We can't disable EPSV when doing IPv6, so this is instead a fail */
+ failf(conn->data, "Failed EPSV attempt, exiting\n");
+ return CURLE_FTP_WEIRD_SERVER_REPLY;
+ }
+
+ infof(conn->data, "Failed EPSV attempt. Disabling EPSV\n");
+ /* disable it for next transfer */
+ conn->bits.ftp_use_epsv = FALSE;
+ conn->data->state.errorbuf = FALSE; /* allow error message to get
+ rewritten */
+ PPSENDF(&conn->proto.ftpc.pp, "%s", "PASV");
+ conn->proto.ftpc.count1++;
+ /* remain in/go to the FTP_PASV state */
+ state(conn, FTP_PASV);
+ return result;
+}
+
+/*
+ * Perform the necessary magic that needs to be done once the TCP connection
+ * to the proxy has completed.
+ */
+static CURLcode proxy_magic(struct connectdata *conn,
+ char *newhost, unsigned short newport,
+ bool *magicdone)
+{
+ CURLcode result = CURLE_OK;
+ struct SessionHandle *data = conn->data;
+
+#if defined(CURL_DISABLE_PROXY)
+ (void) newhost;
+ (void) newport;
+#endif
+
+ *magicdone = FALSE;
+
+ switch(conn->proxytype) {
+ case CURLPROXY_SOCKS5:
+ case CURLPROXY_SOCKS5_HOSTNAME:
+ result = Curl_SOCKS5(conn->proxyuser, conn->proxypasswd, newhost,
+ newport, SECONDARYSOCKET, conn);
+ *magicdone = TRUE;
+ break;
+ case CURLPROXY_SOCKS4:
+ result = Curl_SOCKS4(conn->proxyuser, newhost, newport,
+ SECONDARYSOCKET, conn, FALSE);
+ *magicdone = TRUE;
+ break;
+ case CURLPROXY_SOCKS4A:
+ result = Curl_SOCKS4(conn->proxyuser, newhost, newport,
+ SECONDARYSOCKET, conn, TRUE);
+ *magicdone = TRUE;
+ break;
+ case CURLPROXY_HTTP:
+ case CURLPROXY_HTTP_1_0:
+ /* do nothing here. handled later. */
+ break;
+ default:
+ failf(data, "unknown proxytype option given");
+ result = CURLE_COULDNT_CONNECT;
+ break;
+ }
+
+ if(conn->bits.tunnel_proxy && conn->bits.httpproxy) {
+ /* BLOCKING */
+ /* We want "seamless" FTP operations through HTTP proxy tunnel */
+
+ /* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the
+ * member conn->proto.http; we want FTP through HTTP and we have to
+ * change the member temporarily for connecting to the HTTP proxy. After
+ * Curl_proxyCONNECT we have to set back the member to the original
+ * struct FTP pointer
+ */
+ struct HTTP http_proxy;
+ struct FTP *ftp_save = data->req.protop;
+ memset(&http_proxy, 0, sizeof(http_proxy));
+ data->req.protop = &http_proxy;
+
+ result = Curl_proxyCONNECT(conn, SECONDARYSOCKET, newhost, newport);
+
+ data->req.protop = ftp_save;
+
+ if(result)
+ return result;
+
+ if(conn->tunnel_state[SECONDARYSOCKET] != TUNNEL_COMPLETE) {
+ /* the CONNECT procedure is not complete, the tunnel is not yet up */
+ state(conn, FTP_STOP); /* this phase is completed */
+ return result;
+ }
+ else
+ *magicdone = TRUE;
+ }
+
+ return result;
+}
+
static CURLcode ftp_state_pasv_resp(struct connectdata *conn,
int ftpcode)
{
struct ftp_conn *ftpc = &conn->proto.ftpc;
CURLcode result;
struct SessionHandle *data=conn->data;
- Curl_addrinfo *conninfo;
struct Curl_dns_entry *addr=NULL;
int rc;
unsigned short connectport; /* the local port connect() should use! */
- unsigned short newport=0; /* remote port */
- bool connected;
-
- /* newhost must be able to hold a full IP-style address in ASCII, which
- in the IPv6 case means 5*8-1 = 39 letters */
-#define NEWHOST_BUFSIZE 48
- char newhost[NEWHOST_BUFSIZE];
char *str=&data->state.buffer[4]; /* start on the first letter */
+ /* if we come here again, make sure the former name is cleared */
+ Curl_safefree(ftpc->newhost);
+
if((ftpc->count1 == 0) &&
(ftpcode == 229)) {
/* positive EPSV response */
break;
}
}
+ if(num > 0xffff) {
+ failf(data, "Illegal port number in EPSV reply");
+ return CURLE_FTP_WEIRD_PASV_REPLY;
+ }
if(ptr) {
- newport = (unsigned short)(num & 0xffff);
-
- if(conn->bits.tunnel_proxy ||
- data->set.proxytype == CURLPROXY_SOCKS5 ||
- data->set.proxytype == CURLPROXY_SOCKS5_HOSTNAME ||
- data->set.proxytype == CURLPROXY_SOCKS4 ||
- data->set.proxytype == CURLPROXY_SOCKS4A)
- /* proxy tunnel -> use other host info because ip_addr_str is the
- proxy address not the ftp host */
- snprintf(newhost, sizeof(newhost), "%s", conn->host.name);
- else
- /* use the same IP we are already connected to */
- snprintf(newhost, NEWHOST_BUFSIZE, "%s", conn->ip_addr_str);
+ ftpc->newport = (unsigned short)(num & 0xffff);
+
+ /* use the original host name again */
+ ftpc->newhost = strdup(conn->host.name);
+ if(!ftpc->newhost)
+ return CURLE_OUT_OF_MEMORY;
}
}
else
/* we got OK from server */
if(data->set.ftp_skip_ip) {
- /* told to ignore the remotely given IP but instead use the one we used
+ /* told to ignore the remotely given IP but instead use the host we used
for the control connection */
- infof(data, "Skips %d.%d.%d.%d for data connection, uses %s instead\n",
+ infof(data, "Skip %d.%d.%d.%d for data connection, re-use %s instead\n",
ip[0], ip[1], ip[2], ip[3],
- conn->ip_addr_str);
- if(conn->bits.tunnel_proxy ||
- data->set.proxytype == CURLPROXY_SOCKS5 ||
- data->set.proxytype == CURLPROXY_SOCKS5_HOSTNAME ||
- data->set.proxytype == CURLPROXY_SOCKS4 ||
- data->set.proxytype == CURLPROXY_SOCKS4A)
- /* proxy tunnel -> use other host info because ip_addr_str is the
- proxy address not the ftp host */
- snprintf(newhost, sizeof(newhost), "%s", conn->host.name);
- else
- snprintf(newhost, sizeof(newhost), "%s", conn->ip_addr_str);
+ conn->host.name);
+
+ /* use the original host name again */
+ ftpc->newhost = strdup(conn->host.name);
}
else
- snprintf(newhost, sizeof(newhost),
- "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
- newport = (unsigned short)(((port[0]<<8) + port[1]) & 0xffff);
+ ftpc->newhost = aprintf("%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
+
+ if(!ftpc->newhost)
+ return CURLE_OUT_OF_MEMORY;
+
+ ftpc->newport = (unsigned short)(((port[0]<<8) + port[1]) & 0xffff);
}
else if(ftpc->count1 == 0) {
/* EPSV failed, move on to PASV */
-
- /* disable it for next transfer */
- conn->bits.ftp_use_epsv = FALSE;
- infof(data, "disabling EPSV usage\n");
-
- PPSENDF(&ftpc->pp, "PASV", NULL);
- ftpc->count1++;
- /* remain in the FTP_PASV state */
- return result;
+ return ftp_epsv_disable(conn);
}
else {
failf(data, "Bad PASV/EPSV response: %03d", ftpcode);
return CURLE_FTP_WEIRD_PASV_REPLY;
}
- if(data->set.str[STRING_PROXY] && *data->set.str[STRING_PROXY]) {
+ if(conn->bits.proxy) {
/*
- * This is a tunnel through a http proxy and we need to connect to the
- * proxy again here.
- *
- * We don't want to rely on a former host lookup that might've expired
- * now, instead we remake the lookup here and now!
+ * This connection uses a proxy and we need to connect to the proxy again
+ * here. We don't want to rely on a former host lookup that might've
+ * expired now, instead we remake the lookup here and now!
*/
rc = Curl_resolv(conn, conn->proxy.name, (int)conn->port, &addr);
if(rc == CURLRESOLV_PENDING)
/* BLOCKING, ignores the return code but 'addr' will be NULL in
case of failure */
- (void)Curl_wait_for_resolv(conn, &addr);
+ (void)Curl_resolver_wait_resolv(conn, &addr);
connectport =
(unsigned short)conn->port; /* we connect to the proxy's port */
}
else {
/* normal, direct, ftp connection */
- rc = Curl_resolv(conn, newhost, newport, &addr);
+ rc = Curl_resolv(conn, ftpc->newhost, ftpc->newport, &addr);
if(rc == CURLRESOLV_PENDING)
/* BLOCKING */
- (void)Curl_wait_for_resolv(conn, &addr);
+ (void)Curl_resolver_wait_resolv(conn, &addr);
- connectport = newport; /* we connect to the remote port */
+ connectport = ftpc->newport; /* we connect to the remote port */
if(!addr) {
- failf(data, "Can't resolve new host %s:%hu", newhost, connectport);
+ failf(data, "Can't resolve new host %s:%hu", ftpc->newhost, connectport);
return CURLE_FTP_CANT_GET_HOST;
}
}
- result = Curl_connecthost(conn,
- addr,
- &conn->sock[SECONDARYSOCKET],
- &conninfo,
- &connected);
+ conn->bits.tcpconnect[SECONDARYSOCKET] = FALSE;
+ result = Curl_connecthost(conn, addr);
Curl_resolv_unlock(data, addr); /* we're done using this address */
- if(result && ftpc->count1 == 0 && ftpcode == 229) {
- infof(data, "got positive EPSV response, but can't connect. "
- "Disabling EPSV\n");
- /* disable it for next transfer */
- conn->bits.ftp_use_epsv = FALSE;
- data->state.errorbuf = FALSE; /* allow error message to get rewritten */
- PPSENDF(&ftpc->pp, "PASV", NULL);
- ftpc->count1++;
- /* remain in the FTP_PASV state */
- return result;
- }
+ if(result) {
+ if(ftpc->count1 == 0 && ftpcode == 229)
+ return ftp_epsv_disable(conn);
- if(result)
return result;
+ }
- conn->bits.tcpconnect = connected; /* simply TRUE or FALSE */
/*
* When this is used from the multi interface, this might've returned with
* 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, newhost, connectport);
-
- switch(data->set.proxytype) {
-#ifndef CURL_DISABLE_PROXY
- /* FIX: this MUST wait for a proper connect first if 'connected' is
- * FALSE */
- case CURLPROXY_SOCKS5:
- case CURLPROXY_SOCKS5_HOSTNAME:
- result = Curl_SOCKS5(conn->proxyuser, conn->proxypasswd, newhost, newport,
- SECONDARYSOCKET, conn);
- break;
- case CURLPROXY_SOCKS4:
- result = Curl_SOCKS4(conn->proxyuser, newhost, newport,
- SECONDARYSOCKET, conn, FALSE);
- break;
- case CURLPROXY_SOCKS4A:
- result = Curl_SOCKS4(conn->proxyuser, newhost, newport,
- SECONDARYSOCKET, conn, TRUE);
- break;
-#endif /* CURL_DISABLE_PROXY */
- case CURLPROXY_HTTP:
- case CURLPROXY_HTTP_1_0:
- /* do nothing here. handled later. */
- break;
- default:
- failf(data, "unknown proxytype option given");
- result = CURLE_COULDNT_CONNECT;
- break;
- }
-#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_PROXY)
- if(conn->bits.tunnel_proxy && conn->bits.httpproxy) {
- /* FIX: this MUST wait for a proper connect first if 'connected' is
- * FALSE */
-
- /* BLOCKING */
- /* We want "seamless" FTP operations through HTTP proxy tunnel */
-
- /* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the member
- * conn->proto.http; we want FTP through HTTP and we have to change the
- * member temporarily for connecting to the HTTP proxy. After
- * Curl_proxyCONNECT we have to set back the member to the original struct
- * FTP pointer
- */
- struct HTTP http_proxy;
- struct FTP *ftp_save = data->state.proto.ftp;
- memset(&http_proxy, 0, sizeof(http_proxy));
- data->state.proto.http = &http_proxy;
-
- result = Curl_proxyCONNECT(conn, SECONDARYSOCKET, newhost, newport);
-
- data->state.proto.ftp = ftp_save;
+ * connect to connect.
+ */
- if(CURLE_OK != result)
- return result;
- }
-#endif /* !CURL_DISABLE_HTTP && !CURL_DISABLE_PROXY */
+ if(data->set.verbose)
+ /* this just dumps information about this second connection */
+ ftp_pasv_verbose(conn, conn->ip_addr, ftpc->newhost, connectport);
+ conn->bits.do_more = TRUE;
state(conn, FTP_STOP); /* this phase is completed */
return result;
else {
infof(data, "Connect data stream actively\n");
state(conn, FTP_STOP); /* end of DO phase */
+ result = ftp_dophase_done(conn, FALSE);
}
return result;
{
CURLcode result = CURLE_OK;
struct SessionHandle *data=conn->data;
- struct FTP *ftp = data->state.proto.ftp;
+ struct FTP *ftp = data->req.protop;
struct ftp_conn *ftpc = &conn->proto.ftpc;
switch(ftpcode) {
ftpc->file &&
data->set.get_filetime &&
(data->info.filetime>=0) ) {
- struct tm *tm;
time_t filetime = (time_t)data->info.filetime;
-#ifdef HAVE_GMTIME_R
struct tm buffer;
- tm = (struct tm *)gmtime_r(&filetime, &buffer);
-#else
- tm = gmtime(&filetime);
-#endif
+ const struct tm *tm = &buffer;
+
+ result = Curl_gmtime(filetime, &buffer);
+ if(result)
+ return result;
+
/* format: "Tue, 15 Nov 1994 12:45:26" */
snprintf(buf, BUFSIZE-1,
"Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n",
}
if(!result)
- result = ftp_state_post_mdtm(conn);
+ result = ftp_state_type(conn);
return result;
}
ftpcode);
if(instate == FTP_TYPE)
- result = ftp_state_post_type(conn);
+ result = ftp_state_size(conn);
else if(instate == FTP_LIST_TYPE)
- result = ftp_state_post_listtype(conn);
+ result = ftp_state_list(conn);
else if(instate == FTP_RETR_TYPE)
- result = ftp_state_post_retrtype(conn);
+ result = ftp_state_retr_prequote(conn);
else if(instate == FTP_STOR_TYPE)
- result = ftp_state_post_stortype(conn);
+ result = ftp_state_stor_prequote(conn);
return result;
}
-static CURLcode ftp_state_post_retr_size(struct connectdata *conn,
+static CURLcode ftp_state_retr(struct connectdata *conn,
curl_off_t filesize)
{
CURLcode result = CURLE_OK;
struct SessionHandle *data=conn->data;
- struct FTP *ftp = data->state.proto.ftp;
+ struct FTP *ftp = data->req.protop;
struct ftp_conn *ftpc = &conn->proto.ftpc;
if(data->set.max_filesize && (filesize > data->set.max_filesize)) {
if(data->state.resume_from< 0) {
/* We're supposed to download the last abs(from) bytes */
if(filesize < -data->state.resume_from) {
- failf(data, "Offset (%" FORMAT_OFF_T
- ") was beyond file size (%" FORMAT_OFF_T ")",
+ failf(data, "Offset (%" CURL_FORMAT_CURL_OFF_T
+ ") was beyond file size (%" CURL_FORMAT_CURL_OFF_T ")",
data->state.resume_from, filesize);
return CURLE_BAD_DOWNLOAD_RESUME;
}
}
else {
if(filesize < data->state.resume_from) {
- failf(data, "Offset (%" FORMAT_OFF_T
- ") was beyond file size (%" FORMAT_OFF_T ")",
+ failf(data, "Offset (%" CURL_FORMAT_CURL_OFF_T
+ ") was beyond file size (%" CURL_FORMAT_CURL_OFF_T ")",
data->state.resume_from, filesize);
return CURLE_BAD_DOWNLOAD_RESUME;
}
}
/* Set resume file transfer offset */
- infof(data, "Instructs server to resume from offset %" FORMAT_OFF_T
- "\n", data->state.resume_from);
+ infof(data, "Instructs server to resume from offset %"
+ CURL_FORMAT_CURL_OFF_T "\n", data->state.resume_from);
- PPSENDF(&ftpc->pp, "REST %" FORMAT_OFF_T, data->state.resume_from);
+ PPSENDF(&ftpc->pp, "REST %" CURL_FORMAT_CURL_OFF_T,
+ data->state.resume_from);
state(conn, FTP_RETR_REST);
-
}
else {
/* no resume */
#ifdef CURL_FTP_HTTPSTYLE_HEAD
if(-1 != filesize) {
snprintf(buf, sizeof(data->state.buffer),
- "Content-Length: %" FORMAT_OFF_T "\r\n", filesize);
+ "Content-Length: %" CURL_FORMAT_CURL_OFF_T "\r\n", filesize);
result = Curl_client_write(conn, CLIENTWRITE_BOTH, buf, 0);
if(result)
return result;
}
#endif
Curl_pgrsSetDownloadSize(data, filesize);
- result = ftp_state_post_size(conn);
+ result = ftp_state_rest(conn);
}
else if(instate == FTP_RETR_SIZE) {
Curl_pgrsSetDownloadSize(data, filesize);
- result = ftp_state_post_retr_size(conn, filesize);
+ result = ftp_state_retr(conn, filesize);
}
else if(instate == FTP_STOR_SIZE) {
data->state.resume_from = filesize;
return result;
}
#endif
- result = ftp_state_post_rest(conn);
+ result = ftp_state_prepare_transfer(conn);
break;
case FTP_RETR_REST:
}
static CURLcode ftp_state_stor_resp(struct connectdata *conn,
- int ftpcode)
+ int ftpcode, ftpstate instate)
{
CURLcode result = CURLE_OK;
struct SessionHandle *data = conn->data;
- struct FTP *ftp = data->state.proto.ftp;
if(ftpcode>=400) {
failf(data, "Failed FTP upload: %0d", ftpcode);
+ state(conn, FTP_STOP);
/* oops, we never close the sockets! */
return CURLE_UPLOAD_FAILED;
}
+ conn->proto.ftpc.state_saved = instate;
+
+ /* PORT means we are now awaiting the server to connect to us. */
if(data->set.ftp_use_port) {
- /* BLOCKING */
- /* PORT means we are now awaiting the server to connect to us. */
- result = AllowServerConnect(conn);
- if( result )
- return result;
- }
+ bool connected;
- if(conn->ssl[SECONDARYSOCKET].use) {
- /* since we only have a plaintext TCP connection here, we must now
- do the TLS stuff */
- infof(data, "Doing the SSL/TLS handshake on the data stream\n");
- /* BLOCKING */
- result = Curl_ssl_connect(conn, SECONDARYSOCKET);
+ state(conn, FTP_STOP); /* no longer in STOR state */
+
+ result = AllowServerConnect(conn, &connected);
if(result)
return result;
- }
-
- *(ftp->bytecountp)=0;
-
- /* When we know we're uploading a specified file, we can get the file
- size prior to the actual upload. */
-
- Curl_pgrsSetUploadSize(data, data->set.infilesize);
- /* set the SO_SNDBUF for the secondary socket for those who need it */
- Curl_sndbufset(conn->sock[SECONDARYSOCKET]);
-
- Curl_setup_transfer(conn, -1, -1, FALSE, NULL, /* no download */
- SECONDARYSOCKET, ftp->bytecountp);
- state(conn, FTP_STOP);
-
- conn->proto.ftpc.pp.pending_resp = TRUE; /* expect a server response */
+ if(!connected) {
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+ infof(data, "Data conn was not available immediately\n");
+ ftpc->wait_data_conn = TRUE;
+ }
- return result;
+ return CURLE_OK;
+ }
+ else
+ return InitiateTransfer(conn);
}
/* for LIST and RETR responses */
{
CURLcode result = CURLE_OK;
struct SessionHandle *data = conn->data;
- struct FTP *ftp = data->state.proto.ftp;
+ struct FTP *ftp = data->req.protop;
char *buf = data->state.buffer;
if((ftpcode == 150) || (ftpcode == 125)) {
/*
A;
150 Opening BINARY mode data connection for /etc/passwd (2241
- bytes). (ok, the file is being transfered)
+ bytes). (ok, the file is being transferred)
B:
150 Opening ASCII mode data connection for /bin/ls
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).
+ 150 Opening ASCII mode data connection for [file] (0.0.0.0,0) (545 bytes)
E:
125 Data connection already open; Transfer starting. */
/*
* It seems directory listings either don't show the size or very
* often uses size 0 anyway. ASCII transfers may very well turn out
- * that the transfered amount of data is not the same as this line
+ * that the transferred amount of data is not the same as this line
* tells, why using this number in those cases only confuses us.
*
* Example D above makes this parsing a little tricky */
else if(ftp->downloadsize > -1)
size = ftp->downloadsize;
- if(data->set.ftp_use_port) {
- /* BLOCKING */
- result = AllowServerConnect(conn);
- if( result )
- return result;
- }
-
- if(conn->ssl[SECONDARYSOCKET].use) {
- /* since we only have a plaintext TCP connection here, we must now
- do the TLS stuff */
- infof(data, "Doing the SSL/TLS handshake on the data stream\n");
- result = Curl_ssl_connect(conn, SECONDARYSOCKET);
- if(result)
- return result;
- }
-
if(size > data->req.maxdownload && data->req.maxdownload > 0)
size = data->req.size = data->req.maxdownload;
else if((instate != FTP_LIST) && (data->set.prefer_ascii))
size = -1; /* kludge for servers that understate ASCII mode file size */
- infof(data, "Maxdownload = %" FORMAT_OFF_T "\n", data->req.maxdownload);
+ infof(data, "Maxdownload = %" CURL_FORMAT_CURL_OFF_T "\n",
+ data->req.maxdownload);
if(instate != FTP_LIST)
- infof(data, "Getting file with size: %" FORMAT_OFF_T "\n", size);
+ infof(data, "Getting file with size: %" CURL_FORMAT_CURL_OFF_T "\n",
+ size);
/* FTP download: */
- Curl_setup_transfer(conn, SECONDARYSOCKET, size, FALSE,
- ftp->bytecountp, -1, NULL); /* no upload here */
+ conn->proto.ftpc.state_saved = instate;
+ conn->proto.ftpc.retr_size_saved = size;
- conn->proto.ftpc.pp.pending_resp = TRUE; /* expect server response */
- state(conn, FTP_STOP);
+ if(data->set.ftp_use_port) {
+ bool connected;
+
+ result = AllowServerConnect(conn, &connected);
+ if(result)
+ return result;
+
+ if(!connected) {
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
+ infof(data, "Data conn was not available immediately\n");
+ state(conn, FTP_STOP);
+ ftpc->wait_data_conn = TRUE;
+ }
+ }
+ else
+ return InitiateTransfer(conn);
}
else {
if((instate == FTP_LIST) && (ftpcode == 450)) {
{
CURLcode result = CURLE_OK;
-#ifdef HAVE_KRB4
- if(conn->data->set.krb) {
- /* We may need to issue a KAUTH here to have access to the files
- * do it if user supplied a password
- */
- if(conn->passwd && *conn->passwd) {
- /* BLOCKING */
- result = Curl_krb_kauth(conn);
- if(result)
- return result;
- }
- }
-#endif
if(conn->ssl[FIRSTSOCKET].use) {
/* PBSZ = PROTECTION BUFFER SIZE.
{
CURLcode result = CURLE_OK;
struct SessionHandle *data = conn->data;
- struct FTP *ftp = data->state.proto.ftp;
+ struct FTP *ftp = data->req.protop;
struct ftp_conn *ftpc = &conn->proto.ftpc;
(void)instate; /* no use for this yet */
if(pp->sendleft)
return Curl_pp_flushsend(pp);
- /* we read a piece of response */
result = ftp_readresp(sock, pp, &ftpcode, &nread);
if(result)
return result;
/* we have now received a full FTP server response */
switch(ftpc->state) {
case FTP_WAIT220:
- if(ftpcode != 220) {
+ if(ftpcode == 230)
+ /* 230 User logged in - already! */
+ return ftp_state_user_resp(conn, ftpcode, ftpc->state);
+ else if(ftpcode != 220) {
failf(data, "Got a %03d ftp-server response when 220 was expected",
ftpcode);
return CURLE_FTP_WEIRD_SERVER_REPLY;
}
/* We have received a 220 response fine, now we proceed. */
-#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
+#ifdef HAVE_GSSAPI
if(data->set.krb) {
/* If not anonymous login, try a secure login. Note that this
procedure is still BLOCKING. */
set a valid level */
Curl_sec_request_prot(conn, data->set.str[STRING_KRB_LEVEL]);
- if(Curl_sec_login(conn) != CURLE_OK)
+ if(Curl_sec_login(conn))
infof(data, "Logging in with password in cleartext!\n");
else
infof(data, "Authentication successful\n");
}
#endif
- if(data->set.ftp_ssl && !conn->ssl[FIRSTSOCKET].use) {
+ if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
/* We don't have a SSL/TLS connection yet, but FTPS is
requested. Try a FTPS connection now */
default:
failf(data, "unsupported parameter to CURLOPT_FTPSSLAUTH: %d",
(int)data->set.ftpsslauth);
- return CURLE_FAILED_INIT; /* we don't know what to do */
+ return CURLE_UNKNOWN_OPTION; /* we don't know what to do */
}
PPSENDF(&ftpc->pp, "AUTH %s", ftpauth[ftpc->count1]);
state(conn, FTP_AUTH);
if((ftpcode == 234) || (ftpcode == 334)) {
/* Curl_ssl_connect is BLOCKING */
result = Curl_ssl_connect(conn, FIRSTSOCKET);
- if(CURLE_OK == result) {
- conn->protocol |= PROT_FTPS;
+ if(!result) {
conn->ssl[SECONDARYSOCKET].use = FALSE; /* clear-text data */
result = ftp_state_user(conn);
}
/* remain in this same state */
}
else {
- if(data->set.ftp_ssl > CURLUSESSL_TRY)
+ if(data->set.use_ssl > CURLUSESSL_TRY)
/* we failed and CURLUSESSL_CONTROL or CURLUSESSL_ALL is set */
result = CURLE_USE_SSL_FAILED;
else
case FTP_PBSZ:
PPSENDF(&ftpc->pp, "PROT %c",
- data->set.ftp_ssl == CURLUSESSL_CONTROL ? 'C' : 'P');
+ data->set.use_ssl == CURLUSESSL_CONTROL ? 'C' : 'P');
state(conn, FTP_PROT);
break;
if(ftpcode/100 == 2)
/* We have enabled SSL for the data connection! */
conn->ssl[SECONDARYSOCKET].use =
- (bool)(data->set.ftp_ssl != CURLUSESSL_CONTROL);
+ (data->set.use_ssl != CURLUSESSL_CONTROL) ? TRUE : FALSE;
/* FTP servers typically responds with 500 if they decide to reject
our 'P' request */
- else if(data->set.ftp_ssl > CURLUSESSL_CONTROL)
+ else if(data->set.use_ssl > CURLUSESSL_CONTROL)
/* we failed and bails out */
return CURLE_USE_SSL_FAILED;
if(data->set.ftp_ccc) {
/* CCC - Clear Command Channel
*/
- PPSENDF(&ftpc->pp, "CCC", NULL);
+ PPSENDF(&ftpc->pp, "%s", "CCC");
state(conn, FTP_CCC);
}
else {
return CURLE_OUT_OF_MEMORY;
/* Reply format is like
- 257<space>"<directory-name>"<space><commentary> and the RFC959
- says
+ 257<space>[rubbish]"<directory-name>"<space><commentary> and the
+ RFC959 says
The directory name can contain any character; embedded
double-quotes should be escaped by double-quotes (the
"quote-doubling" convention).
*/
+
+ /* scan for the first double-quote for non-standard responses */
+ while(ptr < &data->state.buffer[sizeof(data->state.buffer)]
+ && *ptr != '\n' && *ptr != '\0' && *ptr != '"')
+ ptr++;
+
if('\"' == *ptr) {
/* it started good */
ptr++;
- for (store = dir; *ptr;) {
+ for(store = dir; *ptr;) {
if('\"' == *ptr) {
if('\"' == ptr[1]) {
/* "quote-doubling" */
store++;
ptr++;
}
- if(ftpc->entrypath)
- free(ftpc->entrypath);
- ftpc->entrypath =dir; /* remember this */
- infof(data, "Entry path is '%s'\n", ftpc->entrypath);
- /* also save it where getinfo can access it: */
- data->state.most_recent_ftp_entrypath = ftpc->entrypath;
/* If the path name does not look like an absolute path (i.e.: it
does not start with a '/'), we probably need some server-dependent
if the path name looks strange to minimize overhead on other
systems. */
- if(!ftpc->server_os && ftpc->entrypath[0] != '/') {
- PPSENDF(&ftpc->pp, "SYST", NULL);
+ if(!ftpc->server_os && dir[0] != '/') {
+
+ result = Curl_pp_sendf(&ftpc->pp, "%s", "SYST");
+ if(result) {
+ free(dir);
+ return result;
+ }
+ Curl_safefree(ftpc->entrypath);
+ ftpc->entrypath = dir; /* remember this */
+ infof(data, "Entry path is '%s'\n", ftpc->entrypath);
+ /* also save it where getinfo can access it: */
+ data->state.most_recent_ftp_entrypath = ftpc->entrypath;
state(conn, FTP_SYST);
break;
}
+
+ Curl_safefree(ftpc->entrypath);
+ ftpc->entrypath = dir; /* remember this */
+ infof(data, "Entry path is '%s'\n", ftpc->entrypath);
+ /* also save it where getinfo can access it: */
+ data->state.most_recent_ftp_entrypath = ftpc->entrypath;
}
else {
/* couldn't get the path */
/* Reply format is like
215<space><OS-name><space><commentary>
*/
- while (*ptr == ' ')
+ while(*ptr == ' ')
ptr++;
- for (store = os; *ptr && *ptr != ' ';)
+ for(store = os; *ptr && *ptr != ' ';)
*store++ = *ptr++;
*store = '\0'; /* zero terminate */
- ftpc->server_os = os;
/* Check for special servers here. */
- if(strequal(ftpc->server_os, "OS/400")) {
+ if(strequal(os, "OS/400")) {
/* Force OS400 name format 1. */
- PPSENDF(&ftpc->pp, "SITE NAMEFMT 1", NULL);
+ result = Curl_pp_sendf(&ftpc->pp, "%s", "SITE NAMEFMT 1");
+ if(result) {
+ free(os);
+ return result;
+ }
+ /* remember target server OS */
+ Curl_safefree(ftpc->server_os);
+ ftpc->server_os = os;
state(conn, FTP_NAMEFMT);
break;
}
- else {
- /* Nothing special for the target server. */
- }
+ else {
+ /* Nothing special for the target server. */
+ /* remember target server OS */
+ Curl_safefree(ftpc->server_os);
+ ftpc->server_os = os;
+ }
}
else {
/* Cannot identify server OS. Continue anyway and cross fingers. */
case FTP_RETR_PREQUOTE:
case FTP_STOR_PREQUOTE:
if((ftpcode >= 400) && !ftpc->count2) {
- /* failure reponse code, and not allowed to fail */
+ /* failure response code, and not allowed to fail */
failf(conn->data, "QUOT command failed with %03d", ftpcode);
return CURLE_QUOTE_ERROR;
}
PPSENDF(&ftpc->pp, "CWD %s", ftpc->dirs[ftpc->count1 - 1]);
}
else {
- result = ftp_state_post_cwd(conn);
+ result = ftp_state_mdtm(conn);
if(result)
return result;
}
case FTP_PRET:
if(ftpcode != 200) {
- /* there only is this one standard OK return code. */
+ /* there only is this one standard OK return code. */
failf(data, "PRET command not accepted: %03d", ftpcode);
return CURLE_FTP_PRET_FAILED;
}
break;
case FTP_STOR:
- result = ftp_state_stor_resp(conn, ftpcode);
+ result = ftp_state_stor_resp(conn, ftpcode, ftpc->state);
break;
case FTP_QUIT:
bool *done)
{
struct ftp_conn *ftpc = &conn->proto.ftpc;
- CURLcode result = Curl_pp_multi_statemach(&ftpc->pp);
+ CURLcode result = Curl_pp_statemach(&ftpc->pp, FALSE);
/* Check for the state outside of the Curl_socket_ready() return code checks
since at times we are in fact already in this state when this function
gets called. */
- *done = (bool)(ftpc->state == FTP_STOP);
+ *done = (ftpc->state == FTP_STOP) ? TRUE : FALSE;
return result;
}
-static CURLcode ftp_easy_statemach(struct connectdata *conn)
+static CURLcode ftp_block_statemach(struct connectdata *conn)
{
struct ftp_conn *ftpc = &conn->proto.ftpc;
struct pingpong *pp = &ftpc->pp;
CURLcode result = CURLE_OK;
while(ftpc->state != FTP_STOP) {
- result = Curl_pp_easy_statemach(pp);
+ result = Curl_pp_statemach(pp, TRUE);
if(result)
break;
}
}
/*
- * Allocate and initialize the struct FTP for the current SessionHandle. If
- * need be.
- */
-
-#if defined(__INTEL_COMPILER) && (__INTEL_COMPILER == 910) && \
- defined(__OPTIMIZE__) && defined(__unix__) && defined(__i386__)
- /* workaround icc 9.1 optimizer issue */
-#pragma optimize("", off)
-#endif
-
-static CURLcode ftp_init(struct connectdata *conn)
-{
- struct FTP *ftp;
-
- if(NULL == conn->data->state.proto.ftp) {
- conn->data->state.proto.ftp = malloc(sizeof(struct FTP));
- if(NULL == conn->data->state.proto.ftp)
- return CURLE_OUT_OF_MEMORY;
- }
-
- ftp = conn->data->state.proto.ftp;
-
- /* get some initial data into the ftp struct */
- ftp->bytecountp = &conn->data->req.bytecount;
- ftp->transfer = FTPTRANSFER_BODY;
- ftp->downloadsize = 0;
-
- /* No need to duplicate user+password, the connectdata struct won't change
- during a session, but we re-init them here since on subsequent inits
- since the conn struct may have changed or been replaced.
- */
- ftp->user = conn->user;
- ftp->passwd = conn->passwd;
- if(TRUE == isBadFtpString(ftp->user))
- return CURLE_URL_MALFORMAT;
- if(TRUE == isBadFtpString(ftp->passwd))
- return CURLE_URL_MALFORMAT;
-
- conn->proto.ftpc.known_filesize = -1; /* unknown size for now */
-
- return CURLE_OK;
-}
-
-#if defined(__INTEL_COMPILER) && (__INTEL_COMPILER == 910) && \
- defined(__OPTIMIZE__) && defined(__unix__) && defined(__i386__)
- /* workaround icc 9.1 optimizer issue */
-#pragma optimize("", on)
-#endif
-
-/*
* ftp_connect() should do everything that is to be considered a part of
* the connection phase.
*
* The variable 'done' points to will be TRUE if the protocol-layer connect
- * phase is done when this function returns, or FALSE is not. When called as
- * a part of the easy interface, it will always be TRUE.
+ * phase is done when this function returns, or FALSE if not.
+ *
*/
static CURLcode ftp_connect(struct connectdata *conn,
bool *done) /* see description above */
{
CURLcode result;
struct ftp_conn *ftpc = &conn->proto.ftpc;
- struct SessionHandle *data=conn->data;
struct pingpong *pp = &ftpc->pp;
*done = FALSE; /* default to not done yet */
- /* If there already is a protocol-specific struct allocated for this
- sessionhandle, deal with it */
- Curl_reset_reqproto(conn);
-
- result = ftp_init(conn);
- if(CURLE_OK != result)
- return result;
-
- /* We always support persistant connections on ftp */
- conn->bits.close = FALSE;
+ /* We always support persistent connections on ftp */
+ connkeep(conn, "FTP default");
pp->response_time = RESP_TIMEOUT; /* set default response time-out */
pp->statemach_act = ftp_statemach_act;
pp->endofresp = ftp_endofresp;
pp->conn = conn;
-#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_PROXY)
- if(conn->bits.tunnel_proxy && conn->bits.httpproxy) {
- /* for FTP over HTTP proxy */
- struct HTTP http_proxy;
- struct FTP *ftp_save;
-
- /* BLOCKING */
- /* We want "seamless" FTP operations through HTTP proxy tunnel */
-
- /* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the member
- * conn->proto.http; we want FTP through HTTP and we have to change the
- * member temporarily for connecting to the HTTP proxy. After
- * Curl_proxyCONNECT we have to set back the member to the original struct
- * FTP pointer
- */
- ftp_save = data->state.proto.ftp;
- memset(&http_proxy, 0, sizeof(http_proxy));
- data->state.proto.http = &http_proxy;
-
- result = Curl_proxyCONNECT(conn, FIRSTSOCKET,
- conn->host.name, conn->remote_port);
-
- data->state.proto.ftp = ftp_save;
-
- if(CURLE_OK != result)
- return result;
- }
-#endif /* !CURL_DISABLE_HTTP && !CURL_DISABLE_PROXY */
-
- if(conn->protocol & PROT_FTPS) {
+ if(conn->handler->flags & PROTOPT_SSL) {
/* BLOCKING */
- /* FTPS is simply ftp with SSL for the control channel */
- /* now, perform the SSL initialization for this socket */
result = Curl_ssl_connect(conn, FIRSTSOCKET);
if(result)
return result;
response */
state(conn, FTP_WAIT220);
- if(data->state.used_interface == Curl_if_multi)
- result = ftp_multi_statemach(conn, done);
- else {
- result = ftp_easy_statemach(conn);
- if(!result)
- *done = TRUE;
- }
+ result = ftp_multi_statemach(conn, done);
return result;
}
bool premature)
{
struct SessionHandle *data = conn->data;
- struct FTP *ftp = data->state.proto.ftp;
+ struct FTP *ftp = data->req.protop;
struct ftp_conn *ftpc = &conn->proto.ftpc;
struct pingpong *pp = &ftpc->pp;
ssize_t nread;
int ftpcode;
- CURLcode result=CURLE_OK;
+ CURLcode result = CURLE_OK;
bool was_ctl_valid = ftpc->ctl_valid;
char *path;
const char *path_to_use = data->state.path;
case CURLE_BAD_DOWNLOAD_RESUME:
case CURLE_FTP_WEIRD_PASV_REPLY:
case CURLE_FTP_PORT_FAILED:
+ case CURLE_FTP_ACCEPT_FAILED:
+ case CURLE_FTP_ACCEPT_TIMEOUT:
case CURLE_FTP_COULDNT_SET_TYPE:
case CURLE_FTP_COULDNT_RETR_FILE:
+ case CURLE_PARTIAL_FILE:
case CURLE_UPLOAD_FAILED:
case CURLE_REMOTE_ACCESS_DENIED:
case CURLE_FILESIZE_EXCEEDED:
ftpc->ctl_valid = FALSE;
ftpc->cwdfail = TRUE; /* set this TRUE to prevent us to remember the
current path, as this connection is going */
- conn->bits.close = TRUE; /* marked for closure */
+ connclose(conn, "FTP ended with bad error code");
result = status; /* use the already set error code */
break;
}
path = curl_easy_unescape(data, path_to_use, 0, NULL);
if(!path) {
/* out of memory, but we can limp along anyway (and should try to
- * since we're in the out of memory cleanup path) */
- ftpc->prevpath = NULL; /* no path */
+ * since we may already be in the out of memory cleanup path) */
+ if(!result)
+ result = CURLE_OUT_OF_MEMORY;
+ ftpc->ctl_valid = FALSE; /* mark control connection as bad */
+ connclose(conn, "FTP: out of memory!"); /* mark for connection closure */
+ ftpc->prevpath = NULL; /* no path remembering */
}
else {
size_t flen = ftpc->file?strlen(ftpc->file):0; /* file is "raw" already */
#endif
if(conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD) {
- if(!result && ftpc->dont_check && data->req.maxdownload > 0)
+ if(!result && ftpc->dont_check && data->req.maxdownload > 0) {
/* partial download completed */
- result = Curl_pp_sendf(pp, "ABOR");
+ result = Curl_pp_sendf(pp, "%s", "ABOR");
+ if(result) {
+ failf(data, "Failure sending ABOR command: %s",
+ curl_easy_strerror(result));
+ ftpc->ctl_valid = FALSE; /* mark control connection as bad */
+ connclose(conn, "ABOR command failed"); /* connection closure */
+ }
+ }
if(conn->ssl[SECONDARYSOCKET].use) {
/* The secondary socket is using SSL so we must close down that part
still requested to use SSL */
}
if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET]) {
- sclose(conn->sock[SECONDARYSOCKET]);
+ Curl_closesocket(conn, conn->sock[SECONDARYSOCKET]);
conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
+ conn->bits.tcpconnect[SECONDARYSOCKET] = FALSE;
}
}
/*
* Let's see what the server says about the transfer we just performed,
* but lower the timeout as sometimes this connection has died while the
- * data has been transfered. This happens when doing through NATs etc that
+ * data has been transferred. This happens when doing through NATs etc that
* abandon old silent connections.
*/
long old_time = pp->response_time;
if(!nread && (CURLE_OPERATION_TIMEDOUT == result)) {
failf(data, "control connection looks dead");
ftpc->ctl_valid = FALSE; /* mark control connection as bad */
- conn->bits.close = TRUE; /* mark for closure */
+ connclose(conn, "Timeout or similar in FTP DONE operation"); /* close */
}
if(result)
/* we have just sent ABOR and there is no reliable way to check if it was
* successful or not; we have to close the connection now */
infof(data, "partial download completed, closing connection\n");
- conn->bits.close = TRUE; /* mark for closure */
+ connclose(conn, "Partial download with no ability to check");
return result;
}
use checking further */
;
else if(data->set.upload) {
- if((-1 != data->set.infilesize) &&
- (data->set.infilesize != *ftp->bytecountp) &&
+ if((-1 != data->state.infilesize) &&
+ (data->state.infilesize != *ftp->bytecountp) &&
!data->set.crlf &&
(ftp->transfer == FTPTRANSFER_BODY)) {
- failf(data, "Uploaded unaligned file size (%" FORMAT_OFF_T
- " out of %" FORMAT_OFF_T " bytes)",
- *ftp->bytecountp, data->set.infilesize);
+ failf(data, "Uploaded unaligned file size (%" CURL_FORMAT_CURL_OFF_T
+ " out of %" CURL_FORMAT_CURL_OFF_T " bytes)",
+ *ftp->bytecountp, data->state.infilesize);
result = CURLE_PARTIAL_FILE;
}
}
*ftp->bytecountp) &&
#endif /* CURL_DO_LINEEND_CONV */
(data->req.maxdownload != *ftp->bytecountp)) {
- failf(data, "Received only partial file: %" FORMAT_OFF_T " bytes",
- *ftp->bytecountp);
+ failf(data, "Received only partial file: %" CURL_FORMAT_CURL_OFF_T
+ " bytes", *ftp->bytecountp);
result = CURLE_PARTIAL_FILE;
}
else if(!ftpc->dont_check &&
acceptfail = TRUE;
}
- FTPSENDF(conn, "%s", cmd);
+ PPSENDF(&conn->proto.ftpc.pp, "%s", cmd);
pp->response = Curl_tvnow(); /* timeout relative now */
if((-1 == to) && (from>=0)) {
/* X - */
data->state.resume_from = from;
- DEBUGF(infof(conn->data, "FTP RANGE %" FORMAT_OFF_T " to end of file\n",
- from));
+ DEBUGF(infof(conn->data, "FTP RANGE %" CURL_FORMAT_CURL_OFF_T
+ " to end of file\n", from));
}
else if(from < 0) {
/* -Y */
data->req.maxdownload = -from;
data->state.resume_from = from;
- DEBUGF(infof(conn->data, "FTP RANGE the last %" FORMAT_OFF_T " bytes\n",
- -from));
+ DEBUGF(infof(conn->data, "FTP RANGE the last %" CURL_FORMAT_CURL_OFF_T
+ " bytes\n", -from));
}
else {
/* X-Y */
data->req.maxdownload = (to-from)+1; /* include last byte */
data->state.resume_from = from;
- DEBUGF(infof(conn->data, "FTP RANGE from %" FORMAT_OFF_T
- " getting %" FORMAT_OFF_T " bytes\n",
+ DEBUGF(infof(conn->data, "FTP RANGE from %" CURL_FORMAT_CURL_OFF_T
+ " getting %" CURL_FORMAT_CURL_OFF_T " bytes\n",
from, data->req.maxdownload));
}
- DEBUGF(infof(conn->data, "range-download from %" FORMAT_OFF_T
- " to %" FORMAT_OFF_T ", totally %" FORMAT_OFF_T " bytes\n",
+ DEBUGF(infof(conn->data, "range-download from %" CURL_FORMAT_CURL_OFF_T
+ " to %" CURL_FORMAT_CURL_OFF_T ", totally %"
+ CURL_FORMAT_CURL_OFF_T " bytes\n",
from, to, data->req.maxdownload));
ftpc->dont_check = TRUE; /* dont check for successful transfer */
}
/*
- * ftp_nextconnect()
+ * ftp_do_more()
*
* This function shall be called when the second FTP (data) connection is
* connected.
+ *
+ * 'complete' can return 0 for incomplete, 1 for done and -1 for go back
+ * (which basically is only for when PASV is being sent to retry a failed
+ * EPSV).
*/
-static CURLcode ftp_nextconnect(struct connectdata *conn)
+static CURLcode ftp_do_more(struct connectdata *conn, int *completep)
{
struct SessionHandle *data=conn->data;
struct ftp_conn *ftpc = &conn->proto.ftpc;
CURLcode result = CURLE_OK;
+ bool connected = FALSE;
+ bool complete = FALSE;
/* the ftp struct is inited in ftp_connect() */
- struct FTP *ftp = data->state.proto.ftp;
+ struct FTP *ftp = data->req.protop;
+
+ /* if the second connection isn't done yet, wait for it */
+ if(!conn->bits.tcpconnect[SECONDARYSOCKET]) {
+ if(conn->tunnel_state[SECONDARYSOCKET] == TUNNEL_CONNECT) {
+ /* As we're in TUNNEL_CONNECT state now, we know the proxy name and port
+ aren't used so we blank their arguments. TODO: make this nicer */
+ result = Curl_proxyCONNECT(conn, SECONDARYSOCKET, NULL, 0);
+
+ return result;
+ }
+
+ result = Curl_is_connected(conn, SECONDARYSOCKET, &connected);
+
+ /* Ready to do more? */
+ if(connected) {
+ DEBUGF(infof(data, "DO-MORE connected phase starts\n"));
+ if(conn->bits.proxy) {
+ infof(data, "Connection to proxy confirmed\n");
+ result = proxy_magic(conn, ftpc->newhost, ftpc->newport, &connected);
+ }
+ }
+ else {
+ if(result && (ftpc->count1 == 0)) {
+ *completep = -1; /* go back to DOING please */
+ /* this is a EPSV connect failing, try PASV instead */
+ return ftp_epsv_disable(conn);
+ }
+ return result;
+ }
+ }
+
+ if(ftpc->state) {
+ /* already in a state so skip the intial commands.
+ They are only done to kickstart the do_more state */
+ result = ftp_multi_statemach(conn, &complete);
- DEBUGF(infof(data, "DO-MORE phase starts\n"));
+ *completep = (int)complete;
+
+ /* if we got an error or if we don't wait for a data connection return
+ immediately */
+ if(result || (ftpc->wait_data_conn != TRUE))
+ return result;
+
+ if(ftpc->wait_data_conn)
+ /* if we reach the end of the FTP state machine here, *complete will be
+ TRUE but so is ftpc->wait_data_conn, which says we need to wait for
+ the data connection and therefore we're not actually complete */
+ *completep = 0;
+ }
if(ftp->transfer <= FTPTRANSFER_INFO) {
/* a transfer is about to take place, or if not a file name was given
so we'll do a SIZE on it later and then we need the right TYPE first */
- if(data->set.upload) {
+ if(ftpc->wait_data_conn == TRUE) {
+ bool serv_conned;
+
+ result = ReceivedServerConnect(conn, &serv_conned);
+ if(result)
+ return result; /* Failed to accept data connection */
+
+ if(serv_conned) {
+ /* It looks data connection is established */
+ result = AcceptServerConnect(conn);
+ ftpc->wait_data_conn = FALSE;
+ if(!result)
+ result = InitiateTransfer(conn);
+
+ if(result)
+ return result;
+
+ *completep = 1; /* this state is now complete when the server has
+ connected back to us */
+ }
+ }
+ else if(data->set.upload) {
result = ftp_nb_type(conn, data->set.prefer_ascii, FTP_STOR_TYPE);
if(result)
return result;
+
+ result = ftp_multi_statemach(conn, &complete);
+ *completep = (int)complete;
}
else {
/* download */
if(result)
return result;
}
+
+ result = ftp_multi_statemach(conn, &complete);
+ *completep = (int)complete;
}
- result = ftp_easy_statemach(conn);
+ return result;
}
- if((result == CURLE_OK) && (ftp->transfer != FTPTRANSFER_BODY))
+ if(!result && (ftp->transfer != FTPTRANSFER_BODY))
/* no data to transfer. FIX: it feels like a kludge to have this here
too! */
Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
- /* end of transfer */
- DEBUGF(infof(data, "DO-MORE phase ends with %d\n", (int)result));
+ if(!ftpc->wait_data_conn) {
+ /* no waiting for the data connection so this is now complete */
+ *completep = 1;
+ DEBUGF(infof(data, "DO-MORE phase ends with %d\n", (int)result));
+ }
return result;
}
if(conn->data->set.opt_no_body) {
/* requested no body means no transfer... */
- struct FTP *ftp = conn->data->state.proto.ftp;
+ struct FTP *ftp = conn->data->req.protop;
ftp->transfer = FTPTRANSFER_INFO;
}
-
*dophase_done = FALSE; /* not done yet */
/* start the first command in the DO phase */
return result;
/* run the state-machine */
- if(conn->data->state.used_interface == Curl_if_multi)
- result = ftp_multi_statemach(conn, dophase_done);
- else {
- result = ftp_easy_statemach(conn);
- *dophase_done = TRUE; /* with the easy interface we are done here */
- }
- *connected = conn->bits.tcpconnect;
+ result = ftp_multi_statemach(conn, dophase_done);
+
+ *connected = conn->bits.tcpconnect[SECONDARYSOCKET];
+
+ infof(conn->data, "ftp_perform ends with SECONDARY: %d\n", *connected);
if(*dophase_done)
- DEBUGF(infof(conn->data, "DO phase is complete\n"));
+ DEBUGF(infof(conn->data, "DO phase is complete1\n"));
return result;
}
char *last_slash;
char *path = conn->data->state.path;
struct WildcardData *wildcard = &(conn->data->wildcard);
- CURLcode ret = CURLE_OK;
+ CURLcode result = CURLE_OK;
struct ftp_wc_tmpdata *ftp_tmp;
last_slash = strrchr(conn->data->state.path, '/');
last_slash++;
if(last_slash[0] == '\0') {
wildcard->state = CURLWC_CLEAN;
- ret = ftp_parse_url_path(conn);
- return ret;
+ result = ftp_parse_url_path(conn);
+ return result;
}
else {
wildcard->pattern = strdup(last_slash);
- if (!wildcard->pattern)
+ if(!wildcard->pattern)
return CURLE_OUT_OF_MEMORY;
last_slash[0] = '\0'; /* cut file from path */
}
else { /* there is only 'wildcard pattern' or nothing */
if(path[0]) {
wildcard->pattern = strdup(path);
- if (!wildcard->pattern)
+ if(!wildcard->pattern)
return CURLE_OUT_OF_MEMORY;
path[0] = '\0';
}
else { /* only list */
wildcard->state = CURLWC_CLEAN;
- ret = ftp_parse_url_path(conn);
- return ret;
+ result = ftp_parse_url_path(conn);
+ return result;
}
}
resources for wildcard transfer */
/* allocate ftp protocol specific temporary wildcard data */
- ftp_tmp = malloc(sizeof(struct ftp_wc_tmpdata));
+ ftp_tmp = calloc(1, sizeof(struct ftp_wc_tmpdata));
if(!ftp_tmp) {
+ Curl_safefree(wildcard->pattern);
return CURLE_OUT_OF_MEMORY;
}
/* INITIALIZE parselist structure */
ftp_tmp->parser = Curl_ftp_parselist_data_alloc();
if(!ftp_tmp->parser) {
- free(ftp_tmp);
+ Curl_safefree(wildcard->pattern);
+ Curl_safefree(ftp_tmp);
return CURLE_OUT_OF_MEMORY;
}
conn->data->set.ftp_filemethod = FTPFILE_MULTICWD;
/* try to parse ftp url */
- ret = ftp_parse_url_path(conn);
- if(ret) {
- return ret;
+ result = ftp_parse_url_path(conn);
+ if(result) {
+ Curl_safefree(wildcard->pattern);
+ wildcard->tmp_dtor(wildcard->tmp);
+ wildcard->tmp_dtor = ZERO_NULL;
+ wildcard->tmp = NULL;
+ return result;
+ }
+
+ wildcard->path = strdup(conn->data->state.path);
+ if(!wildcard->path) {
+ Curl_safefree(wildcard->pattern);
+ wildcard->tmp_dtor(wildcard->tmp);
+ wildcard->tmp_dtor = ZERO_NULL;
+ wildcard->tmp = NULL;
+ return CURLE_OUT_OF_MEMORY;
}
/* backup old write_function */
ftp_tmp->backup.write_function = conn->data->set.fwrite_func;
- /* parsing write function (callback included directly from ftplistparser.c) */
+ /* parsing write function */
conn->data->set.fwrite_func = Curl_ftp_parselist;
/* backup old file descriptor */
ftp_tmp->backup.file_descriptor = conn->data->set.out;
/* let the writefunc callback know what curl pointer is working with */
conn->data->set.out = conn;
- wildcard->path = strdup(conn->data->state.path);
- if(!wildcard->path) {
- return CURLE_OUT_OF_MEMORY;
- }
-
infof(conn->data, "Wildcard - Parsing started\n");
return CURLE_OK;
}
static CURLcode wc_statemach(struct connectdata *conn)
{
struct WildcardData * const wildcard = &(conn->data->wildcard);
- CURLcode ret = CURLE_OK;
+ CURLcode result = CURLE_OK;
switch (wildcard->state) {
case CURLWC_INIT:
- ret = init_wc_data(conn);
+ result = init_wc_data(conn);
if(wildcard->state == CURLWC_CLEAN)
/* only listing! */
break;
else
- wildcard->state = ret ? CURLWC_ERROR : CURLWC_MATCHING;
+ wildcard->state = result ? CURLWC_ERROR : CURLWC_MATCHING;
break;
case CURLWC_MATCHING: {
struct ftp_wc_tmpdata *ftp_tmp = wildcard->tmp;
conn->data->set.fwrite_func = ftp_tmp->backup.write_function;
conn->data->set.out = ftp_tmp->backup.file_descriptor;
+ ftp_tmp->backup.write_function = ZERO_NULL;
+ ftp_tmp->backup.file_descriptor = NULL;
wildcard->state = CURLWC_DOWNLOADING;
if(Curl_ftp_parselist_geterror(ftp_tmp->parser)) {
/* filelist has at least one file, lets get first one */
struct ftp_conn *ftpc = &conn->proto.ftpc;
struct curl_fileinfo *finfo = wildcard->filelist->head->ptr;
- char *tmp_path = malloc(strlen(conn->data->state.path) +
- strlen(finfo->filename) + 1);
- if(!tmp_path) {
+
+ char *tmp_path = aprintf("%s%s", wildcard->path, finfo->filename);
+ if(!tmp_path)
return CURLE_OUT_OF_MEMORY;
- }
- tmp_path[0] = 0;
- /* make full path to matched file */
- strcat(tmp_path, wildcard->path);
- strcat(tmp_path, finfo->filename);
/* switch default "state.pathbuffer" and tmp_path, good to see
ftp_parse_url_path function to understand this trick */
- if(conn->data->state.pathbuffer)
- free(conn->data->state.pathbuffer);
+ Curl_safefree(conn->data->state.pathbuffer);
conn->data->state.pathbuffer = tmp_path;
conn->data->state.path = tmp_path;
if(finfo->flags & CURLFINFOFLAG_KNOWN_SIZE)
ftpc->known_filesize = finfo->size;
- ret = ftp_parse_url_path(conn);
- if(ret) {
- return ret;
- }
+ result = ftp_parse_url_path(conn);
+ if(result)
+ return result;
/* we don't need the Curl_fileinfo of first file anymore */
Curl_llist_remove(wildcard->filelist, wildcard->filelist->head, NULL);
case CURLWC_CLEAN: {
struct ftp_wc_tmpdata *ftp_tmp = wildcard->tmp;
- ret = CURLE_OK;
- if(ftp_tmp) {
- ret = Curl_ftp_parselist_geterror(ftp_tmp->parser);
- }
- wildcard->state = ret ? CURLWC_ERROR : CURLWC_DONE;
+ result = CURLE_OK;
+ if(ftp_tmp)
+ result = Curl_ftp_parselist_geterror(ftp_tmp->parser);
+
+ wildcard->state = result ? CURLWC_ERROR : CURLWC_DONE;
} break;
case CURLWC_DONE:
break;
}
- return ret;
+ return result;
}
/***********************************************************************
*/
static CURLcode ftp_do(struct connectdata *conn, bool *done)
{
- CURLcode retcode = CURLE_OK;
+ CURLcode result = CURLE_OK;
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
*done = FALSE; /* default to false */
-
- /*
- Since connections can be re-used between SessionHandles, this might be a
- connection already existing but on a fresh SessionHandle struct so we must
- make sure we have a good 'struct FTP' to play with. For new connections,
- the struct FTP is allocated and setup in the ftp_connect() function.
- */
- Curl_reset_reqproto(conn);
- retcode = ftp_init(conn);
- if(retcode)
- return retcode;
+ ftpc->wait_data_conn = FALSE; /* default to no such wait */
if(conn->data->set.wildcardmatch) {
- retcode = wc_statemach(conn);
+ result = wc_statemach(conn);
if(conn->data->wildcard.state == CURLWC_SKIP ||
conn->data->wildcard.state == CURLWC_DONE) {
/* do not call ftp_regular_transfer */
return CURLE_OK;
}
- if(retcode) /* error, loop or skipping the file */
- return retcode;
+ if(result) /* error, loop or skipping the file */
+ return result;
}
else { /* no wildcard FSM needed */
- retcode = ftp_parse_url_path(conn);
- if(retcode)
- return retcode;
+ result = ftp_parse_url_path(conn);
+ if(result)
+ return result;
}
- retcode = ftp_regular_transfer(conn, done);
+ result = ftp_regular_transfer(conn, done);
- return retcode;
+ return result;
}
char s[SBUF_SIZE];
size_t write_len;
char *sptr=s;
- CURLcode res = CURLE_OK;
-#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
+ CURLcode result = CURLE_OK;
+#ifdef HAVE_GSSAPI
enum protection_level data_sec = conn->data_prot;
#endif
va_list ap;
va_start(ap, fmt);
- vsnprintf(s, SBUF_SIZE-3, fmt, ap);
+ write_len = vsnprintf(s, SBUF_SIZE-3, fmt, ap);
va_end(ap);
- strcat(s, "\r\n"); /* append a trailing CRLF */
+ strcpy(&s[write_len], "\r\n"); /* append a trailing CRLF */
+ write_len +=2;
bytes_written=0;
- write_len = strlen(s);
-#ifdef CURL_DOES_CONVERSIONS
- res = Curl_convert_to_network(conn->data, s, write_len);
+ result = Curl_convert_to_network(conn->data, s, write_len);
/* Curl_convert_to_network calls failf if unsuccessful */
- if(res != CURLE_OK) {
- return(res);
- }
-#endif /* CURL_DOES_CONVERSIONS */
+ if(result)
+ return(result);
for(;;) {
-#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
+#ifdef HAVE_GSSAPI
conn->data_prot = PROT_CMD;
#endif
- res = Curl_write(conn, conn->sock[FIRSTSOCKET], sptr, write_len,
- &bytes_written);
-#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
+ result = Curl_write(conn, conn->sock[FIRSTSOCKET], sptr, write_len,
+ &bytes_written);
+#ifdef HAVE_GSSAPI
DEBUGASSERT(data_sec > PROT_NONE && data_sec < PROT_LAST);
conn->data_prot = data_sec;
#endif
- if(CURLE_OK != res)
+ if(result)
break;
if(conn->data->set.verbose)
break;
}
- return res;
+ return result;
}
/***********************************************************************
CURLcode result = CURLE_OK;
if(conn->proto.ftpc.ctl_valid) {
- PPSENDF(&conn->proto.ftpc.pp, "QUIT", NULL);
+ result = Curl_pp_sendf(&conn->proto.ftpc.pp, "%s", "QUIT");
+ if(result) {
+ failf(conn->data, "Failure sending QUIT command: %s",
+ curl_easy_strerror(result));
+ conn->proto.ftpc.ctl_valid = FALSE; /* mark control connection as bad */
+ connclose(conn, "QUIT command failed"); /* mark for connection closure */
+ state(conn, FTP_STOP);
+ return result;
+ }
+
state(conn, FTP_QUIT);
- result = ftp_easy_statemach(conn);
+ result = ftp_block_statemach(conn);
}
return result;
Curl_pp_disconnect(pp);
-#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
+#ifdef HAVE_GSSAPI
Curl_sec_end(conn);
#endif
{
struct SessionHandle *data = conn->data;
/* the ftp struct is already inited in ftp_connect() */
- struct FTP *ftp = data->state.proto.ftp;
+ struct FTP *ftp = data->req.protop;
struct ftp_conn *ftpc = &conn->proto.ftpc;
const char *slash_pos; /* position of the first '/' char in curpos */
const char *path_to_use = data->state.path;
}
slash_pos=strrchr(cur_pos, '/');
if(slash_pos || !*cur_pos) {
+ size_t dirlen = slash_pos-cur_pos;
+
ftpc->dirs = calloc(1, sizeof(ftpc->dirs[0]));
if(!ftpc->dirs)
return CURLE_OUT_OF_MEMORY;
+ if(!dirlen)
+ dirlen++;
+
ftpc->dirs[0] = curl_easy_unescape(conn->data, slash_pos ? cur_pos : "/",
- slash_pos?(int)(slash_pos-cur_pos):1,
+ slash_pos ? curlx_uztosi(dirlen) : 1,
NULL);
if(!ftpc->dirs[0]) {
freedirs(ftpc);
/* 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
+ CWD requires a parameter and a non-existent parameter a) doesn't
work on many servers and b) has no effect on the others. */
- int len = (int)(slash_pos - cur_pos + absolute_dir);
+ int len = curlx_sztosi(slash_pos - cur_pos + absolute_dir);
ftpc->dirs[ftpc->dirdepth] =
curl_easy_unescape(conn->data, cur_pos - absolute_dir, len, NULL);
if(!ftpc->dirs[ftpc->dirdepth]) { /* run out of memory ... */
}
else {
cur_pos = slash_pos + 1; /* jump to the rest of the string */
+ if(!ftpc->dirdepth) {
+ /* path starts with a slash, add that as a directory */
+ ftpc->dirs[ftpc->dirdepth] = strdup("/");
+ if(!ftpc->dirs[ftpc->dirdepth++]) { /* run out of memory ... */
+ failf(data, "no memory");
+ freedirs(ftpc);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ }
continue;
}
cur_pos = slash_pos + 1; /* jump to the rest of the string */
if(++ftpc->dirdepth >= ftpc->diralloc) {
/* enlarge array */
- char *bigger;
+ char **bigger;
ftpc->diralloc *= 2; /* double the size each time */
bigger = realloc(ftpc->dirs, ftpc->diralloc * sizeof(ftpc->dirs[0]));
if(!bigger) {
freedirs(ftpc);
return CURLE_OUT_OF_MEMORY;
}
- ftpc->dirs = (char **)bigger;
+ ftpc->dirs = bigger;
}
}
}
return CURLE_OUT_OF_MEMORY;
}
- dlen -= ftpc->file?(int)strlen(ftpc->file):0;
- if((dlen == (int)strlen(ftpc->prevpath)) &&
+ dlen -= ftpc->file?curlx_uztosi(strlen(ftpc->file)):0;
+ if((dlen == curlx_uztosi(strlen(ftpc->prevpath))) &&
strnequal(path, ftpc->prevpath, dlen)) {
infof(data, "Request has same path as previous transfer\n");
ftpc->cwddone = TRUE;
static CURLcode ftp_dophase_done(struct connectdata *conn,
bool connected)
{
- CURLcode result = CURLE_OK;
- struct FTP *ftp = conn->data->state.proto.ftp;
+ struct FTP *ftp = conn->data->req.protop;
struct ftp_conn *ftpc = &conn->proto.ftpc;
- if(connected)
- result = ftp_nextconnect(conn);
+ if(connected) {
+ int completed;
+ CURLcode result = ftp_do_more(conn, &completed);
- if(result && (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;
- return result;
+ if(result) {
+ if(conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD) {
+ /* close the second socket if it was created already */
+ Curl_closesocket(conn, conn->sock[SECONDARYSOCKET]);
+ conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
+ }
+ return result;
+ }
}
if(ftp->transfer != FTPTRANSFER_BODY)
ftpc->ctl_valid = TRUE; /* seems good */
- return result;
+ return CURLE_OK;
}
/* called from multi.c while DOing */
static CURLcode ftp_doing(struct connectdata *conn,
- bool *dophase_done)
+ bool *dophase_done)
{
- CURLcode result;
- result = ftp_multi_statemach(conn, dophase_done);
+ CURLcode result = ftp_multi_statemach(conn, dophase_done);
- if(*dophase_done) {
+ if(result)
+ DEBUGF(infof(conn->data, "DO phase failed\n"));
+ else if(*dophase_done) {
result = ftp_dophase_done(conn, FALSE /* not connected */);
- DEBUGF(infof(conn->data, "DO phase is complete\n"));
+ DEBUGF(infof(conn->data, "DO phase is complete2\n"));
}
return result;
}
Curl_pgrsSetUploadCounter(data, 0);
Curl_pgrsSetDownloadCounter(data, 0);
- Curl_pgrsSetUploadSize(data, 0);
- Curl_pgrsSetDownloadSize(data, 0);
+ Curl_pgrsSetUploadSize(data, -1);
+ Curl_pgrsSetDownloadSize(data, -1);
ftpc->ctl_valid = TRUE; /* starts good */
&connected, /* have we connected after PASV/PORT */
dophase_done); /* all commands in the DO-phase done? */
- if(CURLE_OK == result) {
+ if(!result) {
if(!*dophase_done)
/* the DO phase has not completed yet */
return CURLE_OK;
result = ftp_dophase_done(conn, connected);
+
if(result)
return result;
}
return result;
}
-static CURLcode ftp_setup_connection(struct connectdata * conn)
+static CURLcode ftp_setup_connection(struct connectdata *conn)
{
struct SessionHandle *data = conn->data;
- char * type;
+ char *type;
char command;
+ struct FTP *ftp;
if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
/* Unless we have asked to tunnel ftp operations through the proxy, we
return CURLE_UNSUPPORTED_PROTOCOL;
#endif
}
- /*
- * We explicitly mark this connection as persistent here as we're doing
- * FTP over HTTP and thus we accidentally avoid setting this value
- * otherwise.
- */
- conn->bits.close = FALSE;
+ /* set it up as a HTTP connection instead */
+ return conn->handler->setup_connection(conn);
#else
failf(data, "FTP over http proxy requires HTTP support built-in!");
return CURLE_UNSUPPORTED_PROTOCOL;
#endif
}
+ conn->data->req.protop = ftp = malloc(sizeof(struct FTP));
+ if(NULL == ftp)
+ return CURLE_OUT_OF_MEMORY;
+
data->state.path++; /* don't include the initial slash */
data->state.slash_removed = TRUE; /* we've skipped the slash */
}
}
+ /* get some initial data into the ftp struct */
+ ftp->bytecountp = &conn->data->req.bytecount;
+ ftp->transfer = FTPTRANSFER_BODY;
+ ftp->downloadsize = 0;
+
+ /* No need to duplicate user+password, the connectdata struct won't change
+ during a session, but we re-init them here since on subsequent inits
+ since the conn struct may have changed or been replaced.
+ */
+ ftp->user = conn->user;
+ ftp->passwd = conn->passwd;
+ if(isBadFtpString(ftp->user))
+ return CURLE_URL_MALFORMAT;
+ if(isBadFtpString(ftp->passwd))
+ return CURLE_URL_MALFORMAT;
+
+ conn->proto.ftpc.known_filesize = -1; /* unknown size for now */
+
return CURLE_OK;
}