1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2013, 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"
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_SMTP | 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 */
357 /* Send the EHLO command */
358 result = Curl_pp_sendf(&smtpc->pp, "EHLO %s", smtpc->domain);
361 state(conn, SMTP_EHLO);
366 /***********************************************************************
368 * smtp_perform_helo()
370 * Sends the HELO command to initialise communication with the SMTP server.
372 static CURLcode smtp_perform_helo(struct connectdata *conn)
374 CURLcode result = CURLE_OK;
375 struct smtp_conn *smtpc = &conn->proto.smtpc;
377 smtpc->authused = 0; /* No authentication mechanism used in smtp
380 /* Send the HELO command */
381 result = Curl_pp_sendf(&smtpc->pp, "HELO %s", smtpc->domain);
384 state(conn, SMTP_HELO);
389 /***********************************************************************
391 * smtp_perform_starttls()
393 * Sends the STLS command to start the upgrade to TLS.
395 static CURLcode smtp_perform_starttls(struct connectdata *conn)
397 CURLcode result = CURLE_OK;
399 /* Send the STARTTLS command */
400 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "STARTTLS");
403 state(conn, SMTP_STARTTLS);
408 /***********************************************************************
410 * smtp_perform_upgrade_tls()
412 * Performs the upgrade to TLS.
414 static CURLcode smtp_perform_upgrade_tls(struct connectdata *conn)
416 CURLcode result = CURLE_OK;
417 struct smtp_conn *smtpc = &conn->proto.smtpc;
419 /* Start the SSL connection */
420 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone);
423 if(smtpc->state != SMTP_UPGRADETLS)
424 state(conn, SMTP_UPGRADETLS);
428 result = smtp_perform_ehlo(conn);
435 /***********************************************************************
437 * smtp_perform_auth()
439 * Sends an AUTH command allowing the client to login with the given SASL
440 * authentication mechanism.
442 static CURLcode smtp_perform_auth(struct connectdata *conn,
444 const char *initresp, size_t len,
445 smtpstate state1, smtpstate state2)
447 CURLcode result = CURLE_OK;
448 struct smtp_conn *smtpc = &conn->proto.smtpc;
450 if(initresp && 8 + strlen(mech) + len <= 512) { /* AUTH <mech> ...<crlf> */
451 /* Send the AUTH command with the initial response */
452 result = Curl_pp_sendf(&smtpc->pp, "AUTH %s %s", mech, initresp);
458 /* Send the AUTH command */
459 result = Curl_pp_sendf(&smtpc->pp, "AUTH %s", mech);
468 /***********************************************************************
470 * smtp_perform_authentication()
472 * Initiates the authentication sequence, with the appropriate SASL
473 * authentication mechanism.
475 static CURLcode smtp_perform_authentication(struct connectdata *conn)
477 CURLcode result = CURLE_OK;
478 const char *mech = NULL;
479 char *initresp = NULL;
481 smtpstate state1 = SMTP_STOP;
482 smtpstate state2 = SMTP_STOP;
484 /* Check we have a username and password to authenticate with and end the
485 connect phase if we don't */
486 if(!conn->bits.user_passwd) {
487 state(conn, SMTP_STOP);
492 /* Calculate the SASL login details */
493 result = smtp_calc_sasl_details(conn, &mech, &initresp, &len, &state1,
498 /* Perform SASL based authentication */
499 result = smtp_perform_auth(conn, mech, initresp, len, state1, state2);
501 Curl_safefree(initresp);
504 /* Other mechanisms not supported */
505 infof(conn->data, "No known authentication mechanisms supported!\n");
506 result = CURLE_LOGIN_DENIED;
513 /***********************************************************************
515 * smtp_perform_command()
517 * Sends a SMTP based command.
519 static CURLcode smtp_perform_command(struct connectdata *conn)
521 CURLcode result = CURLE_OK;
522 struct SessionHandle *data = conn->data;
523 struct SMTP *smtp = data->req.protop;
525 /* Send the command */
527 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s %s",
528 smtp->custom && smtp->custom[0] != '\0' ?
529 smtp->custom : "VRFY",
532 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s",
533 smtp->custom && smtp->custom[0] != '\0' ?
534 smtp->custom : "HELP");
537 state(conn, SMTP_COMMAND);
542 /***********************************************************************
544 * smtp_perform_mail()
546 * Sends an MAIL command to initiate the upload of a message.
548 static CURLcode smtp_perform_mail(struct connectdata *conn)
553 CURLcode result = CURLE_OK;
554 struct SessionHandle *data = conn->data;
556 /* Calculate the FROM parameter */
557 if(!data->set.str[STRING_MAIL_FROM])
558 /* Null reverse-path, RFC-5321, sect. 3.6.3 */
560 else if(data->set.str[STRING_MAIL_FROM][0] == '<')
561 from = aprintf("%s", data->set.str[STRING_MAIL_FROM]);
563 from = aprintf("<%s>", data->set.str[STRING_MAIL_FROM]);
566 return CURLE_OUT_OF_MEMORY;
568 /* Calculate the optional AUTH parameter */
569 if(data->set.str[STRING_MAIL_AUTH] && conn->proto.smtpc.authused) {
570 if(data->set.str[STRING_MAIL_AUTH][0] != '\0')
571 auth = aprintf("%s", data->set.str[STRING_MAIL_AUTH]);
573 /* Empty AUTH, RFC-2554, sect. 5 */
579 return CURLE_OUT_OF_MEMORY;
583 /* Calculate the optional SIZE parameter */
584 if(conn->proto.smtpc.size_supported && conn->data->set.infilesize > 0) {
585 size = aprintf("%" FORMAT_OFF_T, data->set.infilesize);
591 return CURLE_OUT_OF_MEMORY;
595 /* Send the MAIL command */
597 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
598 "MAIL FROM:%s", from);
599 else if(auth && !size)
600 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
601 "MAIL FROM:%s AUTH=%s", from, auth);
602 else if(auth && size)
603 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
604 "MAIL FROM:%s AUTH=%s SIZE=%s", from, auth, size);
606 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
607 "MAIL FROM:%s SIZE=%s", from, size);
614 state(conn, SMTP_MAIL);
619 /***********************************************************************
621 * smtp_perform_rcpt_to()
623 * Sends a RCPT TO command for a given recipient as part of the message upload
626 static CURLcode smtp_perform_rcpt_to(struct connectdata *conn)
628 CURLcode result = CURLE_OK;
629 struct SessionHandle *data = conn->data;
630 struct SMTP *smtp = data->req.protop;
632 /* Send the RCPT TO command */
633 if(smtp->rcpt->data[0] == '<')
634 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:%s",
637 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:<%s>",
640 state(conn, SMTP_RCPT);
645 /***********************************************************************
647 * smtp_perform_quit()
649 * Performs the quit action prior to sclose() being called.
651 static CURLcode smtp_perform_quit(struct connectdata *conn)
653 CURLcode result = CURLE_OK;
655 /* Send the QUIT command */
656 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "QUIT");
659 state(conn, SMTP_QUIT);
664 /* For the initial server greeting */
665 static CURLcode smtp_state_servergreet_resp(struct connectdata *conn,
669 CURLcode result = CURLE_OK;
670 struct SessionHandle *data = conn->data;
672 (void)instate; /* no use for this yet */
674 if(smtpcode/100 != 2) {
675 failf(data, "Got unexpected smtp-server response: %d", smtpcode);
676 result = CURLE_FTP_WEIRD_SERVER_REPLY;
679 result = smtp_perform_ehlo(conn);
684 /* For STARTTLS responses */
685 static CURLcode smtp_state_starttls_resp(struct connectdata *conn,
689 CURLcode result = CURLE_OK;
690 struct SessionHandle *data = conn->data;
692 (void)instate; /* no use for this yet */
694 if(smtpcode != 220) {
695 if(data->set.use_ssl != CURLUSESSL_TRY) {
696 failf(data, "STARTTLS denied. %c", smtpcode);
697 result = CURLE_USE_SSL_FAILED;
700 result = smtp_perform_authentication(conn);
703 result = smtp_perform_upgrade_tls(conn);
708 /* For EHLO responses */
709 static CURLcode smtp_state_ehlo_resp(struct connectdata *conn, int smtpcode,
712 CURLcode result = CURLE_OK;
713 struct SessionHandle *data = conn->data;
714 struct smtp_conn *smtpc = &conn->proto.smtpc;
715 const char *line = data->state.buffer;
716 size_t len = strlen(line);
719 (void)instate; /* no use for this yet */
721 if(smtpcode/100 != 2 && smtpcode != 1) {
722 if((data->set.use_ssl <= CURLUSESSL_TRY || conn->ssl[FIRSTSOCKET].use) &&
723 !conn->bits.user_passwd)
724 result = smtp_perform_helo(conn);
726 failf(data, "Remote access denied: %d", smtpcode);
727 result = CURLE_REMOTE_ACCESS_DENIED;
734 /* Does the server support the STARTTLS capability? */
735 if(len >= 8 && !memcmp(line, "STARTTLS", 8))
736 smtpc->tls_supported = TRUE;
738 /* Does the server support the SIZE capability? */
739 else if(len >= 4 && !memcmp(line, "SIZE", 4))
740 smtpc->size_supported = TRUE;
742 /* Do we have the authentication mechanism list? */
743 else if(len >= 5 && !memcmp(line, "AUTH ", 5)) {
747 /* Loop through the data line */
750 (*line == ' ' || *line == '\t' ||
751 *line == '\r' || *line == '\n')) {
760 /* Extract the word */
761 for(wordlen = 0; wordlen < len && line[wordlen] != ' ' &&
762 line[wordlen] != '\t' && line[wordlen] != '\r' &&
763 line[wordlen] != '\n';)
766 /* Test the word for a matching authentication mechanism */
767 if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_LOGIN))
768 smtpc->authmechs |= SASL_MECH_LOGIN;
769 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_PLAIN))
770 smtpc->authmechs |= SASL_MECH_PLAIN;
771 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_CRAM_MD5))
772 smtpc->authmechs |= SASL_MECH_CRAM_MD5;
773 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_DIGEST_MD5))
774 smtpc->authmechs |= SASL_MECH_DIGEST_MD5;
775 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_GSSAPI))
776 smtpc->authmechs |= SASL_MECH_GSSAPI;
777 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_EXTERNAL))
778 smtpc->authmechs |= SASL_MECH_EXTERNAL;
779 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_NTLM))
780 smtpc->authmechs |= SASL_MECH_NTLM;
781 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_XOAUTH2))
782 smtpc->authmechs |= SASL_MECH_XOAUTH2;
790 if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
791 /* We don't have a SSL/TLS connection yet, but SSL is requested */
792 if(smtpc->tls_supported)
793 /* Switch to TLS connection now */
794 result = smtp_perform_starttls(conn);
795 else if(data->set.use_ssl == CURLUSESSL_TRY)
796 /* Fallback and carry on with authentication */
797 result = smtp_perform_authentication(conn);
799 failf(data, "STARTTLS not supported.");
800 result = CURLE_USE_SSL_FAILED;
804 result = smtp_perform_authentication(conn);
811 /* For HELO responses */
812 static CURLcode smtp_state_helo_resp(struct connectdata *conn, int smtpcode,
815 CURLcode result = CURLE_OK;
816 struct SessionHandle *data = conn->data;
818 (void)instate; /* no use for this yet */
820 if(smtpcode/100 != 2) {
821 failf(data, "Remote access denied: %d", smtpcode);
822 result = CURLE_REMOTE_ACCESS_DENIED;
825 /* End of connect phase */
826 state(conn, SMTP_STOP);
831 /* For AUTH PLAIN (without initial response) responses */
832 static CURLcode smtp_state_auth_plain_resp(struct connectdata *conn,
836 CURLcode result = CURLE_OK;
837 struct SessionHandle *data = conn->data;
839 char *plainauth = NULL;
841 (void)instate; /* no use for this yet */
843 if(smtpcode != 334) {
844 failf(data, "Access denied: %d", smtpcode);
845 result = CURLE_LOGIN_DENIED;
848 /* Create the authorisation message */
849 result = Curl_sasl_create_plain_message(conn->data, conn->user,
850 conn->passwd, &plainauth, &len);
851 if(!result && plainauth) {
852 /* Send the message */
853 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", plainauth);
856 state(conn, SMTP_AUTH_FINAL);
860 Curl_safefree(plainauth);
865 /* For AUTH LOGIN (without initial response) responses */
866 static CURLcode smtp_state_auth_login_resp(struct connectdata *conn,
870 CURLcode result = CURLE_OK;
871 struct SessionHandle *data = conn->data;
873 char *authuser = NULL;
875 (void)instate; /* no use for this yet */
877 if(smtpcode != 334) {
878 failf(data, "Access denied: %d", smtpcode);
879 result = CURLE_LOGIN_DENIED;
882 /* Create the user message */
883 result = Curl_sasl_create_login_message(conn->data, conn->user,
885 if(!result && authuser) {
887 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", authuser);
890 state(conn, SMTP_AUTH_LOGIN_PASSWD);
894 Curl_safefree(authuser);
899 /* For AUTH LOGIN user entry responses */
900 static CURLcode smtp_state_auth_login_password_resp(struct connectdata *conn,
904 CURLcode result = CURLE_OK;
905 struct SessionHandle *data = conn->data;
907 char *authpasswd = NULL;
909 (void)instate; /* no use for this yet */
911 if(smtpcode != 334) {
912 failf(data, "Access denied: %d", smtpcode);
913 result = CURLE_LOGIN_DENIED;
916 /* Create the password message */
917 result = Curl_sasl_create_login_message(conn->data, conn->passwd,
919 if(!result && authpasswd) {
920 /* Send the password */
921 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", authpasswd);
924 state(conn, SMTP_AUTH_FINAL);
928 Curl_safefree(authpasswd);
933 #ifndef CURL_DISABLE_CRYPTO_AUTH
934 /* For AUTH CRAM-MD5 responses */
935 static CURLcode smtp_state_auth_cram_resp(struct connectdata *conn,
939 CURLcode result = CURLE_OK;
940 struct SessionHandle *data = conn->data;
943 char *rplyb64 = NULL;
946 (void)instate; /* no use for this yet */
948 if(smtpcode != 334) {
949 failf(data, "Access denied: %d", smtpcode);
950 return CURLE_LOGIN_DENIED;
953 /* Get the challenge message */
954 smtp_get_message(data->state.buffer, &chlg64);
956 /* Decode the challenge message */
957 result = Curl_sasl_decode_cram_md5_message(chlg64, &chlg, &len);
959 /* Send the cancellation */
960 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "*");
963 state(conn, SMTP_AUTH_CANCEL);
966 /* Create the response message */
967 result = Curl_sasl_create_cram_md5_message(data, chlg, conn->user,
968 conn->passwd, &rplyb64, &len);
969 if(!result && rplyb64) {
970 /* Send the response */
971 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", rplyb64);
974 state(conn, SMTP_AUTH_FINAL);
979 Curl_safefree(rplyb64);
984 /* For AUTH DIGEST-MD5 challenge responses */
985 static CURLcode smtp_state_auth_digest_resp(struct connectdata *conn,
989 CURLcode result = CURLE_OK;
990 struct SessionHandle *data = conn->data;
992 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 /* Decode the challange message */
1010 result = Curl_sasl_decode_digest_md5_message(chlg64, nonce, sizeof(nonce),
1011 realm, sizeof(realm),
1012 algorithm, sizeof(algorithm));
1013 if(result || strcmp(algorithm, "md5-sess") != 0) {
1014 /* Send the cancellation */
1015 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "*");
1018 state(conn, SMTP_AUTH_CANCEL);
1021 /* Create the response message */
1022 result = Curl_sasl_create_digest_md5_message(data, nonce, realm,
1023 conn->user, conn->passwd,
1024 "smtp", &rplyb64, &len);
1025 if(!result && rplyb64) {
1026 /* Send the response */
1027 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", rplyb64);
1030 state(conn, SMTP_AUTH_DIGESTMD5_RESP);
1034 Curl_safefree(rplyb64);
1039 /* For AUTH DIGEST-MD5 challenge-response responses */
1040 static CURLcode smtp_state_auth_digest_resp_resp(struct connectdata *conn,
1044 CURLcode result = CURLE_OK;
1045 struct SessionHandle *data = conn->data;
1047 (void)instate; /* no use for this yet */
1049 if(smtpcode != 334) {
1050 failf(data, "Authentication failed: %d", smtpcode);
1051 result = CURLE_LOGIN_DENIED;
1054 /* Send an empty response */
1055 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "");
1058 state(conn, SMTP_AUTH_FINAL);
1067 /* For AUTH NTLM (without initial response) responses */
1068 static CURLcode smtp_state_auth_ntlm_resp(struct connectdata *conn,
1072 CURLcode result = CURLE_OK;
1073 struct SessionHandle *data = conn->data;
1074 char *type1msg = NULL;
1077 (void)instate; /* no use for this yet */
1079 if(smtpcode != 334) {
1080 failf(data, "Access denied: %d", smtpcode);
1081 result = CURLE_LOGIN_DENIED;
1084 /* Create the type-1 message */
1085 result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
1088 if(!result && type1msg) {
1089 /* Send the message */
1090 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", type1msg);
1093 state(conn, SMTP_AUTH_NTLM_TYPE2MSG);
1097 Curl_safefree(type1msg);
1102 /* For NTLM type-2 responses (sent in reponse to our type-1 message) */
1103 static CURLcode smtp_state_auth_ntlm_type2msg_resp(struct connectdata *conn,
1107 CURLcode result = CURLE_OK;
1108 struct SessionHandle *data = conn->data;
1109 char *type2msg = NULL;
1110 char *type3msg = NULL;
1113 (void)instate; /* no use for this yet */
1115 if(smtpcode != 334) {
1116 failf(data, "Access denied: %d", smtpcode);
1117 result = CURLE_LOGIN_DENIED;
1120 /* Get the type-2 message */
1121 smtp_get_message(data->state.buffer, &type2msg);
1123 /* Decode the type-2 message */
1124 result = Curl_sasl_decode_ntlm_type2_message(data, type2msg, &conn->ntlm);
1126 /* Send the cancellation */
1127 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "*");
1130 state(conn, SMTP_AUTH_CANCEL);
1133 /* Create the type-3 message */
1134 result = Curl_sasl_create_ntlm_type3_message(data, conn->user,
1135 conn->passwd, &conn->ntlm,
1137 if(!result && type3msg) {
1138 /* Send the message */
1139 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", type3msg);
1142 state(conn, SMTP_AUTH_FINAL);
1147 Curl_safefree(type3msg);
1153 /* For AUTH XOAUTH2 (without initial response) responses */
1154 static CURLcode smtp_state_auth_xoauth2_resp(struct connectdata *conn,
1155 int smtpcode, smtpstate instate)
1157 CURLcode result = CURLE_OK;
1158 struct SessionHandle *data = conn->data;
1160 char *xoauth = NULL;
1162 (void)instate; /* no use for this yet */
1164 if(smtpcode != 334) {
1165 failf(data, "Access denied: %d", smtpcode);
1166 result = CURLE_LOGIN_DENIED;
1169 /* Create the authorisation message */
1170 result = Curl_sasl_create_xoauth2_message(conn->data, conn->user,
1171 conn->xoauth2_bearer,
1173 if(!result && xoauth) {
1174 /* Send the message */
1175 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", xoauth);
1178 state(conn, SMTP_AUTH_FINAL);
1182 Curl_safefree(xoauth);
1187 /* For AUTH cancellation responses */
1188 static CURLcode smtp_state_auth_cancel_resp(struct connectdata *conn,
1192 struct SessionHandle *data = conn->data;
1195 (void)instate; /* no use for this yet */
1197 failf(data, "Authentication cancelled");
1199 return CURLE_LOGIN_DENIED;
1202 /* For final responses in the AUTH sequence */
1203 static CURLcode smtp_state_auth_final_resp(struct connectdata *conn,
1207 CURLcode result = CURLE_OK;
1208 struct SessionHandle *data = conn->data;
1210 (void)instate; /* no use for this yet */
1212 if(smtpcode != 235) {
1213 failf(data, "Authentication failed: %d", smtpcode);
1214 result = CURLE_LOGIN_DENIED;
1217 /* End of connect phase */
1218 state(conn, SMTP_STOP);
1223 /* For command responses */
1224 static CURLcode smtp_state_command_resp(struct connectdata *conn, int smtpcode,
1227 CURLcode result = CURLE_OK;
1228 struct SessionHandle *data = conn->data;
1229 struct SMTP *smtp = data->req.protop;
1230 char *line = data->state.buffer;
1231 size_t len = strlen(line);
1233 (void)instate; /* no use for this yet */
1235 if((smtp->rcpt && smtpcode/100 != 2 && smtpcode != 553 && smtpcode != 1) ||
1236 (!smtp->rcpt && smtpcode/100 != 2 && smtpcode != 1)) {
1237 failf(data, "Command failed: %d", smtpcode);
1238 result = CURLE_RECV_ERROR;
1241 /* Temporarily add the LF character back and send as body to the client */
1242 if(!data->set.opt_no_body) {
1244 result = Curl_client_write(conn, CLIENTWRITE_BODY, line, len + 1);
1250 smtp->rcpt = smtp->rcpt->next;
1253 /* Send the next command */
1254 result = smtp_perform_command(conn);
1257 /* End of DO phase */
1258 state(conn, SMTP_STOP);
1261 /* End of DO phase */
1262 state(conn, SMTP_STOP);
1269 /* For MAIL responses */
1270 static CURLcode smtp_state_mail_resp(struct connectdata *conn, int smtpcode,
1273 CURLcode result = CURLE_OK;
1274 struct SessionHandle *data = conn->data;
1276 (void)instate; /* no use for this yet */
1278 if(smtpcode/100 != 2) {
1279 failf(data, "MAIL failed: %d", smtpcode);
1280 result = CURLE_SEND_ERROR;
1283 /* Start the RCPT TO command */
1284 result = smtp_perform_rcpt_to(conn);
1289 /* For RCPT responses */
1290 static CURLcode smtp_state_rcpt_resp(struct connectdata *conn, int smtpcode,
1293 CURLcode result = CURLE_OK;
1294 struct SessionHandle *data = conn->data;
1295 struct SMTP *smtp = data->req.protop;
1297 (void)instate; /* no use for this yet */
1299 if(smtpcode/100 != 2) {
1300 failf(data, "RCPT failed: %d", smtpcode);
1301 result = CURLE_SEND_ERROR;
1304 smtp->rcpt = smtp->rcpt->next;
1307 /* Send the next RCPT TO command */
1308 result = smtp_perform_rcpt_to(conn);
1310 /* Send the DATA command */
1311 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "DATA");
1314 state(conn, SMTP_DATA);
1321 /* For DATA response */
1322 static CURLcode smtp_state_data_resp(struct connectdata *conn, int smtpcode,
1325 CURLcode result = CURLE_OK;
1326 struct SessionHandle *data = conn->data;
1328 (void)instate; /* no use for this yet */
1330 if(smtpcode != 354) {
1331 failf(data, "DATA failed: %d", smtpcode);
1332 result = CURLE_SEND_ERROR;
1335 /* Set the progress upload size */
1336 Curl_pgrsSetUploadSize(data, data->set.infilesize);
1339 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL);
1341 /* End of DO phase */
1342 state(conn, SMTP_STOP);
1348 /* For POSTDATA responses, which are received after the entire DATA
1349 part has been sent to the server */
1350 static CURLcode smtp_state_postdata_resp(struct connectdata *conn,
1354 CURLcode result = CURLE_OK;
1356 (void)instate; /* no use for this yet */
1359 result = CURLE_RECV_ERROR;
1361 /* End of DONE phase */
1362 state(conn, SMTP_STOP);
1367 static CURLcode smtp_statemach_act(struct connectdata *conn)
1369 CURLcode result = CURLE_OK;
1370 curl_socket_t sock = conn->sock[FIRSTSOCKET];
1371 struct SessionHandle *data = conn->data;
1373 struct smtp_conn *smtpc = &conn->proto.smtpc;
1374 struct pingpong *pp = &smtpc->pp;
1377 /* Busy upgrading the connection; right now all I/O is SSL/TLS, not SMTP */
1378 if(smtpc->state == SMTP_UPGRADETLS)
1379 return smtp_perform_upgrade_tls(conn);
1381 /* Flush any data that needs to be sent */
1383 return Curl_pp_flushsend(pp);
1386 /* Read the response from the server */
1387 result = Curl_pp_readresp(sock, pp, &smtpcode, &nread);
1391 /* Store the latest response for later retrieval if necessary */
1392 if(smtpc->state != SMTP_QUIT && smtpcode != 1)
1393 data->info.httpcode = smtpcode;
1398 /* We have now received a full SMTP server response */
1399 switch(smtpc->state) {
1400 case SMTP_SERVERGREET:
1401 result = smtp_state_servergreet_resp(conn, smtpcode, smtpc->state);
1405 result = smtp_state_ehlo_resp(conn, smtpcode, smtpc->state);
1409 result = smtp_state_helo_resp(conn, smtpcode, smtpc->state);
1413 result = smtp_state_starttls_resp(conn, smtpcode, smtpc->state);
1416 case SMTP_AUTH_PLAIN:
1417 result = smtp_state_auth_plain_resp(conn, smtpcode, smtpc->state);
1420 case SMTP_AUTH_LOGIN:
1421 result = smtp_state_auth_login_resp(conn, smtpcode, smtpc->state);
1424 case SMTP_AUTH_LOGIN_PASSWD:
1425 result = smtp_state_auth_login_password_resp(conn, smtpcode,
1429 #ifndef CURL_DISABLE_CRYPTO_AUTH
1430 case SMTP_AUTH_CRAMMD5:
1431 result = smtp_state_auth_cram_resp(conn, smtpcode, smtpc->state);
1434 case SMTP_AUTH_DIGESTMD5:
1435 result = smtp_state_auth_digest_resp(conn, smtpcode, smtpc->state);
1438 case SMTP_AUTH_DIGESTMD5_RESP:
1439 result = smtp_state_auth_digest_resp_resp(conn, smtpcode, smtpc->state);
1444 case SMTP_AUTH_NTLM:
1445 result = smtp_state_auth_ntlm_resp(conn, smtpcode, smtpc->state);
1448 case SMTP_AUTH_NTLM_TYPE2MSG:
1449 result = smtp_state_auth_ntlm_type2msg_resp(conn, smtpcode,
1454 case SMTP_AUTH_XOAUTH2:
1455 result = smtp_state_auth_xoauth2_resp(conn, smtpcode, smtpc->state);
1458 case SMTP_AUTH_CANCEL:
1459 result = smtp_state_auth_cancel_resp(conn, smtpcode, smtpc->state);
1462 case SMTP_AUTH_FINAL:
1463 result = smtp_state_auth_final_resp(conn, smtpcode, smtpc->state);
1467 result = smtp_state_command_resp(conn, smtpcode, smtpc->state);
1471 result = smtp_state_mail_resp(conn, smtpcode, smtpc->state);
1475 result = smtp_state_rcpt_resp(conn, smtpcode, smtpc->state);
1479 result = smtp_state_data_resp(conn, smtpcode, smtpc->state);
1483 result = smtp_state_postdata_resp(conn, smtpcode, smtpc->state);
1487 /* fallthrough, just stop! */
1489 /* internal error */
1490 state(conn, SMTP_STOP);
1493 } while(!result && smtpc->state != SMTP_STOP && Curl_pp_moredata(pp));
1498 /* Called repeatedly until done from multi.c */
1499 static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done)
1501 CURLcode result = CURLE_OK;
1502 struct smtp_conn *smtpc = &conn->proto.smtpc;
1504 if((conn->handler->flags & PROTOPT_SSL) && !smtpc->ssldone) {
1505 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone);
1506 if(result || !smtpc->ssldone)
1510 result = Curl_pp_statemach(&smtpc->pp, FALSE);
1511 *done = (smtpc->state == SMTP_STOP) ? TRUE : FALSE;
1516 static CURLcode smtp_block_statemach(struct connectdata *conn)
1518 CURLcode result = CURLE_OK;
1519 struct smtp_conn *smtpc = &conn->proto.smtpc;
1521 while(smtpc->state != SMTP_STOP && !result)
1522 result = Curl_pp_statemach(&smtpc->pp, TRUE);
1527 /* Allocate and initialize the SMTP struct for the current SessionHandle if
1529 static CURLcode smtp_init(struct connectdata *conn)
1531 CURLcode result = CURLE_OK;
1532 struct SessionHandle *data = conn->data;
1535 smtp = data->req.protop = calloc(sizeof(struct SMTP), 1);
1537 result = CURLE_OUT_OF_MEMORY;
1542 /* For the SMTP "protocol connect" and "doing" phases only */
1543 static int smtp_getsock(struct connectdata *conn, curl_socket_t *socks,
1546 return Curl_pp_getsock(&conn->proto.smtpc.pp, socks, numsocks);
1549 /***********************************************************************
1553 * This function should do everything that is to be considered a part of
1554 * the connection phase.
1556 * The variable pointed to by 'done' will be TRUE if the protocol-layer
1557 * connect phase is done when this function returns, or FALSE if not.
1559 static CURLcode smtp_connect(struct connectdata *conn, bool *done)
1561 CURLcode result = CURLE_OK;
1562 struct smtp_conn *smtpc = &conn->proto.smtpc;
1563 struct pingpong *pp = &smtpc->pp;
1565 *done = FALSE; /* default to not done yet */
1567 /* We always support persistent connections in SMTP */
1568 conn->bits.close = FALSE;
1570 /* Set the default response time-out */
1571 pp->response_time = RESP_TIMEOUT;
1572 pp->statemach_act = smtp_statemach_act;
1573 pp->endofresp = smtp_endofresp;
1576 /* Set the default preferred authentication mechanism */
1577 smtpc->prefmech = SASL_AUTH_ANY;
1579 /* Initialise the pingpong layer */
1582 /* Parse the URL options */
1583 result = smtp_parse_url_options(conn);
1587 /* Parse the URL path */
1588 result = smtp_parse_url_path(conn);
1592 /* Start off waiting for the server greeting response */
1593 state(conn, SMTP_SERVERGREET);
1595 result = smtp_multi_statemach(conn, done);
1600 /***********************************************************************
1604 * The DONE function. This does what needs to be done after a single DO has
1607 * Input argument is already checked for validity.
1609 static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
1612 CURLcode result = CURLE_OK;
1613 struct SessionHandle *data = conn->data;
1614 struct SMTP *smtp = data->req.protop;
1615 struct pingpong *pp = &conn->proto.smtpc.pp;
1618 ssize_t bytes_written;
1623 /* When the easy handle is removed from the multi interface while libcurl
1624 is still trying to resolve the host name, the SMTP struct is not yet
1625 initialized. However, the removal action calls Curl_done() which in
1626 turn calls this function, so we simply return success. */
1630 conn->bits.close = TRUE; /* marked for closure */
1631 result = status; /* use the already set error code */
1633 else if(!data->set.connect_only && data->set.upload && data->set.mail_rcpt) {
1634 /* Calculate the EOB taking into account any terminating CRLF from the
1635 previous line of the email or the CRLF of the DATA command when there
1636 is "no mail data". RFC-5321, sect. 4.1.1.4. */
1639 if(smtp->trailing_crlf || !conn->data->set.infilesize) {
1644 /* Send the end of block data */
1645 result = Curl_write(conn, conn->writesockfd, eob, len, &bytes_written);
1649 if(bytes_written != len) {
1650 /* The whole chunk was not sent so keep it around and adjust the
1651 pingpong structure accordingly */
1652 pp->sendthis = strdup(eob);
1654 pp->sendleft = len - bytes_written;
1657 /* Successfully sent so adjust the response timeout relative to now */
1658 pp->response = Curl_tvnow();
1660 state(conn, SMTP_POSTDATA);
1662 /* Run the state-machine
1664 TODO: when the multi interface is used, this _really_ should be using
1665 the smtp_multi_statemach function but we have no general support for
1666 non-blocking DONE operations, not in the multi state machine and with
1667 Curl_done() invokes on several places in the code!
1669 result = smtp_block_statemach(conn);
1672 /* Cleanup our per-request based variables */
1673 Curl_safefree(smtp->custom);
1675 /* Clear the transfer mode for the next request */
1676 smtp->transfer = FTPTRANSFER_BODY;
1681 /***********************************************************************
1685 * This is the actual DO function for SMTP. Transfer a mail, send a command
1686 * or get some data according to the options previously setup.
1688 static CURLcode smtp_perform(struct connectdata *conn, bool *connected,
1691 /* This is SMTP and no proxy */
1692 CURLcode result = CURLE_OK;
1693 struct SessionHandle *data = conn->data;
1694 struct SMTP *smtp = data->req.protop;
1696 DEBUGF(infof(conn->data, "DO phase starts\n"));
1698 if(data->set.opt_no_body) {
1699 /* Requested no body means no transfer */
1700 smtp->transfer = FTPTRANSFER_INFO;
1703 *dophase_done = FALSE; /* not done yet */
1705 /* Store the first recipient (or NULL if not specified) */
1706 smtp->rcpt = data->set.mail_rcpt;
1708 /* Start the first command in the DO phase */
1709 if(data->set.upload && data->set.mail_rcpt)
1711 result = smtp_perform_mail(conn);
1713 /* SMTP based command (VRFY, EXPN, NOOP, RSET or HELP) */
1714 result = smtp_perform_command(conn);
1719 /* Run the state-machine */
1720 result = smtp_multi_statemach(conn, dophase_done);
1722 *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1725 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1730 /***********************************************************************
1734 * This function is registered as 'curl_do' function. It decodes the path
1735 * parts etc as a wrapper to the actual DO function (smtp_perform).
1737 * The input argument is already checked for validity.
1739 static CURLcode smtp_do(struct connectdata *conn, bool *done)
1741 CURLcode result = CURLE_OK;
1743 *done = FALSE; /* default to false */
1745 /* Parse the custom request */
1746 result = smtp_parse_custom_request(conn);
1750 result = smtp_regular_transfer(conn, done);
1755 /***********************************************************************
1759 * Disconnect from an SMTP server. Cleanup protocol-specific per-connection
1760 * resources. BLOCKING.
1762 static CURLcode smtp_disconnect(struct connectdata *conn, bool dead_connection)
1764 struct smtp_conn *smtpc = &conn->proto.smtpc;
1766 /* We cannot send quit unconditionally. If this connection is stale or
1767 bad in any way, sending quit and waiting around here will make the
1768 disconnect wait in vain and cause more problems than we need to. */
1770 /* The SMTP session may or may not have been allocated/setup at this
1772 if(!dead_connection && smtpc->pp.conn && smtpc->pp.conn->bits.protoconnstart)
1773 if(!smtp_perform_quit(conn))
1774 (void)smtp_block_statemach(conn); /* ignore errors on QUIT */
1776 /* Disconnect from the server */
1777 Curl_pp_disconnect(&smtpc->pp);
1779 /* Cleanup the SASL module */
1780 Curl_sasl_cleanup(conn, smtpc->authused);
1782 /* Cleanup our connection based variables */
1783 Curl_safefree(smtpc->domain);
1788 /* Call this when the DO phase has completed */
1789 static CURLcode smtp_dophase_done(struct connectdata *conn, bool connected)
1791 struct SMTP *smtp = conn->data->req.protop;
1795 if(smtp->transfer != FTPTRANSFER_BODY)
1796 /* no data to transfer */
1797 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1802 /* Called from multi.c while DOing */
1803 static CURLcode smtp_doing(struct connectdata *conn, bool *dophase_done)
1805 CURLcode result = smtp_multi_statemach(conn, dophase_done);
1808 DEBUGF(infof(conn->data, "DO phase failed\n"));
1809 else if(*dophase_done) {
1810 result = smtp_dophase_done(conn, FALSE /* not connected */);
1812 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1818 /***********************************************************************
1820 * smtp_regular_transfer()
1822 * The input argument is already checked for validity.
1824 * Performs all commands done before a regular transfer between a local and a
1827 static CURLcode smtp_regular_transfer(struct connectdata *conn,
1830 CURLcode result = CURLE_OK;
1831 bool connected = FALSE;
1832 struct SessionHandle *data = conn->data;
1834 /* Make sure size is unknown at this point */
1835 data->req.size = -1;
1837 /* Set the progress data */
1838 Curl_pgrsSetUploadCounter(data, 0);
1839 Curl_pgrsSetDownloadCounter(data, 0);
1840 Curl_pgrsSetUploadSize(data, 0);
1841 Curl_pgrsSetDownloadSize(data, 0);
1843 /* Carry out the perform */
1844 result = smtp_perform(conn, &connected, dophase_done);
1846 /* Perform post DO phase operations if necessary */
1847 if(!result && *dophase_done)
1848 result = smtp_dophase_done(conn, connected);
1853 static CURLcode smtp_setup_connection(struct connectdata *conn)
1855 struct SessionHandle *data = conn->data;
1858 if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
1859 /* Unless we have asked to tunnel SMTP operations through the proxy, we
1860 switch and use HTTP operations only */
1861 #ifndef CURL_DISABLE_HTTP
1862 if(conn->handler == &Curl_handler_smtp)
1863 conn->handler = &Curl_handler_smtp_proxy;
1866 conn->handler = &Curl_handler_smtps_proxy;
1868 failf(data, "SMTPS not supported!");
1869 return CURLE_UNSUPPORTED_PROTOCOL;
1872 /* set it up as a HTTP connection instead */
1873 return conn->handler->setup_connection(conn);
1876 failf(data, "SMTP over http proxy requires HTTP support built-in!");
1877 return CURLE_UNSUPPORTED_PROTOCOL;
1881 /* Initialise the SMTP layer */
1882 result = smtp_init(conn);
1886 data->state.path++; /* don't include the initial slash */
1891 /***********************************************************************
1893 * smtp_parse_url_options()
1895 * Parse the URL login options.
1897 static CURLcode smtp_parse_url_options(struct connectdata *conn)
1899 CURLcode result = CURLE_OK;
1900 struct smtp_conn *smtpc = &conn->proto.smtpc;
1901 const char *options = conn->options;
1902 const char *ptr = options;
1905 const char *key = ptr;
1907 while(*ptr && *ptr != '=')
1910 if(strnequal(key, "AUTH", 4)) {
1911 const char *value = ptr + 1;
1913 if(strequal(value, "*"))
1914 smtpc->prefmech = SASL_AUTH_ANY;
1915 else if(strequal(value, SASL_MECH_STRING_LOGIN))
1916 smtpc->prefmech = SASL_MECH_LOGIN;
1917 else if(strequal(value, SASL_MECH_STRING_PLAIN))
1918 smtpc->prefmech = SASL_MECH_PLAIN;
1919 else if(strequal(value, SASL_MECH_STRING_CRAM_MD5))
1920 smtpc->prefmech = SASL_MECH_CRAM_MD5;
1921 else if(strequal(value, SASL_MECH_STRING_DIGEST_MD5))
1922 smtpc->prefmech = SASL_MECH_DIGEST_MD5;
1923 else if(strequal(value, SASL_MECH_STRING_GSSAPI))
1924 smtpc->prefmech = SASL_MECH_GSSAPI;
1925 else if(strequal(value, SASL_MECH_STRING_NTLM))
1926 smtpc->prefmech = SASL_MECH_NTLM;
1927 else if(strequal(value, SASL_MECH_STRING_XOAUTH2))
1928 smtpc->prefmech = SASL_MECH_XOAUTH2;
1930 smtpc->prefmech = SASL_AUTH_NONE;
1933 result = CURLE_URL_MALFORMAT;
1939 /***********************************************************************
1941 * smtp_parse_url_path()
1943 * Parse the URL path into separate path components.
1945 static CURLcode smtp_parse_url_path(struct connectdata *conn)
1947 /* The SMTP struct is already initialised in smtp_connect() */
1948 struct SessionHandle *data = conn->data;
1949 struct smtp_conn *smtpc = &conn->proto.smtpc;
1950 const char *path = data->state.path;
1951 char localhost[HOSTNAME_MAX + 1];
1953 /* Calculate the path if necessary */
1955 if(!Curl_gethostname(localhost, sizeof(localhost)))
1961 /* URL decode the path and use it as the domain in our EHLO */
1962 return Curl_urldecode(conn->data, path, 0, &smtpc->domain, NULL, TRUE);
1965 /***********************************************************************
1967 * smtp_parse_custom_request()
1969 * Parse the custom request.
1971 static CURLcode smtp_parse_custom_request(struct connectdata *conn)
1973 CURLcode result = CURLE_OK;
1974 struct SessionHandle *data = conn->data;
1975 struct SMTP *smtp = data->req.protop;
1976 const char *custom = data->set.str[STRING_CUSTOMREQUEST];
1978 /* URL decode the custom request */
1980 result = Curl_urldecode(data, custom, 0, &smtp->custom, NULL, TRUE);
1985 /***********************************************************************
1987 * smtp_calc_sasl_details()
1989 * Calculate the required login details for SASL authentication.
1991 static CURLcode smtp_calc_sasl_details(struct connectdata *conn,
1993 char **initresp, size_t *len,
1994 smtpstate *state1, smtpstate *state2)
1996 CURLcode result = CURLE_OK;
1997 struct SessionHandle *data = conn->data;
1998 struct smtp_conn *smtpc = &conn->proto.smtpc;
2000 /* Calculate the supported authentication mechanism, by decreasing order of
2001 security, as well as the initial response where appropriate */
2002 #ifndef CURL_DISABLE_CRYPTO_AUTH
2003 if((smtpc->authmechs & SASL_MECH_DIGEST_MD5) &&
2004 (smtpc->prefmech & SASL_MECH_DIGEST_MD5)) {
2005 *mech = SASL_MECH_STRING_DIGEST_MD5;
2006 *state1 = SMTP_AUTH_DIGESTMD5;
2007 smtpc->authused = SASL_MECH_DIGEST_MD5;
2009 else if((smtpc->authmechs & SASL_MECH_CRAM_MD5) &&
2010 (smtpc->prefmech & SASL_MECH_CRAM_MD5)) {
2011 *mech = SASL_MECH_STRING_CRAM_MD5;
2012 *state1 = SMTP_AUTH_CRAMMD5;
2013 smtpc->authused = SASL_MECH_CRAM_MD5;
2018 if((smtpc->authmechs & SASL_MECH_NTLM) &&
2019 (smtpc->prefmech & SASL_MECH_NTLM)) {
2020 *mech = SASL_MECH_STRING_NTLM;
2021 *state1 = SMTP_AUTH_NTLM;
2022 *state2 = SMTP_AUTH_NTLM_TYPE2MSG;
2023 smtpc->authused = SASL_MECH_NTLM;
2025 if(data->set.sasl_ir)
2026 result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
2032 if(((smtpc->authmechs & SASL_MECH_XOAUTH2) &&
2033 (smtpc->prefmech & SASL_MECH_XOAUTH2) &&
2034 (smtpc->prefmech != SASL_AUTH_ANY)) || conn->xoauth2_bearer) {
2035 *mech = SASL_MECH_STRING_XOAUTH2;
2036 *state1 = SMTP_AUTH_XOAUTH2;
2037 *state2 = SMTP_AUTH_FINAL;
2038 smtpc->authused = SASL_MECH_XOAUTH2;
2040 if(data->set.sasl_ir)
2041 result = Curl_sasl_create_xoauth2_message(data, conn->user,
2042 conn->xoauth2_bearer,
2045 else if((smtpc->authmechs & SASL_MECH_LOGIN) &&
2046 (smtpc->prefmech & SASL_MECH_LOGIN)) {
2047 *mech = SASL_MECH_STRING_LOGIN;
2048 *state1 = SMTP_AUTH_LOGIN;
2049 *state2 = SMTP_AUTH_LOGIN_PASSWD;
2050 smtpc->authused = SASL_MECH_LOGIN;
2052 if(data->set.sasl_ir)
2053 result = Curl_sasl_create_login_message(data, conn->user, initresp, len);
2055 else if((smtpc->authmechs & SASL_MECH_PLAIN) &&
2056 (smtpc->prefmech & SASL_MECH_PLAIN)) {
2057 *mech = SASL_MECH_STRING_PLAIN;
2058 *state1 = SMTP_AUTH_PLAIN;
2059 *state2 = SMTP_AUTH_FINAL;
2060 smtpc->authused = SASL_MECH_PLAIN;
2062 if(data->set.sasl_ir)
2063 result = Curl_sasl_create_plain_message(data, conn->user, conn->passwd,
2070 CURLcode Curl_smtp_escape_eob(struct connectdata *conn, ssize_t nread)
2072 /* When sending a SMTP payload we must detect CRLF. sequences making sure
2073 they are sent as CRLF.. instead, as a . on the beginning of a line will
2074 be deleted by the server when not part of an EOB terminator and a
2075 genuine CRLF.CRLF which isn't escaped will wrongly be detected as end of
2080 struct SessionHandle *data = conn->data;
2081 struct SMTP *smtp = data->req.protop;
2083 /* Do we need to allocate the scatch buffer? */
2084 if(!data->state.scratch) {
2085 data->state.scratch = malloc(2 * BUFSIZE);
2087 if(!data->state.scratch) {
2088 failf (data, "Failed to alloc scratch buffer!");
2089 return CURLE_OUT_OF_MEMORY;
2093 /* This loop can be improved by some kind of Boyer-Moore style of
2094 approach but that is saved for later... */
2095 for(i = 0, si = 0; i < nread; i++) {
2096 if(SMTP_EOB[smtp->eob] == data->req.upload_fromhere[i]) {
2099 /* Is the EOB potentially the terminating CRLF? */
2100 if(2 == smtp->eob || SMTP_EOB_LEN == smtp->eob)
2101 smtp->trailing_crlf = TRUE;
2103 smtp->trailing_crlf = FALSE;
2105 else if(smtp->eob) {
2106 /* A previous substring matched so output that first */
2107 memcpy(&data->state.scratch[si], SMTP_EOB, smtp->eob);
2110 /* Then compare the first byte */
2111 if(SMTP_EOB[0] == data->req.upload_fromhere[i])
2116 /* Reset the trailing CRLF flag as there was more data */
2117 smtp->trailing_crlf = FALSE;
2120 /* Do we have a match for CRLF. as per RFC-5321, sect. 4.5.2 */
2121 if(SMTP_EOB_FIND_LEN == smtp->eob) {
2122 /* Copy the replacement data to the target buffer */
2123 memcpy(&data->state.scratch[si], SMTP_EOB_REPL, SMTP_EOB_REPL_LEN);
2124 si += SMTP_EOB_REPL_LEN;
2128 data->state.scratch[si++] = data->req.upload_fromhere[i];
2132 /* A substring matched before processing ended so output that now */
2133 memcpy(&data->state.scratch[si], SMTP_EOB, smtp->eob);
2139 /* Only use the new buffer if we replaced something */
2142 /* Upload from the new (replaced) buffer instead */
2143 data->req.upload_fromhere = data->state.scratch;
2145 /* Set the new amount too */
2146 data->req.upload_present = nread;
2152 #endif /* CURL_DISABLE_SMTP */