quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

smtp.c (64691B)


      1 /***************************************************************************
      2  *                                  _   _ ____  _
      3  *  Project                     ___| | | |  _ \| |
      4  *                             / __| | | | |_) | |
      5  *                            | (__| |_| |  _ <| |___
      6  *                             \___|\___/|_| \_\_____|
      7  *
      8  * Copyright (C) Daniel Stenberg, <daniel@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.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  * SPDX-License-Identifier: curl
     22  *
     23  * RFC1870 SMTP Service Extension for Message Size
     24  * RFC2195 CRAM-MD5 authentication
     25  * RFC2831 DIGEST-MD5 authentication
     26  * RFC3207 SMTP over TLS
     27  * RFC4422 Simple Authentication and Security Layer (SASL)
     28  * RFC4616 PLAIN authentication
     29  * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism
     30  * RFC4954 SMTP Authentication
     31  * RFC5321 SMTP protocol
     32  * RFC5890 Internationalized Domain Names for Applications (IDNA)
     33  * RFC6531 SMTP Extension for Internationalized Email
     34  * RFC6532 Internationalized Email Headers
     35  * RFC6749 OAuth 2.0 Authorization Framework
     36  * RFC8314 Use of TLS for Email Submission and Access
     37  * Draft   SMTP URL Interface   <draft-earhart-url-smtp-00.txt>
     38  * Draft   LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt>
     39  *
     40  ***************************************************************************/
     41 
     42 #include "curl_setup.h"
     43 
     44 #ifndef CURL_DISABLE_SMTP
     45 
     46 #ifdef HAVE_NETINET_IN_H
     47 #include <netinet/in.h>
     48 #endif
     49 #ifdef HAVE_ARPA_INET_H
     50 #include <arpa/inet.h>
     51 #endif
     52 #ifdef HAVE_NETDB_H
     53 #include <netdb.h>
     54 #endif
     55 #ifdef __VMS
     56 #include <in.h>
     57 #include <inet.h>
     58 #endif
     59 
     60 #include <curl/curl.h>
     61 #include "urldata.h"
     62 #include "sendf.h"
     63 #include "hostip.h"
     64 #include "progress.h"
     65 #include "transfer.h"
     66 #include "escape.h"
     67 #include "http.h" /* for HTTP proxy tunnel stuff */
     68 #include "mime.h"
     69 #include "socks.h"
     70 #include "smtp.h"
     71 #include "vtls/vtls.h"
     72 #include "cfilters.h"
     73 #include "connect.h"
     74 #include "select.h"
     75 #include "multiif.h"
     76 #include "url.h"
     77 #include "curl_gethostname.h"
     78 #include "bufref.h"
     79 #include "curl_sasl.h"
     80 #include "curlx/warnless.h"
     81 #include "idn.h"
     82 #include "curlx/strparse.h"
     83 
     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 /* meta key for storing protocol meta at easy handle */
     90 #define CURL_META_SMTP_EASY   "meta:proto:smtp:easy"
     91 /* meta key for storing protocol meta at connection */
     92 #define CURL_META_SMTP_CONN   "meta:proto:smtp:conn"
     93 
     94 /****************************************************************************
     95  * SMTP unique setup
     96  ***************************************************************************/
     97 typedef enum {
     98   SMTP_STOP,        /* do nothing state, stops the state machine */
     99   SMTP_SERVERGREET, /* waiting for the initial greeting immediately after
    100                        a connect */
    101   SMTP_EHLO,
    102   SMTP_HELO,
    103   SMTP_STARTTLS,
    104   SMTP_UPGRADETLS,  /* asynchronously upgrade the connection to SSL/TLS
    105                        (multi mode only) */
    106   SMTP_AUTH,
    107   SMTP_COMMAND,     /* VRFY, EXPN, NOOP, RSET and HELP */
    108   SMTP_MAIL,        /* MAIL FROM */
    109   SMTP_RCPT,        /* RCPT TO */
    110   SMTP_DATA,
    111   SMTP_POSTDATA,
    112   SMTP_QUIT,
    113   SMTP_LAST         /* never used */
    114 } smtpstate;
    115 
    116 /* smtp_conn is used for struct connection-oriented data in the connectdata
    117    struct */
    118 struct smtp_conn {
    119   struct pingpong pp;
    120   struct SASL sasl;        /* SASL-related storage */
    121   smtpstate state;         /* Always use smtp.c:state() to change state! */
    122   char *domain;            /* Client address/name to send in the EHLO */
    123   BIT(ssldone);            /* Is connect() over SSL done? */
    124   BIT(tls_supported);      /* StartTLS capability supported by server */
    125   BIT(size_supported);     /* If server supports SIZE extension according to
    126                               RFC 1870 */
    127   BIT(utf8_supported);     /* If server supports SMTPUTF8 extension according
    128                               to RFC 6531 */
    129   BIT(auth_supported);     /* AUTH capability supported by server */
    130 };
    131 
    132 /* This SMTP struct is used in the Curl_easy. All SMTP data that is
    133    connection-oriented must be in smtp_conn to properly deal with the fact that
    134    perhaps the Curl_easy is changed between the times the connection is
    135    used. */
    136 struct SMTP {
    137   curl_pp_transfer transfer;
    138   char *custom;            /* Custom Request */
    139   struct curl_slist *rcpt; /* Recipient list */
    140   int rcpt_last_error;     /* The last error received for RCPT TO command */
    141   size_t eob;              /* Number of bytes of the EOB (End Of Body) that
    142                               have been received so far */
    143   BIT(rcpt_had_ok);        /* Whether any of RCPT TO commands (depends on
    144                               total number of recipients) succeeded so far */
    145   BIT(trailing_crlf);      /* Specifies if the trailing CRLF is present */
    146 };
    147 
    148 /* Local API functions */
    149 static CURLcode smtp_regular_transfer(struct Curl_easy *data,
    150                                       struct smtp_conn *smtpc,
    151                                       struct SMTP *smtp,
    152                                       bool *done);
    153 static CURLcode smtp_do(struct Curl_easy *data, bool *done);
    154 static CURLcode smtp_done(struct Curl_easy *data, CURLcode status,
    155                           bool premature);
    156 static CURLcode smtp_connect(struct Curl_easy *data, bool *done);
    157 static CURLcode smtp_disconnect(struct Curl_easy *data,
    158                                 struct connectdata *conn, bool dead);
    159 static CURLcode smtp_multi_statemach(struct Curl_easy *data, bool *done);
    160 static int smtp_getsock(struct Curl_easy *data,
    161                         struct connectdata *conn, curl_socket_t *socks);
    162 static CURLcode smtp_doing(struct Curl_easy *data, bool *dophase_done);
    163 static CURLcode smtp_setup_connection(struct Curl_easy *data,
    164                                       struct connectdata *conn);
    165 static CURLcode smtp_parse_url_options(struct connectdata *conn,
    166                                        struct smtp_conn *smtpc);
    167 static CURLcode smtp_parse_url_path(struct Curl_easy *data,
    168                                     struct smtp_conn *smtpc);
    169 static CURLcode smtp_parse_custom_request(struct Curl_easy *data,
    170                                           struct SMTP *smtp);
    171 static CURLcode smtp_parse_address(const char *fqma,
    172                                    char **address, struct hostname *host);
    173 static CURLcode smtp_perform_auth(struct Curl_easy *data, const char *mech,
    174                                   const struct bufref *initresp);
    175 static CURLcode smtp_continue_auth(struct Curl_easy *data, const char *mech,
    176                                    const struct bufref *resp);
    177 static CURLcode smtp_cancel_auth(struct Curl_easy *data, const char *mech);
    178 static CURLcode smtp_get_message(struct Curl_easy *data, struct bufref *out);
    179 static CURLcode cr_eob_add(struct Curl_easy *data);
    180 
    181 /*
    182  * SMTP protocol handler.
    183  */
    184 
    185 const struct Curl_handler Curl_handler_smtp = {
    186   "smtp",                           /* scheme */
    187   smtp_setup_connection,            /* setup_connection */
    188   smtp_do,                          /* do_it */
    189   smtp_done,                        /* done */
    190   ZERO_NULL,                        /* do_more */
    191   smtp_connect,                     /* connect_it */
    192   smtp_multi_statemach,             /* connecting */
    193   smtp_doing,                       /* doing */
    194   smtp_getsock,                     /* proto_getsock */
    195   smtp_getsock,                     /* doing_getsock */
    196   ZERO_NULL,                        /* domore_getsock */
    197   ZERO_NULL,                        /* perform_getsock */
    198   smtp_disconnect,                  /* disconnect */
    199   ZERO_NULL,                        /* write_resp */
    200   ZERO_NULL,                        /* write_resp_hd */
    201   ZERO_NULL,                        /* connection_check */
    202   ZERO_NULL,                        /* attach connection */
    203   ZERO_NULL,                        /* follow */
    204   PORT_SMTP,                        /* defport */
    205   CURLPROTO_SMTP,                   /* protocol */
    206   CURLPROTO_SMTP,                   /* family */
    207   PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY | /* flags */
    208   PROTOPT_URLOPTIONS
    209 };
    210 
    211 #ifdef USE_SSL
    212 /*
    213  * SMTPS protocol handler.
    214  */
    215 
    216 const struct Curl_handler Curl_handler_smtps = {
    217   "smtps",                          /* scheme */
    218   smtp_setup_connection,            /* setup_connection */
    219   smtp_do,                          /* do_it */
    220   smtp_done,                        /* done */
    221   ZERO_NULL,                        /* do_more */
    222   smtp_connect,                     /* connect_it */
    223   smtp_multi_statemach,             /* connecting */
    224   smtp_doing,                       /* doing */
    225   smtp_getsock,                     /* proto_getsock */
    226   smtp_getsock,                     /* doing_getsock */
    227   ZERO_NULL,                        /* domore_getsock */
    228   ZERO_NULL,                        /* perform_getsock */
    229   smtp_disconnect,                  /* disconnect */
    230   ZERO_NULL,                        /* write_resp */
    231   ZERO_NULL,                        /* write_resp_hd */
    232   ZERO_NULL,                        /* connection_check */
    233   ZERO_NULL,                        /* attach connection */
    234   ZERO_NULL,                        /* follow */
    235   PORT_SMTPS,                       /* defport */
    236   CURLPROTO_SMTPS,                  /* protocol */
    237   CURLPROTO_SMTP,                   /* family */
    238   PROTOPT_CLOSEACTION | PROTOPT_SSL
    239   | PROTOPT_NOURLQUERY | PROTOPT_URLOPTIONS /* flags */
    240 };
    241 #endif
    242 
    243 /* SASL parameters for the smtp protocol */
    244 static const struct SASLproto saslsmtp = {
    245   "smtp",               /* The service name */
    246   smtp_perform_auth,    /* Send authentication command */
    247   smtp_continue_auth,   /* Send authentication continuation */
    248   smtp_cancel_auth,     /* Cancel authentication */
    249   smtp_get_message,     /* Get SASL response message */
    250   512 - 8,              /* Max line len - strlen("AUTH ") - 1 space - crlf */
    251   334,                  /* Code received when continuation is expected */
    252   235,                  /* Code to receive upon authentication success */
    253   SASL_AUTH_DEFAULT,    /* Default mechanisms */
    254   SASL_FLAG_BASE64      /* Configuration flags */
    255 };
    256 
    257 /***********************************************************************
    258  *
    259  * smtp_endofresp()
    260  *
    261  * Checks for an ending SMTP status code at the start of the given string, but
    262  * also detects various capabilities from the EHLO response including the
    263  * supported authentication mechanisms.
    264  */
    265 static bool smtp_endofresp(struct Curl_easy *data, struct connectdata *conn,
    266                            const char *line, size_t len, int *resp)
    267 {
    268   struct smtp_conn *smtpc = Curl_conn_meta_get(conn, CURL_META_SMTP_CONN);
    269   bool result = FALSE;
    270   (void)data;
    271 
    272   DEBUGASSERT(smtpc);
    273   if(!smtpc)
    274     return FALSE;
    275 
    276   /* Nothing for us */
    277   if(len < 4 || !ISDIGIT(line[0]) || !ISDIGIT(line[1]) || !ISDIGIT(line[2]))
    278     return FALSE;
    279 
    280   /* Do we have a command response? This should be the response code followed
    281      by a space and optionally some text as per RFC-5321 and as outlined in
    282      Section 4. Examples of RFC-4954 but some email servers ignore this and
    283      only send the response code instead as per Section 4.2. */
    284   if(line[3] == ' ' || len == 5) {
    285     char tmpline[6];
    286     curl_off_t code;
    287     const char *p = tmpline;
    288     result = TRUE;
    289     memcpy(tmpline, line, (len == 5 ? 5 : 3));
    290     tmpline[len == 5 ? 5 : 3 ] = 0;
    291     if(curlx_str_number(&p, &code, len == 5 ? 99999 : 999))
    292       return FALSE;
    293     *resp = (int) code;
    294 
    295     /* Make sure real server never sends internal value */
    296     if(*resp == 1)
    297       *resp = 0;
    298   }
    299   /* Do we have a multiline (continuation) response? */
    300   else if(line[3] == '-' &&
    301           (smtpc->state == SMTP_EHLO || smtpc->state == SMTP_COMMAND)) {
    302     result = TRUE;
    303     *resp = 1;  /* Internal response code */
    304   }
    305 
    306   return result;
    307 }
    308 
    309 /***********************************************************************
    310  *
    311  * smtp_get_message()
    312  *
    313  * Gets the authentication message from the response buffer.
    314  */
    315 static CURLcode smtp_get_message(struct Curl_easy *data, struct bufref *out)
    316 {
    317   struct smtp_conn *smtpc =
    318     Curl_conn_meta_get(data->conn, CURL_META_SMTP_CONN);
    319   char *message;
    320   size_t len;
    321 
    322   if(!smtpc)
    323     return CURLE_FAILED_INIT;
    324 
    325   message = curlx_dyn_ptr(&smtpc->pp.recvbuf);
    326   len = smtpc->pp.nfinal;
    327   if(len > 4) {
    328     /* Find the start of the message */
    329     len -= 4;
    330     for(message += 4; *message == ' ' || *message == '\t'; message++, len--)
    331       ;
    332 
    333     /* Find the end of the message */
    334     while(len--)
    335       if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' &&
    336          message[len] != '\t')
    337         break;
    338 
    339     /* Terminate the message */
    340     message[++len] = '\0';
    341     Curl_bufref_set(out, message, len, NULL);
    342   }
    343   else
    344     /* junk input => zero length output */
    345     Curl_bufref_set(out, "", 0, NULL);
    346 
    347   return CURLE_OK;
    348 }
    349 
    350 /***********************************************************************
    351  *
    352  * smtp_state()
    353  *
    354  * This is the ONLY way to change SMTP state!
    355  */
    356 static void smtp_state(struct Curl_easy *data,
    357                        struct smtp_conn *smtpc,
    358                        smtpstate newstate)
    359 {
    360 #if !defined(CURL_DISABLE_VERBOSE_STRINGS)
    361   /* for debug purposes */
    362   static const char * const names[] = {
    363     "STOP",
    364     "SERVERGREET",
    365     "EHLO",
    366     "HELO",
    367     "STARTTLS",
    368     "UPGRADETLS",
    369     "AUTH",
    370     "COMMAND",
    371     "MAIL",
    372     "RCPT",
    373     "DATA",
    374     "POSTDATA",
    375     "QUIT",
    376     /* LAST */
    377   };
    378 
    379   if(smtpc->state != newstate)
    380     CURL_TRC_SMTP(data, "state change from %s to %s",
    381                   names[smtpc->state], names[newstate]);
    382 #endif
    383 
    384   smtpc->state = newstate;
    385 }
    386 
    387 /***********************************************************************
    388  *
    389  * smtp_perform_ehlo()
    390  *
    391  * Sends the EHLO command to not only initialise communication with the ESMTP
    392  * server but to also obtain a list of server side supported capabilities.
    393  */
    394 static CURLcode smtp_perform_ehlo(struct Curl_easy *data,
    395                                   struct smtp_conn *smtpc)
    396 {
    397   CURLcode result = CURLE_OK;
    398 
    399   smtpc->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanism yet */
    400   smtpc->sasl.authused = SASL_AUTH_NONE;  /* Clear the authentication mechanism
    401                                              used for esmtp connections */
    402   smtpc->tls_supported = FALSE;           /* Clear the TLS capability */
    403   smtpc->auth_supported = FALSE;          /* Clear the AUTH capability */
    404 
    405   /* Send the EHLO command */
    406   result = Curl_pp_sendf(data, &smtpc->pp, "EHLO %s", smtpc->domain);
    407 
    408   if(!result)
    409     smtp_state(data, smtpc, SMTP_EHLO);
    410 
    411   return result;
    412 }
    413 
    414 /***********************************************************************
    415  *
    416  * smtp_perform_helo()
    417  *
    418  * Sends the HELO command to initialise communication with the SMTP server.
    419  */
    420 static CURLcode smtp_perform_helo(struct Curl_easy *data,
    421                                   struct smtp_conn *smtpc)
    422 {
    423   CURLcode result = CURLE_OK;
    424 
    425   smtpc->sasl.authused = SASL_AUTH_NONE; /* No authentication mechanism used
    426                                             in smtp connections */
    427 
    428   /* Send the HELO command */
    429   result = Curl_pp_sendf(data, &smtpc->pp, "HELO %s", smtpc->domain);
    430 
    431   if(!result)
    432     smtp_state(data, smtpc, SMTP_HELO);
    433 
    434   return result;
    435 }
    436 
    437 /***********************************************************************
    438  *
    439  * smtp_perform_starttls()
    440  *
    441  * Sends the STLS command to start the upgrade to TLS.
    442  */
    443 static CURLcode smtp_perform_starttls(struct Curl_easy *data,
    444                                       struct smtp_conn *smtpc)
    445 {
    446   /* Send the STARTTLS command */
    447   CURLcode result = Curl_pp_sendf(data, &smtpc->pp, "%s", "STARTTLS");
    448 
    449   if(!result)
    450     smtp_state(data, smtpc, SMTP_STARTTLS);
    451 
    452   return result;
    453 }
    454 
    455 /***********************************************************************
    456  *
    457  * smtp_perform_upgrade_tls()
    458  *
    459  * Performs the upgrade to TLS.
    460  */
    461 static CURLcode smtp_perform_upgrade_tls(struct Curl_easy *data,
    462                                          struct smtp_conn *smtpc)
    463 {
    464 #ifdef USE_SSL
    465   /* Start the SSL connection */
    466   struct connectdata *conn = data->conn;
    467   CURLcode result;
    468   bool ssldone = FALSE;
    469 
    470   DEBUGASSERT(smtpc->state == SMTP_UPGRADETLS);
    471   if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
    472     result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET);
    473     if(result)
    474       goto out;
    475     /* Change the connection handler and SMTP state */
    476     conn->handler = &Curl_handler_smtps;
    477   }
    478 
    479   DEBUGASSERT(!smtpc->ssldone);
    480   result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone);
    481   DEBUGF(infof(data, "smtp_perform_upgrade_tls, connect -> %d, %d",
    482            result, ssldone));
    483   if(!result && ssldone) {
    484     smtpc->ssldone = ssldone;
    485     /* perform EHLO now, changes smtp->state out of SMTP_UPGRADETLS */
    486     result = smtp_perform_ehlo(data, smtpc);
    487   }
    488 out:
    489   return result;
    490 #else
    491   (void)data;
    492   (void)smtpc;
    493   return CURLE_NOT_BUILT_IN;
    494 #endif
    495 }
    496 
    497 /***********************************************************************
    498  *
    499  * smtp_perform_auth()
    500  *
    501  * Sends an AUTH command allowing the client to login with the given SASL
    502  * authentication mechanism.
    503  */
    504 static CURLcode smtp_perform_auth(struct Curl_easy *data,
    505                                   const char *mech,
    506                                   const struct bufref *initresp)
    507 {
    508   CURLcode result = CURLE_OK;
    509   struct smtp_conn *smtpc =
    510     Curl_conn_meta_get(data->conn, CURL_META_SMTP_CONN);
    511   const char *ir = (const char *) Curl_bufref_ptr(initresp);
    512 
    513   if(!smtpc)
    514     return CURLE_FAILED_INIT;
    515 
    516   if(ir) {                                  /* AUTH <mech> ...<crlf> */
    517     /* Send the AUTH command with the initial response */
    518     result = Curl_pp_sendf(data, &smtpc->pp, "AUTH %s %s", mech, ir);
    519   }
    520   else {
    521     /* Send the AUTH command */
    522     result = Curl_pp_sendf(data, &smtpc->pp, "AUTH %s", mech);
    523   }
    524 
    525   return result;
    526 }
    527 
    528 /***********************************************************************
    529  *
    530  * smtp_continue_auth()
    531  *
    532  * Sends SASL continuation data.
    533  */
    534 static CURLcode smtp_continue_auth(struct Curl_easy *data,
    535                                    const char *mech,
    536                                    const struct bufref *resp)
    537 {
    538   struct smtp_conn *smtpc =
    539     Curl_conn_meta_get(data->conn, CURL_META_SMTP_CONN);
    540 
    541   (void)mech;
    542   if(!smtpc)
    543     return CURLE_FAILED_INIT;
    544   return Curl_pp_sendf(data, &smtpc->pp,
    545                        "%s", (const char *) Curl_bufref_ptr(resp));
    546 }
    547 
    548 /***********************************************************************
    549  *
    550  * smtp_cancel_auth()
    551  *
    552  * Sends SASL cancellation.
    553  */
    554 static CURLcode smtp_cancel_auth(struct Curl_easy *data, const char *mech)
    555 {
    556   struct smtp_conn *smtpc =
    557     Curl_conn_meta_get(data->conn, CURL_META_SMTP_CONN);
    558 
    559   (void)mech;
    560   if(!smtpc)
    561     return CURLE_FAILED_INIT;
    562   return Curl_pp_sendf(data, &smtpc->pp, "*");
    563 }
    564 
    565 /***********************************************************************
    566  *
    567  * smtp_perform_authentication()
    568  *
    569  * Initiates the authentication sequence, with the appropriate SASL
    570  * authentication mechanism.
    571  */
    572 static CURLcode smtp_perform_authentication(struct Curl_easy *data,
    573                                             struct smtp_conn *smtpc)
    574 {
    575   CURLcode result = CURLE_OK;
    576   saslprogress progress;
    577 
    578   /* Check we have enough data to authenticate with, and the
    579      server supports authentication, and end the connect phase if not */
    580   if(!smtpc->auth_supported ||
    581      !Curl_sasl_can_authenticate(&smtpc->sasl, data)) {
    582     smtp_state(data, smtpc, SMTP_STOP);
    583     return result;
    584   }
    585 
    586   /* Calculate the SASL login details */
    587   result = Curl_sasl_start(&smtpc->sasl, data, FALSE, &progress);
    588 
    589   if(!result) {
    590     if(progress == SASL_INPROGRESS)
    591       smtp_state(data, smtpc, SMTP_AUTH);
    592     else
    593       result = Curl_sasl_is_blocked(&smtpc->sasl, data);
    594   }
    595 
    596   return result;
    597 }
    598 
    599 /***********************************************************************
    600  *
    601  * smtp_perform_command()
    602  *
    603  * Sends an SMTP based command.
    604  */
    605 static CURLcode smtp_perform_command(struct Curl_easy *data,
    606                                      struct smtp_conn *smtpc,
    607                                      struct SMTP *smtp)
    608 {
    609   CURLcode result = CURLE_OK;
    610 
    611   if(smtp->rcpt) {
    612     /* We notify the server we are sending UTF-8 data if a) it supports the
    613        SMTPUTF8 extension and b) The mailbox contains UTF-8 characters, in
    614        either the local address or hostname parts. This is regardless of
    615        whether the hostname is encoded using IDN ACE */
    616     bool utf8 = FALSE;
    617 
    618     if((!smtp->custom) || (!smtp->custom[0])) {
    619       char *address = NULL;
    620       struct hostname host = { NULL, NULL, NULL, NULL };
    621 
    622       /* Parse the mailbox to verify into the local address and hostname
    623          parts, converting the hostname to an IDN A-label if necessary */
    624       result = smtp_parse_address(smtp->rcpt->data,
    625                                   &address, &host);
    626       if(result)
    627         return result;
    628 
    629       /* Establish whether we should report SMTPUTF8 to the server for this
    630          mailbox as per RFC-6531 sect. 3.1 point 6 */
    631       utf8 = (smtpc->utf8_supported) &&
    632              ((host.encalloc) || (!Curl_is_ASCII_name(address)) ||
    633               (!Curl_is_ASCII_name(host.name)));
    634 
    635       /* Send the VRFY command (Note: The hostname part may be absent when the
    636          host is a local system) */
    637       result = Curl_pp_sendf(data, &smtpc->pp, "VRFY %s%s%s%s",
    638                              address,
    639                              host.name ? "@" : "",
    640                              host.name ? host.name : "",
    641                              utf8 ? " SMTPUTF8" : "");
    642 
    643       Curl_free_idnconverted_hostname(&host);
    644       free(address);
    645     }
    646     else {
    647       /* Establish whether we should report that we support SMTPUTF8 for EXPN
    648          commands to the server as per RFC-6531 sect. 3.1 point 6 */
    649       utf8 = (smtpc->utf8_supported) && (!strcmp(smtp->custom, "EXPN"));
    650 
    651       /* Send the custom recipient based command such as the EXPN command */
    652       result = Curl_pp_sendf(data, &smtpc->pp,
    653                              "%s %s%s", smtp->custom,
    654                              smtp->rcpt->data,
    655                              utf8 ? " SMTPUTF8" : "");
    656     }
    657   }
    658   else
    659     /* Send the non-recipient based command such as HELP */
    660     result = Curl_pp_sendf(data, &smtpc->pp, "%s",
    661                            smtp->custom && smtp->custom[0] != '\0' ?
    662                            smtp->custom : "HELP");
    663 
    664   if(!result)
    665     smtp_state(data, smtpc, SMTP_COMMAND);
    666 
    667   return result;
    668 }
    669 
    670 /***********************************************************************
    671  *
    672  * smtp_perform_mail()
    673  *
    674  * Sends an MAIL command to initiate the upload of a message.
    675  */
    676 static CURLcode smtp_perform_mail(struct Curl_easy *data,
    677                                   struct smtp_conn *smtpc,
    678                                   struct SMTP *smtp)
    679 {
    680   char *from = NULL;
    681   char *auth = NULL;
    682   char *size = NULL;
    683   CURLcode result = CURLE_OK;
    684 
    685   /* We notify the server we are sending UTF-8 data if a) it supports the
    686      SMTPUTF8 extension and b) The mailbox contains UTF-8 characters, in
    687      either the local address or hostname parts. This is regardless of
    688      whether the hostname is encoded using IDN ACE */
    689   bool utf8 = FALSE;
    690 
    691   /* Calculate the FROM parameter */
    692   if(data->set.str[STRING_MAIL_FROM]) {
    693     char *address = NULL;
    694     struct hostname host = { NULL, NULL, NULL, NULL };
    695 
    696     /* Parse the FROM mailbox into the local address and hostname parts,
    697        converting the hostname to an IDN A-label if necessary */
    698     result = smtp_parse_address(data->set.str[STRING_MAIL_FROM],
    699                                 &address, &host);
    700     if(result)
    701       goto out;
    702 
    703     /* Establish whether we should report SMTPUTF8 to the server for this
    704        mailbox as per RFC-6531 sect. 3.1 point 4 and sect. 3.4 */
    705     utf8 = (smtpc->utf8_supported) &&
    706            ((host.encalloc) || (!Curl_is_ASCII_name(address)) ||
    707             (!Curl_is_ASCII_name(host.name)));
    708 
    709     if(host.name) {
    710       from = aprintf("<%s@%s>", address, host.name);
    711 
    712       Curl_free_idnconverted_hostname(&host);
    713     }
    714     else
    715       /* An invalid mailbox was provided but we will simply let the server
    716          worry about that and reply with a 501 error */
    717       from = aprintf("<%s>", address);
    718 
    719     free(address);
    720   }
    721   else
    722     /* Null reverse-path, RFC-5321, sect. 3.6.3 */
    723     from = strdup("<>");
    724 
    725   if(!from) {
    726     result = CURLE_OUT_OF_MEMORY;
    727     goto out;
    728   }
    729 
    730   /* Calculate the optional AUTH parameter */
    731   if(data->set.str[STRING_MAIL_AUTH] && smtpc->sasl.authused) {
    732     if(data->set.str[STRING_MAIL_AUTH][0] != '\0') {
    733       char *address = NULL;
    734       struct hostname host = { NULL, NULL, NULL, NULL };
    735 
    736       /* Parse the AUTH mailbox into the local address and hostname parts,
    737          converting the hostname to an IDN A-label if necessary */
    738       result = smtp_parse_address(data->set.str[STRING_MAIL_AUTH],
    739                                   &address, &host);
    740       if(result)
    741         goto out;
    742 
    743       /* Establish whether we should report SMTPUTF8 to the server for this
    744          mailbox as per RFC-6531 sect. 3.1 point 4 and sect. 3.4 */
    745       if((!utf8) && (smtpc->utf8_supported) &&
    746          ((host.encalloc) || (!Curl_is_ASCII_name(address)) ||
    747           (!Curl_is_ASCII_name(host.name))))
    748         utf8 = TRUE;
    749 
    750       if(host.name) {
    751         auth = aprintf("<%s@%s>", address, host.name);
    752 
    753         Curl_free_idnconverted_hostname(&host);
    754       }
    755       else
    756         /* An invalid mailbox was provided but we will simply let the server
    757            worry about it */
    758         auth = aprintf("<%s>", address);
    759       free(address);
    760     }
    761     else
    762       /* Empty AUTH, RFC-2554, sect. 5 */
    763       auth = strdup("<>");
    764 
    765     if(!auth) {
    766       result = CURLE_OUT_OF_MEMORY;
    767       goto out;
    768     }
    769   }
    770 
    771 #ifndef CURL_DISABLE_MIME
    772   /* Prepare the mime data if some. */
    773   if(data->set.mimepost.kind != MIMEKIND_NONE) {
    774     /* Use the whole structure as data. */
    775     data->set.mimepost.flags &= ~(unsigned int)MIME_BODY_ONLY;
    776 
    777     /* Add external headers and mime version. */
    778     curl_mime_headers(&data->set.mimepost, data->set.headers, 0);
    779     result = Curl_mime_prepare_headers(data, &data->set.mimepost, NULL,
    780                                        NULL, MIMESTRATEGY_MAIL);
    781 
    782     if(!result)
    783       if(!Curl_checkheaders(data, STRCONST("Mime-Version")))
    784         result = Curl_mime_add_header(&data->set.mimepost.curlheaders,
    785                                       "Mime-Version: 1.0");
    786 
    787     if(!result)
    788       result = Curl_creader_set_mime(data, &data->set.mimepost);
    789     if(result)
    790       goto out;
    791     data->state.infilesize = Curl_creader_total_length(data);
    792   }
    793   else
    794 #endif
    795   {
    796     result = Curl_creader_set_fread(data, data->state.infilesize);
    797     if(result)
    798       goto out;
    799   }
    800 
    801   /* Calculate the optional SIZE parameter */
    802   if(smtpc->size_supported && data->state.infilesize > 0) {
    803     size = aprintf("%" FMT_OFF_T, data->state.infilesize);
    804 
    805     if(!size) {
    806       result = CURLE_OUT_OF_MEMORY;
    807       goto out;
    808     }
    809   }
    810 
    811   /* If the mailboxes in the FROM and AUTH parameters do not include a UTF-8
    812      based address then quickly scan through the recipient list and check if
    813      any there do, as we need to correctly identify our support for SMTPUTF8
    814      in the envelope, as per RFC-6531 sect. 3.4 */
    815   if(smtpc->utf8_supported && !utf8) {
    816     struct curl_slist *rcpt = smtp->rcpt;
    817 
    818     while(rcpt && !utf8) {
    819       /* Does the hostname contain non-ASCII characters? */
    820       if(!Curl_is_ASCII_name(rcpt->data))
    821         utf8 = TRUE;
    822 
    823       rcpt = rcpt->next;
    824     }
    825   }
    826 
    827   /* Add the client reader doing STMP EOB escaping */
    828   result = cr_eob_add(data);
    829   if(result)
    830     goto out;
    831 
    832   /* Send the MAIL command */
    833   result = Curl_pp_sendf(data, &smtpc->pp,
    834                          "MAIL FROM:%s%s%s%s%s%s",
    835                          from,                 /* Mandatory                 */
    836                          auth ? " AUTH=" : "", /* Optional on AUTH support  */
    837                          auth ? auth : "",     /*                           */
    838                          size ? " SIZE=" : "", /* Optional on SIZE support  */
    839                          size ? size : "",     /*                           */
    840                          utf8 ? " SMTPUTF8"    /* Internationalised mailbox */
    841                                : "");          /* included in our envelope  */
    842 
    843 out:
    844   free(from);
    845   free(auth);
    846   free(size);
    847 
    848   if(!result)
    849     smtp_state(data, smtpc, SMTP_MAIL);
    850 
    851   return result;
    852 }
    853 
    854 /***********************************************************************
    855  *
    856  * smtp_perform_rcpt_to()
    857  *
    858  * Sends a RCPT TO command for a given recipient as part of the message upload
    859  * process.
    860  */
    861 static CURLcode smtp_perform_rcpt_to(struct Curl_easy *data,
    862                                      struct smtp_conn *smtpc,
    863                                      struct SMTP *smtp)
    864 {
    865   CURLcode result = CURLE_OK;
    866   char *address = NULL;
    867   struct hostname host = { NULL, NULL, NULL, NULL };
    868 
    869   /* Parse the recipient mailbox into the local address and hostname parts,
    870      converting the hostname to an IDN A-label if necessary */
    871   result = smtp_parse_address(smtp->rcpt->data,
    872                               &address, &host);
    873   if(result)
    874     return result;
    875 
    876   /* Send the RCPT TO command */
    877   if(host.name)
    878     result = Curl_pp_sendf(data, &smtpc->pp, "RCPT TO:<%s@%s>",
    879                            address, host.name);
    880   else
    881     /* An invalid mailbox was provided but we will simply let the server worry
    882        about that and reply with a 501 error */
    883     result = Curl_pp_sendf(data, &smtpc->pp, "RCPT TO:<%s>", address);
    884 
    885   Curl_free_idnconverted_hostname(&host);
    886   free(address);
    887 
    888   if(!result)
    889     smtp_state(data, smtpc, SMTP_RCPT);
    890 
    891   return result;
    892 }
    893 
    894 /***********************************************************************
    895  *
    896  * smtp_perform_quit()
    897  *
    898  * Performs the quit action prior to sclose() being called.
    899  */
    900 static CURLcode smtp_perform_quit(struct Curl_easy *data,
    901                                   struct smtp_conn *smtpc)
    902 {
    903   /* Send the QUIT command */
    904   CURLcode result = Curl_pp_sendf(data, &smtpc->pp, "%s", "QUIT");
    905 
    906   if(!result)
    907     smtp_state(data, smtpc, SMTP_QUIT);
    908 
    909   return result;
    910 }
    911 
    912 /* For the initial server greeting */
    913 static CURLcode smtp_state_servergreet_resp(struct Curl_easy *data,
    914                                             struct smtp_conn *smtpc,
    915                                             int smtpcode,
    916                                             smtpstate instate)
    917 {
    918   CURLcode result = CURLE_OK;
    919   (void)instate; /* no use for this yet */
    920 
    921   if(smtpcode/100 != 2) {
    922     failf(data, "Got unexpected smtp-server response: %d", smtpcode);
    923     result = CURLE_WEIRD_SERVER_REPLY;
    924   }
    925   else
    926     result = smtp_perform_ehlo(data, smtpc);
    927 
    928   return result;
    929 }
    930 
    931 /* For STARTTLS responses */
    932 static CURLcode smtp_state_starttls_resp(struct Curl_easy *data,
    933                                          struct smtp_conn *smtpc,
    934                                          int smtpcode,
    935                                          smtpstate instate)
    936 {
    937   CURLcode result = CURLE_OK;
    938   (void)instate; /* no use for this yet */
    939 
    940   /* Pipelining in response is forbidden. */
    941   if(smtpc->pp.overflow)
    942     return CURLE_WEIRD_SERVER_REPLY;
    943 
    944   if(smtpcode != 220) {
    945     if(data->set.use_ssl != CURLUSESSL_TRY) {
    946       failf(data, "STARTTLS denied, code %d", smtpcode);
    947       result = CURLE_USE_SSL_FAILED;
    948     }
    949     else
    950       result = smtp_perform_authentication(data, smtpc);
    951   }
    952   else
    953     smtp_state(data, smtpc, SMTP_UPGRADETLS);
    954 
    955   return result;
    956 }
    957 
    958 /* For EHLO responses */
    959 static CURLcode smtp_state_ehlo_resp(struct Curl_easy *data,
    960                                      struct smtp_conn *smtpc,
    961                                      int smtpcode,
    962                                      smtpstate instate)
    963 {
    964   CURLcode result = CURLE_OK;
    965   const char *line = curlx_dyn_ptr(&smtpc->pp.recvbuf);
    966   size_t len = smtpc->pp.nfinal;
    967 
    968   (void)instate; /* no use for this yet */
    969 
    970   if(smtpcode/100 != 2 && smtpcode != 1) {
    971     if(data->set.use_ssl <= CURLUSESSL_TRY
    972        || Curl_conn_is_ssl(data->conn, FIRSTSOCKET))
    973       result = smtp_perform_helo(data, smtpc);
    974     else {
    975       failf(data, "Remote access denied: %d", smtpcode);
    976       result = CURLE_REMOTE_ACCESS_DENIED;
    977     }
    978   }
    979   else if(len >= 4) {
    980     line += 4;
    981     len -= 4;
    982 
    983     /* Does the server support the STARTTLS capability? */
    984     if(len >= 8 && !memcmp(line, "STARTTLS", 8))
    985       smtpc->tls_supported = TRUE;
    986 
    987     /* Does the server support the SIZE capability? */
    988     else if(len >= 4 && !memcmp(line, "SIZE", 4))
    989       smtpc->size_supported = TRUE;
    990 
    991     /* Does the server support the UTF-8 capability? */
    992     else if(len >= 8 && !memcmp(line, "SMTPUTF8", 8))
    993       smtpc->utf8_supported = TRUE;
    994 
    995     /* Does the server support authentication? */
    996     else if(len >= 5 && !memcmp(line, "AUTH ", 5)) {
    997       smtpc->auth_supported = TRUE;
    998 
    999       /* Advance past the AUTH keyword */
   1000       line += 5;
   1001       len -= 5;
   1002 
   1003       /* Loop through the data line */
   1004       for(;;) {
   1005         size_t llen;
   1006         size_t wordlen;
   1007         unsigned short mechbit;
   1008 
   1009         while(len &&
   1010               (*line == ' ' || *line == '\t' ||
   1011                *line == '\r' || *line == '\n')) {
   1012 
   1013           line++;
   1014           len--;
   1015         }
   1016 
   1017         if(!len)
   1018           break;
   1019 
   1020         /* Extract the word */
   1021         for(wordlen = 0; wordlen < len && line[wordlen] != ' ' &&
   1022               line[wordlen] != '\t' && line[wordlen] != '\r' &&
   1023               line[wordlen] != '\n';)
   1024           wordlen++;
   1025 
   1026         /* Test the word for a matching authentication mechanism */
   1027         mechbit = Curl_sasl_decode_mech(line, wordlen, &llen);
   1028         if(mechbit && llen == wordlen)
   1029           smtpc->sasl.authmechs |= mechbit;
   1030 
   1031         line += wordlen;
   1032         len -= wordlen;
   1033       }
   1034     }
   1035 
   1036     if(smtpcode != 1) {
   1037       if(data->set.use_ssl && !Curl_conn_is_ssl(data->conn, FIRSTSOCKET)) {
   1038         /* We do not have an SSL/TLS connection yet, but SSL is requested */
   1039         if(smtpc->tls_supported)
   1040           /* Switch to TLS connection now */
   1041           result = smtp_perform_starttls(data, smtpc);
   1042         else if(data->set.use_ssl == CURLUSESSL_TRY)
   1043           /* Fallback and carry on with authentication */
   1044           result = smtp_perform_authentication(data, smtpc);
   1045         else {
   1046           failf(data, "STARTTLS not supported.");
   1047           result = CURLE_USE_SSL_FAILED;
   1048         }
   1049       }
   1050       else
   1051         result = smtp_perform_authentication(data, smtpc);
   1052     }
   1053   }
   1054   else {
   1055     failf(data, "Unexpectedly short EHLO response");
   1056     result = CURLE_WEIRD_SERVER_REPLY;
   1057   }
   1058 
   1059   return result;
   1060 }
   1061 
   1062 /* For HELO responses */
   1063 static CURLcode smtp_state_helo_resp(struct Curl_easy *data,
   1064                                      struct smtp_conn *smtpc,
   1065                                      int smtpcode,
   1066                                      smtpstate instate)
   1067 {
   1068   CURLcode result = CURLE_OK;
   1069   (void)instate; /* no use for this yet */
   1070 
   1071   if(smtpcode/100 != 2) {
   1072     failf(data, "Remote access denied: %d", smtpcode);
   1073     result = CURLE_REMOTE_ACCESS_DENIED;
   1074   }
   1075   else
   1076     /* End of connect phase */
   1077     smtp_state(data, smtpc, SMTP_STOP);
   1078 
   1079   return result;
   1080 }
   1081 
   1082 /* For SASL authentication responses */
   1083 static CURLcode smtp_state_auth_resp(struct Curl_easy *data,
   1084                                      struct smtp_conn *smtpc,
   1085                                      int smtpcode,
   1086                                      smtpstate instate)
   1087 {
   1088   CURLcode result = CURLE_OK;
   1089   saslprogress progress;
   1090 
   1091   (void)instate; /* no use for this yet */
   1092 
   1093   result = Curl_sasl_continue(&smtpc->sasl, data, smtpcode, &progress);
   1094   if(!result)
   1095     switch(progress) {
   1096     case SASL_DONE:
   1097       smtp_state(data, smtpc, SMTP_STOP);  /* Authenticated */
   1098       break;
   1099     case SASL_IDLE:            /* No mechanism left after cancellation */
   1100       failf(data, "Authentication cancelled");
   1101       result = CURLE_LOGIN_DENIED;
   1102       break;
   1103     default:
   1104       break;
   1105     }
   1106 
   1107   return result;
   1108 }
   1109 
   1110 /* For command responses */
   1111 static CURLcode smtp_state_command_resp(struct Curl_easy *data,
   1112                                         struct smtp_conn *smtpc,
   1113                                         struct SMTP *smtp,
   1114                                         int smtpcode,
   1115                                         smtpstate instate)
   1116 {
   1117   CURLcode result = CURLE_OK;
   1118   char *line = curlx_dyn_ptr(&smtpc->pp.recvbuf);
   1119   size_t len = smtpc->pp.nfinal;
   1120 
   1121   (void)instate; /* no use for this yet */
   1122 
   1123   if((smtp->rcpt && smtpcode/100 != 2 && smtpcode != 553 && smtpcode != 1) ||
   1124      (!smtp->rcpt && smtpcode/100 != 2 && smtpcode != 1)) {
   1125     failf(data, "Command failed: %d", smtpcode);
   1126     result = CURLE_WEIRD_SERVER_REPLY;
   1127   }
   1128   else {
   1129     if(!data->req.no_body)
   1130       result = Curl_client_write(data, CLIENTWRITE_BODY, line, len);
   1131 
   1132     if(smtpcode != 1) {
   1133       if(smtp->rcpt) {
   1134         smtp->rcpt = smtp->rcpt->next;
   1135 
   1136         if(smtp->rcpt) {
   1137           /* Send the next command */
   1138           result = smtp_perform_command(data, smtpc, smtp);
   1139         }
   1140         else
   1141           /* End of DO phase */
   1142           smtp_state(data, smtpc, SMTP_STOP);
   1143       }
   1144       else
   1145         /* End of DO phase */
   1146         smtp_state(data, smtpc, SMTP_STOP);
   1147     }
   1148   }
   1149 
   1150   return result;
   1151 }
   1152 
   1153 /* For MAIL responses */
   1154 static CURLcode smtp_state_mail_resp(struct Curl_easy *data,
   1155                                      struct smtp_conn *smtpc,
   1156                                      struct SMTP *smtp,
   1157                                      int smtpcode,
   1158                                      smtpstate instate)
   1159 {
   1160   CURLcode result = CURLE_OK;
   1161   (void)instate; /* no use for this yet */
   1162 
   1163   if(smtpcode/100 != 2) {
   1164     failf(data, "MAIL failed: %d", smtpcode);
   1165     result = CURLE_SEND_ERROR;
   1166   }
   1167   else
   1168     /* Start the RCPT TO command */
   1169     result = smtp_perform_rcpt_to(data, smtpc, smtp);
   1170 
   1171   return result;
   1172 }
   1173 
   1174 /* For RCPT responses */
   1175 static CURLcode smtp_state_rcpt_resp(struct Curl_easy *data,
   1176                                      struct smtp_conn *smtpc,
   1177                                      struct SMTP *smtp,
   1178                                      int smtpcode,
   1179                                      smtpstate instate)
   1180 {
   1181   CURLcode result = CURLE_OK;
   1182   bool is_smtp_err = FALSE;
   1183   bool is_smtp_blocking_err = FALSE;
   1184 
   1185   (void)instate; /* no use for this yet */
   1186 
   1187   is_smtp_err = (smtpcode/100 != 2);
   1188 
   1189   /* If there is multiple RCPT TO to be issued, it is possible to ignore errors
   1190      and proceed with only the valid addresses. */
   1191   is_smtp_blocking_err = (is_smtp_err && !data->set.mail_rcpt_allowfails);
   1192 
   1193   if(is_smtp_err) {
   1194     /* Remembering the last failure which we can report if all "RCPT TO" have
   1195        failed and we cannot proceed. */
   1196     smtp->rcpt_last_error = smtpcode;
   1197 
   1198     if(is_smtp_blocking_err) {
   1199       failf(data, "RCPT failed: %d", smtpcode);
   1200       result = CURLE_SEND_ERROR;
   1201     }
   1202   }
   1203   else {
   1204     /* Some RCPT TO commands have succeeded. */
   1205     smtp->rcpt_had_ok = TRUE;
   1206   }
   1207 
   1208   if(!is_smtp_blocking_err) {
   1209     smtp->rcpt = smtp->rcpt->next;
   1210 
   1211     if(smtp->rcpt)
   1212       /* Send the next RCPT TO command */
   1213       result = smtp_perform_rcpt_to(data, smtpc, smtp);
   1214     else {
   1215       /* We were not able to issue a successful RCPT TO command while going
   1216          over recipients (potentially multiple). Sending back last error. */
   1217       if(!smtp->rcpt_had_ok) {
   1218         failf(data, "RCPT failed: %d (last error)", smtp->rcpt_last_error);
   1219         result = CURLE_SEND_ERROR;
   1220       }
   1221       else {
   1222         /* Send the DATA command */
   1223         result = Curl_pp_sendf(data, &smtpc->pp, "%s", "DATA");
   1224 
   1225         if(!result)
   1226           smtp_state(data, smtpc, SMTP_DATA);
   1227       }
   1228     }
   1229   }
   1230 
   1231   return result;
   1232 }
   1233 
   1234 /* For DATA response */
   1235 static CURLcode smtp_state_data_resp(struct Curl_easy *data,
   1236                                      struct smtp_conn *smtpc,
   1237                                      int smtpcode,
   1238                                      smtpstate instate)
   1239 {
   1240   CURLcode result = CURLE_OK;
   1241   (void)instate; /* no use for this yet */
   1242 
   1243   if(smtpcode != 354) {
   1244     failf(data, "DATA failed: %d", smtpcode);
   1245     result = CURLE_SEND_ERROR;
   1246   }
   1247   else {
   1248     /* Set the progress upload size */
   1249     Curl_pgrsSetUploadSize(data, data->state.infilesize);
   1250 
   1251     /* SMTP upload */
   1252     Curl_xfer_setup1(data, CURL_XFER_SEND, -1, FALSE);
   1253 
   1254     /* End of DO phase */
   1255     smtp_state(data, smtpc, SMTP_STOP);
   1256   }
   1257 
   1258   return result;
   1259 }
   1260 
   1261 /* For POSTDATA responses, which are received after the entire DATA
   1262    part has been sent to the server */
   1263 static CURLcode smtp_state_postdata_resp(struct Curl_easy *data,
   1264                                          struct smtp_conn *smtpc,
   1265                                          int smtpcode,
   1266                                          smtpstate instate)
   1267 {
   1268   CURLcode result = CURLE_OK;
   1269 
   1270   (void)instate; /* no use for this yet */
   1271 
   1272   if(smtpcode != 250)
   1273     result = CURLE_WEIRD_SERVER_REPLY;
   1274 
   1275   /* End of DONE phase */
   1276   smtp_state(data, smtpc, SMTP_STOP);
   1277 
   1278   return result;
   1279 }
   1280 
   1281 static CURLcode smtp_pp_statemachine(struct Curl_easy *data,
   1282                                      struct connectdata *conn)
   1283 {
   1284   CURLcode result = CURLE_OK;
   1285   int smtpcode;
   1286   struct smtp_conn *smtpc = Curl_conn_meta_get(conn, CURL_META_SMTP_CONN);
   1287   struct SMTP *smtp = Curl_meta_get(data, CURL_META_SMTP_EASY);
   1288   size_t nread = 0;
   1289 
   1290   if(!smtpc || !smtp)
   1291     return CURLE_FAILED_INIT;
   1292 
   1293   /* Busy upgrading the connection; right now all I/O is SSL/TLS, not SMTP */
   1294 upgrade_tls:
   1295   if(smtpc->state == SMTP_UPGRADETLS) {
   1296     result = smtp_perform_upgrade_tls(data, smtpc);
   1297     if(result || (smtpc->state == SMTP_UPGRADETLS))
   1298       return result;
   1299   }
   1300 
   1301   /* Flush any data that needs to be sent */
   1302   if(smtpc->pp.sendleft)
   1303     return Curl_pp_flushsend(data, &smtpc->pp);
   1304 
   1305   do {
   1306     /* Read the response from the server */
   1307     result = Curl_pp_readresp(data, FIRSTSOCKET, &smtpc->pp,
   1308                               &smtpcode, &nread);
   1309     if(result)
   1310       return result;
   1311 
   1312     /* Store the latest response for later retrieval if necessary */
   1313     if(smtpc->state != SMTP_QUIT && smtpcode != 1)
   1314       data->info.httpcode = smtpcode;
   1315 
   1316     if(!smtpcode)
   1317       break;
   1318 
   1319     /* We have now received a full SMTP server response */
   1320     switch(smtpc->state) {
   1321     case SMTP_SERVERGREET:
   1322       result = smtp_state_servergreet_resp(data, smtpc,
   1323                                            smtpcode, smtpc->state);
   1324       break;
   1325 
   1326     case SMTP_EHLO:
   1327       result = smtp_state_ehlo_resp(data, smtpc, smtpcode, smtpc->state);
   1328       break;
   1329 
   1330     case SMTP_HELO:
   1331       result = smtp_state_helo_resp(data, smtpc, smtpcode, smtpc->state);
   1332       break;
   1333 
   1334     case SMTP_STARTTLS:
   1335       result = smtp_state_starttls_resp(data, smtpc, smtpcode, smtpc->state);
   1336       /* During UPGRADETLS, leave the read loop as we need to connect
   1337        * (e.g. TLS handshake) before we continue sending/receiving. */
   1338       if(!result && (smtpc->state == SMTP_UPGRADETLS))
   1339         goto upgrade_tls;
   1340       break;
   1341 
   1342     case SMTP_AUTH:
   1343       result = smtp_state_auth_resp(data, smtpc, smtpcode, smtpc->state);
   1344       break;
   1345 
   1346     case SMTP_COMMAND:
   1347       result = smtp_state_command_resp(data, smtpc, smtp,
   1348                                        smtpcode, smtpc->state);
   1349       break;
   1350 
   1351     case SMTP_MAIL:
   1352       result = smtp_state_mail_resp(data, smtpc, smtp, smtpcode, smtpc->state);
   1353       break;
   1354 
   1355     case SMTP_RCPT:
   1356       result = smtp_state_rcpt_resp(data, smtpc, smtp, smtpcode, smtpc->state);
   1357       break;
   1358 
   1359     case SMTP_DATA:
   1360       result = smtp_state_data_resp(data, smtpc, smtpcode, smtpc->state);
   1361       break;
   1362 
   1363     case SMTP_POSTDATA:
   1364       result = smtp_state_postdata_resp(data, smtpc, smtpcode, smtpc->state);
   1365       break;
   1366 
   1367     case SMTP_QUIT:
   1368     default:
   1369       /* internal error */
   1370       smtp_state(data, smtpc, SMTP_STOP);
   1371       break;
   1372     }
   1373   } while(!result && smtpc->state != SMTP_STOP &&
   1374           Curl_pp_moredata(&smtpc->pp));
   1375 
   1376   return result;
   1377 }
   1378 
   1379 /* Called repeatedly until done from multi.c */
   1380 static CURLcode smtp_multi_statemach(struct Curl_easy *data, bool *done)
   1381 {
   1382   CURLcode result = CURLE_OK;
   1383   struct smtp_conn *smtpc =
   1384     Curl_conn_meta_get(data->conn, CURL_META_SMTP_CONN);
   1385 
   1386   *done = FALSE;
   1387   if(!smtpc)
   1388     return CURLE_FAILED_INIT;
   1389 
   1390   result = Curl_pp_statemach(data, &smtpc->pp, FALSE, FALSE);
   1391   *done = (smtpc->state == SMTP_STOP);
   1392   return result;
   1393 }
   1394 
   1395 static CURLcode smtp_block_statemach(struct Curl_easy *data,
   1396                                      struct smtp_conn *smtpc,
   1397                                      bool disconnecting)
   1398 {
   1399   CURLcode result = CURLE_OK;
   1400 
   1401   while(smtpc->state != SMTP_STOP && !result)
   1402     result = Curl_pp_statemach(data, &smtpc->pp, TRUE, disconnecting);
   1403 
   1404   return result;
   1405 }
   1406 
   1407 /* For the SMTP "protocol connect" and "doing" phases only */
   1408 static int smtp_getsock(struct Curl_easy *data,
   1409                         struct connectdata *conn, curl_socket_t *socks)
   1410 {
   1411   struct smtp_conn *smtpc = Curl_conn_meta_get(conn, CURL_META_SMTP_CONN);
   1412   return smtpc ?
   1413          Curl_pp_getsock(data, &smtpc->pp, socks) : GETSOCK_BLANK;
   1414 }
   1415 
   1416 /***********************************************************************
   1417  *
   1418  * smtp_connect()
   1419  *
   1420  * This function should do everything that is to be considered a part of
   1421  * the connection phase.
   1422  *
   1423  * The variable pointed to by 'done' will be TRUE if the protocol-layer
   1424  * connect phase is done when this function returns, or FALSE if not.
   1425  */
   1426 static CURLcode smtp_connect(struct Curl_easy *data, bool *done)
   1427 {
   1428   struct smtp_conn *smtpc =
   1429     Curl_conn_meta_get(data->conn, CURL_META_SMTP_CONN);
   1430   CURLcode result = CURLE_OK;
   1431 
   1432   *done = FALSE; /* default to not done yet */
   1433   if(!smtpc)
   1434     return CURLE_FAILED_INIT;
   1435 
   1436   /* We always support persistent connections in SMTP */
   1437   connkeep(data->conn, "SMTP default");
   1438 
   1439   PINGPONG_SETUP(&smtpc->pp, smtp_pp_statemachine, smtp_endofresp);
   1440 
   1441   /* Initialize the SASL storage */
   1442   Curl_sasl_init(&smtpc->sasl, data, &saslsmtp);
   1443 
   1444   /* Initialise the pingpong layer */
   1445   Curl_pp_init(&smtpc->pp);
   1446 
   1447   /* Parse the URL options */
   1448   result = smtp_parse_url_options(data->conn, smtpc);
   1449   if(result)
   1450     return result;
   1451 
   1452   /* Parse the URL path */
   1453   result = smtp_parse_url_path(data, smtpc);
   1454   if(result)
   1455     return result;
   1456 
   1457   /* Start off waiting for the server greeting response */
   1458   smtp_state(data, smtpc, SMTP_SERVERGREET);
   1459 
   1460   result = smtp_multi_statemach(data, done);
   1461 
   1462   return result;
   1463 }
   1464 
   1465 /***********************************************************************
   1466  *
   1467  * smtp_done()
   1468  *
   1469  * The DONE function. This does what needs to be done after a single DO has
   1470  * performed.
   1471  *
   1472  * Input argument is already checked for validity.
   1473  */
   1474 static CURLcode smtp_done(struct Curl_easy *data, CURLcode status,
   1475                           bool premature)
   1476 {
   1477   struct smtp_conn *smtpc =
   1478     Curl_conn_meta_get(data->conn, CURL_META_SMTP_CONN);
   1479   CURLcode result = CURLE_OK;
   1480   struct connectdata *conn = data->conn;
   1481   struct SMTP *smtp = Curl_meta_get(data, CURL_META_SMTP_EASY);
   1482 
   1483   (void)premature;
   1484 
   1485   if(!smtpc)
   1486     return CURLE_FAILED_INIT;
   1487   if(!smtp)
   1488     return CURLE_OK;
   1489 
   1490   /* Cleanup our per-request based variables */
   1491   Curl_safefree(smtp->custom);
   1492 
   1493   if(status) {
   1494     connclose(conn, "SMTP done with bad status"); /* marked for closure */
   1495     result = status;         /* use the already set error code */
   1496   }
   1497   else if(!data->set.connect_only && data->set.mail_rcpt &&
   1498           (data->state.upload || IS_MIME_POST(data))) {
   1499 
   1500     smtp_state(data, smtpc, SMTP_POSTDATA);
   1501 
   1502     /* Run the state-machine */
   1503     result = smtp_block_statemach(data, smtpc, FALSE);
   1504   }
   1505 
   1506   /* Clear the transfer mode for the next request */
   1507   smtp->transfer = PPTRANSFER_BODY;
   1508   CURL_TRC_SMTP(data, "smtp_done(status=%d, premature=%d) -> %d",
   1509                 status, premature, result);
   1510   return result;
   1511 }
   1512 
   1513 /***********************************************************************
   1514  *
   1515  * smtp_perform()
   1516  *
   1517  * This is the actual DO function for SMTP. Transfer a mail, send a command
   1518  * or get some data according to the options previously setup.
   1519  */
   1520 static CURLcode smtp_perform(struct Curl_easy *data,
   1521                              struct smtp_conn *smtpc,
   1522                              struct SMTP *smtp,
   1523                              bool *connected,
   1524                              bool *dophase_done)
   1525 {
   1526   /* This is SMTP and no proxy */
   1527   CURLcode result = CURLE_OK;
   1528 
   1529   CURL_TRC_SMTP(data, "smtp_perform(), start");
   1530 
   1531   if(data->req.no_body) {
   1532     /* Requested no body means no transfer */
   1533     smtp->transfer = PPTRANSFER_INFO;
   1534   }
   1535 
   1536   *dophase_done = FALSE; /* not done yet */
   1537 
   1538   /* Store the first recipient (or NULL if not specified) */
   1539   smtp->rcpt = data->set.mail_rcpt;
   1540 
   1541   /* Track of whether we have successfully sent at least one RCPT TO command */
   1542   smtp->rcpt_had_ok = FALSE;
   1543 
   1544   /* Track of the last error we have received by sending RCPT TO command */
   1545   smtp->rcpt_last_error = 0;
   1546 
   1547   /* Initial data character is the first character in line: it is implicitly
   1548      preceded by a virtual CRLF. */
   1549   smtp->trailing_crlf = TRUE;
   1550   smtp->eob = 2;
   1551 
   1552   /* Start the first command in the DO phase */
   1553   if((data->state.upload || IS_MIME_POST(data)) && data->set.mail_rcpt)
   1554     /* MAIL transfer */
   1555     result = smtp_perform_mail(data, smtpc, smtp);
   1556   else
   1557     /* SMTP based command (VRFY, EXPN, NOOP, RSET or HELP) */
   1558     result = smtp_perform_command(data, smtpc, smtp);
   1559 
   1560   if(result)
   1561     goto out;
   1562 
   1563   /* Run the state-machine */
   1564   result = smtp_multi_statemach(data, dophase_done);
   1565 
   1566   *connected = Curl_conn_is_connected(data->conn, FIRSTSOCKET);
   1567 
   1568 out:
   1569   CURL_TRC_SMTP(data, "smtp_perform() -> %d, connected=%d, done=%d",
   1570                 result, *connected, *dophase_done);
   1571   return result;
   1572 }
   1573 
   1574 /***********************************************************************
   1575  *
   1576  * smtp_do()
   1577  *
   1578  * This function is registered as 'curl_do' function. It decodes the path
   1579  * parts etc as a wrapper to the actual DO function (smtp_perform).
   1580  *
   1581  * The input argument is already checked for validity.
   1582  */
   1583 static CURLcode smtp_do(struct Curl_easy *data, bool *done)
   1584 {
   1585   struct smtp_conn *smtpc =
   1586     Curl_conn_meta_get(data->conn, CURL_META_SMTP_CONN);
   1587   struct SMTP *smtp = Curl_meta_get(data, CURL_META_SMTP_EASY);
   1588   CURLcode result = CURLE_OK;
   1589 
   1590   DEBUGASSERT(data);
   1591   DEBUGASSERT(data->conn);
   1592   *done = FALSE; /* default to false */
   1593   if(!smtpc || !smtp)
   1594     return CURLE_FAILED_INIT;
   1595 
   1596   /* Parse the custom request */
   1597   result = smtp_parse_custom_request(data, smtp);
   1598   if(result)
   1599     return result;
   1600 
   1601   result = smtp_regular_transfer(data, smtpc, smtp, done);
   1602   CURL_TRC_SMTP(data, "smtp_do() -> %d, done=%d", result, *done);
   1603   return result;
   1604 }
   1605 
   1606 /***********************************************************************
   1607  *
   1608  * smtp_disconnect()
   1609  *
   1610  * Disconnect from an SMTP server. Cleanup protocol-specific per-connection
   1611  * resources. BLOCKING.
   1612  */
   1613 static CURLcode smtp_disconnect(struct Curl_easy *data,
   1614                                 struct connectdata *conn,
   1615                                 bool dead_connection)
   1616 {
   1617   struct smtp_conn *smtpc = Curl_conn_meta_get(conn, CURL_META_SMTP_CONN);
   1618 
   1619   (void)data;
   1620   if(!smtpc)
   1621     return CURLE_FAILED_INIT;
   1622 
   1623   /* We cannot send quit unconditionally. If this connection is stale or
   1624      bad in any way, sending quit and waiting around here will make the
   1625      disconnect wait in vain and cause more problems than we need to. */
   1626 
   1627   if(!dead_connection && conn->bits.protoconnstart &&
   1628      !Curl_pp_needs_flush(data, &smtpc->pp)) {
   1629     if(!smtp_perform_quit(data, smtpc))
   1630       (void)smtp_block_statemach(data, smtpc, TRUE); /* ignore on QUIT */
   1631   }
   1632 
   1633   CURL_TRC_SMTP(data, "smtp_disconnect(), finished");
   1634   return CURLE_OK;
   1635 }
   1636 
   1637 /* Call this when the DO phase has completed */
   1638 static CURLcode smtp_dophase_done(struct Curl_easy *data,
   1639                                   struct SMTP *smtp,
   1640                                   bool connected)
   1641 {
   1642   (void)connected;
   1643 
   1644   if(smtp->transfer != PPTRANSFER_BODY)
   1645     /* no data to transfer */
   1646     Curl_xfer_setup_nop(data);
   1647 
   1648   return CURLE_OK;
   1649 }
   1650 
   1651 /* Called from multi.c while DOing */
   1652 static CURLcode smtp_doing(struct Curl_easy *data, bool *dophase_done)
   1653 {
   1654   struct SMTP *smtp = Curl_meta_get(data, CURL_META_SMTP_EASY);
   1655   CURLcode result;
   1656 
   1657   if(!smtp)
   1658     return CURLE_FAILED_INIT;
   1659   result = smtp_multi_statemach(data, dophase_done);
   1660   if(result)
   1661     DEBUGF(infof(data, "DO phase failed"));
   1662   else if(*dophase_done) {
   1663     result = smtp_dophase_done(data, smtp, FALSE /* not connected */);
   1664 
   1665     DEBUGF(infof(data, "DO phase is complete"));
   1666   }
   1667 
   1668   CURL_TRC_SMTP(data, "smtp_doing() -> %d, done=%d", result, *dophase_done);
   1669   return result;
   1670 }
   1671 
   1672 /***********************************************************************
   1673  *
   1674  * smtp_regular_transfer()
   1675  *
   1676  * The input argument is already checked for validity.
   1677  *
   1678  * Performs all commands done before a regular transfer between a local and a
   1679  * remote host.
   1680  */
   1681 static CURLcode smtp_regular_transfer(struct Curl_easy *data,
   1682                                       struct smtp_conn *smtpc,
   1683                                       struct SMTP *smtp,
   1684                                       bool *dophase_done)
   1685 {
   1686   CURLcode result = CURLE_OK;
   1687   bool connected = FALSE;
   1688 
   1689   /* Make sure size is unknown at this point */
   1690   data->req.size = -1;
   1691 
   1692   /* Set the progress data */
   1693   Curl_pgrsSetUploadCounter(data, 0);
   1694   Curl_pgrsSetDownloadCounter(data, 0);
   1695   Curl_pgrsSetUploadSize(data, -1);
   1696   Curl_pgrsSetDownloadSize(data, -1);
   1697 
   1698   /* Carry out the perform */
   1699   result = smtp_perform(data, smtpc, smtp, &connected, dophase_done);
   1700 
   1701   /* Perform post DO phase operations if necessary */
   1702   if(!result && *dophase_done)
   1703     result = smtp_dophase_done(data, smtp, connected);
   1704 
   1705   CURL_TRC_SMTP(data, "smtp_regular_transfer() -> %d, done=%d",
   1706                 result, *dophase_done);
   1707   return result;
   1708 }
   1709 
   1710 
   1711 static void smtp_easy_dtor(void *key, size_t klen, void *entry)
   1712 {
   1713   struct SMTP *smtp = entry;
   1714   (void)key;
   1715   (void)klen;
   1716   free(smtp);
   1717 }
   1718 
   1719 static void smtp_conn_dtor(void *key, size_t klen, void *entry)
   1720 {
   1721   struct smtp_conn *smtpc = entry;
   1722   (void)key;
   1723   (void)klen;
   1724   Curl_pp_disconnect(&smtpc->pp);
   1725   Curl_safefree(smtpc->domain);
   1726   free(smtpc);
   1727 }
   1728 
   1729 static CURLcode smtp_setup_connection(struct Curl_easy *data,
   1730                                       struct connectdata *conn)
   1731 {
   1732   struct smtp_conn *smtpc;
   1733   struct SMTP *smtp;
   1734   CURLcode result = CURLE_OK;
   1735 
   1736   smtpc = calloc(1, sizeof(*smtpc));
   1737   if(!smtpc ||
   1738      Curl_conn_meta_set(conn, CURL_META_SMTP_CONN, smtpc, smtp_conn_dtor)) {
   1739      result = CURLE_OUT_OF_MEMORY;
   1740      goto out;
   1741   }
   1742 
   1743   smtp = calloc(1, sizeof(*smtp));
   1744   if(!smtp ||
   1745      Curl_meta_set(data, CURL_META_SMTP_EASY, smtp, smtp_easy_dtor))
   1746     result = CURLE_OUT_OF_MEMORY;
   1747 
   1748 out:
   1749   CURL_TRC_SMTP(data, "smtp_setup_connection() -> %d", result);
   1750   return result;
   1751 }
   1752 
   1753 /***********************************************************************
   1754  *
   1755  * smtp_parse_url_options()
   1756  *
   1757  * Parse the URL login options.
   1758  */
   1759 static CURLcode smtp_parse_url_options(struct connectdata *conn,
   1760                                        struct smtp_conn *smtpc)
   1761 {
   1762   CURLcode result = CURLE_OK;
   1763   const char *ptr = conn->options;
   1764 
   1765   while(!result && ptr && *ptr) {
   1766     const char *key = ptr;
   1767     const char *value;
   1768 
   1769     while(*ptr && *ptr != '=')
   1770       ptr++;
   1771 
   1772     value = ptr + 1;
   1773 
   1774     while(*ptr && *ptr != ';')
   1775       ptr++;
   1776 
   1777     if(curl_strnequal(key, "AUTH=", 5))
   1778       result = Curl_sasl_parse_url_auth_option(&smtpc->sasl,
   1779                                                value, ptr - value);
   1780     else
   1781       result = CURLE_URL_MALFORMAT;
   1782 
   1783     if(*ptr == ';')
   1784       ptr++;
   1785   }
   1786 
   1787   return result;
   1788 }
   1789 
   1790 /***********************************************************************
   1791  *
   1792  * smtp_parse_url_path()
   1793  *
   1794  * Parse the URL path into separate path components.
   1795  */
   1796 static CURLcode smtp_parse_url_path(struct Curl_easy *data,
   1797                                     struct smtp_conn *smtpc)
   1798 {
   1799   /* The SMTP struct is already initialised in smtp_connect() */
   1800   const char *path = &data->state.up.path[1]; /* skip leading path */
   1801   char localhost[HOSTNAME_MAX + 1];
   1802 
   1803   /* Calculate the path if necessary */
   1804   if(!*path) {
   1805     if(!Curl_gethostname(localhost, sizeof(localhost)))
   1806       path = localhost;
   1807     else
   1808       path = "localhost";
   1809   }
   1810 
   1811   /* URL decode the path and use it as the domain in our EHLO */
   1812   return Curl_urldecode(path, 0, &smtpc->domain, NULL, REJECT_CTRL);
   1813 }
   1814 
   1815 /***********************************************************************
   1816  *
   1817  * smtp_parse_custom_request()
   1818  *
   1819  * Parse the custom request.
   1820  */
   1821 static CURLcode smtp_parse_custom_request(struct Curl_easy *data,
   1822                                           struct SMTP *smtp)
   1823 {
   1824   CURLcode result = CURLE_OK;
   1825   const char *custom = data->set.str[STRING_CUSTOMREQUEST];
   1826 
   1827   /* URL decode the custom request */
   1828   if(custom)
   1829     result = Curl_urldecode(custom, 0, &smtp->custom, NULL, REJECT_CTRL);
   1830 
   1831   return result;
   1832 }
   1833 
   1834 /***********************************************************************
   1835  *
   1836  * smtp_parse_address()
   1837  *
   1838  * Parse the fully qualified mailbox address into a local address part and the
   1839  * hostname, converting the hostname to an IDN A-label, as per RFC-5890, if
   1840  * necessary.
   1841  *
   1842  * Parameters:
   1843  *
   1844  * conn  [in]              - The connection handle.
   1845  * fqma  [in]              - The fully qualified mailbox address (which may or
   1846  *                           may not contain UTF-8 characters).
   1847  * address        [in/out] - A new allocated buffer which holds the local
   1848  *                           address part of the mailbox. This buffer must be
   1849  *                           free'ed by the caller.
   1850  * host           [in/out] - The hostname structure that holds the original,
   1851  *                           and optionally encoded, hostname.
   1852  *                           Curl_free_idnconverted_hostname() must be called
   1853  *                           once the caller has finished with the structure.
   1854  *
   1855  * Returns CURLE_OK on success.
   1856  *
   1857  * Notes:
   1858  *
   1859  * Should a UTF-8 hostname require conversion to IDN ACE and we cannot honor
   1860  * that conversion then we shall return success. This allow the caller to send
   1861  * the data to the server as a U-label (as per RFC-6531 sect. 3.2).
   1862  *
   1863  * If an mailbox '@' separator cannot be located then the mailbox is considered
   1864  * to be either a local mailbox or an invalid mailbox (depending on what the
   1865  * calling function deems it to be) then the input will simply be returned in
   1866  * the address part with the hostname being NULL.
   1867  */
   1868 static CURLcode smtp_parse_address(const char *fqma, char **address,
   1869                                    struct hostname *host)
   1870 {
   1871   CURLcode result = CURLE_OK;
   1872   size_t length;
   1873 
   1874   /* Duplicate the fully qualified email address so we can manipulate it,
   1875      ensuring it does not contain the delimiters if specified */
   1876   char *dup = strdup(fqma[0] == '<' ? fqma + 1  : fqma);
   1877   if(!dup)
   1878     return CURLE_OUT_OF_MEMORY;
   1879 
   1880   length = strlen(dup);
   1881   if(length) {
   1882     if(dup[length - 1] == '>')
   1883       dup[length - 1] = '\0';
   1884   }
   1885 
   1886   /* Extract the hostname from the address (if we can) */
   1887   host->name = strpbrk(dup, "@");
   1888   if(host->name) {
   1889     *host->name = '\0';
   1890     host->name = host->name + 1;
   1891 
   1892     /* Attempt to convert the hostname to IDN ACE */
   1893     (void) Curl_idnconvert_hostname(host);
   1894 
   1895     /* If Curl_idnconvert_hostname() fails then we shall attempt to continue
   1896        and send the hostname using UTF-8 rather than as 7-bit ACE (which is
   1897        our preference) */
   1898   }
   1899 
   1900   /* Extract the local address from the mailbox */
   1901   *address = dup;
   1902 
   1903   return result;
   1904 }
   1905 
   1906 struct cr_eob_ctx {
   1907   struct Curl_creader super;
   1908   struct bufq buf;
   1909   size_t n_eob; /* how many EOB bytes we matched so far */
   1910   size_t eob;       /* Number of bytes of the EOB (End Of Body) that
   1911                        have been received so far */
   1912   BIT(read_eos);  /* we read an EOS from the next reader */
   1913   BIT(eos);       /* we have returned an EOS */
   1914 };
   1915 
   1916 static CURLcode cr_eob_init(struct Curl_easy *data,
   1917                             struct Curl_creader *reader)
   1918 {
   1919   struct cr_eob_ctx *ctx = reader->ctx;
   1920   (void)data;
   1921   /* The first char we read is the first on a line, as if we had
   1922    * read CRLF just before */
   1923   ctx->n_eob = 2;
   1924   Curl_bufq_init2(&ctx->buf, (16 * 1024), 1, BUFQ_OPT_SOFT_LIMIT);
   1925   return CURLE_OK;
   1926 }
   1927 
   1928 static void cr_eob_close(struct Curl_easy *data, struct Curl_creader *reader)
   1929 {
   1930   struct cr_eob_ctx *ctx = reader->ctx;
   1931   (void)data;
   1932   Curl_bufq_free(&ctx->buf);
   1933 }
   1934 
   1935 /* this is the 5-bytes End-Of-Body marker for SMTP */
   1936 #define SMTP_EOB "\r\n.\r\n"
   1937 #define SMTP_EOB_FIND_LEN 3
   1938 
   1939 /* client reader doing SMTP End-Of-Body escaping. */
   1940 static CURLcode cr_eob_read(struct Curl_easy *data,
   1941                             struct Curl_creader *reader,
   1942                             char *buf, size_t blen,
   1943                             size_t *pnread, bool *peos)
   1944 {
   1945   struct cr_eob_ctx *ctx = reader->ctx;
   1946   CURLcode result = CURLE_OK;
   1947   size_t nread, i, start, n;
   1948   bool eos;
   1949 
   1950   if(!ctx->read_eos && Curl_bufq_is_empty(&ctx->buf)) {
   1951     /* Get more and convert it when needed */
   1952     result = Curl_creader_read(data, reader->next, buf, blen, &nread, &eos);
   1953     if(result)
   1954       return result;
   1955 
   1956     ctx->read_eos = eos;
   1957     if(nread) {
   1958       if(!ctx->n_eob && !memchr(buf, SMTP_EOB[0], nread)) {
   1959         /* not in the middle of a match, no EOB start found, just pass */
   1960         *pnread = nread;
   1961         *peos = FALSE;
   1962         return CURLE_OK;
   1963       }
   1964       /* scan for EOB (continuation) and convert */
   1965       for(i = start = 0; i < nread; ++i) {
   1966         if(ctx->n_eob >= SMTP_EOB_FIND_LEN) {
   1967           /* matched the EOB prefix and seeing additional char, add '.' */
   1968           result = Curl_bufq_cwrite(&ctx->buf, buf + start, i - start, &n);
   1969           if(result)
   1970             return result;
   1971           result = Curl_bufq_cwrite(&ctx->buf, ".", 1, &n);
   1972           if(result)
   1973             return result;
   1974           ctx->n_eob = 0;
   1975           start = i;
   1976           if(data->state.infilesize > 0)
   1977             data->state.infilesize++;
   1978         }
   1979 
   1980         if(buf[i] != SMTP_EOB[ctx->n_eob])
   1981           ctx->n_eob = 0;
   1982 
   1983         if(buf[i] == SMTP_EOB[ctx->n_eob]) {
   1984           /* matching another char of the EOB */
   1985           ++ctx->n_eob;
   1986         }
   1987       }
   1988 
   1989       /* add any remainder to buf */
   1990       if(start < nread) {
   1991         result = Curl_bufq_cwrite(&ctx->buf, buf + start, nread - start, &n);
   1992         if(result)
   1993           return result;
   1994       }
   1995     }
   1996 
   1997     if(ctx->read_eos) {
   1998       /* if we last matched a CRLF or if the data was empty, add ".\r\n"
   1999        * to end the body. If we sent something and it did not end with "\r\n",
   2000        * add "\r\n.\r\n" to end the body */
   2001       const char *eob = SMTP_EOB;
   2002       switch(ctx->n_eob) {
   2003         case 2:
   2004           /* seen a CRLF at the end, just add the remainder */
   2005           eob = &SMTP_EOB[2];
   2006           break;
   2007         case 3:
   2008           /* ended with '\r\n.', we should escape the last '.' */
   2009           eob = "." SMTP_EOB;
   2010           break;
   2011         default:
   2012           break;
   2013       }
   2014       result = Curl_bufq_cwrite(&ctx->buf, eob, strlen(eob), &n);
   2015       if(result)
   2016         return result;
   2017     }
   2018   }
   2019 
   2020   *peos = FALSE;
   2021   if(!Curl_bufq_is_empty(&ctx->buf)) {
   2022     result = Curl_bufq_cread(&ctx->buf, buf, blen, pnread);
   2023   }
   2024   else
   2025     *pnread = 0;
   2026 
   2027   if(ctx->read_eos && Curl_bufq_is_empty(&ctx->buf)) {
   2028     /* no more data, read all, done. */
   2029     ctx->eos = TRUE;
   2030   }
   2031   *peos = ctx->eos;
   2032   DEBUGF(infof(data, "cr_eob_read(%zu) -> %d, %zd, %d",
   2033          blen, result, *pnread, *peos));
   2034   return result;
   2035 }
   2036 
   2037 static curl_off_t cr_eob_total_length(struct Curl_easy *data,
   2038                                       struct Curl_creader *reader)
   2039 {
   2040   /* this reader changes length depending on input */
   2041   (void)data;
   2042   (void)reader;
   2043   return -1;
   2044 }
   2045 
   2046 static const struct Curl_crtype cr_eob = {
   2047   "cr-smtp-eob",
   2048   cr_eob_init,
   2049   cr_eob_read,
   2050   cr_eob_close,
   2051   Curl_creader_def_needs_rewind,
   2052   cr_eob_total_length,
   2053   Curl_creader_def_resume_from,
   2054   Curl_creader_def_rewind,
   2055   Curl_creader_def_unpause,
   2056   Curl_creader_def_is_paused,
   2057   Curl_creader_def_done,
   2058   sizeof(struct cr_eob_ctx)
   2059 };
   2060 
   2061 static CURLcode cr_eob_add(struct Curl_easy *data)
   2062 {
   2063   struct Curl_creader *reader = NULL;
   2064   CURLcode result;
   2065 
   2066   result = Curl_creader_create(&reader, data, &cr_eob,
   2067                                CURL_CR_CONTENT_ENCODE);
   2068   if(!result)
   2069     result = Curl_creader_add(data, reader);
   2070 
   2071   if(result && reader)
   2072     Curl_creader_free(data, reader);
   2073   return result;
   2074 }
   2075 
   2076 #endif /* CURL_DISABLE_SMTP */