diff options
Diffstat (limited to 'plugins/FTPFileYM/curl/lib/pop3.c')
-rw-r--r-- | plugins/FTPFileYM/curl/lib/pop3.c | 1094 |
1 files changed, 657 insertions, 437 deletions
diff --git a/plugins/FTPFileYM/curl/lib/pop3.c b/plugins/FTPFileYM/curl/lib/pop3.c index f0c6155c02..a771933840 100644 --- a/plugins/FTPFileYM/curl/lib/pop3.c +++ b/plugins/FTPFileYM/curl/lib/pop3.c @@ -27,6 +27,8 @@ * RFC2831 DIGEST-MD5 authentication * RFC4422 Simple Authentication and Security Layer (SASL) * RFC4616 PLAIN authentication + * RFC5034 POP3 SASL Authentication Mechanism + * RFC6749 OAuth 2.0 Authorization Framework * ***************************************************************************/ @@ -89,8 +91,6 @@ #include "memdebug.h" /* Local API functions */ -static CURLcode pop3_parse_url_path(struct connectdata *conn); -static CURLcode pop3_parse_custom_request(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 status, @@ -102,7 +102,9 @@ 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); -static CURLcode pop3_state_upgrade_tls(struct connectdata *conn); +static CURLcode pop3_parse_url_options(struct connectdata *conn); +static CURLcode pop3_parse_url_path(struct connectdata *conn); +static CURLcode pop3_parse_custom_request(struct connectdata *conn); /* * POP3 protocol handler. @@ -162,7 +164,7 @@ const struct Curl_handler Curl_handler_pop3s = { static const struct Curl_handler Curl_handler_pop3_proxy = { "POP3", /* scheme */ - ZERO_NULL, /* setup_connection */ + Curl_http_setup_conn, /* setup_connection */ Curl_http, /* do_it */ Curl_http_done, /* done */ ZERO_NULL, /* do_more */ @@ -187,7 +189,7 @@ static const struct Curl_handler Curl_handler_pop3_proxy = { static const struct Curl_handler Curl_handler_pop3s_proxy = { "POP3S", /* scheme */ - ZERO_NULL, /* setup_connection */ + Curl_http_setup_conn, /* setup_connection */ Curl_http, /* do_it */ Curl_http_done, /* done */ ZERO_NULL, /* do_more */ @@ -207,15 +209,27 @@ static const struct Curl_handler Curl_handler_pop3s_proxy = { #endif #endif -/* Function that checks for an ending pop3 status code at the start of the - given string, but also detects the APOP timestamp from the server greeting - as well as the supported authentication types and allowed SASL mechanisms - from the CAPA response. */ -static int pop3_endofresp(struct pingpong *pp, int *resp) +#ifdef USE_SSL +static void pop3_to_pop3s(struct connectdata *conn) +{ + conn->handler = &Curl_handler_pop3s; +} +#else +#define pop3_to_pop3s(x) Curl_nop_stmt +#endif + +/*********************************************************************** + * + * pop3_endofresp() + * + * Checks for an ending POP3 status code at the start of the given string, but + * also detects the APOP timestamp from the server greeting and various + * capabilities from the CAPA response including the supported authentication + * types and allowed SASL mechanisms. + */ +static bool pop3_endofresp(struct connectdata *conn, char *line, size_t len, + int *resp) { - char *line = pp->linestart_resp; - size_t len = strlen(pp->linestart_resp); - struct connectdata *conn = pp->conn; struct pop3_conn *pop3c = &conn->proto.pop3c; size_t wordlen; size_t i; @@ -224,7 +238,7 @@ static int pop3_endofresp(struct pingpong *pp, int *resp) if(len >= 4 && !memcmp("-ERR", line, 4)) { *resp = '-'; - return FALSE; + return TRUE; } /* Are we processing servergreet responses? */ @@ -252,90 +266,95 @@ static int pop3_endofresp(struct pingpong *pp, int *resp) } /* Are we processing CAPA command responses? */ else if(pop3c->state == POP3_CAPA) { - - /* Do we have the terminating character? */ + /* Do we have the terminating line? */ if(len >= 1 && !memcmp(line, ".", 1)) { *resp = '+'; return TRUE; } + /* Does the server support the STLS capability? */ + if(len >= 4 && !memcmp(line, "STLS", 4)) + pop3c->tls_supported = TRUE; + /* Does the server support clear text authentication? */ - if(len >= 4 && !memcmp(line, "USER", 4)) { + else if(len >= 4 && !memcmp(line, "USER", 4)) pop3c->authtypes |= POP3_TYPE_CLEARTEXT; - return FALSE; - } /* Does the server support APOP authentication? */ - if(len >= 4 && !memcmp(line, "APOP", 4)) { + else if(len >= 4 && !memcmp(line, "APOP", 4)) pop3c->authtypes |= POP3_TYPE_APOP; - return FALSE; - } /* Does the server support SASL based authentication? */ - if(len < 4 || memcmp(line, "SASL", 4)) - return FALSE; + else if(len >= 5 && !memcmp(line, "SASL ", 5)) { + pop3c->authtypes |= POP3_TYPE_SASL; - pop3c->authtypes |= POP3_TYPE_SASL; + /* Advance past the SASL keyword */ + line += 5; + len -= 5; - /* Advance past the SASL keyword */ - line += 4; - len -= 4; + /* Loop through the data line */ + for(;;) { + while(len && + (*line == ' ' || *line == '\t' || + *line == '\r' || *line == '\n')) { - /* Loop through the data line */ - for(;;) { - while(len && - (*line == ' ' || *line == '\t' || - *line == '\r' || *line == '\n')) { + line++; + len--; + } - if(*line == '\n') - return FALSE; + if(!len) + break; - line++; - len--; + /* Extract the word */ + for(wordlen = 0; wordlen < len && line[wordlen] != ' ' && + line[wordlen] != '\t' && line[wordlen] != '\r' && + line[wordlen] != '\n';) + wordlen++; + + /* Test the word for a matching authentication mechanism */ + if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_LOGIN)) + pop3c->authmechs |= SASL_MECH_LOGIN; + else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_PLAIN)) + pop3c->authmechs |= SASL_MECH_PLAIN; + else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_CRAM_MD5)) + pop3c->authmechs |= SASL_MECH_CRAM_MD5; + else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_DIGEST_MD5)) + pop3c->authmechs |= SASL_MECH_DIGEST_MD5; + else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_GSSAPI)) + pop3c->authmechs |= SASL_MECH_GSSAPI; + else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_EXTERNAL)) + pop3c->authmechs |= SASL_MECH_EXTERNAL; + else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_NTLM)) + pop3c->authmechs |= SASL_MECH_NTLM; + else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_XOAUTH2)) + pop3c->authmechs |= SASL_MECH_XOAUTH2; + + line += wordlen; + len -= wordlen; } - - if(!len) - break; - - /* Extract the word */ - for(wordlen = 0; wordlen < len && line[wordlen] != ' ' && - line[wordlen] != '\t' && line[wordlen] != '\r' && - line[wordlen] != '\n';) - wordlen++; - - /* Test the word for a matching authentication mechanism */ - if(wordlen == 5 && !memcmp(line, "LOGIN", 5)) - pop3c->authmechs |= SASL_MECH_LOGIN; - else if(wordlen == 5 && !memcmp(line, "PLAIN", 5)) - pop3c->authmechs |= SASL_MECH_PLAIN; - else if(wordlen == 8 && !memcmp(line, "CRAM-MD5", 8)) - pop3c->authmechs |= SASL_MECH_CRAM_MD5; - else if(wordlen == 10 && !memcmp(line, "DIGEST-MD5", 10)) - pop3c->authmechs |= SASL_MECH_DIGEST_MD5; - else if(wordlen == 6 && !memcmp(line, "GSSAPI", 6)) - pop3c->authmechs |= SASL_MECH_GSSAPI; - else if(wordlen == 8 && !memcmp(line, "EXTERNAL", 8)) - pop3c->authmechs |= SASL_MECH_EXTERNAL; - else if(wordlen == 4 && !memcmp(line, "NTLM", 4)) - pop3c->authmechs |= SASL_MECH_NTLM; - - line += wordlen; - len -= wordlen; } + + return FALSE; } - if((len < 1 || memcmp("+", line, 1)) && - (len < 3 || memcmp("+OK", line, 3))) - return FALSE; /* Nothing for us */ + /* Do we have a command or continuation response? */ + if((len >= 3 && !memcmp("+OK", line, 3)) || + (len >= 1 && !memcmp("+", line, 1))) { + *resp = '+'; - /* Otherwise it's a positive response */ - *resp = '+'; + return TRUE; + } - return TRUE; + return FALSE; /* Nothing for us */ } -/* This is the ONLY way to change POP3 state! */ +/*********************************************************************** + * + * state() + * + * This is the ONLY way to change POP3 state! + */ static void state(struct connectdata *conn, pop3state newstate) { struct pop3_conn *pop3c = &conn->proto.pop3c; @@ -344,9 +363,9 @@ static void state(struct connectdata *conn, pop3state newstate) static const char * const names[] = { "STOP", "SERVERGREET", + "CAPA", "STARTTLS", "UPGRADETLS", - "CAPA", "AUTH_PLAIN", "AUTH_LOGIN", "AUTH_LOGIN_PASSWD", @@ -355,7 +374,8 @@ static void state(struct connectdata *conn, pop3state newstate) "AUTH_DIGESTMD5_RESP", "AUTH_NTLM", "AUTH_NTLM_TYPE2MSG", - "AUTH", + "AUTH_XOAUTH2", + "AUTH_FINAL", "APOP", "USER", "PASS", @@ -366,57 +386,118 @@ static void state(struct connectdata *conn, pop3state newstate) if(pop3c->state != newstate) infof(conn->data, "POP3 %p state change from %s to %s\n", - pop3c, names[pop3c->state], names[newstate]); + (void *)pop3c, names[pop3c->state], names[newstate]); #endif pop3c->state = newstate; } -static CURLcode pop3_state_capa(struct connectdata *conn) +/*********************************************************************** + * + * pop3_perform_capa() + * + * Sends the CAPA command in order to obtain a list of server side supported + * capabilities. + */ +static CURLcode pop3_perform_capa(struct connectdata *conn) { CURLcode result = CURLE_OK; struct pop3_conn *pop3c = &conn->proto.pop3c; pop3c->authmechs = 0; /* No known authentication mechanisms yet */ pop3c->authused = 0; /* Clear the authentication mechanism used */ + pop3c->tls_supported = FALSE; /* Clear the TLS capability */ - /* Check we have a username and password to authenticate with and end the - connect phase if we don't */ - if(!conn->bits.user_passwd) { - state(conn, POP3_STOP); + /* Send the CAPA command */ + result = Curl_pp_sendf(&pop3c->pp, "%s", "CAPA"); - return result; - } + if(!result) + state(conn, POP3_CAPA); - /* Send the CAPA command */ - result = Curl_pp_sendf(&pop3c->pp, "CAPA"); + return result; +} - if(result) - return result; +/*********************************************************************** + * + * pop3_perform_starttls() + * + * Sends the STLS command to start the upgrade to TLS. + */ +static CURLcode pop3_perform_starttls(struct connectdata *conn) +{ + CURLcode result = CURLE_OK; - state(conn, POP3_CAPA); + /* Send the STLS command */ + result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", "STLS"); - return CURLE_OK; + if(!result) + state(conn, POP3_STARTTLS); + + return result; } -static CURLcode pop3_state_user(struct connectdata *conn) +/*********************************************************************** + * + * pop3_perform_upgrade_tls() + * + * Performs the upgrade to TLS. + */ +static CURLcode pop3_perform_upgrade_tls(struct connectdata *conn) { - CURLcode result; - struct FTP *pop3 = conn->data->state.proto.pop3; + CURLcode result = CURLE_OK; + struct pop3_conn *pop3c = &conn->proto.pop3c; + + /* Start the SSL connection */ + result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &pop3c->ssldone); + + if(!result) { + if(pop3c->state != POP3_UPGRADETLS) + state(conn, POP3_UPGRADETLS); + + if(pop3c->ssldone) { + pop3_to_pop3s(conn); + result = pop3_perform_capa(conn); + } + } + + return result; +} + +/*********************************************************************** + * + * pop3_perform_user() + * + * Sends a clear text USER command to authenticate with. + */ +static CURLcode pop3_perform_user(struct connectdata *conn) +{ + CURLcode result = CURLE_OK; + + /* Check we have a username and password to authenticate with and end the + connect phase if we don't */ + if(!conn->bits.user_passwd) { + state(conn, POP3_STOP); - /* Send the USER command */ - result = Curl_pp_sendf(&conn->proto.pop3c.pp, "USER %s", - pop3->user ? pop3->user : ""); - if(result) return result; + } - state(conn, POP3_USER); + /* Send the USER command */ + result = Curl_pp_sendf(&conn->proto.pop3c.pp, "USER %s", + conn->user ? conn->user : ""); + if(!result) + state(conn, POP3_USER); - return CURLE_OK; + return result; } #ifndef CURL_DISABLE_CRYPTO_AUTH -static CURLcode pop3_state_apop(struct connectdata *conn) +/*********************************************************************** + * + * pop3_perform_apop() + * + * Sends an APOP command to authenticate with. + */ +static CURLcode pop3_perform_apop(struct connectdata *conn) { CURLcode result = CURLE_OK; struct pop3_conn *pop3c = &conn->proto.pop3c; @@ -425,6 +506,15 @@ static CURLcode pop3_state_apop(struct connectdata *conn) unsigned char digest[MD5_DIGEST_LEN]; char secret[2 * MD5_DIGEST_LEN + 1]; + /* Check we have a username and password to authenticate with and end the + connect phase if we don't */ + if(!conn->bits.user_passwd) { + state(conn, POP3_STOP); + + return result; + } + + /* Create the digest */ ctxt = Curl_MD5_init(Curl_DIGEST_MD5); if(!ctxt) return CURLE_OUT_OF_MEMORY; @@ -451,88 +541,203 @@ static CURLcode pop3_state_apop(struct connectdata *conn) } #endif -static CURLcode pop3_authenticate(struct connectdata *conn) +/*********************************************************************** + * + * pop3_perform_authenticate() + * + * Sends an AUTH command allowing the client to login with the appropriate + * SASL authentication mechanism. + * + * Additionally, the function will perform fallback to APOP and USER commands + * should a common mechanism not be available between the client and server. + */ +static CURLcode pop3_perform_authenticate(struct connectdata *conn) { CURLcode result = CURLE_OK; + struct SessionHandle *data = conn->data; struct pop3_conn *pop3c = &conn->proto.pop3c; const char *mech = NULL; - pop3state authstate = POP3_STOP; + char *initresp = NULL; + size_t len = 0; + pop3state state1 = POP3_STOP; + pop3state state2 = POP3_STOP; + + /* Check we have a username and password to authenticate with and end the + connect phase if we don't */ + if(!conn->bits.user_passwd) { + state(conn, POP3_STOP); + + return result; + } /* Calculate the supported authentication mechanism by decreasing order of security */ if(pop3c->authtypes & POP3_TYPE_SASL) { #ifndef CURL_DISABLE_CRYPTO_AUTH - if(pop3c->authmechs & SASL_MECH_DIGEST_MD5) { - mech = "DIGEST-MD5"; - authstate = POP3_AUTH_DIGESTMD5; + if((pop3c->authmechs & SASL_MECH_DIGEST_MD5) && + (pop3c->prefmech & SASL_MECH_DIGEST_MD5)) { + mech = SASL_MECH_STRING_DIGEST_MD5; + state1 = POP3_AUTH_DIGESTMD5; pop3c->authused = SASL_MECH_DIGEST_MD5; } - else if(pop3c->authmechs & SASL_MECH_CRAM_MD5) { - mech = "CRAM-MD5"; - authstate = POP3_AUTH_CRAMMD5; + else if((pop3c->authmechs & SASL_MECH_CRAM_MD5) && + (pop3c->prefmech & SASL_MECH_CRAM_MD5)) { + mech = SASL_MECH_STRING_CRAM_MD5; + state1 = POP3_AUTH_CRAMMD5; pop3c->authused = SASL_MECH_CRAM_MD5; } else #endif #ifdef USE_NTLM - if(pop3c->authmechs & SASL_MECH_NTLM) { - mech = "NTLM"; - authstate = POP3_AUTH_NTLM; + if((pop3c->authmechs & SASL_MECH_NTLM) && + (pop3c->prefmech & SASL_MECH_NTLM)) { + mech = SASL_MECH_STRING_NTLM; + state1 = POP3_AUTH_NTLM; + state2 = POP3_AUTH_NTLM_TYPE2MSG; pop3c->authused = SASL_MECH_NTLM; + + if(data->set.sasl_ir) + result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd, + &conn->ntlm, + &initresp, &len); } else #endif - if(pop3c->authmechs & SASL_MECH_LOGIN) { - mech = "LOGIN"; - authstate = POP3_AUTH_LOGIN; + if(((pop3c->authmechs & SASL_MECH_XOAUTH2) && + (pop3c->prefmech & SASL_MECH_XOAUTH2) && + (pop3c->prefmech != SASL_AUTH_ANY)) || conn->xoauth2_bearer) { + mech = SASL_MECH_STRING_XOAUTH2; + state1 = POP3_AUTH_XOAUTH2; + state2 = POP3_AUTH_FINAL; + pop3c->authused = SASL_MECH_XOAUTH2; + + if(data->set.sasl_ir) + result = Curl_sasl_create_xoauth2_message(conn->data, conn->user, + conn->xoauth2_bearer, + &initresp, &len); + } + else if((pop3c->authmechs & SASL_MECH_LOGIN) && + (pop3c->prefmech & SASL_MECH_LOGIN)) { + mech = SASL_MECH_STRING_LOGIN; + state1 = POP3_AUTH_LOGIN; + state2 = POP3_AUTH_LOGIN_PASSWD; pop3c->authused = SASL_MECH_LOGIN; + + if(data->set.sasl_ir) + result = Curl_sasl_create_login_message(conn->data, conn->user, + &initresp, &len); } - else if(pop3c->authmechs & SASL_MECH_PLAIN) { - mech = "PLAIN"; - authstate = POP3_AUTH_PLAIN; + else if((pop3c->authmechs & SASL_MECH_PLAIN) && + (pop3c->prefmech & SASL_MECH_PLAIN)) { + mech = SASL_MECH_STRING_PLAIN; + state1 = POP3_AUTH_PLAIN; + state2 = POP3_AUTH_FINAL; pop3c->authused = SASL_MECH_PLAIN; + + if(data->set.sasl_ir) + result = Curl_sasl_create_plain_message(conn->data, conn->user, + conn->passwd, &initresp, + &len); } } - if(mech) { - /* Perform SASL based authentication */ - result = Curl_pp_sendf(&pop3c->pp, "AUTH %s", mech); + if(!result) { + if(mech && (pop3c->preftype & POP3_TYPE_SASL)) { + /* Perform SASL based authentication */ + if(initresp && + 8 + strlen(mech) + len <= 255) { /* AUTH <mech> ...<crlf> */ + result = Curl_pp_sendf(&pop3c->pp, "AUTH %s %s", mech, initresp); - if(!result) - state(conn, authstate); - } + if(!result) + state(conn, state2); + } + else { + result = Curl_pp_sendf(&pop3c->pp, "AUTH %s", mech); + + if(!result) + state(conn, state1); + } + + Curl_safefree(initresp); + } #ifndef CURL_DISABLE_CRYPTO_AUTH - else if(pop3c->authtypes & POP3_TYPE_APOP) - /* Perform APOP authentication */ - result = pop3_state_apop(conn); + else if((pop3c->authtypes & POP3_TYPE_APOP) && + (pop3c->preftype & POP3_TYPE_APOP)) + /* Perform APOP authentication */ + result = pop3_perform_apop(conn); #endif - else if(pop3c->authtypes & POP3_TYPE_CLEARTEXT) - /* Perform clear text authentication */ - result = pop3_state_user(conn); - else { - /* Other mechanisms not supported */ - infof(conn->data, "No known authentication mechanisms supported!\n"); - result = CURLE_LOGIN_DENIED; + else if((pop3c->authtypes & POP3_TYPE_CLEARTEXT) && + (pop3c->preftype & POP3_TYPE_CLEARTEXT)) + /* Perform clear text authentication */ + result = pop3_perform_user(conn); + else { + /* Other mechanisms not supported */ + infof(conn->data, "No known authentication mechanisms supported!\n"); + result = CURLE_LOGIN_DENIED; + } } return result; } -/* For the POP3 "protocol connect" and "doing" phases only */ -static int pop3_getsock(struct connectdata *conn, curl_socket_t *socks, - int numsocks) +/*********************************************************************** + * + * pop3_perform_command() + * + * Sends a POP3 based command. + */ +static CURLcode pop3_perform_command(struct connectdata *conn) { - return Curl_pp_getsock(&conn->proto.pop3c.pp, socks, numsocks); + CURLcode result = CURLE_OK; + struct SessionHandle *data = conn->data; + struct POP3 *pop3 = data->req.protop; + const char *command = NULL; + + /* Calculate the default command */ + if(pop3->id[0] == '\0' || conn->data->set.ftp_list_only) { + command = "LIST"; + + if(pop3->id[0] != '\0') + /* Message specific LIST so skip the BODY transfer */ + pop3->transfer = FTPTRANSFER_INFO; + } + else + command = "RETR"; + + /* Send the command */ + if(pop3->id[0] != '\0') + result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s %s", + (pop3->custom && pop3->custom[0] != '\0' ? + pop3->custom : command), pop3->id); + else + result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", + (pop3->custom && pop3->custom[0] != '\0' ? + pop3->custom : command)); + + if(!result) + state(conn, POP3_COMMAND); + + return result; } -#ifdef USE_SSL -static void pop3_to_pop3s(struct connectdata *conn) +/*********************************************************************** + * + * pop3_perform_quit() + * + * Performs the quit action prior to sclose() be called. + */ +static CURLcode pop3_perform_quit(struct connectdata *conn) { - conn->handler = &Curl_handler_pop3s; + CURLcode result = CURLE_OK; + + /* Send the QUIT command */ + result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", "QUIT"); + + if(!result) + state(conn, POP3_QUIT); + + return result; } -#else -#define pop3_to_pop3s(x) Curl_nop_stmt -#endif /* For the initial server greeting */ static CURLcode pop3_state_servergreet_resp(struct connectdata *conn, @@ -541,89 +746,75 @@ static CURLcode pop3_state_servergreet_resp(struct connectdata *conn, { CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; - struct pop3_conn *pop3c = &conn->proto.pop3c; (void)instate; /* no use for this yet */ if(pop3code != '+') { failf(data, "Got unexpected pop3-server response"); - return CURLE_FTP_WEIRD_SERVER_REPLY; - } - - if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) { - /* We don't have a SSL/TLS connection yet, but SSL is requested. Switch - to TLS connection now */ - result = Curl_pp_sendf(&pop3c->pp, "STLS"); - if(!result) - state(conn, POP3_STARTTLS); + result = CURLE_FTP_WEIRD_SERVER_REPLY; } else - result = pop3_state_capa(conn); + result = pop3_perform_capa(conn); return result; } -/* For STARTTLS responses */ -static CURLcode pop3_state_starttls_resp(struct connectdata *conn, - int pop3code, - pop3state instate) +/* For CAPA responses */ +static CURLcode pop3_state_capa_resp(struct connectdata *conn, int pop3code, + pop3state instate) { CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; + struct pop3_conn *pop3c = &conn->proto.pop3c; (void)instate; /* no use for this yet */ - if(pop3code != '+') { - if(data->set.use_ssl != CURLUSESSL_TRY) { - failf(data, "STARTTLS denied. %c", pop3code); + if(pop3code != '+') + result = pop3_perform_user(conn); + else if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) { + /* We don't have a SSL/TLS connection yet, but SSL is requested */ + if(pop3c->tls_supported) + /* Switch to TLS connection now */ + result = pop3_perform_starttls(conn); + else if(data->set.use_ssl == CURLUSESSL_TRY) + /* Fallback and carry on with authentication */ + result = pop3_perform_authenticate(conn); + else { + failf(data, "STLS not supported."); result = CURLE_USE_SSL_FAILED; } - else - result = pop3_state_capa(conn); } else - result = pop3_state_upgrade_tls(conn); + result = pop3_perform_authenticate(conn); return result; } -static CURLcode pop3_state_upgrade_tls(struct connectdata *conn) -{ - struct pop3_conn *pop3c = &conn->proto.pop3c; - CURLcode result; - - result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &pop3c->ssldone); - - if(!result) { - if(pop3c->state != POP3_UPGRADETLS) - state(conn, POP3_UPGRADETLS); - - if(pop3c->ssldone) { - pop3_to_pop3s(conn); - result = pop3_state_capa(conn); - } - } - - return result; -} - -/* For CAPA responses */ -static CURLcode pop3_state_capa_resp(struct connectdata *conn, int pop3code, - pop3state instate) +/* 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 == '+') - result = pop3_authenticate(conn); + if(pop3code != '+') { + if(data->set.use_ssl != CURLUSESSL_TRY) { + failf(data, "STARTTLS denied. %c", pop3code); + result = CURLE_USE_SSL_FAILED; + } + else + result = pop3_perform_authenticate(conn); + } else - result = pop3_state_user(conn); + result = pop3_perform_upgrade_tls(conn); return result; } -/* For AUTH PLAIN responses */ +/* For AUTH PLAIN (without initial response) responses */ static CURLcode pop3_state_auth_plain_resp(struct connectdata *conn, int pop3code, pop3state instate) @@ -650,7 +841,7 @@ static CURLcode pop3_state_auth_plain_resp(struct connectdata *conn, result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", plainauth); if(!result) - state(conn, POP3_AUTH); + state(conn, POP3_AUTH_FINAL); } Curl_safefree(plainauth); @@ -660,7 +851,7 @@ static CURLcode pop3_state_auth_plain_resp(struct connectdata *conn, return result; } -/* For AUTH LOGIN responses */ +/* For AUTH LOGIN (without initial response) responses */ static CURLcode pop3_state_auth_login_resp(struct connectdata *conn, int pop3code, pop3state instate) @@ -724,7 +915,7 @@ static CURLcode pop3_state_auth_login_password_resp(struct connectdata *conn, result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", authpasswd); if(!result) - state(conn, POP3_AUTH); + state(conn, POP3_AUTH_FINAL); } Curl_safefree(authpasswd); @@ -779,7 +970,7 @@ static CURLcode pop3_state_auth_cram_resp(struct connectdata *conn, result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", rplyb64); if(!result) - state(conn, POP3_AUTH); + state(conn, POP3_AUTH_FINAL); } Curl_safefree(rplyb64); @@ -846,10 +1037,10 @@ static CURLcode pop3_state_auth_digest_resp_resp(struct connectdata *conn, } else { /* Send an empty response */ - result = Curl_pp_sendf(&conn->proto.pop3c.pp, ""); + result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", ""); if(!result) - state(conn, POP3_AUTH); + state(conn, POP3_AUTH_FINAL); } return result; @@ -857,7 +1048,7 @@ static CURLcode pop3_state_auth_digest_resp_resp(struct connectdata *conn, #endif #ifdef USE_NTLM -/* For AUTH NTLM responses */ +/* For AUTH NTLM (without initial response) responses */ static CURLcode pop3_state_auth_ntlm_resp(struct connectdata *conn, int pop3code, pop3state instate) @@ -925,7 +1116,7 @@ static CURLcode pop3_state_auth_ntlm_type2msg_resp(struct connectdata *conn, result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", type3msg); if(!result) - state(conn, POP3_AUTH); + state(conn, POP3_AUTH_FINAL); } Curl_safefree(type3msg); @@ -936,6 +1127,43 @@ static CURLcode pop3_state_auth_ntlm_type2msg_resp(struct connectdata *conn, } #endif +/* For AUTH XOAUTH2 (without initial response) responses */ +static CURLcode pop3_state_auth_xoauth2_resp(struct connectdata *conn, + int pop3code, pop3state instate) +{ + CURLcode result = CURLE_OK; + struct SessionHandle *data = conn->data; + size_t len = 0; + char *xoauth = NULL; + + (void)instate; /* no use for this yet */ + + if(pop3code != '+') { + failf(data, "Access denied: %d", pop3code); + result = CURLE_LOGIN_DENIED; + } + else { + /* Create the authorisation message */ + result = Curl_sasl_create_xoauth2_message(conn->data, conn->user, + conn->xoauth2_bearer, + &xoauth, &len); + + /* Send the message */ + if(!result) { + if(xoauth) { + result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", xoauth); + + if(!result) + state(conn, POP3_AUTH_FINAL); + } + + Curl_safefree(xoauth); + } + } + + return result; +} + /* For final responses to the AUTH sequence */ static CURLcode pop3_state_auth_final_resp(struct connectdata *conn, int pop3code, @@ -950,14 +1178,15 @@ static CURLcode pop3_state_auth_final_resp(struct connectdata *conn, failf(data, "Authentication failed: %d", pop3code); result = CURLE_LOGIN_DENIED; } - - /* End of connect phase */ - state(conn, POP3_STOP); + else + /* End of connect phase */ + state(conn, POP3_STOP); return result; } #ifndef CURL_DISABLE_CRYPTO_AUTH +/* For APOP responses */ static CURLcode pop3_state_apop_resp(struct connectdata *conn, int pop3code, pop3state instate) { @@ -970,9 +1199,9 @@ static CURLcode pop3_state_apop_resp(struct connectdata *conn, int pop3code, failf(data, "Authentication failed: %d", pop3code); result = CURLE_LOGIN_DENIED; } - - /* End of connect phase */ - state(conn, POP3_STOP); + else + /* End of connect phase */ + state(conn, POP3_STOP); return result; } @@ -984,7 +1213,6 @@ static CURLcode pop3_state_user_resp(struct connectdata *conn, int pop3code, { CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; - struct FTP *pop3 = data->state.proto.pop3; (void)instate; /* no use for this yet */ @@ -995,11 +1223,9 @@ static CURLcode pop3_state_user_resp(struct connectdata *conn, int pop3code, else /* Send the PASS command */ result = Curl_pp_sendf(&conn->proto.pop3c.pp, "PASS %s", - pop3->passwd ? pop3->passwd : ""); - if(result) - return result; - - state(conn, POP3_PASS); + conn->passwd ? conn->passwd : ""); + if(!result) + state(conn, POP3_PASS); return result; } @@ -1017,47 +1243,9 @@ static CURLcode pop3_state_pass_resp(struct connectdata *conn, int pop3code, failf(data, "Access denied. %c", pop3code); result = CURLE_LOGIN_DENIED; } - - /* End of connect phase */ - state(conn, POP3_STOP); - - return result; -} - -/* Start the DO phase for the command */ -static CURLcode pop3_command(struct connectdata *conn) -{ - CURLcode result = CURLE_OK; - struct pop3_conn *pop3c = &conn->proto.pop3c; - const char *command = NULL; - - /* Calculate the default command */ - if(pop3c->mailbox[0] == '\0' || conn->data->set.ftp_list_only) { - command = "LIST"; - - if(pop3c->mailbox[0] != '\0') { - /* Message specific LIST so skip the BODY transfer */ - struct FTP *pop3 = conn->data->state.proto.pop3; - pop3->transfer = FTPTRANSFER_INFO; - } - } else - command = "RETR"; - - /* Send the command */ - if(pop3c->mailbox[0] != '\0') - result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s %s", - (pop3c->custom && pop3c->custom[0] != '\0' ? - pop3c->custom : command), pop3c->mailbox); - else - result = Curl_pp_sendf(&conn->proto.pop3c.pp, - (pop3c->custom && pop3c->custom[0] != '\0' ? - pop3c->custom : command)); - - if(result) - return result; - - state(conn, POP3_COMMAND); + /* End of connect phase */ + state(conn, POP3_STOP); return result; } @@ -1069,7 +1257,7 @@ static CURLcode pop3_state_command_resp(struct connectdata *conn, { CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; - struct FTP *pop3 = data->state.proto.pop3; + struct POP3 *pop3 = data->req.protop; struct pop3_conn *pop3c = &conn->proto.pop3c; struct pingpong *pp = &pop3c->pp; @@ -1090,29 +1278,30 @@ static CURLcode pop3_state_command_resp(struct connectdata *conn, the strip counter here so that these bytes won't be delivered. */ pop3c->strip = 2; - /* POP3 download */ - Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, pop3->bytecountp, - -1, NULL); /* no upload here */ + if(pop3->transfer == FTPTRANSFER_BODY) { + /* POP3 download */ + Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, NULL, -1, NULL); - if(pp->cache) { - /* The header "cache" contains a bunch of data that is actually body - content so send it as such. Note that there may even be additional - "headers" after the body */ + if(pp->cache) { + /* The header "cache" contains a bunch of data that is actually body + content so send it as such. Note that there may even be additional + "headers" after the body */ - if(!data->set.opt_no_body) { - result = Curl_pop3_write(conn, pp->cache, pp->cache_size); - if(result) - return result; - } + if(!data->set.opt_no_body) { + result = Curl_pop3_write(conn, pp->cache, pp->cache_size); + if(result) + return result; + } - /* Free the cache */ - Curl_safefree(pp->cache); + /* Free the cache */ + Curl_safefree(pp->cache); - /* Reset the cache size */ - pp->cache_size = 0; + /* Reset the cache size */ + pp->cache_size = 0; + } } - /* End of do phase */ + /* End of DO phase */ state(conn, POP3_STOP); return result; @@ -1120,7 +1309,7 @@ static CURLcode pop3_state_command_resp(struct connectdata *conn, static CURLcode pop3_statemach_act(struct connectdata *conn) { - CURLcode result; + CURLcode result = CURLE_OK; curl_socket_t sock = conn->sock[FIRSTSOCKET]; int pop3code; struct pop3_conn *pop3c = &conn->proto.pop3c; @@ -1129,7 +1318,7 @@ static CURLcode pop3_statemach_act(struct connectdata *conn) /* Busy upgrading the connection; right now all I/O is SSL/TLS, not POP3 */ if(pop3c->state == POP3_UPGRADETLS) - return pop3_state_upgrade_tls(conn); + return pop3_perform_upgrade_tls(conn); /* Flush any data that needs to be sent */ if(pp->sendleft) @@ -1147,14 +1336,14 @@ static CURLcode pop3_statemach_act(struct connectdata *conn) 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_STARTTLS: + result = pop3_state_starttls_resp(conn, pop3code, pop3c->state); + break; + case POP3_AUTH_PLAIN: result = pop3_state_auth_plain_resp(conn, pop3code, pop3c->state); break; @@ -1193,7 +1382,11 @@ static CURLcode pop3_statemach_act(struct connectdata *conn) break; #endif - case POP3_AUTH: + case POP3_AUTH_XOAUTH2: + result = pop3_state_auth_xoauth2_resp(conn, pop3code, pop3c->state); + break; + + case POP3_AUTH_FINAL: result = pop3_state_auth_final_resp(conn, pop3code, pop3c->state); break; @@ -1230,30 +1423,28 @@ static CURLcode pop3_statemach_act(struct connectdata *conn) /* Called repeatedly until done from multi.c */ static CURLcode pop3_multi_statemach(struct connectdata *conn, bool *done) { + CURLcode result = CURLE_OK; struct pop3_conn *pop3c = &conn->proto.pop3c; - CURLcode result; - if((conn->handler->flags & PROTOPT_SSL) && !pop3c->ssldone) + if((conn->handler->flags & PROTOPT_SSL) && !pop3c->ssldone) { result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &pop3c->ssldone); - else - result = Curl_pp_multi_statemach(&pop3c->pp); + if(result || !pop3c->ssldone) + return result; + } + result = Curl_pp_statemach(&pop3c->pp, FALSE); *done = (pop3c->state == POP3_STOP) ? TRUE : FALSE; return result; } -static CURLcode pop3_easy_statemach(struct connectdata *conn) +static CURLcode pop3_block_statemach(struct connectdata *conn) { - struct pop3_conn *pop3c = &conn->proto.pop3c; - struct pingpong *pp = &pop3c->pp; CURLcode result = CURLE_OK; + struct pop3_conn *pop3c = &conn->proto.pop3c; - while(pop3c->state != POP3_STOP) { - result = Curl_pp_easy_statemach(pp); - if(result) - break; - } + while(pop3c->state != POP3_STOP && !result) + result = Curl_pp_statemach(&pop3c->pp, TRUE); return result; } @@ -1262,26 +1453,22 @@ static CURLcode pop3_easy_statemach(struct connectdata *conn) required */ static CURLcode pop3_init(struct connectdata *conn) { + CURLcode result = CURLE_OK; 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; - } + struct POP3 *pop3; - /* Get some initial data into the pop3 struct */ - pop3->bytecountp = &data->req.bytecount; + pop3 = data->req.protop = calloc(sizeof(struct POP3), 1); + if(!pop3) + result = CURLE_OUT_OF_MEMORY; - /* 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 result; +} - 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); } /*********************************************************************** @@ -1292,44 +1479,37 @@ static CURLcode pop3_init(struct connectdata *conn) * 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. + * phase is done when this function returns, or FALSE if not. */ static CURLcode pop3_connect(struct connectdata *conn, bool *done) { - CURLcode result; + CURLcode result = CURLE_OK; 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 */ + /* We always support persistent connections in POP3 */ conn->bits.close = FALSE; - pp->response_time = RESP_TIMEOUT; /* set default response time-out */ + /* Set the default response time-out */ + pp->response_time = RESP_TIMEOUT; pp->statemach_act = pop3_statemach_act; pp->endofresp = pop3_endofresp; pp->conn = conn; - if(conn->handler->flags & PROTOPT_SSL) { - /* POP3S is simply pop3 with SSL for the control channel */ - /* so perform the SSL initialization for this socket */ - result = Curl_ssl_connect(conn, FIRSTSOCKET); - if(result) - return result; - } + /* Set the default preferred authentication type and mechanism */ + pop3c->preftype = POP3_TYPE_ANY; + pop3c->prefmech = SASL_AUTH_ANY; - /* Initialise the response reader stuff */ + /* Initialise the pingpong layer */ Curl_pp_init(pp); + /* Parse the URL options */ + result = pop3_parse_url_options(conn); + if(result) + return result; + /* Start off waiting for the server greeting response */ state(conn, POP3_SERVERGREET); @@ -1350,19 +1530,17 @@ static CURLcode pop3_connect(struct connectdata *conn, bool *done) 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; CURLcode result = CURLE_OK; + struct SessionHandle *data = conn->data; + struct POP3 *pop3 = data->req.protop; (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. - */ + /* When the easy handle is removed from the multi interface while libcurl + is still trying to resolve the host name, the POP3 struct is not yet + initialized. However, the removal action calls Curl_done() which in + turn calls this function, so we simply return success. */ return CURLE_OK; if(status) { @@ -1370,11 +1548,11 @@ static CURLcode pop3_done(struct connectdata *conn, CURLcode status, result = status; /* use the already set error code */ } - /* Cleanup our do based variables */ - Curl_safefree(pop3c->mailbox); - Curl_safefree(pop3c->custom); + /* Cleanup our per-request based variables */ + Curl_safefree(pop3->id); + Curl_safefree(pop3->custom); - /* Clear the transfer mode for the next connection */ + /* Clear the transfer mode for the next request */ pop3->transfer = FTPTRANSFER_BODY; return result; @@ -1384,7 +1562,7 @@ static CURLcode pop3_done(struct connectdata *conn, CURLcode status, * * pop3_perform() * - * This is the actual DO function for POP3. Get a file/directory according to + * This is the actual DO function for POP3. Get a message/listing according to * the options previously setup. */ static CURLcode pop3_perform(struct connectdata *conn, bool *connected, @@ -1397,14 +1575,14 @@ static CURLcode pop3_perform(struct connectdata *conn, bool *connected, if(conn->data->set.opt_no_body) { /* Requested no body means no transfer */ - struct FTP *pop3 = conn->data->state.proto.pop3; + struct POP3 *pop3 = conn->data->req.protop; pop3->transfer = FTPTRANSFER_INFO; } *dophase_done = FALSE; /* not done yet */ /* Start the first command in the DO phase */ - result = pop3_command(conn); + result = pop3_perform_command(conn); if(result) return result; @@ -1430,55 +1608,21 @@ static CURLcode pop3_perform(struct connectdata *conn, bool *connected, */ static CURLcode pop3_do(struct connectdata *conn, bool *done) { - CURLcode retcode = CURLE_OK; + CURLcode result = 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; + result = pop3_parse_url_path(conn); + if(result) + return result; /* 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); + result = pop3_parse_custom_request(conn); if(result) return result; - state(conn, POP3_QUIT); - - result = pop3_easy_statemach(conn); + result = pop3_regular_transfer(conn, done); return result; } @@ -1497,12 +1641,13 @@ static CURLcode pop3_disconnect(struct connectdata *conn, /* 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 */ + 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 */ + if(!pop3_perform_quit(conn)) + (void)pop3_block_statemach(conn); /* ignore errors on QUIT */ /* Disconnect from the server */ Curl_pp_disconnect(&pop3c->pp); @@ -1516,48 +1661,12 @@ static CURLcode pop3_disconnect(struct connectdata *conn, 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 initialised 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; - + (void)conn; (void)connected; - if(pop3->transfer != FTPTRANSFER_BODY) - /* no data to transfer */ - Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL); - return CURLE_OK; } @@ -1568,12 +1677,10 @@ static CURLcode pop3_doing(struct connectdata *conn, bool *dophase_done) if(result) DEBUGF(infof(conn->data, "DO phase failed\n")); - else { - if(*dophase_done) { - result = pop3_dophase_done(conn, FALSE /* not connected */); + else if(*dophase_done) { + result = pop3_dophase_done(conn, FALSE /* not connected */); - DEBUGF(infof(conn->data, "DO phase is complete\n")); - } + DEBUGF(infof(conn->data, "DO phase is complete\n")); } return result; @@ -1598,30 +1705,33 @@ static CURLcode pop3_regular_transfer(struct connectdata *conn, /* Make sure size is unknown at this point */ data->req.size = -1; + /* Set the progress data */ Curl_pgrsSetUploadCounter(data, 0); Curl_pgrsSetDownloadCounter(data, 0); Curl_pgrsSetUploadSize(data, 0); Curl_pgrsSetDownloadSize(data, 0); + /* Carry out the perform */ result = pop3_perform(conn, &connected, dophase_done); - if(CURLE_OK == result) { - if(!*dophase_done) - /* The DO phase has not completed yet */ - return CURLE_OK; - + /* Perform post DO phase operations if necessary */ + if(!result && *dophase_done) result = pop3_dophase_done(conn, connected); - } return result; } -static CURLcode pop3_setup_connection(struct connectdata * conn) +static CURLcode pop3_setup_connection(struct connectdata *conn) { struct SessionHandle *data = conn->data; + /* Initialise the POP3 layer */ + CURLcode result = pop3_init(conn); + if(result) + return result; + if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) { - /* Unless we have asked to tunnel pop3 operations through the proxy, we + /* 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) @@ -1635,10 +1745,8 @@ static CURLcode pop3_setup_connection(struct connectdata * conn) #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; + /* set it up as an HTTP connection instead */ + return conn->handler->setup_connection(conn); #else failf(data, "POP3 over http proxy requires HTTP support built-in!"); return CURLE_UNSUPPORTED_PROTOCOL; @@ -1650,8 +1758,120 @@ static CURLcode pop3_setup_connection(struct connectdata * conn) return CURLE_OK; } -/* This function scans the body after the end-of-body and writes everything - until the end is found */ +/*********************************************************************** + * + * pop3_parse_url_options() + * + * Parse the URL login options. + */ +static CURLcode pop3_parse_url_options(struct connectdata *conn) +{ + CURLcode result = CURLE_OK; + struct pop3_conn *pop3c = &conn->proto.pop3c; + const char *options = conn->options; + const char *ptr = options; + + if(options) { + const char *key = ptr; + + while(*ptr && *ptr != '=') + ptr++; + + if(strnequal(key, "AUTH", 4)) { + const char *value = ptr + 1; + + if(strequal(value, "*")) { + pop3c->preftype = POP3_TYPE_ANY; + pop3c->prefmech = SASL_AUTH_ANY; + } + else if(strequal(value, "+APOP")) { + pop3c->preftype = POP3_TYPE_APOP; + pop3c->prefmech = SASL_AUTH_NONE; + } + else if(strequal(value, SASL_MECH_STRING_LOGIN)) { + pop3c->preftype = POP3_TYPE_SASL; + pop3c->prefmech = SASL_MECH_LOGIN; + } + else if(strequal(value, SASL_MECH_STRING_PLAIN)) { + pop3c->preftype = POP3_TYPE_SASL; + pop3c->prefmech = SASL_MECH_PLAIN; + } + else if(strequal(value, SASL_MECH_STRING_CRAM_MD5)) { + pop3c->preftype = POP3_TYPE_SASL; + pop3c->prefmech = SASL_MECH_CRAM_MD5; + } + else if(strequal(value, SASL_MECH_STRING_DIGEST_MD5)) { + pop3c->preftype = POP3_TYPE_SASL; + pop3c->prefmech = SASL_MECH_DIGEST_MD5; + } + else if(strequal(value, SASL_MECH_STRING_GSSAPI)) { + pop3c->preftype = POP3_TYPE_SASL; + pop3c->prefmech = SASL_MECH_GSSAPI; + } + else if(strequal(value, SASL_MECH_STRING_NTLM)) { + pop3c->preftype = POP3_TYPE_SASL; + pop3c->prefmech = SASL_MECH_NTLM; + } + else if(strequal(value, SASL_MECH_STRING_XOAUTH2)) { + pop3c->preftype = POP3_TYPE_SASL; + pop3c->prefmech = SASL_MECH_XOAUTH2; + } + else { + pop3c->preftype = POP3_TYPE_NONE; + pop3c->prefmech = SASL_AUTH_NONE; + } + } + else + result = CURLE_URL_MALFORMAT; + } + + return result; +} + +/*********************************************************************** + * + * 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 initialised in pop3_connect() */ + struct SessionHandle *data = conn->data; + struct POP3 *pop3 = data->req.protop; + const char *path = data->state.path; + + /* URL decode the path for the message ID */ + return Curl_urldecode(data, path, 0, &pop3->id, NULL, TRUE); +} + +/*********************************************************************** + * + * pop3_parse_custom_request() + * + * Parse the custom request. + */ +static CURLcode pop3_parse_custom_request(struct connectdata *conn) +{ + CURLcode result = CURLE_OK; + struct SessionHandle *data = conn->data; + struct POP3 *pop3 = data->req.protop; + const char *custom = data->set.str[STRING_CUSTOMREQUEST]; + + /* URL decode the custom request */ + if(custom) + result = Curl_urldecode(data, custom, 0, &pop3->custom, NULL, TRUE); + + return result; +} + +/*********************************************************************** + * + * Curl_pop3_write() + * + * 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 */ @@ -1668,7 +1888,7 @@ CURLcode Curl_pop3_write(struct connectdata *conn, char *str, size_t nread) 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 */ + over 5 different data chunks. */ for(i = 0; i < nread; i++) { size_t prev = pop3c->eob; |