Coverage Report

Created: 2026-10-22 06:51

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
3.68k
#define CURL_META_SMTP_EASY   "meta:proto:smtp:easy"
81
/* meta key for storing protocol meta at connection */
82
16.2k
#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
427
{
147
427
  CURLcode result = CURLE_OK;
148
427
  const char *ptr = conn->options;
149
150
443
  while(!result && ptr && *ptr) {
151
16
    const char *key = ptr;
152
16
    const char *value;
153
154
232
    while(*ptr && *ptr != '=')
155
216
      ptr++;
156
157
16
    value = ptr + 1;
158
159
236
    while(*ptr && *ptr != ';')
160
220
      ptr++;
161
162
16
    if(curl_strnequal(key, "AUTH=", 5))
163
0
      result = Curl_sasl_parse_url_auth_option(&smtpc->sasl,
164
0
                                               value, ptr - value);
165
16
    else
166
16
      result = CURLE_URL_MALFORMAT;
167
168
16
    if(*ptr == ';')
169
1
      ptr++;
170
16
  }
171
172
427
  return result;
173
427
}
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
411
{
184
  /* The SMTP struct is already initialized in smtp_connect() */
185
411
  const char *path = &data->state.up.path[1]; /* skip leading path */
186
411
  char localhost[HOSTNAME_MAX + 1];
187
188
  /* Calculate the path if necessary */
189
411
  if(!*path) {
190
245
    if(!Curl_gethostname(localhost, sizeof(localhost)))
191
245
      path = localhost;
192
0
    else
193
0
      path = "localhost";
194
245
  }
195
196
  /* URL decode the path and use it as the domain in our EHLO */
197
411
  return Curl_urldecode(path, 0, &smtpc->domain, NULL, REJECT_CTRL);
198
411
}
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
282
{
209
282
  CURLcode result = CURLE_OK;
210
282
  const char *custom = CURL_EASY_STR(data, STRING_CUSTOMREQUEST);
211
212
  /* URL decode the custom request */
213
282
  if(custom)
214
19
    result = Curl_urldecode(custom, 0, &smtp->custom, NULL, REJECT_CTRL);
215
216
282
  return result;
217
282
}
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
1.06k
{
256
1.06k
  CURLcode result = CURLE_OK;
257
1.06k
  size_t length;
258
1.06k
  char *addressend;
259
1.06k
  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
1.06k
  if(strpbrk(fqma, "\r\n")) {
265
3
    failf(data, "Refusing to send email address with a CR or LF");
266
3
    return CURLE_BAD_FUNCTION_ARGUMENT;
267
3
  }
268
269
  /* Duplicate the fully qualified email address so we can manipulate it,
270
     ensuring it does not contain the delimiters if specified */
271
1.06k
  dup = curlx_strdup(fqma[0] == '<' ? fqma + 1 : fqma);
272
1.06k
  if(!dup)
273
0
    return CURLE_OUT_OF_MEMORY;
274
275
1.06k
  if(fqma[0] != '<') {
276
1.03k
    length = strlen(dup);
277
1.03k
    if(length && dup[length - 1] == '>')
278
47
      dup[length - 1] = '\0';
279
1.03k
  }
280
29
  else {
281
29
    addressend = strrchr(dup, '>');
282
29
    if(addressend) {
283
18
      *addressend = '\0';
284
18
      *suffix = addressend + 1;
285
18
    }
286
29
  }
287
288
  /* Extract the hostname from the address (if we can) */
289
1.06k
  host->name = strpbrk(dup, "@");
290
1.06k
  if(host->name) {
291
756
    *host->name = '\0';
292
756
    host->name = host->name + 1;
293
294
    /* Attempt to convert the hostname to IDN ACE */
295
756
    (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
756
  }
301
302
  /* Extract the local address from the mailbox */
303
1.06k
  *address = dup;
304
305
1.06k
  return result;
306
1.06k
}
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
135
{
322
135
  struct cr_eob_ctx *ctx = reader->ctx;
323
135
  (void)data;
324
  /* The first char we read is the first on a line, as if we had
325
   * read CRLF before */
326
135
  ctx->n_eob = 2;
327
135
  Curl_bufq_init2(&ctx->buf, (16 * 1024), 1, BUFQ_OPT_SOFT_LIMIT);
328
135
  return CURLE_OK;
329
135
}
330
331
static void cr_eob_close(struct Curl_easy *data, struct Curl_creader *reader)
332
135
{
333
135
  struct cr_eob_ctx *ctx = reader->ctx;
334
135
  (void)data;
335
135
  Curl_bufq_free(&ctx->buf);
336
135
}
337
338
/* this is the 5-bytes End-Of-Body marker for SMTP */
339
394k
#define SMTP_EOB          "\r\n.\r\n"
340
197k
#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
97
{
348
97
  struct cr_eob_ctx *ctx = reader->ctx;
349
97
  CURLcode result = CURLE_OK;
350
97
  size_t nread, i, start, n;
351
97
  bool eos;
352
353
97
  if(!ctx->read_eos && Curl_bufq_is_empty(&ctx->buf)) {
354
    /* Get more and convert it when needed */
355
91
    result = Curl_creader_read(data, reader->next, buf, blen, &nread, &eos);
356
91
    CURL_TRC_SMTP(data, "cr_eob_read, next_read(len=%zu) -> %d, %zu eos=%d",
357
91
                  blen, (int)result, nread, eos);
358
91
    if(result)
359
0
      return result;
360
361
91
    ctx->read_eos = eos;
362
91
    if(nread) {
363
90
      if(!ctx->n_eob && !memchr(buf, SMTP_EOB[0], nread)) {
364
        /* not in the middle of a match, no EOB start found, pass */
365
2
        *pnread = nread;
366
2
        *peos = FALSE;
367
2
        return CURLE_OK;
368
2
      }
369
      /* scan for EOB (continuation) and convert */
370
197k
      for(i = start = 0; i < nread; ++i) {
371
197k
        if(ctx->n_eob >= SMTP_EOB_FIND_LEN) {
372
          /* matched the EOB prefix and seeing additional char, add '.' */
373
469
          result = Curl_bufq_cwrite(&ctx->buf, buf + start, i - start, &n);
374
469
          if(result)
375
0
            return result;
376
469
          result = Curl_bufq_cwrite(&ctx->buf, ".", 1, &n);
377
469
          if(result)
378
0
            return result;
379
469
          ctx->n_eob = 0;
380
469
          start = i;
381
469
          if(data->state.infilesize > 0)
382
258
            data->state.infilesize++;
383
469
        }
384
385
197k
        if(buf[i] != SMTP_EOB[ctx->n_eob])
386
180k
          ctx->n_eob = 0;
387
388
197k
        if(buf[i] == SMTP_EOB[ctx->n_eob]) {
389
          /* matching another char of the EOB */
390
21.8k
          ++ctx->n_eob;
391
21.8k
        }
392
197k
      }
393
394
      /* add any remainder to buf */
395
88
      if(start < nread) {
396
88
        result = Curl_bufq_cwrite(&ctx->buf, buf + start, nread - start, &n);
397
88
        if(result)
398
0
          return result;
399
88
      }
400
88
    }
401
91
  }
402
403
95
  *peos = FALSE;
404
405
95
  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
68
    const char *eob = SMTP_EOB;
410
68
    CURL_TRC_SMTP(data, "auto-ending mail body with '\\r\\n.\\r\\n'");
411
68
    switch(ctx->n_eob) {
412
63
    case 2:
413
      /* seen a CRLF at the end, add the remainder */
414
63
      eob = &SMTP_EOB[2];
415
63
      break;
416
1
    case 3:
417
      /* ended with '\r\n.', we should escape the last '.' */
418
1
      eob = "." SMTP_EOB;
419
1
      break;
420
4
    default:
421
4
      break;
422
68
    }
423
68
    result = Curl_bufq_cwrite(&ctx->buf, eob, strlen(eob), &n);
424
68
    if(result)
425
0
      return result;
426
68
    ctx->processed_eos = TRUE;
427
68
  }
428
429
95
  if(!Curl_bufq_is_empty(&ctx->buf)) {
430
95
    result = Curl_bufq_cread(&ctx->buf, buf, blen, pnread);
431
95
  }
432
0
  else
433
0
    *pnread = 0;
434
435
95
  if(ctx->read_eos && Curl_bufq_is_empty(&ctx->buf)) {
436
    /* no more data, read all, done. */
437
67
    CURL_TRC_SMTP(data, "mail body complete, returning EOS");
438
67
    ctx->eos = TRUE;
439
67
  }
440
95
  *peos = (bool)ctx->eos;
441
95
  DEBUGF(infof(data, "cr_eob_read(%zu) -> %d, %zu, %d",
442
95
               blen, (int)result, *pnread, *peos));
443
95
  return result;
444
95
}
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
135
{
471
135
  struct Curl_creader *reader = NULL;
472
135
  CURLcode result;
473
474
135
  result = Curl_creader_create(&reader, data, &cr_eob, CURL_CR_CONTENT_ENCODE);
475
135
  if(!result)
476
135
    result = Curl_creader_add(data, reader);
477
478
135
  if(result && reader)
479
0
    Curl_creader_free(data, reader);
480
135
  return result;
481
135
}
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
9.60k
{
494
9.60k
  struct smtp_conn *smtpc = Curl_conn_meta_get(conn, CURL_META_SMTP_CONN);
495
9.60k
  bool end = FALSE;
496
9.60k
  (void)data;
497
498
9.60k
  DEBUGASSERT(smtpc);
499
9.60k
  if(!smtpc)
500
0
    return FALSE;
501
502
  /* Nothing for us */
503
9.60k
  if(len < 4 || !ISDIGIT(line[0]) || !ISDIGIT(line[1]) || !ISDIGIT(line[2]))
504
2.51k
    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
7.09k
  if(line[3] == ' ' || len == 5) {
511
1.91k
    char tmpline[6];
512
1.91k
    curl_off_t code;
513
1.91k
    const char *p = tmpline;
514
1.91k
    end = TRUE;
515
1.91k
    memcpy(tmpline, line, (len == 5 ? 5 : 3));
516
1.91k
    tmpline[len == 5 ? 5 : 3] = 0;
517
1.91k
    if(curlx_str_number(&p, &code, len == 5 ? 99999 : 999))
518
0
      return FALSE;
519
1.91k
    *resp = (int)code;
520
521
    /* Make sure real server never sends internal value */
522
1.91k
    if(*resp == 1)
523
6
      *resp = 0;
524
1.91k
  }
525
  /* Do we have a multiline (continuation) response? */
526
5.17k
  else if(line[3] == '-' &&
527
4.46k
          (smtpc->state == SMTP_EHLO || smtpc->state == SMTP_COMMAND)) {
528
3.58k
    end = TRUE;
529
3.58k
    *resp = 1;  /* Internal response code */
530
3.58k
  }
531
532
7.09k
  return end;
533
7.09k
}
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
0
{
543
0
  struct smtp_conn *smtpc =
544
0
    Curl_conn_meta_get(data->conn, CURL_META_SMTP_CONN);
545
0
  char *message;
546
0
  size_t len;
547
548
0
  if(!smtpc)
549
0
    return CURLE_FAILED_INIT;
550
551
0
  message = curlx_dyn_ptr(&smtpc->pp.recvbuf);
552
0
  len = smtpc->pp.nfinal;
553
0
  if(len > 4) {
554
    /* Find the start of the message */
555
0
    len -= 4;
556
0
    for(message += 4; ISBLANK(*message); message++, len--)
557
0
      ;
558
559
    /* Find the end of the message */
560
0
    while(len--)
561
0
      if(!ISNEWLINE(message[len]) && !ISBLANK(message[len]))
562
0
        break;
563
564
    /* Terminate the message */
565
0
    message[++len] = '\0';
566
0
    Curl_bufref_set(out, message, len, NULL);
567
0
  }
568
0
  else
569
    /* junk input => zero length output */
570
0
    Curl_bufref_set(out, "", 0, NULL);
571
572
0
  return CURLE_OK;
573
0
}
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
2.91k
{
585
2.91k
#ifdef CURLVERBOSE
586
  /* for debug purposes */
587
2.91k
  static const char * const names[] = {
588
2.91k
    "STOP",
589
2.91k
    "SERVERGREET",
590
2.91k
    "EHLO",
591
2.91k
    "HELO",
592
2.91k
    "STARTTLS",
593
2.91k
    "UPGRADETLS",
594
2.91k
    "AUTH",
595
2.91k
    "COMMAND",
596
2.91k
    "MAIL",
597
2.91k
    "RCPT",
598
2.91k
    "DATA",
599
2.91k
    "POSTDATA",
600
2.91k
    "QUIT",
601
    /* LAST */
602
2.91k
  };
603
604
2.91k
  if(smtpc->state != newstate)
605
2.00k
    CURL_TRC_SMTP(data, "state change from %s to %s",
606
2.91k
                  names[smtpc->state], names[newstate]);
607
#else
608
  (void)data;
609
#endif
610
611
2.91k
  smtpc->state = newstate;
612
2.91k
}
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
331
{
624
331
  CURLcode result = CURLE_OK;
625
626
331
  smtpc->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanism yet */
627
331
  smtpc->sasl.authused = SASL_AUTH_NONE;  /* Clear the authentication mechanism
628
                                             used for esmtp connections */
629
331
  smtpc->tls_supported = FALSE;           /* Clear the TLS capability */
630
331
  smtpc->auth_supported = FALSE;          /* Clear the AUTH capability */
631
632
  /* Send the EHLO command */
633
331
  result = Curl_pp_sendf(data, &smtpc->pp, "EHLO %s", smtpc->domain);
634
635
331
  if(!result)
636
331
    smtp_state(data, smtpc, SMTP_EHLO);
637
638
331
  return result;
639
331
}
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
30
{
650
30
  CURLcode result = CURLE_OK;
651
652
30
  smtpc->sasl.authused = SASL_AUTH_NONE; /* No authentication mechanism used
653
                                            in smtp connections */
654
655
  /* Send the HELO command */
656
30
  result = Curl_pp_sendf(data, &smtpc->pp, "HELO %s", smtpc->domain);
657
658
30
  if(!result)
659
30
    smtp_state(data, smtpc, SMTP_HELO);
660
661
30
  return result;
662
30
}
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
0
{
673
  /* Send the STARTTLS command */
674
0
  CURLcode result = Curl_pp_sendf(data, &smtpc->pp, "%s", "STARTTLS");
675
676
0
  if(!result)
677
0
    smtp_state(data, smtpc, SMTP_STARTTLS);
678
679
0
  return result;
680
0
}
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
0
{
736
0
  CURLcode result = CURLE_OK;
737
0
  struct smtp_conn *smtpc =
738
0
    Curl_conn_meta_get(data->conn, CURL_META_SMTP_CONN);
739
0
  const char *ir = Curl_bufref_ptr(initresp);
740
741
0
  if(!smtpc)
742
0
    return CURLE_FAILED_INIT;
743
744
0
  if(ir) {                                  /* AUTH <mech> ...<crlf> */
745
    /* Send the AUTH command with the initial response */
746
0
    result = Curl_pp_sendf(data, &smtpc->pp, "AUTH %s %s",
747
0
                           mech, *ir ? ir : "=");
748
0
  }
749
0
  else {
750
    /* Send the AUTH command */
751
0
    result = Curl_pp_sendf(data, &smtpc->pp, "AUTH %s", mech);
752
0
  }
753
754
0
  return result;
755
0
}
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
0
{
767
0
  struct smtp_conn *smtpc =
768
0
    Curl_conn_meta_get(data->conn, CURL_META_SMTP_CONN);
769
770
0
  (void)mech;
771
0
  if(!smtpc)
772
0
    return CURLE_FAILED_INIT;
773
0
  return Curl_pp_sendf(data, &smtpc->pp, "%s", Curl_bufref_ptr(resp));
774
0
}
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
0
{
784
0
  struct smtp_conn *smtpc =
785
0
    Curl_conn_meta_get(data->conn, CURL_META_SMTP_CONN);
786
787
0
  (void)mech;
788
0
  if(!smtpc)
789
0
    return CURLE_FAILED_INIT;
790
0
  return Curl_pp_sendf(data, &smtpc->pp, "*");
791
0
}
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
260
{
803
260
  CURLcode result = CURLE_OK;
804
260
  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
260
  if(!smtpc->auth_supported ||
809
260
     !Curl_sasl_can_authenticate(&smtpc->sasl, data)) {
810
260
    smtp_state(data, smtpc, SMTP_STOP);
811
260
    return result;
812
260
  }
813
814
  /* Calculate the SASL login details */
815
0
  result = Curl_sasl_start(&smtpc->sasl, data, FALSE, &progress);
816
817
0
  if(!result) {
818
0
    if(progress == SASL_INPROGRESS)
819
0
      smtp_state(data, smtpc, SMTP_AUTH);
820
0
    else
821
0
      result = Curl_sasl_is_blocked(&smtpc->sasl, data);
822
0
  }
823
824
0
  return result;
825
260
}
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
808
{
837
808
  CURLcode result = CURLE_OK;
838
839
808
  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
761
    bool utf8 = FALSE;
845
846
761
    if(!smtp->custom || !smtp->custom[0]) {
847
694
      char *address = NULL;
848
694
      struct hostname host = { NULL, NULL, NULL, NULL };
849
694
      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
694
      result = smtp_parse_address(data, smtp->rcpt->data,
854
694
                                  &address, &host, &suffix);
855
694
      if(result)
856
1
        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
693
      utf8 = smtpc->utf8_supported &&
861
0
             (host.encalloc ||
862
0
              !Curl_is_ASCII_name(address) ||
863
0
              !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
693
      result = Curl_pp_sendf(data, &smtpc->pp, "VRFY %s%s%s%s",
868
693
                             address,
869
693
                             host.name ? "@" : "",
870
693
                             host.name ? host.name : "",
871
693
                             utf8 ? " SMTPUTF8" : "");
872
873
693
      Curl_free_idnconverted_hostname(&host);
874
693
      curlx_free(address);
875
693
    }
876
67
    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
67
      utf8 = smtpc->utf8_supported && !strcmp(smtp->custom, "EXPN");
880
881
      /* Send the custom recipient based command such as the EXPN command */
882
67
      result = Curl_pp_sendf(data, &smtpc->pp,
883
67
                             "%s %s%s", smtp->custom,
884
67
                             smtp->rcpt->data,
885
67
                             utf8 ? " SMTPUTF8" : "");
886
67
    }
887
761
  }
