Coverage Report

Created: 2026-07-16 06:10

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 "hostip.h"
64
#include "progress.h"
65
#include "transfer.h"
66
#include "escape.h"
67
#include "pingpong.h"
68
#include "mime.h"
69
#include "vtls/vtls.h"
70
#include "cfilters.h"
71
#include "connect.h"
72
#include "select.h"
73
#include "url.h"
74
#include "curl_gethostname.h"
75
#include "bufref.h"
76
#include "curl_sasl.h"
77
#include "idn.h"
78
#include "curlx/strparse.h"
79
80
/* meta key for storing protocol meta at easy handle */
81
0
#define CURL_META_SMTP_EASY   "meta:proto:smtp:easy"
82
/* meta key for storing protocol meta at connection */
83
0
#define CURL_META_SMTP_CONN   "meta:proto:smtp:conn"
84
85
/****************************************************************************
86
 * SMTP unique setup
87
 ***************************************************************************/
88
typedef enum {
89
  SMTP_STOP,        /* do nothing state, stops the state machine */
90
  SMTP_SERVERGREET, /* waiting for the initial greeting immediately after
91
                       a connect */
92
  SMTP_EHLO,
93
  SMTP_HELO,
94
  SMTP_STARTTLS,
95
  SMTP_UPGRADETLS,  /* asynchronously upgrade the connection to SSL/TLS
96
                       (multi mode only) */
97
  SMTP_AUTH,
98
  SMTP_COMMAND,     /* VRFY, EXPN, NOOP, RSET and HELP */
99
  SMTP_MAIL,        /* MAIL FROM */
100
  SMTP_RCPT,        /* RCPT TO */
101
  SMTP_DATA,
102
  SMTP_POSTDATA,
103
  SMTP_QUIT,
104
  SMTP_LAST         /* never used */
105
} smtpstate;
106
107
/* smtp_conn is used for struct connection-oriented data in the connectdata
108
   struct */
109
struct smtp_conn {
110
  struct pingpong pp;
111
  struct SASL sasl;        /* SASL-related storage */
112
  smtpstate state;         /* Always use smtp.c:state() to change state! */
113
  char *domain;            /* Client address/name to send in the EHLO */
114
  BIT(ssldone);            /* Is connect() over SSL done? */
115
  BIT(tls_supported);      /* StartTLS capability supported by server */
116
  BIT(size_supported);     /* If server supports SIZE extension according to
117
                              RFC 1870 */
118
  BIT(utf8_supported);     /* If server supports SMTPUTF8 extension according
119
                              to RFC 6531 */
120
  BIT(auth_supported);     /* AUTH capability supported by server */
121
};
122
123
/* This SMTP struct is used in the Curl_easy. All SMTP data that is
124
   connection-oriented must be in smtp_conn to properly deal with the fact that
125
   perhaps the Curl_easy is changed between the times the connection is
126
   used. */
127
struct SMTP {
128
  curl_pp_transfer transfer;
129
  char *custom;            /* Custom Request */
130
  struct curl_slist *rcpt; /* Recipient list */
131
  int rcpt_last_error;     /* The last error received for RCPT TO command */
132
  size_t eob;              /* Number of bytes of the EOB (End Of Body) that
133
                              have been received so far */
134
  BIT(rcpt_had_ok);        /* Whether any of RCPT TO commands (depends on
135
                              total number of recipients) succeeded so far */
136
  BIT(trailing_crlf);      /* Specifies if the trailing CRLF is present */
137
};
138
139
/***********************************************************************
140
 *
141
 * smtp_parse_url_options()
142
 *
143
 * Parse the URL login options.
144
 */
145
static CURLcode smtp_parse_url_options(struct connectdata *conn,
146
                                       struct smtp_conn *smtpc)
147
0
{
148
0
  CURLcode result = CURLE_OK;
149
0
  const char *ptr = conn->options;
150
151
0
  while(!result && ptr && *ptr) {
152
0
    const char *key = ptr;
153
0
    const char *value;
154
155
0
    while(*ptr && *ptr != '=')
156
0
      ptr++;
157
158
0
    value = ptr + 1;
159
160
0
    while(*ptr && *ptr != ';')
161
0
      ptr++;
162
163
0
    if(curl_strnequal(key, "AUTH=", 5))
164
0
      result = Curl_sasl_parse_url_auth_option(&smtpc->sasl,
165
0
                                               value, ptr - value);
166
0
    else
167
0
      result = CURLE_URL_MALFORMAT;
168
169
0
    if(*ptr == ';')
170
0
      ptr++;
171
0
  }
172
173
0
  return result;
174
0
}
175
176
/***********************************************************************
177
 *
178
 * smtp_parse_url_path()
179
 *
180
 * Parse the URL path into separate path components.
181
 */
182
static CURLcode smtp_parse_url_path(struct Curl_easy *data,
183
                                    struct smtp_conn *smtpc)
184
0
{
185
  /* The SMTP struct is already initialized in smtp_connect() */
186
0
  const char *path = &data->state.up.path[1]; /* skip leading path */
187
0
  char localhost[HOSTNAME_MAX + 1];
188
189
  /* Calculate the path if necessary */
190
0
  if(!*path) {
191
0
    if(!Curl_gethostname(localhost, sizeof(localhost)))
192
0
      path = localhost;
193
0
    else
194
0
      path = "localhost";
195
0
  }
196
197
  /* URL decode the path and use it as the domain in our EHLO */
198
0
  return Curl_urldecode(path, 0, &smtpc->domain, NULL, REJECT_CTRL);
199
0
}
200
201
/***********************************************************************
202
 *
203
 * smtp_parse_custom_request()
204
 *
205
 * Parse the custom request.
206
 */
207
static CURLcode smtp_parse_custom_request(struct Curl_easy *data,
208
                                          struct SMTP *smtp)
