diff options
Diffstat (limited to 'lib/smtp.c')
-rw-r--r-- | lib/smtp.c | 1779 |
1 files changed, 972 insertions, 807 deletions
@@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2010, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -18,30 +18,25 @@ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * - * RFC2821 SMTP protocol - * RFC3207 SMTP over TLS - * RFC4954 SMTP Authentication + * RFC1870 SMTP Service Extension for Message Size * RFC2195 CRAM-MD5 authentication + * RFC2831 DIGEST-MD5 authentication + * RFC3207 SMTP over TLS + * RFC4422 Simple Authentication and Security Layer (SASL) * RFC4616 PLAIN authentication + * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism + * RFC4954 SMTP Authentication + * RFC5321 SMTP protocol + * RFC6749 OAuth 2.0 Authorization Framework + * Draft SMTP URL Interface <draft-earhart-url-smtp-00.txt> + * Draft LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt> * ***************************************************************************/ -#include "setup.h" +#include "curl_setup.h" #ifndef CURL_DISABLE_SMTP -#include <stdio.h> -#include <string.h> -#include <stdlib.h> -#include <stdarg.h> -#include <ctype.h> - -#ifdef HAVE_UNISTD_H -#include <unistd.h> -#endif -#ifdef HAVE_SYS_SOCKET_H -#include <sys/socket.h> -#endif #ifdef HAVE_NETINET_IN_H #include <netinet/in.h> #endif @@ -67,9 +62,6 @@ #include <curl/curl.h> #include "urldata.h" #include "sendf.h" -#include "easyif.h" /* for Curl_convert_... prototypes */ - -#include "if2ip.h" #include "hostip.h" #include "progress.h" #include "transfer.h" @@ -80,22 +72,17 @@ #include "strtoofft.h" #include "strequal.h" -#include "sslgen.h" +#include "vtls/vtls.h" #include "connect.h" #include "strerror.h" #include "select.h" #include "multiif.h" #include "url.h" #include "rawstr.h" -#include "strtoofft.h" -#include "curl_base64.h" -#include "curl_md5.h" -#include "curl_hmac.h" #include "curl_gethostname.h" - -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> - +#include "curl_sasl.h" +#include "warnless.h" +#include "curl_printf.h" #include "curl_memory.h" /* The last #include file should be: */ #include "memdebug.h" @@ -103,18 +90,22 @@ /* Local API functions */ static CURLcode smtp_regular_transfer(struct connectdata *conn, bool *done); static CURLcode smtp_do(struct connectdata *conn, bool *done); -static CURLcode smtp_done(struct connectdata *conn, - CURLcode, bool premature); +static CURLcode smtp_done(struct connectdata *conn, CURLcode status, + bool premature); static CURLcode smtp_connect(struct connectdata *conn, bool *done); -static CURLcode smtp_disconnect(struct connectdata *conn); +static CURLcode smtp_disconnect(struct connectdata *conn, bool dead); static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done); -static int smtp_getsock(struct connectdata *conn, - curl_socket_t *socks, +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_doing(struct connectdata *conn, bool *dophase_done); +static CURLcode smtp_setup_connection(struct connectdata *conn); +static CURLcode smtp_parse_url_options(struct connectdata *conn); +static CURLcode smtp_parse_url_path(struct connectdata *conn); +static CURLcode smtp_parse_custom_request(struct connectdata *conn); +static CURLcode smtp_perform_auth(struct connectdata *conn, const char *mech, + const char *initresp); +static CURLcode smtp_continue_auth(struct connectdata *conn, const char *resp); +static void smtp_get_message(char *buffer, char** outptr); /* * SMTP protocol handler. @@ -131,13 +122,15 @@ const struct Curl_handler Curl_handler_smtp = { smtp_doing, /* doing */ smtp_getsock, /* proto_getsock */ smtp_getsock, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ ZERO_NULL, /* perform_getsock */ smtp_disconnect, /* disconnect */ + ZERO_NULL, /* readwrite */ PORT_SMTP, /* defport */ - PROT_SMTP /* protocol */ + CURLPROTO_SMTP, /* protocol */ + PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY /* flags */ }; - #ifdef USE_SSL /* * SMTPS protocol handler. @@ -154,10 +147,14 @@ const struct Curl_handler Curl_handler_smtps = { smtp_doing, /* doing */ smtp_getsock, /* proto_getsock */ smtp_getsock, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ ZERO_NULL, /* perform_getsock */ smtp_disconnect, /* disconnect */ + ZERO_NULL, /* readwrite */ PORT_SMTPS, /* defport */ - PROT_SMTP | PROT_SMTPS | PROT_SSL /* protocol */ + CURLPROTO_SMTPS, /* protocol */ + PROTOPT_CLOSEACTION | PROTOPT_SSL + | PROTOPT_NOURLQUERY /* flags */ }; #endif @@ -168,7 +165,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 */ @@ -177,13 +174,15 @@ static const struct Curl_handler Curl_handler_smtp_proxy = { ZERO_NULL, /* doing */ ZERO_NULL, /* proto_getsock */ ZERO_NULL, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ ZERO_NULL, /* perform_getsock */ ZERO_NULL, /* disconnect */ + ZERO_NULL, /* readwrite */ PORT_SMTP, /* defport */ - PROT_HTTP /* protocol */ + CURLPROTO_HTTP, /* protocol */ + PROTOPT_NONE /* flags */ }; - #ifdef USE_SSL /* * HTTP-proxyed SMTPS protocol handler. @@ -191,7 +190,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 */ @@ -200,96 +199,126 @@ static const struct Curl_handler Curl_handler_smtps_proxy = { ZERO_NULL, /* doing */ ZERO_NULL, /* proto_getsock */ ZERO_NULL, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ ZERO_NULL, /* perform_getsock */ ZERO_NULL, /* disconnect */ + ZERO_NULL, /* readwrite */ PORT_SMTPS, /* defport */ - PROT_HTTP /* protocol */ + CURLPROTO_HTTP, /* protocol */ + PROTOPT_NONE /* flags */ }; #endif #endif +/* SASL parameters for the smtp protocol */ +static const struct SASLproto saslsmtp = { + "smtp", /* The service name */ + 334, /* Code received when continuation is expected */ + 235, /* Code to receive upon authentication success */ + 512 - 8, /* Maximum initial response length (no max) */ + smtp_perform_auth, /* Send authentication command */ + smtp_continue_auth, /* Send authentication continuation */ + smtp_get_message /* Get SASL response message */ +}; + +#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 -/* Function that checks for an ending smtp status code at the start of the - given string. - As a side effect, it also flags allowed authentication mechanisms according - to EHLO AUTH response. */ -static int smtp_endofresp(struct pingpong *pp, int *resp) +/*********************************************************************** + * + * smtp_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; - size_t wordlen; + bool result = FALSE; + /* 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 as per Section 4.2. */ + if(line[3] == ' ' || len == 5) { + result = TRUE; + *resp = curlx_sltosi(strtol(line, NULL, 10)); + + /* Make sure real server never sends internal value */ + if(*resp == 1) + *resp = 0; + } + /* Do we have a multiline (continuation) response? */ + else if(line[3] == '-' && + (smtpc->state == SMTP_EHLO || smtpc->state == SMTP_COMMAND)) { + result = TRUE; + *resp = 1; /* Internal response code */ + } - if((result = line[3] == ' ')) - *resp = atoi(line); + return result; +} - line += 4; - len -= 4; +/*********************************************************************** + * + * smtp_get_message() + * + * Gets the authentication message from the response buffer. + */ +static void smtp_get_message(char *buffer, char** outptr) +{ + size_t len = 0; + char* message = NULL; - if(smtpc->state == SMTP_EHLO && len >= 5 && !memcmp(line, "AUTH ", 5)) { - line += 5; - len -= 5; + /* Find the start of the message */ + for(message = buffer + 4; *message == ' ' || *message == '\t'; message++) + ; - for (;;) { - while (len && - (*line == ' ' || *line == '\t' || - *line == '\r' || *line == '\n')) { - line++; - len--; - } + /* Find the end of the message */ + for(len = strlen(message); len--;) + if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' && + message[len] != '\t') + break; - if(!len) - break; - - for (wordlen = 0; wordlen < len && line[wordlen] != ' ' && - line[wordlen] != '\t' && line[wordlen] != '\r' && - line[wordlen] != '\n';) - wordlen++; - - if(wordlen == 5 && !memcmp(line, "LOGIN", 5)) - smtpc->authmechs |= SMTP_AUTH_LOGIN; - else if(wordlen == 5 && !memcmp(line, "PLAIN", 5)) - smtpc->authmechs |= SMTP_AUTH_PLAIN; - else if(wordlen == 8 && !memcmp(line, "CRAM-MD5", 8)) - smtpc->authmechs |= SMTP_AUTH_CRAM_MD5; - else if(wordlen == 10 && !memcmp(line, "DIGEST-MD5", 10)) - smtpc->authmechs |= SMTP_AUTH_DIGEST_MD5; - else if(wordlen == 6 && !memcmp(line, "GSSAPI", 6)) - smtpc->authmechs |= SMTP_AUTH_GSSAPI; - else if(wordlen == 8 && !memcmp(line, "EXTERNAL", 8)) - smtpc->authmechs |= SMTP_AUTH_EXTERNAL; - - line += wordlen; - len -= wordlen; - } + /* Terminate the message */ + if(++len) { + message[len] = '\0'; } - return result; + *outptr = message; } -/* This is the ONLY way to change SMTP state! */ -static void state(struct connectdata *conn, - smtpstate newstate) +/*********************************************************************** + * + * 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; #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) /* for debug purposes */ - static const char * const names[]={ + static const char * const names[] = { "STOP", "SERVERGREET", "EHLO", "HELO", "STARTTLS", - "AUTHPLAIN", - "AUTHLOGIN", - "AUTHPASSWD", - "AUTHCRAM", + "UPGRADETLS", "AUTH", + "COMMAND", "MAIL", "RCPT", "DATA", @@ -297,438 +326,479 @@ static void state(struct connectdata *conn, "QUIT", /* LAST */ }; + 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->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanism yet */ + smtpc->sasl.authused = SASL_AUTH_NONE; /* Clear the authentication mechanism + used for esmtp connections */ + smtpc->tls_supported = FALSE; /* Clear the TLS capability */ + smtpc->auth_supported = FALSE; /* Clear the AUTH capability */ - /* send EHLO */ + /* 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; - /* send HELO */ + smtpc->sasl.authused = SASL_AUTH_NONE; /* No authentication mechanism used + in smtp connections */ + + /* 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 CURLE_OK; -} - -static size_t smtp_auth_plain_data(struct connectdata * conn, char * * outptr) -{ - char plainauth[2 * MAX_CURL_USER_LENGTH + MAX_CURL_PASSWORD_LENGTH]; - size_t ulen; - size_t plen; - - ulen = strlen(conn->user); - plen = strlen(conn->passwd); - - if(2 * ulen + plen + 2 > sizeof plainauth) - return 0; - - memcpy(plainauth, conn->user, ulen); - plainauth[ulen] = '\0'; - memcpy(plainauth + ulen + 1, conn->user, ulen); - plainauth[2 * ulen + 1] = '\0'; - memcpy(plainauth + 2 * ulen + 2, conn->passwd, plen); - return Curl_base64_encode(conn->data, plainauth, 2 * ulen + plen + 2, outptr); + return result; } -static size_t smtp_auth_login_user(struct connectdata * conn, char * * outptr) +/*********************************************************************** + * + * smtp_perform_starttls() + * + * Sends the STLS command to start the upgrade to TLS. + */ +static CURLcode smtp_perform_starttls(struct connectdata *conn) { - size_t ulen; + CURLcode result = CURLE_OK; - ulen = strlen(conn->user); + /* Send the STARTTLS command */ + result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "STARTTLS"); - if(!ulen) { - *outptr = strdup("="); - return *outptr? 1: 0; - } + if(!result) + state(conn, SMTP_STARTTLS); - return Curl_base64_encode(conn->data, conn->user, ulen, outptr); + 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; - const char * mech; - size_t l; - smtpstate state1; - smtpstate state2; - - if(!conn->bits.user_passwd) - state(conn, SMTP_STOP); /* End of connect phase. */ - else { - initresp = (char *) NULL; - l = 1; - - /* Check supported authentication mechanisms by decreasing order of - preference. */ - mech = (const char *) NULL; /* Avoid compiler warnings. */ - state1 = SMTP_STOP; - state2 = SMTP_STOP; - -#ifndef CURL_DISABLE_CRYPTO_AUTH - if(smtpc->authmechs & SMTP_AUTH_CRAM_MD5) { - mech = "CRAM-MD5"; - state1 = SMTP_AUTHCRAM; - } - else -#endif - if(smtpc->authmechs & SMTP_AUTH_PLAIN) { - mech = "PLAIN"; - state1 = SMTP_AUTHPLAIN; - state2 = SMTP_AUTH; - l = smtp_auth_plain_data(conn, &initresp); - } - else if(smtpc->authmechs & SMTP_AUTH_LOGIN) { - mech = "LOGIN"; - state1 = SMTP_AUTHLOGIN; - state2 = SMTP_AUTHPASSWD; - l = smtp_auth_login_user(conn, &initresp); - } - else { - infof(conn->data, "No known auth mechanisms supported!\n"); - result = CURLE_LOGIN_DENIED; /* Other mechanisms not supported. */ - } - if(!result) { - if(!l) - result = CURLE_OUT_OF_MEMORY; - else if(initresp && - l + strlen(mech) <= 512 - 8) { /* AUTH <mech> ...<crlf> */ - result = Curl_pp_sendf(&smtpc->pp, "AUTH %s %s", mech, initresp); - free(initresp); - - if(!result) - state(conn, state2); - } - else { - Curl_safefree(initresp); + /* Start the SSL connection */ + result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone); - result = Curl_pp_sendf(&smtpc->pp, "AUTH %s", mech); + if(!result) { + if(smtpc->state != SMTP_UPGRADETLS) + state(conn, SMTP_UPGRADETLS); - if(!result) - state(conn, state1); - } + if(smtpc->ssldone) { + smtp_to_smtps(conn); + result = smtp_perform_ehlo(conn); } } return result; } -/* 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); -} - -/* for STARTTLS responses */ -static CURLcode smtp_state_starttls_resp(struct connectdata *conn, - int smtpcode, - smtpstate instate) +/*********************************************************************** + * + * smtp_perform_auth() + * + * Sends an AUTH command allowing the client to login with the given SASL + * authentication mechanism. + */ +static CURLcode smtp_perform_auth(struct connectdata *conn, + const char *mech, + const char *initresp) { CURLcode result = CURLE_OK; - struct SessionHandle *data = conn->data; - (void)instate; /* no use for this yet */ + struct smtp_conn *smtpc = &conn->proto.smtpc; - if(smtpcode != 220) { - if(data->set.ftp_ssl != CURLUSESSL_TRY) { - failf(data, "STARTTLS denied. %c", smtpcode); - result = CURLE_LOGIN_DENIED; - } - else - result = smtp_authenticate(conn); + if(initresp) { /* AUTH <mech> ...<crlf> */ + /* Send the AUTH command with the initial response */ + result = Curl_pp_sendf(&smtpc->pp, "AUTH %s %s", mech, initresp); } else { - /* Curl_ssl_connect is BLOCKING */ - result = Curl_ssl_connect(conn, FIRSTSOCKET); - if(CURLE_OK == result) { - conn->protocol |= PROT_SMTPS; - result = smtp_state_ehlo(conn); - } + /* Send the AUTH command */ + result = Curl_pp_sendf(&smtpc->pp, "AUTH %s", mech); } + return result; } -/* for EHLO responses */ -static CURLcode smtp_state_ehlo_resp(struct connectdata *conn, - int smtpcode, - smtpstate instate) +/*********************************************************************** + * + * smtp_continue_auth() + * + * Sends SASL continuation data or cancellation. + */ +static CURLcode smtp_continue_auth(struct connectdata *conn, const char *resp) +{ + struct smtp_conn *smtpc = &conn->proto.smtpc; + + return Curl_pp_sendf(&smtpc->pp, "%s", resp); +} + +/*********************************************************************** + * + * smtp_perform_authentication() + * + * Initiates the authentication sequence, with the appropriate SASL + * authentication mechanism. + */ +static CURLcode smtp_perform_authentication(struct connectdata *conn) { CURLcode result = CURLE_OK; - struct SessionHandle *data = conn->data; + struct smtp_conn *smtpc = &conn->proto.smtpc; + saslprogress progress; - (void)instate; /* no use for this yet */ + /* Check we have enough data to authenticate with, and the + server supports authentiation, and end the connect phase if not */ + if(!smtpc->auth_supported || + !Curl_sasl_can_authenticate(&smtpc->sasl, conn)) { + state(conn, SMTP_STOP); + return result; + } - if(smtpcode/100 != 2) { - if((data->set.ftp_ssl <= CURLUSESSL_TRY || conn->ssl[FIRSTSOCKET].use) && - !conn->bits.user_passwd) - result = smtp_state_helo(conn); + /* Calculate the SASL login details */ + result = Curl_sasl_start(&smtpc->sasl, conn, FALSE, &progress); + + if(!result) { + if(progress == SASL_INPROGRESS) + state(conn, SMTP_AUTH); else { - failf(data, "Access denied: %d", smtpcode); + /* Other mechanisms not supported */ + infof(conn->data, "No known authentication mechanisms supported!\n"); result = CURLE_LOGIN_DENIED; } } - else if(data->set.ftp_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"); - state(conn, SMTP_STARTTLS); - } - else - result = smtp_authenticate(conn); return result; } -/* for HELO responses */ -static CURLcode smtp_state_helo_resp(struct connectdata *conn, - int smtpcode, - smtpstate instate) +/*********************************************************************** + * + * smtp_perform_command() + * + * Sends a SMTP based command. + */ +static CURLcode smtp_perform_command(struct connectdata *conn) { CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; + struct SMTP *smtp = data->req.protop; + + /* Send the command */ + if(smtp->rcpt) + result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s %s", + smtp->custom && smtp->custom[0] != '\0' ? + smtp->custom : "VRFY", + smtp->rcpt->data); + else + result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", + smtp->custom && smtp->custom[0] != '\0' ? + smtp->custom : "HELP"); - (void)instate; /* no use for this yet */ + if(!result) + state(conn, SMTP_COMMAND); - if(smtpcode/100 != 2) { - failf(data, "Access denied: %d", smtpcode); - result = CURLE_LOGIN_DENIED; - } - else { - /* end the connect phase */ - state(conn, SMTP_STOP); - } return result; } -/* for AUTH PLAIN (without initial response) responses */ -static CURLcode smtp_state_authplain_resp(struct connectdata *conn, - int smtpcode, - smtpstate instate) +/*********************************************************************** + * + * 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; - size_t l; - char * plainauth; - (void)instate; /* no use for this yet */ + /* 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.sasl.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(smtpcode != 334) { - failf(data, "Access denied: %d", smtpcode); - result = CURLE_LOGIN_DENIED; + if(!auth) { + free(from); + + return CURLE_OUT_OF_MEMORY; + } } - else { - l = smtp_auth_plain_data(conn, &plainauth); - if(!l) - result = CURLE_OUT_OF_MEMORY; - else { - result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", plainauth); - free(plainauth); + /* Calculate the optional SIZE parameter */ + if(conn->proto.smtpc.size_supported && conn->data->state.infilesize > 0) { + size = aprintf("%" CURL_FORMAT_CURL_OFF_T, data->state.infilesize); - if(!result) - state(conn, SMTP_AUTH); + if(!size) { + free(from); + free(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); + + free(from); + free(auth); + free(size); + + if(!result) + state(conn, SMTP_MAIL); + return result; } -/* for AUTH LOGIN (without initial response) responses */ -static CURLcode smtp_state_authlogin_resp(struct connectdata *conn, - int smtpcode, - smtpstate instate) +/*********************************************************************** + * + * 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) { CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; - size_t l; - char * authuser; + struct SMTP *smtp = data->req.protop; - (void)instate; /* no use for this yet */ + /* Send the RCPT TO command */ + 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); - if(smtpcode != 334) { - failf(data, "Access denied: %d", smtpcode); - result = CURLE_LOGIN_DENIED; - } - else { - l = smtp_auth_login_user(conn, &authuser); + return result; +} - if(!l) - result = CURLE_OUT_OF_MEMORY; - else { - result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", authuser); - free(authuser); +/*********************************************************************** + * + * smtp_perform_quit() + * + * Performs the quit action prior to sclose() being called. + */ +static CURLcode smtp_perform_quit(struct connectdata *conn) +{ + CURLcode result = CURLE_OK; - if(!result) - state(conn, SMTP_AUTHPASSWD); - } - } + /* Send the QUIT command */ + result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "QUIT"); + + if(!result) + state(conn, SMTP_QUIT); return result; } -/* for responses to user entry of AUTH LOGIN. */ -static CURLcode smtp_state_authpasswd_resp(struct connectdata *conn, - int smtpcode, - smtpstate instate) +/* For the initial server greeting */ +static CURLcode smtp_state_servergreet_resp(struct connectdata *conn, + int smtpcode, + smtpstate instate) { CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; - size_t plen; - size_t l; - char *authpasswd; (void)instate; /* no use for this yet */ - if(smtpcode != 334) { - failf(data, "Access denied: %d", smtpcode); - result = CURLE_LOGIN_DENIED; - } - else { - plen = strlen(conn->passwd); - - if(!plen) - result = Curl_pp_sendf(&conn->proto.smtpc.pp, "="); - else { - l = Curl_base64_encode(data, conn->passwd, plen, &authpasswd); - - if(!l) - result = CURLE_OUT_OF_MEMORY; - else { - result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", authpasswd); - free(authpasswd); - - if(!result) - state(conn, SMTP_AUTH); - } - } + if(smtpcode/100 != 2) { + failf(data, "Got unexpected smtp-server response: %d", smtpcode); + result = CURLE_FTP_WEIRD_SERVER_REPLY; } + else + result = smtp_perform_ehlo(conn); return result; } -#ifndef CURL_DISABLE_CRYPTO_AUTH - -/* for AUTH CRAM-MD5 responses. */ -static CURLcode smtp_state_authcram_resp(struct connectdata *conn, +/* For STARTTLS responses */ +static CURLcode smtp_state_starttls_resp(struct connectdata *conn, int smtpcode, smtpstate instate) { CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; - char * chlg64 = data->state.buffer; - unsigned char * chlg; - size_t chlglen; - size_t l; - char * rplyb64; - HMAC_context * ctxt; - unsigned char digest[16]; - char reply[MAX_CURL_USER_LENGTH + 32 /* 2 * size of MD5 digest */ + 1]; (void)instate; /* no use for this yet */ - if(smtpcode != 334) { - failf(data, "Access denied: %d", smtpcode); - return CURLE_LOGIN_DENIED; + if(smtpcode != 220) { + if(data->set.use_ssl != CURLUSESSL_TRY) { + failf(data, "STARTTLS denied. %c", smtpcode); + result = CURLE_USE_SSL_FAILED; + } + else + result = smtp_perform_authentication(conn); } + else + result = smtp_perform_upgrade_tls(conn); - /* Get the challenge. */ - for (chlg64 += 4; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++) - ; - - chlg = (unsigned char *) NULL; - chlglen = 0; + return result; +} - if(*chlg64 != '=') { - for (l = strlen(chlg64); l--;) - if(chlg64[l] != '\r' && chlg64[l] != '\n' && chlg64[l] != ' ' && - chlg64[l] != '\t') - break; +/* For EHLO responses */ +static CURLcode smtp_state_ehlo_resp(struct connectdata *conn, int smtpcode, + smtpstate instate) +{ + 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; - if(++l) { - chlg64[l] = '\0'; + (void)instate; /* no use for this yet */ - if(!(chlglen = Curl_base64_decode(chlg64, &chlg))) - return CURLE_OUT_OF_MEMORY; + if(smtpcode/100 != 2 && smtpcode != 1) { + if(data->set.use_ssl <= CURLUSESSL_TRY || conn->ssl[FIRSTSOCKET].use) + result = smtp_perform_helo(conn); + else { + failf(data, "Remote access denied: %d", smtpcode); + result = CURLE_REMOTE_ACCESS_DENIED; } } - - /* Compute digest. */ - ctxt = Curl_HMAC_init(Curl_HMAC_MD5, - (const unsigned char *) conn->passwd, - (unsigned int)(strlen(conn->passwd))); - - if(!ctxt) { - if(chlg) - free(chlg); - - return CURLE_OUT_OF_MEMORY; - } - - if(chlglen > 0) - Curl_HMAC_update(ctxt, chlg, (unsigned int)(chlglen)); - - if(chlg) - free(chlg); - - Curl_HMAC_final(ctxt, digest); - - /* Prepare the reply. */ - snprintf(reply, sizeof reply, - "%s %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", - conn->user, digest[0], digest[1], digest[2], digest[3], digest[4], digest[5], - digest[6], digest[7], digest[8], digest[9], digest[10], digest[11], - digest[12], digest[13], digest[14], digest[15]); - - /* Encode it to base64 and send it. */ - l = Curl_base64_encode(data, reply, 0, &rplyb64); - - if(!l) - result = CURLE_OUT_OF_MEMORY; else { - result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", rplyb64); - free(rplyb64); + 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; + + /* Does the server support authentication? */ + else if(len >= 5 && !memcmp(line, "AUTH ", 5)) { + smtpc->auth_supported = TRUE; + + /* Advance past the AUTH keyword */ + line += 5; + len -= 5; + + /* Loop through the data line */ + for(;;) { + size_t llen; + unsigned int mechbit; + + 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((mechbit = Curl_sasl_decode_mech(line, wordlen, &llen)) && + llen == wordlen) + smtpc->sasl.authmechs |= mechbit; + + line += wordlen; + len -= wordlen; + } + } - if(!result) - state(conn, SMTP_AUTH); + 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_authentication(conn); + else { + failf(data, "STARTTLS not supported."); + result = CURLE_USE_SSL_FAILED; + } + } + else + result = smtp_perform_authentication(conn); + } } return result; } -#endif - -/* for final responses to AUTH sequences. */ -static CURLcode smtp_state_auth_resp(struct connectdata *conn, - int smtpcode, +/* For HELO responses */ +static CURLcode smtp_state_helo_resp(struct connectdata *conn, int smtpcode, smtpstate instate) { CURLcode result = CURLE_OK; @@ -736,139 +806,176 @@ static CURLcode smtp_state_auth_resp(struct connectdata *conn, (void)instate; /* no use for this yet */ - if(smtpcode != 235) { - failf(data, "Authentication failed: %d", smtpcode); - result = CURLE_LOGIN_DENIED; + if(smtpcode/100 != 2) { + failf(data, "Remote access denied: %d", smtpcode); + result = CURLE_REMOTE_ACCESS_DENIED; } else - state(conn, SMTP_STOP); /* End of connect phase. */ + /* End of connect phase */ + state(conn, SMTP_STOP); return result; } -/* start the DO phase */ -static CURLcode smtp_mail(struct connectdata *conn) +/* For SASL authentication responses */ +static CURLcode smtp_state_auth_resp(struct connectdata *conn, + int smtpcode, + smtpstate instate) { CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; + struct smtp_conn *smtpc = &conn->proto.smtpc; + saslprogress progress; - /* send MAIL */ - result = Curl_pp_sendf(&conn->proto.smtpc.pp, "MAIL FROM:%s", - data->set.str[STRING_MAIL_FROM]); - if(result) - return result; + (void)instate; /* no use for this yet */ + + result = Curl_sasl_continue(&smtpc->sasl, conn, smtpcode, &progress); + if(!result) + switch(progress) { + case SASL_DONE: + state(conn, SMTP_STOP); /* Authenticated */ + break; + case SASL_IDLE: /* No mechanism left after cancellation */ + failf(data, "Authentication cancelled"); + result = CURLE_LOGIN_DENIED; + break; + default: + break; + } - state(conn, SMTP_MAIL); return result; } -static CURLcode smtp_rcpt_to(struct connectdata *conn) +/* For command responses */ +static CURLcode smtp_state_command_resp(struct connectdata *conn, int smtpcode, + smtpstate instate) { CURLcode result = CURLE_OK; - struct smtp_conn *smtpc = &conn->proto.smtpc; + struct SessionHandle *data = conn->data; + struct SMTP *smtp = data->req.protop; + char *line = data->state.buffer; + size_t len = strlen(line); - /* send RCPT TO */ - 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((smtp->rcpt && smtpcode/100 != 2 && smtpcode != 553 && smtpcode != 1) || + (!smtp->rcpt && smtpcode/100 != 2 && smtpcode != 1)) { + failf(data, "Command failed: %d", smtpcode); + result = CURLE_RECV_ERROR; } + else { + /* Temporarily add the LF character back and send as body to the client */ + if(!data->set.opt_no_body) { + line[len] = '\n'; + result = Curl_client_write(conn, CLIENTWRITE_BODY, line, len + 1); + line[len] = '\0'; + } + + if(smtpcode != 1) { + if(smtp->rcpt) { + smtp->rcpt = smtp->rcpt->next; + + if(smtp->rcpt) { + /* Send the next command */ + result = smtp_perform_command(conn); + } + else + /* End of DO phase */ + state(conn, SMTP_STOP); + } + else + /* End of DO phase */ + state(conn, SMTP_STOP); + } + } + return result; } -/* for MAIL responses */ -static CURLcode smtp_state_mail_resp(struct connectdata *conn, - int smtpcode, +/* For MAIL responses */ +static CURLcode smtp_state_mail_resp(struct connectdata *conn, int smtpcode, smtpstate instate) { CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; + (void)instate; /* no use for this yet */ if(smtpcode/100 != 2) { - failf(data, "Access denied: %d", smtpcode); - result = CURLE_LOGIN_DENIED; - state(conn, SMTP_STOP); - } - else { - struct smtp_conn *smtpc = &conn->proto.smtpc; - smtpc->rcpt = data->set.mail_rcpt; - - result = smtp_rcpt_to(conn); + failf(data, "MAIL failed: %d", smtpcode); + result = CURLE_SEND_ERROR; } + else + /* Start the RCPT TO command */ + result = smtp_perform_rcpt_to(conn); return result; } -/* for RCPT responses */ -static CURLcode smtp_state_rcpt_resp(struct connectdata *conn, - int smtpcode, +/* For RCPT responses */ +static CURLcode smtp_state_rcpt_resp(struct connectdata *conn, int smtpcode, smtpstate instate) { CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; + struct SMTP *smtp = data->req.protop; + (void)instate; /* no use for this yet */ if(smtpcode/100 != 2) { - failf(data, "Access denied: %d", smtpcode); - result = CURLE_LOGIN_DENIED; - state(conn, SMTP_STOP); + failf(data, "RCPT failed: %d", smtpcode); + result = CURLE_SEND_ERROR; } else { - struct smtp_conn *smtpc = &conn->proto.smtpc; + smtp->rcpt = smtp->rcpt->next; - if(smtpc->rcpt) { - smtpc->rcpt = smtpc->rcpt->next; - result = smtp_rcpt_to(conn); + if(smtp->rcpt) + /* Send the next RCPT TO command */ + result = smtp_perform_rcpt_to(conn); + else { + /* Send the DATA command */ + result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "DATA"); - /* if we failed or still is in RCPT sending, return */ - if(result || smtpc->rcpt) - return result; + if(!result) + state(conn, SMTP_DATA); } - - /* send DATA */ - result = Curl_pp_sendf(&conn->proto.smtpc.pp, "DATA"); - if(result) - return result; - - state(conn, SMTP_DATA); } + return result; } -/* for the DATA response */ -static CURLcode smtp_state_data_resp(struct connectdata *conn, - int smtpcode, +/* For DATA response */ +static CURLcode smtp_state_data_resp(struct connectdata *conn, int smtpcode, smtpstate instate) { + CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; - struct FTP *smtp = data->state.proto.smtp; (void)instate; /* no use for this yet */ if(smtpcode != 354) { - state(conn, SMTP_STOP); - return CURLE_RECV_ERROR; + failf(data, "DATA failed: %d", smtpcode); + result = CURLE_SEND_ERROR; } + else { + /* Set the progress upload size */ + Curl_pgrsSetUploadSize(data, data->state.infilesize); - /* SMTP upload */ - Curl_setup_transfer(conn, -1, -1, FALSE, NULL, /* no download */ - FIRSTSOCKET, smtp->bytecountp); + /* SMTP upload */ + Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL); - state(conn, SMTP_STOP); - return CURLE_OK; + /* End of DO phase */ + state(conn, SMTP_STOP); + } + + return result; } -/* for the POSTDATA response, which is received after the entire DATA - part has been sent off to the server */ +/* For POSTDATA responses, which are received after the entire DATA + part has been sent to the server */ static CURLcode smtp_state_postdata_resp(struct connectdata *conn, - int smtpcode, - smtpstate instate) + int smtpcode, + smtpstate instate) { CURLcode result = CURLE_OK; @@ -877,41 +984,47 @@ static CURLcode smtp_state_postdata_resp(struct connectdata *conn, if(smtpcode != 250) result = CURLE_RECV_ERROR; + /* End of DONE phase */ state(conn, SMTP_STOP); + return result; } 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; + struct SessionHandle *data = conn->data; int smtpcode; struct smtp_conn *smtpc = &conn->proto.smtpc; struct pingpong *pp = &smtpc->pp; size_t nread = 0; + /* Busy upgrading the connection; right now all I/O is SSL/TLS, not SMTP */ + if(smtpc->state == SMTP_UPGRADETLS) + return smtp_perform_upgrade_tls(conn); + + /* Flush any data that needs to be sent */ if(pp->sendleft) - /* we have a piece of a command still left to send */ return Curl_pp_flushsend(pp); - /* we read a piece of response */ - result = Curl_pp_readresp(sock, pp, &smtpcode, &nread); - if(result) - return result; + do { + /* Read the response from the server */ + result = Curl_pp_readresp(sock, pp, &smtpcode, &nread); + if(result) + return result; + + /* Store the latest response for later retrieval if necessary */ + if(smtpc->state != SMTP_QUIT && smtpcode != 1) + data->info.httpcode = smtpcode; + + if(!smtpcode) + break; - if(smtpcode) { - /* we have now received a full SMTP server response */ + /* We have now received a full SMTP server response */ switch(smtpc->state) { case SMTP_SERVERGREET: - if(smtpcode/100 != 2) { - failf(data, "Got unexpected smtp-server response: %d", smtpcode); - return CURLE_FTP_WEIRD_SERVER_REPLY; - } - - result = smtp_state_ehlo(conn); - if(result) - return result; + result = smtp_state_servergreet_resp(conn, smtpcode, smtpc->state); break; case SMTP_EHLO: @@ -926,28 +1039,14 @@ static CURLcode smtp_statemach_act(struct connectdata *conn) result = smtp_state_starttls_resp(conn, smtpcode, smtpc->state); break; - case SMTP_AUTHPLAIN: - result = smtp_state_authplain_resp(conn, smtpcode, smtpc->state); - break; - - case SMTP_AUTHLOGIN: - result = smtp_state_authlogin_resp(conn, smtpcode, smtpc->state); - break; - - case SMTP_AUTHPASSWD: - result = smtp_state_authpasswd_resp(conn, smtpcode, smtpc->state); - break; - -#ifndef CURL_DISABLE_CRYPTO_AUTH - case SMTP_AUTHCRAM: - result = smtp_state_authcram_resp(conn, smtpcode, smtpc->state); - break; -#endif - case SMTP_AUTH: result = smtp_state_auth_resp(conn, smtpcode, smtpc->state); break; + case SMTP_COMMAND: + result = smtp_state_command_resp(conn, smtpcode, smtpc->state); + break; + case SMTP_MAIL: result = smtp_state_mail_resp(conn, smtpcode, smtpc->state); break; @@ -971,169 +1070,109 @@ static CURLcode smtp_statemach_act(struct connectdata *conn) state(conn, SMTP_STOP); break; } - } + } while(!result && smtpc->state != SMTP_STOP && Curl_pp_moredata(pp)); + return result; } -/* called repeatedly until done from multi.c */ -static CURLcode smtp_multi_statemach(struct connectdata *conn, - bool *done) +/* 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 = Curl_pp_multi_statemach(&smtpc->pp); - *done = (bool)(smtpc->state == SMTP_STOP); + if((conn->handler->flags & PROTOPT_SSL) && !smtpc->ssldone) { + result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone); + 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; } -/* - * Allocate and initialize the struct SMTP for the current SessionHandle. If - * need be. - */ +/* Allocate and initialize the SMTP struct for the current SessionHandle if + required */ static CURLcode smtp_init(struct connectdata *conn) { + CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; - struct FTP *smtp = data->state.proto.smtp; - if(!smtp) { - smtp = data->state.proto.smtp = calloc(sizeof(struct FTP), 1); - if(!smtp) - return CURLE_OUT_OF_MEMORY; - } + struct SMTP *smtp; - /* 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); } -/* - * smtp_connect() should do everything that is to be considered a part of +/*********************************************************************** + * + * smtp_connect() + * + * This function should do everything that is to be considered a part of * the connection phase. * - * The variable 'done' points to will be TRUE if the protocol-layer connect - * phase is done when this function returns, or FALSE is not. When called as - * a part of the easy interface, it will always be TRUE. + * 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. */ -static CURLcode smtp_connect(struct connectdata *conn, - bool *done) /* see description above */ +static CURLcode smtp_connect(struct connectdata *conn, bool *done) { - CURLcode result; + CURLcode result = CURLE_OK; struct smtp_conn *smtpc = &conn->proto.smtpc; - struct SessionHandle *data=conn->data; - struct pingpong *pp=&smtpc->pp; - const char *path = conn->data->state.path; - int len; - char localhost[1024 + 1]; + struct pingpong *pp = &smtpc->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); + /* We always support persistent connections in SMTP */ + connkeep(conn, "SMTP default"); - result = smtp_init(conn); - if(CURLE_OK != result) - return result; - - /* We always support persistant connections on smtp */ - 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 = smtp_statemach_act; pp->endofresp = smtp_endofresp; pp->conn = conn; -#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_PROXY) - if(conn->bits.tunnel_proxy && conn->bits.httpproxy) { - /* for SMTP over HTTP proxy */ - struct HTTP http_proxy; - struct FTP *smtp_save; - - /* BLOCKING */ - /* We want "seamless" SMTP operations through HTTP proxy tunnel */ - - /* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the member - * conn->proto.http; we want SMTP through HTTP and we have to change the - * member temporarily for connecting to the HTTP proxy. After - * Curl_proxyCONNECT we have to set back the member to the original struct - * SMTP pointer - */ - smtp_save = data->state.proto.smtp; - memset(&http_proxy, 0, sizeof(http_proxy)); - data->state.proto.http = &http_proxy; + /* Initialize the SASL storage */ + Curl_sasl_init(&smtpc->sasl, &saslsmtp); - result = Curl_proxyCONNECT(conn, FIRSTSOCKET, - conn->host.name, conn->remote_port); - - data->state.proto.smtp = smtp_save; - - if(CURLE_OK != result) - return result; - } -#endif /* !CURL_DISABLE_HTTP && !CURL_DISABLE_PROXY */ - - if(conn->protocol & PROT_SMTPS) { - /* BLOCKING */ - /* SMTPS is simply smtp with SSL for the control channel */ - /* now, perform the SSL initialization for this socket */ - result = Curl_ssl_connect(conn, FIRSTSOCKET); - if(result) - return result; - } + /* Initialise the pingpong layer */ + Curl_pp_init(pp); - Curl_pp_init(pp); /* init the response reader stuff */ - - pp->response_time = RESP_TIMEOUT; /* set default response time-out */ - pp->statemach_act = smtp_statemach_act; - pp->endofresp = smtp_endofresp; - pp->conn = conn; - - if(!*path) { - if(!Curl_gethostname(localhost, sizeof localhost)) - path = localhost; - else - path = "localhost"; - } + /* Parse the URL options */ + result = smtp_parse_url_options(conn); + if(result) + return result; - /* url decode the path and use it as domain with EHLO */ - smtpc->domain = curl_easy_unescape(conn->data, path, 0, &len); - if(!smtpc->domain) - return CURLE_OUT_OF_MEMORY; + /* Parse the URL path */ + result = smtp_parse_url_path(conn); + if(result) + return result; - /* When we connect, we start in the state where we await the server greeting - */ + /* Start off waiting for the server greeting response */ state(conn, SMTP_SERVERGREET); - if(data->state.used_interface == Curl_if_multi) - result = smtp_multi_statemach(conn, done); - else { - result = smtp_easy_statemach(conn); - if(!result) - *done = TRUE; - } + result = smtp_multi_statemach(conn, done); return result; } @@ -1150,52 +1189,85 @@ static CURLcode smtp_connect(struct connectdata *conn, static CURLcode smtp_done(struct connectdata *conn, CURLcode status, bool premature) { + CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; - struct FTP *smtp = data->state.proto.smtp; - CURLcode result=CURLE_OK; + struct SMTP *smtp = data->req.protop; + struct pingpong *pp = &conn->proto.smtpc.pp; + 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. - */ + if(!smtp || !pp->conn) + /* 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) { - conn->bits.close = TRUE; /* marked for closure */ - result = status; /* use the already set error code */ + connclose(conn, "SMTP done with bad status"); /* marked for closure */ + result = status; /* use the already set error code */ } - else - /* TODO: make this work even when the socket is EWOULDBLOCK in this call! */ + else if(!data->set.connect_only && data->set.upload && data->set.mail_rcpt) { + /* 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. + + Note: As some SSL backends, such as OpenSSL, will cause Curl_write() to + fail when using a different pointer following a previous write, that + returned CURLE_AGAIN, we duplicate the EOB now rather than when the + bytes written doesn't equal len. */ + if(smtp->trailing_crlf || !conn->data->state.infilesize) { + eob = strdup(SMTP_EOB + 2); + len = SMTP_EOB_LEN - 2; + } + else { + eob = strdup(SMTP_EOB); + len = SMTP_EOB_LEN; + } - /* write to socket (send away 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 */ + if(!eob) + return CURLE_OUT_OF_MEMORY; + /* Send the end of block data */ + result = Curl_write(conn, conn->writesockfd, eob, len, &bytes_written); + if(result) { + free(eob); + return result; + } + + if(bytes_written != len) { + /* The whole chunk was not sent so keep it around and adjust the + pingpong structure accordingly */ + pp->sendthis = eob; + pp->sendsize = len; + pp->sendleft = len - bytes_written; + } + else { + /* Successfully sent so adjust the response timeout relative to now */ + pp->response = Curl_tvnow(); - if(status == CURLE_OK) { - struct smtp_conn *smtpc = &conn->proto.smtpc; - struct pingpong *pp= &smtpc->pp; - pp->response = Curl_tvnow(); /* timeout relative now */ + free(eob); + } state(conn, SMTP_POSTDATA); - /* run the state-machine + + /* Run the state-machine TODO: when the multi interface is used, this _really_ should be using the smtp_multi_statemach function but we have no general support for 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 these for next connection */ + /* Cleanup our per-request based variables */ + Curl_safefree(smtp->custom); + + /* Clear the transfer mode for the next request */ smtp->transfer = FTPTRANSFER_BODY; return result; @@ -1205,41 +1277,44 @@ 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. Transfer a mail, send a command + * or get some data according to the options previously setup. */ - -static -CURLcode smtp_perform(struct connectdata *conn, - bool *connected, /* connect status after PASV / PORT */ - bool *dophase_done) +static CURLcode smtp_perform(struct connectdata *conn, bool *connected, + bool *dophase_done) { - /* this is SMTP and no proxy */ - CURLcode result=CURLE_OK; + /* This is SMTP and no proxy */ + CURLcode result = CURLE_OK; + struct SessionHandle *data = conn->data; + struct SMTP *smtp = data->req.protop; DEBUGF(infof(conn->data, "DO phase starts\n")); - if(conn->data->set.opt_no_body) { - /* requested no body means no transfer... */ - struct FTP *smtp = conn->data->state.proto.smtp; + if(data->set.opt_no_body) { + /* Requested no body means no transfer */ smtp->transfer = FTPTRANSFER_INFO; } *dophase_done = FALSE; /* not done yet */ - /* start the first command in the DO phase */ - result = smtp_mail(conn); + /* Store the first recipient (or NULL if not specified) */ + smtp->rcpt = data->set.mail_rcpt; + + /* Start the first command in the DO phase */ + if(data->set.upload && data->set.mail_rcpt) + /* MAIL transfer */ + result = smtp_perform_mail(conn); + else + /* SMTP based command (VRFY, EXPN, NOOP, RSET or HELP) */ + result = smtp_perform_command(conn); + if(result) return result; - /* run the state-machine */ - if(conn->data->state.used_interface == Curl_if_multi) - result = smtp_multi_statemach(conn, dophase_done); - else { - result = smtp_easy_statemach(conn); - *dophase_done = TRUE; /* with the easy interface we are done here */ - } - *connected = conn->bits.tcpconnect; + /* Run the state-machine */ + result = smtp_multi_statemach(conn, dophase_done); + + *connected = conn->bits.tcpconnect[FIRSTSOCKET]; if(*dophase_done) DEBUGF(infof(conn->data, "DO phase is complete\n")); @@ -1258,45 +1333,16 @@ CURLcode smtp_perform(struct connectdata *conn, */ static CURLcode smtp_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 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"); + /* Parse the custom request */ + result = smtp_parse_custom_request(conn); if(result) return result; - state(conn, SMTP_QUIT); - result = smtp_easy_statemach(conn); + result = smtp_regular_transfer(conn, done); return result; } @@ -1308,59 +1354,59 @@ static CURLcode smtp_quit(struct connectdata *conn) * Disconnect from an SMTP server. Cleanup protocol-specific per-connection * resources. BLOCKING. */ -static CURLcode smtp_disconnect(struct connectdata *conn) +static CURLcode smtp_disconnect(struct connectdata *conn, bool dead_connection) { - struct smtp_conn *smtpc= &conn->proto.smtpc; + struct smtp_conn *smtpc = &conn->proto.smtpc; /* 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(smtpc->pp.conn) - (void)smtp_quit(conn); /* ignore errors on the LOGOUT */ + if(!dead_connection && smtpc->pp.conn && smtpc->pp.conn->bits.protoconnstart) + if(!smtp_perform_quit(conn)) + (void)smtp_block_statemach(conn); /* ignore errors on QUIT */ + /* Disconnect from the server */ Curl_pp_disconnect(&smtpc->pp); - /* This won't already be freed in some error cases */ + /* Cleanup the SASL module */ + Curl_sasl_cleanup(conn, smtpc->sasl.authused); + + /* Cleanup our connection based variables */ Curl_safefree(smtpc->domain); - smtpc->domain = NULL; return CURLE_OK; } -/* call this when the DO phase has completed */ -static CURLcode smtp_dophase_done(struct connectdata *conn, - bool connected) +/* 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_conn *smtpc= &conn->proto.smtpc; + struct SMTP *smtp = conn->data->req.protop; + (void)connected; if(smtp->transfer != FTPTRANSFER_BODY) /* no data to transfer */ Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL); - free(smtpc->domain); - smtpc->domain = NULL; - return CURLE_OK; } -/* called from multi.c while DOing */ -static CURLcode smtp_doing(struct connectdata *conn, - bool *dophase_done) +/* Called from multi.c while DOing */ +static CURLcode smtp_doing(struct connectdata *conn, bool *dophase_done) { - CURLcode result; - result = smtp_multi_statemach(conn, dophase_done); + CURLcode result = smtp_multi_statemach(conn, dophase_done); - if(*dophase_done) { + if(result) + DEBUGF(infof(conn->data, "DO phase failed\n")); + else if(*dophase_done) { result = smtp_dophase_done(conn, FALSE /* not connected */); DEBUGF(infof(conn->data, "DO phase is complete\n")); } + return result; } @@ -1373,44 +1419,39 @@ static CURLcode smtp_doing(struct connectdata *conn, * Performs all commands done before a regular transfer between a local and a * remote host. */ -static -CURLcode smtp_regular_transfer(struct connectdata *conn, - bool *dophase_done) +static CURLcode smtp_regular_transfer(struct connectdata *conn, + bool *dophase_done) { - CURLcode result=CURLE_OK; - bool connected=FALSE; + CURLcode result = CURLE_OK; + bool connected = FALSE; struct SessionHandle *data = conn->data; - data->req.size = -1; /* make sure this is unknown at this point */ + /* 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); - - result = smtp_perform(conn, - &connected, /* have we connected after PASV/PORT */ - dophase_done); /* all commands in the DO-phase done? */ - - if(CURLE_OK == result) { + Curl_pgrsSetUploadSize(data, -1); + Curl_pgrsSetDownloadSize(data, -1); - if(!*dophase_done) - /* the DO phase has not completed yet */ - return CURLE_OK; + /* Carry out the perform */ + result = smtp_perform(conn, &connected, dophase_done); + /* Perform post DO phase operations if necessary */ + if(!result && *dophase_done) result = smtp_dophase_done(conn, connected); - if(result) - return result; - } return result; } -static CURLcode smtp_setup_connection(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) @@ -1423,83 +1464,207 @@ static CURLcode smtp_setup_connection(struct connectdata * conn) return CURLE_UNSUPPORTED_PROTOCOL; #endif } - /* - * 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; + /* set it up as a HTTP connection instead */ + return conn->handler->setup_connection(conn); + #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; } -CURLcode Curl_smtp_escape_eob(struct connectdata *conn, ssize_t nread) +/*********************************************************************** + * + * 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 *ptr = conn->options; + + smtpc->sasl.resetprefs = TRUE; + + while(!result && ptr && *ptr) { + const char *key = ptr; + const char *value; + + while(*ptr && *ptr != '=') + ptr++; + + value = ptr + 1; + + while(*ptr && *ptr != ';') + ptr++; + + if(strnequal(key, "AUTH=", 5)) + result = Curl_sasl_parse_url_auth_option(&smtpc->sasl, + value, ptr - value); + else + result = CURLE_URL_MALFORMAT; + + if(*ptr == ';') + ptr++; + } + + 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); +} + +/*********************************************************************** + * + * smtp_parse_custom_request() + * + * Parse the custom request. + */ +static CURLcode smtp_parse_custom_request(struct connectdata *conn) { - /* When sending SMTP payload, we must detect CRLF.CRLF sequences in - * the data and make sure it is sent as CRLF..CRLF instead, as - * otherwise it will wrongly be detected as end of data by the server. - */ + CURLcode result = CURLE_OK; + struct SessionHandle *data = conn->data; + struct SMTP *smtp = data->req.protop; + const char *custom = data->set.str[STRING_CUSTOMREQUEST]; + + /* URL decode the custom request */ + if(custom) + result = Curl_urldecode(data, custom, 0, &smtp->custom, NULL, TRUE); + + return result; +} + +CURLcode Curl_smtp_escape_eob(struct connectdata *conn, const 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 + */ ssize_t i; ssize_t si; - struct smtp_conn *smtpc = &conn->proto.smtpc; struct SessionHandle *data = conn->data; + struct SMTP *smtp = data->req.protop; + char *scratch = data->state.scratch; + char *newscratch = NULL; + char *oldscratch = NULL; + size_t eob_sent; - if(data->state.scratch == NULL) - data->state.scratch = malloc(2*BUFSIZE); - if(data->state.scratch == NULL) { - failf (data, "Failed to alloc scratch buffer!"); - return CURLE_OUT_OF_MEMORY; + /* Do we need to allocate a scratch buffer? */ + if(!scratch || data->set.crlf) { + oldscratch = scratch; + + scratch = newscratch = malloc(2 * BUFSIZE); + if(!newscratch) { + failf(data, "Failed to alloc scratch buffer!"); + + return CURLE_OUT_OF_MEMORY; + } } + + /* Have we already sent part of the EOB? */ + eob_sent = smtp->eob; + /* 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++, si++) { - ssize_t left = nread - i; - - if(left>= (ssize_t)(SMTP_EOB_LEN-smtpc->eob)) { - if(!memcmp(SMTP_EOB+smtpc->eob, &data->req.upload_fromhere[i], - SMTP_EOB_LEN-smtpc->eob)) { - /* It matched, copy the replacement data to the target buffer - instead. Note that the replacement does not contain the - trailing CRLF but we instead continue to match on that one - to deal with repeated sequences. Like CRLF.CRLF.CRLF etc - */ - memcpy(&data->state.scratch[si], SMTP_EOB_REPL, - SMTP_EOB_REPL_LEN); - si+=SMTP_EOB_REPL_LEN-1; /* minus one since the for() increments - it */ - i+=SMTP_EOB_LEN-smtpc->eob-1-2; - smtpc->eob = 0; /* start over */ - continue; - } + for(i = 0, si = 0; i < nread; i++) { + 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(!memcmp(SMTP_EOB+smtpc->eob, &data->req.upload_fromhere[i], - left)) { - /* the last piece of the data matches the EOB so we can't send that - until we know the rest of it */ - smtpc->eob += left; - break; + else if(smtp->eob) { + /* A previous substring matched so output that first */ + memcpy(&scratch[si], &SMTP_EOB[eob_sent], smtp->eob - eob_sent); + si += smtp->eob - eob_sent; + + /* Then compare the first byte */ + if(SMTP_EOB[0] == data->req.upload_fromhere[i]) + smtp->eob = 1; + else + smtp->eob = 0; + + eob_sent = 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-5321, sect. 4.5.2 */ + if(SMTP_EOB_FIND_LEN == smtp->eob) { + /* Copy the replacement data to the target buffer */ + memcpy(&scratch[si], &SMTP_EOB_REPL[eob_sent], + SMTP_EOB_REPL_LEN - eob_sent); + si += SMTP_EOB_REPL_LEN - eob_sent; + smtp->eob = 0; + eob_sent = 0; } + else if(!smtp->eob) + scratch[si++] = data->req.upload_fromhere[i]; + } - data->state.scratch[si] = data->req.upload_fromhere[i]; - } /* for() */ + if(smtp->eob - eob_sent) { + /* A substring matched before processing ended so output that now */ + memcpy(&scratch[si], &SMTP_EOB[eob_sent], smtp->eob - eob_sent); + si += smtp->eob - eob_sent; + } + /* Only use the new buffer if we replaced something */ if(si != nread) { - /* only use the new buffer if we replaced something */ - nread = si; + /* Upload from the new (replaced) buffer instead */ + data->req.upload_fromhere = scratch; + + /* Save the buffer so it can be freed later */ + data->state.scratch = scratch; - /* upload from the new (replaced) buffer instead */ - data->req.upload_fromhere = data->state.scratch; + /* Free the old scratch buffer */ + free(oldscratch); - /* set the new amount too */ - data->req.upload_present = nread; + /* Set the new amount too */ + data->req.upload_present = si; } + else + free(newscratch); + return CURLE_OK; } |