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