1/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2022, Daniel Stenberg, <[email protected]>, 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
42#include "curl_setup.h"
43
44#ifndef CURL_DISABLE_SMTP
45
46#ifdef HAVE_NETINET_IN_H
47#include <netinet/in.h>
48#endif
49#ifdef HAVE_ARPA_INET_H
50#include <arpa/inet.h>
51#endif
52#ifdef HAVE_UTSNAME_H
53#include <sys/utsname.h>
54#endif
55#ifdef HAVE_NETDB_H
56#include <netdb.h>
57#endif
58#ifdef __VMS
59#include <in.h>
60#include <inet.h>
61#endif
62
63#if (defined(NETWARE) && defined(__NOVELL_LIBC__))
64#undef in_addr_t
65#define in_addr_t unsigned long
66#endif
67
68#include <curl/curl.h>
69#include "urldata.h"
70#include "sendf.h"
71#include "hostip.h"
72#include "progress.h"
73#include "transfer.h"
74#include "escape.h"
75#include "http.h" /* for HTTP proxy tunnel stuff */
76#include "mime.h"
77#include "socks.h"
78#include "smtp.h"
79#include "strtoofft.h"
80#include "strcase.h"
81#include "vtls/vtls.h"
82#include "connect.h"
83#include "select.h"
84#include "multiif.h"
85#include "url.h"
86#include "curl_gethostname.h"
87#include "bufref.h"
88#include "curl_sasl.h"
89#include "warnless.h"
90/* The last 3 #include files should be in this order */
91#include "curl_printf.h"
92#include "curl_memory.h"
93#include "memdebug.h"
94
95/* Local API functions */
96static CURLcode smtp_regular_transfer(struct Curl_easy *data, bool *done);
97static CURLcode smtp_do(struct Curl_easy *data, bool *done);
98static CURLcode smtp_done(struct Curl_easy *data, CURLcode status,
99 bool premature);
100static CURLcode smtp_connect(struct Curl_easy *data, bool *done);
101static CURLcode smtp_disconnect(struct Curl_easy *data,
102 struct connectdata *conn, bool dead);
103static CURLcode smtp_multi_statemach(struct Curl_easy *data, bool *done);
104static int smtp_getsock(struct Curl_easy *data,
105 struct connectdata *conn, curl_socket_t *socks);
106static CURLcode smtp_doing(struct Curl_easy *data, bool *dophase_done);
107static CURLcode smtp_setup_connection(struct Curl_easy *data,
108 struct connectdata *conn);
109static CURLcode smtp_parse_url_options(struct connectdata *conn);
110static CURLcode smtp_parse_url_path(struct Curl_easy *data);
111static CURLcode smtp_parse_custom_request(struct Curl_easy *data);
112static CURLcode smtp_parse_address(struct Curl_easy *data, const char *fqma,
113 char **address, struct hostname *host);
114static CURLcode smtp_perform_auth(struct Curl_easy *data, const char *mech,
115 const struct bufref *initresp);
116static CURLcode smtp_continue_auth(struct Curl_easy *data, const char *mech,
117 const struct bufref *resp);
118static CURLcode smtp_cancel_auth(struct Curl_easy *data, const char *mech);
119static CURLcode smtp_get_message(struct Curl_easy *data, struct bufref *out);
120
121/*
122 * SMTP protocol handler.
123 */
124
125const struct Curl_handler Curl_handler_smtp = {
126 "SMTP", /* scheme */
127 smtp_setup_connection, /* setup_connection */
128 smtp_do, /* do_it */
129 smtp_done, /* done */
130 ZERO_NULL, /* do_more */
131 smtp_connect, /* connect_it */
132 smtp_multi_statemach, /* connecting */
133 smtp_doing, /* doing */
134 smtp_getsock, /* proto_getsock */
135 smtp_getsock, /* doing_getsock */
136 ZERO_NULL, /* domore_getsock */
137 ZERO_NULL, /* perform_getsock */
138 smtp_disconnect, /* disconnect */
139 ZERO_NULL, /* readwrite */
140 ZERO_NULL, /* connection_check */
141 ZERO_NULL, /* attach connection */
142 PORT_SMTP, /* defport */
143 CURLPROTO_SMTP, /* protocol */
144 CURLPROTO_SMTP, /* family */
145 PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY | /* flags */
146 PROTOPT_URLOPTIONS
147};
148
149#ifdef USE_SSL
150/*
151 * SMTPS protocol handler.
152 */
153
154const struct Curl_handler Curl_handler_smtps = {
155 "SMTPS", /* scheme */
156 smtp_setup_connection, /* setup_connection */
157 smtp_do, /* do_it */
158 smtp_done, /* done */
159 ZERO_NULL, /* do_more */
160 smtp_connect, /* connect_it */
161 smtp_multi_statemach, /* connecting */
162 smtp_doing, /* doing */
163 smtp_getsock, /* proto_getsock */
164 smtp_getsock, /* doing_getsock */
165 ZERO_NULL, /* domore_getsock */
166 ZERO_NULL, /* perform_getsock */
167 smtp_disconnect, /* disconnect */
168 ZERO_NULL, /* readwrite */
169 ZERO_NULL, /* connection_check */
170 ZERO_NULL, /* attach connection */
171 PORT_SMTPS, /* defport */
172 CURLPROTO_SMTPS, /* protocol */
173 CURLPROTO_SMTP, /* family */
174 PROTOPT_CLOSEACTION | PROTOPT_SSL
175 | PROTOPT_NOURLQUERY | PROTOPT_URLOPTIONS /* flags */
176};
177#endif
178
179/* SASL parameters for the smtp protocol */
180static const struct SASLproto saslsmtp = {
181 "smtp", /* The service name */
182 smtp_perform_auth, /* Send authentication command */
183 smtp_continue_auth, /* Send authentication continuation */
184 smtp_cancel_auth, /* Cancel authentication */
185 smtp_get_message, /* Get SASL response message */
186 512 - 8, /* Max line len - strlen("AUTH ") - 1 space - crlf */
187 334, /* Code received when continuation is expected */
188 235, /* Code to receive upon authentication success */
189 SASL_AUTH_DEFAULT, /* Default mechanisms */
190 SASL_FLAG_BASE64 /* Configuration flags */
191};
192
193#ifdef USE_SSL
194static void smtp_to_smtps(struct connectdata *conn)
195{
196 /* Change the connection handler */
197 conn->handler = &Curl_handler_smtps;
198
199 /* Set the connection's upgraded to TLS flag */
200 conn->bits.tls_upgraded = TRUE;
201}
202#else
203#define smtp_to_smtps(x) Curl_nop_stmt
204#endif
205
206/***********************************************************************
207 *
208 * smtp_endofresp()
209 *
210 * Checks for an ending SMTP status code at the start of the given string, but
211 * also detects various capabilities from the EHLO response including the
212 * supported authentication mechanisms.
213 */
214static bool smtp_endofresp(struct Curl_easy *data, struct connectdata *conn,
215 char *line, size_t len, int *resp)
216{
217 struct smtp_conn *smtpc = &conn->proto.smtpc;
218 bool result = FALSE;
219 (void)data;
220
221 /* Nothing for us */
222 if(len < 4 || !ISDIGIT(line[0]) || !ISDIGIT(line[1]) || !ISDIGIT(line[2]))
223 return FALSE;
224
225 /* Do we have a command response? This should be the response code followed
226 by a space and optionally some text as per RFC-5321 and as outlined in
227 Section 4. Examples of RFC-4954 but some email servers ignore this and
228 only send the response code instead as per Section 4.2. */
229 if(line[3] == ' ' || len == 5) {
230 char tmpline[6];
231
232 result = TRUE;
233 memset(tmpline, '\0', sizeof(tmpline));
234 memcpy(tmpline, line, (len == 5 ? 5 : 3));
235 *resp = curlx_sltosi(strtol(tmpline, NULL, 10));
236
237 /* Make sure real server never sends internal value */
238 if(*resp == 1)
239 *resp = 0;
240 }
241 /* Do we have a multiline (continuation) response? */
242 else if(line[3] == '-' &&
243 (smtpc->state == SMTP_EHLO || smtpc->state == SMTP_COMMAND)) {
244 result = TRUE;
245 *resp = 1; /* Internal response code */
246 }
247
248 return result;
249}
250
251/***********************************************************************
252 *
253 * smtp_get_message()
254 *
255 * Gets the authentication message from the response buffer.
256 */
257static CURLcode smtp_get_message(struct Curl_easy *data, struct bufref *out)
258{
259 char *message = data->state.buffer;
260 size_t len = strlen(message);
261
262 if(len > 4) {
263 /* Find the start of the message */
264 len -= 4;
265 for(message += 4; *message == ' ' || *message == '\t'; message++, len--)
266 ;
267
268 /* Find the end of the message */
269 while(len--)
270 if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' &&
271 message[len] != '\t')
272 break;
273
274 /* Terminate the message */
275 message[++len] = '\0';
276 Curl_bufref_set(out, message, len, NULL);
277 }
278 else
279 /* junk input => zero length output */
280 Curl_bufref_set(out, "", 0, NULL);
281
282 return CURLE_OK;
283}
284
285/***********************************************************************
286 *
287 * state()
288 *
289 * This is the ONLY way to change SMTP state!
290 */
291static void state(struct Curl_easy *data, smtpstate newstate)
292{
293 struct smtp_conn *smtpc = &data->conn->proto.smtpc;
294#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
295 /* for debug purposes */
296 static const char * const names[] = {
297 "STOP",
298 "SERVERGREET",
299 "EHLO",
300 "HELO",
301 "STARTTLS",
302 "UPGRADETLS",
303 "AUTH",
304 "COMMAND",
305 "MAIL",
306 "RCPT",
307 "DATA",
308 "POSTDATA",
309 "QUIT",
310 /* LAST */
311 };
312
313 if(smtpc->state != newstate)
314 infof(data, "SMTP %p state change from %s to %s",
315 (void *)smtpc, names[smtpc->state], names[newstate]);
316#endif
317
318 smtpc->state = newstate;
319}
320
321/***********************************************************************
322 *
323 * smtp_perform_ehlo()
324 *
325 * Sends the EHLO command to not only initialise communication with the ESMTP
326 * server but to also obtain a list of server side supported capabilities.
327 */
328static CURLcode smtp_perform_ehlo(struct Curl_easy *data)
329{
330 CURLcode result = CURLE_OK;
331 struct connectdata *conn = data->conn;
332 struct smtp_conn *smtpc = &conn->proto.smtpc;
333
334 smtpc->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanism yet */
335 smtpc->sasl.authused = SASL_AUTH_NONE; /* Clear the authentication mechanism
336 used for esmtp connections */
337 smtpc->tls_supported = FALSE; /* Clear the TLS capability */
338 smtpc->auth_supported = FALSE; /* Clear the AUTH capability */
339
340 /* Send the EHLO command */
341 result = Curl_pp_sendf(data, &smtpc->pp, "EHLO %s", smtpc->domain);
342
343 if(!result)
344 state(data, SMTP_EHLO);
345
346 return result;
347}
348
349/***********************************************************************
350 *
351 * smtp_perform_helo()
352 *
353 * Sends the HELO command to initialise communication with the SMTP server.
354 */
355static CURLcode smtp_perform_helo(struct Curl_easy *data,
356 struct connectdata *conn)
357{
358 CURLcode result = CURLE_OK;
359 struct smtp_conn *smtpc = &conn->proto.smtpc;
360
361 smtpc->sasl.authused = SASL_AUTH_NONE; /* No authentication mechanism used
362 in smtp connections */
363
364 /* Send the HELO command */
365 result = Curl_pp_sendf(data, &smtpc->pp, "HELO %s", smtpc->domain);
366
367 if(!result)
368 state(data, SMTP_HELO);
369
370 return result;
371}
372
373/***********************************************************************
374 *
375 * smtp_perform_starttls()
376 *
377 * Sends the STLS command to start the upgrade to TLS.
378 */
379static CURLcode smtp_perform_starttls(struct Curl_easy *data,
380 struct connectdata *conn)
381{
382 /* Send the STARTTLS command */
383 CURLcode result = Curl_pp_sendf(data, &conn->proto.smtpc.pp,
384 "%s", "STARTTLS");
385
386 if(!result)
387 state(data, SMTP_STARTTLS);
388
389 return result;
390}
391
392/***********************************************************************
393 *
394 * smtp_perform_upgrade_tls()
395 *
396 * Performs the upgrade to TLS.
397 */
398static CURLcode smtp_perform_upgrade_tls(struct Curl_easy *data)
399{
400 /* Start the SSL connection */
401 struct connectdata *conn = data->conn;
402 struct smtp_conn *smtpc = &conn->proto.smtpc;
403 CURLcode result = Curl_ssl_connect_nonblocking(data, conn, FALSE,
404 FIRSTSOCKET,
405 &smtpc->ssldone);
406
407 if(!result) {
408 if(smtpc->state != SMTP_UPGRADETLS)
409 state(data, SMTP_UPGRADETLS);
410
411 if(smtpc->ssldone) {
412 smtp_to_smtps(conn);
413 result = smtp_perform_ehlo(data);
414 }
415 }
416
417 return result;
418}
419
420/***********************************************************************
421 *
422 * smtp_perform_auth()
423 *
424 * Sends an AUTH command allowing the client to login with the given SASL
425 * authentication mechanism.
426 */
427static CURLcode smtp_perform_auth(struct Curl_easy *data,
428 const char *mech,
429 const struct bufref *initresp)
430{
431 CURLcode result = CURLE_OK;
432 struct smtp_conn *smtpc = &data->conn->proto.smtpc;
433 const char *ir = (const char *) Curl_bufref_ptr(initresp);
434
435 if(ir) { /* AUTH <mech> ...<crlf> */
436 /* Send the AUTH command with the initial response */
437 result = Curl_pp_sendf(data, &smtpc->pp, "AUTH %s %s", mech, ir);
438 }
439 else {
440 /* Send the AUTH command */
441 result = Curl_pp_sendf(data, &smtpc->pp, "AUTH %s", mech);
442 }
443
444 return result;
445}
446
447/***********************************************************************
448 *
449 * smtp_continue_auth()
450 *
451 * Sends SASL continuation data.
452 */
453static CURLcode smtp_continue_auth(struct Curl_easy *data,
454 const char *mech,
455 const struct bufref *resp)
456{
457 struct smtp_conn *smtpc = &data->conn->proto.smtpc;
458
459 (void)mech;
460
461 return Curl_pp_sendf(data, &smtpc->pp,
462 "%s", (const char *) Curl_bufref_ptr(resp));
463}
464
465/***********************************************************************
466 *
467 * smtp_cancel_auth()
468 *
469 * Sends SASL cancellation.
470 */
471static CURLcode smtp_cancel_auth(struct Curl_easy *data, const char *mech)
472{
473 struct smtp_conn *smtpc = &data->conn->proto.smtpc;
474
475 (void)mech;
476
477 return Curl_pp_sendf(data, &smtpc->pp, "*");
478}
479
480/***********************************************************************
481 *
482 * smtp_perform_authentication()
483 *
484 * Initiates the authentication sequence, with the appropriate SASL
485 * authentication mechanism.
486 */
487static CURLcode smtp_perform_authentication(struct Curl_easy *data)
488{
489 CURLcode result = CURLE_OK;
490 struct connectdata *conn = data->conn;
491 struct smtp_conn *smtpc = &conn->proto.smtpc;
492 saslprogress progress;
493
494 /* Check we have enough data to authenticate with, and the
495 server supports authentication, and end the connect phase if not */
496 if(!smtpc->auth_supported ||
497 !Curl_sasl_can_authenticate(&smtpc->sasl, data)) {
498 state(data, SMTP_STOP);
499 return result;
500 }
501
502 /* Calculate the SASL login details */
503 result = Curl_sasl_start(&smtpc->sasl, data, FALSE, &progress);
504
505 if(!result) {
506 if(progress == SASL_INPROGRESS)
507 state(data, SMTP_AUTH);
508 else {
509 /* Other mechanisms not supported */
510 infof(data, "No known authentication mechanisms supported");
511 result = CURLE_LOGIN_DENIED;
512 }
513 }
514
515 return result;
516}
517
518/***********************************************************************
519 *
520 * smtp_perform_command()
521 *
522 * Sends a SMTP based command.
523 */
524static CURLcode smtp_perform_command(struct Curl_easy *data)
525{
526 CURLcode result = CURLE_OK;
527 struct connectdata *conn = data->conn;
528 struct SMTP *smtp = data->req.p.smtp;
529
530 if(smtp->rcpt) {
531 /* We notify the server we are sending UTF-8 data if a) it supports the
532 SMTPUTF8 extension and b) The mailbox contains UTF-8 characters, in
533 either the local address or host name parts. This is regardless of
534 whether the host name is encoded using IDN ACE */
535 bool utf8 = FALSE;
536
537 if((!smtp->custom) || (!smtp->custom[0])) {
538 char *address = NULL;
539 struct hostname host = { NULL, NULL, NULL, NULL };
540
541 /* Parse the mailbox to verify into the local address and host name
542 parts, converting the host name to an IDN A-label if necessary */
543 result = smtp_parse_address(data, smtp->rcpt->data,
544 &address, &host);
545 if(result)
546 return result;
547
548 /* Establish whether we should report SMTPUTF8 to the server for this
549 mailbox as per RFC-6531 sect. 3.1 point 6 */
550 utf8 = (conn->proto.smtpc.utf8_supported) &&
551 ((host.encalloc) || (!Curl_is_ASCII_name(address)) ||
552 (!Curl_is_ASCII_name(host.name)));
553
554 /* Send the VRFY command (Note: The host name part may be absent when the
555 host is a local system) */
556 result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "VRFY %s%s%s%s",
557 address,
558 host.name ? "@" : "",
559 host.name ? host.name : "",
560 utf8 ? " SMTPUTF8" : "");
561
562 Curl_free_idnconverted_hostname(&host);
563 free(address);
564 }
565 else {
566 /* Establish whether we should report that we support SMTPUTF8 for EXPN
567 commands to the server as per RFC-6531 sect. 3.1 point 6 */
568 utf8 = (conn->proto.smtpc.utf8_supported) &&
569 (!strcmp(smtp->custom, "EXPN"));
570
571 /* Send the custom recipient based command such as the EXPN command */
572 result = Curl_pp_sendf(data, &conn->proto.smtpc.pp,
573 "%s %s%s", smtp->custom,
574 smtp->rcpt->data,
575 utf8 ? " SMTPUTF8" : "");
576 }
577 }
578 else
579 /* Send the non-recipient based command such as HELP */
580 result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "%s",
581 smtp->custom && smtp->custom[0] != '\0' ?
582 smtp->custom : "HELP");
583
584 if(!result)
585 state(data, SMTP_COMMAND);
586
587 return result;
588}
589
590/***********************************************************************
591 *
592 * smtp_perform_mail()
593 *
594 * Sends an MAIL command to initiate the upload of a message.
595 */
596static CURLcode smtp_perform_mail(struct Curl_easy *data)
597{
598 char *from = NULL;
599 char *auth = NULL;
600 char *size = NULL;
601 CURLcode result = CURLE_OK;
602 struct connectdata *conn = data->conn;
603
604 /* We notify the server we are sending UTF-8 data if a) it supports the
605 SMTPUTF8 extension and b) The mailbox contains UTF-8 characters, in
606 either the local address or host name parts. This is regardless of
607 whether the host name is encoded using IDN ACE */
608 bool utf8 = FALSE;
609
610 /* Calculate the FROM parameter */
611 if(data->set.str[STRING_MAIL_FROM]) {
612 char *address = NULL;
613 struct hostname host = { NULL, NULL, NULL, NULL };
614
615 /* Parse the FROM mailbox into the local address and host name parts,
616 converting the host name to an IDN A-label if necessary */
617 result = smtp_parse_address(data, data->set.str[STRING_MAIL_FROM],
618 &address, &host);
619 if(result)
620 return result;
621
622 /* Establish whether we should report SMTPUTF8 to the server for this
623 mailbox as per RFC-6531 sect. 3.1 point 4 and sect. 3.4 */
624 utf8 = (conn->proto.smtpc.utf8_supported) &&
625 ((host.encalloc) || (!Curl_is_ASCII_name(address)) ||
626 (!Curl_is_ASCII_name(host.name)));
627
628 if(host.name) {
629 from = aprintf("<%s@%s>", address, host.name);
630
631 Curl_free_idnconverted_hostname(&host);
632 }
633 else
634 /* An invalid mailbox was provided but we'll simply let the server worry
635 about that and reply with a 501 error */
636 from = aprintf("<%s>", address);
637
638 free(address);
639 }
640 else
641 /* Null reverse-path, RFC-5321, sect. 3.6.3 */
642 from = strdup("<>");
643
644 if(!from)
645 return CURLE_OUT_OF_MEMORY;
646
647 /* Calculate the optional AUTH parameter */
648 if(data->set.str[STRING_MAIL_AUTH] && conn->proto.smtpc.sasl.authused) {
649 if(data->set.str[STRING_MAIL_AUTH][0] != '\0') {
650 char *address = NULL;
651 struct hostname host = { NULL, NULL, NULL, NULL };
652
653 /* Parse the AUTH mailbox into the local address and host name parts,
654 converting the host name to an IDN A-label if necessary */
655 result = smtp_parse_address(data, data->set.str[STRING_MAIL_AUTH],
656 &address, &host);
657 if(result) {
658 free(from);
659 return result;
660 }
661
662 /* Establish whether we should report SMTPUTF8 to the server for this
663 mailbox as per RFC-6531 sect. 3.1 point 4 and sect. 3.4 */
664 if((!utf8) && (conn->proto.smtpc.utf8_supported) &&
665 ((host.encalloc) || (!Curl_is_ASCII_name(address)) ||
666 (!Curl_is_ASCII_name(host.name))))
667 utf8 = TRUE;
668
669 if(host.name) {
670 auth = aprintf("<%s@%s>", address, host.name);
671
672 Curl_free_idnconverted_hostname(&host);
673 }
674 else
675 /* An invalid mailbox was provided but we'll simply let the server
676 worry about it */
677 auth = aprintf("<%s>", address);
678
679 free(address);
680 }
681 else
682 /* Empty AUTH, RFC-2554, sect. 5 */
683 auth = strdup("<>");
684
685 if(!auth) {
686 free(from);
687
688 return CURLE_OUT_OF_MEMORY;
689 }
690 }
691
692 /* Prepare the mime data if some. */
693 if(data->set.mimepost.kind != MIMEKIND_NONE) {
694 /* Use the whole structure as data. */
695 data->set.mimepost.flags &= ~MIME_BODY_ONLY;
696
697 /* Add external headers and mime version. */
698 curl_mime_headers(&data->set.mimepost, data->set.headers, 0);
699 result = Curl_mime_prepare_headers(&data->set.mimepost, NULL,
700 NULL, MIMESTRATEGY_MAIL);
701
702 if(!result)
703 if(!Curl_checkheaders(data, STRCONST("Mime-Version")))
704 result = Curl_mime_add_header(&data->set.mimepost.curlheaders,
705 "Mime-Version: 1.0");
706
707 /* Make sure we will read the entire mime structure. */
708 if(!result)
709 result = Curl_mime_rewind(&data->set.mimepost);
710
711 if(result) {
712 free(from);
713 free(auth);
714
715 return result;
716 }
717
718 data->state.infilesize = Curl_mime_size(&data->set.mimepost);
719
720 /* Read from mime structure. */
721 data->state.fread_func = (curl_read_callback) Curl_mime_read;
722 data->state.in = (void *) &data->set.mimepost;
723 }
724
725 /* Calculate the optional SIZE parameter */
726 if(conn->proto.smtpc.size_supported && data->state.infilesize > 0) {
727 size = aprintf("%" CURL_FORMAT_CURL_OFF_T, data->state.infilesize);
728
729 if(!size) {
730 free(from);
731 free(auth);
732
733 return CURLE_OUT_OF_MEMORY;
734 }
735 }
736
737 /* If the mailboxes in the FROM and AUTH parameters don't include a UTF-8
738 based address then quickly scan through the recipient list and check if
739 any there do, as we need to correctly identify our support for SMTPUTF8
740 in the envelope, as per RFC-6531 sect. 3.4 */
741 if(conn->proto.smtpc.utf8_supported && !utf8) {
742 struct SMTP *smtp = data->req.p.smtp;
743 struct curl_slist *rcpt = smtp->rcpt;
744
745 while(rcpt && !utf8) {
746 /* Does the host name contain non-ASCII characters? */
747 if(!Curl_is_ASCII_name(rcpt->data))
748 utf8 = TRUE;
749
750 rcpt = rcpt->next;
751 }
752 }
753
754 /* Send the MAIL command */
755 result = Curl_pp_sendf(data, &conn->proto.smtpc.pp,
756 "MAIL FROM:%s%s%s%s%s%s",
757 from, /* Mandatory */
758 auth ? " AUTH=" : "", /* Optional on AUTH support */
759 auth ? auth : "", /* */
760 size ? " SIZE=" : "", /* Optional on SIZE support */
761 size ? size : "", /* */
762 utf8 ? " SMTPUTF8" /* Internationalised mailbox */
763 : ""); /* included in our envelope */
764
765 free(from);
766 free(auth);
767 free(size);
768
769 if(!result)
770 state(data, SMTP_MAIL);
771
772 return result;
773}
774
775/***********************************************************************
776 *
777 * smtp_perform_rcpt_to()
778 *
779 * Sends a RCPT TO command for a given recipient as part of the message upload
780 * process.
781 */
782static CURLcode smtp_perform_rcpt_to(struct Curl_easy *data)
783{
784 CURLcode result = CURLE_OK;
785 struct connectdata *conn = data->conn;
786 struct SMTP *smtp = data->req.p.smtp;
787 char *address = NULL;
788 struct hostname host = { NULL, NULL, NULL, NULL };
789
790 /* Parse the recipient mailbox into the local address and host name parts,
791 converting the host name to an IDN A-label if necessary */
792 result = smtp_parse_address(data, smtp->rcpt->data,
793 &address, &host);
794 if(result)
795 return result;
796
797 /* Send the RCPT TO command */
798 if(host.name)
799 result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "RCPT TO:<%s@%s>",
800 address, host.name);
801 else
802 /* An invalid mailbox was provided but we'll simply let the server worry
803 about that and reply with a 501 error */
804 result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "RCPT TO:<%s>",
805 address);
806
807 Curl_free_idnconverted_hostname(&host);
808 free(address);
809
810 if(!result)
811 state(data, SMTP_RCPT);
812
813 return result;
814}
815
816/***********************************************************************
817 *
818 * smtp_perform_quit()
819 *
820 * Performs the quit action prior to sclose() being called.
821 */
822static CURLcode smtp_perform_quit(struct Curl_easy *data,
823 struct connectdata *conn)
824{
825 /* Send the QUIT command */
826 CURLcode result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "%s", "QUIT");
827
828 if(!result)
829 state(data, SMTP_QUIT);
830
831 return result;
832}
833
834/* For the initial server greeting */
835static CURLcode smtp_state_servergreet_resp(struct Curl_easy *data,
836 int smtpcode,
837 smtpstate instate)
838{
839 CURLcode result = CURLE_OK;
840 (void)instate; /* no use for this yet */
841
842 if(smtpcode/100 != 2) {
843 failf(data, "Got unexpected smtp-server response: %d", smtpcode);
844 result = CURLE_WEIRD_SERVER_REPLY;
845 }
846 else
847 result = smtp_perform_ehlo(data);
848
849 return result;
850}
851
852/* For STARTTLS responses */
853static CURLcode smtp_state_starttls_resp(struct Curl_easy *data,
854 int smtpcode,
855 smtpstate instate)
856{
857 CURLcode result = CURLE_OK;
858 (void)instate; /* no use for this yet */
859
860 /* Pipelining in response is forbidden. */
861 if(data->conn->proto.smtpc.pp.cache_size)
862 return CURLE_WEIRD_SERVER_REPLY;
863
864 if(smtpcode != 220) {
865 if(data->set.use_ssl != CURLUSESSL_TRY) {
866 failf(data, "STARTTLS denied, code %d", smtpcode);
867 result = CURLE_USE_SSL_FAILED;
868 }
869 else
870 result = smtp_perform_authentication(data);
871 }
872 else
873 result = smtp_perform_upgrade_tls(data);
874
875 return result;
876}
877
878/* For EHLO responses */
879static CURLcode smtp_state_ehlo_resp(struct Curl_easy *data,
880 struct connectdata *conn, int smtpcode,
881 smtpstate instate)
882{
883 CURLcode result = CURLE_OK;
884 struct smtp_conn *smtpc = &conn->proto.smtpc;
885 const char *line = data->state.buffer;
886 size_t len = strlen(line);
887
888 (void)instate; /* no use for this yet */
889
890 if(smtpcode/100 != 2 && smtpcode != 1) {
891 if(data->set.use_ssl <= CURLUSESSL_TRY || conn->ssl[FIRSTSOCKET].use)
892 result = smtp_perform_helo(data, conn);
893 else {
894 failf(data, "Remote access denied: %d", smtpcode);
895 result = CURLE_REMOTE_ACCESS_DENIED;
896 }
897 }
898 else if(len >= 4) {
899 line += 4;
900 len -= 4;
901
902 /* Does the server support the STARTTLS capability? */
903 if(len >= 8 && !memcmp(line, "STARTTLS", 8))
904 smtpc->tls_supported = TRUE;
905
906 /* Does the server support the SIZE capability? */
907 else if(len >= 4 && !memcmp(line, "SIZE", 4))
908 smtpc->size_supported = TRUE;
909
910 /* Does the server support the UTF-8 capability? */
911 else if(len >= 8 && !memcmp(line, "SMTPUTF8", 8))
912 smtpc->utf8_supported = TRUE;
913
914 /* Does the server support authentication? */
915 else if(len >= 5 && !memcmp(line, "AUTH ", 5)) {
916 smtpc->auth_supported = TRUE;
917
918 /* Advance past the AUTH keyword */
919 line += 5;
920 len -= 5;
921
922 /* Loop through the data line */
923 for(;;) {
924 size_t llen;
925 size_t wordlen;
926 unsigned short mechbit;
927
928 while(len &&
929 (*line == ' ' || *line == '\t' ||
930 *line == '\r' || *line == '\n')) {
931
932 line++;
933 len--;
934 }
935
936 if(!len)
937 break;
938
939 /* Extract the word */
940 for(wordlen = 0; wordlen < len && line[wordlen] != ' ' &&
941 line[wordlen] != '\t' && line[wordlen] != '\r' &&
942 line[wordlen] != '\n';)
943 wordlen++;
944
945 /* Test the word for a matching authentication mechanism */
946 mechbit = Curl_sasl_decode_mech(line, wordlen, &llen);
947 if(mechbit && llen == wordlen)
948 smtpc->sasl.authmechs |= mechbit;
949
950 line += wordlen;
951 len -= wordlen;
952 }
953 }
954
955 if(smtpcode != 1) {
956 if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
957 /* We don't have a SSL/TLS connection yet, but SSL is requested */
958 if(smtpc->tls_supported)
959 /* Switch to TLS connection now */
960 result = smtp_perform_starttls(data, conn);
961 else if(data->set.use_ssl == CURLUSESSL_TRY)
962 /* Fallback and carry on with authentication */
963 result = smtp_perform_authentication(data);
964 else {
965 failf(data, "STARTTLS not supported.");
966 result = CURLE_USE_SSL_FAILED;
967 }
968 }
969 else
970 result = smtp_perform_authentication(data);
971 }
972 }
973 else {
974 failf(data, "Unexpectedly short EHLO response");
975 result = CURLE_WEIRD_SERVER_REPLY;
976 }
977
978 return result;
979}
980
981/* For HELO responses */
982static CURLcode smtp_state_helo_resp(struct Curl_easy *data, int smtpcode,
983 smtpstate instate)
984{
985 CURLcode result = CURLE_OK;
986 (void)instate; /* no use for this yet */
987
988 if(smtpcode/100 != 2) {
989 failf(data, "Remote access denied: %d", smtpcode);
990 result = CURLE_REMOTE_ACCESS_DENIED;
991 }
992 else
993 /* End of connect phase */
994 state(data, SMTP_STOP);
995
996 return result;
997}
998
999/* For SASL authentication responses */
1000static CURLcode smtp_state_auth_resp(struct Curl_easy *data,
1001 int smtpcode,
1002 smtpstate instate)
1003{
1004 CURLcode result = CURLE_OK;
1005 struct connectdata *conn = data->conn;
1006 struct smtp_conn *smtpc = &conn->proto.smtpc;
1007 saslprogress progress;
1008
1009 (void)instate; /* no use for this yet */
1010
1011 result = Curl_sasl_continue(&smtpc->sasl, data, smtpcode, &progress);
1012 if(!result)
1013 switch(progress) {
1014 case SASL_DONE:
1015 state(data, SMTP_STOP); /* Authenticated */
1016 break;
1017 case SASL_IDLE: /* No mechanism left after cancellation */
1018 failf(data, "Authentication cancelled");
1019 result = CURLE_LOGIN_DENIED;
1020 break;
1021 default:
1022 break;
1023 }
1024
1025 return result;
1026}
1027
1028/* For command responses */
1029static CURLcode smtp_state_command_resp(struct Curl_easy *data, int smtpcode,
1030 smtpstate instate)
1031{
1032 CURLcode result = CURLE_OK;
1033 struct SMTP *smtp = data->req.p.smtp;
1034 char *line = data->state.buffer;
1035 size_t len = strlen(line);
1036
1037 (void)instate; /* no use for this yet */
1038
1039 if((smtp->rcpt && smtpcode/100 != 2 && smtpcode != 553 && smtpcode != 1) ||
1040 (!smtp->rcpt && smtpcode/100 != 2 && smtpcode != 1)) {
1041 failf(data, "Command failed: %d", smtpcode);
1042 result = CURLE_WEIRD_SERVER_REPLY;
1043 }
1044 else {
1045 /* Temporarily add the LF character back and send as body to the client */
1046 if(!data->set.opt_no_body) {
1047 line[len] = '\n';
1048 result = Curl_client_write(data, CLIENTWRITE_BODY, line, len + 1);
1049 line[len] = '\0';
1050 }
1051
1052 if(smtpcode != 1) {
1053 if(smtp->rcpt) {
1054 smtp->rcpt = smtp->rcpt->next;
1055
1056 if(smtp->rcpt) {
1057 /* Send the next command */
1058 result = smtp_perform_command(data);
1059 }
1060 else
1061 /* End of DO phase */
1062 state(data, SMTP_STOP);
1063 }
1064 else
1065 /* End of DO phase */
1066 state(data, SMTP_STOP);
1067 }
1068 }
1069
1070 return result;
1071}
1072
1073/* For MAIL responses */
1074static CURLcode smtp_state_mail_resp(struct Curl_easy *data, int smtpcode,
1075 smtpstate instate)
1076{
1077 CURLcode result = CURLE_OK;
1078 (void)instate; /* no use for this yet */
1079
1080 if(smtpcode/100 != 2) {
1081 failf(data, "MAIL failed: %d", smtpcode);
1082 result = CURLE_SEND_ERROR;
1083 }
1084 else
1085 /* Start the RCPT TO command */
1086 result = smtp_perform_rcpt_to(data);
1087
1088 return result;
1089}
1090
1091/* For RCPT responses */
1092static CURLcode smtp_state_rcpt_resp(struct Curl_easy *data,
1093 struct connectdata *conn, int smtpcode,
1094 smtpstate instate)
1095{
1096 CURLcode result = CURLE_OK;
1097 struct SMTP *smtp = data->req.p.smtp;
1098 bool is_smtp_err = FALSE;
1099 bool is_smtp_blocking_err = FALSE;
1100
1101 (void)instate; /* no use for this yet */
1102
1103 is_smtp_err = (smtpcode/100 != 2) ? TRUE : FALSE;
1104
1105 /* If there's multiple RCPT TO to be issued, it's possible to ignore errors
1106 and proceed with only the valid addresses. */
1107 is_smtp_blocking_err =
1108 (is_smtp_err && !data->set.mail_rcpt_allowfails) ? TRUE : FALSE;
1109
1110 if(is_smtp_err) {
1111 /* Remembering the last failure which we can report if all "RCPT TO" have
1112 failed and we cannot proceed. */
1113 smtp->rcpt_last_error = smtpcode;
1114
1115 if(is_smtp_blocking_err) {
1116 failf(data, "RCPT failed: %d", smtpcode);
1117 result = CURLE_SEND_ERROR;
1118 }
1119 }
1120 else {
1121 /* Some RCPT TO commands have succeeded. */
1122 smtp->rcpt_had_ok = TRUE;
1123 }
1124
1125 if(!is_smtp_blocking_err) {
1126 smtp->rcpt = smtp->rcpt->next;
1127
1128 if(smtp->rcpt)
1129 /* Send the next RCPT TO command */
1130 result = smtp_perform_rcpt_to(data);
1131 else {
1132 /* We weren't able to issue a successful RCPT TO command while going
1133 over recipients (potentially multiple). Sending back last error. */
1134 if(!smtp->rcpt_had_ok) {
1135 failf(data, "RCPT failed: %d (last error)", smtp->rcpt_last_error);
1136 result = CURLE_SEND_ERROR;
1137 }
1138 else {
1139 /* Send the DATA command */
1140 result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "%s", "DATA");
1141
1142 if(!result)
1143 state(data, SMTP_DATA);
1144 }
1145 }
1146 }
1147
1148 return result;
1149}
1150
1151/* For DATA response */
1152static CURLcode smtp_state_data_resp(struct Curl_easy *data, int smtpcode,
1153 smtpstate instate)
1154{
1155 CURLcode result = CURLE_OK;
1156 (void)instate; /* no use for this yet */
1157
1158 if(smtpcode != 354) {
1159 failf(data, "DATA failed: %d", smtpcode);
1160 result = CURLE_SEND_ERROR;
1161 }
1162 else {
1163 /* Set the progress upload size */
1164 Curl_pgrsSetUploadSize(data, data->state.infilesize);
1165
1166 /* SMTP upload */
1167 Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET);
1168
1169 /* End of DO phase */
1170 state(data, SMTP_STOP);
1171 }
1172
1173 return result;
1174}
1175
1176/* For POSTDATA responses, which are received after the entire DATA
1177 part has been sent to the server */
1178static CURLcode smtp_state_postdata_resp(struct Curl_easy *data,
1179 int smtpcode,
1180 smtpstate instate)
1181{
1182 CURLcode result = CURLE_OK;
1183
1184 (void)instate; /* no use for this yet */
1185
1186 if(smtpcode != 250)
1187 result = CURLE_WEIRD_SERVER_REPLY;
1188
1189 /* End of DONE phase */
1190 state(data, SMTP_STOP);
1191
1192 return result;
1193}
1194
1195static CURLcode smtp_statemachine(struct Curl_easy *data,
1196 struct connectdata *conn)
1197{
1198 CURLcode result = CURLE_OK;
1199 curl_socket_t sock = conn->sock[FIRSTSOCKET];
1200 int smtpcode;
1201 struct smtp_conn *smtpc = &conn->proto.smtpc;
1202 struct pingpong *pp = &smtpc->pp;
1203 size_t nread = 0;
1204
1205 /* Busy upgrading the connection; right now all I/O is SSL/TLS, not SMTP */
1206 if(smtpc->state == SMTP_UPGRADETLS)
1207 return smtp_perform_upgrade_tls(data);
1208
1209 /* Flush any data that needs to be sent */
1210 if(pp->sendleft)
1211 return Curl_pp_flushsend(data, pp);
1212
1213 do {
1214 /* Read the response from the server */
1215 result = Curl_pp_readresp(data, sock, pp, &smtpcode, &nread);
1216 if(result)
1217 return result;
1218
1219 /* Store the latest response for later retrieval if necessary */
1220 if(smtpc->state != SMTP_QUIT && smtpcode != 1)
1221 data->info.httpcode = smtpcode;
1222
1223 if(!smtpcode)
1224 break;
1225
1226 /* We have now received a full SMTP server response */
1227 switch(smtpc->state) {
1228 case SMTP_SERVERGREET:
1229 result = smtp_state_servergreet_resp(data, smtpcode, smtpc->state);
1230 break;
1231
1232 case SMTP_EHLO:
1233 result = smtp_state_ehlo_resp(data, conn, smtpcode, smtpc->state);
1234 break;
1235
1236 case SMTP_HELO:
1237 result = smtp_state_helo_resp(data, smtpcode, smtpc->state);
1238 break;
1239
1240 case SMTP_STARTTLS:
1241 result = smtp_state_starttls_resp(data, smtpcode, smtpc->state);
1242 break;
1243
1244 case SMTP_AUTH:
1245 result = smtp_state_auth_resp(data, smtpcode, smtpc->state);
1246 break;
1247
1248 case SMTP_COMMAND:
1249 result = smtp_state_command_resp(data, smtpcode, smtpc->state);
1250 break;
1251
1252 case SMTP_MAIL:
1253 result = smtp_state_mail_resp(data, smtpcode, smtpc->state);
1254 break;
1255
1256 case SMTP_RCPT:
1257 result = smtp_state_rcpt_resp(data, conn, smtpcode, smtpc->state);
1258 break;
1259
1260 case SMTP_DATA:
1261 result = smtp_state_data_resp(data, smtpcode, smtpc->state);
1262 break;
1263
1264 case SMTP_POSTDATA:
1265 result = smtp_state_postdata_resp(data, smtpcode, smtpc->state);
1266 break;
1267
1268 case SMTP_QUIT:
1269 /* fallthrough, just stop! */
1270 default:
1271 /* internal error */
1272 state(data, SMTP_STOP);
1273 break;
1274 }
1275 } while(!result && smtpc->state != SMTP_STOP && Curl_pp_moredata(pp));
1276
1277 return result;
1278}
1279
1280/* Called repeatedly until done from multi.c */
1281static CURLcode smtp_multi_statemach(struct Curl_easy *data, bool *done)
1282{
1283 CURLcode result = CURLE_OK;
1284 struct connectdata *conn = data->conn;
1285 struct smtp_conn *smtpc = &conn->proto.smtpc;
1286
1287 if((conn->handler->flags & PROTOPT_SSL) && !smtpc->ssldone) {
1288 result = Curl_ssl_connect_nonblocking(data, conn, FALSE,
1289 FIRSTSOCKET, &smtpc->ssldone);
1290 if(result || !smtpc->ssldone)
1291 return result;
1292 }
1293
1294 result = Curl_pp_statemach(data, &smtpc->pp, FALSE, FALSE);
1295 *done = (smtpc->state == SMTP_STOP) ? TRUE : FALSE;
1296
1297 return result;
1298}
1299
1300static CURLcode smtp_block_statemach(struct Curl_easy *data,
1301 struct connectdata *conn,
1302 bool disconnecting)
1303{
1304 CURLcode result = CURLE_OK;
1305 struct smtp_conn *smtpc = &conn->proto.smtpc;
1306
1307 while(smtpc->state != SMTP_STOP && !result)
1308 result = Curl_pp_statemach(data, &smtpc->pp, TRUE, disconnecting);
1309
1310 return result;
1311}
1312
1313/* Allocate and initialize the SMTP struct for the current Curl_easy if
1314 required */
1315static CURLcode smtp_init(struct Curl_easy *data)
1316{
1317 CURLcode result = CURLE_OK;
1318 struct SMTP *smtp;
1319
1320 smtp = data->req.p.smtp = calloc(sizeof(struct SMTP), 1);
1321 if(!smtp)
1322 result = CURLE_OUT_OF_MEMORY;
1323
1324 return result;
1325}
1326
1327/* For the SMTP "protocol connect" and "doing" phases only */
1328static int smtp_getsock(struct Curl_easy *data,
1329 struct connectdata *conn, curl_socket_t *socks)
1330{
1331 return Curl_pp_getsock(data, &conn->proto.smtpc.pp, socks);
1332}
1333
1334/***********************************************************************
1335 *
1336 * smtp_connect()
1337 *
1338 * This function should do everything that is to be considered a part of
1339 * the connection phase.
1340 *
1341 * The variable pointed to by 'done' will be TRUE if the protocol-layer
1342 * connect phase is done when this function returns, or FALSE if not.
1343 */
1344static CURLcode smtp_connect(struct Curl_easy *data, bool *done)
1345{
1346 CURLcode result = CURLE_OK;
1347 struct connectdata *conn = data->conn;
1348 struct smtp_conn *smtpc = &conn->proto.smtpc;
1349 struct pingpong *pp = &smtpc->pp;
1350
1351 *done = FALSE; /* default to not done yet */
1352
1353 /* We always support persistent connections in SMTP */
1354 connkeep(conn, "SMTP default");
1355
1356 PINGPONG_SETUP(pp, smtp_statemachine, smtp_endofresp);
1357
1358 /* Initialize the SASL storage */
1359 Curl_sasl_init(&smtpc->sasl, data, &saslsmtp);
1360
1361 /* Initialise the pingpong layer */
1362 Curl_pp_setup(pp);
1363 Curl_pp_init(data, pp);
1364
1365 /* Parse the URL options */
1366 result = smtp_parse_url_options(conn);
1367 if(result)
1368 return result;
1369
1370 /* Parse the URL path */
1371 result = smtp_parse_url_path(data);
1372 if(result)
1373 return result;
1374
1375 /* Start off waiting for the server greeting response */
1376 state(data, SMTP_SERVERGREET);
1377
1378 result = smtp_multi_statemach(data, done);
1379
1380 return result;
1381}
1382
1383/***********************************************************************
1384 *
1385 * smtp_done()
1386 *
1387 * The DONE function. This does what needs to be done after a single DO has
1388 * performed.
1389 *
1390 * Input argument is already checked for validity.
1391 */
1392static CURLcode smtp_done(struct Curl_easy *data, CURLcode status,
1393 bool premature)
1394{
1395 CURLcode result = CURLE_OK;
1396 struct connectdata *conn = data->conn;
1397 struct SMTP *smtp = data->req.p.smtp;
1398 struct pingpong *pp = &conn->proto.smtpc.pp;
1399 char *eob;
1400 ssize_t len;
1401 ssize_t bytes_written;
1402
1403 (void)premature;
1404
1405 if(!smtp)
1406 return CURLE_OK;
1407
1408 /* Cleanup our per-request based variables */
1409 Curl_safefree(smtp->custom);
1410
1411 if(status) {
1412 connclose(conn, "SMTP done with bad status"); /* marked for closure */
1413 result = status; /* use the already set error code */
1414 }
1415 else if(!data->set.connect_only && data->set.mail_rcpt &&
1416 (data->set.upload || data->set.mimepost.kind)) {
1417 /* Calculate the EOB taking into account any terminating CRLF from the
1418 previous line of the email or the CRLF of the DATA command when there
1419 is "no mail data". RFC-5321, sect. 4.1.1.4.
1420
1421 Note: As some SSL backends, such as OpenSSL, will cause Curl_write() to
1422 fail when using a different pointer following a previous write, that
1423 returned CURLE_AGAIN, we duplicate the EOB now rather than when the
1424 bytes written doesn't equal len. */
1425 if(smtp->trailing_crlf || !data->state.infilesize) {
1426 eob = strdup(&SMTP_EOB[2]);
1427 len = SMTP_EOB_LEN - 2;
1428 }
1429 else {
1430 eob = strdup(SMTP_EOB);
1431 len = SMTP_EOB_LEN;
1432 }
1433
1434 if(!eob)
1435 return CURLE_OUT_OF_MEMORY;
1436
1437 /* Send the end of block data */
1438 result = Curl_write(data, conn->writesockfd, eob, len, &bytes_written);
1439 if(result) {
1440 free(eob);
1441 return result;
1442 }
1443
1444 if(bytes_written != len) {
1445 /* The whole chunk was not sent so keep it around and adjust the
1446 pingpong structure accordingly */
1447 pp->sendthis = eob;
1448 pp->sendsize = len;
1449 pp->sendleft = len - bytes_written;
1450 }
1451 else {
1452 /* Successfully sent so adjust the response timeout relative to now */
1453 pp->response = Curl_now();
1454
1455 free(eob);
1456 }
1457
1458 state(data, SMTP_POSTDATA);
1459
1460 /* Run the state-machine */
1461 result = smtp_block_statemach(data, conn, FALSE);
1462 }
1463
1464 /* Clear the transfer mode for the next request */
1465 smtp->transfer = PPTRANSFER_BODY;
1466
1467 return result;
1468}
1469
1470/***********************************************************************
1471 *
1472 * smtp_perform()
1473 *
1474 * This is the actual DO function for SMTP. Transfer a mail, send a command
1475 * or get some data according to the options previously setup.
1476 */
1477static CURLcode smtp_perform(struct Curl_easy *data, bool *connected,
1478 bool *dophase_done)
1479{
1480 /* This is SMTP and no proxy */
1481 CURLcode result = CURLE_OK;
1482 struct connectdata *conn = data->conn;
1483 struct SMTP *smtp = data->req.p.smtp;
1484
1485 DEBUGF(infof(data, "DO phase starts"));
1486
1487 if(data->set.opt_no_body) {
1488 /* Requested no body means no transfer */
1489 smtp->transfer = PPTRANSFER_INFO;
1490 }
1491
1492 *dophase_done = FALSE; /* not done yet */
1493
1494 /* Store the first recipient (or NULL if not specified) */
1495 smtp->rcpt = data->set.mail_rcpt;
1496
1497 /* Track of whether we've successfully sent at least one RCPT TO command */
1498 smtp->rcpt_had_ok = FALSE;
1499
1500 /* Track of the last error we've received by sending RCPT TO command */
1501 smtp->rcpt_last_error = 0;
1502
1503 /* Initial data character is the first character in line: it is implicitly
1504 preceded by a virtual CRLF. */
1505 smtp->trailing_crlf = TRUE;
1506 smtp->eob = 2;
1507
1508 /* Start the first command in the DO phase */
1509 if((data->set.upload || data->set.mimepost.kind) && data->set.mail_rcpt)
1510 /* MAIL transfer */
1511 result = smtp_perform_mail(data);
1512 else
1513 /* SMTP based command (VRFY, EXPN, NOOP, RSET or HELP) */
1514 result = smtp_perform_command(data);
1515
1516 if(result)
1517 return result;
1518
1519 /* Run the state-machine */
1520 result = smtp_multi_statemach(data, dophase_done);
1521
1522 *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1523
1524 if(*dophase_done)
1525 DEBUGF(infof(data, "DO phase is complete"));
1526
1527 return result;
1528}
1529
1530/***********************************************************************
1531 *
1532 * smtp_do()
1533 *
1534 * This function is registered as 'curl_do' function. It decodes the path
1535 * parts etc as a wrapper to the actual DO function (smtp_perform).
1536 *
1537 * The input argument is already checked for validity.
1538 */
1539static CURLcode smtp_do(struct Curl_easy *data, bool *done)
1540{
1541 CURLcode result = CURLE_OK;
1542 *done = FALSE; /* default to false */
1543
1544 /* Parse the custom request */
1545 result = smtp_parse_custom_request(data);
1546 if(result)
1547 return result;
1548
1549 result = smtp_regular_transfer(data, done);
1550
1551 return result;
1552}
1553
1554/***********************************************************************
1555 *
1556 * smtp_disconnect()
1557 *
1558 * Disconnect from an SMTP server. Cleanup protocol-specific per-connection
1559 * resources. BLOCKING.
1560 */
1561static CURLcode smtp_disconnect(struct Curl_easy *data,
1562 struct connectdata *conn,
1563 bool dead_connection)
1564{
1565 struct smtp_conn *smtpc = &conn->proto.smtpc;
1566 (void)data;
1567
1568 /* We cannot send quit unconditionally. If this connection is stale or
1569 bad in any way, sending quit and waiting around here will make the
1570 disconnect wait in vain and cause more problems than we need to. */
1571
1572 if(!dead_connection && conn->bits.protoconnstart) {
1573 if(!smtp_perform_quit(data, conn))
1574 (void)smtp_block_statemach(data, conn, TRUE); /* ignore errors on QUIT */
1575 }
1576
1577 /* Disconnect from the server */
1578 Curl_pp_disconnect(&smtpc->pp);
1579
1580 /* Cleanup the SASL module */
1581 Curl_sasl_cleanup(conn, smtpc->sasl.authused);
1582
1583 /* Cleanup our connection based variables */
1584 Curl_safefree(smtpc->domain);
1585
1586 return CURLE_OK;
1587}
1588
1589/* Call this when the DO phase has completed */
1590static CURLcode smtp_dophase_done(struct Curl_easy *data, bool connected)
1591{
1592 struct SMTP *smtp = data->req.p.smtp;
1593
1594 (void)connected;
1595
1596 if(smtp->transfer != PPTRANSFER_BODY)
1597 /* no data to transfer */
1598 Curl_setup_transfer(data, -1, -1, FALSE, -1);
1599
1600 return CURLE_OK;
1601}
1602
1603/* Called from multi.c while DOing */
1604static CURLcode smtp_doing(struct Curl_easy *data, bool *dophase_done)
1605{
1606 CURLcode result = smtp_multi_statemach(data, dophase_done);
1607
1608 if(result)
1609 DEBUGF(infof(data, "DO phase failed"));
1610 else if(*dophase_done) {
1611 result = smtp_dophase_done(data, FALSE /* not connected */);
1612
1613 DEBUGF(infof(data, "DO phase is complete"));
1614 }
1615
1616 return result;
1617}
1618
1619/***********************************************************************
1620 *
1621 * smtp_regular_transfer()
1622 *
1623 * The input argument is already checked for validity.
1624 *
1625 * Performs all commands done before a regular transfer between a local and a
1626 * remote host.
1627 */
1628static CURLcode smtp_regular_transfer(struct Curl_easy *data,
1629 bool *dophase_done)
1630{
1631 CURLcode result = CURLE_OK;
1632 bool connected = FALSE;
1633
1634 /* Make sure size is unknown at this point */
1635 data->req.size = -1;
1636
1637 /* Set the progress data */
1638 Curl_pgrsSetUploadCounter(data, 0);
1639 Curl_pgrsSetDownloadCounter(data, 0);
1640 Curl_pgrsSetUploadSize(data, -1);
1641 Curl_pgrsSetDownloadSize(data, -1);
1642
1643 /* Carry out the perform */
1644 result = smtp_perform(data, &connected, dophase_done);
1645
1646 /* Perform post DO phase operations if necessary */
1647 if(!result && *dophase_done)
1648 result = smtp_dophase_done(data, connected);
1649
1650 return result;
1651}
1652
1653static CURLcode smtp_setup_connection(struct Curl_easy *data,
1654 struct connectdata *conn)
1655{
1656 CURLcode result;
1657
1658 /* Clear the TLS upgraded flag */
1659 conn->bits.tls_upgraded = FALSE;
1660
1661 /* Initialise the SMTP layer */
1662 result = smtp_init(data);
1663 if(result)
1664 return result;
1665
1666 return CURLE_OK;
1667}
1668
1669/***********************************************************************
1670 *
1671 * smtp_parse_url_options()
1672 *
1673 * Parse the URL login options.
1674 */
1675static CURLcode smtp_parse_url_options(struct connectdata *conn)
1676{
1677 CURLcode result = CURLE_OK;
1678 struct smtp_conn *smtpc = &conn->proto.smtpc;
1679 const char *ptr = conn->options;
1680
1681 while(!result && ptr && *ptr) {
1682 const char *key = ptr;
1683 const char *value;
1684
1685 while(*ptr && *ptr != '=')
1686 ptr++;
1687
1688 value = ptr + 1;
1689
1690 while(*ptr && *ptr != ';')
1691 ptr++;
1692
1693 if(strncasecompare(key, "AUTH=", 5))
1694 result = Curl_sasl_parse_url_auth_option(&smtpc->sasl,
1695 value, ptr - value);
1696 else
1697 result = CURLE_URL_MALFORMAT;
1698
1699 if(*ptr == ';')
1700 ptr++;
1701 }
1702
1703 return result;
1704}
1705
1706/***********************************************************************
1707 *
1708 * smtp_parse_url_path()
1709 *
1710 * Parse the URL path into separate path components.
1711 */
1712static CURLcode smtp_parse_url_path(struct Curl_easy *data)
1713{
1714 /* The SMTP struct is already initialised in smtp_connect() */
1715 struct connectdata *conn = data->conn;
1716 struct smtp_conn *smtpc = &conn->proto.smtpc;
1717 const char *path = &data->state.up.path[1]; /* skip leading path */
1718 char localhost[HOSTNAME_MAX + 1];
1719
1720 /* Calculate the path if necessary */
1721 if(!*path) {
1722 if(!Curl_gethostname(localhost, sizeof(localhost)))
1723 path = localhost;
1724 else
1725 path = "localhost";
1726 }
1727
1728 /* URL decode the path and use it as the domain in our EHLO */
1729 return Curl_urldecode(path, 0, &smtpc->domain, NULL, REJECT_CTRL);
1730}
1731
1732/***********************************************************************
1733 *
1734 * smtp_parse_custom_request()
1735 *
1736 * Parse the custom request.
1737 */
1738static CURLcode smtp_parse_custom_request(struct Curl_easy *data)
1739{
1740 CURLcode result = CURLE_OK;
1741 struct SMTP *smtp = data->req.p.smtp;
1742 const char *custom = data->set.str[STRING_CUSTOMREQUEST];
1743
1744 /* URL decode the custom request */
1745 if(custom)
1746 result = Curl_urldecode(custom, 0, &smtp->custom, NULL, REJECT_CTRL);
1747
1748 return result;
1749}
1750
1751/***********************************************************************
1752 *
1753 * smtp_parse_address()
1754 *
1755 * Parse the fully qualified mailbox address into a local address part and the
1756 * host name, converting the host name to an IDN A-label, as per RFC-5890, if
1757 * necessary.
1758 *
1759 * Parameters:
1760 *
1761 * conn [in] - The connection handle.
1762 * fqma [in] - The fully qualified mailbox address (which may or
1763 * may not contain UTF-8 characters).
1764 * address [in/out] - A new allocated buffer which holds the local
1765 * address part of the mailbox. This buffer must be
1766 * free'ed by the caller.
1767 * host [in/out] - The host name structure that holds the original,
1768 * and optionally encoded, host name.
1769 * Curl_free_idnconverted_hostname() must be called
1770 * once the caller has finished with the structure.
1771 *
1772 * Returns CURLE_OK on success.
1773 *
1774 * Notes:
1775 *
1776 * Should a UTF-8 host name require conversion to IDN ACE and we cannot honor
1777 * that conversion then we shall return success. This allow the caller to send
1778 * the data to the server as a U-label (as per RFC-6531 sect. 3.2).
1779 *
1780 * If an mailbox '@' separator cannot be located then the mailbox is considered
1781 * to be either a local mailbox or an invalid mailbox (depending on what the
1782 * calling function deems it to be) then the input will simply be returned in
1783 * the address part with the host name being NULL.
1784 */
1785static CURLcode smtp_parse_address(struct Curl_easy *data, const char *fqma,
1786 char **address, struct hostname *host)
1787{
1788 CURLcode result = CURLE_OK;
1789 size_t length;
1790
1791 /* Duplicate the fully qualified email address so we can manipulate it,
1792 ensuring it doesn't contain the delimiters if specified */
1793 char *dup = strdup(fqma[0] == '<' ? fqma + 1 : fqma);
1794 if(!dup)
1795 return CURLE_OUT_OF_MEMORY;
1796
1797 length = strlen(dup);
1798 if(length) {
1799 if(dup[length - 1] == '>')
1800 dup[length - 1] = '\0';
1801 }
1802
1803 /* Extract the host name from the address (if we can) */
1804 host->name = strpbrk(dup, "@");
1805 if(host->name) {
1806 *host->name = '\0';
1807 host->name = host->name + 1;
1808
1809 /* Attempt to convert the host name to IDN ACE */
1810 (void) Curl_idnconvert_hostname(data, host);
1811
1812 /* If Curl_idnconvert_hostname() fails then we shall attempt to continue
1813 and send the host name using UTF-8 rather than as 7-bit ACE (which is
1814 our preference) */
1815 }
1816
1817 /* Extract the local address from the mailbox */
1818 *address = dup;
1819
1820 return result;
1821}
1822
1823CURLcode Curl_smtp_escape_eob(struct Curl_easy *data,
1824 const ssize_t nread,
1825 const ssize_t offset)
1826{
1827 /* When sending a SMTP payload we must detect CRLF. sequences making sure
1828 they are sent as CRLF.. instead, as a . on the beginning of a line will
1829 be deleted by the server when not part of an EOB terminator and a
1830 genuine CRLF.CRLF which isn't escaped will wrongly be detected as end of
1831 data by the server
1832 */
1833 ssize_t i;
1834 ssize_t si;
1835 struct SMTP *smtp = data->req.p.smtp;
1836 char *scratch = data->state.scratch;
1837 char *newscratch = NULL;
1838 char *oldscratch = NULL;
1839 size_t eob_sent;
1840
1841 /* Do we need to allocate a scratch buffer? */
1842 if(!scratch || data->set.crlf) {
1843 oldscratch = scratch;
1844
1845 scratch = newscratch = malloc(2 * data->set.upload_buffer_size);
1846 if(!newscratch) {
1847 failf(data, "Failed to alloc scratch buffer");
1848
1849 return CURLE_OUT_OF_MEMORY;
1850 }
1851 }
1852 DEBUGASSERT((size_t)data->set.upload_buffer_size >= (size_t)nread);
1853
1854 /* Have we already sent part of the EOB? */
1855 eob_sent = smtp->eob;
1856
1857 /* This loop can be improved by some kind of Boyer-Moore style of
1858 approach but that is saved for later... */
1859 if(offset)
1860 memcpy(scratch, data->req.upload_fromhere, offset);
1861 for(i = offset, si = offset; i < nread; i++) {
1862 if(SMTP_EOB[smtp->eob] == data->req.upload_fromhere[i]) {
1863 smtp->eob++;
1864
1865 /* Is the EOB potentially the terminating CRLF? */
1866 if(2 == smtp->eob || SMTP_EOB_LEN == smtp->eob)
1867 smtp->trailing_crlf = TRUE;
1868 else
1869 smtp->trailing_crlf = FALSE;
1870 }
1871 else if(smtp->eob) {
1872 /* A previous substring matched so output that first */
1873 memcpy(&scratch[si], &SMTP_EOB[eob_sent], smtp->eob - eob_sent);
1874 si += smtp->eob - eob_sent;
1875
1876 /* Then compare the first byte */
1877 if(SMTP_EOB[0] == data->req.upload_fromhere[i])
1878 smtp->eob = 1;
1879 else
1880 smtp->eob = 0;
1881
1882 eob_sent = 0;
1883
1884 /* Reset the trailing CRLF flag as there was more data */
1885 smtp->trailing_crlf = FALSE;
1886 }
1887
1888 /* Do we have a match for CRLF. as per RFC-5321, sect. 4.5.2 */
1889 if(SMTP_EOB_FIND_LEN == smtp->eob) {
1890 /* Copy the replacement data to the target buffer */
1891 memcpy(&scratch[si], &SMTP_EOB_REPL[eob_sent],
1892 SMTP_EOB_REPL_LEN - eob_sent);
1893 si += SMTP_EOB_REPL_LEN - eob_sent;
1894 smtp->eob = 0;
1895 eob_sent = 0;
1896 }
1897 else if(!smtp->eob)
1898 scratch[si++] = data->req.upload_fromhere[i];
1899 }
1900
1901 if(smtp->eob - eob_sent) {
1902 /* A substring matched before processing ended so output that now */
1903 memcpy(&scratch[si], &SMTP_EOB[eob_sent], smtp->eob - eob_sent);
1904 si += smtp->eob - eob_sent;
1905 }
1906
1907 /* Only use the new buffer if we replaced something */
1908 if(si != nread) {
1909 /* Upload from the new (replaced) buffer instead */
1910 data->req.upload_fromhere = scratch;
1911
1912 /* Save the buffer so it can be freed later */
1913 data->state.scratch = scratch;
1914
1915 /* Free the old scratch buffer */
1916 free(oldscratch);
1917
1918 /* Set the new amount too */
1919 data->req.upload_present = si;
1920 }
1921 else
1922 free(newscratch);
1923
1924 return CURLE_OK;
1925}
1926
1927#endif /* CURL_DISABLE_SMTP */
1928