Newer
Older
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:
result = pop3_state_servergreet_resp(conn, pop3code, pop3c->state);
break;
case POP3_STARTTLS:
result = pop3_state_starttls_resp(conn, pop3code, pop3c->state);
break;
case POP3_CAPA:
result = pop3_state_capa_resp(conn, pop3code, pop3c->state);
break;
case POP3_AUTH_PLAIN:
result = pop3_state_auth_plain_resp(conn, pop3code, pop3c->state);
break;
case POP3_AUTH_LOGIN:
result = pop3_state_auth_login_resp(conn, pop3code, pop3c->state);
break;
case POP3_AUTH_LOGIN_PASSWD:
result = pop3_state_auth_login_password_resp(conn, pop3code,
pop3c->state);
#ifndef CURL_DISABLE_CRYPTO_AUTH
case POP3_AUTH_CRAMMD5:
result = pop3_state_auth_cram_resp(conn, pop3code, pop3c->state);
break;
case POP3_AUTH_DIGESTMD5:
result = pop3_state_auth_digest_resp(conn, pop3code, pop3c->state);
break;
case POP3_AUTH_DIGESTMD5_RESP:
result = pop3_state_auth_digest_resp_resp(conn, pop3code, pop3c->state);
break;
#ifdef USE_NTLM
case POP3_AUTH_NTLM:
result = pop3_state_auth_ntlm_resp(conn, pop3code, pop3c->state);
break;
case POP3_AUTH_NTLM_TYPE2MSG:
result = pop3_state_auth_ntlm_type2msg_resp(conn, pop3code,
pop3c->state);
case POP3_AUTH_FINAL:
result = pop3_state_auth_final_resp(conn, pop3code, pop3c->state);
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_COMMAND:
result = pop3_state_command_resp(conn, pop3code, pop3c->state);
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 = (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) {
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
/* 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];
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
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);
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
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 */