209
0
{
210
0
  CURLcode result = CURLE_OK;
211
0
  const char *custom = data->set.str[STRING_CUSTOMREQUEST];
212
213
  /* URL decode the custom request */
214
0
  if(custom)
215
0
    result = Curl_urldecode(custom, 0, &smtp->custom, NULL, REJECT_CTRL);
216
217
0
  return result;
218
0
}
219
220
/***********************************************************************
221
 *
222
 * smtp_parse_address()
223
 *
224
 * Parse the fully qualified mailbox address into a local address part and the
225
 * hostname, converting the hostname to an IDN A-label, as per RFC-5890, if
226
 * necessary.
227
 *
228
 * Parameters:
229
 *
230
 * fqma           [in]     - The fully qualified mailbox address (which may or
231
 *                           may not contain UTF-8 characters).
232
 * address        [in/out] - A new allocated buffer which holds the local
233
 *                           address part of the mailbox. This buffer must be
234
 *                           free'ed by the caller.
235
 * host           [in/out] - The hostname structure that holds the original,
236
 *                           and optionally encoded, hostname.
237
 *                           Curl_free_idnconverted_hostname() must be called
238
 *                           once the caller has finished with the structure.
239
 *
240
 * Returns CURLE_OK on success.
241
 *
242
 * Notes:
243
 *
244
 * Should a UTF-8 hostname require conversion to IDN ACE and we cannot honor
245
 * that conversion then we shall return success. This allow the caller to send
246
 * the data to the server as a U-label (as per RFC-6531 sect. 3.2).
247
 *
248
 * If an mailbox '@' separator cannot be located then the mailbox is considered
249
 * to be either a local mailbox or an invalid mailbox (depending on what the
250
 * calling function deems it to be) then the input will be returned in
251
 * the address part with the hostname being NULL.
252
 */
253
static CURLcode smtp_parse_address(struct Curl_easy *data, const char *fqma,
254
                                   char **address, struct hostname *host,
255
                                   const char **suffix)
256
0
{
257
0
  CURLcode result = CURLE_OK;
258
0
  size_t length;
259
0
  char *addressend;
260
0
  char *dup;
261
262
  /* A CR or LF in the address ends up verbatim in the MAIL FROM/RCPT TO
263
     command line, so a crafted address could smuggle further SMTP commands
264
     onto the wire. Reject it before the command is built. */
265
0
  if(strpbrk(fqma, "\r\n")) {
266
0
    failf(data, "Refusing to send email address with a CR or LF");
267
0
    return CURLE_BAD_FUNCTION_ARGUMENT;
268
0
  }
269
270
  /* Duplicate the fully qualified email address so we can manipulate it,
271
     ensuring it does not contain the delimiters if specified */
272
0
  dup = curlx_strdup(fqma[0] == '<' ? fqma + 1 : fqma);
273
0
  if(!dup)
274
0
    return CURLE_OUT_OF_MEMORY;
275
276
0
  if(fqma[0] != '<') {
277
0
    length = strlen(dup);
278
0
    if(length) {
279
0
      if(dup[length - 1] == '>')
280
0
        dup[length - 1] = '\0';
281
0
    }
282
0
  }
283
0
  else {
284
0
    addressend = strrchr(dup, '>');
285
0
    if(addressend) {
286
0
      *addressend = '\0';
287
0
      *suffix = addressend + 1;
288
0
    }
289
0
  }
290
291
  /* Extract the hostname from the address (if we can) */
292
0
  host->name = strpbrk(dup, "@");
293
0
  if(host->name) {
294
0
    *host->name = '\0';
295
0
    host->name = host->name + 1;
296
297
    /* Attempt to convert the hostname to IDN ACE */
298
0
    (void)Curl_idnconvert_hostname(host);
299
300
    /* If Curl_idnconvert_hostname() fails then we shall attempt to continue
301
       and send the hostname using UTF-8 rather than as 7-bit ACE (which is
302
       our preference) */
303
0
  }
304
305
  /* Extract the local address from the mailbox */
306
0
  *address = dup;
307
308
0
  return result;
309
0
}
310
311
struct cr_eob_ctx {
312
  struct Curl_creader super;
313
  struct bufq buf;
314
  size_t n_eob; /* how many EOB bytes we matched so far */
315
  size_t eob;       /* Number of bytes of the EOB (End Of Body) that
316
                       have been received so far */
317
  BIT(read_eos);  /* we read an EOS from the next reader */
318
  BIT(processed_eos);  /* we read and processed an EOS */
319
  BIT(eos);       /* we have returned an EOS */
320
};
321
322
static CURLcode cr_eob_init(struct Curl_easy *data,
323
                            struct Curl_creader *reader)
324
0
{
325
0
  struct cr_eob_ctx *ctx = reader->ctx;
326
0
  (void)data;
327
  /* The first char we read is the first on a line, as if we had
328
   * read CRLF before */
329
0
  ctx->n_eob = 2;
330
0
  Curl_bufq_init2(&ctx->buf, (16 * 1024), 1, BUFQ_OPT_SOFT_LIMIT);
331
0
  return CURLE_OK;
332
0
}
333
334
static void cr_eob_close(struct Curl_easy *data, struct Curl_creader *reader)
335
0
{
336
0
  struct cr_eob_ctx *ctx = reader->ctx;
337
0
  (void)data;
338
0
  Curl_bufq_free(&ctx->buf);
339
0
}
340
341
/* this is the 5-bytes End-Of-Body marker for SMTP */
342
0
#define SMTP_EOB          "\r\n.\r\n"
343
0
#define SMTP_EOB_FIND_LEN 3
344
345
/* client reader doing SMTP End-Of-Body escaping. */
346
static CURLcode cr_eob_read(struct Curl_easy *data,
347
                            struct Curl_creader *reader,
348
                            char *buf, size_t blen,
349
                            size_t *pnread, bool *peos)
