1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9 *
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at https://curl.se/docs/copyright.html.
13 *
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 * SPDX-License-Identifier: curl
22 *
23 * RFC1870 SMTP Service Extension for Message Size
24 * RFC2195 CRAM-MD5 authentication
25 * RFC2831 DIGEST-MD5 authentication
26 * RFC3207 SMTP over TLS
27 * RFC4422 Simple Authentication and Security Layer (SASL)
28 * RFC4616 PLAIN authentication
29 * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism
30 * RFC4954 SMTP Authentication
31 * RFC5321 SMTP protocol
32 * RFC5890 Internationalized Domain Names for Applications (IDNA)
33 * RFC6531 SMTP Extension for Internationalized Email
34 * RFC6532 Internationalized Email Headers
35 * RFC6749 OAuth 2.0 Authorization Framework
36 * RFC8314 Use of TLS for Email Submission and Access
37 * Draft SMTP URL Interface <draft-earhart-url-smtp-00.txt>
38 * Draft LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt>
39 *
40 ***************************************************************************/
41
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_NETDB_H
53 #include <netdb.h>
54 #endif
55 #ifdef __VMS
56 #include <in.h>
57 #include <inet.h>
58 #endif
59
60 #include <curl/curl.h>
61 #include "urldata.h"
62 #include "sendf.h"
63 #include "hostip.h"
64 #include "progress.h"
65 #include "transfer.h"
66 #include "escape.h"
67 #include "http.h" /* for HTTP proxy tunnel stuff */
68 #include "mime.h"
69 #include "socks.h"
70 #include "smtp.h"
71 #include "strtoofft.h"
72 #include "strcase.h"
73 #include "vtls/vtls.h"
74 #include "cfilters.h"
75 #include "connect.h"
76 #include "select.h"
77 #include "multiif.h"
78 #include "url.h"
79 #include "curl_gethostname.h"
80 #include "bufref.h"
81 #include "curl_sasl.h"
82 #include "warnless.h"
83 #include "idn.h"
84 /* The last 3 #include files should be in this order */
85 #include "curl_printf.h"
86 #include "curl_memory.h"
87 #include "memdebug.h"
88
89 /* Local API functions */
90 static CURLcode smtp_regular_transfer(struct Curl_easy *data, bool *done);
91 static CURLcode smtp_do(struct Curl_easy *data, bool *done);
92 static CURLcode smtp_done(struct Curl_easy *data, CURLcode status,
93 bool premature);
94 static CURLcode smtp_connect(struct Curl_easy *data, bool *done);
95 static CURLcode smtp_disconnect(struct Curl_easy *data,
96 struct connectdata *conn, bool dead);
97 static CURLcode smtp_multi_statemach(struct Curl_easy *data, bool *done);
98 static int smtp_getsock(struct Curl_easy *data,
99 struct connectdata *conn, curl_socket_t *socks);
100 static CURLcode smtp_doing(struct Curl_easy *data, bool *dophase_done);
101 static CURLcode smtp_setup_connection(struct Curl_easy *data,
102 struct connectdata *conn);
103 static CURLcode smtp_parse_url_options(struct connectdata *conn);
104 static CURLcode smtp_parse_url_path(struct Curl_easy *data);
105 static CURLcode smtp_parse_custom_request(struct Curl_easy *data);
106 static CURLcode smtp_parse_address(const char *fqma,
107 char **address, struct hostname *host);
108 static CURLcode smtp_perform_auth(struct Curl_easy *data, const char *mech,
109 const struct bufref *initresp);
110 static CURLcode smtp_continue_auth(struct Curl_easy *data, const char *mech,
111 const struct bufref *resp);
112 static CURLcode smtp_cancel_auth(struct Curl_easy *data, const char *mech);
113 static CURLcode smtp_get_message(struct Curl_easy *data, struct bufref *out);
114 static CURLcode cr_eob_add(struct Curl_easy *data);
115
116 /*
117 * SMTP protocol handler.
118 */
119
120 const struct Curl_handler Curl_handler_smtp = {
121 "smtp", /* scheme */
122 smtp_setup_connection, /* setup_connection */
123 smtp_do, /* do_it */
124 smtp_done, /* done */
125 ZERO_NULL, /* do_more */
126 smtp_connect, /* connect_it */
127 smtp_multi_statemach, /* connecting */
128 smtp_doing, /* doing */
129 smtp_getsock, /* proto_getsock */
130 smtp_getsock, /* doing_getsock */
131 ZERO_NULL, /* domore_getsock */
132 ZERO_NULL, /* perform_getsock */
133 smtp_disconnect, /* disconnect */
134 ZERO_NULL, /* write_resp */
135 ZERO_NULL, /* write_resp_hd */
136 ZERO_NULL, /* connection_check */
137 ZERO_NULL, /* attach connection */
138 PORT_SMTP, /* defport */
139 CURLPROTO_SMTP, /* protocol */
140 CURLPROTO_SMTP, /* family */
141 PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY | /* flags */
142 PROTOPT_URLOPTIONS
143 };
144
145 #ifdef USE_SSL
146 /*
147 * SMTPS protocol handler.
148 */
149
150 const struct Curl_handler Curl_handler_smtps = {
151 "smtps", /* scheme */
152 smtp_setup_connection, /* setup_connection */
153 smtp_do, /* do_it */
154 smtp_done, /* done */
155 ZERO_NULL, /* do_more */
156 smtp_connect, /* connect_it */
157 smtp_multi_statemach, /* connecting */
158 smtp_doing, /* doing */
159 smtp_getsock, /* proto_getsock */
160 smtp_getsock, /* doing_getsock */
161 ZERO_NULL, /* domore_getsock */
162 ZERO_NULL, /* perform_getsock */
163 smtp_disconnect, /* disconnect */
164 ZERO_NULL, /* write_resp */
165 ZERO_NULL, /* write_resp_hd */
166 ZERO_NULL, /* connection_check */
167 ZERO_NULL, /* attach connection */
168 PORT_SMTPS, /* defport */
169 CURLPROTO_SMTPS, /* protocol */
170 CURLPROTO_SMTP, /* family */
171 PROTOPT_CLOSEACTION | PROTOPT_SSL
172 | PROTOPT_NOURLQUERY | PROTOPT_URLOPTIONS /* flags */
173 };
174 #endif
175
176 /* SASL parameters for the smtp protocol */
177 static const struct SASLproto saslsmtp = {
178 "smtp", /* The service name */
179 smtp_perform_auth, /* Send authentication command */
180 smtp_continue_auth, /* Send authentication continuation */
181 smtp_cancel_auth, /* Cancel authentication */
182 smtp_get_message, /* Get SASL response message */
183 512 - 8, /* Max line len - strlen("AUTH ") - 1 space - crlf */
184 334, /* Code received when continuation is expected */
185 235, /* Code to receive upon authentication success */
186 SASL_AUTH_DEFAULT, /* Default mechanisms */
187 SASL_FLAG_BASE64 /* Configuration flags */
188 };
189
190 #ifdef USE_SSL
smtp_to_smtps(struct connectdata * conn)191 static void smtp_to_smtps(struct connectdata *conn)
192 {
193 /* Change the connection handler */
194 conn->handler = &Curl_handler_smtps;
195
196 /* Set the connection's upgraded to TLS flag */
197 conn->bits.tls_upgraded = TRUE;
198 }
199 #else
200 #define smtp_to_smtps(x) Curl_nop_stmt
201 #endif
202
203 /***********************************************************************
204 *
205 * smtp_endofresp()
206 *
207 * Checks for an ending SMTP status code at the start of the given string, but
208 * also detects various capabilities from the EHLO response including the
209 * supported authentication mechanisms.
210 */
smtp_endofresp(struct Curl_easy * data,struct connectdata * conn,char * line,size_t len,int * resp)211 static bool smtp_endofresp(struct Curl_easy *data, struct connectdata *conn,
212 char *line, size_t len, int *resp)
213 {
214 struct smtp_conn *smtpc = &conn->proto.smtpc;
215 bool result = FALSE;
216 (void)data;
217
218 /* Nothing for us */
219 if(len < 4 || !ISDIGIT(line[0]) || !ISDIGIT(line[1]) || !ISDIGIT(line[2]))
220 return FALSE;
221
222 /* Do we have a command response? This should be the response code followed
223 by a space and optionally some text as per RFC-5321 and as outlined in
224 Section 4. Examples of RFC-4954 but some email servers ignore this and
225 only send the response code instead as per Section 4.2. */
226 if(line[3] == ' ' || len == 5) {
227 char tmpline[6];
228
229 result = TRUE;
230 memset(tmpline, '\0', sizeof(tmpline));
231 memcpy(tmpline, line, (len == 5 ? 5 : 3));
232 *resp = curlx_sltosi(strtol(tmpline, NULL, 10));
233
234 /* Make sure real server never sends internal value */
235 if(*resp == 1)
236 *resp = 0;
237 }
238 /* Do we have a multiline (continuation) response? */
239 else if(line[3] == '-' &&
240 (smtpc->state == SMTP_EHLO || smtpc->state == SMTP_COMMAND)) {
241 result = TRUE;
242 *resp = 1; /* Internal response code */
243 }
244
245 return result;
246 }
247
248 /***********************************************************************
249 *
250 * smtp_get_message()
251 *
252 * Gets the authentication message from the response buffer.
253 */
smtp_get_message(struct Curl_easy * data,struct bufref * out)254 static CURLcode smtp_get_message(struct Curl_easy *data, struct bufref *out)
255 {
256 char *message = Curl_dyn_ptr(&data->conn->proto.smtpc.pp.recvbuf);
257 size_t len = data->conn->proto.smtpc.pp.nfinal;
258
259 if(len > 4) {
260 /* Find the start of the message */
261 len -= 4;
262 for(message += 4; *message == ' ' || *message == '\t'; message++, len--)
263 ;
264
265 /* Find the end of the message */
266 while(len--)
267 if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' &&
268 message[len] != '\t')
269 break;
270
271 /* Terminate the message */
272 message[++len] = '\0';
273 Curl_bufref_set(out, message, len, NULL);
274 }
275 else
276 /* junk input => zero length output */
277 Curl_bufref_set(out, "", 0, NULL);
278
279 return CURLE_OK;
280 }
281
282 /***********************************************************************
283 *
284 * smtp_state()
285 *
286 * This is the ONLY way to change SMTP state!
287 */
smtp_state(struct Curl_easy * data,smtpstate newstate)288 static void smtp_state(struct Curl_easy *data, smtpstate newstate)
289 {
290 struct smtp_conn *smtpc = &data->conn->proto.smtpc;
291 #if !defined(CURL_DISABLE_VERBOSE_STRINGS)
292 /* for debug purposes */
293 static const char * const names[] = {
294 "STOP",
295 "SERVERGREET",
296 "EHLO",
297 "HELO",
298 "STARTTLS",
299 "UPGRADETLS",
300 "AUTH",
301 "COMMAND",
302 "MAIL",
303 "RCPT",
304 "DATA",
305 "POSTDATA",
306 "QUIT",
307 /* LAST */
308 };
309
310 if(smtpc->state != newstate)
311 CURL_TRC_SMTP(data, "state change from %s to %s",
312 names[smtpc->state], names[newstate]);
313 #endif
314
315 smtpc->state = newstate;
316 }
317
318 /***********************************************************************
319 *
320 * smtp_perform_ehlo()
321 *
322 * Sends the EHLO command to not only initialise communication with the ESMTP
323 * server but to also obtain a list of server side supported capabilities.
324 */
smtp_perform_ehlo(struct Curl_easy * data)325 static CURLcode smtp_perform_ehlo(struct Curl_easy *data)
326 {
327 CURLcode result = CURLE_OK;
328 struct connectdata *conn = data->conn;
329 struct smtp_conn *smtpc = &conn->proto.smtpc;
330
331 smtpc->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanism yet */
332 smtpc->sasl.authused = SASL_AUTH_NONE; /* Clear the authentication mechanism
333 used for esmtp connections */
334 smtpc->tls_supported = FALSE; /* Clear the TLS capability */
335 smtpc->auth_supported = FALSE; /* Clear the AUTH capability */
336
337 /* Send the EHLO command */
338 result = Curl_pp_sendf(data, &smtpc->pp, "EHLO %s", smtpc->domain);
339
340 if(!result)
341 smtp_state(data, SMTP_EHLO);
342
343 return result;
344 }
345
346 /***********************************************************************
347 *
348 * smtp_perform_helo()
349 *
350 * Sends the HELO command to initialise communication with the SMTP server.
351 */
smtp_perform_helo(struct Curl_easy * data,struct connectdata * conn)352 static CURLcode smtp_perform_helo(struct Curl_easy *data,
353 struct connectdata *conn)
354 {
355 CURLcode result = CURLE_OK;
356 struct smtp_conn *smtpc = &conn->proto.smtpc;
357
358 smtpc->sasl.authused = SASL_AUTH_NONE; /* No authentication mechanism used
359 in smtp connections */
360
361 /* Send the HELO command */
362 result = Curl_pp_sendf(data, &smtpc->pp, "HELO %s", smtpc->domain);
363
364 if(!result)
365 smtp_state(data, SMTP_HELO);
366
367 return result;
368 }
369
370 /***********************************************************************
371 *
372 * smtp_perform_starttls()
373 *
374 * Sends the STLS command to start the upgrade to TLS.
375 */
smtp_perform_starttls(struct Curl_easy * data,struct connectdata * conn)376 static CURLcode smtp_perform_starttls(struct Curl_easy *data,
377 struct connectdata *conn)
378 {
379 /* Send the STARTTLS command */
380 CURLcode result = Curl_pp_sendf(data, &conn->proto.smtpc.pp,
381 "%s", "STARTTLS");
382
383 if(!result)
384 smtp_state(data, SMTP_STARTTLS);
385
386 return result;
387 }
388
389 /***********************************************************************
390 *
391 * smtp_perform_upgrade_tls()
392 *
393 * Performs the upgrade to TLS.
394 */
smtp_perform_upgrade_tls(struct Curl_easy * data)395 static CURLcode smtp_perform_upgrade_tls(struct Curl_easy *data)
396 {
397 /* Start the SSL connection */
398 struct connectdata *conn = data->conn;
399 struct smtp_conn *smtpc = &conn->proto.smtpc;
400 CURLcode result;
401 bool ssldone = FALSE;
402
403 if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
404 result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET);
405 if(result)
406 goto out;
407 }
408
409 result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone);
410 if(!result) {
411 smtpc->ssldone = ssldone;
412 if(smtpc->state != SMTP_UPGRADETLS)
413 smtp_state(data, SMTP_UPGRADETLS);
414
415 if(smtpc->ssldone) {
416 smtp_to_smtps(conn);
417 result = smtp_perform_ehlo(data);
418 }
419 }
420 out:
421 return result;
422 }
423
424 /***********************************************************************
425 *
426 * smtp_perform_auth()
427 *
428 * Sends an AUTH command allowing the client to login with the given SASL
429 * authentication mechanism.
430 */
smtp_perform_auth(struct Curl_easy * data,const char * mech,const struct bufref * initresp)431 static CURLcode smtp_perform_auth(struct Curl_easy *data,
432 const char *mech,
433 const struct bufref *initresp)
434 {
435 CURLcode result = CURLE_OK;
436 struct smtp_conn *smtpc = &data->conn->proto.smtpc;
437 const char *ir = (const char *) Curl_bufref_ptr(initresp);
438
439 if(ir) { /* AUTH <mech> ...<crlf> */
440 /* Send the AUTH command with the initial response */
441 result = Curl_pp_sendf(data, &smtpc->pp, "AUTH %s %s", mech, ir);
442 }
443 else {
444 /* Send the AUTH command */
445 result = Curl_pp_sendf(data, &smtpc->pp, "AUTH %s", mech);
446 }
447
448 return result;
449 }
450
451 /***********************************************************************
452 *
453 * smtp_continue_auth()
454 *
455 * Sends SASL continuation data.
456 */
smtp_continue_auth(struct Curl_easy * data,const char * mech,const struct bufref * resp)457 static CURLcode smtp_continue_auth(struct Curl_easy *data,
458 const char *mech,
459 const struct bufref *resp)
460 {
461 struct smtp_conn *smtpc = &data->conn->proto.smtpc;
462
463 (void)mech;
464
465 return Curl_pp_sendf(data, &smtpc->pp,
466 "%s", (const char *) Curl_bufref_ptr(resp));
467 }
468
469 /***********************************************************************
470 *
471 * smtp_cancel_auth()
472 *
473 * Sends SASL cancellation.
474 */
smtp_cancel_auth(struct Curl_easy * data,const char * mech)475 static CURLcode smtp_cancel_auth(struct Curl_easy *data, const char *mech)
476 {
477 struct smtp_conn *smtpc = &data->conn->proto.smtpc;
478
479 (void)mech;
480
481 return Curl_pp_sendf(data, &smtpc->pp, "*");
482 }
483
484 /***********************************************************************
485 *
486 * smtp_perform_authentication()
487 *
488 * Initiates the authentication sequence, with the appropriate SASL
489 * authentication mechanism.
490 */
smtp_perform_authentication(struct Curl_easy * data)491 static CURLcode smtp_perform_authentication(struct Curl_easy *data)
492 {
493 CURLcode result = CURLE_OK;
494 struct connectdata *conn = data->conn;
495 struct smtp_conn *smtpc = &conn->proto.smtpc;
496 saslprogress progress;
497
498 /* Check we have enough data to authenticate with, and the
499 server supports authentication, and end the connect phase if not */
500 if(!smtpc->auth_supported ||
501 !Curl_sasl_can_authenticate(&smtpc->sasl, data)) {
502 smtp_state(data, SMTP_STOP);
503 return result;
504 }
505
506 /* Calculate the SASL login details */
507 result = Curl_sasl_start(&smtpc->sasl, data, FALSE, &progress);
508
509 if(!result) {
510 if(progress == SASL_INPROGRESS)
511 smtp_state(data, SMTP_AUTH);
512 else {
513 /* Other mechanisms not supported */
514 infof(data, "No known authentication mechanisms supported");
515 result = CURLE_LOGIN_DENIED;
516 }
517 }
518
519 return result;
520 }
521
522 /***********************************************************************
523 *
524 * smtp_perform_command()
525 *
526 * Sends a SMTP based command.
527 */
smtp_perform_command(struct Curl_easy * data)528 static CURLcode smtp_perform_command(struct Curl_easy *data)
529 {
530 CURLcode result = CURLE_OK;
531 struct connectdata *conn = data->conn;
532 struct SMTP *smtp = data->req.p.smtp;
533
534 if(smtp->rcpt) {
535 /* We notify the server we are sending UTF-8 data if a) it supports the
536 SMTPUTF8 extension and b) The mailbox contains UTF-8 characters, in
537 either the local address or hostname parts. This is regardless of
538 whether the hostname is encoded using IDN ACE */
539 bool utf8 = FALSE;
540
541 if((!smtp->custom) || (!smtp->custom[0])) {
542 char *address = NULL;
543 struct hostname host = { NULL, NULL, NULL, NULL };
544
545 /* Parse the mailbox to verify into the local address and hostname
546 parts, converting the hostname to an IDN A-label if necessary */
547 result = smtp_parse_address(smtp->rcpt->data,
548 &address, &host);
549 if(result)
550 return result;
551
552 /* Establish whether we should report SMTPUTF8 to the server for this
553 mailbox as per RFC-6531 sect. 3.1 point 6 */
554 utf8 = (conn->proto.smtpc.utf8_supported) &&
555 ((host.encalloc) || (!Curl_is_ASCII_name(address)) ||
556 (!Curl_is_ASCII_name(host.name)));
557
558 /* Send the VRFY command (Note: The hostname part may be absent when the
559 host is a local system) */
560 result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "VRFY %s%s%s%s",
561 address,
562 host.name ? "@" : "",
563 host.name ? host.name : "",
564 utf8 ? " SMTPUTF8" : "");
565
566 Curl_free_idnconverted_hostname(&host);
567 free(address);
568 }
569 else {
570 /* Establish whether we should report that we support SMTPUTF8 for EXPN
571 commands to the server as per RFC-6531 sect. 3.1 point 6 */
572 utf8 = (conn->proto.smtpc.utf8_supported) &&
573 (!strcmp(smtp->custom, "EXPN"));
574
575 /* Send the custom recipient based command such as the EXPN command */
576 result = Curl_pp_sendf(data, &conn->proto.smtpc.pp,
577 "%s %s%s", smtp->custom,
578 smtp->rcpt->data,
579 utf8 ? " SMTPUTF8" : "");
580 }
581 }
582 else
583 /* Send the non-recipient based command such as HELP */
584 result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "%s",
585 smtp->custom && smtp->custom[0] != '\0' ?
586 smtp->custom : "HELP");
587
588 if(!result)
589 smtp_state(data, SMTP_COMMAND);
590
591 return result;
592 }
593
594 /***********************************************************************
595 *
596 * smtp_perform_mail()
597 *
598 * Sends an MAIL command to initiate the upload of a message.
599 */
smtp_perform_mail(struct Curl_easy * data)600 static CURLcode smtp_perform_mail(struct Curl_easy *data)
601 {
602 char *from = NULL;
603 char *auth = NULL;
604 char *size = NULL;
605 CURLcode result = CURLE_OK;
606 struct connectdata *conn = data->conn;
607
608 /* We notify the server we are sending UTF-8 data if a) it supports the
609 SMTPUTF8 extension and b) The mailbox contains UTF-8 characters, in
610 either the local address or hostname parts. This is regardless of
611 whether the hostname is encoded using IDN ACE */
612 bool utf8 = FALSE;
613
614 /* Calculate the FROM parameter */
615 if(data->set.str[STRING_MAIL_FROM]) {
616 char *address = NULL;
617 struct hostname host = { NULL, NULL, NULL, NULL };
618
619 /* Parse the FROM mailbox into the local address and hostname parts,
620 converting the hostname to an IDN A-label if necessary */
621 result = smtp_parse_address(data->set.str[STRING_MAIL_FROM],
622 &address, &host);
623 if(result)
624 goto out;
625
626 /* Establish whether we should report SMTPUTF8 to the server for this
627 mailbox as per RFC-6531 sect. 3.1 point 4 and sect. 3.4 */
628 utf8 = (conn->proto.smtpc.utf8_supported) &&
629 ((host.encalloc) || (!Curl_is_ASCII_name(address)) ||
630 (!Curl_is_ASCII_name(host.name)));
631
632 if(host.name) {
633 from = aprintf("<%s@%s>", address, host.name);
634
635 Curl_free_idnconverted_hostname(&host);
636 }
637 else
638 /* An invalid mailbox was provided but we will simply let the server
639 worry about that and reply with a 501 error */
640 from = aprintf("<%s>", address);
641
642 free(address);
643 }
644 else
645 /* Null reverse-path, RFC-5321, sect. 3.6.3 */
646 from = strdup("<>");
647
648 if(!from) {
649 result = CURLE_OUT_OF_MEMORY;
650 goto out;
651 }
652
653 /* Calculate the optional AUTH parameter */
654 if(data->set.str[STRING_MAIL_AUTH] && conn->proto.smtpc.sasl.authused) {
655 if(data->set.str[STRING_MAIL_AUTH][0] != '\0') {
656 char *address = NULL;
657 struct hostname host = { NULL, NULL, NULL, NULL };
658
659 /* Parse the AUTH mailbox into the local address and hostname parts,
660 converting the hostname to an IDN A-label if necessary */
661 result = smtp_parse_address(data->set.str[STRING_MAIL_AUTH],
662 &address, &host);
663 if(result)
664 goto out;
665
666 /* Establish whether we should report SMTPUTF8 to the server for this
667 mailbox as per RFC-6531 sect. 3.1 point 4 and sect. 3.4 */
668 if((!utf8) && (conn->proto.smtpc.utf8_supported) &&
669 ((host.encalloc) || (!Curl_is_ASCII_name(address)) ||
670 (!Curl_is_ASCII_name(host.name))))
671 utf8 = TRUE;
672
673 if(host.name) {
674 auth = aprintf("<%s@%s>", address, host.name);
675
676 Curl_free_idnconverted_hostname(&host);
677 }
678 else
679 /* An invalid mailbox was provided but we will simply let the server
680 worry about it */
681 auth = aprintf("<%s>", address);
682 free(address);
683 }
684 else
685 /* Empty AUTH, RFC-2554, sect. 5 */
686 auth = strdup("<>");
687
688 if(!auth) {
689 result = CURLE_OUT_OF_MEMORY;
690 goto out;
691 }
692 }
693
694 #ifndef CURL_DISABLE_MIME
695 /* Prepare the mime data if some. */
696 if(data->set.mimepost.kind != MIMEKIND_NONE) {
697 /* Use the whole structure as data. */
698 data->set.mimepost.flags &= ~(unsigned int)MIME_BODY_ONLY;
699
700 /* Add external headers and mime version. */
701 curl_mime_headers(&data->set.mimepost, data->set.headers, 0);
702 result = Curl_mime_prepare_headers(data, &data->set.mimepost, NULL,
703 NULL, MIMESTRATEGY_MAIL);
704
705 if(!result)
706 if(!Curl_checkheaders(data, STRCONST("Mime-Version")))
707 result = Curl_mime_add_header(&data->set.mimepost.curlheaders,
708 "Mime-Version: 1.0");
709
710 if(!result)
711 result = Curl_creader_set_mime(data, &data->set.mimepost);
712 if(result)
713 goto out;
714 data->state.infilesize = Curl_creader_total_length(data);
715 }
716 else
717 #endif
718 {
719 result = Curl_creader_set_fread(data, data->state.infilesize);
720 if(result)
721 goto out;
722 }
723
724 /* Calculate the optional SIZE parameter */
725 if(conn->proto.smtpc.size_supported && data->state.infilesize > 0) {
726 size = aprintf("%" FMT_OFF_T, data->state.infilesize);
727
728 if(!size) {
729 result = CURLE_OUT_OF_MEMORY;
730 goto out;
731 }
732 }
733
734 /* If the mailboxes in the FROM and AUTH parameters do not include a UTF-8
735 based address then quickly scan through the recipient list and check if
736 any there do, as we need to correctly identify our support for SMTPUTF8
737 in the envelope, as per RFC-6531 sect. 3.4 */
738 if(conn->proto.smtpc.utf8_supported && !utf8) {
739 struct SMTP *smtp = data->req.p.smtp;
740 struct curl_slist *rcpt = smtp->rcpt;
741
742 while(rcpt && !utf8) {
743 /* Does the hostname contain non-ASCII characters? */
744 if(!Curl_is_ASCII_name(rcpt->data))
745 utf8 = TRUE;
746
747 rcpt = rcpt->next;
748 }
749 }
750
751 /* Add the client reader doing STMP EOB escaping */
752 result = cr_eob_add(data);
753 if(result)
754 goto out;
755
756 /* Send the MAIL command */
757 result = Curl_pp_sendf(data, &conn->proto.smtpc.pp,
758 "MAIL FROM:%s%s%s%s%s%s",
759 from, /* Mandatory */
760 auth ? " AUTH=" : "", /* Optional on AUTH support */
761 auth ? auth : "", /* */
762 size ? " SIZE=" : "", /* Optional on SIZE support */
763 size ? size : "", /* */
764 utf8 ? " SMTPUTF8" /* Internationalised mailbox */
765 : ""); /* included in our envelope */
766
767 out:
768 free(from);
769 free(auth);
770 free(size);
771
772 if(!result)
773 smtp_state(data, SMTP_MAIL);
774
775 return result;
776 }
777
778 /***********************************************************************
779 *
780 * smtp_perform_rcpt_to()
781 *
782 * Sends a RCPT TO command for a given recipient as part of the message upload
783 * process.
784 */
smtp_perform_rcpt_to(struct Curl_easy * data)785 static CURLcode smtp_perform_rcpt_to(struct Curl_easy *data)
786 {
787 CURLcode result = CURLE_OK;
788 struct connectdata *conn = data->conn;
789 struct SMTP *smtp = data->req.p.smtp;
790 char *address = NULL;
791 struct hostname host = { NULL, NULL, NULL, NULL };
792
793 /* Parse the recipient mailbox into the local address and hostname parts,
794 converting the hostname to an IDN A-label if necessary */
795 result = smtp_parse_address(smtp->rcpt->data,
796 &address, &host);
797 if(result)
798 return result;
799
800 /* Send the RCPT TO command */
801 if(host.name)
802 result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "RCPT TO:<%s@%s>",
803 address, host.name);
804 else
805 /* An invalid mailbox was provided but we will simply let the server worry
806 about that and reply with a 501 error */
807 result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "RCPT TO:<%s>",
808 address);
809
810 Curl_free_idnconverted_hostname(&host);
811 free(address);
812
813 if(!result)
814 smtp_state(data, SMTP_RCPT);
815
816 return result;
817 }
818
819 /***********************************************************************
820 *
821 * smtp_perform_quit()
822 *
823 * Performs the quit action prior to sclose() being called.
824 */
smtp_perform_quit(struct Curl_easy * data,struct connectdata * conn)825 static CURLcode smtp_perform_quit(struct Curl_easy *data,
826 struct connectdata *conn)
827 {
828 /* Send the QUIT command */
829 CURLcode result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "%s", "QUIT");
830
831 if(!result)
832 smtp_state(data, SMTP_QUIT);
833
834 return result;
835 }
836
837 /* For the initial server greeting */
smtp_state_servergreet_resp(struct Curl_easy * data,int smtpcode,smtpstate instate)838 static CURLcode smtp_state_servergreet_resp(struct Curl_easy *data,
839 int smtpcode,
840 smtpstate instate)
841 {
842 CURLcode result = CURLE_OK;
843 (void)instate; /* no use for this yet */
844
845 if(smtpcode/100 != 2) {
846 failf(data, "Got unexpected smtp-server response: %d", smtpcode);
847 result = CURLE_WEIRD_SERVER_REPLY;
848 }
849 else
850 result = smtp_perform_ehlo(data);
851
852 return result;
853 }
854
855 /* For STARTTLS responses */
smtp_state_starttls_resp(struct Curl_easy * data,int smtpcode,smtpstate instate)856 static CURLcode smtp_state_starttls_resp(struct Curl_easy *data,
857 int smtpcode,
858 smtpstate instate)
859 {
860 CURLcode result = CURLE_OK;
861 (void)instate; /* no use for this yet */
862
863 /* Pipelining in response is forbidden. */
864 if(data->conn->proto.smtpc.pp.overflow)
865 return CURLE_WEIRD_SERVER_REPLY;
866
867 if(smtpcode != 220) {
868 if(data->set.use_ssl != CURLUSESSL_TRY) {
869 failf(data, "STARTTLS denied, code %d", smtpcode);
870 result = CURLE_USE_SSL_FAILED;
871 }
872 else
873 result = smtp_perform_authentication(data);
874 }
875 else
876 result = smtp_perform_upgrade_tls(data);
877
878 return result;
879 }
880
881 /* For EHLO responses */
smtp_state_ehlo_resp(struct Curl_easy * data,struct connectdata * conn,int smtpcode,smtpstate instate)882 static CURLcode smtp_state_ehlo_resp(struct Curl_easy *data,
883 struct connectdata *conn, int smtpcode,
884 smtpstate instate)
885 {
886 CURLcode result = CURLE_OK;
887 struct smtp_conn *smtpc = &conn->proto.smtpc;
888 const char *line = Curl_dyn_ptr(&smtpc->pp.recvbuf);
889 size_t len = smtpc->pp.nfinal;
890
891 (void)instate; /* no use for this yet */
892
893 if(smtpcode/100 != 2 && smtpcode != 1) {
894 if(data->set.use_ssl <= CURLUSESSL_TRY
895 || Curl_conn_is_ssl(conn, FIRSTSOCKET))
896 result = smtp_perform_helo(data, conn);
897 else {
898 failf(data, "Remote access denied: %d", smtpcode);
899 result = CURLE_REMOTE_ACCESS_DENIED;
900 }
901 }
902 else if(len >= 4) {
903 line += 4;
904 len -= 4;
905
906 /* Does the server support the STARTTLS capability? */
907 if(len >= 8 && !memcmp(line, "STARTTLS", 8))
908 smtpc->tls_supported = TRUE;
909
910 /* Does the server support the SIZE capability? */
911 else if(len >= 4 && !memcmp(line, "SIZE", 4))
912 smtpc->size_supported = TRUE;
913
914 /* Does the server support the UTF-8 capability? */
915 else if(len >= 8 && !memcmp(line, "SMTPUTF8", 8))
916 smtpc->utf8_supported = TRUE;
917
918 /* Does the server support authentication? */
919 else if(len >= 5 && !memcmp(line, "AUTH ", 5)) {
920 smtpc->auth_supported = TRUE;
921
922 /* Advance past the AUTH keyword */
923 line += 5;
924 len -= 5;
925
926 /* Loop through the data line */
927 for(;;) {
928 size_t llen;
929 size_t wordlen;
930 unsigned short mechbit;
931
932 while(len &&
933 (*line == ' ' || *line == '\t' ||
934 *line == '\r' || *line == '\n')) {
935
936 line++;
937 len--;
938 }
939
940 if(!len)
941 break;
942
943 /* Extract the word */
944 for(wordlen = 0; wordlen < len && line[wordlen] != ' ' &&
945 line[wordlen] != '\t' && line[wordlen] != '\r' &&
946 line[wordlen] != '\n';)
947 wordlen++;
948
949 /* Test the word for a matching authentication mechanism */
950 mechbit = Curl_sasl_decode_mech(line, wordlen, &llen);
951 if(mechbit && llen == wordlen)
952 smtpc->sasl.authmechs |= mechbit;
953
954 line += wordlen;
955 len -= wordlen;
956 }
957 }
958
959 if(smtpcode != 1) {
960 if(data->set.use_ssl && !Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
961 /* We do not have a SSL/TLS connection yet, but SSL is requested */
962 if(smtpc->tls_supported)
963 /* Switch to TLS connection now */
964 result = smtp_perform_starttls(data, conn);
965 else if(data->set.use_ssl == CURLUSESSL_TRY)
966 /* Fallback and carry on with authentication */
967 result = smtp_perform_authentication(data);
968 else {
969 failf(data, "STARTTLS not supported.");
970 result = CURLE_USE_SSL_FAILED;
971 }
972 }
973 else
974 result = smtp_perform_authentication(data);
975 }
976 }
977 else {
978 failf(data, "Unexpectedly short EHLO response");
979 result = CURLE_WEIRD_SERVER_REPLY;
980 }
981
982 return result;
983 }
984
985 /* For HELO responses */
smtp_state_helo_resp(struct Curl_easy * data,int smtpcode,smtpstate instate)986 static CURLcode smtp_state_helo_resp(struct Curl_easy *data, int smtpcode,
987 smtpstate instate)
988 {
989 CURLcode result = CURLE_OK;
990 (void)instate; /* no use for this yet */
991
992 if(smtpcode/100 != 2) {
993 failf(data, "Remote access denied: %d", smtpcode);
994 result = CURLE_REMOTE_ACCESS_DENIED;
995 }
996 else
997 /* End of connect phase */
998 smtp_state(data, SMTP_STOP);
999
1000 return result;
1001 }
1002
1003 /* For SASL authentication responses */
smtp_state_auth_resp(struct Curl_easy * data,int smtpcode,smtpstate instate)1004 static CURLcode smtp_state_auth_resp(struct Curl_easy *data,
1005 int smtpcode,
1006 smtpstate instate)
1007 {
1008 CURLcode result = CURLE_OK;
1009 struct connectdata *conn = data->conn;
1010 struct smtp_conn *smtpc = &conn->proto.smtpc;
1011 saslprogress progress;
1012
1013 (void)instate; /* no use for this yet */
1014
1015 result = Curl_sasl_continue(&smtpc->sasl, data, smtpcode, &progress);
1016 if(!result)
1017 switch(progress) {
1018 case SASL_DONE:
1019 smtp_state(data, SMTP_STOP); /* Authenticated */
1020 break;
1021 case SASL_IDLE: /* No mechanism left after cancellation */
1022 failf(data, "Authentication cancelled");
1023 result = CURLE_LOGIN_DENIED;
1024 break;
1025 default:
1026 break;
1027 }
1028
1029 return result;
1030 }
1031
1032 /* For command responses */
smtp_state_command_resp(struct Curl_easy * data,int smtpcode,smtpstate instate)1033 static CURLcode smtp_state_command_resp(struct Curl_easy *data, int smtpcode,
1034 smtpstate instate)
1035 {
1036 CURLcode result = CURLE_OK;
1037 struct SMTP *smtp = data->req.p.smtp;
1038 char *line = Curl_dyn_ptr(&data->conn->proto.smtpc.pp.recvbuf);
1039 size_t len = data->conn->proto.smtpc.pp.nfinal;
1040
1041 (void)instate; /* no use for this yet */
1042
1043 if((smtp->rcpt && smtpcode/100 != 2 && smtpcode != 553 && smtpcode != 1) ||
1044 (!smtp->rcpt && smtpcode/100 != 2 && smtpcode != 1)) {
1045 failf(data, "Command failed: %d", smtpcode);
1046 result = CURLE_WEIRD_SERVER_REPLY;
1047 }
1048 else {
1049 if(!data->req.no_body)
1050 result = Curl_client_write(data, CLIENTWRITE_BODY, line, len);
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 smtp_state(data, SMTP_STOP);
1063 }
1064 else
1065 /* End of DO phase */
1066 smtp_state(data, SMTP_STOP);
1067 }
1068 }
1069
1070 return result;
1071 }
1072
1073 /* For MAIL responses */
smtp_state_mail_resp(struct Curl_easy * data,int smtpcode,smtpstate instate)1074 static 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 */
smtp_state_rcpt_resp(struct Curl_easy * data,struct connectdata * conn,int smtpcode,smtpstate instate)1092 static 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 is multiple RCPT TO to be issued, it is 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 were not 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 smtp_state(data, SMTP_DATA);
1144 }
1145 }
1146 }
1147
1148 return result;
1149 }
1150
1151 /* For DATA response */
smtp_state_data_resp(struct Curl_easy * data,int smtpcode,smtpstate instate)1152 static 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_xfer_setup1(data, CURL_XFER_SEND, -1, FALSE);
1168
1169 /* End of DO phase */
1170 smtp_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 */
smtp_state_postdata_resp(struct Curl_easy * data,int smtpcode,smtpstate instate)1178 static 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 smtp_state(data, SMTP_STOP);
1191
1192 return result;
1193 }
1194
smtp_statemachine(struct Curl_easy * data,struct connectdata * conn)1195 static CURLcode smtp_statemachine(struct Curl_easy *data,
1196 struct connectdata *conn)
1197 {
1198 CURLcode result = CURLE_OK;
1199 int smtpcode;
1200 struct smtp_conn *smtpc = &conn->proto.smtpc;
1201 struct pingpong *pp = &smtpc->pp;
1202 size_t nread = 0;
1203
1204 /* Busy upgrading the connection; right now all I/O is SSL/TLS, not SMTP */
1205 upgrade_tls:
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, FIRSTSOCKET, 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 /* During UPGRADETLS, leave the read loop as we need to connect
1243 * (e.g. TLS handshake) before we continue sending/receiving. */
1244 if(!result && (smtpc->state == SMTP_UPGRADETLS))
1245 goto upgrade_tls;
1246 break;
1247
1248 case SMTP_AUTH:
1249 result = smtp_state_auth_resp(data, smtpcode, smtpc->state);
1250 break;
1251
1252 case SMTP_COMMAND:
1253 result = smtp_state_command_resp(data, smtpcode, smtpc->state);
1254 break;
1255
1256 case SMTP_MAIL:
1257 result = smtp_state_mail_resp(data, smtpcode, smtpc->state);
1258 break;
1259
1260 case SMTP_RCPT:
1261 result = smtp_state_rcpt_resp(data, conn, smtpcode, smtpc->state);
1262 break;
1263
1264 case SMTP_DATA:
1265 result = smtp_state_data_resp(data, smtpcode, smtpc->state);
1266 break;
1267
1268 case SMTP_POSTDATA:
1269 result = smtp_state_postdata_resp(data, smtpcode, smtpc->state);
1270 break;
1271
1272 case SMTP_QUIT:
1273 default:
1274 /* internal error */
1275 smtp_state(data, SMTP_STOP);
1276 break;
1277 }
1278 } while(!result && smtpc->state != SMTP_STOP && Curl_pp_moredata(pp));
1279
1280 return result;
1281 }
1282
1283 /* Called repeatedly until done from multi.c */
smtp_multi_statemach(struct Curl_easy * data,bool * done)1284 static CURLcode smtp_multi_statemach(struct Curl_easy *data, bool *done)
1285 {
1286 CURLcode result = CURLE_OK;
1287 struct connectdata *conn = data->conn;
1288 struct smtp_conn *smtpc = &conn->proto.smtpc;
1289
1290 if((conn->handler->flags & PROTOPT_SSL) && !smtpc->ssldone) {
1291 bool ssldone = FALSE;
1292 result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone);
1293 smtpc->ssldone = ssldone;
1294 if(result || !smtpc->ssldone)
1295 return result;
1296 }
1297
1298 result = Curl_pp_statemach(data, &smtpc->pp, FALSE, FALSE);
1299 *done = (smtpc->state == SMTP_STOP) ? TRUE : FALSE;
1300
1301 return result;
1302 }
1303
smtp_block_statemach(struct Curl_easy * data,struct connectdata * conn,bool disconnecting)1304 static CURLcode smtp_block_statemach(struct Curl_easy *data,
1305 struct connectdata *conn,
1306 bool disconnecting)
1307 {
1308 CURLcode result = CURLE_OK;
1309 struct smtp_conn *smtpc = &conn->proto.smtpc;
1310
1311 while(smtpc->state != SMTP_STOP && !result)
1312 result = Curl_pp_statemach(data, &smtpc->pp, TRUE, disconnecting);
1313
1314 return result;
1315 }
1316
1317 /* Allocate and initialize the SMTP struct for the current Curl_easy if
1318 required */
smtp_init(struct Curl_easy * data)1319 static CURLcode smtp_init(struct Curl_easy *data)
1320 {
1321 CURLcode result = CURLE_OK;
1322 struct SMTP *smtp;
1323
1324 smtp = data->req.p.smtp = calloc(1, sizeof(struct SMTP));
1325 if(!smtp)
1326 result = CURLE_OUT_OF_MEMORY;
1327
1328 return result;
1329 }
1330
1331 /* For the SMTP "protocol connect" and "doing" phases only */
smtp_getsock(struct Curl_easy * data,struct connectdata * conn,curl_socket_t * socks)1332 static int smtp_getsock(struct Curl_easy *data,
1333 struct connectdata *conn, curl_socket_t *socks)
1334 {
1335 return Curl_pp_getsock(data, &conn->proto.smtpc.pp, socks);
1336 }
1337
1338 /***********************************************************************
1339 *
1340 * smtp_connect()
1341 *
1342 * This function should do everything that is to be considered a part of
1343 * the connection phase.
1344 *
1345 * The variable pointed to by 'done' will be TRUE if the protocol-layer
1346 * connect phase is done when this function returns, or FALSE if not.
1347 */
smtp_connect(struct Curl_easy * data,bool * done)1348 static CURLcode smtp_connect(struct Curl_easy *data, bool *done)
1349 {
1350 CURLcode result = CURLE_OK;
1351 struct connectdata *conn = data->conn;
1352 struct smtp_conn *smtpc = &conn->proto.smtpc;
1353 struct pingpong *pp = &smtpc->pp;
1354
1355 *done = FALSE; /* default to not done yet */
1356
1357 /* We always support persistent connections in SMTP */
1358 connkeep(conn, "SMTP default");
1359
1360 PINGPONG_SETUP(pp, smtp_statemachine, smtp_endofresp);
1361
1362 /* Initialize the SASL storage */
1363 Curl_sasl_init(&smtpc->sasl, data, &saslsmtp);
1364
1365 /* Initialise the pingpong layer */
1366 Curl_pp_init(pp);
1367
1368 /* Parse the URL options */
1369 result = smtp_parse_url_options(conn);
1370 if(result)
1371 return result;
1372
1373 /* Parse the URL path */
1374 result = smtp_parse_url_path(data);
1375 if(result)
1376 return result;
1377
1378 /* Start off waiting for the server greeting response */
1379 smtp_state(data, SMTP_SERVERGREET);
1380
1381 result = smtp_multi_statemach(data, done);
1382
1383 return result;
1384 }
1385
1386 /***********************************************************************
1387 *
1388 * smtp_done()
1389 *
1390 * The DONE function. This does what needs to be done after a single DO has
1391 * performed.
1392 *
1393 * Input argument is already checked for validity.
1394 */
smtp_done(struct Curl_easy * data,CURLcode status,bool premature)1395 static CURLcode smtp_done(struct Curl_easy *data, CURLcode status,
1396 bool premature)
1397 {
1398 CURLcode result = CURLE_OK;
1399 struct connectdata *conn = data->conn;
1400 struct SMTP *smtp = data->req.p.smtp;
1401
1402 (void)premature;
1403
1404 if(!smtp)
1405 return CURLE_OK;
1406
1407 /* Cleanup our per-request based variables */
1408 Curl_safefree(smtp->custom);
1409
1410 if(status) {
1411 connclose(conn, "SMTP done with bad status"); /* marked for closure */
1412 result = status; /* use the already set error code */
1413 }
1414 else if(!data->set.connect_only && data->set.mail_rcpt &&
1415 (data->state.upload || IS_MIME_POST(data))) {
1416
1417 smtp_state(data, SMTP_POSTDATA);
1418
1419 /* Run the state-machine */
1420 result = smtp_block_statemach(data, conn, FALSE);
1421 }
1422
1423 /* Clear the transfer mode for the next request */
1424 smtp->transfer = PPTRANSFER_BODY;
1425 CURL_TRC_SMTP(data, "smtp_done(status=%d, premature=%d) -> %d",
1426 status, premature, result);
1427 return result;
1428 }
1429
1430 /***********************************************************************
1431 *
1432 * smtp_perform()
1433 *
1434 * This is the actual DO function for SMTP. Transfer a mail, send a command
1435 * or get some data according to the options previously setup.
1436 */
smtp_perform(struct Curl_easy * data,bool * connected,bool * dophase_done)1437 static CURLcode smtp_perform(struct Curl_easy *data, bool *connected,
1438 bool *dophase_done)
1439 {
1440 /* This is SMTP and no proxy */
1441 CURLcode result = CURLE_OK;
1442 struct SMTP *smtp = data->req.p.smtp;
1443
1444 CURL_TRC_SMTP(data, "smtp_perform(), start");
1445
1446 if(data->req.no_body) {
1447 /* Requested no body means no transfer */
1448 smtp->transfer = PPTRANSFER_INFO;
1449 }
1450
1451 *dophase_done = FALSE; /* not done yet */
1452
1453 /* Store the first recipient (or NULL if not specified) */
1454 smtp->rcpt = data->set.mail_rcpt;
1455
1456 /* Track of whether we have successfully sent at least one RCPT TO command */
1457 smtp->rcpt_had_ok = FALSE;
1458
1459 /* Track of the last error we have received by sending RCPT TO command */
1460 smtp->rcpt_last_error = 0;
1461
1462 /* Initial data character is the first character in line: it is implicitly
1463 preceded by a virtual CRLF. */
1464 smtp->trailing_crlf = TRUE;
1465 smtp->eob = 2;
1466
1467 /* Start the first command in the DO phase */
1468 if((data->state.upload || IS_MIME_POST(data)) && data->set.mail_rcpt)
1469 /* MAIL transfer */
1470 result = smtp_perform_mail(data);
1471 else
1472 /* SMTP based command (VRFY, EXPN, NOOP, RSET or HELP) */
1473 result = smtp_perform_command(data);
1474
1475 if(result)
1476 goto out;
1477
1478 /* Run the state-machine */
1479 result = smtp_multi_statemach(data, dophase_done);
1480
1481 *connected = Curl_conn_is_connected(data->conn, FIRSTSOCKET);
1482
1483 out:
1484 CURL_TRC_SMTP(data, "smtp_perform() -> %d, connected=%d, done=%d",
1485 result, *connected, *dophase_done);
1486 return result;
1487 }
1488
1489 /***********************************************************************
1490 *
1491 * smtp_do()
1492 *
1493 * This function is registered as 'curl_do' function. It decodes the path
1494 * parts etc as a wrapper to the actual DO function (smtp_perform).
1495 *
1496 * The input argument is already checked for validity.
1497 */
smtp_do(struct Curl_easy * data,bool * done)1498 static CURLcode smtp_do(struct Curl_easy *data, bool *done)
1499 {
1500 CURLcode result = CURLE_OK;
1501 DEBUGASSERT(data);
1502 DEBUGASSERT(data->conn);
1503 *done = FALSE; /* default to false */
1504
1505 /* Parse the custom request */
1506 result = smtp_parse_custom_request(data);
1507 if(result)
1508 return result;
1509
1510 result = smtp_regular_transfer(data, done);
1511 CURL_TRC_SMTP(data, "smtp_do() -> %d, done=%d", result, *done);
1512 return result;
1513 }
1514
1515 /***********************************************************************
1516 *
1517 * smtp_disconnect()
1518 *
1519 * Disconnect from an SMTP server. Cleanup protocol-specific per-connection
1520 * resources. BLOCKING.
1521 */
smtp_disconnect(struct Curl_easy * data,struct connectdata * conn,bool dead_connection)1522 static CURLcode smtp_disconnect(struct Curl_easy *data,
1523 struct connectdata *conn,
1524 bool dead_connection)
1525 {
1526 struct smtp_conn *smtpc = &conn->proto.smtpc;
1527 (void)data;
1528
1529 /* We cannot send quit unconditionally. If this connection is stale or
1530 bad in any way, sending quit and waiting around here will make the
1531 disconnect wait in vain and cause more problems than we need to. */
1532
1533 if(!dead_connection && conn->bits.protoconnstart) {
1534 if(!smtp_perform_quit(data, conn))
1535 (void)smtp_block_statemach(data, conn, TRUE); /* ignore errors on QUIT */
1536 }
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 CURL_TRC_SMTP(data, "smtp_disconnect(), finished");
1547
1548 return CURLE_OK;
1549 }
1550
1551 /* Call this when the DO phase has completed */
smtp_dophase_done(struct Curl_easy * data,bool connected)1552 static CURLcode smtp_dophase_done(struct Curl_easy *data, bool connected)
1553 {
1554 struct SMTP *smtp = data->req.p.smtp;
1555
1556 (void)connected;
1557
1558 if(smtp->transfer != PPTRANSFER_BODY)
1559 /* no data to transfer */
1560 Curl_xfer_setup_nop(data);
1561
1562 return CURLE_OK;
1563 }
1564
1565 /* Called from multi.c while DOing */
smtp_doing(struct Curl_easy * data,bool * dophase_done)1566 static CURLcode smtp_doing(struct Curl_easy *data, bool *dophase_done)
1567 {
1568 CURLcode result = smtp_multi_statemach(data, dophase_done);
1569
1570 if(result)
1571 DEBUGF(infof(data, "DO phase failed"));
1572 else if(*dophase_done) {
1573 result = smtp_dophase_done(data, FALSE /* not connected */);
1574
1575 DEBUGF(infof(data, "DO phase is complete"));
1576 }
1577
1578 CURL_TRC_SMTP(data, "smtp_doing() -> %d, done=%d", result, *dophase_done);
1579 return result;
1580 }
1581
1582 /***********************************************************************
1583 *
1584 * smtp_regular_transfer()
1585 *
1586 * The input argument is already checked for validity.
1587 *
1588 * Performs all commands done before a regular transfer between a local and a
1589 * remote host.
1590 */
smtp_regular_transfer(struct Curl_easy * data,bool * dophase_done)1591 static CURLcode smtp_regular_transfer(struct Curl_easy *data,
1592 bool *dophase_done)
1593 {
1594 CURLcode result = CURLE_OK;
1595 bool connected = FALSE;
1596
1597 /* Make sure size is unknown at this point */
1598 data->req.size = -1;
1599
1600 /* Set the progress data */
1601 Curl_pgrsSetUploadCounter(data, 0);
1602 Curl_pgrsSetDownloadCounter(data, 0);
1603 Curl_pgrsSetUploadSize(data, -1);
1604 Curl_pgrsSetDownloadSize(data, -1);
1605
1606 /* Carry out the perform */
1607 result = smtp_perform(data, &connected, dophase_done);
1608
1609 /* Perform post DO phase operations if necessary */
1610 if(!result && *dophase_done)
1611 result = smtp_dophase_done(data, connected);
1612
1613 CURL_TRC_SMTP(data, "smtp_regular_transfer() -> %d, done=%d",
1614 result, *dophase_done);
1615 return result;
1616 }
1617
smtp_setup_connection(struct Curl_easy * data,struct connectdata * conn)1618 static CURLcode smtp_setup_connection(struct Curl_easy *data,
1619 struct connectdata *conn)
1620 {
1621 CURLcode result;
1622
1623 /* Clear the TLS upgraded flag */
1624 conn->bits.tls_upgraded = FALSE;
1625
1626 /* Initialise the SMTP layer */
1627 result = smtp_init(data);
1628 CURL_TRC_SMTP(data, "smtp_setup_connection() -> %d", result);
1629 return result;
1630 }
1631
1632 /***********************************************************************
1633 *
1634 * smtp_parse_url_options()
1635 *
1636 * Parse the URL login options.
1637 */
smtp_parse_url_options(struct connectdata * conn)1638 static CURLcode smtp_parse_url_options(struct connectdata *conn)
1639 {
1640 CURLcode result = CURLE_OK;
1641 struct smtp_conn *smtpc = &conn->proto.smtpc;
1642 const char *ptr = conn->options;
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 Curl_easy * data)1675 static CURLcode smtp_parse_url_path(struct Curl_easy *data)
1676 {
1677 /* The SMTP struct is already initialised in smtp_connect() */
1678 struct connectdata *conn = data->conn;
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(path, 0, &smtpc->domain, NULL, REJECT_CTRL);
1693 }
1694
1695 /***********************************************************************
1696 *
1697 * smtp_parse_custom_request()
1698 *
1699 * Parse the custom request.
1700 */
smtp_parse_custom_request(struct Curl_easy * data)1701 static CURLcode smtp_parse_custom_request(struct Curl_easy *data)
1702 {
1703 CURLcode result = CURLE_OK;
1704 struct SMTP *smtp = data->req.p.smtp;
1705 const char *custom = data->set.str[STRING_CUSTOMREQUEST];
1706
1707 /* URL decode the custom request */
1708 if(custom)
1709 result = Curl_urldecode(custom, 0, &smtp->custom, NULL, REJECT_CTRL);
1710
1711 return result;
1712 }
1713
1714 /***********************************************************************
1715 *
1716 * smtp_parse_address()
1717 *
1718 * Parse the fully qualified mailbox address into a local address part and the
1719 * hostname, converting the hostname to an IDN A-label, as per RFC-5890, if
1720 * necessary.
1721 *
1722 * Parameters:
1723 *
1724 * conn [in] - The connection handle.
1725 * fqma [in] - The fully qualified mailbox address (which may or
1726 * may not contain UTF-8 characters).
1727 * address [in/out] - A new allocated buffer which holds the local
1728 * address part of the mailbox. This buffer must be
1729 * free'ed by the caller.
1730 * host [in/out] - The hostname structure that holds the original,
1731 * and optionally encoded, hostname.
1732 * Curl_free_idnconverted_hostname() must be called
1733 * once the caller has finished with the structure.
1734 *
1735 * Returns CURLE_OK on success.
1736 *
1737 * Notes:
1738 *
1739 * Should a UTF-8 hostname require conversion to IDN ACE and we cannot honor
1740 * that conversion then we shall return success. This allow the caller to send
1741 * the data to the server as a U-label (as per RFC-6531 sect. 3.2).
1742 *
1743 * If an mailbox '@' separator cannot be located then the mailbox is considered
1744 * to be either a local mailbox or an invalid mailbox (depending on what the
1745 * calling function deems it to be) then the input will simply be returned in
1746 * the address part with the hostname being NULL.
1747 */
smtp_parse_address(const char * fqma,char ** address,struct hostname * host)1748 static CURLcode smtp_parse_address(const char *fqma, char **address,
1749 struct hostname *host)
1750 {
1751 CURLcode result = CURLE_OK;
1752 size_t length;
1753
1754 /* Duplicate the fully qualified email address so we can manipulate it,
1755 ensuring it does not contain the delimiters if specified */
1756 char *dup = strdup(fqma[0] == '<' ? fqma + 1 : fqma);
1757 if(!dup)
1758 return CURLE_OUT_OF_MEMORY;
1759
1760 length = strlen(dup);
1761 if(length) {
1762 if(dup[length - 1] == '>')
1763 dup[length - 1] = '\0';
1764 }
1765
1766 /* Extract the hostname 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 hostname to IDN ACE */
1773 (void) Curl_idnconvert_hostname(host);
1774
1775 /* If Curl_idnconvert_hostname() fails then we shall attempt to continue
1776 and send the hostname 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
1786 struct cr_eob_ctx {
1787 struct Curl_creader super;
1788 struct bufq buf;
1789 size_t n_eob; /* how many EOB bytes we matched so far */
1790 size_t eob; /* Number of bytes of the EOB (End Of Body) that
1791 have been received so far */
1792 BIT(read_eos); /* we read an EOS from the next reader */
1793 BIT(eos); /* we have returned an EOS */
1794 };
1795
cr_eob_init(struct Curl_easy * data,struct Curl_creader * reader)1796 static CURLcode cr_eob_init(struct Curl_easy *data,
1797 struct Curl_creader *reader)
1798 {
1799 struct cr_eob_ctx *ctx = reader->ctx;
1800 (void)data;
1801 /* The first char we read is the first on a line, as if we had
1802 * read CRLF just before */
1803 ctx->n_eob = 2;
1804 Curl_bufq_init2(&ctx->buf, (16 * 1024), 1, BUFQ_OPT_SOFT_LIMIT);
1805 return CURLE_OK;
1806 }
1807
cr_eob_close(struct Curl_easy * data,struct Curl_creader * reader)1808 static void cr_eob_close(struct Curl_easy *data, struct Curl_creader *reader)
1809 {
1810 struct cr_eob_ctx *ctx = reader->ctx;
1811 (void)data;
1812 Curl_bufq_free(&ctx->buf);
1813 }
1814
1815 /* this is the 5-bytes End-Of-Body marker for SMTP */
1816 #define SMTP_EOB "\r\n.\r\n"
1817 #define SMTP_EOB_FIND_LEN 3
1818
1819 /* client reader doing SMTP End-Of-Body escaping. */
cr_eob_read(struct Curl_easy * data,struct Curl_creader * reader,char * buf,size_t blen,size_t * pnread,bool * peos)1820 static CURLcode cr_eob_read(struct Curl_easy *data,
1821 struct Curl_creader *reader,
1822 char *buf, size_t blen,
1823 size_t *pnread, bool *peos)
1824 {
1825 struct cr_eob_ctx *ctx = reader->ctx;
1826 CURLcode result = CURLE_OK;
1827 size_t nread, i, start, n;
1828 bool eos;
1829
1830 if(!ctx->read_eos && Curl_bufq_is_empty(&ctx->buf)) {
1831 /* Get more and convert it when needed */
1832 result = Curl_creader_read(data, reader->next, buf, blen, &nread, &eos);
1833 if(result)
1834 return result;
1835
1836 ctx->read_eos = eos;
1837 if(nread) {
1838 if(!ctx->n_eob && !memchr(buf, SMTP_EOB[0], nread)) {
1839 /* not in the middle of a match, no EOB start found, just pass */
1840 *pnread = nread;
1841 *peos = FALSE;
1842 return CURLE_OK;
1843 }
1844 /* scan for EOB (continuation) and convert */
1845 for(i = start = 0; i < nread; ++i) {
1846 if(ctx->n_eob >= SMTP_EOB_FIND_LEN) {
1847 /* matched the EOB prefix and seeing additional char, add '.' */
1848 result = Curl_bufq_cwrite(&ctx->buf, buf + start, i - start, &n);
1849 if(result)
1850 return result;
1851 result = Curl_bufq_cwrite(&ctx->buf, ".", 1, &n);
1852 if(result)
1853 return result;
1854 ctx->n_eob = 0;
1855 start = i;
1856 if(data->state.infilesize > 0)
1857 data->state.infilesize++;
1858 }
1859
1860 if(buf[i] != SMTP_EOB[ctx->n_eob])
1861 ctx->n_eob = 0;
1862
1863 if(buf[i] == SMTP_EOB[ctx->n_eob]) {
1864 /* matching another char of the EOB */
1865 ++ctx->n_eob;
1866 }
1867 }
1868
1869 /* add any remainder to buf */
1870 if(start < nread) {
1871 result = Curl_bufq_cwrite(&ctx->buf, buf + start, nread - start, &n);
1872 if(result)
1873 return result;
1874 }
1875 }
1876
1877 if(ctx->read_eos) {
1878 /* if we last matched a CRLF or if the data was empty, add ".\r\n"
1879 * to end the body. If we sent something and it did not end with "\r\n",
1880 * add "\r\n.\r\n" to end the body */
1881 const char *eob = SMTP_EOB;
1882 switch(ctx->n_eob) {
1883 case 2:
1884 /* seen a CRLF at the end, just add the remainder */
1885 eob = &SMTP_EOB[2];
1886 break;
1887 case 3:
1888 /* ended with '\r\n.', we should escpe the last '.' */
1889 eob = "." SMTP_EOB;
1890 break;
1891 default:
1892 break;
1893 }
1894 result = Curl_bufq_cwrite(&ctx->buf, eob, strlen(eob), &n);
1895 if(result)
1896 return result;
1897 }
1898 }
1899
1900 *peos = FALSE;
1901 if(!Curl_bufq_is_empty(&ctx->buf)) {
1902 result = Curl_bufq_cread(&ctx->buf, buf, blen, pnread);
1903 }
1904 else
1905 *pnread = 0;
1906
1907 if(ctx->read_eos && Curl_bufq_is_empty(&ctx->buf)) {
1908 /* no more data, read all, done. */
1909 ctx->eos = TRUE;
1910 }
1911 *peos = ctx->eos;
1912 DEBUGF(infof(data, "cr_eob_read(%zu) -> %d, %zd, %d",
1913 blen, result, *pnread, *peos));
1914 return result;
1915 }
1916
cr_eob_total_length(struct Curl_easy * data,struct Curl_creader * reader)1917 static curl_off_t cr_eob_total_length(struct Curl_easy *data,
1918 struct Curl_creader *reader)
1919 {
1920 /* this reader changes length depending on input */
1921 (void)data;
1922 (void)reader;
1923 return -1;
1924 }
1925
1926 static const struct Curl_crtype cr_eob = {
1927 "cr-smtp-eob",
1928 cr_eob_init,
1929 cr_eob_read,
1930 cr_eob_close,
1931 Curl_creader_def_needs_rewind,
1932 cr_eob_total_length,
1933 Curl_creader_def_resume_from,
1934 Curl_creader_def_rewind,
1935 Curl_creader_def_unpause,
1936 Curl_creader_def_is_paused,
1937 Curl_creader_def_done,
1938 sizeof(struct cr_eob_ctx)
1939 };
1940
cr_eob_add(struct Curl_easy * data)1941 static CURLcode cr_eob_add(struct Curl_easy *data)
1942 {
1943 struct Curl_creader *reader = NULL;
1944 CURLcode result;
1945
1946 result = Curl_creader_create(&reader, data, &cr_eob,
1947 CURL_CR_CONTENT_ENCODE);
1948 if(!result)
1949 result = Curl_creader_add(data, reader);
1950
1951 if(result && reader)
1952 Curl_creader_free(data, reader);
1953 return result;
1954 }
1955
1956 #endif /* CURL_DISABLE_SMTP */
1957