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"
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_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("%" CURL_FORMAT_CURL_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 CURLcode result = CURLE_OK;
1193 struct SessionHandle *data = conn->data;
1194 struct smtp_conn *smtpc = &conn->proto.smtpc;
1195 const char *mech = NULL;
1196 char *initresp = NULL;
1198 smtpstate state1 = SMTP_STOP;
1199 smtpstate state2 = SMTP_STOP;
1202 (void)instate; /* no use for this yet */
1204 /* Remove the offending mechanism from the supported list */
1205 smtpc->authmechs ^= smtpc->authused;
1207 /* Calculate alternative SASL login details */
1208 result = smtp_calc_sasl_details(conn, &mech, &initresp, &len, &state1,
1212 /* Do we have any mechanisms left? */
1214 /* Retry SASL based authentication */
1215 result = smtp_perform_auth(conn, mech, initresp, len, state1, state2);
1217 Curl_safefree(initresp);
1220 failf(data, "Authentication cancelled");
1222 result = CURLE_LOGIN_DENIED;
1229 /* For final responses in the AUTH sequence */
1230 static CURLcode smtp_state_auth_final_resp(struct connectdata *conn,
1234 CURLcode result = CURLE_OK;
1235 struct SessionHandle *data = conn->data;
1237 (void)instate; /* no use for this yet */
1239 if(smtpcode != 235) {
1240 failf(data, "Authentication failed: %d", smtpcode);
1241 result = CURLE_LOGIN_DENIED;
1244 /* End of connect phase */
1245 state(conn, SMTP_STOP);
1250 /* For command responses */
1251 static CURLcode smtp_state_command_resp(struct connectdata *conn, int smtpcode,
1254 CURLcode result = CURLE_OK;
1255 struct SessionHandle *data = conn->data;
1256 struct SMTP *smtp = data->req.protop;
1257 char *line = data->state.buffer;
1258 size_t len = strlen(line);
1260 (void)instate; /* no use for this yet */
1262 if((smtp->rcpt && smtpcode/100 != 2 && smtpcode != 553 && smtpcode != 1) ||
1263 (!smtp->rcpt && smtpcode/100 != 2 && smtpcode != 1)) {
1264 failf(data, "Command failed: %d", smtpcode);
1265 result = CURLE_RECV_ERROR;
1268 /* Temporarily add the LF character back and send as body to the client */
1269 if(!data->set.opt_no_body) {
1271 result = Curl_client_write(conn, CLIENTWRITE_BODY, line, len + 1);
1277 smtp->rcpt = smtp->rcpt->next;
1280 /* Send the next command */
1281 result = smtp_perform_command(conn);
1284 /* End of DO phase */
1285 state(conn, SMTP_STOP);
1288 /* End of DO phase */
1289 state(conn, SMTP_STOP);
1296 /* For MAIL responses */
1297 static CURLcode smtp_state_mail_resp(struct connectdata *conn, int smtpcode,
1300 CURLcode result = CURLE_OK;
1301 struct SessionHandle *data = conn->data;
1303 (void)instate; /* no use for this yet */
1305 if(smtpcode/100 != 2) {
1306 failf(data, "MAIL failed: %d", smtpcode);
1307 result = CURLE_SEND_ERROR;
1310 /* Start the RCPT TO command */
1311 result = smtp_perform_rcpt_to(conn);
1316 /* For RCPT responses */
1317 static CURLcode smtp_state_rcpt_resp(struct connectdata *conn, int smtpcode,
1320 CURLcode result = CURLE_OK;
1321 struct SessionHandle *data = conn->data;
1322 struct SMTP *smtp = data->req.protop;
1324 (void)instate; /* no use for this yet */
1326 if(smtpcode/100 != 2) {
1327 failf(data, "RCPT failed: %d", smtpcode);
1328 result = CURLE_SEND_ERROR;
1331 smtp->rcpt = smtp->rcpt->next;
1334 /* Send the next RCPT TO command */
1335 result = smtp_perform_rcpt_to(conn);
1337 /* Send the DATA command */
1338 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "DATA");
1341 state(conn, SMTP_DATA);
1348 /* For DATA response */
1349 static CURLcode smtp_state_data_resp(struct connectdata *conn, int smtpcode,
1352 CURLcode result = CURLE_OK;
1353 struct SessionHandle *data = conn->data;
1355 (void)instate; /* no use for this yet */
1357 if(smtpcode != 354) {
1358 failf(data, "DATA failed: %d", smtpcode);
1359 result = CURLE_SEND_ERROR;
1362 /* Set the progress upload size */
1363 Curl_pgrsSetUploadSize(data, data->set.infilesize);
1366 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL);
1368 /* End of DO phase */
1369 state(conn, SMTP_STOP);
1375 /* For POSTDATA responses, which are received after the entire DATA
1376 part has been sent to the server */
1377 static CURLcode smtp_state_postdata_resp(struct connectdata *conn,
1381 CURLcode result = CURLE_OK;
1383 (void)instate; /* no use for this yet */
1386 result = CURLE_RECV_ERROR;
1388 /* End of DONE phase */
1389 state(conn, SMTP_STOP);
1394 static CURLcode smtp_statemach_act(struct connectdata *conn)
1396 CURLcode result = CURLE_OK;
1397 curl_socket_t sock = conn->sock[FIRSTSOCKET];
1398 struct SessionHandle *data = conn->data;
1400 struct smtp_conn *smtpc = &conn->proto.smtpc;
1401 struct pingpong *pp = &smtpc->pp;
1404 /* Busy upgrading the connection; right now all I/O is SSL/TLS, not SMTP */
1405 if(smtpc->state == SMTP_UPGRADETLS)
1406 return smtp_perform_upgrade_tls(conn);
1408 /* Flush any data that needs to be sent */
1410 return Curl_pp_flushsend(pp);
1413 /* Read the response from the server */
1414 result = Curl_pp_readresp(sock, pp, &smtpcode, &nread);
1418 /* Store the latest response for later retrieval if necessary */
1419 if(smtpc->state != SMTP_QUIT && smtpcode != 1)
1420 data->info.httpcode = smtpcode;
1425 /* We have now received a full SMTP server response */
1426 switch(smtpc->state) {
1427 case SMTP_SERVERGREET:
1428 result = smtp_state_servergreet_resp(conn, smtpcode, smtpc->state);
1432 result = smtp_state_ehlo_resp(conn, smtpcode, smtpc->state);
1436 result = smtp_state_helo_resp(conn, smtpcode, smtpc->state);
1440 result = smtp_state_starttls_resp(conn, smtpcode, smtpc->state);
1443 case SMTP_AUTH_PLAIN:
1444 result = smtp_state_auth_plain_resp(conn, smtpcode, smtpc->state);
1447 case SMTP_AUTH_LOGIN:
1448 result = smtp_state_auth_login_resp(conn, smtpcode, smtpc->state);
1451 case SMTP_AUTH_LOGIN_PASSWD:
1452 result = smtp_state_auth_login_password_resp(conn, smtpcode,
1456 #ifndef CURL_DISABLE_CRYPTO_AUTH
1457 case SMTP_AUTH_CRAMMD5:
1458 result = smtp_state_auth_cram_resp(conn, smtpcode, smtpc->state);
1461 case SMTP_AUTH_DIGESTMD5:
1462 result = smtp_state_auth_digest_resp(conn, smtpcode, smtpc->state);
1465 case SMTP_AUTH_DIGESTMD5_RESP:
1466 result = smtp_state_auth_digest_resp_resp(conn, smtpcode, smtpc->state);
1471 case SMTP_AUTH_NTLM:
1472 result = smtp_state_auth_ntlm_resp(conn, smtpcode, smtpc->state);
1475 case SMTP_AUTH_NTLM_TYPE2MSG:
1476 result = smtp_state_auth_ntlm_type2msg_resp(conn, smtpcode,
1481 case SMTP_AUTH_XOAUTH2:
1482 result = smtp_state_auth_xoauth2_resp(conn, smtpcode, smtpc->state);
1485 case SMTP_AUTH_CANCEL:
1486 result = smtp_state_auth_cancel_resp(conn, smtpcode, smtpc->state);
1489 case SMTP_AUTH_FINAL:
1490 result = smtp_state_auth_final_resp(conn, smtpcode, smtpc->state);
1494 result = smtp_state_command_resp(conn, smtpcode, smtpc->state);
1498 result = smtp_state_mail_resp(conn, smtpcode, smtpc->state);
1502 result = smtp_state_rcpt_resp(conn, smtpcode, smtpc->state);
1506 result = smtp_state_data_resp(conn, smtpcode, smtpc->state);
1510 result = smtp_state_postdata_resp(conn, smtpcode, smtpc->state);
1514 /* fallthrough, just stop! */
1516 /* internal error */
1517 state(conn, SMTP_STOP);
1520 } while(!result && smtpc->state != SMTP_STOP && Curl_pp_moredata(pp));
1525 /* Called repeatedly until done from multi.c */
1526 static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done)
1528 CURLcode result = CURLE_OK;
1529 struct smtp_conn *smtpc = &conn->proto.smtpc;
1531 if((conn->handler->flags & PROTOPT_SSL) && !smtpc->ssldone) {
1532 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone);
1533 if(result || !smtpc->ssldone)
1537 result = Curl_pp_statemach(&smtpc->pp, FALSE);
1538 *done = (smtpc->state == SMTP_STOP) ? TRUE : FALSE;
1543 static CURLcode smtp_block_statemach(struct connectdata *conn)
1545 CURLcode result = CURLE_OK;
1546 struct smtp_conn *smtpc = &conn->proto.smtpc;
1548 while(smtpc->state != SMTP_STOP && !result)
1549 result = Curl_pp_statemach(&smtpc->pp, TRUE);
1554 /* Allocate and initialize the SMTP struct for the current SessionHandle if
1556 static CURLcode smtp_init(struct connectdata *conn)
1558 CURLcode result = CURLE_OK;
1559 struct SessionHandle *data = conn->data;
1562 smtp = data->req.protop = calloc(sizeof(struct SMTP), 1);
1564 result = CURLE_OUT_OF_MEMORY;
1569 /* For the SMTP "protocol connect" and "doing" phases only */
1570 static int smtp_getsock(struct connectdata *conn, curl_socket_t *socks,
1573 return Curl_pp_getsock(&conn->proto.smtpc.pp, socks, numsocks);
1576 /***********************************************************************
1580 * This function should do everything that is to be considered a part of
1581 * the connection phase.
1583 * The variable pointed to by 'done' will be TRUE if the protocol-layer
1584 * connect phase is done when this function returns, or FALSE if not.
1586 static CURLcode smtp_connect(struct connectdata *conn, bool *done)
1588 CURLcode result = CURLE_OK;
1589 struct smtp_conn *smtpc = &conn->proto.smtpc;
1590 struct pingpong *pp = &smtpc->pp;
1592 *done = FALSE; /* default to not done yet */
1594 /* We always support persistent connections in SMTP */
1595 conn->bits.close = FALSE;
1597 /* Set the default response time-out */
1598 pp->response_time = RESP_TIMEOUT;
1599 pp->statemach_act = smtp_statemach_act;
1600 pp->endofresp = smtp_endofresp;
1603 /* Set the default preferred authentication mechanism */
1604 smtpc->prefmech = SASL_AUTH_ANY;
1606 /* Initialise the pingpong layer */
1609 /* Parse the URL options */
1610 result = smtp_parse_url_options(conn);
1614 /* Parse the URL path */
1615 result = smtp_parse_url_path(conn);
1619 /* Start off waiting for the server greeting response */
1620 state(conn, SMTP_SERVERGREET);
1622 result = smtp_multi_statemach(conn, done);
1627 /***********************************************************************
1631 * The DONE function. This does what needs to be done after a single DO has
1634 * Input argument is already checked for validity.
1636 static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
1639 CURLcode result = CURLE_OK;
1640 struct SessionHandle *data = conn->data;
1641 struct SMTP *smtp = data->req.protop;
1642 struct pingpong *pp = &conn->proto.smtpc.pp;
1645 ssize_t bytes_written;
1650 /* When the easy handle is removed from the multi interface while libcurl
1651 is still trying to resolve the host name, the SMTP struct is not yet
1652 initialized. However, the removal action calls Curl_done() which in
1653 turn calls this function, so we simply return success. */
1657 conn->bits.close = TRUE; /* marked for closure */
1658 result = status; /* use the already set error code */
1660 else if(!data->set.connect_only && data->set.upload && data->set.mail_rcpt) {
1661 /* Calculate the EOB taking into account any terminating CRLF from the
1662 previous line of the email or the CRLF of the DATA command when there
1663 is "no mail data". RFC-5321, sect. 4.1.1.4. */
1666 if(smtp->trailing_crlf || !conn->data->set.infilesize) {
1671 /* Send the end of block data */
1672 result = Curl_write(conn, conn->writesockfd, eob, len, &bytes_written);
1676 if(bytes_written != len) {
1677 /* The whole chunk was not sent so keep it around and adjust the
1678 pingpong structure accordingly */
1679 pp->sendthis = strdup(eob);
1681 pp->sendleft = len - bytes_written;
1684 /* Successfully sent so adjust the response timeout relative to now */
1685 pp->response = Curl_tvnow();
1687 state(conn, SMTP_POSTDATA);
1689 /* Run the state-machine
1691 TODO: when the multi interface is used, this _really_ should be using
1692 the smtp_multi_statemach function but we have no general support for
1693 non-blocking DONE operations, not in the multi state machine and with
1694 Curl_done() invokes on several places in the code!
1696 result = smtp_block_statemach(conn);
1699 /* Cleanup our per-request based variables */
1700 Curl_safefree(smtp->custom);
1702 /* Clear the transfer mode for the next request */
1703 smtp->transfer = FTPTRANSFER_BODY;
1708 /***********************************************************************
1712 * This is the actual DO function for SMTP. Transfer a mail, send a command
1713 * or get some data according to the options previously setup.
1715 static CURLcode smtp_perform(struct connectdata *conn, bool *connected,
1718 /* This is SMTP and no proxy */
1719 CURLcode result = CURLE_OK;
1720 struct SessionHandle *data = conn->data;
1721 struct SMTP *smtp = data->req.protop;
1723 DEBUGF(infof(conn->data, "DO phase starts\n"));
1725 if(data->set.opt_no_body) {
1726 /* Requested no body means no transfer */
1727 smtp->transfer = FTPTRANSFER_INFO;
1730 *dophase_done = FALSE; /* not done yet */
1732 /* Store the first recipient (or NULL if not specified) */
1733 smtp->rcpt = data->set.mail_rcpt;
1735 /* Start the first command in the DO phase */
1736 if(data->set.upload && data->set.mail_rcpt)
1738 result = smtp_perform_mail(conn);
1740 /* SMTP based command (VRFY, EXPN, NOOP, RSET or HELP) */
1741 result = smtp_perform_command(conn);
1746 /* Run the state-machine */
1747 result = smtp_multi_statemach(conn, dophase_done);
1749 *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1752 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1757 /***********************************************************************
1761 * This function is registered as 'curl_do' function. It decodes the path
1762 * parts etc as a wrapper to the actual DO function (smtp_perform).
1764 * The input argument is already checked for validity.
1766 static CURLcode smtp_do(struct connectdata *conn, bool *done)
1768 CURLcode result = CURLE_OK;
1770 *done = FALSE; /* default to false */
1772 /* Parse the custom request */
1773 result = smtp_parse_custom_request(conn);
1777 result = smtp_regular_transfer(conn, done);
1782 /***********************************************************************
1786 * Disconnect from an SMTP server. Cleanup protocol-specific per-connection
1787 * resources. BLOCKING.
1789 static CURLcode smtp_disconnect(struct connectdata *conn, bool dead_connection)
1791 struct smtp_conn *smtpc = &conn->proto.smtpc;
1793 /* We cannot send quit unconditionally. If this connection is stale or
1794 bad in any way, sending quit and waiting around here will make the
1795 disconnect wait in vain and cause more problems than we need to. */
1797 /* The SMTP session may or may not have been allocated/setup at this
1799 if(!dead_connection && smtpc->pp.conn && smtpc->pp.conn->bits.protoconnstart)
1800 if(!smtp_perform_quit(conn))
1801 (void)smtp_block_statemach(conn); /* ignore errors on QUIT */
1803 /* Disconnect from the server */
1804 Curl_pp_disconnect(&smtpc->pp);
1806 /* Cleanup the SASL module */
1807 Curl_sasl_cleanup(conn, smtpc->authused);
1809 /* Cleanup our connection based variables */
1810 Curl_safefree(smtpc->domain);
1815 /* Call this when the DO phase has completed */
1816 static CURLcode smtp_dophase_done(struct connectdata *conn, bool connected)
1818 struct SMTP *smtp = conn->data->req.protop;
1822 if(smtp->transfer != FTPTRANSFER_BODY)
1823 /* no data to transfer */
1824 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1829 /* Called from multi.c while DOing */
1830 static CURLcode smtp_doing(struct connectdata *conn, bool *dophase_done)
1832 CURLcode result = smtp_multi_statemach(conn, dophase_done);
1835 DEBUGF(infof(conn->data, "DO phase failed\n"));
1836 else if(*dophase_done) {
1837 result = smtp_dophase_done(conn, FALSE /* not connected */);
1839 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1845 /***********************************************************************
1847 * smtp_regular_transfer()
1849 * The input argument is already checked for validity.
1851 * Performs all commands done before a regular transfer between a local and a
1854 static CURLcode smtp_regular_transfer(struct connectdata *conn,
1857 CURLcode result = CURLE_OK;
1858 bool connected = FALSE;
1859 struct SessionHandle *data = conn->data;
1861 /* Make sure size is unknown at this point */
1862 data->req.size = -1;
1864 /* Set the progress data */
1865 Curl_pgrsSetUploadCounter(data, 0);
1866 Curl_pgrsSetDownloadCounter(data, 0);
1867 Curl_pgrsSetUploadSize(data, 0);
1868 Curl_pgrsSetDownloadSize(data, 0);
1870 /* Carry out the perform */
1871 result = smtp_perform(conn, &connected, dophase_done);
1873 /* Perform post DO phase operations if necessary */
1874 if(!result && *dophase_done)
1875 result = smtp_dophase_done(conn, connected);
1880 static CURLcode smtp_setup_connection(struct connectdata *conn)
1882 struct SessionHandle *data = conn->data;
1885 if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
1886 /* Unless we have asked to tunnel SMTP operations through the proxy, we
1887 switch and use HTTP operations only */
1888 #ifndef CURL_DISABLE_HTTP
1889 if(conn->handler == &Curl_handler_smtp)
1890 conn->handler = &Curl_handler_smtp_proxy;
1893 conn->handler = &Curl_handler_smtps_proxy;
1895 failf(data, "SMTPS not supported!");
1896 return CURLE_UNSUPPORTED_PROTOCOL;
1899 /* set it up as a HTTP connection instead */
1900 return conn->handler->setup_connection(conn);
1903 failf(data, "SMTP over http proxy requires HTTP support built-in!");
1904 return CURLE_UNSUPPORTED_PROTOCOL;
1908 /* Initialise the SMTP layer */
1909 result = smtp_init(conn);
1913 data->state.path++; /* don't include the initial slash */
1918 /***********************************************************************
1920 * smtp_parse_url_options()
1922 * Parse the URL login options.
1924 static CURLcode smtp_parse_url_options(struct connectdata *conn)
1926 CURLcode result = CURLE_OK;
1927 struct smtp_conn *smtpc = &conn->proto.smtpc;
1928 const char *options = conn->options;
1929 const char *ptr = options;
1932 while(ptr && *ptr) {
1933 const char *key = ptr;
1935 while(*ptr && *ptr != '=')
1938 if(strnequal(key, "AUTH", 4)) {
1940 const char *value = ++ptr;
1944 smtpc->prefmech = SASL_AUTH_NONE;
1947 while(*ptr && *ptr != ';') {
1952 if(strnequal(value, "*", len))
1953 smtpc->prefmech = SASL_AUTH_ANY;
1954 else if(strnequal(value, SASL_MECH_STRING_LOGIN, len))
1955 smtpc->prefmech |= SASL_MECH_LOGIN;
1956 else if(strnequal(value, SASL_MECH_STRING_PLAIN, len))
1957 smtpc->prefmech |= SASL_MECH_PLAIN;
1958 else if(strnequal(value, SASL_MECH_STRING_CRAM_MD5, len))
1959 smtpc->prefmech |= SASL_MECH_CRAM_MD5;
1960 else if(strnequal(value, SASL_MECH_STRING_DIGEST_MD5, len))
1961 smtpc->prefmech |= SASL_MECH_DIGEST_MD5;
1962 else if(strnequal(value, SASL_MECH_STRING_GSSAPI, len))
1963 smtpc->prefmech |= SASL_MECH_GSSAPI;
1964 else if(strnequal(value, SASL_MECH_STRING_NTLM, len))
1965 smtpc->prefmech |= SASL_MECH_NTLM;
1966 else if(strnequal(value, SASL_MECH_STRING_XOAUTH2, len))
1967 smtpc->prefmech |= SASL_MECH_XOAUTH2;
1973 result = CURLE_URL_MALFORMAT;
1979 /***********************************************************************
1981 * smtp_parse_url_path()
1983 * Parse the URL path into separate path components.
1985 static CURLcode smtp_parse_url_path(struct connectdata *conn)
1987 /* The SMTP struct is already initialised in smtp_connect() */
1988 struct SessionHandle *data = conn->data;
1989 struct smtp_conn *smtpc = &conn->proto.smtpc;
1990 const char *path = data->state.path;
1991 char localhost[HOSTNAME_MAX + 1];
1993 /* Calculate the path if necessary */
1995 if(!Curl_gethostname(localhost, sizeof(localhost)))
2001 /* URL decode the path and use it as the domain in our EHLO */
2002 return Curl_urldecode(conn->data, path, 0, &smtpc->domain, NULL, TRUE);
2005 /***********************************************************************
2007 * smtp_parse_custom_request()
2009 * Parse the custom request.
2011 static CURLcode smtp_parse_custom_request(struct connectdata *conn)
2013 CURLcode result = CURLE_OK;
2014 struct SessionHandle *data = conn->data;
2015 struct SMTP *smtp = data->req.protop;
2016 const char *custom = data->set.str[STRING_CUSTOMREQUEST];
2018 /* URL decode the custom request */
2020 result = Curl_urldecode(data, custom, 0, &smtp->custom, NULL, TRUE);
2025 /***********************************************************************
2027 * smtp_calc_sasl_details()
2029 * Calculate the required login details for SASL authentication.
2031 static CURLcode smtp_calc_sasl_details(struct connectdata *conn,
2033 char **initresp, size_t *len,
2034 smtpstate *state1, smtpstate *state2)
2036 CURLcode result = CURLE_OK;
2037 struct SessionHandle *data = conn->data;
2038 struct smtp_conn *smtpc = &conn->proto.smtpc;
2040 /* Calculate the supported authentication mechanism, by decreasing order of
2041 security, as well as the initial response where appropriate */
2042 #ifndef CURL_DISABLE_CRYPTO_AUTH
2043 if((smtpc->authmechs & SASL_MECH_DIGEST_MD5) &&
2044 (smtpc->prefmech & SASL_MECH_DIGEST_MD5)) {
2045 *mech = SASL_MECH_STRING_DIGEST_MD5;
2046 *state1 = SMTP_AUTH_DIGESTMD5;
2047 smtpc->authused = SASL_MECH_DIGEST_MD5;
2049 else if((smtpc->authmechs & SASL_MECH_CRAM_MD5) &&
2050 (smtpc->prefmech & SASL_MECH_CRAM_MD5)) {
2051 *mech = SASL_MECH_STRING_CRAM_MD5;
2052 *state1 = SMTP_AUTH_CRAMMD5;
2053 smtpc->authused = SASL_MECH_CRAM_MD5;
2058 if((smtpc->authmechs & SASL_MECH_NTLM) &&
2059 (smtpc->prefmech & SASL_MECH_NTLM)) {
2060 *mech = SASL_MECH_STRING_NTLM;
2061 *state1 = SMTP_AUTH_NTLM;
2062 *state2 = SMTP_AUTH_NTLM_TYPE2MSG;
2063 smtpc->authused = SASL_MECH_NTLM;
2065 if(data->set.sasl_ir)
2066 result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
2072 if(((smtpc->authmechs & SASL_MECH_XOAUTH2) &&
2073 (smtpc->prefmech & SASL_MECH_XOAUTH2) &&
2074 (smtpc->prefmech != SASL_AUTH_ANY)) || conn->xoauth2_bearer) {
2075 *mech = SASL_MECH_STRING_XOAUTH2;
2076 *state1 = SMTP_AUTH_XOAUTH2;
2077 *state2 = SMTP_AUTH_FINAL;
2078 smtpc->authused = SASL_MECH_XOAUTH2;
2080 if(data->set.sasl_ir)
2081 result = Curl_sasl_create_xoauth2_message(data, conn->user,
2082 conn->xoauth2_bearer,
2085 else if((smtpc->authmechs & SASL_MECH_LOGIN) &&
2086 (smtpc->prefmech & SASL_MECH_LOGIN)) {
2087 *mech = SASL_MECH_STRING_LOGIN;
2088 *state1 = SMTP_AUTH_LOGIN;
2089 *state2 = SMTP_AUTH_LOGIN_PASSWD;
2090 smtpc->authused = SASL_MECH_LOGIN;
2092 if(data->set.sasl_ir)
2093 result = Curl_sasl_create_login_message(data, conn->user, initresp, len);
2095 else if((smtpc->authmechs & SASL_MECH_PLAIN) &&
2096 (smtpc->prefmech & SASL_MECH_PLAIN)) {
2097 *mech = SASL_MECH_STRING_PLAIN;
2098 *state1 = SMTP_AUTH_PLAIN;
2099 *state2 = SMTP_AUTH_FINAL;
2100 smtpc->authused = SASL_MECH_PLAIN;
2102 if(data->set.sasl_ir)
2103 result = Curl_sasl_create_plain_message(data, conn->user, conn->passwd,
2110 CURLcode Curl_smtp_escape_eob(struct connectdata *conn, ssize_t nread)
2112 /* When sending a SMTP payload we must detect CRLF. sequences making sure
2113 they are sent as CRLF.. instead, as a . on the beginning of a line will
2114 be deleted by the server when not part of an EOB terminator and a
2115 genuine CRLF.CRLF which isn't escaped will wrongly be detected as end of
2120 struct SessionHandle *data = conn->data;
2121 struct SMTP *smtp = data->req.protop;
2123 /* Do we need to allocate the scatch buffer? */
2124 if(!data->state.scratch) {
2125 data->state.scratch = malloc(2 * BUFSIZE);
2127 if(!data->state.scratch) {
2128 failf (data, "Failed to alloc scratch buffer!");
2129 return CURLE_OUT_OF_MEMORY;
2133 /* This loop can be improved by some kind of Boyer-Moore style of
2134 approach but that is saved for later... */
2135 for(i = 0, si = 0; i < nread; i++) {
2136 if(SMTP_EOB[smtp->eob] == data->req.upload_fromhere[i]) {
2139 /* Is the EOB potentially the terminating CRLF? */
2140 if(2 == smtp->eob || SMTP_EOB_LEN == smtp->eob)
2141 smtp->trailing_crlf = TRUE;
2143 smtp->trailing_crlf = FALSE;
2145 else if(smtp->eob) {
2146 /* A previous substring matched so output that first */
2147 memcpy(&data->state.scratch[si], SMTP_EOB, smtp->eob);
2150 /* Then compare the first byte */
2151 if(SMTP_EOB[0] == data->req.upload_fromhere[i])
2156 /* Reset the trailing CRLF flag as there was more data */
2157 smtp->trailing_crlf = FALSE;
2160 /* Do we have a match for CRLF. as per RFC-5321, sect. 4.5.2 */
2161 if(SMTP_EOB_FIND_LEN == smtp->eob) {
2162 /* Copy the replacement data to the target buffer */
2163 memcpy(&data->state.scratch[si], SMTP_EOB_REPL, SMTP_EOB_REPL_LEN);
2164 si += SMTP_EOB_REPL_LEN;
2168 data->state.scratch[si++] = data->req.upload_fromhere[i];
2172 /* A substring matched before processing ended so output that now */
2173 memcpy(&data->state.scratch[si], SMTP_EOB, smtp->eob);
2179 /* Only use the new buffer if we replaced something */
2182 /* Upload from the new (replaced) buffer instead */
2183 data->req.upload_fromhere = data->state.scratch;
2185 /* Set the new amount too */
2186 data->req.upload_present = nread;
2192 #endif /* CURL_DISABLE_SMTP */