350
0
{
351
0
  struct cr_eob_ctx *ctx = reader->ctx;
352
0
  CURLcode result = CURLE_OK;
353
0
  size_t nread, i, start, n;
354
0
  bool eos;
355
356
0
  if(!ctx->read_eos && Curl_bufq_is_empty(&ctx->buf)) {
357
    /* Get more and convert it when needed */
358
0
    result = Curl_creader_read(data, reader->next, buf, blen, &nread, &eos);
359
0
    CURL_TRC_SMTP(data, "cr_eob_read, next_read(len=%zu) -> %d, %zu eos=%d",
360
0
                  blen, (int)result, nread, eos);
361
0
    if(result)
362
0
      return result;
363
364
0
    ctx->read_eos = eos;
365
0
    if(nread) {
366
0
      if(!ctx->n_eob && !memchr(buf, SMTP_EOB[0], nread)) {
367
        /* not in the middle of a match, no EOB start found, pass */
368
0
        *pnread = nread;
369
0
        *peos = FALSE;
370
0
        return CURLE_OK;
371
0
      }
372
      /* scan for EOB (continuation) and convert */
373
0
      for(i = start = 0; i < nread; ++i) {
374
0
        if(ctx->n_eob >= SMTP_EOB_FIND_LEN) {
375
          /* matched the EOB prefix and seeing additional char, add '.' */
376
0
          result = Curl_bufq_cwrite(&ctx->buf, buf + start, i - start, &n);
377
0
          if(result)
378
0
            return result;
379
0
          result = Curl_bufq_cwrite(&ctx->buf, ".", 1, &n);
380
0
          if(result)
381
0
            return result;
382
0
          ctx->n_eob = 0;
383
0
          start = i;
384
0
          if(data->state.infilesize > 0)
385
0
            data->state.infilesize++;
386
0
        }
387
388
0
        if(buf[i] != SMTP_EOB[ctx->n_eob])
389
0
          ctx->n_eob = 0;
390
391
0
        if(buf[i] == SMTP_EOB[ctx->n_eob]) {
392
          /* matching another char of the EOB */
393
0
          ++ctx->n_eob;
394
0
        }
395
0
      }
396
397
      /* add any remainder to buf */
398
0
      if(start < nread) {
399
0
        result = Curl_bufq_cwrite(&ctx->buf, buf + start, nread - start, &n);
400
0
        if(result)
401
0
          return result;
402
0
      }
403
0
    }
404
0
  }
405
406
0
  *peos = FALSE;
407
408
0
  if(ctx->read_eos && !ctx->processed_eos) {
409
    /* if we last matched a CRLF or if the data was empty, add ".\r\n"
410
     * to end the body. If we sent something and it did not end with "\r\n",
411
     * add "\r\n.\r\n" to end the body */
412
0
    const char *eob = SMTP_EOB;
413
0
    CURL_TRC_SMTP(data, "auto-ending mail body with '\\r\\n.\\r\\n'");
414
0
    switch(ctx->n_eob) {
415
0
    case 2:
416
      /* seen a CRLF at the end, add the remainder */
417
0
      eob = &SMTP_EOB[2];
418
0
      break;
419
0
    case 3:
420
      /* ended with '\r\n.', we should escape the last '.' */
421
0
      eob = "." SMTP_EOB;
422
0
      break;
423
0
    default:
424
0
      break;
425
0
    }
426
0
    result = Curl_bufq_cwrite(&ctx->buf, eob, strlen(eob), &n);
427
0
    if(result)
428
0
      return result;
429
0
    ctx->processed_eos = TRUE;
430
0
  }
431
432
0
  if(!Curl_bufq_is_empty(&ctx->buf)) {
433
0
    result = Curl_bufq_cread(&ctx->buf, buf, blen, pnread);
434
0
  }
435
0
  else
436
0
    *pnread = 0;
437
438
0
  if(ctx->read_eos && Curl_bufq_is_empty(&ctx->buf)) {
439
    /* no more data, read all, done. */
440
0
    CURL_TRC_SMTP(data, "mail body complete, returning EOS");
441
0
    ctx->eos = TRUE;
442
0
  }
443
0
  *peos = (bool)ctx->eos;
444
0
  DEBUGF(infof(data, "cr_eob_read(%zu) -> %d, %zu, %d",
445
0
               blen, (int)result, *pnread, *peos));
446
0
  return result;
447
0
}
448
449
static curl_off_t cr_eob_total_length(struct Curl_easy *data,
450
                                      struct Curl_creader *reader)
451
0
{
452
  /* this reader changes length depending on input */
453
0
  (void)data;
454
0
  (void)reader;
455
0
  return -1;
456
0
}
457
458
static const struct Curl_crtype cr_eob = {
459
  "cr-smtp-eob",
460
  cr_eob_init,
461
  cr_eob_read,
462
  cr_eob_close,
463
  Curl_creader_def_needs_rewind,
464
  cr_eob_total_length,
465
  Curl_creader_def_resume_from,
466
  Curl_creader_def_cntrl,
467
  Curl_creader_def_is_paused,
468
  Curl_creader_def_done,
469
  sizeof(struct cr_eob_ctx)
470
};
471
472
static CURLcode cr_eob_add(struct Curl_easy *data)
473
0
{
474
0
  struct Curl_creader *reader = NULL;
475
0
  CURLcode result;
476
477
0
  result = Curl_creader_create(&reader, data, &cr_eob, CURL_CR_CONTENT_ENCODE);
478
0
  if(!result)
479
0
    result = Curl_creader_add(data, reader);
480
481
0
  if(result && reader)
482
0
    Curl_creader_free(data, reader);
483
0
  return result;
484
0
}
485
486
/***********************************************************************
487
 *
488
 * smtp_endofresp()
489
 *
490
 * Checks for an ending SMTP status code at the start of the given string, but
491
 * also detects various capabilities from the EHLO response including the
492
 * supported authentication mechanisms.
493
 */
494
static bool smtp_endofresp(struct Curl_easy *data, struct connectdata *conn,
495
                           const char *line, size_t len, int *resp)
