Coverage Report

Created: 2026-10-15 06:19

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/curl/lib/smtp.c
Line
Count
Source
1
/***************************************************************************
2
 *                                  _   _ ____  _
3
 *  Project                     ___| | | |  _ \| |
4
 *                             / __| | | | |_) | |
5
 *                            | (__| |_| |  _ <| |___
6
 *                             \___|\___/|_| \_\_____|
7
 *
8
 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9
 *
10
 * This software is licensed as described in the file COPYING, which
11
 * you should have received as part of this distribution. The terms
12
 * are also available at https://curl.se/docs/copyright.html.
13
 *
14
 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15
 * copies of the Software, and permit persons to whom the Software is
16
 * furnished to do so, under the terms of the COPYING file.
17
 *
18
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19
 * KIND, either express or implied.
20
 *
21
 * SPDX-License-Identifier: curl
22
 *
23
 * RFC1870 SMTP Service Extension for Message Size
24
 * RFC2195 CRAM-MD5 authentication
25
 * RFC2831 DIGEST-MD5 authentication
26
 * RFC3207 SMTP over TLS
27
 * RFC4422 Simple Authentication and Security Layer (SASL)
28
 * RFC4616 PLAIN authentication
29
 * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism
30
 * RFC4954 SMTP Authentication
31
 * RFC5321 SMTP protocol
32
 * RFC5890 Internationalized Domain Names for Applications (IDNA)
33
 * RFC6531 SMTP Extension for Internationalized Email
34
 * RFC6532 Internationalized Email Headers
35
 * RFC6749 OAuth 2.0 Authorization Framework
36
 * RFC8314 Use of TLS for Email Submission and Access
37
 * Draft   SMTP URL Interface   <draft-earhart-url-smtp-00.txt>
38
 * Draft   LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt>
39
 *
40
 ***************************************************************************/
41
#include "curl_setup.h"
42
#include "urldata.h"
43
#include "smtp.h"
44
45
#ifndef CURL_DISABLE_SMTP
46
47
#ifdef HAVE_NETINET_IN_H
48
#include <netinet/in.h>
49
#endif
50
#ifdef HAVE_ARPA_INET_H
51
#include <arpa/inet.h>
52
#endif
53
#ifdef HAVE_NETDB_H
54
#include <netdb.h>
55
#endif
56
#ifdef __VMS
57
#include <in.h>
58
#include <inet.h>
59
#endif
60
61
#include "sendf.h"
62
#include "curl_trc.h"
63
#include "progress.h"
64
#include "transfer.h"
65
#include "escape.h"
66
#include "pingpong.h"
67
#include "mime.h"
68
#include "vtls/vtls.h"
69
#include "cfilters.h"
70
#include "connect.h"
71
#include "select.h"
72
#include "url.h"
73
#include "curl_gethostname.h"
74
#include "bufref.h"
75
#include "curl_sasl.h"
76
#include "idn.h"
77
#include "curlx/strparse.h"
78
79
/* meta key for storing protocol meta at easy handle */
80
1.44M
#define CURL_META_SMTP_EASY   "meta:proto:smtp:easy"
81
/* meta key for storing protocol meta at connection */
82
1.51M
#define CURL_META_SMTP_CONN   "meta:proto:smtp:conn"
83
84
/****************************************************************************
85
 * SMTP unique setup
86
 ***************************************************************************/
87
typedef enum {
88
  SMTP_STOP,        /* do nothing state, stops the state machine */
89
  SMTP_SERVERGREET, /* waiting for the initial greeting immediately after
90
                       a connect */
91
  SMTP_EHLO,
92
  SMTP_HELO,
93
  SMTP_STARTTLS,
94
  SMTP_UPGRADETLS,  /* asynchronously upgrade the connection to SSL/TLS
95
                       (multi mode only) */
96
  SMTP_AUTH,
97
  SMTP_COMMAND,     /* VRFY, EXPN, NOOP, RSET and HELP */
98
  SMTP_MAIL,        /* MAIL FROM */
99
  SMTP_RCPT,        /* RCPT TO */
100
  SMTP_DATA,
101
  SMTP_POSTDATA,
102
  SMTP_QUIT,
103
  SMTP_LAST         /* never used */
104
} smtpstate;
105
106
/* smtp_conn is used for struct connection-oriented data in the connectdata
107
   struct */
108
struct smtp_conn {
109
  struct pingpong pp;
110
  struct SASL sasl;        /* SASL-related storage */
111
  smtpstate state;         /* Always use smtp.c:state() to change state! */
112
  char *domain;            /* Client address/name to send in the EHLO */
113
  BIT(ssldone);            /* Is connect() over SSL done? */
114
  BIT(tls_supported);      /* StartTLS capability supported by server */
115
  BIT(size_supported);     /* If server supports SIZE extension according to
116
                              RFC 1870 */
117
  BIT(utf8_supported);     /* If server supports SMTPUTF8 extension according
118
                              to RFC 6531 */
119
  BIT(auth_supported);     /* AUTH capability supported by server */
120
};
121
122
/* This SMTP struct is used in the Curl_easy. All SMTP data that is
123
   connection-oriented must be in smtp_conn to properly deal with the fact that
124
   perhaps the Curl_easy is changed between the times the connection is
125
   used. */
126
struct SMTP {
127
  curl_pp_transfer transfer;
128
  char *custom;            /* Custom Request */
129
  struct curl_slist *rcpt; /* Recipient list */
130
  int rcpt_last_error;     /* The last error received for RCPT TO command */
131
  size_t eob;              /* Number of bytes of the EOB (End Of Body) that
132
                              have been received so far */
133
  BIT(rcpt_had_ok);        /* Whether any of RCPT TO commands (depends on
134
                              total number of recipients) succeeded so far */
135
  BIT(trailing_crlf);      /* Specifies if the trailing CRLF is present */
136
};
137
138
/***********************************************************************
139
 *
140
 * smtp_parse_url_options()
141
 *
142
 * Parse the URL login options.
143
 */
144
static CURLcode smtp_parse_url_options(struct connectdata *conn,
145
                                       struct smtp_conn *smtpc)
146
2.97k
{
147
2.97k
  CURLcode result = CURLE_OK;
148
2.97k
  const char *ptr = conn->options;
149
150
3.15k
  while(!result && ptr && *ptr) {
151
176
    const char *key = ptr;
152
176
    const char *value;
153
154
1.02k
    while(*ptr && *ptr != '=')
155
846
      ptr++;
156
157
176
    value = ptr + 1;
158
159
11.0k
    while(*ptr && *ptr != ';')
160
10.8k
      ptr++;
161
162
176
    if(curl_strnequal(key, "AUTH=", 5))
163
152
      result = Curl_sasl_parse_url_auth_option(&smtpc->sasl,
164
152
                                               value, ptr - value);
165
24
    else
166
24
      result = CURLE_URL_MALFORMAT;
167
168
176
    if(*ptr == ';')
169
129
      ptr++;
170
176
  }
171
172
2.97k
  return result;
173
2.97k
}
174
175
/***********************************************************************
176
 *
177
 * smtp_parse_url_path()
178
 *
179
 * Parse the URL path into separate path components.
180
 */
181
static CURLcode smtp_parse_url_path(struct Curl_easy *data,
182
                                    struct smtp_conn *smtpc)
183
2.92k
{
184
  /* The SMTP struct is already initialized in smtp_connect() */
185
2.92k
  const char *path = &data->state.up.path[1]; /* skip leading path */
186
2.92k
  char localhost[HOSTNAME_MAX + 1];
187
188
  /* Calculate the path if necessary */
189
2.92k
  if(!*path) {
190
2.81k
    if(!Curl_gethostname(localhost, sizeof(localhost)))
191
2.81k
      path = localhost;
192
0
    else
193
0
      path = "localhost";
194
2.81k
  }
195
196
  /* URL decode the path and use it as the domain in our EHLO */
197
2.92k
  return Curl_urldecode(path, 0, &smtpc->domain, NULL, REJECT_CTRL);
198
2.92k
}
199
200
/***********************************************************************
201
 *
202
 * smtp_parse_custom_request()
203
 *
204
 * Parse the custom request.
205
 */
206
static CURLcode smtp_parse_custom_request(struct Curl_easy *data,
207
                                          struct SMTP *smtp)
208
1.44k
{
209
1.44k
  CURLcode result = CURLE_OK;
210
1.44k
  const char *custom = CURL_EASY_STR(data, STRING_CUSTOMREQUEST);
211
212
  /* URL decode the custom request */
213
1.44k
  if(custom)
214
52
    result = Curl_urldecode(custom, 0, &smtp->custom, NULL, REJECT_CTRL);
215
216
1.44k
  return result;
217
1.44k
}
218
219
/***********************************************************************
220
 *
221
 * smtp_parse_address()
222
 *
223
 * Parse the fully qualified mailbox address into a local address part and the
224
 * hostname, converting the hostname to an IDN A-label, as per RFC-5890, if
225
 * necessary.
226
 *
227
 * Parameters:
228
 *
229
 * fqma           [in]     - The fully qualified mailbox address (which may or
230
 *                           may not contain UTF-8 characters).
231
 * address        [in/out] - A new allocated buffer which holds the local
232
 *                           address part of the mailbox. This buffer must be
233
 *                           free'ed by the caller.
234
 * host           [in/out] - The hostname structure that holds the original,
235
 *                           and optionally encoded, hostname.
236
 *                           Curl_free_idnconverted_hostname() must be called
237
 *                           once the caller has finished with the structure.
238
 *
239
 * Returns CURLE_OK on success.
240
 *
241
 * Notes:
242
 *
243
 * Should a UTF-8 hostname require conversion to IDN ACE and we cannot honor
244
 * that conversion then we shall return success. This allow the caller to send
245
 * the data to the server as a U-label (as per RFC-6531 sect. 3.2).
246
 *
247
 * If an mailbox '@' separator cannot be located then the mailbox is considered
248
 * to be either a local mailbox or an invalid mailbox (depending on what the
249
 * calling function deems it to be) then the input will be returned in
250
 * the address part with the hostname being NULL.
251
 */
252
static CURLcode smtp_parse_address(struct Curl_easy *data, const char *fqma,
253
                                   char **address, struct hostname *host,
254
                                   const char **suffix)
