Initial fix to make the multi interface return control while waiting for

the initial connect to "come through".

This should work fine for connect and for FTP-PASV connects. Needs massive
testing.
This commit is contained in:
Daniel Stenberg
2002-08-12 09:43:20 +00:00
parent 2df4866cfa
commit cb895ec335
8 changed files with 433 additions and 170 deletions

282
lib/ftp.c
View File

@@ -387,8 +387,10 @@ int Curl_GetFTPResponse(char *buf,
return nread; /* total amount of bytes read */
}
/* ftp_connect() should do everything that is to be considered a part
of the connection phase. */
/*
* Curl_ftp_connect() should do everything that is to be considered a part of
* the connection phase.
*/
CURLcode Curl_ftp_connect(struct connectdata *conn)
{
/* this is FTP and no proxy */
@@ -1321,7 +1323,8 @@ CURLcode ftp_use_port(struct connectdata *conn)
*/
static
CURLcode ftp_use_pasv(struct connectdata *conn)
CURLcode ftp_use_pasv(struct connectdata *conn,
bool *connected)
{
struct SessionHandle *data = conn->data;
ssize_t nread;
@@ -1473,7 +1476,14 @@ CURLcode ftp_use_pasv(struct connectdata *conn)
addr,
connectport,
&conn->secondarysocket,
&conninfo);
&conninfo,
connected);
/*
* 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((CURLE_OK == result) &&
data->set.verbose)
@@ -1494,127 +1504,24 @@ CURLcode ftp_use_pasv(struct connectdata *conn)
return CURLE_OK;
}
/***********************************************************************
/*
* Curl_ftp_nextconnect()
*
* ftp_perform()
*
* This is the actual DO function for FTP. Get a file/directory according to
* the options previously setup.
* This function shall be called when the second FTP connection has been
* established and is confirmed connected.
*/
static
CURLcode ftp_perform(struct connectdata *conn)
CURLcode Curl_ftp_nextconnect(struct connectdata *conn)
{
/* this is FTP and no proxy */
ssize_t nread;
CURLcode result=CURLE_OK;
struct SessionHandle *data=conn->data;
char *buf = data->state.buffer; /* this is our buffer */
CURLcode result;
ssize_t nread;
int ftpcode; /* for ftp status */
/* the ftp struct is already inited in ftp_connect() */
struct FTP *ftp = conn->proto.ftp;
long *bytecountp = ftp->bytecountp;
int ftpcode; /* for ftp status */
/* Send any QUOTE strings? */
if(data->set.quote) {
if ((result = ftp_sendquote(conn, data->set.quote)) != CURLE_OK)
return result;
}
/* This is a re-used connection. Since we change directory to where the
transfer is taking place, we must now get back to the original dir
where we ended up after login: */
if (conn->bits.reuse) {
if ((result = ftp_cwd(conn, ftp->entrypath)) != CURLE_OK)
return result;
}
/* change directory first! */
if(ftp->dir && ftp->dir[0]) {
if ((result = ftp_cwd(conn, ftp->dir)) != CURLE_OK)
return result;
}
/* Requested time of file? */
if(data->set.get_filetime && ftp->file) {
result = ftp_getfiletime(conn, ftp->file);
if(result)
return result;
}
/* If we have selected NOBODY and HEADER, it means that we only want file
information. Which in FTP can't be much more than the file size and
date. */
if(data->set.no_body && data->set.include_header && ftp->file) {
/* The SIZE command is _not_ RFC 959 specified, and therefor many servers
may not support it! It is however the only way we have to get a file's
size! */
ssize_t filesize;
ftp->no_transfer = TRUE; /* this means no actual transfer is made */
/* Some servers return different sizes for different modes, and thus we
must set the proper type before we check the size */
result = ftp_transfertype(conn, data->set.ftp_ascii);
if(result)
return result;
/* failing to get size is not a serious error */
result = ftp_getsize(conn, ftp->file, &filesize);
if(CURLE_OK == result) {
sprintf(buf, "Content-Length: %d\r\n", filesize);
result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0);
if(result)
return result;
}
/* If we asked for a time of the file and we actually got one as
well, we "emulate" a HTTP-style header in our output. */
#ifdef HAVE_STRFTIME
if(data->set.get_filetime && data->info.filetime) {
struct tm *tm;
#ifdef HAVE_LOCALTIME_R
struct tm buffer;
tm = (struct tm *)localtime_r(&data->info.filetime, &buffer);
#else
tm = localtime((unsigned long *)&data->info.filetime);
#endif
/* format: "Tue, 15 Nov 1994 12:45:26 GMT" */
strftime(buf, BUFSIZE-1, "Last-Modified: %a, %d %b %Y %H:%M:%S %Z\r\n",
tm);
result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0);
if(result)
return result;
}
#endif
return CURLE_OK;
}
if(data->set.no_body)
/* doesn't really transfer any data */
ftp->no_transfer = TRUE;
/* Get us a second connection up and connected */
else if(data->set.ftp_use_port) {
/* We have chosen to use the PORT command */
result = ftp_use_port(conn);
if(CURLE_OK == result)
/* we have the data connection ready */
infof(data, "Connected the data stream with PORT!\n");
}
else {
/* We have chosen (this is default) to use the PASV command */
result = ftp_use_pasv(conn);
if(CURLE_OK == result)
infof(data, "Connected the data stream with PASV!\n");
}
if(result)
return result;
if(data->set.upload) {
@@ -1991,6 +1898,128 @@ CURLcode ftp_perform(struct connectdata *conn)
return CURLE_OK;
}
/***********************************************************************
*
* ftp_perform()
*
* This is the actual DO function for FTP. Get a file/directory according to
* the options previously setup.
*/
static
CURLcode ftp_perform(struct connectdata *conn,
bool *connected) /* for the TCP connect status after
PASV / PORT */
{
/* this is FTP and no proxy */
CURLcode result=CURLE_OK;
struct SessionHandle *data=conn->data;
char *buf = data->state.buffer; /* this is our buffer */
/* the ftp struct is already inited in ftp_connect() */
struct FTP *ftp = conn->proto.ftp;
/* Send any QUOTE strings? */
if(data->set.quote) {
if ((result = ftp_sendquote(conn, data->set.quote)) != CURLE_OK)
return result;
}
/* This is a re-used connection. Since we change directory to where the
transfer is taking place, we must now get back to the original dir
where we ended up after login: */
if (conn->bits.reuse) {
if ((result = ftp_cwd(conn, ftp->entrypath)) != CURLE_OK)
return result;
}
/* change directory first! */
if(ftp->dir && ftp->dir[0]) {
if ((result = ftp_cwd(conn, ftp->dir)) != CURLE_OK)
return result;
}
/* Requested time of file? */
if(data->set.get_filetime && ftp->file) {
result = ftp_getfiletime(conn, ftp->file);
if(result)
return result;
}
/* If we have selected NOBODY and HEADER, it means that we only want file
information. Which in FTP can't be much more than the file size and
date. */
if(data->set.no_body && data->set.include_header && ftp->file) {
/* The SIZE command is _not_ RFC 959 specified, and therefor many servers
may not support it! It is however the only way we have to get a file's
size! */
ssize_t filesize;
ftp->no_transfer = TRUE; /* this means no actual transfer is made */
/* Some servers return different sizes for different modes, and thus we
must set the proper type before we check the size */
result = ftp_transfertype(conn, data->set.ftp_ascii);
if(result)
return result;
/* failing to get size is not a serious error */
result = ftp_getsize(conn, ftp->file, &filesize);
if(CURLE_OK == result) {
sprintf(buf, "Content-Length: %d\r\n", filesize);
result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0);
if(result)
return result;
}
/* If we asked for a time of the file and we actually got one as
well, we "emulate" a HTTP-style header in our output. */
#ifdef HAVE_STRFTIME
if(data->set.get_filetime && data->info.filetime) {
struct tm *tm;
#ifdef HAVE_LOCALTIME_R
struct tm buffer;
tm = (struct tm *)localtime_r(&data->info.filetime, &buffer);
#else
tm = localtime((unsigned long *)&data->info.filetime);
#endif
/* format: "Tue, 15 Nov 1994 12:45:26 GMT" */
strftime(buf, BUFSIZE-1, "Last-Modified: %a, %d %b %Y %H:%M:%S %Z\r\n",
tm);
result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0);
if(result)
return result;
}
#endif
return CURLE_OK;
}
if(data->set.no_body)
/* doesn't really transfer any data */
ftp->no_transfer = TRUE;
/* Get us a second connection up and connected */
else if(data->set.ftp_use_port) {
/* We have chosen to use the PORT command */
result = ftp_use_port(conn);
if(CURLE_OK == result) {
/* we have the data connection ready */
infof(data, "Ordered connect of the data stream with PORT!\n");
*connected = TRUE; /* mark us "still connected" */
}
}
else {
/* We have chosen (this is default) to use the PASV command */
result = ftp_use_pasv(conn, connected);
if(connected)
infof(data, "Connected the data stream with PASV!\n");
}
return result;
}
/***********************************************************************
*
* Curl_ftp()
@@ -2003,6 +2032,7 @@ CURLcode ftp_perform(struct connectdata *conn)
CURLcode Curl_ftp(struct connectdata *conn)
{
CURLcode retcode;
bool connected;
struct SessionHandle *data = conn->data;
struct FTP *ftp;
@@ -2049,15 +2079,15 @@ CURLcode Curl_ftp(struct connectdata *conn)
else
ftp->dir = NULL;
retcode = ftp_perform(conn);
retcode = ftp_perform(conn, &connected);
/* clean up here, success or error doesn't matter */
if(ftp->file)
free(ftp->file);
if(ftp->dir)
free(ftp->dir);
ftp->file = ftp->dir = NULL; /* zero */
if(CURLE_OK == retcode) {
if(connected)
retcode = Curl_ftp_nextconnect(conn);
else
/* since we didn't connect now, we want do_more to get called */
conn->do_more = TRUE;
}
return retcode;
}
@@ -2128,6 +2158,12 @@ CURLcode Curl_ftp_disconnect(struct connectdata *conn)
free(ftp->entrypath);
if(ftp->cache)
free(ftp->cache);
if(ftp->file)
free(ftp->file);
if(ftp->dir)
free(ftp->dir);
ftp->file = ftp->dir = NULL; /* zero */
}
return CURLE_OK;
}