VirtualBox

source: vbox/trunk/src/libs/curl-8.7.1/lib/smtp.c@ 104085

Last change on this file since 104085 was 104083, checked in by vboxsync, 11 months ago

curl-8.7.1: Applied and adjusted our curl changes to 8.4.0. bugref:10639

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette