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