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 * RFC4954 SMTP Authentication
28 * RFC5321 SMTP protocol
29 * RFC6749 OAuth 2.0 Authorization Framework
30 * Draft SMTP URL Interface <draft-earhart-url-smtp-00.txt>
31 * Draft LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt>
33 ***************************************************************************/
35 #include "curl_setup.h"
37 #ifndef CURL_DISABLE_SMTP
39 #ifdef HAVE_NETINET_IN_H
40 #include <netinet/in.h>
42 #ifdef HAVE_ARPA_INET_H
43 #include <arpa/inet.h>
46 #include <sys/utsname.h>
56 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
58 #define in_addr_t unsigned long
61 #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",
332 if(smtpc->state != newstate)
333 infof(conn->data, "SMTP %p state change from %s to %s\n",
334 (void *)smtpc, names[smtpc->state], names[newstate]);
337 smtpc->state = newstate;
340 /***********************************************************************
342 * smtp_perform_ehlo()
344 * Sends the EHLO command to not only initialise communication with the ESMTP
345 * server but to also obtain a list of server side supported capabilities.
347 static CURLcode smtp_perform_ehlo(struct connectdata *conn)
349 CURLcode result = CURLE_OK;
350 struct smtp_conn *smtpc = &conn->proto.smtpc;
352 smtpc->authmechs = 0; /* No known authentication mechanisms yet */
353 smtpc->authused = 0; /* Clear the authentication mechanism used
354 for esmtp connections */
355 smtpc->tls_supported = FALSE; /* Clear the TLS capability */
356 smtpc->auth_supported = FALSE; /* Clear the AUTH capability */
358 /* Send the EHLO command */
359 result = Curl_pp_sendf(&smtpc->pp, "EHLO %s", smtpc->domain);
362 state(conn, SMTP_EHLO);
367 /***********************************************************************
369 * smtp_perform_helo()
371 * Sends the HELO command to initialise communication with the SMTP server.
373 static CURLcode smtp_perform_helo(struct connectdata *conn)
375 CURLcode result = CURLE_OK;
376 struct smtp_conn *smtpc = &conn->proto.smtpc;
378 smtpc->authused = 0; /* No authentication mechanism used in smtp
381 /* Send the HELO command */
382 result = Curl_pp_sendf(&smtpc->pp, "HELO %s", smtpc->domain);
385 state(conn, SMTP_HELO);
390 /***********************************************************************
392 * smtp_perform_starttls()
394 * Sends the STLS command to start the upgrade to TLS.
396 static CURLcode smtp_perform_starttls(struct connectdata *conn)
398 CURLcode result = CURLE_OK;
400 /* Send the STARTTLS command */
401 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "STARTTLS");
404 state(conn, SMTP_STARTTLS);
409 /***********************************************************************
411 * smtp_perform_upgrade_tls()
413 * Performs the upgrade to TLS.
415 static CURLcode smtp_perform_upgrade_tls(struct connectdata *conn)
417 CURLcode result = CURLE_OK;
418 struct smtp_conn *smtpc = &conn->proto.smtpc;
420 /* Start the SSL connection */
421 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone);
424 if(smtpc->state != SMTP_UPGRADETLS)
425 state(conn, SMTP_UPGRADETLS);
429 result = smtp_perform_ehlo(conn);
436 /***********************************************************************
438 * smtp_perform_auth()
440 * Sends an AUTH command allowing the client to login with the given SASL
441 * authentication mechanism.
443 static CURLcode smtp_perform_auth(struct connectdata *conn,
445 const char *initresp, size_t len,
446 smtpstate state1, smtpstate state2)
448 CURLcode result = CURLE_OK;
449 struct smtp_conn *smtpc = &conn->proto.smtpc;
451 if(initresp && 8 + strlen(mech) + len <= 512) { /* AUTH <mech> ...<crlf> */
452 /* Send the AUTH command with the initial response */
453 result = Curl_pp_sendf(&smtpc->pp, "AUTH %s %s", mech, initresp);
459 /* Send the AUTH command */
460 result = Curl_pp_sendf(&smtpc->pp, "AUTH %s", mech);
469 /***********************************************************************
471 * smtp_perform_authentication()
473 * Initiates the authentication sequence, with the appropriate SASL
474 * authentication mechanism.
476 static CURLcode smtp_perform_authentication(struct connectdata *conn)
478 CURLcode result = CURLE_OK;
479 struct smtp_conn *smtpc = &conn->proto.smtpc;
480 const char *mech = NULL;
481 char *initresp = NULL;
483 smtpstate state1 = SMTP_STOP;
484 smtpstate state2 = SMTP_STOP;
486 /* Check we have a username and password to authenticate with, and the
487 server supports authentiation, and end the connect phase if not */
488 if(!conn->bits.user_passwd || !smtpc->auth_supported) {
489 state(conn, SMTP_STOP);
494 /* Calculate the SASL login details */
495 result = smtp_calc_sasl_details(conn, &mech, &initresp, &len, &state1,
500 /* Perform SASL based authentication */
501 result = smtp_perform_auth(conn, mech, initresp, len, state1, state2);
503 Curl_safefree(initresp);
506 /* Other mechanisms not supported */
507 infof(conn->data, "No known authentication mechanisms supported!\n");
508 result = CURLE_LOGIN_DENIED;
515 /***********************************************************************
517 * smtp_perform_command()
519 * Sends a SMTP based command.
521 static CURLcode smtp_perform_command(struct connectdata *conn)
523 CURLcode result = CURLE_OK;
524 struct SessionHandle *data = conn->data;
525 struct SMTP *smtp = data->req.protop;
527 /* Send the command */
529 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s %s",
530 smtp->custom && smtp->custom[0] != '\0' ?
531 smtp->custom : "VRFY",
534 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s",
535 smtp->custom && smtp->custom[0] != '\0' ?
536 smtp->custom : "HELP");
539 state(conn, SMTP_COMMAND);
544 /***********************************************************************
546 * smtp_perform_mail()
548 * Sends an MAIL command to initiate the upload of a message.
550 static CURLcode smtp_perform_mail(struct connectdata *conn)
555 CURLcode result = CURLE_OK;
556 struct SessionHandle *data = conn->data;
558 /* Calculate the FROM parameter */
559 if(!data->set.str[STRING_MAIL_FROM])
560 /* Null reverse-path, RFC-5321, sect. 3.6.3 */
562 else if(data->set.str[STRING_MAIL_FROM][0] == '<')
563 from = aprintf("%s", data->set.str[STRING_MAIL_FROM]);
565 from = aprintf("<%s>", data->set.str[STRING_MAIL_FROM]);
568 return CURLE_OUT_OF_MEMORY;
570 /* Calculate the optional AUTH parameter */
571 if(data->set.str[STRING_MAIL_AUTH] && conn->proto.smtpc.authused) {
572 if(data->set.str[STRING_MAIL_AUTH][0] != '\0')
573 auth = aprintf("%s", data->set.str[STRING_MAIL_AUTH]);
575 /* Empty AUTH, RFC-2554, sect. 5 */
581 return CURLE_OUT_OF_MEMORY;
585 /* Calculate the optional SIZE parameter */
586 if(conn->proto.smtpc.size_supported && conn->data->state.infilesize > 0) {
587 size = aprintf("%" CURL_FORMAT_CURL_OFF_T, data->state.infilesize);
593 return CURLE_OUT_OF_MEMORY;
597 /* Send the MAIL command */
599 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
600 "MAIL FROM:%s", from);
601 else if(auth && !size)
602 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
603 "MAIL FROM:%s AUTH=%s", from, auth);
604 else if(auth && size)
605 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
606 "MAIL FROM:%s AUTH=%s SIZE=%s", from, auth, size);
608 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
609 "MAIL FROM:%s SIZE=%s", from, size);
616 state(conn, SMTP_MAIL);
621 /***********************************************************************
623 * smtp_perform_rcpt_to()
625 * Sends a RCPT TO command for a given recipient as part of the message upload
628 static CURLcode smtp_perform_rcpt_to(struct connectdata *conn)
630 CURLcode result = CURLE_OK;
631 struct SessionHandle *data = conn->data;
632 struct SMTP *smtp = data->req.protop;
634 /* Send the RCPT TO command */
635 if(smtp->rcpt->data[0] == '<')
636 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:%s",
639 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:<%s>",
642 state(conn, SMTP_RCPT);
647 /***********************************************************************
649 * smtp_perform_quit()
651 * Performs the quit action prior to sclose() being called.
653 static CURLcode smtp_perform_quit(struct connectdata *conn)
655 CURLcode result = CURLE_OK;
657 /* Send the QUIT command */
658 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "QUIT");
661 state(conn, SMTP_QUIT);
666 /* For the initial server greeting */
667 static CURLcode smtp_state_servergreet_resp(struct connectdata *conn,
671 CURLcode result = CURLE_OK;
672 struct SessionHandle *data = conn->data;
674 (void)instate; /* no use for this yet */
676 if(smtpcode/100 != 2) {
677 failf(data, "Got unexpected smtp-server response: %d", smtpcode);
678 result = CURLE_FTP_WEIRD_SERVER_REPLY;
681 result = smtp_perform_ehlo(conn);
686 /* For STARTTLS responses */
687 static CURLcode smtp_state_starttls_resp(struct connectdata *conn,
691 CURLcode result = CURLE_OK;
692 struct SessionHandle *data = conn->data;
694 (void)instate; /* no use for this yet */
696 if(smtpcode != 220) {
697 if(data->set.use_ssl != CURLUSESSL_TRY) {
698 failf(data, "STARTTLS denied. %c", smtpcode);
699 result = CURLE_USE_SSL_FAILED;
702 result = smtp_perform_authentication(conn);
705 result = smtp_perform_upgrade_tls(conn);
710 /* For EHLO responses */
711 static CURLcode smtp_state_ehlo_resp(struct connectdata *conn, int smtpcode,
714 CURLcode result = CURLE_OK;
715 struct SessionHandle *data = conn->data;
716 struct smtp_conn *smtpc = &conn->proto.smtpc;
717 const char *line = data->state.buffer;
718 size_t len = strlen(line);
721 (void)instate; /* no use for this yet */
723 if(smtpcode/100 != 2 && smtpcode != 1) {
724 if(data->set.use_ssl <= CURLUSESSL_TRY || conn->ssl[FIRSTSOCKET].use)
725 result = smtp_perform_helo(conn);
727 failf(data, "Remote access denied: %d", smtpcode);
728 result = CURLE_REMOTE_ACCESS_DENIED;
735 /* Does the server support the STARTTLS capability? */
736 if(len >= 8 && !memcmp(line, "STARTTLS", 8))
737 smtpc->tls_supported = TRUE;
739 /* Does the server support the SIZE capability? */
740 else if(len >= 4 && !memcmp(line, "SIZE", 4))
741 smtpc->size_supported = TRUE;
743 /* Does the server support authentication? */
744 else if(len >= 5 && !memcmp(line, "AUTH ", 5)) {
745 smtpc->auth_supported = TRUE;
747 /* Advance past the AUTH keyword */
751 /* Loop through the data line */
754 (*line == ' ' || *line == '\t' ||
755 *line == '\r' || *line == '\n')) {
764 /* Extract the word */
765 for(wordlen = 0; wordlen < len && line[wordlen] != ' ' &&
766 line[wordlen] != '\t' && line[wordlen] != '\r' &&
767 line[wordlen] != '\n';)
770 /* Test the word for a matching authentication mechanism */
771 if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_LOGIN))
772 smtpc->authmechs |= SASL_MECH_LOGIN;
773 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_PLAIN))
774 smtpc->authmechs |= SASL_MECH_PLAIN;
775 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_CRAM_MD5))
776 smtpc->authmechs |= SASL_MECH_CRAM_MD5;
777 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_DIGEST_MD5))
778 smtpc->authmechs |= SASL_MECH_DIGEST_MD5;
779 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_GSSAPI))
780 smtpc->authmechs |= SASL_MECH_GSSAPI;
781 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_EXTERNAL))
782 smtpc->authmechs |= SASL_MECH_EXTERNAL;
783 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_NTLM))
784 smtpc->authmechs |= SASL_MECH_NTLM;
785 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_XOAUTH2))
786 smtpc->authmechs |= SASL_MECH_XOAUTH2;
794 if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
795 /* We don't have a SSL/TLS connection yet, but SSL is requested */
796 if(smtpc->tls_supported)
797 /* Switch to TLS connection now */
798 result = smtp_perform_starttls(conn);
799 else if(data->set.use_ssl == CURLUSESSL_TRY)
800 /* Fallback and carry on with authentication */
801 result = smtp_perform_authentication(conn);
803 failf(data, "STARTTLS not supported.");
804 result = CURLE_USE_SSL_FAILED;
808 result = smtp_perform_authentication(conn);
815 /* For HELO responses */
816 static CURLcode smtp_state_helo_resp(struct connectdata *conn, int smtpcode,
819 CURLcode result = CURLE_OK;
820 struct SessionHandle *data = conn->data;
822 (void)instate; /* no use for this yet */
824 if(smtpcode/100 != 2) {
825 failf(data, "Remote access denied: %d", smtpcode);
826 result = CURLE_REMOTE_ACCESS_DENIED;
829 /* End of connect phase */
830 state(conn, SMTP_STOP);
835 /* For AUTH PLAIN (without initial response) responses */
836 static CURLcode smtp_state_auth_plain_resp(struct connectdata *conn,
840 CURLcode result = CURLE_OK;
841 struct SessionHandle *data = conn->data;
843 char *plainauth = NULL;
845 (void)instate; /* no use for this yet */
847 if(smtpcode != 334) {
848 failf(data, "Access denied: %d", smtpcode);
849 result = CURLE_LOGIN_DENIED;
852 /* Create the authorisation message */
853 result = Curl_sasl_create_plain_message(conn->data, conn->user,
854 conn->passwd, &plainauth, &len);
855 if(!result && plainauth) {
856 /* Send the message */
857 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", plainauth);
860 state(conn, SMTP_AUTH_FINAL);
864 Curl_safefree(plainauth);
869 /* For AUTH LOGIN (without initial response) responses */
870 static CURLcode smtp_state_auth_login_resp(struct connectdata *conn,
874 CURLcode result = CURLE_OK;
875 struct SessionHandle *data = conn->data;
877 char *authuser = NULL;
879 (void)instate; /* no use for this yet */
881 if(smtpcode != 334) {
882 failf(data, "Access denied: %d", smtpcode);
883 result = CURLE_LOGIN_DENIED;
886 /* Create the user message */
887 result = Curl_sasl_create_login_message(conn->data, conn->user,
889 if(!result && authuser) {
891 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", authuser);
894 state(conn, SMTP_AUTH_LOGIN_PASSWD);
898 Curl_safefree(authuser);
903 /* For AUTH LOGIN user entry responses */
904 static CURLcode smtp_state_auth_login_password_resp(struct connectdata *conn,
908 CURLcode result = CURLE_OK;
909 struct SessionHandle *data = conn->data;
911 char *authpasswd = NULL;
913 (void)instate; /* no use for this yet */
915 if(smtpcode != 334) {
916 failf(data, "Access denied: %d", smtpcode);
917 result = CURLE_LOGIN_DENIED;
920 /* Create the password message */
921 result = Curl_sasl_create_login_message(conn->data, conn->passwd,
923 if(!result && authpasswd) {
924 /* Send the password */
925 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", authpasswd);
928 state(conn, SMTP_AUTH_FINAL);
932 Curl_safefree(authpasswd);
937 #ifndef CURL_DISABLE_CRYPTO_AUTH
938 /* For AUTH CRAM-MD5 responses */
939 static CURLcode smtp_state_auth_cram_resp(struct connectdata *conn,
943 CURLcode result = CURLE_OK;
944 struct SessionHandle *data = conn->data;
947 char *rplyb64 = NULL;
950 (void)instate; /* no use for this yet */
952 if(smtpcode != 334) {
953 failf(data, "Access denied: %d", smtpcode);
954 return CURLE_LOGIN_DENIED;
957 /* Get the challenge message */
958 smtp_get_message(data->state.buffer, &chlg64);
960 /* Decode the challenge message */
961 result = Curl_sasl_decode_cram_md5_message(chlg64, &chlg, &len);
963 /* Send the cancellation */
964 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "*");
967 state(conn, SMTP_AUTH_CANCEL);
970 /* Create the response message */
971 result = Curl_sasl_create_cram_md5_message(data, chlg, conn->user,
972 conn->passwd, &rplyb64, &len);
973 if(!result && rplyb64) {
974 /* Send the response */
975 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", rplyb64);
978 state(conn, SMTP_AUTH_FINAL);
983 Curl_safefree(rplyb64);
988 /* For AUTH DIGEST-MD5 challenge responses */
989 static CURLcode smtp_state_auth_digest_resp(struct connectdata *conn,
993 CURLcode result = CURLE_OK;
994 struct SessionHandle *data = conn->data;
996 char *rplyb64 = NULL;
999 (void)instate; /* no use for this yet */
1001 if(smtpcode != 334) {
1002 failf(data, "Access denied: %d", smtpcode);
1003 return CURLE_LOGIN_DENIED;
1006 /* Get the challenge message */
1007 smtp_get_message(data->state.buffer, &chlg64);
1009 /* Create the response message */
1010 result = Curl_sasl_create_digest_md5_message(data, chlg64,
1011 conn->user, conn->passwd,
1012 "smtp", &rplyb64, &len);
1014 if(result == CURLE_BAD_CONTENT_ENCODING) {
1015 /* Send the cancellation */
1016 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "*");
1019 state(conn, SMTP_AUTH_CANCEL);
1023 /* Send the response */
1024 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", rplyb64);
1027 state(conn, SMTP_AUTH_DIGESTMD5_RESP);
1030 Curl_safefree(rplyb64);
1035 /* For AUTH DIGEST-MD5 challenge-response responses */
1036 static CURLcode smtp_state_auth_digest_resp_resp(struct connectdata *conn,
1040 CURLcode result = CURLE_OK;
1041 struct SessionHandle *data = conn->data;
1043 (void)instate; /* no use for this yet */
1045 if(smtpcode != 334) {
1046 failf(data, "Authentication failed: %d", smtpcode);
1047 result = CURLE_LOGIN_DENIED;
1050 /* Send an empty response */
1051 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "");
1054 state(conn, SMTP_AUTH_FINAL);
1063 /* For AUTH NTLM (without initial response) responses */
1064 static CURLcode smtp_state_auth_ntlm_resp(struct connectdata *conn,
1068 CURLcode result = CURLE_OK;
1069 struct SessionHandle *data = conn->data;
1070 char *type1msg = NULL;
1073 (void)instate; /* no use for this yet */
1075 if(smtpcode != 334) {
1076 failf(data, "Access denied: %d", smtpcode);
1077 result = CURLE_LOGIN_DENIED;
1080 /* Create the type-1 message */
1081 result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
1084 if(!result && type1msg) {
1085 /* Send the message */
1086 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", type1msg);
1089 state(conn, SMTP_AUTH_NTLM_TYPE2MSG);
1093 Curl_safefree(type1msg);
1098 /* For NTLM type-2 responses (sent in reponse to our type-1 message) */
1099 static CURLcode smtp_state_auth_ntlm_type2msg_resp(struct connectdata *conn,
1103 CURLcode result = CURLE_OK;
1104 struct SessionHandle *data = conn->data;
1105 char *type2msg = NULL;
1106 char *type3msg = NULL;
1109 (void)instate; /* no use for this yet */
1111 if(smtpcode != 334) {
1112 failf(data, "Access denied: %d", smtpcode);
1113 result = CURLE_LOGIN_DENIED;
1116 /* Get the type-2 message */
1117 smtp_get_message(data->state.buffer, &type2msg);
1119 /* Decode the type-2 message */
1120 result = Curl_sasl_decode_ntlm_type2_message(data, type2msg, &conn->ntlm);
1122 /* Send the cancellation */
1123 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "*");
1126 state(conn, SMTP_AUTH_CANCEL);
1129 /* Create the type-3 message */
1130 result = Curl_sasl_create_ntlm_type3_message(data, conn->user,
1131 conn->passwd, &conn->ntlm,
1133 if(!result && type3msg) {
1134 /* Send the message */
1135 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", type3msg);
1138 state(conn, SMTP_AUTH_FINAL);
1143 Curl_safefree(type3msg);
1149 /* For AUTH XOAUTH2 (without initial response) responses */
1150 static CURLcode smtp_state_auth_xoauth2_resp(struct connectdata *conn,
1151 int smtpcode, smtpstate instate)
1153 CURLcode result = CURLE_OK;
1154 struct SessionHandle *data = conn->data;
1156 char *xoauth = NULL;
1158 (void)instate; /* no use for this yet */
1160 if(smtpcode != 334) {
1161 failf(data, "Access denied: %d", smtpcode);
1162 result = CURLE_LOGIN_DENIED;
1165 /* Create the authorisation message */
1166 result = Curl_sasl_create_xoauth2_message(conn->data, conn->user,
1167 conn->xoauth2_bearer,
1169 if(!result && xoauth) {
1170 /* Send the message */
1171 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", xoauth);
1174 state(conn, SMTP_AUTH_FINAL);
1178 Curl_safefree(xoauth);
1183 /* For AUTH cancellation responses */
1184 static CURLcode smtp_state_auth_cancel_resp(struct connectdata *conn,
1188 CURLcode result = CURLE_OK;
1189 struct SessionHandle *data = conn->data;
1190 struct smtp_conn *smtpc = &conn->proto.smtpc;
1191 const char *mech = NULL;
1192 char *initresp = NULL;
1194 smtpstate state1 = SMTP_STOP;
1195 smtpstate state2 = SMTP_STOP;
1198 (void)instate; /* no use for this yet */
1200 /* Remove the offending mechanism from the supported list */
1201 smtpc->authmechs ^= smtpc->authused;
1203 /* Calculate alternative SASL login details */
1204 result = smtp_calc_sasl_details(conn, &mech, &initresp, &len, &state1,
1208 /* Do we have any mechanisms left? */
1210 /* Retry SASL based authentication */
1211 result = smtp_perform_auth(conn, mech, initresp, len, state1, state2);
1213 Curl_safefree(initresp);
1216 failf(data, "Authentication cancelled");
1218 result = CURLE_LOGIN_DENIED;
1225 /* For final responses in the AUTH sequence */
1226 static CURLcode smtp_state_auth_final_resp(struct connectdata *conn,
1230 CURLcode result = CURLE_OK;
1231 struct SessionHandle *data = conn->data;
1233 (void)instate; /* no use for this yet */
1235 if(smtpcode != 235) {
1236 failf(data, "Authentication failed: %d", smtpcode);
1237 result = CURLE_LOGIN_DENIED;
1240 /* End of connect phase */
1241 state(conn, SMTP_STOP);
1246 /* For command responses */
1247 static CURLcode smtp_state_command_resp(struct connectdata *conn, int smtpcode,
1250 CURLcode result = CURLE_OK;
1251 struct SessionHandle *data = conn->data;
1252 struct SMTP *smtp = data->req.protop;
1253 char *line = data->state.buffer;
1254 size_t len = strlen(line);
1256 (void)instate; /* no use for this yet */
1258 if((smtp->rcpt && smtpcode/100 != 2 && smtpcode != 553 && smtpcode != 1) ||
1259 (!smtp->rcpt && smtpcode/100 != 2 && smtpcode != 1)) {
1260 failf(data, "Command failed: %d", smtpcode);
1261 result = CURLE_RECV_ERROR;
1264 /* Temporarily add the LF character back and send as body to the client */
1265 if(!data->set.opt_no_body) {
1267 result = Curl_client_write(conn, CLIENTWRITE_BODY, line, len + 1);
1273 smtp->rcpt = smtp->rcpt->next;
1276 /* Send the next command */
1277 result = smtp_perform_command(conn);
1280 /* End of DO phase */
1281 state(conn, SMTP_STOP);
1284 /* End of DO phase */
1285 state(conn, SMTP_STOP);
1292 /* For MAIL responses */
1293 static CURLcode smtp_state_mail_resp(struct connectdata *conn, int smtpcode,
1296 CURLcode result = CURLE_OK;
1297 struct SessionHandle *data = conn->data;
1299 (void)instate; /* no use for this yet */
1301 if(smtpcode/100 != 2) {
1302 failf(data, "MAIL failed: %d", smtpcode);
1303 result = CURLE_SEND_ERROR;
1306 /* Start the RCPT TO command */
1307 result = smtp_perform_rcpt_to(conn);
1312 /* For RCPT responses */
1313 static CURLcode smtp_state_rcpt_resp(struct connectdata *conn, int smtpcode,
1316 CURLcode result = CURLE_OK;
1317 struct SessionHandle *data = conn->data;
1318 struct SMTP *smtp = data->req.protop;
1320 (void)instate; /* no use for this yet */
1322 if(smtpcode/100 != 2) {
1323 failf(data, "RCPT failed: %d", smtpcode);
1324 result = CURLE_SEND_ERROR;
1327 smtp->rcpt = smtp->rcpt->next;
1330 /* Send the next RCPT TO command */
1331 result = smtp_perform_rcpt_to(conn);
1333 /* Send the DATA command */
1334 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "DATA");
1337 state(conn, SMTP_DATA);
1344 /* For DATA response */
1345 static CURLcode smtp_state_data_resp(struct connectdata *conn, int smtpcode,
1348 CURLcode result = CURLE_OK;
1349 struct SessionHandle *data = conn->data;
1351 (void)instate; /* no use for this yet */
1353 if(smtpcode != 354) {
1354 failf(data, "DATA failed: %d", smtpcode);
1355 result = CURLE_SEND_ERROR;
1358 /* Set the progress upload size */
1359 Curl_pgrsSetUploadSize(data, data->state.infilesize);
1362 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL);
1364 /* End of DO phase */
1365 state(conn, SMTP_STOP);
1371 /* For POSTDATA responses, which are received after the entire DATA
1372 part has been sent to the server */
1373 static CURLcode smtp_state_postdata_resp(struct connectdata *conn,
1377 CURLcode result = CURLE_OK;
1379 (void)instate; /* no use for this yet */
1382 result = CURLE_RECV_ERROR;
1384 /* End of DONE phase */
1385 state(conn, SMTP_STOP);
1390 static CURLcode smtp_statemach_act(struct connectdata *conn)
1392 CURLcode result = CURLE_OK;
1393 curl_socket_t sock = conn->sock[FIRSTSOCKET];
1394 struct SessionHandle *data = conn->data;
1396 struct smtp_conn *smtpc = &conn->proto.smtpc;
1397 struct pingpong *pp = &smtpc->pp;
1400 /* Busy upgrading the connection; right now all I/O is SSL/TLS, not SMTP */
1401 if(smtpc->state == SMTP_UPGRADETLS)
1402 return smtp_perform_upgrade_tls(conn);
1404 /* Flush any data that needs to be sent */
1406 return Curl_pp_flushsend(pp);
1409 /* Read the response from the server */
1410 result = Curl_pp_readresp(sock, pp, &smtpcode, &nread);
1414 /* Store the latest response for later retrieval if necessary */
1415 if(smtpc->state != SMTP_QUIT && smtpcode != 1)
1416 data->info.httpcode = smtpcode;
1421 /* We have now received a full SMTP server response */
1422 switch(smtpc->state) {
1423 case SMTP_SERVERGREET:
1424 result = smtp_state_servergreet_resp(conn, smtpcode, smtpc->state);
1428 result = smtp_state_ehlo_resp(conn, smtpcode, smtpc->state);
1432 result = smtp_state_helo_resp(conn, smtpcode, smtpc->state);
1436 result = smtp_state_starttls_resp(conn, smtpcode, smtpc->state);
1439 case SMTP_AUTH_PLAIN:
1440 result = smtp_state_auth_plain_resp(conn, smtpcode, smtpc->state);
1443 case SMTP_AUTH_LOGIN:
1444 result = smtp_state_auth_login_resp(conn, smtpcode, smtpc->state);
1447 case SMTP_AUTH_LOGIN_PASSWD:
1448 result = smtp_state_auth_login_password_resp(conn, smtpcode,
1452 #ifndef CURL_DISABLE_CRYPTO_AUTH
1453 case SMTP_AUTH_CRAMMD5:
1454 result = smtp_state_auth_cram_resp(conn, smtpcode, smtpc->state);
1457 case SMTP_AUTH_DIGESTMD5:
1458 result = smtp_state_auth_digest_resp(conn, smtpcode, smtpc->state);
1461 case SMTP_AUTH_DIGESTMD5_RESP:
1462 result = smtp_state_auth_digest_resp_resp(conn, smtpcode, smtpc->state);
1467 case SMTP_AUTH_NTLM:
1468 result = smtp_state_auth_ntlm_resp(conn, smtpcode, smtpc->state);
1471 case SMTP_AUTH_NTLM_TYPE2MSG:
1472 result = smtp_state_auth_ntlm_type2msg_resp(conn, smtpcode,
1477 case SMTP_AUTH_XOAUTH2:
1478 result = smtp_state_auth_xoauth2_resp(conn, smtpcode, smtpc->state);
1481 case SMTP_AUTH_CANCEL:
1482 result = smtp_state_auth_cancel_resp(conn, smtpcode, smtpc->state);
1485 case SMTP_AUTH_FINAL:
1486 result = smtp_state_auth_final_resp(conn, smtpcode, smtpc->state);
1490 result = smtp_state_command_resp(conn, smtpcode, smtpc->state);
1494 result = smtp_state_mail_resp(conn, smtpcode, smtpc->state);
1498 result = smtp_state_rcpt_resp(conn, smtpcode, smtpc->state);
1502 result = smtp_state_data_resp(conn, smtpcode, smtpc->state);
1506 result = smtp_state_postdata_resp(conn, smtpcode, smtpc->state);
1510 /* fallthrough, just stop! */
1512 /* internal error */
1513 state(conn, SMTP_STOP);
1516 } while(!result && smtpc->state != SMTP_STOP && Curl_pp_moredata(pp));
1521 /* Called repeatedly until done from multi.c */
1522 static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done)
1524 CURLcode result = CURLE_OK;
1525 struct smtp_conn *smtpc = &conn->proto.smtpc;
1527 if((conn->handler->flags & PROTOPT_SSL) && !smtpc->ssldone) {
1528 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone);
1529 if(result || !smtpc->ssldone)
1533 result = Curl_pp_statemach(&smtpc->pp, FALSE);
1534 *done = (smtpc->state == SMTP_STOP) ? TRUE : FALSE;
1539 static CURLcode smtp_block_statemach(struct connectdata *conn)
1541 CURLcode result = CURLE_OK;
1542 struct smtp_conn *smtpc = &conn->proto.smtpc;
1544 while(smtpc->state != SMTP_STOP && !result)
1545 result = Curl_pp_statemach(&smtpc->pp, TRUE);
1550 /* Allocate and initialize the SMTP struct for the current SessionHandle if
1552 static CURLcode smtp_init(struct connectdata *conn)
1554 CURLcode result = CURLE_OK;
1555 struct SessionHandle *data = conn->data;
1558 smtp = data->req.protop = calloc(sizeof(struct SMTP), 1);
1560 result = CURLE_OUT_OF_MEMORY;
1565 /* For the SMTP "protocol connect" and "doing" phases only */
1566 static int smtp_getsock(struct connectdata *conn, curl_socket_t *socks,
1569 return Curl_pp_getsock(&conn->proto.smtpc.pp, socks, numsocks);
1572 /***********************************************************************
1576 * This function should do everything that is to be considered a part of
1577 * the connection phase.
1579 * The variable pointed to by 'done' will be TRUE if the protocol-layer
1580 * connect phase is done when this function returns, or FALSE if not.
1582 static CURLcode smtp_connect(struct connectdata *conn, bool *done)
1584 CURLcode result = CURLE_OK;
1585 struct smtp_conn *smtpc = &conn->proto.smtpc;
1586 struct pingpong *pp = &smtpc->pp;
1588 *done = FALSE; /* default to not done yet */
1590 /* We always support persistent connections in SMTP */
1591 connkeep(conn, "SMTP default");
1593 /* Set the default response time-out */
1594 pp->response_time = RESP_TIMEOUT;
1595 pp->statemach_act = smtp_statemach_act;
1596 pp->endofresp = smtp_endofresp;
1599 /* Set the default preferred authentication mechanism */
1600 smtpc->prefmech = SASL_AUTH_ANY;
1602 /* Initialise the pingpong layer */
1605 /* Parse the URL options */
1606 result = smtp_parse_url_options(conn);
1610 /* Parse the URL path */
1611 result = smtp_parse_url_path(conn);
1615 /* Start off waiting for the server greeting response */
1616 state(conn, SMTP_SERVERGREET);
1618 result = smtp_multi_statemach(conn, done);
1623 /***********************************************************************
1627 * The DONE function. This does what needs to be done after a single DO has
1630 * Input argument is already checked for validity.
1632 static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
1635 CURLcode result = CURLE_OK;
1636 struct SessionHandle *data = conn->data;
1637 struct SMTP *smtp = data->req.protop;
1638 struct pingpong *pp = &conn->proto.smtpc.pp;
1641 ssize_t bytes_written;
1646 /* When the easy handle is removed from the multi interface while libcurl
1647 is still trying to resolve the host name, the SMTP struct is not yet
1648 initialized. However, the removal action calls Curl_done() which in
1649 turn calls this function, so we simply return success. */
1653 connclose(conn, "SMTP done with bad status"); /* marked for closure */
1654 result = status; /* use the already set error code */
1656 else if(!data->set.connect_only && data->set.upload && data->set.mail_rcpt) {
1657 /* Calculate the EOB taking into account any terminating CRLF from the
1658 previous line of the email or the CRLF of the DATA command when there
1659 is "no mail data". RFC-5321, sect. 4.1.1.4. */
1662 if(smtp->trailing_crlf || !conn->data->state.infilesize) {
1667 /* Send the end of block data */
1668 result = Curl_write(conn, conn->writesockfd, eob, len, &bytes_written);
1672 if(bytes_written != len) {
1673 /* The whole chunk was not sent so keep it around and adjust the
1674 pingpong structure accordingly */
1675 pp->sendthis = strdup(eob);
1677 pp->sendleft = len - bytes_written;
1680 /* Successfully sent so adjust the response timeout relative to now */
1681 pp->response = Curl_tvnow();
1683 state(conn, SMTP_POSTDATA);
1685 /* Run the state-machine
1687 TODO: when the multi interface is used, this _really_ should be using
1688 the smtp_multi_statemach function but we have no general support for
1689 non-blocking DONE operations, not in the multi state machine and with
1690 Curl_done() invokes on several places in the code!
1692 result = smtp_block_statemach(conn);
1695 /* Cleanup our per-request based variables */
1696 Curl_safefree(smtp->custom);
1698 /* Clear the transfer mode for the next request */
1699 smtp->transfer = FTPTRANSFER_BODY;
1704 /***********************************************************************
1708 * This is the actual DO function for SMTP. Transfer a mail, send a command
1709 * or get some data according to the options previously setup.
1711 static CURLcode smtp_perform(struct connectdata *conn, bool *connected,
1714 /* This is SMTP and no proxy */
1715 CURLcode result = CURLE_OK;
1716 struct SessionHandle *data = conn->data;
1717 struct SMTP *smtp = data->req.protop;
1719 DEBUGF(infof(conn->data, "DO phase starts\n"));
1721 if(data->set.opt_no_body) {
1722 /* Requested no body means no transfer */
1723 smtp->transfer = FTPTRANSFER_INFO;
1726 *dophase_done = FALSE; /* not done yet */
1728 /* Store the first recipient (or NULL if not specified) */
1729 smtp->rcpt = data->set.mail_rcpt;
1731 /* Start the first command in the DO phase */
1732 if(data->set.upload && data->set.mail_rcpt)
1734 result = smtp_perform_mail(conn);
1736 /* SMTP based command (VRFY, EXPN, NOOP, RSET or HELP) */
1737 result = smtp_perform_command(conn);
1742 /* Run the state-machine */
1743 result = smtp_multi_statemach(conn, dophase_done);
1745 *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1748 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1753 /***********************************************************************
1757 * This function is registered as 'curl_do' function. It decodes the path
1758 * parts etc as a wrapper to the actual DO function (smtp_perform).
1760 * The input argument is already checked for validity.
1762 static CURLcode smtp_do(struct connectdata *conn, bool *done)
1764 CURLcode result = CURLE_OK;
1766 *done = FALSE; /* default to false */
1768 /* Parse the custom request */
1769 result = smtp_parse_custom_request(conn);
1773 result = smtp_regular_transfer(conn, done);
1778 /***********************************************************************
1782 * Disconnect from an SMTP server. Cleanup protocol-specific per-connection
1783 * resources. BLOCKING.
1785 static CURLcode smtp_disconnect(struct connectdata *conn, bool dead_connection)
1787 struct smtp_conn *smtpc = &conn->proto.smtpc;
1789 /* We cannot send quit unconditionally. If this connection is stale or
1790 bad in any way, sending quit and waiting around here will make the
1791 disconnect wait in vain and cause more problems than we need to. */
1793 /* The SMTP session may or may not have been allocated/setup at this
1795 if(!dead_connection && smtpc->pp.conn && smtpc->pp.conn->bits.protoconnstart)
1796 if(!smtp_perform_quit(conn))
1797 (void)smtp_block_statemach(conn); /* ignore errors on QUIT */
1799 /* Disconnect from the server */
1800 Curl_pp_disconnect(&smtpc->pp);
1802 /* Cleanup the SASL module */
1803 Curl_sasl_cleanup(conn, smtpc->authused);
1805 /* Cleanup our connection based variables */
1806 Curl_safefree(smtpc->domain);
1811 /* Call this when the DO phase has completed */
1812 static CURLcode smtp_dophase_done(struct connectdata *conn, bool connected)
1814 struct SMTP *smtp = conn->data->req.protop;
1818 if(smtp->transfer != FTPTRANSFER_BODY)
1819 /* no data to transfer */
1820 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1825 /* Called from multi.c while DOing */
1826 static CURLcode smtp_doing(struct connectdata *conn, bool *dophase_done)
1828 CURLcode result = smtp_multi_statemach(conn, dophase_done);
1831 DEBUGF(infof(conn->data, "DO phase failed\n"));
1832 else if(*dophase_done) {
1833 result = smtp_dophase_done(conn, FALSE /* not connected */);
1835 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1841 /***********************************************************************
1843 * smtp_regular_transfer()
1845 * The input argument is already checked for validity.
1847 * Performs all commands done before a regular transfer between a local and a
1850 static CURLcode smtp_regular_transfer(struct connectdata *conn,
1853 CURLcode result = CURLE_OK;
1854 bool connected = FALSE;
1855 struct SessionHandle *data = conn->data;
1857 /* Make sure size is unknown at this point */
1858 data->req.size = -1;
1860 /* Set the progress data */
1861 Curl_pgrsSetUploadCounter(data, 0);
1862 Curl_pgrsSetDownloadCounter(data, 0);
1863 Curl_pgrsSetUploadSize(data, 0);
1864 Curl_pgrsSetDownloadSize(data, 0);
1866 /* Carry out the perform */
1867 result = smtp_perform(conn, &connected, dophase_done);
1869 /* Perform post DO phase operations if necessary */
1870 if(!result && *dophase_done)
1871 result = smtp_dophase_done(conn, connected);
1876 static CURLcode smtp_setup_connection(struct connectdata *conn)
1878 struct SessionHandle *data = conn->data;
1881 if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
1882 /* Unless we have asked to tunnel SMTP operations through the proxy, we
1883 switch and use HTTP operations only */
1884 #ifndef CURL_DISABLE_HTTP
1885 if(conn->handler == &Curl_handler_smtp)
1886 conn->handler = &Curl_handler_smtp_proxy;
1889 conn->handler = &Curl_handler_smtps_proxy;
1891 failf(data, "SMTPS not supported!");
1892 return CURLE_UNSUPPORTED_PROTOCOL;
1895 /* set it up as a HTTP connection instead */
1896 return conn->handler->setup_connection(conn);
1899 failf(data, "SMTP over http proxy requires HTTP support built-in!");
1900 return CURLE_UNSUPPORTED_PROTOCOL;
1904 /* Initialise the SMTP layer */
1905 result = smtp_init(conn);
1909 data->state.path++; /* don't include the initial slash */
1914 /***********************************************************************
1916 * smtp_parse_url_options()
1918 * Parse the URL login options.
1920 static CURLcode smtp_parse_url_options(struct connectdata *conn)
1922 CURLcode result = CURLE_OK;
1923 struct smtp_conn *smtpc = &conn->proto.smtpc;
1924 const char *options = conn->options;
1925 const char *ptr = options;
1928 while(ptr && *ptr) {
1929 const char *key = ptr;
1931 while(*ptr && *ptr != '=')
1934 if(strnequal(key, "AUTH", 4)) {
1936 const char *value = ++ptr;
1940 smtpc->prefmech = SASL_AUTH_NONE;
1943 while(*ptr && *ptr != ';') {
1948 if(strnequal(value, "*", len))
1949 smtpc->prefmech = SASL_AUTH_ANY;
1950 else if(strnequal(value, SASL_MECH_STRING_LOGIN, len))
1951 smtpc->prefmech |= SASL_MECH_LOGIN;
1952 else if(strnequal(value, SASL_MECH_STRING_PLAIN, len))
1953 smtpc->prefmech |= SASL_MECH_PLAIN;
1954 else if(strnequal(value, SASL_MECH_STRING_CRAM_MD5, len))
1955 smtpc->prefmech |= SASL_MECH_CRAM_MD5;
1956 else if(strnequal(value, SASL_MECH_STRING_DIGEST_MD5, len))
1957 smtpc->prefmech |= SASL_MECH_DIGEST_MD5;
1958 else if(strnequal(value, SASL_MECH_STRING_GSSAPI, len))
1959 smtpc->prefmech |= SASL_MECH_GSSAPI;
1960 else if(strnequal(value, SASL_MECH_STRING_NTLM, len))
1961 smtpc->prefmech |= SASL_MECH_NTLM;
1962 else if(strnequal(value, SASL_MECH_STRING_XOAUTH2, len))
1963 smtpc->prefmech |= SASL_MECH_XOAUTH2;
1969 result = CURLE_URL_MALFORMAT;
1975 /***********************************************************************
1977 * smtp_parse_url_path()
1979 * Parse the URL path into separate path components.
1981 static CURLcode smtp_parse_url_path(struct connectdata *conn)
1983 /* The SMTP struct is already initialised in smtp_connect() */
1984 struct SessionHandle *data = conn->data;
1985 struct smtp_conn *smtpc = &conn->proto.smtpc;
1986 const char *path = data->state.path;
1987 char localhost[HOSTNAME_MAX + 1];
1989 /* Calculate the path if necessary */
1991 if(!Curl_gethostname(localhost, sizeof(localhost)))
1997 /* URL decode the path and use it as the domain in our EHLO */
1998 return Curl_urldecode(conn->data, path, 0, &smtpc->domain, NULL, TRUE);
2001 /***********************************************************************
2003 * smtp_parse_custom_request()
2005 * Parse the custom request.
2007 static CURLcode smtp_parse_custom_request(struct connectdata *conn)
2009 CURLcode result = CURLE_OK;
2010 struct SessionHandle *data = conn->data;
2011 struct SMTP *smtp = data->req.protop;
2012 const char *custom = data->set.str[STRING_CUSTOMREQUEST];
2014 /* URL decode the custom request */
2016 result = Curl_urldecode(data, custom, 0, &smtp->custom, NULL, TRUE);
2021 /***********************************************************************
2023 * smtp_calc_sasl_details()
2025 * Calculate the required login details for SASL authentication.
2027 static CURLcode smtp_calc_sasl_details(struct connectdata *conn,
2029 char **initresp, size_t *len,
2030 smtpstate *state1, smtpstate *state2)
2032 CURLcode result = CURLE_OK;
2033 struct SessionHandle *data = conn->data;
2034 struct smtp_conn *smtpc = &conn->proto.smtpc;
2036 /* Calculate the supported authentication mechanism, by decreasing order of
2037 security, as well as the initial response where appropriate */
2038 #ifndef CURL_DISABLE_CRYPTO_AUTH
2039 if((smtpc->authmechs & SASL_MECH_DIGEST_MD5) &&
2040 (smtpc->prefmech & SASL_MECH_DIGEST_MD5)) {
2041 *mech = SASL_MECH_STRING_DIGEST_MD5;
2042 *state1 = SMTP_AUTH_DIGESTMD5;
2043 smtpc->authused = SASL_MECH_DIGEST_MD5;
2045 else if((smtpc->authmechs & SASL_MECH_CRAM_MD5) &&
2046 (smtpc->prefmech & SASL_MECH_CRAM_MD5)) {
2047 *mech = SASL_MECH_STRING_CRAM_MD5;
2048 *state1 = SMTP_AUTH_CRAMMD5;
2049 smtpc->authused = SASL_MECH_CRAM_MD5;
2054 if((smtpc->authmechs & SASL_MECH_NTLM) &&
2055 (smtpc->prefmech & SASL_MECH_NTLM)) {
2056 *mech = SASL_MECH_STRING_NTLM;
2057 *state1 = SMTP_AUTH_NTLM;
2058 *state2 = SMTP_AUTH_NTLM_TYPE2MSG;
2059 smtpc->authused = SASL_MECH_NTLM;
2061 if(data->set.sasl_ir)
2062 result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
2068 if(((smtpc->authmechs & SASL_MECH_XOAUTH2) &&
2069 (smtpc->prefmech & SASL_MECH_XOAUTH2) &&
2070 (smtpc->prefmech != SASL_AUTH_ANY)) || conn->xoauth2_bearer) {
2071 *mech = SASL_MECH_STRING_XOAUTH2;
2072 *state1 = SMTP_AUTH_XOAUTH2;
2073 *state2 = SMTP_AUTH_FINAL;
2074 smtpc->authused = SASL_MECH_XOAUTH2;
2076 if(data->set.sasl_ir)
2077 result = Curl_sasl_create_xoauth2_message(data, conn->user,
2078 conn->xoauth2_bearer,
2081 else if((smtpc->authmechs & SASL_MECH_LOGIN) &&
2082 (smtpc->prefmech & SASL_MECH_LOGIN)) {
2083 *mech = SASL_MECH_STRING_LOGIN;
2084 *state1 = SMTP_AUTH_LOGIN;
2085 *state2 = SMTP_AUTH_LOGIN_PASSWD;
2086 smtpc->authused = SASL_MECH_LOGIN;
2088 if(data->set.sasl_ir)
2089 result = Curl_sasl_create_login_message(data, conn->user, initresp, len);
2091 else if((smtpc->authmechs & SASL_MECH_PLAIN) &&
2092 (smtpc->prefmech & SASL_MECH_PLAIN)) {
2093 *mech = SASL_MECH_STRING_PLAIN;
2094 *state1 = SMTP_AUTH_PLAIN;
2095 *state2 = SMTP_AUTH_FINAL;
2096 smtpc->authused = SASL_MECH_PLAIN;
2098 if(data->set.sasl_ir)
2099 result = Curl_sasl_create_plain_message(data, conn->user, conn->passwd,
2106 CURLcode Curl_smtp_escape_eob(struct connectdata *conn, ssize_t nread)
2108 /* When sending a SMTP payload we must detect CRLF. sequences making sure
2109 they are sent as CRLF.. instead, as a . on the beginning of a line will
2110 be deleted by the server when not part of an EOB terminator and a
2111 genuine CRLF.CRLF which isn't escaped will wrongly be detected as end of
2116 struct SessionHandle *data = conn->data;
2117 struct SMTP *smtp = data->req.protop;
2119 /* Do we need to allocate the scatch buffer? */
2120 if(!data->state.scratch) {
2121 data->state.scratch = malloc(2 * BUFSIZE);
2123 if(!data->state.scratch) {
2124 failf (data, "Failed to alloc scratch buffer!");
2125 return CURLE_OUT_OF_MEMORY;
2129 /* This loop can be improved by some kind of Boyer-Moore style of
2130 approach but that is saved for later... */
2131 for(i = 0, si = 0; i < nread; i++) {
2132 if(SMTP_EOB[smtp->eob] == data->req.upload_fromhere[i]) {
2135 /* Is the EOB potentially the terminating CRLF? */
2136 if(2 == smtp->eob || SMTP_EOB_LEN == smtp->eob)
2137 smtp->trailing_crlf = TRUE;
2139 smtp->trailing_crlf = FALSE;
2141 else if(smtp->eob) {
2142 /* A previous substring matched so output that first */
2143 memcpy(&data->state.scratch[si], SMTP_EOB, smtp->eob);
2146 /* Then compare the first byte */
2147 if(SMTP_EOB[0] == data->req.upload_fromhere[i])
2152 /* Reset the trailing CRLF flag as there was more data */
2153 smtp->trailing_crlf = FALSE;
2156 /* Do we have a match for CRLF. as per RFC-5321, sect. 4.5.2 */
2157 if(SMTP_EOB_FIND_LEN == smtp->eob) {
2158 /* Copy the replacement data to the target buffer */
2159 memcpy(&data->state.scratch[si], SMTP_EOB_REPL, SMTP_EOB_REPL_LEN);
2160 si += SMTP_EOB_REPL_LEN;
2164 data->state.scratch[si++] = data->req.upload_fromhere[i];
2168 /* A substring matched before processing ended so output that now */
2169 memcpy(&data->state.scratch[si], SMTP_EOB, smtp->eob);
2175 /* Only use the new buffer if we replaced something */
2178 /* Upload from the new (replaced) buffer instead */
2179 data->req.upload_fromhere = data->state.scratch;
2181 /* Set the new amount too */
2182 data->req.upload_present = nread;
2188 #endif /* CURL_DISABLE_SMTP */