888
47
  else
889
    /* Send the non-recipient based command such as HELP */
890
47
    result = Curl_pp_sendf(data, &smtpc->pp, "%s",
891
47
                           smtp->custom && smtp->custom[0] != '\0' ?
892
46
                           smtp->custom : "HELP");
893
894
807
  if(!result)
895
807
    smtp_state(data, smtpc, SMTP_COMMAND);
896
897
807
  return result;
898
808
}
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
137
{
910
137
  char *from = NULL;
911
137
  char *auth = NULL;
912
137
  char *size = NULL;
913
137
  const char *str;
914
137
  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
137
  bool utf8 = FALSE;
921
922
  /* Calculate the FROM parameter */
923
137
  str = CURL_EASY_STR(data, STRING_MAIL_FROM);
924
137
  if(str) {
925
9
    char *address = NULL;
926
9
    struct hostname host = { NULL, NULL, NULL, NULL };
927
9
    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
9
    result = smtp_parse_address(data, str, &address, &host, &suffix);
932
9
    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
8
    utf8 = smtpc->utf8_supported &&
938
0
           (host.encalloc ||
939
0
            !Curl_is_ASCII_name(address) ||
940
0
            !Curl_is_ASCII_name(host.name));
941
942
8
    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
6
    else
948
      /* An invalid mailbox was provided but we let the server worry
949
         about that and reply with a 501 error */
950
6
      from = curl_maprintf("<%s>%s", address, suffix);
951
952
8
    curlx_free(address);
953
8
  }
954
128
  else
955
    /* Null reverse-path, RFC-5321, sect. 3.6.3 */
956
128
    from = curlx_strdup("<>");
957
958
136
  if(!from) {
959
0
    result = CURLE_OUT_OF_MEMORY;
960
0
    goto out;
961
0
  }
962
963
  /* Calculate the optional AUTH parameter */
964
136
  str = CURL_EASY_STR(data, STRING_MAIL_AUTH);
965
136
  if(str && smtpc->sasl.authused) {
966
0
    if(str[0] != '\0') {
967
0
      char *address = NULL;
968
0
      struct hostname host = { NULL, NULL, NULL, NULL };
969
0
      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
0
      result = smtp_parse_address(data, str, &address, &host, &suffix);
974
0
      if(result)
975
0
        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
0
      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
0
      if(host.name) {
986
0
        auth = curl_maprintf("<%s@%s>%s", address, host.name, suffix);
987
988
0
        Curl_free_idnconverted_hostname(&host);
989
0
      }
990
0
      else
991
        /* An invalid mailbox was provided but we let the server worry
992
           about it */
993
0
        auth = curl_maprintf("<%s>%s", address, suffix);
994
0
      curlx_free(address);
995
0
    }
996
0
    else
997
      /* Empty AUTH, RFC-2554, sect. 5 */
998
0
      auth = curlx_strdup("<>");
999
1000
0
    if(!auth) {
1001
0
      result = CURLE_OUT_OF_MEMORY;
1002
0
      goto out;
1003
0
    }
1004
0
  }
1005
1006
136
#ifndef CURL_DISABLE_MIME
1007
  /* Prepare the mime data if some. */
1008
136
  if(IS_MIME_POST(data)) {
1009
114
    curl_mimepart *postp = data->set.mimepostp;
1010
1011
    /* Use the whole structure as data. */
1012
114
    postp->flags &= ~(unsigned int)MIME_BODY_ONLY;
1013
1014
    /* Add external headers and mime version. */
1015
114
    curl_mime_headers(postp, data->set.headers, 0);
1016
114
    result = Curl_mime_prepare_headers(data, postp, NULL,
1017
114
                                       NULL, MIMESTRATEGY_MAIL);
1018
1019
114
    if(!result && !Curl_checkheaders(data, STRCONST("Mime-Version")))
1020
113
      result = Curl_mime_add_header(&postp->curlheaders, "Mime-Version: 1.0");
1021
114
    if(!result)
1022
113
      result = Curl_creader_set_mime(data, postp);
1023
114
    if(result)
1024
1
      goto out;
1025
113
    data->state.infilesize = Curl_creader_total_length(data);
1026
113
  }
1027
22
  else
1028
22
#endif
1029
22
  {
1030
22
    result = Curl_creader_set_fread(data, data->state.infilesize);
1031
22
    if(result)
1032
0
      goto out;
1033
22
  }
1034
1035
  /* Calculate the optional SIZE parameter */
1036
135
  if(smtpc->size_supported && data->state.infilesize > 0) {
1037
35
    size = curl_maprintf("%" FMT_OFF_T, data->state.infilesize);
1038
1039
35
    if(!size) {
1040
0
      result = CURLE_OUT_OF_MEMORY;
1041
0
      goto out;
1042
0
    }
1043
35
  }
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
135
  if(smtpc->utf8_supported && !utf8) {
1050
0
    struct curl_slist *rcpt = smtp->rcpt;
1051
1052
0
    while(rcpt && !utf8) {
1053
      /* Does the hostname contain non-ASCII characters? */
1054
0
      if(!Curl_is_ASCII_name(rcpt->data))
1055
0
        utf8 = TRUE;
1056
1057
0
      rcpt = rcpt->next;
1058
0
    }
1059
0
  }
1060
1061
  /* Add the client reader doing STMP EOB escaping */
1062
135
  result = cr_eob_add(data);
1063
135
  if(result)
1064
0
    goto out;
1065
1066
  /* Send the MAIL command */
1067
135
  result = Curl_pp_sendf(data, &smtpc->pp,
1068
135
                         "MAIL FROM:%s%s%s%s%s%s",
1069
135
                         from,                 /* Mandatory                 */
1070
135
                         auth ? " AUTH=" : "", /* Optional on AUTH support  */
1071
135
                         auth ? auth : "",
1072
135
                         size ? " SIZE=" : "", /* Optional on SIZE support  */
1073
135
                         size ? size : "",
1074
135
                         utf8 ? " SMTPUTF8"    /* Internationalised mailbox */
1075
135
                               : "");          /* included in our envelope  */
1076
1077
137
out:
1078
137
  curlx_free(from);
1079
137
  curlx_free(auth);
1080
137
  curlx_free(size);
1081
1082
137
  if(!result)
1083
135
    smtp_state(data, smtpc, SMTP_MAIL);
1084
1085
137
  return result;
1086
135
}
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
365
{
1099
365
  CURLcode result = CURLE_OK;
1100
365
  char *address = NULL;
1101
365
  struct hostname host = { NULL, NULL, NULL, NULL };
1102
365
  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
365
  result = smtp_parse_address(data, smtp->rcpt->data,
1107
365
                              &address, &host, &suffix);
1108
365
  if(result)
1109
1
    return result;
1110
1111
  /* Send the RCPT TO command */
1112
364
  if(host.name)
1113
241
    result = Curl_pp_sendf(data, &smtpc->pp, "RCPT TO:<%s@%s>%s",
1114
241
                           address, host.name, suffix);
1115
123
  else
1116
    /* An invalid mailbox was provided but we let the server worry about
1117
       that and reply with a 501 error */
1118
123
    result = Curl_pp_sendf(data, &smtpc->pp, "RCPT TO:<%s>%s",
1119
123
                           address, suffix);
1120
1121
364
  Curl_free_idnconverted_hostname(&host);
1122
364
  curlx_free(address);
1123
1124
364
  if(!result)
1125
364
    smtp_state(data, smtpc, SMTP_RCPT);
1126
1127
364
  return result;
1128
365
}
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
258
{
1139
  /* Send the QUIT command */
1140
258
  CURLcode result = Curl_pp_sendf(data, &smtpc->pp, "%s", "QUIT");
1141
1142
258
  if(!result)
1143
258
    smtp_state(data, smtpc, SMTP_QUIT);
1144
1145
258
  return result;
1146
258
}
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
333
{
1154
333
  CURLcode result = CURLE_OK;
1155
333
  (void)instate;
1156
1157
333
  if(smtpcode / 100 != 2) {
1158
2
    failf(data, "Got unexpected smtp-server response: %d", smtpcode);
1159
2
    result = CURLE_WEIRD_SERVER_REPLY;
1160
2
  }
1161
331
  else
1162
331
    result = smtp_perform_ehlo(data, smtpc);
1163
1164
333
  return result;
1165
333
}
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
0
{
1173
0
  CURLcode result = CURLE_OK;
1174
0
  (void)instate;
1175
1176
  /* Pipelining in response is forbidden. */
1177
0
  if(smtpc->pp.overflow)
1178
0
    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
0
}
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
2.00k
{
1200
2.00k
  CURLcode result = CURLE_OK;
1201
2.00k
  const char *line = curlx_dyn_ptr(&smtpc->pp.recvbuf);
1202
2.00k
  size_t len = smtpc->pp.nfinal;
1203
1204
2.00k
  (void)instate;
1205
1206
2.00k
  if(smtpcode / 100 != 2 && smtpcode != 1) {
1207
31
    if(data->set.use_ssl <= CURLUSESSL_TRY ||
1208
1
       Curl_conn_is_ssl(data->conn, FIRSTSOCKET))
1209
30
      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
31
  }
1215
1.97k
  else if(len >= 4) {
1216
1.97k
    line += 4;
1217
1.97k
    len -= 4;
1218
1219
    /* Does the server support the STARTTLS capability? */
1220
1.97k
    if(len >= 8 && curl_strnequal(line, "STARTTLS", 8))
1221
0
      smtpc->tls_supported = TRUE;
1222
1223
    /* Does the server support the SIZE capability? */
1224
1.97k
    else if(len >= 4 && curl_strnequal(line, "SIZE", 4))
1225
51
      smtpc->size_supported = TRUE;
1226
1227
    /* Does the server support the UTF-8 capability? */
1228
1.92k
    else if(len >= 8 && curl_strnequal(line, "SMTPUTF8", 8))
1229
0
      smtpc->utf8_supported = TRUE;
1230
1231
    /* Does the server support authentication? */
1232
1.92k
    else if(len >= 5 && curl_strnequal(line, "AUTH ", 5)) {
1233
0
      smtpc->auth_supported = TRUE;
1234
1235
      /* Advance past the AUTH keyword */
1236
0
      line += 5;
1237
0
      len -= 5;
1238
1239
      /* Loop through the data line */
1240
0
      for(;;) {
1241
0
        size_t llen;
1242
0
        size_t wordlen;
1243
0
        unsigned short mechbit;
1244
1245
0
        while(len && (ISBLANK(*line) || ISNEWLINE(*line))) {
1246
0
          line++;
1247
0
          len--;
1248
0
        }
1249
1250
0
        if(!len)
1251
0
          break;
1252
1253
        /* Extract the word */
1254
0
        for(wordlen = 0; wordlen < len && !ISBLANK(line[wordlen]) &&
1255
0
              !ISNEWLINE(line[wordlen]);)
1256
0
          wordlen++;
1257
1258
        /* Test the word for a matching authentication mechanism */
1259
0
        mechbit = Curl_sasl_decode_mech(line, wordlen, &llen);
1260
0
        if(mechbit && llen == wordlen)
1261
0
          smtpc->sasl.authmechs |= mechbit;
1262
1263
0
        line += wordlen;
1264
0
        len -= wordlen;
1265
0
      }
1266
0
    }
1267
1268
1.97k
    if(smtpcode != 1) {
1269
261
      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
2
        if(smtpc->tls_supported)
1272
          /* Switch to TLS connection now */
1273
0
          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
2
      }
1282
259
      else
1283
259
        result = smtp_perform_authentication(data, smtpc);
1284
261
    }
1285
1.97k
  }