255
2.17k
{
256
2.17k
  CURLcode result = CURLE_OK;
257
2.17k
  size_t length;
258
2.17k
  char *addressend;
259
2.17k
  char *dup;
260
261
  /* A CR or LF in the address ends up verbatim in the MAIL FROM/RCPT TO
262
     command line, so a crafted address could smuggle further SMTP commands
263
     onto the wire. Reject it before the command is built. */
264
2.17k
  if(strpbrk(fqma, "\r\n")) {
265
5
    failf(data, "Refusing to send email address with a CR or LF");
266
5
    return CURLE_BAD_FUNCTION_ARGUMENT;
267
5
  }
268
269
  /* Duplicate the fully qualified email address so we can manipulate it,
270
     ensuring it does not contain the delimiters if specified */
271
2.16k
  dup = curlx_strdup(fqma[0] == '<' ? fqma + 1 : fqma);
272
2.16k
  if(!dup)
273
0
    return CURLE_OUT_OF_MEMORY;
274
275
2.16k
  if(fqma[0] != '<') {
276
2.06k
    length = strlen(dup);
277
2.06k
    if(length && dup[length - 1] == '>')
278
35
      dup[length - 1] = '\0';
279
2.06k
  }
280
100
  else {
281
100
    addressend = strrchr(dup, '>');
282
100
    if(addressend) {
283
34
      *addressend = '\0';
284
34
      *suffix = addressend + 1;
285
34
    }
286
100
  }
287
288
  /* Extract the hostname from the address (if we can) */
289
2.16k
  host->name = strpbrk(dup, "@");
290
2.16k
  if(host->name) {
291
457
    *host->name = '\0';
292
457
    host->name = host->name + 1;
293
294
    /* Attempt to convert the hostname to IDN ACE */
295
457
    (void)Curl_idnconvert_hostname(host);
296
297
    /* If Curl_idnconvert_hostname() fails then we shall attempt to continue
298
       and send the hostname using UTF-8 rather than as 7-bit ACE (which is
299
       our preference) */
300
457
  }
301
302
  /* Extract the local address from the mailbox */
303
2.16k
  *address = dup;
304
305
2.16k
  return result;
306
2.16k
}
307
308
struct cr_eob_ctx {
309
  struct Curl_creader super;
310
  struct bufq buf;
311
  size_t n_eob; /* how many EOB bytes we matched so far */
312
  size_t eob;       /* Number of bytes of the EOB (End Of Body) that
313
                       have been received so far */
314
  BIT(read_eos);  /* we read an EOS from the next reader */
315
  BIT(processed_eos);  /* we read and processed an EOS */
316
  BIT(eos);       /* we have returned an EOS */
317
};
318
319
static CURLcode cr_eob_init(struct Curl_easy *data,
320
                            struct Curl_creader *reader)
321
941
{
322
941
  struct cr_eob_ctx *ctx = reader->ctx;
323
941
  (void)data;
324
  /* The first char we read is the first on a line, as if we had
325
   * read CRLF before */
326
941
  ctx->n_eob = 2;
327
941
  Curl_bufq_init2(&ctx->buf, (16 * 1024), 1, BUFQ_OPT_SOFT_LIMIT);
328
941
  return CURLE_OK;
329
941
}
330
331
static void cr_eob_close(struct Curl_easy *data, struct Curl_creader *reader)
332
941
{
333
941
  struct cr_eob_ctx *ctx = reader->ctx;
334
941
  (void)data;
335
941
  Curl_bufq_free(&ctx->buf);
336
941
}
337
338
/* this is the 5-bytes End-Of-Body marker for SMTP */
339
137M
#define SMTP_EOB          "\r\n.\r\n"
340
68.8M
#define SMTP_EOB_FIND_LEN 3
341
342
/* client reader doing SMTP End-Of-Body escaping. */
343
static CURLcode cr_eob_read(struct Curl_easy *data,
344
                            struct Curl_creader *reader,
345
                            char *buf, size_t blen,
346
                            size_t *pnread, bool *peos)
347
1.12k
{
348
1.12k
  struct cr_eob_ctx *ctx = reader->ctx;
349
1.12k
  CURLcode result = CURLE_OK;
350
1.12k
  size_t nread, i, start, n;
351
1.12k
  bool eos;
352
353
1.12k
  if(!ctx->read_eos && Curl_bufq_is_empty(&ctx->buf)) {
354
    /* Get more and convert it when needed */
355
1.08k
    result = Curl_creader_read(data, reader->next, buf, blen, &nread, &eos);
356
1.08k
    CURL_TRC_SMTP(data, "cr_eob_read, next_read(len=%zu) -> %d, %zu eos=%d",
357
1.08k
                  blen, (int)result, nread, eos);
358
1.08k
    if(result)
359
0
      return result;
360
361
1.08k
    ctx->read_eos = eos;
362
1.08k
    if(nread) {
363
1.05k
      if(!ctx->n_eob && !memchr(buf, SMTP_EOB[0], nread)) {
364
        /* not in the middle of a match, no EOB start found, pass */
365
52
        *pnread = nread;
366
52
        *peos = FALSE;
367
52
        return CURLE_OK;
368
52
      }
369
      /* scan for EOB (continuation) and convert */
370
68.8M
      for(i = start = 0; i < nread; ++i) {
371
68.8M
        if(ctx->n_eob >= SMTP_EOB_FIND_LEN) {
372
          /* matched the EOB prefix and seeing additional char, add '.' */
373
879
          result = Curl_bufq_cwrite(&ctx->buf, buf + start, i - start, &n);
374
879
          if(result)
375
0
            return result;
376
879
          result = Curl_bufq_cwrite(&ctx->buf, ".", 1, &n);
377
879
          if(result)
378
0
            return result;
379
879
          ctx->n_eob = 0;
380
879
          start = i;
381
879
          if(data->state.infilesize > 0)
382
593
            data->state.infilesize++;
383
879
        }
384
385
68.8M
        if(buf[i] != SMTP_EOB[ctx->n_eob])
386
63.8M
          ctx->n_eob = 0;
387
388
68.8M
        if(buf[i] == SMTP_EOB[ctx->n_eob]) {
389
          /* matching another char of the EOB */
390
7.41M
          ++ctx->n_eob;
391
7.41M
        }
392
68.8M
      }
393
394
      /* add any remainder to buf */
395
1.00k
      if(start < nread) {
396
1.00k
        result = Curl_bufq_cwrite(&ctx->buf, buf + start, nread - start, &n);
397
1.00k
        if(result)
398
0
          return result;
399
1.00k
      }
400
1.00k
    }
401
1.08k
  }
402
403
1.06k
  *peos = FALSE;
404
405
1.06k
  if(ctx->read_eos && !ctx->processed_eos) {
406
    /* if we last matched a CRLF or if the data was empty, add ".\r\n"
407
     * to end the body. If we sent something and it did not end with "\r\n",
408
     * add "\r\n.\r\n" to end the body */
409
606
    const char *eob = SMTP_EOB;
410
606
    CURL_TRC_SMTP(data, "auto-ending mail body with '\\r\\n.\\r\\n'");
411
606
    switch(ctx->n_eob) {
412
559
    case 2:
413
      /* seen a CRLF at the end, add the remainder */
414
559
      eob = &SMTP_EOB[2];
415
559
      break;
416
3
    case 3:
417
      /* ended with '\r\n.', we should escape the last '.' */
418
3
      eob = "." SMTP_EOB;
419
3
      break;
420
44
    default:
421
44
      break;
422
606
    }
423
606
    result = Curl_bufq_cwrite(&ctx->buf, eob, strlen(eob), &n);
424
606
    if(result)
425
0
      return result;
426
606
    ctx->processed_eos = TRUE;
427
606
  }
428
429
1.06k
  if(!Curl_bufq_is_empty(&ctx->buf)) {
430
1.06k
    result = Curl_bufq_cread(&ctx->buf, buf, blen, pnread);
431
1.06k
  }
432
0
  else
433
0
    *pnread = 0;
434
435
1.06k
  if(ctx->read_eos && Curl_bufq_is_empty(&ctx->buf)) {
436
    /* no more data, read all, done. */
437
599
    CURL_TRC_SMTP(data, "mail body complete, returning EOS");
438
599
    ctx->eos = TRUE;
439
599
  }
440
1.06k
  *peos = (bool)ctx->eos;
441
1.06k
  DEBUGF(infof(data, "cr_eob_read(%zu) -> %d, %zu, %d",
442
1.06k
               blen, (int)result, *pnread, *peos));
443
1.06k
  return result;
444
1.06k
}
445
446
static curl_off_t cr_eob_total_length(struct Curl_easy *data,
447
                                      struct Curl_creader *reader)
448
0
{
449
  /* this reader changes length depending on input */
450
0
  (void)data;
451
0
  (void)reader;
452
0
  return -1;
453
0
}
454
455
static const struct Curl_crtype cr_eob = {
456
  "cr-smtp-eob",
457
  cr_eob_init,
458
  cr_eob_read,
459
  cr_eob_close,
460
  Curl_creader_def_needs_rewind,
461
  cr_eob_total_length,
462
  Curl_creader_def_resume_from,
463
  Curl_creader_def_cntrl,
464
  Curl_creader_def_is_paused,
465
  Curl_creader_def_done,
466
  sizeof(struct cr_eob_ctx)
467
};
468
469
static CURLcode cr_eob_add(struct Curl_easy *data)
470
941
{
471
941
  struct Curl_creader *reader = NULL;
472
941
  CURLcode result;
473
474
941
  result = Curl_creader_create(&reader, data, &cr_eob, CURL_CR_CONTENT_ENCODE);
475
941
  if(!result)
476
941
    result = Curl_creader_add(data, reader);
477
478
941
  if(result && reader)
479
0
    Curl_creader_free(data, reader);
480
941
  return result;
481
941
}
482
483
/***********************************************************************
484
 *
485
 * smtp_endofresp()
486
 *
487
 * Checks for an ending SMTP status code at the start of the given string, but
488
 * also detects various capabilities from the EHLO response including the
489
 * supported authentication mechanisms.
490
 */
491
static bool smtp_endofresp(struct Curl_easy *data, struct connectdata *conn,
492
                           const char *line, size_t len, int *resp)
