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