1286
0
  else {
1287
0
    failf(data, "Unexpectedly short EHLO response");
1288
0
    result = CURLE_WEIRD_SERVER_REPLY;
1289
0
  }
1290
1291
2.00k
  return result;
1292
2.00k
}
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
24
{
1300
24
  CURLcode result = CURLE_OK;
1301
24
  (void)instate;
1302
1303
24
  if(smtpcode / 100 != 2) {
1304
2
    failf(data, "Remote access denied: %d", smtpcode);
1305
2
    result = CURLE_REMOTE_ACCESS_DENIED;
1306
2
  }
1307
22
  else
1308
    /* End of connect phase */
1309
22
    smtp_state(data, smtpc, SMTP_STOP);
1310
1311
24
  return result;
1312
24
}
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
0
{
1320
0
  CURLcode result = CURLE_OK;
1321
0
  saslprogress progress;
1322
1323
0
  (void)instate;
1324
1325
0
  result = Curl_sasl_continue(&smtpc->sasl, data, smtpcode, &progress);
1326
0
  if(!result)
1327
0
    switch(progress) {
1328
0
    case SASL_DONE:
1329
0
      smtp_state(data, smtpc, SMTP_STOP);  /* Authenticated */
1330
0
      break;
1331
0
    case SASL_IDLE:            /* No mechanism left after cancellation */
1332
0
      failf(data, "Authentication cancelled");
1333
0
      result = CURLE_LOGIN_DENIED;
1334
0
      break;
1335
0
    default:
1336
0
      break;
1337
0
    }
1338
1339
0
  return result;
1340
0
}
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
2.57k
{
1349
2.57k
  CURLcode result = CURLE_OK;
1350
2.57k
  const char *line = curlx_dyn_ptr(&smtpc->pp.recvbuf);
1351
2.57k
  size_t len = smtpc->pp.nfinal;
1352
1353
2.57k
  (void)instate;
1354
1355
2.57k
  if((smtp->rcpt && smtpcode / 100 != 2 && smtpcode != 553 && smtpcode != 1) ||
1356
2.57k
     (!smtp->rcpt && smtpcode / 100 != 2 && smtpcode != 1)) {
1357
6
    failf(data, "Command failed: %d", smtpcode);
1358
6
    result = CURLE_WEIRD_SERVER_REPLY;
1359
6
  }
1360
2.57k
  else {
1361
2.57k
    if(!data->req.no_body)
1362
2.30k
      result = Curl_client_write(data, CLIENTWRITE_BODY, line, len);
1363
1364
2.57k
    if(!result && (smtpcode != 1)) {
1365
698
      if(smtp->rcpt) {
1366
691
        smtp->rcpt = smtp->rcpt->next;
1367
1368
691
        if(smtp->rcpt) {
1369
          /* Send the next command */
1370
664
          result = smtp_perform_command(data, smtpc, smtp);
1371
664
        }
1372
27
        else
1373
          /* End of DO phase */
1374
27
          smtp_state(data, smtpc, SMTP_STOP);
1375
691
      }
1376
7
      else
1377
        /* End of DO phase */
1378
7
        smtp_state(data, smtpc, SMTP_STOP);
1379
698
    }
1380
2.57k
  }
1381
1382
2.57k
  return result;
1383
2.57k
}
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
122
{
1392
122
  CURLcode result = CURLE_OK;
1393
122
  (void)instate;
1394
1395
122
  if(smtpcode / 100 != 2) {
1396
4
    failf(data, "MAIL failed: %d", smtpcode);
1397
4
    result = CURLE_SEND_ERROR;
1398
4
  }
1399
118
  else
1400
    /* Start the RCPT TO command */
1401
118
    result = smtp_perform_rcpt_to(data, smtpc, smtp);
1402
1403
122
  return result;
1404
122
}
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
348
{
1413
348
  CURLcode result = CURLE_OK;
1414
348
  bool is_smtp_err = FALSE;
1415
348
  bool is_smtp_blocking_err = FALSE;
1416
1417
348
  (void)instate;
1418
1419
348
  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
348
  is_smtp_blocking_err = (is_smtp_err && !data->set.mail_rcpt_allowfails);
1424
1425
348
  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
19
    smtp->rcpt_last_error = smtpcode;
1429
1430
19
    if(is_smtp_blocking_err) {
1431
2
      failf(data, "RCPT failed: %d", smtpcode);
1432
2
      result = CURLE_SEND_ERROR;
1433
2
    }
1434
19
  }