493
50.7k
{
494
50.7k
  struct smtp_conn *smtpc = Curl_conn_meta_get(conn, CURL_META_SMTP_CONN);
495
50.7k
  bool end = FALSE;
496
50.7k
  (void)data;
497
498
50.7k
  DEBUGASSERT(smtpc);
499
50.7k
  if(!smtpc)
500
0
    return FALSE;
501
502
  /* Nothing for us */
503
50.7k
  if(len < 4 || !ISDIGIT(line[0]) || !ISDIGIT(line[1]) || !ISDIGIT(line[2]))
504
24.1k
    return FALSE;
505
506
  /* Do we have a command response? This should be the response code followed
507
     by a space and optionally some text as per RFC-5321 and as outlined in
508
     Section 4. Examples of RFC-4954 but some email servers ignore this and
509
     only send the response code instead as per Section 4.2. */
510
26.5k
  if(line[3] == ' ' || len == 5) {
511
19.9k
    char tmpline[6];
512
19.9k
    curl_off_t code;
513
19.9k
    const char *p = tmpline;
514
19.9k
    end = TRUE;
515
19.9k
    memcpy(tmpline, line, (len == 5 ? 5 : 3));
516
19.9k
    tmpline[len == 5 ? 5 : 3] = 0;
517
19.9k
    if(curlx_str_number(&p, &code, len == 5 ? 99999 : 999))
518
0
      return FALSE;
519
19.9k
    *resp = (int)code;
520
521
    /* Make sure real server never sends internal value */
522
19.9k
    if(*resp == 1)
523
6.77k
      *resp = 0;
524
19.9k
  }
525
  /* Do we have a multiline (continuation) response? */
526
6.61k
  else if(line[3] == '-' &&
527
5.38k
          (smtpc->state == SMTP_EHLO || smtpc->state == SMTP_COMMAND)) {
528
4.65k
    end = TRUE;
529
4.65k
    *resp = 1;  /* Internal response code */
530
4.65k
  }
531
532
26.5k
  return end;
533
26.5k
}
534
535
/***********************************************************************
536
 *
537
 * smtp_get_message()
538
 *
539
 * Gets the authentication message from the response buffer.
540
 */
541
static CURLcode smtp_get_message(struct Curl_easy *data, struct bufref *out)
542
155
{
543
155
  struct smtp_conn *smtpc =
544
155
    Curl_conn_meta_get(data->conn, CURL_META_SMTP_CONN);
545
155
  char *message;
546
155
  size_t len;
547
548
155
  if(!smtpc)
549
0
    return CURLE_FAILED_INIT;
550
551
155
  message = curlx_dyn_ptr(&smtpc->pp.recvbuf);
552
155
  len = smtpc->pp.nfinal;
553
155
  if(len > 4) {
554
    /* Find the start of the message */
555
155
    len -= 4;
556
628
    for(message += 4; ISBLANK(*message); message++, len--)
557
473
      ;
558
559
    /* Find the end of the message */
560
801
    while(len--)
561
754
      if(!ISNEWLINE(message[len]) && !ISBLANK(message[len]))
562
108
        break;
563
564
    /* Terminate the message */
565
155
    message[++len] = '\0';
566
155
    Curl_bufref_set(out, message, len, NULL);
567
155
  }
568
0
  else
569
    /* junk input => zero length output */
570
0
    Curl_bufref_set(out, "", 0, NULL);
571
572
155
  return CURLE_OK;
573
155
}
574
575
/***********************************************************************
576
 *
577
 * smtp_state()
578
 *
579
 * This is the ONLY way to change SMTP state!
580
 */
581
static void smtp_state(struct Curl_easy *data,
582
                       struct smtp_conn *smtpc,
583
                       smtpstate newstate)
584
13.9k
{
585
13.9k
#ifdef CURLVERBOSE
586
  /* for debug purposes */
587
13.9k
  static const char * const names[] = {
588
13.9k
    "STOP",
589
13.9k
    "SERVERGREET",
590
13.9k
    "EHLO",
591
13.9k
    "HELO",
592
13.9k
    "STARTTLS",
593
13.9k
    "UPGRADETLS",
594
13.9k
    "AUTH",
595
13.9k
    "COMMAND",
596
13.9k
    "MAIL",
597
13.9k
    "RCPT",
598
13.9k
    "DATA",
599
13.9k
    "POSTDATA",
600
13.9k
    "QUIT",
601
    /* LAST */
602
13.9k
  };
603
604
13.9k
  if(smtpc->state != newstate)
605
12.4k
    CURL_TRC_SMTP(data, "state change from %s to %s",
606
13.9k
                  names[smtpc->state], names[newstate]);
607
#else
608
  (void)data;
609
#endif
610
611
13.9k
  smtpc->state = newstate;
612
13.9k
}
613
614
/***********************************************************************
615
 *
616
 * smtp_perform_ehlo()
617
 *
618
 * Sends the EHLO command to not only initialize communication with the ESMTP
619
 * server but to also obtain a list of server side supported capabilities.
620
 */
621
static CURLcode smtp_perform_ehlo(struct Curl_easy *data,
622
                                  struct smtp_conn *smtpc)
623
1.85k
{
624
1.85k
  CURLcode result = CURLE_OK;
625
626
1.85k
  smtpc->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanism yet */
627
1.85k
  smtpc->sasl.authused = SASL_AUTH_NONE;  /* Clear the authentication mechanism
628
                                             used for esmtp connections */
629
1.85k
  smtpc->tls_supported = FALSE;           /* Clear the TLS capability */
630
1.85k
  smtpc->auth_supported = FALSE;          /* Clear the AUTH capability */
631
632
  /* Send the EHLO command */
633
1.85k
  result = Curl_pp_sendf(data, &smtpc->pp, "EHLO %s", smtpc->domain);
634
635
1.85k
  if(!result)
636
1.84k
    smtp_state(data, smtpc, SMTP_EHLO);
637
638
1.85k
  return result;
639
1.85k
}
640
641
/***********************************************************************
642
 *
643
 * smtp_perform_helo()
644
 *
645
 * Sends the HELO command to initialize communication with the SMTP server.
646
 */
647
static CURLcode smtp_perform_helo(struct Curl_easy *data,
648
                                  struct smtp_conn *smtpc)
649
58
{
650
58
  CURLcode result = CURLE_OK;
651
652
58
  smtpc->sasl.authused = SASL_AUTH_NONE; /* No authentication mechanism used
653
                                            in smtp connections */
654
655
  /* Send the HELO command */
656
58
  result = Curl_pp_sendf(data, &smtpc->pp, "HELO %s", smtpc->domain);
657
658
58
  if(!result)
659
58
    smtp_state(data, smtpc, SMTP_HELO);
660
661
58
  return result;
662
58
}
663
664
/***********************************************************************
665
 *
666
 * smtp_perform_starttls()
667
 *
668
 * Sends the STLS command to start the upgrade to TLS.
669
 */
670
static CURLcode smtp_perform_starttls(struct Curl_easy *data,
671
                                      struct smtp_conn *smtpc)
672
2
{
673
  /* Send the STARTTLS command */
674
2
  CURLcode result = Curl_pp_sendf(data, &smtpc->pp, "%s", "STARTTLS");
675
676
2
  if(!result)
677
2
    smtp_state(data, smtpc, SMTP_STARTTLS);
678
679
2
  return result;
680
2
}
681
682
/***********************************************************************
683
 *
684
 * smtp_perform_upgrade_tls()
685
 *
686
 * Performs the upgrade to TLS.
687
 */
688
static CURLcode smtp_perform_upgrade_tls(struct Curl_easy *data,
689
                                         struct smtp_conn *smtpc)
690
0
{
691
0
#ifdef USE_SSL
692
  /* Start the SSL connection */
693
0
  struct connectdata *conn = data->conn;
694
0
  CURLcode result;
695
0
  bool ssldone = FALSE;
696
697
0
  DEBUGASSERT(smtpc->state == SMTP_UPGRADETLS);
698
0
  if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
699
0
    result = Curl_ssl_cfilter_add(
700
0
      data, Curl_conn_get_origin(conn, FIRSTSOCKET), conn, FIRSTSOCKET);
701
0
    if(result)
702
0
      goto out;
703
    /* Change the connection handler and SMTP state */
704
0
    conn->scheme = &Curl_scheme_smtps;
705
0
  }
706
707
0
  DEBUGASSERT(!smtpc->ssldone);
708
0
  result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone);
709
0
  DEBUGF(infof(data, "smtp_perform_upgrade_tls, connect -> %d, %d",
710
0
               (int)result, ssldone));
711
0
  if(!result && ssldone) {
712
0
    smtpc->ssldone = ssldone;
713
    /* perform EHLO now, changes smtp->state out of SMTP_UPGRADETLS */
714
0
    result = smtp_perform_ehlo(data, smtpc);
715
0
  }
716
0
out:
717
0
  return result;
718
#else
719
  (void)data;
720
  (void)smtpc;
721
  return CURLE_NOT_BUILT_IN;
722
#endif
723
0
}
724
725
/***********************************************************************
726
 *
727
 * smtp_perform_auth()
728
 *
729
 * Sends an AUTH command allowing the client to login with the given SASL
730
 * authentication mechanism.
731
 */
732
static CURLcode smtp_perform_auth(struct Curl_easy *data,
733
                                  const char *mech,
734
                                  const struct bufref *initresp)
735
202
{
736
202
  CURLcode result = CURLE_OK;
737
202
  struct smtp_conn *smtpc =
738
202
    Curl_conn_meta_get(data->conn, CURL_META_SMTP_CONN);
739
202
  const char *ir = Curl_bufref_ptr(initresp);
740
741
202
  if(!smtpc)
742
0
    return CURLE_FAILED_INIT;
743
744
202
  if(ir) {                                  /* AUTH <mech> ...<crlf> */
745
    /* Send the AUTH command with the initial response */
746
3
    result = Curl_pp_sendf(data, &smtpc->pp, "AUTH %s %s",
747
3
                           mech, *ir ? ir : "=");
748
3
  }
749
199
  else {
750
    /* Send the AUTH command */
751
199
    result = Curl_pp_sendf(data, &smtpc->pp, "AUTH %s", mech);
752
199
  }
753
754
202
  return result;
755
202
}
756
757
/***********************************************************************
758
 *
759
 * smtp_continue_auth()
760
 *
761
 * Sends SASL continuation data.
762
 */
763
static CURLcode smtp_continue_auth(struct Curl_easy *data,
764
                                   const char *mech,
765
                                   const struct bufref *resp)
766
106
{
767
106
  struct smtp_conn *smtpc =
768
106
    Curl_conn_meta_get(data->conn, CURL_META_SMTP_CONN);
769
770
106
  (void)mech;
771
106
  if(!smtpc)
772
0
    return CURLE_FAILED_INIT;
773
106
  return Curl_pp_sendf(data, &smtpc->pp, "%s", Curl_bufref_ptr(resp));
774
106
}
775
776
/***********************************************************************
777
 *
778
 * smtp_cancel_auth()
779
 *
780
 * Sends SASL cancellation.
781
 */
782
static CURLcode smtp_cancel_auth(struct Curl_easy *data, const char *mech)
783
88
{
784
88
  struct smtp_conn *smtpc =
785
88
    Curl_conn_meta_get(data->conn, CURL_META_SMTP_CONN);
786
787
88
  (void)mech;
788
88
  if(!smtpc)
789
0
    return CURLE_FAILED_INIT;
790
88
  return Curl_pp_sendf(data, &smtpc->pp, "*");
791
88
}
792
793
/***********************************************************************
794
 *
795
 * smtp_perform_authentication()
796
 *
797
 * Initiates the authentication sequence, with the appropriate SASL
798
 * authentication mechanism.
799
 */
