Coverage Report

Created: 2026-09-01 06:58

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