1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2020, 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.haxx.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 * RFC1870 SMTP Service Extension for Message Size
22 * RFC2195 CRAM-MD5 authentication
23 * RFC2831 DIGEST-MD5 authentication
24 * RFC3207 SMTP over TLS
25 * RFC4422 Simple Authentication and Security Layer (SASL)
26 * RFC4616 PLAIN authentication
27 * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism
28 * RFC4954 SMTP Authentication
29 * RFC5321 SMTP protocol
30 * RFC5890 Internationalized Domain Names for Applications (IDNA)
31 * RFC6531 SMTP Extension for Internationalized Email
32 * RFC6532 Internationalized Email Headers
33 * RFC6749 OAuth 2.0 Authorization Framework
34 * RFC8314 Use of TLS for Email Submission and Access
35 * Draft SMTP URL Interface <draft-earhart-url-smtp-00.txt>
36 * Draft LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt>
38 ***************************************************************************/
40 #include "curl_setup.h"
42 #ifndef CURL_DISABLE_SMTP
44 #ifdef HAVE_NETINET_IN_H
45 #include <netinet/in.h>
47 #ifdef HAVE_ARPA_INET_H
48 #include <arpa/inet.h>
51 #include <sys/utsname.h>
61 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
63 #define in_addr_t unsigned long
66 #include <curl/curl.h>
73 #include "http.h" /* for HTTP proxy tunnel stuff */
77 #include "strtoofft.h"
79 #include "vtls/vtls.h"
85 #include "curl_gethostname.h"
86 #include "curl_sasl.h"
88 /* The last 3 #include files should be in this order */
89 #include "curl_printf.h"
90 #include "curl_memory.h"
93 /* Local API functions */
94 static CURLcode smtp_regular_transfer(struct connectdata *conn, bool *done);
95 static CURLcode smtp_do(struct connectdata *conn, bool *done);
96 static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
98 static CURLcode smtp_connect(struct connectdata *conn, bool *done);
99 static CURLcode smtp_disconnect(struct connectdata *conn, bool dead);
100 static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done);
101 static int smtp_getsock(struct connectdata *conn, curl_socket_t *socks);
102 static CURLcode smtp_doing(struct connectdata *conn, bool *dophase_done);
103 static CURLcode smtp_setup_connection(struct connectdata *conn);
104 static CURLcode smtp_parse_url_options(struct connectdata *conn);
105 static CURLcode smtp_parse_url_path(struct connectdata *conn);
106 static CURLcode smtp_parse_custom_request(struct connectdata *conn);
107 static CURLcode smtp_parse_address(struct connectdata *conn, const char *fqma,
108 char **address, struct hostname *host);
109 static CURLcode smtp_perform_auth(struct connectdata *conn, const char *mech,
110 const char *initresp);
111 static CURLcode smtp_continue_auth(struct connectdata *conn, const char *resp);
112 static void smtp_get_message(char *buffer, char **outptr);
115 * SMTP protocol handler.
118 const struct Curl_handler Curl_handler_smtp = {
120 smtp_setup_connection, /* setup_connection */
122 smtp_done, /* done */
123 ZERO_NULL, /* do_more */
124 smtp_connect, /* connect_it */
125 smtp_multi_statemach, /* connecting */
126 smtp_doing, /* doing */
127 smtp_getsock, /* proto_getsock */
128 smtp_getsock, /* doing_getsock */
129 ZERO_NULL, /* domore_getsock */
130 ZERO_NULL, /* perform_getsock */
131 smtp_disconnect, /* disconnect */
132 ZERO_NULL, /* readwrite */
133 ZERO_NULL, /* connection_check */
134 PORT_SMTP, /* defport */
135 CURLPROTO_SMTP, /* protocol */
136 CURLPROTO_SMTP, /* family */
137 PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY | /* flags */
143 * SMTPS protocol handler.
146 const struct Curl_handler Curl_handler_smtps = {
147 "SMTPS", /* scheme */
148 smtp_setup_connection, /* setup_connection */
150 smtp_done, /* done */
151 ZERO_NULL, /* do_more */
152 smtp_connect, /* connect_it */
153 smtp_multi_statemach, /* connecting */
154 smtp_doing, /* doing */
155 smtp_getsock, /* proto_getsock */
156 smtp_getsock, /* doing_getsock */
157 ZERO_NULL, /* domore_getsock */
158 ZERO_NULL, /* perform_getsock */
159 smtp_disconnect, /* disconnect */
160 ZERO_NULL, /* readwrite */
161 ZERO_NULL, /* connection_check */
162 PORT_SMTPS, /* defport */
163 CURLPROTO_SMTPS, /* protocol */
164 CURLPROTO_SMTP, /* family */
165 PROTOPT_CLOSEACTION | PROTOPT_SSL
166 | PROTOPT_NOURLQUERY | PROTOPT_URLOPTIONS /* flags */
170 /* SASL parameters for the smtp protocol */
171 static const struct SASLproto saslsmtp = {
172 "smtp", /* The service name */
173 334, /* Code received when continuation is expected */
174 235, /* Code to receive upon authentication success */
175 512 - 8, /* Maximum initial response length (no max) */
176 smtp_perform_auth, /* Send authentication command */
177 smtp_continue_auth, /* Send authentication continuation */
178 smtp_get_message /* Get SASL response message */
182 static void smtp_to_smtps(struct connectdata *conn)
184 /* Change the connection handler */
185 conn->handler = &Curl_handler_smtps;
187 /* Set the connection's upgraded to TLS flag */
188 conn->bits.tls_upgraded = TRUE;
191 #define smtp_to_smtps(x) Curl_nop_stmt
194 /***********************************************************************
198 * Checks for an ending SMTP status code at the start of the given string, but
199 * also detects various capabilities from the EHLO response including the
200 * supported authentication mechanisms.
202 static bool smtp_endofresp(struct connectdata *conn, char *line, size_t len,
205 struct smtp_conn *smtpc = &conn->proto.smtpc;
209 if(len < 4 || !ISDIGIT(line[0]) || !ISDIGIT(line[1]) || !ISDIGIT(line[2]))
212 /* Do we have a command response? This should be the response code followed
213 by a space and optionally some text as per RFC-5321 and as outlined in
214 Section 4. Examples of RFC-4954 but some e-mail servers ignore this and
215 only send the response code instead as per Section 4.2. */
216 if(line[3] == ' ' || len == 5) {
220 memset(tmpline, '\0', sizeof(tmpline));
221 memcpy(tmpline, line, (len == 5 ? 5 : 3));
222 *resp = curlx_sltosi(strtol(tmpline, NULL, 10));
224 /* Make sure real server never sends internal value */
228 /* Do we have a multiline (continuation) response? */
229 else if(line[3] == '-' &&
230 (smtpc->state == SMTP_EHLO || smtpc->state == SMTP_COMMAND)) {
232 *resp = 1; /* Internal response code */
238 /***********************************************************************
242 * Gets the authentication message from the response buffer.
244 static void smtp_get_message(char *buffer, char **outptr)
246 size_t len = strlen(buffer);
247 char *message = NULL;
250 /* Find the start of the message */
252 for(message = buffer + 4; *message == ' ' || *message == '\t';
256 /* Find the end of the message */
258 if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' &&
259 message[len] != '\t')
262 /* Terminate the message */
268 /* junk input => zero length output */
269 message = &buffer[len];
274 /***********************************************************************
278 * This is the ONLY way to change SMTP state!
280 static void state(struct connectdata *conn, smtpstate newstate)
282 struct smtp_conn *smtpc = &conn->proto.smtpc;
283 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
284 /* for debug purposes */
285 static const char * const names[] = {
302 if(smtpc->state != newstate)
303 infof(conn->data, "SMTP %p state change from %s to %s\n",
304 (void *)smtpc, names[smtpc->state], names[newstate]);
307 smtpc->state = newstate;
310 /***********************************************************************
312 * smtp_perform_ehlo()
314 * Sends the EHLO command to not only initialise communication with the ESMTP
315 * server but to also obtain a list of server side supported capabilities.
317 static CURLcode smtp_perform_ehlo(struct connectdata *conn)
319 CURLcode result = CURLE_OK;
320 struct smtp_conn *smtpc = &conn->proto.smtpc;
322 smtpc->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanism yet */
323 smtpc->sasl.authused = SASL_AUTH_NONE; /* Clear the authentication mechanism
324 used for esmtp connections */
325 smtpc->tls_supported = FALSE; /* Clear the TLS capability */
326 smtpc->auth_supported = FALSE; /* Clear the AUTH capability */
328 /* Send the EHLO command */
329 result = Curl_pp_sendf(&smtpc->pp, "EHLO %s", smtpc->domain);
332 state(conn, SMTP_EHLO);
337 /***********************************************************************
339 * smtp_perform_helo()
341 * Sends the HELO command to initialise communication with the SMTP server.
343 static CURLcode smtp_perform_helo(struct connectdata *conn)
345 CURLcode result = CURLE_OK;
346 struct smtp_conn *smtpc = &conn->proto.smtpc;
348 smtpc->sasl.authused = SASL_AUTH_NONE; /* No authentication mechanism used
349 in smtp connections */
351 /* Send the HELO command */
352 result = Curl_pp_sendf(&smtpc->pp, "HELO %s", smtpc->domain);
355 state(conn, SMTP_HELO);
360 /***********************************************************************
362 * smtp_perform_starttls()
364 * Sends the STLS command to start the upgrade to TLS.
366 static CURLcode smtp_perform_starttls(struct connectdata *conn)
368 /* Send the STARTTLS command */
369 CURLcode result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "STARTTLS");
372 state(conn, SMTP_STARTTLS);
377 /***********************************************************************
379 * smtp_perform_upgrade_tls()
381 * Performs the upgrade to TLS.
383 static CURLcode smtp_perform_upgrade_tls(struct connectdata *conn)
385 /* Start the SSL connection */
386 struct smtp_conn *smtpc = &conn->proto.smtpc;
387 CURLcode result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET,
391 if(smtpc->state != SMTP_UPGRADETLS)
392 state(conn, SMTP_UPGRADETLS);
396 result = smtp_perform_ehlo(conn);
403 /***********************************************************************
405 * smtp_perform_auth()
407 * Sends an AUTH command allowing the client to login with the given SASL
408 * authentication mechanism.
410 static CURLcode smtp_perform_auth(struct connectdata *conn,
412 const char *initresp)
414 CURLcode result = CURLE_OK;
415 struct smtp_conn *smtpc = &conn->proto.smtpc;
417 if(initresp) { /* AUTH <mech> ...<crlf> */
418 /* Send the AUTH command with the initial response */
419 result = Curl_pp_sendf(&smtpc->pp, "AUTH %s %s", mech, initresp);
422 /* Send the AUTH command */
423 result = Curl_pp_sendf(&smtpc->pp, "AUTH %s", mech);
429 /***********************************************************************
431 * smtp_continue_auth()
433 * Sends SASL continuation data or cancellation.
435 static CURLcode smtp_continue_auth(struct connectdata *conn, const char *resp)
437 struct smtp_conn *smtpc = &conn->proto.smtpc;
439 return Curl_pp_sendf(&smtpc->pp, "%s", resp);
442 /***********************************************************************
444 * smtp_perform_authentication()
446 * Initiates the authentication sequence, with the appropriate SASL
447 * authentication mechanism.
449 static CURLcode smtp_perform_authentication(struct connectdata *conn)
451 CURLcode result = CURLE_OK;
452 struct smtp_conn *smtpc = &conn->proto.smtpc;
453 saslprogress progress;
455 /* Check we have enough data to authenticate with, and the
456 server supports authentiation, and end the connect phase if not */
457 if(!smtpc->auth_supported ||
458 !Curl_sasl_can_authenticate(&smtpc->sasl, conn)) {
459 state(conn, SMTP_STOP);
463 /* Calculate the SASL login details */
464 result = Curl_sasl_start(&smtpc->sasl, conn, FALSE, &progress);
467 if(progress == SASL_INPROGRESS)
468 state(conn, SMTP_AUTH);
470 /* Other mechanisms not supported */
471 infof(conn->data, "No known authentication mechanisms supported!\n");
472 result = CURLE_LOGIN_DENIED;
479 /***********************************************************************
481 * smtp_perform_command()
483 * Sends a SMTP based command.
485 static CURLcode smtp_perform_command(struct connectdata *conn)
487 CURLcode result = CURLE_OK;
488 struct Curl_easy *data = conn->data;
489 struct SMTP *smtp = data->req.protop;
492 /* We notify the server we are sending UTF-8 data if a) it supports the
493 SMTPUTF8 extension and b) The mailbox contains UTF-8 charaacters, in
494 either the local address or host name parts. This is regardless of
495 whether the host name is encoded using IDN ACE */
498 if((!smtp->custom) || (!smtp->custom[0])) {
499 char *address = NULL;
500 struct hostname host = { NULL, NULL, NULL, NULL };
502 /* Parse the mailbox to verify into the local address and host name
503 parts, converting the host name to an IDN A-label if necessary */
504 result = smtp_parse_address(conn, smtp->rcpt->data,
509 /* Establish whether we should report SMTPUTF8 to the server for this
510 mailbox as per RFC-6531 sect. 3.1 point 6 */
511 utf8 = (conn->proto.smtpc.utf8_supported) &&
512 ((host.encalloc) || (!Curl_is_ASCII_name(address)) ||
513 (!Curl_is_ASCII_name(host.name)));
515 /* Send the VRFY command (Note: The host name part may be absent when the
516 host is a local system) */
517 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "VRFY %s%s%s%s",
519 host.name ? "@" : "",
520 host.name ? host.name : "",
521 utf8 ? " SMTPUTF8" : "");
523 Curl_free_idnconverted_hostname(&host);
527 /* Establish whether we should report that we support SMTPUTF8 for EXPN
528 commands to the server as per RFC-6531 sect. 3.1 point 6 */
529 utf8 = (conn->proto.smtpc.utf8_supported) &&
530 (!strcmp(smtp->custom, "EXPN"));
532 /* Send the custom recipient based command such as the EXPN command */
533 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s %s%s", smtp->custom,
535 utf8 ? " SMTPUTF8" : "");
539 /* Send the non-recipient based command such as HELP */
540 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s",
541 smtp->custom && smtp->custom[0] != '\0' ?
542 smtp->custom : "HELP");
545 state(conn, SMTP_COMMAND);
550 /***********************************************************************
552 * smtp_perform_mail()
554 * Sends an MAIL command to initiate the upload of a message.
556 static CURLcode smtp_perform_mail(struct connectdata *conn)
561 CURLcode result = CURLE_OK;
562 struct Curl_easy *data = conn->data;
564 /* We notify the server we are sending UTF-8 data if a) it supports the
565 SMTPUTF8 extension and b) The mailbox contains UTF-8 charaacters, in
566 either the local address or host name parts. This is regardless of
567 whether the host name is encoded using IDN ACE */
570 /* Calculate the FROM parameter */
571 if(data->set.str[STRING_MAIL_FROM]) {
572 char *address = NULL;
573 struct hostname host = { NULL, NULL, NULL, NULL };
575 /* Parse the FROM mailbox into the local address and host name parts,
576 converting the host name to an IDN A-label if necessary */
577 result = smtp_parse_address(conn, data->set.str[STRING_MAIL_FROM],
582 /* Establish whether we should report SMTPUTF8 to the server for this
583 mailbox as per RFC-6531 sect. 3.1 point 4 and sect. 3.4 */
584 utf8 = (conn->proto.smtpc.utf8_supported) &&
585 ((host.encalloc) || (!Curl_is_ASCII_name(address)) ||
586 (!Curl_is_ASCII_name(host.name)));
589 from = aprintf("<%s@%s>", address, host.name);
591 Curl_free_idnconverted_hostname(&host);
594 /* An invalid mailbox was provided but we'll simply let the server worry
595 about that and reply with a 501 error */
596 from = aprintf("<%s>", address);
601 /* Null reverse-path, RFC-5321, sect. 3.6.3 */
605 return CURLE_OUT_OF_MEMORY;
607 /* Calculate the optional AUTH parameter */
608 if(data->set.str[STRING_MAIL_AUTH] && conn->proto.smtpc.sasl.authused) {
609 if(data->set.str[STRING_MAIL_AUTH][0] != '\0') {
610 char *address = NULL;
611 struct hostname host = { NULL, NULL, NULL, NULL };
613 /* Parse the AUTH mailbox into the local address and host name parts,
614 converting the host name to an IDN A-label if necessary */
615 result = smtp_parse_address(conn, data->set.str[STRING_MAIL_AUTH],
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 if((!utf8) && (conn->proto.smtpc.utf8_supported) &&
625 ((host.encalloc) || (!Curl_is_ASCII_name(address)) ||
626 (!Curl_is_ASCII_name(host.name))))
630 auth = aprintf("<%s@%s>", address, host.name);
632 Curl_free_idnconverted_hostname(&host);
635 /* An invalid mailbox was provided but we'll simply let the server
637 auth = aprintf("<%s>", address);
642 /* Empty AUTH, RFC-2554, sect. 5 */
648 return CURLE_OUT_OF_MEMORY;
652 /* Prepare the mime data if some. */
653 if(data->set.mimepost.kind != MIMEKIND_NONE) {
654 /* Use the whole structure as data. */
655 data->set.mimepost.flags &= ~MIME_BODY_ONLY;
657 /* Add external headers and mime version. */
658 curl_mime_headers(&data->set.mimepost, data->set.headers, 0);
659 result = Curl_mime_prepare_headers(&data->set.mimepost, NULL,
660 NULL, MIMESTRATEGY_MAIL);
663 if(!Curl_checkheaders(conn, "Mime-Version"))
664 result = Curl_mime_add_header(&data->set.mimepost.curlheaders,
665 "Mime-Version: 1.0");
667 /* Make sure we will read the entire mime structure. */
669 result = Curl_mime_rewind(&data->set.mimepost);
678 data->state.infilesize = Curl_mime_size(&data->set.mimepost);
680 /* Read from mime structure. */
681 data->state.fread_func = (curl_read_callback) Curl_mime_read;
682 data->state.in = (void *) &data->set.mimepost;
685 /* Calculate the optional SIZE parameter */
686 if(conn->proto.smtpc.size_supported && data->state.infilesize > 0) {
687 size = aprintf("%" CURL_FORMAT_CURL_OFF_T, data->state.infilesize);
693 return CURLE_OUT_OF_MEMORY;
697 /* If the mailboxes in the FROM and AUTH parameters don't include a UTF-8
698 based address then quickly scan through the recipient list and check if
699 any there do, as we need to correctly identify our support for SMTPUTF8
700 in the envelope, as per RFC-6531 sect. 3.4 */
701 if(conn->proto.smtpc.utf8_supported && !utf8) {
702 struct SMTP *smtp = data->req.protop;
703 struct curl_slist *rcpt = smtp->rcpt;
705 while(rcpt && !utf8) {
706 /* Does the host name contain non-ASCII characters? */
707 if(!Curl_is_ASCII_name(rcpt->data))
714 /* Send the MAIL command */
715 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
716 "MAIL FROM:%s%s%s%s%s%s",
717 from, /* Mandatory */
718 auth ? " AUTH=" : "", /* Optional on AUTH support */
719 auth ? auth : "", /* */
720 size ? " SIZE=" : "", /* Optional on SIZE support */
721 size ? size : "", /* */
722 utf8 ? " SMTPUTF8" /* Internationalised mailbox */
723 : ""); /* included in our envelope */
730 state(conn, SMTP_MAIL);
735 /***********************************************************************
737 * smtp_perform_rcpt_to()
739 * Sends a RCPT TO command for a given recipient as part of the message upload
742 static CURLcode smtp_perform_rcpt_to(struct connectdata *conn)
744 CURLcode result = CURLE_OK;
745 struct Curl_easy *data = conn->data;
746 struct SMTP *smtp = data->req.protop;
747 char *address = NULL;
748 struct hostname host = { NULL, NULL, NULL, NULL };
750 /* Parse the recipient mailbox into the local address and host name parts,
751 converting the host name to an IDN A-label if necessary */
752 result = smtp_parse_address(conn, smtp->rcpt->data,
757 /* Send the RCPT TO command */
759 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:<%s@%s>", address,
762 /* An invalid mailbox was provided but we'll simply let the server worry
763 about that and reply with a 501 error */
764 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:<%s>", address);
766 Curl_free_idnconverted_hostname(&host);
770 state(conn, SMTP_RCPT);
775 /***********************************************************************
777 * smtp_perform_quit()
779 * Performs the quit action prior to sclose() being called.
781 static CURLcode smtp_perform_quit(struct connectdata *conn)
783 /* Send the QUIT command */
784 CURLcode result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "QUIT");
787 state(conn, SMTP_QUIT);
792 /* For the initial server greeting */
793 static CURLcode smtp_state_servergreet_resp(struct connectdata *conn,
797 CURLcode result = CURLE_OK;
798 struct Curl_easy *data = conn->data;
800 (void)instate; /* no use for this yet */
802 if(smtpcode/100 != 2) {
803 failf(data, "Got unexpected smtp-server response: %d", smtpcode);
804 result = CURLE_WEIRD_SERVER_REPLY;
807 result = smtp_perform_ehlo(conn);
812 /* For STARTTLS responses */
813 static CURLcode smtp_state_starttls_resp(struct connectdata *conn,
817 CURLcode result = CURLE_OK;
818 struct Curl_easy *data = conn->data;
820 (void)instate; /* no use for this yet */
822 if(smtpcode != 220) {
823 if(data->set.use_ssl != CURLUSESSL_TRY) {
824 failf(data, "STARTTLS denied, code %d", smtpcode);
825 result = CURLE_USE_SSL_FAILED;
828 result = smtp_perform_authentication(conn);
831 result = smtp_perform_upgrade_tls(conn);
836 /* For EHLO responses */
837 static CURLcode smtp_state_ehlo_resp(struct connectdata *conn, int smtpcode,
840 CURLcode result = CURLE_OK;
841 struct Curl_easy *data = conn->data;
842 struct smtp_conn *smtpc = &conn->proto.smtpc;
843 const char *line = data->state.buffer;
844 size_t len = strlen(line);
846 (void)instate; /* no use for this yet */
848 if(smtpcode/100 != 2 && smtpcode != 1) {
849 if(data->set.use_ssl <= CURLUSESSL_TRY || conn->ssl[FIRSTSOCKET].use)
850 result = smtp_perform_helo(conn);
852 failf(data, "Remote access denied: %d", smtpcode);
853 result = CURLE_REMOTE_ACCESS_DENIED;
860 /* Does the server support the STARTTLS capability? */
861 if(len >= 8 && !memcmp(line, "STARTTLS", 8))
862 smtpc->tls_supported = TRUE;
864 /* Does the server support the SIZE capability? */
865 else if(len >= 4 && !memcmp(line, "SIZE", 4))
866 smtpc->size_supported = TRUE;
868 /* Does the server support the UTF-8 capability? */
869 else if(len >= 8 && !memcmp(line, "SMTPUTF8", 8))
870 smtpc->utf8_supported = TRUE;
872 /* Does the server support authentication? */
873 else if(len >= 5 && !memcmp(line, "AUTH ", 5)) {
874 smtpc->auth_supported = TRUE;
876 /* Advance past the AUTH keyword */
880 /* Loop through the data line */
884 unsigned int mechbit;
887 (*line == ' ' || *line == '\t' ||
888 *line == '\r' || *line == '\n')) {
897 /* Extract the word */
898 for(wordlen = 0; wordlen < len && line[wordlen] != ' ' &&
899 line[wordlen] != '\t' && line[wordlen] != '\r' &&
900 line[wordlen] != '\n';)
903 /* Test the word for a matching authentication mechanism */
904 mechbit = Curl_sasl_decode_mech(line, wordlen, &llen);
905 if(mechbit && llen == wordlen)
906 smtpc->sasl.authmechs |= mechbit;
914 if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
915 /* We don't have a SSL/TLS connection yet, but SSL is requested */
916 if(smtpc->tls_supported)
917 /* Switch to TLS connection now */
918 result = smtp_perform_starttls(conn);
919 else if(data->set.use_ssl == CURLUSESSL_TRY)
920 /* Fallback and carry on with authentication */
921 result = smtp_perform_authentication(conn);
923 failf(data, "STARTTLS not supported.");
924 result = CURLE_USE_SSL_FAILED;
928 result = smtp_perform_authentication(conn);
932 failf(data, "Unexpectedly short EHLO response");
933 result = CURLE_WEIRD_SERVER_REPLY;
939 /* For HELO responses */
940 static CURLcode smtp_state_helo_resp(struct connectdata *conn, int smtpcode,
943 CURLcode result = CURLE_OK;
944 struct Curl_easy *data = conn->data;
946 (void)instate; /* no use for this yet */
948 if(smtpcode/100 != 2) {
949 failf(data, "Remote access denied: %d", smtpcode);
950 result = CURLE_REMOTE_ACCESS_DENIED;
953 /* End of connect phase */
954 state(conn, SMTP_STOP);
959 /* For SASL authentication responses */
960 static CURLcode smtp_state_auth_resp(struct connectdata *conn,
964 CURLcode result = CURLE_OK;
965 struct Curl_easy *data = conn->data;
966 struct smtp_conn *smtpc = &conn->proto.smtpc;
967 saslprogress progress;
969 (void)instate; /* no use for this yet */
971 result = Curl_sasl_continue(&smtpc->sasl, conn, smtpcode, &progress);
975 state(conn, SMTP_STOP); /* Authenticated */
977 case SASL_IDLE: /* No mechanism left after cancellation */
978 failf(data, "Authentication cancelled");
979 result = CURLE_LOGIN_DENIED;
988 /* For command responses */
989 static CURLcode smtp_state_command_resp(struct connectdata *conn, int smtpcode,
992 CURLcode result = CURLE_OK;
993 struct Curl_easy *data = conn->data;
994 struct SMTP *smtp = data->req.protop;
995 char *line = data->state.buffer;
996 size_t len = strlen(line);
998 (void)instate; /* no use for this yet */
1000 if((smtp->rcpt && smtpcode/100 != 2 && smtpcode != 553 && smtpcode != 1) ||
1001 (!smtp->rcpt && smtpcode/100 != 2 && smtpcode != 1)) {
1002 failf(data, "Command failed: %d", smtpcode);
1003 result = CURLE_RECV_ERROR;
1006 /* Temporarily add the LF character back and send as body to the client */
1007 if(!data->set.opt_no_body) {
1009 result = Curl_client_write(conn, CLIENTWRITE_BODY, line, len + 1);
1015 smtp->rcpt = smtp->rcpt->next;
1018 /* Send the next command */
1019 result = smtp_perform_command(conn);
1022 /* End of DO phase */
1023 state(conn, SMTP_STOP);
1026 /* End of DO phase */
1027 state(conn, SMTP_STOP);
1034 /* For MAIL responses */
1035 static CURLcode smtp_state_mail_resp(struct connectdata *conn, int smtpcode,
1038 CURLcode result = CURLE_OK;
1039 struct Curl_easy *data = conn->data;
1041 (void)instate; /* no use for this yet */
1043 if(smtpcode/100 != 2) {
1044 failf(data, "MAIL failed: %d", smtpcode);
1045 result = CURLE_SEND_ERROR;
1048 /* Start the RCPT TO command */
1049 result = smtp_perform_rcpt_to(conn);
1054 /* For RCPT responses */
1055 static CURLcode smtp_state_rcpt_resp(struct connectdata *conn, int smtpcode,
1058 CURLcode result = CURLE_OK;
1059 struct Curl_easy *data = conn->data;
1060 struct SMTP *smtp = data->req.protop;
1061 bool is_smtp_err = FALSE;
1062 bool is_smtp_blocking_err = FALSE;
1064 (void)instate; /* no use for this yet */
1066 is_smtp_err = (smtpcode/100 != 2) ? TRUE : FALSE;
1068 /* If there's multiple RCPT TO to be issued, it's possible to ignore errors
1069 and proceed with only the valid addresses. */
1070 is_smtp_blocking_err =
1071 (is_smtp_err && !data->set.mail_rcpt_allowfails) ? TRUE : FALSE;
1074 /* Remembering the last failure which we can report if all "RCPT TO" have
1075 failed and we cannot proceed. */
1076 smtp->rcpt_last_error = smtpcode;
1078 if(is_smtp_blocking_err) {
1079 failf(data, "RCPT failed: %d", smtpcode);
1080 result = CURLE_SEND_ERROR;
1084 /* Some RCPT TO commands have succeeded. */
1085 smtp->rcpt_had_ok = TRUE;
1088 if(!is_smtp_blocking_err) {
1089 smtp->rcpt = smtp->rcpt->next;
1092 /* Send the next RCPT TO command */
1093 result = smtp_perform_rcpt_to(conn);
1095 /* We weren't able to issue a successful RCPT TO command while going
1096 over recipients (potentially multiple). Sending back last error. */
1097 if(!smtp->rcpt_had_ok) {
1098 failf(data, "RCPT failed: %d (last error)", smtp->rcpt_last_error);
1099 result = CURLE_SEND_ERROR;
1102 /* Send the DATA command */
1103 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "DATA");
1106 state(conn, SMTP_DATA);
1114 /* For DATA response */
1115 static CURLcode smtp_state_data_resp(struct connectdata *conn, int smtpcode,
1118 CURLcode result = CURLE_OK;
1119 struct Curl_easy *data = conn->data;
1121 (void)instate; /* no use for this yet */
1123 if(smtpcode != 354) {
1124 failf(data, "DATA failed: %d", smtpcode);
1125 result = CURLE_SEND_ERROR;
1128 /* Set the progress upload size */
1129 Curl_pgrsSetUploadSize(data, data->state.infilesize);
1132 Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET);
1134 /* End of DO phase */
1135 state(conn, SMTP_STOP);
1141 /* For POSTDATA responses, which are received after the entire DATA
1142 part has been sent to the server */
1143 static CURLcode smtp_state_postdata_resp(struct connectdata *conn,
1147 CURLcode result = CURLE_OK;
1149 (void)instate; /* no use for this yet */
1152 result = CURLE_RECV_ERROR;
1154 /* End of DONE phase */
1155 state(conn, SMTP_STOP);
1160 static CURLcode smtp_statemach_act(struct connectdata *conn)
1162 CURLcode result = CURLE_OK;
1163 curl_socket_t sock = conn->sock[FIRSTSOCKET];
1164 struct Curl_easy *data = conn->data;
1166 struct smtp_conn *smtpc = &conn->proto.smtpc;
1167 struct pingpong *pp = &smtpc->pp;
1170 /* Busy upgrading the connection; right now all I/O is SSL/TLS, not SMTP */
1171 if(smtpc->state == SMTP_UPGRADETLS)
1172 return smtp_perform_upgrade_tls(conn);
1174 /* Flush any data that needs to be sent */
1176 return Curl_pp_flushsend(pp);
1179 /* Read the response from the server */
1180 result = Curl_pp_readresp(sock, pp, &smtpcode, &nread);
1184 /* Store the latest response for later retrieval if necessary */
1185 if(smtpc->state != SMTP_QUIT && smtpcode != 1)
1186 data->info.httpcode = smtpcode;
1191 /* We have now received a full SMTP server response */
1192 switch(smtpc->state) {
1193 case SMTP_SERVERGREET:
1194 result = smtp_state_servergreet_resp(conn, smtpcode, smtpc->state);
1198 result = smtp_state_ehlo_resp(conn, smtpcode, smtpc->state);
1202 result = smtp_state_helo_resp(conn, smtpcode, smtpc->state);
1206 result = smtp_state_starttls_resp(conn, smtpcode, smtpc->state);
1210 result = smtp_state_auth_resp(conn, smtpcode, smtpc->state);
1214 result = smtp_state_command_resp(conn, smtpcode, smtpc->state);
1218 result = smtp_state_mail_resp(conn, smtpcode, smtpc->state);
1222 result = smtp_state_rcpt_resp(conn, smtpcode, smtpc->state);
1226 result = smtp_state_data_resp(conn, smtpcode, smtpc->state);
1230 result = smtp_state_postdata_resp(conn, smtpcode, smtpc->state);
1234 /* fallthrough, just stop! */
1236 /* internal error */
1237 state(conn, SMTP_STOP);
1240 } while(!result && smtpc->state != SMTP_STOP && Curl_pp_moredata(pp));
1245 /* Called repeatedly until done from multi.c */
1246 static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done)
1248 CURLcode result = CURLE_OK;
1249 struct smtp_conn *smtpc = &conn->proto.smtpc;
1251 if((conn->handler->flags & PROTOPT_SSL) && !smtpc->ssldone) {
1252 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone);
1253 if(result || !smtpc->ssldone)
1257 result = Curl_pp_statemach(&smtpc->pp, FALSE, FALSE);
1258 *done = (smtpc->state == SMTP_STOP) ? TRUE : FALSE;
1263 static CURLcode smtp_block_statemach(struct connectdata *conn,
1266 CURLcode result = CURLE_OK;
1267 struct smtp_conn *smtpc = &conn->proto.smtpc;
1269 while(smtpc->state != SMTP_STOP && !result)
1270 result = Curl_pp_statemach(&smtpc->pp, TRUE, disconnecting);
1275 /* Allocate and initialize the SMTP struct for the current Curl_easy if
1277 static CURLcode smtp_init(struct connectdata *conn)
1279 CURLcode result = CURLE_OK;
1280 struct Curl_easy *data = conn->data;
1283 smtp = data->req.protop = calloc(sizeof(struct SMTP), 1);
1285 result = CURLE_OUT_OF_MEMORY;
1290 /* For the SMTP "protocol connect" and "doing" phases only */
1291 static int smtp_getsock(struct connectdata *conn, curl_socket_t *socks)
1293 return Curl_pp_getsock(&conn->proto.smtpc.pp, socks);
1296 /***********************************************************************
1300 * This function should do everything that is to be considered a part of
1301 * the connection phase.
1303 * The variable pointed to by 'done' will be TRUE if the protocol-layer
1304 * connect phase is done when this function returns, or FALSE if not.
1306 static CURLcode smtp_connect(struct connectdata *conn, bool *done)
1308 CURLcode result = CURLE_OK;
1309 struct smtp_conn *smtpc = &conn->proto.smtpc;
1310 struct pingpong *pp = &smtpc->pp;
1312 *done = FALSE; /* default to not done yet */
1314 /* We always support persistent connections in SMTP */
1315 connkeep(conn, "SMTP default");
1317 /* Set the default response time-out */
1318 pp->response_time = RESP_TIMEOUT;
1319 pp->statemach_act = smtp_statemach_act;
1320 pp->endofresp = smtp_endofresp;
1323 /* Initialize the SASL storage */
1324 Curl_sasl_init(&smtpc->sasl, &saslsmtp);
1326 /* Initialise the pingpong layer */
1330 /* Parse the URL options */
1331 result = smtp_parse_url_options(conn);
1335 /* Parse the URL path */
1336 result = smtp_parse_url_path(conn);
1340 /* Start off waiting for the server greeting response */
1341 state(conn, SMTP_SERVERGREET);
1343 result = smtp_multi_statemach(conn, done);
1348 /***********************************************************************
1352 * The DONE function. This does what needs to be done after a single DO has
1355 * Input argument is already checked for validity.
1357 static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
1360 CURLcode result = CURLE_OK;
1361 struct Curl_easy *data = conn->data;
1362 struct SMTP *smtp = data->req.protop;
1363 struct pingpong *pp = &conn->proto.smtpc.pp;
1366 ssize_t bytes_written;
1370 if(!smtp || !pp->conn)
1373 /* Cleanup our per-request based variables */
1374 Curl_safefree(smtp->custom);
1377 connclose(conn, "SMTP done with bad status"); /* marked for closure */
1378 result = status; /* use the already set error code */
1380 else if(!data->set.connect_only && data->set.mail_rcpt &&
1381 (data->set.upload || data->set.mimepost.kind)) {
1382 /* Calculate the EOB taking into account any terminating CRLF from the
1383 previous line of the email or the CRLF of the DATA command when there
1384 is "no mail data". RFC-5321, sect. 4.1.1.4.
1386 Note: As some SSL backends, such as OpenSSL, will cause Curl_write() to
1387 fail when using a different pointer following a previous write, that
1388 returned CURLE_AGAIN, we duplicate the EOB now rather than when the
1389 bytes written doesn't equal len. */
1390 if(smtp->trailing_crlf || !conn->data->state.infilesize) {
1391 eob = strdup(&SMTP_EOB[2]);
1392 len = SMTP_EOB_LEN - 2;
1395 eob = strdup(SMTP_EOB);
1400 return CURLE_OUT_OF_MEMORY;
1402 /* Send the end of block data */
1403 result = Curl_write(conn, conn->writesockfd, eob, len, &bytes_written);
1409 if(bytes_written != len) {
1410 /* The whole chunk was not sent so keep it around and adjust the
1411 pingpong structure accordingly */
1414 pp->sendleft = len - bytes_written;
1417 /* Successfully sent so adjust the response timeout relative to now */
1418 pp->response = Curl_now();
1423 state(conn, SMTP_POSTDATA);
1425 /* Run the state-machine */
1426 result = smtp_block_statemach(conn, FALSE);
1429 /* Clear the transfer mode for the next request */
1430 smtp->transfer = FTPTRANSFER_BODY;
1435 /***********************************************************************
1439 * This is the actual DO function for SMTP. Transfer a mail, send a command
1440 * or get some data according to the options previously setup.
1442 static CURLcode smtp_perform(struct connectdata *conn, bool *connected,
1445 /* This is SMTP and no proxy */
1446 CURLcode result = CURLE_OK;
1447 struct Curl_easy *data = conn->data;
1448 struct SMTP *smtp = data->req.protop;
1450 DEBUGF(infof(conn->data, "DO phase starts\n"));
1452 if(data->set.opt_no_body) {
1453 /* Requested no body means no transfer */
1454 smtp->transfer = FTPTRANSFER_INFO;
1457 *dophase_done = FALSE; /* not done yet */
1459 /* Store the first recipient (or NULL if not specified) */
1460 smtp->rcpt = data->set.mail_rcpt;
1462 /* Track of whether we've successfully sent at least one RCPT TO command */
1463 smtp->rcpt_had_ok = FALSE;
1465 /* Track of the last error we've received by sending RCPT TO command */
1466 smtp->rcpt_last_error = 0;
1468 /* Initial data character is the first character in line: it is implicitly
1469 preceded by a virtual CRLF. */
1470 smtp->trailing_crlf = TRUE;
1473 /* Start the first command in the DO phase */
1474 if((data->set.upload || data->set.mimepost.kind) && data->set.mail_rcpt)
1476 result = smtp_perform_mail(conn);
1478 /* SMTP based command (VRFY, EXPN, NOOP, RSET or HELP) */
1479 result = smtp_perform_command(conn);
1484 /* Run the state-machine */
1485 result = smtp_multi_statemach(conn, dophase_done);
1487 *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1490 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1495 /***********************************************************************
1499 * This function is registered as 'curl_do' function. It decodes the path
1500 * parts etc as a wrapper to the actual DO function (smtp_perform).
1502 * The input argument is already checked for validity.
1504 static CURLcode smtp_do(struct connectdata *conn, bool *done)
1506 CURLcode result = CURLE_OK;
1508 *done = FALSE; /* default to false */
1510 /* Parse the custom request */
1511 result = smtp_parse_custom_request(conn);
1515 result = smtp_regular_transfer(conn, done);
1520 /***********************************************************************
1524 * Disconnect from an SMTP server. Cleanup protocol-specific per-connection
1525 * resources. BLOCKING.
1527 static CURLcode smtp_disconnect(struct connectdata *conn, bool dead_connection)
1529 struct smtp_conn *smtpc = &conn->proto.smtpc;
1531 /* We cannot send quit unconditionally. If this connection is stale or
1532 bad in any way, sending quit and waiting around here will make the
1533 disconnect wait in vain and cause more problems than we need to. */
1535 /* The SMTP session may or may not have been allocated/setup at this
1537 if(!dead_connection && smtpc->pp.conn && smtpc->pp.conn->bits.protoconnstart)
1538 if(!smtp_perform_quit(conn))
1539 (void)smtp_block_statemach(conn, TRUE); /* ignore errors on QUIT */
1541 /* Disconnect from the server */
1542 Curl_pp_disconnect(&smtpc->pp);
1544 /* Cleanup the SASL module */
1545 Curl_sasl_cleanup(conn, smtpc->sasl.authused);
1547 /* Cleanup our connection based variables */
1548 Curl_safefree(smtpc->domain);
1553 /* Call this when the DO phase has completed */
1554 static CURLcode smtp_dophase_done(struct connectdata *conn, bool connected)
1556 struct SMTP *smtp = conn->data->req.protop;
1560 if(smtp->transfer != FTPTRANSFER_BODY)
1561 /* no data to transfer */
1562 Curl_setup_transfer(conn->data, -1, -1, FALSE, -1);
1567 /* Called from multi.c while DOing */
1568 static CURLcode smtp_doing(struct connectdata *conn, bool *dophase_done)
1570 CURLcode result = smtp_multi_statemach(conn, dophase_done);
1573 DEBUGF(infof(conn->data, "DO phase failed\n"));
1574 else if(*dophase_done) {
1575 result = smtp_dophase_done(conn, FALSE /* not connected */);
1577 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1583 /***********************************************************************
1585 * smtp_regular_transfer()
1587 * The input argument is already checked for validity.
1589 * Performs all commands done before a regular transfer between a local and a
1592 static CURLcode smtp_regular_transfer(struct connectdata *conn,
1595 CURLcode result = CURLE_OK;
1596 bool connected = FALSE;
1597 struct Curl_easy *data = conn->data;
1599 /* Make sure size is unknown at this point */
1600 data->req.size = -1;
1602 /* Set the progress data */
1603 Curl_pgrsSetUploadCounter(data, 0);
1604 Curl_pgrsSetDownloadCounter(data, 0);
1605 Curl_pgrsSetUploadSize(data, -1);
1606 Curl_pgrsSetDownloadSize(data, -1);
1608 /* Carry out the perform */
1609 result = smtp_perform(conn, &connected, dophase_done);
1611 /* Perform post DO phase operations if necessary */
1612 if(!result && *dophase_done)
1613 result = smtp_dophase_done(conn, connected);
1618 static CURLcode smtp_setup_connection(struct connectdata *conn)
1622 /* Clear the TLS upgraded flag */
1623 conn->bits.tls_upgraded = FALSE;
1625 /* Initialise the SMTP layer */
1626 result = smtp_init(conn);
1633 /***********************************************************************
1635 * smtp_parse_url_options()
1637 * Parse the URL login options.
1639 static CURLcode smtp_parse_url_options(struct connectdata *conn)
1641 CURLcode result = CURLE_OK;
1642 struct smtp_conn *smtpc = &conn->proto.smtpc;
1643 const char *ptr = conn->options;
1645 smtpc->sasl.resetprefs = TRUE;
1647 while(!result && ptr && *ptr) {
1648 const char *key = ptr;
1651 while(*ptr && *ptr != '=')
1656 while(*ptr && *ptr != ';')
1659 if(strncasecompare(key, "AUTH=", 5))
1660 result = Curl_sasl_parse_url_auth_option(&smtpc->sasl,
1661 value, ptr - value);
1663 result = CURLE_URL_MALFORMAT;
1672 /***********************************************************************
1674 * smtp_parse_url_path()
1676 * Parse the URL path into separate path components.
1678 static CURLcode smtp_parse_url_path(struct connectdata *conn)
1680 /* The SMTP struct is already initialised in smtp_connect() */
1681 struct Curl_easy *data = conn->data;
1682 struct smtp_conn *smtpc = &conn->proto.smtpc;
1683 const char *path = &data->state.up.path[1]; /* skip leading path */
1684 char localhost[HOSTNAME_MAX + 1];
1686 /* Calculate the path if necessary */
1688 if(!Curl_gethostname(localhost, sizeof(localhost)))
1694 /* URL decode the path and use it as the domain in our EHLO */
1695 return Curl_urldecode(conn->data, path, 0, &smtpc->domain, NULL,
1699 /***********************************************************************
1701 * smtp_parse_custom_request()
1703 * Parse the custom request.
1705 static CURLcode smtp_parse_custom_request(struct connectdata *conn)
1707 CURLcode result = CURLE_OK;
1708 struct Curl_easy *data = conn->data;
1709 struct SMTP *smtp = data->req.protop;
1710 const char *custom = data->set.str[STRING_CUSTOMREQUEST];
1712 /* URL decode the custom request */
1714 result = Curl_urldecode(data, custom, 0, &smtp->custom, NULL, REJECT_CTRL);
1719 /***********************************************************************
1721 * smtp_parse_address()
1723 * Parse the fully qualified mailbox address into a local address part and the
1724 * host name, converting the host name to an IDN A-label, as per RFC-5890, if
1729 * conn [in] - The connection handle.
1730 * fqma [in] - The fully qualified mailbox address (which may or
1731 * may not contain UTF-8 characters).
1732 * address [in/out] - A new allocated buffer which holds the local
1733 * address part of the mailbox. This buffer must be
1734 * free'ed by the caller.
1735 * host [in/out] - The host name structure that holds the original,
1736 * and optionally encoded, host name.
1737 * Curl_free_idnconverted_hostname() must be called
1738 * once the caller has finished with the structure.
1740 * Returns CURLE_OK on success.
1744 * Should a UTF-8 host name require conversion to IDN ACE and we cannot honor
1745 * that conversion then we shall return success. This allow the caller to send
1746 * the data to the server as a U-label (as per RFC-6531 sect. 3.2).
1748 * If an mailbox '@' separator cannot be located then the mailbox is considered
1749 * to be either a local mailbox or an invalid mailbox (depending on what the
1750 * calling function deems it to be) then the input will simply be returned in
1751 * the address part with the host name being NULL.
1753 static CURLcode smtp_parse_address(struct connectdata *conn, const char *fqma,
1754 char **address, struct hostname *host)
1756 CURLcode result = CURLE_OK;
1759 /* Duplicate the fully qualified email address so we can manipulate it,
1760 ensuring it doesn't contain the delimiters if specified */
1761 char *dup = strdup(fqma[0] == '<' ? fqma + 1 : fqma);
1763 return CURLE_OUT_OF_MEMORY;
1765 length = strlen(dup);
1767 if(dup[length - 1] == '>')
1768 dup[length - 1] = '\0';
1771 /* Extract the host name from the address (if we can) */
1772 host->name = strpbrk(dup, "@");
1775 host->name = host->name + 1;
1777 /* Attempt to convert the host name to IDN ACE */
1778 (void) Curl_idnconvert_hostname(conn, host);
1780 /* If Curl_idnconvert_hostname() fails then we shall attempt to continue
1781 and send the host name using UTF-8 rather than as 7-bit ACE (which is
1785 /* Extract the local address from the mailbox */
1791 CURLcode Curl_smtp_escape_eob(struct connectdata *conn, const ssize_t nread)
1793 /* When sending a SMTP payload we must detect CRLF. sequences making sure
1794 they are sent as CRLF.. instead, as a . on the beginning of a line will
1795 be deleted by the server when not part of an EOB terminator and a
1796 genuine CRLF.CRLF which isn't escaped will wrongly be detected as end of
1801 struct Curl_easy *data = conn->data;
1802 struct SMTP *smtp = data->req.protop;
1803 char *scratch = data->state.scratch;
1804 char *newscratch = NULL;
1805 char *oldscratch = NULL;
1808 /* Do we need to allocate a scratch buffer? */
1809 if(!scratch || data->set.crlf) {
1810 oldscratch = scratch;
1812 scratch = newscratch = malloc(2 * data->set.upload_buffer_size);
1814 failf(data, "Failed to alloc scratch buffer!");
1816 return CURLE_OUT_OF_MEMORY;
1819 DEBUGASSERT(data->set.upload_buffer_size >= (size_t)nread);
1821 /* Have we already sent part of the EOB? */
1822 eob_sent = smtp->eob;
1824 /* This loop can be improved by some kind of Boyer-Moore style of
1825 approach but that is saved for later... */
1826 for(i = 0, si = 0; i < nread; i++) {
1827 if(SMTP_EOB[smtp->eob] == data->req.upload_fromhere[i]) {
1830 /* Is the EOB potentially the terminating CRLF? */
1831 if(2 == smtp->eob || SMTP_EOB_LEN == smtp->eob)
1832 smtp->trailing_crlf = TRUE;
1834 smtp->trailing_crlf = FALSE;
1836 else if(smtp->eob) {
1837 /* A previous substring matched so output that first */
1838 memcpy(&scratch[si], &SMTP_EOB[eob_sent], smtp->eob - eob_sent);
1839 si += smtp->eob - eob_sent;
1841 /* Then compare the first byte */
1842 if(SMTP_EOB[0] == data->req.upload_fromhere[i])
1849 /* Reset the trailing CRLF flag as there was more data */
1850 smtp->trailing_crlf = FALSE;
1853 /* Do we have a match for CRLF. as per RFC-5321, sect. 4.5.2 */
1854 if(SMTP_EOB_FIND_LEN == smtp->eob) {
1855 /* Copy the replacement data to the target buffer */
1856 memcpy(&scratch[si], &SMTP_EOB_REPL[eob_sent],
1857 SMTP_EOB_REPL_LEN - eob_sent);
1858 si += SMTP_EOB_REPL_LEN - eob_sent;
1863 scratch[si++] = data->req.upload_fromhere[i];
1866 if(smtp->eob - eob_sent) {
1867 /* A substring matched before processing ended so output that now */
1868 memcpy(&scratch[si], &SMTP_EOB[eob_sent], smtp->eob - eob_sent);
1869 si += smtp->eob - eob_sent;
1872 /* Only use the new buffer if we replaced something */
1874 /* Upload from the new (replaced) buffer instead */
1875 data->req.upload_fromhere = scratch;
1877 /* Save the buffer so it can be freed later */
1878 data->state.scratch = scratch;
1880 /* Free the old scratch buffer */
1883 /* Set the new amount too */
1884 data->req.upload_present = si;
1892 #endif /* CURL_DISABLE_SMTP */