800
static CURLcode smtp_perform_authentication(struct Curl_easy *data,
801
                                            struct smtp_conn *smtpc)
802
1.59k
{
803
1.59k
  CURLcode result = CURLE_OK;
804
1.59k
  saslprogress progress;
805
806
  /* Check we have enough data to authenticate with, and the
807
     server supports authentication, and end the connect phase if not */
808
1.59k
  if(!smtpc->auth_supported ||
809
1.40k
     !Curl_sasl_can_authenticate(&smtpc->sasl, data)) {
810
1.40k
    smtp_state(data, smtpc, SMTP_STOP);
811
1.40k
    return result;
812
1.40k
  }
813
814
  /* Calculate the SASL login details */
815
184
  result = Curl_sasl_start(&smtpc->sasl, data, FALSE, &progress);
816
817
184
  if(!result) {
818
184
    if(progress == SASL_INPROGRESS)
819
180
      smtp_state(data, smtpc, SMTP_AUTH);
820
4
    else
821
4
      result = Curl_sasl_is_blocked(&smtpc->sasl, data);
822
184
  }
823
824
184
  return result;
825
1.59k
}
826
827
/***********************************************************************
828
 *
829
 * smtp_perform_command()
830
 *
831
 * Sends an SMTP based command.
832
 */
833
static CURLcode smtp_perform_command(struct Curl_easy *data,
834
                                     struct smtp_conn *smtpc,
835
                                     struct SMTP *smtp)
836
1.68k
{
837
1.68k
  CURLcode result = CURLE_OK;
838
839
1.68k
  if(smtp->rcpt) {
840
    /* We notify the server we are sending UTF-8 data if a) it supports the
841
       SMTPUTF8 extension and b) The mailbox contains UTF-8 characters, in
842
       either the local address or hostname parts. This is regardless of
843
       whether the hostname is encoded using IDN ACE */
844
1.37k
    bool utf8 = FALSE;
845
846
1.37k
    if(!smtp->custom || !smtp->custom[0]) {
847
918
      char *address = NULL;
848
918
      struct hostname host = { NULL, NULL, NULL, NULL };
849
918
      const char *suffix = "";
850
851
      /* Parse the mailbox to verify into the local address and hostname
852
         parts, converting the hostname to an IDN A-label if necessary */
853
918
      result = smtp_parse_address(data, smtp->rcpt->data,
854
918
                                  &address, &host, &suffix);
855
918
      if(result)
856
2
        return result;
857
858
      /* Establish whether we should report SMTPUTF8 to the server for this
859
         mailbox as per RFC-6531 sect. 3.1 point 6 */
860
916
      utf8 = smtpc->utf8_supported &&
861
330
             (host.encalloc ||
862
330
              !Curl_is_ASCII_name(address) ||
863
245
              !Curl_is_ASCII_name(host.name));
864
865
      /* Send the VRFY command (Note: The hostname part may be absent when the
866
         host is a local system) */
867
916
      result = Curl_pp_sendf(data, &smtpc->pp, "VRFY %s%s%s%s",
868
916
                             address,
869
916
                             host.name ? "@" : "",
870
916
                             host.name ? host.name : "",
871
916
                             utf8 ? " SMTPUTF8" : "");
872
873
916
      Curl_free_idnconverted_hostname(&host);
874
916
      curlx_free(address);
875
916
    }
876
458
    else {
877
      /* Establish whether we should report that we support SMTPUTF8 for EXPN
878
         commands to the server as per RFC-6531 sect. 3.1 point 6 */
879
458
      utf8 = smtpc->utf8_supported && !strcmp(smtp->custom, "EXPN");
880
881
      /* Send the custom recipient based command such as the EXPN command */
882
458
      result = Curl_pp_sendf(data, &smtpc->pp,
883
458
                             "%s %s%s", smtp->custom,
884
458
                             smtp->rcpt->data,
885
458
                             utf8 ? " SMTPUTF8" : "");
886
458
    }
887
1.37k
  }
888
304
  else
889
    /* Send the non-recipient based command such as HELP */
890
304
    result = Curl_pp_sendf(data, &smtpc->pp, "%s",
891
304
                           smtp->custom && smtp->custom[0] != '\0' ?
892
303
                           smtp->custom : "HELP");
893
894
1.67k
  if(!result)
895
1.67k
    smtp_state(data, smtpc, SMTP_COMMAND);
896
897
1.67k
  return result;
898
1.68k
}
899
900
/***********************************************************************
901
 *
902
 * smtp_perform_mail()
903
 *
904
 * Sends an MAIL command to initiate the upload of a message.
905
 */
906
static CURLcode smtp_perform_mail(struct Curl_easy *data,
907
                                  struct smtp_conn *smtpc,
908
                                  struct SMTP *smtp)
909
944
{
910
944
  char *from = NULL;
911
944
  char *auth = NULL;
912
944
  char *size = NULL;
913
944
  const char *str;
914
944
  CURLcode result = CURLE_OK;
915
916
  /* We notify the server we are sending UTF-8 data if a) it supports the
917
     SMTPUTF8 extension and b) The mailbox contains UTF-8 characters, in
918
     either the local address or hostname parts. This is regardless of
919
     whether the hostname is encoded using IDN ACE */
920
944
  bool utf8 = FALSE;
921
922
  /* Calculate the FROM parameter */
923
944
  str = CURL_EASY_STR(data, STRING_MAIL_FROM);
924
944
  if(str) {
925
71
    char *address = NULL;
926
71
    struct hostname host = { NULL, NULL, NULL, NULL };
927
71
    const char *suffix = "";
928
929
    /* Parse the FROM mailbox into the local address and hostname parts,
930
       converting the hostname to an IDN A-label if necessary */
931
71
    result = smtp_parse_address(data, str, &address, &host, &suffix);
932
71
    if(result)
933
1
      goto out;
934
935
    /* Establish whether we should report SMTPUTF8 to the server for this
936
       mailbox as per RFC-6531 sect. 3.1 point 4 and sect. 3.4 */
937
70
    utf8 = smtpc->utf8_supported &&
938
3
           (host.encalloc ||
939
3
            !Curl_is_ASCII_name(address) ||
940
1
            !Curl_is_ASCII_name(host.name));
941
942
70
    if(host.name) {
943
2
      from = curl_maprintf("<%s@%s>%s", address, host.name, suffix);
944
945
2
      Curl_free_idnconverted_hostname(&host);
946
2
    }
947
68
    else
948
      /* An invalid mailbox was provided but we let the server worry
949
         about that and reply with a 501 error */
950
68
      from = curl_maprintf("<%s>%s", address, suffix);
951
952
70
    curlx_free(address);
953
70
  }
954
873
  else
955
    /* Null reverse-path, RFC-5321, sect. 3.6.3 */
956
873
    from = curlx_strdup("<>");
957
958
943
  if(!from) {
959
0
    result = CURLE_OUT_OF_MEMORY;
960
0
    goto out;
961
0
  }
962
963
  /* Calculate the optional AUTH parameter */
964
943
  str = CURL_EASY_STR(data, STRING_MAIL_AUTH);
965
943
  if(str && smtpc->sasl.authused) {
966
4
    if(str[0] != '\0') {
967
3
      char *address = NULL;
968
3
      struct hostname host = { NULL, NULL, NULL, NULL };
969
3
      const char *suffix = "";
970
971
      /* Parse the AUTH mailbox into the local address and hostname parts,
972
         converting the hostname to an IDN A-label if necessary */
973
3
      result = smtp_parse_address(data, str, &address, &host, &suffix);
974
3
      if(result)
975
1
        goto out;
976
977
      /* Establish whether we should report SMTPUTF8 to the server for this
978
         mailbox as per RFC-6531 sect. 3.1 point 4 and sect. 3.4 */
979
2
      if(!utf8 && smtpc->utf8_supported &&
980
0
         (host.encalloc ||
981
0
          !Curl_is_ASCII_name(address) ||
982
0
          !Curl_is_ASCII_name(host.name)))
983
0
        utf8 = TRUE;
984
985
2
      if(host.name) {
986
1
        auth = curl_maprintf("<%s@%s>%s", address, host.name, suffix);
987
988
1
        Curl_free_idnconverted_hostname(&host);
989
1
      }
990
1
      else
991
        /* An invalid mailbox was provided but we let the server worry
992
           about it */
993
1
        auth = curl_maprintf("<%s>%s", address, suffix);
994
2
      curlx_free(address);
995
2
    }
996
1
    else
997
      /* Empty AUTH, RFC-2554, sect. 5 */
998
1
      auth = curlx_strdup("<>");
999
1000
3
    if(!auth) {
1001
0
      result = CURLE_OUT_OF_MEMORY;
1002
0
      goto out;
1003
0
    }
1004
3
  }
1005
1006
942
#ifndef CURL_DISABLE_MIME
1007
  /* Prepare the mime data if some. */
1008
942
  if(IS_MIME_POST(data)) {
1009
819
    curl_mimepart *postp = data->set.mimepostp;
1010
1011
    /* Use the whole structure as data. */
1012
819
    postp->flags &= ~(unsigned int)MIME_BODY_ONLY;
1013
1014
    /* Add external headers and mime version. */
1015
819
    curl_mime_headers(postp, data->set.headers, 0);
1016
819
    result = Curl_mime_prepare_headers(data, postp, NULL,
1017
819
                                       NULL, MIMESTRATEGY_MAIL);
1018
1019
819
    if(!result && !Curl_checkheaders(data, STRCONST("Mime-Version")))
1020
818
      result = Curl_mime_add_header(&postp->curlheaders, "Mime-Version: 1.0");
1021
819
    if(!result)
1022
818
      result = Curl_creader_set_mime(data, postp);
1023
819
    if(result)
1024
1
      goto out;
1025
818
    data->state.infilesize = Curl_creader_total_length(data);
1026
818
  }
1027
123
  else
1028
123
#endif
1029
123
  {
1030
123
    result = Curl_creader_set_fread(data, data->state.infilesize);
1031
123
    if(result)
1032
0
      goto out;
1033
123
  }
1034
1035
  /* Calculate the optional SIZE parameter */
1036
941
  if(smtpc->size_supported && data->state.infilesize > 0) {
1037
3
    size = curl_maprintf("%" FMT_OFF_T, data->state.infilesize);
1038
1039
3
    if(!size) {
1040
0
      result = CURLE_OUT_OF_MEMORY;
1041
0
      goto out;
1042
0
    }
1043
3
  }
1044
1045
  /* If the mailboxes in the FROM and AUTH parameters do not include a UTF-8
1046
     based address then quickly scan through the recipient list and check if
1047
     any there do, as we need to correctly identify our support for SMTPUTF8
1048
     in the envelope, as per RFC-6531 sect. 3.4 */
1049
941
  if(smtpc->utf8_supported && !utf8) {
1050
22
    struct curl_slist *rcpt = smtp->rcpt;
1051
1052
530
    while(rcpt && !utf8) {
1053
      /* Does the hostname contain non-ASCII characters? */
1054
508
      if(!Curl_is_ASCII_name(rcpt->data))
1055
4
        utf8 = TRUE;
1056
1057
508
      rcpt = rcpt->next;
1058
508
    }
1059
22
  }
1060
1061
  /* Add the client reader doing STMP EOB escaping */
1062
941
  result = cr_eob_add(data);
1063
941
  if(result)
1064
0
    goto out;
1065
1066
  /* Send the MAIL command */
1067
941
  result = Curl_pp_sendf(data, &smtpc->pp,
1068
941
                         "MAIL FROM:%s%s%s%s%s%s",
1069
941
                         from,                 /* Mandatory                 */
1070
941
                         auth ? " AUTH=" : "", /* Optional on AUTH support  */
1071
941
                         auth ? auth : "",
1072
941
                         size ? " SIZE=" : "", /* Optional on SIZE support  */
1073
941
                         size ? size : "",
1074
941
                         utf8 ? " SMTPUTF8"    /* Internationalised mailbox */
1075
941
                               : "");          /* included in our envelope  */
1076
1077
944
out:
1078
944
  curlx_free(from);
1079
944
  curlx_free(auth);
1080
944
  curlx_free(size);
1081
1082
944
  if(!result)
1083
939
    smtp_state(data, smtpc, SMTP_MAIL);
1084
1085
944
  return result;
1086
941
}
1087
1088
/***********************************************************************
1089
 *
1090
 * smtp_perform_rcpt_to()
1091
 *
1092
 * Sends a RCPT TO command for a given recipient as part of the message upload
1093
 * process.
1094
 */
