Newer
Older
/* 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 = (pop3c->state == POP3_STOP) ? TRUE : FALSE;
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 POP3 struct for the current SessionHandle if
required */
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()
*
* This function 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)
{
CURLcode result;
struct pop3_conn *pop3c = &conn->proto.pop3c;
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 persistent 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(conn->handler->flags & PROTOPT_SSL) {
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
/* BLOCKING */
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;
struct pop3_conn *pop3c = &conn->proto.pop3c;
(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 our variables for the next connection */
Curl_safefree(pop3c->mailbox);
Curl_safefree(pop3c->custom);
/* Clear the transfer mode for the 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,
bool *dophase_done)
{
/* this is POP3 and no proxy */
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_command(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[FIRSTSOCKET];
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
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;
/* Parse the URL path */
retcode = pop3_parse_url_path(conn);
if(retcode)
return retcode;
/* Parse the custom request */
retcode = pop3_parse_custom_request(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, bool dead_connection)
{
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! */
if(!dead_connection && pop3c->pp.conn)
(void)pop3_quit(conn); /* ignore errors on the LOGOUT */
Curl_pp_disconnect(&pop3c->pp);
/* Cleanup the sasl module */
Curl_sasl_cleanup(conn, pop3c->authused);
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;
/* URL decode the path and use this mailbox */
return Curl_urldecode(data, path, 0, &pop3c->mailbox, NULL, TRUE);
}
static CURLcode pop3_parse_custom_request(struct connectdata *conn)
{
CURLcode result = CURLE_OK;
struct pop3_conn *pop3c = &conn->proto.pop3c;
struct SessionHandle *data = conn->data;
const char *custom = conn->data->set.str[STRING_CUSTOMREQUEST];
/* URL decode the custom request */
if(custom)
result = Curl_urldecode(data, custom, 0, &pop3c->custom, NULL, TRUE);
return result;
}
/* call this when the DO phase has completed */
static CURLcode pop3_dophase_done(struct connectdata *conn, bool connected)
{
struct FTP *pop3 = conn->data->state.proto.pop3;
if(pop3->transfer != FTPTRANSFER_BODY)
/* no data to transfer */
Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
}
/* 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, dophase_done);
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
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 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 = CURLE_OK;
struct SessionHandle *data = conn->data;
struct SingleRequest *k = &data->req;
struct pop3_conn *pop3c = &conn->proto.pop3c;
bool strip_dot = FALSE;
size_t last = 0;
/* Search through the buffer looking for the end-of-body marker which is
5 bytes (0d 0a 2e 0d 0a). Note that a line starting with a dot matches
the eob so the server will have prefixed it with an extra dot which we
need to strip out. Additionally the marker could of course be spread out
over 5 different data chunks */
for(i = 0; i < nread; i++) {
if(pop3c->eob == 0) {
pop3c->eob++;
if(i) {
/* Write out the body part that didn't match */
result = Curl_client_write(conn, CLIENTWRITE_BODY, &str[last],
i - last);
if(result)
return result;
last = i;
}
}
else if(pop3c->eob == 3)
/* If the character match wasn't at position 0 or 3 then restart the
pattern matching */
pop3c->eob = 1;
if(pop3c->eob == 1 || pop3c->eob == 4)
/* If the character match wasn't at position 1 or 4 then start the
search again */
pop3c->eob = 0;
case 0x2e:
if(pop3c->eob == 2)
pop3c->eob++;
else if(pop3c->eob == 3) {
/* We have an extra dot after the CRLF which we need to strip off */
strip_dot = TRUE;
pop3c->eob = 0;
}
/* If the character match wasn't at position 2 then start the search
again */
pop3c->eob = 0;
pop3c->eob = 0;
/* Did we have a partial match which has subsequently failed? */
if(prev && prev >= pop3c->eob) {
/* Strip can only be non-zero for the very first mismatch after CRLF
and then both prev and strip are equal and nothing will be output
below */
while(prev && pop3c->strip) {
prev--;
pop3c->strip--;
}
if(prev) {
/* If the partial match was the CRLF and dot then only write the CRLF
as the server would have inserted the dot */
result = Curl_client_write(conn, CLIENTWRITE_BODY, (char*)POP3_EOB,
strip_dot ? prev - 1 : prev);
if(result)
return result;
last = i;
strip_dot = FALSE;
}
}
if(pop3c->eob == POP3_EOB_LEN) {
/* We have a full match so the transfer is done, however we must transfer
the CRLF at the start of the EOB as this is considered to be part of the
message as per RFC-1939, sect. 3 */
result = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)POP3_EOB, 2);
k->keepon &= ~KEEP_RECV;
pop3c->eob = 0;
if(pop3c->eob)
/* While EOB is matching nothing should be output */
return CURLE_OK;
if(nread - last) {
result = Curl_client_write(conn, CLIENTWRITE_BODY, &str[last],
nread - last);
return result;
}
#endif /* CURL_DISABLE_POP3 */