diff --git a/lib/smtp.c b/lib/smtp.c index 02af49560..77d36e909 100644 --- a/lib/smtp.c +++ b/lib/smtp.c @@ -230,78 +230,27 @@ static bool smtp_endofresp(struct connectdata *conn, char *line, size_t len, { struct smtp_conn *smtpc = &conn->proto.smtpc; bool result = FALSE; - size_t wordlen; + /* Nothing for us */ if(len < 4 || !ISDIGIT(line[0]) || !ISDIGIT(line[1]) || !ISDIGIT(line[2])) - return FALSE; /* Nothing for us */ + return FALSE; /* Do we have a command response? This should be the response code followed by a space and optionally some text as per RFC-5321 and as outlined in Section 4. Examples of RFC-4954 but some e-mail servers ignore this and - only send the response code instead. */ - result = (line[3] == ' ' || len == 5) ? TRUE : FALSE; - if(result) + only send the response code instead as per Section 4.2. */ + if(line[3] == ' ' || len == 5) { + result = TRUE; *resp = curlx_sltosi(strtol(line, NULL, 10)); - /* 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; - } - } + /* Make sure real server never sends internal value */ + if(*resp == 1) + *resp = 0; + } + /* Do we have a multiline (continuation) response? */ + else if(line[3] == '-') { + result = TRUE; + *resp = 1; /* Internal response code */ } return result; @@ -774,10 +723,13 @@ 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; + const char *line = data->state.buffer; + size_t len = strlen(line); + size_t wordlen; (void)instate; /* no use for this yet */ - if(smtpcode/100 != 2) { + if(smtpcode/100 != 2 && smtpcode != 1) { if((data->set.use_ssl <= CURLUSESSL_TRY || conn->ssl[FIRSTSOCKET].use) && !conn->bits.user_passwd) result = smtp_perform_helo(conn); @@ -786,21 +738,83 @@ static CURLcode smtp_state_ehlo_resp(struct connectdata *conn, int 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 */ - 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 { + 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(smtpcode != 1) { + if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) { + /* 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_perform_authenticate(conn); } } - else - result = smtp_perform_authenticate(conn); return result; } @@ -1344,7 +1358,7 @@ static CURLcode smtp_statemach_act(struct connectdata *conn) return result; /* Store the latest response for later retrieval */ - if(smtpc->state != SMTP_QUIT) + if(smtpc->state != SMTP_QUIT && smtpcode != 1) data->info.httpcode = smtpcode; if(smtpcode) {