1435
329
  else {
1436
    /* Some RCPT TO commands have succeeded. */
1437
329
    smtp->rcpt_had_ok = TRUE;
1438
329
  }
1439
1440
348
  if(!is_smtp_blocking_err) {
1441
346
    smtp->rcpt = smtp->rcpt->next;
1442
1443
346
    if(smtp->rcpt)
1444
      /* Send the next RCPT TO command */
1445
247
      result = smtp_perform_rcpt_to(data, smtpc, smtp);
1446
99
    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
99
      if(!smtp->rcpt_had_ok) {
1450
1
        failf(data, "RCPT failed: %d (last error)", smtp->rcpt_last_error);
1451
1
        result = CURLE_SEND_ERROR;
1452
1
      }
1453
98
      else {
1454
        /* Send the DATA command */
1455
98
        result = Curl_pp_sendf(data, &smtpc->pp, "%s", "DATA");
1456
1457
98
        if(!result)
1458
98
          smtp_state(data, smtpc, SMTP_DATA);
1459
98
      }
1460
99
    }
1461
346
  }
1462
1463
348
  return result;
1464
348
}
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
86
{
1472
86
  CURLcode result = CURLE_OK;
1473
86
  (void)instate;
1474
1475
86
  if(smtpcode != 354) {
1476
7
    failf(data, "DATA failed: %d", smtpcode);
1477
7
    result = CURLE_SEND_ERROR;
1478
7
  }
1479
79
  else {
1480
    /* Set the progress upload size */
1481
79
    Curl_pgrsSetUploadSize(data, data->state.infilesize);
1482
1483
    /* SMTP upload */
1484
79
    Curl_xfer_setup_send(data, FIRSTSOCKET);
1485
1486
    /* End of DO phase */
1487
79
    smtp_state(data, smtpc, SMTP_STOP);
1488
79
  }
1489
1490
86
  return result;
1491
86
}
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
2
{
1500
2
  CURLcode result = CURLE_OK;
1501
1502
2
  (void)instate;
1503
1504
2
  if(smtpcode != 250)
1505
1
    result = CURLE_WEIRD_SERVER_REPLY;
1506
1507
  /* End of DONE phase */
1508
2
  smtp_state(data, smtpc, SMTP_STOP);
1509
1510
2
  return result;
1511
2
}
1512
1513
static CURLcode smtp_pp_statemachine(struct Curl_easy *data,
1514
                                     struct connectdata *conn)
