1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2014, 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 http://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 */
73 #include "strtoofft.h"
75 #include "vtls/vtls.h"
82 #include "curl_gethostname.h"
83 #include "curl_sasl.h"
86 #define _MPRINTF_REPLACE /* use our functions only */
87 #include <curl/mprintf.h>
89 #include "curl_memory.h"
90 /* The last #include file should be: */
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,
103 static CURLcode smtp_doing(struct connectdata *conn, bool *dophase_done);
104 static CURLcode smtp_setup_connection(struct connectdata *conn);
105 static CURLcode smtp_parse_url_options(struct connectdata *conn);
106 static CURLcode smtp_parse_url_path(struct connectdata *conn);
107 static CURLcode smtp_parse_custom_request(struct connectdata *conn);
108 static CURLcode smtp_calc_sasl_details(struct connectdata *conn,
110 char **initresp, size_t *len,
111 smtpstate *state1, smtpstate *state2);
114 * SMTP protocol handler.
117 const struct Curl_handler Curl_handler_smtp = {
119 smtp_setup_connection, /* setup_connection */
121 smtp_done, /* done */
122 ZERO_NULL, /* do_more */
123 smtp_connect, /* connect_it */
124 smtp_multi_statemach, /* connecting */
125 smtp_doing, /* doing */
126 smtp_getsock, /* proto_getsock */
127 smtp_getsock, /* doing_getsock */
128 ZERO_NULL, /* domore_getsock */
129 ZERO_NULL, /* perform_getsock */
130 smtp_disconnect, /* disconnect */
131 ZERO_NULL, /* readwrite */
132 PORT_SMTP, /* defport */
133 CURLPROTO_SMTP, /* protocol */
134 PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY /* flags */
139 * SMTPS protocol handler.
142 const struct Curl_handler Curl_handler_smtps = {
143 "SMTPS", /* scheme */
144 smtp_setup_connection, /* setup_connection */
146 smtp_done, /* done */
147 ZERO_NULL, /* do_more */
148 smtp_connect, /* connect_it */
149 smtp_multi_statemach, /* connecting */
150 smtp_doing, /* doing */
151 smtp_getsock, /* proto_getsock */
152 smtp_getsock, /* doing_getsock */
153 ZERO_NULL, /* domore_getsock */
154 ZERO_NULL, /* perform_getsock */
155 smtp_disconnect, /* disconnect */
156 ZERO_NULL, /* readwrite */
157 PORT_SMTPS, /* defport */
158 CURLPROTO_SMTPS, /* protocol */
159 PROTOPT_CLOSEACTION | PROTOPT_SSL
160 | PROTOPT_NOURLQUERY /* flags */
164 #ifndef CURL_DISABLE_HTTP
166 * HTTP-proxyed SMTP protocol handler.
169 static const struct Curl_handler Curl_handler_smtp_proxy = {
171 Curl_http_setup_conn, /* setup_connection */
172 Curl_http, /* do_it */
173 Curl_http_done, /* done */
174 ZERO_NULL, /* do_more */
175 ZERO_NULL, /* connect_it */
176 ZERO_NULL, /* connecting */
177 ZERO_NULL, /* doing */
178 ZERO_NULL, /* proto_getsock */
179 ZERO_NULL, /* doing_getsock */
180 ZERO_NULL, /* domore_getsock */
181 ZERO_NULL, /* perform_getsock */
182 ZERO_NULL, /* disconnect */
183 ZERO_NULL, /* readwrite */
184 PORT_SMTP, /* defport */
185 CURLPROTO_HTTP, /* protocol */
186 PROTOPT_NONE /* flags */
191 * HTTP-proxyed SMTPS protocol handler.
194 static const struct Curl_handler Curl_handler_smtps_proxy = {
195 "SMTPS", /* scheme */
196 Curl_http_setup_conn, /* setup_connection */
197 Curl_http, /* do_it */
198 Curl_http_done, /* done */
199 ZERO_NULL, /* do_more */
200 ZERO_NULL, /* connect_it */
201 ZERO_NULL, /* connecting */
202 ZERO_NULL, /* doing */
203 ZERO_NULL, /* proto_getsock */
204 ZERO_NULL, /* doing_getsock */
205 ZERO_NULL, /* domore_getsock */
206 ZERO_NULL, /* perform_getsock */
207 ZERO_NULL, /* disconnect */
208 ZERO_NULL, /* readwrite */
209 PORT_SMTPS, /* defport */
210 CURLPROTO_HTTP, /* protocol */
211 PROTOPT_NONE /* flags */
217 static void smtp_to_smtps(struct connectdata *conn)
219 conn->handler = &Curl_handler_smtps;
222 #define smtp_to_smtps(x) Curl_nop_stmt
225 /***********************************************************************
229 * Checks for an ending SMTP status code at the start of the given string, but
230 * also detects various capabilities from the EHLO response including the
231 * supported authentication mechanisms.
233 static bool smtp_endofresp(struct connectdata *conn, char *line, size_t len,
236 struct smtp_conn *smtpc = &conn->proto.smtpc;
240 if(len < 4 || !ISDIGIT(line[0]) || !ISDIGIT(line[1]) || !ISDIGIT(line[2]))
243 /* Do we have a command response? This should be the response code followed
244 by a space and optionally some text as per RFC-5321 and as outlined in
245 Section 4. Examples of RFC-4954 but some e-mail servers ignore this and
246 only send the response code instead as per Section 4.2. */
247 if(line[3] == ' ' || len == 5) {
249 *resp = curlx_sltosi(strtol(line, NULL, 10));
251 /* Make sure real server never sends internal value */
255 /* Do we have a multiline (continuation) response? */
256 else if(line[3] == '-' &&
257 (smtpc->state == SMTP_EHLO || smtpc->state == SMTP_COMMAND)) {
259 *resp = 1; /* Internal response code */
265 /***********************************************************************
269 * Gets the authentication message from the response buffer.
271 static void smtp_get_message(char *buffer, char** outptr)
274 char* message = NULL;
276 /* Find the start of the message */
277 for(message = buffer + 4; *message == ' ' || *message == '\t'; message++)
280 /* Find the end of the message */
281 for(len = strlen(message); len--;)
282 if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' &&
283 message[len] != '\t')
286 /* Terminate the message */
294 /***********************************************************************
298 * This is the ONLY way to change SMTP state!
300 static void state(struct connectdata *conn, smtpstate newstate)
302 struct smtp_conn *smtpc = &conn->proto.smtpc;
303 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
304 /* for debug purposes */
305 static const char * const names[] = {
317 "AUTH_DIGESTMD5_RESP",
319 "AUTH_NTLM_TYPE2MSG",
322 "AUTH_GSSAPI_NO_DATA",
335 if(smtpc->state != newstate)
336 infof(conn->data, "SMTP %p state change from %s to %s\n",
337 (void *)smtpc, names[smtpc->state], names[newstate]);
340 smtpc->state = newstate;
343 /***********************************************************************
345 * smtp_perform_ehlo()
347 * Sends the EHLO command to not only initialise communication with the ESMTP
348 * server but to also obtain a list of server side supported capabilities.
350 static CURLcode smtp_perform_ehlo(struct connectdata *conn)
352 CURLcode result = CURLE_OK;
353 struct smtp_conn *smtpc = &conn->proto.smtpc;
355 smtpc->authmechs = 0; /* No known authentication mechanisms yet */
356 smtpc->authused = 0; /* Clear the authentication mechanism used
357 for esmtp connections */
358 smtpc->tls_supported = FALSE; /* Clear the TLS capability */
359 smtpc->auth_supported = FALSE; /* Clear the AUTH capability */
361 /* Send the EHLO command */
362 result = Curl_pp_sendf(&smtpc->pp, "EHLO %s", smtpc->domain);
365 state(conn, SMTP_EHLO);
370 /***********************************************************************
372 * smtp_perform_helo()
374 * Sends the HELO command to initialise communication with the SMTP server.
376 static CURLcode smtp_perform_helo(struct connectdata *conn)
378 CURLcode result = CURLE_OK;
379 struct smtp_conn *smtpc = &conn->proto.smtpc;
381 smtpc->authused = 0; /* No authentication mechanism used in smtp
384 /* Send the HELO command */
385 result = Curl_pp_sendf(&smtpc->pp, "HELO %s", smtpc->domain);
388 state(conn, SMTP_HELO);
393 /***********************************************************************
395 * smtp_perform_starttls()
397 * Sends the STLS command to start the upgrade to TLS.
399 static CURLcode smtp_perform_starttls(struct connectdata *conn)
401 CURLcode result = CURLE_OK;
403 /* Send the STARTTLS command */
404 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "STARTTLS");
407 state(conn, SMTP_STARTTLS);
412 /***********************************************************************
414 * smtp_perform_upgrade_tls()
416 * Performs the upgrade to TLS.
418 static CURLcode smtp_perform_upgrade_tls(struct connectdata *conn)
420 CURLcode result = CURLE_OK;
421 struct smtp_conn *smtpc = &conn->proto.smtpc;
423 /* Start the SSL connection */
424 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone);
427 if(smtpc->state != SMTP_UPGRADETLS)
428 state(conn, SMTP_UPGRADETLS);
432 result = smtp_perform_ehlo(conn);
439 /***********************************************************************
441 * smtp_perform_auth()
443 * Sends an AUTH command allowing the client to login with the given SASL
444 * authentication mechanism.
446 static CURLcode smtp_perform_auth(struct connectdata *conn,
448 const char *initresp, size_t len,
449 smtpstate state1, smtpstate state2)
451 CURLcode result = CURLE_OK;
452 struct smtp_conn *smtpc = &conn->proto.smtpc;
454 if(initresp && 8 + strlen(mech) + len <= 512) { /* AUTH <mech> ...<crlf> */
455 /* Send the AUTH command with the initial response */
456 result = Curl_pp_sendf(&smtpc->pp, "AUTH %s %s", mech, initresp);
462 /* Send the AUTH command */
463 result = Curl_pp_sendf(&smtpc->pp, "AUTH %s", mech);
472 /***********************************************************************
474 * smtp_perform_authentication()
476 * Initiates the authentication sequence, with the appropriate SASL
477 * authentication mechanism.
479 static CURLcode smtp_perform_authentication(struct connectdata *conn)
481 CURLcode result = CURLE_OK;
482 struct smtp_conn *smtpc = &conn->proto.smtpc;
483 const char *mech = NULL;
484 char *initresp = NULL;
486 smtpstate state1 = SMTP_STOP;
487 smtpstate state2 = SMTP_STOP;
489 /* Check we have a username and password to authenticate with, and the
490 server supports authentiation, and end the connect phase if not */
491 if(!conn->bits.user_passwd || !smtpc->auth_supported) {
492 state(conn, SMTP_STOP);
497 /* Calculate the SASL login details */
498 result = smtp_calc_sasl_details(conn, &mech, &initresp, &len, &state1,
503 /* Perform SASL based authentication */
504 result = smtp_perform_auth(conn, mech, initresp, len, state1, state2);
506 Curl_safefree(initresp);
509 /* Other mechanisms not supported */
510 infof(conn->data, "No known authentication mechanisms supported!\n");
511 result = CURLE_LOGIN_DENIED;
518 /***********************************************************************
520 * smtp_perform_command()
522 * Sends a SMTP based command.
524 static CURLcode smtp_perform_command(struct connectdata *conn)
526 CURLcode result = CURLE_OK;
527 struct SessionHandle *data = conn->data;
528 struct SMTP *smtp = data->req.protop;
530 /* Send the command */
532 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s %s",
533 smtp->custom && smtp->custom[0] != '\0' ?
534 smtp->custom : "VRFY",
537 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s",
538 smtp->custom && smtp->custom[0] != '\0' ?
539 smtp->custom : "HELP");
542 state(conn, SMTP_COMMAND);
547 /***********************************************************************
549 * smtp_perform_mail()
551 * Sends an MAIL command to initiate the upload of a message.
553 static CURLcode smtp_perform_mail(struct connectdata *conn)
558 CURLcode result = CURLE_OK;
559 struct SessionHandle *data = conn->data;
561 /* Calculate the FROM parameter */
562 if(!data->set.str[STRING_MAIL_FROM])
563 /* Null reverse-path, RFC-5321, sect. 3.6.3 */
565 else if(data->set.str[STRING_MAIL_FROM][0] == '<')
566 from = aprintf("%s", data->set.str[STRING_MAIL_FROM]);
568 from = aprintf("<%s>", data->set.str[STRING_MAIL_FROM]);
571 return CURLE_OUT_OF_MEMORY;
573 /* Calculate the optional AUTH parameter */
574 if(data->set.str[STRING_MAIL_AUTH] && conn->proto.smtpc.authused) {
575 if(data->set.str[STRING_MAIL_AUTH][0] != '\0')
576 auth = aprintf("%s", data->set.str[STRING_MAIL_AUTH]);
578 /* Empty AUTH, RFC-2554, sect. 5 */
584 return CURLE_OUT_OF_MEMORY;
588 /* Calculate the optional SIZE parameter */
589 if(conn->proto.smtpc.size_supported && conn->data->state.infilesize > 0) {
590 size = aprintf("%" CURL_FORMAT_CURL_OFF_T, data->state.infilesize);
596 return CURLE_OUT_OF_MEMORY;
600 /* Send the MAIL command */
602 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
603 "MAIL FROM:%s", from);
604 else if(auth && !size)
605 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
606 "MAIL FROM:%s AUTH=%s", from, auth);
607 else if(auth && size)
608 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
609 "MAIL FROM:%s AUTH=%s SIZE=%s", from, auth, size);
611 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
612 "MAIL FROM:%s SIZE=%s", from, size);
619 state(conn, SMTP_MAIL);
624 /***********************************************************************
626 * smtp_perform_rcpt_to()
628 * Sends a RCPT TO command for a given recipient as part of the message upload
631 static CURLcode smtp_perform_rcpt_to(struct connectdata *conn)
633 CURLcode result = CURLE_OK;
634 struct SessionHandle *data = conn->data;
635 struct SMTP *smtp = data->req.protop;
637 /* Send the RCPT TO command */
638 if(smtp->rcpt->data[0] == '<')
639 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:%s",
642 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:<%s>",
645 state(conn, SMTP_RCPT);
650 /***********************************************************************
652 * smtp_perform_quit()
654 * Performs the quit action prior to sclose() being called.
656 static CURLcode smtp_perform_quit(struct connectdata *conn)
658 CURLcode result = CURLE_OK;
660 /* Send the QUIT command */
661 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "QUIT");
664 state(conn, SMTP_QUIT);
669 /* For the initial server greeting */
670 static CURLcode smtp_state_servergreet_resp(struct connectdata *conn,
674 CURLcode result = CURLE_OK;
675 struct SessionHandle *data = conn->data;
677 (void)instate; /* no use for this yet */
679 if(smtpcode/100 != 2) {
680 failf(data, "Got unexpected smtp-server response: %d", smtpcode);
681 result = CURLE_FTP_WEIRD_SERVER_REPLY;
684 result = smtp_perform_ehlo(conn);
689 /* For STARTTLS responses */
690 static CURLcode smtp_state_starttls_resp(struct connectdata *conn,
694 CURLcode result = CURLE_OK;
695 struct SessionHandle *data = conn->data;
697 (void)instate; /* no use for this yet */
699 if(smtpcode != 220) {
700 if(data->set.use_ssl != CURLUSESSL_TRY) {
701 failf(data, "STARTTLS denied. %c", smtpcode);
702 result = CURLE_USE_SSL_FAILED;
705 result = smtp_perform_authentication(conn);
708 result = smtp_perform_upgrade_tls(conn);
713 /* For EHLO responses */
714 static CURLcode smtp_state_ehlo_resp(struct connectdata *conn, int smtpcode,
717 CURLcode result = CURLE_OK;
718 struct SessionHandle *data = conn->data;
719 struct smtp_conn *smtpc = &conn->proto.smtpc;
720 const char *line = data->state.buffer;
721 size_t len = strlen(line);
724 (void)instate; /* no use for this yet */
726 if(smtpcode/100 != 2 && smtpcode != 1) {
727 if(data->set.use_ssl <= CURLUSESSL_TRY || conn->ssl[FIRSTSOCKET].use)
728 result = smtp_perform_helo(conn);
730 failf(data, "Remote access denied: %d", smtpcode);
731 result = CURLE_REMOTE_ACCESS_DENIED;
738 /* Does the server support the STARTTLS capability? */
739 if(len >= 8 && !memcmp(line, "STARTTLS", 8))
740 smtpc->tls_supported = TRUE;
742 /* Does the server support the SIZE capability? */
743 else if(len >= 4 && !memcmp(line, "SIZE", 4))
744 smtpc->size_supported = TRUE;
746 /* Does the server support authentication? */
747 else if(len >= 5 && !memcmp(line, "AUTH ", 5)) {
748 smtpc->auth_supported = TRUE;
750 /* Advance past the AUTH keyword */
754 /* Loop through the data line */
757 (*line == ' ' || *line == '\t' ||
758 *line == '\r' || *line == '\n')) {
767 /* Extract the word */
768 for(wordlen = 0; wordlen < len && line[wordlen] != ' ' &&
769 line[wordlen] != '\t' && line[wordlen] != '\r' &&
770 line[wordlen] != '\n';)
773 /* Test the word for a matching authentication mechanism */
774 if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_LOGIN))
775 smtpc->authmechs |= SASL_MECH_LOGIN;
776 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_PLAIN))
777 smtpc->authmechs |= SASL_MECH_PLAIN;
778 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_CRAM_MD5))
779 smtpc->authmechs |= SASL_MECH_CRAM_MD5;
780 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_DIGEST_MD5))
781 smtpc->authmechs |= SASL_MECH_DIGEST_MD5;
782 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_GSSAPI))
783 smtpc->authmechs |= SASL_MECH_GSSAPI;
784 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_EXTERNAL))
785 smtpc->authmechs |= SASL_MECH_EXTERNAL;
786 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_NTLM))
787 smtpc->authmechs |= SASL_MECH_NTLM;
788 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_XOAUTH2))
789 smtpc->authmechs |= SASL_MECH_XOAUTH2;
797 if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
798 /* We don't have a SSL/TLS connection yet, but SSL is requested */
799 if(smtpc->tls_supported)
800 /* Switch to TLS connection now */
801 result = smtp_perform_starttls(conn);
802 else if(data->set.use_ssl == CURLUSESSL_TRY)
803 /* Fallback and carry on with authentication */
804 result = smtp_perform_authentication(conn);
806 failf(data, "STARTTLS not supported.");
807 result = CURLE_USE_SSL_FAILED;
811 result = smtp_perform_authentication(conn);
818 /* For HELO responses */
819 static CURLcode smtp_state_helo_resp(struct connectdata *conn, int smtpcode,
822 CURLcode result = CURLE_OK;
823 struct SessionHandle *data = conn->data;
825 (void)instate; /* no use for this yet */
827 if(smtpcode/100 != 2) {
828 failf(data, "Remote access denied: %d", smtpcode);
829 result = CURLE_REMOTE_ACCESS_DENIED;
832 /* End of connect phase */
833 state(conn, SMTP_STOP);
838 /* For AUTH PLAIN (without initial response) responses */
839 static CURLcode smtp_state_auth_plain_resp(struct connectdata *conn,
843 CURLcode result = CURLE_OK;
844 struct SessionHandle *data = conn->data;
846 char *plainauth = NULL;
848 (void)instate; /* no use for this yet */
850 if(smtpcode != 334) {
851 failf(data, "Access denied: %d", smtpcode);
852 result = CURLE_LOGIN_DENIED;
855 /* Create the authorisation message */
856 result = Curl_sasl_create_plain_message(conn->data, conn->user,
857 conn->passwd, &plainauth, &len);
858 if(!result && plainauth) {
859 /* Send the message */
860 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", plainauth);
863 state(conn, SMTP_AUTH_FINAL);
867 Curl_safefree(plainauth);
872 /* For AUTH LOGIN (without initial response) responses */
873 static CURLcode smtp_state_auth_login_resp(struct connectdata *conn,
877 CURLcode result = CURLE_OK;
878 struct SessionHandle *data = conn->data;
880 char *authuser = NULL;
882 (void)instate; /* no use for this yet */
884 if(smtpcode != 334) {
885 failf(data, "Access denied: %d", smtpcode);
886 result = CURLE_LOGIN_DENIED;
889 /* Create the user message */
890 result = Curl_sasl_create_login_message(conn->data, conn->user,
892 if(!result && authuser) {
894 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", authuser);
897 state(conn, SMTP_AUTH_LOGIN_PASSWD);
901 Curl_safefree(authuser);
906 /* For AUTH LOGIN user entry responses */
907 static CURLcode smtp_state_auth_login_password_resp(struct connectdata *conn,
911 CURLcode result = CURLE_OK;
912 struct SessionHandle *data = conn->data;
914 char *authpasswd = NULL;
916 (void)instate; /* no use for this yet */
918 if(smtpcode != 334) {
919 failf(data, "Access denied: %d", smtpcode);
920 result = CURLE_LOGIN_DENIED;
923 /* Create the password message */
924 result = Curl_sasl_create_login_message(conn->data, conn->passwd,
926 if(!result && authpasswd) {
927 /* Send the password */
928 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", authpasswd);
931 state(conn, SMTP_AUTH_FINAL);
935 Curl_safefree(authpasswd);
940 #ifndef CURL_DISABLE_CRYPTO_AUTH
941 /* For AUTH CRAM-MD5 responses */
942 static CURLcode smtp_state_auth_cram_resp(struct connectdata *conn,
946 CURLcode result = CURLE_OK;
947 struct SessionHandle *data = conn->data;
950 char *rplyb64 = NULL;
953 (void)instate; /* no use for this yet */
955 if(smtpcode != 334) {
956 failf(data, "Access denied: %d", smtpcode);
957 return CURLE_LOGIN_DENIED;
960 /* Get the challenge message */
961 smtp_get_message(data->state.buffer, &chlg64);
963 /* Decode the challenge message */
964 result = Curl_sasl_decode_cram_md5_message(chlg64, &chlg, &len);
966 /* Send the cancellation */
967 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "*");
970 state(conn, SMTP_AUTH_CANCEL);
973 /* Create the response message */
974 result = Curl_sasl_create_cram_md5_message(data, chlg, conn->user,
975 conn->passwd, &rplyb64, &len);
976 if(!result && rplyb64) {
977 /* Send the response */
978 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", rplyb64);
981 state(conn, SMTP_AUTH_FINAL);
986 Curl_safefree(rplyb64);
991 /* For AUTH DIGEST-MD5 challenge responses */
992 static CURLcode smtp_state_auth_digest_resp(struct connectdata *conn,
996 CURLcode result = CURLE_OK;
997 struct SessionHandle *data = conn->data;
999 char *rplyb64 = NULL;
1002 (void)instate; /* no use for this yet */
1004 if(smtpcode != 334) {
1005 failf(data, "Access denied: %d", smtpcode);
1006 return CURLE_LOGIN_DENIED;
1009 /* Get the challenge message */
1010 smtp_get_message(data->state.buffer, &chlg64);
1012 /* Create the response message */
1013 result = Curl_sasl_create_digest_md5_message(data, chlg64,
1014 conn->user, conn->passwd,
1015 "smtp", &rplyb64, &len);
1017 if(result == CURLE_BAD_CONTENT_ENCODING) {
1018 /* Send the cancellation */
1019 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "*");
1022 state(conn, SMTP_AUTH_CANCEL);
1026 /* Send the response */
1027 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", rplyb64);
1030 state(conn, SMTP_AUTH_DIGESTMD5_RESP);
1033 Curl_safefree(rplyb64);
1038 /* For AUTH DIGEST-MD5 challenge-response responses */
1039 static CURLcode smtp_state_auth_digest_resp_resp(struct connectdata *conn,
1043 CURLcode result = CURLE_OK;
1044 struct SessionHandle *data = conn->data;
1046 (void)instate; /* no use for this yet */
1048 if(smtpcode != 334) {
1049 failf(data, "Authentication failed: %d", smtpcode);
1050 result = CURLE_LOGIN_DENIED;
1053 /* Send an empty response */
1054 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "");
1057 state(conn, SMTP_AUTH_FINAL);
1066 /* For AUTH NTLM (without initial response) responses */
1067 static CURLcode smtp_state_auth_ntlm_resp(struct connectdata *conn,
1071 CURLcode result = CURLE_OK;
1072 struct SessionHandle *data = conn->data;
1073 char *type1msg = NULL;
1076 (void)instate; /* no use for this yet */
1078 if(smtpcode != 334) {
1079 failf(data, "Access denied: %d", smtpcode);
1080 result = CURLE_LOGIN_DENIED;
1083 /* Create the type-1 message */
1084 result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
1087 if(!result && type1msg) {
1088 /* Send the message */
1089 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", type1msg);
1092 state(conn, SMTP_AUTH_NTLM_TYPE2MSG);
1096 Curl_safefree(type1msg);
1101 /* For NTLM type-2 responses (sent in reponse to our type-1 message) */
1102 static CURLcode smtp_state_auth_ntlm_type2msg_resp(struct connectdata *conn,
1106 CURLcode result = CURLE_OK;
1107 struct SessionHandle *data = conn->data;
1108 char *type2msg = NULL;
1109 char *type3msg = NULL;
1112 (void)instate; /* no use for this yet */
1114 if(smtpcode != 334) {
1115 failf(data, "Access denied: %d", smtpcode);
1116 result = CURLE_LOGIN_DENIED;
1119 /* Get the type-2 message */
1120 smtp_get_message(data->state.buffer, &type2msg);
1122 /* Decode the type-2 message */
1123 result = Curl_sasl_decode_ntlm_type2_message(data, type2msg, &conn->ntlm);
1125 /* Send the cancellation */
1126 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "*");
1129 state(conn, SMTP_AUTH_CANCEL);
1132 /* Create the type-3 message */
1133 result = Curl_sasl_create_ntlm_type3_message(data, conn->user,
1134 conn->passwd, &conn->ntlm,
1136 if(!result && type3msg) {
1137 /* Send the message */
1138 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", type3msg);
1141 state(conn, SMTP_AUTH_FINAL);
1146 Curl_safefree(type3msg);
1152 #if defined(USE_KERBEROS5)
1153 /* For AUTH GSSAPI (without initial response) responses */
1154 static CURLcode smtp_state_auth_gssapi_resp(struct connectdata *conn,
1158 CURLcode result = CURLE_OK;
1159 struct SessionHandle *data = conn->data;
1160 struct smtp_conn *smtpc = &conn->proto.smtpc;
1161 char *respmsg = NULL;
1164 (void)instate; /* no use for this yet */
1166 if(smtpcode != 334) {
1167 failf(data, "Access denied: %d", smtpcode);
1168 result = CURLE_LOGIN_DENIED;
1171 /* Create the initial response message */
1172 result = Curl_sasl_create_gssapi_user_message(data, conn->user,
1173 conn->passwd, "smtp",
1174 smtpc->mutual_auth, NULL,
1177 if(!result && respmsg) {
1178 /* Send the message */
1179 result = Curl_pp_sendf(&smtpc->pp, "%s", respmsg);
1182 state(conn, SMTP_AUTH_GSSAPI_TOKEN);
1186 Curl_safefree(respmsg);
1191 /* For AUTH GSSAPI user token responses */
1192 static CURLcode smtp_state_auth_gssapi_token_resp(struct connectdata *conn,
1196 CURLcode result = CURLE_OK;
1197 struct SessionHandle *data = conn->data;
1198 struct smtp_conn *smtpc = &conn->proto.smtpc;
1199 char *chlgmsg = NULL;
1200 char *respmsg = NULL;
1203 (void)instate; /* no use for this yet */
1205 if(smtpcode != 334) {
1206 failf(data, "Access denied: %d", smtpcode);
1207 result = CURLE_LOGIN_DENIED;
1210 /* Get the challenge message */
1211 smtp_get_message(data->state.buffer, &chlgmsg);
1213 if(smtpc->mutual_auth)
1214 /* Decode the user token challenge and create the optional response
1216 result = Curl_sasl_create_gssapi_user_message(data, NULL, NULL, NULL,
1218 chlgmsg, &conn->krb5,
1221 /* Decode the security challenge and create the response message */
1222 result = Curl_sasl_create_gssapi_security_message(data, chlgmsg,
1227 if(result == CURLE_BAD_CONTENT_ENCODING) {
1228 /* Send the cancellation */
1229 result = Curl_pp_sendf(&smtpc->pp, "%s", "*");
1232 state(conn, SMTP_AUTH_CANCEL);
1236 /* Send the response */
1238 result = Curl_pp_sendf(&smtpc->pp, "%s", respmsg);
1240 result = Curl_pp_sendf(&smtpc->pp, "%s", "");
1243 state(conn, (smtpc->mutual_auth ? SMTP_AUTH_GSSAPI_NO_DATA :
1248 Curl_safefree(respmsg);
1253 /* For AUTH GSSAPI no data responses */
1254 static CURLcode smtp_state_auth_gssapi_no_data_resp(struct connectdata *conn,
1258 CURLcode result = CURLE_OK;
1259 struct SessionHandle *data = conn->data;
1260 char *chlgmsg = NULL;
1261 char *respmsg = NULL;
1264 (void)instate; /* no use for this yet */
1266 if(smtpcode != 334) {
1267 failf(data, "Access denied: %d", smtpcode);
1268 result = CURLE_LOGIN_DENIED;
1271 /* Get the challenge message */
1272 smtp_get_message(data->state.buffer, &chlgmsg);
1274 /* Decode the security challenge and create the response message */
1275 result = Curl_sasl_create_gssapi_security_message(data, chlgmsg,
1279 if(result == CURLE_BAD_CONTENT_ENCODING) {
1280 /* Send the cancellation */
1281 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "*");
1284 state(conn, SMTP_AUTH_CANCEL);
1288 /* Send the response */
1290 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", respmsg);
1293 state(conn, SMTP_AUTH_FINAL);
1298 Curl_safefree(respmsg);
1304 /* For AUTH XOAUTH2 (without initial response) responses */
1305 static CURLcode smtp_state_auth_xoauth2_resp(struct connectdata *conn,
1306 int smtpcode, smtpstate instate)
1308 CURLcode result = CURLE_OK;
1309 struct SessionHandle *data = conn->data;
1311 char *xoauth = NULL;
1313 (void)instate; /* no use for this yet */
1315 if(smtpcode != 334) {
1316 failf(data, "Access denied: %d", smtpcode);
1317 result = CURLE_LOGIN_DENIED;
1320 /* Create the authorisation message */
1321 result = Curl_sasl_create_xoauth2_message(conn->data, conn->user,
1322 conn->xoauth2_bearer,
1324 if(!result && xoauth) {
1325 /* Send the message */
1326 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", xoauth);
1329 state(conn, SMTP_AUTH_FINAL);
1333 Curl_safefree(xoauth);
1338 /* For AUTH cancellation responses */
1339 static CURLcode smtp_state_auth_cancel_resp(struct connectdata *conn,
1343 CURLcode result = CURLE_OK;
1344 struct SessionHandle *data = conn->data;
1345 struct smtp_conn *smtpc = &conn->proto.smtpc;
1346 const char *mech = NULL;
1347 char *initresp = NULL;
1349 smtpstate state1 = SMTP_STOP;
1350 smtpstate state2 = SMTP_STOP;
1353 (void)instate; /* no use for this yet */
1355 /* Remove the offending mechanism from the supported list */
1356 smtpc->authmechs ^= smtpc->authused;
1358 /* Calculate alternative SASL login details */
1359 result = smtp_calc_sasl_details(conn, &mech, &initresp, &len, &state1,
1363 /* Do we have any mechanisms left? */
1365 /* Retry SASL based authentication */
1366 result = smtp_perform_auth(conn, mech, initresp, len, state1, state2);
1368 Curl_safefree(initresp);
1371 failf(data, "Authentication cancelled");
1373 result = CURLE_LOGIN_DENIED;
1380 /* For final responses in the AUTH sequence */
1381 static CURLcode smtp_state_auth_final_resp(struct connectdata *conn,
1385 CURLcode result = CURLE_OK;
1386 struct SessionHandle *data = conn->data;
1388 (void)instate; /* no use for this yet */
1390 if(smtpcode != 235) {
1391 failf(data, "Authentication failed: %d", smtpcode);
1392 result = CURLE_LOGIN_DENIED;
1395 /* End of connect phase */
1396 state(conn, SMTP_STOP);
1401 /* For command responses */
1402 static CURLcode smtp_state_command_resp(struct connectdata *conn, int smtpcode,
1405 CURLcode result = CURLE_OK;
1406 struct SessionHandle *data = conn->data;
1407 struct SMTP *smtp = data->req.protop;
1408 char *line = data->state.buffer;
1409 size_t len = strlen(line);
1411 (void)instate; /* no use for this yet */
1413 if((smtp->rcpt && smtpcode/100 != 2 && smtpcode != 553 && smtpcode != 1) ||
1414 (!smtp->rcpt && smtpcode/100 != 2 && smtpcode != 1)) {
1415 failf(data, "Command failed: %d", smtpcode);
1416 result = CURLE_RECV_ERROR;
1419 /* Temporarily add the LF character back and send as body to the client */
1420 if(!data->set.opt_no_body) {
1422 result = Curl_client_write(conn, CLIENTWRITE_BODY, line, len + 1);
1428 smtp->rcpt = smtp->rcpt->next;
1431 /* Send the next command */
1432 result = smtp_perform_command(conn);
1435 /* End of DO phase */
1436 state(conn, SMTP_STOP);
1439 /* End of DO phase */
1440 state(conn, SMTP_STOP);
1447 /* For MAIL responses */
1448 static CURLcode smtp_state_mail_resp(struct connectdata *conn, int smtpcode,
1451 CURLcode result = CURLE_OK;
1452 struct SessionHandle *data = conn->data;
1454 (void)instate; /* no use for this yet */
1456 if(smtpcode/100 != 2) {
1457 failf(data, "MAIL failed: %d", smtpcode);
1458 result = CURLE_SEND_ERROR;
1461 /* Start the RCPT TO command */
1462 result = smtp_perform_rcpt_to(conn);
1467 /* For RCPT responses */
1468 static CURLcode smtp_state_rcpt_resp(struct connectdata *conn, int smtpcode,
1471 CURLcode result = CURLE_OK;
1472 struct SessionHandle *data = conn->data;
1473 struct SMTP *smtp = data->req.protop;
1475 (void)instate; /* no use for this yet */
1477 if(smtpcode/100 != 2) {
1478 failf(data, "RCPT failed: %d", smtpcode);
1479 result = CURLE_SEND_ERROR;
1482 smtp->rcpt = smtp->rcpt->next;
1485 /* Send the next RCPT TO command */
1486 result = smtp_perform_rcpt_to(conn);
1488 /* Send the DATA command */
1489 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "DATA");
1492 state(conn, SMTP_DATA);
1499 /* For DATA response */
1500 static CURLcode smtp_state_data_resp(struct connectdata *conn, int smtpcode,
1503 CURLcode result = CURLE_OK;
1504 struct SessionHandle *data = conn->data;
1506 (void)instate; /* no use for this yet */
1508 if(smtpcode != 354) {
1509 failf(data, "DATA failed: %d", smtpcode);
1510 result = CURLE_SEND_ERROR;
1513 /* Set the progress upload size */
1514 Curl_pgrsSetUploadSize(data, data->state.infilesize);
1517 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL);
1519 /* End of DO phase */
1520 state(conn, SMTP_STOP);
1526 /* For POSTDATA responses, which are received after the entire DATA
1527 part has been sent to the server */
1528 static CURLcode smtp_state_postdata_resp(struct connectdata *conn,
1532 CURLcode result = CURLE_OK;
1534 (void)instate; /* no use for this yet */
1537 result = CURLE_RECV_ERROR;
1539 /* End of DONE phase */
1540 state(conn, SMTP_STOP);
1545 static CURLcode smtp_statemach_act(struct connectdata *conn)
1547 CURLcode result = CURLE_OK;
1548 curl_socket_t sock = conn->sock[FIRSTSOCKET];
1549 struct SessionHandle *data = conn->data;
1551 struct smtp_conn *smtpc = &conn->proto.smtpc;
1552 struct pingpong *pp = &smtpc->pp;
1555 /* Busy upgrading the connection; right now all I/O is SSL/TLS, not SMTP */
1556 if(smtpc->state == SMTP_UPGRADETLS)
1557 return smtp_perform_upgrade_tls(conn);
1559 /* Flush any data that needs to be sent */
1561 return Curl_pp_flushsend(pp);
1564 /* Read the response from the server */
1565 result = Curl_pp_readresp(sock, pp, &smtpcode, &nread);
1569 /* Store the latest response for later retrieval if necessary */
1570 if(smtpc->state != SMTP_QUIT && smtpcode != 1)
1571 data->info.httpcode = smtpcode;
1576 /* We have now received a full SMTP server response */
1577 switch(smtpc->state) {
1578 case SMTP_SERVERGREET:
1579 result = smtp_state_servergreet_resp(conn, smtpcode, smtpc->state);
1583 result = smtp_state_ehlo_resp(conn, smtpcode, smtpc->state);
1587 result = smtp_state_helo_resp(conn, smtpcode, smtpc->state);
1591 result = smtp_state_starttls_resp(conn, smtpcode, smtpc->state);
1594 case SMTP_AUTH_PLAIN:
1595 result = smtp_state_auth_plain_resp(conn, smtpcode, smtpc->state);
1598 case SMTP_AUTH_LOGIN:
1599 result = smtp_state_auth_login_resp(conn, smtpcode, smtpc->state);
1602 case SMTP_AUTH_LOGIN_PASSWD:
1603 result = smtp_state_auth_login_password_resp(conn, smtpcode,
1607 #ifndef CURL_DISABLE_CRYPTO_AUTH
1608 case SMTP_AUTH_CRAMMD5:
1609 result = smtp_state_auth_cram_resp(conn, smtpcode, smtpc->state);
1612 case SMTP_AUTH_DIGESTMD5:
1613 result = smtp_state_auth_digest_resp(conn, smtpcode, smtpc->state);
1616 case SMTP_AUTH_DIGESTMD5_RESP:
1617 result = smtp_state_auth_digest_resp_resp(conn, smtpcode, smtpc->state);
1622 case SMTP_AUTH_NTLM:
1623 result = smtp_state_auth_ntlm_resp(conn, smtpcode, smtpc->state);
1626 case SMTP_AUTH_NTLM_TYPE2MSG:
1627 result = smtp_state_auth_ntlm_type2msg_resp(conn, smtpcode,
1632 #if defined(USE_KERBEROS5)
1633 case SMTP_AUTH_GSSAPI:
1634 result = smtp_state_auth_gssapi_resp(conn, smtpcode, smtpc->state);
1637 case SMTP_AUTH_GSSAPI_TOKEN:
1638 result = smtp_state_auth_gssapi_token_resp(conn, smtpcode, smtpc->state);
1641 case SMTP_AUTH_GSSAPI_NO_DATA:
1642 result = smtp_state_auth_gssapi_no_data_resp(conn, smtpcode,
1647 case SMTP_AUTH_XOAUTH2:
1648 result = smtp_state_auth_xoauth2_resp(conn, smtpcode, smtpc->state);
1651 case SMTP_AUTH_CANCEL:
1652 result = smtp_state_auth_cancel_resp(conn, smtpcode, smtpc->state);
1655 case SMTP_AUTH_FINAL:
1656 result = smtp_state_auth_final_resp(conn, smtpcode, smtpc->state);
1660 result = smtp_state_command_resp(conn, smtpcode, smtpc->state);
1664 result = smtp_state_mail_resp(conn, smtpcode, smtpc->state);
1668 result = smtp_state_rcpt_resp(conn, smtpcode, smtpc->state);
1672 result = smtp_state_data_resp(conn, smtpcode, smtpc->state);
1676 result = smtp_state_postdata_resp(conn, smtpcode, smtpc->state);
1680 /* fallthrough, just stop! */
1682 /* internal error */
1683 state(conn, SMTP_STOP);
1686 } while(!result && smtpc->state != SMTP_STOP && Curl_pp_moredata(pp));
1691 /* Called repeatedly until done from multi.c */
1692 static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done)
1694 CURLcode result = CURLE_OK;
1695 struct smtp_conn *smtpc = &conn->proto.smtpc;
1697 if((conn->handler->flags & PROTOPT_SSL) && !smtpc->ssldone) {
1698 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone);
1699 if(result || !smtpc->ssldone)
1703 result = Curl_pp_statemach(&smtpc->pp, FALSE);
1704 *done = (smtpc->state == SMTP_STOP) ? TRUE : FALSE;
1709 static CURLcode smtp_block_statemach(struct connectdata *conn)
1711 CURLcode result = CURLE_OK;
1712 struct smtp_conn *smtpc = &conn->proto.smtpc;
1714 while(smtpc->state != SMTP_STOP && !result)
1715 result = Curl_pp_statemach(&smtpc->pp, TRUE);
1720 /* Allocate and initialize the SMTP struct for the current SessionHandle if
1722 static CURLcode smtp_init(struct connectdata *conn)
1724 CURLcode result = CURLE_OK;
1725 struct SessionHandle *data = conn->data;
1728 smtp = data->req.protop = calloc(sizeof(struct SMTP), 1);
1730 result = CURLE_OUT_OF_MEMORY;
1735 /* For the SMTP "protocol connect" and "doing" phases only */
1736 static int smtp_getsock(struct connectdata *conn, curl_socket_t *socks,
1739 return Curl_pp_getsock(&conn->proto.smtpc.pp, socks, numsocks);
1742 /***********************************************************************
1746 * This function should do everything that is to be considered a part of
1747 * the connection phase.
1749 * The variable pointed to by 'done' will be TRUE if the protocol-layer
1750 * connect phase is done when this function returns, or FALSE if not.
1752 static CURLcode smtp_connect(struct connectdata *conn, bool *done)
1754 CURLcode result = CURLE_OK;
1755 struct smtp_conn *smtpc = &conn->proto.smtpc;
1756 struct pingpong *pp = &smtpc->pp;
1758 *done = FALSE; /* default to not done yet */
1760 /* We always support persistent connections in SMTP */
1761 connkeep(conn, "SMTP default");
1763 /* Set the default response time-out */
1764 pp->response_time = RESP_TIMEOUT;
1765 pp->statemach_act = smtp_statemach_act;
1766 pp->endofresp = smtp_endofresp;
1769 /* Set the default preferred authentication mechanism */
1770 smtpc->prefmech = SASL_AUTH_ANY;
1772 /* Initialise the pingpong layer */
1775 /* Parse the URL options */
1776 result = smtp_parse_url_options(conn);
1780 /* Parse the URL path */
1781 result = smtp_parse_url_path(conn);
1785 /* Start off waiting for the server greeting response */
1786 state(conn, SMTP_SERVERGREET);
1788 result = smtp_multi_statemach(conn, done);
1793 /***********************************************************************
1797 * The DONE function. This does what needs to be done after a single DO has
1800 * Input argument is already checked for validity.
1802 static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
1805 CURLcode result = CURLE_OK;
1806 struct SessionHandle *data = conn->data;
1807 struct SMTP *smtp = data->req.protop;
1808 struct pingpong *pp = &conn->proto.smtpc.pp;
1811 ssize_t bytes_written;
1815 if(!smtp || !pp->conn)
1816 /* When the easy handle is removed from the multi interface while libcurl
1817 is still trying to resolve the host name, the SMTP struct is not yet
1818 initialized. However, the removal action calls Curl_done() which in
1819 turn calls this function, so we simply return success. */
1823 connclose(conn, "SMTP done with bad status"); /* marked for closure */
1824 result = status; /* use the already set error code */
1826 else if(!data->set.connect_only && data->set.upload && data->set.mail_rcpt) {
1827 /* Calculate the EOB taking into account any terminating CRLF from the
1828 previous line of the email or the CRLF of the DATA command when there
1829 is "no mail data". RFC-5321, sect. 4.1.1.4.
1831 Note: As some SSL backends, such as OpenSSL, will cause Curl_write() to
1832 fail when using a different pointer following a previous write, that
1833 returned CURLE_AGAIN, we duplicate the EOB now rather than when the
1834 bytes written doesn't equal len. */
1835 if(smtp->trailing_crlf || !conn->data->state.infilesize) {
1836 eob = strdup(SMTP_EOB + 2);
1837 len = SMTP_EOB_LEN - 2;
1840 eob = strdup(SMTP_EOB);
1845 return CURLE_OUT_OF_MEMORY;
1847 /* Send the end of block data */
1848 result = Curl_write(conn, conn->writesockfd, eob, len, &bytes_written);
1854 if(bytes_written != len) {
1855 /* The whole chunk was not sent so keep it around and adjust the
1856 pingpong structure accordingly */
1859 pp->sendleft = len - bytes_written;
1862 /* Successfully sent so adjust the response timeout relative to now */
1863 pp->response = Curl_tvnow();
1868 state(conn, SMTP_POSTDATA);
1870 /* Run the state-machine
1872 TODO: when the multi interface is used, this _really_ should be using
1873 the smtp_multi_statemach function but we have no general support for
1874 non-blocking DONE operations, not in the multi state machine and with
1875 Curl_done() invokes on several places in the code!
1877 result = smtp_block_statemach(conn);
1880 /* Cleanup our per-request based variables */
1881 Curl_safefree(smtp->custom);
1883 /* Clear the transfer mode for the next request */
1884 smtp->transfer = FTPTRANSFER_BODY;
1889 /***********************************************************************
1893 * This is the actual DO function for SMTP. Transfer a mail, send a command
1894 * or get some data according to the options previously setup.
1896 static CURLcode smtp_perform(struct connectdata *conn, bool *connected,
1899 /* This is SMTP and no proxy */
1900 CURLcode result = CURLE_OK;
1901 struct SessionHandle *data = conn->data;
1902 struct SMTP *smtp = data->req.protop;
1904 DEBUGF(infof(conn->data, "DO phase starts\n"));
1906 if(data->set.opt_no_body) {
1907 /* Requested no body means no transfer */
1908 smtp->transfer = FTPTRANSFER_INFO;
1911 *dophase_done = FALSE; /* not done yet */
1913 /* Store the first recipient (or NULL if not specified) */
1914 smtp->rcpt = data->set.mail_rcpt;
1916 /* Start the first command in the DO phase */
1917 if(data->set.upload && data->set.mail_rcpt)
1919 result = smtp_perform_mail(conn);
1921 /* SMTP based command (VRFY, EXPN, NOOP, RSET or HELP) */
1922 result = smtp_perform_command(conn);
1927 /* Run the state-machine */
1928 result = smtp_multi_statemach(conn, dophase_done);
1930 *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1933 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1938 /***********************************************************************
1942 * This function is registered as 'curl_do' function. It decodes the path
1943 * parts etc as a wrapper to the actual DO function (smtp_perform).
1945 * The input argument is already checked for validity.
1947 static CURLcode smtp_do(struct connectdata *conn, bool *done)
1949 CURLcode result = CURLE_OK;
1951 *done = FALSE; /* default to false */
1953 /* Parse the custom request */
1954 result = smtp_parse_custom_request(conn);
1958 result = smtp_regular_transfer(conn, done);
1963 /***********************************************************************
1967 * Disconnect from an SMTP server. Cleanup protocol-specific per-connection
1968 * resources. BLOCKING.
1970 static CURLcode smtp_disconnect(struct connectdata *conn, bool dead_connection)
1972 struct smtp_conn *smtpc = &conn->proto.smtpc;
1974 /* We cannot send quit unconditionally. If this connection is stale or
1975 bad in any way, sending quit and waiting around here will make the
1976 disconnect wait in vain and cause more problems than we need to. */
1978 /* The SMTP session may or may not have been allocated/setup at this
1980 if(!dead_connection && smtpc->pp.conn && smtpc->pp.conn->bits.protoconnstart)
1981 if(!smtp_perform_quit(conn))
1982 (void)smtp_block_statemach(conn); /* ignore errors on QUIT */
1984 /* Disconnect from the server */
1985 Curl_pp_disconnect(&smtpc->pp);
1987 /* Cleanup the SASL module */
1988 Curl_sasl_cleanup(conn, smtpc->authused);
1990 /* Cleanup our connection based variables */
1991 Curl_safefree(smtpc->domain);
1996 /* Call this when the DO phase has completed */
1997 static CURLcode smtp_dophase_done(struct connectdata *conn, bool connected)
1999 struct SMTP *smtp = conn->data->req.protop;
2003 if(smtp->transfer != FTPTRANSFER_BODY)
2004 /* no data to transfer */
2005 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
2010 /* Called from multi.c while DOing */
2011 static CURLcode smtp_doing(struct connectdata *conn, bool *dophase_done)
2013 CURLcode result = smtp_multi_statemach(conn, dophase_done);
2016 DEBUGF(infof(conn->data, "DO phase failed\n"));
2017 else if(*dophase_done) {
2018 result = smtp_dophase_done(conn, FALSE /* not connected */);
2020 DEBUGF(infof(conn->data, "DO phase is complete\n"));
2026 /***********************************************************************
2028 * smtp_regular_transfer()
2030 * The input argument is already checked for validity.
2032 * Performs all commands done before a regular transfer between a local and a
2035 static CURLcode smtp_regular_transfer(struct connectdata *conn,
2038 CURLcode result = CURLE_OK;
2039 bool connected = FALSE;
2040 struct SessionHandle *data = conn->data;
2042 /* Make sure size is unknown at this point */
2043 data->req.size = -1;
2045 /* Set the progress data */
2046 Curl_pgrsSetUploadCounter(data, 0);
2047 Curl_pgrsSetDownloadCounter(data, 0);
2048 Curl_pgrsSetUploadSize(data, -1);
2049 Curl_pgrsSetDownloadSize(data, -1);
2051 /* Carry out the perform */
2052 result = smtp_perform(conn, &connected, dophase_done);
2054 /* Perform post DO phase operations if necessary */
2055 if(!result && *dophase_done)
2056 result = smtp_dophase_done(conn, connected);
2061 static CURLcode smtp_setup_connection(struct connectdata *conn)
2063 struct SessionHandle *data = conn->data;
2066 if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
2067 /* Unless we have asked to tunnel SMTP operations through the proxy, we
2068 switch and use HTTP operations only */
2069 #ifndef CURL_DISABLE_HTTP
2070 if(conn->handler == &Curl_handler_smtp)
2071 conn->handler = &Curl_handler_smtp_proxy;
2074 conn->handler = &Curl_handler_smtps_proxy;
2076 failf(data, "SMTPS not supported!");
2077 return CURLE_UNSUPPORTED_PROTOCOL;
2080 /* set it up as a HTTP connection instead */
2081 return conn->handler->setup_connection(conn);
2084 failf(data, "SMTP over http proxy requires HTTP support built-in!");
2085 return CURLE_UNSUPPORTED_PROTOCOL;
2089 /* Initialise the SMTP layer */
2090 result = smtp_init(conn);
2094 data->state.path++; /* don't include the initial slash */
2099 /***********************************************************************
2101 * smtp_parse_url_options()
2103 * Parse the URL login options.
2105 static CURLcode smtp_parse_url_options(struct connectdata *conn)
2107 CURLcode result = CURLE_OK;
2108 struct smtp_conn *smtpc = &conn->proto.smtpc;
2109 const char *options = conn->options;
2110 const char *ptr = options;
2113 while(ptr && *ptr) {
2114 const char *key = ptr;
2116 while(*ptr && *ptr != '=')
2119 if(strnequal(key, "AUTH", 4)) {
2121 const char *value = ++ptr;
2125 smtpc->prefmech = SASL_AUTH_NONE;
2128 while(*ptr && *ptr != ';') {
2133 if(strnequal(value, "*", len))
2134 smtpc->prefmech = SASL_AUTH_ANY;
2135 else if(strnequal(value, SASL_MECH_STRING_LOGIN, len))
2136 smtpc->prefmech |= SASL_MECH_LOGIN;
2137 else if(strnequal(value, SASL_MECH_STRING_PLAIN, len))
2138 smtpc->prefmech |= SASL_MECH_PLAIN;
2139 else if(strnequal(value, SASL_MECH_STRING_CRAM_MD5, len))
2140 smtpc->prefmech |= SASL_MECH_CRAM_MD5;
2141 else if(strnequal(value, SASL_MECH_STRING_DIGEST_MD5, len))
2142 smtpc->prefmech |= SASL_MECH_DIGEST_MD5;
2143 else if(strnequal(value, SASL_MECH_STRING_GSSAPI, len))
2144 smtpc->prefmech |= SASL_MECH_GSSAPI;
2145 else if(strnequal(value, SASL_MECH_STRING_NTLM, len))
2146 smtpc->prefmech |= SASL_MECH_NTLM;
2147 else if(strnequal(value, SASL_MECH_STRING_XOAUTH2, len))
2148 smtpc->prefmech |= SASL_MECH_XOAUTH2;
2154 result = CURLE_URL_MALFORMAT;
2160 /***********************************************************************
2162 * smtp_parse_url_path()
2164 * Parse the URL path into separate path components.
2166 static CURLcode smtp_parse_url_path(struct connectdata *conn)
2168 /* The SMTP struct is already initialised in smtp_connect() */
2169 struct SessionHandle *data = conn->data;
2170 struct smtp_conn *smtpc = &conn->proto.smtpc;
2171 const char *path = data->state.path;
2172 char localhost[HOSTNAME_MAX + 1];
2174 /* Calculate the path if necessary */
2176 if(!Curl_gethostname(localhost, sizeof(localhost)))
2182 /* URL decode the path and use it as the domain in our EHLO */
2183 return Curl_urldecode(conn->data, path, 0, &smtpc->domain, NULL, TRUE);
2186 /***********************************************************************
2188 * smtp_parse_custom_request()
2190 * Parse the custom request.
2192 static CURLcode smtp_parse_custom_request(struct connectdata *conn)
2194 CURLcode result = CURLE_OK;
2195 struct SessionHandle *data = conn->data;
2196 struct SMTP *smtp = data->req.protop;
2197 const char *custom = data->set.str[STRING_CUSTOMREQUEST];
2199 /* URL decode the custom request */
2201 result = Curl_urldecode(data, custom, 0, &smtp->custom, NULL, TRUE);
2206 /***********************************************************************
2208 * smtp_calc_sasl_details()
2210 * Calculate the required login details for SASL authentication.
2212 static CURLcode smtp_calc_sasl_details(struct connectdata *conn,
2214 char **initresp, size_t *len,
2215 smtpstate *state1, smtpstate *state2)
2217 CURLcode result = CURLE_OK;
2218 struct SessionHandle *data = conn->data;
2219 struct smtp_conn *smtpc = &conn->proto.smtpc;
2221 /* Calculate the supported authentication mechanism, by decreasing order of
2222 security, as well as the initial response where appropriate */
2223 #if defined(USE_KERBEROS5)
2224 if((smtpc->authmechs & SASL_MECH_GSSAPI) &&
2225 (smtpc->prefmech & SASL_MECH_GSSAPI)) {
2226 smtpc->mutual_auth = FALSE; /* TODO: Calculate mutual authentication */
2228 *mech = SASL_MECH_STRING_GSSAPI;
2229 *state1 = SMTP_AUTH_GSSAPI;
2230 *state2 = SMTP_AUTH_GSSAPI_TOKEN;
2231 smtpc->authused = SASL_MECH_GSSAPI;
2233 if(data->set.sasl_ir)
2234 result = Curl_sasl_create_gssapi_user_message(data, conn->user,
2235 conn->passwd, "smtp",
2242 #ifndef CURL_DISABLE_CRYPTO_AUTH
2243 if((smtpc->authmechs & SASL_MECH_DIGEST_MD5) &&
2244 (smtpc->prefmech & SASL_MECH_DIGEST_MD5)) {
2245 *mech = SASL_MECH_STRING_DIGEST_MD5;
2246 *state1 = SMTP_AUTH_DIGESTMD5;
2247 smtpc->authused = SASL_MECH_DIGEST_MD5;
2249 else if((smtpc->authmechs & SASL_MECH_CRAM_MD5) &&
2250 (smtpc->prefmech & SASL_MECH_CRAM_MD5)) {
2251 *mech = SASL_MECH_STRING_CRAM_MD5;
2252 *state1 = SMTP_AUTH_CRAMMD5;
2253 smtpc->authused = SASL_MECH_CRAM_MD5;
2258 if((smtpc->authmechs & SASL_MECH_NTLM) &&
2259 (smtpc->prefmech & SASL_MECH_NTLM)) {
2260 *mech = SASL_MECH_STRING_NTLM;
2261 *state1 = SMTP_AUTH_NTLM;
2262 *state2 = SMTP_AUTH_NTLM_TYPE2MSG;
2263 smtpc->authused = SASL_MECH_NTLM;
2265 if(data->set.sasl_ir)
2266 result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
2272 if(((smtpc->authmechs & SASL_MECH_XOAUTH2) &&
2273 (smtpc->prefmech & SASL_MECH_XOAUTH2) &&
2274 (smtpc->prefmech != SASL_AUTH_ANY)) || conn->xoauth2_bearer) {
2275 *mech = SASL_MECH_STRING_XOAUTH2;
2276 *state1 = SMTP_AUTH_XOAUTH2;
2277 *state2 = SMTP_AUTH_FINAL;
2278 smtpc->authused = SASL_MECH_XOAUTH2;
2280 if(data->set.sasl_ir)
2281 result = Curl_sasl_create_xoauth2_message(data, conn->user,
2282 conn->xoauth2_bearer,
2285 else if((smtpc->authmechs & SASL_MECH_LOGIN) &&
2286 (smtpc->prefmech & SASL_MECH_LOGIN)) {
2287 *mech = SASL_MECH_STRING_LOGIN;
2288 *state1 = SMTP_AUTH_LOGIN;
2289 *state2 = SMTP_AUTH_LOGIN_PASSWD;
2290 smtpc->authused = SASL_MECH_LOGIN;
2292 if(data->set.sasl_ir)
2293 result = Curl_sasl_create_login_message(data, conn->user, initresp, len);
2295 else if((smtpc->authmechs & SASL_MECH_PLAIN) &&
2296 (smtpc->prefmech & SASL_MECH_PLAIN)) {
2297 *mech = SASL_MECH_STRING_PLAIN;
2298 *state1 = SMTP_AUTH_PLAIN;
2299 *state2 = SMTP_AUTH_FINAL;
2300 smtpc->authused = SASL_MECH_PLAIN;
2302 if(data->set.sasl_ir)
2303 result = Curl_sasl_create_plain_message(data, conn->user, conn->passwd,
2310 CURLcode Curl_smtp_escape_eob(struct connectdata *conn, const ssize_t nread)
2312 /* When sending a SMTP payload we must detect CRLF. sequences making sure
2313 they are sent as CRLF.. instead, as a . on the beginning of a line will
2314 be deleted by the server when not part of an EOB terminator and a
2315 genuine CRLF.CRLF which isn't escaped will wrongly be detected as end of
2320 struct SessionHandle *data = conn->data;
2321 struct SMTP *smtp = data->req.protop;
2322 char *scratch = data->state.scratch;
2323 char *newscratch = NULL;
2324 char *oldscratch = NULL;
2327 /* Do we need to allocate a scratch buffer? */
2328 if(!scratch || data->set.crlf) {
2329 oldscratch = scratch;
2331 scratch = newscratch = malloc(2 * BUFSIZE);
2333 failf(data, "Failed to alloc scratch buffer!");
2335 return CURLE_OUT_OF_MEMORY;
2339 /* Have we already sent part of the EOB? */
2340 eob_sent = smtp->eob;
2342 /* This loop can be improved by some kind of Boyer-Moore style of
2343 approach but that is saved for later... */
2344 for(i = 0, si = 0; i < nread; i++) {
2345 if(SMTP_EOB[smtp->eob] == data->req.upload_fromhere[i]) {
2348 /* Is the EOB potentially the terminating CRLF? */
2349 if(2 == smtp->eob || SMTP_EOB_LEN == smtp->eob)
2350 smtp->trailing_crlf = TRUE;
2352 smtp->trailing_crlf = FALSE;
2354 else if(smtp->eob) {
2355 /* A previous substring matched so output that first */
2356 memcpy(&scratch[si], &SMTP_EOB[eob_sent], smtp->eob - eob_sent);
2357 si += smtp->eob - eob_sent;
2359 /* Then compare the first byte */
2360 if(SMTP_EOB[0] == data->req.upload_fromhere[i])
2367 /* Reset the trailing CRLF flag as there was more data */
2368 smtp->trailing_crlf = FALSE;
2371 /* Do we have a match for CRLF. as per RFC-5321, sect. 4.5.2 */
2372 if(SMTP_EOB_FIND_LEN == smtp->eob) {
2373 /* Copy the replacement data to the target buffer */
2374 memcpy(&scratch[si], &SMTP_EOB_REPL[eob_sent],
2375 SMTP_EOB_REPL_LEN - eob_sent);
2376 si += SMTP_EOB_REPL_LEN - eob_sent;
2381 scratch[si++] = data->req.upload_fromhere[i];
2384 if(smtp->eob - eob_sent) {
2385 /* A substring matched before processing ended so output that now */
2386 memcpy(&scratch[si], &SMTP_EOB[eob_sent], smtp->eob - eob_sent);
2387 si += smtp->eob - eob_sent;
2390 /* Only use the new buffer if we replaced something */
2392 /* Upload from the new (replaced) buffer instead */
2393 data->req.upload_fromhere = scratch;
2395 /* Save the buffer so it can be freed later */
2396 data->state.scratch = scratch;
2398 /* Free the old scratch buffer */
2399 Curl_safefree(oldscratch);
2401 /* Set the new amount too */
2402 data->req.upload_present = si;
2405 Curl_safefree(newscratch);
2410 #endif /* CURL_DISABLE_SMTP */