1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
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.
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.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 * SPDX-License-Identifier: curl
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>
40 ***************************************************************************/
42 #include "curl_setup.h"
44 #ifndef CURL_DISABLE_SMTP
46 #ifdef HAVE_NETINET_IN_H
47 #include <netinet/in.h>
49 #ifdef HAVE_ARPA_INET_H
50 #include <arpa/inet.h>
53 #include <sys/utsname.h>
63 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
65 #define in_addr_t unsigned long
68 #include <curl/curl.h>
75 #include "http.h" /* for HTTP proxy tunnel stuff */
79 #include "strtoofft.h"
81 #include "vtls/vtls.h"
86 #include "curl_gethostname.h"
88 #include "curl_sasl.h"
90 /* The last 3 #include files should be in this order */
91 #include "curl_printf.h"
92 #include "curl_memory.h"
95 /* Local API functions */
96 static CURLcode smtp_regular_transfer(struct Curl_easy *data, bool *done);
97 static CURLcode smtp_do(struct Curl_easy *data, bool *done);
98 static CURLcode smtp_done(struct Curl_easy *data, CURLcode status,
100 static CURLcode smtp_connect(struct Curl_easy *data, bool *done);
101 static CURLcode smtp_disconnect(struct Curl_easy *data,
102 struct connectdata *conn, bool dead);
103 static CURLcode smtp_multi_statemach(struct Curl_easy *data, bool *done);
104 static int smtp_getsock(struct Curl_easy *data,
105 struct connectdata *conn, curl_socket_t *socks);
106 static CURLcode smtp_doing(struct Curl_easy *data, bool *dophase_done);
107 static CURLcode smtp_setup_connection(struct Curl_easy *data,
108 struct connectdata *conn);
109 static CURLcode smtp_parse_url_options(struct connectdata *conn);
110 static CURLcode smtp_parse_url_path(struct Curl_easy *data);
111 static CURLcode smtp_parse_custom_request(struct Curl_easy *data);
112 static CURLcode smtp_parse_address(struct Curl_easy *data, const char *fqma,
113 char **address, struct hostname *host);
114 static CURLcode smtp_perform_auth(struct Curl_easy *data, const char *mech,
115 const struct bufref *initresp);
116 static CURLcode smtp_continue_auth(struct Curl_easy *data, const char *mech,
117 const struct bufref *resp);
118 static CURLcode smtp_cancel_auth(struct Curl_easy *data, const char *mech);
119 static CURLcode smtp_get_message(struct Curl_easy *data, struct bufref *out);
122 * SMTP protocol handler.
125 const struct Curl_handler Curl_handler_smtp = {
127 smtp_setup_connection, /* setup_connection */
129 smtp_done, /* done */
130 ZERO_NULL, /* do_more */
131 smtp_connect, /* connect_it */
132 smtp_multi_statemach, /* connecting */
133 smtp_doing, /* doing */
134 smtp_getsock, /* proto_getsock */
135 smtp_getsock, /* doing_getsock */
136 ZERO_NULL, /* domore_getsock */
137 ZERO_NULL, /* perform_getsock */
138 smtp_disconnect, /* disconnect */
139 ZERO_NULL, /* readwrite */
140 ZERO_NULL, /* connection_check */
141 ZERO_NULL, /* attach connection */
142 PORT_SMTP, /* defport */
143 CURLPROTO_SMTP, /* protocol */
144 CURLPROTO_SMTP, /* family */
145 PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY | /* flags */
151 * SMTPS protocol handler.
154 const struct Curl_handler Curl_handler_smtps = {
155 "SMTPS", /* scheme */
156 smtp_setup_connection, /* setup_connection */
158 smtp_done, /* done */
159 ZERO_NULL, /* do_more */
160 smtp_connect, /* connect_it */
161 smtp_multi_statemach, /* connecting */
162 smtp_doing, /* doing */
163 smtp_getsock, /* proto_getsock */
164 smtp_getsock, /* doing_getsock */
165 ZERO_NULL, /* domore_getsock */
166 ZERO_NULL, /* perform_getsock */
167 smtp_disconnect, /* disconnect */
168 ZERO_NULL, /* readwrite */
169 ZERO_NULL, /* connection_check */
170 ZERO_NULL, /* attach connection */
171 PORT_SMTPS, /* defport */
172 CURLPROTO_SMTPS, /* protocol */
173 CURLPROTO_SMTP, /* family */
174 PROTOPT_CLOSEACTION | PROTOPT_SSL
175 | PROTOPT_NOURLQUERY | PROTOPT_URLOPTIONS /* flags */
179 /* SASL parameters for the smtp protocol */
180 static const struct SASLproto saslsmtp = {
181 "smtp", /* The service name */
182 smtp_perform_auth, /* Send authentication command */
183 smtp_continue_auth, /* Send authentication continuation */
184 smtp_cancel_auth, /* Cancel authentication */
185 smtp_get_message, /* Get SASL response message */
186 512 - 8, /* Max line len - strlen("AUTH ") - 1 space - crlf */
187 334, /* Code received when continuation is expected */
188 235, /* Code to receive upon authentication success */
189 SASL_AUTH_DEFAULT, /* Default mechanisms */
190 SASL_FLAG_BASE64 /* Configuration flags */
194 static void smtp_to_smtps(struct connectdata *conn)
196 /* Change the connection handler */
197 conn->handler = &Curl_handler_smtps;
199 /* Set the connection's upgraded to TLS flag */
200 conn->bits.tls_upgraded = TRUE;
203 #define smtp_to_smtps(x) Curl_nop_stmt
206 /***********************************************************************
210 * Checks for an ending SMTP status code at the start of the given string, but
211 * also detects various capabilities from the EHLO response including the
212 * supported authentication mechanisms.
214 static bool smtp_endofresp(struct Curl_easy *data, struct connectdata *conn,
215 char *line, size_t len, int *resp)
217 struct smtp_conn *smtpc = &conn->proto.smtpc;
222 if(len < 4 || !ISDIGIT(line[0]) || !ISDIGIT(line[1]) || !ISDIGIT(line[2]))
225 /* Do we have a command response? This should be the response code followed
226 by a space and optionally some text as per RFC-5321 and as outlined in
227 Section 4. Examples of RFC-4954 but some email servers ignore this and
228 only send the response code instead as per Section 4.2. */
229 if(line[3] == ' ' || len == 5) {
233 memset(tmpline, '\0', sizeof(tmpline));
234 memcpy(tmpline, line, (len == 5 ? 5 : 3));
235 *resp = curlx_sltosi(strtol(tmpline, NULL, 10));
237 /* Make sure real server never sends internal value */
241 /* Do we have a multiline (continuation) response? */
242 else if(line[3] == '-' &&
243 (smtpc->state == SMTP_EHLO || smtpc->state == SMTP_COMMAND)) {
245 *resp = 1; /* Internal response code */
251 /***********************************************************************
255 * Gets the authentication message from the response buffer.
257 static CURLcode smtp_get_message(struct Curl_easy *data, struct bufref *out)
259 char *message = data->state.buffer;
260 size_t len = strlen(message);
263 /* Find the start of the message */
265 for(message += 4; *message == ' ' || *message == '\t'; message++, len--)
268 /* Find the end of the message */
270 if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' &&
271 message[len] != '\t')
274 /* Terminate the message */
275 message[++len] = '\0';
276 Curl_bufref_set(out, message, len, NULL);
279 /* junk input => zero length output */
280 Curl_bufref_set(out, "", 0, NULL);
285 /***********************************************************************
289 * This is the ONLY way to change SMTP state!
291 static void state(struct Curl_easy *data, smtpstate newstate)
293 struct smtp_conn *smtpc = &data->conn->proto.smtpc;
294 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
295 /* for debug purposes */
296 static const char * const names[] = {
313 if(smtpc->state != newstate)
314 infof(data, "SMTP %p state change from %s to %s",
315 (void *)smtpc, names[smtpc->state], names[newstate]);
318 smtpc->state = newstate;
321 /***********************************************************************
323 * smtp_perform_ehlo()
325 * Sends the EHLO command to not only initialise communication with the ESMTP
326 * server but to also obtain a list of server side supported capabilities.
328 static CURLcode smtp_perform_ehlo(struct Curl_easy *data)
330 CURLcode result = CURLE_OK;
331 struct connectdata *conn = data->conn;
332 struct smtp_conn *smtpc = &conn->proto.smtpc;
334 smtpc->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanism yet */
335 smtpc->sasl.authused = SASL_AUTH_NONE; /* Clear the authentication mechanism
336 used for esmtp connections */
337 smtpc->tls_supported = FALSE; /* Clear the TLS capability */
338 smtpc->auth_supported = FALSE; /* Clear the AUTH capability */
340 /* Send the EHLO command */
341 result = Curl_pp_sendf(data, &smtpc->pp, "EHLO %s", smtpc->domain);
344 state(data, SMTP_EHLO);
349 /***********************************************************************
351 * smtp_perform_helo()
353 * Sends the HELO command to initialise communication with the SMTP server.
355 static CURLcode smtp_perform_helo(struct Curl_easy *data,
356 struct connectdata *conn)
358 CURLcode result = CURLE_OK;
359 struct smtp_conn *smtpc = &conn->proto.smtpc;
361 smtpc->sasl.authused = SASL_AUTH_NONE; /* No authentication mechanism used
362 in smtp connections */
364 /* Send the HELO command */
365 result = Curl_pp_sendf(data, &smtpc->pp, "HELO %s", smtpc->domain);
368 state(data, SMTP_HELO);
373 /***********************************************************************
375 * smtp_perform_starttls()
377 * Sends the STLS command to start the upgrade to TLS.
379 static CURLcode smtp_perform_starttls(struct Curl_easy *data,
380 struct connectdata *conn)
382 /* Send the STARTTLS command */
383 CURLcode result = Curl_pp_sendf(data, &conn->proto.smtpc.pp,
387 state(data, SMTP_STARTTLS);
392 /***********************************************************************
394 * smtp_perform_upgrade_tls()
396 * Performs the upgrade to TLS.
398 static CURLcode smtp_perform_upgrade_tls(struct Curl_easy *data)
400 /* Start the SSL connection */
401 struct connectdata *conn = data->conn;
402 struct smtp_conn *smtpc = &conn->proto.smtpc;
403 CURLcode result = Curl_ssl_connect_nonblocking(data, conn, FALSE,
408 if(smtpc->state != SMTP_UPGRADETLS)
409 state(data, SMTP_UPGRADETLS);
413 result = smtp_perform_ehlo(data);
420 /***********************************************************************
422 * smtp_perform_auth()
424 * Sends an AUTH command allowing the client to login with the given SASL
425 * authentication mechanism.
427 static CURLcode smtp_perform_auth(struct Curl_easy *data,
429 const struct bufref *initresp)
431 CURLcode result = CURLE_OK;
432 struct smtp_conn *smtpc = &data->conn->proto.smtpc;
433 const char *ir = (const char *) Curl_bufref_ptr(initresp);
435 if(ir) { /* AUTH <mech> ...<crlf> */
436 /* Send the AUTH command with the initial response */
437 result = Curl_pp_sendf(data, &smtpc->pp, "AUTH %s %s", mech, ir);
440 /* Send the AUTH command */
441 result = Curl_pp_sendf(data, &smtpc->pp, "AUTH %s", mech);
447 /***********************************************************************
449 * smtp_continue_auth()
451 * Sends SASL continuation data.
453 static CURLcode smtp_continue_auth(struct Curl_easy *data,
455 const struct bufref *resp)
457 struct smtp_conn *smtpc = &data->conn->proto.smtpc;
461 return Curl_pp_sendf(data, &smtpc->pp,
462 "%s", (const char *) Curl_bufref_ptr(resp));
465 /***********************************************************************
469 * Sends SASL cancellation.
471 static CURLcode smtp_cancel_auth(struct Curl_easy *data, const char *mech)
473 struct smtp_conn *smtpc = &data->conn->proto.smtpc;
477 return Curl_pp_sendf(data, &smtpc->pp, "*");
480 /***********************************************************************
482 * smtp_perform_authentication()
484 * Initiates the authentication sequence, with the appropriate SASL
485 * authentication mechanism.
487 static CURLcode smtp_perform_authentication(struct Curl_easy *data)
489 CURLcode result = CURLE_OK;
490 struct connectdata *conn = data->conn;
491 struct smtp_conn *smtpc = &conn->proto.smtpc;
492 saslprogress progress;
494 /* Check we have enough data to authenticate with, and the
495 server supports authentication, and end the connect phase if not */
496 if(!smtpc->auth_supported ||
497 !Curl_sasl_can_authenticate(&smtpc->sasl, data)) {
498 state(data, SMTP_STOP);
502 /* Calculate the SASL login details */
503 result = Curl_sasl_start(&smtpc->sasl, data, FALSE, &progress);
506 if(progress == SASL_INPROGRESS)
507 state(data, SMTP_AUTH);
509 /* Other mechanisms not supported */
510 infof(data, "No known authentication mechanisms supported");
511 result = CURLE_LOGIN_DENIED;
518 /***********************************************************************
520 * smtp_perform_command()
522 * Sends a SMTP based command.
524 static CURLcode smtp_perform_command(struct Curl_easy *data)
526 CURLcode result = CURLE_OK;
527 struct connectdata *conn = data->conn;
528 struct SMTP *smtp = data->req.p.smtp;
531 /* We notify the server we are sending UTF-8 data if a) it supports the
532 SMTPUTF8 extension and b) The mailbox contains UTF-8 characters, in
533 either the local address or host name parts. This is regardless of
534 whether the host name is encoded using IDN ACE */
537 if((!smtp->custom) || (!smtp->custom[0])) {
538 char *address = NULL;
539 struct hostname host = { NULL, NULL, NULL, NULL };
541 /* Parse the mailbox to verify into the local address and host name
542 parts, converting the host name to an IDN A-label if necessary */
543 result = smtp_parse_address(data, smtp->rcpt->data,
548 /* Establish whether we should report SMTPUTF8 to the server for this
549 mailbox as per RFC-6531 sect. 3.1 point 6 */
550 utf8 = (conn->proto.smtpc.utf8_supported) &&
551 ((host.encalloc) || (!Curl_is_ASCII_name(address)) ||
552 (!Curl_is_ASCII_name(host.name)));
554 /* Send the VRFY command (Note: The host name part may be absent when the
555 host is a local system) */
556 result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "VRFY %s%s%s%s",
558 host.name ? "@" : "",
559 host.name ? host.name : "",
560 utf8 ? " SMTPUTF8" : "");
562 Curl_free_idnconverted_hostname(&host);
566 /* Establish whether we should report that we support SMTPUTF8 for EXPN
567 commands to the server as per RFC-6531 sect. 3.1 point 6 */
568 utf8 = (conn->proto.smtpc.utf8_supported) &&
569 (!strcmp(smtp->custom, "EXPN"));
571 /* Send the custom recipient based command such as the EXPN command */
572 result = Curl_pp_sendf(data, &conn->proto.smtpc.pp,
573 "%s %s%s", smtp->custom,
575 utf8 ? " SMTPUTF8" : "");
579 /* Send the non-recipient based command such as HELP */
580 result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "%s",
581 smtp->custom && smtp->custom[0] != '\0' ?
582 smtp->custom : "HELP");
585 state(data, SMTP_COMMAND);
590 /***********************************************************************
592 * smtp_perform_mail()
594 * Sends an MAIL command to initiate the upload of a message.
596 static CURLcode smtp_perform_mail(struct Curl_easy *data)
601 CURLcode result = CURLE_OK;
602 struct connectdata *conn = data->conn;
604 /* We notify the server we are sending UTF-8 data if a) it supports the
605 SMTPUTF8 extension and b) The mailbox contains UTF-8 characters, in
606 either the local address or host name parts. This is regardless of
607 whether the host name is encoded using IDN ACE */
610 /* Calculate the FROM parameter */
611 if(data->set.str[STRING_MAIL_FROM]) {
612 char *address = NULL;
613 struct hostname host = { NULL, NULL, NULL, NULL };
615 /* Parse the FROM mailbox into the local address and host name parts,
616 converting the host name to an IDN A-label if necessary */
617 result = smtp_parse_address(data, data->set.str[STRING_MAIL_FROM],
622 /* Establish whether we should report SMTPUTF8 to the server for this
623 mailbox as per RFC-6531 sect. 3.1 point 4 and sect. 3.4 */
624 utf8 = (conn->proto.smtpc.utf8_supported) &&
625 ((host.encalloc) || (!Curl_is_ASCII_name(address)) ||
626 (!Curl_is_ASCII_name(host.name)));
629 from = aprintf("<%s@%s>", address, host.name);
631 Curl_free_idnconverted_hostname(&host);
634 /* An invalid mailbox was provided but we'll simply let the server worry
635 about that and reply with a 501 error */
636 from = aprintf("<%s>", address);
641 /* Null reverse-path, RFC-5321, sect. 3.6.3 */
645 return CURLE_OUT_OF_MEMORY;
647 /* Calculate the optional AUTH parameter */
648 if(data->set.str[STRING_MAIL_AUTH] && conn->proto.smtpc.sasl.authused) {
649 if(data->set.str[STRING_MAIL_AUTH][0] != '\0') {
650 char *address = NULL;
651 struct hostname host = { NULL, NULL, NULL, NULL };
653 /* Parse the AUTH mailbox into the local address and host name parts,
654 converting the host name to an IDN A-label if necessary */
655 result = smtp_parse_address(data, data->set.str[STRING_MAIL_AUTH],
662 /* Establish whether we should report SMTPUTF8 to the server for this
663 mailbox as per RFC-6531 sect. 3.1 point 4 and sect. 3.4 */
664 if((!utf8) && (conn->proto.smtpc.utf8_supported) &&
665 ((host.encalloc) || (!Curl_is_ASCII_name(address)) ||
666 (!Curl_is_ASCII_name(host.name))))
670 auth = aprintf("<%s@%s>", address, host.name);
672 Curl_free_idnconverted_hostname(&host);
675 /* An invalid mailbox was provided but we'll simply let the server
677 auth = aprintf("<%s>", address);
682 /* Empty AUTH, RFC-2554, sect. 5 */
688 return CURLE_OUT_OF_MEMORY;
692 /* Prepare the mime data if some. */
693 if(data->set.mimepost.kind != MIMEKIND_NONE) {
694 /* Use the whole structure as data. */
695 data->set.mimepost.flags &= ~MIME_BODY_ONLY;
697 /* Add external headers and mime version. */
698 curl_mime_headers(&data->set.mimepost, data->set.headers, 0);
699 result = Curl_mime_prepare_headers(&data->set.mimepost, NULL,
700 NULL, MIMESTRATEGY_MAIL);
703 if(!Curl_checkheaders(data, STRCONST("Mime-Version")))
704 result = Curl_mime_add_header(&data->set.mimepost.curlheaders,
705 "Mime-Version: 1.0");
707 /* Make sure we will read the entire mime structure. */
709 result = Curl_mime_rewind(&data->set.mimepost);
718 data->state.infilesize = Curl_mime_size(&data->set.mimepost);
720 /* Read from mime structure. */
721 data->state.fread_func = (curl_read_callback) Curl_mime_read;
722 data->state.in = (void *) &data->set.mimepost;
725 /* Calculate the optional SIZE parameter */
726 if(conn->proto.smtpc.size_supported && data->state.infilesize > 0) {
727 size = aprintf("%" CURL_FORMAT_CURL_OFF_T, data->state.infilesize);
733 return CURLE_OUT_OF_MEMORY;
737 /* If the mailboxes in the FROM and AUTH parameters don't include a UTF-8
738 based address then quickly scan through the recipient list and check if
739 any there do, as we need to correctly identify our support for SMTPUTF8
740 in the envelope, as per RFC-6531 sect. 3.4 */
741 if(conn->proto.smtpc.utf8_supported && !utf8) {
742 struct SMTP *smtp = data->req.p.smtp;
743 struct curl_slist *rcpt = smtp->rcpt;
745 while(rcpt && !utf8) {
746 /* Does the host name contain non-ASCII characters? */
747 if(!Curl_is_ASCII_name(rcpt->data))
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 */
770 state(data, SMTP_MAIL);
775 /***********************************************************************
777 * smtp_perform_rcpt_to()
779 * Sends a RCPT TO command for a given recipient as part of the message upload
782 static CURLcode smtp_perform_rcpt_to(struct Curl_easy *data)
784 CURLcode result = CURLE_OK;
785 struct connectdata *conn = data->conn;
786 struct SMTP *smtp = data->req.p.smtp;
787 char *address = NULL;
788 struct hostname host = { NULL, NULL, NULL, NULL };
790 /* Parse the recipient mailbox into the local address and host name parts,
791 converting the host name to an IDN A-label if necessary */
792 result = smtp_parse_address(data, smtp->rcpt->data,
797 /* Send the RCPT TO command */
799 result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "RCPT TO:<%s@%s>",
802 /* An invalid mailbox was provided but we'll simply let the server worry
803 about that and reply with a 501 error */
804 result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "RCPT TO:<%s>",
807 Curl_free_idnconverted_hostname(&host);
811 state(data, SMTP_RCPT);
816 /***********************************************************************
818 * smtp_perform_quit()
820 * Performs the quit action prior to sclose() being called.
822 static CURLcode smtp_perform_quit(struct Curl_easy *data,
823 struct connectdata *conn)
825 /* Send the QUIT command */
826 CURLcode result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "%s", "QUIT");
829 state(data, SMTP_QUIT);
834 /* For the initial server greeting */
835 static CURLcode smtp_state_servergreet_resp(struct Curl_easy *data,
839 CURLcode result = CURLE_OK;
840 (void)instate; /* no use for this yet */
842 if(smtpcode/100 != 2) {
843 failf(data, "Got unexpected smtp-server response: %d", smtpcode);
844 result = CURLE_WEIRD_SERVER_REPLY;
847 result = smtp_perform_ehlo(data);
852 /* For STARTTLS responses */
853 static CURLcode smtp_state_starttls_resp(struct Curl_easy *data,
857 CURLcode result = CURLE_OK;
858 (void)instate; /* no use for this yet */
860 /* Pipelining in response is forbidden. */
861 if(data->conn->proto.smtpc.pp.cache_size)
862 return CURLE_WEIRD_SERVER_REPLY;
864 if(smtpcode != 220) {
865 if(data->set.use_ssl != CURLUSESSL_TRY) {
866 failf(data, "STARTTLS denied, code %d", smtpcode);
867 result = CURLE_USE_SSL_FAILED;
870 result = smtp_perform_authentication(data);
873 result = smtp_perform_upgrade_tls(data);
878 /* For EHLO responses */
879 static CURLcode smtp_state_ehlo_resp(struct Curl_easy *data,
880 struct connectdata *conn, int smtpcode,
883 CURLcode result = CURLE_OK;
884 struct smtp_conn *smtpc = &conn->proto.smtpc;
885 const char *line = data->state.buffer;
886 size_t len = strlen(line);
888 (void)instate; /* no use for this yet */
890 if(smtpcode/100 != 2 && smtpcode != 1) {
891 if(data->set.use_ssl <= CURLUSESSL_TRY || conn->ssl[FIRSTSOCKET].use)
892 result = smtp_perform_helo(data, conn);
894 failf(data, "Remote access denied: %d", smtpcode);
895 result = CURLE_REMOTE_ACCESS_DENIED;
902 /* Does the server support the STARTTLS capability? */
903 if(len >= 8 && !memcmp(line, "STARTTLS", 8))
904 smtpc->tls_supported = TRUE;
906 /* Does the server support the SIZE capability? */
907 else if(len >= 4 && !memcmp(line, "SIZE", 4))
908 smtpc->size_supported = TRUE;
910 /* Does the server support the UTF-8 capability? */
911 else if(len >= 8 && !memcmp(line, "SMTPUTF8", 8))
912 smtpc->utf8_supported = TRUE;
914 /* Does the server support authentication? */
915 else if(len >= 5 && !memcmp(line, "AUTH ", 5)) {
916 smtpc->auth_supported = TRUE;
918 /* Advance past the AUTH keyword */
922 /* Loop through the data line */
926 unsigned short mechbit;
929 (*line == ' ' || *line == '\t' ||
930 *line == '\r' || *line == '\n')) {
939 /* Extract the word */
940 for(wordlen = 0; wordlen < len && line[wordlen] != ' ' &&
941 line[wordlen] != '\t' && line[wordlen] != '\r' &&
942 line[wordlen] != '\n';)
945 /* Test the word for a matching authentication mechanism */
946 mechbit = Curl_sasl_decode_mech(line, wordlen, &llen);
947 if(mechbit && llen == wordlen)
948 smtpc->sasl.authmechs |= mechbit;
956 if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
957 /* We don't have a SSL/TLS connection yet, but SSL is requested */
958 if(smtpc->tls_supported)
959 /* Switch to TLS connection now */
960 result = smtp_perform_starttls(data, conn);
961 else if(data->set.use_ssl == CURLUSESSL_TRY)
962 /* Fallback and carry on with authentication */
963 result = smtp_perform_authentication(data);
965 failf(data, "STARTTLS not supported.");
966 result = CURLE_USE_SSL_FAILED;
970 result = smtp_perform_authentication(data);
974 failf(data, "Unexpectedly short EHLO response");
975 result = CURLE_WEIRD_SERVER_REPLY;
981 /* For HELO responses */
982 static CURLcode smtp_state_helo_resp(struct Curl_easy *data, int smtpcode,
985 CURLcode result = CURLE_OK;
986 (void)instate; /* no use for this yet */
988 if(smtpcode/100 != 2) {
989 failf(data, "Remote access denied: %d", smtpcode);
990 result = CURLE_REMOTE_ACCESS_DENIED;
993 /* End of connect phase */
994 state(data, SMTP_STOP);
999 /* For SASL authentication responses */
1000 static CURLcode smtp_state_auth_resp(struct Curl_easy *data,
1004 CURLcode result = CURLE_OK;
1005 struct connectdata *conn = data->conn;
1006 struct smtp_conn *smtpc = &conn->proto.smtpc;
1007 saslprogress progress;
1009 (void)instate; /* no use for this yet */
1011 result = Curl_sasl_continue(&smtpc->sasl, data, smtpcode, &progress);
1015 state(data, SMTP_STOP); /* Authenticated */
1017 case SASL_IDLE: /* No mechanism left after cancellation */
1018 failf(data, "Authentication cancelled");
1019 result = CURLE_LOGIN_DENIED;
1028 /* For command responses */
1029 static CURLcode smtp_state_command_resp(struct Curl_easy *data, int smtpcode,
1032 CURLcode result = CURLE_OK;
1033 struct SMTP *smtp = data->req.p.smtp;
1034 char *line = data->state.buffer;
1035 size_t len = strlen(line);
1037 (void)instate; /* no use for this yet */
1039 if((smtp->rcpt && smtpcode/100 != 2 && smtpcode != 553 && smtpcode != 1) ||
1040 (!smtp->rcpt && smtpcode/100 != 2 && smtpcode != 1)) {
1041 failf(data, "Command failed: %d", smtpcode);
1042 result = CURLE_WEIRD_SERVER_REPLY;
1045 /* Temporarily add the LF character back and send as body to the client */
1046 if(!data->set.opt_no_body) {
1048 result = Curl_client_write(data, CLIENTWRITE_BODY, line, len + 1);
1054 smtp->rcpt = smtp->rcpt->next;
1057 /* Send the next command */
1058 result = smtp_perform_command(data);
1061 /* End of DO phase */
1062 state(data, SMTP_STOP);
1065 /* End of DO phase */
1066 state(data, SMTP_STOP);
1073 /* For MAIL responses */
1074 static CURLcode smtp_state_mail_resp(struct Curl_easy *data, int smtpcode,
1077 CURLcode result = CURLE_OK;
1078 (void)instate; /* no use for this yet */
1080 if(smtpcode/100 != 2) {
1081 failf(data, "MAIL failed: %d", smtpcode);
1082 result = CURLE_SEND_ERROR;
1085 /* Start the RCPT TO command */
1086 result = smtp_perform_rcpt_to(data);
1091 /* For RCPT responses */
1092 static CURLcode smtp_state_rcpt_resp(struct Curl_easy *data,
1093 struct connectdata *conn, int smtpcode,
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;
1101 (void)instate; /* no use for this yet */
1103 is_smtp_err = (smtpcode/100 != 2) ? TRUE : FALSE;
1105 /* If there's multiple RCPT TO to be issued, it's possible to ignore errors
1106 and proceed with only the valid addresses. */
1107 is_smtp_blocking_err =
1108 (is_smtp_err && !data->set.mail_rcpt_allowfails) ? TRUE : FALSE;
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;
1115 if(is_smtp_blocking_err) {
1116 failf(data, "RCPT failed: %d", smtpcode);
1117 result = CURLE_SEND_ERROR;
1121 /* Some RCPT TO commands have succeeded. */
1122 smtp->rcpt_had_ok = TRUE;
1125 if(!is_smtp_blocking_err) {
1126 smtp->rcpt = smtp->rcpt->next;
1129 /* Send the next RCPT TO command */
1130 result = smtp_perform_rcpt_to(data);
1132 /* We weren't able to issue a successful RCPT TO command while going
1133 over recipients (potentially multiple). Sending back last error. */
1134 if(!smtp->rcpt_had_ok) {
1135 failf(data, "RCPT failed: %d (last error)", smtp->rcpt_last_error);
1136 result = CURLE_SEND_ERROR;
1139 /* Send the DATA command */
1140 result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "%s", "DATA");
1143 state(data, SMTP_DATA);
1151 /* For DATA response */
1152 static CURLcode smtp_state_data_resp(struct Curl_easy *data, int smtpcode,
1155 CURLcode result = CURLE_OK;
1156 (void)instate; /* no use for this yet */
1158 if(smtpcode != 354) {
1159 failf(data, "DATA failed: %d", smtpcode);
1160 result = CURLE_SEND_ERROR;
1163 /* Set the progress upload size */
1164 Curl_pgrsSetUploadSize(data, data->state.infilesize);
1167 Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET);
1169 /* End of DO phase */
1170 state(data, SMTP_STOP);
1176 /* For POSTDATA responses, which are received after the entire DATA
1177 part has been sent to the server */
1178 static CURLcode smtp_state_postdata_resp(struct Curl_easy *data,
1182 CURLcode result = CURLE_OK;
1184 (void)instate; /* no use for this yet */
1187 result = CURLE_WEIRD_SERVER_REPLY;
1189 /* End of DONE phase */
1190 state(data, SMTP_STOP);
1195 static CURLcode smtp_statemachine(struct Curl_easy *data,
1196 struct connectdata *conn)
1198 CURLcode result = CURLE_OK;
1199 curl_socket_t sock = conn->sock[FIRSTSOCKET];
1201 struct smtp_conn *smtpc = &conn->proto.smtpc;
1202 struct pingpong *pp = &smtpc->pp;
1205 /* Busy upgrading the connection; right now all I/O is SSL/TLS, not SMTP */
1206 if(smtpc->state == SMTP_UPGRADETLS)
1207 return smtp_perform_upgrade_tls(data);
1209 /* Flush any data that needs to be sent */
1211 return Curl_pp_flushsend(data, pp);
1214 /* Read the response from the server */
1215 result = Curl_pp_readresp(data, sock, pp, &smtpcode, &nread);
1219 /* Store the latest response for later retrieval if necessary */
1220 if(smtpc->state != SMTP_QUIT && smtpcode != 1)
1221 data->info.httpcode = smtpcode;
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);
1233 result = smtp_state_ehlo_resp(data, conn, smtpcode, smtpc->state);
1237 result = smtp_state_helo_resp(data, smtpcode, smtpc->state);
1241 result = smtp_state_starttls_resp(data, smtpcode, smtpc->state);
1245 result = smtp_state_auth_resp(data, smtpcode, smtpc->state);
1249 result = smtp_state_command_resp(data, smtpcode, smtpc->state);
1253 result = smtp_state_mail_resp(data, smtpcode, smtpc->state);
1257 result = smtp_state_rcpt_resp(data, conn, smtpcode, smtpc->state);
1261 result = smtp_state_data_resp(data, smtpcode, smtpc->state);
1265 result = smtp_state_postdata_resp(data, smtpcode, smtpc->state);
1269 /* fallthrough, just stop! */
1271 /* internal error */
1272 state(data, SMTP_STOP);
1275 } while(!result && smtpc->state != SMTP_STOP && Curl_pp_moredata(pp));
1280 /* Called repeatedly until done from multi.c */
1281 static CURLcode smtp_multi_statemach(struct Curl_easy *data, bool *done)
1283 CURLcode result = CURLE_OK;
1284 struct connectdata *conn = data->conn;
1285 struct smtp_conn *smtpc = &conn->proto.smtpc;
1287 if((conn->handler->flags & PROTOPT_SSL) && !smtpc->ssldone) {
1288 result = Curl_ssl_connect_nonblocking(data, conn, FALSE,
1289 FIRSTSOCKET, &smtpc->ssldone);
1290 if(result || !smtpc->ssldone)
1294 result = Curl_pp_statemach(data, &smtpc->pp, FALSE, FALSE);
1295 *done = (smtpc->state == SMTP_STOP) ? TRUE : FALSE;
1300 static CURLcode smtp_block_statemach(struct Curl_easy *data,
1301 struct connectdata *conn,
1304 CURLcode result = CURLE_OK;
1305 struct smtp_conn *smtpc = &conn->proto.smtpc;
1307 while(smtpc->state != SMTP_STOP && !result)
1308 result = Curl_pp_statemach(data, &smtpc->pp, TRUE, disconnecting);
1313 /* Allocate and initialize the SMTP struct for the current Curl_easy if
1315 static CURLcode smtp_init(struct Curl_easy *data)
1317 CURLcode result = CURLE_OK;
1320 smtp = data->req.p.smtp = calloc(sizeof(struct SMTP), 1);
1322 result = CURLE_OUT_OF_MEMORY;
1327 /* For the SMTP "protocol connect" and "doing" phases only */
1328 static int smtp_getsock(struct Curl_easy *data,
1329 struct connectdata *conn, curl_socket_t *socks)
1331 return Curl_pp_getsock(data, &conn->proto.smtpc.pp, socks);
1334 /***********************************************************************
1338 * This function should do everything that is to be considered a part of
1339 * the connection phase.
1341 * The variable pointed to by 'done' will be TRUE if the protocol-layer
1342 * connect phase is done when this function returns, or FALSE if not.
1344 static CURLcode smtp_connect(struct Curl_easy *data, bool *done)
1346 CURLcode result = CURLE_OK;
1347 struct connectdata *conn = data->conn;
1348 struct smtp_conn *smtpc = &conn->proto.smtpc;
1349 struct pingpong *pp = &smtpc->pp;
1351 *done = FALSE; /* default to not done yet */
1353 /* We always support persistent connections in SMTP */
1354 connkeep(conn, "SMTP default");
1356 PINGPONG_SETUP(pp, smtp_statemachine, smtp_endofresp);
1358 /* Initialize the SASL storage */
1359 Curl_sasl_init(&smtpc->sasl, data, &saslsmtp);
1361 /* Initialise the pingpong layer */
1363 Curl_pp_init(data, pp);
1365 /* Parse the URL options */
1366 result = smtp_parse_url_options(conn);
1370 /* Parse the URL path */
1371 result = smtp_parse_url_path(data);
1375 /* Start off waiting for the server greeting response */
1376 state(data, SMTP_SERVERGREET);
1378 result = smtp_multi_statemach(data, done);
1383 /***********************************************************************
1387 * The DONE function. This does what needs to be done after a single DO has
1390 * Input argument is already checked for validity.
1392 static CURLcode smtp_done(struct Curl_easy *data, CURLcode status,
1395 CURLcode result = CURLE_OK;
1396 struct connectdata *conn = data->conn;
1397 struct SMTP *smtp = data->req.p.smtp;
1398 struct pingpong *pp = &conn->proto.smtpc.pp;
1401 ssize_t bytes_written;
1408 /* Cleanup our per-request based variables */
1409 Curl_safefree(smtp->custom);
1412 connclose(conn, "SMTP done with bad status"); /* marked for closure */
1413 result = status; /* use the already set error code */
1415 else if(!data->set.connect_only && data->set.mail_rcpt &&
1416 (data->set.upload || data->set.mimepost.kind)) {
1417 /* Calculate the EOB taking into account any terminating CRLF from the
1418 previous line of the email or the CRLF of the DATA command when there
1419 is "no mail data". RFC-5321, sect. 4.1.1.4.
1421 Note: As some SSL backends, such as OpenSSL, will cause Curl_write() to
1422 fail when using a different pointer following a previous write, that
1423 returned CURLE_AGAIN, we duplicate the EOB now rather than when the
1424 bytes written doesn't equal len. */
1425 if(smtp->trailing_crlf || !data->state.infilesize) {
1426 eob = strdup(&SMTP_EOB[2]);
1427 len = SMTP_EOB_LEN - 2;
1430 eob = strdup(SMTP_EOB);
1435 return CURLE_OUT_OF_MEMORY;
1437 /* Send the end of block data */
1438 result = Curl_write(data, conn->writesockfd, eob, len, &bytes_written);
1444 if(bytes_written != len) {
1445 /* The whole chunk was not sent so keep it around and adjust the
1446 pingpong structure accordingly */
1449 pp->sendleft = len - bytes_written;
1452 /* Successfully sent so adjust the response timeout relative to now */
1453 pp->response = Curl_now();
1458 state(data, SMTP_POSTDATA);
1460 /* Run the state-machine */
1461 result = smtp_block_statemach(data, conn, FALSE);
1464 /* Clear the transfer mode for the next request */
1465 smtp->transfer = PPTRANSFER_BODY;
1470 /***********************************************************************
1474 * This is the actual DO function for SMTP. Transfer a mail, send a command
1475 * or get some data according to the options previously setup.
1477 static CURLcode smtp_perform(struct Curl_easy *data, bool *connected,
1480 /* This is SMTP and no proxy */
1481 CURLcode result = CURLE_OK;
1482 struct connectdata *conn = data->conn;
1483 struct SMTP *smtp = data->req.p.smtp;
1485 DEBUGF(infof(data, "DO phase starts"));
1487 if(data->set.opt_no_body) {
1488 /* Requested no body means no transfer */
1489 smtp->transfer = PPTRANSFER_INFO;
1492 *dophase_done = FALSE; /* not done yet */
1494 /* Store the first recipient (or NULL if not specified) */
1495 smtp->rcpt = data->set.mail_rcpt;
1497 /* Track of whether we've successfully sent at least one RCPT TO command */
1498 smtp->rcpt_had_ok = FALSE;
1500 /* Track of the last error we've received by sending RCPT TO command */
1501 smtp->rcpt_last_error = 0;
1503 /* Initial data character is the first character in line: it is implicitly
1504 preceded by a virtual CRLF. */
1505 smtp->trailing_crlf = TRUE;
1508 /* Start the first command in the DO phase */
1509 if((data->set.upload || data->set.mimepost.kind) && data->set.mail_rcpt)
1511 result = smtp_perform_mail(data);
1513 /* SMTP based command (VRFY, EXPN, NOOP, RSET or HELP) */
1514 result = smtp_perform_command(data);
1519 /* Run the state-machine */
1520 result = smtp_multi_statemach(data, dophase_done);
1522 *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1525 DEBUGF(infof(data, "DO phase is complete"));
1530 /***********************************************************************
1534 * This function is registered as 'curl_do' function. It decodes the path
1535 * parts etc as a wrapper to the actual DO function (smtp_perform).
1537 * The input argument is already checked for validity.
1539 static CURLcode smtp_do(struct Curl_easy *data, bool *done)
1541 CURLcode result = CURLE_OK;
1542 *done = FALSE; /* default to false */
1544 /* Parse the custom request */
1545 result = smtp_parse_custom_request(data);
1549 result = smtp_regular_transfer(data, done);
1554 /***********************************************************************
1558 * Disconnect from an SMTP server. Cleanup protocol-specific per-connection
1559 * resources. BLOCKING.
1561 static CURLcode smtp_disconnect(struct Curl_easy *data,
1562 struct connectdata *conn,
1563 bool dead_connection)
1565 struct smtp_conn *smtpc = &conn->proto.smtpc;
1568 /* We cannot send quit unconditionally. If this connection is stale or
1569 bad in any way, sending quit and waiting around here will make the
1570 disconnect wait in vain and cause more problems than we need to. */
1572 if(!dead_connection && conn->bits.protoconnstart) {
1573 if(!smtp_perform_quit(data, conn))
1574 (void)smtp_block_statemach(data, conn, TRUE); /* ignore errors on QUIT */
1577 /* Disconnect from the server */
1578 Curl_pp_disconnect(&smtpc->pp);
1580 /* Cleanup the SASL module */
1581 Curl_sasl_cleanup(conn, smtpc->sasl.authused);
1583 /* Cleanup our connection based variables */
1584 Curl_safefree(smtpc->domain);
1589 /* Call this when the DO phase has completed */
1590 static CURLcode smtp_dophase_done(struct Curl_easy *data, bool connected)
1592 struct SMTP *smtp = data->req.p.smtp;
1596 if(smtp->transfer != PPTRANSFER_BODY)
1597 /* no data to transfer */
1598 Curl_setup_transfer(data, -1, -1, FALSE, -1);
1603 /* Called from multi.c while DOing */
1604 static CURLcode smtp_doing(struct Curl_easy *data, bool *dophase_done)
1606 CURLcode result = smtp_multi_statemach(data, dophase_done);
1609 DEBUGF(infof(data, "DO phase failed"));
1610 else if(*dophase_done) {
1611 result = smtp_dophase_done(data, FALSE /* not connected */);
1613 DEBUGF(infof(data, "DO phase is complete"));
1619 /***********************************************************************
1621 * smtp_regular_transfer()
1623 * The input argument is already checked for validity.
1625 * Performs all commands done before a regular transfer between a local and a
1628 static CURLcode smtp_regular_transfer(struct Curl_easy *data,
1631 CURLcode result = CURLE_OK;
1632 bool connected = FALSE;
1634 /* Make sure size is unknown at this point */
1635 data->req.size = -1;
1637 /* Set the progress data */
1638 Curl_pgrsSetUploadCounter(data, 0);
1639 Curl_pgrsSetDownloadCounter(data, 0);
1640 Curl_pgrsSetUploadSize(data, -1);
1641 Curl_pgrsSetDownloadSize(data, -1);
1643 /* Carry out the perform */
1644 result = smtp_perform(data, &connected, dophase_done);
1646 /* Perform post DO phase operations if necessary */
1647 if(!result && *dophase_done)
1648 result = smtp_dophase_done(data, connected);
1653 static CURLcode smtp_setup_connection(struct Curl_easy *data,
1654 struct connectdata *conn)
1658 /* Clear the TLS upgraded flag */
1659 conn->bits.tls_upgraded = FALSE;
1661 /* Initialise the SMTP layer */
1662 result = smtp_init(data);
1669 /***********************************************************************
1671 * smtp_parse_url_options()
1673 * Parse the URL login options.
1675 static CURLcode smtp_parse_url_options(struct connectdata *conn)
1677 CURLcode result = CURLE_OK;
1678 struct smtp_conn *smtpc = &conn->proto.smtpc;
1679 const char *ptr = conn->options;
1681 while(!result && ptr && *ptr) {
1682 const char *key = ptr;
1685 while(*ptr && *ptr != '=')
1690 while(*ptr && *ptr != ';')
1693 if(strncasecompare(key, "AUTH=", 5))
1694 result = Curl_sasl_parse_url_auth_option(&smtpc->sasl,
1695 value, ptr - value);
1697 result = CURLE_URL_MALFORMAT;
1706 /***********************************************************************
1708 * smtp_parse_url_path()
1710 * Parse the URL path into separate path components.
1712 static CURLcode smtp_parse_url_path(struct Curl_easy *data)
1714 /* The SMTP struct is already initialised in smtp_connect() */
1715 struct connectdata *conn = data->conn;
1716 struct smtp_conn *smtpc = &conn->proto.smtpc;
1717 const char *path = &data->state.up.path[1]; /* skip leading path */
1718 char localhost[HOSTNAME_MAX + 1];
1720 /* Calculate the path if necessary */
1722 if(!Curl_gethostname(localhost, sizeof(localhost)))
1728 /* URL decode the path and use it as the domain in our EHLO */
1729 return Curl_urldecode(path, 0, &smtpc->domain, NULL, REJECT_CTRL);
1732 /***********************************************************************
1734 * smtp_parse_custom_request()
1736 * Parse the custom request.
1738 static CURLcode smtp_parse_custom_request(struct Curl_easy *data)
1740 CURLcode result = CURLE_OK;
1741 struct SMTP *smtp = data->req.p.smtp;
1742 const char *custom = data->set.str[STRING_CUSTOMREQUEST];
1744 /* URL decode the custom request */
1746 result = Curl_urldecode(custom, 0, &smtp->custom, NULL, REJECT_CTRL);
1751 /***********************************************************************
1753 * smtp_parse_address()
1755 * Parse the fully qualified mailbox address into a local address part and the
1756 * host name, converting the host name to an IDN A-label, as per RFC-5890, if
1761 * conn [in] - The connection handle.
1762 * fqma [in] - The fully qualified mailbox address (which may or
1763 * may not contain UTF-8 characters).
1764 * address [in/out] - A new allocated buffer which holds the local
1765 * address part of the mailbox. This buffer must be
1766 * free'ed by the caller.
1767 * host [in/out] - The host name structure that holds the original,
1768 * and optionally encoded, host name.
1769 * Curl_free_idnconverted_hostname() must be called
1770 * once the caller has finished with the structure.
1772 * Returns CURLE_OK on success.
1776 * Should a UTF-8 host name require conversion to IDN ACE and we cannot honor
1777 * that conversion then we shall return success. This allow the caller to send
1778 * the data to the server as a U-label (as per RFC-6531 sect. 3.2).
1780 * If an mailbox '@' separator cannot be located then the mailbox is considered
1781 * to be either a local mailbox or an invalid mailbox (depending on what the
1782 * calling function deems it to be) then the input will simply be returned in
1783 * the address part with the host name being NULL.
1785 static CURLcode smtp_parse_address(struct Curl_easy *data, const char *fqma,
1786 char **address, struct hostname *host)
1788 CURLcode result = CURLE_OK;
1791 /* Duplicate the fully qualified email address so we can manipulate it,
1792 ensuring it doesn't contain the delimiters if specified */
1793 char *dup = strdup(fqma[0] == '<' ? fqma + 1 : fqma);
1795 return CURLE_OUT_OF_MEMORY;
1797 length = strlen(dup);
1799 if(dup[length - 1] == '>')
1800 dup[length - 1] = '\0';
1803 /* Extract the host name from the address (if we can) */
1804 host->name = strpbrk(dup, "@");
1807 host->name = host->name + 1;
1809 /* Attempt to convert the host name to IDN ACE */
1810 (void) Curl_idnconvert_hostname(data, host);
1812 /* If Curl_idnconvert_hostname() fails then we shall attempt to continue
1813 and send the host name using UTF-8 rather than as 7-bit ACE (which is
1817 /* Extract the local address from the mailbox */
1823 CURLcode Curl_smtp_escape_eob(struct Curl_easy *data,
1824 const ssize_t nread,
1825 const ssize_t offset)
1827 /* When sending a SMTP payload we must detect CRLF. sequences making sure
1828 they are sent as CRLF.. instead, as a . on the beginning of a line will
1829 be deleted by the server when not part of an EOB terminator and a
1830 genuine CRLF.CRLF which isn't escaped will wrongly be detected as end of
1835 struct SMTP *smtp = data->req.p.smtp;
1836 char *scratch = data->state.scratch;
1837 char *newscratch = NULL;
1838 char *oldscratch = NULL;
1841 /* Do we need to allocate a scratch buffer? */
1842 if(!scratch || data->set.crlf) {
1843 oldscratch = scratch;
1845 scratch = newscratch = malloc(2 * data->set.upload_buffer_size);
1847 failf(data, "Failed to alloc scratch buffer");
1849 return CURLE_OUT_OF_MEMORY;
1852 DEBUGASSERT((size_t)data->set.upload_buffer_size >= (size_t)nread);
1854 /* Have we already sent part of the EOB? */
1855 eob_sent = smtp->eob;
1857 /* This loop can be improved by some kind of Boyer-Moore style of
1858 approach but that is saved for later... */
1860 memcpy(scratch, data->req.upload_fromhere, offset);
1861 for(i = offset, si = offset; i < nread; i++) {
1862 if(SMTP_EOB[smtp->eob] == data->req.upload_fromhere[i]) {
1865 /* Is the EOB potentially the terminating CRLF? */
1866 if(2 == smtp->eob || SMTP_EOB_LEN == smtp->eob)
1867 smtp->trailing_crlf = TRUE;
1869 smtp->trailing_crlf = FALSE;
1871 else if(smtp->eob) {
1872 /* A previous substring matched so output that first */
1873 memcpy(&scratch[si], &SMTP_EOB[eob_sent], smtp->eob - eob_sent);
1874 si += smtp->eob - eob_sent;
1876 /* Then compare the first byte */
1877 if(SMTP_EOB[0] == data->req.upload_fromhere[i])
1884 /* Reset the trailing CRLF flag as there was more data */
1885 smtp->trailing_crlf = FALSE;
1888 /* Do we have a match for CRLF. as per RFC-5321, sect. 4.5.2 */
1889 if(SMTP_EOB_FIND_LEN == smtp->eob) {
1890 /* Copy the replacement data to the target buffer */
1891 memcpy(&scratch[si], &SMTP_EOB_REPL[eob_sent],
1892 SMTP_EOB_REPL_LEN - eob_sent);
1893 si += SMTP_EOB_REPL_LEN - eob_sent;
1898 scratch[si++] = data->req.upload_fromhere[i];
1901 if(smtp->eob - eob_sent) {
1902 /* A substring matched before processing ended so output that now */
1903 memcpy(&scratch[si], &SMTP_EOB[eob_sent], smtp->eob - eob_sent);
1904 si += smtp->eob - eob_sent;
1907 /* Only use the new buffer if we replaced something */
1909 /* Upload from the new (replaced) buffer instead */
1910 data->req.upload_fromhere = scratch;
1912 /* Save the buffer so it can be freed later */
1913 data->state.scratch = scratch;
1915 /* Free the old scratch buffer */
1918 /* Set the new amount too */
1919 data->req.upload_present = si;
1927 #endif /* CURL_DISABLE_SMTP */