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