1515
1.79k
{
1516
1.79k
  CURLcode result = CURLE_OK;
1517
1.79k
  int smtpcode;
1518
1.79k
  struct smtp_conn *smtpc = Curl_conn_meta_get(conn, CURL_META_SMTP_CONN);
1519
1.79k
  struct SMTP *smtp = Curl_meta_get(data, CURL_META_SMTP_EASY);
1520
1.79k
  size_t nread = 0;
1521
1522
1.79k
  if(!smtpc || !smtp)
1523
253
    return CURLE_FAILED_INIT;
1524
1525
  /* Busy upgrading the connection; right now all I/O is SSL/TLS, not SMTP */
1526
1.53k
upgrade_tls:
1527
1.53k
  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.53k
  if(smtpc->pp.sendleft)
1535
0
    return Curl_pp_flushsend(data, &smtpc->pp);
1536
1537
6.04k
  do {
1538
    /* Read the response from the server */
1539
6.04k
    result = Curl_pp_readresp(data, FIRSTSOCKET, &smtpc->pp,
1540
6.04k
                              &smtpcode, &nread);
1541
6.04k
    if(result)
1542
317
      return result;
1543
1544
    /* Store the latest response for later retrieval if necessary */
1545
5.72k
    if(smtpc->state != SMTP_QUIT && smtpcode != 1)
1546
2.14k
      data->info.httpcode = smtpcode;
1547
1548
5.72k
    if(!smtpcode)
1549
229
      break;
1550
1551
    /* We have now received a full SMTP server response */
1552
5.49k
    switch(smtpc->state) {
1553
333
    case SMTP_SERVERGREET:
1554
333
      result = smtp_state_servergreet_resp(data, smtpc,
1555
333
                                           smtpcode, smtpc->state);
1556
333
      break;
1557
1558
2.00k
    case SMTP_EHLO:
1559
2.00k
      result = smtp_state_ehlo_resp(data, smtpc, smtpcode, smtpc->state);
1560
2.00k
      break;
1561
1562
24
    case SMTP_HELO:
1563
24
      result = smtp_state_helo_resp(data, smtpc, smtpcode, smtpc->state);
1564
24
      break;
1565
1566
0
    case SMTP_STARTTLS:
1567
0
      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
0
      if(!result && (smtpc->state == SMTP_UPGRADETLS))
1571
0
        goto upgrade_tls;
1572
0
      break;
1573
1574
0
    case SMTP_AUTH:
1575
0
      result = smtp_state_auth_resp(data, smtpc, smtpcode, smtpc->state);
1576
0
      break;
1577
1578
2.57k
    case SMTP_COMMAND:
1579
2.57k
      result = smtp_state_command_resp(data, smtpc, smtp,
1580
2.57k
                                       smtpcode, smtpc->state);
1581
2.57k
      break;
1582
1583
122
    case SMTP_MAIL:
1584
122
      result = smtp_state_mail_resp(data, smtpc, smtp, smtpcode, smtpc->state);
1585
122
      break;
1586
1587
348
    case SMTP_RCPT:
1588
348
      result = smtp_state_rcpt_resp(data, smtpc, smtp, smtpcode, smtpc->state);
1589
348
      break;
1590
1591
86
    case SMTP_DATA:
1592
86
      result = smtp_state_data_resp(data, smtpc, smtpcode, smtpc->state);
1593
86
      break;
1594
1595
2
    case SMTP_POSTDATA:
1596
2
      result = smtp_state_postdata_resp(data, smtpc, smtpcode, smtpc->state);
1597
2
      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
5.49k
    }
1605
5.49k
  } while(!result && smtpc->state != SMTP_STOP &&
1606
5.07k
          Curl_pp_moredata(&smtpc->pp));
1607
1608
1.22k
  return result;
1609
1.53k
}
1610
1611
/* Called repeatedly until done from multi.c */
1612
static CURLcode smtp_multi_statemach(struct Curl_easy *data, bool *done)
1613
2.02k
{
1614
2.02k
  CURLcode result = CURLE_OK;
1615
2.02k
  struct smtp_conn *smtpc =
1616
2.02k
    Curl_conn_meta_get(data->conn, CURL_META_SMTP_CONN);
1617
1618
2.02k
  *done = FALSE;
1619
2.02k
  if(!smtpc)
1620
0
    return CURLE_FAILED_INIT;
1621
1622
2.02k
  result = Curl_pp_statemach(data, &smtpc->pp, FALSE, FALSE);
1623
2.02k
  *done = (smtpc->state == SMTP_STOP);
1624
2.02k
  return result;
1625
2.02k
}
1626
1627
static CURLcode smtp_block_statemach(struct Curl_easy *data,
1628
                                     struct smtp_conn *smtpc,
1629
                                     bool disconnecting)
