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