1095
static CURLcode smtp_perform_rcpt_to(struct Curl_easy *data,
1096
                                     struct smtp_conn *smtpc,
1097
                                     struct SMTP *smtp)
1098
1.18k
{
1099
1.18k
  CURLcode result = CURLE_OK;
1100
1.18k
  char *address = NULL;
1101
1.18k
  struct hostname host = { NULL, NULL, NULL, NULL };
1102
1.18k
  const char *suffix = "";
1103
1104
  /* Parse the recipient mailbox into the local address and hostname parts,
1105
     converting the hostname to an IDN A-label if necessary */
1106
1.18k
  result = smtp_parse_address(data, smtp->rcpt->data,
1107
1.18k
                              &address, &host, &suffix);
1108
1.18k
  if(result)
1109
1
    return result;
1110
1111
  /* Send the RCPT TO command */
1112
1.18k
  if(host.name)
1113
136
    result = Curl_pp_sendf(data, &smtpc->pp, "RCPT TO:<%s@%s>%s",
1114
136
                           address, host.name, suffix);
1115
1.04k
  else
1116
    /* An invalid mailbox was provided but we let the server worry about
1117
       that and reply with a 501 error */
1118
1.04k
    result = Curl_pp_sendf(data, &smtpc->pp, "RCPT TO:<%s>%s",
1119
1.04k
                           address, suffix);
1120
1121
1.18k
  Curl_free_idnconverted_hostname(&host);
1122
1.18k
  curlx_free(address);
1123
1124
1.18k
  if(!result)
1125
1.17k
    smtp_state(data, smtpc, SMTP_RCPT);
1126
1127
1.18k
  return result;
1128
1.18k
}
1129
1130
/***********************************************************************
1131
 *
1132
 * smtp_perform_quit()
1133
 *
1134
 * Performs the quit action prior to sclose() being called.
1135
 */
1136
static CURLcode smtp_perform_quit(struct Curl_easy *data,
1137
                                  struct smtp_conn *smtpc)
1138
1.16k
{
1139
  /* Send the QUIT command */
1140
1.16k
  CURLcode result = Curl_pp_sendf(data, &smtpc->pp, "%s", "QUIT");
1141
1142
1.16k
  if(!result)
1143
1.16k
    smtp_state(data, smtpc, SMTP_QUIT);
1144
1145
1.16k
  return result;
1146
1.16k
}
1147
1148
/* For the initial server greeting */
1149
static CURLcode smtp_state_servergreet_resp(struct Curl_easy *data,
1150
                                            struct smtp_conn *smtpc,
1151
                                            int smtpcode,
1152
                                            smtpstate instate)
1153
1.87k
{
1154
1.87k
  CURLcode result = CURLE_OK;
1155
1.87k
  (void)instate;
1156
1157
1.87k
  if(smtpcode / 100 != 2) {
1158
18
    failf(data, "Got unexpected smtp-server response: %d", smtpcode);
1159
18
    result = CURLE_WEIRD_SERVER_REPLY;
1160
18
  }
1161
1.85k
  else
1162
1.85k
    result = smtp_perform_ehlo(data, smtpc);
1163
1164
1.87k
  return result;
1165
1.87k
}
1166
1167
/* For STARTTLS responses */
1168
static CURLcode smtp_state_starttls_resp(struct Curl_easy *data,
1169
                                         struct smtp_conn *smtpc,
1170
                                         int smtpcode,
1171
                                         smtpstate instate)
1172
1
{
1173
1
  CURLcode result = CURLE_OK;
1174
1
  (void)instate;
1175
1176
  /* Pipelining in response is forbidden. */
1177
1
  if(smtpc->pp.overflow)
1178
1
    return CURLE_WEIRD_SERVER_REPLY;
1179
1180
0
  if(smtpcode != 220) {
1181
0
    if(data->set.use_ssl != CURLUSESSL_TRY) {
1182
0
      failf(data, "STARTTLS denied, code %d", smtpcode);
1183
0
      result = CURLE_USE_SSL_FAILED;
1184
0
    }
1185
0
    else
1186
0
      result = smtp_perform_authentication(data, smtpc);
1187
0
  }
1188
0
  else
1189
0
    smtp_state(data, smtpc, SMTP_UPGRADETLS);
1190
1191
0
  return result;
1192
1
}
1193
1194
/* For EHLO responses */
1195
static CURLcode smtp_state_ehlo_resp(struct Curl_easy *data,
1196
                                     struct smtp_conn *smtpc,
1197
                                     int smtpcode,
1198
                                     smtpstate instate)
1199
3.80k
{
1200
3.80k
  CURLcode result = CURLE_OK;
1201
3.80k
  const char *line = curlx_dyn_ptr(&smtpc->pp.recvbuf);
1202
3.80k
  size_t len = smtpc->pp.nfinal;
1203
1204
3.80k
  (void)instate;
1205
1206
3.80k
  if(smtpcode / 100 != 2 && smtpcode != 1) {
1207
59
    if(data->set.use_ssl <= CURLUSESSL_TRY ||
1208
1
       Curl_conn_is_ssl(data->conn, FIRSTSOCKET))
1209
58
      result = smtp_perform_helo(data, smtpc);
1210
1
    else {
1211
1
      failf(data, "Remote access denied: %d", smtpcode);
1212
1
      result = CURLE_REMOTE_ACCESS_DENIED;
1213
1
    }
1214
59
  }
1215
3.74k
  else if(len >= 4) {
1216
3.74k
    line += 4;
1217
3.74k
    len -= 4;
1218
1219
    /* Does the server support the STARTTLS capability? */
1220
3.74k
    if(len >= 8 && curl_strnequal(line, "STARTTLS", 8))
1221
70
      smtpc->tls_supported = TRUE;
1222
1223
    /* Does the server support the SIZE capability? */
1224
3.67k
    else if(len >= 4 && curl_strnequal(line, "SIZE", 4))
1225
5
      smtpc->size_supported = TRUE;
1226
1227
    /* Does the server support the UTF-8 capability? */
1228
3.67k
    else if(len >= 8 && curl_strnequal(line, "SMTPUTF8", 8))
1229
196
      smtpc->utf8_supported = TRUE;
1230
1231
    /* Does the server support authentication? */
1232
3.47k
    else if(len >= 5 && curl_strnequal(line, "AUTH ", 5)) {
1233
479
      smtpc->auth_supported = TRUE;
1234
1235
      /* Advance past the AUTH keyword */
1236
479
      line += 5;
1237
479
      len -= 5;
1238
1239
      /* Loop through the data line */
1240
4.04k
      for(;;) {
1241
4.04k
        size_t llen;
1242
4.04k
        size_t wordlen;
1243
4.04k
        unsigned short mechbit;
1244
1245
8.65k
        while(len && (ISBLANK(*line) || ISNEWLINE(*line))) {
1246
4.61k
          line++;
1247
4.61k
          len--;
1248
4.61k
        }
1249
1250
4.04k
        if(!len)
1251
479
          break;
1252
1253
        /* Extract the word */
1254
80.5k
        for(wordlen = 0; wordlen < len && !ISBLANK(line[wordlen]) &&
1255
77.8k
              !ISNEWLINE(line[wordlen]);)
1256
76.9k
          wordlen++;
1257
1258
        /* Test the word for a matching authentication mechanism */
1259
3.56k
        mechbit = Curl_sasl_decode_mech(line, wordlen, &llen);
1260
3.56k
        if(mechbit && llen == wordlen)
1261
280
          smtpc->sasl.authmechs |= mechbit;
1262
1263
3.56k
        line += wordlen;
1264
3.56k
        len -= wordlen;
1265
3.56k
      }
1266
479
    }
1267
1268
3.74k
    if(smtpcode != 1) {
1269
1.59k
      if(data->set.use_ssl && !Curl_conn_is_ssl(data->conn, FIRSTSOCKET)) {
1270
        /* We do not have an SSL/TLS connection yet, but SSL is requested */
1271
4
        if(smtpc->tls_supported)
1272
          /* Switch to TLS connection now */
1273
2
          result = smtp_perform_starttls(data, smtpc);
1274
2
        else if(data->set.use_ssl == CURLUSESSL_TRY)
1275
          /* Fallback and carry on with authentication */
1276
1
          result = smtp_perform_authentication(data, smtpc);
1277
1
        else {
1278
1
          failf(data, "STARTTLS not supported.");
1279
1
          result = CURLE_USE_SSL_FAILED;
1280
1
        }
1281
4
      }
1282
1.58k
      else
1283
1.58k
        result = smtp_perform_authentication(data, smtpc);
1284
1.59k
    }
1285
3.74k
  }
1286
0
  else {
1287
0
    failf(data, "Unexpectedly short EHLO response");
1288
0
    result = CURLE_WEIRD_SERVER_REPLY;
1289
0
  }
1290
1291
3.80k
  return result;
1292
3.80k
}
1293
1294
/* For HELO responses */
1295
static CURLcode smtp_state_helo_resp(struct Curl_easy *data,
1296
                                     struct smtp_conn *smtpc,
1297
                                     int smtpcode,
1298
                                     smtpstate instate)