496
0
{
497
0
  struct smtp_conn *smtpc = Curl_conn_meta_get(conn, CURL_META_SMTP_CONN);
498
0
  bool end = FALSE;
499
0
  (void)data;
500
501
0
  DEBUGASSERT(smtpc);
502
0
  if(!smtpc)
503
0
    return FALSE;
504
505
  /* Nothing for us */
506
0
  if(len < 4 || !ISDIGIT(line[0]) || !ISDIGIT(line[1]) || !ISDIGIT(line[2]))
507
0
    return FALSE;
508
509
  /* Do we have a command response? This should be the response code followed
510
     by a space and optionally some text as per RFC-5321 and as outlined in
511
     Section 4. Examples of RFC-4954 but some email servers ignore this and
512
     only send the response code instead as per Section 4.2. */
513
0
  if(line[3] == ' ' || len == 5) {
514
0
    char tmpline[6];
515
0
    curl_off_t code;
516
0
    const char *p = tmpline;
517
0
    end = TRUE;
518
0
    memcpy(tmpline, line, (len == 5 ? 5 : 3));
519
0
    tmpline[len == 5 ? 5 : 3] = 0;
520
0
    if(curlx_str_number(&p, &code, len == 5 ? 99999 : 999))
521
0
      return FALSE;
522
0
    *resp = (int)code;
523
524
    /* Make sure real server never sends internal value */
525
0
    if(*resp == 1)
526
0
      *resp = 0;
527
0
  }
528
  /* Do we have a multiline (continuation) response? */
529
0
  else if(line[3] == '-' &&
530
0
          (smtpc->state == SMTP_EHLO || smtpc->state == SMTP_COMMAND)) {
531
0
    end = TRUE;
532
0
    *resp = 1;  /* Internal response code */
533
0
  }
534
535
0
  return end;
536
0
}
537
538
/***********************************************************************
539
 *
540
 * smtp_get_message()
541
 *
542
 * Gets the authentication message from the response buffer.
543
 */
544
static CURLcode smtp_get_message(struct Curl_easy *data, struct bufref *out)
545
0
{
546
0
  struct smtp_conn *smtpc =
547
0
    Curl_conn_meta_get(data->conn, CURL_META_SMTP_CONN);
548
0
  char *message;
549
0
  size_t len;
550
551
0
  if(!smtpc)
552
0
    return CURLE_FAILED_INIT;
553
554
0
  message = curlx_dyn_ptr(&smtpc->pp.recvbuf);
555
0
  len = smtpc->pp.nfinal;
556
0
  if(len > 4) {
557
    /* Find the start of the message */
558
0
    len -= 4;
559
0
    for(message += 4; ISBLANK(*message); message++, len--)
560
0
      ;
561
562
    /* Find the end of the message */
563
0
    while(len--)
564
0
      if(!ISNEWLINE(message[len]) && !ISBLANK(message[len]))
565
0
        break;
566
567
    /* Terminate the message */
568
0
    message[++len] = '\0';
569
0
    Curl_bufref_set(out, message, len, NULL);
570
0
  }
571
0
  else
572
    /* junk input => zero length output */
573
0
    Curl_bufref_set(out, "", 0, NULL);
574
575
0
  return CURLE_OK;
576
0
}
577
578
/***********************************************************************
579
 *
580
 * smtp_state()
581
 *
582
 * This is the ONLY way to change SMTP state!
583
 */
584
static void smtp_state(struct Curl_easy *data,
585
                       struct smtp_conn *smtpc,
586
                       smtpstate newstate)
587
0
{
588
0
#ifdef CURLVERBOSE
589
  /* for debug purposes */
590
0
  static const char * const names[] = {
591
0
    "STOP",
592
0
    "SERVERGREET",
593
0
    "EHLO",
594
0
    "HELO",
595
0
    "STARTTLS",
596
0
    "UPGRADETLS",
597
0
    "AUTH",
598
0
    "COMMAND",
599
0
    "MAIL",
600
0
    "RCPT",
601
0
    "DATA",
602
0
    "POSTDATA",
603
0
    "QUIT",
604
    /* LAST */
605
0
  };
606
607
0
  if(smtpc->state != newstate)
608
0
    CURL_TRC_SMTP(data, "state change from %s to %s",
609
0
                  names[smtpc->state], names[newstate]);
610
#else
611
  (void)data;
612
#endif
613
614
0
  smtpc->state = newstate;
615
0
}
616
617
/***********************************************************************
618
 *
619
 * smtp_perform_ehlo()
620
 *
621
 * Sends the EHLO command to not only initialize communication with the ESMTP
622
 * server but to also obtain a list of server side supported capabilities.
623
 */
624
static CURLcode smtp_perform_ehlo(struct Curl_easy *data,
625
                                  struct smtp_conn *smtpc)
626
0
{
627
0
  CURLcode result = CURLE_OK;
628
629
0
  smtpc->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanism yet */
630
0
  smtpc->sasl.authused = SASL_AUTH_NONE;  /* Clear the authentication mechanism
631
                                             used for esmtp connections */
632
0
  smtpc->tls_supported = FALSE;           /* Clear the TLS capability */
633
0
  smtpc->auth_supported = FALSE;          /* Clear the AUTH capability */
634
635
  /* Send the EHLO command */
636
0
  result = Curl_pp_sendf(data, &smtpc->pp, "EHLO %s", smtpc->domain);
637
638
0
  if(!result)
639
0
    smtp_state(data, smtpc, SMTP_EHLO);
640
641
0
  return result;
642
0
}
643
644
/***********************************************************************
645
 *
646
 * smtp_perform_helo()
647
 *
648
 * Sends the HELO command to initialize communication with the SMTP server.
649
 */
650
static CURLcode smtp_perform_helo(struct Curl_easy *data,
651
                                  struct smtp_conn *smtpc)
