113498266Sopenharmony_ci/***************************************************************************
213498266Sopenharmony_ci *                                  _   _ ____  _
313498266Sopenharmony_ci *  Project                     ___| | | |  _ \| |
413498266Sopenharmony_ci *                             / __| | | | |_) | |
513498266Sopenharmony_ci *                            | (__| |_| |  _ <| |___
613498266Sopenharmony_ci *                             \___|\___/|_| \_\_____|
713498266Sopenharmony_ci *
813498266Sopenharmony_ci * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
913498266Sopenharmony_ci *
1013498266Sopenharmony_ci * This software is licensed as described in the file COPYING, which
1113498266Sopenharmony_ci * you should have received as part of this distribution. The terms
1213498266Sopenharmony_ci * are also available at https://curl.se/docs/copyright.html.
1313498266Sopenharmony_ci *
1413498266Sopenharmony_ci * You may opt to use, copy, modify, merge, publish, distribute and/or sell
1513498266Sopenharmony_ci * copies of the Software, and permit persons to whom the Software is
1613498266Sopenharmony_ci * furnished to do so, under the terms of the COPYING file.
1713498266Sopenharmony_ci *
1813498266Sopenharmony_ci * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
1913498266Sopenharmony_ci * KIND, either express or implied.
2013498266Sopenharmony_ci *
2113498266Sopenharmony_ci * SPDX-License-Identifier: curl
2213498266Sopenharmony_ci *
2313498266Sopenharmony_ci * RFC1870 SMTP Service Extension for Message Size
2413498266Sopenharmony_ci * RFC2195 CRAM-MD5 authentication
2513498266Sopenharmony_ci * RFC2831 DIGEST-MD5 authentication
2613498266Sopenharmony_ci * RFC3207 SMTP over TLS
2713498266Sopenharmony_ci * RFC4422 Simple Authentication and Security Layer (SASL)
2813498266Sopenharmony_ci * RFC4616 PLAIN authentication
2913498266Sopenharmony_ci * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism
3013498266Sopenharmony_ci * RFC4954 SMTP Authentication
3113498266Sopenharmony_ci * RFC5321 SMTP protocol
3213498266Sopenharmony_ci * RFC5890 Internationalized Domain Names for Applications (IDNA)
3313498266Sopenharmony_ci * RFC6531 SMTP Extension for Internationalized Email
3413498266Sopenharmony_ci * RFC6532 Internationalized Email Headers
3513498266Sopenharmony_ci * RFC6749 OAuth 2.0 Authorization Framework
3613498266Sopenharmony_ci * RFC8314 Use of TLS for Email Submission and Access
3713498266Sopenharmony_ci * Draft   SMTP URL Interface   <draft-earhart-url-smtp-00.txt>
3813498266Sopenharmony_ci * Draft   LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt>
3913498266Sopenharmony_ci *
4013498266Sopenharmony_ci ***************************************************************************/
4113498266Sopenharmony_ci
4213498266Sopenharmony_ci#include "curl_setup.h"
4313498266Sopenharmony_ci
4413498266Sopenharmony_ci#ifndef CURL_DISABLE_SMTP
4513498266Sopenharmony_ci
4613498266Sopenharmony_ci#ifdef HAVE_NETINET_IN_H
4713498266Sopenharmony_ci#include <netinet/in.h>
4813498266Sopenharmony_ci#endif
4913498266Sopenharmony_ci#ifdef HAVE_ARPA_INET_H
5013498266Sopenharmony_ci#include <arpa/inet.h>
5113498266Sopenharmony_ci#endif
5213498266Sopenharmony_ci#ifdef HAVE_NETDB_H
5313498266Sopenharmony_ci#include <netdb.h>
5413498266Sopenharmony_ci#endif
5513498266Sopenharmony_ci#ifdef __VMS
5613498266Sopenharmony_ci#include <in.h>
5713498266Sopenharmony_ci#include <inet.h>
5813498266Sopenharmony_ci#endif
5913498266Sopenharmony_ci
6013498266Sopenharmony_ci#include <curl/curl.h>
6113498266Sopenharmony_ci#include "urldata.h"
6213498266Sopenharmony_ci#include "sendf.h"
6313498266Sopenharmony_ci#include "hostip.h"
6413498266Sopenharmony_ci#include "progress.h"
6513498266Sopenharmony_ci#include "transfer.h"
6613498266Sopenharmony_ci#include "escape.h"
6713498266Sopenharmony_ci#include "http.h" /* for HTTP proxy tunnel stuff */
6813498266Sopenharmony_ci#include "mime.h"
6913498266Sopenharmony_ci#include "socks.h"
7013498266Sopenharmony_ci#include "smtp.h"
7113498266Sopenharmony_ci#include "strtoofft.h"
7213498266Sopenharmony_ci#include "strcase.h"
7313498266Sopenharmony_ci#include "vtls/vtls.h"
7413498266Sopenharmony_ci#include "cfilters.h"
7513498266Sopenharmony_ci#include "connect.h"
7613498266Sopenharmony_ci#include "select.h"
7713498266Sopenharmony_ci#include "multiif.h"
7813498266Sopenharmony_ci#include "url.h"
7913498266Sopenharmony_ci#include "curl_gethostname.h"
8013498266Sopenharmony_ci#include "bufref.h"
8113498266Sopenharmony_ci#include "curl_sasl.h"
8213498266Sopenharmony_ci#include "warnless.h"
8313498266Sopenharmony_ci#include "idn.h"
8413498266Sopenharmony_ci/* The last 3 #include files should be in this order */
8513498266Sopenharmony_ci#include "curl_printf.h"
8613498266Sopenharmony_ci#include "curl_memory.h"
8713498266Sopenharmony_ci#include "memdebug.h"
8813498266Sopenharmony_ci
8913498266Sopenharmony_ci/* Local API functions */
9013498266Sopenharmony_cistatic CURLcode smtp_regular_transfer(struct Curl_easy *data, bool *done);
9113498266Sopenharmony_cistatic CURLcode smtp_do(struct Curl_easy *data, bool *done);
9213498266Sopenharmony_cistatic CURLcode smtp_done(struct Curl_easy *data, CURLcode status,
9313498266Sopenharmony_ci                          bool premature);
9413498266Sopenharmony_cistatic CURLcode smtp_connect(struct Curl_easy *data, bool *done);
9513498266Sopenharmony_cistatic CURLcode smtp_disconnect(struct Curl_easy *data,
9613498266Sopenharmony_ci                                struct connectdata *conn, bool dead);
9713498266Sopenharmony_cistatic CURLcode smtp_multi_statemach(struct Curl_easy *data, bool *done);
9813498266Sopenharmony_cistatic int smtp_getsock(struct Curl_easy *data,
9913498266Sopenharmony_ci                        struct connectdata *conn, curl_socket_t *socks);
10013498266Sopenharmony_cistatic CURLcode smtp_doing(struct Curl_easy *data, bool *dophase_done);
10113498266Sopenharmony_cistatic CURLcode smtp_setup_connection(struct Curl_easy *data,
10213498266Sopenharmony_ci                                      struct connectdata *conn);
10313498266Sopenharmony_cistatic CURLcode smtp_parse_url_options(struct connectdata *conn);
10413498266Sopenharmony_cistatic CURLcode smtp_parse_url_path(struct Curl_easy *data);
10513498266Sopenharmony_cistatic CURLcode smtp_parse_custom_request(struct Curl_easy *data);
10613498266Sopenharmony_cistatic CURLcode smtp_parse_address(const char *fqma,
10713498266Sopenharmony_ci                                   char **address, struct hostname *host);
10813498266Sopenharmony_cistatic CURLcode smtp_perform_auth(struct Curl_easy *data, const char *mech,
10913498266Sopenharmony_ci                                  const struct bufref *initresp);
11013498266Sopenharmony_cistatic CURLcode smtp_continue_auth(struct Curl_easy *data, const char *mech,
11113498266Sopenharmony_ci                                   const struct bufref *resp);
11213498266Sopenharmony_cistatic CURLcode smtp_cancel_auth(struct Curl_easy *data, const char *mech);
11313498266Sopenharmony_cistatic CURLcode smtp_get_message(struct Curl_easy *data, struct bufref *out);
11413498266Sopenharmony_ci
11513498266Sopenharmony_ci/*
11613498266Sopenharmony_ci * SMTP protocol handler.
11713498266Sopenharmony_ci */
11813498266Sopenharmony_ci
11913498266Sopenharmony_ciconst struct Curl_handler Curl_handler_smtp = {
12013498266Sopenharmony_ci  "SMTP",                           /* scheme */
12113498266Sopenharmony_ci  smtp_setup_connection,            /* setup_connection */
12213498266Sopenharmony_ci  smtp_do,                          /* do_it */
12313498266Sopenharmony_ci  smtp_done,                        /* done */
12413498266Sopenharmony_ci  ZERO_NULL,                        /* do_more */
12513498266Sopenharmony_ci  smtp_connect,                     /* connect_it */
12613498266Sopenharmony_ci  smtp_multi_statemach,             /* connecting */
12713498266Sopenharmony_ci  smtp_doing,                       /* doing */
12813498266Sopenharmony_ci  smtp_getsock,                     /* proto_getsock */
12913498266Sopenharmony_ci  smtp_getsock,                     /* doing_getsock */
13013498266Sopenharmony_ci  ZERO_NULL,                        /* domore_getsock */
13113498266Sopenharmony_ci  ZERO_NULL,                        /* perform_getsock */
13213498266Sopenharmony_ci  smtp_disconnect,                  /* disconnect */
13313498266Sopenharmony_ci  ZERO_NULL,                        /* write_resp */
13413498266Sopenharmony_ci  ZERO_NULL,                        /* connection_check */
13513498266Sopenharmony_ci  ZERO_NULL,                        /* attach connection */
13613498266Sopenharmony_ci  PORT_SMTP,                        /* defport */
13713498266Sopenharmony_ci  CURLPROTO_SMTP,                   /* protocol */
13813498266Sopenharmony_ci  CURLPROTO_SMTP,                   /* family */
13913498266Sopenharmony_ci  PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY | /* flags */
14013498266Sopenharmony_ci  PROTOPT_URLOPTIONS
14113498266Sopenharmony_ci};
14213498266Sopenharmony_ci
14313498266Sopenharmony_ci#ifdef USE_SSL
14413498266Sopenharmony_ci/*
14513498266Sopenharmony_ci * SMTPS protocol handler.
14613498266Sopenharmony_ci */
14713498266Sopenharmony_ci
14813498266Sopenharmony_ciconst struct Curl_handler Curl_handler_smtps = {
14913498266Sopenharmony_ci  "SMTPS",                          /* scheme */
15013498266Sopenharmony_ci  smtp_setup_connection,            /* setup_connection */
15113498266Sopenharmony_ci  smtp_do,                          /* do_it */
15213498266Sopenharmony_ci  smtp_done,                        /* done */
15313498266Sopenharmony_ci  ZERO_NULL,                        /* do_more */
15413498266Sopenharmony_ci  smtp_connect,                     /* connect_it */
15513498266Sopenharmony_ci  smtp_multi_statemach,             /* connecting */
15613498266Sopenharmony_ci  smtp_doing,                       /* doing */
15713498266Sopenharmony_ci  smtp_getsock,                     /* proto_getsock */
15813498266Sopenharmony_ci  smtp_getsock,                     /* doing_getsock */
15913498266Sopenharmony_ci  ZERO_NULL,                        /* domore_getsock */
16013498266Sopenharmony_ci  ZERO_NULL,                        /* perform_getsock */
16113498266Sopenharmony_ci  smtp_disconnect,                  /* disconnect */
16213498266Sopenharmony_ci  ZERO_NULL,                        /* write_resp */
16313498266Sopenharmony_ci  ZERO_NULL,                        /* connection_check */
16413498266Sopenharmony_ci  ZERO_NULL,                        /* attach connection */
16513498266Sopenharmony_ci  PORT_SMTPS,                       /* defport */
16613498266Sopenharmony_ci  CURLPROTO_SMTPS,                  /* protocol */
16713498266Sopenharmony_ci  CURLPROTO_SMTP,                   /* family */
16813498266Sopenharmony_ci  PROTOPT_CLOSEACTION | PROTOPT_SSL
16913498266Sopenharmony_ci  | PROTOPT_NOURLQUERY | PROTOPT_URLOPTIONS /* flags */
17013498266Sopenharmony_ci};
17113498266Sopenharmony_ci#endif
17213498266Sopenharmony_ci
17313498266Sopenharmony_ci/* SASL parameters for the smtp protocol */
17413498266Sopenharmony_cistatic const struct SASLproto saslsmtp = {
17513498266Sopenharmony_ci  "smtp",               /* The service name */
17613498266Sopenharmony_ci  smtp_perform_auth,    /* Send authentication command */
17713498266Sopenharmony_ci  smtp_continue_auth,   /* Send authentication continuation */
17813498266Sopenharmony_ci  smtp_cancel_auth,     /* Cancel authentication */
17913498266Sopenharmony_ci  smtp_get_message,     /* Get SASL response message */
18013498266Sopenharmony_ci  512 - 8,              /* Max line len - strlen("AUTH ") - 1 space - crlf */
18113498266Sopenharmony_ci  334,                  /* Code received when continuation is expected */
18213498266Sopenharmony_ci  235,                  /* Code to receive upon authentication success */
18313498266Sopenharmony_ci  SASL_AUTH_DEFAULT,    /* Default mechanisms */
18413498266Sopenharmony_ci  SASL_FLAG_BASE64      /* Configuration flags */
18513498266Sopenharmony_ci};
18613498266Sopenharmony_ci
18713498266Sopenharmony_ci#ifdef USE_SSL
18813498266Sopenharmony_cistatic void smtp_to_smtps(struct connectdata *conn)
18913498266Sopenharmony_ci{
19013498266Sopenharmony_ci  /* Change the connection handler */
19113498266Sopenharmony_ci  conn->handler = &Curl_handler_smtps;
19213498266Sopenharmony_ci
19313498266Sopenharmony_ci  /* Set the connection's upgraded to TLS flag */
19413498266Sopenharmony_ci  conn->bits.tls_upgraded = TRUE;
19513498266Sopenharmony_ci}
19613498266Sopenharmony_ci#else
19713498266Sopenharmony_ci#define smtp_to_smtps(x) Curl_nop_stmt
19813498266Sopenharmony_ci#endif
19913498266Sopenharmony_ci
20013498266Sopenharmony_ci/***********************************************************************
20113498266Sopenharmony_ci *
20213498266Sopenharmony_ci * smtp_endofresp()
20313498266Sopenharmony_ci *
20413498266Sopenharmony_ci * Checks for an ending SMTP status code at the start of the given string, but
20513498266Sopenharmony_ci * also detects various capabilities from the EHLO response including the
20613498266Sopenharmony_ci * supported authentication mechanisms.
20713498266Sopenharmony_ci */
20813498266Sopenharmony_cistatic bool smtp_endofresp(struct Curl_easy *data, struct connectdata *conn,
20913498266Sopenharmony_ci                           char *line, size_t len, int *resp)
21013498266Sopenharmony_ci{
21113498266Sopenharmony_ci  struct smtp_conn *smtpc = &conn->proto.smtpc;
21213498266Sopenharmony_ci  bool result = FALSE;
21313498266Sopenharmony_ci  (void)data;
21413498266Sopenharmony_ci
21513498266Sopenharmony_ci  /* Nothing for us */
21613498266Sopenharmony_ci  if(len < 4 || !ISDIGIT(line[0]) || !ISDIGIT(line[1]) || !ISDIGIT(line[2]))
21713498266Sopenharmony_ci    return FALSE;
21813498266Sopenharmony_ci
21913498266Sopenharmony_ci  /* Do we have a command response? This should be the response code followed
22013498266Sopenharmony_ci     by a space and optionally some text as per RFC-5321 and as outlined in
22113498266Sopenharmony_ci     Section 4. Examples of RFC-4954 but some email servers ignore this and
22213498266Sopenharmony_ci     only send the response code instead as per Section 4.2. */
22313498266Sopenharmony_ci  if(line[3] == ' ' || len == 5) {
22413498266Sopenharmony_ci    char tmpline[6];
22513498266Sopenharmony_ci
22613498266Sopenharmony_ci    result = TRUE;
22713498266Sopenharmony_ci    memset(tmpline, '\0', sizeof(tmpline));
22813498266Sopenharmony_ci    memcpy(tmpline, line, (len == 5 ? 5 : 3));
22913498266Sopenharmony_ci    *resp = curlx_sltosi(strtol(tmpline, NULL, 10));
23013498266Sopenharmony_ci
23113498266Sopenharmony_ci    /* Make sure real server never sends internal value */
23213498266Sopenharmony_ci    if(*resp == 1)
23313498266Sopenharmony_ci      *resp = 0;
23413498266Sopenharmony_ci  }
23513498266Sopenharmony_ci  /* Do we have a multiline (continuation) response? */
23613498266Sopenharmony_ci  else if(line[3] == '-' &&
23713498266Sopenharmony_ci          (smtpc->state == SMTP_EHLO || smtpc->state == SMTP_COMMAND)) {
23813498266Sopenharmony_ci    result = TRUE;
23913498266Sopenharmony_ci    *resp = 1;  /* Internal response code */
24013498266Sopenharmony_ci  }
24113498266Sopenharmony_ci
24213498266Sopenharmony_ci  return result;
24313498266Sopenharmony_ci}
24413498266Sopenharmony_ci
24513498266Sopenharmony_ci/***********************************************************************
24613498266Sopenharmony_ci *
24713498266Sopenharmony_ci * smtp_get_message()
24813498266Sopenharmony_ci *
24913498266Sopenharmony_ci * Gets the authentication message from the response buffer.
25013498266Sopenharmony_ci */
25113498266Sopenharmony_cistatic CURLcode smtp_get_message(struct Curl_easy *data, struct bufref *out)
25213498266Sopenharmony_ci{
25313498266Sopenharmony_ci  char *message = Curl_dyn_ptr(&data->conn->proto.smtpc.pp.recvbuf);
25413498266Sopenharmony_ci  size_t len = data->conn->proto.smtpc.pp.nfinal;
25513498266Sopenharmony_ci
25613498266Sopenharmony_ci  if(len > 4) {
25713498266Sopenharmony_ci    /* Find the start of the message */
25813498266Sopenharmony_ci    len -= 4;
25913498266Sopenharmony_ci    for(message += 4; *message == ' ' || *message == '\t'; message++, len--)
26013498266Sopenharmony_ci      ;
26113498266Sopenharmony_ci
26213498266Sopenharmony_ci    /* Find the end of the message */
26313498266Sopenharmony_ci    while(len--)
26413498266Sopenharmony_ci      if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' &&
26513498266Sopenharmony_ci         message[len] != '\t')
26613498266Sopenharmony_ci        break;
26713498266Sopenharmony_ci
26813498266Sopenharmony_ci    /* Terminate the message */
26913498266Sopenharmony_ci    message[++len] = '\0';
27013498266Sopenharmony_ci    Curl_bufref_set(out, message, len, NULL);
27113498266Sopenharmony_ci  }
27213498266Sopenharmony_ci  else
27313498266Sopenharmony_ci    /* junk input => zero length output */
27413498266Sopenharmony_ci    Curl_bufref_set(out, "", 0, NULL);
27513498266Sopenharmony_ci
27613498266Sopenharmony_ci  return CURLE_OK;
27713498266Sopenharmony_ci}
27813498266Sopenharmony_ci
27913498266Sopenharmony_ci/***********************************************************************
28013498266Sopenharmony_ci *
28113498266Sopenharmony_ci * smtp_state()
28213498266Sopenharmony_ci *
28313498266Sopenharmony_ci * This is the ONLY way to change SMTP state!
28413498266Sopenharmony_ci */
28513498266Sopenharmony_cistatic void smtp_state(struct Curl_easy *data, smtpstate newstate)
28613498266Sopenharmony_ci{
28713498266Sopenharmony_ci  struct smtp_conn *smtpc = &data->conn->proto.smtpc;
28813498266Sopenharmony_ci#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
28913498266Sopenharmony_ci  /* for debug purposes */
29013498266Sopenharmony_ci  static const char * const names[] = {
29113498266Sopenharmony_ci    "STOP",
29213498266Sopenharmony_ci    "SERVERGREET",
29313498266Sopenharmony_ci    "EHLO",
29413498266Sopenharmony_ci    "HELO",
29513498266Sopenharmony_ci    "STARTTLS",
29613498266Sopenharmony_ci    "UPGRADETLS",
29713498266Sopenharmony_ci    "AUTH",
29813498266Sopenharmony_ci    "COMMAND",
29913498266Sopenharmony_ci    "MAIL",
30013498266Sopenharmony_ci    "RCPT",
30113498266Sopenharmony_ci    "DATA",
30213498266Sopenharmony_ci    "POSTDATA",
30313498266Sopenharmony_ci    "QUIT",
30413498266Sopenharmony_ci    /* LAST */
30513498266Sopenharmony_ci  };
30613498266Sopenharmony_ci
30713498266Sopenharmony_ci  if(smtpc->state != newstate)
30813498266Sopenharmony_ci    infof(data, "SMTP %p state change from %s to %s",
30913498266Sopenharmony_ci          (void *)smtpc, names[smtpc->state], names[newstate]);
31013498266Sopenharmony_ci#endif
31113498266Sopenharmony_ci
31213498266Sopenharmony_ci  smtpc->state = newstate;
31313498266Sopenharmony_ci}
31413498266Sopenharmony_ci
31513498266Sopenharmony_ci/***********************************************************************
31613498266Sopenharmony_ci *
31713498266Sopenharmony_ci * smtp_perform_ehlo()
31813498266Sopenharmony_ci *
31913498266Sopenharmony_ci * Sends the EHLO command to not only initialise communication with the ESMTP
32013498266Sopenharmony_ci * server but to also obtain a list of server side supported capabilities.
32113498266Sopenharmony_ci */
32213498266Sopenharmony_cistatic CURLcode smtp_perform_ehlo(struct Curl_easy *data)
32313498266Sopenharmony_ci{
32413498266Sopenharmony_ci  CURLcode result = CURLE_OK;
32513498266Sopenharmony_ci  struct connectdata *conn = data->conn;
32613498266Sopenharmony_ci  struct smtp_conn *smtpc = &conn->proto.smtpc;
32713498266Sopenharmony_ci
32813498266Sopenharmony_ci  smtpc->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanism yet */
32913498266Sopenharmony_ci  smtpc->sasl.authused = SASL_AUTH_NONE;  /* Clear the authentication mechanism
33013498266Sopenharmony_ci                                             used for esmtp connections */
33113498266Sopenharmony_ci  smtpc->tls_supported = FALSE;           /* Clear the TLS capability */
33213498266Sopenharmony_ci  smtpc->auth_supported = FALSE;          /* Clear the AUTH capability */
33313498266Sopenharmony_ci
33413498266Sopenharmony_ci  /* Send the EHLO command */
33513498266Sopenharmony_ci  result = Curl_pp_sendf(data, &smtpc->pp, "EHLO %s", smtpc->domain);
33613498266Sopenharmony_ci
33713498266Sopenharmony_ci  if(!result)
33813498266Sopenharmony_ci    smtp_state(data, SMTP_EHLO);
33913498266Sopenharmony_ci
34013498266Sopenharmony_ci  return result;
34113498266Sopenharmony_ci}
34213498266Sopenharmony_ci
34313498266Sopenharmony_ci/***********************************************************************
34413498266Sopenharmony_ci *
34513498266Sopenharmony_ci * smtp_perform_helo()
34613498266Sopenharmony_ci *
34713498266Sopenharmony_ci * Sends the HELO command to initialise communication with the SMTP server.
34813498266Sopenharmony_ci */
34913498266Sopenharmony_cistatic CURLcode smtp_perform_helo(struct Curl_easy *data,
35013498266Sopenharmony_ci                                  struct connectdata *conn)
35113498266Sopenharmony_ci{
35213498266Sopenharmony_ci  CURLcode result = CURLE_OK;
35313498266Sopenharmony_ci  struct smtp_conn *smtpc = &conn->proto.smtpc;
35413498266Sopenharmony_ci
35513498266Sopenharmony_ci  smtpc->sasl.authused = SASL_AUTH_NONE; /* No authentication mechanism used
35613498266Sopenharmony_ci                                            in smtp connections */
35713498266Sopenharmony_ci
35813498266Sopenharmony_ci  /* Send the HELO command */
35913498266Sopenharmony_ci  result = Curl_pp_sendf(data, &smtpc->pp, "HELO %s", smtpc->domain);
36013498266Sopenharmony_ci
36113498266Sopenharmony_ci  if(!result)
36213498266Sopenharmony_ci    smtp_state(data, SMTP_HELO);
36313498266Sopenharmony_ci
36413498266Sopenharmony_ci  return result;
36513498266Sopenharmony_ci}
36613498266Sopenharmony_ci
36713498266Sopenharmony_ci/***********************************************************************
36813498266Sopenharmony_ci *
36913498266Sopenharmony_ci * smtp_perform_starttls()
37013498266Sopenharmony_ci *
37113498266Sopenharmony_ci * Sends the STLS command to start the upgrade to TLS.
37213498266Sopenharmony_ci */
37313498266Sopenharmony_cistatic CURLcode smtp_perform_starttls(struct Curl_easy *data,
37413498266Sopenharmony_ci                                      struct connectdata *conn)
37513498266Sopenharmony_ci{
37613498266Sopenharmony_ci  /* Send the STARTTLS command */
37713498266Sopenharmony_ci  CURLcode result = Curl_pp_sendf(data, &conn->proto.smtpc.pp,
37813498266Sopenharmony_ci                                  "%s", "STARTTLS");
37913498266Sopenharmony_ci
38013498266Sopenharmony_ci  if(!result)
38113498266Sopenharmony_ci    smtp_state(data, SMTP_STARTTLS);
38213498266Sopenharmony_ci
38313498266Sopenharmony_ci  return result;
38413498266Sopenharmony_ci}
38513498266Sopenharmony_ci
38613498266Sopenharmony_ci/***********************************************************************
38713498266Sopenharmony_ci *
38813498266Sopenharmony_ci * smtp_perform_upgrade_tls()
38913498266Sopenharmony_ci *
39013498266Sopenharmony_ci * Performs the upgrade to TLS.
39113498266Sopenharmony_ci */
39213498266Sopenharmony_cistatic CURLcode smtp_perform_upgrade_tls(struct Curl_easy *data)
39313498266Sopenharmony_ci{
39413498266Sopenharmony_ci  /* Start the SSL connection */
39513498266Sopenharmony_ci  struct connectdata *conn = data->conn;
39613498266Sopenharmony_ci  struct smtp_conn *smtpc = &conn->proto.smtpc;
39713498266Sopenharmony_ci  CURLcode result;
39813498266Sopenharmony_ci  bool ssldone = FALSE;
39913498266Sopenharmony_ci
40013498266Sopenharmony_ci  if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
40113498266Sopenharmony_ci    result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET);
40213498266Sopenharmony_ci    if(result)
40313498266Sopenharmony_ci      goto out;
40413498266Sopenharmony_ci  }
40513498266Sopenharmony_ci
40613498266Sopenharmony_ci  result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone);
40713498266Sopenharmony_ci  if(!result) {
40813498266Sopenharmony_ci    smtpc->ssldone = ssldone;
40913498266Sopenharmony_ci    if(smtpc->state != SMTP_UPGRADETLS)
41013498266Sopenharmony_ci      smtp_state(data, SMTP_UPGRADETLS);
41113498266Sopenharmony_ci
41213498266Sopenharmony_ci    if(smtpc->ssldone) {
41313498266Sopenharmony_ci      smtp_to_smtps(conn);
41413498266Sopenharmony_ci      result = smtp_perform_ehlo(data);
41513498266Sopenharmony_ci    }
41613498266Sopenharmony_ci  }
41713498266Sopenharmony_ciout:
41813498266Sopenharmony_ci  return result;
41913498266Sopenharmony_ci}
42013498266Sopenharmony_ci
42113498266Sopenharmony_ci/***********************************************************************
42213498266Sopenharmony_ci *
42313498266Sopenharmony_ci * smtp_perform_auth()
42413498266Sopenharmony_ci *
42513498266Sopenharmony_ci * Sends an AUTH command allowing the client to login with the given SASL
42613498266Sopenharmony_ci * authentication mechanism.
42713498266Sopenharmony_ci */
42813498266Sopenharmony_cistatic CURLcode smtp_perform_auth(struct Curl_easy *data,
42913498266Sopenharmony_ci                                  const char *mech,
43013498266Sopenharmony_ci                                  const struct bufref *initresp)
43113498266Sopenharmony_ci{
43213498266Sopenharmony_ci  CURLcode result = CURLE_OK;
43313498266Sopenharmony_ci  struct smtp_conn *smtpc = &data->conn->proto.smtpc;
43413498266Sopenharmony_ci  const char *ir = (const char *) Curl_bufref_ptr(initresp);
43513498266Sopenharmony_ci
43613498266Sopenharmony_ci  if(ir) {                                  /* AUTH <mech> ...<crlf> */
43713498266Sopenharmony_ci    /* Send the AUTH command with the initial response */
43813498266Sopenharmony_ci    result = Curl_pp_sendf(data, &smtpc->pp, "AUTH %s %s", mech, ir);
43913498266Sopenharmony_ci  }
44013498266Sopenharmony_ci  else {
44113498266Sopenharmony_ci    /* Send the AUTH command */
44213498266Sopenharmony_ci    result = Curl_pp_sendf(data, &smtpc->pp, "AUTH %s", mech);
44313498266Sopenharmony_ci  }
44413498266Sopenharmony_ci
44513498266Sopenharmony_ci  return result;
44613498266Sopenharmony_ci}
44713498266Sopenharmony_ci
44813498266Sopenharmony_ci/***********************************************************************
44913498266Sopenharmony_ci *
45013498266Sopenharmony_ci * smtp_continue_auth()
45113498266Sopenharmony_ci *
45213498266Sopenharmony_ci * Sends SASL continuation data.
45313498266Sopenharmony_ci */
45413498266Sopenharmony_cistatic CURLcode smtp_continue_auth(struct Curl_easy *data,
45513498266Sopenharmony_ci                                   const char *mech,
45613498266Sopenharmony_ci                                   const struct bufref *resp)
45713498266Sopenharmony_ci{
45813498266Sopenharmony_ci  struct smtp_conn *smtpc = &data->conn->proto.smtpc;
45913498266Sopenharmony_ci
46013498266Sopenharmony_ci  (void)mech;
46113498266Sopenharmony_ci
46213498266Sopenharmony_ci  return Curl_pp_sendf(data, &smtpc->pp,
46313498266Sopenharmony_ci                       "%s", (const char *) Curl_bufref_ptr(resp));
46413498266Sopenharmony_ci}
46513498266Sopenharmony_ci
46613498266Sopenharmony_ci/***********************************************************************
46713498266Sopenharmony_ci *
46813498266Sopenharmony_ci * smtp_cancel_auth()
46913498266Sopenharmony_ci *
47013498266Sopenharmony_ci * Sends SASL cancellation.
47113498266Sopenharmony_ci */
47213498266Sopenharmony_cistatic CURLcode smtp_cancel_auth(struct Curl_easy *data, const char *mech)
47313498266Sopenharmony_ci{
47413498266Sopenharmony_ci  struct smtp_conn *smtpc = &data->conn->proto.smtpc;
47513498266Sopenharmony_ci
47613498266Sopenharmony_ci  (void)mech;
47713498266Sopenharmony_ci
47813498266Sopenharmony_ci  return Curl_pp_sendf(data, &smtpc->pp, "*");
47913498266Sopenharmony_ci}
48013498266Sopenharmony_ci
48113498266Sopenharmony_ci/***********************************************************************
48213498266Sopenharmony_ci *
48313498266Sopenharmony_ci * smtp_perform_authentication()
48413498266Sopenharmony_ci *
48513498266Sopenharmony_ci * Initiates the authentication sequence, with the appropriate SASL
48613498266Sopenharmony_ci * authentication mechanism.
48713498266Sopenharmony_ci */
48813498266Sopenharmony_cistatic CURLcode smtp_perform_authentication(struct Curl_easy *data)
48913498266Sopenharmony_ci{
49013498266Sopenharmony_ci  CURLcode result = CURLE_OK;
49113498266Sopenharmony_ci  struct connectdata *conn = data->conn;
49213498266Sopenharmony_ci  struct smtp_conn *smtpc = &conn->proto.smtpc;
49313498266Sopenharmony_ci  saslprogress progress;
49413498266Sopenharmony_ci
49513498266Sopenharmony_ci  /* Check we have enough data to authenticate with, and the
49613498266Sopenharmony_ci     server supports authentication, and end the connect phase if not */
49713498266Sopenharmony_ci  if(!smtpc->auth_supported ||
49813498266Sopenharmony_ci     !Curl_sasl_can_authenticate(&smtpc->sasl, data)) {
49913498266Sopenharmony_ci    smtp_state(data, SMTP_STOP);
50013498266Sopenharmony_ci    return result;
50113498266Sopenharmony_ci  }
50213498266Sopenharmony_ci
50313498266Sopenharmony_ci  /* Calculate the SASL login details */
50413498266Sopenharmony_ci  result = Curl_sasl_start(&smtpc->sasl, data, FALSE, &progress);
50513498266Sopenharmony_ci
50613498266Sopenharmony_ci  if(!result) {
50713498266Sopenharmony_ci    if(progress == SASL_INPROGRESS)
50813498266Sopenharmony_ci      smtp_state(data, SMTP_AUTH);
50913498266Sopenharmony_ci    else {
51013498266Sopenharmony_ci      /* Other mechanisms not supported */
51113498266Sopenharmony_ci      infof(data, "No known authentication mechanisms supported");
51213498266Sopenharmony_ci      result = CURLE_LOGIN_DENIED;
51313498266Sopenharmony_ci    }
51413498266Sopenharmony_ci  }
51513498266Sopenharmony_ci
51613498266Sopenharmony_ci  return result;
51713498266Sopenharmony_ci}
51813498266Sopenharmony_ci
51913498266Sopenharmony_ci/***********************************************************************
52013498266Sopenharmony_ci *
52113498266Sopenharmony_ci * smtp_perform_command()
52213498266Sopenharmony_ci *
52313498266Sopenharmony_ci * Sends a SMTP based command.
52413498266Sopenharmony_ci */
52513498266Sopenharmony_cistatic CURLcode smtp_perform_command(struct Curl_easy *data)
52613498266Sopenharmony_ci{
52713498266Sopenharmony_ci  CURLcode result = CURLE_OK;
52813498266Sopenharmony_ci  struct connectdata *conn = data->conn;
52913498266Sopenharmony_ci  struct SMTP *smtp = data->req.p.smtp;
53013498266Sopenharmony_ci
53113498266Sopenharmony_ci  if(smtp->rcpt) {
53213498266Sopenharmony_ci    /* We notify the server we are sending UTF-8 data if a) it supports the
53313498266Sopenharmony_ci       SMTPUTF8 extension and b) The mailbox contains UTF-8 characters, in
53413498266Sopenharmony_ci       either the local address or host name parts. This is regardless of
53513498266Sopenharmony_ci       whether the host name is encoded using IDN ACE */
53613498266Sopenharmony_ci    bool utf8 = FALSE;
53713498266Sopenharmony_ci
53813498266Sopenharmony_ci    if((!smtp->custom) || (!smtp->custom[0])) {
53913498266Sopenharmony_ci      char *address = NULL;
54013498266Sopenharmony_ci      struct hostname host = { NULL, NULL, NULL, NULL };
54113498266Sopenharmony_ci
54213498266Sopenharmony_ci      /* Parse the mailbox to verify into the local address and host name
54313498266Sopenharmony_ci         parts, converting the host name to an IDN A-label if necessary */
54413498266Sopenharmony_ci      result = smtp_parse_address(smtp->rcpt->data,
54513498266Sopenharmony_ci                                  &address, &host);
54613498266Sopenharmony_ci      if(result)
54713498266Sopenharmony_ci        return result;
54813498266Sopenharmony_ci
54913498266Sopenharmony_ci      /* Establish whether we should report SMTPUTF8 to the server for this
55013498266Sopenharmony_ci         mailbox as per RFC-6531 sect. 3.1 point 6 */
55113498266Sopenharmony_ci      utf8 = (conn->proto.smtpc.utf8_supported) &&
55213498266Sopenharmony_ci             ((host.encalloc) || (!Curl_is_ASCII_name(address)) ||
55313498266Sopenharmony_ci              (!Curl_is_ASCII_name(host.name)));
55413498266Sopenharmony_ci
55513498266Sopenharmony_ci      /* Send the VRFY command (Note: The host name part may be absent when the
55613498266Sopenharmony_ci         host is a local system) */
55713498266Sopenharmony_ci      result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "VRFY %s%s%s%s",
55813498266Sopenharmony_ci                             address,
55913498266Sopenharmony_ci                             host.name ? "@" : "",
56013498266Sopenharmony_ci                             host.name ? host.name : "",
56113498266Sopenharmony_ci                             utf8 ? " SMTPUTF8" : "");
56213498266Sopenharmony_ci
56313498266Sopenharmony_ci      Curl_free_idnconverted_hostname(&host);
56413498266Sopenharmony_ci      free(address);
56513498266Sopenharmony_ci    }
56613498266Sopenharmony_ci    else {
56713498266Sopenharmony_ci      /* Establish whether we should report that we support SMTPUTF8 for EXPN
56813498266Sopenharmony_ci         commands to the server as per RFC-6531 sect. 3.1 point 6 */
56913498266Sopenharmony_ci      utf8 = (conn->proto.smtpc.utf8_supported) &&
57013498266Sopenharmony_ci             (!strcmp(smtp->custom, "EXPN"));
57113498266Sopenharmony_ci
57213498266Sopenharmony_ci      /* Send the custom recipient based command such as the EXPN command */
57313498266Sopenharmony_ci      result = Curl_pp_sendf(data, &conn->proto.smtpc.pp,
57413498266Sopenharmony_ci                             "%s %s%s", smtp->custom,
57513498266Sopenharmony_ci                             smtp->rcpt->data,
57613498266Sopenharmony_ci                             utf8 ? " SMTPUTF8" : "");
57713498266Sopenharmony_ci    }
57813498266Sopenharmony_ci  }
57913498266Sopenharmony_ci  else
58013498266Sopenharmony_ci    /* Send the non-recipient based command such as HELP */
58113498266Sopenharmony_ci    result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "%s",
58213498266Sopenharmony_ci                           smtp->custom && smtp->custom[0] != '\0' ?
58313498266Sopenharmony_ci                           smtp->custom : "HELP");
58413498266Sopenharmony_ci
58513498266Sopenharmony_ci  if(!result)
58613498266Sopenharmony_ci    smtp_state(data, SMTP_COMMAND);
58713498266Sopenharmony_ci
58813498266Sopenharmony_ci  return result;
58913498266Sopenharmony_ci}
59013498266Sopenharmony_ci
59113498266Sopenharmony_ci/***********************************************************************
59213498266Sopenharmony_ci *
59313498266Sopenharmony_ci * smtp_perform_mail()
59413498266Sopenharmony_ci *
59513498266Sopenharmony_ci * Sends an MAIL command to initiate the upload of a message.
59613498266Sopenharmony_ci */
59713498266Sopenharmony_cistatic CURLcode smtp_perform_mail(struct Curl_easy *data)
59813498266Sopenharmony_ci{
59913498266Sopenharmony_ci  char *from = NULL;
60013498266Sopenharmony_ci  char *auth = NULL;
60113498266Sopenharmony_ci  char *size = NULL;
60213498266Sopenharmony_ci  CURLcode result = CURLE_OK;
60313498266Sopenharmony_ci  struct connectdata *conn = data->conn;
60413498266Sopenharmony_ci
60513498266Sopenharmony_ci  /* We notify the server we are sending UTF-8 data if a) it supports the
60613498266Sopenharmony_ci     SMTPUTF8 extension and b) The mailbox contains UTF-8 characters, in
60713498266Sopenharmony_ci     either the local address or host name parts. This is regardless of
60813498266Sopenharmony_ci     whether the host name is encoded using IDN ACE */
60913498266Sopenharmony_ci  bool utf8 = FALSE;
61013498266Sopenharmony_ci
61113498266Sopenharmony_ci  /* Calculate the FROM parameter */
61213498266Sopenharmony_ci  if(data->set.str[STRING_MAIL_FROM]) {
61313498266Sopenharmony_ci    char *address = NULL;
61413498266Sopenharmony_ci    struct hostname host = { NULL, NULL, NULL, NULL };
61513498266Sopenharmony_ci
61613498266Sopenharmony_ci    /* Parse the FROM mailbox into the local address and host name parts,
61713498266Sopenharmony_ci       converting the host name to an IDN A-label if necessary */
61813498266Sopenharmony_ci    result = smtp_parse_address(data->set.str[STRING_MAIL_FROM],
61913498266Sopenharmony_ci                                &address, &host);
62013498266Sopenharmony_ci    if(result)
62113498266Sopenharmony_ci      return result;
62213498266Sopenharmony_ci
62313498266Sopenharmony_ci    /* Establish whether we should report SMTPUTF8 to the server for this
62413498266Sopenharmony_ci       mailbox as per RFC-6531 sect. 3.1 point 4 and sect. 3.4 */
62513498266Sopenharmony_ci    utf8 = (conn->proto.smtpc.utf8_supported) &&
62613498266Sopenharmony_ci           ((host.encalloc) || (!Curl_is_ASCII_name(address)) ||
62713498266Sopenharmony_ci            (!Curl_is_ASCII_name(host.name)));
62813498266Sopenharmony_ci
62913498266Sopenharmony_ci    if(host.name) {
63013498266Sopenharmony_ci      from = aprintf("<%s@%s>", address, host.name);
63113498266Sopenharmony_ci
63213498266Sopenharmony_ci      Curl_free_idnconverted_hostname(&host);
63313498266Sopenharmony_ci    }
63413498266Sopenharmony_ci    else
63513498266Sopenharmony_ci      /* An invalid mailbox was provided but we'll simply let the server worry
63613498266Sopenharmony_ci         about that and reply with a 501 error */
63713498266Sopenharmony_ci      from = aprintf("<%s>", address);
63813498266Sopenharmony_ci
63913498266Sopenharmony_ci    free(address);
64013498266Sopenharmony_ci  }
64113498266Sopenharmony_ci  else
64213498266Sopenharmony_ci    /* Null reverse-path, RFC-5321, sect. 3.6.3 */
64313498266Sopenharmony_ci    from = strdup("<>");
64413498266Sopenharmony_ci
64513498266Sopenharmony_ci  if(!from)
64613498266Sopenharmony_ci    return CURLE_OUT_OF_MEMORY;
64713498266Sopenharmony_ci
64813498266Sopenharmony_ci  /* Calculate the optional AUTH parameter */
64913498266Sopenharmony_ci  if(data->set.str[STRING_MAIL_AUTH] && conn->proto.smtpc.sasl.authused) {
65013498266Sopenharmony_ci    if(data->set.str[STRING_MAIL_AUTH][0] != '\0') {
65113498266Sopenharmony_ci      char *address = NULL;
65213498266Sopenharmony_ci      struct hostname host = { NULL, NULL, NULL, NULL };
65313498266Sopenharmony_ci
65413498266Sopenharmony_ci      /* Parse the AUTH mailbox into the local address and host name parts,
65513498266Sopenharmony_ci         converting the host name to an IDN A-label if necessary */
65613498266Sopenharmony_ci      result = smtp_parse_address(data->set.str[STRING_MAIL_AUTH],
65713498266Sopenharmony_ci                                  &address, &host);
65813498266Sopenharmony_ci      if(result) {
65913498266Sopenharmony_ci        free(from);
66013498266Sopenharmony_ci        return result;
66113498266Sopenharmony_ci      }
66213498266Sopenharmony_ci
66313498266Sopenharmony_ci      /* Establish whether we should report SMTPUTF8 to the server for this
66413498266Sopenharmony_ci         mailbox as per RFC-6531 sect. 3.1 point 4 and sect. 3.4 */
66513498266Sopenharmony_ci      if((!utf8) && (conn->proto.smtpc.utf8_supported) &&
66613498266Sopenharmony_ci         ((host.encalloc) || (!Curl_is_ASCII_name(address)) ||
66713498266Sopenharmony_ci          (!Curl_is_ASCII_name(host.name))))
66813498266Sopenharmony_ci        utf8 = TRUE;
66913498266Sopenharmony_ci
67013498266Sopenharmony_ci      if(host.name) {
67113498266Sopenharmony_ci        auth = aprintf("<%s@%s>", address, host.name);
67213498266Sopenharmony_ci
67313498266Sopenharmony_ci        Curl_free_idnconverted_hostname(&host);
67413498266Sopenharmony_ci      }
67513498266Sopenharmony_ci      else
67613498266Sopenharmony_ci        /* An invalid mailbox was provided but we'll simply let the server
67713498266Sopenharmony_ci           worry about it */
67813498266Sopenharmony_ci        auth = aprintf("<%s>", address);
67913498266Sopenharmony_ci
68013498266Sopenharmony_ci      free(address);
68113498266Sopenharmony_ci    }
68213498266Sopenharmony_ci    else
68313498266Sopenharmony_ci      /* Empty AUTH, RFC-2554, sect. 5 */
68413498266Sopenharmony_ci      auth = strdup("<>");
68513498266Sopenharmony_ci
68613498266Sopenharmony_ci    if(!auth) {
68713498266Sopenharmony_ci      free(from);
68813498266Sopenharmony_ci
68913498266Sopenharmony_ci      return CURLE_OUT_OF_MEMORY;
69013498266Sopenharmony_ci    }
69113498266Sopenharmony_ci  }
69213498266Sopenharmony_ci
69313498266Sopenharmony_ci  /* Prepare the mime data if some. */
69413498266Sopenharmony_ci  if(data->set.mimepost.kind != MIMEKIND_NONE) {
69513498266Sopenharmony_ci    /* Use the whole structure as data. */
69613498266Sopenharmony_ci    data->set.mimepost.flags &= ~MIME_BODY_ONLY;
69713498266Sopenharmony_ci
69813498266Sopenharmony_ci    /* Add external headers and mime version. */
69913498266Sopenharmony_ci    curl_mime_headers(&data->set.mimepost, data->set.headers, 0);
70013498266Sopenharmony_ci    result = Curl_mime_prepare_headers(data, &data->set.mimepost, NULL,
70113498266Sopenharmony_ci                                       NULL, MIMESTRATEGY_MAIL);
70213498266Sopenharmony_ci
70313498266Sopenharmony_ci    if(!result)
70413498266Sopenharmony_ci      if(!Curl_checkheaders(data, STRCONST("Mime-Version")))
70513498266Sopenharmony_ci        result = Curl_mime_add_header(&data->set.mimepost.curlheaders,
70613498266Sopenharmony_ci                                      "Mime-Version: 1.0");
70713498266Sopenharmony_ci
70813498266Sopenharmony_ci    /* Make sure we will read the entire mime structure. */
70913498266Sopenharmony_ci    if(!result)
71013498266Sopenharmony_ci      result = Curl_mime_rewind(&data->set.mimepost);
71113498266Sopenharmony_ci
71213498266Sopenharmony_ci    if(result) {
71313498266Sopenharmony_ci      free(from);
71413498266Sopenharmony_ci      free(auth);
71513498266Sopenharmony_ci
71613498266Sopenharmony_ci      return result;
71713498266Sopenharmony_ci    }
71813498266Sopenharmony_ci
71913498266Sopenharmony_ci    data->state.infilesize = Curl_mime_size(&data->set.mimepost);
72013498266Sopenharmony_ci
72113498266Sopenharmony_ci    /* Read from mime structure. */
72213498266Sopenharmony_ci    data->state.fread_func = (curl_read_callback) Curl_mime_read;
72313498266Sopenharmony_ci    data->state.in = (void *) &data->set.mimepost;
72413498266Sopenharmony_ci  }
72513498266Sopenharmony_ci
72613498266Sopenharmony_ci  /* Calculate the optional SIZE parameter */
72713498266Sopenharmony_ci  if(conn->proto.smtpc.size_supported && data->state.infilesize > 0) {
72813498266Sopenharmony_ci    size = aprintf("%" CURL_FORMAT_CURL_OFF_T, data->state.infilesize);
72913498266Sopenharmony_ci
73013498266Sopenharmony_ci    if(!size) {
73113498266Sopenharmony_ci      free(from);
73213498266Sopenharmony_ci      free(auth);
73313498266Sopenharmony_ci
73413498266Sopenharmony_ci      return CURLE_OUT_OF_MEMORY;
73513498266Sopenharmony_ci    }
73613498266Sopenharmony_ci  }
73713498266Sopenharmony_ci
73813498266Sopenharmony_ci  /* If the mailboxes in the FROM and AUTH parameters don't include a UTF-8
73913498266Sopenharmony_ci     based address then quickly scan through the recipient list and check if
74013498266Sopenharmony_ci     any there do, as we need to correctly identify our support for SMTPUTF8
74113498266Sopenharmony_ci     in the envelope, as per RFC-6531 sect. 3.4 */
74213498266Sopenharmony_ci  if(conn->proto.smtpc.utf8_supported && !utf8) {
74313498266Sopenharmony_ci    struct SMTP *smtp = data->req.p.smtp;
74413498266Sopenharmony_ci    struct curl_slist *rcpt = smtp->rcpt;
74513498266Sopenharmony_ci
74613498266Sopenharmony_ci    while(rcpt && !utf8) {
74713498266Sopenharmony_ci      /* Does the host name contain non-ASCII characters? */
74813498266Sopenharmony_ci      if(!Curl_is_ASCII_name(rcpt->data))
74913498266Sopenharmony_ci        utf8 = TRUE;
75013498266Sopenharmony_ci
75113498266Sopenharmony_ci      rcpt = rcpt->next;
75213498266Sopenharmony_ci    }
75313498266Sopenharmony_ci  }
75413498266Sopenharmony_ci
75513498266Sopenharmony_ci  /* Send the MAIL command */
75613498266Sopenharmony_ci  result = Curl_pp_sendf(data, &conn->proto.smtpc.pp,
75713498266Sopenharmony_ci                         "MAIL FROM:%s%s%s%s%s%s",
75813498266Sopenharmony_ci                         from,                 /* Mandatory                 */
75913498266Sopenharmony_ci                         auth ? " AUTH=" : "", /* Optional on AUTH support  */
76013498266Sopenharmony_ci                         auth ? auth : "",     /*                           */
76113498266Sopenharmony_ci                         size ? " SIZE=" : "", /* Optional on SIZE support  */
76213498266Sopenharmony_ci                         size ? size : "",     /*                           */
76313498266Sopenharmony_ci                         utf8 ? " SMTPUTF8"    /* Internationalised mailbox */
76413498266Sopenharmony_ci                               : "");          /* included in our envelope  */
76513498266Sopenharmony_ci
76613498266Sopenharmony_ci  free(from);
76713498266Sopenharmony_ci  free(auth);
76813498266Sopenharmony_ci  free(size);
76913498266Sopenharmony_ci
77013498266Sopenharmony_ci  if(!result)
77113498266Sopenharmony_ci    smtp_state(data, SMTP_MAIL);
77213498266Sopenharmony_ci
77313498266Sopenharmony_ci  return result;
77413498266Sopenharmony_ci}
77513498266Sopenharmony_ci
77613498266Sopenharmony_ci/***********************************************************************
77713498266Sopenharmony_ci *
77813498266Sopenharmony_ci * smtp_perform_rcpt_to()
77913498266Sopenharmony_ci *
78013498266Sopenharmony_ci * Sends a RCPT TO command for a given recipient as part of the message upload
78113498266Sopenharmony_ci * process.
78213498266Sopenharmony_ci */
78313498266Sopenharmony_cistatic CURLcode smtp_perform_rcpt_to(struct Curl_easy *data)
78413498266Sopenharmony_ci{
78513498266Sopenharmony_ci  CURLcode result = CURLE_OK;
78613498266Sopenharmony_ci  struct connectdata *conn = data->conn;
78713498266Sopenharmony_ci  struct SMTP *smtp = data->req.p.smtp;
78813498266Sopenharmony_ci  char *address = NULL;
78913498266Sopenharmony_ci  struct hostname host = { NULL, NULL, NULL, NULL };
79013498266Sopenharmony_ci
79113498266Sopenharmony_ci  /* Parse the recipient mailbox into the local address and host name parts,
79213498266Sopenharmony_ci     converting the host name to an IDN A-label if necessary */
79313498266Sopenharmony_ci  result = smtp_parse_address(smtp->rcpt->data,
79413498266Sopenharmony_ci                              &address, &host);
79513498266Sopenharmony_ci  if(result)
79613498266Sopenharmony_ci    return result;
79713498266Sopenharmony_ci
79813498266Sopenharmony_ci  /* Send the RCPT TO command */
79913498266Sopenharmony_ci  if(host.name)
80013498266Sopenharmony_ci    result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "RCPT TO:<%s@%s>",
80113498266Sopenharmony_ci                           address, host.name);
80213498266Sopenharmony_ci  else
80313498266Sopenharmony_ci    /* An invalid mailbox was provided but we'll simply let the server worry
80413498266Sopenharmony_ci       about that and reply with a 501 error */
80513498266Sopenharmony_ci    result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "RCPT TO:<%s>",
80613498266Sopenharmony_ci                           address);
80713498266Sopenharmony_ci
80813498266Sopenharmony_ci  Curl_free_idnconverted_hostname(&host);
80913498266Sopenharmony_ci  free(address);
81013498266Sopenharmony_ci
81113498266Sopenharmony_ci  if(!result)
81213498266Sopenharmony_ci    smtp_state(data, SMTP_RCPT);
81313498266Sopenharmony_ci
81413498266Sopenharmony_ci  return result;
81513498266Sopenharmony_ci}
81613498266Sopenharmony_ci
81713498266Sopenharmony_ci/***********************************************************************
81813498266Sopenharmony_ci *
81913498266Sopenharmony_ci * smtp_perform_quit()
82013498266Sopenharmony_ci *
82113498266Sopenharmony_ci * Performs the quit action prior to sclose() being called.
82213498266Sopenharmony_ci */
82313498266Sopenharmony_cistatic CURLcode smtp_perform_quit(struct Curl_easy *data,
82413498266Sopenharmony_ci                                  struct connectdata *conn)
82513498266Sopenharmony_ci{
82613498266Sopenharmony_ci  /* Send the QUIT command */
82713498266Sopenharmony_ci  CURLcode result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "%s", "QUIT");
82813498266Sopenharmony_ci
82913498266Sopenharmony_ci  if(!result)
83013498266Sopenharmony_ci    smtp_state(data, SMTP_QUIT);
83113498266Sopenharmony_ci
83213498266Sopenharmony_ci  return result;
83313498266Sopenharmony_ci}
83413498266Sopenharmony_ci
83513498266Sopenharmony_ci/* For the initial server greeting */
83613498266Sopenharmony_cistatic CURLcode smtp_state_servergreet_resp(struct Curl_easy *data,
83713498266Sopenharmony_ci                                            int smtpcode,
83813498266Sopenharmony_ci                                            smtpstate instate)
83913498266Sopenharmony_ci{
84013498266Sopenharmony_ci  CURLcode result = CURLE_OK;
84113498266Sopenharmony_ci  (void)instate; /* no use for this yet */
84213498266Sopenharmony_ci
84313498266Sopenharmony_ci  if(smtpcode/100 != 2) {
84413498266Sopenharmony_ci    failf(data, "Got unexpected smtp-server response: %d", smtpcode);
84513498266Sopenharmony_ci    result = CURLE_WEIRD_SERVER_REPLY;
84613498266Sopenharmony_ci  }
84713498266Sopenharmony_ci  else
84813498266Sopenharmony_ci    result = smtp_perform_ehlo(data);
84913498266Sopenharmony_ci
85013498266Sopenharmony_ci  return result;
85113498266Sopenharmony_ci}
85213498266Sopenharmony_ci
85313498266Sopenharmony_ci/* For STARTTLS responses */
85413498266Sopenharmony_cistatic CURLcode smtp_state_starttls_resp(struct Curl_easy *data,
85513498266Sopenharmony_ci                                         int smtpcode,
85613498266Sopenharmony_ci                                         smtpstate instate)
85713498266Sopenharmony_ci{
85813498266Sopenharmony_ci  CURLcode result = CURLE_OK;
85913498266Sopenharmony_ci  (void)instate; /* no use for this yet */
86013498266Sopenharmony_ci
86113498266Sopenharmony_ci  /* Pipelining in response is forbidden. */
86213498266Sopenharmony_ci  if(data->conn->proto.smtpc.pp.overflow)
86313498266Sopenharmony_ci    return CURLE_WEIRD_SERVER_REPLY;
86413498266Sopenharmony_ci
86513498266Sopenharmony_ci  if(smtpcode != 220) {
86613498266Sopenharmony_ci    if(data->set.use_ssl != CURLUSESSL_TRY) {
86713498266Sopenharmony_ci      failf(data, "STARTTLS denied, code %d", smtpcode);
86813498266Sopenharmony_ci      result = CURLE_USE_SSL_FAILED;
86913498266Sopenharmony_ci    }
87013498266Sopenharmony_ci    else
87113498266Sopenharmony_ci      result = smtp_perform_authentication(data);
87213498266Sopenharmony_ci  }
87313498266Sopenharmony_ci  else
87413498266Sopenharmony_ci    result = smtp_perform_upgrade_tls(data);
87513498266Sopenharmony_ci
87613498266Sopenharmony_ci  return result;
87713498266Sopenharmony_ci}
87813498266Sopenharmony_ci
87913498266Sopenharmony_ci/* For EHLO responses */
88013498266Sopenharmony_cistatic CURLcode smtp_state_ehlo_resp(struct Curl_easy *data,
88113498266Sopenharmony_ci                                     struct connectdata *conn, int smtpcode,
88213498266Sopenharmony_ci                                     smtpstate instate)
88313498266Sopenharmony_ci{
88413498266Sopenharmony_ci  CURLcode result = CURLE_OK;
88513498266Sopenharmony_ci  struct smtp_conn *smtpc = &conn->proto.smtpc;
88613498266Sopenharmony_ci  const char *line = Curl_dyn_ptr(&smtpc->pp.recvbuf);
88713498266Sopenharmony_ci  size_t len = smtpc->pp.nfinal;
88813498266Sopenharmony_ci
88913498266Sopenharmony_ci  (void)instate; /* no use for this yet */
89013498266Sopenharmony_ci
89113498266Sopenharmony_ci  if(smtpcode/100 != 2 && smtpcode != 1) {
89213498266Sopenharmony_ci    if(data->set.use_ssl <= CURLUSESSL_TRY
89313498266Sopenharmony_ci       || Curl_conn_is_ssl(conn, FIRSTSOCKET))
89413498266Sopenharmony_ci      result = smtp_perform_helo(data, conn);
89513498266Sopenharmony_ci    else {
89613498266Sopenharmony_ci      failf(data, "Remote access denied: %d", smtpcode);
89713498266Sopenharmony_ci      result = CURLE_REMOTE_ACCESS_DENIED;
89813498266Sopenharmony_ci    }
89913498266Sopenharmony_ci  }
90013498266Sopenharmony_ci  else if(len >= 4) {
90113498266Sopenharmony_ci    line += 4;
90213498266Sopenharmony_ci    len -= 4;
90313498266Sopenharmony_ci
90413498266Sopenharmony_ci    /* Does the server support the STARTTLS capability? */
90513498266Sopenharmony_ci    if(len >= 8 && !memcmp(line, "STARTTLS", 8))
90613498266Sopenharmony_ci      smtpc->tls_supported = TRUE;
90713498266Sopenharmony_ci
90813498266Sopenharmony_ci    /* Does the server support the SIZE capability? */
90913498266Sopenharmony_ci    else if(len >= 4 && !memcmp(line, "SIZE", 4))
91013498266Sopenharmony_ci      smtpc->size_supported = TRUE;
91113498266Sopenharmony_ci
91213498266Sopenharmony_ci    /* Does the server support the UTF-8 capability? */
91313498266Sopenharmony_ci    else if(len >= 8 && !memcmp(line, "SMTPUTF8", 8))
91413498266Sopenharmony_ci      smtpc->utf8_supported = TRUE;
91513498266Sopenharmony_ci
91613498266Sopenharmony_ci    /* Does the server support authentication? */
91713498266Sopenharmony_ci    else if(len >= 5 && !memcmp(line, "AUTH ", 5)) {
91813498266Sopenharmony_ci      smtpc->auth_supported = TRUE;
91913498266Sopenharmony_ci
92013498266Sopenharmony_ci      /* Advance past the AUTH keyword */
92113498266Sopenharmony_ci      line += 5;
92213498266Sopenharmony_ci      len -= 5;
92313498266Sopenharmony_ci
92413498266Sopenharmony_ci      /* Loop through the data line */
92513498266Sopenharmony_ci      for(;;) {
92613498266Sopenharmony_ci        size_t llen;
92713498266Sopenharmony_ci        size_t wordlen;
92813498266Sopenharmony_ci        unsigned short mechbit;
92913498266Sopenharmony_ci
93013498266Sopenharmony_ci        while(len &&
93113498266Sopenharmony_ci              (*line == ' ' || *line == '\t' ||
93213498266Sopenharmony_ci               *line == '\r' || *line == '\n')) {
93313498266Sopenharmony_ci
93413498266Sopenharmony_ci          line++;
93513498266Sopenharmony_ci          len--;
93613498266Sopenharmony_ci        }
93713498266Sopenharmony_ci
93813498266Sopenharmony_ci        if(!len)
93913498266Sopenharmony_ci          break;
94013498266Sopenharmony_ci
94113498266Sopenharmony_ci        /* Extract the word */
94213498266Sopenharmony_ci        for(wordlen = 0; wordlen < len && line[wordlen] != ' ' &&
94313498266Sopenharmony_ci              line[wordlen] != '\t' && line[wordlen] != '\r' &&
94413498266Sopenharmony_ci              line[wordlen] != '\n';)
94513498266Sopenharmony_ci          wordlen++;
94613498266Sopenharmony_ci
94713498266Sopenharmony_ci        /* Test the word for a matching authentication mechanism */
94813498266Sopenharmony_ci        mechbit = Curl_sasl_decode_mech(line, wordlen, &llen);
94913498266Sopenharmony_ci        if(mechbit && llen == wordlen)
95013498266Sopenharmony_ci          smtpc->sasl.authmechs |= mechbit;
95113498266Sopenharmony_ci
95213498266Sopenharmony_ci        line += wordlen;
95313498266Sopenharmony_ci        len -= wordlen;
95413498266Sopenharmony_ci      }
95513498266Sopenharmony_ci    }
95613498266Sopenharmony_ci
95713498266Sopenharmony_ci    if(smtpcode != 1) {
95813498266Sopenharmony_ci      if(data->set.use_ssl && !Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
95913498266Sopenharmony_ci        /* We don't have a SSL/TLS connection yet, but SSL is requested */
96013498266Sopenharmony_ci        if(smtpc->tls_supported)
96113498266Sopenharmony_ci          /* Switch to TLS connection now */
96213498266Sopenharmony_ci          result = smtp_perform_starttls(data, conn);
96313498266Sopenharmony_ci        else if(data->set.use_ssl == CURLUSESSL_TRY)
96413498266Sopenharmony_ci          /* Fallback and carry on with authentication */
96513498266Sopenharmony_ci          result = smtp_perform_authentication(data);
96613498266Sopenharmony_ci        else {
96713498266Sopenharmony_ci          failf(data, "STARTTLS not supported.");
96813498266Sopenharmony_ci          result = CURLE_USE_SSL_FAILED;
96913498266Sopenharmony_ci        }
97013498266Sopenharmony_ci      }
97113498266Sopenharmony_ci      else
97213498266Sopenharmony_ci        result = smtp_perform_authentication(data);
97313498266Sopenharmony_ci    }
97413498266Sopenharmony_ci  }
97513498266Sopenharmony_ci  else {
97613498266Sopenharmony_ci    failf(data, "Unexpectedly short EHLO response");
97713498266Sopenharmony_ci    result = CURLE_WEIRD_SERVER_REPLY;
97813498266Sopenharmony_ci  }
97913498266Sopenharmony_ci
98013498266Sopenharmony_ci  return result;
98113498266Sopenharmony_ci}
98213498266Sopenharmony_ci
98313498266Sopenharmony_ci/* For HELO responses */
98413498266Sopenharmony_cistatic CURLcode smtp_state_helo_resp(struct Curl_easy *data, int smtpcode,
98513498266Sopenharmony_ci                                     smtpstate instate)
98613498266Sopenharmony_ci{
98713498266Sopenharmony_ci  CURLcode result = CURLE_OK;
98813498266Sopenharmony_ci  (void)instate; /* no use for this yet */
98913498266Sopenharmony_ci
99013498266Sopenharmony_ci  if(smtpcode/100 != 2) {
99113498266Sopenharmony_ci    failf(data, "Remote access denied: %d", smtpcode);
99213498266Sopenharmony_ci    result = CURLE_REMOTE_ACCESS_DENIED;
99313498266Sopenharmony_ci  }
99413498266Sopenharmony_ci  else
99513498266Sopenharmony_ci    /* End of connect phase */
99613498266Sopenharmony_ci    smtp_state(data, SMTP_STOP);
99713498266Sopenharmony_ci
99813498266Sopenharmony_ci  return result;
99913498266Sopenharmony_ci}
100013498266Sopenharmony_ci
100113498266Sopenharmony_ci/* For SASL authentication responses */
100213498266Sopenharmony_cistatic CURLcode smtp_state_auth_resp(struct Curl_easy *data,
100313498266Sopenharmony_ci                                     int smtpcode,
100413498266Sopenharmony_ci                                     smtpstate instate)
100513498266Sopenharmony_ci{
100613498266Sopenharmony_ci  CURLcode result = CURLE_OK;
100713498266Sopenharmony_ci  struct connectdata *conn = data->conn;
100813498266Sopenharmony_ci  struct smtp_conn *smtpc = &conn->proto.smtpc;
100913498266Sopenharmony_ci  saslprogress progress;
101013498266Sopenharmony_ci
101113498266Sopenharmony_ci  (void)instate; /* no use for this yet */
101213498266Sopenharmony_ci
101313498266Sopenharmony_ci  result = Curl_sasl_continue(&smtpc->sasl, data, smtpcode, &progress);
101413498266Sopenharmony_ci  if(!result)
101513498266Sopenharmony_ci    switch(progress) {
101613498266Sopenharmony_ci    case SASL_DONE:
101713498266Sopenharmony_ci      smtp_state(data, SMTP_STOP);  /* Authenticated */
101813498266Sopenharmony_ci      break;
101913498266Sopenharmony_ci    case SASL_IDLE:            /* No mechanism left after cancellation */
102013498266Sopenharmony_ci      failf(data, "Authentication cancelled");
102113498266Sopenharmony_ci      result = CURLE_LOGIN_DENIED;
102213498266Sopenharmony_ci      break;
102313498266Sopenharmony_ci    default:
102413498266Sopenharmony_ci      break;
102513498266Sopenharmony_ci    }
102613498266Sopenharmony_ci
102713498266Sopenharmony_ci  return result;
102813498266Sopenharmony_ci}
102913498266Sopenharmony_ci
103013498266Sopenharmony_ci/* For command responses */
103113498266Sopenharmony_cistatic CURLcode smtp_state_command_resp(struct Curl_easy *data, int smtpcode,
103213498266Sopenharmony_ci                                        smtpstate instate)
103313498266Sopenharmony_ci{
103413498266Sopenharmony_ci  CURLcode result = CURLE_OK;
103513498266Sopenharmony_ci  struct SMTP *smtp = data->req.p.smtp;
103613498266Sopenharmony_ci  char *line = Curl_dyn_ptr(&data->conn->proto.smtpc.pp.recvbuf);
103713498266Sopenharmony_ci  size_t len = data->conn->proto.smtpc.pp.nfinal;
103813498266Sopenharmony_ci
103913498266Sopenharmony_ci  (void)instate; /* no use for this yet */
104013498266Sopenharmony_ci
104113498266Sopenharmony_ci  if((smtp->rcpt && smtpcode/100 != 2 && smtpcode != 553 && smtpcode != 1) ||
104213498266Sopenharmony_ci     (!smtp->rcpt && smtpcode/100 != 2 && smtpcode != 1)) {
104313498266Sopenharmony_ci    failf(data, "Command failed: %d", smtpcode);
104413498266Sopenharmony_ci    result = CURLE_WEIRD_SERVER_REPLY;
104513498266Sopenharmony_ci  }
104613498266Sopenharmony_ci  else {
104713498266Sopenharmony_ci    if(!data->req.no_body)
104813498266Sopenharmony_ci      result = Curl_client_write(data, CLIENTWRITE_BODY, line, len);
104913498266Sopenharmony_ci
105013498266Sopenharmony_ci    if(smtpcode != 1) {
105113498266Sopenharmony_ci      if(smtp->rcpt) {
105213498266Sopenharmony_ci        smtp->rcpt = smtp->rcpt->next;
105313498266Sopenharmony_ci
105413498266Sopenharmony_ci        if(smtp->rcpt) {
105513498266Sopenharmony_ci          /* Send the next command */
105613498266Sopenharmony_ci          result = smtp_perform_command(data);
105713498266Sopenharmony_ci        }
105813498266Sopenharmony_ci        else
105913498266Sopenharmony_ci          /* End of DO phase */
106013498266Sopenharmony_ci          smtp_state(data, SMTP_STOP);
106113498266Sopenharmony_ci      }
106213498266Sopenharmony_ci      else
106313498266Sopenharmony_ci        /* End of DO phase */
106413498266Sopenharmony_ci        smtp_state(data, SMTP_STOP);
106513498266Sopenharmony_ci    }
106613498266Sopenharmony_ci  }
106713498266Sopenharmony_ci
106813498266Sopenharmony_ci  return result;
106913498266Sopenharmony_ci}
107013498266Sopenharmony_ci
107113498266Sopenharmony_ci/* For MAIL responses */
107213498266Sopenharmony_cistatic CURLcode smtp_state_mail_resp(struct Curl_easy *data, int smtpcode,
107313498266Sopenharmony_ci                                     smtpstate instate)
107413498266Sopenharmony_ci{
107513498266Sopenharmony_ci  CURLcode result = CURLE_OK;
107613498266Sopenharmony_ci  (void)instate; /* no use for this yet */
107713498266Sopenharmony_ci
107813498266Sopenharmony_ci  if(smtpcode/100 != 2) {
107913498266Sopenharmony_ci    failf(data, "MAIL failed: %d", smtpcode);
108013498266Sopenharmony_ci    result = CURLE_SEND_ERROR;
108113498266Sopenharmony_ci  }
108213498266Sopenharmony_ci  else
108313498266Sopenharmony_ci    /* Start the RCPT TO command */
108413498266Sopenharmony_ci    result = smtp_perform_rcpt_to(data);
108513498266Sopenharmony_ci
108613498266Sopenharmony_ci  return result;
108713498266Sopenharmony_ci}
108813498266Sopenharmony_ci
108913498266Sopenharmony_ci/* For RCPT responses */
109013498266Sopenharmony_cistatic CURLcode smtp_state_rcpt_resp(struct Curl_easy *data,
109113498266Sopenharmony_ci                                     struct connectdata *conn, int smtpcode,
109213498266Sopenharmony_ci                                     smtpstate instate)
109313498266Sopenharmony_ci{
109413498266Sopenharmony_ci  CURLcode result = CURLE_OK;
109513498266Sopenharmony_ci  struct SMTP *smtp = data->req.p.smtp;
109613498266Sopenharmony_ci  bool is_smtp_err = FALSE;
109713498266Sopenharmony_ci  bool is_smtp_blocking_err = FALSE;
109813498266Sopenharmony_ci
109913498266Sopenharmony_ci  (void)instate; /* no use for this yet */
110013498266Sopenharmony_ci
110113498266Sopenharmony_ci  is_smtp_err = (smtpcode/100 != 2) ? TRUE : FALSE;
110213498266Sopenharmony_ci
110313498266Sopenharmony_ci  /* If there's multiple RCPT TO to be issued, it's possible to ignore errors
110413498266Sopenharmony_ci     and proceed with only the valid addresses. */
110513498266Sopenharmony_ci  is_smtp_blocking_err =
110613498266Sopenharmony_ci    (is_smtp_err && !data->set.mail_rcpt_allowfails) ? TRUE : FALSE;
110713498266Sopenharmony_ci
110813498266Sopenharmony_ci  if(is_smtp_err) {
110913498266Sopenharmony_ci    /* Remembering the last failure which we can report if all "RCPT TO" have
111013498266Sopenharmony_ci       failed and we cannot proceed. */
111113498266Sopenharmony_ci    smtp->rcpt_last_error = smtpcode;
111213498266Sopenharmony_ci
111313498266Sopenharmony_ci    if(is_smtp_blocking_err) {
111413498266Sopenharmony_ci      failf(data, "RCPT failed: %d", smtpcode);
111513498266Sopenharmony_ci      result = CURLE_SEND_ERROR;
111613498266Sopenharmony_ci    }
111713498266Sopenharmony_ci  }
111813498266Sopenharmony_ci  else {
111913498266Sopenharmony_ci    /* Some RCPT TO commands have succeeded. */
112013498266Sopenharmony_ci    smtp->rcpt_had_ok = TRUE;
112113498266Sopenharmony_ci  }
112213498266Sopenharmony_ci
112313498266Sopenharmony_ci  if(!is_smtp_blocking_err) {
112413498266Sopenharmony_ci    smtp->rcpt = smtp->rcpt->next;
112513498266Sopenharmony_ci
112613498266Sopenharmony_ci    if(smtp->rcpt)
112713498266Sopenharmony_ci      /* Send the next RCPT TO command */
112813498266Sopenharmony_ci      result = smtp_perform_rcpt_to(data);
112913498266Sopenharmony_ci    else {
113013498266Sopenharmony_ci      /* We weren't able to issue a successful RCPT TO command while going
113113498266Sopenharmony_ci         over recipients (potentially multiple). Sending back last error. */
113213498266Sopenharmony_ci      if(!smtp->rcpt_had_ok) {
113313498266Sopenharmony_ci        failf(data, "RCPT failed: %d (last error)", smtp->rcpt_last_error);
113413498266Sopenharmony_ci        result = CURLE_SEND_ERROR;
113513498266Sopenharmony_ci      }
113613498266Sopenharmony_ci      else {
113713498266Sopenharmony_ci        /* Send the DATA command */
113813498266Sopenharmony_ci        result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "%s", "DATA");
113913498266Sopenharmony_ci
114013498266Sopenharmony_ci        if(!result)
114113498266Sopenharmony_ci          smtp_state(data, SMTP_DATA);
114213498266Sopenharmony_ci      }
114313498266Sopenharmony_ci    }
114413498266Sopenharmony_ci  }
114513498266Sopenharmony_ci
114613498266Sopenharmony_ci  return result;
114713498266Sopenharmony_ci}
114813498266Sopenharmony_ci
114913498266Sopenharmony_ci/* For DATA response */
115013498266Sopenharmony_cistatic CURLcode smtp_state_data_resp(struct Curl_easy *data, int smtpcode,
115113498266Sopenharmony_ci                                     smtpstate instate)
115213498266Sopenharmony_ci{
115313498266Sopenharmony_ci  CURLcode result = CURLE_OK;
115413498266Sopenharmony_ci  (void)instate; /* no use for this yet */
115513498266Sopenharmony_ci
115613498266Sopenharmony_ci  if(smtpcode != 354) {
115713498266Sopenharmony_ci    failf(data, "DATA failed: %d", smtpcode);
115813498266Sopenharmony_ci    result = CURLE_SEND_ERROR;
115913498266Sopenharmony_ci  }
116013498266Sopenharmony_ci  else {
116113498266Sopenharmony_ci    /* Set the progress upload size */
116213498266Sopenharmony_ci    Curl_pgrsSetUploadSize(data, data->state.infilesize);
116313498266Sopenharmony_ci
116413498266Sopenharmony_ci    /* SMTP upload */
116513498266Sopenharmony_ci    Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET);
116613498266Sopenharmony_ci
116713498266Sopenharmony_ci    /* End of DO phase */
116813498266Sopenharmony_ci    smtp_state(data, SMTP_STOP);
116913498266Sopenharmony_ci  }
117013498266Sopenharmony_ci
117113498266Sopenharmony_ci  return result;
117213498266Sopenharmony_ci}
117313498266Sopenharmony_ci
117413498266Sopenharmony_ci/* For POSTDATA responses, which are received after the entire DATA
117513498266Sopenharmony_ci   part has been sent to the server */
117613498266Sopenharmony_cistatic CURLcode smtp_state_postdata_resp(struct Curl_easy *data,
117713498266Sopenharmony_ci                                         int smtpcode,
117813498266Sopenharmony_ci                                         smtpstate instate)
117913498266Sopenharmony_ci{
118013498266Sopenharmony_ci  CURLcode result = CURLE_OK;
118113498266Sopenharmony_ci
118213498266Sopenharmony_ci  (void)instate; /* no use for this yet */
118313498266Sopenharmony_ci
118413498266Sopenharmony_ci  if(smtpcode != 250)
118513498266Sopenharmony_ci    result = CURLE_WEIRD_SERVER_REPLY;
118613498266Sopenharmony_ci
118713498266Sopenharmony_ci  /* End of DONE phase */
118813498266Sopenharmony_ci  smtp_state(data, SMTP_STOP);
118913498266Sopenharmony_ci
119013498266Sopenharmony_ci  return result;
119113498266Sopenharmony_ci}
119213498266Sopenharmony_ci
119313498266Sopenharmony_cistatic CURLcode smtp_statemachine(struct Curl_easy *data,
119413498266Sopenharmony_ci                                  struct connectdata *conn)
119513498266Sopenharmony_ci{
119613498266Sopenharmony_ci  CURLcode result = CURLE_OK;
119713498266Sopenharmony_ci  curl_socket_t sock = conn->sock[FIRSTSOCKET];
119813498266Sopenharmony_ci  int smtpcode;
119913498266Sopenharmony_ci  struct smtp_conn *smtpc = &conn->proto.smtpc;
120013498266Sopenharmony_ci  struct pingpong *pp = &smtpc->pp;
120113498266Sopenharmony_ci  size_t nread = 0;
120213498266Sopenharmony_ci
120313498266Sopenharmony_ci  /* Busy upgrading the connection; right now all I/O is SSL/TLS, not SMTP */
120413498266Sopenharmony_ci  if(smtpc->state == SMTP_UPGRADETLS)
120513498266Sopenharmony_ci    return smtp_perform_upgrade_tls(data);
120613498266Sopenharmony_ci
120713498266Sopenharmony_ci  /* Flush any data that needs to be sent */
120813498266Sopenharmony_ci  if(pp->sendleft)
120913498266Sopenharmony_ci    return Curl_pp_flushsend(data, pp);
121013498266Sopenharmony_ci
121113498266Sopenharmony_ci  do {
121213498266Sopenharmony_ci    /* Read the response from the server */
121313498266Sopenharmony_ci    result = Curl_pp_readresp(data, sock, pp, &smtpcode, &nread);
121413498266Sopenharmony_ci    if(result)
121513498266Sopenharmony_ci      return result;
121613498266Sopenharmony_ci
121713498266Sopenharmony_ci    /* Store the latest response for later retrieval if necessary */
121813498266Sopenharmony_ci    if(smtpc->state != SMTP_QUIT && smtpcode != 1)
121913498266Sopenharmony_ci      data->info.httpcode = smtpcode;
122013498266Sopenharmony_ci
122113498266Sopenharmony_ci    if(!smtpcode)
122213498266Sopenharmony_ci      break;
122313498266Sopenharmony_ci
122413498266Sopenharmony_ci    /* We have now received a full SMTP server response */
122513498266Sopenharmony_ci    switch(smtpc->state) {
122613498266Sopenharmony_ci    case SMTP_SERVERGREET:
122713498266Sopenharmony_ci      result = smtp_state_servergreet_resp(data, smtpcode, smtpc->state);
122813498266Sopenharmony_ci      break;
122913498266Sopenharmony_ci
123013498266Sopenharmony_ci    case SMTP_EHLO:
123113498266Sopenharmony_ci      result = smtp_state_ehlo_resp(data, conn, smtpcode, smtpc->state);
123213498266Sopenharmony_ci      break;
123313498266Sopenharmony_ci
123413498266Sopenharmony_ci    case SMTP_HELO:
123513498266Sopenharmony_ci      result = smtp_state_helo_resp(data, smtpcode, smtpc->state);
123613498266Sopenharmony_ci      break;
123713498266Sopenharmony_ci
123813498266Sopenharmony_ci    case SMTP_STARTTLS:
123913498266Sopenharmony_ci      result = smtp_state_starttls_resp(data, smtpcode, smtpc->state);
124013498266Sopenharmony_ci      break;
124113498266Sopenharmony_ci
124213498266Sopenharmony_ci    case SMTP_AUTH:
124313498266Sopenharmony_ci      result = smtp_state_auth_resp(data, smtpcode, smtpc->state);
124413498266Sopenharmony_ci      break;
124513498266Sopenharmony_ci
124613498266Sopenharmony_ci    case SMTP_COMMAND:
124713498266Sopenharmony_ci      result = smtp_state_command_resp(data, smtpcode, smtpc->state);
124813498266Sopenharmony_ci      break;
124913498266Sopenharmony_ci
125013498266Sopenharmony_ci    case SMTP_MAIL:
125113498266Sopenharmony_ci      result = smtp_state_mail_resp(data, smtpcode, smtpc->state);
125213498266Sopenharmony_ci      break;
125313498266Sopenharmony_ci
125413498266Sopenharmony_ci    case SMTP_RCPT:
125513498266Sopenharmony_ci      result = smtp_state_rcpt_resp(data, conn, smtpcode, smtpc->state);
125613498266Sopenharmony_ci      break;
125713498266Sopenharmony_ci
125813498266Sopenharmony_ci    case SMTP_DATA:
125913498266Sopenharmony_ci      result = smtp_state_data_resp(data, smtpcode, smtpc->state);
126013498266Sopenharmony_ci      break;
126113498266Sopenharmony_ci
126213498266Sopenharmony_ci    case SMTP_POSTDATA:
126313498266Sopenharmony_ci      result = smtp_state_postdata_resp(data, smtpcode, smtpc->state);
126413498266Sopenharmony_ci      break;
126513498266Sopenharmony_ci
126613498266Sopenharmony_ci    case SMTP_QUIT:
126713498266Sopenharmony_ci    default:
126813498266Sopenharmony_ci      /* internal error */
126913498266Sopenharmony_ci      smtp_state(data, SMTP_STOP);
127013498266Sopenharmony_ci      break;
127113498266Sopenharmony_ci    }
127213498266Sopenharmony_ci  } while(!result && smtpc->state != SMTP_STOP && Curl_pp_moredata(pp));
127313498266Sopenharmony_ci
127413498266Sopenharmony_ci  return result;
127513498266Sopenharmony_ci}
127613498266Sopenharmony_ci
127713498266Sopenharmony_ci/* Called repeatedly until done from multi.c */
127813498266Sopenharmony_cistatic CURLcode smtp_multi_statemach(struct Curl_easy *data, bool *done)
127913498266Sopenharmony_ci{
128013498266Sopenharmony_ci  CURLcode result = CURLE_OK;
128113498266Sopenharmony_ci  struct connectdata *conn = data->conn;
128213498266Sopenharmony_ci  struct smtp_conn *smtpc = &conn->proto.smtpc;
128313498266Sopenharmony_ci
128413498266Sopenharmony_ci  if((conn->handler->flags & PROTOPT_SSL) && !smtpc->ssldone) {
128513498266Sopenharmony_ci    bool ssldone = FALSE;
128613498266Sopenharmony_ci    result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone);
128713498266Sopenharmony_ci    smtpc->ssldone = ssldone;
128813498266Sopenharmony_ci    if(result || !smtpc->ssldone)
128913498266Sopenharmony_ci      return result;
129013498266Sopenharmony_ci  }
129113498266Sopenharmony_ci
129213498266Sopenharmony_ci  result = Curl_pp_statemach(data, &smtpc->pp, FALSE, FALSE);
129313498266Sopenharmony_ci  *done = (smtpc->state == SMTP_STOP) ? TRUE : FALSE;
129413498266Sopenharmony_ci
129513498266Sopenharmony_ci  return result;
129613498266Sopenharmony_ci}
129713498266Sopenharmony_ci
129813498266Sopenharmony_cistatic CURLcode smtp_block_statemach(struct Curl_easy *data,
129913498266Sopenharmony_ci                                     struct connectdata *conn,
130013498266Sopenharmony_ci                                     bool disconnecting)
130113498266Sopenharmony_ci{
130213498266Sopenharmony_ci  CURLcode result = CURLE_OK;
130313498266Sopenharmony_ci  struct smtp_conn *smtpc = &conn->proto.smtpc;
130413498266Sopenharmony_ci
130513498266Sopenharmony_ci  while(smtpc->state != SMTP_STOP && !result)
130613498266Sopenharmony_ci    result = Curl_pp_statemach(data, &smtpc->pp, TRUE, disconnecting);
130713498266Sopenharmony_ci
130813498266Sopenharmony_ci  return result;
130913498266Sopenharmony_ci}
131013498266Sopenharmony_ci
131113498266Sopenharmony_ci/* Allocate and initialize the SMTP struct for the current Curl_easy if
131213498266Sopenharmony_ci   required */
131313498266Sopenharmony_cistatic CURLcode smtp_init(struct Curl_easy *data)
131413498266Sopenharmony_ci{
131513498266Sopenharmony_ci  CURLcode result = CURLE_OK;
131613498266Sopenharmony_ci  struct SMTP *smtp;
131713498266Sopenharmony_ci
131813498266Sopenharmony_ci  smtp = data->req.p.smtp = calloc(1, sizeof(struct SMTP));
131913498266Sopenharmony_ci  if(!smtp)
132013498266Sopenharmony_ci    result = CURLE_OUT_OF_MEMORY;
132113498266Sopenharmony_ci
132213498266Sopenharmony_ci  return result;
132313498266Sopenharmony_ci}
132413498266Sopenharmony_ci
132513498266Sopenharmony_ci/* For the SMTP "protocol connect" and "doing" phases only */
132613498266Sopenharmony_cistatic int smtp_getsock(struct Curl_easy *data,
132713498266Sopenharmony_ci                        struct connectdata *conn, curl_socket_t *socks)
132813498266Sopenharmony_ci{
132913498266Sopenharmony_ci  return Curl_pp_getsock(data, &conn->proto.smtpc.pp, socks);
133013498266Sopenharmony_ci}
133113498266Sopenharmony_ci
133213498266Sopenharmony_ci/***********************************************************************
133313498266Sopenharmony_ci *
133413498266Sopenharmony_ci * smtp_connect()
133513498266Sopenharmony_ci *
133613498266Sopenharmony_ci * This function should do everything that is to be considered a part of
133713498266Sopenharmony_ci * the connection phase.
133813498266Sopenharmony_ci *
133913498266Sopenharmony_ci * The variable pointed to by 'done' will be TRUE if the protocol-layer
134013498266Sopenharmony_ci * connect phase is done when this function returns, or FALSE if not.
134113498266Sopenharmony_ci */
134213498266Sopenharmony_cistatic CURLcode smtp_connect(struct Curl_easy *data, bool *done)
134313498266Sopenharmony_ci{
134413498266Sopenharmony_ci  CURLcode result = CURLE_OK;
134513498266Sopenharmony_ci  struct connectdata *conn = data->conn;
134613498266Sopenharmony_ci  struct smtp_conn *smtpc = &conn->proto.smtpc;
134713498266Sopenharmony_ci  struct pingpong *pp = &smtpc->pp;
134813498266Sopenharmony_ci
134913498266Sopenharmony_ci  *done = FALSE; /* default to not done yet */
135013498266Sopenharmony_ci
135113498266Sopenharmony_ci  /* We always support persistent connections in SMTP */
135213498266Sopenharmony_ci  connkeep(conn, "SMTP default");
135313498266Sopenharmony_ci
135413498266Sopenharmony_ci  PINGPONG_SETUP(pp, smtp_statemachine, smtp_endofresp);
135513498266Sopenharmony_ci
135613498266Sopenharmony_ci  /* Initialize the SASL storage */
135713498266Sopenharmony_ci  Curl_sasl_init(&smtpc->sasl, data, &saslsmtp);
135813498266Sopenharmony_ci
135913498266Sopenharmony_ci  /* Initialise the pingpong layer */
136013498266Sopenharmony_ci  Curl_pp_init(pp);
136113498266Sopenharmony_ci
136213498266Sopenharmony_ci  /* Parse the URL options */
136313498266Sopenharmony_ci  result = smtp_parse_url_options(conn);
136413498266Sopenharmony_ci  if(result)
136513498266Sopenharmony_ci    return result;
136613498266Sopenharmony_ci
136713498266Sopenharmony_ci  /* Parse the URL path */
136813498266Sopenharmony_ci  result = smtp_parse_url_path(data);
136913498266Sopenharmony_ci  if(result)
137013498266Sopenharmony_ci    return result;
137113498266Sopenharmony_ci
137213498266Sopenharmony_ci  /* Start off waiting for the server greeting response */
137313498266Sopenharmony_ci  smtp_state(data, SMTP_SERVERGREET);
137413498266Sopenharmony_ci
137513498266Sopenharmony_ci  result = smtp_multi_statemach(data, done);
137613498266Sopenharmony_ci
137713498266Sopenharmony_ci  return result;
137813498266Sopenharmony_ci}
137913498266Sopenharmony_ci
138013498266Sopenharmony_ci/***********************************************************************
138113498266Sopenharmony_ci *
138213498266Sopenharmony_ci * smtp_done()
138313498266Sopenharmony_ci *
138413498266Sopenharmony_ci * The DONE function. This does what needs to be done after a single DO has
138513498266Sopenharmony_ci * performed.
138613498266Sopenharmony_ci *
138713498266Sopenharmony_ci * Input argument is already checked for validity.
138813498266Sopenharmony_ci */
138913498266Sopenharmony_cistatic CURLcode smtp_done(struct Curl_easy *data, CURLcode status,
139013498266Sopenharmony_ci                          bool premature)
139113498266Sopenharmony_ci{
139213498266Sopenharmony_ci  CURLcode result = CURLE_OK;
139313498266Sopenharmony_ci  struct connectdata *conn = data->conn;
139413498266Sopenharmony_ci  struct SMTP *smtp = data->req.p.smtp;
139513498266Sopenharmony_ci  struct pingpong *pp = &conn->proto.smtpc.pp;
139613498266Sopenharmony_ci  char *eob;
139713498266Sopenharmony_ci  ssize_t len;
139813498266Sopenharmony_ci  ssize_t bytes_written;
139913498266Sopenharmony_ci
140013498266Sopenharmony_ci  (void)premature;
140113498266Sopenharmony_ci
140213498266Sopenharmony_ci  if(!smtp)
140313498266Sopenharmony_ci    return CURLE_OK;
140413498266Sopenharmony_ci
140513498266Sopenharmony_ci  /* Cleanup our per-request based variables */
140613498266Sopenharmony_ci  Curl_safefree(smtp->custom);
140713498266Sopenharmony_ci
140813498266Sopenharmony_ci  if(status) {
140913498266Sopenharmony_ci    connclose(conn, "SMTP done with bad status"); /* marked for closure */
141013498266Sopenharmony_ci    result = status;         /* use the already set error code */
141113498266Sopenharmony_ci  }
141213498266Sopenharmony_ci  else if(!data->set.connect_only && data->set.mail_rcpt &&
141313498266Sopenharmony_ci          (data->state.upload || data->set.mimepost.kind)) {
141413498266Sopenharmony_ci    /* Calculate the EOB taking into account any terminating CRLF from the
141513498266Sopenharmony_ci       previous line of the email or the CRLF of the DATA command when there
141613498266Sopenharmony_ci       is "no mail data". RFC-5321, sect. 4.1.1.4.
141713498266Sopenharmony_ci
141813498266Sopenharmony_ci       Note: As some SSL backends, such as OpenSSL, will cause Curl_write() to
141913498266Sopenharmony_ci       fail when using a different pointer following a previous write, that
142013498266Sopenharmony_ci       returned CURLE_AGAIN, we duplicate the EOB now rather than when the
142113498266Sopenharmony_ci       bytes written doesn't equal len. */
142213498266Sopenharmony_ci    if(smtp->trailing_crlf || !data->state.infilesize) {
142313498266Sopenharmony_ci      eob = strdup(&SMTP_EOB[2]);
142413498266Sopenharmony_ci      len = SMTP_EOB_LEN - 2;
142513498266Sopenharmony_ci    }
142613498266Sopenharmony_ci    else {
142713498266Sopenharmony_ci      eob = strdup(SMTP_EOB);
142813498266Sopenharmony_ci      len = SMTP_EOB_LEN;
142913498266Sopenharmony_ci    }
143013498266Sopenharmony_ci
143113498266Sopenharmony_ci    if(!eob)
143213498266Sopenharmony_ci      return CURLE_OUT_OF_MEMORY;
143313498266Sopenharmony_ci
143413498266Sopenharmony_ci    /* Send the end of block data */
143513498266Sopenharmony_ci    result = Curl_write(data, conn->writesockfd, eob, len, &bytes_written);
143613498266Sopenharmony_ci    if(result) {
143713498266Sopenharmony_ci      free(eob);
143813498266Sopenharmony_ci      return result;
143913498266Sopenharmony_ci    }
144013498266Sopenharmony_ci
144113498266Sopenharmony_ci    if(bytes_written != len) {
144213498266Sopenharmony_ci      /* The whole chunk was not sent so keep it around and adjust the
144313498266Sopenharmony_ci         pingpong structure accordingly */
144413498266Sopenharmony_ci      pp->sendthis = eob;
144513498266Sopenharmony_ci      pp->sendsize = len;
144613498266Sopenharmony_ci      pp->sendleft = len - bytes_written;
144713498266Sopenharmony_ci    }
144813498266Sopenharmony_ci    else {
144913498266Sopenharmony_ci      /* Successfully sent so adjust the response timeout relative to now */
145013498266Sopenharmony_ci      pp->response = Curl_now();
145113498266Sopenharmony_ci
145213498266Sopenharmony_ci      free(eob);
145313498266Sopenharmony_ci    }
145413498266Sopenharmony_ci
145513498266Sopenharmony_ci    smtp_state(data, SMTP_POSTDATA);
145613498266Sopenharmony_ci
145713498266Sopenharmony_ci    /* Run the state-machine */
145813498266Sopenharmony_ci    result = smtp_block_statemach(data, conn, FALSE);
145913498266Sopenharmony_ci  }
146013498266Sopenharmony_ci
146113498266Sopenharmony_ci  /* Clear the transfer mode for the next request */
146213498266Sopenharmony_ci  smtp->transfer = PPTRANSFER_BODY;
146313498266Sopenharmony_ci
146413498266Sopenharmony_ci  return result;
146513498266Sopenharmony_ci}
146613498266Sopenharmony_ci
146713498266Sopenharmony_ci/***********************************************************************
146813498266Sopenharmony_ci *
146913498266Sopenharmony_ci * smtp_perform()
147013498266Sopenharmony_ci *
147113498266Sopenharmony_ci * This is the actual DO function for SMTP. Transfer a mail, send a command
147213498266Sopenharmony_ci * or get some data according to the options previously setup.
147313498266Sopenharmony_ci */
147413498266Sopenharmony_cistatic CURLcode smtp_perform(struct Curl_easy *data, bool *connected,
147513498266Sopenharmony_ci                             bool *dophase_done)
147613498266Sopenharmony_ci{
147713498266Sopenharmony_ci  /* This is SMTP and no proxy */
147813498266Sopenharmony_ci  CURLcode result = CURLE_OK;
147913498266Sopenharmony_ci  struct SMTP *smtp = data->req.p.smtp;
148013498266Sopenharmony_ci
148113498266Sopenharmony_ci  DEBUGF(infof(data, "DO phase starts"));
148213498266Sopenharmony_ci
148313498266Sopenharmony_ci  if(data->req.no_body) {
148413498266Sopenharmony_ci    /* Requested no body means no transfer */
148513498266Sopenharmony_ci    smtp->transfer = PPTRANSFER_INFO;
148613498266Sopenharmony_ci  }
148713498266Sopenharmony_ci
148813498266Sopenharmony_ci  *dophase_done = FALSE; /* not done yet */
148913498266Sopenharmony_ci
149013498266Sopenharmony_ci  /* Store the first recipient (or NULL if not specified) */
149113498266Sopenharmony_ci  smtp->rcpt = data->set.mail_rcpt;
149213498266Sopenharmony_ci
149313498266Sopenharmony_ci  /* Track of whether we've successfully sent at least one RCPT TO command */
149413498266Sopenharmony_ci  smtp->rcpt_had_ok = FALSE;
149513498266Sopenharmony_ci
149613498266Sopenharmony_ci  /* Track of the last error we've received by sending RCPT TO command */
149713498266Sopenharmony_ci  smtp->rcpt_last_error = 0;
149813498266Sopenharmony_ci
149913498266Sopenharmony_ci  /* Initial data character is the first character in line: it is implicitly
150013498266Sopenharmony_ci     preceded by a virtual CRLF. */
150113498266Sopenharmony_ci  smtp->trailing_crlf = TRUE;
150213498266Sopenharmony_ci  smtp->eob = 2;
150313498266Sopenharmony_ci
150413498266Sopenharmony_ci  /* Start the first command in the DO phase */
150513498266Sopenharmony_ci  if((data->state.upload || data->set.mimepost.kind) && data->set.mail_rcpt)
150613498266Sopenharmony_ci    /* MAIL transfer */
150713498266Sopenharmony_ci    result = smtp_perform_mail(data);
150813498266Sopenharmony_ci  else
150913498266Sopenharmony_ci    /* SMTP based command (VRFY, EXPN, NOOP, RSET or HELP) */
151013498266Sopenharmony_ci    result = smtp_perform_command(data);
151113498266Sopenharmony_ci
151213498266Sopenharmony_ci  if(result)
151313498266Sopenharmony_ci    return result;
151413498266Sopenharmony_ci
151513498266Sopenharmony_ci  /* Run the state-machine */
151613498266Sopenharmony_ci  result = smtp_multi_statemach(data, dophase_done);
151713498266Sopenharmony_ci
151813498266Sopenharmony_ci  *connected = Curl_conn_is_connected(data->conn, FIRSTSOCKET);
151913498266Sopenharmony_ci
152013498266Sopenharmony_ci  if(*dophase_done)
152113498266Sopenharmony_ci    DEBUGF(infof(data, "DO phase is complete"));
152213498266Sopenharmony_ci
152313498266Sopenharmony_ci  return result;
152413498266Sopenharmony_ci}
152513498266Sopenharmony_ci
152613498266Sopenharmony_ci/***********************************************************************
152713498266Sopenharmony_ci *
152813498266Sopenharmony_ci * smtp_do()
152913498266Sopenharmony_ci *
153013498266Sopenharmony_ci * This function is registered as 'curl_do' function. It decodes the path
153113498266Sopenharmony_ci * parts etc as a wrapper to the actual DO function (smtp_perform).
153213498266Sopenharmony_ci *
153313498266Sopenharmony_ci * The input argument is already checked for validity.
153413498266Sopenharmony_ci */
153513498266Sopenharmony_cistatic CURLcode smtp_do(struct Curl_easy *data, bool *done)
153613498266Sopenharmony_ci{
153713498266Sopenharmony_ci  CURLcode result = CURLE_OK;
153813498266Sopenharmony_ci  DEBUGASSERT(data);
153913498266Sopenharmony_ci  DEBUGASSERT(data->conn);
154013498266Sopenharmony_ci  *done = FALSE; /* default to false */
154113498266Sopenharmony_ci
154213498266Sopenharmony_ci  /* Parse the custom request */
154313498266Sopenharmony_ci  result = smtp_parse_custom_request(data);
154413498266Sopenharmony_ci  if(result)
154513498266Sopenharmony_ci    return result;
154613498266Sopenharmony_ci
154713498266Sopenharmony_ci  result = smtp_regular_transfer(data, done);
154813498266Sopenharmony_ci
154913498266Sopenharmony_ci  return result;
155013498266Sopenharmony_ci}
155113498266Sopenharmony_ci
155213498266Sopenharmony_ci/***********************************************************************
155313498266Sopenharmony_ci *
155413498266Sopenharmony_ci * smtp_disconnect()
155513498266Sopenharmony_ci *
155613498266Sopenharmony_ci * Disconnect from an SMTP server. Cleanup protocol-specific per-connection
155713498266Sopenharmony_ci * resources. BLOCKING.
155813498266Sopenharmony_ci */
155913498266Sopenharmony_cistatic CURLcode smtp_disconnect(struct Curl_easy *data,
156013498266Sopenharmony_ci                                struct connectdata *conn,
156113498266Sopenharmony_ci                                bool dead_connection)
156213498266Sopenharmony_ci{
156313498266Sopenharmony_ci  struct smtp_conn *smtpc = &conn->proto.smtpc;
156413498266Sopenharmony_ci  (void)data;
156513498266Sopenharmony_ci
156613498266Sopenharmony_ci  /* We cannot send quit unconditionally. If this connection is stale or
156713498266Sopenharmony_ci     bad in any way, sending quit and waiting around here will make the
156813498266Sopenharmony_ci     disconnect wait in vain and cause more problems than we need to. */
156913498266Sopenharmony_ci
157013498266Sopenharmony_ci  if(!dead_connection && conn->bits.protoconnstart) {
157113498266Sopenharmony_ci    if(!smtp_perform_quit(data, conn))
157213498266Sopenharmony_ci      (void)smtp_block_statemach(data, conn, TRUE); /* ignore errors on QUIT */
157313498266Sopenharmony_ci  }
157413498266Sopenharmony_ci
157513498266Sopenharmony_ci  /* Disconnect from the server */
157613498266Sopenharmony_ci  Curl_pp_disconnect(&smtpc->pp);
157713498266Sopenharmony_ci
157813498266Sopenharmony_ci  /* Cleanup the SASL module */
157913498266Sopenharmony_ci  Curl_sasl_cleanup(conn, smtpc->sasl.authused);
158013498266Sopenharmony_ci
158113498266Sopenharmony_ci  /* Cleanup our connection based variables */
158213498266Sopenharmony_ci  Curl_safefree(smtpc->domain);
158313498266Sopenharmony_ci
158413498266Sopenharmony_ci  return CURLE_OK;
158513498266Sopenharmony_ci}
158613498266Sopenharmony_ci
158713498266Sopenharmony_ci/* Call this when the DO phase has completed */
158813498266Sopenharmony_cistatic CURLcode smtp_dophase_done(struct Curl_easy *data, bool connected)
158913498266Sopenharmony_ci{
159013498266Sopenharmony_ci  struct SMTP *smtp = data->req.p.smtp;
159113498266Sopenharmony_ci
159213498266Sopenharmony_ci  (void)connected;
159313498266Sopenharmony_ci
159413498266Sopenharmony_ci  if(smtp->transfer != PPTRANSFER_BODY)
159513498266Sopenharmony_ci    /* no data to transfer */
159613498266Sopenharmony_ci    Curl_setup_transfer(data, -1, -1, FALSE, -1);
159713498266Sopenharmony_ci
159813498266Sopenharmony_ci  return CURLE_OK;
159913498266Sopenharmony_ci}
160013498266Sopenharmony_ci
160113498266Sopenharmony_ci/* Called from multi.c while DOing */
160213498266Sopenharmony_cistatic CURLcode smtp_doing(struct Curl_easy *data, bool *dophase_done)
160313498266Sopenharmony_ci{
160413498266Sopenharmony_ci  CURLcode result = smtp_multi_statemach(data, dophase_done);
160513498266Sopenharmony_ci
160613498266Sopenharmony_ci  if(result)
160713498266Sopenharmony_ci    DEBUGF(infof(data, "DO phase failed"));
160813498266Sopenharmony_ci  else if(*dophase_done) {
160913498266Sopenharmony_ci    result = smtp_dophase_done(data, FALSE /* not connected */);
161013498266Sopenharmony_ci
161113498266Sopenharmony_ci    DEBUGF(infof(data, "DO phase is complete"));
161213498266Sopenharmony_ci  }
161313498266Sopenharmony_ci
161413498266Sopenharmony_ci  return result;
161513498266Sopenharmony_ci}
161613498266Sopenharmony_ci
161713498266Sopenharmony_ci/***********************************************************************
161813498266Sopenharmony_ci *
161913498266Sopenharmony_ci * smtp_regular_transfer()
162013498266Sopenharmony_ci *
162113498266Sopenharmony_ci * The input argument is already checked for validity.
162213498266Sopenharmony_ci *
162313498266Sopenharmony_ci * Performs all commands done before a regular transfer between a local and a
162413498266Sopenharmony_ci * remote host.
162513498266Sopenharmony_ci */
162613498266Sopenharmony_cistatic CURLcode smtp_regular_transfer(struct Curl_easy *data,
162713498266Sopenharmony_ci                                      bool *dophase_done)
162813498266Sopenharmony_ci{
162913498266Sopenharmony_ci  CURLcode result = CURLE_OK;
163013498266Sopenharmony_ci  bool connected = FALSE;
163113498266Sopenharmony_ci
163213498266Sopenharmony_ci  /* Make sure size is unknown at this point */
163313498266Sopenharmony_ci  data->req.size = -1;
163413498266Sopenharmony_ci
163513498266Sopenharmony_ci  /* Set the progress data */
163613498266Sopenharmony_ci  Curl_pgrsSetUploadCounter(data, 0);
163713498266Sopenharmony_ci  Curl_pgrsSetDownloadCounter(data, 0);
163813498266Sopenharmony_ci  Curl_pgrsSetUploadSize(data, -1);
163913498266Sopenharmony_ci  Curl_pgrsSetDownloadSize(data, -1);
164013498266Sopenharmony_ci
164113498266Sopenharmony_ci  /* Carry out the perform */
164213498266Sopenharmony_ci  result = smtp_perform(data, &connected, dophase_done);
164313498266Sopenharmony_ci
164413498266Sopenharmony_ci  /* Perform post DO phase operations if necessary */
164513498266Sopenharmony_ci  if(!result && *dophase_done)
164613498266Sopenharmony_ci    result = smtp_dophase_done(data, connected);
164713498266Sopenharmony_ci
164813498266Sopenharmony_ci  return result;
164913498266Sopenharmony_ci}
165013498266Sopenharmony_ci
165113498266Sopenharmony_cistatic CURLcode smtp_setup_connection(struct Curl_easy *data,
165213498266Sopenharmony_ci                                      struct connectdata *conn)
165313498266Sopenharmony_ci{
165413498266Sopenharmony_ci  CURLcode result;
165513498266Sopenharmony_ci
165613498266Sopenharmony_ci  /* Clear the TLS upgraded flag */
165713498266Sopenharmony_ci  conn->bits.tls_upgraded = FALSE;
165813498266Sopenharmony_ci
165913498266Sopenharmony_ci  /* Initialise the SMTP layer */
166013498266Sopenharmony_ci  result = smtp_init(data);
166113498266Sopenharmony_ci  if(result)
166213498266Sopenharmony_ci    return result;
166313498266Sopenharmony_ci
166413498266Sopenharmony_ci  return CURLE_OK;
166513498266Sopenharmony_ci}
166613498266Sopenharmony_ci
166713498266Sopenharmony_ci/***********************************************************************
166813498266Sopenharmony_ci *
166913498266Sopenharmony_ci * smtp_parse_url_options()
167013498266Sopenharmony_ci *
167113498266Sopenharmony_ci * Parse the URL login options.
167213498266Sopenharmony_ci */
167313498266Sopenharmony_cistatic CURLcode smtp_parse_url_options(struct connectdata *conn)
167413498266Sopenharmony_ci{
167513498266Sopenharmony_ci  CURLcode result = CURLE_OK;
167613498266Sopenharmony_ci  struct smtp_conn *smtpc = &conn->proto.smtpc;
167713498266Sopenharmony_ci  const char *ptr = conn->options;
167813498266Sopenharmony_ci
167913498266Sopenharmony_ci  while(!result && ptr && *ptr) {
168013498266Sopenharmony_ci    const char *key = ptr;
168113498266Sopenharmony_ci    const char *value;
168213498266Sopenharmony_ci
168313498266Sopenharmony_ci    while(*ptr && *ptr != '=')
168413498266Sopenharmony_ci      ptr++;
168513498266Sopenharmony_ci
168613498266Sopenharmony_ci    value = ptr + 1;
168713498266Sopenharmony_ci
168813498266Sopenharmony_ci    while(*ptr && *ptr != ';')
168913498266Sopenharmony_ci      ptr++;
169013498266Sopenharmony_ci
169113498266Sopenharmony_ci    if(strncasecompare(key, "AUTH=", 5))
169213498266Sopenharmony_ci      result = Curl_sasl_parse_url_auth_option(&smtpc->sasl,
169313498266Sopenharmony_ci                                               value, ptr - value);
169413498266Sopenharmony_ci    else
169513498266Sopenharmony_ci      result = CURLE_URL_MALFORMAT;
169613498266Sopenharmony_ci
169713498266Sopenharmony_ci    if(*ptr == ';')
169813498266Sopenharmony_ci      ptr++;
169913498266Sopenharmony_ci  }
170013498266Sopenharmony_ci
170113498266Sopenharmony_ci  return result;
170213498266Sopenharmony_ci}
170313498266Sopenharmony_ci
170413498266Sopenharmony_ci/***********************************************************************
170513498266Sopenharmony_ci *
170613498266Sopenharmony_ci * smtp_parse_url_path()
170713498266Sopenharmony_ci *
170813498266Sopenharmony_ci * Parse the URL path into separate path components.
170913498266Sopenharmony_ci */
171013498266Sopenharmony_cistatic CURLcode smtp_parse_url_path(struct Curl_easy *data)
171113498266Sopenharmony_ci{
171213498266Sopenharmony_ci  /* The SMTP struct is already initialised in smtp_connect() */
171313498266Sopenharmony_ci  struct connectdata *conn = data->conn;
171413498266Sopenharmony_ci  struct smtp_conn *smtpc = &conn->proto.smtpc;
171513498266Sopenharmony_ci  const char *path = &data->state.up.path[1]; /* skip leading path */
171613498266Sopenharmony_ci  char localhost[HOSTNAME_MAX + 1];
171713498266Sopenharmony_ci
171813498266Sopenharmony_ci  /* Calculate the path if necessary */
171913498266Sopenharmony_ci  if(!*path) {
172013498266Sopenharmony_ci    if(!Curl_gethostname(localhost, sizeof(localhost)))
172113498266Sopenharmony_ci      path = localhost;
172213498266Sopenharmony_ci    else
172313498266Sopenharmony_ci      path = "localhost";
172413498266Sopenharmony_ci  }
172513498266Sopenharmony_ci
172613498266Sopenharmony_ci  /* URL decode the path and use it as the domain in our EHLO */
172713498266Sopenharmony_ci  return Curl_urldecode(path, 0, &smtpc->domain, NULL, REJECT_CTRL);
172813498266Sopenharmony_ci}
172913498266Sopenharmony_ci
173013498266Sopenharmony_ci/***********************************************************************
173113498266Sopenharmony_ci *
173213498266Sopenharmony_ci * smtp_parse_custom_request()
173313498266Sopenharmony_ci *
173413498266Sopenharmony_ci * Parse the custom request.
173513498266Sopenharmony_ci */
173613498266Sopenharmony_cistatic CURLcode smtp_parse_custom_request(struct Curl_easy *data)
173713498266Sopenharmony_ci{
173813498266Sopenharmony_ci  CURLcode result = CURLE_OK;
173913498266Sopenharmony_ci  struct SMTP *smtp = data->req.p.smtp;
174013498266Sopenharmony_ci  const char *custom = data->set.str[STRING_CUSTOMREQUEST];
174113498266Sopenharmony_ci
174213498266Sopenharmony_ci  /* URL decode the custom request */
174313498266Sopenharmony_ci  if(custom)
174413498266Sopenharmony_ci    result = Curl_urldecode(custom, 0, &smtp->custom, NULL, REJECT_CTRL);
174513498266Sopenharmony_ci
174613498266Sopenharmony_ci  return result;
174713498266Sopenharmony_ci}
174813498266Sopenharmony_ci
174913498266Sopenharmony_ci/***********************************************************************
175013498266Sopenharmony_ci *
175113498266Sopenharmony_ci * smtp_parse_address()
175213498266Sopenharmony_ci *
175313498266Sopenharmony_ci * Parse the fully qualified mailbox address into a local address part and the
175413498266Sopenharmony_ci * host name, converting the host name to an IDN A-label, as per RFC-5890, if
175513498266Sopenharmony_ci * necessary.
175613498266Sopenharmony_ci *
175713498266Sopenharmony_ci * Parameters:
175813498266Sopenharmony_ci *
175913498266Sopenharmony_ci * conn  [in]              - The connection handle.
176013498266Sopenharmony_ci * fqma  [in]              - The fully qualified mailbox address (which may or
176113498266Sopenharmony_ci *                           may not contain UTF-8 characters).
176213498266Sopenharmony_ci * address        [in/out] - A new allocated buffer which holds the local
176313498266Sopenharmony_ci *                           address part of the mailbox. This buffer must be
176413498266Sopenharmony_ci *                           free'ed by the caller.
176513498266Sopenharmony_ci * host           [in/out] - The host name structure that holds the original,
176613498266Sopenharmony_ci *                           and optionally encoded, host name.
176713498266Sopenharmony_ci *                           Curl_free_idnconverted_hostname() must be called
176813498266Sopenharmony_ci *                           once the caller has finished with the structure.
176913498266Sopenharmony_ci *
177013498266Sopenharmony_ci * Returns CURLE_OK on success.
177113498266Sopenharmony_ci *
177213498266Sopenharmony_ci * Notes:
177313498266Sopenharmony_ci *
177413498266Sopenharmony_ci * Should a UTF-8 host name require conversion to IDN ACE and we cannot honor
177513498266Sopenharmony_ci * that conversion then we shall return success. This allow the caller to send
177613498266Sopenharmony_ci * the data to the server as a U-label (as per RFC-6531 sect. 3.2).
177713498266Sopenharmony_ci *
177813498266Sopenharmony_ci * If an mailbox '@' separator cannot be located then the mailbox is considered
177913498266Sopenharmony_ci * to be either a local mailbox or an invalid mailbox (depending on what the
178013498266Sopenharmony_ci * calling function deems it to be) then the input will simply be returned in
178113498266Sopenharmony_ci * the address part with the host name being NULL.
178213498266Sopenharmony_ci */
178313498266Sopenharmony_cistatic CURLcode smtp_parse_address(const char *fqma, char **address,
178413498266Sopenharmony_ci                                   struct hostname *host)
178513498266Sopenharmony_ci{
178613498266Sopenharmony_ci  CURLcode result = CURLE_OK;
178713498266Sopenharmony_ci  size_t length;
178813498266Sopenharmony_ci
178913498266Sopenharmony_ci  /* Duplicate the fully qualified email address so we can manipulate it,
179013498266Sopenharmony_ci     ensuring it doesn't contain the delimiters if specified */
179113498266Sopenharmony_ci  char *dup = strdup(fqma[0] == '<' ? fqma + 1  : fqma);
179213498266Sopenharmony_ci  if(!dup)
179313498266Sopenharmony_ci    return CURLE_OUT_OF_MEMORY;
179413498266Sopenharmony_ci
179513498266Sopenharmony_ci  length = strlen(dup);
179613498266Sopenharmony_ci  if(length) {
179713498266Sopenharmony_ci    if(dup[length - 1] == '>')
179813498266Sopenharmony_ci      dup[length - 1] = '\0';
179913498266Sopenharmony_ci  }
180013498266Sopenharmony_ci
180113498266Sopenharmony_ci  /* Extract the host name from the address (if we can) */
180213498266Sopenharmony_ci  host->name = strpbrk(dup, "@");
180313498266Sopenharmony_ci  if(host->name) {
180413498266Sopenharmony_ci    *host->name = '\0';
180513498266Sopenharmony_ci    host->name = host->name + 1;
180613498266Sopenharmony_ci
180713498266Sopenharmony_ci    /* Attempt to convert the host name to IDN ACE */
180813498266Sopenharmony_ci    (void) Curl_idnconvert_hostname(host);
180913498266Sopenharmony_ci
181013498266Sopenharmony_ci    /* If Curl_idnconvert_hostname() fails then we shall attempt to continue
181113498266Sopenharmony_ci       and send the host name using UTF-8 rather than as 7-bit ACE (which is
181213498266Sopenharmony_ci       our preference) */
181313498266Sopenharmony_ci  }
181413498266Sopenharmony_ci
181513498266Sopenharmony_ci  /* Extract the local address from the mailbox */
181613498266Sopenharmony_ci  *address = dup;
181713498266Sopenharmony_ci
181813498266Sopenharmony_ci  return result;
181913498266Sopenharmony_ci}
182013498266Sopenharmony_ci
182113498266Sopenharmony_ciCURLcode Curl_smtp_escape_eob(struct Curl_easy *data,
182213498266Sopenharmony_ci                              const ssize_t nread,
182313498266Sopenharmony_ci                              const ssize_t offset)
182413498266Sopenharmony_ci{
182513498266Sopenharmony_ci  /* When sending a SMTP payload we must detect CRLF. sequences making sure
182613498266Sopenharmony_ci     they are sent as CRLF.. instead, as a . on the beginning of a line will
182713498266Sopenharmony_ci     be deleted by the server when not part of an EOB terminator and a
182813498266Sopenharmony_ci     genuine CRLF.CRLF which isn't escaped will wrongly be detected as end of
182913498266Sopenharmony_ci     data by the server
183013498266Sopenharmony_ci  */
183113498266Sopenharmony_ci  ssize_t i;
183213498266Sopenharmony_ci  ssize_t si;
183313498266Sopenharmony_ci  struct SMTP *smtp = data->req.p.smtp;
183413498266Sopenharmony_ci  char *scratch = data->state.scratch;
183513498266Sopenharmony_ci  char *newscratch = NULL;
183613498266Sopenharmony_ci  char *oldscratch = NULL;
183713498266Sopenharmony_ci  size_t eob_sent;
183813498266Sopenharmony_ci
183913498266Sopenharmony_ci  /* Do we need to allocate a scratch buffer? */
184013498266Sopenharmony_ci  if(!scratch || data->set.crlf) {
184113498266Sopenharmony_ci    oldscratch = scratch;
184213498266Sopenharmony_ci
184313498266Sopenharmony_ci    scratch = newscratch = malloc(2 * data->set.upload_buffer_size);
184413498266Sopenharmony_ci    if(!newscratch) {
184513498266Sopenharmony_ci      failf(data, "Failed to alloc scratch buffer");
184613498266Sopenharmony_ci
184713498266Sopenharmony_ci      return CURLE_OUT_OF_MEMORY;
184813498266Sopenharmony_ci    }
184913498266Sopenharmony_ci  }
185013498266Sopenharmony_ci  DEBUGASSERT((size_t)data->set.upload_buffer_size >= (size_t)nread);
185113498266Sopenharmony_ci
185213498266Sopenharmony_ci  /* Have we already sent part of the EOB? */
185313498266Sopenharmony_ci  eob_sent = smtp->eob;
185413498266Sopenharmony_ci
185513498266Sopenharmony_ci  /* This loop can be improved by some kind of Boyer-Moore style of
185613498266Sopenharmony_ci     approach but that is saved for later... */
185713498266Sopenharmony_ci  if(offset)
185813498266Sopenharmony_ci    memcpy(scratch, data->req.upload_fromhere, offset);
185913498266Sopenharmony_ci  for(i = offset, si = offset; i < nread; i++) {
186013498266Sopenharmony_ci    if(SMTP_EOB[smtp->eob] == data->req.upload_fromhere[i]) {
186113498266Sopenharmony_ci      smtp->eob++;
186213498266Sopenharmony_ci
186313498266Sopenharmony_ci      /* Is the EOB potentially the terminating CRLF? */
186413498266Sopenharmony_ci      if(2 == smtp->eob || SMTP_EOB_LEN == smtp->eob)
186513498266Sopenharmony_ci        smtp->trailing_crlf = TRUE;
186613498266Sopenharmony_ci      else
186713498266Sopenharmony_ci        smtp->trailing_crlf = FALSE;
186813498266Sopenharmony_ci    }
186913498266Sopenharmony_ci    else if(smtp->eob) {
187013498266Sopenharmony_ci      /* A previous substring matched so output that first */
187113498266Sopenharmony_ci      memcpy(&scratch[si], &SMTP_EOB[eob_sent], smtp->eob - eob_sent);
187213498266Sopenharmony_ci      si += smtp->eob - eob_sent;
187313498266Sopenharmony_ci
187413498266Sopenharmony_ci      /* Then compare the first byte */
187513498266Sopenharmony_ci      if(SMTP_EOB[0] == data->req.upload_fromhere[i])
187613498266Sopenharmony_ci        smtp->eob = 1;
187713498266Sopenharmony_ci      else
187813498266Sopenharmony_ci        smtp->eob = 0;
187913498266Sopenharmony_ci
188013498266Sopenharmony_ci      eob_sent = 0;
188113498266Sopenharmony_ci
188213498266Sopenharmony_ci      /* Reset the trailing CRLF flag as there was more data */
188313498266Sopenharmony_ci      smtp->trailing_crlf = FALSE;
188413498266Sopenharmony_ci    }
188513498266Sopenharmony_ci
188613498266Sopenharmony_ci    /* Do we have a match for CRLF. as per RFC-5321, sect. 4.5.2 */
188713498266Sopenharmony_ci    if(SMTP_EOB_FIND_LEN == smtp->eob) {
188813498266Sopenharmony_ci      /* Copy the replacement data to the target buffer */
188913498266Sopenharmony_ci      memcpy(&scratch[si], &SMTP_EOB_REPL[eob_sent],
189013498266Sopenharmony_ci             SMTP_EOB_REPL_LEN - eob_sent);
189113498266Sopenharmony_ci      si += SMTP_EOB_REPL_LEN - eob_sent;
189213498266Sopenharmony_ci      smtp->eob = 0;
189313498266Sopenharmony_ci      eob_sent = 0;
189413498266Sopenharmony_ci    }
189513498266Sopenharmony_ci    else if(!smtp->eob)
189613498266Sopenharmony_ci      scratch[si++] = data->req.upload_fromhere[i];
189713498266Sopenharmony_ci  }
189813498266Sopenharmony_ci
189913498266Sopenharmony_ci  if(smtp->eob - eob_sent) {
190013498266Sopenharmony_ci    /* A substring matched before processing ended so output that now */
190113498266Sopenharmony_ci    memcpy(&scratch[si], &SMTP_EOB[eob_sent], smtp->eob - eob_sent);
190213498266Sopenharmony_ci    si += smtp->eob - eob_sent;
190313498266Sopenharmony_ci  }
190413498266Sopenharmony_ci
190513498266Sopenharmony_ci  /* Only use the new buffer if we replaced something */
190613498266Sopenharmony_ci  if(si != nread) {
190713498266Sopenharmony_ci    /* Upload from the new (replaced) buffer instead */
190813498266Sopenharmony_ci    data->req.upload_fromhere = scratch;
190913498266Sopenharmony_ci
191013498266Sopenharmony_ci    /* Save the buffer so it can be freed later */
191113498266Sopenharmony_ci    data->state.scratch = scratch;
191213498266Sopenharmony_ci
191313498266Sopenharmony_ci    /* Free the old scratch buffer */
191413498266Sopenharmony_ci    free(oldscratch);
191513498266Sopenharmony_ci
191613498266Sopenharmony_ci    /* Set the new amount too */
191713498266Sopenharmony_ci    data->req.upload_present = si;
191813498266Sopenharmony_ci  }
191913498266Sopenharmony_ci  else
192013498266Sopenharmony_ci    free(newscratch);
192113498266Sopenharmony_ci
192213498266Sopenharmony_ci  return CURLE_OK;
192313498266Sopenharmony_ci}
192413498266Sopenharmony_ci
192513498266Sopenharmony_ci#endif /* CURL_DISABLE_SMTP */
1926