1299
39
{
1300
39
  CURLcode result = CURLE_OK;
1301
39
  (void)instate;
1302
1303
39
  if(smtpcode / 100 != 2) {
1304
7
    failf(data, "Remote access denied: %d", smtpcode);
1305
7
    result = CURLE_REMOTE_ACCESS_DENIED;
1306
7
  }
1307
32
  else
1308
    /* End of connect phase */
1309
32
    smtp_state(data, smtpc, SMTP_STOP);
1310
1311
39
  return result;
1312
39
}
1313
1314
/* For SASL authentication responses */
1315
static CURLcode smtp_state_auth_resp(struct Curl_easy *data,
1316
                                     struct smtp_conn *smtpc,
1317
                                     int smtpcode,
1318
                                     smtpstate instate)
1319
244
{
1320
244
  CURLcode result = CURLE_OK;
1321
244
  saslprogress progress;
1322
1323
244
  (void)instate;
1324
1325
244
  result = Curl_sasl_continue(&smtpc->sasl, data, smtpcode, &progress);
1326
244
  if(!result)
1327
218
    switch(progress) {
1328
5
    case SASL_DONE:
1329
5
      smtp_state(data, smtpc, SMTP_STOP);  /* Authenticated */
1330
5
      break;
1331
4
    case SASL_IDLE:            /* No mechanism left after cancellation */
1332
4
      failf(data, "Authentication cancelled");
1333
4
      result = CURLE_LOGIN_DENIED;
1334
4
      break;
1335
209
    default:
1336
209
      break;
1337
218
    }
1338
1339
244
  return result;
1340
244
}
1341
1342
/* For command responses */
1343
static CURLcode smtp_state_command_resp(struct Curl_easy *data,
1344
                                        struct smtp_conn *smtpc,
1345
                                        struct SMTP *smtp,
1346
                                        int smtpcode,
1347
                                        smtpstate instate)
1348
3.92k
{
1349
3.92k
  CURLcode result = CURLE_OK;
1350
3.92k
  const char *line = curlx_dyn_ptr(&smtpc->pp.recvbuf);
1351
3.92k
  size_t len = smtpc->pp.nfinal;
1352
1353
3.92k
  (void)instate;
1354
1355
3.92k
  if((smtp->rcpt && smtpcode / 100 != 2 && smtpcode != 553 && smtpcode != 1) ||
1356
3.91k
     (!smtp->rcpt && smtpcode / 100 != 2 && smtpcode != 1)) {
1357
20
    failf(data, "Command failed: %d", smtpcode);
1358
20
    result = CURLE_WEIRD_SERVER_REPLY;
1359
20
  }
1360
3.90k
  else {
1361
3.90k
    if(!data->req.no_body)
1362
3.70k
      result = Curl_client_write(data, CLIENTWRITE_BODY, line, len);
1363
1364
3.90k
    if(!result && (smtpcode != 1)) {
1365
1.39k
      if(smtp->rcpt) {
1366
1.24k
        smtp->rcpt = smtp->rcpt->next;
1367
1368
1.24k
        if(smtp->rcpt) {
1369
          /* Send the next command */
1370
1.18k
          result = smtp_perform_command(data, smtpc, smtp);
1371
1.18k
        }
1372
56
        else
1373
          /* End of DO phase */
1374
56
          smtp_state(data, smtpc, SMTP_STOP);
1375
1.24k
      }
1376
157
      else
1377
        /* End of DO phase */
1378
157
        smtp_state(data, smtpc, SMTP_STOP);
1379
1.39k
    }
1380
3.90k
  }
1381
1382
3.92k
  return result;
1383
3.92k
}
1384
1385
/* For MAIL responses */
1386
static CURLcode smtp_state_mail_resp(struct Curl_easy *data,
1387
                                     struct smtp_conn *smtpc,
1388
                                     struct SMTP *smtp,
1389
                                     int smtpcode,
1390
                                     smtpstate instate)
1391
828
{
1392
828
  CURLcode result = CURLE_OK;
1393
828
  (void)instate;
1394
1395
828
  if(smtpcode / 100 != 2) {
1396
12
    failf(data, "MAIL failed: %d", smtpcode);
1397
12
    result = CURLE_SEND_ERROR;
1398
12
  }
1399
816
  else
1400
    /* Start the RCPT TO command */
1401
816
    result = smtp_perform_rcpt_to(data, smtpc, smtp);
1402
1403
828
  return result;
1404
828
}
1405
1406
/* For RCPT responses */
1407
static CURLcode smtp_state_rcpt_resp(struct Curl_easy *data,
1408
                                     struct smtp_conn *smtpc,
1409
                                     struct SMTP *smtp,
1410
                                     int smtpcode,
1411
                                     smtpstate instate)
1412
1.15k
{
1413
1.15k
  CURLcode result = CURLE_OK;
1414
1.15k
  bool is_smtp_err = FALSE;
1415
1.15k
  bool is_smtp_blocking_err = FALSE;
1416
1417
1.15k
  (void)instate;
1418
1419
1.15k
  is_smtp_err = (smtpcode / 100 != 2);
1420
1421
  /* If there are multiple RCPT TO commands to issue, it is possible to
1422
     ignore errors and proceed with only the valid addresses. */
1423
1.15k
  is_smtp_blocking_err = (is_smtp_err && !data->set.mail_rcpt_allowfails);
1424
1425
1.15k
  if(is_smtp_err) {
1426
    /* Remembering the last failure which we can report if all "RCPT TO" have
1427
       failed and we cannot proceed. */
1428
22
    smtp->rcpt_last_error = smtpcode;
1429
1430
22
    if(is_smtp_blocking_err) {
1431
9
      failf(data, "RCPT failed: %d", smtpcode);
1432
9
      result = CURLE_SEND_ERROR;
1433
9
    }
1434
22
  }
1435
1.13k
  else {
1436
    /* Some RCPT TO commands have succeeded. */
1437
1.13k
    smtp->rcpt_had_ok = TRUE;
1438
1.13k
  }
1439
1440
1.15k
  if(!is_smtp_blocking_err) {
1441
1.14k
    smtp->rcpt = smtp->rcpt->next;
1442
1443
1.14k
    if(smtp->rcpt)
1444
      /* Send the next RCPT TO command */
1445
365
      result = smtp_perform_rcpt_to(data, smtpc, smtp);
1446
780
    else {
1447
      /* We were not able to issue a successful RCPT TO command while going
1448
         over recipients (potentially multiple). Sending back last error. */
1449
780
      if(!smtp->rcpt_had_ok) {
1450
4
        failf(data, "RCPT failed: %d (last error)", smtp->rcpt_last_error);
1451
4
        result = CURLE_SEND_ERROR;
1452
4
      }
1453
776
      else {
1454
        /* Send the DATA command */
1455
776
        result = Curl_pp_sendf(data, &smtpc->pp, "%s", "DATA");
1456
1457
776
        if(!result)
1458
776
          smtp_state(data, smtpc, SMTP_DATA);
1459
776
      }
1460
780
    }
1461
1.14k
  }
1462
1463
1.15k
  return result;
1464
1.15k
}
1465
1466
/* For DATA response */
1467
static CURLcode smtp_state_data_resp(struct Curl_easy *data,
1468
                                     struct smtp_conn *smtpc,
1469
                                     int smtpcode,
1470
                                     smtpstate instate)
1471
744
{
1472
744
  CURLcode result = CURLE_OK;
1473
744
  (void)instate;
1474
1475
744
  if(smtpcode != 354) {
1476
12
    failf(data, "DATA failed: %d", smtpcode);
1477
12
    result = CURLE_SEND_ERROR;
1478
12
  }
1479
732
  else {
1480
    /* Set the progress upload size */
1481
732
    Curl_pgrsSetUploadSize(data, data->state.infilesize);
1482
1483
    /* SMTP upload */
1484
732
    Curl_xfer_setup_send(data, FIRSTSOCKET);
1485
1486
    /* End of DO phase */
1487
732
    smtp_state(data, smtpc, SMTP_STOP);
1488
732
  }
1489
1490
744
  return result;
1491
744
}
1492
1493
/* For POSTDATA responses, which are received after the entire DATA
1494
   part has been sent to the server */
1495
static CURLcode smtp_state_postdata_resp(struct Curl_easy *data,
1496
                                         struct smtp_conn *smtpc,
1497
                                         int smtpcode,
1498
                                         smtpstate instate)
1499
15
{
1500
15
  CURLcode result = CURLE_OK;
1501
1502
15
  (void)instate;
1503
1504
15
  if(smtpcode != 250)
1505
5
    result = CURLE_WEIRD_SERVER_REPLY;
1506
1507
  /* End of DONE phase */
1508
15
  smtp_state(data, smtpc, SMTP_STOP);
1509
1510
15
  return result;
1511
15
}
1512
1513
static CURLcode smtp_pp_statemachine(struct Curl_easy *data,
1514
                                     struct connectdata *conn)