652
0
{
653
0
  CURLcode result = CURLE_OK;
654
655
0
  smtpc->sasl.authused = SASL_AUTH_NONE; /* No authentication mechanism used
656
                                            in smtp connections */
657
658
  /* Send the HELO command */
659
0
  result = Curl_pp_sendf(data, &smtpc->pp, "HELO %s", smtpc->domain);
660
661
0
  if(!result)
662
0
    smtp_state(data, smtpc, SMTP_HELO);
663
664
0
  return result;
665
0
}
666
667
/***********************************************************************
668
 *
669
 * smtp_perform_starttls()
670
 *
671
 * Sends the STLS command to start the upgrade to TLS.
672
 */
673
static CURLcode smtp_perform_starttls(struct Curl_easy *data,
674
                                      struct smtp_conn *smtpc)
675
0
{
676
  /* Send the STARTTLS command */
677
0
  CURLcode result = Curl_pp_sendf(data, &smtpc->pp, "%s", "STARTTLS");
678
679
0
  if(!result)
680
0
    smtp_state(data, smtpc, SMTP_STARTTLS);
681
682
0
  return result;
683
0
}
684
685
/***********************************************************************
686
 *
687
 * smtp_perform_upgrade_tls()
688
 *
689
 * Performs the upgrade to TLS.
690
 */
691
static CURLcode smtp_perform_upgrade_tls(struct Curl_easy *data,
692
                                         struct smtp_conn *smtpc)
693
0
{
694
0
#ifdef USE_SSL
695
  /* Start the SSL connection */
696
0
  struct connectdata *conn = data->conn;
697
0
  CURLcode result;
698
0
  bool ssldone = FALSE;
699
700
0
  DEBUGASSERT(smtpc->state == SMTP_UPGRADETLS);
701
0
  if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
702
0
    result = Curl_ssl_cfilter_add(
703
0
      data, Curl_conn_get_origin(conn, FIRSTSOCKET), conn, FIRSTSOCKET);
704
0
    if(result)
705
0
      goto out;
706
    /* Change the connection handler and SMTP state */
707
0
    conn->scheme = &Curl_scheme_smtps;
708
0
  }
709
710
0
  DEBUGASSERT(!smtpc->ssldone);
711
0
  result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone);
712
0
  DEBUGF(infof(data, "smtp_perform_upgrade_tls, connect -> %d, %d",
713
0
               (int)result, ssldone));
714
0
  if(!result && ssldone) {
715
0
    smtpc->ssldone = ssldone;
716
    /* perform EHLO now, changes smtp->state out of SMTP_UPGRADETLS */
717
0
    result = smtp_perform_ehlo(data, smtpc);
718
0
  }
719
0
out:
720
0
  return result;
721
#else
722
  (void)data;
723
  (void)smtpc;
724
  return CURLE_NOT_BUILT_IN;
725
#endif
726
0
}
727
728
/***********************************************************************
729
 *
730
 * smtp_perform_auth()
731
 *
732
 * Sends an AUTH command allowing the client to login with the given SASL
733
 * authentication mechanism.
734
 */
735
static CURLcode smtp_perform_auth(struct Curl_easy *data,
736
                                  const char *mech,
737
                                  const struct bufref *initresp)
738
0
{
739
0
  CURLcode result = CURLE_OK;
740
0
  struct smtp_conn *smtpc =
741
0
    Curl_conn_meta_get(data->conn, CURL_META_SMTP_CONN);
742
0
  const char *ir = Curl_bufref_ptr(initresp);
743
744
0
  if(!smtpc)
745
0
    return CURLE_FAILED_INIT;
746
747
0
  if(ir) {                                  /* AUTH <mech> ...<crlf> */
748
    /* Send the AUTH command with the initial response */
749
0
    result = Curl_pp_sendf(data, &smtpc->pp, "AUTH %s %s",
750
0
                           mech, *ir ? ir : "=");
751
0
  }
752
0
  else {
753
    /* Send the AUTH command */
754
0
    result = Curl_pp_sendf(data, &smtpc->pp, "AUTH %s", mech);
755
0
  }
756
757
0
  return result;
758
0
}
759
760
/***********************************************************************
761
 *
762
 * smtp_continue_auth()
763
 *
764
 * Sends SASL continuation data.
765
 */
766
static CURLcode smtp_continue_auth(struct Curl_easy *data,
767
                                   const char *mech,
768
                                   const struct bufref *resp)
769
0
{
770
0
  struct smtp_conn *smtpc =
771
0
    Curl_conn_meta_get(data->conn, CURL_META_SMTP_CONN);
772
773
0
  (void)mech;
774
0
  if(!smtpc)
775
0
    return CURLE_FAILED_INIT;
776
0
  return Curl_pp_sendf(data, &smtpc->pp, "%s", Curl_bufref_ptr(resp));
777
0
}
778
779
/***********************************************************************
780
 *
781
 * smtp_cancel_auth()
782
 *
783
 * Sends SASL cancellation.
784
 */
785
static CURLcode smtp_cancel_auth(struct Curl_easy *data, const char *mech)
786
0
{
787
0
  struct smtp_conn *smtpc =
788
0
    Curl_conn_meta_get(data->conn, CURL_META_SMTP_CONN);
789
790
0
  (void)mech;
791
0
  if(!smtpc)
792
0
    return CURLE_FAILED_INIT;
793
0
  return Curl_pp_sendf(data, &smtpc->pp, "*");
794
0
}
795
796
/***********************************************************************
797
 *
798
 * smtp_perform_authentication()
799
 *
800
 * Initiates the authentication sequence, with the appropriate SASL
801
 * authentication mechanism.
802
 */
803
static CURLcode smtp_perform_authentication(struct Curl_easy *data,
804
                                            struct smtp_conn *smtpc)
