1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2017, 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 * RFC6749 OAuth 2.0 Authorization Framework
31 * Draft SMTP URL Interface <draft-earhart-url-smtp-00.txt>
32 * Draft LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt>
34 ***************************************************************************/
36 #include "curl_setup.h"
38 #ifndef CURL_DISABLE_SMTP
40 #ifdef HAVE_NETINET_IN_H
41 #include <netinet/in.h>
43 #ifdef HAVE_ARPA_INET_H
44 #include <arpa/inet.h>
47 #include <sys/utsname.h>
57 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
59 #define in_addr_t unsigned long
62 #include <curl/curl.h>
69 #include "http.h" /* for HTTP proxy tunnel stuff */
72 #include "strtoofft.h"
74 #include "vtls/vtls.h"
80 #include "curl_gethostname.h"
81 #include "curl_sasl.h"
83 /* The last 3 #include files should be in this order */
84 #include "curl_printf.h"
85 #include "curl_memory.h"
88 /* Local API functions */
89 static CURLcode smtp_regular_transfer(struct connectdata *conn, bool *done);
90 static CURLcode smtp_do(struct connectdata *conn, bool *done);
91 static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
93 static CURLcode smtp_connect(struct connectdata *conn, bool *done);
94 static CURLcode smtp_disconnect(struct connectdata *conn, bool dead);
95 static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done);
96 static int smtp_getsock(struct connectdata *conn, curl_socket_t *socks,
98 static CURLcode smtp_doing(struct connectdata *conn, bool *dophase_done);
99 static CURLcode smtp_setup_connection(struct connectdata *conn);
100 static CURLcode smtp_parse_url_options(struct connectdata *conn);
101 static CURLcode smtp_parse_url_path(struct connectdata *conn);
102 static CURLcode smtp_parse_custom_request(struct connectdata *conn);
103 static CURLcode smtp_perform_auth(struct connectdata *conn, const char *mech,
104 const char *initresp);
105 static CURLcode smtp_continue_auth(struct connectdata *conn, const char *resp);
106 static void smtp_get_message(char *buffer, char **outptr);
109 * SMTP protocol handler.
112 const struct Curl_handler Curl_handler_smtp = {
114 smtp_setup_connection, /* setup_connection */
116 smtp_done, /* done */
117 ZERO_NULL, /* do_more */
118 smtp_connect, /* connect_it */
119 smtp_multi_statemach, /* connecting */
120 smtp_doing, /* doing */
121 smtp_getsock, /* proto_getsock */
122 smtp_getsock, /* doing_getsock */
123 ZERO_NULL, /* domore_getsock */
124 ZERO_NULL, /* perform_getsock */
125 smtp_disconnect, /* disconnect */
126 ZERO_NULL, /* readwrite */
127 PORT_SMTP, /* defport */
128 CURLPROTO_SMTP, /* protocol */
129 PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY | /* flags */
135 * SMTPS protocol handler.
138 const struct Curl_handler Curl_handler_smtps = {
139 "SMTPS", /* scheme */
140 smtp_setup_connection, /* setup_connection */
142 smtp_done, /* done */
143 ZERO_NULL, /* do_more */
144 smtp_connect, /* connect_it */
145 smtp_multi_statemach, /* connecting */
146 smtp_doing, /* doing */
147 smtp_getsock, /* proto_getsock */
148 smtp_getsock, /* doing_getsock */
149 ZERO_NULL, /* domore_getsock */
150 ZERO_NULL, /* perform_getsock */
151 smtp_disconnect, /* disconnect */
152 ZERO_NULL, /* readwrite */
153 PORT_SMTPS, /* defport */
154 CURLPROTO_SMTPS, /* protocol */
155 PROTOPT_CLOSEACTION | PROTOPT_SSL
156 | PROTOPT_NOURLQUERY | PROTOPT_URLOPTIONS /* flags */
160 #ifndef CURL_DISABLE_HTTP
162 * HTTP-proxyed SMTP protocol handler.
165 static const struct Curl_handler Curl_handler_smtp_proxy = {
167 Curl_http_setup_conn, /* setup_connection */
168 Curl_http, /* do_it */
169 Curl_http_done, /* done */
170 ZERO_NULL, /* do_more */
171 ZERO_NULL, /* connect_it */
172 ZERO_NULL, /* connecting */
173 ZERO_NULL, /* doing */
174 ZERO_NULL, /* proto_getsock */
175 ZERO_NULL, /* doing_getsock */
176 ZERO_NULL, /* domore_getsock */
177 ZERO_NULL, /* perform_getsock */
178 ZERO_NULL, /* disconnect */
179 ZERO_NULL, /* readwrite */
180 PORT_SMTP, /* defport */
181 CURLPROTO_HTTP, /* protocol */
182 PROTOPT_NONE /* flags */
187 * HTTP-proxyed SMTPS protocol handler.
190 static const struct Curl_handler Curl_handler_smtps_proxy = {
191 "SMTPS", /* scheme */
192 Curl_http_setup_conn, /* setup_connection */
193 Curl_http, /* do_it */
194 Curl_http_done, /* done */
195 ZERO_NULL, /* do_more */
196 ZERO_NULL, /* connect_it */
197 ZERO_NULL, /* connecting */
198 ZERO_NULL, /* doing */
199 ZERO_NULL, /* proto_getsock */
200 ZERO_NULL, /* doing_getsock */
201 ZERO_NULL, /* domore_getsock */
202 ZERO_NULL, /* perform_getsock */
203 ZERO_NULL, /* disconnect */
204 ZERO_NULL, /* readwrite */
205 PORT_SMTPS, /* defport */
206 CURLPROTO_HTTP, /* protocol */
207 PROTOPT_NONE /* flags */
212 /* SASL parameters for the smtp protocol */
213 static const struct SASLproto saslsmtp = {
214 "smtp", /* The service name */
215 334, /* Code received when continuation is expected */
216 235, /* Code to receive upon authentication success */
217 512 - 8, /* Maximum initial response length (no max) */
218 smtp_perform_auth, /* Send authentication command */
219 smtp_continue_auth, /* Send authentication continuation */
220 smtp_get_message /* Get SASL response message */
224 static void smtp_to_smtps(struct connectdata *conn)
226 /* Change the connection handler */
227 conn->handler = &Curl_handler_smtps;
229 /* Set the connection's upgraded to TLS flag */
230 conn->tls_upgraded = TRUE;
233 #define smtp_to_smtps(x) Curl_nop_stmt
236 /***********************************************************************
240 * Checks for an ending SMTP status code at the start of the given string, but
241 * also detects various capabilities from the EHLO response including the
242 * supported authentication mechanisms.
244 static bool smtp_endofresp(struct connectdata *conn, char *line, size_t len,
247 struct smtp_conn *smtpc = &conn->proto.smtpc;
251 if(len < 4 || !ISDIGIT(line[0]) || !ISDIGIT(line[1]) || !ISDIGIT(line[2]))
254 /* Do we have a command response? This should be the response code followed
255 by a space and optionally some text as per RFC-5321 and as outlined in
256 Section 4. Examples of RFC-4954 but some e-mail servers ignore this and
257 only send the response code instead as per Section 4.2. */
258 if(line[3] == ' ' || len == 5) {
260 *resp = curlx_sltosi(strtol(line, NULL, 10));
262 /* Make sure real server never sends internal value */
266 /* Do we have a multiline (continuation) response? */
267 else if(line[3] == '-' &&
268 (smtpc->state == SMTP_EHLO || smtpc->state == SMTP_COMMAND)) {
270 *resp = 1; /* Internal response code */
276 /***********************************************************************
280 * Gets the authentication message from the response buffer.
282 static void smtp_get_message(char *buffer, char **outptr)
285 char *message = NULL;
287 /* Find the start of the message */
288 for(message = buffer + 4; *message == ' ' || *message == '\t'; message++)
291 /* Find the end of the message */
292 for(len = strlen(message); len--;)
293 if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' &&
294 message[len] != '\t')
297 /* Terminate the message */
305 /***********************************************************************
309 * This is the ONLY way to change SMTP state!
311 static void state(struct connectdata *conn, smtpstate newstate)
313 struct smtp_conn *smtpc = &conn->proto.smtpc;
314 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
315 /* for debug purposes */
316 static const char * const names[] = {
333 if(smtpc->state != newstate)
334 infof(conn->data, "SMTP %p state change from %s to %s\n",
335 (void *)smtpc, names[smtpc->state], names[newstate]);
338 smtpc->state = newstate;
341 /***********************************************************************
343 * smtp_perform_ehlo()
345 * Sends the EHLO command to not only initialise communication with the ESMTP
346 * server but to also obtain a list of server side supported capabilities.
348 static CURLcode smtp_perform_ehlo(struct connectdata *conn)
350 CURLcode result = CURLE_OK;
351 struct smtp_conn *smtpc = &conn->proto.smtpc;
353 smtpc->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanism yet */
354 smtpc->sasl.authused = SASL_AUTH_NONE; /* Clear the authentication mechanism
355 used for esmtp connections */
356 smtpc->tls_supported = FALSE; /* Clear the TLS capability */
357 smtpc->auth_supported = FALSE; /* Clear the AUTH capability */
359 /* Send the EHLO command */
360 result = Curl_pp_sendf(&smtpc->pp, "EHLO %s", smtpc->domain);
363 state(conn, SMTP_EHLO);
368 /***********************************************************************
370 * smtp_perform_helo()
372 * Sends the HELO command to initialise communication with the SMTP server.
374 static CURLcode smtp_perform_helo(struct connectdata *conn)
376 CURLcode result = CURLE_OK;
377 struct smtp_conn *smtpc = &conn->proto.smtpc;
379 smtpc->sasl.authused = SASL_AUTH_NONE; /* No authentication mechanism used
380 in smtp connections */
382 /* Send the HELO command */
383 result = Curl_pp_sendf(&smtpc->pp, "HELO %s", smtpc->domain);
386 state(conn, SMTP_HELO);
391 /***********************************************************************
393 * smtp_perform_starttls()
395 * Sends the STLS command to start the upgrade to TLS.
397 static CURLcode smtp_perform_starttls(struct connectdata *conn)
399 CURLcode result = CURLE_OK;
401 /* Send the STARTTLS command */
402 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "STARTTLS");
405 state(conn, SMTP_STARTTLS);
410 /***********************************************************************
412 * smtp_perform_upgrade_tls()
414 * Performs the upgrade to TLS.
416 static CURLcode smtp_perform_upgrade_tls(struct connectdata *conn)
418 CURLcode result = CURLE_OK;
419 struct smtp_conn *smtpc = &conn->proto.smtpc;
421 /* Start the SSL connection */
422 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone);
425 if(smtpc->state != SMTP_UPGRADETLS)
426 state(conn, SMTP_UPGRADETLS);
430 result = smtp_perform_ehlo(conn);
437 /***********************************************************************
439 * smtp_perform_auth()
441 * Sends an AUTH command allowing the client to login with the given SASL
442 * authentication mechanism.
444 static CURLcode smtp_perform_auth(struct connectdata *conn,
446 const char *initresp)
448 CURLcode result = CURLE_OK;
449 struct smtp_conn *smtpc = &conn->proto.smtpc;
451 if(initresp) { /* AUTH <mech> ...<crlf> */
452 /* Send the AUTH command with the initial response */
453 result = Curl_pp_sendf(&smtpc->pp, "AUTH %s %s", mech, initresp);
456 /* Send the AUTH command */
457 result = Curl_pp_sendf(&smtpc->pp, "AUTH %s", mech);
463 /***********************************************************************
465 * smtp_continue_auth()
467 * Sends SASL continuation data or cancellation.
469 static CURLcode smtp_continue_auth(struct connectdata *conn, const char *resp)
471 struct smtp_conn *smtpc = &conn->proto.smtpc;
473 return Curl_pp_sendf(&smtpc->pp, "%s", resp);
476 /***********************************************************************
478 * smtp_perform_authentication()
480 * Initiates the authentication sequence, with the appropriate SASL
481 * authentication mechanism.
483 static CURLcode smtp_perform_authentication(struct connectdata *conn)
485 CURLcode result = CURLE_OK;
486 struct smtp_conn *smtpc = &conn->proto.smtpc;
487 saslprogress progress;
489 /* Check we have enough data to authenticate with, and the
490 server supports authentiation, and end the connect phase if not */
491 if(!smtpc->auth_supported ||
492 !Curl_sasl_can_authenticate(&smtpc->sasl, conn)) {
493 state(conn, SMTP_STOP);
497 /* Calculate the SASL login details */
498 result = Curl_sasl_start(&smtpc->sasl, conn, FALSE, &progress);
501 if(progress == SASL_INPROGRESS)
502 state(conn, SMTP_AUTH);
504 /* Other mechanisms not supported */
505 infof(conn->data, "No known authentication mechanisms supported!\n");
506 result = CURLE_LOGIN_DENIED;
513 /***********************************************************************
515 * smtp_perform_command()
517 * Sends a SMTP based command.
519 static CURLcode smtp_perform_command(struct connectdata *conn)
521 CURLcode result = CURLE_OK;
522 struct Curl_easy *data = conn->data;
523 struct SMTP *smtp = data->req.protop;
525 /* Send the command */
527 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s %s",
528 smtp->custom && smtp->custom[0] != '\0' ?
529 smtp->custom : "VRFY",
532 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s",
533 smtp->custom && smtp->custom[0] != '\0' ?
534 smtp->custom : "HELP");
537 state(conn, SMTP_COMMAND);
542 /***********************************************************************
544 * smtp_perform_mail()
546 * Sends an MAIL command to initiate the upload of a message.
548 static CURLcode smtp_perform_mail(struct connectdata *conn)
553 CURLcode result = CURLE_OK;
554 struct Curl_easy *data = conn->data;
556 /* Calculate the FROM parameter */
557 if(!data->set.str[STRING_MAIL_FROM])
558 /* Null reverse-path, RFC-5321, sect. 3.6.3 */
560 else if(data->set.str[STRING_MAIL_FROM][0] == '<')
561 from = aprintf("%s", data->set.str[STRING_MAIL_FROM]);
563 from = aprintf("<%s>", data->set.str[STRING_MAIL_FROM]);
566 return CURLE_OUT_OF_MEMORY;
568 /* Calculate the optional AUTH parameter */
569 if(data->set.str[STRING_MAIL_AUTH] && conn->proto.smtpc.sasl.authused) {
570 if(data->set.str[STRING_MAIL_AUTH][0] != '\0')
571 auth = aprintf("%s", data->set.str[STRING_MAIL_AUTH]);
573 /* Empty AUTH, RFC-2554, sect. 5 */
579 return CURLE_OUT_OF_MEMORY;
583 /* Calculate the optional SIZE parameter */
584 if(conn->proto.smtpc.size_supported && conn->data->state.infilesize > 0) {
585 size = aprintf("%" CURL_FORMAT_CURL_OFF_T, data->state.infilesize);
591 return CURLE_OUT_OF_MEMORY;
595 /* Send the MAIL command */
597 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
598 "MAIL FROM:%s", from);
599 else if(auth && !size)
600 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
601 "MAIL FROM:%s AUTH=%s", from, auth);
602 else if(auth && size)
603 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
604 "MAIL FROM:%s AUTH=%s SIZE=%s", from, auth, size);
606 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
607 "MAIL FROM:%s SIZE=%s", from, size);
614 state(conn, SMTP_MAIL);
619 /***********************************************************************
621 * smtp_perform_rcpt_to()
623 * Sends a RCPT TO command for a given recipient as part of the message upload
626 static CURLcode smtp_perform_rcpt_to(struct connectdata *conn)
628 CURLcode result = CURLE_OK;
629 struct Curl_easy *data = conn->data;
630 struct SMTP *smtp = data->req.protop;
632 /* Send the RCPT TO command */
633 if(smtp->rcpt->data[0] == '<')
634 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:%s",
637 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:<%s>",
640 state(conn, SMTP_RCPT);
645 /***********************************************************************
647 * smtp_perform_quit()
649 * Performs the quit action prior to sclose() being called.
651 static CURLcode smtp_perform_quit(struct connectdata *conn)
653 CURLcode result = CURLE_OK;
655 /* Send the QUIT command */
656 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "QUIT");
659 state(conn, SMTP_QUIT);
664 /* For the initial server greeting */
665 static CURLcode smtp_state_servergreet_resp(struct connectdata *conn,
669 CURLcode result = CURLE_OK;
670 struct Curl_easy *data = conn->data;
672 (void)instate; /* no use for this yet */
674 if(smtpcode/100 != 2) {
675 failf(data, "Got unexpected smtp-server response: %d", smtpcode);
676 result = CURLE_WEIRD_SERVER_REPLY;
679 result = smtp_perform_ehlo(conn);
684 /* For STARTTLS responses */
685 static CURLcode smtp_state_starttls_resp(struct connectdata *conn,
689 CURLcode result = CURLE_OK;
690 struct Curl_easy *data = conn->data;
692 (void)instate; /* no use for this yet */
694 if(smtpcode != 220) {
695 if(data->set.use_ssl != CURLUSESSL_TRY) {
696 failf(data, "STARTTLS denied, code %d", smtpcode);
697 result = CURLE_USE_SSL_FAILED;
700 result = smtp_perform_authentication(conn);
703 result = smtp_perform_upgrade_tls(conn);
708 /* For EHLO responses */
709 static CURLcode smtp_state_ehlo_resp(struct connectdata *conn, int smtpcode,
712 CURLcode result = CURLE_OK;
713 struct Curl_easy *data = conn->data;
714 struct smtp_conn *smtpc = &conn->proto.smtpc;
715 const char *line = data->state.buffer;
716 size_t len = strlen(line);
719 (void)instate; /* no use for this yet */
721 if(smtpcode/100 != 2 && smtpcode != 1) {
722 if(data->set.use_ssl <= CURLUSESSL_TRY || conn->ssl[FIRSTSOCKET].use)
723 result = smtp_perform_helo(conn);
725 failf(data, "Remote access denied: %d", smtpcode);
726 result = CURLE_REMOTE_ACCESS_DENIED;
733 /* Does the server support the STARTTLS capability? */
734 if(len >= 8 && !memcmp(line, "STARTTLS", 8))
735 smtpc->tls_supported = TRUE;
737 /* Does the server support the SIZE capability? */
738 else if(len >= 4 && !memcmp(line, "SIZE", 4))
739 smtpc->size_supported = TRUE;
741 /* Does the server support authentication? */
742 else if(len >= 5 && !memcmp(line, "AUTH ", 5)) {
743 smtpc->auth_supported = TRUE;
745 /* Advance past the AUTH keyword */
749 /* Loop through the data line */
752 unsigned int mechbit;
755 (*line == ' ' || *line == '\t' ||
756 *line == '\r' || *line == '\n')) {
765 /* Extract the word */
766 for(wordlen = 0; wordlen < len && line[wordlen] != ' ' &&
767 line[wordlen] != '\t' && line[wordlen] != '\r' &&
768 line[wordlen] != '\n';)
771 /* Test the word for a matching authentication mechanism */
772 mechbit = Curl_sasl_decode_mech(line, wordlen, &llen);
773 if(mechbit && llen == wordlen)
774 smtpc->sasl.authmechs |= mechbit;
782 if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
783 /* We don't have a SSL/TLS connection yet, but SSL is requested */
784 if(smtpc->tls_supported)
785 /* Switch to TLS connection now */
786 result = smtp_perform_starttls(conn);
787 else if(data->set.use_ssl == CURLUSESSL_TRY)
788 /* Fallback and carry on with authentication */
789 result = smtp_perform_authentication(conn);
791 failf(data, "STARTTLS not supported.");
792 result = CURLE_USE_SSL_FAILED;
796 result = smtp_perform_authentication(conn);
803 /* For HELO responses */
804 static CURLcode smtp_state_helo_resp(struct connectdata *conn, int smtpcode,
807 CURLcode result = CURLE_OK;
808 struct Curl_easy *data = conn->data;
810 (void)instate; /* no use for this yet */
812 if(smtpcode/100 != 2) {
813 failf(data, "Remote access denied: %d", smtpcode);
814 result = CURLE_REMOTE_ACCESS_DENIED;
817 /* End of connect phase */
818 state(conn, SMTP_STOP);
823 /* For SASL authentication responses */
824 static CURLcode smtp_state_auth_resp(struct connectdata *conn,
828 CURLcode result = CURLE_OK;
829 struct Curl_easy *data = conn->data;
830 struct smtp_conn *smtpc = &conn->proto.smtpc;
831 saslprogress progress;
833 (void)instate; /* no use for this yet */
835 result = Curl_sasl_continue(&smtpc->sasl, conn, smtpcode, &progress);
839 state(conn, SMTP_STOP); /* Authenticated */
841 case SASL_IDLE: /* No mechanism left after cancellation */
842 failf(data, "Authentication cancelled");
843 result = CURLE_LOGIN_DENIED;
852 /* For command responses */
853 static CURLcode smtp_state_command_resp(struct connectdata *conn, int smtpcode,
856 CURLcode result = CURLE_OK;
857 struct Curl_easy *data = conn->data;
858 struct SMTP *smtp = data->req.protop;
859 char *line = data->state.buffer;
860 size_t len = strlen(line);
862 (void)instate; /* no use for this yet */
864 if((smtp->rcpt && smtpcode/100 != 2 && smtpcode != 553 && smtpcode != 1) ||
865 (!smtp->rcpt && smtpcode/100 != 2 && smtpcode != 1)) {
866 failf(data, "Command failed: %d", smtpcode);
867 result = CURLE_RECV_ERROR;
870 /* Temporarily add the LF character back and send as body to the client */
871 if(!data->set.opt_no_body) {
873 result = Curl_client_write(conn, CLIENTWRITE_BODY, line, len + 1);
879 smtp->rcpt = smtp->rcpt->next;
882 /* Send the next command */
883 result = smtp_perform_command(conn);
886 /* End of DO phase */
887 state(conn, SMTP_STOP);
890 /* End of DO phase */
891 state(conn, SMTP_STOP);
898 /* For MAIL responses */
899 static CURLcode smtp_state_mail_resp(struct connectdata *conn, int smtpcode,
902 CURLcode result = CURLE_OK;
903 struct Curl_easy *data = conn->data;
905 (void)instate; /* no use for this yet */
907 if(smtpcode/100 != 2) {
908 failf(data, "MAIL failed: %d", smtpcode);
909 result = CURLE_SEND_ERROR;
912 /* Start the RCPT TO command */
913 result = smtp_perform_rcpt_to(conn);
918 /* For RCPT responses */
919 static CURLcode smtp_state_rcpt_resp(struct connectdata *conn, int smtpcode,
922 CURLcode result = CURLE_OK;
923 struct Curl_easy *data = conn->data;
924 struct SMTP *smtp = data->req.protop;
926 (void)instate; /* no use for this yet */
928 if(smtpcode/100 != 2) {
929 failf(data, "RCPT failed: %d", smtpcode);
930 result = CURLE_SEND_ERROR;
933 smtp->rcpt = smtp->rcpt->next;
936 /* Send the next RCPT TO command */
937 result = smtp_perform_rcpt_to(conn);
939 /* Send the DATA command */
940 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "DATA");
943 state(conn, SMTP_DATA);
950 /* For DATA response */
951 static CURLcode smtp_state_data_resp(struct connectdata *conn, int smtpcode,
954 CURLcode result = CURLE_OK;
955 struct Curl_easy *data = conn->data;
957 (void)instate; /* no use for this yet */
959 if(smtpcode != 354) {
960 failf(data, "DATA failed: %d", smtpcode);
961 result = CURLE_SEND_ERROR;
964 /* Set the progress upload size */
965 Curl_pgrsSetUploadSize(data, data->state.infilesize);
968 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL);
970 /* End of DO phase */
971 state(conn, SMTP_STOP);
977 /* For POSTDATA responses, which are received after the entire DATA
978 part has been sent to the server */
979 static CURLcode smtp_state_postdata_resp(struct connectdata *conn,
983 CURLcode result = CURLE_OK;
985 (void)instate; /* no use for this yet */
988 result = CURLE_RECV_ERROR;
990 /* End of DONE phase */
991 state(conn, SMTP_STOP);
996 static CURLcode smtp_statemach_act(struct connectdata *conn)
998 CURLcode result = CURLE_OK;
999 curl_socket_t sock = conn->sock[FIRSTSOCKET];
1000 struct Curl_easy *data = conn->data;
1002 struct smtp_conn *smtpc = &conn->proto.smtpc;
1003 struct pingpong *pp = &smtpc->pp;
1006 /* Busy upgrading the connection; right now all I/O is SSL/TLS, not SMTP */
1007 if(smtpc->state == SMTP_UPGRADETLS)
1008 return smtp_perform_upgrade_tls(conn);
1010 /* Flush any data that needs to be sent */
1012 return Curl_pp_flushsend(pp);
1015 /* Read the response from the server */
1016 result = Curl_pp_readresp(sock, pp, &smtpcode, &nread);
1020 /* Store the latest response for later retrieval if necessary */
1021 if(smtpc->state != SMTP_QUIT && smtpcode != 1)
1022 data->info.httpcode = smtpcode;
1027 /* We have now received a full SMTP server response */
1028 switch(smtpc->state) {
1029 case SMTP_SERVERGREET:
1030 result = smtp_state_servergreet_resp(conn, smtpcode, smtpc->state);
1034 result = smtp_state_ehlo_resp(conn, smtpcode, smtpc->state);
1038 result = smtp_state_helo_resp(conn, smtpcode, smtpc->state);
1042 result = smtp_state_starttls_resp(conn, smtpcode, smtpc->state);
1046 result = smtp_state_auth_resp(conn, smtpcode, smtpc->state);
1050 result = smtp_state_command_resp(conn, smtpcode, smtpc->state);
1054 result = smtp_state_mail_resp(conn, smtpcode, smtpc->state);
1058 result = smtp_state_rcpt_resp(conn, smtpcode, smtpc->state);
1062 result = smtp_state_data_resp(conn, smtpcode, smtpc->state);
1066 result = smtp_state_postdata_resp(conn, smtpcode, smtpc->state);
1070 /* fallthrough, just stop! */
1072 /* internal error */
1073 state(conn, SMTP_STOP);
1076 } while(!result && smtpc->state != SMTP_STOP && Curl_pp_moredata(pp));
1081 /* Called repeatedly until done from multi.c */
1082 static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done)
1084 CURLcode result = CURLE_OK;
1085 struct smtp_conn *smtpc = &conn->proto.smtpc;
1087 if((conn->handler->flags & PROTOPT_SSL) && !smtpc->ssldone) {
1088 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone);
1089 if(result || !smtpc->ssldone)
1093 result = Curl_pp_statemach(&smtpc->pp, FALSE);
1094 *done = (smtpc->state == SMTP_STOP) ? TRUE : FALSE;
1099 static CURLcode smtp_block_statemach(struct connectdata *conn)
1101 CURLcode result = CURLE_OK;
1102 struct smtp_conn *smtpc = &conn->proto.smtpc;
1104 while(smtpc->state != SMTP_STOP && !result)
1105 result = Curl_pp_statemach(&smtpc->pp, TRUE);
1110 /* Allocate and initialize the SMTP struct for the current Curl_easy if
1112 static CURLcode smtp_init(struct connectdata *conn)
1114 CURLcode result = CURLE_OK;
1115 struct Curl_easy *data = conn->data;
1118 smtp = data->req.protop = calloc(sizeof(struct SMTP), 1);
1120 result = CURLE_OUT_OF_MEMORY;
1125 /* For the SMTP "protocol connect" and "doing" phases only */
1126 static int smtp_getsock(struct connectdata *conn, curl_socket_t *socks,
1129 return Curl_pp_getsock(&conn->proto.smtpc.pp, socks, numsocks);
1132 /***********************************************************************
1136 * This function should do everything that is to be considered a part of
1137 * the connection phase.
1139 * The variable pointed to by 'done' will be TRUE if the protocol-layer
1140 * connect phase is done when this function returns, or FALSE if not.
1142 static CURLcode smtp_connect(struct connectdata *conn, bool *done)
1144 CURLcode result = CURLE_OK;
1145 struct smtp_conn *smtpc = &conn->proto.smtpc;
1146 struct pingpong *pp = &smtpc->pp;
1148 *done = FALSE; /* default to not done yet */
1150 /* We always support persistent connections in SMTP */
1151 connkeep(conn, "SMTP default");
1153 /* Set the default response time-out */
1154 pp->response_time = RESP_TIMEOUT;
1155 pp->statemach_act = smtp_statemach_act;
1156 pp->endofresp = smtp_endofresp;
1159 /* Initialize the SASL storage */
1160 Curl_sasl_init(&smtpc->sasl, &saslsmtp);
1162 /* Initialise the pingpong layer */
1165 /* Parse the URL options */
1166 result = smtp_parse_url_options(conn);
1170 /* Parse the URL path */
1171 result = smtp_parse_url_path(conn);
1175 /* Start off waiting for the server greeting response */
1176 state(conn, SMTP_SERVERGREET);
1178 result = smtp_multi_statemach(conn, done);
1183 /***********************************************************************
1187 * The DONE function. This does what needs to be done after a single DO has
1190 * Input argument is already checked for validity.
1192 static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
1195 CURLcode result = CURLE_OK;
1196 struct Curl_easy *data = conn->data;
1197 struct SMTP *smtp = data->req.protop;
1198 struct pingpong *pp = &conn->proto.smtpc.pp;
1201 ssize_t bytes_written;
1205 if(!smtp || !pp->conn)
1209 connclose(conn, "SMTP done with bad status"); /* marked for closure */
1210 result = status; /* use the already set error code */
1212 else if(!data->set.connect_only && data->set.upload && data->set.mail_rcpt) {
1213 /* Calculate the EOB taking into account any terminating CRLF from the
1214 previous line of the email or the CRLF of the DATA command when there
1215 is "no mail data". RFC-5321, sect. 4.1.1.4.
1217 Note: As some SSL backends, such as OpenSSL, will cause Curl_write() to
1218 fail when using a different pointer following a previous write, that
1219 returned CURLE_AGAIN, we duplicate the EOB now rather than when the
1220 bytes written doesn't equal len. */
1221 if(smtp->trailing_crlf || !conn->data->state.infilesize) {
1222 eob = strdup(SMTP_EOB + 2);
1223 len = SMTP_EOB_LEN - 2;
1226 eob = strdup(SMTP_EOB);
1231 return CURLE_OUT_OF_MEMORY;
1233 /* Send the end of block data */
1234 result = Curl_write(conn, conn->writesockfd, eob, len, &bytes_written);
1240 if(bytes_written != len) {
1241 /* The whole chunk was not sent so keep it around and adjust the
1242 pingpong structure accordingly */
1245 pp->sendleft = len - bytes_written;
1248 /* Successfully sent so adjust the response timeout relative to now */
1249 pp->response = Curl_tvnow();
1254 state(conn, SMTP_POSTDATA);
1256 /* Run the state-machine
1258 TODO: when the multi interface is used, this _really_ should be using
1259 the smtp_multi_statemach function but we have no general support for
1260 non-blocking DONE operations!
1262 result = smtp_block_statemach(conn);
1265 /* Cleanup our per-request based variables */
1266 Curl_safefree(smtp->custom);
1268 /* Clear the transfer mode for the next request */
1269 smtp->transfer = FTPTRANSFER_BODY;
1274 /***********************************************************************
1278 * This is the actual DO function for SMTP. Transfer a mail, send a command
1279 * or get some data according to the options previously setup.
1281 static CURLcode smtp_perform(struct connectdata *conn, bool *connected,
1284 /* This is SMTP and no proxy */
1285 CURLcode result = CURLE_OK;
1286 struct Curl_easy *data = conn->data;
1287 struct SMTP *smtp = data->req.protop;
1289 DEBUGF(infof(conn->data, "DO phase starts\n"));
1291 if(data->set.opt_no_body) {
1292 /* Requested no body means no transfer */
1293 smtp->transfer = FTPTRANSFER_INFO;
1296 *dophase_done = FALSE; /* not done yet */
1298 /* Store the first recipient (or NULL if not specified) */
1299 smtp->rcpt = data->set.mail_rcpt;
1301 /* Start the first command in the DO phase */
1302 if(data->set.upload && data->set.mail_rcpt)
1304 result = smtp_perform_mail(conn);
1306 /* SMTP based command (VRFY, EXPN, NOOP, RSET or HELP) */
1307 result = smtp_perform_command(conn);
1312 /* Run the state-machine */
1313 result = smtp_multi_statemach(conn, dophase_done);
1315 *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1318 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1323 /***********************************************************************
1327 * This function is registered as 'curl_do' function. It decodes the path
1328 * parts etc as a wrapper to the actual DO function (smtp_perform).
1330 * The input argument is already checked for validity.
1332 static CURLcode smtp_do(struct connectdata *conn, bool *done)
1334 CURLcode result = CURLE_OK;
1336 *done = FALSE; /* default to false */
1338 /* Parse the custom request */
1339 result = smtp_parse_custom_request(conn);
1343 result = smtp_regular_transfer(conn, done);
1348 /***********************************************************************
1352 * Disconnect from an SMTP server. Cleanup protocol-specific per-connection
1353 * resources. BLOCKING.
1355 static CURLcode smtp_disconnect(struct connectdata *conn, bool dead_connection)
1357 struct smtp_conn *smtpc = &conn->proto.smtpc;
1359 /* We cannot send quit unconditionally. If this connection is stale or
1360 bad in any way, sending quit and waiting around here will make the
1361 disconnect wait in vain and cause more problems than we need to. */
1363 /* The SMTP session may or may not have been allocated/setup at this
1365 if(!dead_connection && smtpc->pp.conn && smtpc->pp.conn->bits.protoconnstart)
1366 if(!smtp_perform_quit(conn))
1367 (void)smtp_block_statemach(conn); /* ignore errors on QUIT */
1369 /* Disconnect from the server */
1370 Curl_pp_disconnect(&smtpc->pp);
1372 /* Cleanup the SASL module */
1373 Curl_sasl_cleanup(conn, smtpc->sasl.authused);
1375 /* Cleanup our connection based variables */
1376 Curl_safefree(smtpc->domain);
1381 /* Call this when the DO phase has completed */
1382 static CURLcode smtp_dophase_done(struct connectdata *conn, bool connected)
1384 struct SMTP *smtp = conn->data->req.protop;
1388 if(smtp->transfer != FTPTRANSFER_BODY)
1389 /* no data to transfer */
1390 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1395 /* Called from multi.c while DOing */
1396 static CURLcode smtp_doing(struct connectdata *conn, bool *dophase_done)
1398 CURLcode result = smtp_multi_statemach(conn, dophase_done);
1401 DEBUGF(infof(conn->data, "DO phase failed\n"));
1402 else if(*dophase_done) {
1403 result = smtp_dophase_done(conn, FALSE /* not connected */);
1405 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1411 /***********************************************************************
1413 * smtp_regular_transfer()
1415 * The input argument is already checked for validity.
1417 * Performs all commands done before a regular transfer between a local and a
1420 static CURLcode smtp_regular_transfer(struct connectdata *conn,
1423 CURLcode result = CURLE_OK;
1424 bool connected = FALSE;
1425 struct Curl_easy *data = conn->data;
1427 /* Make sure size is unknown at this point */
1428 data->req.size = -1;
1430 /* Set the progress data */
1431 Curl_pgrsSetUploadCounter(data, 0);
1432 Curl_pgrsSetDownloadCounter(data, 0);
1433 Curl_pgrsSetUploadSize(data, -1);
1434 Curl_pgrsSetDownloadSize(data, -1);
1436 /* Carry out the perform */
1437 result = smtp_perform(conn, &connected, dophase_done);
1439 /* Perform post DO phase operations if necessary */
1440 if(!result && *dophase_done)
1441 result = smtp_dophase_done(conn, connected);
1446 static CURLcode smtp_setup_connection(struct connectdata *conn)
1448 struct Curl_easy *data = conn->data;
1451 /* Clear the TLS upgraded flag */
1452 conn->tls_upgraded = FALSE;
1454 /* Set up the proxy if necessary */
1455 if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
1456 /* Unless we have asked to tunnel SMTP operations through the proxy, we
1457 switch and use HTTP operations only */
1458 #ifndef CURL_DISABLE_HTTP
1459 if(conn->handler == &Curl_handler_smtp)
1460 conn->handler = &Curl_handler_smtp_proxy;
1463 conn->handler = &Curl_handler_smtps_proxy;
1465 failf(data, "SMTPS not supported!");
1466 return CURLE_UNSUPPORTED_PROTOCOL;
1469 /* set it up as a HTTP connection instead */
1470 return conn->handler->setup_connection(conn);
1473 failf(data, "SMTP over http proxy requires HTTP support built-in!");
1474 return CURLE_UNSUPPORTED_PROTOCOL;
1478 /* Initialise the SMTP layer */
1479 result = smtp_init(conn);
1483 data->state.path++; /* don't include the initial slash */
1488 /***********************************************************************
1490 * smtp_parse_url_options()
1492 * Parse the URL login options.
1494 static CURLcode smtp_parse_url_options(struct connectdata *conn)
1496 CURLcode result = CURLE_OK;
1497 struct smtp_conn *smtpc = &conn->proto.smtpc;
1498 const char *ptr = conn->options;
1500 smtpc->sasl.resetprefs = TRUE;
1502 while(!result && ptr && *ptr) {
1503 const char *key = ptr;
1506 while(*ptr && *ptr != '=')
1511 while(*ptr && *ptr != ';')
1514 if(strncasecompare(key, "AUTH=", 5))
1515 result = Curl_sasl_parse_url_auth_option(&smtpc->sasl,
1516 value, ptr - value);
1518 result = CURLE_URL_MALFORMAT;
1527 /***********************************************************************
1529 * smtp_parse_url_path()
1531 * Parse the URL path into separate path components.
1533 static CURLcode smtp_parse_url_path(struct connectdata *conn)
1535 /* The SMTP struct is already initialised in smtp_connect() */
1536 struct Curl_easy *data = conn->data;
1537 struct smtp_conn *smtpc = &conn->proto.smtpc;
1538 const char *path = data->state.path;
1539 char localhost[HOSTNAME_MAX + 1];
1541 /* Calculate the path if necessary */
1543 if(!Curl_gethostname(localhost, sizeof(localhost)))
1549 /* URL decode the path and use it as the domain in our EHLO */
1550 return Curl_urldecode(conn->data, path, 0, &smtpc->domain, NULL, TRUE);
1553 /***********************************************************************
1555 * smtp_parse_custom_request()
1557 * Parse the custom request.
1559 static CURLcode smtp_parse_custom_request(struct connectdata *conn)
1561 CURLcode result = CURLE_OK;
1562 struct Curl_easy *data = conn->data;
1563 struct SMTP *smtp = data->req.protop;
1564 const char *custom = data->set.str[STRING_CUSTOMREQUEST];
1566 /* URL decode the custom request */
1568 result = Curl_urldecode(data, custom, 0, &smtp->custom, NULL, TRUE);
1573 CURLcode Curl_smtp_escape_eob(struct connectdata *conn, const ssize_t nread)
1575 /* When sending a SMTP payload we must detect CRLF. sequences making sure
1576 they are sent as CRLF.. instead, as a . on the beginning of a line will
1577 be deleted by the server when not part of an EOB terminator and a
1578 genuine CRLF.CRLF which isn't escaped will wrongly be detected as end of
1583 struct Curl_easy *data = conn->data;
1584 struct SMTP *smtp = data->req.protop;
1585 char *scratch = data->state.scratch;
1586 char *newscratch = NULL;
1587 char *oldscratch = NULL;
1590 /* Do we need to allocate a scratch buffer? */
1591 if(!scratch || data->set.crlf) {
1592 oldscratch = scratch;
1594 scratch = newscratch = malloc(2 * BUFSIZE);
1596 failf(data, "Failed to alloc scratch buffer!");
1598 return CURLE_OUT_OF_MEMORY;
1602 /* Have we already sent part of the EOB? */
1603 eob_sent = smtp->eob;
1605 /* This loop can be improved by some kind of Boyer-Moore style of
1606 approach but that is saved for later... */
1607 for(i = 0, si = 0; i < nread; i++) {
1608 if(SMTP_EOB[smtp->eob] == data->req.upload_fromhere[i]) {
1611 /* Is the EOB potentially the terminating CRLF? */
1612 if(2 == smtp->eob || SMTP_EOB_LEN == smtp->eob)
1613 smtp->trailing_crlf = TRUE;
1615 smtp->trailing_crlf = FALSE;
1617 else if(smtp->eob) {
1618 /* A previous substring matched so output that first */
1619 memcpy(&scratch[si], &SMTP_EOB[eob_sent], smtp->eob - eob_sent);
1620 si += smtp->eob - eob_sent;
1622 /* Then compare the first byte */
1623 if(SMTP_EOB[0] == data->req.upload_fromhere[i])
1630 /* Reset the trailing CRLF flag as there was more data */
1631 smtp->trailing_crlf = FALSE;
1634 /* Do we have a match for CRLF. as per RFC-5321, sect. 4.5.2 */
1635 if(SMTP_EOB_FIND_LEN == smtp->eob) {
1636 /* Copy the replacement data to the target buffer */
1637 memcpy(&scratch[si], &SMTP_EOB_REPL[eob_sent],
1638 SMTP_EOB_REPL_LEN - eob_sent);
1639 si += SMTP_EOB_REPL_LEN - eob_sent;
1644 scratch[si++] = data->req.upload_fromhere[i];
1647 if(smtp->eob - eob_sent) {
1648 /* A substring matched before processing ended so output that now */
1649 memcpy(&scratch[si], &SMTP_EOB[eob_sent], smtp->eob - eob_sent);
1650 si += smtp->eob - eob_sent;
1653 /* Only use the new buffer if we replaced something */
1655 /* Upload from the new (replaced) buffer instead */
1656 data->req.upload_fromhere = scratch;
1658 /* Save the buffer so it can be freed later */
1659 data->state.scratch = scratch;
1661 /* Free the old scratch buffer */
1664 /* Set the new amount too */
1665 data->req.upload_present = si;
1673 #endif /* CURL_DISABLE_SMTP */