Coverage Report

Created: 2026-09-14 07:05

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