mirror of
https://github.com/moparisthebest/curl
synced 2024-12-21 23:58:49 -05:00
introducing IMAP, POP3 and SMTP support (still lots of polish left to do)
This commit is contained in:
parent
463d2d395c
commit
ec3bb8f727
@ -613,6 +613,12 @@ typedef enum {
|
||||
#define CURLPROTO_DICT (1<<9)
|
||||
#define CURLPROTO_FILE (1<<10)
|
||||
#define CURLPROTO_TFTP (1<<11)
|
||||
#define CURLPROTO_IMAP (1<<12)
|
||||
#define CURLPROTO_IMAPS (1<<13)
|
||||
#define CURLPROTO_POP3 (1<<14)
|
||||
#define CURLPROTO_POP3S (1<<15)
|
||||
#define CURLPROTO_SMTP (1<<16)
|
||||
#define CURLPROTO_SMTPS (1<<17)
|
||||
#define CURLPROTO_ALL (~0) /* enable everything */
|
||||
|
||||
/* long may be 32 or 64 bits, but we should never depend on anything else
|
||||
@ -1028,6 +1034,7 @@ typedef enum {
|
||||
essentially places a demand on the FTP server to acknowledge commands
|
||||
in a timely manner. */
|
||||
CINIT(FTP_RESPONSE_TIMEOUT, LONG, 112),
|
||||
#define CURLOPT_SERVER_RESPONSE_TIMEOUT CURLOPT_FTP_RESPONSE_TIMEOUT
|
||||
|
||||
/* Set this option to one of the CURL_IPRESOLVE_* defines (see below) to
|
||||
tell libcurl to resolve names to those IP versions only. This only has
|
||||
@ -1272,6 +1279,12 @@ typedef enum {
|
||||
/* set the SSH host key callback custom pointer */
|
||||
CINIT(SSH_KEYDATA, OBJECTPOINT, 185),
|
||||
|
||||
/* set the SMTP mail originator */
|
||||
CINIT(MAIL_FROM, OBJECTPOINT, 186),
|
||||
|
||||
/* set the SMTP mail receiver(s) */
|
||||
CINIT(MAIL_RCPT, OBJECTPOINT, 187),
|
||||
|
||||
CURLOPT_LASTENTRY /* the last unused */
|
||||
} CURLoption;
|
||||
|
||||
|
@ -27,7 +27,7 @@ VCPROJ = libcurl.vcproj
|
||||
|
||||
DOCS = README.encoding README.memoryleak README.ares README.curlx \
|
||||
README.hostip README.multi_socket README.httpauth README.pipelining \
|
||||
README.curl_off_t README.cmake
|
||||
README.curl_off_t README.cmake README.pingpong
|
||||
|
||||
CMAKE_DIST = CMakeLists.txt curl_config.h.cmake
|
||||
|
||||
|
@ -11,7 +11,7 @@ CSOURCES = file.c timeval.c base64.c hostip.c progress.c formdata.c \
|
||||
inet_ntop.c parsedate.c select.c gtls.c sslgen.c tftp.c splay.c \
|
||||
strdup.c socks.c ssh.c nss.c qssl.c rawstr.c curl_addrinfo.c \
|
||||
socks_gssapi.c socks_sspi.c curl_sspi.c slist.c nonblock.c \
|
||||
curl_memrchr.c
|
||||
curl_memrchr.c imap.c pop3.c smtp.c pingpong.c
|
||||
|
||||
HHEADERS = arpa_telnet.h netrc.h file.h timeval.h qssl.h hostip.h \
|
||||
progress.h formdata.h cookie.h http.h sendf.h ftp.h url.h dict.h \
|
||||
@ -23,5 +23,4 @@ HHEADERS = arpa_telnet.h netrc.h file.h timeval.h qssl.h hostip.h \
|
||||
transfer.h select.h easyif.h multiif.h parsedate.h sslgen.h gtls.h \
|
||||
tftp.h sockaddr.h splay.h strdup.h setup_once.h socks.h ssh.h nssg.h \
|
||||
curl_base64.h rawstr.h curl_addrinfo.h curl_sspi.h slist.h nonblock.h \
|
||||
curl_memrchr.h
|
||||
|
||||
curl_memrchr.h imap.h pop3.h smtp.h pingpong.h
|
||||
|
30
lib/README.pingpong
Normal file
30
lib/README.pingpong
Normal file
@ -0,0 +1,30 @@
|
||||
Date: December 5, 2009
|
||||
|
||||
Pingpong
|
||||
========
|
||||
|
||||
Pingpong is just my (Daniel's) jestful collective name on the protocols that
|
||||
share a very similar kind of back-and-forth procedure with command and
|
||||
responses to and from the server. FTP was previously the only protocol in
|
||||
that family that libcurl supported, but when POP3, IMAP and SMTP joined the
|
||||
team I moved some of the internals into a separate pingpong module to be
|
||||
easier to get used by all these protocols to reduce code duplication and ease
|
||||
code re-use between these protocols.
|
||||
|
||||
FTP
|
||||
|
||||
In 7.20.0 we converted code to use the new pingpong code from previously
|
||||
having been all "native" FTP code.
|
||||
|
||||
POP3
|
||||
|
||||
There's no support in the documented URL format to specify the exact mail to
|
||||
get, but we support that as the path specified in the URL.
|
||||
|
||||
IMAP
|
||||
|
||||
SMTP
|
||||
|
||||
There's no official URL syntax defined for SMTP, but we use only the generic
|
||||
one and we provide two additional libcurl options to specify receivers and
|
||||
sender of the actual mail.
|
571
lib/ftp.c
571
lib/ftp.c
@ -106,14 +106,6 @@
|
||||
#define INET_ADDRSTRLEN 16
|
||||
#endif
|
||||
|
||||
#ifdef __SYMBIAN32__
|
||||
/* Symbian OS panics when given a timeout much greater than 1/2 hour */
|
||||
#define RESP_TIMEOUT (1800*1000)
|
||||
#else
|
||||
/* Default response timeout in milliseconds */
|
||||
#define RESP_TIMEOUT (3600*1000)
|
||||
#endif
|
||||
|
||||
#ifdef CURL_DISABLE_VERBOSE_STRINGS
|
||||
#define ftp_pasv_verbose(a,b,c,d) do { } while(0)
|
||||
#endif
|
||||
@ -155,7 +147,7 @@ static CURLcode ftp_setup_connection(struct connectdata * conn);
|
||||
/* easy-to-use macro: */
|
||||
#define FTPSENDF(x,y,z) if((result = Curl_ftpsendf(x,y,z)) != CURLE_OK) \
|
||||
return result
|
||||
#define NBFTPSENDF(x,y,z) if((result = Curl_nbftpsendf(x,y,z)) != CURLE_OK) \
|
||||
#define PPSENDF(x,y,z) if((result = Curl_pp_sendf(x,y,z)) != CURLE_OK) \
|
||||
return result
|
||||
|
||||
|
||||
@ -362,15 +354,6 @@ static CURLcode AllowServerConnect(struct connectdata *conn)
|
||||
/* never reaches this point */
|
||||
}
|
||||
|
||||
/* initialize stuff to prepare for reading a fresh new response */
|
||||
static void ftp_respinit(struct connectdata *conn)
|
||||
{
|
||||
struct ftp_conn *ftpc = &conn->proto.ftpc;
|
||||
ftpc->nread_resp = 0;
|
||||
ftpc->linestart_resp = conn->data->state.buffer;
|
||||
ftpc->pending_resp = TRUE;
|
||||
}
|
||||
|
||||
/* macro to check for a three-digit ftp status code at the start of the
|
||||
given string */
|
||||
#define STATUSCODE(line) (ISDIGIT(line[0]) && ISDIGIT(line[1]) && \
|
||||
@ -379,200 +362,31 @@ static void ftp_respinit(struct connectdata *conn)
|
||||
/* 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)
|
||||
{
|
||||
char *line = pp->linestart_resp;
|
||||
size_t len = pp->nread_resp;
|
||||
|
||||
if((len > 3) && LASTLINE(line)) {
|
||||
*code = atoi(line);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static CURLcode ftp_readresp(curl_socket_t sockfd,
|
||||
struct connectdata *conn,
|
||||
struct pingpong *pp,
|
||||
int *ftpcode, /* return the ftp-code if done */
|
||||
size_t *size) /* size of the response */
|
||||
{
|
||||
ssize_t perline; /* count bytes per line */
|
||||
bool keepon=TRUE;
|
||||
ssize_t gotbytes;
|
||||
char *ptr;
|
||||
struct connectdata *conn = pp->conn;
|
||||
struct SessionHandle *data = conn->data;
|
||||
char * const buf = data->state.buffer;
|
||||
CURLcode result = CURLE_OK;
|
||||
struct ftp_conn *ftpc = &conn->proto.ftpc;
|
||||
int code = 0;
|
||||
int code;
|
||||
|
||||
*ftpcode = 0; /* 0 for errors or not done */
|
||||
*size = 0;
|
||||
|
||||
ptr=buf + ftpc->nread_resp;
|
||||
|
||||
/* number of bytes in the current line, so far */
|
||||
perline = (ssize_t)(ptr-ftpc->linestart_resp);
|
||||
|
||||
keepon=TRUE;
|
||||
|
||||
while((ftpc->nread_resp<BUFSIZE) && (keepon && !result)) {
|
||||
|
||||
if(ftpc->cache) {
|
||||
/* we had data in the "cache", copy that instead of doing an actual
|
||||
* read
|
||||
*
|
||||
* ftp->cache_size is cast to int here. This should be safe,
|
||||
* because it would have been populated with something of size
|
||||
* int to begin with, even though its datatype may be larger
|
||||
* than an int.
|
||||
*/
|
||||
DEBUGASSERT((ptr+ftpc->cache_size) <= (buf+BUFSIZE+1));
|
||||
memcpy(ptr, ftpc->cache, (int)ftpc->cache_size);
|
||||
gotbytes = (int)ftpc->cache_size;
|
||||
free(ftpc->cache); /* free the cache */
|
||||
ftpc->cache = NULL; /* clear the pointer */
|
||||
ftpc->cache_size = 0; /* zero the size just in case */
|
||||
}
|
||||
else {
|
||||
int res;
|
||||
#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
|
||||
enum protection_level prot = conn->data_prot;
|
||||
|
||||
conn->data_prot = 0;
|
||||
#endif
|
||||
DEBUGASSERT((ptr+BUFSIZE-ftpc->nread_resp) <= (buf+BUFSIZE+1));
|
||||
res = Curl_read(conn, sockfd, ptr, BUFSIZE-ftpc->nread_resp,
|
||||
&gotbytes);
|
||||
#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
|
||||
conn->data_prot = prot;
|
||||
#endif
|
||||
if(res < 0)
|
||||
/* EWOULDBLOCK */
|
||||
return CURLE_OK; /* return */
|
||||
|
||||
#ifdef CURL_DOES_CONVERSIONS
|
||||
if((res == CURLE_OK) && (gotbytes > 0)) {
|
||||
/* convert from the network encoding */
|
||||
res = Curl_convert_from_network(data, ptr, gotbytes);
|
||||
/* Curl_convert_from_network calls failf if unsuccessful */
|
||||
}
|
||||
#endif /* CURL_DOES_CONVERSIONS */
|
||||
|
||||
if(CURLE_OK != res) {
|
||||
result = (CURLcode)res; /* Set outer result variable to this error. */
|
||||
keepon = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if(!keepon)
|
||||
;
|
||||
else if(gotbytes <= 0) {
|
||||
keepon = FALSE;
|
||||
result = CURLE_RECV_ERROR;
|
||||
failf(data, "FTP response reading failed");
|
||||
}
|
||||
else {
|
||||
/* we got a whole chunk of data, which can be anything from one
|
||||
* byte to a set of lines and possible just a piece of the last
|
||||
* line */
|
||||
ssize_t i;
|
||||
ssize_t clipamount = 0;
|
||||
bool restart = FALSE;
|
||||
|
||||
data->req.headerbytecount += gotbytes;
|
||||
|
||||
ftpc->nread_resp += gotbytes;
|
||||
for(i = 0; i < gotbytes; ptr++, i++) {
|
||||
perline++;
|
||||
if(*ptr=='\n') {
|
||||
/* a newline is CRLF in ftp-talk, so the CR is ignored as
|
||||
the line isn't really terminated until the LF comes */
|
||||
|
||||
/* output debug output if that is requested */
|
||||
#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
|
||||
if(!conn->sec_complete)
|
||||
#endif
|
||||
if(data->set.verbose)
|
||||
Curl_debug(data, CURLINFO_HEADER_IN,
|
||||
ftpc->linestart_resp, (size_t)perline, conn);
|
||||
|
||||
/*
|
||||
* We pass all response-lines to the callback function registered
|
||||
* for "headers". The response lines can be seen as a kind of
|
||||
* headers.
|
||||
*/
|
||||
result = Curl_client_write(conn, CLIENTWRITE_HEADER,
|
||||
ftpc->linestart_resp, perline);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
if(perline>3 && LASTLINE(ftpc->linestart_resp)) {
|
||||
/* This is the end of the last line, copy the last line to the
|
||||
start of the buffer and zero terminate, for old times sake (and
|
||||
krb4)! */
|
||||
char *meow;
|
||||
int n;
|
||||
for(meow=ftpc->linestart_resp, n=0; meow<ptr; meow++, n++)
|
||||
buf[n] = *meow;
|
||||
*meow=0; /* zero terminate */
|
||||
keepon=FALSE;
|
||||
ftpc->linestart_resp = ptr+1; /* advance pointer */
|
||||
i++; /* skip this before getting out */
|
||||
|
||||
*size = ftpc->nread_resp; /* size of the response */
|
||||
ftpc->nread_resp = 0; /* restart */
|
||||
break;
|
||||
}
|
||||
perline=0; /* line starts over here */
|
||||
ftpc->linestart_resp = ptr+1;
|
||||
}
|
||||
}
|
||||
|
||||
if(!keepon && (i != gotbytes)) {
|
||||
/* We found the end of the response lines, but we didn't parse the
|
||||
full chunk of data we have read from the server. We therefore need
|
||||
to store the rest of the data to be checked on the next invoke as
|
||||
it may actually contain another end of response already! */
|
||||
clipamount = gotbytes - i;
|
||||
restart = TRUE;
|
||||
}
|
||||
else if(keepon) {
|
||||
|
||||
if((perline == gotbytes) && (gotbytes > BUFSIZE/2)) {
|
||||
/* We got an excessive line without newlines and we need to deal
|
||||
with it. First, check if it seems to start with a valid status
|
||||
code and then we keep just that in the line cache. Then throw
|
||||
away the rest. */
|
||||
infof(data, "Excessive FTP response line length received, %zd bytes."
|
||||
" Stripping\n", gotbytes);
|
||||
restart = TRUE;
|
||||
if(STATUSCODE(ftpc->linestart_resp))
|
||||
/* we copy 4 bytes since after the three-digit number there is a
|
||||
dash or a space and it is significant */
|
||||
clipamount = 4;
|
||||
}
|
||||
else if(ftpc->nread_resp > BUFSIZE/2) {
|
||||
/* We got a large chunk of data and there's potentially still trailing
|
||||
data to take care of, so we put any such part in the "cache", clear
|
||||
the buffer to make space and restart. */
|
||||
clipamount = perline;
|
||||
restart = TRUE;
|
||||
}
|
||||
}
|
||||
else if(i == gotbytes)
|
||||
restart = TRUE;
|
||||
|
||||
if(clipamount) {
|
||||
ftpc->cache_size = clipamount;
|
||||
ftpc->cache = malloc((int)ftpc->cache_size);
|
||||
if(ftpc->cache)
|
||||
memcpy(ftpc->cache, ftpc->linestart_resp, (int)ftpc->cache_size);
|
||||
else
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
}
|
||||
if(restart) {
|
||||
/* now reset a few variables to start over nicely from the start of
|
||||
the big buffer */
|
||||
ftpc->nread_resp = 0; /* start over from scratch in the buffer */
|
||||
ptr = ftpc->linestart_resp = buf;
|
||||
perline = 0;
|
||||
}
|
||||
|
||||
} /* there was data */
|
||||
|
||||
} /* while there's buffer left and loop is requested */
|
||||
|
||||
if(!result)
|
||||
code = atoi(buf);
|
||||
result = Curl_pp_readresp(sockfd, pp, &code, size);
|
||||
|
||||
#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
|
||||
/* handle the security-oriented responses 6xx ***/
|
||||
@ -593,12 +407,11 @@ static CURLcode ftp_readresp(curl_socket_t sockfd,
|
||||
}
|
||||
#endif
|
||||
|
||||
*ftpcode=code; /* return the initial number like this */
|
||||
|
||||
/* store the latest code for later retrieval */
|
||||
conn->data->info.httpcode=code;
|
||||
|
||||
ftpc->pending_resp = FALSE;
|
||||
if(ftpcode)
|
||||
*ftpcode = code;
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -628,7 +441,7 @@ CURLcode Curl_GetFTPResponse(ssize_t *nreadp, /* return number of bytes read */
|
||||
struct SessionHandle *data = conn->data;
|
||||
CURLcode result = CURLE_OK;
|
||||
struct ftp_conn *ftpc = &conn->proto.ftpc;
|
||||
struct timeval now = Curl_tvnow();
|
||||
struct pingpong *pp = &ftpc->pp;
|
||||
size_t nread;
|
||||
int cache_skip=0;
|
||||
int value_to_be_ignored=0;
|
||||
@ -643,23 +456,7 @@ CURLcode Curl_GetFTPResponse(ssize_t *nreadp, /* return number of bytes read */
|
||||
|
||||
while(!*ftpcode && !result) {
|
||||
/* check and reset timeout value every lap */
|
||||
if(data->set.ftp_response_timeout )
|
||||
/* if CURLOPT_FTP_RESPONSE_TIMEOUT is set, use that to determine
|
||||
remaining time. Also, use "now" as opposed to "conn->now"
|
||||
because ftp_response_timeout is only supposed to govern
|
||||
the response for any given ftp response, not for the time
|
||||
from connect to the given ftp response. */
|
||||
timeout = data->set.ftp_response_timeout - /* timeout time */
|
||||
Curl_tvdiff(Curl_tvnow(), now); /* spent time */
|
||||
else if(data->set.timeout)
|
||||
/* if timeout is requested, find out how much remaining time we have */
|
||||
timeout = data->set.timeout - /* timeout time */
|
||||
Curl_tvdiff(Curl_tvnow(), conn->now); /* spent time */
|
||||
else
|
||||
/* Even without a requested timeout, we only wait response_time
|
||||
seconds for the full response to arrive before we bail out */
|
||||
timeout = ftpc->response_time -
|
||||
Curl_tvdiff(Curl_tvnow(), now); /* spent time */
|
||||
timeout = Curl_pp_state_timeout(pp);
|
||||
|
||||
if(timeout <=0 ) {
|
||||
failf(data, "FTP response timeout");
|
||||
@ -684,7 +481,7 @@ CURLcode Curl_GetFTPResponse(ssize_t *nreadp, /* return number of bytes read */
|
||||
*
|
||||
*/
|
||||
|
||||
if(ftpc->cache && (cache_skip < 2)) {
|
||||
if(pp->cache && (cache_skip < 2)) {
|
||||
/*
|
||||
* There's a cache left since before. We then skipping the wait for
|
||||
* socket action, unless this is the same cache like the previous round
|
||||
@ -708,11 +505,11 @@ CURLcode Curl_GetFTPResponse(ssize_t *nreadp, /* return number of bytes read */
|
||||
break;
|
||||
}
|
||||
}
|
||||
result = ftp_readresp(sockfd, conn, ftpcode, &nread);
|
||||
result = ftp_readresp(sockfd, pp, ftpcode, &nread);
|
||||
if(result)
|
||||
break;
|
||||
|
||||
if(!nread && ftpc->cache)
|
||||
if(!nread && pp->cache)
|
||||
/* bump cache skip counter as on repeated skips we must wait for more
|
||||
data */
|
||||
cache_skip++;
|
||||
@ -725,7 +522,7 @@ CURLcode Curl_GetFTPResponse(ssize_t *nreadp, /* return number of bytes read */
|
||||
|
||||
} /* while there's buffer left and loop is requested */
|
||||
|
||||
ftpc->pending_resp = FALSE;
|
||||
pp->pending_resp = FALSE;
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -787,7 +584,7 @@ static CURLcode ftp_state_user(struct connectdata *conn)
|
||||
CURLcode result;
|
||||
struct FTP *ftp = conn->data->state.proto.ftp;
|
||||
/* send USER */
|
||||
NBFTPSENDF(conn, "USER %s", ftp->user?ftp->user:"");
|
||||
PPSENDF(&conn->proto.ftpc.pp, "USER %s", ftp->user?ftp->user:"");
|
||||
|
||||
state(conn, FTP_USER);
|
||||
conn->data->state.ftp_trying_alternative = FALSE;
|
||||
@ -800,7 +597,7 @@ static CURLcode ftp_state_pwd(struct connectdata *conn)
|
||||
CURLcode result;
|
||||
|
||||
/* send PWD to discover our entry point */
|
||||
NBFTPSENDF(conn, "PWD", NULL);
|
||||
PPSENDF(&conn->proto.ftpc.pp, "PWD", NULL);
|
||||
state(conn, FTP_PWD);
|
||||
|
||||
return CURLE_OK;
|
||||
@ -811,20 +608,7 @@ static int ftp_getsock(struct connectdata *conn,
|
||||
curl_socket_t *socks,
|
||||
int numsocks)
|
||||
{
|
||||
struct ftp_conn *ftpc = &conn->proto.ftpc;
|
||||
|
||||
if(!numsocks)
|
||||
return GETSOCK_BLANK;
|
||||
|
||||
socks[0] = conn->sock[FIRSTSOCKET];
|
||||
|
||||
if(ftpc->sendleft) {
|
||||
/* write mode */
|
||||
return GETSOCK_WRITESOCK(0);
|
||||
}
|
||||
|
||||
/* read mode */
|
||||
return GETSOCK_READSOCK(0);
|
||||
return Curl_pp_getsock(&conn->proto.ftpc.pp, socks, numsocks);
|
||||
}
|
||||
|
||||
/* This is called after the FTP_QUOTE state is passed.
|
||||
@ -855,7 +639,7 @@ static CURLcode ftp_state_cwd(struct connectdata *conn)
|
||||
where we ended up after login: */
|
||||
ftpc->count1 = 0; /* we count this as the first path, then we add one
|
||||
for all upcoming ones in the ftp->dirs[] array */
|
||||
NBFTPSENDF(conn, "CWD %s", ftpc->entrypath);
|
||||
PPSENDF(&conn->proto.ftpc.pp, "CWD %s", ftpc->entrypath);
|
||||
state(conn, FTP_CWD);
|
||||
}
|
||||
else {
|
||||
@ -863,7 +647,7 @@ static CURLcode ftp_state_cwd(struct connectdata *conn)
|
||||
ftpc->count1 = 1;
|
||||
/* issue the first CWD, the rest is sent when the CWD responses are
|
||||
received... */
|
||||
NBFTPSENDF(conn, "CWD %s", ftpc->dirs[ftpc->count1 -1]);
|
||||
PPSENDF(&conn->proto.ftpc.pp, "CWD %s", ftpc->dirs[ftpc->count1 -1]);
|
||||
state(conn, FTP_CWD);
|
||||
}
|
||||
else {
|
||||
@ -1190,7 +974,7 @@ static CURLcode ftp_state_use_port(struct connectdata *conn,
|
||||
* EPRT |2|1080::8:800:200C:417A|5282|
|
||||
*/
|
||||
|
||||
result = Curl_nbftpsendf(conn, "%s |%d|%s|%d|", mode[fcmd],
|
||||
result = Curl_pp_sendf(&ftpc->pp, "%s |%d|%s|%d|", mode[fcmd],
|
||||
sa->sa_family == AF_INET?1:2,
|
||||
myhost, port);
|
||||
if(result)
|
||||
@ -1213,7 +997,7 @@ static CURLcode ftp_state_use_port(struct connectdata *conn,
|
||||
*dest = 0;
|
||||
snprintf(dest, 20, ",%d,%d", port>>8, port&0xff);
|
||||
|
||||
result = Curl_nbftpsendf(conn, "%s %s", mode[fcmd], tmp);
|
||||
result = Curl_pp_sendf(&ftpc->pp, "%s %s", mode[fcmd], tmp);
|
||||
if(result)
|
||||
return result;
|
||||
break;
|
||||
@ -1273,9 +1057,7 @@ static CURLcode ftp_state_use_pasv(struct connectdata *conn)
|
||||
|
||||
modeoff = conn->bits.ftp_use_epsv?0:1;
|
||||
|
||||
result = Curl_nbftpsendf(conn, "%s", mode[modeoff]);
|
||||
if(result)
|
||||
return result;
|
||||
PPSENDF(&ftpc->pp, "%s", mode[modeoff]);
|
||||
|
||||
ftpc->count1 = modeoff;
|
||||
state(conn, FTP_PASV);
|
||||
@ -1322,7 +1104,7 @@ static CURLcode ftp_state_post_size(struct connectdata *conn)
|
||||
|
||||
/* Determine if server can respond to REST command and therefore
|
||||
whether it supports range */
|
||||
NBFTPSENDF(conn, "REST %d", 0);
|
||||
PPSENDF(&conn->proto.ftpc.pp, "REST %d", 0);
|
||||
|
||||
state(conn, FTP_REST);
|
||||
}
|
||||
@ -1342,7 +1124,7 @@ static CURLcode ftp_state_post_type(struct connectdata *conn)
|
||||
/* if a "head"-like request is being made (on a file) */
|
||||
|
||||
/* we know ftpc->file is a valid pointer to a file name */
|
||||
NBFTPSENDF(conn, "SIZE %s", ftpc->file);
|
||||
PPSENDF(&ftpc->pp, "SIZE %s", ftpc->file);
|
||||
|
||||
state(conn, FTP_SIZE);
|
||||
}
|
||||
@ -1406,7 +1188,7 @@ static CURLcode ftp_state_post_listtype(struct connectdata *conn)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
NBFTPSENDF(conn, "%s",cmd);
|
||||
PPSENDF(&conn->proto.ftpc.pp, "%s",cmd);
|
||||
|
||||
if(lstArg)
|
||||
free(lstArg);
|
||||
@ -1484,7 +1266,7 @@ static CURLcode ftp_state_post_cwd(struct connectdata *conn)
|
||||
|
||||
/* we have requested to get the modified-time of the file, this is a white
|
||||
spot as the MDTM is not mentioned in RFC959 */
|
||||
NBFTPSENDF(conn, "MDTM %s", ftpc->file);
|
||||
PPSENDF(&ftpc->pp, "MDTM %s", ftpc->file);
|
||||
|
||||
state(conn, FTP_MDTM);
|
||||
}
|
||||
@ -1522,7 +1304,7 @@ static CURLcode ftp_state_ul_setup(struct connectdata *conn,
|
||||
|
||||
if(data->state.resume_from < 0 ) {
|
||||
/* Got no given size to start from, figure it out */
|
||||
NBFTPSENDF(conn, "SIZE %s", ftpc->file);
|
||||
PPSENDF(&ftpc->pp, "SIZE %s", ftpc->file);
|
||||
state(conn, FTP_STOR_SIZE);
|
||||
return result;
|
||||
}
|
||||
@ -1586,7 +1368,7 @@ static CURLcode ftp_state_ul_setup(struct connectdata *conn,
|
||||
/* we've passed, proceed as normal */
|
||||
} /* resume_from */
|
||||
|
||||
NBFTPSENDF(conn, data->set.ftp_append?"APPE %s":"STOR %s",
|
||||
PPSENDF(&ftpc->pp, data->set.ftp_append?"APPE %s":"STOR %s",
|
||||
ftpc->file);
|
||||
|
||||
state(conn, FTP_STOR);
|
||||
@ -1633,7 +1415,7 @@ static CURLcode ftp_state_quote(struct connectdata *conn,
|
||||
i++;
|
||||
}
|
||||
if(item) {
|
||||
NBFTPSENDF(conn, "%s", item->data);
|
||||
PPSENDF(&ftpc->pp, "%s", item->data);
|
||||
state(conn, instate);
|
||||
quote = TRUE;
|
||||
}
|
||||
@ -1650,7 +1432,7 @@ static CURLcode ftp_state_quote(struct connectdata *conn,
|
||||
if(ftp->transfer != FTPTRANSFER_BODY)
|
||||
state(conn, FTP_STOP);
|
||||
else {
|
||||
NBFTPSENDF(conn, "SIZE %s", ftpc->file);
|
||||
PPSENDF(&ftpc->pp, "SIZE %s", ftpc->file);
|
||||
state(conn, FTP_RETR_SIZE);
|
||||
}
|
||||
break;
|
||||
@ -1791,7 +1573,7 @@ static CURLcode ftp_state_pasv_resp(struct connectdata *conn,
|
||||
conn->bits.ftp_use_epsv = FALSE;
|
||||
infof(data, "disabling EPSV usage\n");
|
||||
|
||||
NBFTPSENDF(conn, "PASV", NULL);
|
||||
PPSENDF(&ftpc->pp, "PASV", NULL);
|
||||
ftpc->count1++;
|
||||
/* remain in the FTP_PASV state */
|
||||
return result;
|
||||
@ -1852,7 +1634,7 @@ static CURLcode ftp_state_pasv_resp(struct connectdata *conn,
|
||||
/* disable it for next transfer */
|
||||
conn->bits.ftp_use_epsv = FALSE;
|
||||
data->state.errorbuf = FALSE; /* allow error message to get rewritten */
|
||||
NBFTPSENDF(conn, "PASV", NULL);
|
||||
PPSENDF(&ftpc->pp, "PASV", NULL);
|
||||
ftpc->count1++;
|
||||
/* remain in the FTP_PASV state */
|
||||
return result;
|
||||
@ -2168,14 +1950,14 @@ static CURLcode ftp_state_post_retr_size(struct connectdata *conn,
|
||||
infof(data, "Instructs server to resume from offset %" FORMAT_OFF_T
|
||||
"\n", data->state.resume_from);
|
||||
|
||||
NBFTPSENDF(conn, "REST %" FORMAT_OFF_T, data->state.resume_from);
|
||||
PPSENDF(&ftpc->pp, "REST %" FORMAT_OFF_T, data->state.resume_from);
|
||||
|
||||
state(conn, FTP_RETR_REST);
|
||||
|
||||
}
|
||||
else {
|
||||
/* no resume */
|
||||
NBFTPSENDF(conn, "RETR %s", ftpc->file);
|
||||
PPSENDF(&ftpc->pp, "RETR %s", ftpc->file);
|
||||
state(conn, FTP_RETR);
|
||||
}
|
||||
|
||||
@ -2246,7 +2028,7 @@ static CURLcode ftp_state_rest_resp(struct connectdata *conn,
|
||||
result = CURLE_FTP_COULDNT_USE_REST;
|
||||
}
|
||||
else {
|
||||
NBFTPSENDF(conn, "RETR %s", ftpc->file);
|
||||
PPSENDF(&ftpc->pp, "RETR %s", ftpc->file);
|
||||
state(conn, FTP_RETR);
|
||||
}
|
||||
break;
|
||||
@ -2300,7 +2082,7 @@ static CURLcode ftp_state_stor_resp(struct connectdata *conn,
|
||||
SECONDARYSOCKET, ftp->bytecountp);
|
||||
state(conn, FTP_STOP);
|
||||
|
||||
conn->proto.ftpc.pending_resp = TRUE; /* we expect a server response more */
|
||||
conn->proto.ftpc.pp.pending_resp = TRUE; /* expect a server response */
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -2414,7 +2196,7 @@ static CURLcode ftp_state_get_resp(struct connectdata *conn,
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
conn->proto.ftpc.pending_resp = TRUE; /* we expect a server response more */
|
||||
conn->proto.ftpc.pp.pending_resp = TRUE; /* expect server response */
|
||||
state(conn, FTP_STOP);
|
||||
}
|
||||
else {
|
||||
@ -2466,7 +2248,7 @@ static CURLcode ftp_state_loggedin(struct connectdata *conn)
|
||||
parameter of '0' to indicate that no buffering is taking place
|
||||
and the data connection should not be encapsulated.
|
||||
*/
|
||||
NBFTPSENDF(conn, "PBSZ %d", 0);
|
||||
PPSENDF(&conn->proto.ftpc.pp, "PBSZ %d", 0);
|
||||
state(conn, FTP_PBSZ);
|
||||
}
|
||||
else {
|
||||
@ -2490,7 +2272,7 @@ static CURLcode ftp_state_user_resp(struct connectdata *conn,
|
||||
if((ftpcode == 331) && (ftpc->state == FTP_USER)) {
|
||||
/* 331 Password required for ...
|
||||
(the server requires to send the user's password too) */
|
||||
NBFTPSENDF(conn, "PASS %s", ftp->passwd?ftp->passwd:"");
|
||||
PPSENDF(&ftpc->pp, "PASS %s", ftp->passwd?ftp->passwd:"");
|
||||
state(conn, FTP_PASS);
|
||||
}
|
||||
else if(ftpcode/100 == 2) {
|
||||
@ -2500,7 +2282,7 @@ static CURLcode ftp_state_user_resp(struct connectdata *conn,
|
||||
}
|
||||
else if(ftpcode == 332) {
|
||||
if(data->set.str[STRING_FTP_ACCOUNT]) {
|
||||
NBFTPSENDF(conn, "ACCT %s", data->set.str[STRING_FTP_ACCOUNT]);
|
||||
PPSENDF(&ftpc->pp, "ACCT %s", data->set.str[STRING_FTP_ACCOUNT]);
|
||||
state(conn, FTP_ACCT);
|
||||
}
|
||||
else {
|
||||
@ -2517,7 +2299,7 @@ static CURLcode ftp_state_user_resp(struct connectdata *conn,
|
||||
if(conn->data->set.str[STRING_FTP_ALTERNATIVE_TO_USER] &&
|
||||
!conn->data->state.ftp_trying_alternative) {
|
||||
/* Ok, USER failed. Let's try the supplied command. */
|
||||
NBFTPSENDF(conn, "%s",
|
||||
PPSENDF(&conn->proto.ftpc.pp, "%s",
|
||||
conn->data->set.str[STRING_FTP_ALTERNATIVE_TO_USER]);
|
||||
conn->data->state.ftp_trying_alternative = TRUE;
|
||||
state(conn, FTP_USER);
|
||||
@ -2555,32 +2337,15 @@ static CURLcode ftp_statemach_act(struct connectdata *conn)
|
||||
struct SessionHandle *data=conn->data;
|
||||
int ftpcode;
|
||||
struct ftp_conn *ftpc = &conn->proto.ftpc;
|
||||
struct pingpong *pp = &ftpc->pp;
|
||||
static const char ftpauth[][4] = { "SSL", "TLS" };
|
||||
size_t nread = 0;
|
||||
|
||||
if(ftpc->sendleft) {
|
||||
/* we have a piece of a command still left to send */
|
||||
ssize_t written;
|
||||
result = Curl_write(conn, sock, ftpc->sendthis + ftpc->sendsize -
|
||||
ftpc->sendleft, ftpc->sendleft, &written);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
if(written != (ssize_t)ftpc->sendleft) {
|
||||
/* only a fraction was sent */
|
||||
ftpc->sendleft -= written;
|
||||
}
|
||||
else {
|
||||
free(ftpc->sendthis);
|
||||
ftpc->sendthis=NULL;
|
||||
ftpc->sendleft = ftpc->sendsize = 0;
|
||||
ftpc->response = Curl_tvnow();
|
||||
}
|
||||
return CURLE_OK;
|
||||
}
|
||||
if(pp->sendleft)
|
||||
return Curl_pp_flushsend(pp);
|
||||
|
||||
/* we read a piece of response */
|
||||
result = ftp_readresp(sock, conn, &ftpcode, &nread);
|
||||
result = ftp_readresp(sock, pp, &ftpcode, &nread);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
@ -2632,7 +2397,7 @@ static CURLcode ftp_statemach_act(struct connectdata *conn)
|
||||
data->set.ftpsslauth);
|
||||
return CURLE_FAILED_INIT; /* we don't know what to do */
|
||||
}
|
||||
NBFTPSENDF(conn, "AUTH %s", ftpauth[ftpc->count1]);
|
||||
PPSENDF(&ftpc->pp, "AUTH %s", ftpauth[ftpc->count1]);
|
||||
state(conn, FTP_AUTH);
|
||||
}
|
||||
else {
|
||||
@ -2665,7 +2430,7 @@ static CURLcode ftp_statemach_act(struct connectdata *conn)
|
||||
else if(ftpc->count3 < 1) {
|
||||
ftpc->count3++;
|
||||
ftpc->count1 += ftpc->count2; /* get next attempt */
|
||||
result = Curl_nbftpsendf(conn, "AUTH %s", ftpauth[ftpc->count1]);
|
||||
result = Curl_pp_sendf(&ftpc->pp, "AUTH %s", ftpauth[ftpc->count1]);
|
||||
/* remain in this same state */
|
||||
}
|
||||
else {
|
||||
@ -2691,7 +2456,7 @@ static CURLcode ftp_statemach_act(struct connectdata *conn)
|
||||
break;
|
||||
|
||||
case FTP_PBSZ:
|
||||
NBFTPSENDF(conn, "PROT %c",
|
||||
PPSENDF(&ftpc->pp, "PROT %c",
|
||||
data->set.ftp_ssl == CURLUSESSL_CONTROL ? 'C' : 'P');
|
||||
state(conn, FTP_PROT);
|
||||
|
||||
@ -2711,7 +2476,7 @@ static CURLcode ftp_statemach_act(struct connectdata *conn)
|
||||
if(data->set.ftp_ccc) {
|
||||
/* CCC - Clear Command Channel
|
||||
*/
|
||||
NBFTPSENDF(conn, "CCC", NULL);
|
||||
PPSENDF(&ftpc->pp, "CCC", NULL);
|
||||
state(conn, FTP_CCC);
|
||||
}
|
||||
else {
|
||||
@ -2797,7 +2562,7 @@ static CURLcode ftp_statemach_act(struct connectdata *conn)
|
||||
systems. */
|
||||
|
||||
if(!ftpc->server_os && ftpc->entrypath[0] != '/') {
|
||||
NBFTPSENDF(conn, "SYST", NULL);
|
||||
PPSENDF(&ftpc->pp, "SYST", NULL);
|
||||
state(conn, FTP_SYST);
|
||||
break;
|
||||
}
|
||||
@ -2836,7 +2601,7 @@ static CURLcode ftp_statemach_act(struct connectdata *conn)
|
||||
|
||||
if(strequal(ftpc->server_os, "OS/400")) {
|
||||
/* Force OS400 name format 1. */
|
||||
NBFTPSENDF(conn, "SITE NAMEFMT 1", NULL);
|
||||
PPSENDF(&ftpc->pp, "SITE NAMEFMT 1", NULL);
|
||||
state(conn, FTP_NAMEFMT);
|
||||
break;
|
||||
}
|
||||
@ -2884,7 +2649,7 @@ static CURLcode ftp_statemach_act(struct connectdata *conn)
|
||||
ftpc->count1 && !ftpc->count2) {
|
||||
/* try making it */
|
||||
ftpc->count2++; /* counter to prevent CWD-MKD loops */
|
||||
NBFTPSENDF(conn, "MKD %s", ftpc->dirs[ftpc->count1 - 1]);
|
||||
PPSENDF(&ftpc->pp, "MKD %s", ftpc->dirs[ftpc->count1 - 1]);
|
||||
state(conn, FTP_MKD);
|
||||
}
|
||||
else {
|
||||
@ -2900,7 +2665,7 @@ static CURLcode ftp_statemach_act(struct connectdata *conn)
|
||||
ftpc->count2=0;
|
||||
if(++ftpc->count1 <= ftpc->dirdepth) {
|
||||
/* send next CWD */
|
||||
NBFTPSENDF(conn, "CWD %s", ftpc->dirs[ftpc->count1 - 1]);
|
||||
PPSENDF(&ftpc->pp, "CWD %s", ftpc->dirs[ftpc->count1 - 1]);
|
||||
}
|
||||
else {
|
||||
result = ftp_state_post_cwd(conn);
|
||||
@ -2918,7 +2683,7 @@ static CURLcode ftp_statemach_act(struct connectdata *conn)
|
||||
}
|
||||
state(conn, FTP_CWD);
|
||||
/* send CWD */
|
||||
NBFTPSENDF(conn, "CWD %s", ftpc->dirs[ftpc->count1 - 1]);
|
||||
PPSENDF(&ftpc->pp, "CWD %s", ftpc->dirs[ftpc->count1 - 1]);
|
||||
break;
|
||||
|
||||
case FTP_MDTM:
|
||||
@ -2972,65 +2737,13 @@ static CURLcode ftp_statemach_act(struct connectdata *conn)
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Returns timeout in ms. 0 or negative number means the timeout has already
|
||||
triggered */
|
||||
static long ftp_state_timeout(struct connectdata *conn)
|
||||
{
|
||||
struct SessionHandle *data=conn->data;
|
||||
struct ftp_conn *ftpc = &conn->proto.ftpc;
|
||||
long timeout_ms=360000; /* in milliseconds */
|
||||
|
||||
if(data->set.ftp_response_timeout )
|
||||
/* if CURLOPT_FTP_RESPONSE_TIMEOUT is set, use that to determine remaining
|
||||
time. Also, use ftp->response because FTP_RESPONSE_TIMEOUT is supposed
|
||||
to govern the response for any given ftp response, not for the time
|
||||
from connect to the given ftp response. */
|
||||
timeout_ms = data->set.ftp_response_timeout - /* timeout time */
|
||||
Curl_tvdiff(Curl_tvnow(), ftpc->response); /* spent time */
|
||||
else if(data->set.timeout)
|
||||
/* if timeout is requested, find out how much remaining time we have */
|
||||
timeout_ms = data->set.timeout - /* timeout time */
|
||||
Curl_tvdiff(Curl_tvnow(), conn->now); /* spent time */
|
||||
else
|
||||
/* Without a requested timeout, we only wait 'response_time' seconds for
|
||||
the full response to arrive before we bail out */
|
||||
timeout_ms = ftpc->response_time -
|
||||
Curl_tvdiff(Curl_tvnow(), ftpc->response); /* spent time */
|
||||
|
||||
return timeout_ms;
|
||||
}
|
||||
|
||||
|
||||
/* called repeatedly until done from multi.c */
|
||||
static CURLcode ftp_multi_statemach(struct connectdata *conn,
|
||||
bool *done)
|
||||
{
|
||||
curl_socket_t sock = conn->sock[FIRSTSOCKET];
|
||||
int rc;
|
||||
struct SessionHandle *data=conn->data;
|
||||
struct ftp_conn *ftpc = &conn->proto.ftpc;
|
||||
CURLcode result = CURLE_OK;
|
||||
long timeout_ms = ftp_state_timeout(conn);
|
||||
|
||||
*done = FALSE; /* default to not done yet */
|
||||
|
||||
if(timeout_ms <= 0) {
|
||||
failf(data, "FTP response timeout");
|
||||
return CURLE_OPERATION_TIMEDOUT;
|
||||
}
|
||||
|
||||
rc = Curl_socket_ready(ftpc->sendleft?CURL_SOCKET_BAD:sock, /* reading */
|
||||
ftpc->sendleft?sock:CURL_SOCKET_BAD, /* writing */
|
||||
0);
|
||||
|
||||
if(rc == -1) {
|
||||
failf(data, "select/poll error");
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
}
|
||||
else if(rc != 0) {
|
||||
result = ftp_statemach_act(conn);
|
||||
}
|
||||
/* if rc == 0, then select() timed out */
|
||||
CURLcode result = Curl_pp_multi_statemach(&ftpc->pp);
|
||||
|
||||
/* 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
|
||||
@ -3042,44 +2755,12 @@ static CURLcode ftp_multi_statemach(struct connectdata *conn,
|
||||
|
||||
static CURLcode ftp_easy_statemach(struct connectdata *conn)
|
||||
{
|
||||
curl_socket_t sock = conn->sock[FIRSTSOCKET];
|
||||
int rc;
|
||||
struct SessionHandle *data=conn->data;
|
||||
struct ftp_conn *ftpc = &conn->proto.ftpc;
|
||||
struct pingpong *pp = &ftpc->pp;
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
while(ftpc->state != FTP_STOP) {
|
||||
long interval_ms;
|
||||
long timeout_ms = ftp_state_timeout(conn);
|
||||
|
||||
if(timeout_ms <=0 ) {
|
||||
failf(data, "FTP response timeout");
|
||||
return CURLE_OPERATION_TIMEDOUT; /* already too little time */
|
||||
}
|
||||
|
||||
interval_ms = 1000; /* use 1 second timeout intervals */
|
||||
if(timeout_ms < interval_ms)
|
||||
interval_ms = timeout_ms;
|
||||
|
||||
rc = Curl_socket_ready(ftpc->sendleft?CURL_SOCKET_BAD:sock, /* reading */
|
||||
ftpc->sendleft?sock:CURL_SOCKET_BAD, /* writing */
|
||||
(int)interval_ms);
|
||||
|
||||
if(Curl_pgrsUpdate(conn))
|
||||
result = CURLE_ABORTED_BY_CALLBACK;
|
||||
else
|
||||
result = Curl_speedcheck(data, Curl_tvnow());
|
||||
|
||||
if(result)
|
||||
break;
|
||||
|
||||
if(rc == -1) {
|
||||
failf(data, "select/poll error");
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
}
|
||||
else if(rc)
|
||||
result = ftp_statemach_act(conn);
|
||||
|
||||
result = Curl_pp_easy_statemach(pp);
|
||||
if(result)
|
||||
break;
|
||||
}
|
||||
@ -3136,6 +2817,7 @@ static CURLcode ftp_connect(struct connectdata *conn,
|
||||
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 */
|
||||
|
||||
@ -3150,7 +2832,10 @@ static CURLcode ftp_connect(struct connectdata *conn,
|
||||
/* We always support persistant connections on ftp */
|
||||
conn->bits.close = FALSE;
|
||||
|
||||
ftpc->response_time = RESP_TIMEOUT; /* set default response time-out */
|
||||
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) {
|
||||
@ -3190,11 +2875,11 @@ static CURLcode ftp_connect(struct connectdata *conn,
|
||||
return result;
|
||||
}
|
||||
|
||||
Curl_pp_init(pp); /* init the generic pingpong data */
|
||||
|
||||
/* When we connect, we start in the state where we await the 220
|
||||
response */
|
||||
ftp_respinit(conn); /* init the response reader stuff */
|
||||
state(conn, FTP_WAIT220);
|
||||
ftpc->response = Curl_tvnow(); /* start response time-out now! */
|
||||
|
||||
if(data->state.used_interface == Curl_if_multi)
|
||||
result = ftp_multi_statemach(conn, done);
|
||||
@ -3222,6 +2907,7 @@ static CURLcode ftp_done(struct connectdata *conn, CURLcode status,
|
||||
struct SessionHandle *data = conn->data;
|
||||
struct FTP *ftp = data->state.proto.ftp;
|
||||
struct ftp_conn *ftpc = &conn->proto.ftpc;
|
||||
struct pingpong *pp = &ftpc->pp;
|
||||
ssize_t nread;
|
||||
int ftpcode;
|
||||
CURLcode result=CURLE_OK;
|
||||
@ -3330,20 +3016,20 @@ static CURLcode ftp_done(struct connectdata *conn, CURLcode status,
|
||||
}
|
||||
|
||||
if((ftp->transfer == FTPTRANSFER_BODY) && ftpc->ctl_valid &&
|
||||
ftpc->pending_resp && !premature) {
|
||||
pp->pending_resp && !premature) {
|
||||
/*
|
||||
* 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
|
||||
* abandon old silent connections.
|
||||
*/
|
||||
long old_time = ftpc->response_time;
|
||||
long old_time = pp->response_time;
|
||||
|
||||
ftpc->response_time = 60*1000; /* give it only a minute for now */
|
||||
pp->response_time = 60*1000; /* give it only a minute for now */
|
||||
|
||||
result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
|
||||
|
||||
ftpc->response_time = old_time; /* set this back to previous value */
|
||||
pp->response_time = old_time; /* set this back to previous value */
|
||||
|
||||
if(!nread && (CURLE_OPERATION_TIMEDOUT == result)) {
|
||||
failf(data, "control connection looks dead");
|
||||
@ -3497,7 +3183,7 @@ static CURLcode ftp_nb_type(struct connectdata *conn,
|
||||
return ftp_state_type_resp(conn, 200, newstate);
|
||||
}
|
||||
|
||||
NBFTPSENDF(conn, "TYPE %c", want);
|
||||
PPSENDF(&ftpc->pp, "TYPE %c", want);
|
||||
state(conn, newstate);
|
||||
|
||||
/* keep track of our current transfer type */
|
||||
@ -3735,93 +3421,12 @@ static CURLcode ftp_do(struct connectdata *conn, bool *done)
|
||||
return retcode;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
*
|
||||
* Curl_(nb)ftpsendf()
|
||||
*
|
||||
* Sends the formated string as a ftp command to a ftp server
|
||||
*
|
||||
* NOTE: we build the command in a fixed-length buffer, which sets length
|
||||
* restrictions on the command!
|
||||
*
|
||||
* The "nb" version is made to Never Block.
|
||||
*/
|
||||
CURLcode Curl_nbftpsendf(struct connectdata *conn,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
ssize_t bytes_written;
|
||||
/* may still not be big enough for some krb5 tokens */
|
||||
#define SBUF_SIZE 1024
|
||||
char s[SBUF_SIZE];
|
||||
size_t write_len;
|
||||
char *sptr=s;
|
||||
CURLcode res = CURLE_OK;
|
||||
struct SessionHandle *data = conn->data;
|
||||
struct ftp_conn *ftpc = &conn->proto.ftpc;
|
||||
#if defined(HAVE_KRB4) || defined(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);
|
||||
va_end(ap);
|
||||
|
||||
strcat(s, "\r\n"); /* append a trailing CRLF */
|
||||
|
||||
bytes_written=0;
|
||||
write_len = strlen(s);
|
||||
|
||||
ftp_respinit(conn);
|
||||
|
||||
#ifdef CURL_DOES_CONVERSIONS
|
||||
res = Curl_convert_to_network(data, s, write_len);
|
||||
/* Curl_convert_to_network calls failf if unsuccessful */
|
||||
if(res != CURLE_OK) {
|
||||
return res;
|
||||
}
|
||||
#endif /* CURL_DOES_CONVERSIONS */
|
||||
|
||||
#if defined(HAVE_KRB4) || defined(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)
|
||||
conn->data_prot = data_sec;
|
||||
#endif
|
||||
|
||||
if(CURLE_OK != res)
|
||||
return res;
|
||||
|
||||
if(conn->data->set.verbose)
|
||||
Curl_debug(conn->data, CURLINFO_HEADER_OUT,
|
||||
sptr, (size_t)bytes_written, conn);
|
||||
|
||||
if(bytes_written != (ssize_t)write_len) {
|
||||
/* the whole chunk was not sent, store the rest of the data */
|
||||
write_len -= bytes_written;
|
||||
sptr += bytes_written;
|
||||
ftpc->sendthis = malloc(write_len);
|
||||
if(ftpc->sendthis) {
|
||||
memcpy(ftpc->sendthis, sptr, write_len);
|
||||
ftpc->sendsize = ftpc->sendleft = write_len;
|
||||
}
|
||||
else {
|
||||
failf(data, "out of memory");
|
||||
res = CURLE_OUT_OF_MEMORY;
|
||||
}
|
||||
}
|
||||
else
|
||||
ftpc->response = Curl_tvnow();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
CURLcode Curl_ftpsendf(struct connectdata *conn,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
ssize_t bytes_written;
|
||||
#define SBUF_SIZE 1024
|
||||
char s[SBUF_SIZE];
|
||||
size_t write_len;
|
||||
char *sptr=s;
|
||||
@ -3891,7 +3496,7 @@ static CURLcode ftp_quit(struct connectdata *conn)
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
if(conn->proto.ftpc.ctl_valid) {
|
||||
NBFTPSENDF(conn, "QUIT", NULL);
|
||||
PPSENDF(&conn->proto.ftpc.pp, "QUIT", NULL);
|
||||
state(conn, FTP_QUIT);
|
||||
|
||||
result = ftp_easy_statemach(conn);
|
||||
@ -3910,6 +3515,7 @@ static CURLcode ftp_quit(struct connectdata *conn)
|
||||
static CURLcode ftp_disconnect(struct connectdata *conn)
|
||||
{
|
||||
struct ftp_conn *ftpc= &conn->proto.ftpc;
|
||||
struct pingpong *pp = &ftpc->pp;
|
||||
|
||||
/* We cannot send quit unconditionally. If this connection is stale or
|
||||
bad in any way, sending quit and waiting around here will make the
|
||||
@ -3930,10 +3536,7 @@ static CURLcode ftp_disconnect(struct connectdata *conn)
|
||||
free(ftpc->entrypath);
|
||||
ftpc->entrypath = NULL;
|
||||
}
|
||||
if(ftpc->cache) {
|
||||
free(ftpc->cache);
|
||||
ftpc->cache = NULL;
|
||||
}
|
||||
|
||||
freedirs(ftpc);
|
||||
if(ftpc->prevpath) {
|
||||
free(ftpc->prevpath);
|
||||
@ -3944,6 +3547,8 @@ static CURLcode ftp_disconnect(struct connectdata *conn)
|
||||
ftpc->server_os = NULL;
|
||||
}
|
||||
|
||||
Curl_pp_disconnect(pp);
|
||||
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
|
106
lib/ftp.h
106
lib/ftp.h
@ -7,7 +7,7 @@
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
* Copyright (C) 1998 - 2007, 2009, 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
|
||||
@ -23,6 +23,8 @@
|
||||
* $Id$
|
||||
***************************************************************************/
|
||||
|
||||
#include "pingpong.h"
|
||||
|
||||
#ifndef CURL_DISABLE_FTP
|
||||
extern const struct Curl_handler Curl_handler_ftp;
|
||||
|
||||
@ -39,8 +41,108 @@ extern const struct Curl_handler Curl_handler_ftps_proxy;
|
||||
#endif
|
||||
|
||||
CURLcode Curl_ftpsendf(struct connectdata *, const char *fmt, ...);
|
||||
CURLcode Curl_nbftpsendf(struct connectdata *, const char *fmt, ...);
|
||||
CURLcode Curl_GetFTPResponse(ssize_t *nread, struct connectdata *conn,
|
||||
int *ftpcode);
|
||||
#endif /* CURL_DISABLE_FTP */
|
||||
|
||||
/****************************************************************************
|
||||
* FTP unique setup
|
||||
***************************************************************************/
|
||||
typedef enum {
|
||||
FTP_STOP, /* do nothing state, stops the state machine */
|
||||
FTP_WAIT220, /* waiting for the initial 220 response immediately after
|
||||
a connect */
|
||||
FTP_AUTH,
|
||||
FTP_USER,
|
||||
FTP_PASS,
|
||||
FTP_ACCT,
|
||||
FTP_PBSZ,
|
||||
FTP_PROT,
|
||||
FTP_CCC,
|
||||
FTP_PWD,
|
||||
FTP_SYST,
|
||||
FTP_NAMEFMT,
|
||||
FTP_QUOTE, /* waiting for a response to a command sent in a quote list */
|
||||
FTP_RETR_PREQUOTE,
|
||||
FTP_STOR_PREQUOTE,
|
||||
FTP_POSTQUOTE,
|
||||
FTP_CWD, /* change dir */
|
||||
FTP_MKD, /* if the dir didn't exist */
|
||||
FTP_MDTM, /* to figure out the datestamp */
|
||||
FTP_TYPE, /* to set type when doing a head-like request */
|
||||
FTP_LIST_TYPE, /* set type when about to do a dir list */
|
||||
FTP_RETR_TYPE, /* set type when about to RETR a file */
|
||||
FTP_STOR_TYPE, /* set type when about to STOR a file */
|
||||
FTP_SIZE, /* get the remote file's size for head-like request */
|
||||
FTP_RETR_SIZE, /* get the remote file's size for RETR */
|
||||
FTP_STOR_SIZE, /* get the size for (resumed) STOR */
|
||||
FTP_REST, /* when used to check if the server supports it in head-like */
|
||||
FTP_RETR_REST, /* when asking for "resume" in for RETR */
|
||||
FTP_PORT, /* generic state for PORT, LPRT and EPRT, check count1 */
|
||||
FTP_PASV, /* generic state for PASV and EPSV, check count1 */
|
||||
FTP_LIST, /* generic state for LIST, NLST or a custom list command */
|
||||
FTP_RETR,
|
||||
FTP_STOR, /* generic state for STOR and APPE */
|
||||
FTP_QUIT,
|
||||
FTP_LAST /* never used */
|
||||
} ftpstate;
|
||||
|
||||
typedef enum {
|
||||
FTPFILE_MULTICWD = 1, /* as defined by RFC1738 */
|
||||
FTPFILE_NOCWD = 2, /* use SIZE / RETR / STOR on the full path */
|
||||
FTPFILE_SINGLECWD = 3 /* make one CWD, then SIZE / RETR / STOR on the file */
|
||||
} curl_ftpfile;
|
||||
|
||||
typedef enum {
|
||||
FTPTRANSFER_BODY, /* yes do transfer a body */
|
||||
FTPTRANSFER_INFO, /* do still go through to get info/headers */
|
||||
FTPTRANSFER_NONE, /* don't get anything and don't get info */
|
||||
FTPTRANSFER_LAST /* end of list marker, never used */
|
||||
} curl_ftptransfer;
|
||||
|
||||
/* This FTP struct is used in the SessionHandle. All FTP data that is
|
||||
connection-oriented must be in FTP_conn to properly deal with the fact that
|
||||
perhaps the SessionHandle is changed between the times the connection is
|
||||
used. */
|
||||
struct FTP {
|
||||
curl_off_t *bytecountp;
|
||||
char *user; /* user name string */
|
||||
char *passwd; /* password string */
|
||||
|
||||
/* transfer a file/body or not, done as a typedefed enum just to make
|
||||
debuggers display the full symbol and not just the numerical value */
|
||||
curl_ftptransfer transfer;
|
||||
curl_off_t downloadsize;
|
||||
};
|
||||
|
||||
|
||||
/* ftp_conn is used for struct connection-oriented data in the connectdata
|
||||
struct */
|
||||
struct ftp_conn {
|
||||
struct pingpong pp;
|
||||
char *entrypath; /* the PWD reply when we logged on */
|
||||
char **dirs; /* realloc()ed array for path components */
|
||||
int dirdepth; /* number of entries used in the 'dirs' array */
|
||||
int diralloc; /* number of entries allocated for the 'dirs' array */
|
||||
char *file; /* decoded file */
|
||||
bool dont_check; /* Set to TRUE to prevent the final (post-transfer)
|
||||
file size and 226/250 status check. It should still
|
||||
read the line, just ignore the result. */
|
||||
bool ctl_valid; /* Tells Curl_ftp_quit() whether or not to do anything. If
|
||||
the connection has timed out or been closed, this
|
||||
should be FALSE when it gets to Curl_ftp_quit() */
|
||||
bool cwddone; /* if it has been determined that the proper CWD combo
|
||||
already has been done */
|
||||
bool cwdfail; /* set TRUE if a CWD command fails, as then we must prevent
|
||||
caching the current directory */
|
||||
char *prevpath; /* conn->path from the previous transfer */
|
||||
char transfertype; /* set by ftp_transfertype for use by Curl_client_write()a
|
||||
and others (A/I or zero) */
|
||||
int count1; /* general purpose counter for the state machine */
|
||||
int count2; /* general purpose counter for the state machine */
|
||||
int count3; /* general purpose counter for the state machine */
|
||||
ftpstate state; /* always use ftp.c:state() to change state! */
|
||||
char * server_os; /* The target server operating system. */
|
||||
};
|
||||
|
||||
#endif /* __FTP_H */
|
||||
|
1026
lib/imap.c
Normal file
1026
lib/imap.c
Normal file
File diff suppressed because it is too large
Load Diff
56
lib/imap.h
Normal file
56
lib/imap.h
Normal file
@ -0,0 +1,56 @@
|
||||
#ifndef __IMAP_H
|
||||
#define __IMAP_H
|
||||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) 2009, Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution. The terms
|
||||
* are also available at http://curl.haxx.se/docs/copyright.html.
|
||||
*
|
||||
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||
* copies of the Software, and permit persons to whom the Software is
|
||||
* furnished to do so, under the terms of the COPYING file.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
* $Id$
|
||||
***************************************************************************/
|
||||
|
||||
#include "pingpong.h"
|
||||
|
||||
/****************************************************************************
|
||||
* IMAP unique setup
|
||||
***************************************************************************/
|
||||
typedef enum {
|
||||
IMAP_STOP, /* do nothing state, stops the state machine */
|
||||
IMAP_SERVERGREET, /* waiting for the initial greeting immediately after
|
||||
a connect */
|
||||
IMAP_LOGIN,
|
||||
IMAP_STARTTLS,
|
||||
IMAP_SELECT,
|
||||
IMAP_FETCH,
|
||||
IMAP_LOGOUT,
|
||||
IMAP_LAST /* never used */
|
||||
} imapstate;
|
||||
|
||||
/* imap_conn is used for struct connection-oriented data in the connectdata
|
||||
struct */
|
||||
struct imap_conn {
|
||||
struct pingpong pp;
|
||||
char *mailbox; /* what to FETCH */
|
||||
imapstate state; /* always use imap.c:state() to change state! */
|
||||
int cmdid; /* id number/index */
|
||||
const char *idstr; /* pointer to a string for which to wait for as id */
|
||||
};
|
||||
|
||||
extern const struct Curl_handler Curl_handler_imap;
|
||||
extern const struct Curl_handler Curl_handler_imaps;
|
||||
|
||||
#endif /* __IMAP_H */
|
536
lib/pingpong.c
Normal file
536
lib/pingpong.c
Normal file
@ -0,0 +1,536 @@
|
||||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) 1998 - 2009, Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution. The terms
|
||||
* are also available at http://curl.haxx.se/docs/copyright.html.
|
||||
*
|
||||
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||
* copies of the Software, and permit persons to whom the Software is
|
||||
* furnished to do so, under the terms of the COPYING file.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
* 'pingpong' is for generic back-and-forth support functions used by FTP,
|
||||
* IMAP, POP3, SMTP and whatever more that likes them.
|
||||
*
|
||||
* $Id$
|
||||
***************************************************************************/
|
||||
|
||||
#include "setup.h"
|
||||
|
||||
#include "urldata.h"
|
||||
#include "sendf.h"
|
||||
#include "select.h"
|
||||
#include "progress.h"
|
||||
#include "speedcheck.h"
|
||||
#include "pingpong.h"
|
||||
#include "multiif.h"
|
||||
|
||||
#define _MPRINTF_REPLACE /* use our functions only */
|
||||
#include <curl/mprintf.h>
|
||||
|
||||
#include "curl_memory.h"
|
||||
/* The last #include file should be: */
|
||||
#include "memdebug.h"
|
||||
|
||||
#ifdef USE_PINGPONG
|
||||
|
||||
/* Returns timeout in ms. 0 or negative number means the timeout has already
|
||||
triggered */
|
||||
long Curl_pp_state_timeout(struct pingpong *pp)
|
||||
{
|
||||
struct connectdata *conn = pp->conn;
|
||||
struct SessionHandle *data=conn->data;
|
||||
long timeout_ms=360000; /* in milliseconds */
|
||||
|
||||
if(data->set.server_response_timeout )
|
||||
/* if CURLOPT_SERVER_RESPONSE_TIMEOUT is set, use that to determine
|
||||
remaining time. Also, use pp->response because SERVER_RESPONSE_TIMEOUT
|
||||
is supposed to govern the response for any given server response, not
|
||||
for the time from connect to the given server response. */
|
||||
timeout_ms = data->set.server_response_timeout - /* timeout time */
|
||||
Curl_tvdiff(Curl_tvnow(), pp->response); /* spent time */
|
||||
else if(data->set.timeout)
|
||||
/* if timeout is requested, find out how much remaining time we have */
|
||||
timeout_ms = data->set.timeout - /* timeout time */
|
||||
Curl_tvdiff(Curl_tvnow(), conn->now); /* spent time */
|
||||
else
|
||||
/* Without a requested timeout, we only wait 'response_time' seconds for
|
||||
the full response to arrive before we bail out */
|
||||
timeout_ms = pp->response_time -
|
||||
Curl_tvdiff(Curl_tvnow(), pp->response); /* spent time */
|
||||
|
||||
return timeout_ms;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Curl_pp_multi_statemach()
|
||||
*
|
||||
* called repeatedly until done when the multi interface is used.
|
||||
*/
|
||||
CURLcode Curl_pp_multi_statemach(struct pingpong *pp)
|
||||
{
|
||||
struct connectdata *conn = pp->conn;
|
||||
curl_socket_t sock = conn->sock[FIRSTSOCKET];
|
||||
int rc;
|
||||
struct SessionHandle *data=conn->data;
|
||||
CURLcode result = CURLE_OK;
|
||||
long timeout_ms = Curl_pp_state_timeout(pp);
|
||||
|
||||
if(timeout_ms <= 0) {
|
||||
failf(data, "server response timeout");
|
||||
return CURLE_OPERATION_TIMEDOUT;
|
||||
}
|
||||
|
||||
rc = Curl_socket_ready(pp->sendleft?CURL_SOCKET_BAD:sock, /* reading */
|
||||
pp->sendleft?sock:CURL_SOCKET_BAD, /* writing */
|
||||
0);
|
||||
|
||||
if(rc == -1) {
|
||||
failf(data, "select/poll error");
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
}
|
||||
else if(rc != 0)
|
||||
result = pp->statemach_act(conn);
|
||||
|
||||
/* if rc == 0, then select() timed out */
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Curl_pp_easy_statemach()
|
||||
*
|
||||
* called repeatedly until done when the easy interface is used.
|
||||
*/
|
||||
CURLcode Curl_pp_easy_statemach(struct pingpong *pp)
|
||||
{
|
||||
struct connectdata *conn = pp->conn;
|
||||
curl_socket_t sock = conn->sock[FIRSTSOCKET];
|
||||
int rc;
|
||||
long interval_ms;
|
||||
long timeout_ms = Curl_pp_state_timeout(pp);
|
||||
struct SessionHandle *data=conn->data;
|
||||
CURLcode result;
|
||||
|
||||
if(timeout_ms <=0 ) {
|
||||
failf(data, "server response timeout");
|
||||
return CURLE_OPERATION_TIMEDOUT; /* already too little time */
|
||||
}
|
||||
|
||||
interval_ms = 1000; /* use 1 second timeout intervals */
|
||||
if(timeout_ms < interval_ms)
|
||||
interval_ms = timeout_ms;
|
||||
|
||||
rc = Curl_socket_ready(pp->sendleft?CURL_SOCKET_BAD:sock, /* reading */
|
||||
pp->sendleft?sock:CURL_SOCKET_BAD, /* writing */
|
||||
(int)interval_ms);
|
||||
|
||||
if(Curl_pgrsUpdate(conn))
|
||||
result = CURLE_ABORTED_BY_CALLBACK;
|
||||
else
|
||||
result = Curl_speedcheck(data, Curl_tvnow());
|
||||
|
||||
if(result)
|
||||
;
|
||||
else if(rc == -1) {
|
||||
failf(data, "select/poll error");
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
}
|
||||
else if(rc)
|
||||
result = pp->statemach_act(conn);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* initialize stuff to prepare for reading a fresh new response */
|
||||
void Curl_pp_init(struct pingpong *pp)
|
||||
{
|
||||
struct connectdata *conn = pp->conn;
|
||||
pp->nread_resp = 0;
|
||||
pp->linestart_resp = conn->data->state.buffer;
|
||||
pp->pending_resp = TRUE;
|
||||
pp->response = Curl_tvnow(); /* start response time-out now! */
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
*
|
||||
* Curl_pp_sendfv()
|
||||
*
|
||||
* Send the formated string as a command to a pingpong server. Note that
|
||||
* the string should not have any CRLF appended, as this function will
|
||||
* append the necessary things itself.
|
||||
*
|
||||
* NOTE: we build the command in a fixed-length buffer, which sets length
|
||||
* restrictions on the command!
|
||||
*
|
||||
* made to never block
|
||||
*/
|
||||
CURLcode Curl_pp_vsendf(struct pingpong *pp,
|
||||
const char *fmt,
|
||||
va_list args)
|
||||
{
|
||||
ssize_t bytes_written;
|
||||
/* may still not be big enough for some krb5 tokens */
|
||||
#define SBUF_SIZE 1024
|
||||
char s[SBUF_SIZE];
|
||||
size_t write_len;
|
||||
char *sptr=s;
|
||||
CURLcode res = CURLE_OK;
|
||||
struct connectdata *conn = pp->conn;
|
||||
struct SessionHandle *data = conn->data;
|
||||
|
||||
#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
|
||||
enum protection_level data_sec = conn->data_prot;
|
||||
#endif
|
||||
|
||||
vsnprintf(s, SBUF_SIZE-3, fmt, args);
|
||||
|
||||
strcat(s, "\r\n"); /* append a trailing CRLF */
|
||||
|
||||
bytes_written=0;
|
||||
write_len = strlen(s);
|
||||
|
||||
Curl_pp_init(pp);
|
||||
|
||||
#ifdef CURL_DOES_CONVERSIONS
|
||||
res = Curl_convert_to_network(data, s, write_len);
|
||||
/* Curl_convert_to_network calls failf if unsuccessful */
|
||||
if(res != CURLE_OK) {
|
||||
return res;
|
||||
}
|
||||
#endif /* CURL_DOES_CONVERSIONS */
|
||||
|
||||
#if defined(HAVE_KRB4) || defined(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)
|
||||
conn->data_prot = data_sec;
|
||||
#endif
|
||||
|
||||
if(CURLE_OK != res)
|
||||
return res;
|
||||
|
||||
if(conn->data->set.verbose)
|
||||
Curl_debug(conn->data, CURLINFO_HEADER_OUT,
|
||||
sptr, (size_t)bytes_written, conn);
|
||||
|
||||
if(bytes_written != (ssize_t)write_len) {
|
||||
/* the whole chunk was not sent, store the rest of the data */
|
||||
write_len -= bytes_written;
|
||||
sptr += bytes_written;
|
||||
pp->sendthis = malloc(write_len);
|
||||
if(pp->sendthis) {
|
||||
memcpy(pp->sendthis, sptr, write_len);
|
||||
pp->sendsize = pp->sendleft = write_len;
|
||||
}
|
||||
else {
|
||||
failf(data, "out of memory");
|
||||
res = CURLE_OUT_OF_MEMORY;
|
||||
}
|
||||
}
|
||||
else
|
||||
pp->response = Curl_tvnow();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
*
|
||||
* Curl_pp_sendf()
|
||||
*
|
||||
* Send the formated string as a command to a pingpong server. Note that
|
||||
* the string should not have any CRLF appended, as this function will
|
||||
* append the necessary things itself.
|
||||
*
|
||||
* NOTE: we build the command in a fixed-length buffer, which sets length
|
||||
* restrictions on the command!
|
||||
*
|
||||
* made to never block
|
||||
*/
|
||||
CURLcode Curl_pp_sendf(struct pingpong *pp,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
CURLcode res;
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
|
||||
res = Curl_pp_vsendf(pp, fmt, ap);
|
||||
|
||||
va_end(ap);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Curl_pp_readresp()
|
||||
*
|
||||
* Reads a piece of a server response.
|
||||
*/
|
||||
CURLcode Curl_pp_readresp(curl_socket_t sockfd,
|
||||
struct pingpong *pp,
|
||||
int *code, /* return the server code if done */
|
||||
size_t *size) /* size of the response */
|
||||
{
|
||||
ssize_t perline; /* count bytes per line */
|
||||
bool keepon=TRUE;
|
||||
ssize_t gotbytes;
|
||||
char *ptr;
|
||||
struct connectdata *conn = pp->conn;
|
||||
struct SessionHandle *data = conn->data;
|
||||
char * const buf = data->state.buffer;
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
*code = 0; /* 0 for errors or not done */
|
||||
*size = 0;
|
||||
|
||||
ptr=buf + pp->nread_resp;
|
||||
|
||||
/* number of bytes in the current line, so far */
|
||||
perline = (ssize_t)(ptr-pp->linestart_resp);
|
||||
|
||||
keepon=TRUE;
|
||||
|
||||
while((pp->nread_resp<BUFSIZE) && (keepon && !result)) {
|
||||
|
||||
if(pp->cache) {
|
||||
/* we had data in the "cache", copy that instead of doing an actual
|
||||
* read
|
||||
*
|
||||
* ftp->cache_size is cast to int here. This should be safe,
|
||||
* because it would have been populated with something of size
|
||||
* int to begin with, even though its datatype may be larger
|
||||
* than an int.
|
||||
*/
|
||||
DEBUGASSERT((ptr+pp->cache_size) <= (buf+BUFSIZE+1));
|
||||
memcpy(ptr, pp->cache, pp->cache_size);
|
||||
gotbytes = pp->cache_size;
|
||||
free(pp->cache); /* free the cache */
|
||||
pp->cache = NULL; /* clear the pointer */
|
||||
pp->cache_size = 0; /* zero the size just in case */
|
||||
}
|
||||
else {
|
||||
int res;
|
||||
#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
|
||||
enum protection_level prot = conn->data_prot;
|
||||
|
||||
conn->data_prot = 0;
|
||||
#endif
|
||||
DEBUGASSERT((ptr+BUFSIZE-pp->nread_resp) <= (buf+BUFSIZE+1));
|
||||
res = Curl_read(conn, sockfd, ptr, BUFSIZE-pp->nread_resp,
|
||||
&gotbytes);
|
||||
#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
|
||||
conn->data_prot = prot;
|
||||
#endif
|
||||
if(res < 0)
|
||||
/* EWOULDBLOCK */
|
||||
return CURLE_OK; /* return */
|
||||
|
||||
#ifdef CURL_DOES_CONVERSIONS
|
||||
if((res == CURLE_OK) && (gotbytes > 0)) {
|
||||
/* convert from the network encoding */
|
||||
res = Curl_convert_from_network(data, ptr, gotbytes);
|
||||
/* Curl_convert_from_network calls failf if unsuccessful */
|
||||
}
|
||||
#endif /* CURL_DOES_CONVERSIONS */
|
||||
|
||||
if(CURLE_OK != res) {
|
||||
result = (CURLcode)res; /* Set outer result variable to this error. */
|
||||
keepon = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if(!keepon)
|
||||
;
|
||||
else if(gotbytes <= 0) {
|
||||
keepon = FALSE;
|
||||
result = CURLE_RECV_ERROR;
|
||||
failf(data, "FTP response reading failed");
|
||||
}
|
||||
else {
|
||||
/* we got a whole chunk of data, which can be anything from one
|
||||
* byte to a set of lines and possible just a piece of the last
|
||||
* line */
|
||||
ssize_t i;
|
||||
ssize_t clipamount = 0;
|
||||
bool restart = FALSE;
|
||||
|
||||
data->req.headerbytecount += gotbytes;
|
||||
|
||||
pp->nread_resp += gotbytes;
|
||||
for(i = 0; i < gotbytes; ptr++, i++) {
|
||||
perline++;
|
||||
if(*ptr=='\n') {
|
||||
/* a newline is CRLF in ftp-talk, so the CR is ignored as
|
||||
the line isn't really terminated until the LF comes */
|
||||
|
||||
/* output debug output if that is requested */
|
||||
#if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
|
||||
if(!conn->sec_complete)
|
||||
#endif
|
||||
if(data->set.verbose)
|
||||
Curl_debug(data, CURLINFO_HEADER_IN,
|
||||
pp->linestart_resp, (size_t)perline, conn);
|
||||
|
||||
/*
|
||||
* We pass all response-lines to the callback function registered
|
||||
* for "headers". The response lines can be seen as a kind of
|
||||
* headers.
|
||||
*/
|
||||
result = Curl_client_write(conn, CLIENTWRITE_HEADER,
|
||||
pp->linestart_resp, perline);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
if(pp->endofresp(pp, code)) {
|
||||
/* This is the end of the last line, copy the last line to the
|
||||
start of the buffer and zero terminate, for old times sake (and
|
||||
krb4)! */
|
||||
char *meow;
|
||||
int n;
|
||||
for(meow=pp->linestart_resp, n=0; meow<ptr; meow++, n++)
|
||||
buf[n] = *meow;
|
||||
*meow=0; /* zero terminate */
|
||||
keepon=FALSE;
|
||||
pp->linestart_resp = ptr+1; /* advance pointer */
|
||||
i++; /* skip this before getting out */
|
||||
|
||||
*size = pp->nread_resp; /* size of the response */
|
||||
pp->nread_resp = 0; /* restart */
|
||||
break;
|
||||
}
|
||||
perline=0; /* line starts over here */
|
||||
pp->linestart_resp = ptr+1;
|
||||
}
|
||||
}
|
||||
|
||||
if(!keepon && (i != gotbytes)) {
|
||||
/* We found the end of the response lines, but we didn't parse the
|
||||
full chunk of data we have read from the server. We therefore need
|
||||
to store the rest of the data to be checked on the next invoke as
|
||||
it may actually contain another end of response already! */
|
||||
clipamount = gotbytes - i;
|
||||
restart = TRUE;
|
||||
}
|
||||
else if(keepon) {
|
||||
|
||||
if((perline == gotbytes) && (gotbytes > BUFSIZE/2)) {
|
||||
/* We got an excessive line without newlines and we need to deal
|
||||
with it. We keep the first bytes of the line then we throw
|
||||
away the rest. */
|
||||
infof(data, "Excessive server response line length received, %zd bytes."
|
||||
" Stripping\n", gotbytes);
|
||||
restart = TRUE;
|
||||
|
||||
/* we keep 40 bytes since all our pingpong protocols are only
|
||||
interested in the first piece */
|
||||
clipamount = 40;
|
||||
}
|
||||
else if(pp->nread_resp > BUFSIZE/2) {
|
||||
/* We got a large chunk of data and there's potentially still trailing
|
||||
data to take care of, so we put any such part in the "cache", clear
|
||||
the buffer to make space and restart. */
|
||||
clipamount = perline;
|
||||
restart = TRUE;
|
||||
}
|
||||
}
|
||||
else if(i == gotbytes)
|
||||
restart = TRUE;
|
||||
|
||||
if(clipamount) {
|
||||
pp->cache_size = clipamount;
|
||||
pp->cache = malloc(pp->cache_size);
|
||||
if(pp->cache)
|
||||
memcpy(pp->cache, pp->linestart_resp, pp->cache_size);
|
||||
else
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
}
|
||||
if(restart) {
|
||||
/* now reset a few variables to start over nicely from the start of
|
||||
the big buffer */
|
||||
pp->nread_resp = 0; /* start over from scratch in the buffer */
|
||||
ptr = pp->linestart_resp = buf;
|
||||
perline = 0;
|
||||
}
|
||||
|
||||
} /* there was data */
|
||||
|
||||
} /* while there's buffer left and loop is requested */
|
||||
|
||||
pp->pending_resp = FALSE;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int Curl_pp_getsock(struct pingpong *pp,
|
||||
curl_socket_t *socks,
|
||||
int numsocks)
|
||||
{
|
||||
struct connectdata *conn = pp->conn;
|
||||
|
||||
if(!numsocks)
|
||||
return GETSOCK_BLANK;
|
||||
|
||||
socks[0] = conn->sock[FIRSTSOCKET];
|
||||
|
||||
if(pp->sendleft) {
|
||||
/* write mode */
|
||||
return GETSOCK_WRITESOCK(0);
|
||||
}
|
||||
|
||||
/* read mode */
|
||||
return GETSOCK_READSOCK(0);
|
||||
}
|
||||
|
||||
CURLcode Curl_pp_flushsend(struct pingpong *pp)
|
||||
{
|
||||
/* we have a piece of a command still left to send */
|
||||
struct connectdata *conn = pp->conn;
|
||||
ssize_t written;
|
||||
CURLcode result = CURLE_OK;
|
||||
curl_socket_t sock = conn->sock[FIRSTSOCKET];
|
||||
|
||||
result = Curl_write(conn, sock, pp->sendthis + pp->sendsize -
|
||||
pp->sendleft, pp->sendleft, &written);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
if(written != (ssize_t)pp->sendleft) {
|
||||
/* only a fraction was sent */
|
||||
pp->sendleft -= written;
|
||||
}
|
||||
else {
|
||||
free(pp->sendthis);
|
||||
pp->sendthis=NULL;
|
||||
pp->sendleft = pp->sendsize = 0;
|
||||
pp->response = Curl_tvnow();
|
||||
}
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
CURLcode Curl_pp_disconnect(struct pingpong *pp)
|
||||
{
|
||||
if(pp->cache) {
|
||||
free(pp->cache);
|
||||
pp->cache = NULL;
|
||||
}
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
148
lib/pingpong.h
Normal file
148
lib/pingpong.h
Normal file
@ -0,0 +1,148 @@
|
||||
#ifndef __PINGPONG_H
|
||||
#define __PINGPONG_H
|
||||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) 1998 - 2007, 2009, Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution. The terms
|
||||
* are also available at http://curl.haxx.se/docs/copyright.html.
|
||||
*
|
||||
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||
* copies of the Software, and permit persons to whom the Software is
|
||||
* furnished to do so, under the terms of the COPYING file.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
* $Id$
|
||||
***************************************************************************/
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "setup.h"
|
||||
|
||||
#if !defined(CURL_DISABLE_IMAP) || !defined(CURL_DISABLE_FTP) || \
|
||||
!defined(CURL_DISABLE_POP3) || !defined(CURL_DISABLE_SMTP)
|
||||
#define USE_PINGPONG
|
||||
#endif
|
||||
|
||||
/* forward-declaration, this is defined in urldata.h */
|
||||
struct connectdata;
|
||||
|
||||
/*
|
||||
* 'pingpong' is the generic struct used for protocols doing server<->client
|
||||
* conversations in a back-and-forth style such as FTP, IMAP, POP3, SMTP etc.
|
||||
*
|
||||
* It holds response cache and non-blocking sending data.
|
||||
*/
|
||||
struct pingpong {
|
||||
char *cache; /* data cache between getresponse()-calls */
|
||||
size_t cache_size; /* size of cache in bytes */
|
||||
size_t nread_resp; /* number of bytes currently read of a server response */
|
||||
char *linestart_resp; /* line start pointer for the server response
|
||||
reader function */
|
||||
bool pending_resp; /* set TRUE when a server response is pending or in
|
||||
progress, and is cleared once the last response is
|
||||
read */
|
||||
char *sendthis; /* allocated pointer to a buffer that is to be sent to the
|
||||
server */
|
||||
size_t sendleft; /* number of bytes left to send from the sendthis buffer */
|
||||
size_t sendsize; /* total size of the sendthis buffer */
|
||||
struct timeval response; /* set to Curl_tvnow() when a command has been sent
|
||||
off, used to time-out response reading */
|
||||
long response_time; /* When no timeout is given, this is the amount of
|
||||
seconds we await for a server response. */
|
||||
|
||||
struct connectdata *conn; /* points to the connectdata struct that this
|
||||
belongs to */
|
||||
|
||||
/* Function pointers the protocols MUST implement and provide for the
|
||||
pingpong layer to function */
|
||||
|
||||
CURLcode (*statemach_act)(struct connectdata *conn);
|
||||
|
||||
int (*endofresp)(struct pingpong *pp, int *code);
|
||||
};
|
||||
|
||||
/*
|
||||
* Curl_pp_multi_statemach()
|
||||
*
|
||||
* called repeatedly until done when the multi interface is used.
|
||||
*/
|
||||
CURLcode Curl_pp_multi_statemach(struct pingpong *pp);
|
||||
|
||||
/*
|
||||
* Curl_pp_easy_statemach()
|
||||
*
|
||||
* called repeatedly until done when the easy interface is used.
|
||||
*/
|
||||
CURLcode Curl_pp_easy_statemach(struct pingpong *pp);
|
||||
|
||||
|
||||
/* initialize stuff to prepare for reading a fresh new response */
|
||||
void Curl_pp_init(struct pingpong *pp);
|
||||
|
||||
/* Returns timeout in ms. 0 or negative number means the timeout has already
|
||||
triggered */
|
||||
long Curl_pp_state_timeout(struct pingpong *pp);
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
*
|
||||
* Curl_pp_sendf()
|
||||
*
|
||||
* Send the formated string as a command to a pingpong server. Note that
|
||||
* the string should not have any CRLF appended, as this function will
|
||||
* append the necessary things itself.
|
||||
*
|
||||
* NOTE: we build the command in a fixed-length buffer, which sets length
|
||||
* restrictions on the command!
|
||||
*
|
||||
* made to never block
|
||||
*/
|
||||
CURLcode Curl_pp_sendf(struct pingpong *pp,
|
||||
const char *fmt, ...);
|
||||
|
||||
/***********************************************************************
|
||||
*
|
||||
* Curl_pp_vsendf()
|
||||
*
|
||||
* Send the formated string as a command to a pingpong server. Note that
|
||||
* the string should not have any CRLF appended, as this function will
|
||||
* append the necessary things itself.
|
||||
*
|
||||
* NOTE: we build the command in a fixed-length buffer, which sets length
|
||||
* restrictions on the command!
|
||||
*
|
||||
* made to never block
|
||||
*/
|
||||
CURLcode Curl_pp_vsendf(struct pingpong *pp,
|
||||
const char *fmt,
|
||||
va_list args);
|
||||
|
||||
/*
|
||||
* Curl_pp_readresp()
|
||||
*
|
||||
* Reads a piece of a server response.
|
||||
*/
|
||||
CURLcode Curl_pp_readresp(curl_socket_t sockfd,
|
||||
struct pingpong *pp,
|
||||
int *code, /* return the server code if done */
|
||||
size_t *size); /* size of the response */
|
||||
|
||||
|
||||
CURLcode Curl_pp_flushsend(struct pingpong *pp);
|
||||
|
||||
/* call this when a pingpong connection is disconnected */
|
||||
CURLcode Curl_pp_disconnect(struct pingpong *pp);
|
||||
|
||||
int Curl_pp_getsock(struct pingpong *pp, curl_socket_t *socks,
|
||||
int numsocks);
|
||||
|
||||
#endif /* __PINGPONG_H */
|
961
lib/pop3.c
Normal file
961
lib/pop3.c
Normal file
@ -0,0 +1,961 @@
|
||||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) 1998 - 2009, Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution. The terms
|
||||
* are also available at http://curl.haxx.se/docs/copyright.html.
|
||||
*
|
||||
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||
* copies of the Software, and permit persons to whom the Software is
|
||||
* furnished to do so, under the terms of the COPYING file.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
* RFC1939 POP3 protocol
|
||||
* RFC2384 POP URL Scheme
|
||||
* RFC2595 Using TLS with IMAP, POP3 and ACAP
|
||||
*
|
||||
* $Id$
|
||||
***************************************************************************/
|
||||
|
||||
#include "setup.h"
|
||||
|
||||
#ifndef CURL_DISABLE_POP3
|
||||
#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
|
||||
#ifdef HAVE_ARPA_INET_H
|
||||
#include <arpa/inet.h>
|
||||
#endif
|
||||
#ifdef HAVE_UTSNAME_H
|
||||
#include <sys/utsname.h>
|
||||
#endif
|
||||
#ifdef HAVE_NETDB_H
|
||||
#include <netdb.h>
|
||||
#endif
|
||||
#ifdef VMS
|
||||
#include <in.h>
|
||||
#include <inet.h>
|
||||
#endif
|
||||
|
||||
#if (defined(NETWARE) && defined(__NOVELL_LIBC__))
|
||||
#undef in_addr_t
|
||||
#define in_addr_t unsigned long
|
||||
#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 "transfer.h"
|
||||
#include "escape.h"
|
||||
#include "http.h" /* for HTTP proxy tunnel stuff */
|
||||
#include "socks.h"
|
||||
#include "pop3.h"
|
||||
|
||||
#include "strtoofft.h"
|
||||
#include "strequal.h"
|
||||
#include "sslgen.h"
|
||||
#include "connect.h"
|
||||
#include "strerror.h"
|
||||
#include "select.h"
|
||||
#include "multiif.h"
|
||||
#include "url.h"
|
||||
#include "rawstr.h"
|
||||
#include "strtoofft.h"
|
||||
|
||||
#define _MPRINTF_REPLACE /* use our functions only */
|
||||
#include <curl/mprintf.h>
|
||||
|
||||
#include "curl_memory.h"
|
||||
/* The last #include file should be: */
|
||||
#include "memdebug.h"
|
||||
|
||||
/* Local API functions */
|
||||
static CURLcode pop3_parse_url_path(struct connectdata *conn);
|
||||
static CURLcode pop3_regular_transfer(struct connectdata *conn, bool *done);
|
||||
static CURLcode pop3_do(struct connectdata *conn, bool *done);
|
||||
static CURLcode pop3_done(struct connectdata *conn,
|
||||
CURLcode, bool premature);
|
||||
static CURLcode pop3_connect(struct connectdata *conn, bool *done);
|
||||
static CURLcode pop3_disconnect(struct connectdata *conn);
|
||||
static CURLcode pop3_multi_statemach(struct connectdata *conn, bool *done);
|
||||
static int pop3_getsock(struct connectdata *conn,
|
||||
curl_socket_t *socks,
|
||||
int numsocks);
|
||||
static CURLcode pop3_doing(struct connectdata *conn,
|
||||
bool *dophase_done);
|
||||
static CURLcode pop3_setup_connection(struct connectdata * conn);
|
||||
|
||||
/*
|
||||
* POP3 protocol handler.
|
||||
*/
|
||||
|
||||
const struct Curl_handler Curl_handler_pop3 = {
|
||||
"POP3", /* scheme */
|
||||
pop3_setup_connection, /* setup_connection */
|
||||
pop3_do, /* do_it */
|
||||
pop3_done, /* done */
|
||||
ZERO_NULL, /* do_more */
|
||||
pop3_connect, /* connect_it */
|
||||
pop3_multi_statemach, /* connecting */
|
||||
pop3_doing, /* doing */
|
||||
pop3_getsock, /* proto_getsock */
|
||||
pop3_getsock, /* doing_getsock */
|
||||
ZERO_NULL, /* perform_getsock */
|
||||
pop3_disconnect, /* disconnect */
|
||||
PORT_POP3, /* defport */
|
||||
PROT_POP3 /* protocol */
|
||||
};
|
||||
|
||||
|
||||
#ifdef USE_SSL
|
||||
/*
|
||||
* POP3S protocol handler.
|
||||
*/
|
||||
|
||||
const struct Curl_handler Curl_handler_pop3s = {
|
||||
"POP3S", /* scheme */
|
||||
pop3_setup_connection, /* setup_connection */
|
||||
pop3_do, /* do_it */
|
||||
pop3_done, /* done */
|
||||
ZERO_NULL, /* do_more */
|
||||
pop3_connect, /* connect_it */
|
||||
pop3_multi_statemach, /* connecting */
|
||||
pop3_doing, /* doing */
|
||||
pop3_getsock, /* proto_getsock */
|
||||
pop3_getsock, /* doing_getsock */
|
||||
ZERO_NULL, /* perform_getsock */
|
||||
pop3_disconnect, /* disconnect */
|
||||
PORT_POP3S, /* defport */
|
||||
PROT_POP3 | PROT_POP3S | PROT_SSL /* protocol */
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifndef CURL_DISABLE_HTTP
|
||||
/*
|
||||
* HTTP-proxyed POP3 protocol handler.
|
||||
*/
|
||||
|
||||
const struct Curl_handler Curl_handler_pop3_proxy = {
|
||||
"POP3", /* scheme */
|
||||
ZERO_NULL, /* setup_connection */
|
||||
Curl_http, /* do_it */
|
||||
Curl_http_done, /* done */
|
||||
ZERO_NULL, /* do_more */
|
||||
ZERO_NULL, /* connect_it */
|
||||
ZERO_NULL, /* connecting */
|
||||
ZERO_NULL, /* doing */
|
||||
ZERO_NULL, /* proto_getsock */
|
||||
ZERO_NULL, /* doing_getsock */
|
||||
ZERO_NULL, /* perform_getsock */
|
||||
ZERO_NULL, /* disconnect */
|
||||
PORT_POP3, /* defport */
|
||||
PROT_HTTP /* protocol */
|
||||
};
|
||||
|
||||
|
||||
#ifdef USE_SSL
|
||||
/*
|
||||
* HTTP-proxyed POP3S protocol handler.
|
||||
*/
|
||||
|
||||
const struct Curl_handler Curl_handler_pop3s_proxy = {
|
||||
"POP3S", /* scheme */
|
||||
ZERO_NULL, /* setup_connection */
|
||||
Curl_http, /* do_it */
|
||||
Curl_http_done, /* done */
|
||||
ZERO_NULL, /* do_more */
|
||||
ZERO_NULL, /* connect_it */
|
||||
ZERO_NULL, /* connecting */
|
||||
ZERO_NULL, /* doing */
|
||||
ZERO_NULL, /* proto_getsock */
|
||||
ZERO_NULL, /* doing_getsock */
|
||||
ZERO_NULL, /* perform_getsock */
|
||||
ZERO_NULL, /* disconnect */
|
||||
PORT_POP3S, /* defport */
|
||||
PROT_HTTP /* protocol */
|
||||
};
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
/* function that checks for a pop3 status code at the start of the given
|
||||
string */
|
||||
static int pop3_endofresp(struct pingpong *pp,
|
||||
int *resp)
|
||||
{
|
||||
char *line = pp->linestart_resp;
|
||||
size_t len = pp->nread_resp;
|
||||
|
||||
if( ((len >= 3) && !memcmp("+OK", line, 3)) ||
|
||||
((len >= 4) && !memcmp("-ERR", line, 4)) ) {
|
||||
*resp=line[1]; /* O or E */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE; /* nothing for us */
|
||||
}
|
||||
|
||||
/* This is the ONLY way to change POP3 state! */
|
||||
static void state(struct connectdata *conn,
|
||||
pop3state newstate)
|
||||
{
|
||||
#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
|
||||
/* for debug purposes */
|
||||
static const char * const names[]={
|
||||
"STOP",
|
||||
"SERVERGREET",
|
||||
"USER",
|
||||
"PASS",
|
||||
"STARTTLS",
|
||||
"RETR",
|
||||
"QUIT",
|
||||
/* LAST */
|
||||
};
|
||||
#endif
|
||||
struct pop3_conn *pop3c = &conn->proto.pop3c;
|
||||
#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
|
||||
if(pop3c->state != newstate)
|
||||
infof(conn->data, "POP3 %p state change from %s to %s\n",
|
||||
pop3c, names[pop3c->state], names[newstate]);
|
||||
#endif
|
||||
pop3c->state = newstate;
|
||||
}
|
||||
|
||||
static CURLcode pop3_state_user(struct connectdata *conn)
|
||||
{
|
||||
CURLcode result;
|
||||
struct FTP *pop3 = conn->data->state.proto.pop3;
|
||||
|
||||
/* send USER */
|
||||
result = Curl_pp_sendf(&conn->proto.pop3c.pp, "USER %s",
|
||||
pop3->user?pop3->user:"");
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
state(conn, POP3_USER);
|
||||
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
/* For the POP3 "protocol connect" and "doing" phases only */
|
||||
static int pop3_getsock(struct connectdata *conn,
|
||||
curl_socket_t *socks,
|
||||
int numsocks)
|
||||
{
|
||||
return Curl_pp_getsock(&conn->proto.pop3c.pp, socks, numsocks);
|
||||
}
|
||||
|
||||
/* for STARTTLS responses */
|
||||
static CURLcode pop3_state_starttls_resp(struct connectdata *conn,
|
||||
int pop3code,
|
||||
pop3state instate)
|
||||
{
|
||||
CURLcode result = CURLE_OK;
|
||||
struct SessionHandle *data = conn->data;
|
||||
(void)instate; /* no use for this yet */
|
||||
|
||||
if(pop3code != 'O') {
|
||||
failf(data, "STARTTLS denied. %c", pop3code);
|
||||
result = CURLE_LOGIN_DENIED;
|
||||
}
|
||||
else {
|
||||
/* Curl_ssl_connect is BLOCKING */
|
||||
result = Curl_ssl_connect(conn, FIRSTSOCKET);
|
||||
if(CURLE_OK == result) {
|
||||
conn->protocol |= PROT_POP3S;
|
||||
result = pop3_state_user(conn);
|
||||
}
|
||||
}
|
||||
state(conn, POP3_STOP);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* for USER responses */
|
||||
static CURLcode pop3_state_user_resp(struct connectdata *conn,
|
||||
int pop3code,
|
||||
pop3state instate)
|
||||
{
|
||||
CURLcode result = CURLE_OK;
|
||||
struct SessionHandle *data = conn->data;
|
||||
struct FTP *pop3 = data->state.proto.pop3;
|
||||
|
||||
(void)instate; /* no use for this yet */
|
||||
|
||||
if(pop3code != 'O') {
|
||||
failf(data, "Access denied. %c", pop3code);
|
||||
result = CURLE_LOGIN_DENIED;
|
||||
}
|
||||
|
||||
/* send PASS */
|
||||
result = Curl_pp_sendf(&conn->proto.pop3c.pp, "PASS %s",
|
||||
pop3->passwd?pop3->passwd:"");
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
state(conn, POP3_PASS);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* for PASS responses */
|
||||
static CURLcode pop3_state_pass_resp(struct connectdata *conn,
|
||||
int pop3code,
|
||||
pop3state instate)
|
||||
{
|
||||
CURLcode result = CURLE_OK;
|
||||
struct SessionHandle *data = conn->data;
|
||||
(void)instate; /* no use for this yet */
|
||||
|
||||
if(pop3code != 'O') {
|
||||
failf(data, "Access denied. %c", pop3code);
|
||||
result = CURLE_LOGIN_DENIED;
|
||||
}
|
||||
|
||||
state(conn, POP3_STOP);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* for the retr response */
|
||||
static CURLcode pop3_state_retr_resp(struct connectdata *conn,
|
||||
int pop3code,
|
||||
pop3state instate)
|
||||
{
|
||||
CURLcode result = CURLE_OK;
|
||||
struct SessionHandle *data = conn->data;
|
||||
struct FTP *pop3 = data->state.proto.pop3;
|
||||
struct pop3_conn *pop3c = &conn->proto.pop3c;
|
||||
struct pingpong *pp = &pop3c->pp;
|
||||
|
||||
(void)instate; /* no use for this yet */
|
||||
|
||||
if('O' != pop3code) {
|
||||
state(conn, POP3_STOP);
|
||||
return CURLE_RECV_ERROR;
|
||||
}
|
||||
|
||||
/* POP3 download */
|
||||
result=Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE,
|
||||
pop3->bytecountp,
|
||||
-1, NULL); /* no upload here */
|
||||
|
||||
if(pp->cache) {
|
||||
/* At this point there is a bunch of data in the header "cache" that is
|
||||
actually body content, send it as body and then skip it. Do note
|
||||
that there may even be additional "headers" after the body. */
|
||||
|
||||
/* we may get the EOB already here! */
|
||||
result = Curl_pop3_write(conn, pp->cache, pp->cache_size);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
/* cache is drained */
|
||||
free(pp->cache);
|
||||
pp->cache = NULL;
|
||||
pp->cache_size = 0;
|
||||
}
|
||||
|
||||
state(conn, POP3_STOP);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* start the DO phase */
|
||||
static CURLcode pop3_retr(struct connectdata *conn)
|
||||
{
|
||||
CURLcode result = CURLE_OK;
|
||||
struct pop3_conn *pop3c = &conn->proto.pop3c;
|
||||
|
||||
result = Curl_pp_sendf(&conn->proto.pop3c.pp, "RETR %s", pop3c->mailbox);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
state(conn, POP3_RETR);
|
||||
return result;
|
||||
}
|
||||
|
||||
static CURLcode pop3_statemach_act(struct connectdata *conn)
|
||||
{
|
||||
CURLcode result;
|
||||
curl_socket_t sock = conn->sock[FIRSTSOCKET];
|
||||
struct SessionHandle *data=conn->data;
|
||||
int pop3code;
|
||||
struct pop3_conn *pop3c = &conn->proto.pop3c;
|
||||
struct pingpong *pp = &pop3c->pp;
|
||||
size_t nread = 0;
|
||||
|
||||
if(pp->sendleft)
|
||||
return Curl_pp_flushsend(pp);
|
||||
|
||||
/* we read a piece of response */
|
||||
result = Curl_pp_readresp(sock, pp, &pop3code, &nread);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
if(pop3code) {
|
||||
/* we have now received a full POP3 server response */
|
||||
switch(pop3c->state) {
|
||||
case POP3_SERVERGREET:
|
||||
if(pop3code != 'O') {
|
||||
failf(data, "Got unexpected pop3-server response");
|
||||
return CURLE_FTP_WEIRD_SERVER_REPLY;
|
||||
}
|
||||
|
||||
if(data->set.ftp_ssl && !conn->ssl[FIRSTSOCKET].use) {
|
||||
/* We don't have a SSL/TLS connection yet, but SSL is requested. Switch
|
||||
to TLS connection now */
|
||||
const char *str;
|
||||
|
||||
result = Curl_pp_sendf(&pop3c->pp, "STARTTLS", str);
|
||||
state(conn, POP3_STARTTLS);
|
||||
}
|
||||
else
|
||||
result = pop3_state_user(conn);
|
||||
if(result)
|
||||
return result;
|
||||
break;
|
||||
|
||||
case POP3_USER:
|
||||
result = pop3_state_user_resp(conn, pop3code, pop3c->state);
|
||||
break;
|
||||
|
||||
case POP3_PASS:
|
||||
result = pop3_state_pass_resp(conn, pop3code, pop3c->state);
|
||||
break;
|
||||
|
||||
case POP3_STARTTLS:
|
||||
result = pop3_state_starttls_resp(conn, pop3code, pop3c->state);
|
||||
break;
|
||||
|
||||
case POP3_RETR:
|
||||
result = pop3_state_retr_resp(conn, pop3code, pop3c->state);
|
||||
break;
|
||||
|
||||
case POP3_QUIT:
|
||||
/* fallthrough, just stop! */
|
||||
default:
|
||||
/* internal error */
|
||||
state(conn, POP3_STOP);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/* called repeatedly until done from multi.c */
|
||||
static CURLcode pop3_multi_statemach(struct connectdata *conn, bool *done)
|
||||
{
|
||||
struct pop3_conn *pop3c = &conn->proto.pop3c;
|
||||
CURLcode result = Curl_pp_multi_statemach(&pop3c->pp);
|
||||
|
||||
*done = (bool)(pop3c->state == POP3_STOP);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static CURLcode pop3_easy_statemach(struct connectdata *conn)
|
||||
{
|
||||
struct pop3_conn *pop3c = &conn->proto.pop3c;
|
||||
struct pingpong *pp = &pop3c->pp;
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
while(pop3c->state != POP3_STOP) {
|
||||
result = Curl_pp_easy_statemach(pp);
|
||||
if(result)
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate and initialize the struct POP3 for the current SessionHandle. If
|
||||
* need be.
|
||||
*/
|
||||
static CURLcode pop3_init(struct connectdata *conn)
|
||||
{
|
||||
struct SessionHandle *data = conn->data;
|
||||
struct FTP *pop3 = data->state.proto.pop3;
|
||||
if(!pop3) {
|
||||
pop3 = data->state.proto.pop3 = calloc(sizeof(struct FTP), 1);
|
||||
if(!pop3)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
/* get some initial data into the pop3 struct */
|
||||
pop3->bytecountp = &data->req.bytecount;
|
||||
|
||||
/* 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.
|
||||
*/
|
||||
pop3->user = conn->user;
|
||||
pop3->passwd = conn->passwd;
|
||||
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* pop3_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.
|
||||
*/
|
||||
static CURLcode pop3_connect(struct connectdata *conn,
|
||||
bool *done) /* see description above */
|
||||
{
|
||||
CURLcode result;
|
||||
struct pop3_conn *pop3c = &conn->proto.pop3c;
|
||||
struct SessionHandle *data=conn->data;
|
||||
struct pingpong *pp = &pop3c->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 = pop3_init(conn);
|
||||
if(CURLE_OK != result)
|
||||
return result;
|
||||
|
||||
/* We always support persistant connections on pop3 */
|
||||
conn->bits.close = FALSE;
|
||||
|
||||
pp->response_time = RESP_TIMEOUT; /* set default response time-out */
|
||||
pp->statemach_act = pop3_statemach_act;
|
||||
pp->endofresp = pop3_endofresp;
|
||||
pp->conn = conn;
|
||||
|
||||
#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_PROXY)
|
||||
if(conn->bits.tunnel_proxy && conn->bits.httpproxy) {
|
||||
/* for POP3 over HTTP proxy */
|
||||
struct HTTP http_proxy;
|
||||
struct FTP *pop3_save;
|
||||
|
||||
/* BLOCKING */
|
||||
/* We want "seamless" POP3 operations through HTTP proxy tunnel */
|
||||
|
||||
/* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the member
|
||||
* conn->proto.http; we want POP3 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
|
||||
* POP3 pointer
|
||||
*/
|
||||
pop3_save = data->state.proto.pop3;
|
||||
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.pop3 = pop3_save;
|
||||
|
||||
if(CURLE_OK != result)
|
||||
return result;
|
||||
}
|
||||
#endif /* !CURL_DISABLE_HTTP && !CURL_DISABLE_PROXY */
|
||||
|
||||
if(conn->protocol & PROT_POP3S) {
|
||||
/* BLOCKING */
|
||||
/* POP3S is simply pop3 with SSL for the control channel */
|
||||
/* now, perform the SSL initialization for this socket */
|
||||
result = Curl_ssl_connect(conn, FIRSTSOCKET);
|
||||
if(result)
|
||||
return result;
|
||||
}
|
||||
|
||||
Curl_pp_init(pp); /* init the response reader stuff */
|
||||
|
||||
/* When we connect, we start in the state where we await the server greet
|
||||
response */
|
||||
state(conn, POP3_SERVERGREET);
|
||||
|
||||
if(data->state.used_interface == Curl_if_multi)
|
||||
result = pop3_multi_statemach(conn, done);
|
||||
else {
|
||||
result = pop3_easy_statemach(conn);
|
||||
if(!result)
|
||||
*done = TRUE;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
*
|
||||
* pop3_done()
|
||||
*
|
||||
* The DONE function. This does what needs to be done after a single DO has
|
||||
* performed.
|
||||
*
|
||||
* Input argument is already checked for validity.
|
||||
*/
|
||||
static CURLcode pop3_done(struct connectdata *conn, CURLcode status,
|
||||
bool premature)
|
||||
{
|
||||
struct SessionHandle *data = conn->data;
|
||||
struct FTP *pop3 = data->state.proto.pop3;
|
||||
CURLcode result=CURLE_OK;
|
||||
(void)premature;
|
||||
|
||||
if(!pop3)
|
||||
/* When the easy handle is removed from the multi while libcurl is still
|
||||
* trying to resolve the host name, it seems that the pop3 struct is not
|
||||
* yet initialized, but the removal action calls Curl_done() which calls
|
||||
* this function. So we simply return success if no pop3 pointer is set.
|
||||
*/
|
||||
return CURLE_OK;
|
||||
|
||||
if(status) {
|
||||
conn->bits.close = TRUE; /* marked for closure */
|
||||
result = status; /* use the already set error code */
|
||||
}
|
||||
|
||||
/* clear these for next connection */
|
||||
pop3->transfer = FTPTRANSFER_BODY;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
*
|
||||
* pop3_perform()
|
||||
*
|
||||
* This is the actual DO function for POP3. Get a file/directory according to
|
||||
* the options previously setup.
|
||||
*/
|
||||
|
||||
static
|
||||
CURLcode pop3_perform(struct connectdata *conn,
|
||||
bool *connected, /* connect status after PASV / PORT */
|
||||
bool *dophase_done)
|
||||
{
|
||||
/* this is POP3 and no proxy */
|
||||
CURLcode result=CURLE_OK;
|
||||
|
||||
DEBUGF(infof(conn->data, "DO phase starts\n"));
|
||||
|
||||
if(conn->data->set.opt_no_body) {
|
||||
/* requested no body means no transfer... */
|
||||
struct FTP *pop3 = conn->data->state.proto.pop3;
|
||||
pop3->transfer = FTPTRANSFER_INFO;
|
||||
}
|
||||
|
||||
*dophase_done = FALSE; /* not done yet */
|
||||
|
||||
/* start the first command in the DO phase */
|
||||
result = pop3_retr(conn);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
/* run the state-machine */
|
||||
if(conn->data->state.used_interface == Curl_if_multi)
|
||||
result = pop3_multi_statemach(conn, dophase_done);
|
||||
else {
|
||||
result = pop3_easy_statemach(conn);
|
||||
*dophase_done = TRUE; /* with the easy interface we are done here */
|
||||
}
|
||||
*connected = conn->bits.tcpconnect;
|
||||
|
||||
if(*dophase_done)
|
||||
DEBUGF(infof(conn->data, "DO phase is complete\n"));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
*
|
||||
* pop3_do()
|
||||
*
|
||||
* This function is registered as 'curl_do' function. It decodes the path
|
||||
* parts etc as a wrapper to the actual DO function (pop3_perform).
|
||||
*
|
||||
* The input argument is already checked for validity.
|
||||
*/
|
||||
static CURLcode pop3_do(struct connectdata *conn, bool *done)
|
||||
{
|
||||
CURLcode retcode = CURLE_OK;
|
||||
|
||||
*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 POP3' to play with. For new connections,
|
||||
the struct POP3 is allocated and setup in the pop3_connect() function.
|
||||
*/
|
||||
Curl_reset_reqproto(conn);
|
||||
retcode = pop3_init(conn);
|
||||
if(retcode)
|
||||
return retcode;
|
||||
|
||||
retcode = pop3_parse_url_path(conn);
|
||||
if(retcode)
|
||||
return retcode;
|
||||
|
||||
retcode = pop3_regular_transfer(conn, done);
|
||||
|
||||
return retcode;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
*
|
||||
* pop3_quit()
|
||||
*
|
||||
* This should be called before calling sclose(). We should then wait for the
|
||||
* response from the server before returning. The calling code should then try
|
||||
* to close the connection.
|
||||
*
|
||||
*/
|
||||
static CURLcode pop3_quit(struct connectdata *conn)
|
||||
{
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
result = Curl_pp_sendf(&conn->proto.pop3c.pp, "QUIT", NULL);
|
||||
if(result)
|
||||
return result;
|
||||
state(conn, POP3_QUIT);
|
||||
|
||||
result = pop3_easy_statemach(conn);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
*
|
||||
* pop3_disconnect()
|
||||
*
|
||||
* Disconnect from an POP3 server. Cleanup protocol-specific per-connection
|
||||
* resources. BLOCKING.
|
||||
*/
|
||||
static CURLcode pop3_disconnect(struct connectdata *conn)
|
||||
{
|
||||
struct pop3_conn *pop3c= &conn->proto.pop3c;
|
||||
|
||||
/* We cannot send quit unconditionally. If this connection is stale or
|
||||
bad in any way, sending quit and waiting around here will make the
|
||||
disconnect wait in vain and cause more problems than we need to.
|
||||
*/
|
||||
|
||||
/* The POP3 session may or may not have been allocated/setup at this
|
||||
point! */
|
||||
(void)pop3_quit(conn); /* ignore errors on the LOGOUT */
|
||||
|
||||
|
||||
Curl_pp_disconnect(&pop3c->pp);
|
||||
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
*
|
||||
* pop3_parse_url_path()
|
||||
*
|
||||
* Parse the URL path into separate path components.
|
||||
*
|
||||
*/
|
||||
static CURLcode pop3_parse_url_path(struct connectdata *conn)
|
||||
{
|
||||
/* the pop3 struct is already inited in pop3_connect() */
|
||||
struct pop3_conn *pop3c = &conn->proto.pop3c;
|
||||
struct SessionHandle *data = conn->data;
|
||||
const char *path = data->state.path;
|
||||
int len;
|
||||
|
||||
if(!*path)
|
||||
path = "INBOX";
|
||||
|
||||
/* url decode the path and use this mailbox */
|
||||
pop3c->mailbox = curl_easy_unescape(data, path, 0, &len);
|
||||
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
/* call this when the DO phase has completed */
|
||||
static CURLcode pop3_dophase_done(struct connectdata *conn,
|
||||
bool connected)
|
||||
{
|
||||
CURLcode result = CURLE_OK;
|
||||
struct FTP *pop3 = conn->data->state.proto.pop3;
|
||||
struct pop3_conn *pop3c = &conn->proto.pop3c;
|
||||
(void)connected;
|
||||
|
||||
if(pop3->transfer != FTPTRANSFER_BODY)
|
||||
/* no data to transfer */
|
||||
result=Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
|
||||
|
||||
free(pop3c->mailbox);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* called from multi.c while DOing */
|
||||
static CURLcode pop3_doing(struct connectdata *conn,
|
||||
bool *dophase_done)
|
||||
{
|
||||
CURLcode result;
|
||||
result = pop3_multi_statemach(conn, dophase_done);
|
||||
|
||||
if(*dophase_done) {
|
||||
result = pop3_dophase_done(conn, FALSE /* not connected */);
|
||||
|
||||
DEBUGF(infof(conn->data, "DO phase is complete\n"));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
*
|
||||
* pop3_regular_transfer()
|
||||
*
|
||||
* The input argument is already checked for validity.
|
||||
*
|
||||
* Performs all commands done before a regular transfer between a local and a
|
||||
* remote host.
|
||||
*
|
||||
*/
|
||||
static
|
||||
CURLcode pop3_regular_transfer(struct connectdata *conn,
|
||||
bool *dophase_done)
|
||||
{
|
||||
CURLcode result=CURLE_OK;
|
||||
bool connected=FALSE;
|
||||
struct SessionHandle *data = conn->data;
|
||||
data->req.size = -1; /* make sure this is unknown at this point */
|
||||
|
||||
Curl_pgrsSetUploadCounter(data, 0);
|
||||
Curl_pgrsSetDownloadCounter(data, 0);
|
||||
Curl_pgrsSetUploadSize(data, 0);
|
||||
Curl_pgrsSetDownloadSize(data, 0);
|
||||
|
||||
result = pop3_perform(conn,
|
||||
&connected, /* have we connected after PASV/PORT */
|
||||
dophase_done); /* all commands in the DO-phase done? */
|
||||
|
||||
if(CURLE_OK == result) {
|
||||
|
||||
if(!*dophase_done)
|
||||
/* the DO phase has not completed yet */
|
||||
return CURLE_OK;
|
||||
|
||||
result = pop3_dophase_done(conn, connected);
|
||||
if(result)
|
||||
return result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static CURLcode pop3_setup_connection(struct connectdata * conn)
|
||||
{
|
||||
struct SessionHandle *data = conn->data;
|
||||
|
||||
if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
|
||||
/* Unless we have asked to tunnel pop3 operations through the proxy, we
|
||||
switch and use HTTP operations only */
|
||||
#ifndef CURL_DISABLE_HTTP
|
||||
if(conn->handler == &Curl_handler_pop3)
|
||||
conn->handler = &Curl_handler_pop3_proxy;
|
||||
else {
|
||||
#ifdef USE_SSL
|
||||
conn->handler = &Curl_handler_pop3s_proxy;
|
||||
#else
|
||||
failf(data, "POP3S not supported!");
|
||||
return CURLE_UNSUPPORTED_PROTOCOL;
|
||||
#endif
|
||||
}
|
||||
/*
|
||||
* We explicitly mark this connection as persistent here as we're doing
|
||||
* POP3 over HTTP and thus we accidentally avoid setting this value
|
||||
* otherwise.
|
||||
*/
|
||||
conn->bits.close = FALSE;
|
||||
#else
|
||||
failf(data, "POP3 over http proxy requires HTTP support built-in!");
|
||||
return CURLE_UNSUPPORTED_PROTOCOL;
|
||||
#endif
|
||||
}
|
||||
|
||||
data->state.path++; /* don't include the initial slash */
|
||||
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
/* this is the 5-bytes End-Of-Body marker for POP3 */
|
||||
#define POP3_EOB "\x0d\x0a\x2e\x0d\x0a"
|
||||
#define POP3_EOB_LEN 5
|
||||
|
||||
/*
|
||||
* This function scans the body after the end-of-body and writes everything
|
||||
* until the end is found.
|
||||
*/
|
||||
CURLcode Curl_pop3_write(struct connectdata *conn,
|
||||
char *str,
|
||||
size_t nread)
|
||||
{
|
||||
/* This code could be made into a special function in the handler struct. */
|
||||
CURLcode result;
|
||||
struct SessionHandle *data = conn->data;
|
||||
struct SingleRequest *k = &data->req;
|
||||
|
||||
/* Detect the end-of-body marker, which is 5 bytes:
|
||||
0d 0a 2e 0d 0a. This marker can of course be spread out
|
||||
over up to 5 different data chunks. Deal with it! */
|
||||
struct pop3_conn *pop3c = &conn->proto.pop3c;
|
||||
int checkmax = (nread >= POP3_EOB_LEN?POP3_EOB_LEN:nread);
|
||||
int checkleft = POP3_EOB_LEN-pop3c->eob;
|
||||
int check = checkmax>= checkleft?checkleft:checkmax;
|
||||
|
||||
if(!memcmp(POP3_EOB, &str[nread - check], check)) {
|
||||
/* substring match */
|
||||
pop3c->eob += check;
|
||||
if(pop3c->eob == POP3_EOB_LEN) {
|
||||
/* full match, the transfer is done! */
|
||||
nread -= check;
|
||||
k->keepon &= ~KEEP_RECV;
|
||||
pop3c->eob = 0;
|
||||
}
|
||||
}
|
||||
else if(pop3c->eob) {
|
||||
/* not a match, but we matched a piece before so we must now
|
||||
send that part as body first, before we move on and send
|
||||
this buffer */
|
||||
result = Curl_client_write(conn, CLIENTWRITE_BODY,
|
||||
(char *)POP3_EOB, pop3c->eob);
|
||||
if(result)
|
||||
return result;
|
||||
pop3c->eob = 0;
|
||||
}
|
||||
|
||||
result = Curl_client_write(conn, CLIENTWRITE_BODY, str, nread);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif /* CURL_DISABLE_POP3 */
|
62
lib/pop3.h
Normal file
62
lib/pop3.h
Normal file
@ -0,0 +1,62 @@
|
||||
#ifndef __POP3_H
|
||||
#define __POP3_H
|
||||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) 2009, Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution. The terms
|
||||
* are also available at http://curl.haxx.se/docs/copyright.html.
|
||||
*
|
||||
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||
* copies of the Software, and permit persons to whom the Software is
|
||||
* furnished to do so, under the terms of the COPYING file.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
* $Id$
|
||||
***************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* POP3 unique setup
|
||||
***************************************************************************/
|
||||
typedef enum {
|
||||
POP3_STOP, /* do nothing state, stops the state machine */
|
||||
POP3_SERVERGREET, /* waiting for the initial greeting immediately after
|
||||
a connect */
|
||||
POP3_USER,
|
||||
POP3_PASS,
|
||||
POP3_STARTTLS,
|
||||
POP3_RETR,
|
||||
POP3_QUIT,
|
||||
POP3_LAST /* never used */
|
||||
} pop3state;
|
||||
|
||||
/* pop3_conn is used for struct connection-oriented data in the connectdata
|
||||
struct */
|
||||
struct pop3_conn {
|
||||
struct pingpong pp;
|
||||
char *mailbox; /* what to RETR */
|
||||
int eob; /* number of bytes of the EOB (End Of Body) that has been
|
||||
received thus far */
|
||||
pop3state state; /* always use pop3.c:state() to change state! */
|
||||
};
|
||||
|
||||
extern const struct Curl_handler Curl_handler_pop3;
|
||||
extern const struct Curl_handler Curl_handler_pop3s;
|
||||
|
||||
/*
|
||||
* This function scans the body after the end-of-body and writes everything
|
||||
* until the end is found.
|
||||
*/
|
||||
CURLcode Curl_pop3_write(struct connectdata *conn,
|
||||
char *str,
|
||||
size_t nread);
|
||||
|
||||
#endif /* __POP3_H */
|
921
lib/smtp.c
Normal file
921
lib/smtp.c
Normal file
@ -0,0 +1,921 @@
|
||||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) 1998 - 2009, Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution. The terms
|
||||
* are also available at http://curl.haxx.se/docs/copyright.html.
|
||||
*
|
||||
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||
* copies of the Software, and permit persons to whom the Software is
|
||||
* furnished to do so, under the terms of the COPYING file.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
* RFC2821 SMTP protocol
|
||||
*
|
||||
* $Id$
|
||||
***************************************************************************/
|
||||
|
||||
#include "setup.h"
|
||||
|
||||
#ifndef CURL_DISABLE_SMTP
|
||||
#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
|
||||
#ifdef HAVE_ARPA_INET_H
|
||||
#include <arpa/inet.h>
|
||||
#endif
|
||||
#ifdef HAVE_UTSNAME_H
|
||||
#include <sys/utsname.h>
|
||||
#endif
|
||||
#ifdef HAVE_NETDB_H
|
||||
#include <netdb.h>
|
||||
#endif
|
||||
#ifdef VMS
|
||||
#include <in.h>
|
||||
#include <inet.h>
|
||||
#endif
|
||||
|
||||
#if (defined(NETWARE) && defined(__NOVELL_LIBC__))
|
||||
#undef in_addr_t
|
||||
#define in_addr_t unsigned long
|
||||
#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 "transfer.h"
|
||||
#include "escape.h"
|
||||
#include "http.h" /* for HTTP proxy tunnel stuff */
|
||||
#include "socks.h"
|
||||
#include "smtp.h"
|
||||
|
||||
#include "strtoofft.h"
|
||||
#include "strequal.h"
|
||||
#include "sslgen.h"
|
||||
#include "connect.h"
|
||||
#include "strerror.h"
|
||||
#include "select.h"
|
||||
#include "multiif.h"
|
||||
#include "url.h"
|
||||
#include "rawstr.h"
|
||||
#include "strtoofft.h"
|
||||
|
||||
#define _MPRINTF_REPLACE /* use our functions only */
|
||||
#include <curl/mprintf.h>
|
||||
|
||||
#include "curl_memory.h"
|
||||
/* The last #include file should be: */
|
||||
#include "memdebug.h"
|
||||
|
||||
/* Local API functions */
|
||||
static CURLcode smtp_parse_url_path(struct connectdata *conn);
|
||||
static CURLcode smtp_regular_transfer(struct connectdata *conn, bool *done);
|
||||
static CURLcode smtp_do(struct connectdata *conn, bool *done);
|
||||
static CURLcode smtp_done(struct connectdata *conn,
|
||||
CURLcode, bool premature);
|
||||
static CURLcode smtp_connect(struct connectdata *conn, bool *done);
|
||||
static CURLcode smtp_disconnect(struct connectdata *conn);
|
||||
static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done);
|
||||
static int smtp_getsock(struct connectdata *conn,
|
||||
curl_socket_t *socks,
|
||||
int numsocks);
|
||||
static CURLcode smtp_doing(struct connectdata *conn,
|
||||
bool *dophase_done);
|
||||
static CURLcode smtp_setup_connection(struct connectdata * conn);
|
||||
static void smtp_respinit(struct connectdata *conn);
|
||||
|
||||
/*
|
||||
* SMTP protocol handler.
|
||||
*/
|
||||
|
||||
const struct Curl_handler Curl_handler_smtp = {
|
||||
"SMTP", /* scheme */
|
||||
smtp_setup_connection, /* setup_connection */
|
||||
smtp_do, /* do_it */
|
||||
smtp_done, /* done */
|
||||
ZERO_NULL, /* do_more */
|
||||
smtp_connect, /* connect_it */
|
||||
smtp_multi_statemach, /* connecting */
|
||||
smtp_doing, /* doing */
|
||||
smtp_getsock, /* proto_getsock */
|
||||
smtp_getsock, /* doing_getsock */
|
||||
ZERO_NULL, /* perform_getsock */
|
||||
smtp_disconnect, /* disconnect */
|
||||
PORT_SMTP, /* defport */
|
||||
PROT_SMTP /* protocol */
|
||||
};
|
||||
|
||||
|
||||
#ifdef USE_SSL
|
||||
/*
|
||||
* SMTPS protocol handler.
|
||||
*/
|
||||
|
||||
const struct Curl_handler Curl_handler_smtps = {
|
||||
"SMTPS", /* scheme */
|
||||
smtp_setup_connection, /* setup_connection */
|
||||
smtp_do, /* do_it */
|
||||
smtp_done, /* done */
|
||||
ZERO_NULL, /* do_more */
|
||||
smtp_connect, /* connect_it */
|
||||
smtp_multi_statemach, /* connecting */
|
||||
smtp_doing, /* doing */
|
||||
smtp_getsock, /* proto_getsock */
|
||||
smtp_getsock, /* doing_getsock */
|
||||
ZERO_NULL, /* perform_getsock */
|
||||
smtp_disconnect, /* disconnect */
|
||||
PORT_SMTPS, /* defport */
|
||||
PROT_SMTP | PROT_SMTPS | PROT_SSL /* protocol */
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifndef CURL_DISABLE_HTTP
|
||||
/*
|
||||
* HTTP-proxyed SMTP protocol handler.
|
||||
*/
|
||||
|
||||
const struct Curl_handler Curl_handler_smtp_proxy = {
|
||||
"SMTP", /* scheme */
|
||||
ZERO_NULL, /* setup_connection */
|
||||
Curl_http, /* do_it */
|
||||
Curl_http_done, /* done */
|
||||
ZERO_NULL, /* do_more */
|
||||
ZERO_NULL, /* connect_it */
|
||||
ZERO_NULL, /* connecting */
|
||||
ZERO_NULL, /* doing */
|
||||
ZERO_NULL, /* proto_getsock */
|
||||
ZERO_NULL, /* doing_getsock */
|
||||
ZERO_NULL, /* perform_getsock */
|
||||
ZERO_NULL, /* disconnect */
|
||||
PORT_SMTP, /* defport */
|
||||
PROT_HTTP /* protocol */
|
||||
};
|
||||
|
||||
|
||||
#ifdef USE_SSL
|
||||
/*
|
||||
* HTTP-proxyed SMTPS protocol handler.
|
||||
*/
|
||||
|
||||
const struct Curl_handler Curl_handler_smtps_proxy = {
|
||||
"SMTPS", /* scheme */
|
||||
ZERO_NULL, /* setup_connection */
|
||||
Curl_http, /* do_it */
|
||||
Curl_http_done, /* done */
|
||||
ZERO_NULL, /* do_more */
|
||||
ZERO_NULL, /* connect_it */
|
||||
ZERO_NULL, /* connecting */
|
||||
ZERO_NULL, /* doing */
|
||||
ZERO_NULL, /* proto_getsock */
|
||||
ZERO_NULL, /* doing_getsock */
|
||||
ZERO_NULL, /* perform_getsock */
|
||||
ZERO_NULL, /* disconnect */
|
||||
PORT_SMTPS, /* defport */
|
||||
PROT_HTTP /* protocol */
|
||||
};
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
/* fucntion that checks for an ending smtp status code at the start of the
|
||||
given string */
|
||||
static int smtp_endofresp(struct pingpong *pp, int *resp)
|
||||
{
|
||||
char *line = pp->linestart_resp;
|
||||
size_t len = pp->nread_resp;
|
||||
|
||||
if( (len >= 4) && (' ' == line[3]) &&
|
||||
ISDIGIT(line[0]) && ISDIGIT(line[1]) && ISDIGIT(line[2])) {
|
||||
*resp=atoi(line);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE; /* nothing for us */
|
||||
}
|
||||
|
||||
/* This is the ONLY way to change SMTP state! */
|
||||
static void state(struct connectdata *conn,
|
||||
smtpstate newstate)
|
||||
{
|
||||
#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
|
||||
/* for debug purposes */
|
||||
static const char * const names[]={
|
||||
"STOP",
|
||||
"SERVERGREET",
|
||||
"EHLO",
|
||||
"STARTTLS",
|
||||
"MAIL",
|
||||
"RCPT",
|
||||
"DATA",
|
||||
"QUIT",
|
||||
/* LAST */
|
||||
};
|
||||
#endif
|
||||
struct smtp_conn *smtpc = &conn->proto.smtpc;
|
||||
#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
|
||||
if(smtpc->state != newstate)
|
||||
infof(conn->data, "SMTP %p state change from %s to %s\n",
|
||||
smtpc, names[smtpc->state], names[newstate]);
|
||||
#endif
|
||||
smtpc->state = newstate;
|
||||
}
|
||||
|
||||
static CURLcode smtp_state_ehlo(struct connectdata *conn)
|
||||
{
|
||||
CURLcode result;
|
||||
struct FTP *smtp = conn->data->state.proto.smtp;
|
||||
|
||||
/* send EHLO */
|
||||
result = Curl_pp_sendf(&conn->proto.smtpc.pp, "EHLO %s",
|
||||
smtp->user?smtp->user:"");
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
state(conn, SMTP_EHLO);
|
||||
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
/* For the SMTP "protocol connect" and "doing" phases only */
|
||||
static int smtp_getsock(struct connectdata *conn,
|
||||
curl_socket_t *socks,
|
||||
int numsocks)
|
||||
{
|
||||
return Curl_pp_getsock(&conn->proto.smtpc.pp, socks, numsocks);
|
||||
}
|
||||
|
||||
/* for STARTTLS responses */
|
||||
static CURLcode smtp_state_starttls_resp(struct connectdata *conn,
|
||||
int smtpcode,
|
||||
smtpstate instate)
|
||||
{
|
||||
CURLcode result = CURLE_OK;
|
||||
struct SessionHandle *data = conn->data;
|
||||
(void)instate; /* no use for this yet */
|
||||
|
||||
if(smtpcode != 'O') {
|
||||
failf(data, "STARTTLS denied. %c", smtpcode);
|
||||
result = CURLE_LOGIN_DENIED;
|
||||
}
|
||||
else {
|
||||
/* Curl_ssl_connect is BLOCKING */
|
||||
result = Curl_ssl_connect(conn, FIRSTSOCKET);
|
||||
if(CURLE_OK == result) {
|
||||
conn->protocol |= PROT_SMTPS;
|
||||
result = smtp_state_ehlo(conn);
|
||||
}
|
||||
}
|
||||
state(conn, SMTP_STOP);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* for EHLO responses */
|
||||
static CURLcode smtp_state_ehlo_resp(struct connectdata *conn,
|
||||
int smtpcode,
|
||||
smtpstate instate)
|
||||
{
|
||||
CURLcode result = CURLE_OK;
|
||||
struct SessionHandle *data = conn->data;
|
||||
|
||||
(void)instate; /* no use for this yet */
|
||||
|
||||
if(smtpcode/100 != 2) {
|
||||
failf(data, "Access denied: %d", smtpcode);
|
||||
result = CURLE_LOGIN_DENIED;
|
||||
}
|
||||
|
||||
/* end the connect phase */
|
||||
state(conn, SMTP_STOP);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* start the DO phase */
|
||||
static CURLcode smtp_mail(struct connectdata *conn)
|
||||
{
|
||||
CURLcode result = CURLE_OK;
|
||||
struct SessionHandle *data = conn->data;
|
||||
struct FTP *smtp = data->state.proto.smtp;
|
||||
|
||||
/* send MAIL */
|
||||
result = Curl_pp_sendf(&conn->proto.smtpc.pp, "MAIL FROM:<%s>",
|
||||
data->set.str[STRING_MAIL_FROM]);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
state(conn, SMTP_MAIL);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* for MAIL responses */
|
||||
static CURLcode smtp_state_mail_resp(struct connectdata *conn,
|
||||
int smtpcode,
|
||||
smtpstate instate)
|
||||
{
|
||||
CURLcode result = CURLE_OK;
|
||||
struct SessionHandle *data = conn->data;
|
||||
(void)instate; /* no use for this yet */
|
||||
|
||||
if(smtpcode/100 != 2) {
|
||||
failf(data, "Access denied: %d", smtpcode);
|
||||
result = CURLE_LOGIN_DENIED;
|
||||
state(conn, SMTP_STOP);
|
||||
}
|
||||
else {
|
||||
/* send RCPT TO */
|
||||
result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:<%s>",
|
||||
data->set.str[STRING_MAIL_RCPT]);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
state(conn, SMTP_RCPT);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/* for RCPT responses */
|
||||
static CURLcode smtp_state_rcpt_resp(struct connectdata *conn,
|
||||
int smtpcode,
|
||||
smtpstate instate)
|
||||
{
|
||||
CURLcode result = CURLE_OK;
|
||||
struct SessionHandle *data = conn->data;
|
||||
(void)instate; /* no use for this yet */
|
||||
|
||||
if(smtpcode/100 != 2) {
|
||||
failf(data, "Access denied: %d", smtpcode);
|
||||
result = CURLE_LOGIN_DENIED;
|
||||
state(conn, SMTP_STOP);
|
||||
}
|
||||
else {
|
||||
/* send DATA */
|
||||
result = Curl_pp_sendf(&conn->proto.smtpc.pp, "DATA", "");
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
state(conn, SMTP_DATA);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/* for the DATA response */
|
||||
static CURLcode smtp_state_data_resp(struct connectdata *conn,
|
||||
int smtpcode,
|
||||
smtpstate instate)
|
||||
{
|
||||
CURLcode result = CURLE_OK;
|
||||
struct SessionHandle *data = conn->data;
|
||||
struct FTP *smtp = data->state.proto.smtp;
|
||||
|
||||
(void)instate; /* no use for this yet */
|
||||
|
||||
if(smtpcode/100 != 2) {
|
||||
state(conn, SMTP_STOP);
|
||||
return CURLE_RECV_ERROR;
|
||||
}
|
||||
|
||||
/* SMTP upload */
|
||||
result=Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE,
|
||||
smtp->bytecountp,
|
||||
-1, NULL); /* no upload here */
|
||||
|
||||
state(conn, SMTP_STOP);
|
||||
return result;
|
||||
}
|
||||
|
||||
static CURLcode smtp_statemach_act(struct connectdata *conn)
|
||||
{
|
||||
CURLcode result;
|
||||
curl_socket_t sock = conn->sock[FIRSTSOCKET];
|
||||
struct SessionHandle *data=conn->data;
|
||||
int smtpcode;
|
||||
struct smtp_conn *smtpc = &conn->proto.smtpc;
|
||||
struct pingpong *pp = &smtpc->pp;
|
||||
size_t nread = 0;
|
||||
|
||||
if(pp->sendleft)
|
||||
/* we have a piece of a command still left to send */
|
||||
return Curl_pp_flushsend(pp);
|
||||
|
||||
/* we read a piece of response */
|
||||
result = Curl_pp_readresp(sock, pp, &smtpcode, &nread);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
if(smtpcode) {
|
||||
/* we have now received a full SMTP server response */
|
||||
switch(smtpc->state) {
|
||||
case SMTP_SERVERGREET:
|
||||
if(smtpcode/100 != 2) {
|
||||
failf(data, "Got unexpected smtp-server response: %d", smtpcode);
|
||||
return CURLE_FTP_WEIRD_SERVER_REPLY;
|
||||
}
|
||||
|
||||
if(data->set.ftp_ssl && !conn->ssl[FIRSTSOCKET].use) {
|
||||
/* We don't have a SSL/TLS connection yet, but SSL is requested. Switch
|
||||
to TLS connection now */
|
||||
const char *str;
|
||||
|
||||
result = Curl_pp_sendf(&smtpc->pp, "STARTTLS", str);
|
||||
state(conn, SMTP_STARTTLS);
|
||||
}
|
||||
else
|
||||
result = smtp_state_ehlo(conn);
|
||||
if(result)
|
||||
return result;
|
||||
break;
|
||||
|
||||
case SMTP_EHLO:
|
||||
result = smtp_state_ehlo_resp(conn, smtpcode, smtpc->state);
|
||||
break;
|
||||
|
||||
case SMTP_MAIL:
|
||||
result = smtp_state_mail_resp(conn, smtpcode, smtpc->state);
|
||||
break;
|
||||
|
||||
case SMTP_RCPT:
|
||||
result = smtp_state_rcpt_resp(conn, smtpcode, smtpc->state);
|
||||
break;
|
||||
|
||||
case SMTP_STARTTLS:
|
||||
result = smtp_state_starttls_resp(conn, smtpcode, smtpc->state);
|
||||
break;
|
||||
|
||||
case SMTP_DATA:
|
||||
result = smtp_state_data_resp(conn, smtpcode, smtpc->state);
|
||||
break;
|
||||
|
||||
case SMTP_QUIT:
|
||||
/* fallthrough, just stop! */
|
||||
default:
|
||||
/* internal error */
|
||||
state(conn, SMTP_STOP);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/* called repeatedly until done from multi.c */
|
||||
static CURLcode smtp_multi_statemach(struct connectdata *conn,
|
||||
bool *done)
|
||||
{
|
||||
struct smtp_conn *smtpc = &conn->proto.smtpc;
|
||||
CURLcode result = Curl_pp_multi_statemach(&smtpc->pp);
|
||||
|
||||
*done = (bool)(smtpc->state == SMTP_STOP);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static CURLcode smtp_easy_statemach(struct connectdata *conn)
|
||||
{
|
||||
struct smtp_conn *smtpc = &conn->proto.smtpc;
|
||||
struct pingpong *pp = &smtpc->pp;
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
while(smtpc->state != SMTP_STOP) {
|
||||
result = Curl_pp_easy_statemach(pp);
|
||||
if(result)
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate and initialize the struct SMTP for the current SessionHandle. If
|
||||
* need be.
|
||||
*/
|
||||
static CURLcode smtp_init(struct connectdata *conn)
|
||||
{
|
||||
struct SessionHandle *data = conn->data;
|
||||
struct FTP *smtp = data->state.proto.smtp;
|
||||
if(!smtp) {
|
||||
smtp = data->state.proto.smtp = calloc(sizeof(struct FTP), 1);
|
||||
if(!smtp)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
/* get some initial data into the smtp struct */
|
||||
smtp->bytecountp = &data->req.bytecount;
|
||||
|
||||
/* 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.
|
||||
*/
|
||||
smtp->user = conn->user;
|
||||
smtp->passwd = conn->passwd;
|
||||
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* smtp_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.
|
||||
*/
|
||||
static CURLcode smtp_connect(struct connectdata *conn,
|
||||
bool *done) /* see description above */
|
||||
{
|
||||
CURLcode result;
|
||||
struct smtp_conn *smtpc = &conn->proto.smtpc;
|
||||
struct SessionHandle *data=conn->data;
|
||||
struct pingpong *pp=&smtpc->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 = smtp_init(conn);
|
||||
if(CURLE_OK != result)
|
||||
return result;
|
||||
|
||||
/* We always support persistant connections on smtp */
|
||||
conn->bits.close = FALSE;
|
||||
|
||||
pp->response_time = RESP_TIMEOUT; /* set default response time-out */
|
||||
pp->statemach_act = smtp_statemach_act;
|
||||
pp->endofresp = smtp_endofresp;
|
||||
pp->conn = conn;
|
||||
|
||||
#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_PROXY)
|
||||
if(conn->bits.tunnel_proxy && conn->bits.httpproxy) {
|
||||
/* for SMTP over HTTP proxy */
|
||||
struct HTTP http_proxy;
|
||||
struct FTP *smtp_save;
|
||||
|
||||
/* BLOCKING */
|
||||
/* We want "seamless" SMTP operations through HTTP proxy tunnel */
|
||||
|
||||
/* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the member
|
||||
* conn->proto.http; we want SMTP 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
|
||||
* SMTP pointer
|
||||
*/
|
||||
smtp_save = data->state.proto.smtp;
|
||||
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.smtp = smtp_save;
|
||||
|
||||
if(CURLE_OK != result)
|
||||
return result;
|
||||
}
|
||||
#endif /* !CURL_DISABLE_HTTP && !CURL_DISABLE_PROXY */
|
||||
|
||||
if(conn->protocol & PROT_SMTPS) {
|
||||
/* BLOCKING */
|
||||
/* SMTPS is simply smtp with SSL for the control channel */
|
||||
/* now, perform the SSL initialization for this socket */
|
||||
result = Curl_ssl_connect(conn, FIRSTSOCKET);
|
||||
if(result)
|
||||
return result;
|
||||
}
|
||||
|
||||
Curl_pp_init(pp); /* init the response reader stuff */
|
||||
|
||||
pp->response_time = RESP_TIMEOUT; /* set default response time-out */
|
||||
pp->statemach_act = smtp_statemach_act;
|
||||
pp->endofresp = smtp_endofresp;
|
||||
pp->conn = conn;
|
||||
|
||||
/* When we connect, we start in the state where we await the server greeting
|
||||
*/
|
||||
state(conn, SMTP_SERVERGREET);
|
||||
|
||||
if(data->state.used_interface == Curl_if_multi)
|
||||
result = smtp_multi_statemach(conn, done);
|
||||
else {
|
||||
result = smtp_easy_statemach(conn);
|
||||
if(!result)
|
||||
*done = TRUE;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
*
|
||||
* smtp_done()
|
||||
*
|
||||
* The DONE function. This does what needs to be done after a single DO has
|
||||
* performed.
|
||||
*
|
||||
* Input argument is already checked for validity.
|
||||
*/
|
||||
static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
|
||||
bool premature)
|
||||
{
|
||||
struct SessionHandle *data = conn->data;
|
||||
struct FTP *smtp = data->state.proto.smtp;
|
||||
CURLcode result=CURLE_OK;
|
||||
(void)premature;
|
||||
|
||||
if(!smtp)
|
||||
/* When the easy handle is removed from the multi while libcurl is still
|
||||
* trying to resolve the host name, it seems that the smtp struct is not
|
||||
* yet initialized, but the removal action calls Curl_done() which calls
|
||||
* this function. So we simply return success if no smtp pointer is set.
|
||||
*/
|
||||
return CURLE_OK;
|
||||
|
||||
if(status) {
|
||||
conn->bits.close = TRUE; /* marked for closure */
|
||||
result = status; /* use the already set error code */
|
||||
}
|
||||
|
||||
/* clear these for next connection */
|
||||
smtp->transfer = FTPTRANSFER_BODY;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
*
|
||||
* smtp_perform()
|
||||
*
|
||||
* This is the actual DO function for SMTP. Get a file/directory according to
|
||||
* the options previously setup.
|
||||
*/
|
||||
|
||||
static
|
||||
CURLcode smtp_perform(struct connectdata *conn,
|
||||
bool *connected, /* connect status after PASV / PORT */
|
||||
bool *dophase_done)
|
||||
{
|
||||
/* this is SMTP and no proxy */
|
||||
CURLcode result=CURLE_OK;
|
||||
|
||||
DEBUGF(infof(conn->data, "DO phase starts\n"));
|
||||
|
||||
if(conn->data->set.opt_no_body) {
|
||||
/* requested no body means no transfer... */
|
||||
struct FTP *smtp = conn->data->state.proto.smtp;
|
||||
smtp->transfer = FTPTRANSFER_INFO;
|
||||
}
|
||||
|
||||
*dophase_done = FALSE; /* not done yet */
|
||||
|
||||
/* start the first command in the DO phase */
|
||||
result = smtp_mail(conn);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
/* run the state-machine */
|
||||
if(conn->data->state.used_interface == Curl_if_multi)
|
||||
result = smtp_multi_statemach(conn, dophase_done);
|
||||
else {
|
||||
result = smtp_easy_statemach(conn);
|
||||
*dophase_done = TRUE; /* with the easy interface we are done here */
|
||||
}
|
||||
*connected = conn->bits.tcpconnect;
|
||||
|
||||
if(*dophase_done)
|
||||
DEBUGF(infof(conn->data, "DO phase is complete\n"));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
*
|
||||
* smtp_do()
|
||||
*
|
||||
* This function is registered as 'curl_do' function. It decodes the path
|
||||
* parts etc as a wrapper to the actual DO function (smtp_perform).
|
||||
*
|
||||
* The input argument is already checked for validity.
|
||||
*/
|
||||
static CURLcode smtp_do(struct connectdata *conn, bool *done)
|
||||
{
|
||||
CURLcode retcode = CURLE_OK;
|
||||
|
||||
*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 SMTP' to play with. For new connections,
|
||||
the struct SMTP is allocated and setup in the smtp_connect() function.
|
||||
*/
|
||||
Curl_reset_reqproto(conn);
|
||||
retcode = smtp_init(conn);
|
||||
if(retcode)
|
||||
return retcode;
|
||||
|
||||
retcode = smtp_parse_url_path(conn);
|
||||
if(retcode)
|
||||
return retcode;
|
||||
|
||||
retcode = smtp_regular_transfer(conn, done);
|
||||
|
||||
return retcode;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
*
|
||||
* smtp_quit()
|
||||
*
|
||||
* This should be called before calling sclose(). We should then wait for the
|
||||
* response from the server before returning. The calling code should then try
|
||||
* to close the connection.
|
||||
*
|
||||
*/
|
||||
static CURLcode smtp_quit(struct connectdata *conn)
|
||||
{
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
result = Curl_pp_sendf(&conn->proto.smtpc.pp, "QUIT", NULL);
|
||||
if(result)
|
||||
return result;
|
||||
state(conn, SMTP_QUIT);
|
||||
|
||||
result = smtp_easy_statemach(conn);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
*
|
||||
* smtp_disconnect()
|
||||
*
|
||||
* Disconnect from an SMTP server. Cleanup protocol-specific per-connection
|
||||
* resources. BLOCKING.
|
||||
*/
|
||||
static CURLcode smtp_disconnect(struct connectdata *conn)
|
||||
{
|
||||
struct smtp_conn *smtpc= &conn->proto.smtpc;
|
||||
|
||||
/* We cannot send quit unconditionally. If this connection is stale or
|
||||
bad in any way, sending quit and waiting around here will make the
|
||||
disconnect wait in vain and cause more problems than we need to.
|
||||
*/
|
||||
|
||||
/* The SMTP session may or may not have been allocated/setup at this
|
||||
point! */
|
||||
(void)smtp_quit(conn); /* ignore errors on the LOGOUT */
|
||||
|
||||
Curl_pp_disconnect(&smtpc->pp);
|
||||
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
*
|
||||
* smtp_parse_url_path()
|
||||
*
|
||||
* Parse the URL path into separate path components.
|
||||
*
|
||||
*/
|
||||
static CURLcode smtp_parse_url_path(struct connectdata *conn)
|
||||
{
|
||||
/* the smtp struct is already inited in smtp_connect() */
|
||||
struct smtp_conn *smtpc = &conn->proto.smtpc;
|
||||
struct SessionHandle *data = conn->data;
|
||||
|
||||
/* url decode... */
|
||||
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
/* call this when the DO phase has completed */
|
||||
static CURLcode smtp_dophase_done(struct connectdata *conn,
|
||||
bool connected)
|
||||
{
|
||||
CURLcode result = CURLE_OK;
|
||||
struct FTP *smtp = conn->data->state.proto.smtp;
|
||||
(void)connected;
|
||||
|
||||
if(smtp->transfer != FTPTRANSFER_BODY)
|
||||
/* no data to transfer */
|
||||
result=Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* called from multi.c while DOing */
|
||||
static CURLcode smtp_doing(struct connectdata *conn,
|
||||
bool *dophase_done)
|
||||
{
|
||||
CURLcode result;
|
||||
result = smtp_multi_statemach(conn, dophase_done);
|
||||
|
||||
if(*dophase_done) {
|
||||
result = smtp_dophase_done(conn, FALSE /* not connected */);
|
||||
|
||||
DEBUGF(infof(conn->data, "DO phase is complete\n"));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
*
|
||||
* smtp_regular_transfer()
|
||||
*
|
||||
* The input argument is already checked for validity.
|
||||
*
|
||||
* Performs all commands done before a regular transfer between a local and a
|
||||
* remote host.
|
||||
*/
|
||||
static
|
||||
CURLcode smtp_regular_transfer(struct connectdata *conn,
|
||||
bool *dophase_done)
|
||||
{
|
||||
CURLcode result=CURLE_OK;
|
||||
bool connected=FALSE;
|
||||
struct SessionHandle *data = conn->data;
|
||||
data->req.size = -1; /* make sure this is unknown at this point */
|
||||
|
||||
Curl_pgrsSetUploadCounter(data, 0);
|
||||
Curl_pgrsSetDownloadCounter(data, 0);
|
||||
Curl_pgrsSetUploadSize(data, 0);
|
||||
Curl_pgrsSetDownloadSize(data, 0);
|
||||
|
||||
result = smtp_perform(conn,
|
||||
&connected, /* have we connected after PASV/PORT */
|
||||
dophase_done); /* all commands in the DO-phase done? */
|
||||
|
||||
if(CURLE_OK == result) {
|
||||
|
||||
if(!*dophase_done)
|
||||
/* the DO phase has not completed yet */
|
||||
return CURLE_OK;
|
||||
|
||||
result = smtp_dophase_done(conn, connected);
|
||||
if(result)
|
||||
return result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static CURLcode smtp_setup_connection(struct connectdata * conn)
|
||||
{
|
||||
struct SessionHandle *data = conn->data;
|
||||
|
||||
if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
|
||||
/* Unless we have asked to tunnel smtp operations through the proxy, we
|
||||
switch and use HTTP operations only */
|
||||
#ifndef CURL_DISABLE_HTTP
|
||||
if(conn->handler == &Curl_handler_smtp)
|
||||
conn->handler = &Curl_handler_smtp_proxy;
|
||||
else {
|
||||
#ifdef USE_SSL
|
||||
conn->handler = &Curl_handler_smtps_proxy;
|
||||
#else
|
||||
failf(data, "SMTPS not supported!");
|
||||
return CURLE_UNSUPPORTED_PROTOCOL;
|
||||
#endif
|
||||
}
|
||||
/*
|
||||
* We explicitly mark this connection as persistent here as we're doing
|
||||
* SMTP over HTTP and thus we accidentally avoid setting this value
|
||||
* otherwise.
|
||||
*/
|
||||
conn->bits.close = FALSE;
|
||||
#else
|
||||
failf(data, "SMTP over http proxy requires HTTP support built-in!");
|
||||
return CURLE_UNSUPPORTED_PROTOCOL;
|
||||
#endif
|
||||
}
|
||||
|
||||
data->state.path++; /* don't include the initial slash */
|
||||
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
#endif /* CURL_DISABLE_SMTP */
|
61
lib/smtp.h
Normal file
61
lib/smtp.h
Normal file
@ -0,0 +1,61 @@
|
||||
#ifndef __SMTP_H
|
||||
#define __SMTP_H
|
||||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) 2009, Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution. The terms
|
||||
* are also available at http://curl.haxx.se/docs/copyright.html.
|
||||
*
|
||||
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||
* copies of the Software, and permit persons to whom the Software is
|
||||
* furnished to do so, under the terms of the COPYING file.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
* $Id$
|
||||
***************************************************************************/
|
||||
|
||||
#include "pingpong.h"
|
||||
|
||||
/****************************************************************************
|
||||
* SMTP unique setup
|
||||
***************************************************************************/
|
||||
typedef enum {
|
||||
SMTP_STOP, /* do nothing state, stops the state machine */
|
||||
SMTP_SERVERGREET, /* waiting for the initial greeting immediately after
|
||||
a connect */
|
||||
SMTP_EHLO,
|
||||
SMTP_STARTTLS,
|
||||
SMTP_MAIL, /* MAIL FROM */
|
||||
SMTP_RCPT, /* RCPT TO */
|
||||
SMTP_DATA,
|
||||
SMTP_QUIT,
|
||||
SMTP_LAST /* never used */
|
||||
} smtpstate;
|
||||
|
||||
/* smtp_conn is used for struct connection-oriented data in the connectdata
|
||||
struct */
|
||||
struct smtp_conn {
|
||||
struct pingpong pp;
|
||||
char *domain; /* what to send in the EHLO */
|
||||
int eob; /* number of bytes of the EOB (End Of Body) that has been
|
||||
received thus far */
|
||||
smtpstate state; /* always use smtp.c:state() to change state! */
|
||||
};
|
||||
|
||||
extern const struct Curl_handler Curl_handler_smtp;
|
||||
extern const struct Curl_handler Curl_handler_smtps;
|
||||
|
||||
/* this is the 5-bytes End-Of-Body marker for SMTP */
|
||||
#define SMTP_EOB "\x0d\x0a\x2e\x0d\x0a"
|
||||
#define SMTP_EOB_LEN 5
|
||||
|
||||
#endif /* __SMTP_H */
|
@ -690,9 +690,17 @@ static CURLcode readwrite_data(struct SessionHandle *data,
|
||||
/* This is the default when the server sends no
|
||||
Content-Encoding header. See Curl_readwrite_init; the
|
||||
memset() call initializes k->content_encoding to zero. */
|
||||
if(!k->ignorebody)
|
||||
if(!k->ignorebody) {
|
||||
|
||||
#ifndef CURL_DISABLE_POP3
|
||||
if(conn->protocol&PROT_POP3)
|
||||
result = Curl_pop3_write(conn, k->str, nread);
|
||||
else
|
||||
#endif /* CURL_DISABLE_POP3 */
|
||||
|
||||
result = Curl_client_write(conn, CLIENTWRITE_BODY, k->str,
|
||||
nread);
|
||||
}
|
||||
#ifdef HAVE_LIBZ
|
||||
break;
|
||||
|
||||
|
44
lib/url.c
44
lib/url.c
@ -130,6 +130,7 @@ void idn_free (void *ptr); /* prototype from idn-free.h, not provided by
|
||||
#include "file.h"
|
||||
#include "curl_ldap.h"
|
||||
#include "ssh.h"
|
||||
#include "imap.h"
|
||||
#include "url.h"
|
||||
#include "connect.h"
|
||||
#include "inet_ntop.h"
|
||||
@ -204,6 +205,27 @@ static const struct Curl_handler * const protocols[] = {
|
||||
&Curl_handler_sftp,
|
||||
#endif
|
||||
|
||||
#ifndef CURL_DISABLE_IMAP
|
||||
&Curl_handler_imap,
|
||||
#ifdef USE_SSL
|
||||
&Curl_handler_imaps,
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef CURL_DISABLE_POP3
|
||||
&Curl_handler_pop3,
|
||||
#ifdef USE_SSL
|
||||
&Curl_handler_pop3s,
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef CURL_DISABLE_SMTP
|
||||
&Curl_handler_smtp,
|
||||
#ifdef USE_SSL
|
||||
&Curl_handler_smtps,
|
||||
#endif
|
||||
#endif
|
||||
|
||||
(struct Curl_handler *) NULL
|
||||
};
|
||||
|
||||
@ -950,12 +972,12 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option,
|
||||
*/
|
||||
data->set.ftp_create_missing_dirs = (int)va_arg(param, long);
|
||||
break;
|
||||
case CURLOPT_FTP_RESPONSE_TIMEOUT:
|
||||
case CURLOPT_SERVER_RESPONSE_TIMEOUT:
|
||||
/*
|
||||
* An FTP option that specifies how quickly an FTP response must be
|
||||
* obtained before it is considered failure.
|
||||
* Option that specifies how quickly an server response must be obtained
|
||||
* before it is considered failure. For pingpong protocols.
|
||||
*/
|
||||
data->set.ftp_response_timeout = va_arg( param , long ) * 1000;
|
||||
data->set.server_response_timeout = va_arg( param , long ) * 1000;
|
||||
break;
|
||||
case CURLOPT_TFTP_BLKSIZE:
|
||||
/*
|
||||
@ -2286,6 +2308,16 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option,
|
||||
data->set.redir_protocols = va_arg(param, long) & PROT_EXTMASK;
|
||||
break;
|
||||
|
||||
case CURLOPT_MAIL_FROM:
|
||||
result = setstropt(&data->set.str[STRING_MAIL_FROM],
|
||||
va_arg(param, char *));
|
||||
break;
|
||||
|
||||
case CURLOPT_MAIL_RCPT:
|
||||
result = setstropt(&data->set.str[STRING_MAIL_RCPT],
|
||||
va_arg(param, char *));
|
||||
break;
|
||||
|
||||
default:
|
||||
/* unknown tag and its companion, just ignore: */
|
||||
result = CURLE_FAILED_INIT; /* correct this */
|
||||
@ -3334,6 +3366,8 @@ static CURLcode ParseURLAndFillConnection(struct SessionHandle *data,
|
||||
strcpy(conn->protostr, "DICT");
|
||||
else if(checkprefix("LDAP.", conn->host.name))
|
||||
strcpy(conn->protostr, "LDAP");
|
||||
else if(checkprefix("IMAP.", conn->host.name))
|
||||
strcpy(conn->protostr, "IMAP");
|
||||
else {
|
||||
strcpy(conn->protostr, "http");
|
||||
}
|
||||
@ -4069,7 +4103,7 @@ static CURLcode set_userpass(struct connectdata *conn,
|
||||
const char *user, const char *passwd)
|
||||
{
|
||||
/* If our protocol needs a password and we have none, use the defaults */
|
||||
if( (conn->protocol & PROT_FTP) &&
|
||||
if( (conn->protocol & (PROT_FTP|PROT_IMAP)) &&
|
||||
!conn->bits.user_passwd) {
|
||||
|
||||
conn->user = strdup(CURL_DEFAULT_USER);
|
||||
|
163
lib/urldata.h
163
lib/urldata.h
@ -37,6 +37,12 @@
|
||||
#define PORT_LDAPS 636
|
||||
#define PORT_TFTP 69
|
||||
#define PORT_SSH 22
|
||||
#define PORT_IMAP 143
|
||||
#define PORT_IMAPS 993
|
||||
#define PORT_POP3 110
|
||||
#define PORT_POP3S 995
|
||||
#define PORT_SMTP 25
|
||||
#define PORT_SMTPS 465 /* sometimes called SSMTP */
|
||||
|
||||
#define DICT_MATCH "/MATCH:"
|
||||
#define DICT_MATCH2 "/M:"
|
||||
@ -51,6 +57,11 @@
|
||||
/* length of longest IPv6 address string including the trailing null */
|
||||
#define MAX_IPADR_LEN sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")
|
||||
|
||||
/* Default FTP/IMAP etc response timeout in milliseconds.
|
||||
Symbian OS panics when given a timeout much greater than 1/2 hour.
|
||||
*/
|
||||
#define RESP_TIMEOUT (1800*1000)
|
||||
|
||||
#include "cookie.h"
|
||||
#include "formdata.h"
|
||||
|
||||
@ -129,6 +140,11 @@
|
||||
#include "hash.h"
|
||||
#include "splay.h"
|
||||
|
||||
#include "imap.h"
|
||||
#include "pop3.h"
|
||||
#include "smtp.h"
|
||||
#include "ftp.h"
|
||||
|
||||
#ifdef HAVE_GSSAPI
|
||||
# ifdef HAVE_GSSGNU
|
||||
# include <gss.h>
|
||||
@ -351,121 +367,6 @@ struct HTTP {
|
||||
points to an allocated send_buffer struct */
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
* FTP unique setup
|
||||
***************************************************************************/
|
||||
typedef enum {
|
||||
FTP_STOP, /* do nothing state, stops the state machine */
|
||||
FTP_WAIT220, /* waiting for the initial 220 response immediately after
|
||||
a connect */
|
||||
FTP_AUTH,
|
||||
FTP_USER,
|
||||
FTP_PASS,
|
||||
FTP_ACCT,
|
||||
FTP_PBSZ,
|
||||
FTP_PROT,
|
||||
FTP_CCC,
|
||||
FTP_PWD,
|
||||
FTP_SYST,
|
||||
FTP_NAMEFMT,
|
||||
FTP_QUOTE, /* waiting for a response to a command sent in a quote list */
|
||||
FTP_RETR_PREQUOTE,
|
||||
FTP_STOR_PREQUOTE,
|
||||
FTP_POSTQUOTE,
|
||||
FTP_CWD, /* change dir */
|
||||
FTP_MKD, /* if the dir didn't exist */
|
||||
FTP_MDTM, /* to figure out the datestamp */
|
||||
FTP_TYPE, /* to set type when doing a head-like request */
|
||||
FTP_LIST_TYPE, /* set type when about to do a dir list */
|
||||
FTP_RETR_TYPE, /* set type when about to RETR a file */
|
||||
FTP_STOR_TYPE, /* set type when about to STOR a file */
|
||||
FTP_SIZE, /* get the remote file's size for head-like request */
|
||||
FTP_RETR_SIZE, /* get the remote file's size for RETR */
|
||||
FTP_STOR_SIZE, /* get the size for (resumed) STOR */
|
||||
FTP_REST, /* when used to check if the server supports it in head-like */
|
||||
FTP_RETR_REST, /* when asking for "resume" in for RETR */
|
||||
FTP_PORT, /* generic state for PORT, LPRT and EPRT, check count1 */
|
||||
FTP_PASV, /* generic state for PASV and EPSV, check count1 */
|
||||
FTP_LIST, /* generic state for LIST, NLST or a custom list command */
|
||||
FTP_RETR,
|
||||
FTP_STOR, /* generic state for STOR and APPE */
|
||||
FTP_QUIT,
|
||||
FTP_LAST /* never used */
|
||||
} ftpstate;
|
||||
|
||||
typedef enum {
|
||||
FTPFILE_MULTICWD = 1, /* as defined by RFC1738 */
|
||||
FTPFILE_NOCWD = 2, /* use SIZE / RETR / STOR on the full path */
|
||||
FTPFILE_SINGLECWD = 3 /* make one CWD, then SIZE / RETR / STOR on the file */
|
||||
} curl_ftpfile;
|
||||
|
||||
typedef enum {
|
||||
FTPTRANSFER_BODY, /* yes do transfer a body */
|
||||
FTPTRANSFER_INFO, /* do still go through to get info/headers */
|
||||
FTPTRANSFER_NONE, /* don't get anything and don't get info */
|
||||
FTPTRANSFER_LAST /* end of list marker, never used */
|
||||
} curl_ftptransfer;
|
||||
|
||||
/* This FTP struct is used in the SessionHandle. All FTP data that is
|
||||
connection-oriented must be in FTP_conn to properly deal with the fact that
|
||||
perhaps the SessionHandle is changed between the times the connection is
|
||||
used. */
|
||||
struct FTP {
|
||||
curl_off_t *bytecountp;
|
||||
char *user; /* user name string */
|
||||
char *passwd; /* password string */
|
||||
|
||||
/* transfer a file/body or not, done as a typedefed enum just to make
|
||||
debuggers display the full symbol and not just the numerical value */
|
||||
curl_ftptransfer transfer;
|
||||
curl_off_t downloadsize;
|
||||
};
|
||||
|
||||
/* ftp_conn is used for struct connection-oriented data in the connectdata
|
||||
struct */
|
||||
struct ftp_conn {
|
||||
char *entrypath; /* the PWD reply when we logged on */
|
||||
char **dirs; /* realloc()ed array for path components */
|
||||
int dirdepth; /* number of entries used in the 'dirs' array */
|
||||
int diralloc; /* number of entries allocated for the 'dirs' array */
|
||||
char *file; /* decoded file */
|
||||
char *cache; /* data cache between getresponse()-calls */
|
||||
curl_off_t cache_size; /* size of cache in bytes */
|
||||
bool dont_check; /* Set to TRUE to prevent the final (post-transfer)
|
||||
file size and 226/250 status check. It should still
|
||||
read the line, just ignore the result. */
|
||||
long response_time; /* When no timeout is given, this is the amount of
|
||||
seconds we await for an FTP response. Initialized
|
||||
in Curl_ftp_connect() */
|
||||
bool ctl_valid; /* Tells Curl_ftp_quit() whether or not to do anything. If
|
||||
the connection has timed out or been closed, this
|
||||
should be FALSE when it gets to Curl_ftp_quit() */
|
||||
bool cwddone; /* if it has been determined that the proper CWD combo
|
||||
already has been done */
|
||||
bool cwdfail; /* set TRUE if a CWD command fails, as then we must prevent
|
||||
caching the current directory */
|
||||
char *prevpath; /* conn->path from the previous transfer */
|
||||
char transfertype; /* set by ftp_transfertype for use by Curl_client_write()a
|
||||
and others (A/I or zero) */
|
||||
size_t nread_resp; /* number of bytes currently read of a server response */
|
||||
char *linestart_resp; /* line start pointer for the FTP server response
|
||||
reader function */
|
||||
bool pending_resp; /* set TRUE when a server response is pending or in
|
||||
progress, and is cleared once the last response is
|
||||
read */
|
||||
|
||||
int count1; /* general purpose counter for the state machine */
|
||||
int count2; /* general purpose counter for the state machine */
|
||||
int count3; /* general purpose counter for the state machine */
|
||||
char *sendthis; /* allocated pointer to a buffer that is to be sent to the
|
||||
ftp server */
|
||||
size_t sendleft; /* number of bytes left to send from the sendthis buffer */
|
||||
size_t sendsize; /* total size of the sendthis buffer */
|
||||
struct timeval response; /* set to Curl_tvnow() when a command has been sent
|
||||
off, used to time-out response reading */
|
||||
ftpstate state; /* always use ftp.c:state() to change state! */
|
||||
char * server_os; /* The target server operating system. */
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
* SSH unique setup
|
||||
@ -922,18 +823,23 @@ struct connectdata {
|
||||
#define PROT_TFTP CURLPROTO_TFTP
|
||||
#define PROT_SCP CURLPROTO_SCP
|
||||
#define PROT_SFTP CURLPROTO_SFTP
|
||||
#define PROT_IMAP CURLPROTO_IMAP
|
||||
#define PROT_IMAPS CURLPROTO_IMAPS
|
||||
#define PROT_POP3 CURLPROTO_POP3
|
||||
#define PROT_POP3S CURLPROTO_POP3S
|
||||
#define PROT_SMTP CURLPROTO_SMTP
|
||||
#define PROT_SMTPS CURLPROTO_SMTPS
|
||||
|
||||
/* CURLPROTO_TFTP (1<<11) is currently the highest used bit in the public
|
||||
bitmask. We make sure we use "private bits" above the first 16 to make
|
||||
things easier. */
|
||||
/* (1<<17) is currently the highest used bit in the public bitmask. We make
|
||||
sure we use "private bits" above the public ones to make things easier. */
|
||||
|
||||
#define PROT_EXTMASK 0xffff
|
||||
#define PROT_EXTMASK 0xfffff
|
||||
|
||||
#define PROT_SSL (1<<22) /* protocol requires SSL */
|
||||
#define PROT_MISSING (1<<23)
|
||||
#define PROT_SSL (1<<25) /* protocol requires SSL */
|
||||
#define PROT_MISSING (1<<26)
|
||||
|
||||
/* these ones need action before socket close */
|
||||
#define PROT_CLOSEACTION (PROT_FTP | PROT_TFTP)
|
||||
#define PROT_CLOSEACTION (PROT_FTP | PROT_TFTP | PROT_IMAP | PROT_POP3)
|
||||
#define PROT_DUALCHANNEL PROT_FTP /* these protocols use two connections */
|
||||
|
||||
/* 'dns_entry' is the particular host we use. This points to an entry in the
|
||||
@ -1075,6 +981,9 @@ struct connectdata {
|
||||
struct ftp_conn ftpc;
|
||||
struct ssh_conn sshc;
|
||||
struct tftp_state_data *tftpc;
|
||||
struct imap_conn imapc;
|
||||
struct pop3_conn pop3c;
|
||||
struct smtp_conn smtpc;
|
||||
} proto;
|
||||
|
||||
int cselect_bits; /* bitmask of socket events */
|
||||
@ -1331,6 +1240,9 @@ struct UrlState {
|
||||
void *telnet; /* private for telnet.c-eyes only */
|
||||
void *generic;
|
||||
struct SSHPROTO *ssh;
|
||||
struct FTP *imap;
|
||||
struct FTP *pop3;
|
||||
struct FTP *smtp;
|
||||
} proto;
|
||||
/* current user of this SessionHandle instance, or NULL */
|
||||
struct connectdata *current_conn;
|
||||
@ -1412,6 +1324,8 @@ enum dupstring {
|
||||
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
|
||||
STRING_SOCKS5_GSSAPI_SERVICE, /* GSSAPI service name */
|
||||
#endif
|
||||
STRING_MAIL_FROM,
|
||||
STRING_MAIL_RCPT,
|
||||
|
||||
/* -- end of strings -- */
|
||||
STRING_LAST /* not used, just an end-of-list marker */
|
||||
@ -1471,7 +1385,7 @@ struct UserDefined {
|
||||
void *ioctl_client; /* pointer to pass to the ioctl callback */
|
||||
long timeout; /* in milliseconds, 0 means no timeout */
|
||||
long connecttimeout; /* in milliseconds, 0 means no timeout */
|
||||
long ftp_response_timeout; /* in milliseconds, 0 means no timeout */
|
||||
long server_response_timeout; /* in milliseconds, 0 means no timeout */
|
||||
long tftp_blksize ; /* in bytes, 0 means use default */
|
||||
curl_off_t infilesize; /* size of file to upload, -1 means unknown */
|
||||
long low_speed_limit; /* bytes/second */
|
||||
@ -1555,7 +1469,8 @@ struct UserDefined {
|
||||
bool ftp_use_epsv; /* if EPSV is to be attempted or not */
|
||||
bool ftp_use_eprt; /* if EPRT is to be attempted or not */
|
||||
|
||||
curl_usessl ftp_ssl; /* if AUTH TLS is to be attempted etc */
|
||||
curl_usessl ftp_ssl; /* if AUTH TLS is to be attempted etc, for FTP or
|
||||
IMAP or POP3 or others! */
|
||||
curl_ftpauth ftpsslauth; /* what AUTH XXX to be attempted */
|
||||
curl_ftpccc ftp_ccc; /* FTP CCC options */
|
||||
bool no_signal; /* do not use any signal/alarm handler */
|
||||
|
@ -158,6 +158,27 @@ static const char * const protocols[] = {
|
||||
"sftp",
|
||||
#endif
|
||||
|
||||
#ifndef CURL_DISABLE_IMAP
|
||||
"imap",
|
||||
#ifdef USE_SSL
|
||||
"imaps",
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef CURL_DISABLE_POP3
|
||||
"pop3",
|
||||
#ifdef USE_SSL
|
||||
"pop3s",
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef CURL_DISABLE_SMTP
|
||||
"smtp",
|
||||
#ifdef USE_SSL
|
||||
"smtps",
|
||||
#endif
|
||||
#endif
|
||||
|
||||
NULL
|
||||
};
|
||||
|
||||
|
19
src/main.c
19
src/main.c
@ -500,6 +500,8 @@ struct Configurable {
|
||||
char *proxy;
|
||||
int proxyver; /* set to CURLPROXY_HTTP* define */
|
||||
char *noproxy;
|
||||
char *mail_from;
|
||||
char *mail_rcpt;
|
||||
bool proxytunnel;
|
||||
bool ftp_append; /* APPE on ftp */
|
||||
bool mute; /* shutup */
|
||||
@ -822,6 +824,8 @@ static void help(void)
|
||||
" -L/--location Follow Location: hints (H)",
|
||||
" --location-trusted Follow Location: and send auth to other hosts (H)",
|
||||
" -M/--manual Display the full manual",
|
||||
" --mail-from <from> Mail from this address",
|
||||
" --mail-rcpt <to> Mail to this receiver(s)",
|
||||
" --max-filesize <bytes> Maximum file size to download (H/F)",
|
||||
" --max-redirs <num> Maximum number of redirects allowed (H)",
|
||||
" -m/--max-time <seconds> Maximum time allowed for the transfer",
|
||||
@ -1740,6 +1744,8 @@ static ParameterError getparameter(char *flag, /* f or -long-flag */
|
||||
#endif
|
||||
{"$8", "proxy1.0", TRUE},
|
||||
{"$9", "tftp-blksize", TRUE},
|
||||
{"$A", "mail-from", TRUE},
|
||||
{"$B", "mail-rcpt", TRUE},
|
||||
{"0", "http1.0", FALSE},
|
||||
{"1", "tlsv1", FALSE},
|
||||
{"2", "sslv2", FALSE},
|
||||
@ -2269,6 +2275,12 @@ static ParameterError getparameter(char *flag, /* f or -long-flag */
|
||||
case '9': /* --tftp-blksize */
|
||||
str2num(&config->tftp_blksize, nextarg);
|
||||
break;
|
||||
case 'A': /* --mail-from */
|
||||
GetStr(&config->mail_from, nextarg);
|
||||
break;
|
||||
case 'B': /* --mail-rcpt */
|
||||
GetStr(&config->mail_rcpt, nextarg);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case '#': /* --progress-bar */
|
||||
@ -5006,9 +5018,16 @@ operate(struct Configurable *config, int argc, argv_item_t argv[])
|
||||
my_setopt(curl, CURLOPT_POSTREDIR, config->post301 |
|
||||
(config->post302 ? CURL_REDIR_POST_302 : FALSE));
|
||||
|
||||
/* curl 7.20.0 */
|
||||
if(config->tftp_blksize)
|
||||
my_setopt(curl, CURLOPT_TFTP_BLKSIZE, config->tftp_blksize);
|
||||
|
||||
if(config->mail_from)
|
||||
my_setopt_str(curl, CURLOPT_MAIL_FROM, config->mail_from);
|
||||
|
||||
if(config->mail_rcpt)
|
||||
my_setopt_str(curl, CURLOPT_MAIL_RCPT, config->mail_rcpt);
|
||||
|
||||
retry_numretries = config->req_retry;
|
||||
|
||||
retrystart = cutil_tvnow();
|
||||
|
@ -63,7 +63,7 @@ EXTRA_DIST = test1 test108 test117 test127 test20 test27 test34 test46 \
|
||||
test1089 test1090 test1091 test1092 test1093 test1094 test1095 test1096 \
|
||||
test1097 test560 test561 test1098 test1099 test562 test563 test1100 \
|
||||
test564 test1101 test1102 test1103 test1104 test299 test310 test311 \
|
||||
test312 test1105 test565
|
||||
test312 test1105 test565 test800
|
||||
|
||||
filecheck:
|
||||
@mkdir test-place; \
|
||||
|
47
tests/data/test800
Normal file
47
tests/data/test800
Normal file
@ -0,0 +1,47 @@
|
||||
<testcase>
|
||||
<info>
|
||||
<keywords>
|
||||
POP3
|
||||
RETR
|
||||
</keywords>
|
||||
</info>
|
||||
|
||||
#
|
||||
# Server-side
|
||||
<reply>
|
||||
<data>
|
||||
From: me@somewhere
|
||||
To: fake@nowhere
|
||||
|
||||
body
|
||||
|
||||
--
|
||||
yours sincerely
|
||||
</data>
|
||||
</reply>
|
||||
|
||||
#
|
||||
# Client-side
|
||||
<client>
|
||||
<server>
|
||||
pop3
|
||||
</server>
|
||||
<name>
|
||||
POP3 RETR
|
||||
</name>
|
||||
<command>
|
||||
pop3://%HOSTIP:%POP3PORT/800 -u user:secret
|
||||
</command>
|
||||
</client>
|
||||
|
||||
#
|
||||
# Verify data after the test has been "shot"
|
||||
<verify>
|
||||
<protocol>
|
||||
USER user
|
||||
PASS secret
|
||||
RETR 800
|
||||
QUIT
|
||||
</protocol>
|
||||
</verify>
|
||||
</testcase>
|
@ -22,7 +22,11 @@
|
||||
# $Id$
|
||||
###########################################################################
|
||||
|
||||
# This is the FTP server designed for the curl test suite.
|
||||
# This is a server designed for the curl test suite.
|
||||
#
|
||||
# In December 2009 we started remaking the server to support more protocols
|
||||
# that are similar in spirit. Like POP3, IMAP and SMTP in addition to the
|
||||
# FTP it already supported since a long time.
|
||||
#
|
||||
# It is meant to exercise curl, it is not meant to be a fully working
|
||||
# or even very standard compliant server.
|
||||
@ -88,6 +92,8 @@ my $pidfile = ".ftpd.pid"; # a default, use --pidfile
|
||||
my $SERVERLOGS_LOCK="log/serverlogs.lock"; # server logs advisor read lock
|
||||
my $serverlogslocked=0;
|
||||
|
||||
my $proto="ftp";
|
||||
|
||||
do {
|
||||
if($ARGV[0] eq "-v") {
|
||||
$verbose=1;
|
||||
@ -100,6 +106,11 @@ do {
|
||||
$ftpdnum=$ARGV[1];
|
||||
shift @ARGV;
|
||||
}
|
||||
elsif($ARGV[0] eq "--proto") {
|
||||
# ftp pop3 imap smtp
|
||||
$proto=$ARGV[1];
|
||||
shift @ARGV;
|
||||
}
|
||||
elsif($ARGV[0] eq "--pidfile") {
|
||||
$pidfile=$ARGV[1];
|
||||
shift @ARGV;
|
||||
@ -115,23 +126,28 @@ do {
|
||||
}
|
||||
elsif($ARGV[0] eq "--addr") {
|
||||
$listenaddr = $ARGV[1];
|
||||
$listenaddr =~ s/^\[(.*)\]$/\1/;
|
||||
$listenaddr =~ s/^\[(.*)\]$/$1/;
|
||||
shift @ARGV;
|
||||
}
|
||||
} while(shift @ARGV);
|
||||
|
||||
# a dedicated protocol has been selected, check that it's a fine one
|
||||
if($proto !~ /^(ftp|imap|pop3|smtp)\z/) {
|
||||
die "unsupported protocol selected";
|
||||
}
|
||||
|
||||
sub catch_zap {
|
||||
my $signame = shift;
|
||||
print STDERR "ftpserver.pl received SIG$signame, exiting\n";
|
||||
ftpkillslaves(1);
|
||||
unlink($pidfile);
|
||||
if($serverlogslocked) {
|
||||
$serverlogslocked = 0;
|
||||
clear_advisor_read_lock($SERVERLOGS_LOCK);
|
||||
}
|
||||
exit;
|
||||
die "Somebody sent me a SIG$signame";
|
||||
}
|
||||
$SIG{INT} = \&catch_zap;
|
||||
$SIG{TERM} = \&catch_zap;
|
||||
$SIG{KILL} = \&catch_zap;
|
||||
|
||||
my $sfpid;
|
||||
|
||||
@ -153,7 +169,6 @@ sub sysread_or_die {
|
||||
logmsg "Error: ftp$ftpdnum$ext sysread error: $!\n";
|
||||
kill(9, $sfpid);
|
||||
waitpid($sfpid, 0);
|
||||
unlink($pidfile);
|
||||
if($serverlogslocked) {
|
||||
$serverlogslocked = 0;
|
||||
clear_advisor_read_lock($SERVERLOGS_LOCK);
|
||||
@ -167,7 +182,6 @@ sub sysread_or_die {
|
||||
logmsg "Error: ftp$ftpdnum$ext read zero\n";
|
||||
kill(9, $sfpid);
|
||||
waitpid($sfpid, 0);
|
||||
unlink($pidfile);
|
||||
if($serverlogslocked) {
|
||||
$serverlogslocked = 0;
|
||||
clear_advisor_read_lock($SERVERLOGS_LOCK);
|
||||
@ -193,7 +207,6 @@ sub startsf {
|
||||
logmsg "Failed sockfilt command: $cmd\n";
|
||||
kill(9, $sfpid);
|
||||
waitpid($sfpid, 0);
|
||||
unlink($pidfile);
|
||||
if($serverlogslocked) {
|
||||
$serverlogslocked = 0;
|
||||
clear_advisor_read_lock($SERVERLOGS_LOCK);
|
||||
@ -202,9 +215,13 @@ sub startsf {
|
||||
}
|
||||
}
|
||||
|
||||
# remove the file here so that if startsf() fails, it is very noticeable
|
||||
unlink($pidfile);
|
||||
|
||||
startsf();
|
||||
|
||||
logmsg sprintf("FTP server listens on port IPv%d/$port\n", $ipv6?6:4);
|
||||
logmsg sprintf("%s server listens on port IPv%d/$port\n", uc($proto),
|
||||
$ipv6?6:4);
|
||||
open(PID, ">$pidfile");
|
||||
print PID $$."\n";
|
||||
close(PID);
|
||||
@ -273,8 +290,14 @@ sub senddata {
|
||||
}
|
||||
}
|
||||
|
||||
# this text is shown before the function specified below is run
|
||||
my %displaytext = ('USER' => '331 We are happy you popped in!',
|
||||
my %displaytext;
|
||||
my %commandfunc;
|
||||
|
||||
# callback functions for certain commands
|
||||
# and text shown before the function specified below is run
|
||||
|
||||
if($proto eq "ftp") {
|
||||
%displaytext = ('USER' => '331 We are happy you popped in!',
|
||||
'PASS' => '230 Welcome you silly person',
|
||||
'PORT' => '200 You said PORT - I say FINE',
|
||||
'TYPE' => '200 I modify TYPE as you wanted',
|
||||
@ -294,8 +317,7 @@ my %displaytext = ('USER' => '331 We are happy you popped in!',
|
||||
'PROT' => '500 PROT not implemented',
|
||||
);
|
||||
|
||||
# callback functions for certain commands
|
||||
my %commandfunc = ( 'PORT' => \&PORT_command,
|
||||
%commandfunc = ( 'PORT' => \&PORT_command,
|
||||
'EPRT' => \&PORT_command,
|
||||
'LIST' => \&LIST_command,
|
||||
'NLST' => \&NLST_command,
|
||||
@ -308,6 +330,26 @@ my %commandfunc = ( 'PORT' => \&PORT_command,
|
||||
'APPE' => \&STOR_command, # append looks like upload
|
||||
'MDTM' => \&MDTM_command,
|
||||
);
|
||||
}
|
||||
elsif($proto eq "pop3") {
|
||||
%commandfunc = ('RETR' => \&RETR_pop3,
|
||||
);
|
||||
|
||||
%displaytext = ('USER' => '+OK We are happy you popped in!',
|
||||
'PASS' => '+OK Access granted',
|
||||
'QUIT' => '+OK byebye',
|
||||
);
|
||||
|
||||
}
|
||||
elsif($proto eq "imap") {
|
||||
%commandfunc = ('FETCH' => \&FETCH_imap,
|
||||
);
|
||||
|
||||
%displaytext = ('LOGIN' => ' OK We are happy you popped in!',
|
||||
'SELECT' => ' OK selection done',
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
||||
sub close_dataconn {
|
||||
@ -330,6 +372,98 @@ sub close_dataconn {
|
||||
$slavepid=0;
|
||||
}
|
||||
|
||||
################
|
||||
################ IMAP commands
|
||||
################
|
||||
|
||||
sub FETCH_imap {
|
||||
my ($testno) = @_;
|
||||
my @data;
|
||||
|
||||
if($testno =~ /^verifiedserver$/) {
|
||||
# this is the secret command that verifies that this actually is
|
||||
# the curl test server
|
||||
my $response = "WE ROOLZ: $$\r\n";
|
||||
if($verbose) {
|
||||
print STDERR "FTPD: We returned proof we are the test server\n";
|
||||
}
|
||||
$data[0] = $response;
|
||||
logmsg "return proof we are we\n";
|
||||
}
|
||||
else {
|
||||
logmsg "retrieve a mail\n";
|
||||
|
||||
$testno =~ s/^([^0-9]*)//;
|
||||
my $testpart = "";
|
||||
if ($testno > 10000) {
|
||||
$testpart = $testno % 10000;
|
||||
$testno = int($testno / 10000);
|
||||
}
|
||||
|
||||
# send mail content
|
||||
loadtest("$srcdir/data/test$testno");
|
||||
|
||||
@data = getpart("reply", "data$testpart");
|
||||
}
|
||||
|
||||
sendcontrol "- OK Mail transfer starts\r\n";
|
||||
|
||||
for my $d (@data) {
|
||||
sendcontrol $d;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
################
|
||||
################ POP3 commands
|
||||
################
|
||||
|
||||
sub RETR_pop3 {
|
||||
my ($testno) = @_;
|
||||
my @data;
|
||||
|
||||
if($testno =~ /^verifiedserver$/) {
|
||||
# this is the secret command that verifies that this actually is
|
||||
# the curl test server
|
||||
my $response = "WE ROOLZ: $$\r\n";
|
||||
if($verbose) {
|
||||
print STDERR "FTPD: We returned proof we are the test server\n";
|
||||
}
|
||||
$data[0] = $response;
|
||||
logmsg "return proof we are we\n";
|
||||
}
|
||||
else {
|
||||
logmsg "retrieve a mail\n";
|
||||
|
||||
$testno =~ s/^([^0-9]*)//;
|
||||
my $testpart = "";
|
||||
if ($testno > 10000) {
|
||||
$testpart = $testno % 10000;
|
||||
$testno = int($testno / 10000);
|
||||
}
|
||||
|
||||
# send mail content
|
||||
loadtest("$srcdir/data/test$testno");
|
||||
|
||||
@data = getpart("reply", "data$testpart");
|
||||
}
|
||||
|
||||
sendcontrol "+OK Mail transfer starts\r\n";
|
||||
|
||||
for my $d (@data) {
|
||||
sendcontrol $d;
|
||||
}
|
||||
|
||||
# end with the magic 5-byte end of mail marker
|
||||
sendcontrol "\r\n.\r\n";
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
################
|
||||
################ FTP commands
|
||||
################
|
||||
my $rest=0;
|
||||
sub REST_command {
|
||||
$rest = $_[0];
|
||||
@ -798,12 +932,34 @@ sub customize {
|
||||
close(CUSTOM);
|
||||
}
|
||||
|
||||
my @welcome=(
|
||||
my @welcome;
|
||||
|
||||
if($proto eq "ftp") {
|
||||
@welcome=(
|
||||
'220- _ _ ____ _ '."\r\n",
|
||||
'220- ___| | | | _ \| | '."\r\n",
|
||||
'220- / __| | | | |_) | | '."\r\n",
|
||||
'220- | (__| |_| | _ <| |___ '."\r\n",
|
||||
'220 \___|\___/|_| \_\_____|'."\r\n");
|
||||
}
|
||||
elsif($proto eq "pop3") {
|
||||
@welcome=(
|
||||
' _ _ ____ _ '."\r\n",
|
||||
' ___| | | | _ \| | '."\r\n",
|
||||
' / __| | | | |_) | | '."\r\n",
|
||||
' | (__| |_| | _ <| |___ '."\r\n",
|
||||
' \___|\___/|_| \_\_____|'."\r\n",
|
||||
'+OK cURL POP3 server ready to serve'."\r\n");
|
||||
}
|
||||
elsif($proto eq "imap") {
|
||||
@welcome=(
|
||||
' _ _ ____ _ '."\r\n",
|
||||
' ___| | | | _ \| | '."\r\n",
|
||||
' / __| | | | |_) | | '."\r\n",
|
||||
' | (__| |_| | _ <| |___ '."\r\n",
|
||||
' \___|\___/|_| \_\_____|'."\r\n",
|
||||
'* OK cURL IMAP server ready to serve'."\r\n");
|
||||
}
|
||||
|
||||
|
||||
while(1) {
|
||||
@ -872,13 +1028,28 @@ while(1) {
|
||||
# Remove trailing CRLF.
|
||||
s/[\n\r]+$//;
|
||||
|
||||
my $cmdid;
|
||||
my $FTPCMD;
|
||||
my $FTPARG;
|
||||
my $full=$_;
|
||||
if($proto eq "imap") {
|
||||
# IMAP is different with its identifier first on the command line
|
||||
unless (m/^([^ ]+) ([^ ]+) (.*)/i) {
|
||||
sendcontrol "500 '$_': command not understood.\r\n";
|
||||
last;
|
||||
}
|
||||
$cmdid=$1;
|
||||
$FTPCMD=$2;
|
||||
$FTPARG=$3;
|
||||
}
|
||||
else {
|
||||
unless (m/^([A-Z]{3,4})\s?(.*)/i) {
|
||||
sendcontrol "500 '$_': command not understood.\r\n";
|
||||
last;
|
||||
}
|
||||
my $FTPCMD=$1;
|
||||
my $FTPARG=$2;
|
||||
my $full=$_;
|
||||
$FTPCMD=$1;
|
||||
$FTPARG=$2;
|
||||
}
|
||||
|
||||
logmsg "< \"$full\"\n";
|
||||
|
||||
@ -907,7 +1078,7 @@ while(1) {
|
||||
}
|
||||
my $check;
|
||||
if($text) {
|
||||
sendcontrol "$text\r\n";
|
||||
sendcontrol "$cmdid$text\r\n";
|
||||
}
|
||||
else {
|
||||
$check=1; # no response yet
|
||||
@ -939,8 +1110,6 @@ while(1) {
|
||||
print SFWRITE "QUIT\n";
|
||||
waitpid $sfpid, 0;
|
||||
|
||||
unlink($pidfile);
|
||||
|
||||
if($serverlogslocked) {
|
||||
$serverlogslocked = 0;
|
||||
clear_advisor_read_lock($SERVERLOGS_LOCK);
|
||||
|
@ -109,6 +109,9 @@ my $TFTPPORT; # TFTP
|
||||
my $TFTP6PORT; # TFTP
|
||||
my $SSHPORT; # SCP/SFTP
|
||||
my $SOCKSPORT; # SOCKS4/5 port
|
||||
my $POP3PORT; # POP3
|
||||
my $IMAPPORT; # IMAP
|
||||
my $SMTPPORT; # SMTP
|
||||
|
||||
my $srcdir = $ENV{'srcdir'} || '.';
|
||||
my $CURL="../src/curl"; # what curl executable to run on the tests
|
||||
@ -147,6 +150,9 @@ my $TFTPPIDFILE=".tftpd.pid";
|
||||
my $TFTP6PIDFILE=".tftp6.pid";
|
||||
my $SSHPIDFILE=".ssh.pid";
|
||||
my $SOCKSPIDFILE=".socks.pid";
|
||||
my $POP3PIDFILE=".pop3.pid";
|
||||
my $IMAPPIDFILE=".imap.pid";
|
||||
my $SMTPPIDFILE=".smtp.pid";
|
||||
|
||||
# invoke perl like this:
|
||||
my $perl="perl -I$srcdir";
|
||||
@ -663,7 +669,7 @@ sub verifyftp {
|
||||
}
|
||||
if($pid <= 0 && $data[0]) {
|
||||
# this is not a known server
|
||||
logmsg "RUN: Unknown server on our FTP port: $port\n";
|
||||
logmsg "RUN: Unknown server on our $proto port: $port\n";
|
||||
return 0;
|
||||
}
|
||||
# we can/should use the time it took to verify the FTP server as a measure
|
||||
@ -671,7 +677,7 @@ sub verifyftp {
|
||||
my $took = time()-$time;
|
||||
|
||||
if($verbose) {
|
||||
logmsg "RUN: Verifying our test FTP server took $took seconds\n";
|
||||
logmsg "RUN: Verifying our test $proto server took $took seconds\n";
|
||||
}
|
||||
$ftpchecktime = $took?$took:1; # make sure it never is zero
|
||||
|
||||
@ -773,6 +779,9 @@ sub verifysocks {
|
||||
my %protofunc = ('http' => \&verifyhttp,
|
||||
'https' => \&verifyhttp,
|
||||
'ftp' => \&verifyftp,
|
||||
'pop3' => \&verifyftp,
|
||||
'imap' => \&verifyftp,
|
||||
'smtp' => \&verifyftp,
|
||||
'ftps' => \&verifyftp,
|
||||
'tftp' => \&verifyftp,
|
||||
'ssh' => \&verifyssh,
|
||||
@ -942,18 +951,22 @@ sub runhttpsserver {
|
||||
}
|
||||
|
||||
#######################################################################
|
||||
# start the ftp server
|
||||
# start the pingpong server (FTP, POP3, IMAP, SMTP)
|
||||
#
|
||||
sub runftpserver {
|
||||
my ($id, $verbose, $ipv6) = @_;
|
||||
sub runpingpongserver {
|
||||
my ($proto, $id, $verbose, $ipv6) = @_;
|
||||
my $STATUS;
|
||||
my $RUNNING;
|
||||
my $port = $id?$FTP2PORT:$FTPPORT;
|
||||
# check for pidfile
|
||||
my $pidfile = $id?$FTP2PIDFILE:$FTPPIDFILE;
|
||||
my $port;
|
||||
my $pidfile;
|
||||
my $ip=$HOSTIP;
|
||||
my $nameext;
|
||||
my $cmd;
|
||||
my $flag;
|
||||
|
||||
if($proto eq "ftp") {
|
||||
$port = $id?$FTP2PORT:$FTPPORT;
|
||||
$pidfile = $id?$FTP2PIDFILE:$FTPPIDFILE;
|
||||
|
||||
if($ipv6) {
|
||||
# if IPv6, use a different setup
|
||||
@ -962,6 +975,24 @@ sub runftpserver {
|
||||
$ip = $HOST6IP;
|
||||
$nameext="-ipv6";
|
||||
}
|
||||
}
|
||||
elsif($proto eq "pop3") {
|
||||
$port = $POP3PORT;
|
||||
$pidfile = $POP3PIDFILE;
|
||||
}
|
||||
elsif($proto eq "imap") {
|
||||
$port = $IMAPPORT;
|
||||
$pidfile = $IMAPPIDFILE;
|
||||
}
|
||||
elsif($proto eq "smtp") {
|
||||
$port = $SMTPPORT;
|
||||
$pidfile = $SMTPPIDFILE;
|
||||
}
|
||||
else {
|
||||
print STDERR "Unsupported protocol $proto!!\n";
|
||||
return 0;
|
||||
}
|
||||
$flag .= "--proto $proto ";
|
||||
|
||||
# don't retry if the server doesn't work
|
||||
if ($doesntrun{$pidfile}) {
|
||||
@ -975,7 +1006,7 @@ sub runftpserver {
|
||||
unlink($pidfile);
|
||||
|
||||
# start our server:
|
||||
my $flag=$debugprotocol?"-v ":"";
|
||||
$flag.=$debugprotocol?"-v ":"";
|
||||
$flag .= "-s \"$srcdir\" ";
|
||||
my $addr;
|
||||
if($id) {
|
||||
@ -993,7 +1024,7 @@ sub runftpserver {
|
||||
|
||||
if($ftppid <= 0 || !kill(0, $ftppid)) {
|
||||
# it is NOT alive
|
||||
logmsg "RUN: failed to start the FTP$id$nameext server\n";
|
||||
logmsg "RUN: failed to start the $proto$id$nameext server\n";
|
||||
stopserver("$pid2");
|
||||
displaylogs($testnumcheck);
|
||||
$doesntrun{$pidfile} = 1;
|
||||
@ -1001,9 +1032,9 @@ sub runftpserver {
|
||||
}
|
||||
|
||||
# Server is up. Verify that we can speak to it.
|
||||
my $pid3 = verifyserver("ftp", $ip, $port);
|
||||
my $pid3 = verifyserver($proto, $ip, $port);
|
||||
if(!$pid3) {
|
||||
logmsg "RUN: FTP$id$nameext server failed verification\n";
|
||||
logmsg "RUN: $proto$id$nameext server failed verification\n";
|
||||
# failed to talk to it properly. Kill the server and return failure
|
||||
stopserver("$ftppid $pid2");
|
||||
displaylogs($testnumcheck);
|
||||
@ -1013,7 +1044,7 @@ sub runftpserver {
|
||||
$pid2 = $pid3;
|
||||
|
||||
if($verbose) {
|
||||
logmsg "RUN: FTP$id$nameext server is now running PID $ftppid\n";
|
||||
logmsg "RUN: $proto$id$nameext server is now running PID $ftppid\n";
|
||||
}
|
||||
|
||||
sleep(1);
|
||||
@ -1661,41 +1692,46 @@ sub checksystem {
|
||||
"* Host: $hostname",
|
||||
"* System: $hosttype");
|
||||
|
||||
logmsg sprintf("* Server SSL: %s\n", $stunnel?"ON":"OFF");
|
||||
logmsg sprintf("* libcurl SSL: %s\n", $ssl_version?"ON":"OFF");
|
||||
logmsg sprintf("* debug build: %s\n", $debug_build?"ON":"OFF");
|
||||
logmsg sprintf("* track memory: %s\n", $curl_debug?"ON":"OFF");
|
||||
logmsg sprintf("* valgrind: %s\n", $valgrind?"ON":"OFF");
|
||||
logmsg sprintf("* HTTP IPv6 %s\n", $http_ipv6?"ON":"OFF");
|
||||
logmsg sprintf("* FTP IPv6 %s\n", $ftp_ipv6?"ON":"OFF");
|
||||
|
||||
logmsg sprintf("* HTTP port: %d\n", $HTTPPORT);
|
||||
logmsg sprintf("* FTP port: %d\n", $FTPPORT);
|
||||
logmsg sprintf("* FTP port 2: %d\n", $FTP2PORT);
|
||||
if($stunnel) {
|
||||
logmsg sprintf("* FTPS port: %d\n", $FTPSPORT);
|
||||
logmsg sprintf("* HTTPS port: %d\n", $HTTPSPORT);
|
||||
}
|
||||
if($http_ipv6) {
|
||||
logmsg sprintf("* HTTP IPv6 port: %d\n", $HTTP6PORT);
|
||||
}
|
||||
if($ftp_ipv6) {
|
||||
logmsg sprintf("* FTP IPv6 port: %d\n", $FTP6PORT);
|
||||
}
|
||||
logmsg sprintf("* TFTP port: %d\n", $TFTPPORT);
|
||||
if($tftp_ipv6) {
|
||||
logmsg sprintf("* TFTP IPv6 port: %d\n", $TFTP6PORT);
|
||||
}
|
||||
logmsg sprintf("* SCP/SFTP port: %d\n", $SSHPORT);
|
||||
logmsg sprintf("* SOCKS port: %d\n", $SOCKSPORT);
|
||||
|
||||
logmsg sprintf("* Server SSL: %8s", $stunnel?"ON ":"OFF");
|
||||
logmsg sprintf(" libcurl SSL: %s\n", $ssl_version?"ON ":"OFF");
|
||||
logmsg sprintf("* debug build: %8s", $debug_build?"ON ":"OFF");
|
||||
logmsg sprintf(" track memory: %s\n", $curl_debug?"ON ":"OFF");
|
||||
logmsg sprintf("* valgrind: %8s", $valgrind?"ON ":"OFF");
|
||||
logmsg sprintf(" HTTP IPv6 %s\n", $http_ipv6?"ON ":"OFF");
|
||||
logmsg sprintf("* FTP IPv6 %8s", $ftp_ipv6?"ON ":"OFF");
|
||||
logmsg sprintf(" Libtool lib: %s\n", $libtool?"ON ":"OFF");
|
||||
if($ssl_version) {
|
||||
logmsg sprintf("* SSL library: %s\n", $ssllib);
|
||||
}
|
||||
|
||||
logmsg "* Ports:\n";
|
||||
|
||||
logmsg sprintf("* HTTP/%d ", $HTTPPORT);
|
||||
logmsg sprintf("FTP/%d ", $FTPPORT);
|
||||
logmsg sprintf("FTP2/%d ", $FTP2PORT);
|
||||
if($stunnel) {
|
||||
logmsg sprintf("FTPS/%d ", $FTPSPORT);
|
||||
logmsg sprintf("HTTPS/%d ", $HTTPSPORT);
|
||||
}
|
||||
logmsg sprintf("\n* TFTP/%d ", $TFTPPORT);
|
||||
if($http_ipv6) {
|
||||
logmsg sprintf("HTTP-IPv6/%d ", $HTTP6PORT);
|
||||
}
|
||||
if($ftp_ipv6) {
|
||||
logmsg sprintf("FTP-IPv6/%d ", $FTP6PORT);
|
||||
}
|
||||
if($tftp_ipv6) {
|
||||
logmsg sprintf("TFTP-IPv6/%d ", $TFTP6PORT);
|
||||
}
|
||||
logmsg sprintf("\n* SSH/%d ", $SSHPORT);
|
||||
logmsg sprintf("SOCKS/%d ", $SOCKSPORT);
|
||||
logmsg sprintf("POP3/%d ", $POP3PORT);
|
||||
logmsg sprintf("IMAP/%d ", $IMAPPORT);
|
||||
logmsg sprintf("SMTP/%d\n", $SMTPPORT);
|
||||
|
||||
|
||||
$has_textaware = ($^O eq 'MSWin32') || ($^O eq 'msys');
|
||||
|
||||
logmsg sprintf("* Libtool lib: %s\n", $libtool?"ON":"OFF");
|
||||
logmsg "***************************************** \n";
|
||||
}
|
||||
|
||||
@ -1720,6 +1756,9 @@ sub subVariables {
|
||||
$$thing =~ s/%TFTP6PORT/$TFTP6PORT/g;
|
||||
$$thing =~ s/%SSHPORT/$SSHPORT/g;
|
||||
$$thing =~ s/%SOCKSPORT/$SOCKSPORT/g;
|
||||
$$thing =~ s/%POP3PORT/$POP3PORT/g;
|
||||
$$thing =~ s/%IMAPPORT/$IMAPPORT/g;
|
||||
$$thing =~ s/%SMTPPORT/$SMTPPORT/g;
|
||||
$$thing =~ s/%CURL/$CURL/g;
|
||||
$$thing =~ s/%USER/$USER/g;
|
||||
$$thing =~ s/%CLIENTIP/$CLIENTIP/g;
|
||||
@ -2546,19 +2585,22 @@ sub startservers {
|
||||
my $what = lc($whatlist[0]);
|
||||
$what =~ s/[^a-z0-9-]//g;
|
||||
|
||||
if($what eq "ftp") {
|
||||
if(!$run{'ftp'}) {
|
||||
($pid, $pid2) = runftpserver("", $verbose);
|
||||
if(($what eq "pop3") ||
|
||||
($what eq "ftp") ||
|
||||
($what eq "imap") ||
|
||||
($what eq "smtp")) {
|
||||
if(!$run{$what}) {
|
||||
($pid, $pid2) = runpingpongserver($what, "", $verbose);
|
||||
if($pid <= 0) {
|
||||
return "failed starting FTP server";
|
||||
return "failed starting $what server";
|
||||
}
|
||||
printf ("* pid ftp => %d %d\n", $pid, $pid2) if($verbose);
|
||||
$run{'ftp'}="$pid $pid2";
|
||||
printf ("* pid $what => %d %d\n", $pid, $pid2) if($verbose);
|
||||
$run{$what}="$pid $pid2";
|
||||
}
|
||||
}
|
||||
elsif($what eq "ftp2") {
|
||||
if(!$run{'ftp2'}) {
|
||||
($pid, $pid2) = runftpserver("2", $verbose);
|
||||
($pid, $pid2) = runpingpongserver("ftp", "2", $verbose);
|
||||
if($pid <= 0) {
|
||||
return "failed starting FTP2 server";
|
||||
}
|
||||
@ -2568,7 +2610,7 @@ sub startservers {
|
||||
}
|
||||
elsif($what eq "ftp-ipv6") {
|
||||
if(!$run{'ftp-ipv6'}) {
|
||||
($pid, $pid2) = runftpserver("", $verbose, "ipv6");
|
||||
($pid, $pid2) = runpingpongserver("ftp", "", $verbose, "ipv6");
|
||||
if($pid <= 0) {
|
||||
return "failed starting FTP-IPv6 server";
|
||||
}
|
||||
@ -2609,7 +2651,7 @@ sub startservers {
|
||||
}
|
||||
|
||||
if(!$run{'ftp'}) {
|
||||
($pid, $pid2) = runftpserver("", $verbose);
|
||||
($pid, $pid2) = runpingpongserver("ftp", "", $verbose);
|
||||
if($pid <= 0) {
|
||||
return "failed starting FTP server";
|
||||
}
|
||||
@ -2939,18 +2981,21 @@ if ($gdbthis) {
|
||||
}
|
||||
}
|
||||
|
||||
$HTTPPORT = $base + 0; # HTTP server port
|
||||
$HTTPSPORT = $base + 1; # HTTPS server port
|
||||
$FTPPORT = $base + 2; # FTP server port
|
||||
$FTPSPORT = $base + 3; # FTPS server port
|
||||
$HTTP6PORT = $base + 4; # HTTP IPv6 server port (different IP protocol
|
||||
$HTTPPORT = $base++; # HTTP server port
|
||||
$HTTPSPORT = $base++; # HTTPS server port
|
||||
$FTPPORT = $base++; # FTP server port
|
||||
$FTPSPORT = $base++; # FTPS server port
|
||||
$HTTP6PORT = $base++; # HTTP IPv6 server port (different IP protocol
|
||||
# but we follow the same port scheme anyway)
|
||||
$FTP2PORT = $base + 5; # FTP server 2 port
|
||||
$FTP6PORT = $base + 6; # FTP IPv6 port
|
||||
$TFTPPORT = $base + 7; # TFTP (UDP) port
|
||||
$TFTP6PORT = $base + 8; # TFTP IPv6 (UDP) port
|
||||
$SSHPORT = $base + 9; # SSH (SCP/SFTP) port
|
||||
$SOCKSPORT = $base + 10; # SOCKS port
|
||||
$FTP2PORT = $base++; # FTP server 2 port
|
||||
$FTP6PORT = $base++; # FTP IPv6 port
|
||||
$TFTPPORT = $base++; # TFTP (UDP) port
|
||||
$TFTP6PORT = $base++; # TFTP IPv6 (UDP) port
|
||||
$SSHPORT = $base++; # SSH (SCP/SFTP) port
|
||||
$SOCKSPORT = $base++; # SOCKS port
|
||||
$POP3PORT = $base++;
|
||||
$IMAPPORT = $base++;
|
||||
$SMTPPORT = $base++;
|
||||
|
||||
#######################################################################
|
||||
# clear and create logging directory:
|
||||
|
Loading…
Reference in New Issue
Block a user