1515
1.42M
{
1516
1.42M
  CURLcode result = CURLE_OK;
1517
1.42M
  int smtpcode;
1518
1.42M
  struct smtp_conn *smtpc = Curl_conn_meta_get(conn, CURL_META_SMTP_CONN);
1519
1.42M
  struct SMTP *smtp = Curl_meta_get(data, CURL_META_SMTP_EASY);
1520
1.42M
  size_t nread = 0;
1521
1522
1.42M
  if(!smtpc || !smtp)
1523
1.10k
    return CURLE_FAILED_INIT;
1524
1525
  /* Busy upgrading the connection; right now all I/O is SSL/TLS, not SMTP */
1526
1.42M
upgrade_tls:
1527
1.42M
  if(smtpc->state == SMTP_UPGRADETLS) {
1528
0
    result = smtp_perform_upgrade_tls(data, smtpc);
1529
0
    if(result || (smtpc->state == SMTP_UPGRADETLS))
1530
0
      return result;
1531
0
  }
1532
1533
  /* Flush any data that needs to be sent */
1534
1.42M
  if(smtpc->pp.sendleft)
1535
1.40M
    return Curl_pp_flushsend(data, &smtpc->pp);
1536
1537
28.5k
  do {
1538
    /* Read the response from the server */
1539
28.5k
    result = Curl_pp_readresp(data, FIRSTSOCKET, &smtpc->pp,
1540
28.5k
                              &smtpcode, &nread);
1541
28.5k
    if(result)
1542
2.32k
      return result;
1543
1544
    /* Store the latest response for later retrieval if necessary */
1545
26.1k
    if(smtpc->state != SMTP_QUIT && smtpcode != 1)
1546
21.5k
      data->info.httpcode = smtpcode;
1547
1548
26.1k
    if(!smtpcode)
1549
13.5k
      break;
1550
1551
    /* We have now received a full SMTP server response */
1552
12.6k
    switch(smtpc->state) {
1553
1.87k
    case SMTP_SERVERGREET:
1554
1.87k
      result = smtp_state_servergreet_resp(data, smtpc,
1555
1.87k
                                           smtpcode, smtpc->state);
1556
1.87k
      break;
1557
1558
3.80k
    case SMTP_EHLO:
1559
3.80k
      result = smtp_state_ehlo_resp(data, smtpc, smtpcode, smtpc->state);
1560
3.80k
      break;
1561
1562
39
    case SMTP_HELO:
1563
39
      result = smtp_state_helo_resp(data, smtpc, smtpcode, smtpc->state);
1564
39
      break;
1565
1566
1
    case SMTP_STARTTLS:
1567
1
      result = smtp_state_starttls_resp(data, smtpc, smtpcode, smtpc->state);
1568
      /* During UPGRADETLS, leave the read loop as we need to connect
1569
       * (e.g. TLS handshake) before we continue sending/receiving. */
1570
1
      if(!result && (smtpc->state == SMTP_UPGRADETLS))
1571
0
        goto upgrade_tls;
1572
1
      break;
1573
1574
244
    case SMTP_AUTH:
1575
244
      result = smtp_state_auth_resp(data, smtpc, smtpcode, smtpc->state);
1576
244
      break;
1577
1578
3.92k
    case SMTP_COMMAND:
1579
3.92k
      result = smtp_state_command_resp(data, smtpc, smtp,
1580
3.92k
                                       smtpcode, smtpc->state);
1581
3.92k
      break;
1582
1583
828
    case SMTP_MAIL:
1584
828
      result = smtp_state_mail_resp(data, smtpc, smtp, smtpcode, smtpc->state);
1585
828
      break;
1586
1587
1.15k
    case SMTP_RCPT:
1588
1.15k
      result = smtp_state_rcpt_resp(data, smtpc, smtp, smtpcode, smtpc->state);
1589
1.15k
      break;
1590
1591
744
    case SMTP_DATA:
1592
744
      result = smtp_state_data_resp(data, smtpc, smtpcode, smtpc->state);
1593
744
      break;
1594
1595
15
    case SMTP_POSTDATA:
1596
15
      result = smtp_state_postdata_resp(data, smtpc, smtpcode, smtpc->state);
1597
15
      break;
1598
1599
0
    case SMTP_QUIT:
1600
0
    default:
1601
      /* internal error */
1602
0
      smtp_state(data, smtpc, SMTP_STOP);
1603
0
      break;
1604
12.6k
    }
1605
12.6k
  } while(!result && smtpc->state != SMTP_STOP &&
1606
10.0k
          Curl_pp_moredata(&smtpc->pp));
1607
1608
16.5k
  return result;
1609
18.8k
}
1610
1611
/* Called repeatedly until done from multi.c */
1612
static CURLcode smtp_multi_statemach(struct Curl_easy *data, bool *done)
1613
6.09k
{
1614
6.09k
  CURLcode result = CURLE_OK;
1615
6.09k
  struct smtp_conn *smtpc =
1616
6.09k
    Curl_conn_meta_get(data->conn, CURL_META_SMTP_CONN);
1617
1618
6.09k
  *done = FALSE;
1619
6.09k
  if(!smtpc)
1620
0
    return CURLE_FAILED_INIT;
1621
1622
6.09k
  result = Curl_pp_statemach(data, &smtpc->pp, FALSE, FALSE);
1623
6.09k
  *done = (smtpc->state == SMTP_STOP);
1624
6.09k
  return result;
1625
6.09k
}
1626
1627
static CURLcode smtp_block_statemach(struct Curl_easy *data,
1628
                                     struct smtp_conn *smtpc,
1629
                                     bool disconnecting)
1630
1.99k
{
1631
1.99k
  CURLcode result = CURLE_OK;
1632
1633
1.42M
  while(smtpc->state != SMTP_STOP && !result)
1634
1.42M
    result = Curl_pp_statemach(data, &smtpc->pp, TRUE, disconnecting);
1635
1636
1.99k
  return result;
1637
1.99k
}
1638
1639
/* For the SMTP "protocol connect" and "doing" phases only */
1640
static CURLcode smtp_pollset(struct Curl_easy *data,
1641
                             struct easy_pollset *ps)
1642
873
{
1643
873
  struct smtp_conn *smtpc =
1644
873
    Curl_conn_meta_get(data->conn, CURL_META_SMTP_CONN);
1645
873
  return smtpc ? Curl_pp_pollset(data, &smtpc->pp, ps) : CURLE_OK;
1646
873
}
1647
1648
/* SASL parameters for the smtp protocol */
1649
static const struct SASLproto saslsmtp = {
1650
  "smtp",               /* The service name */
1651
  smtp_perform_auth,    /* Send authentication command */
1652
  smtp_continue_auth,   /* Send authentication continuation */
1653
  smtp_cancel_auth,     /* Cancel authentication */
1654
  smtp_get_message,     /* Get SASL response message */
1655
  512 - 8,              /* Max line len - strlen("AUTH ") - 1 space - CRLF */
1656
  334,                  /* Code received when continuation is expected */
1657
  235,                  /* Code to receive upon authentication success */
1658
  SASL_AUTH_DEFAULT,    /* Default mechanisms */
1659
  SASL_FLAG_BASE64      /* Configuration flags */
1660
};
1661
1662
/***********************************************************************
1663
 *
1664
 * smtp_connect()
1665
 *
1666
 * This function should do everything that is to be considered a part of
1667
 * the connection phase.
1668
 *
1669
 * The variable pointed to by 'done' will be TRUE if the protocol-layer
1670
 * connect phase is done when this function returns, or FALSE if not.
1671
 */
1672
static CURLcode smtp_connect(struct Curl_easy *data, bool *done)
1673
2.97k
{
1674
2.97k
  struct smtp_conn *smtpc =
1675
2.97k
    Curl_conn_meta_get(data->conn, CURL_META_SMTP_CONN);
1676
2.97k
  CURLcode result = CURLE_OK;
1677
1678
2.97k
  *done = FALSE; /* default to not done yet */
1679
2.97k
  if(!smtpc)
1680
0
    return CURLE_FAILED_INIT;
1681
1682
2.97k
  PINGPONG_SETUP(&smtpc->pp, smtp_pp_statemachine, smtp_endofresp);
1683
1684
  /* Initialize the SASL storage */
1685
2.97k
  Curl_sasl_init(&smtpc->sasl, data, &saslsmtp);
1686
1687
  /* Initialize the pingpong layer */
1688
2.97k
  Curl_pp_init(&smtpc->pp, Curl_pgrs_now(data));
1689
1690
  /* Parse the URL options */
1691
2.97k
  result = smtp_parse_url_options(data->conn, smtpc);
1692
2.97k
  if(result)
1693
56
    return result;
1694
1695
  /* Parse the URL path */
1696
2.92k
  result = smtp_parse_url_path(data, smtpc);
1697
2.92k
  if(result)
1698
9
    return result;
1699
1700
  /* Start off waiting for the server greeting response */
1701
2.91k
  smtp_state(data, smtpc, SMTP_SERVERGREET);
1702
1703
2.91k
  result = smtp_multi_statemach(data, done);
1704
1705
2.91k
  return result;
1706
2.92k
}
1707
1708
/***********************************************************************
1709
 *
1710
 * smtp_done()
1711
 *
1712
 * The DONE function. This does what needs to be done after a single DO has
1713
 * performed.
1714
 *
1715
 * Input argument is already checked for validity.
1716
 */
1717
static CURLcode smtp_done(struct Curl_easy *data, CURLcode status,
1718
                          bool premature)
1719
2.97k
{
1720
2.97k
  struct smtp_conn *smtpc =
1721
2.97k
    Curl_conn_meta_get(data->conn, CURL_META_SMTP_CONN);
1722
2.97k
  CURLcode result = CURLE_OK;
1723
2.97k
  struct connectdata *conn = data->conn;
1724
2.97k
  struct SMTP *smtp = Curl_meta_get(data, CURL_META_SMTP_EASY);
1725
1726
2.97k
  (void)premature;
1727
1728
2.97k
  if(!smtpc)
1729
0
    return CURLE_FAILED_INIT;
1730
2.97k
  if(!smtp)
1731
0
    return CURLE_OK;
1732
1733
  /* Cleanup our per-request based variables */
1734
2.97k
  curlx_safefree(smtp->custom);
1735
1736
2.97k
  if(status) {
1737
1.82k
    CURL_TRC_M(data, "SMTP done with bad status");
1738
1.82k
    connclose(conn); /* marked for closure */
1739
1.82k
    result = status;         /* use the already set error code */
1740
1.82k
  }
1741
1.14k
  else if(!data->set.connect_only && data->set.mail_rcpt &&
1742
900
          (data->state.upload || IS_MIME_POST(data))) {
1743
1744
828
    smtp_state(data, smtpc, SMTP_POSTDATA);
1745
1746
    /* Run the state-machine */
1747
828
    result = smtp_block_statemach(data, smtpc, FALSE);
1748
828
  }
1749
1750
  /* Clear the transfer mode for the next request */
1751
2.97k
  smtp->transfer = PPTRANSFER_BODY;
1752
2.97k
  CURL_TRC_SMTP(data, "smtp_done(status=%d, premature=%d) -> %d",
1753
2.97k
                (int)status, premature, (int)result);
1754
2.97k
  return result;
1755
2.97k
}
1756
1757
/***********************************************************************
1758
 *
1759
 * smtp_perform()
1760
 *
1761
 * This is the actual DO function for SMTP. Transfer a mail, send a command
1762
 * or get some data according to the options previously setup.
1763
 */
1764
static CURLcode smtp_perform(struct Curl_easy *data,
1765
                             struct smtp_conn *smtpc,
1766
                             struct SMTP *smtp,
1767
                             bool *connected,
1768
                             bool *dophase_done)
