mirror of
https://github.com/moparisthebest/curl
synced 2024-12-22 08:08:50 -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_DICT (1<<9)
|
||||||
#define CURLPROTO_FILE (1<<10)
|
#define CURLPROTO_FILE (1<<10)
|
||||||
#define CURLPROTO_TFTP (1<<11)
|
#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 */
|
#define CURLPROTO_ALL (~0) /* enable everything */
|
||||||
|
|
||||||
/* long may be 32 or 64 bits, but we should never depend on anything else
|
/* 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
|
essentially places a demand on the FTP server to acknowledge commands
|
||||||
in a timely manner. */
|
in a timely manner. */
|
||||||
CINIT(FTP_RESPONSE_TIMEOUT, LONG, 112),
|
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
|
/* 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
|
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 */
|
/* set the SSH host key callback custom pointer */
|
||||||
CINIT(SSH_KEYDATA, OBJECTPOINT, 185),
|
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 */
|
CURLOPT_LASTENTRY /* the last unused */
|
||||||
} CURLoption;
|
} CURLoption;
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ VCPROJ = libcurl.vcproj
|
|||||||
|
|
||||||
DOCS = README.encoding README.memoryleak README.ares README.curlx \
|
DOCS = README.encoding README.memoryleak README.ares README.curlx \
|
||||||
README.hostip README.multi_socket README.httpauth README.pipelining \
|
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
|
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 \
|
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 \
|
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 \
|
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 \
|
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 \
|
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 \
|
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 \
|
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_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.
|
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
|
* This software is licensed as described in the file COPYING, which
|
||||||
* you should have received as part of this distribution. The terms
|
* you should have received as part of this distribution. The terms
|
||||||
@ -23,6 +23,8 @@
|
|||||||
* $Id$
|
* $Id$
|
||||||
***************************************************************************/
|
***************************************************************************/
|
||||||
|
|
||||||
|
#include "pingpong.h"
|
||||||
|
|
||||||
#ifndef CURL_DISABLE_FTP
|
#ifndef CURL_DISABLE_FTP
|
||||||
extern const struct Curl_handler Curl_handler_ftp;
|
extern const struct Curl_handler Curl_handler_ftp;
|
||||||
|
|
||||||
@ -39,8 +41,108 @@ extern const struct Curl_handler Curl_handler_ftps_proxy;
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
CURLcode Curl_ftpsendf(struct connectdata *, const char *fmt, ...);
|
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,
|
CURLcode Curl_GetFTPResponse(ssize_t *nread, struct connectdata *conn,
|
||||||
int *ftpcode);
|
int *ftpcode);
|
||||||
#endif /* CURL_DISABLE_FTP */
|
#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 */
|
#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
|
/* This is the default when the server sends no
|
||||||
Content-Encoding header. See Curl_readwrite_init; the
|
Content-Encoding header. See Curl_readwrite_init; the
|
||||||
memset() call initializes k->content_encoding to zero. */
|
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,
|
result = Curl_client_write(conn, CLIENTWRITE_BODY, k->str,
|
||||||
nread);
|
nread);
|
||||||
|
}
|
||||||
#ifdef HAVE_LIBZ
|
#ifdef HAVE_LIBZ
|
||||||
break;
|
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 "file.h"
|
||||||
#include "curl_ldap.h"
|
#include "curl_ldap.h"
|
||||||
#include "ssh.h"
|
#include "ssh.h"
|
||||||
|
#include "imap.h"
|
||||||
#include "url.h"
|
#include "url.h"
|
||||||
#include "connect.h"
|
#include "connect.h"
|
||||||
#include "inet_ntop.h"
|
#include "inet_ntop.h"
|
||||||
@ -204,6 +205,27 @@ static const struct Curl_handler * const protocols[] = {
|
|||||||
&Curl_handler_sftp,
|
&Curl_handler_sftp,
|
||||||
#endif
|
#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
|
(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);
|
data->set.ftp_create_missing_dirs = (int)va_arg(param, long);
|
||||||
break;
|
break;
|
||||||
case CURLOPT_FTP_RESPONSE_TIMEOUT:
|
case CURLOPT_SERVER_RESPONSE_TIMEOUT:
|
||||||
/*
|
/*
|
||||||
* An FTP option that specifies how quickly an FTP response must be
|
* Option that specifies how quickly an server response must be obtained
|
||||||
* obtained before it is considered failure.
|
* 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;
|
break;
|
||||||
case CURLOPT_TFTP_BLKSIZE:
|
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;
|
data->set.redir_protocols = va_arg(param, long) & PROT_EXTMASK;
|
||||||
break;
|
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:
|
default:
|
||||||
/* unknown tag and its companion, just ignore: */
|
/* unknown tag and its companion, just ignore: */
|
||||||
result = CURLE_FAILED_INIT; /* correct this */
|
result = CURLE_FAILED_INIT; /* correct this */
|
||||||
@ -3334,6 +3366,8 @@ static CURLcode ParseURLAndFillConnection(struct SessionHandle *data,
|
|||||||
strcpy(conn->protostr, "DICT");
|
strcpy(conn->protostr, "DICT");
|
||||||
else if(checkprefix("LDAP.", conn->host.name))
|
else if(checkprefix("LDAP.", conn->host.name))
|
||||||
strcpy(conn->protostr, "LDAP");
|
strcpy(conn->protostr, "LDAP");
|
||||||
|
else if(checkprefix("IMAP.", conn->host.name))
|
||||||
|
strcpy(conn->protostr, "IMAP");
|
||||||
else {
|
else {
|
||||||
strcpy(conn->protostr, "http");
|
strcpy(conn->protostr, "http");
|
||||||
}
|
}
|
||||||
@ -4069,7 +4103,7 @@ static CURLcode set_userpass(struct connectdata *conn,
|
|||||||
const char *user, const char *passwd)
|
const char *user, const char *passwd)
|
||||||
{
|
{
|
||||||
/* If our protocol needs a password and we have none, use the defaults */
|
/* 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->bits.user_passwd) {
|
||||||
|
|
||||||
conn->user = strdup(CURL_DEFAULT_USER);
|
conn->user = strdup(CURL_DEFAULT_USER);
|
||||||
|
165
lib/urldata.h
165
lib/urldata.h
@ -37,6 +37,12 @@
|
|||||||
#define PORT_LDAPS 636
|
#define PORT_LDAPS 636
|
||||||
#define PORT_TFTP 69
|
#define PORT_TFTP 69
|
||||||
#define PORT_SSH 22
|
#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_MATCH "/MATCH:"
|
||||||
#define DICT_MATCH2 "/M:"
|
#define DICT_MATCH2 "/M:"
|
||||||
@ -51,6 +57,11 @@
|
|||||||
/* length of longest IPv6 address string including the trailing null */
|
/* 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")
|
#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 "cookie.h"
|
||||||
#include "formdata.h"
|
#include "formdata.h"
|
||||||
|
|
||||||
@ -129,6 +140,11 @@
|
|||||||
#include "hash.h"
|
#include "hash.h"
|
||||||
#include "splay.h"
|
#include "splay.h"
|
||||||
|
|
||||||
|
#include "imap.h"
|
||||||
|
#include "pop3.h"
|
||||||
|
#include "smtp.h"
|
||||||
|
#include "ftp.h"
|
||||||
|
|
||||||
#ifdef HAVE_GSSAPI
|
#ifdef HAVE_GSSAPI
|
||||||
# ifdef HAVE_GSSGNU
|
# ifdef HAVE_GSSGNU
|
||||||
# include <gss.h>
|
# include <gss.h>
|
||||||
@ -351,121 +367,6 @@ struct HTTP {
|
|||||||
points to an allocated send_buffer struct */
|
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
|
* SSH unique setup
|
||||||
@ -922,18 +823,23 @@ struct connectdata {
|
|||||||
#define PROT_TFTP CURLPROTO_TFTP
|
#define PROT_TFTP CURLPROTO_TFTP
|
||||||
#define PROT_SCP CURLPROTO_SCP
|
#define PROT_SCP CURLPROTO_SCP
|
||||||
#define PROT_SFTP CURLPROTO_SFTP
|
#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
|
/* (1<<17) is currently the highest used bit in the public bitmask. We make
|
||||||
bitmask. We make sure we use "private bits" above the first 16 to make
|
sure we use "private bits" above the public ones to make things easier. */
|
||||||
things easier. */
|
|
||||||
|
|
||||||
#define PROT_EXTMASK 0xffff
|
#define PROT_EXTMASK 0xfffff
|
||||||
|
|
||||||
#define PROT_SSL (1<<22) /* protocol requires SSL */
|
#define PROT_SSL (1<<25) /* protocol requires SSL */
|
||||||
#define PROT_MISSING (1<<23)
|
#define PROT_MISSING (1<<26)
|
||||||
|
|
||||||
/* these ones need action before socket close */
|
/* 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 */
|
#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
|
/* 'dns_entry' is the particular host we use. This points to an entry in the
|
||||||
@ -1035,7 +941,7 @@ struct connectdata {
|
|||||||
struct curl_llist *pend_pipe; /* List of pending handles on
|
struct curl_llist *pend_pipe; /* List of pending handles on
|
||||||
this pipeline */
|
this pipeline */
|
||||||
struct curl_llist *done_pipe; /* Handles that are finished, but
|
struct curl_llist *done_pipe; /* Handles that are finished, but
|
||||||
still reference this connectdata */
|
still reference this connectdata */
|
||||||
#define MAX_PIPELINE_LENGTH 5
|
#define MAX_PIPELINE_LENGTH 5
|
||||||
|
|
||||||
char* master_buffer; /* The master buffer allocated on-demand;
|
char* master_buffer; /* The master buffer allocated on-demand;
|
||||||
@ -1075,6 +981,9 @@ struct connectdata {
|
|||||||
struct ftp_conn ftpc;
|
struct ftp_conn ftpc;
|
||||||
struct ssh_conn sshc;
|
struct ssh_conn sshc;
|
||||||
struct tftp_state_data *tftpc;
|
struct tftp_state_data *tftpc;
|
||||||
|
struct imap_conn imapc;
|
||||||
|
struct pop3_conn pop3c;
|
||||||
|
struct smtp_conn smtpc;
|
||||||
} proto;
|
} proto;
|
||||||
|
|
||||||
int cselect_bits; /* bitmask of socket events */
|
int cselect_bits; /* bitmask of socket events */
|
||||||
@ -1331,6 +1240,9 @@ struct UrlState {
|
|||||||
void *telnet; /* private for telnet.c-eyes only */
|
void *telnet; /* private for telnet.c-eyes only */
|
||||||
void *generic;
|
void *generic;
|
||||||
struct SSHPROTO *ssh;
|
struct SSHPROTO *ssh;
|
||||||
|
struct FTP *imap;
|
||||||
|
struct FTP *pop3;
|
||||||
|
struct FTP *smtp;
|
||||||
} proto;
|
} proto;
|
||||||
/* current user of this SessionHandle instance, or NULL */
|
/* current user of this SessionHandle instance, or NULL */
|
||||||
struct connectdata *current_conn;
|
struct connectdata *current_conn;
|
||||||
@ -1412,6 +1324,8 @@ enum dupstring {
|
|||||||
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
|
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
|
||||||
STRING_SOCKS5_GSSAPI_SERVICE, /* GSSAPI service name */
|
STRING_SOCKS5_GSSAPI_SERVICE, /* GSSAPI service name */
|
||||||
#endif
|
#endif
|
||||||
|
STRING_MAIL_FROM,
|
||||||
|
STRING_MAIL_RCPT,
|
||||||
|
|
||||||
/* -- end of strings -- */
|
/* -- end of strings -- */
|
||||||
STRING_LAST /* not used, just an end-of-list marker */
|
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 */
|
void *ioctl_client; /* pointer to pass to the ioctl callback */
|
||||||
long timeout; /* in milliseconds, 0 means no timeout */
|
long timeout; /* in milliseconds, 0 means no timeout */
|
||||||
long connecttimeout; /* 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 */
|
long tftp_blksize ; /* in bytes, 0 means use default */
|
||||||
curl_off_t infilesize; /* size of file to upload, -1 means unknown */
|
curl_off_t infilesize; /* size of file to upload, -1 means unknown */
|
||||||
long low_speed_limit; /* bytes/second */
|
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_epsv; /* if EPSV is to be attempted or not */
|
||||||
bool ftp_use_eprt; /* if EPRT 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_ftpauth ftpsslauth; /* what AUTH XXX to be attempted */
|
||||||
curl_ftpccc ftp_ccc; /* FTP CCC options */
|
curl_ftpccc ftp_ccc; /* FTP CCC options */
|
||||||
bool no_signal; /* do not use any signal/alarm handler */
|
bool no_signal; /* do not use any signal/alarm handler */
|
||||||
|
@ -158,6 +158,27 @@ static const char * const protocols[] = {
|
|||||||
"sftp",
|
"sftp",
|
||||||
#endif
|
#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
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
19
src/main.c
19
src/main.c
@ -500,6 +500,8 @@ struct Configurable {
|
|||||||
char *proxy;
|
char *proxy;
|
||||||
int proxyver; /* set to CURLPROXY_HTTP* define */
|
int proxyver; /* set to CURLPROXY_HTTP* define */
|
||||||
char *noproxy;
|
char *noproxy;
|
||||||
|
char *mail_from;
|
||||||
|
char *mail_rcpt;
|
||||||
bool proxytunnel;
|
bool proxytunnel;
|
||||||
bool ftp_append; /* APPE on ftp */
|
bool ftp_append; /* APPE on ftp */
|
||||||
bool mute; /* shutup */
|
bool mute; /* shutup */
|
||||||
@ -822,6 +824,8 @@ static void help(void)
|
|||||||
" -L/--location Follow Location: hints (H)",
|
" -L/--location Follow Location: hints (H)",
|
||||||
" --location-trusted Follow Location: and send auth to other hosts (H)",
|
" --location-trusted Follow Location: and send auth to other hosts (H)",
|
||||||
" -M/--manual Display the full manual",
|
" -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-filesize <bytes> Maximum file size to download (H/F)",
|
||||||
" --max-redirs <num> Maximum number of redirects allowed (H)",
|
" --max-redirs <num> Maximum number of redirects allowed (H)",
|
||||||
" -m/--max-time <seconds> Maximum time allowed for the transfer",
|
" -m/--max-time <seconds> Maximum time allowed for the transfer",
|
||||||
@ -1740,6 +1744,8 @@ static ParameterError getparameter(char *flag, /* f or -long-flag */
|
|||||||
#endif
|
#endif
|
||||||
{"$8", "proxy1.0", TRUE},
|
{"$8", "proxy1.0", TRUE},
|
||||||
{"$9", "tftp-blksize", TRUE},
|
{"$9", "tftp-blksize", TRUE},
|
||||||
|
{"$A", "mail-from", TRUE},
|
||||||
|
{"$B", "mail-rcpt", TRUE},
|
||||||
{"0", "http1.0", FALSE},
|
{"0", "http1.0", FALSE},
|
||||||
{"1", "tlsv1", FALSE},
|
{"1", "tlsv1", FALSE},
|
||||||
{"2", "sslv2", FALSE},
|
{"2", "sslv2", FALSE},
|
||||||
@ -2269,6 +2275,12 @@ static ParameterError getparameter(char *flag, /* f or -long-flag */
|
|||||||
case '9': /* --tftp-blksize */
|
case '9': /* --tftp-blksize */
|
||||||
str2num(&config->tftp_blksize, nextarg);
|
str2num(&config->tftp_blksize, nextarg);
|
||||||
break;
|
break;
|
||||||
|
case 'A': /* --mail-from */
|
||||||
|
GetStr(&config->mail_from, nextarg);
|
||||||
|
break;
|
||||||
|
case 'B': /* --mail-rcpt */
|
||||||
|
GetStr(&config->mail_rcpt, nextarg);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case '#': /* --progress-bar */
|
case '#': /* --progress-bar */
|
||||||
@ -5006,9 +5018,16 @@ operate(struct Configurable *config, int argc, argv_item_t argv[])
|
|||||||
my_setopt(curl, CURLOPT_POSTREDIR, config->post301 |
|
my_setopt(curl, CURLOPT_POSTREDIR, config->post301 |
|
||||||
(config->post302 ? CURL_REDIR_POST_302 : FALSE));
|
(config->post302 ? CURL_REDIR_POST_302 : FALSE));
|
||||||
|
|
||||||
|
/* curl 7.20.0 */
|
||||||
if(config->tftp_blksize)
|
if(config->tftp_blksize)
|
||||||
my_setopt(curl, CURLOPT_TFTP_BLKSIZE, 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;
|
retry_numretries = config->req_retry;
|
||||||
|
|
||||||
retrystart = cutil_tvnow();
|
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 \
|
test1089 test1090 test1091 test1092 test1093 test1094 test1095 test1096 \
|
||||||
test1097 test560 test561 test1098 test1099 test562 test563 test1100 \
|
test1097 test560 test561 test1098 test1099 test562 test563 test1100 \
|
||||||
test564 test1101 test1102 test1103 test1104 test299 test310 test311 \
|
test564 test1101 test1102 test1103 test1104 test299 test310 test311 \
|
||||||
test312 test1105 test565
|
test312 test1105 test565 test800
|
||||||
|
|
||||||
filecheck:
|
filecheck:
|
||||||
@mkdir test-place; \
|
@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$
|
# $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
|
# It is meant to exercise curl, it is not meant to be a fully working
|
||||||
# or even very standard compliant server.
|
# 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 $SERVERLOGS_LOCK="log/serverlogs.lock"; # server logs advisor read lock
|
||||||
my $serverlogslocked=0;
|
my $serverlogslocked=0;
|
||||||
|
|
||||||
|
my $proto="ftp";
|
||||||
|
|
||||||
do {
|
do {
|
||||||
if($ARGV[0] eq "-v") {
|
if($ARGV[0] eq "-v") {
|
||||||
$verbose=1;
|
$verbose=1;
|
||||||
@ -100,6 +106,11 @@ do {
|
|||||||
$ftpdnum=$ARGV[1];
|
$ftpdnum=$ARGV[1];
|
||||||
shift @ARGV;
|
shift @ARGV;
|
||||||
}
|
}
|
||||||
|
elsif($ARGV[0] eq "--proto") {
|
||||||
|
# ftp pop3 imap smtp
|
||||||
|
$proto=$ARGV[1];
|
||||||
|
shift @ARGV;
|
||||||
|
}
|
||||||
elsif($ARGV[0] eq "--pidfile") {
|
elsif($ARGV[0] eq "--pidfile") {
|
||||||
$pidfile=$ARGV[1];
|
$pidfile=$ARGV[1];
|
||||||
shift @ARGV;
|
shift @ARGV;
|
||||||
@ -115,23 +126,28 @@ do {
|
|||||||
}
|
}
|
||||||
elsif($ARGV[0] eq "--addr") {
|
elsif($ARGV[0] eq "--addr") {
|
||||||
$listenaddr = $ARGV[1];
|
$listenaddr = $ARGV[1];
|
||||||
$listenaddr =~ s/^\[(.*)\]$/\1/;
|
$listenaddr =~ s/^\[(.*)\]$/$1/;
|
||||||
shift @ARGV;
|
shift @ARGV;
|
||||||
}
|
}
|
||||||
} while(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 {
|
sub catch_zap {
|
||||||
my $signame = shift;
|
my $signame = shift;
|
||||||
|
print STDERR "ftpserver.pl received SIG$signame, exiting\n";
|
||||||
ftpkillslaves(1);
|
ftpkillslaves(1);
|
||||||
unlink($pidfile);
|
|
||||||
if($serverlogslocked) {
|
if($serverlogslocked) {
|
||||||
$serverlogslocked = 0;
|
$serverlogslocked = 0;
|
||||||
clear_advisor_read_lock($SERVERLOGS_LOCK);
|
clear_advisor_read_lock($SERVERLOGS_LOCK);
|
||||||
}
|
}
|
||||||
exit;
|
die "Somebody sent me a SIG$signame";
|
||||||
}
|
}
|
||||||
$SIG{INT} = \&catch_zap;
|
$SIG{INT} = \&catch_zap;
|
||||||
$SIG{TERM} = \&catch_zap;
|
$SIG{KILL} = \&catch_zap;
|
||||||
|
|
||||||
my $sfpid;
|
my $sfpid;
|
||||||
|
|
||||||
@ -153,7 +169,6 @@ sub sysread_or_die {
|
|||||||
logmsg "Error: ftp$ftpdnum$ext sysread error: $!\n";
|
logmsg "Error: ftp$ftpdnum$ext sysread error: $!\n";
|
||||||
kill(9, $sfpid);
|
kill(9, $sfpid);
|
||||||
waitpid($sfpid, 0);
|
waitpid($sfpid, 0);
|
||||||
unlink($pidfile);
|
|
||||||
if($serverlogslocked) {
|
if($serverlogslocked) {
|
||||||
$serverlogslocked = 0;
|
$serverlogslocked = 0;
|
||||||
clear_advisor_read_lock($SERVERLOGS_LOCK);
|
clear_advisor_read_lock($SERVERLOGS_LOCK);
|
||||||
@ -167,7 +182,6 @@ sub sysread_or_die {
|
|||||||
logmsg "Error: ftp$ftpdnum$ext read zero\n";
|
logmsg "Error: ftp$ftpdnum$ext read zero\n";
|
||||||
kill(9, $sfpid);
|
kill(9, $sfpid);
|
||||||
waitpid($sfpid, 0);
|
waitpid($sfpid, 0);
|
||||||
unlink($pidfile);
|
|
||||||
if($serverlogslocked) {
|
if($serverlogslocked) {
|
||||||
$serverlogslocked = 0;
|
$serverlogslocked = 0;
|
||||||
clear_advisor_read_lock($SERVERLOGS_LOCK);
|
clear_advisor_read_lock($SERVERLOGS_LOCK);
|
||||||
@ -193,7 +207,6 @@ sub startsf {
|
|||||||
logmsg "Failed sockfilt command: $cmd\n";
|
logmsg "Failed sockfilt command: $cmd\n";
|
||||||
kill(9, $sfpid);
|
kill(9, $sfpid);
|
||||||
waitpid($sfpid, 0);
|
waitpid($sfpid, 0);
|
||||||
unlink($pidfile);
|
|
||||||
if($serverlogslocked) {
|
if($serverlogslocked) {
|
||||||
$serverlogslocked = 0;
|
$serverlogslocked = 0;
|
||||||
clear_advisor_read_lock($SERVERLOGS_LOCK);
|
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();
|
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");
|
open(PID, ">$pidfile");
|
||||||
print PID $$."\n";
|
print PID $$."\n";
|
||||||
close(PID);
|
close(PID);
|
||||||
@ -273,41 +290,66 @@ sub senddata {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# this text is shown before the function specified below is run
|
my %displaytext;
|
||||||
my %displaytext = ('USER' => '331 We are happy you popped in!',
|
my %commandfunc;
|
||||||
'PASS' => '230 Welcome you silly person',
|
|
||||||
'PORT' => '200 You said PORT - I say FINE',
|
|
||||||
'TYPE' => '200 I modify TYPE as you wanted',
|
|
||||||
'LIST' => '150 here comes a directory',
|
|
||||||
'NLST' => '150 here comes a directory',
|
|
||||||
'CWD' => '250 CWD command successful.',
|
|
||||||
'SYST' => '215 UNIX Type: L8', # just fake something
|
|
||||||
'QUIT' => '221 bye bye baby', # just reply something
|
|
||||||
'PWD' => '257 "/nowhere/anywhere" is current directory',
|
|
||||||
'MKD' => '257 Created your requested directory',
|
|
||||||
'REST' => '350 Yeah yeah we set it there for you',
|
|
||||||
'DELE' => '200 OK OK OK whatever you say',
|
|
||||||
'RNFR' => '350 Received your order. Please provide more',
|
|
||||||
'RNTO' => '250 Ok, thanks. File renaming completed.',
|
|
||||||
'NOOP' => '200 Yes, I\'m very good at doing nothing.',
|
|
||||||
'PBSZ' => '500 PBSZ not implemented',
|
|
||||||
'PROT' => '500 PROT not implemented',
|
|
||||||
);
|
|
||||||
|
|
||||||
# callback functions for certain commands
|
# callback functions for certain commands
|
||||||
my %commandfunc = ( 'PORT' => \&PORT_command,
|
# and text shown before the function specified below is run
|
||||||
'EPRT' => \&PORT_command,
|
|
||||||
'LIST' => \&LIST_command,
|
if($proto eq "ftp") {
|
||||||
'NLST' => \&NLST_command,
|
%displaytext = ('USER' => '331 We are happy you popped in!',
|
||||||
'PASV' => \&PASV_command,
|
'PASS' => '230 Welcome you silly person',
|
||||||
'EPSV' => \&PASV_command,
|
'PORT' => '200 You said PORT - I say FINE',
|
||||||
'RETR' => \&RETR_command,
|
'TYPE' => '200 I modify TYPE as you wanted',
|
||||||
'SIZE' => \&SIZE_command,
|
'LIST' => '150 here comes a directory',
|
||||||
'REST' => \&REST_command,
|
'NLST' => '150 here comes a directory',
|
||||||
'STOR' => \&STOR_command,
|
'CWD' => '250 CWD command successful.',
|
||||||
'APPE' => \&STOR_command, # append looks like upload
|
'SYST' => '215 UNIX Type: L8', # just fake something
|
||||||
'MDTM' => \&MDTM_command,
|
'QUIT' => '221 bye bye baby', # just reply something
|
||||||
);
|
'PWD' => '257 "/nowhere/anywhere" is current directory',
|
||||||
|
'MKD' => '257 Created your requested directory',
|
||||||
|
'REST' => '350 Yeah yeah we set it there for you',
|
||||||
|
'DELE' => '200 OK OK OK whatever you say',
|
||||||
|
'RNFR' => '350 Received your order. Please provide more',
|
||||||
|
'RNTO' => '250 Ok, thanks. File renaming completed.',
|
||||||
|
'NOOP' => '200 Yes, I\'m very good at doing nothing.',
|
||||||
|
'PBSZ' => '500 PBSZ not implemented',
|
||||||
|
'PROT' => '500 PROT not implemented',
|
||||||
|
);
|
||||||
|
|
||||||
|
%commandfunc = ( 'PORT' => \&PORT_command,
|
||||||
|
'EPRT' => \&PORT_command,
|
||||||
|
'LIST' => \&LIST_command,
|
||||||
|
'NLST' => \&NLST_command,
|
||||||
|
'PASV' => \&PASV_command,
|
||||||
|
'EPSV' => \&PASV_command,
|
||||||
|
'RETR' => \&RETR_command,
|
||||||
|
'SIZE' => \&SIZE_command,
|
||||||
|
'REST' => \&REST_command,
|
||||||
|
'STOR' => \&STOR_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 {
|
sub close_dataconn {
|
||||||
@ -330,6 +372,98 @@ sub close_dataconn {
|
|||||||
$slavepid=0;
|
$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;
|
my $rest=0;
|
||||||
sub REST_command {
|
sub REST_command {
|
||||||
$rest = $_[0];
|
$rest = $_[0];
|
||||||
@ -798,12 +932,34 @@ sub customize {
|
|||||||
close(CUSTOM);
|
close(CUSTOM);
|
||||||
}
|
}
|
||||||
|
|
||||||
my @welcome=(
|
my @welcome;
|
||||||
'220- _ _ ____ _ '."\r\n",
|
|
||||||
'220- ___| | | | _ \| | '."\r\n",
|
if($proto eq "ftp") {
|
||||||
'220- / __| | | | |_) | | '."\r\n",
|
@welcome=(
|
||||||
'220- | (__| |_| | _ <| |___ '."\r\n",
|
'220- _ _ ____ _ '."\r\n",
|
||||||
'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) {
|
while(1) {
|
||||||
@ -872,13 +1028,28 @@ while(1) {
|
|||||||
# Remove trailing CRLF.
|
# Remove trailing CRLF.
|
||||||
s/[\n\r]+$//;
|
s/[\n\r]+$//;
|
||||||
|
|
||||||
unless (m/^([A-Z]{3,4})\s?(.*)/i) {
|
my $cmdid;
|
||||||
sendcontrol "500 '$_': command not understood.\r\n";
|
my $FTPCMD;
|
||||||
last;
|
my $FTPARG;
|
||||||
}
|
|
||||||
my $FTPCMD=$1;
|
|
||||||
my $FTPARG=$2;
|
|
||||||
my $full=$_;
|
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;
|
||||||
|
}
|
||||||
|
$FTPCMD=$1;
|
||||||
|
$FTPARG=$2;
|
||||||
|
}
|
||||||
|
|
||||||
logmsg "< \"$full\"\n";
|
logmsg "< \"$full\"\n";
|
||||||
|
|
||||||
@ -907,7 +1078,7 @@ while(1) {
|
|||||||
}
|
}
|
||||||
my $check;
|
my $check;
|
||||||
if($text) {
|
if($text) {
|
||||||
sendcontrol "$text\r\n";
|
sendcontrol "$cmdid$text\r\n";
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$check=1; # no response yet
|
$check=1; # no response yet
|
||||||
@ -939,8 +1110,6 @@ while(1) {
|
|||||||
print SFWRITE "QUIT\n";
|
print SFWRITE "QUIT\n";
|
||||||
waitpid $sfpid, 0;
|
waitpid $sfpid, 0;
|
||||||
|
|
||||||
unlink($pidfile);
|
|
||||||
|
|
||||||
if($serverlogslocked) {
|
if($serverlogslocked) {
|
||||||
$serverlogslocked = 0;
|
$serverlogslocked = 0;
|
||||||
clear_advisor_read_lock($SERVERLOGS_LOCK);
|
clear_advisor_read_lock($SERVERLOGS_LOCK);
|
||||||
|
@ -109,6 +109,9 @@ my $TFTPPORT; # TFTP
|
|||||||
my $TFTP6PORT; # TFTP
|
my $TFTP6PORT; # TFTP
|
||||||
my $SSHPORT; # SCP/SFTP
|
my $SSHPORT; # SCP/SFTP
|
||||||
my $SOCKSPORT; # SOCKS4/5 port
|
my $SOCKSPORT; # SOCKS4/5 port
|
||||||
|
my $POP3PORT; # POP3
|
||||||
|
my $IMAPPORT; # IMAP
|
||||||
|
my $SMTPPORT; # SMTP
|
||||||
|
|
||||||
my $srcdir = $ENV{'srcdir'} || '.';
|
my $srcdir = $ENV{'srcdir'} || '.';
|
||||||
my $CURL="../src/curl"; # what curl executable to run on the tests
|
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 $TFTP6PIDFILE=".tftp6.pid";
|
||||||
my $SSHPIDFILE=".ssh.pid";
|
my $SSHPIDFILE=".ssh.pid";
|
||||||
my $SOCKSPIDFILE=".socks.pid";
|
my $SOCKSPIDFILE=".socks.pid";
|
||||||
|
my $POP3PIDFILE=".pop3.pid";
|
||||||
|
my $IMAPPIDFILE=".imap.pid";
|
||||||
|
my $SMTPPIDFILE=".smtp.pid";
|
||||||
|
|
||||||
# invoke perl like this:
|
# invoke perl like this:
|
||||||
my $perl="perl -I$srcdir";
|
my $perl="perl -I$srcdir";
|
||||||
@ -663,7 +669,7 @@ sub verifyftp {
|
|||||||
}
|
}
|
||||||
if($pid <= 0 && $data[0]) {
|
if($pid <= 0 && $data[0]) {
|
||||||
# this is not a known server
|
# 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;
|
return 0;
|
||||||
}
|
}
|
||||||
# we can/should use the time it took to verify the FTP server as a measure
|
# 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;
|
my $took = time()-$time;
|
||||||
|
|
||||||
if($verbose) {
|
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
|
$ftpchecktime = $took?$took:1; # make sure it never is zero
|
||||||
|
|
||||||
@ -773,6 +779,9 @@ sub verifysocks {
|
|||||||
my %protofunc = ('http' => \&verifyhttp,
|
my %protofunc = ('http' => \&verifyhttp,
|
||||||
'https' => \&verifyhttp,
|
'https' => \&verifyhttp,
|
||||||
'ftp' => \&verifyftp,
|
'ftp' => \&verifyftp,
|
||||||
|
'pop3' => \&verifyftp,
|
||||||
|
'imap' => \&verifyftp,
|
||||||
|
'smtp' => \&verifyftp,
|
||||||
'ftps' => \&verifyftp,
|
'ftps' => \&verifyftp,
|
||||||
'tftp' => \&verifyftp,
|
'tftp' => \&verifyftp,
|
||||||
'ssh' => \&verifyssh,
|
'ssh' => \&verifyssh,
|
||||||
@ -942,26 +951,48 @@ sub runhttpsserver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#######################################################################
|
#######################################################################
|
||||||
# start the ftp server
|
# start the pingpong server (FTP, POP3, IMAP, SMTP)
|
||||||
#
|
#
|
||||||
sub runftpserver {
|
sub runpingpongserver {
|
||||||
my ($id, $verbose, $ipv6) = @_;
|
my ($proto, $id, $verbose, $ipv6) = @_;
|
||||||
my $STATUS;
|
my $STATUS;
|
||||||
my $RUNNING;
|
my $RUNNING;
|
||||||
my $port = $id?$FTP2PORT:$FTPPORT;
|
my $port;
|
||||||
# check for pidfile
|
my $pidfile;
|
||||||
my $pidfile = $id?$FTP2PIDFILE:$FTPPIDFILE;
|
|
||||||
my $ip=$HOSTIP;
|
my $ip=$HOSTIP;
|
||||||
my $nameext;
|
my $nameext;
|
||||||
my $cmd;
|
my $cmd;
|
||||||
|
my $flag;
|
||||||
|
|
||||||
if($ipv6) {
|
if($proto eq "ftp") {
|
||||||
# if IPv6, use a different setup
|
$port = $id?$FTP2PORT:$FTPPORT;
|
||||||
$pidfile = $FTP6PIDFILE;
|
$pidfile = $id?$FTP2PIDFILE:$FTPPIDFILE;
|
||||||
$port = $FTP6PORT;
|
|
||||||
$ip = $HOST6IP;
|
if($ipv6) {
|
||||||
$nameext="-ipv6";
|
# if IPv6, use a different setup
|
||||||
|
$pidfile = $FTP6PIDFILE;
|
||||||
|
$port = $FTP6PORT;
|
||||||
|
$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
|
# don't retry if the server doesn't work
|
||||||
if ($doesntrun{$pidfile}) {
|
if ($doesntrun{$pidfile}) {
|
||||||
@ -975,7 +1006,7 @@ sub runftpserver {
|
|||||||
unlink($pidfile);
|
unlink($pidfile);
|
||||||
|
|
||||||
# start our server:
|
# start our server:
|
||||||
my $flag=$debugprotocol?"-v ":"";
|
$flag.=$debugprotocol?"-v ":"";
|
||||||
$flag .= "-s \"$srcdir\" ";
|
$flag .= "-s \"$srcdir\" ";
|
||||||
my $addr;
|
my $addr;
|
||||||
if($id) {
|
if($id) {
|
||||||
@ -993,7 +1024,7 @@ sub runftpserver {
|
|||||||
|
|
||||||
if($ftppid <= 0 || !kill(0, $ftppid)) {
|
if($ftppid <= 0 || !kill(0, $ftppid)) {
|
||||||
# it is NOT alive
|
# 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");
|
stopserver("$pid2");
|
||||||
displaylogs($testnumcheck);
|
displaylogs($testnumcheck);
|
||||||
$doesntrun{$pidfile} = 1;
|
$doesntrun{$pidfile} = 1;
|
||||||
@ -1001,9 +1032,9 @@ sub runftpserver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Server is up. Verify that we can speak to it.
|
# Server is up. Verify that we can speak to it.
|
||||||
my $pid3 = verifyserver("ftp", $ip, $port);
|
my $pid3 = verifyserver($proto, $ip, $port);
|
||||||
if(!$pid3) {
|
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
|
# failed to talk to it properly. Kill the server and return failure
|
||||||
stopserver("$ftppid $pid2");
|
stopserver("$ftppid $pid2");
|
||||||
displaylogs($testnumcheck);
|
displaylogs($testnumcheck);
|
||||||
@ -1013,7 +1044,7 @@ sub runftpserver {
|
|||||||
$pid2 = $pid3;
|
$pid2 = $pid3;
|
||||||
|
|
||||||
if($verbose) {
|
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);
|
sleep(1);
|
||||||
@ -1661,41 +1692,46 @@ sub checksystem {
|
|||||||
"* Host: $hostname",
|
"* Host: $hostname",
|
||||||
"* System: $hosttype");
|
"* System: $hosttype");
|
||||||
|
|
||||||
logmsg sprintf("* Server SSL: %s\n", $stunnel?"ON":"OFF");
|
logmsg sprintf("* Server SSL: %8s", $stunnel?"ON ":"OFF");
|
||||||
logmsg sprintf("* libcurl SSL: %s\n", $ssl_version?"ON":"OFF");
|
logmsg sprintf(" libcurl SSL: %s\n", $ssl_version?"ON ":"OFF");
|
||||||
logmsg sprintf("* debug build: %s\n", $debug_build?"ON":"OFF");
|
logmsg sprintf("* debug build: %8s", $debug_build?"ON ":"OFF");
|
||||||
logmsg sprintf("* track memory: %s\n", $curl_debug?"ON":"OFF");
|
logmsg sprintf(" track memory: %s\n", $curl_debug?"ON ":"OFF");
|
||||||
logmsg sprintf("* valgrind: %s\n", $valgrind?"ON":"OFF");
|
logmsg sprintf("* valgrind: %8s", $valgrind?"ON ":"OFF");
|
||||||
logmsg sprintf("* HTTP IPv6 %s\n", $http_ipv6?"ON":"OFF");
|
logmsg sprintf(" HTTP IPv6 %s\n", $http_ipv6?"ON ":"OFF");
|
||||||
logmsg sprintf("* FTP IPv6 %s\n", $ftp_ipv6?"ON":"OFF");
|
logmsg sprintf("* FTP IPv6 %8s", $ftp_ipv6?"ON ":"OFF");
|
||||||
|
logmsg sprintf(" Libtool lib: %s\n", $libtool?"ON ":"OFF");
|
||||||
logmsg sprintf("* HTTP port: %d\n", $HTTPPORT);
|
if($ssl_version) {
|
||||||
logmsg sprintf("* FTP port: %d\n", $FTPPORT);
|
logmsg sprintf("* SSL library: %s\n", $ssllib);
|
||||||
logmsg sprintf("* FTP port 2: %d\n", $FTP2PORT);
|
|
||||||
if($stunnel) {
|
|
||||||
logmsg sprintf("* FTPS port: %d\n", $FTPSPORT);
|
|
||||||
logmsg sprintf("* HTTPS port: %d\n", $HTTPSPORT);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
if($http_ipv6) {
|
||||||
logmsg sprintf("* HTTP IPv6 port: %d\n", $HTTP6PORT);
|
logmsg sprintf("HTTP-IPv6/%d ", $HTTP6PORT);
|
||||||
}
|
}
|
||||||
if($ftp_ipv6) {
|
if($ftp_ipv6) {
|
||||||
logmsg sprintf("* FTP IPv6 port: %d\n", $FTP6PORT);
|
logmsg sprintf("FTP-IPv6/%d ", $FTP6PORT);
|
||||||
}
|
}
|
||||||
logmsg sprintf("* TFTP port: %d\n", $TFTPPORT);
|
|
||||||
if($tftp_ipv6) {
|
if($tftp_ipv6) {
|
||||||
logmsg sprintf("* TFTP IPv6 port: %d\n", $TFTP6PORT);
|
logmsg sprintf("TFTP-IPv6/%d ", $TFTP6PORT);
|
||||||
}
|
}
|
||||||
logmsg sprintf("* SCP/SFTP port: %d\n", $SSHPORT);
|
logmsg sprintf("\n* SSH/%d ", $SSHPORT);
|
||||||
logmsg sprintf("* SOCKS port: %d\n", $SOCKSPORT);
|
logmsg sprintf("SOCKS/%d ", $SOCKSPORT);
|
||||||
|
logmsg sprintf("POP3/%d ", $POP3PORT);
|
||||||
|
logmsg sprintf("IMAP/%d ", $IMAPPORT);
|
||||||
|
logmsg sprintf("SMTP/%d\n", $SMTPPORT);
|
||||||
|
|
||||||
if($ssl_version) {
|
|
||||||
logmsg sprintf("* SSL library: %s\n", $ssllib);
|
|
||||||
}
|
|
||||||
|
|
||||||
$has_textaware = ($^O eq 'MSWin32') || ($^O eq 'msys');
|
$has_textaware = ($^O eq 'MSWin32') || ($^O eq 'msys');
|
||||||
|
|
||||||
logmsg sprintf("* Libtool lib: %s\n", $libtool?"ON":"OFF");
|
|
||||||
logmsg "***************************************** \n";
|
logmsg "***************************************** \n";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1720,6 +1756,9 @@ sub subVariables {
|
|||||||
$$thing =~ s/%TFTP6PORT/$TFTP6PORT/g;
|
$$thing =~ s/%TFTP6PORT/$TFTP6PORT/g;
|
||||||
$$thing =~ s/%SSHPORT/$SSHPORT/g;
|
$$thing =~ s/%SSHPORT/$SSHPORT/g;
|
||||||
$$thing =~ s/%SOCKSPORT/$SOCKSPORT/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/%CURL/$CURL/g;
|
||||||
$$thing =~ s/%USER/$USER/g;
|
$$thing =~ s/%USER/$USER/g;
|
||||||
$$thing =~ s/%CLIENTIP/$CLIENTIP/g;
|
$$thing =~ s/%CLIENTIP/$CLIENTIP/g;
|
||||||
@ -2546,19 +2585,22 @@ sub startservers {
|
|||||||
my $what = lc($whatlist[0]);
|
my $what = lc($whatlist[0]);
|
||||||
$what =~ s/[^a-z0-9-]//g;
|
$what =~ s/[^a-z0-9-]//g;
|
||||||
|
|
||||||
if($what eq "ftp") {
|
if(($what eq "pop3") ||
|
||||||
if(!$run{'ftp'}) {
|
($what eq "ftp") ||
|
||||||
($pid, $pid2) = runftpserver("", $verbose);
|
($what eq "imap") ||
|
||||||
|
($what eq "smtp")) {
|
||||||
|
if(!$run{$what}) {
|
||||||
|
($pid, $pid2) = runpingpongserver($what, "", $verbose);
|
||||||
if($pid <= 0) {
|
if($pid <= 0) {
|
||||||
return "failed starting FTP server";
|
return "failed starting $what server";
|
||||||
}
|
}
|
||||||
printf ("* pid ftp => %d %d\n", $pid, $pid2) if($verbose);
|
printf ("* pid $what => %d %d\n", $pid, $pid2) if($verbose);
|
||||||
$run{'ftp'}="$pid $pid2";
|
$run{$what}="$pid $pid2";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
elsif($what eq "ftp2") {
|
elsif($what eq "ftp2") {
|
||||||
if(!$run{'ftp2'}) {
|
if(!$run{'ftp2'}) {
|
||||||
($pid, $pid2) = runftpserver("2", $verbose);
|
($pid, $pid2) = runpingpongserver("ftp", "2", $verbose);
|
||||||
if($pid <= 0) {
|
if($pid <= 0) {
|
||||||
return "failed starting FTP2 server";
|
return "failed starting FTP2 server";
|
||||||
}
|
}
|
||||||
@ -2568,7 +2610,7 @@ sub startservers {
|
|||||||
}
|
}
|
||||||
elsif($what eq "ftp-ipv6") {
|
elsif($what eq "ftp-ipv6") {
|
||||||
if(!$run{'ftp-ipv6'}) {
|
if(!$run{'ftp-ipv6'}) {
|
||||||
($pid, $pid2) = runftpserver("", $verbose, "ipv6");
|
($pid, $pid2) = runpingpongserver("ftp", "", $verbose, "ipv6");
|
||||||
if($pid <= 0) {
|
if($pid <= 0) {
|
||||||
return "failed starting FTP-IPv6 server";
|
return "failed starting FTP-IPv6 server";
|
||||||
}
|
}
|
||||||
@ -2609,7 +2651,7 @@ sub startservers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(!$run{'ftp'}) {
|
if(!$run{'ftp'}) {
|
||||||
($pid, $pid2) = runftpserver("", $verbose);
|
($pid, $pid2) = runpingpongserver("ftp", "", $verbose);
|
||||||
if($pid <= 0) {
|
if($pid <= 0) {
|
||||||
return "failed starting FTP server";
|
return "failed starting FTP server";
|
||||||
}
|
}
|
||||||
@ -2939,18 +2981,21 @@ if ($gdbthis) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$HTTPPORT = $base + 0; # HTTP server port
|
$HTTPPORT = $base++; # HTTP server port
|
||||||
$HTTPSPORT = $base + 1; # HTTPS server port
|
$HTTPSPORT = $base++; # HTTPS server port
|
||||||
$FTPPORT = $base + 2; # FTP server port
|
$FTPPORT = $base++; # FTP server port
|
||||||
$FTPSPORT = $base + 3; # FTPS server port
|
$FTPSPORT = $base++; # FTPS server port
|
||||||
$HTTP6PORT = $base + 4; # HTTP IPv6 server port (different IP protocol
|
$HTTP6PORT = $base++; # HTTP IPv6 server port (different IP protocol
|
||||||
# but we follow the same port scheme anyway)
|
# but we follow the same port scheme anyway)
|
||||||
$FTP2PORT = $base + 5; # FTP server 2 port
|
$FTP2PORT = $base++; # FTP server 2 port
|
||||||
$FTP6PORT = $base + 6; # FTP IPv6 port
|
$FTP6PORT = $base++; # FTP IPv6 port
|
||||||
$TFTPPORT = $base + 7; # TFTP (UDP) port
|
$TFTPPORT = $base++; # TFTP (UDP) port
|
||||||
$TFTP6PORT = $base + 8; # TFTP IPv6 (UDP) port
|
$TFTP6PORT = $base++; # TFTP IPv6 (UDP) port
|
||||||
$SSHPORT = $base + 9; # SSH (SCP/SFTP) port
|
$SSHPORT = $base++; # SSH (SCP/SFTP) port
|
||||||
$SOCKSPORT = $base + 10; # SOCKS port
|
$SOCKSPORT = $base++; # SOCKS port
|
||||||
|
$POP3PORT = $base++;
|
||||||
|
$IMAPPORT = $base++;
|
||||||
|
$SMTPPORT = $base++;
|
||||||
|
|
||||||
#######################################################################
|
#######################################################################
|
||||||
# clear and create logging directory:
|
# clear and create logging directory:
|
||||||
|
Loading…
Reference in New Issue
Block a user