Home | History | Annotate | Download | only in lib
      1 /***************************************************************************
      2  *                                  _   _ ____  _
      3  *  Project                     ___| | | |  _ \| |
      4  *                             / __| | | | |_) | |
      5  *                            | (__| |_| |  _ <| |___
      6  *                             \___|\___/|_| \_\_____|
      7  *
      8  * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel (at) haxx.se>, et al.
      9  *
     10  * This software is licensed as described in the file COPYING, which
     11  * you should have received as part of this distribution. The terms
     12  * are also available at https://curl.haxx.se/docs/copyright.html.
     13  *
     14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
     15  * copies of the Software, and permit persons to whom the Software is
     16  * furnished to do so, under the terms of the COPYING file.
     17  *
     18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
     19  * KIND, either express or implied.
     20  *
     21  * RFC1870 SMTP Service Extension for Message Size
     22  * RFC2195 CRAM-MD5 authentication
     23  * RFC2831 DIGEST-MD5 authentication
     24  * RFC3207 SMTP over TLS
     25  * RFC4422 Simple Authentication and Security Layer (SASL)
     26  * RFC4616 PLAIN authentication
     27  * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism
     28  * RFC4954 SMTP Authentication
     29  * RFC5321 SMTP protocol
     30  * RFC6749 OAuth 2.0 Authorization Framework
     31  * Draft   SMTP URL Interface   <draft-earhart-url-smtp-00.txt>
     32  * Draft   LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt>
     33  *
     34  ***************************************************************************/
     35 
     36 #include "curl_setup.h"
     37 
     38 #ifndef CURL_DISABLE_SMTP
     39 
     40 #ifdef HAVE_NETINET_IN_H
     41 #include <netinet/in.h>
     42 #endif
     43 #ifdef HAVE_ARPA_INET_H
     44 #include <arpa/inet.h>
     45 #endif
     46 #ifdef HAVE_UTSNAME_H
     47 #include <sys/utsname.h>
     48 #endif
     49 #ifdef HAVE_NETDB_H
     50 #include <netdb.h>
     51 #endif
     52 #ifdef __VMS
     53 #include <in.h>
     54 #include <inet.h>
     55 #endif
     56 
     57 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
     58 #undef in_addr_t
     59 #define in_addr_t unsigned long
     60 #endif
     61 
     62 #include <curl/curl.h>
     63 #include "urldata.h"
     64 #include "sendf.h"
     65 #include "hostip.h"
     66 #include "progress.h"
     67 #include "transfer.h"
     68 #include "escape.h"
     69 #include "http.h" /* for HTTP proxy tunnel stuff */
     70 #include "mime.h"
     71 #include "socks.h"
     72 #include "smtp.h"
     73 #include "strtoofft.h"
     74 #include "strcase.h"
     75 #include "vtls/vtls.h"
     76 #include "connect.h"
     77 #include "strerror.h"
     78 #include "select.h"
     79 #include "multiif.h"
     80 #include "url.h"
     81 #include "curl_gethostname.h"
     82 #include "curl_sasl.h"
     83 #include "warnless.h"
     84 /* The last 3 #include files should be in this order */
     85 #include "curl_printf.h"
     86 #include "curl_memory.h"
     87 #include "memdebug.h"
     88 
     89 /* Local API functions */
     90 static CURLcode smtp_regular_transfer(struct connectdata *conn, bool *done);
     91 static CURLcode smtp_do(struct connectdata *conn, bool *done);
     92 static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
     93                           bool premature);
     94 static CURLcode smtp_connect(struct connectdata *conn, bool *done);
     95 static CURLcode smtp_disconnect(struct connectdata *conn, bool dead);
     96 static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done);
     97 static int smtp_getsock(struct connectdata *conn, curl_socket_t *socks,
     98                         int numsocks);
     99 static CURLcode smtp_doing(struct connectdata *conn, bool *dophase_done);
    100 static CURLcode smtp_setup_connection(struct connectdata *conn);
    101 static CURLcode smtp_parse_url_options(struct connectdata *conn);
    102 static CURLcode smtp_parse_url_path(struct connectdata *conn);
    103 static CURLcode smtp_parse_custom_request(struct connectdata *conn);
    104 static CURLcode smtp_perform_auth(struct connectdata *conn, const char *mech,
    105                                   const char *initresp);
    106 static CURLcode smtp_continue_auth(struct connectdata *conn, const char *resp);
    107 static void smtp_get_message(char *buffer, char **outptr);
    108 
    109 /*
    110  * SMTP protocol handler.
    111  */
    112 
    113 const struct Curl_handler Curl_handler_smtp = {
    114   "SMTP",                           /* scheme */
    115   smtp_setup_connection,            /* setup_connection */
    116   smtp_do,                          /* do_it */
    117   smtp_done,                        /* done */
    118   ZERO_NULL,                        /* do_more */
    119   smtp_connect,                     /* connect_it */
    120   smtp_multi_statemach,             /* connecting */
    121   smtp_doing,                       /* doing */
    122   smtp_getsock,                     /* proto_getsock */
    123   smtp_getsock,                     /* doing_getsock */
    124   ZERO_NULL,                        /* domore_getsock */
    125   ZERO_NULL,                        /* perform_getsock */
    126   smtp_disconnect,                  /* disconnect */
    127   ZERO_NULL,                        /* readwrite */
    128   ZERO_NULL,                        /* connection_check */
    129   PORT_SMTP,                        /* defport */
    130   CURLPROTO_SMTP,                   /* protocol */
    131   PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY | /* flags */
    132   PROTOPT_URLOPTIONS
    133 };
    134 
    135 #ifdef USE_SSL
    136 /*
    137  * SMTPS protocol handler.
    138  */
    139 
    140 const struct Curl_handler Curl_handler_smtps = {
    141   "SMTPS",                          /* scheme */
    142   smtp_setup_connection,            /* setup_connection */
    143   smtp_do,                          /* do_it */
    144   smtp_done,                        /* done */
    145   ZERO_NULL,                        /* do_more */
    146   smtp_connect,                     /* connect_it */
    147   smtp_multi_statemach,             /* connecting */
    148   smtp_doing,                       /* doing */
    149   smtp_getsock,                     /* proto_getsock */
    150   smtp_getsock,                     /* doing_getsock */
    151   ZERO_NULL,                        /* domore_getsock */
    152   ZERO_NULL,                        /* perform_getsock */
    153   smtp_disconnect,                  /* disconnect */
    154   ZERO_NULL,                        /* readwrite */
    155   ZERO_NULL,                        /* connection_check */
    156   PORT_SMTPS,                       /* defport */
    157   CURLPROTO_SMTPS,                  /* protocol */
    158   PROTOPT_CLOSEACTION | PROTOPT_SSL
    159   | PROTOPT_NOURLQUERY | PROTOPT_URLOPTIONS /* flags */
    160 };
    161 #endif
    162 
    163 /* SASL parameters for the smtp protocol */
    164 static const struct SASLproto saslsmtp = {
    165   "smtp",                     /* The service name */
    166   334,                        /* Code received when continuation is expected */
    167   235,                        /* Code to receive upon authentication success */
    168   512 - 8,                    /* Maximum initial response length (no max) */
    169   smtp_perform_auth,          /* Send authentication command */
    170   smtp_continue_auth,         /* Send authentication continuation */
    171   smtp_get_message            /* Get SASL response message */
    172 };
    173 
    174 #ifdef USE_SSL
    175 static void smtp_to_smtps(struct connectdata *conn)
    176 {
    177   /* Change the connection handler */
    178   conn->handler = &Curl_handler_smtps;
    179 
    180   /* Set the connection's upgraded to TLS flag */
    181   conn->tls_upgraded = TRUE;
    182 }
    183 #else
    184 #define smtp_to_smtps(x) Curl_nop_stmt
    185 #endif
    186 
    187 /***********************************************************************
    188  *
    189  * smtp_endofresp()
    190  *
    191  * Checks for an ending SMTP status code at the start of the given string, but
    192  * also detects various capabilities from the EHLO response including the
    193  * supported authentication mechanisms.
    194  */
    195 static bool smtp_endofresp(struct connectdata *conn, char *line, size_t len,
    196                            int *resp)
    197 {
    198   struct smtp_conn *smtpc = &conn->proto.smtpc;
    199   bool result = FALSE;
    200 
    201   /* Nothing for us */
    202   if(len < 4 || !ISDIGIT(line[0]) || !ISDIGIT(line[1]) || !ISDIGIT(line[2]))
    203     return FALSE;
    204 
    205   /* Do we have a command response? This should be the response code followed
    206      by a space and optionally some text as per RFC-5321 and as outlined in
    207      Section 4. Examples of RFC-4954 but some e-mail servers ignore this and
    208      only send the response code instead as per Section 4.2. */
    209   if(line[3] == ' ' || len == 5) {
    210     char tmpline[6];
    211 
    212     result = TRUE;
    213     memset(tmpline, '\0', sizeof(tmpline));
    214     memcpy(tmpline, line, (len == 5 ? 5 : 3));
    215     *resp = curlx_sltosi(strtol(tmpline, NULL, 10));
    216 
    217     /* Make sure real server never sends internal value */
    218     if(*resp == 1)
    219       *resp = 0;
    220   }
    221   /* Do we have a multiline (continuation) response? */
    222   else if(line[3] == '-' &&
    223           (smtpc->state == SMTP_EHLO || smtpc->state == SMTP_COMMAND)) {
    224     result = TRUE;
    225     *resp = 1;  /* Internal response code */
    226   }
    227 
    228   return result;
    229 }
    230 
    231 /***********************************************************************
    232  *
    233  * smtp_get_message()
    234  *
    235  * Gets the authentication message from the response buffer.
    236  */
    237 static void smtp_get_message(char *buffer, char **outptr)
    238 {
    239   size_t len = strlen(buffer);
    240   char *message = NULL;
    241 
    242   if(len > 4) {
    243     /* Find the start of the message */
    244     len -= 4;
    245     for(message = buffer + 4; *message == ' ' || *message == '\t';
    246         message++, len--)
    247       ;
    248 
    249     /* Find the end of the message */
    250     for(; len--;)
    251       if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' &&
    252          message[len] != '\t')
    253         break;
    254 
    255     /* Terminate the message */
    256     if(++len) {
    257       message[len] = '\0';
    258     }
    259   }
    260   else
    261     /* junk input => zero length output */
    262     message = &buffer[len];
    263 
    264   *outptr = message;
    265 }
    266 
    267 /***********************************************************************
    268  *
    269  * state()
    270  *
    271  * This is the ONLY way to change SMTP state!
    272  */
    273 static void state(struct connectdata *conn, smtpstate newstate)
    274 {
    275   struct smtp_conn *smtpc = &conn->proto.smtpc;
    276 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
    277   /* for debug purposes */
    278   static const char * const names[] = {
    279     "STOP",
    280     "SERVERGREET",
    281     "EHLO",
    282     "HELO",
    283     "STARTTLS",
    284     "UPGRADETLS",
    285     "AUTH",
    286     "COMMAND",
    287     "MAIL",
    288     "RCPT",
    289     "DATA",
    290     "POSTDATA",
    291     "QUIT",
    292     /* LAST */
    293   };
    294 
    295   if(smtpc->state != newstate)
    296     infof(conn->data, "SMTP %p state change from %s to %s\n",
    297           (void *)smtpc, names[smtpc->state], names[newstate]);
    298 #endif
    299 
    300   smtpc->state = newstate;
    301 }
    302 
    303 /***********************************************************************
    304  *
    305  * smtp_perform_ehlo()
    306  *
    307  * Sends the EHLO command to not only initialise communication with the ESMTP
    308  * server but to also obtain a list of server side supported capabilities.
    309  */
    310 static CURLcode smtp_perform_ehlo(struct connectdata *conn)
    311 {
    312   CURLcode result = CURLE_OK;
    313   struct smtp_conn *smtpc = &conn->proto.smtpc;
    314 
    315   smtpc->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanism yet */
    316   smtpc->sasl.authused = SASL_AUTH_NONE;  /* Clear the authentication mechanism
    317                                              used for esmtp connections */
    318   smtpc->tls_supported = FALSE;           /* Clear the TLS capability */
    319   smtpc->auth_supported = FALSE;          /* Clear the AUTH capability */
    320 
    321   /* Send the EHLO command */
    322   result = Curl_pp_sendf(&smtpc->pp, "EHLO %s", smtpc->domain);
    323 
    324   if(!result)
    325     state(conn, SMTP_EHLO);
    326 
    327   return result;
    328 }
    329 
    330 /***********************************************************************
    331  *
    332  * smtp_perform_helo()
    333  *
    334  * Sends the HELO command to initialise communication with the SMTP server.
    335  */
    336 static CURLcode smtp_perform_helo(struct connectdata *conn)
    337 {
    338   CURLcode result = CURLE_OK;
    339   struct smtp_conn *smtpc = &conn->proto.smtpc;
    340 
    341   smtpc->sasl.authused = SASL_AUTH_NONE; /* No authentication mechanism used
    342                                             in smtp connections */
    343 
    344   /* Send the HELO command */
    345   result = Curl_pp_sendf(&smtpc->pp, "HELO %s", smtpc->domain);
    346 
    347   if(!result)
    348     state(conn, SMTP_HELO);
    349 
    350   return result;
    351 }
    352 
    353 /***********************************************************************
    354  *
    355  * smtp_perform_starttls()
    356  *
    357  * Sends the STLS command to start the upgrade to TLS.
    358  */
    359 static CURLcode smtp_perform_starttls(struct connectdata *conn)
    360 {
    361   CURLcode result = CURLE_OK;
    362 
    363   /* Send the STARTTLS command */
    364   result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "STARTTLS");
    365 
    366   if(!result)
    367     state(conn, SMTP_STARTTLS);
    368 
    369   return result;
    370 }
    371 
    372 /***********************************************************************
    373  *
    374  * smtp_perform_upgrade_tls()
    375  *
    376  * Performs the upgrade to TLS.
    377  */
    378 static CURLcode smtp_perform_upgrade_tls(struct connectdata *conn)
    379 {
    380   CURLcode result = CURLE_OK;
    381   struct smtp_conn *smtpc = &conn->proto.smtpc;
    382 
    383   /* Start the SSL connection */
    384   result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone);
    385 
    386   if(!result) {
    387     if(smtpc->state != SMTP_UPGRADETLS)
    388       state(conn, SMTP_UPGRADETLS);
    389 
    390     if(smtpc->ssldone) {
    391       smtp_to_smtps(conn);
    392       result = smtp_perform_ehlo(conn);
    393     }
    394   }
    395 
    396   return result;
    397 }
    398 
    399 /***********************************************************************
    400  *
    401  * smtp_perform_auth()
    402  *
    403  * Sends an AUTH command allowing the client to login with the given SASL
    404  * authentication mechanism.
    405  */
    406 static CURLcode smtp_perform_auth(struct connectdata *conn,
    407                                   const char *mech,
    408                                   const char *initresp)
    409 {
    410   CURLcode result = CURLE_OK;
    411   struct smtp_conn *smtpc = &conn->proto.smtpc;
    412 
    413   if(initresp) {                                  /* AUTH <mech> ...<crlf> */
    414     /* Send the AUTH command with the initial response */
    415     result = Curl_pp_sendf(&smtpc->pp, "AUTH %s %s", mech, initresp);
    416   }
    417   else {
    418     /* Send the AUTH command */
    419     result = Curl_pp_sendf(&smtpc->pp, "AUTH %s", mech);
    420   }
    421 
    422   return result;
    423 }
    424 
    425 /***********************************************************************
    426  *
    427  * smtp_continue_auth()
    428  *
    429  * Sends SASL continuation data or cancellation.
    430  */
    431 static CURLcode smtp_continue_auth(struct connectdata *conn, const char *resp)
    432 {
    433   struct smtp_conn *smtpc = &conn->proto.smtpc;
    434 
    435   return Curl_pp_sendf(&smtpc->pp, "%s", resp);
    436 }
    437 
    438 /***********************************************************************
    439  *
    440  * smtp_perform_authentication()
    441  *
    442  * Initiates the authentication sequence, with the appropriate SASL
    443  * authentication mechanism.
    444  */
    445 static CURLcode smtp_perform_authentication(struct connectdata *conn)
    446 {
    447   CURLcode result = CURLE_OK;
    448   struct smtp_conn *smtpc = &conn->proto.smtpc;
    449   saslprogress progress;
    450 
    451   /* Check we have enough data to authenticate with, and the
    452      server supports authentiation, and end the connect phase if not */
    453   if(!smtpc->auth_supported ||
    454      !Curl_sasl_can_authenticate(&smtpc->sasl, conn)) {
    455     state(conn, SMTP_STOP);
    456     return result;
    457   }
    458 
    459   /* Calculate the SASL login details */
    460   result = Curl_sasl_start(&smtpc->sasl, conn, FALSE, &progress);
    461 
    462   if(!result) {
    463     if(progress == SASL_INPROGRESS)
    464       state(conn, SMTP_AUTH);
    465     else {
    466       /* Other mechanisms not supported */
    467       infof(conn->data, "No known authentication mechanisms supported!\n");
    468       result = CURLE_LOGIN_DENIED;
    469     }
    470   }
    471 
    472   return result;
    473 }
    474 
    475 /***********************************************************************
    476  *
    477  * smtp_perform_command()
    478  *
    479  * Sends a SMTP based command.
    480  */
    481 static CURLcode smtp_perform_command(struct connectdata *conn)
    482 {
    483   CURLcode result = CURLE_OK;
    484   struct Curl_easy *data = conn->data;
    485   struct SMTP *smtp = data->req.protop;
    486 
    487   /* Send the command */
    488   if(smtp->rcpt)
    489     result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s %s",
    490                            smtp->custom && smtp->custom[0] != '\0' ?
    491                            smtp->custom : "VRFY",
    492                            smtp->rcpt->data);
    493   else
    494     result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s",
    495                            smtp->custom && smtp->custom[0] != '\0' ?
    496                            smtp->custom : "HELP");
    497 
    498   if(!result)
    499     state(conn, SMTP_COMMAND);
    500 
    501   return result;
    502 }
    503 
    504 /***********************************************************************
    505  *
    506  * smtp_perform_mail()
    507  *
    508  * Sends an MAIL command to initiate the upload of a message.
    509  */
    510 static CURLcode smtp_perform_mail(struct connectdata *conn)
    511 {
    512   char *from = NULL;
    513   char *auth = NULL;
    514   char *size = NULL;
    515   CURLcode result = CURLE_OK;
    516   struct Curl_easy *data = conn->data;
    517 
    518   /* Calculate the FROM parameter */
    519   if(!data->set.str[STRING_MAIL_FROM])
    520     /* Null reverse-path, RFC-5321, sect. 3.6.3 */
    521     from = strdup("<>");
    522   else if(data->set.str[STRING_MAIL_FROM][0] == '<')
    523     from = aprintf("%s", data->set.str[STRING_MAIL_FROM]);
    524   else
    525     from = aprintf("<%s>", data->set.str[STRING_MAIL_FROM]);
    526 
    527   if(!from)
    528     return CURLE_OUT_OF_MEMORY;
    529 
    530   /* Calculate the optional AUTH parameter */
    531   if(data->set.str[STRING_MAIL_AUTH] && conn->proto.smtpc.sasl.authused) {
    532     if(data->set.str[STRING_MAIL_AUTH][0] != '\0')
    533       auth = aprintf("%s", data->set.str[STRING_MAIL_AUTH]);
    534     else
    535       /* Empty AUTH, RFC-2554, sect. 5 */
    536       auth = strdup("<>");
    537 
    538     if(!auth) {
    539       free(from);
    540 
    541       return CURLE_OUT_OF_MEMORY;
    542     }
    543   }
    544 
    545   /* Prepare the mime data if some. */
    546   if(data->set.mimepost.kind != MIMEKIND_NONE) {
    547     /* Use the whole structure as data. */
    548     data->set.mimepost.flags &= ~MIME_BODY_ONLY;
    549 
    550     /* Add external headers and mime version. */
    551     curl_mime_headers(&data->set.mimepost, data->set.headers, 0);
    552     result = Curl_mime_prepare_headers(&data->set.mimepost, NULL,
    553                                        NULL, MIMESTRATEGY_MAIL);
    554 
    555     if(!result)
    556       if(!Curl_checkheaders(conn, "Mime-Version"))
    557         result = Curl_mime_add_header(&data->set.mimepost.curlheaders,
    558                                       "Mime-Version: 1.0");
    559 
    560     /* Make sure we will read the entire mime structure. */
    561     if(!result)
    562       result = Curl_mime_rewind(&data->set.mimepost);
    563 
    564     if(result) {
    565       free(from);
    566       free(auth);
    567       return result;
    568     }
    569 
    570     data->state.infilesize = Curl_mime_size(&data->set.mimepost);
    571 
    572     /* Read from mime structure. */
    573     data->state.fread_func = (curl_read_callback) Curl_mime_read;
    574     data->state.in = (void *) &data->set.mimepost;
    575   }
    576 
    577   /* Calculate the optional SIZE parameter */
    578   if(conn->proto.smtpc.size_supported && data->state.infilesize > 0) {
    579     size = aprintf("%" CURL_FORMAT_CURL_OFF_T, data->state.infilesize);
    580 
    581     if(!size) {
    582       free(from);
    583       free(auth);
    584 
    585       return CURLE_OUT_OF_MEMORY;
    586     }
    587   }
    588 
    589   /* Send the MAIL command */
    590   if(!auth && !size)
    591     result = Curl_pp_sendf(&conn->proto.smtpc.pp,
    592                            "MAIL FROM:%s", from);
    593   else if(auth && !size)
    594     result = Curl_pp_sendf(&conn->proto.smtpc.pp,
    595                            "MAIL FROM:%s AUTH=%s", from, auth);
    596   else if(auth && size)
    597     result = Curl_pp_sendf(&conn->proto.smtpc.pp,
    598                            "MAIL FROM:%s AUTH=%s SIZE=%s", from, auth, size);
    599   else
    600     result = Curl_pp_sendf(&conn->proto.smtpc.pp,
    601                            "MAIL FROM:%s SIZE=%s", from, size);
    602 
    603   free(from);
    604   free(auth);
    605   free(size);
    606 
    607   if(!result)
    608     state(conn, SMTP_MAIL);
    609 
    610   return result;
    611 }
    612 
    613 /***********************************************************************
    614  *
    615  * smtp_perform_rcpt_to()
    616  *
    617  * Sends a RCPT TO command for a given recipient as part of the message upload
    618  * process.
    619  */
    620 static CURLcode smtp_perform_rcpt_to(struct connectdata *conn)
    621 {
    622   CURLcode result = CURLE_OK;
    623   struct Curl_easy *data = conn->data;
    624   struct SMTP *smtp = data->req.protop;
    625 
    626   /* Send the RCPT TO command */
    627   if(smtp->rcpt->data[0] == '<')
    628     result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:%s",
    629                            smtp->rcpt->data);
    630   else
    631     result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:<%s>",
    632                            smtp->rcpt->data);
    633   if(!result)
    634     state(conn, SMTP_RCPT);
    635 
    636   return result;
    637 }
    638 
    639 /***********************************************************************
    640  *
    641  * smtp_perform_quit()
    642  *
    643  * Performs the quit action prior to sclose() being called.
    644  */
    645 static CURLcode smtp_perform_quit(struct connectdata *conn)
    646 {
    647   CURLcode result = CURLE_OK;
    648 
    649   /* Send the QUIT command */
    650   result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "QUIT");
    651 
    652   if(!result)
    653     state(conn, SMTP_QUIT);
    654 
    655   return result;
    656 }
    657 
    658 /* For the initial server greeting */
    659 static CURLcode smtp_state_servergreet_resp(struct connectdata *conn,
    660                                             int smtpcode,
    661                                             smtpstate instate)
    662 {
    663   CURLcode result = CURLE_OK;
    664   struct Curl_easy *data = conn->data;
    665 
    666   (void)instate; /* no use for this yet */
    667 
    668   if(smtpcode/100 != 2) {
    669     failf(data, "Got unexpected smtp-server response: %d", smtpcode);
    670     result = CURLE_WEIRD_SERVER_REPLY;
    671   }
    672   else
    673     result = smtp_perform_ehlo(conn);
    674 
    675   return result;
    676 }
    677 
    678 /* For STARTTLS responses */
    679 static CURLcode smtp_state_starttls_resp(struct connectdata *conn,
    680                                          int smtpcode,
    681                                          smtpstate instate)
    682 {
    683   CURLcode result = CURLE_OK;
    684   struct Curl_easy *data = conn->data;
    685 
    686   (void)instate; /* no use for this yet */
    687 
    688   if(smtpcode != 220) {
    689     if(data->set.use_ssl != CURLUSESSL_TRY) {
    690       failf(data, "STARTTLS denied, code %d", smtpcode);
    691       result = CURLE_USE_SSL_FAILED;
    692     }
    693     else
    694       result = smtp_perform_authentication(conn);
    695   }
    696   else
    697     result = smtp_perform_upgrade_tls(conn);
    698 
    699   return result;
    700 }
    701 
    702 /* For EHLO responses */
    703 static CURLcode smtp_state_ehlo_resp(struct connectdata *conn, int smtpcode,
    704                                      smtpstate instate)
    705 {
    706   CURLcode result = CURLE_OK;
    707   struct Curl_easy *data = conn->data;
    708   struct smtp_conn *smtpc = &conn->proto.smtpc;
    709   const char *line = data->state.buffer;
    710   size_t len = strlen(line);
    711 
    712   (void)instate; /* no use for this yet */
    713 
    714   if(smtpcode/100 != 2 && smtpcode != 1) {
    715     if(data->set.use_ssl <= CURLUSESSL_TRY || conn->ssl[FIRSTSOCKET].use)
    716       result = smtp_perform_helo(conn);
    717     else {
    718       failf(data, "Remote access denied: %d", smtpcode);
    719       result = CURLE_REMOTE_ACCESS_DENIED;
    720     }
    721   }
    722   else {
    723     line += 4;
    724     len -= 4;
    725 
    726     /* Does the server support the STARTTLS capability? */
    727     if(len >= 8 && !memcmp(line, "STARTTLS", 8))
    728       smtpc->tls_supported = TRUE;
    729 
    730     /* Does the server support the SIZE capability? */
    731     else if(len >= 4 && !memcmp(line, "SIZE", 4))
    732       smtpc->size_supported = TRUE;
    733 
    734     /* Does the server support authentication? */
    735     else if(len >= 5 && !memcmp(line, "AUTH ", 5)) {
    736       smtpc->auth_supported = TRUE;
    737 
    738       /* Advance past the AUTH keyword */
    739       line += 5;
    740       len -= 5;
    741 
    742       /* Loop through the data line */
    743       for(;;) {
    744         size_t llen;
    745         size_t wordlen;
    746         unsigned int mechbit;
    747 
    748         while(len &&
    749               (*line == ' ' || *line == '\t' ||
    750                *line == '\r' || *line == '\n')) {
    751 
    752           line++;
    753           len--;
    754         }
    755 
    756         if(!len)
    757           break;
    758 
    759         /* Extract the word */
    760         for(wordlen = 0; wordlen < len && line[wordlen] != ' ' &&
    761               line[wordlen] != '\t' && line[wordlen] != '\r' &&
    762               line[wordlen] != '\n';)
    763           wordlen++;
    764 
    765         /* Test the word for a matching authentication mechanism */
    766         mechbit = Curl_sasl_decode_mech(line, wordlen, &llen);
    767         if(mechbit && llen == wordlen)
    768           smtpc->sasl.authmechs |= mechbit;
    769 
    770         line += wordlen;
    771         len -= wordlen;
    772       }
    773     }
    774 
    775     if(smtpcode != 1) {
    776       if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
    777         /* We don't have a SSL/TLS connection yet, but SSL is requested */
    778         if(smtpc->tls_supported)
    779           /* Switch to TLS connection now */
    780           result = smtp_perform_starttls(conn);
    781         else if(data->set.use_ssl == CURLUSESSL_TRY)
    782           /* Fallback and carry on with authentication */
    783           result = smtp_perform_authentication(conn);
    784         else {
    785           failf(data, "STARTTLS not supported.");
    786           result = CURLE_USE_SSL_FAILED;
    787         }
    788       }
    789       else
    790         result = smtp_perform_authentication(conn);
    791     }
    792   }
    793 
    794   return result;
    795 }
    796 
    797 /* For HELO responses */
    798 static CURLcode smtp_state_helo_resp(struct connectdata *conn, int smtpcode,
    799                                      smtpstate instate)
    800 {
    801   CURLcode result = CURLE_OK;
    802   struct Curl_easy *data = conn->data;
    803 
    804   (void)instate; /* no use for this yet */
    805 
    806   if(smtpcode/100 != 2) {
    807     failf(data, "Remote access denied: %d", smtpcode);
    808     result = CURLE_REMOTE_ACCESS_DENIED;
    809   }
    810   else
    811     /* End of connect phase */
    812     state(conn, SMTP_STOP);
    813 
    814   return result;
    815 }
    816 
    817 /* For SASL authentication responses */
    818 static CURLcode smtp_state_auth_resp(struct connectdata *conn,
    819                                      int smtpcode,
    820                                      smtpstate instate)
    821 {
    822   CURLcode result = CURLE_OK;
    823   struct Curl_easy *data = conn->data;
    824   struct smtp_conn *smtpc = &conn->proto.smtpc;
    825   saslprogress progress;
    826 
    827   (void)instate; /* no use for this yet */
    828 
    829   result = Curl_sasl_continue(&smtpc->sasl, conn, smtpcode, &progress);
    830   if(!result)
    831     switch(progress) {
    832     case SASL_DONE:
    833       state(conn, SMTP_STOP);  /* Authenticated */
    834       break;
    835     case SASL_IDLE:            /* No mechanism left after cancellation */
    836       failf(data, "Authentication cancelled");
    837       result = CURLE_LOGIN_DENIED;
    838       break;
    839     default:
    840       break;
    841     }
    842 
    843   return result;
    844 }
    845 
    846 /* For command responses */
    847 static CURLcode smtp_state_command_resp(struct connectdata *conn, int smtpcode,
    848                                         smtpstate instate)
    849 {
    850   CURLcode result = CURLE_OK;
    851   struct Curl_easy *data = conn->data;
    852   struct SMTP *smtp = data->req.protop;
    853   char *line = data->state.buffer;
    854   size_t len = strlen(line);
    855 
    856   (void)instate; /* no use for this yet */
    857 
    858   if((smtp->rcpt && smtpcode/100 != 2 && smtpcode != 553 && smtpcode != 1) ||
    859      (!smtp->rcpt && smtpcode/100 != 2 && smtpcode != 1)) {
    860     failf(data, "Command failed: %d", smtpcode);
    861     result = CURLE_RECV_ERROR;
    862   }
    863   else {
    864     /* Temporarily add the LF character back and send as body to the client */
    865     if(!data->set.opt_no_body) {
    866       line[len] = '\n';
    867       result = Curl_client_write(conn, CLIENTWRITE_BODY, line, len + 1);
    868       line[len] = '\0';
    869     }
    870 
    871     if(smtpcode != 1) {
    872       if(smtp->rcpt) {
    873         smtp->rcpt = smtp->rcpt->next;
    874 
    875         if(smtp->rcpt) {
    876           /* Send the next command */
    877           result = smtp_perform_command(conn);
    878         }
    879         else
    880           /* End of DO phase */
    881           state(conn, SMTP_STOP);
    882       }
    883       else
    884         /* End of DO phase */
    885         state(conn, SMTP_STOP);
    886     }
    887   }
    888 
    889   return result;
    890 }
    891 
    892 /* For MAIL responses */
    893 static CURLcode smtp_state_mail_resp(struct connectdata *conn, int smtpcode,
    894                                      smtpstate instate)
    895 {
    896   CURLcode result = CURLE_OK;
    897   struct Curl_easy *data = conn->data;
    898 
    899   (void)instate; /* no use for this yet */
    900 
    901   if(smtpcode/100 != 2) {
    902     failf(data, "MAIL failed: %d", smtpcode);
    903     result = CURLE_SEND_ERROR;
    904   }
    905   else
    906     /* Start the RCPT TO command */
    907     result = smtp_perform_rcpt_to(conn);
    908 
    909   return result;
    910 }
    911 
    912 /* For RCPT responses */
    913 static CURLcode smtp_state_rcpt_resp(struct connectdata *conn, int smtpcode,
    914                                      smtpstate instate)
    915 {
    916   CURLcode result = CURLE_OK;
    917   struct Curl_easy *data = conn->data;
    918   struct SMTP *smtp = data->req.protop;
    919 
    920   (void)instate; /* no use for this yet */
    921 
    922   if(smtpcode/100 != 2) {
    923     failf(data, "RCPT failed: %d", smtpcode);
    924     result = CURLE_SEND_ERROR;
    925   }
    926   else {
    927     smtp->rcpt = smtp->rcpt->next;
    928 
    929     if(smtp->rcpt)
    930       /* Send the next RCPT TO command */
    931       result = smtp_perform_rcpt_to(conn);
    932     else {
    933       /* Send the DATA command */
    934       result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "DATA");
    935 
    936       if(!result)
    937         state(conn, SMTP_DATA);
    938     }
    939   }
    940 
    941   return result;
    942 }
    943 
    944 /* For DATA response */
    945 static CURLcode smtp_state_data_resp(struct connectdata *conn, int smtpcode,
    946                                      smtpstate instate)
    947 {
    948   CURLcode result = CURLE_OK;
    949   struct Curl_easy *data = conn->data;
    950 
    951   (void)instate; /* no use for this yet */
    952 
    953   if(smtpcode != 354) {
    954     failf(data, "DATA failed: %d", smtpcode);
    955     result = CURLE_SEND_ERROR;
    956   }
    957   else {
    958     /* Set the progress upload size */
    959     Curl_pgrsSetUploadSize(data, data->state.infilesize);
    960 
    961     /* SMTP upload */
    962     Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET);
    963 
    964     /* End of DO phase */
    965     state(conn, SMTP_STOP);
    966   }
    967 
    968   return result;
    969 }
    970 
    971 /* For POSTDATA responses, which are received after the entire DATA
    972    part has been sent to the server */
    973 static CURLcode smtp_state_postdata_resp(struct connectdata *conn,
    974                                          int smtpcode,
    975                                          smtpstate instate)
    976 {
    977   CURLcode result = CURLE_OK;
    978 
    979   (void)instate; /* no use for this yet */
    980 
    981   if(smtpcode != 250)
    982     result = CURLE_RECV_ERROR;
    983 
    984   /* End of DONE phase */
    985   state(conn, SMTP_STOP);
    986 
    987   return result;
    988 }
    989 
    990 static CURLcode smtp_statemach_act(struct connectdata *conn)
    991 {
    992   CURLcode result = CURLE_OK;
    993   curl_socket_t sock = conn->sock[FIRSTSOCKET];
    994   struct Curl_easy *data = conn->data;
    995   int smtpcode;
    996   struct smtp_conn *smtpc = &conn->proto.smtpc;
    997   struct pingpong *pp = &smtpc->pp;
    998   size_t nread = 0;
    999 
   1000   /* Busy upgrading the connection; right now all I/O is SSL/TLS, not SMTP */
   1001   if(smtpc->state == SMTP_UPGRADETLS)
   1002     return smtp_perform_upgrade_tls(conn);
   1003 
   1004   /* Flush any data that needs to be sent */
   1005   if(pp->sendleft)
   1006     return Curl_pp_flushsend(pp);
   1007 
   1008   do {
   1009     /* Read the response from the server */
   1010     result = Curl_pp_readresp(sock, pp, &smtpcode, &nread);
   1011     if(result)
   1012       return result;
   1013 
   1014     /* Store the latest response for later retrieval if necessary */
   1015     if(smtpc->state != SMTP_QUIT && smtpcode != 1)
   1016       data->info.httpcode = smtpcode;
   1017 
   1018     if(!smtpcode)
   1019       break;
   1020 
   1021     /* We have now received a full SMTP server response */
   1022     switch(smtpc->state) {
   1023     case SMTP_SERVERGREET:
   1024       result = smtp_state_servergreet_resp(conn, smtpcode, smtpc->state);
   1025       break;
   1026 
   1027     case SMTP_EHLO:
   1028       result = smtp_state_ehlo_resp(conn, smtpcode, smtpc->state);
   1029       break;
   1030 
   1031     case SMTP_HELO:
   1032       result = smtp_state_helo_resp(conn, smtpcode, smtpc->state);
   1033       break;
   1034 
   1035     case SMTP_STARTTLS:
   1036       result = smtp_state_starttls_resp(conn, smtpcode, smtpc->state);
   1037       break;
   1038 
   1039     case SMTP_AUTH:
   1040       result = smtp_state_auth_resp(conn, smtpcode, smtpc->state);
   1041       break;
   1042 
   1043     case SMTP_COMMAND:
   1044       result = smtp_state_command_resp(conn, smtpcode, smtpc->state);
   1045       break;
   1046 
   1047     case SMTP_MAIL:
   1048       result = smtp_state_mail_resp(conn, smtpcode, smtpc->state);
   1049       break;
   1050 
   1051     case SMTP_RCPT:
   1052       result = smtp_state_rcpt_resp(conn, smtpcode, smtpc->state);
   1053       break;
   1054 
   1055     case SMTP_DATA:
   1056       result = smtp_state_data_resp(conn, smtpcode, smtpc->state);
   1057       break;
   1058 
   1059     case SMTP_POSTDATA:
   1060       result = smtp_state_postdata_resp(conn, smtpcode, smtpc->state);
   1061       break;
   1062 
   1063     case SMTP_QUIT:
   1064       /* fallthrough, just stop! */
   1065     default:
   1066       /* internal error */
   1067       state(conn, SMTP_STOP);
   1068       break;
   1069     }
   1070   } while(!result && smtpc->state != SMTP_STOP && Curl_pp_moredata(pp));
   1071 
   1072   return result;
   1073 }
   1074 
   1075 /* Called repeatedly until done from multi.c */
   1076 static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done)
   1077 {
   1078   CURLcode result = CURLE_OK;
   1079   struct smtp_conn *smtpc = &conn->proto.smtpc;
   1080 
   1081   if((conn->handler->flags & PROTOPT_SSL) && !smtpc->ssldone) {
   1082     result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone);
   1083     if(result || !smtpc->ssldone)
   1084       return result;
   1085   }
   1086 
   1087   result = Curl_pp_statemach(&smtpc->pp, FALSE, FALSE);
   1088   *done = (smtpc->state == SMTP_STOP) ? TRUE : FALSE;
   1089 
   1090   return result;
   1091 }
   1092 
   1093 static CURLcode smtp_block_statemach(struct connectdata *conn,
   1094                                      bool disconnecting)
   1095 {
   1096   CURLcode result = CURLE_OK;
   1097   struct smtp_conn *smtpc = &conn->proto.smtpc;
   1098 
   1099   while(smtpc->state != SMTP_STOP && !result)
   1100     result = Curl_pp_statemach(&smtpc->pp, TRUE, disconnecting);
   1101 
   1102   return result;
   1103 }
   1104 
   1105 /* Allocate and initialize the SMTP struct for the current Curl_easy if
   1106    required */
   1107 static CURLcode smtp_init(struct connectdata *conn)
   1108 {
   1109   CURLcode result = CURLE_OK;
   1110   struct Curl_easy *data = conn->data;
   1111   struct SMTP *smtp;
   1112 
   1113   smtp = data->req.protop = calloc(sizeof(struct SMTP), 1);
   1114   if(!smtp)
   1115     result = CURLE_OUT_OF_MEMORY;
   1116 
   1117   return result;
   1118 }
   1119 
   1120 /* For the SMTP "protocol connect" and "doing" phases only */
   1121 static int smtp_getsock(struct connectdata *conn, curl_socket_t *socks,
   1122                         int numsocks)
   1123 {
   1124   return Curl_pp_getsock(&conn->proto.smtpc.pp, socks, numsocks);
   1125 }
   1126 
   1127 /***********************************************************************
   1128  *
   1129  * smtp_connect()
   1130  *
   1131  * This function should do everything that is to be considered a part of
   1132  * the connection phase.
   1133  *
   1134  * The variable pointed to by 'done' will be TRUE if the protocol-layer
   1135  * connect phase is done when this function returns, or FALSE if not.
   1136  */
   1137 static CURLcode smtp_connect(struct connectdata *conn, bool *done)
   1138 {
   1139   CURLcode result = CURLE_OK;
   1140   struct smtp_conn *smtpc = &conn->proto.smtpc;
   1141   struct pingpong *pp = &smtpc->pp;
   1142 
   1143   *done = FALSE; /* default to not done yet */
   1144 
   1145   /* We always support persistent connections in SMTP */
   1146   connkeep(conn, "SMTP default");
   1147 
   1148   /* Set the default response time-out */
   1149   pp->response_time = RESP_TIMEOUT;
   1150   pp->statemach_act = smtp_statemach_act;
   1151   pp->endofresp = smtp_endofresp;
   1152   pp->conn = conn;
   1153 
   1154   /* Initialize the SASL storage */
   1155   Curl_sasl_init(&smtpc->sasl, &saslsmtp);
   1156 
   1157   /* Initialise the pingpong layer */
   1158   Curl_pp_init(pp);
   1159 
   1160   /* Parse the URL options */
   1161   result = smtp_parse_url_options(conn);
   1162   if(result)
   1163     return result;
   1164 
   1165   /* Parse the URL path */
   1166   result = smtp_parse_url_path(conn);
   1167   if(result)
   1168     return result;
   1169 
   1170   /* Start off waiting for the server greeting response */
   1171   state(conn, SMTP_SERVERGREET);
   1172 
   1173   result = smtp_multi_statemach(conn, done);
   1174 
   1175   return result;
   1176 }
   1177 
   1178 /***********************************************************************
   1179  *
   1180  * smtp_done()
   1181  *
   1182  * The DONE function. This does what needs to be done after a single DO has
   1183  * performed.
   1184  *
   1185  * Input argument is already checked for validity.
   1186  */
   1187 static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
   1188                           bool premature)
   1189 {
   1190   CURLcode result = CURLE_OK;
   1191   struct Curl_easy *data = conn->data;
   1192   struct SMTP *smtp = data->req.protop;
   1193   struct pingpong *pp = &conn->proto.smtpc.pp;
   1194   char *eob;
   1195   ssize_t len;
   1196   ssize_t bytes_written;
   1197 
   1198   (void)premature;
   1199 
   1200   if(!smtp || !pp->conn)
   1201     return CURLE_OK;
   1202 
   1203   /* Cleanup our per-request based variables */
   1204   Curl_safefree(smtp->custom);
   1205 
   1206   if(status) {
   1207     connclose(conn, "SMTP done with bad status"); /* marked for closure */
   1208     result = status;         /* use the already set error code */
   1209   }
   1210   else if(!data->set.connect_only && data->set.mail_rcpt &&
   1211           (data->set.upload || data->set.mimepost.kind)) {
   1212     /* Calculate the EOB taking into account any terminating CRLF from the
   1213        previous line of the email or the CRLF of the DATA command when there
   1214        is "no mail data". RFC-5321, sect. 4.1.1.4.
   1215 
   1216        Note: As some SSL backends, such as OpenSSL, will cause Curl_write() to
   1217        fail when using a different pointer following a previous write, that
   1218        returned CURLE_AGAIN, we duplicate the EOB now rather than when the
   1219        bytes written doesn't equal len. */
   1220     if(smtp->trailing_crlf || !conn->data->state.infilesize) {
   1221       eob = strdup(SMTP_EOB + 2);
   1222       len = SMTP_EOB_LEN - 2;
   1223     }
   1224     else {
   1225       eob = strdup(SMTP_EOB);
   1226       len = SMTP_EOB_LEN;
   1227     }
   1228 
   1229     if(!eob)
   1230       return CURLE_OUT_OF_MEMORY;
   1231 
   1232     /* Send the end of block data */
   1233     result = Curl_write(conn, conn->writesockfd, eob, len, &bytes_written);
   1234     if(result) {
   1235       free(eob);
   1236       return result;
   1237     }
   1238 
   1239     if(bytes_written != len) {
   1240       /* The whole chunk was not sent so keep it around and adjust the
   1241          pingpong structure accordingly */
   1242       pp->sendthis = eob;
   1243       pp->sendsize = len;
   1244       pp->sendleft = len - bytes_written;
   1245     }
   1246     else {
   1247       /* Successfully sent so adjust the response timeout relative to now */
   1248       pp->response = Curl_now();
   1249 
   1250       free(eob);
   1251     }
   1252 
   1253     state(conn, SMTP_POSTDATA);
   1254 
   1255     /* Run the state-machine
   1256 
   1257        TODO: when the multi interface is used, this _really_ should be using
   1258        the smtp_multi_statemach function but we have no general support for
   1259        non-blocking DONE operations!
   1260     */
   1261     result = smtp_block_statemach(conn, FALSE);
   1262   }
   1263 
   1264   /* Clear the transfer mode for the next request */
   1265   smtp->transfer = FTPTRANSFER_BODY;
   1266 
   1267   return result;
   1268 }
   1269 
   1270 /***********************************************************************
   1271  *
   1272  * smtp_perform()
   1273  *
   1274  * This is the actual DO function for SMTP. Transfer a mail, send a command
   1275  * or get some data according to the options previously setup.
   1276  */
   1277 static CURLcode smtp_perform(struct connectdata *conn, bool *connected,
   1278                              bool *dophase_done)
   1279 {
   1280   /* This is SMTP and no proxy */
   1281   CURLcode result = CURLE_OK;
   1282   struct Curl_easy *data = conn->data;
   1283   struct SMTP *smtp = data->req.protop;
   1284 
   1285   DEBUGF(infof(conn->data, "DO phase starts\n"));
   1286 
   1287   if(data->set.opt_no_body) {
   1288     /* Requested no body means no transfer */
   1289     smtp->transfer = FTPTRANSFER_INFO;
   1290   }
   1291 
   1292   *dophase_done = FALSE; /* not done yet */
   1293 
   1294   /* Store the first recipient (or NULL if not specified) */
   1295   smtp->rcpt = data->set.mail_rcpt;
   1296 
   1297   /* Initial data character is the first character in line: it is implicitly
   1298      preceded by a virtual CRLF. */
   1299   smtp->trailing_crlf = TRUE;
   1300   smtp->eob = 2;
   1301 
   1302   /* Start the first command in the DO phase */
   1303   if((data->set.upload || data->set.mimepost.kind) && data->set.mail_rcpt)
   1304     /* MAIL transfer */
   1305     result = smtp_perform_mail(conn);
   1306   else
   1307     /* SMTP based command (VRFY, EXPN, NOOP, RSET or HELP) */
   1308     result = smtp_perform_command(conn);
   1309 
   1310   if(result)
   1311     return result;
   1312 
   1313   /* Run the state-machine */
   1314   result = smtp_multi_statemach(conn, dophase_done);
   1315 
   1316   *connected = conn->bits.tcpconnect[FIRSTSOCKET];
   1317 
   1318   if(*dophase_done)
   1319     DEBUGF(infof(conn->data, "DO phase is complete\n"));
   1320 
   1321   return result;
   1322 }
   1323 
   1324 /***********************************************************************
   1325  *
   1326  * smtp_do()
   1327  *
   1328  * This function is registered as 'curl_do' function. It decodes the path
   1329  * parts etc as a wrapper to the actual DO function (smtp_perform).
   1330  *
   1331  * The input argument is already checked for validity.
   1332  */
   1333 static CURLcode smtp_do(struct connectdata *conn, bool *done)
   1334 {
   1335   CURLcode result = CURLE_OK;
   1336 
   1337   *done = FALSE; /* default to false */
   1338 
   1339   /* Parse the custom request */
   1340   result = smtp_parse_custom_request(conn);
   1341   if(result)
   1342     return result;
   1343 
   1344   result = smtp_regular_transfer(conn, done);
   1345 
   1346   return result;
   1347 }
   1348 
   1349 /***********************************************************************
   1350  *
   1351  * smtp_disconnect()
   1352  *
   1353  * Disconnect from an SMTP server. Cleanup protocol-specific per-connection
   1354  * resources. BLOCKING.
   1355  */
   1356 static CURLcode smtp_disconnect(struct connectdata *conn, bool dead_connection)
   1357 {
   1358   struct smtp_conn *smtpc = &conn->proto.smtpc;
   1359 
   1360   /* We cannot send quit unconditionally. If this connection is stale or
   1361      bad in any way, sending quit and waiting around here will make the
   1362      disconnect wait in vain and cause more problems than we need to. */
   1363 
   1364   /* The SMTP session may or may not have been allocated/setup at this
   1365      point! */
   1366   if(!dead_connection && smtpc->pp.conn && smtpc->pp.conn->bits.protoconnstart)
   1367     if(!smtp_perform_quit(conn))
   1368       (void)smtp_block_statemach(conn, TRUE); /* ignore errors on QUIT */
   1369 
   1370   /* Disconnect from the server */
   1371   Curl_pp_disconnect(&smtpc->pp);
   1372 
   1373   /* Cleanup the SASL module */
   1374   Curl_sasl_cleanup(conn, smtpc->sasl.authused);
   1375 
   1376   /* Cleanup our connection based variables */
   1377   Curl_safefree(smtpc->domain);
   1378 
   1379   return CURLE_OK;
   1380 }
   1381 
   1382 /* Call this when the DO phase has completed */
   1383 static CURLcode smtp_dophase_done(struct connectdata *conn, bool connected)
   1384 {
   1385   struct SMTP *smtp = conn->data->req.protop;
   1386 
   1387   (void)connected;
   1388 
   1389   if(smtp->transfer != FTPTRANSFER_BODY)
   1390     /* no data to transfer */
   1391     Curl_setup_transfer(conn->data, -1, -1, FALSE, -1);
   1392 
   1393   return CURLE_OK;
   1394 }
   1395 
   1396 /* Called from multi.c while DOing */
   1397 static CURLcode smtp_doing(struct connectdata *conn, bool *dophase_done)
   1398 {
   1399   CURLcode result = smtp_multi_statemach(conn, dophase_done);
   1400 
   1401   if(result)
   1402     DEBUGF(infof(conn->data, "DO phase failed\n"));
   1403   else if(*dophase_done) {
   1404     result = smtp_dophase_done(conn, FALSE /* not connected */);
   1405 
   1406     DEBUGF(infof(conn->data, "DO phase is complete\n"));
   1407   }
   1408 
   1409   return result;
   1410 }
   1411 
   1412 /***********************************************************************
   1413  *
   1414  * smtp_regular_transfer()
   1415  *
   1416  * The input argument is already checked for validity.
   1417  *
   1418  * Performs all commands done before a regular transfer between a local and a
   1419  * remote host.
   1420  */
   1421 static CURLcode smtp_regular_transfer(struct connectdata *conn,
   1422                                       bool *dophase_done)
   1423 {
   1424   CURLcode result = CURLE_OK;
   1425   bool connected = FALSE;
   1426   struct Curl_easy *data = conn->data;
   1427 
   1428   /* Make sure size is unknown at this point */
   1429   data->req.size = -1;
   1430 
   1431   /* Set the progress data */
   1432   Curl_pgrsSetUploadCounter(data, 0);
   1433   Curl_pgrsSetDownloadCounter(data, 0);
   1434   Curl_pgrsSetUploadSize(data, -1);
   1435   Curl_pgrsSetDownloadSize(data, -1);
   1436 
   1437   /* Carry out the perform */
   1438   result = smtp_perform(conn, &connected, dophase_done);
   1439 
   1440   /* Perform post DO phase operations if necessary */
   1441   if(!result && *dophase_done)
   1442     result = smtp_dophase_done(conn, connected);
   1443 
   1444   return result;
   1445 }
   1446 
   1447 static CURLcode smtp_setup_connection(struct connectdata *conn)
   1448 {
   1449   CURLcode result;
   1450 
   1451   /* Clear the TLS upgraded flag */
   1452   conn->tls_upgraded = FALSE;
   1453 
   1454   /* Initialise the SMTP layer */
   1455   result = smtp_init(conn);
   1456   if(result)
   1457     return result;
   1458 
   1459   return CURLE_OK;
   1460 }
   1461 
   1462 /***********************************************************************
   1463  *
   1464  * smtp_parse_url_options()
   1465  *
   1466  * Parse the URL login options.
   1467  */
   1468 static CURLcode smtp_parse_url_options(struct connectdata *conn)
   1469 {
   1470   CURLcode result = CURLE_OK;
   1471   struct smtp_conn *smtpc = &conn->proto.smtpc;
   1472   const char *ptr = conn->options;
   1473 
   1474   smtpc->sasl.resetprefs = TRUE;
   1475 
   1476   while(!result && ptr && *ptr) {
   1477     const char *key = ptr;
   1478     const char *value;
   1479 
   1480     while(*ptr && *ptr != '=')
   1481       ptr++;
   1482 
   1483     value = ptr + 1;
   1484 
   1485     while(*ptr && *ptr != ';')
   1486       ptr++;
   1487 
   1488     if(strncasecompare(key, "AUTH=", 5))
   1489       result = Curl_sasl_parse_url_auth_option(&smtpc->sasl,
   1490                                                value, ptr - value);
   1491     else
   1492       result = CURLE_URL_MALFORMAT;
   1493 
   1494     if(*ptr == ';')
   1495       ptr++;
   1496   }
   1497 
   1498   return result;
   1499 }
   1500 
   1501 /***********************************************************************
   1502  *
   1503  * smtp_parse_url_path()
   1504  *
   1505  * Parse the URL path into separate path components.
   1506  */
   1507 static CURLcode smtp_parse_url_path(struct connectdata *conn)
   1508 {
   1509   /* The SMTP struct is already initialised in smtp_connect() */
   1510   struct Curl_easy *data = conn->data;
   1511   struct smtp_conn *smtpc = &conn->proto.smtpc;
   1512   const char *path = &data->state.up.path[1]; /* skip leading path */
   1513   char localhost[HOSTNAME_MAX + 1];
   1514 
   1515   /* Calculate the path if necessary */
   1516   if(!*path) {
   1517     if(!Curl_gethostname(localhost, sizeof(localhost)))
   1518       path = localhost;
   1519     else
   1520       path = "localhost";
   1521   }
   1522 
   1523   /* URL decode the path and use it as the domain in our EHLO */
   1524   return Curl_urldecode(conn->data, path, 0, &smtpc->domain, NULL, TRUE);
   1525 }
   1526 
   1527 /***********************************************************************
   1528  *
   1529  * smtp_parse_custom_request()
   1530  *
   1531  * Parse the custom request.
   1532  */
   1533 static CURLcode smtp_parse_custom_request(struct connectdata *conn)
   1534 {
   1535   CURLcode result = CURLE_OK;
   1536   struct Curl_easy *data = conn->data;
   1537   struct SMTP *smtp = data->req.protop;
   1538   const char *custom = data->set.str[STRING_CUSTOMREQUEST];
   1539 
   1540   /* URL decode the custom request */
   1541   if(custom)
   1542     result = Curl_urldecode(data, custom, 0, &smtp->custom, NULL, TRUE);
   1543 
   1544   return result;
   1545 }
   1546 
   1547 CURLcode Curl_smtp_escape_eob(struct connectdata *conn, const ssize_t nread)
   1548 {
   1549   /* When sending a SMTP payload we must detect CRLF. sequences making sure
   1550      they are sent as CRLF.. instead, as a . on the beginning of a line will
   1551      be deleted by the server when not part of an EOB terminator and a
   1552      genuine CRLF.CRLF which isn't escaped will wrongly be detected as end of
   1553      data by the server
   1554   */
   1555   ssize_t i;
   1556   ssize_t si;
   1557   struct Curl_easy *data = conn->data;
   1558   struct SMTP *smtp = data->req.protop;
   1559   char *scratch = data->state.scratch;
   1560   char *newscratch = NULL;
   1561   char *oldscratch = NULL;
   1562   size_t eob_sent;
   1563 
   1564   /* Do we need to allocate a scratch buffer? */
   1565   if(!scratch || data->set.crlf) {
   1566     oldscratch = scratch;
   1567 
   1568     scratch = newscratch = malloc(2 * data->set.upload_buffer_size);
   1569     if(!newscratch) {
   1570       failf(data, "Failed to alloc scratch buffer!");
   1571 
   1572       return CURLE_OUT_OF_MEMORY;
   1573     }
   1574   }
   1575   DEBUGASSERT(data->set.upload_buffer_size >= (size_t)nread);
   1576 
   1577   /* Have we already sent part of the EOB? */
   1578   eob_sent = smtp->eob;
   1579 
   1580   /* This loop can be improved by some kind of Boyer-Moore style of
   1581      approach but that is saved for later... */
   1582   for(i = 0, si = 0; i < nread; i++) {
   1583     if(SMTP_EOB[smtp->eob] == data->req.upload_fromhere[i]) {
   1584       smtp->eob++;
   1585 
   1586       /* Is the EOB potentially the terminating CRLF? */
   1587       if(2 == smtp->eob || SMTP_EOB_LEN == smtp->eob)
   1588         smtp->trailing_crlf = TRUE;
   1589       else
   1590         smtp->trailing_crlf = FALSE;
   1591     }
   1592     else if(smtp->eob) {
   1593       /* A previous substring matched so output that first */
   1594       memcpy(&scratch[si], &SMTP_EOB[eob_sent], smtp->eob - eob_sent);
   1595       si += smtp->eob - eob_sent;
   1596 
   1597       /* Then compare the first byte */
   1598       if(SMTP_EOB[0] == data->req.upload_fromhere[i])
   1599         smtp->eob = 1;
   1600       else
   1601         smtp->eob = 0;
   1602 
   1603       eob_sent = 0;
   1604 
   1605       /* Reset the trailing CRLF flag as there was more data */
   1606       smtp->trailing_crlf = FALSE;
   1607     }
   1608 
   1609     /* Do we have a match for CRLF. as per RFC-5321, sect. 4.5.2 */
   1610     if(SMTP_EOB_FIND_LEN == smtp->eob) {
   1611       /* Copy the replacement data to the target buffer */
   1612       memcpy(&scratch[si], &SMTP_EOB_REPL[eob_sent],
   1613              SMTP_EOB_REPL_LEN - eob_sent);
   1614       si += SMTP_EOB_REPL_LEN - eob_sent;
   1615       smtp->eob = 0;
   1616       eob_sent = 0;
   1617     }
   1618     else if(!smtp->eob)
   1619       scratch[si++] = data->req.upload_fromhere[i];
   1620   }
   1621 
   1622   if(smtp->eob - eob_sent) {
   1623     /* A substring matched before processing ended so output that now */
   1624     memcpy(&scratch[si], &SMTP_EOB[eob_sent], smtp->eob - eob_sent);
   1625     si += smtp->eob - eob_sent;
   1626   }
   1627 
   1628   /* Only use the new buffer if we replaced something */
   1629   if(si != nread) {
   1630     /* Upload from the new (replaced) buffer instead */
   1631     data->req.upload_fromhere = scratch;
   1632 
   1633     /* Save the buffer so it can be freed later */
   1634     data->state.scratch = scratch;
   1635 
   1636     /* Free the old scratch buffer */
   1637     free(oldscratch);
   1638 
   1639     /* Set the new amount too */
   1640     data->req.upload_present = si;
   1641   }
   1642   else
   1643     free(newscratch);
   1644 
   1645   return CURLE_OK;
   1646 }
   1647 
   1648 #endif /* CURL_DISABLE_SMTP */
   1649