diff options
Diffstat (limited to 'plugins/FTPFileYM/curl/lib/smtp.c')
-rw-r--r-- | plugins/FTPFileYM/curl/lib/smtp.c | 1072 |
1 files changed, 634 insertions, 438 deletions
diff --git a/plugins/FTPFileYM/curl/lib/smtp.c b/plugins/FTPFileYM/curl/lib/smtp.c index d2d4aeb141..9626a30d83 100644 --- a/plugins/FTPFileYM/curl/lib/smtp.c +++ b/plugins/FTPFileYM/curl/lib/smtp.c @@ -26,6 +26,8 @@ * RFC4616 PLAIN authentication * RFC4954 SMTP Authentication * RFC5321 SMTP protocol + * RFC6749 OAuth 2.0 Authorization Framework + * Draft SMTP URL Interface * ***************************************************************************/ @@ -99,7 +101,8 @@ 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 CURLcode smtp_state_upgrade_tls(struct connectdata *conn); +static CURLcode smtp_parse_url_options(struct connectdata *conn); +static CURLcode smtp_parse_url_path(struct connectdata *conn); /* * SMTP protocol handler. @@ -159,7 +162,7 @@ const struct Curl_handler Curl_handler_smtps = { static const struct Curl_handler Curl_handler_smtp_proxy = { "SMTP", /* scheme */ - ZERO_NULL, /* setup_connection */ + Curl_http_setup_conn, /* setup_connection */ Curl_http, /* do_it */ Curl_http_done, /* done */ ZERO_NULL, /* do_more */ @@ -184,7 +187,7 @@ static const struct Curl_handler Curl_handler_smtp_proxy = { static const struct Curl_handler Curl_handler_smtps_proxy = { "SMTPS", /* scheme */ - ZERO_NULL, /* setup_connection */ + Curl_http_setup_conn, /* setup_connection */ Curl_http, /* do_it */ Curl_http_done, /* done */ ZERO_NULL, /* do_more */ @@ -204,81 +207,108 @@ static const struct Curl_handler Curl_handler_smtps_proxy = { #endif #endif -/* Function that checks for an ending smtp status code at the start of the - given string, but also detects the supported authentication mechanisms - from the EHLO AUTH response. */ -static int smtp_endofresp(struct pingpong *pp, int *resp) +#ifdef USE_SSL +static void smtp_to_smtps(struct connectdata *conn) +{ + conn->handler = &Curl_handler_smtps; +} +#else +#define smtp_to_smtps(x) Curl_nop_stmt +#endif + +/*********************************************************************** + * + * pop3_endofresp() + * + * Checks for an ending SMTP status code at the start of the given string, but + * also detects various capabilities from the EHLO response including the + * supported authentication mechanisms. + */ +static bool smtp_endofresp(struct connectdata *conn, char *line, size_t len, + int *resp) { - char *line = pp->linestart_resp; - size_t len = pp->nread_resp; - struct connectdata *conn = pp->conn; struct smtp_conn *smtpc = &conn->proto.smtpc; - int result; + bool result = FALSE; size_t wordlen; if(len < 4 || !ISDIGIT(line[0]) || !ISDIGIT(line[1]) || !ISDIGIT(line[2])) return FALSE; /* Nothing for us */ - /* Extract the response code if necessary */ - if((result = (line[3] == ' ')) != 0) + /* Do we have a command response? */ + result = (line[3] == ' ') ? TRUE : FALSE; + if(result) *resp = curlx_sltosi(strtol(line, NULL, 10)); - line += 4; - len -= 4; - - /* Does the server support the SIZE capability? */ - if(smtpc->state == SMTP_EHLO && len >= 4 && !memcmp(line, "SIZE", 4)) { - DEBUGF(infof(conn->data, "Server supports SIZE extension.\n")); - smtpc->size_supported = true; - } - - /* Do we have the authentication mechanism list? */ - if(smtpc->state == SMTP_EHLO && len >= 5 && !memcmp(line, "AUTH ", 5)) { - line += 5; - len -= 5; - - for(;;) { - while(len && - (*line == ' ' || *line == '\t' || - *line == '\r' || *line == '\n')) { - line++; - len--; + /* Are we processing EHLO command data? */ + if(smtpc->state == SMTP_EHLO && (!result || (result && *resp/100 == 2))) { + line += 4; + len -= 4; + + /* Does the server support the STARTTLS capability? */ + if(len >= 8 && !memcmp(line, "STARTTLS", 8)) + smtpc->tls_supported = TRUE; + + /* Does the server support the SIZE capability? */ + else if(len >= 4 && !memcmp(line, "SIZE", 4)) + smtpc->size_supported = TRUE; + + /* Do we have the authentication mechanism list? */ + else if(len >= 5 && !memcmp(line, "AUTH ", 5)) { + line += 5; + len -= 5; + + /* Loop through the data line */ + for(;;) { + while(len && + (*line == ' ' || *line == '\t' || + *line == '\r' || *line == '\n')) { + + line++; + len--; + } + + 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(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_LOGIN)) + smtpc->authmechs |= SASL_MECH_LOGIN; + else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_PLAIN)) + smtpc->authmechs |= SASL_MECH_PLAIN; + else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_CRAM_MD5)) + smtpc->authmechs |= SASL_MECH_CRAM_MD5; + else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_DIGEST_MD5)) + smtpc->authmechs |= SASL_MECH_DIGEST_MD5; + else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_GSSAPI)) + smtpc->authmechs |= SASL_MECH_GSSAPI; + else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_EXTERNAL)) + smtpc->authmechs |= SASL_MECH_EXTERNAL; + else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_NTLM)) + smtpc->authmechs |= SASL_MECH_NTLM; + else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_XOAUTH2)) + smtpc->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)) - smtpc->authmechs |= SASL_MECH_LOGIN; - else if(wordlen == 5 && !memcmp(line, "PLAIN", 5)) - smtpc->authmechs |= SASL_MECH_PLAIN; - else if(wordlen == 8 && !memcmp(line, "CRAM-MD5", 8)) - smtpc->authmechs |= SASL_MECH_CRAM_MD5; - else if(wordlen == 10 && !memcmp(line, "DIGEST-MD5", 10)) - smtpc->authmechs |= SASL_MECH_DIGEST_MD5; - else if(wordlen == 6 && !memcmp(line, "GSSAPI", 6)) - smtpc->authmechs |= SASL_MECH_GSSAPI; - else if(wordlen == 8 && !memcmp(line, "EXTERNAL", 8)) - smtpc->authmechs |= SASL_MECH_EXTERNAL; - else if(wordlen == 4 && !memcmp(line, "NTLM", 4)) - smtpc->authmechs |= SASL_MECH_NTLM; - - line += wordlen; - len -= wordlen; } } return result; } -/* This is the ONLY way to change SMTP state! */ +/*********************************************************************** + * + * state() + * + * This is the ONLY way to change SMTP state! + */ static void state(struct connectdata *conn, smtpstate newstate) { struct smtp_conn *smtpc = &conn->proto.smtpc; @@ -293,13 +323,14 @@ static void state(struct connectdata *conn, smtpstate newstate) "UPGRADETLS", "AUTH_PLAIN", "AUTH_LOGIN", - "AUTH_PASSWD", + "AUTH_LOGIN_PASSWD", "AUTH_CRAMMD5", "AUTH_DIGESTMD5", "AUTH_DIGESTMD5_RESP", "AUTH_NTLM", "AUTH_NTLM_TYPE2MSG", - "AUTH", + "AUTH_XOAUTH2", + "AUTH_FINAL", "MAIL", "RCPT", "DATA", @@ -310,35 +341,47 @@ static void state(struct connectdata *conn, smtpstate newstate) if(smtpc->state != newstate) infof(conn->data, "SMTP %p state change from %s to %s\n", - smtpc, names[smtpc->state], names[newstate]); + (void *)smtpc, names[smtpc->state], names[newstate]); #endif smtpc->state = newstate; } -static CURLcode smtp_state_ehlo(struct connectdata *conn) +/*********************************************************************** + * + * smtp_perform_ehlo() + * + * Sends the EHLO command to not only initialise communication with the ESMTP + * server but to also obtain a list of server side supported capabilities. + */ +static CURLcode smtp_perform_ehlo(struct connectdata *conn) { - CURLcode result; + CURLcode result = CURLE_OK; struct smtp_conn *smtpc = &conn->proto.smtpc; smtpc->authmechs = 0; /* No known authentication mechanisms yet */ smtpc->authused = 0; /* Clear the authentication mechanism used for esmtp connections */ + smtpc->tls_supported = FALSE; /* Clear the TLS capability */ /* Send the EHLO command */ result = Curl_pp_sendf(&smtpc->pp, "EHLO %s", smtpc->domain); - if(result) - return result; + if(!result) + state(conn, SMTP_EHLO); - state(conn, SMTP_EHLO); - - return CURLE_OK; + return result; } -static CURLcode smtp_state_helo(struct connectdata *conn) +/*********************************************************************** + * + * smtp_perform_helo() + * + * Sends the HELO command to initialise communication with the SMTP server. + */ +static CURLcode smtp_perform_helo(struct connectdata *conn) { - CURLcode result; + CURLcode result = CURLE_OK; struct smtp_conn *smtpc = &conn->proto.smtpc; smtpc->authused = 0; /* No authentication mechanism used in smtp @@ -347,20 +390,72 @@ static CURLcode smtp_state_helo(struct connectdata *conn) /* Send the HELO command */ result = Curl_pp_sendf(&smtpc->pp, "HELO %s", smtpc->domain); - if(result) - return result; + if(!result) + state(conn, SMTP_HELO); - state(conn, SMTP_HELO); + return result; +} - return CURLE_OK; +/*********************************************************************** + * + * smtp_perform_starttls() + * + * Sends the STLS command to start the upgrade to TLS. + */ +static CURLcode smtp_perform_starttls(struct connectdata *conn) +{ + CURLcode result = CURLE_OK; + + /* Send the STARTTLS command */ + result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "STARTTLS"); + + if(!result) + state(conn, SMTP_STARTTLS); + + return result; } -static CURLcode smtp_authenticate(struct connectdata *conn) +/*********************************************************************** + * + * smtp_perform_upgrade_tls() + * + * Performs the upgrade to TLS. + */ +static CURLcode smtp_perform_upgrade_tls(struct connectdata *conn) { CURLcode result = CURLE_OK; struct smtp_conn *smtpc = &conn->proto.smtpc; - char *initresp = NULL; + + /* Start the SSL connection */ + result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone); + + if(!result) { + if(smtpc->state != SMTP_UPGRADETLS) + state(conn, SMTP_UPGRADETLS); + + if(smtpc->ssldone) { + smtp_to_smtps(conn); + result = smtp_perform_ehlo(conn); + } + } + + return result; +} + +/*********************************************************************** + * + * smtp_perform_authenticate() + * + * Sends an AUTH command allowing the client to login with the appropriate + * SASL authentication mechanism. + */ +static CURLcode smtp_perform_authenticate(struct connectdata *conn) +{ + CURLcode result = CURLE_OK; + struct SessionHandle *data = conn->data; + struct smtp_conn *smtpc = &conn->proto.smtpc; const char *mech = NULL; + char *initresp = NULL; size_t len = 0; smtpstate state1 = SMTP_STOP; smtpstate state2 = SMTP_STOP; @@ -376,89 +471,223 @@ static CURLcode smtp_authenticate(struct connectdata *conn) /* Calculate the supported authentication mechanism, by decreasing order of security, as well as the initial response where appropriate */ #ifndef CURL_DISABLE_CRYPTO_AUTH - if(smtpc->authmechs & SASL_MECH_DIGEST_MD5) { - mech = "DIGEST-MD5"; + if((smtpc->authmechs & SASL_MECH_DIGEST_MD5) && + (smtpc->prefmech & SASL_MECH_DIGEST_MD5)) { + mech = SASL_MECH_STRING_DIGEST_MD5; state1 = SMTP_AUTH_DIGESTMD5; smtpc->authused = SASL_MECH_DIGEST_MD5; } - else if(smtpc->authmechs & SASL_MECH_CRAM_MD5) { - mech = "CRAM-MD5"; + else if((smtpc->authmechs & SASL_MECH_CRAM_MD5) && + (smtpc->prefmech & SASL_MECH_CRAM_MD5)) { + mech = SASL_MECH_STRING_CRAM_MD5; state1 = SMTP_AUTH_CRAMMD5; smtpc->authused = SASL_MECH_CRAM_MD5; } else #endif #ifdef USE_NTLM - if(smtpc->authmechs & SASL_MECH_NTLM) { - mech = "NTLM"; + if((smtpc->authmechs & SASL_MECH_NTLM) && + (smtpc->prefmech & SASL_MECH_NTLM)) { + mech = SASL_MECH_STRING_NTLM; state1 = SMTP_AUTH_NTLM; state2 = SMTP_AUTH_NTLM_TYPE2MSG; smtpc->authused = SASL_MECH_NTLM; - result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd, - &conn->ntlm, - &initresp, &len); - } + + if(data->set.sasl_ir) + result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd, + &conn->ntlm, + &initresp, &len); + } else #endif - if(smtpc->authmechs & SASL_MECH_LOGIN) { - mech = "LOGIN"; + if(((smtpc->authmechs & SASL_MECH_XOAUTH2) && + (smtpc->prefmech & SASL_MECH_XOAUTH2) && + (smtpc->prefmech != SASL_AUTH_ANY)) || conn->xoauth2_bearer) { + mech = SASL_MECH_STRING_XOAUTH2; + state1 = SMTP_AUTH_XOAUTH2; + state2 = SMTP_AUTH_FINAL; + smtpc->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((smtpc->authmechs & SASL_MECH_LOGIN) && + (smtpc->prefmech & SASL_MECH_LOGIN)) { + mech = SASL_MECH_STRING_LOGIN; state1 = SMTP_AUTH_LOGIN; - state2 = SMTP_AUTH_PASSWD; + state2 = SMTP_AUTH_LOGIN_PASSWD; smtpc->authused = SASL_MECH_LOGIN; - result = Curl_sasl_create_login_message(conn->data, conn->user, - &initresp, &len); + + if(data->set.sasl_ir) + result = Curl_sasl_create_login_message(conn->data, conn->user, + &initresp, &len); } - else if(smtpc->authmechs & SASL_MECH_PLAIN) { - mech = "PLAIN"; + else if((smtpc->authmechs & SASL_MECH_PLAIN) && + (smtpc->prefmech & SASL_MECH_PLAIN)) { + mech = SASL_MECH_STRING_PLAIN; state1 = SMTP_AUTH_PLAIN; - state2 = SMTP_AUTH; + state2 = SMTP_AUTH_FINAL; smtpc->authused = SASL_MECH_PLAIN; - result = Curl_sasl_create_plain_message(conn->data, conn->user, - conn->passwd, &initresp, &len); - } - else { - /* Other mechanisms not supported */ - infof(conn->data, "No known authentication mechanisms supported!\n"); - result = CURLE_LOGIN_DENIED; + + if(data->set.sasl_ir) + result = Curl_sasl_create_plain_message(conn->data, conn->user, + conn->passwd, &initresp, &len); } if(!result) { - /* Perform SASL based authentication */ - if(initresp && - strlen(mech) + len <= 512 - 8) { /* AUTH <mech> ...<crlf> */ - result = Curl_pp_sendf(&smtpc->pp, "AUTH %s %s", mech, initresp); + if(mech) { + /* Perform SASL based authentication */ + if(initresp && + 8 + strlen(mech) + len <= 512) { /* AUTH <mech> ...<crlf> */ + result = Curl_pp_sendf(&smtpc->pp, "AUTH %s %s", mech, initresp); - if(!result) - state(conn, state2); + if(!result) + state(conn, state2); + } + else { + result = Curl_pp_sendf(&smtpc->pp, "AUTH %s", mech); + + if(!result) + state(conn, state1); + } + + Curl_safefree(initresp); } else { - result = Curl_pp_sendf(&smtpc->pp, "AUTH %s", mech); + /* Other mechanisms not supported */ + infof(conn->data, "No known authentication mechanisms supported!\n"); + result = CURLE_LOGIN_DENIED; + } + } - if(!result) - state(conn, state1); + return result; +} + +/*********************************************************************** + * + * smtp_perform_mail() + * + * Sends an MAIL command to initiate the upload of a message. + */ +static CURLcode smtp_perform_mail(struct connectdata *conn) +{ + char *from = NULL; + char *auth = NULL; + char *size = NULL; + CURLcode result = CURLE_OK; + struct SessionHandle *data = conn->data; + + /* Calculate the FROM parameter */ + if(!data->set.str[STRING_MAIL_FROM]) + /* Null reverse-path, RFC-5321, sect. 3.6.3 */ + from = strdup("<>"); + else if(data->set.str[STRING_MAIL_FROM][0] == '<') + from = aprintf("%s", data->set.str[STRING_MAIL_FROM]); + else + from = aprintf("<%s>", data->set.str[STRING_MAIL_FROM]); + + if(!from) + return CURLE_OUT_OF_MEMORY; + + /* Calculate the optional AUTH parameter */ + if(data->set.str[STRING_MAIL_AUTH] && conn->proto.smtpc.authused) { + if(data->set.str[STRING_MAIL_AUTH][0] != '\0') + auth = aprintf("%s", data->set.str[STRING_MAIL_AUTH]); + else + /* Empty AUTH, RFC-2554, sect. 5 */ + auth = strdup("<>"); + + if(!auth) { + Curl_safefree(from); + + return CURLE_OUT_OF_MEMORY; } + } - Curl_safefree(initresp); + /* Calculate the optional SIZE parameter */ + if(conn->proto.smtpc.size_supported && conn->data->set.infilesize > 0) { + size = aprintf("%" FORMAT_OFF_T, data->set.infilesize); + + if(!size) { + Curl_safefree(from); + Curl_safefree(auth); + + return CURLE_OUT_OF_MEMORY; + } } + /* Send the MAIL command */ + if(!auth && !size) + result = Curl_pp_sendf(&conn->proto.smtpc.pp, + "MAIL FROM:%s", from); + else if(auth && !size) + result = Curl_pp_sendf(&conn->proto.smtpc.pp, + "MAIL FROM:%s AUTH=%s", from, auth); + else if(auth && size) + result = Curl_pp_sendf(&conn->proto.smtpc.pp, + "MAIL FROM:%s AUTH=%s SIZE=%s", from, auth, size); + else + result = Curl_pp_sendf(&conn->proto.smtpc.pp, + "MAIL FROM:%s SIZE=%s", from, size); + + Curl_safefree(from); + Curl_safefree(auth); + Curl_safefree(size); + + if(!result) + state(conn, SMTP_MAIL); + return result; } -/* For the SMTP "protocol connect" and "doing" phases only */ -static int smtp_getsock(struct connectdata *conn, curl_socket_t *socks, - int numsocks) +/*********************************************************************** + * + * smtp_perform_rcpt_to() + * + * Sends a RCPT TO command for a given recipient as part of the message upload + * process. + */ +static CURLcode smtp_perform_rcpt_to(struct connectdata *conn) { - return Curl_pp_getsock(&conn->proto.smtpc.pp, socks, numsocks); + CURLcode result = CURLE_OK; + struct SessionHandle *data = conn->data; + struct SMTP *smtp = data->req.protop; + + /* Send the RCPT TO command */ + if(smtp->rcpt) { + if(smtp->rcpt->data[0] == '<') + result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:%s", + smtp->rcpt->data); + else + result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:<%s>", + smtp->rcpt->data); + if(!result) + state(conn, SMTP_RCPT); + } + + return result; } -#ifdef USE_SSL -static void smtp_to_smtps(struct connectdata *conn) +/*********************************************************************** + * + * smtp_perform_quit() + * + * Performs the quit action prior to sclose() being called. + */ +static CURLcode smtp_perform_quit(struct connectdata *conn) { - conn->handler = &Curl_handler_smtps; + CURLcode result = CURLE_OK; + + /* Send the QUIT command */ + result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "QUIT"); + + if(!result) + state(conn, SMTP_QUIT); + + return result; } -#else -#define smtp_to_smtps(x) Curl_nop_stmt -#endif /* For the initial server greeting */ static CURLcode smtp_state_servergreet_resp(struct connectdata *conn, @@ -472,10 +701,10 @@ static CURLcode smtp_state_servergreet_resp(struct connectdata *conn, if(smtpcode/100 != 2) { failf(data, "Got unexpected smtp-server response: %d", smtpcode); - return CURLE_FTP_WEIRD_SERVER_REPLY; + result = CURLE_FTP_WEIRD_SERVER_REPLY; } - - result = smtp_state_ehlo(conn); + else + result = smtp_perform_ehlo(conn); return result; } @@ -496,30 +725,10 @@ static CURLcode smtp_state_starttls_resp(struct connectdata *conn, result = CURLE_USE_SSL_FAILED; } else - result = smtp_authenticate(conn); + result = smtp_perform_authenticate(conn); } else - result = smtp_state_upgrade_tls(conn); - - return result; -} - -static CURLcode smtp_state_upgrade_tls(struct connectdata *conn) -{ - struct smtp_conn *smtpc = &conn->proto.smtpc; - CURLcode result; - - result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone); - - if(!result) { - if(smtpc->state != SMTP_UPGRADETLS) - state(conn, SMTP_UPGRADETLS); - - if(smtpc->ssldone) { - smtp_to_smtps(conn); - result = smtp_state_ehlo(conn); - } - } + result = smtp_perform_upgrade_tls(conn); return result; } @@ -530,27 +739,34 @@ static CURLcode smtp_state_ehlo_resp(struct connectdata *conn, int smtpcode, { CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; + struct smtp_conn *smtpc = &conn->proto.smtpc; (void)instate; /* no use for this yet */ if(smtpcode/100 != 2) { if((data->set.use_ssl <= CURLUSESSL_TRY || conn->ssl[FIRSTSOCKET].use) && !conn->bits.user_passwd) - result = smtp_state_helo(conn); + result = smtp_perform_helo(conn); else { failf(data, "Remote access denied: %d", smtpcode); result = CURLE_REMOTE_ACCESS_DENIED; } } else 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(&conn->proto.smtpc.pp, "STARTTLS"); - if(!result) - state(conn, SMTP_STARTTLS); + /* We don't have a SSL/TLS connection yet, but SSL is requested */ + if(smtpc->tls_supported) + /* Switch to TLS connection now */ + result = smtp_perform_starttls(conn); + else if(data->set.use_ssl == CURLUSESSL_TRY) + /* Fallback and carry on with authentication */ + result = smtp_perform_authenticate(conn); + else { + failf(data, "STARTTLS not supported."); + result = CURLE_USE_SSL_FAILED; + } } else - result = smtp_authenticate(conn); + result = smtp_perform_authenticate(conn); return result; } @@ -602,7 +818,7 @@ static CURLcode smtp_state_auth_plain_resp(struct connectdata *conn, result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", plainauth); if(!result) - state(conn, SMTP_AUTH); + state(conn, SMTP_AUTH_FINAL); } Curl_safefree(plainauth); @@ -639,7 +855,7 @@ static CURLcode smtp_state_auth_login_resp(struct connectdata *conn, result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", authuser); if(!result) - state(conn, SMTP_AUTH_PASSWD); + state(conn, SMTP_AUTH_LOGIN_PASSWD); } Curl_safefree(authuser); @@ -649,10 +865,10 @@ static CURLcode smtp_state_auth_login_resp(struct connectdata *conn, return result; } -/* For responses to user entry of AUTH LOGIN */ -static CURLcode smtp_state_auth_passwd_resp(struct connectdata *conn, - int smtpcode, - smtpstate instate) +/* For AUTH LOGIN user entry responses */ +static CURLcode smtp_state_auth_login_password_resp(struct connectdata *conn, + int smtpcode, + smtpstate instate) { CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; @@ -676,7 +892,7 @@ static CURLcode smtp_state_auth_passwd_resp(struct connectdata *conn, result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", authpasswd); if(!result) - state(conn, SMTP_AUTH); + state(conn, SMTP_AUTH_FINAL); } Curl_safefree(authpasswd); @@ -731,7 +947,7 @@ static CURLcode smtp_state_auth_cram_resp(struct connectdata *conn, result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", rplyb64); if(!result) - state(conn, SMTP_AUTH); + state(conn, SMTP_AUTH_FINAL); } Curl_safefree(rplyb64); @@ -798,10 +1014,10 @@ static CURLcode smtp_state_auth_digest_resp_resp(struct connectdata *conn, } else { /* Send an empty response */ - result = Curl_pp_sendf(&conn->proto.smtpc.pp, ""); + result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", ""); if(!result) - state(conn, SMTP_AUTH); + state(conn, SMTP_AUTH_FINAL); } return result; @@ -878,7 +1094,7 @@ static CURLcode smtp_state_auth_ntlm_type2msg_resp(struct connectdata *conn, result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", type3msg); if(!result) - state(conn, SMTP_AUTH); + state(conn, SMTP_AUTH_FINAL); } Curl_safefree(type3msg); @@ -889,116 +1105,60 @@ static CURLcode smtp_state_auth_ntlm_type2msg_resp(struct connectdata *conn, } #endif -/* For the final responses to the AUTH sequence */ -static CURLcode smtp_state_auth_resp(struct connectdata *conn, int smtpcode, - smtpstate instate) +/* For AUTH XOAUTH2 (without initial response) responses */ +static CURLcode smtp_state_auth_xoauth2_resp(struct connectdata *conn, + int smtpcode, smtpstate 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(smtpcode != 235) { - failf(data, "Authentication failed: %d", smtpcode); + if(smtpcode != 334) { + failf(data, "Access denied: %d", smtpcode); result = CURLE_LOGIN_DENIED; } - else - /* End of connect phase */ - state(conn, SMTP_STOP); - - return result; -} - -/* Start the DO phase */ -static CURLcode smtp_mail(struct connectdata *conn) -{ - char *from = NULL; - char *auth = NULL; - char *size = NULL; - CURLcode result = CURLE_OK; - struct SessionHandle *data = conn->data; - - /* Calculate the FROM parameter */ - if(!data->set.str[STRING_MAIL_FROM]) - /* Null reverse-path, RFC-2821, sect. 3.7 */ - from = strdup("<>"); - else if(data->set.str[STRING_MAIL_FROM][0] == '<') - from = aprintf("%s", data->set.str[STRING_MAIL_FROM]); - else - from = aprintf("<%s>", data->set.str[STRING_MAIL_FROM]); - - if(!from) - return CURLE_OUT_OF_MEMORY; + else { + /* Create the authorisation message */ + result = Curl_sasl_create_xoauth2_message(conn->data, conn->user, + conn->xoauth2_bearer, + &xoauth, &len); - /* Calculate the optional AUTH parameter */ - if(data->set.str[STRING_MAIL_AUTH] && conn->proto.smtpc.authused) { - if(data->set.str[STRING_MAIL_AUTH][0] != '\0') - auth = aprintf("%s", data->set.str[STRING_MAIL_AUTH]); - else - /* Empty AUTH, RFC-2554, sect. 5 */ - auth = strdup("<>"); + /* Send the message */ + if(!result) { + if(xoauth) { + result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", xoauth); - if(!auth) { - Curl_safefree(from); + if(!result) + state(conn, SMTP_AUTH_FINAL); + } - return CURLE_OUT_OF_MEMORY; + Curl_safefree(xoauth); } } - /* calculate the optional SIZE parameter */ - if(conn->proto.smtpc.size_supported && conn->data->set.infilesize > 0) { - size = aprintf("%" FORMAT_OFF_T, data->set.infilesize); - - if(!size) { - Curl_safefree(from); - Curl_safefree(auth); - - return CURLE_OUT_OF_MEMORY; - } - } - - /* Send the MAIL command */ - if(!auth && !size) - result = Curl_pp_sendf(&conn->proto.smtpc.pp, - "MAIL FROM:%s", from); - else if(auth && !size) - result = Curl_pp_sendf(&conn->proto.smtpc.pp, - "MAIL FROM:%s AUTH=%s", from, auth); - else if(auth && size) - result = Curl_pp_sendf(&conn->proto.smtpc.pp, - "MAIL FROM:%s AUTH=%s SIZE=%s", from, auth, size); - else - result = Curl_pp_sendf(&conn->proto.smtpc.pp, - "MAIL FROM:%s SIZE=%s", from, size); - - Curl_safefree(from); - Curl_safefree(auth); - Curl_safefree(size); - - if(result) - return result; - - state(conn, SMTP_MAIL); - return result; } -static CURLcode smtp_rcpt_to(struct connectdata *conn) +/* For the final responses to the AUTH sequence */ +static CURLcode smtp_state_auth_final_resp(struct connectdata *conn, + int smtpcode, + smtpstate instate) { CURLcode result = CURLE_OK; - struct smtp_conn *smtpc = &conn->proto.smtpc; + struct SessionHandle *data = conn->data; - /* Send the RCPT TO command */ - if(smtpc->rcpt) { - if(smtpc->rcpt->data[0] == '<') - result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:%s", - smtpc->rcpt->data); - else - result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:<%s>", - smtpc->rcpt->data); - if(!result) - state(conn, SMTP_RCPT); + (void)instate; /* no use for this yet */ + + if(smtpcode != 235) { + failf(data, "Authentication failed: %d", smtpcode); + result = CURLE_LOGIN_DENIED; } + else + /* End of connect phase */ + state(conn, SMTP_STOP); return result; } @@ -1009,6 +1169,7 @@ static CURLcode smtp_state_mail_resp(struct connectdata *conn, int smtpcode, { CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; + struct SMTP *smtp = data->req.protop; (void)instate; /* no use for this yet */ @@ -1018,10 +1179,9 @@ static CURLcode smtp_state_mail_resp(struct connectdata *conn, int smtpcode, state(conn, SMTP_STOP); } else { - struct smtp_conn *smtpc = &conn->proto.smtpc; - smtpc->rcpt = data->set.mail_rcpt; + smtp->rcpt = data->set.mail_rcpt; - result = smtp_rcpt_to(conn); + result = smtp_perform_rcpt_to(conn); } return result; @@ -1033,6 +1193,7 @@ static CURLcode smtp_state_rcpt_resp(struct connectdata *conn, int smtpcode, { CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; + struct SMTP *smtp = data->req.protop; (void)instate; /* no use for this yet */ @@ -1042,24 +1203,20 @@ static CURLcode smtp_state_rcpt_resp(struct connectdata *conn, int smtpcode, state(conn, SMTP_STOP); } else { - struct smtp_conn *smtpc = &conn->proto.smtpc; - - if(smtpc->rcpt) { - smtpc->rcpt = smtpc->rcpt->next; - result = smtp_rcpt_to(conn); + if(smtp->rcpt) { + smtp->rcpt = smtp->rcpt->next; + result = smtp_perform_rcpt_to(conn); /* If we failed or still are sending RCPT data then return */ - if(result || smtpc->rcpt) + if(result || smtp->rcpt) return result; } /* Send the DATA command */ - result = Curl_pp_sendf(&conn->proto.smtpc.pp, "DATA"); + result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "DATA"); - if(result) - return result; - - state(conn, SMTP_DATA); + if(!result) + state(conn, SMTP_DATA); } return result; @@ -1070,7 +1227,6 @@ static CURLcode smtp_state_data_resp(struct connectdata *conn, int smtpcode, smtpstate instate) { struct SessionHandle *data = conn->data; - struct FTP *smtp = data->state.proto.smtp; (void)instate; /* no use for this yet */ @@ -1079,11 +1235,13 @@ static CURLcode smtp_state_data_resp(struct connectdata *conn, int smtpcode, return CURLE_SEND_ERROR; } + /* Set the progress upload size */ + Curl_pgrsSetUploadSize(data, data->set.infilesize); + /* SMTP upload */ - Curl_setup_transfer(conn, -1, -1, FALSE, NULL, /* no download */ - FIRSTSOCKET, smtp->bytecountp); + Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL); - /* End of do phase */ + /* End of DO phase */ state(conn, SMTP_STOP); return CURLE_OK; @@ -1102,7 +1260,7 @@ static CURLcode smtp_state_postdata_resp(struct connectdata *conn, if(smtpcode != 250) result = CURLE_RECV_ERROR; - /* End of done phase */ + /* End of DONE phase */ state(conn, SMTP_STOP); return result; @@ -1110,7 +1268,7 @@ static CURLcode smtp_state_postdata_resp(struct connectdata *conn, static CURLcode smtp_statemach_act(struct connectdata *conn) { - CURLcode result; + CURLcode result = CURLE_OK; curl_socket_t sock = conn->sock[FIRSTSOCKET]; struct SessionHandle *data = conn->data; int smtpcode; @@ -1120,7 +1278,7 @@ static CURLcode smtp_statemach_act(struct connectdata *conn) /* Busy upgrading the connection; right now all I/O is SSL/TLS, not SMTP */ if(smtpc->state == SMTP_UPGRADETLS) - return smtp_state_upgrade_tls(conn); + return smtp_perform_upgrade_tls(conn); /* Flush any data that needs to be sent */ if(pp->sendleft) @@ -1162,8 +1320,9 @@ static CURLcode smtp_statemach_act(struct connectdata *conn) result = smtp_state_auth_login_resp(conn, smtpcode, smtpc->state); break; - case SMTP_AUTH_PASSWD: - result = smtp_state_auth_passwd_resp(conn, smtpcode, smtpc->state); + case SMTP_AUTH_LOGIN_PASSWD: + result = smtp_state_auth_login_password_resp(conn, smtpcode, + smtpc->state); break; #ifndef CURL_DISABLE_CRYPTO_AUTH @@ -1191,8 +1350,12 @@ static CURLcode smtp_statemach_act(struct connectdata *conn) break; #endif - case SMTP_AUTH: - result = smtp_state_auth_resp(conn, smtpcode, smtpc->state); + case SMTP_AUTH_XOAUTH2: + result = smtp_state_auth_xoauth2_resp(conn, smtpcode, smtpc->state); + break; + + case SMTP_AUTH_FINAL: + result = smtp_state_auth_final_resp(conn, smtpcode, smtpc->state); break; case SMTP_MAIL: @@ -1226,30 +1389,28 @@ static CURLcode smtp_statemach_act(struct connectdata *conn) /* Called repeatedly until done from multi.c */ static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done) { + CURLcode result = CURLE_OK; struct smtp_conn *smtpc = &conn->proto.smtpc; - CURLcode result; - if((conn->handler->flags & PROTOPT_SSL) && !smtpc->ssldone) + if((conn->handler->flags & PROTOPT_SSL) && !smtpc->ssldone) { result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone); - else - result = Curl_pp_multi_statemach(&smtpc->pp); + if(result || !smtpc->ssldone) + return result; + } + result = Curl_pp_statemach(&smtpc->pp, FALSE); *done = (smtpc->state == SMTP_STOP) ? TRUE : FALSE; return result; } -static CURLcode smtp_easy_statemach(struct connectdata *conn) +static CURLcode smtp_block_statemach(struct connectdata *conn) { - struct smtp_conn *smtpc = &conn->proto.smtpc; - struct pingpong *pp = &smtpc->pp; CURLcode result = CURLE_OK; + struct smtp_conn *smtpc = &conn->proto.smtpc; - while(smtpc->state != SMTP_STOP) { - result = Curl_pp_easy_statemach(pp); - if(result) - break; - } + while(smtpc->state != SMTP_STOP && !result) + result = Curl_pp_statemach(&smtpc->pp, TRUE); return result; } @@ -1258,26 +1419,22 @@ static CURLcode smtp_easy_statemach(struct connectdata *conn) required */ static CURLcode smtp_init(struct connectdata *conn) { + CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; - struct FTP *smtp = data->state.proto.smtp; + struct SMTP *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; + smtp = data->req.protop = calloc(sizeof(struct SMTP), 1); + if(!smtp) + 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. - */ - smtp->user = conn->user; - smtp->passwd = conn->passwd; + return result; +} - 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); } /*********************************************************************** @@ -1288,54 +1445,38 @@ static CURLcode smtp_init(struct connectdata *conn) * the connection phase. * * The variable pointed to by 'done' will be TRUE if the protocol-layer - * connect phase is done when this function returns, or FALSE if not. When - * called as a part of the easy interface, it will always be TRUE. + * connect phase is done when this function returns, or FALSE if not. */ static CURLcode smtp_connect(struct connectdata *conn, bool *done) { - CURLcode result; + CURLcode result = CURLE_OK; struct smtp_conn *smtpc = &conn->proto.smtpc; struct pingpong *pp = &smtpc->pp; - const char *path = conn->data->state.path; - char localhost[HOSTNAME_MAX + 1]; *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 persistent connections on smtp */ + /* We always support persistent connections in 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; - - /* Initialise the response reader stuff */ - Curl_pp_init(pp); - /* Set the default response time-out */ pp->response_time = RESP_TIMEOUT; pp->statemach_act = smtp_statemach_act; pp->endofresp = smtp_endofresp; pp->conn = conn; - /* Calculate the path if necessary */ - if(!*path) { - if(!Curl_gethostname(localhost, sizeof(localhost))) - path = localhost; - else - path = "localhost"; - } + /* Set the default preferred authentication mechanism */ + smtpc->prefmech = SASL_AUTH_ANY; - /* URL decode the path and use it as the domain in our EHLO */ - result = Curl_urldecode(conn->data, path, 0, &smtpc->domain, NULL, TRUE); + /* Initialise the pingpong layer */ + Curl_pp_init(pp); + + /* Parse the URL options */ + result = smtp_parse_url_options(conn); + if(result) + return result; + + /* Parse the URL path */ + result = smtp_parse_url_path(conn); if(result) return result; @@ -1359,19 +1500,21 @@ static CURLcode smtp_connect(struct connectdata *conn, bool *done) 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; + struct SessionHandle *data = conn->data; + struct SMTP *smtp = data->req.protop; + struct pingpong *pp = &conn->proto.smtpc.pp; + const char *eob; + ssize_t len; ssize_t bytes_written; (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. - */ + /* When the easy handle is removed from the multi interface while libcurl + is still trying to resolve the host name, the SMTP 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) { @@ -1379,25 +1522,27 @@ static CURLcode smtp_done(struct connectdata *conn, CURLcode status, result = status; /* use the already set error code */ } else if(!data->set.connect_only) { - struct smtp_conn *smtpc = &conn->proto.smtpc; - struct pingpong *pp = &smtpc->pp; + /* Calculate the EOB taking into account any terminating CRLF from the + previous line of the email or the CRLF of the DATA command when there + is "no mail data". RFC-5321, sect. 4.1.1.4. */ + eob = SMTP_EOB; + len = SMTP_EOB_LEN; + if(smtp->trailing_crlf || !conn->data->set.infilesize) { + eob += 2; + len -= 2; + } /* Send the end of block data */ - result = Curl_write(conn, - conn->writesockfd, /* socket to send to */ - SMTP_EOB, /* buffer pointer */ - SMTP_EOB_LEN, /* buffer size */ - &bytes_written); /* actually sent away */ - + result = Curl_write(conn, conn->writesockfd, eob, len, &bytes_written); if(result) return result; - if(bytes_written != SMTP_EOB_LEN) { + if(bytes_written != len) { /* The whole chunk was not sent so keep it around and adjust the pingpong structure accordingly */ - pp->sendthis = strdup(SMTP_EOB); - pp->sendsize = SMTP_EOB_LEN; - pp->sendleft = SMTP_EOB_LEN - bytes_written; + pp->sendthis = strdup(eob); + pp->sendsize = len; + pp->sendleft = len - bytes_written; } else /* Successfully sent so adjust the response timeout relative to now */ @@ -1412,10 +1557,10 @@ static CURLcode smtp_done(struct connectdata *conn, CURLcode status, non-blocking DONE operations, not in the multi state machine and with Curl_done() invokes on several places in the code! */ - result = smtp_easy_statemach(conn); + result = smtp_block_statemach(conn); } - /* Clear the transfer mode for the next connection */ + /* Clear the transfer mode for the next request */ smtp->transfer = FTPTRANSFER_BODY; return result; @@ -1425,8 +1570,8 @@ static CURLcode smtp_done(struct connectdata *conn, CURLcode status, * * smtp_perform() * - * This is the actual DO function for SMTP. Get a file/directory according to - * the options previously setup. + * This is the actual DO function for SMTP. Send a mail according to the + * options previously setup. */ static CURLcode smtp_perform(struct connectdata *conn, bool *connected, bool *dophase_done) @@ -1438,14 +1583,14 @@ static CURLcode smtp_perform(struct connectdata *conn, bool *connected, if(conn->data->set.opt_no_body) { /* Requested no body means no transfer */ - struct FTP *smtp = conn->data->state.proto.smtp; + struct SMTP *smtp = conn->data->req.protop; smtp->transfer = FTPTRANSFER_INFO; } *dophase_done = FALSE; /* not done yet */ /* Start the first command in the DO phase */ - result = smtp_mail(conn); + result = smtp_perform_mail(conn); if(result) return result; @@ -1471,45 +1616,11 @@ static CURLcode smtp_perform(struct connectdata *conn, bool *connected, */ 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_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"); - if(result) - return result; - - state(conn, SMTP_QUIT); + *done = FALSE; /* default to false */ - result = smtp_easy_statemach(conn); + result = smtp_regular_transfer(conn, done); return result; } @@ -1528,12 +1639,13 @@ static CURLcode smtp_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 SMTP session may or may not have been allocated/setup at this point! */ if(!dead_connection && smtpc->pp.conn) - (void)smtp_quit(conn); /* ignore errors on the LOGOUT */ + if(!smtp_perform_quit(conn)) + (void)smtp_block_statemach(conn); /* ignore errors on QUIT */ /* Disconnect from the server */ Curl_pp_disconnect(&smtpc->pp); @@ -1550,7 +1662,7 @@ static CURLcode smtp_disconnect(struct connectdata *conn, /* Call this when the DO phase has completed */ static CURLcode smtp_dophase_done(struct connectdata *conn, bool connected) { - struct FTP *smtp = conn->data->state.proto.smtp; + struct SMTP *smtp = conn->data->req.protop; (void)connected; @@ -1568,12 +1680,10 @@ static CURLcode smtp_doing(struct connectdata *conn, bool *dophase_done) if(result) DEBUGF(infof(conn->data, "DO phase failed\n")); - else { - if(*dophase_done) { - result = smtp_dophase_done(conn, FALSE /* not connected */); + else if(*dophase_done) { + result = smtp_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,20 +1708,18 @@ static CURLcode smtp_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 = smtp_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 = smtp_dophase_done(conn, connected); - } return result; } @@ -1619,9 +1727,10 @@ static CURLcode smtp_regular_transfer(struct connectdata *conn, static CURLcode smtp_setup_connection(struct connectdata *conn) { struct SessionHandle *data = conn->data; + CURLcode result; if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) { - /* Unless we have asked to tunnel smtp operations through the proxy, we + /* 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) @@ -1634,34 +1743,111 @@ static CURLcode smtp_setup_connection(struct connectdata *conn) return CURLE_UNSUPPORTED_PROTOCOL; #endif } + /* set it up as a HTTP connection instead */ + return conn->handler->setup_connection(conn); - /* 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 } + /* Initialise the SMTP layer */ + result = smtp_init(conn); + if(result) + return result; + data->state.path++; /* don't include the initial slash */ return CURLE_OK; } +/*********************************************************************** + * + * smtp_parse_url_options() + * + * Parse the URL login options. + */ +static CURLcode smtp_parse_url_options(struct connectdata *conn) +{ + CURLcode result = CURLE_OK; + struct smtp_conn *smtpc = &conn->proto.smtpc; + 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, "*")) + smtpc->prefmech = SASL_AUTH_ANY; + else if(strequal(value, SASL_MECH_STRING_LOGIN)) + smtpc->prefmech = SASL_MECH_LOGIN; + else if(strequal(value, SASL_MECH_STRING_PLAIN)) + smtpc->prefmech = SASL_MECH_PLAIN; + else if(strequal(value, SASL_MECH_STRING_CRAM_MD5)) + smtpc->prefmech = SASL_MECH_CRAM_MD5; + else if(strequal(value, SASL_MECH_STRING_DIGEST_MD5)) + smtpc->prefmech = SASL_MECH_DIGEST_MD5; + else if(strequal(value, SASL_MECH_STRING_GSSAPI)) + smtpc->prefmech = SASL_MECH_GSSAPI; + else if(strequal(value, SASL_MECH_STRING_NTLM)) + smtpc->prefmech = SASL_MECH_NTLM; + else if(strequal(value, SASL_MECH_STRING_XOAUTH2)) + smtpc->prefmech = SASL_MECH_XOAUTH2; + else + smtpc->prefmech = SASL_AUTH_NONE; + } + else + result = CURLE_URL_MALFORMAT; + } + + return result; +} + +/*********************************************************************** + * + * 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 initialised in smtp_connect() */ + struct SessionHandle *data = conn->data; + struct smtp_conn *smtpc = &conn->proto.smtpc; + const char *path = data->state.path; + char localhost[HOSTNAME_MAX + 1]; + + /* Calculate the path if necessary */ + if(!*path) { + if(!Curl_gethostname(localhost, sizeof(localhost))) + path = localhost; + else + path = "localhost"; + } + + /* URL decode the path and use it as the domain in our EHLO */ + return Curl_urldecode(conn->data, path, 0, &smtpc->domain, NULL, TRUE); +} + CURLcode Curl_smtp_escape_eob(struct connectdata *conn, ssize_t nread) { /* When sending a SMTP payload we must detect CRLF. sequences making sure they are sent as CRLF.. instead, as a . on the beginning of a line will be deleted by the server when not part of an EOB terminator and a genuine CRLF.CRLF which isn't escaped will wrongly be detected as end of - data by the server. + data by the server */ ssize_t i; ssize_t si; - struct smtp_conn *smtpc = &conn->proto.smtpc; struct SessionHandle *data = conn->data; + struct SMTP *smtp = data->req.protop; /* Do we need to allocate the scatch buffer? */ if(!data->state.scratch) { @@ -1676,36 +1862,46 @@ CURLcode Curl_smtp_escape_eob(struct connectdata *conn, ssize_t nread) /* This loop can be improved by some kind of Boyer-Moore style of approach but that is saved for later... */ for(i = 0, si = 0; i < nread; i++) { - if(SMTP_EOB[smtpc->eob] == data->req.upload_fromhere[i]) - smtpc->eob++; - else if(smtpc->eob) { + if(SMTP_EOB[smtp->eob] == data->req.upload_fromhere[i]) { + smtp->eob++; + + /* Is the EOB potentially the terminating CRLF? */ + if(2 == smtp->eob || SMTP_EOB_LEN == smtp->eob) + smtp->trailing_crlf = TRUE; + else + smtp->trailing_crlf = FALSE; + } + else if(smtp->eob) { /* A previous substring matched so output that first */ - memcpy(&data->state.scratch[si], SMTP_EOB, smtpc->eob); - si += smtpc->eob; + memcpy(&data->state.scratch[si], SMTP_EOB, smtp->eob); + si += smtp->eob; /* Then compare the first byte */ if(SMTP_EOB[0] == data->req.upload_fromhere[i]) - smtpc->eob = 1; + smtp->eob = 1; else - smtpc->eob = 0; + smtp->eob = 0; + + /* Reset the trailing CRLF flag as there was more data */ + smtp->trailing_crlf = FALSE; } - /* Do we have a match for CRLF. as per RFC-2821, sect. 4.5.2 */ - if(SMTP_EOB_FIND_LEN == smtpc->eob) { + /* Do we have a match for CRLF. as per RFC-5321, sect. 4.5.2 */ + if(SMTP_EOB_FIND_LEN == smtp->eob) { /* Copy the replacement data to the target buffer */ memcpy(&data->state.scratch[si], SMTP_EOB_REPL, SMTP_EOB_REPL_LEN); si += SMTP_EOB_REPL_LEN; - smtpc->eob = 0; + smtp->eob = 0; } - else if(!smtpc->eob) + else if(!smtp->eob) data->state.scratch[si++] = data->req.upload_fromhere[i]; } - if(smtpc->eob) { + if(smtp->eob) { /* A substring matched before processing ended so output that now */ - memcpy(&data->state.scratch[si], SMTP_EOB, smtpc->eob); - si += smtpc->eob; - smtpc->eob = 0; + memcpy(&data->state.scratch[si], SMTP_EOB, smtp->eob); + si += smtp->eob; + smtp->eob = 0; } if(si != nread) { |