1630
345
{
1631
345
  CURLcode result = CURLE_OK;
1632
1633
706
  while(smtpc->state != SMTP_STOP && !result)
1634
361
    result = Curl_pp_statemach(data, &smtpc->pp, TRUE, disconnecting);
1635
1636
345
  return result;
1637
345
}
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
821
{
1643
821
  struct smtp_conn *smtpc =
1644
821
    Curl_conn_meta_get(data->conn, CURL_META_SMTP_CONN);
1645
821
  return smtpc ? Curl_pp_pollset(data, &smtpc->pp, ps) : CURLE_OK;
1646
821
}
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
427
{
1674
427
  struct smtp_conn *smtpc =
1675
427
    Curl_conn_meta_get(data->conn, CURL_META_SMTP_CONN);
1676
427
  CURLcode result = CURLE_OK;
1677
1678
427
  *done = FALSE; /* default to not done yet */
1679
427
  if(!smtpc)
1680
0
    return CURLE_FAILED_INIT;
1681
1682
427
  PINGPONG_SETUP(&smtpc->pp, smtp_pp_statemachine, smtp_endofresp);
1683
1684
  /* Initialize the SASL storage */
1685
427
  Curl_sasl_init(&smtpc->sasl, data, &saslsmtp);
1686
1687
  /* Initialize the pingpong layer */
1688
427
  Curl_pp_init(&smtpc->pp, Curl_pgrs_now(data));
1689
1690
  /* Parse the URL options */
1691
427
  result = smtp_parse_url_options(data->conn, smtpc);
1692
427
  if(result)
1693
16
    return result;
1694
1695
  /* Parse the URL path */
1696
411
  result = smtp_parse_url_path(data, smtpc);
1697
411
  if(result)
1698
1
    return result;
1699
1700
  /* Start off waiting for the server greeting response */
1701
410
  smtp_state(data, smtpc, SMTP_SERVERGREET);
1702
1703
410
  result = smtp_multi_statemach(data, done);
1704
1705
410
  return result;
1706
411
}
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
427
{
1720
427
  struct smtp_conn *smtpc =
1721
427
    Curl_conn_meta_get(data->conn, CURL_META_SMTP_CONN);
1722
427
  CURLcode result = CURLE_OK;
1723
427
  struct connectdata *conn = data->conn;
1724
427
  struct SMTP *smtp = Curl_meta_get(data, CURL_META_SMTP_EASY);
1725
1726
427
  (void)premature;
1727
1728
427
  if(!smtpc)
1729
0
    return CURLE_FAILED_INIT;
1730
427
  if(!smtp)
1731
0
    return CURLE_OK;
1732
1733
  /* Cleanup our per-request based variables */
1734
427
  curlx_safefree(smtp->custom);
1735
1736
427
  if(status) {
1737
290
    CURL_TRC_M(data, "SMTP done with bad status");
1738
290
    connclose(conn); /* marked for closure */
1739
290
    result = status;         /* use the already set error code */
1740
290
  }
1741
137
  else if(!data->set.connect_only && data->set.mail_rcpt &&
1742
116
          (data->state.upload || IS_MIME_POST(data))) {
1743
1744
87
    smtp_state(data, smtpc, SMTP_POSTDATA);
1745
1746
    /* Run the state-machine */
1747
87
    result = smtp_block_statemach(data, smtpc, FALSE);
1748
87
  }
1749
1750
  /* Clear the transfer mode for the next request */
1751
427
  smtp->transfer = PPTRANSFER_BODY;
1752
427
  CURL_TRC_SMTP(data, "smtp_done(status=%d, premature=%d) -> %d",
1753
427
                (int)status, premature, (int)result);
1754
427
  return result;
1755
427
}
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
281
{
1770
  /* This is SMTP and no proxy */
1771
281
  CURLcode result = CURLE_OK;
1772
1773
281
  CURL_TRC_SMTP(data, "smtp_perform(), start");
1774
1775
281
  if(data->req.no_body) {
1776
    /* Requested no body means no transfer */
1777
12
    smtp->transfer = PPTRANSFER_INFO;
1778
12
  }
1779
1780
281
  *dophase_done = FALSE; /* not done yet */
1781
1782
  /* Store the first recipient (or NULL if not specified) */
1783
281
  smtp->rcpt = data->set.mail_rcpt;
1784
1785
  /* Track of whether we have successfully sent at least one RCPT TO command */
1786
281
  smtp->rcpt_had_ok = FALSE;
1787
1788
  /* Track of the last error we have received by sending RCPT TO command */
1789
281
  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
281
  smtp->trailing_crlf = TRUE;
1794
281
  smtp->eob = 2;
1795
1796
  /* Start the first command in the DO phase */
1797
281
  if((data->state.upload || IS_MIME_POST(data)) && data->set.mail_rcpt)
1798
    /* MAIL transfer */
1799
137
    result = smtp_perform_mail(data, smtpc, smtp);
1800
144
  else
1801
    /* SMTP based command (VRFY, EXPN, NOOP, RSET or HELP) */
1802
144
    result = smtp_perform_command(data, smtpc, smtp);
1803
1804
281
  if(result)
1805
3
    goto out;
1806
1807
  /* Run the state-machine */
1808
278
  result = smtp_multi_statemach(data, dophase_done);
1809
1810
278
  *connected = Curl_conn_is_connected(data->conn, FIRSTSOCKET);
1811
1812
281
out:
1813
281
  CURL_TRC_SMTP(data, "smtp_perform() -> %d, connected=%d, done=%d",
1814
281
                (int)result, *connected, *dophase_done);
1815
281
  return result;
1816
278
}
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
113
{
1823
113
  (void)connected;
1824
1825
113
  if(smtp->transfer != PPTRANSFER_BODY)
1826
    /* no data to transfer */
1827
4
    Curl_xfer_setup_nop(data);
1828
1829
113
  return CURLE_OK;
1830
113
}
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
281
{
1846
281
  CURLcode result = CURLE_OK;
1847
281
  bool connected = FALSE;
1848
1849
  /* Make sure size is unknown at this point */
1850
281
  data->req.size = -1;
1851
1852
  /* Set the progress data */
1853
281
  Curl_pgrsReset(data);
1854
1855
  /* Carry out the perform */
1856
281
  result = smtp_perform(data, smtpc, smtp, &connected, dophase_done);
1857
1858
  /* Perform post DO phase operations if necessary */
1859
281
  if(!result && *dophase_done)
1860
20
    result = smtp_dophase_done(data, smtp, connected);
1861
1862
281
  CURL_TRC_SMTP(data, "smtp_regular_transfer() -> %d, done=%d",
1863
281
                (int)result, *dophase_done);
1864
281
  return result;
1865
281
}
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
282
{
1878
282
  struct smtp_conn *smtpc =
1879
282
    Curl_conn_meta_get(data->conn, CURL_META_SMTP_CONN);
1880
282
  struct SMTP *smtp = Curl_meta_get(data, CURL_META_SMTP_EASY);
1881
282
  CURLcode result = CURLE_OK;
1882
1883
282
  DEBUGASSERT(data);
1884
282
  DEBUGASSERT(data->conn);
1885
282
  *done = FALSE; /* default to false */
1886
282
  if(!smtpc || !smtp)
1887
0
    return CURLE_FAILED_INIT;
1888
1889
  /* Parse the custom request */
1890
282
  result = smtp_parse_custom_request(data, smtp);
1891
282
  if(result)
1892
1
    return result;
1893
1894
281
  result = smtp_regular_transfer(data, smtpc, smtp, done);
1895
281
  CURL_TRC_SMTP(data, "smtp_do() -> %d, done=%d", (int)result, *done);
1896
281
  return result;
1897
282
}
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
440
{
1910
440
  struct smtp_conn *smtpc = Curl_conn_meta_get(conn, CURL_META_SMTP_CONN);
1911
1912
440
  if(!smtpc)
1913
1
    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
439
  if(!dead_connection && conn->bits.protoconnstart &&
1920
258
     !Curl_pp_needs_flush(data, &smtpc->pp) &&
1921
258
     !smtp_perform_quit(data, smtpc))
1922
258
    (void)smtp_block_statemach(data, smtpc, TRUE); /* ignore on QUIT */
1923
1924
439
  CURL_TRC_SMTP(data, "smtp_disconnect(), finished");
1925
439
  return CURLE_OK;
1926
440
}
1927
1928
/* Called from multi.c while DOing */
1929
static CURLcode smtp_doing(struct Curl_easy *data, bool *dophase_done)
1930
748
{
1931
748
  struct SMTP *smtp = Curl_meta_get(data, CURL_META_SMTP_EASY);
1932
748
  CURLcode result;
1933
1934
748
  if(!smtp)
1935
0
    return CURLE_FAILED_INIT;
1936
748
  result = smtp_multi_statemach(data, dophase_done);
1937
748
  if(result)
1938
120
    DEBUGF(infof(data, "DO phase failed"));
1939
628
  else if(*dophase_done) {
1940
93
    result = smtp_dophase_done(data, smtp, FALSE /* not connected */);
1941
1942
93
    DEBUGF(infof(data, "DO phase is complete"));
1943
93
  }
1944
1945
748
  CURL_TRC_SMTP(data, "smtp_doing() -> %d, done=%d", (int)result,
1946
748
                *dophase_done);
1947
748
  return result;
1948
748
}
1949
1950
static void smtp_easy_dtor(const void *key, size_t klen, void *entry)
1951
439
{
1952
439
  struct SMTP *smtp = entry;
1953
439
  (void)key;
1954
439
  (void)klen;
1955
439
  curlx_free(smtp);
1956
439
}
1957
1958
static void smtp_conn_dtor(const void *key, size_t klen, void *entry)
1959
439
{
1960
439
  struct smtp_conn *smtpc = entry;
1961
439
  (void)key;
1962
439
  (void)klen;
1963
439
  Curl_pp_disconnect(&smtpc->pp);
1964
439
  curlx_safefree(smtpc->domain);
1965
439
  curlx_free(smtpc);
1966
439
}
1967
1968
static CURLcode smtp_setup_connection(struct Curl_easy *data,
1969
                                      struct connectdata *conn)
1970
439
{
1971
439
  struct smtp_conn *smtpc;
1972
439
  struct SMTP *smtp;
1973
439
  CURLcode result = CURLE_OK;
1974
1975
439
  smtpc = curlx_calloc(1, sizeof(*smtpc));
1976
439
  if(!smtpc ||
1977
439
     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
439
  smtp = curlx_calloc(1, sizeof(*smtp));
1983
439
  if(!smtp ||
1984
439
     Curl_meta_set(data, CURL_META_SMTP_EASY, smtp, smtp_easy_dtor))
1985
0
    result = CURLE_OUT_OF_MEMORY;
1986
1987
439
out:
1988
439
  CURL_TRC_SMTP(data, "smtp_setup_connection() -> %d", (int)result);
1989
439
  return result;
1990
439
}
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 */