805
0
{
806
0
  CURLcode result = CURLE_OK;
807
0
  saslprogress progress;
808
809
  /* Check we have enough data to authenticate with, and the
810
     server supports authentication, and end the connect phase if not */
811
0
  if(!smtpc->auth_supported ||
812
0
     !Curl_sasl_can_authenticate(&smtpc->sasl, data)) {
813
0
    smtp_state(data, smtpc, SMTP_STOP);
814
0
    return result;
815
0
  }
816
817
  /* Calculate the SASL login details */
818
0
  result = Curl_sasl_start(&smtpc->sasl, data, FALSE, &progress);
819
820
0
  if(!result) {
821
0
    if(progress == SASL_INPROGRESS)
822
0
      smtp_state(data, smtpc, SMTP_AUTH);
823
0
    else
824
0
      result = Curl_sasl_is_blocked(&smtpc->sasl, data);
825
0
  }
826
827
0
  return result;
828
0
}
829
830
/***********************************************************************
831
 *
832
 * smtp_perform_command()
833
 *
834
 * Sends an SMTP based command.
835
 */
836
static CURLcode smtp_perform_command(struct Curl_easy *data,
837
                                     struct smtp_conn *smtpc,
838
                                     struct SMTP *smtp)
839
0
{
840
0
  CURLcode result = CURLE_OK;
841
842
0
  if(smtp->rcpt) {
843
    /* We notify the server we are sending UTF-8 data if a) it supports the
844
       SMTPUTF8 extension and b) The mailbox contains UTF-8 characters, in
845
       either the local address or hostname parts. This is regardless of
846
       whether the hostname is encoded using IDN ACE */
847
0
    bool utf8 = FALSE;
848
849
0
    if(!smtp->custom || !smtp->custom[0]) {
850
0
      char *address = NULL;
851
0
      struct hostname host = { NULL, NULL, NULL, NULL };
852
0
      const char *suffix = "";
853
854
      /* Parse the mailbox to verify into the local address and hostname
855
         parts, converting the hostname to an IDN A-label if necessary */
856
0
      result = smtp_parse_address(data, smtp->rcpt->data,
857
0
                                  &address, &host, &suffix);
858
0
      if(result)
859
0
        return result;
860
861
      /* Establish whether we should report SMTPUTF8 to the server for this
862
         mailbox as per RFC-6531 sect. 3.1 point 6 */
863
0
      utf8 = smtpc->utf8_supported &&
864
0
             (host.encalloc ||
865
0
              !Curl_is_ASCII_name(address) ||
866
0
              !Curl_is_ASCII_name(host.name));
867
868
      /* Send the VRFY command (Note: The hostname part may be absent when the
869
         host is a local system) */
870
0
      result = Curl_pp_sendf(data, &smtpc->pp, "VRFY %s%s%s%s",
871
0
                             address,
872
0
                             host.name ? "@" : "",
873
0
                             host.name ? host.name : "",
874
0
                             utf8 ? " SMTPUTF8" : "");
875
876
0
      Curl_free_idnconverted_hostname(&host);
877
0
      curlx_free(address);
878
0
    }
879
0
    else {
880
      /* Establish whether we should report that we support SMTPUTF8 for EXPN
881
         commands to the server as per RFC-6531 sect. 3.1 point 6 */
882
0
      utf8 = (smtpc->utf8_supported) && (!strcmp(smtp->custom, "EXPN"));
883
884
      /* Send the custom recipient based command such as the EXPN command */
885
0
      result = Curl_pp_sendf(data, &smtpc->pp,
886
0
                             "%s %s%s", smtp->custom,
887
0
                             smtp->rcpt->data,
888
0
                             utf8 ? " SMTPUTF8" : "");
889
0
    }
890
0
  }
891
0
  else
892
    /* Send the non-recipient based command such as HELP */
893
0
    result = Curl_pp_sendf(data, &smtpc->pp, "%s",
894
0
                           smtp->custom && smtp->custom[0] != '\0' ?
895
0
                           smtp->custom : "HELP");
896
897
0
  if(!result)
898
0
    smtp_state(data, smtpc, SMTP_COMMAND);
899
900
0
  return result;
901
0
}
902
903
/***********************************************************************
904
 *
905
 * smtp_perform_mail()
906
 *
907
 * Sends an MAIL command to initiate the upload of a message.
908
 */
909
static CURLcode smtp_perform_mail(struct Curl_easy *data,
910
                                  struct smtp_conn *smtpc,
911
                                  struct SMTP *smtp)