1769
1.43k
{
1770
  /* This is SMTP and no proxy */
1771
1.43k
  CURLcode result = CURLE_OK;
1772
1773
1.43k
  CURL_TRC_SMTP(data, "smtp_perform(), start");
1774
1775
1.43k
  if(data->req.no_body) {
1776
    /* Requested no body means no transfer */
1777
10
    smtp->transfer = PPTRANSFER_INFO;
1778
10
  }
1779
1780
1.43k
  *dophase_done = FALSE; /* not done yet */
1781
1782
  /* Store the first recipient (or NULL if not specified) */
1783
1.43k
  smtp->rcpt = data->set.mail_rcpt;
1784
1785
  /* Track of whether we have successfully sent at least one RCPT TO command */
1786
1.43k
  smtp->rcpt_had_ok = FALSE;
1787
1788
  /* Track of the last error we have received by sending RCPT TO command */
1789
1.43k
  smtp->rcpt_last_error = 0;
1790
1791
  /* Initial data character is the first character in line: it is implicitly
1792
     preceded by a virtual CRLF. */
1793
1.43k
  smtp->trailing_crlf = TRUE;
1794
1.43k
  smtp->eob = 2;
1795
1796
  /* Start the first command in the DO phase */
1797
1.43k
  if((data->state.upload || IS_MIME_POST(data)) && data->set.mail_rcpt)
1798
    /* MAIL transfer */
1799
944
    result = smtp_perform_mail(data, smtpc, smtp);
1800
495
  else
1801
    /* SMTP based command (VRFY, EXPN, NOOP, RSET or HELP) */
1802
495
    result = smtp_perform_command(data, smtpc, smtp);
1803
1804
1.43k
  if(result)
1805
8
    goto out;
1806
1807
  /* Run the state-machine */
1808
1.43k
  result = smtp_multi_statemach(data, dophase_done);
1809
1810
1.43k
  *connected = Curl_conn_is_connected(data->conn, FIRSTSOCKET);
1811
1812
1.43k
out:
1813
1.43k
  CURL_TRC_SMTP(data, "smtp_perform() -> %d, connected=%d, done=%d",
1814
1.43k
                (int)result, *connected, *dophase_done);
1815
1.43k
  return result;
1816
1.43k
}
1817
1818
/* Call this when the DO phase has completed */
1819
static CURLcode smtp_dophase_done(struct Curl_easy *data,
1820
                                  struct SMTP *smtp,
1821
                                  bool connected)
1822
945
{
1823
945
  (void)connected;
1824
1825
945
  if(smtp->transfer != PPTRANSFER_BODY)
1826
    /* no data to transfer */
1827
3
    Curl_xfer_setup_nop(data);
1828
1829
945
  return CURLE_OK;
1830
945
}
1831
1832
/***********************************************************************
1833
 *
1834
 * smtp_regular_transfer()
1835
 *
1836
 * The input argument is already checked for validity.
1837
 *
1838
 * Performs all commands done before a regular transfer between a local and a
1839
 * remote host.
1840
 */
1841
static CURLcode smtp_regular_transfer(struct Curl_easy *data,
1842
                                      struct smtp_conn *smtpc,
1843
                                      struct SMTP *smtp,
1844
                                      bool *dophase_done)
1845
1.43k
{
1846
1.43k
  CURLcode result = CURLE_OK;
1847
1.43k
  bool connected = FALSE;
1848
1849
  /* Make sure size is unknown at this point */
1850
1.43k
  data->req.size = -1;
1851
1852
  /* Set the progress data */
1853
1.43k
  Curl_pgrsReset(data);
1854
1855
  /* Carry out the perform */
1856
1.43k
  result = smtp_perform(data, smtpc, smtp, &connected, dophase_done);
1857
1858
  /* Perform post DO phase operations if necessary */
1859
1.43k
  if(!result && *dophase_done)
1860
906
    result = smtp_dophase_done(data, smtp, connected);
1861
1862
1.43k
  CURL_TRC_SMTP(data, "smtp_regular_transfer() -> %d, done=%d",
1863
1.43k
                (int)result, *dophase_done);
1864
1.43k
  return result;
1865
1.43k
}
1866
1867
/***********************************************************************
1868
 *
1869
 * smtp_do()
1870
 *
1871
 * This function is registered as 'curl_do' function. It decodes the path
1872
 * parts etc as a wrapper to the actual DO function (smtp_perform).
1873
 *
1874
 * The input argument is already checked for validity.
1875
 */
1876
static CURLcode smtp_do(struct Curl_easy *data, bool *done)
1877
1.44k
{
1878
1.44k
  struct smtp_conn *smtpc =
1879
1.44k
    Curl_conn_meta_get(data->conn, CURL_META_SMTP_CONN);
1880
1.44k
  struct SMTP *smtp = Curl_meta_get(data, CURL_META_SMTP_EASY);
1881
1.44k
  CURLcode result = CURLE_OK;
1882
1883
1.44k
  DEBUGASSERT(data);
1884
1.44k
  DEBUGASSERT(data->conn);
1885
1.44k
  *done = FALSE; /* default to false */
1886
1.44k
  if(!smtpc || !smtp)
1887
0
    return CURLE_FAILED_INIT;
1888
1889
  /* Parse the custom request */
1890
1.44k
  result = smtp_parse_custom_request(data, smtp);
1891
1.44k
  if(result)
1892
2
    return result;
1893
1894
1.43k
  result = smtp_regular_transfer(data, smtpc, smtp, done);
1895
1.43k
  CURL_TRC_SMTP(data, "smtp_do() -> %d, done=%d", (int)result, *done);
1896
1.43k
  return result;
1897
1.44k
}
1898
1899
/***********************************************************************
1900
 *
1901
 * smtp_disconnect()
1902
 *
1903
 * Disconnect from an SMTP server. Cleanup protocol-specific per-connection
1904
 * resources. BLOCKING.
1905
 */
1906
static CURLcode smtp_disconnect(struct Curl_easy *data,
1907
                                struct connectdata *conn,
1908
                                bool dead_connection)
1909
8.31k
{
1910
8.31k
  struct smtp_conn *smtpc = Curl_conn_meta_get(conn, CURL_META_SMTP_CONN);
1911
1912
8.31k
  if(!smtpc)
1913
2
    return CURLE_FAILED_INIT;
1914
1915
  /* We cannot send quit unconditionally. If this connection is stale or
1916
     bad in any way, sending quit and waiting around here will make the
1917
     disconnect wait in vain and cause more problems than we need to. */
1918
1919
8.31k
  if(!dead_connection && conn->bits.protoconnstart &&
1920
1.16k
     !Curl_pp_needs_flush(data, &smtpc->pp) &&
1921
1.16k
     !smtp_perform_quit(data, smtpc))
1922
1.16k
    (void)smtp_block_statemach(data, smtpc, TRUE); /* ignore on QUIT */
1923
1924
8.31k
  CURL_TRC_SMTP(data, "smtp_disconnect(), finished");
1925
8.31k
  return CURLE_OK;
1926
8.31k
}
1927
1928
/* Called from multi.c while DOing */
1929
static CURLcode smtp_doing(struct Curl_easy *data, bool *dophase_done)
1930
650
{
1931
650
  struct SMTP *smtp = Curl_meta_get(data, CURL_META_SMTP_EASY);
1932
650
  CURLcode result;
1933
1934
650
  if(!smtp)
1935
0
    return CURLE_FAILED_INIT;
1936
650
  result = smtp_multi_statemach(data, dophase_done);
1937
650
  if(result)
1938
239
    DEBUGF(infof(data, "DO phase failed"));
1939
411
  else if(*dophase_done) {
1940
39
    result = smtp_dophase_done(data, smtp, FALSE /* not connected */);
1941
1942
39
    DEBUGF(infof(data, "DO phase is complete"));
1943
39
  }
1944
1945
650
  CURL_TRC_SMTP(data, "smtp_doing() -> %d, done=%d", (int)result,
1946
650
                *dophase_done);
1947
650
  return result;
1948
650
}
1949
1950
static void smtp_easy_dtor(const void *key, size_t klen, void *entry)
1951
8.31k
{
1952
8.31k
  struct SMTP *smtp = entry;
1953
8.31k
  (void)key;
1954
8.31k
  (void)klen;
1955
8.31k
  curlx_free(smtp);
1956
8.31k
}
1957
1958
static void smtp_conn_dtor(const void *key, size_t klen, void *entry)
1959
8.31k
{
1960
8.31k
  struct smtp_conn *smtpc = entry;
1961
8.31k
  (void)key;
1962
8.31k
  (void)klen;
1963
8.31k
  Curl_pp_disconnect(&smtpc->pp);
1964
8.31k
  curlx_safefree(smtpc->domain);
1965
8.31k
  curlx_free(smtpc);
1966
8.31k
}
1967
1968
static CURLcode smtp_setup_connection(struct Curl_easy *data,
1969
                                      struct connectdata *conn)
1970
8.31k
{
1971
8.31k
  struct smtp_conn *smtpc;
1972
8.31k
  struct SMTP *smtp;
1973
8.31k
  CURLcode result = CURLE_OK;
1974
1975
8.31k
  smtpc = curlx_calloc(1, sizeof(*smtpc));
1976
8.31k
  if(!smtpc ||
1977
8.31k
     Curl_conn_meta_set(conn, CURL_META_SMTP_CONN, smtpc, smtp_conn_dtor)) {
1978
0
    result = CURLE_OUT_OF_MEMORY;
1979
0
    goto out;
1980
0
  }
1981
1982
8.31k
  smtp = curlx_calloc(1, sizeof(*smtp));
1983
8.31k
  if(!smtp ||
1984
8.31k
     Curl_meta_set(data, CURL_META_SMTP_EASY, smtp, smtp_easy_dtor))
1985
0
    result = CURLE_OUT_OF_MEMORY;
1986
1987
8.31k
out:
1988
8.31k
  CURL_TRC_SMTP(data, "smtp_setup_connection() -> %d", (int)result);
1989
8.31k
  return result;
1990
8.31k
}
1991
1992
/*
1993
 * SMTP protocol handler.
1994
 */
1995
const struct Curl_protocol Curl_protocol_smtp = {
1996
  smtp_setup_connection,            /* setup_connection */
1997
  smtp_do,                          /* do_it */
1998
  smtp_done,                        /* done */
1999
  ZERO_NULL,                        /* do_more */
2000
  smtp_connect,                     /* connect_it */
2001
  smtp_multi_statemach,             /* connecting */
2002
  smtp_doing,                       /* doing */
2003
  smtp_pollset,                     /* proto_pollset */
2004
  smtp_pollset,                     /* doing_pollset */
2005
  ZERO_NULL,                        /* domore_pollset */
2006
  ZERO_NULL,                        /* perform_pollset */
2007
  smtp_disconnect,                  /* disconnect */
2008
  ZERO_NULL,                        /* write_resp */
2009
  ZERO_NULL,                        /* write_resp_hd */
2010
  ZERO_NULL,                        /* connection_is_dead */
2011
  ZERO_NULL,                        /* attach connection */
2012
  ZERO_NULL,                        /* follow */
2013
};
2014
2015
#endif /* CURL_DISABLE_SMTP */