File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / curl / lib / smtp.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Jun 3 10:01:15 2020 UTC (5 years ago) by misho
Branches: curl, MAIN
CVS tags: v7_70_0p4, HEAD
curl

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>