912
0
{
913
0
  char *from = NULL;
914
0
  char *auth = NULL;
915
0
  char *size = NULL;
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
  if(data->set.str[STRING_MAIL_FROM]) {
926
0
    char *address = NULL;
927
0
    struct hostname host = { NULL, NULL, NULL, NULL };
928
0
    const char *suffix = "";
929
930
    /* Parse the FROM mailbox into the local address and hostname parts,
931
       converting the hostname to an IDN A-label if necessary */
932
0
    result = smtp_parse_address(data, data->set.str[STRING_MAIL_FROM],
933
0
                                &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
  if(data->set.str[STRING_MAIL_AUTH] && smtpc->sasl.authused) {
967
0
    if(data->set.str[STRING_MAIL_AUTH][0] != '\0') {
968
0
      char *address = NULL;
969
0
      struct hostname host = { NULL, NULL, NULL, NULL };
970
0
      const char *suffix = "";
971
972
      /* Parse the AUTH mailbox into the local address and hostname parts,
973
         converting the hostname to an IDN A-label if necessary */
974
0
      result = smtp_parse_address(data, data->set.str[STRING_MAIL_AUTH],
975
0
                                  &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 is multiple RCPT TO to be issued, it is possible to ignore errors
1427
     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
    connclose(conn, "SMTP done with bad status"); /* marked for closure */
1743
0
    result = status;         /* use the already set error code */
1744
0
  }
1745
0
  else if(!data->set.connect_only && data->set.mail_rcpt &&
1746
0
          (data->state.upload || IS_MIME_POST(data))) {
1747
1748
0
    smtp_state(data, smtpc, SMTP_POSTDATA);
1749
1750
    /* Run the state-machine */
1751
0
    result = smtp_block_statemach(data, smtpc, FALSE);
1752
0
  }
1753
1754
  /* Clear the transfer mode for the next request */
1755
0
  smtp->transfer = PPTRANSFER_BODY;
1756
0
  CURL_TRC_SMTP(data, "smtp_done(status=%d, premature=%d) -> %d",
1757
0
                (int)status, premature, (int)result);
1758
0
  return result;
1759
0
}
1760
1761
/***********************************************************************
1762
 *
1763
 * smtp_perform()
1764
 *
1765
 * This is the actual DO function for SMTP. Transfer a mail, send a command
1766
 * or get some data according to the options previously setup.
1767
 */
1768
static CURLcode smtp_perform(struct Curl_easy *data,
1769
                             struct smtp_conn *smtpc,
1770
                             struct SMTP *smtp,
1771
                             bool *connected,
1772
                             bool *dophase_done)
1773
0
{
1774
  /* This is SMTP and no proxy */
1775
0
  CURLcode result = CURLE_OK;
1776
1777
0
  CURL_TRC_SMTP(data, "smtp_perform(), start");
1778
1779
0
  if(data->req.no_body) {
1780
    /* Requested no body means no transfer */
1781
0
    smtp->transfer = PPTRANSFER_INFO;
1782
0
  }
1783
1784
0
  *dophase_done = FALSE; /* not done yet */
1785
1786
  /* Store the first recipient (or NULL if not specified) */
1787
0
  smtp->rcpt = data->set.mail_rcpt;
1788
1789
  /* Track of whether we have successfully sent at least one RCPT TO command */
1790
0
  smtp->rcpt_had_ok = FALSE;
1791
1792
  /* Track of the last error we have received by sending RCPT TO command */
1793
0
  smtp->rcpt_last_error = 0;
1794
1795
  /* Initial data character is the first character in line: it is implicitly
1796
     preceded by a virtual CRLF. */
1797
0
  smtp->trailing_crlf = TRUE;
1798
0
  smtp->eob = 2;
1799
1800
  /* Start the first command in the DO phase */
1801
0
  if((data->state.upload || IS_MIME_POST(data)) && data->set.mail_rcpt)
1802
    /* MAIL transfer */
1803
0
    result = smtp_perform_mail(data, smtpc, smtp);
1804
0
  else
1805
    /* SMTP based command (VRFY, EXPN, NOOP, RSET or HELP) */
1806
0
    result = smtp_perform_command(data, smtpc, smtp);
1807
1808
0
  if(result)
1809
0
    goto out;
1810
1811
  /* Run the state-machine */
1812
0
  result = smtp_multi_statemach(data, dophase_done);
1813
1814
0
  *connected = Curl_conn_is_connected(data->conn, FIRSTSOCKET);
1815
1816
0
out:
1817
0
  CURL_TRC_SMTP(data, "smtp_perform() -> %d, connected=%d, done=%d",
1818
0
                (int)result, *connected, *dophase_done);
1819
0
  return result;
1820
0
}
1821
1822
/* Call this when the DO phase has completed */
1823
static CURLcode smtp_dophase_done(struct Curl_easy *data,
1824
                                  struct SMTP *smtp,
1825
                                  bool connected)
1826
0
{
1827
0
  (void)connected;
1828
1829
0
  if(smtp->transfer != PPTRANSFER_BODY)
1830
    /* no data to transfer */
1831
0
    Curl_xfer_setup_nop(data);
1832
1833
0
  return CURLE_OK;
1834
0
}
1835
1836
/***********************************************************************
1837
 *
1838
 * smtp_regular_transfer()
1839
 *
1840
 * The input argument is already checked for validity.
1841
 *
1842
 * Performs all commands done before a regular transfer between a local and a
1843
 * remote host.
1844
 */
1845
static CURLcode smtp_regular_transfer(struct Curl_easy *data,
1846
                                      struct smtp_conn *smtpc,
1847
                                      struct SMTP *smtp,
1848
                                      bool *dophase_done)
1849
0
{
1850
0
  CURLcode result = CURLE_OK;
1851
0
  bool connected = FALSE;
1852
1853
  /* Make sure size is unknown at this point */
1854
0
  data->req.size = -1;
1855
1856
  /* Set the progress data */
1857
0
  Curl_pgrsReset(data);
1858
1859
  /* Carry out the perform */
1860
0
  result = smtp_perform(data, smtpc, smtp, &connected, dophase_done);
1861
1862
  /* Perform post DO phase operations if necessary */
1863
0
  if(!result && *dophase_done)
1864
0
    result = smtp_dophase_done(data, smtp, connected);
1865
1866
0
  CURL_TRC_SMTP(data, "smtp_regular_transfer() -> %d, done=%d",
1867
0
                (int)result, *dophase_done);
1868
0
  return result;
1869
0
}
1870
1871
/***********************************************************************
1872
 *
1873
 * smtp_do()
1874
 *
1875
 * This function is registered as 'curl_do' function. It decodes the path
1876
 * parts etc as a wrapper to the actual DO function (smtp_perform).
1877
 *
1878
 * The input argument is already checked for validity.
1879
 */
1880
static CURLcode smtp_do(struct Curl_easy *data, bool *done)
1881
0
{
1882
0
  struct smtp_conn *smtpc =
1883
0
    Curl_conn_meta_get(data->conn, CURL_META_SMTP_CONN);
1884
0
  struct SMTP *smtp = Curl_meta_get(data, CURL_META_SMTP_EASY);
1885
0
  CURLcode result = CURLE_OK;
1886
1887
0
  DEBUGASSERT(data);
1888
0
  DEBUGASSERT(data->conn);
1889
0
  *done = FALSE; /* default to false */
1890
0
  if(!smtpc || !smtp)
1891
0
    return CURLE_FAILED_INIT;
1892
1893
  /* Parse the custom request */
1894
0
  result = smtp_parse_custom_request(data, smtp);
1895
0
  if(result)
1896
0
    return result;
1897
1898
0
  result = smtp_regular_transfer(data, smtpc, smtp, done);
1899
0
  CURL_TRC_SMTP(data, "smtp_do() -> %d, done=%d", (int)result, *done);
1900
0
  return result;
1901
0
}
1902
1903
/***********************************************************************
1904
 *
1905
 * smtp_disconnect()
1906
 *
1907
 * Disconnect from an SMTP server. Cleanup protocol-specific per-connection
1908
 * resources. BLOCKING.
1909
 */
1910
static CURLcode smtp_disconnect(struct Curl_easy *data,
1911
                                struct connectdata *conn,
1912
                                bool dead_connection)
1913
0
{
1914
0
  struct smtp_conn *smtpc = Curl_conn_meta_get(conn, CURL_META_SMTP_CONN);
1915
1916
0
  if(!smtpc)
1917
0
    return CURLE_FAILED_INIT;
1918
1919
  /* We cannot send quit unconditionally. If this connection is stale or
1920
     bad in any way, sending quit and waiting around here will make the
1921
     disconnect wait in vain and cause more problems than we need to. */
1922
1923
0
  if(!dead_connection && conn->bits.protoconnstart &&
1924
0
     !Curl_pp_needs_flush(data, &smtpc->pp)) {
1925
0
    if(!smtp_perform_quit(data, smtpc))
1926
0
      (void)smtp_block_statemach(data, smtpc, TRUE); /* ignore on QUIT */
1927
0
  }
1928
1929
0
  CURL_TRC_SMTP(data, "smtp_disconnect(), finished");
1930
0
  return CURLE_OK;
1931
0
}
1932
1933
/* Called from multi.c while DOing */
1934
static CURLcode smtp_doing(struct Curl_easy *data, bool *dophase_done)
1935
0
{
1936
0
  struct SMTP *smtp = Curl_meta_get(data, CURL_META_SMTP_EASY);
1937
0
  CURLcode result;
1938
1939
0
  if(!smtp)
1940
0
    return CURLE_FAILED_INIT;
1941
0
  result = smtp_multi_statemach(data, dophase_done);
1942
0
  if(result)
1943
0
    DEBUGF(infof(data, "DO phase failed"));
1944
0
  else if(*dophase_done) {
1945
0
    result = smtp_dophase_done(data, smtp, FALSE /* not connected */);
1946
1947
0
    DEBUGF(infof(data, "DO phase is complete"));
1948
0
  }
1949
1950
0
  CURL_TRC_SMTP(data, "smtp_doing() -> %d, done=%d", (int)result,
1951
0
                *dophase_done);
1952
0
  return result;
1953
0
}
1954
1955
static void smtp_easy_dtor(void *key, size_t klen, void *entry)
1956
0
{
1957
0
  struct SMTP *smtp = entry;
1958
0
  (void)key;
1959
0
  (void)klen;
1960
0
  curlx_free(smtp);
1961
0
}
1962
1963
static void smtp_conn_dtor(void *key, size_t klen, void *entry)
1964
0
{
1965
0
  struct smtp_conn *smtpc = entry;
1966
0
  (void)key;
1967
0
  (void)klen;
1968
0
  Curl_pp_disconnect(&smtpc->pp);
1969
0
  curlx_safefree(smtpc->domain);
1970
0
  curlx_free(smtpc);
1971
0
}
1972
1973
static CURLcode smtp_setup_connection(struct Curl_easy *data,
1974
                                      struct connectdata *conn)
1975
0
{
1976
0
  struct smtp_conn *smtpc;
1977
0
  struct SMTP *smtp;
1978
0
  CURLcode result = CURLE_OK;
1979
1980
0
  smtpc = curlx_calloc(1, sizeof(*smtpc));
1981
0
  if(!smtpc ||
1982
0
     Curl_conn_meta_set(conn, CURL_META_SMTP_CONN, smtpc, smtp_conn_dtor)) {
1983
0
    result = CURLE_OUT_OF_MEMORY;
1984
0
    goto out;
1985
0
  }
1986
1987
0
  smtp = curlx_calloc(1, sizeof(*smtp));
1988
0
  if(!smtp ||
1989
0
     Curl_meta_set(data, CURL_META_SMTP_EASY, smtp, smtp_easy_dtor))
1990
0
    result = CURLE_OUT_OF_MEMORY;
1991
1992
0
out:
1993
0
  CURL_TRC_SMTP(data, "smtp_setup_connection() -> %d", (int)result);
1994
0
  return result;
1995
0
}
1996
1997
/*
1998
 * SMTP protocol handler.
1999
 */
2000
const struct Curl_protocol Curl_protocol_smtp = {
2001
  smtp_setup_connection,            /* setup_connection */
2002
  smtp_do,                          /* do_it */
2003
  smtp_done,                        /* done */
2004
  ZERO_NULL,                        /* do_more */
2005
  smtp_connect,                     /* connect_it */
2006
  smtp_multi_statemach,             /* connecting */
2007
  smtp_doing,                       /* doing */
2008
  smtp_pollset,                     /* proto_pollset */
2009
  smtp_pollset,                     /* doing_pollset */
2010
  ZERO_NULL,                        /* domore_pollset */
2011
  ZERO_NULL,                        /* perform_pollset */
2012
  smtp_disconnect,                  /* disconnect */
2013
  ZERO_NULL,                        /* write_resp */
2014
  ZERO_NULL,                        /* write_resp_hd */
2015
  ZERO_NULL,                        /* connection_is_dead */
2016
  ZERO_NULL,                        /* attach connection */
2017
  ZERO_NULL,                        /* follow */
2018
};
2019
2020
#endif /* CURL_DISABLE_SMTP */