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 * Draft SMTP URL Interface
31 ***************************************************************************/
33 #include "curl_setup.h"
35 #ifndef CURL_DISABLE_SMTP
37 #ifdef HAVE_NETINET_IN_H
38 #include <netinet/in.h>
40 #ifdef HAVE_ARPA_INET_H
41 #include <arpa/inet.h>
44 #include <sys/utsname.h>
54 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
56 #define in_addr_t unsigned long
59 #include <curl/curl.h>
67 #include "http.h" /* for HTTP proxy tunnel stuff */
71 #include "strtoofft.h"
80 #include "curl_gethostname.h"
81 #include "curl_sasl.h"
84 #define _MPRINTF_REPLACE /* use our functions only */
85 #include <curl/mprintf.h>
87 #include "curl_memory.h"
88 /* The last #include file should be: */
91 /* Local API functions */
92 static CURLcode smtp_regular_transfer(struct connectdata *conn, bool *done);
93 static CURLcode smtp_do(struct connectdata *conn, bool *done);
94 static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
96 static CURLcode smtp_connect(struct connectdata *conn, bool *done);
97 static CURLcode smtp_disconnect(struct connectdata *conn, bool dead);
98 static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done);
99 static int smtp_getsock(struct connectdata *conn, curl_socket_t *socks,
101 static CURLcode smtp_doing(struct connectdata *conn, bool *dophase_done);
102 static CURLcode smtp_setup_connection(struct connectdata *conn);
103 static CURLcode smtp_parse_url_options(struct connectdata *conn);
104 static CURLcode smtp_parse_url_path(struct connectdata *conn);
107 * SMTP protocol handler.
110 const struct Curl_handler Curl_handler_smtp = {
112 smtp_setup_connection, /* setup_connection */
114 smtp_done, /* done */
115 ZERO_NULL, /* do_more */
116 smtp_connect, /* connect_it */
117 smtp_multi_statemach, /* connecting */
118 smtp_doing, /* doing */
119 smtp_getsock, /* proto_getsock */
120 smtp_getsock, /* doing_getsock */
121 ZERO_NULL, /* domore_getsock */
122 ZERO_NULL, /* perform_getsock */
123 smtp_disconnect, /* disconnect */
124 ZERO_NULL, /* readwrite */
125 PORT_SMTP, /* defport */
126 CURLPROTO_SMTP, /* protocol */
127 PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY /* flags */
132 * SMTPS protocol handler.
135 const struct Curl_handler Curl_handler_smtps = {
136 "SMTPS", /* scheme */
137 smtp_setup_connection, /* setup_connection */
139 smtp_done, /* done */
140 ZERO_NULL, /* do_more */
141 smtp_connect, /* connect_it */
142 smtp_multi_statemach, /* connecting */
143 smtp_doing, /* doing */
144 smtp_getsock, /* proto_getsock */
145 smtp_getsock, /* doing_getsock */
146 ZERO_NULL, /* domore_getsock */
147 ZERO_NULL, /* perform_getsock */
148 smtp_disconnect, /* disconnect */
149 ZERO_NULL, /* readwrite */
150 PORT_SMTPS, /* defport */
151 CURLPROTO_SMTP | CURLPROTO_SMTPS, /* protocol */
152 PROTOPT_CLOSEACTION | PROTOPT_SSL
153 | PROTOPT_NOURLQUERY /* flags */
157 #ifndef CURL_DISABLE_HTTP
159 * HTTP-proxyed SMTP protocol handler.
162 static const struct Curl_handler Curl_handler_smtp_proxy = {
164 ZERO_NULL, /* setup_connection */
165 Curl_http, /* do_it */
166 Curl_http_done, /* done */
167 ZERO_NULL, /* do_more */
168 ZERO_NULL, /* connect_it */
169 ZERO_NULL, /* connecting */
170 ZERO_NULL, /* doing */
171 ZERO_NULL, /* proto_getsock */
172 ZERO_NULL, /* doing_getsock */
173 ZERO_NULL, /* domore_getsock */
174 ZERO_NULL, /* perform_getsock */
175 ZERO_NULL, /* disconnect */
176 ZERO_NULL, /* readwrite */
177 PORT_SMTP, /* defport */
178 CURLPROTO_HTTP, /* protocol */
179 PROTOPT_NONE /* flags */
184 * HTTP-proxyed SMTPS protocol handler.
187 static const struct Curl_handler Curl_handler_smtps_proxy = {
188 "SMTPS", /* scheme */
189 ZERO_NULL, /* setup_connection */
190 Curl_http, /* do_it */
191 Curl_http_done, /* done */
192 ZERO_NULL, /* do_more */
193 ZERO_NULL, /* connect_it */
194 ZERO_NULL, /* connecting */
195 ZERO_NULL, /* doing */
196 ZERO_NULL, /* proto_getsock */
197 ZERO_NULL, /* doing_getsock */
198 ZERO_NULL, /* domore_getsock */
199 ZERO_NULL, /* perform_getsock */
200 ZERO_NULL, /* disconnect */
201 ZERO_NULL, /* readwrite */
202 PORT_SMTPS, /* defport */
203 CURLPROTO_HTTP, /* protocol */
204 PROTOPT_NONE /* flags */
210 static void smtp_to_smtps(struct connectdata *conn)
212 conn->handler = &Curl_handler_smtps;
215 #define smtp_to_smtps(x) Curl_nop_stmt
218 /* Function that checks for an ending SMTP status code at the start of the
219 given string, but also detects various capabilities from the EHLO response
220 including the supported authentication mechanisms. */
221 static bool smtp_endofresp(struct connectdata *conn, char *line, size_t len,
224 struct smtp_conn *smtpc = &conn->proto.smtpc;
228 if(len < 4 || !ISDIGIT(line[0]) || !ISDIGIT(line[1]) || !ISDIGIT(line[2]))
229 return FALSE; /* Nothing for us */
231 /* Do we have a command response? */
232 result = (line[3] == ' ') ? TRUE : FALSE;
234 *resp = curlx_sltosi(strtol(line, NULL, 10));
236 /* Are we processing EHLO command data? */
237 if(smtpc->state == SMTP_EHLO && (!result || (result && *resp/100 == 2))) {
241 /* Does the server support the STARTTLS capability? */
242 if(len >= 8 && !memcmp(line, "STARTTLS", 8))
243 smtpc->tls_supported = TRUE;
245 /* Does the server support the SIZE capability? */
246 else if(len >= 4 && !memcmp(line, "SIZE", 4))
247 smtpc->size_supported = TRUE;
249 /* Do we have the authentication mechanism list? */
250 else if(len >= 5 && !memcmp(line, "AUTH ", 5)) {
254 /* Loop through the data line */
257 (*line == ' ' || *line == '\t' ||
258 *line == '\r' || *line == '\n')) {
267 /* Extract the word */
268 for(wordlen = 0; wordlen < len && line[wordlen] != ' ' &&
269 line[wordlen] != '\t' && line[wordlen] != '\r' &&
270 line[wordlen] != '\n';)
273 /* Test the word for a matching authentication mechanism */
274 if(wordlen == 5 && !memcmp(line, "LOGIN", 5))
275 smtpc->authmechs |= SASL_MECH_LOGIN;
276 else if(wordlen == 5 && !memcmp(line, "PLAIN", 5))
277 smtpc->authmechs |= SASL_MECH_PLAIN;
278 else if(wordlen == 8 && !memcmp(line, "CRAM-MD5", 8))
279 smtpc->authmechs |= SASL_MECH_CRAM_MD5;
280 else if(wordlen == 10 && !memcmp(line, "DIGEST-MD5", 10))
281 smtpc->authmechs |= SASL_MECH_DIGEST_MD5;
282 else if(wordlen == 6 && !memcmp(line, "GSSAPI", 6))
283 smtpc->authmechs |= SASL_MECH_GSSAPI;
284 else if(wordlen == 8 && !memcmp(line, "EXTERNAL", 8))
285 smtpc->authmechs |= SASL_MECH_EXTERNAL;
286 else if(wordlen == 4 && !memcmp(line, "NTLM", 4))
287 smtpc->authmechs |= SASL_MECH_NTLM;
298 /* This is the ONLY way to change SMTP state! */
299 static void state(struct connectdata *conn, smtpstate newstate)
301 struct smtp_conn *smtpc = &conn->proto.smtpc;
302 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
303 /* for debug purposes */
304 static const char * const names[] = {
316 "AUTH_DIGESTMD5_RESP",
318 "AUTH_NTLM_TYPE2MSG",
328 if(smtpc->state != newstate)
329 infof(conn->data, "SMTP %p state change from %s to %s\n",
330 smtpc, names[smtpc->state], names[newstate]);
333 smtpc->state = newstate;
336 static CURLcode smtp_state_ehlo(struct connectdata *conn)
338 CURLcode result = CURLE_OK;
339 struct smtp_conn *smtpc = &conn->proto.smtpc;
341 smtpc->authmechs = 0; /* No known authentication mechanisms yet */
342 smtpc->authused = 0; /* Clear the authentication mechanism used
343 for esmtp connections */
344 smtpc->tls_supported = FALSE; /* Clear the TLS capability */
346 /* Send the EHLO command */
347 result = Curl_pp_sendf(&smtpc->pp, "EHLO %s", smtpc->domain);
350 state(conn, SMTP_EHLO);
355 static CURLcode smtp_state_helo(struct connectdata *conn)
357 CURLcode result = CURLE_OK;
358 struct smtp_conn *smtpc = &conn->proto.smtpc;
360 smtpc->authused = 0; /* No authentication mechanism used in smtp
363 /* Send the HELO command */
364 result = Curl_pp_sendf(&smtpc->pp, "HELO %s", smtpc->domain);
367 state(conn, SMTP_HELO);
372 static CURLcode smtp_state_starttls(struct connectdata *conn)
374 CURLcode result = CURLE_OK;
376 /* Send the STARTTLS command */
377 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "STARTTLS");
380 state(conn, SMTP_STARTTLS);
385 static CURLcode smtp_state_upgrade_tls(struct connectdata *conn)
387 CURLcode result = CURLE_OK;
388 struct smtp_conn *smtpc = &conn->proto.smtpc;
390 /* Start the SSL connection */
391 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone);
394 if(smtpc->state != SMTP_UPGRADETLS)
395 state(conn, SMTP_UPGRADETLS);
399 result = smtp_state_ehlo(conn);
406 static CURLcode smtp_authenticate(struct connectdata *conn)
408 CURLcode result = CURLE_OK;
409 struct smtp_conn *smtpc = &conn->proto.smtpc;
410 const char *mech = NULL;
411 char *initresp = NULL;
413 smtpstate state1 = SMTP_STOP;
414 smtpstate state2 = SMTP_STOP;
416 /* Check we have a username and password to authenticate with and end the
417 connect phase if we don't */
418 if(!conn->bits.user_passwd) {
419 state(conn, SMTP_STOP);
424 /* Calculate the supported authentication mechanism, by decreasing order of
425 security, as well as the initial response where appropriate */
426 #ifndef CURL_DISABLE_CRYPTO_AUTH
427 if((smtpc->authmechs & SASL_MECH_DIGEST_MD5) &&
428 (smtpc->prefmech & SASL_MECH_DIGEST_MD5)) {
430 state1 = SMTP_AUTH_DIGESTMD5;
431 smtpc->authused = SASL_MECH_DIGEST_MD5;
433 else if((smtpc->authmechs & SASL_MECH_CRAM_MD5) &&
434 (smtpc->prefmech & SASL_MECH_CRAM_MD5)) {
436 state1 = SMTP_AUTH_CRAMMD5;
437 smtpc->authused = SASL_MECH_CRAM_MD5;
442 if((smtpc->authmechs & SASL_MECH_NTLM) &&
443 (smtpc->prefmech & SASL_MECH_NTLM)) {
445 state1 = SMTP_AUTH_NTLM;
446 state2 = SMTP_AUTH_NTLM_TYPE2MSG;
447 smtpc->authused = SASL_MECH_NTLM;
448 result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
454 if((smtpc->authmechs & SASL_MECH_LOGIN) &&
455 (smtpc->prefmech & SASL_MECH_LOGIN)) {
457 state1 = SMTP_AUTH_LOGIN;
458 state2 = SMTP_AUTH_LOGIN_PASSWD;
459 smtpc->authused = SASL_MECH_LOGIN;
460 result = Curl_sasl_create_login_message(conn->data, conn->user,
463 else if((smtpc->authmechs & SASL_MECH_PLAIN) &&
464 (smtpc->prefmech & SASL_MECH_PLAIN)) {
466 state1 = SMTP_AUTH_PLAIN;
467 state2 = SMTP_AUTH_FINAL;
468 smtpc->authused = SASL_MECH_PLAIN;
469 result = Curl_sasl_create_plain_message(conn->data, conn->user,
470 conn->passwd, &initresp, &len);
473 /* Other mechanisms not supported */
474 infof(conn->data, "No known authentication mechanisms supported!\n");
475 result = CURLE_LOGIN_DENIED;
479 /* Perform SASL based authentication */
481 strlen(mech) + len <= 512 - 8) { /* AUTH <mech> ...<crlf> */
482 result = Curl_pp_sendf(&smtpc->pp, "AUTH %s %s", mech, initresp);
488 result = Curl_pp_sendf(&smtpc->pp, "AUTH %s", mech);
494 Curl_safefree(initresp);
500 /* Start the DO phase */
501 static CURLcode smtp_mail(struct connectdata *conn)
506 CURLcode result = CURLE_OK;
507 struct SessionHandle *data = conn->data;
509 /* Calculate the FROM parameter */
510 if(!data->set.str[STRING_MAIL_FROM])
511 /* Null reverse-path, RFC-2821, sect. 3.7 */
513 else if(data->set.str[STRING_MAIL_FROM][0] == '<')
514 from = aprintf("%s", data->set.str[STRING_MAIL_FROM]);
516 from = aprintf("<%s>", data->set.str[STRING_MAIL_FROM]);
519 return CURLE_OUT_OF_MEMORY;
521 /* Calculate the optional AUTH parameter */
522 if(data->set.str[STRING_MAIL_AUTH] && conn->proto.smtpc.authused) {
523 if(data->set.str[STRING_MAIL_AUTH][0] != '\0')
524 auth = aprintf("%s", data->set.str[STRING_MAIL_AUTH]);
526 /* Empty AUTH, RFC-2554, sect. 5 */
532 return CURLE_OUT_OF_MEMORY;
536 /* calculate the optional SIZE parameter */
537 if(conn->proto.smtpc.size_supported && conn->data->set.infilesize > 0) {
538 size = aprintf("%" FORMAT_OFF_T, data->set.infilesize);
544 return CURLE_OUT_OF_MEMORY;
548 /* Send the MAIL command */
550 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
551 "MAIL FROM:%s", from);
552 else if(auth && !size)
553 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
554 "MAIL FROM:%s AUTH=%s", from, auth);
555 else if(auth && size)
556 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
557 "MAIL FROM:%s AUTH=%s SIZE=%s", from, auth, size);
559 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
560 "MAIL FROM:%s SIZE=%s", from, size);
567 state(conn, SMTP_MAIL);
572 static CURLcode smtp_rcpt_to(struct connectdata *conn)
574 CURLcode result = CURLE_OK;
575 struct SessionHandle *data = conn->data;
576 struct SMTP *smtp = data->state.proto.smtp;
578 /* Send the RCPT TO command */
580 if(smtp->rcpt->data[0] == '<')
581 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:%s",
584 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:<%s>",
587 state(conn, SMTP_RCPT);
593 /* For the initial server greeting */
594 static CURLcode smtp_state_servergreet_resp(struct connectdata *conn,
598 CURLcode result = CURLE_OK;
599 struct SessionHandle *data = conn->data;
601 (void)instate; /* no use for this yet */
603 if(smtpcode/100 != 2) {
604 failf(data, "Got unexpected smtp-server response: %d", smtpcode);
605 result = CURLE_FTP_WEIRD_SERVER_REPLY;
608 result = smtp_state_ehlo(conn);
613 /* For STARTTLS responses */
614 static CURLcode smtp_state_starttls_resp(struct connectdata *conn,
618 CURLcode result = CURLE_OK;
619 struct SessionHandle *data = conn->data;
621 (void)instate; /* no use for this yet */
623 if(smtpcode != 220) {
624 if(data->set.use_ssl != CURLUSESSL_TRY) {
625 failf(data, "STARTTLS denied. %c", smtpcode);
626 result = CURLE_USE_SSL_FAILED;
629 result = smtp_authenticate(conn);
632 result = smtp_state_upgrade_tls(conn);
637 /* For EHLO responses */
638 static CURLcode smtp_state_ehlo_resp(struct connectdata *conn, int smtpcode,
641 CURLcode result = CURLE_OK;
642 struct SessionHandle *data = conn->data;
643 struct smtp_conn *smtpc = &conn->proto.smtpc;
645 (void)instate; /* no use for this yet */
647 if(smtpcode/100 != 2) {
648 if((data->set.use_ssl <= CURLUSESSL_TRY || conn->ssl[FIRSTSOCKET].use) &&
649 !conn->bits.user_passwd)
650 result = smtp_state_helo(conn);
652 failf(data, "Remote access denied: %d", smtpcode);
653 result = CURLE_REMOTE_ACCESS_DENIED;
656 else if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
657 /* We don't have a SSL/TLS connection yet, but SSL is requested */
658 if(smtpc->tls_supported)
659 /* Switch to TLS connection now */
660 result = smtp_state_starttls(conn);
661 else if(data->set.use_ssl == CURLUSESSL_TRY)
662 /* Fallback and carry on with authentication */
663 result = smtp_authenticate(conn);
665 failf(data, "STARTTLS not supported.");
666 result = CURLE_USE_SSL_FAILED;
670 result = smtp_authenticate(conn);
675 /* For HELO responses */
676 static CURLcode smtp_state_helo_resp(struct connectdata *conn, int smtpcode,
679 CURLcode result = CURLE_OK;
680 struct SessionHandle *data = conn->data;
682 (void)instate; /* no use for this yet */
684 if(smtpcode/100 != 2) {
685 failf(data, "Remote access denied: %d", smtpcode);
686 result = CURLE_REMOTE_ACCESS_DENIED;
689 /* End of connect phase */
690 state(conn, SMTP_STOP);
695 /* For AUTH PLAIN (without initial response) responses */
696 static CURLcode smtp_state_auth_plain_resp(struct connectdata *conn,
700 CURLcode result = CURLE_OK;
701 struct SessionHandle *data = conn->data;
703 char *plainauth = NULL;
705 (void)instate; /* no use for this yet */
707 if(smtpcode != 334) {
708 failf(data, "Access denied: %d", smtpcode);
709 result = CURLE_LOGIN_DENIED;
712 /* Create the authorisation message */
713 result = Curl_sasl_create_plain_message(conn->data, conn->user,
714 conn->passwd, &plainauth, &len);
716 /* Send the message */
719 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", plainauth);
722 state(conn, SMTP_AUTH_FINAL);
725 Curl_safefree(plainauth);
732 /* For AUTH LOGIN (without initial response) responses */
733 static CURLcode smtp_state_auth_login_resp(struct connectdata *conn,
737 CURLcode result = CURLE_OK;
738 struct SessionHandle *data = conn->data;
740 char *authuser = NULL;
742 (void)instate; /* no use for this yet */
744 if(smtpcode != 334) {
745 failf(data, "Access denied: %d", smtpcode);
746 result = CURLE_LOGIN_DENIED;
749 /* Create the user message */
750 result = Curl_sasl_create_login_message(conn->data, conn->user,
756 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", authuser);
759 state(conn, SMTP_AUTH_LOGIN_PASSWD);
762 Curl_safefree(authuser);
769 /* For AUTH LOGIN user entry responses */
770 static CURLcode smtp_state_auth_login_password_resp(struct connectdata *conn,
774 CURLcode result = CURLE_OK;
775 struct SessionHandle *data = conn->data;
777 char *authpasswd = NULL;
779 (void)instate; /* no use for this yet */
781 if(smtpcode != 334) {
782 failf(data, "Access denied: %d", smtpcode);
783 result = CURLE_LOGIN_DENIED;
786 /* Create the password message */
787 result = Curl_sasl_create_login_message(conn->data, conn->passwd,
790 /* Send the password */
793 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", authpasswd);
796 state(conn, SMTP_AUTH_FINAL);
799 Curl_safefree(authpasswd);
806 #ifndef CURL_DISABLE_CRYPTO_AUTH
807 /* For AUTH CRAM-MD5 responses */
808 static CURLcode smtp_state_auth_cram_resp(struct connectdata *conn,
812 CURLcode result = CURLE_OK;
813 struct SessionHandle *data = conn->data;
814 char *chlg64 = data->state.buffer;
816 char *rplyb64 = NULL;
818 (void)instate; /* no use for this yet */
820 if(smtpcode != 334) {
821 failf(data, "Access denied: %d", smtpcode);
822 return CURLE_LOGIN_DENIED;
825 /* Get the challenge */
826 for(chlg64 += 4; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
829 /* Terminate the challenge */
831 for(len = strlen(chlg64); len--;)
832 if(chlg64[len] != '\r' && chlg64[len] != '\n' && chlg64[len] != ' ' &&
841 /* Create the response message */
842 result = Curl_sasl_create_cram_md5_message(data, chlg64, conn->user,
843 conn->passwd, &rplyb64, &len);
845 /* Send the response */
848 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", rplyb64);
851 state(conn, SMTP_AUTH_FINAL);
854 Curl_safefree(rplyb64);
860 /* For AUTH DIGEST-MD5 challenge responses */
861 static CURLcode smtp_state_auth_digest_resp(struct connectdata *conn,
865 CURLcode result = CURLE_OK;
866 struct SessionHandle *data = conn->data;
867 char *chlg64 = data->state.buffer;
869 char *rplyb64 = NULL;
871 (void)instate; /* no use for this yet */
873 if(smtpcode != 334) {
874 failf(data, "Access denied: %d", smtpcode);
875 return CURLE_LOGIN_DENIED;
878 /* Get the challenge */
879 for(chlg64 += 4; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
882 /* Create the response message */
883 result = Curl_sasl_create_digest_md5_message(data, chlg64, conn->user,
884 conn->passwd, "smtp",
887 /* Send the response */
890 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", rplyb64);
893 state(conn, SMTP_AUTH_DIGESTMD5_RESP);
896 Curl_safefree(rplyb64);
902 /* For AUTH DIGEST-MD5 challenge-response responses */
903 static CURLcode smtp_state_auth_digest_resp_resp(struct connectdata *conn,
907 CURLcode result = CURLE_OK;
908 struct SessionHandle *data = conn->data;
910 (void)instate; /* no use for this yet */
912 if(smtpcode != 334) {
913 failf(data, "Authentication failed: %d", smtpcode);
914 result = CURLE_LOGIN_DENIED;
917 /* Send an empty response */
918 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "");
921 state(conn, SMTP_AUTH_FINAL);
930 /* For AUTH NTLM (without initial response) responses */
931 static CURLcode smtp_state_auth_ntlm_resp(struct connectdata *conn,
935 CURLcode result = CURLE_OK;
936 struct SessionHandle *data = conn->data;
937 char *type1msg = NULL;
940 (void)instate; /* no use for this yet */
942 if(smtpcode != 334) {
943 failf(data, "Access denied: %d", smtpcode);
944 result = CURLE_LOGIN_DENIED;
947 /* Create the type-1 message */
948 result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
952 /* Send the message */
955 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", type1msg);
958 state(conn, SMTP_AUTH_NTLM_TYPE2MSG);
961 Curl_safefree(type1msg);
968 /* For NTLM type-2 responses (sent in reponse to our type-1 message) */
969 static CURLcode smtp_state_auth_ntlm_type2msg_resp(struct connectdata *conn,
973 CURLcode result = CURLE_OK;
974 struct SessionHandle *data = conn->data;
975 char *type3msg = NULL;
978 (void)instate; /* no use for this yet */
980 if(smtpcode != 334) {
981 failf(data, "Access denied: %d", smtpcode);
982 result = CURLE_LOGIN_DENIED;
985 /* Create the type-3 message */
986 result = Curl_sasl_create_ntlm_type3_message(data,
987 data->state.buffer + 4,
988 conn->user, conn->passwd,
992 /* Send the message */
995 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", type3msg);
998 state(conn, SMTP_AUTH_FINAL);
1001 Curl_safefree(type3msg);
1009 /* For the final responses to the AUTH sequence */
1010 static CURLcode smtp_state_auth_final_resp(struct connectdata *conn,
1014 CURLcode result = CURLE_OK;
1015 struct SessionHandle *data = conn->data;
1017 (void)instate; /* no use for this yet */
1019 if(smtpcode != 235) {
1020 failf(data, "Authentication failed: %d", smtpcode);
1021 result = CURLE_LOGIN_DENIED;
1024 /* End of connect phase */
1025 state(conn, SMTP_STOP);
1030 /* For MAIL responses */
1031 static CURLcode smtp_state_mail_resp(struct connectdata *conn, int smtpcode,
1034 CURLcode result = CURLE_OK;
1035 struct SessionHandle *data = conn->data;
1036 struct SMTP *smtp = data->state.proto.smtp;
1038 (void)instate; /* no use for this yet */
1040 if(smtpcode/100 != 2) {
1041 failf(data, "MAIL failed: %d", smtpcode);
1042 result = CURLE_SEND_ERROR;
1043 state(conn, SMTP_STOP);
1046 smtp->rcpt = data->set.mail_rcpt;
1048 result = smtp_rcpt_to(conn);
1054 /* For RCPT responses */
1055 static CURLcode smtp_state_rcpt_resp(struct connectdata *conn, int smtpcode,
1058 CURLcode result = CURLE_OK;
1059 struct SessionHandle *data = conn->data;
1060 struct SMTP *smtp = data->state.proto.smtp;
1062 (void)instate; /* no use for this yet */
1064 if(smtpcode/100 != 2) {
1065 failf(data, "RCPT failed: %d", smtpcode);
1066 result = CURLE_SEND_ERROR;
1067 state(conn, SMTP_STOP);
1071 smtp->rcpt = smtp->rcpt->next;
1072 result = smtp_rcpt_to(conn);
1074 /* If we failed or still are sending RCPT data then return */
1075 if(result || smtp->rcpt)
1079 /* Send the DATA command */
1080 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "DATA");
1083 state(conn, SMTP_DATA);
1089 /* For DATA response */
1090 static CURLcode smtp_state_data_resp(struct connectdata *conn, int smtpcode,
1093 (void)instate; /* no use for this yet */
1095 if(smtpcode != 354) {
1096 state(conn, SMTP_STOP);
1097 return CURLE_SEND_ERROR;
1101 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL);
1103 /* End of DO phase */
1104 state(conn, SMTP_STOP);
1109 /* For POSTDATA responses, which are received after the entire DATA
1110 part has been sent to the server */
1111 static CURLcode smtp_state_postdata_resp(struct connectdata *conn,
1115 CURLcode result = CURLE_OK;
1117 (void)instate; /* no use for this yet */
1120 result = CURLE_RECV_ERROR;
1122 /* End of DONE phase */
1123 state(conn, SMTP_STOP);
1128 static CURLcode smtp_statemach_act(struct connectdata *conn)
1130 CURLcode result = CURLE_OK;
1131 curl_socket_t sock = conn->sock[FIRSTSOCKET];
1132 struct SessionHandle *data = conn->data;
1134 struct smtp_conn *smtpc = &conn->proto.smtpc;
1135 struct pingpong *pp = &smtpc->pp;
1138 /* Busy upgrading the connection; right now all I/O is SSL/TLS, not SMTP */
1139 if(smtpc->state == SMTP_UPGRADETLS)
1140 return smtp_state_upgrade_tls(conn);
1142 /* Flush any data that needs to be sent */
1144 return Curl_pp_flushsend(pp);
1146 /* Read the response from the server */
1147 result = Curl_pp_readresp(sock, pp, &smtpcode, &nread);
1151 /* Store the latest response for later retrieval */
1152 if(smtpc->state != SMTP_QUIT)
1153 data->info.httpcode = smtpcode;
1156 /* We have now received a full SMTP server response */
1157 switch(smtpc->state) {
1158 case SMTP_SERVERGREET:
1159 result = smtp_state_servergreet_resp(conn, smtpcode, smtpc->state);
1163 result = smtp_state_ehlo_resp(conn, smtpcode, smtpc->state);
1167 result = smtp_state_helo_resp(conn, smtpcode, smtpc->state);
1171 result = smtp_state_starttls_resp(conn, smtpcode, smtpc->state);
1174 case SMTP_AUTH_PLAIN:
1175 result = smtp_state_auth_plain_resp(conn, smtpcode, smtpc->state);
1178 case SMTP_AUTH_LOGIN:
1179 result = smtp_state_auth_login_resp(conn, smtpcode, smtpc->state);
1182 case SMTP_AUTH_LOGIN_PASSWD:
1183 result = smtp_state_auth_login_password_resp(conn, smtpcode,
1187 #ifndef CURL_DISABLE_CRYPTO_AUTH
1188 case SMTP_AUTH_CRAMMD5:
1189 result = smtp_state_auth_cram_resp(conn, smtpcode, smtpc->state);
1192 case SMTP_AUTH_DIGESTMD5:
1193 result = smtp_state_auth_digest_resp(conn, smtpcode, smtpc->state);
1196 case SMTP_AUTH_DIGESTMD5_RESP:
1197 result = smtp_state_auth_digest_resp_resp(conn, smtpcode, smtpc->state);
1202 case SMTP_AUTH_NTLM:
1203 result = smtp_state_auth_ntlm_resp(conn, smtpcode, smtpc->state);
1206 case SMTP_AUTH_NTLM_TYPE2MSG:
1207 result = smtp_state_auth_ntlm_type2msg_resp(conn, smtpcode,
1212 case SMTP_AUTH_FINAL:
1213 result = smtp_state_auth_final_resp(conn, smtpcode, smtpc->state);
1217 result = smtp_state_mail_resp(conn, smtpcode, smtpc->state);
1221 result = smtp_state_rcpt_resp(conn, smtpcode, smtpc->state);
1225 result = smtp_state_data_resp(conn, smtpcode, smtpc->state);
1229 result = smtp_state_postdata_resp(conn, smtpcode, smtpc->state);
1233 /* fallthrough, just stop! */
1235 /* internal error */
1236 state(conn, SMTP_STOP);
1244 /* Called repeatedly until done from multi.c */
1245 static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done)
1247 CURLcode result = CURLE_OK;
1248 struct smtp_conn *smtpc = &conn->proto.smtpc;
1250 if((conn->handler->flags & PROTOPT_SSL) && !smtpc->ssldone)
1251 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone);
1253 result = Curl_pp_statemach(&smtpc->pp, FALSE);
1255 *done = (smtpc->state == SMTP_STOP) ? TRUE : FALSE;
1260 static CURLcode smtp_block_statemach(struct connectdata *conn)
1262 CURLcode result = CURLE_OK;
1263 struct smtp_conn *smtpc = &conn->proto.smtpc;
1265 while(smtpc->state != SMTP_STOP && !result)
1266 result = Curl_pp_statemach(&smtpc->pp, TRUE);
1271 /* Allocate and initialize the SMTP struct for the current SessionHandle if
1273 static CURLcode smtp_init(struct connectdata *conn)
1275 CURLcode result = CURLE_OK;
1276 struct SessionHandle *data = conn->data;
1277 struct SMTP *smtp = data->state.proto.smtp;
1280 smtp = data->state.proto.smtp = calloc(sizeof(struct SMTP), 1);
1282 result = CURLE_OUT_OF_MEMORY;
1288 /* For the SMTP "protocol connect" and "doing" phases only */
1289 static int smtp_getsock(struct connectdata *conn, curl_socket_t *socks,
1292 return Curl_pp_getsock(&conn->proto.smtpc.pp, socks, numsocks);
1295 /***********************************************************************
1299 * This function should do everything that is to be considered a part of
1300 * the connection phase.
1302 * The variable pointed to by 'done' will be TRUE if the protocol-layer
1303 * connect phase is done when this function returns, or FALSE if not. When
1304 * called as a part of the easy interface, it will always be TRUE.
1306 static CURLcode smtp_connect(struct connectdata *conn, bool *done)
1308 CURLcode result = CURLE_OK;
1309 struct smtp_conn *smtpc = &conn->proto.smtpc;
1310 struct pingpong *pp = &smtpc->pp;
1312 *done = FALSE; /* default to not done yet */
1314 /* If there already is a protocol-specific struct allocated for this
1315 sessionhandle, deal with it */
1316 Curl_reset_reqproto(conn);
1318 /* Initialise the SMTP layer */
1319 result = smtp_init(conn);
1323 /* We always support persistent connections in SMTP */
1324 conn->bits.close = FALSE;
1326 /* Set the default response time-out */
1327 pp->response_time = RESP_TIMEOUT;
1328 pp->statemach_act = smtp_statemach_act;
1329 pp->endofresp = smtp_endofresp;
1332 /* Set the default preferred authentication mechanism */
1333 smtpc->prefmech = SASL_AUTH_ANY;
1335 /* Initialise the pingpong layer */
1338 /* Parse the URL options */
1339 result = smtp_parse_url_options(conn);
1343 /* Parse the URL path */
1344 result = smtp_parse_url_path(conn);
1348 /* Start off waiting for the server greeting response */
1349 state(conn, SMTP_SERVERGREET);
1351 result = smtp_multi_statemach(conn, done);
1356 /***********************************************************************
1360 * The DONE function. This does what needs to be done after a single DO has
1363 * Input argument is already checked for validity.
1365 static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
1368 CURLcode result = CURLE_OK;
1369 struct SessionHandle *data = conn->data;
1370 struct SMTP *smtp = data->state.proto.smtp;
1371 ssize_t bytes_written;
1376 /* When the easy handle is removed from the multi interface while libcurl
1377 is still trying to resolve the host name, the SMTP struct is not yet
1378 initialized. However, the removal action calls Curl_done() which in
1379 turn calls this function, so we simply return success. */
1383 conn->bits.close = TRUE; /* marked for closure */
1384 result = status; /* use the already set error code */
1386 else if(!data->set.connect_only) {
1387 struct smtp_conn *smtpc = &conn->proto.smtpc;
1388 struct pingpong *pp = &smtpc->pp;
1390 /* Send the end of block data */
1391 result = Curl_write(conn,
1392 conn->writesockfd, /* socket to send to */
1393 SMTP_EOB, /* buffer pointer */
1394 SMTP_EOB_LEN, /* buffer size */
1395 &bytes_written); /* actually sent away */
1400 if(bytes_written != SMTP_EOB_LEN) {
1401 /* The whole chunk was not sent so keep it around and adjust the
1402 pingpong structure accordingly */
1403 pp->sendthis = strdup(SMTP_EOB);
1404 pp->sendsize = SMTP_EOB_LEN;
1405 pp->sendleft = SMTP_EOB_LEN - bytes_written;
1408 /* Successfully sent so adjust the response timeout relative to now */
1409 pp->response = Curl_tvnow();
1411 state(conn, SMTP_POSTDATA);
1413 /* Run the state-machine
1415 TODO: when the multi interface is used, this _really_ should be using
1416 the smtp_multi_statemach function but we have no general support for
1417 non-blocking DONE operations, not in the multi state machine and with
1418 Curl_done() invokes on several places in the code!
1420 result = smtp_block_statemach(conn);
1423 /* Clear the transfer mode for the next request */
1424 smtp->transfer = FTPTRANSFER_BODY;
1429 /***********************************************************************
1433 * This is the actual DO function for SMTP. Send a mail according to the
1434 * options previously setup.
1436 static CURLcode smtp_perform(struct connectdata *conn, bool *connected,
1439 /* This is SMTP and no proxy */
1440 CURLcode result = CURLE_OK;
1442 DEBUGF(infof(conn->data, "DO phase starts\n"));
1444 if(conn->data->set.opt_no_body) {
1445 /* Requested no body means no transfer */
1446 struct SMTP *smtp = conn->data->state.proto.smtp;
1447 smtp->transfer = FTPTRANSFER_INFO;
1450 *dophase_done = FALSE; /* not done yet */
1452 /* Start the first command in the DO phase */
1453 result = smtp_mail(conn);
1457 /* run the state-machine */
1458 result = smtp_multi_statemach(conn, dophase_done);
1460 *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1463 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1468 /***********************************************************************
1472 * This function is registered as 'curl_do' function. It decodes the path
1473 * parts etc as a wrapper to the actual DO function (smtp_perform).
1475 * The input argument is already checked for validity.
1477 static CURLcode smtp_do(struct connectdata *conn, bool *done)
1479 CURLcode result = CURLE_OK;
1481 *done = FALSE; /* default to false */
1483 /* Since connections can be re-used between SessionHandles, there might be a
1484 connection already existing but on a fresh SessionHandle struct. As such
1485 we make sure we have a good SMTP struct to play with. For new connections
1486 the SMTP struct is allocated and setup in the smtp_connect() function. */
1487 Curl_reset_reqproto(conn);
1488 result = smtp_init(conn);
1492 result = smtp_regular_transfer(conn, done);
1497 /***********************************************************************
1501 * Performs the quit action prior to sclose() being called.
1503 static CURLcode smtp_quit(struct connectdata *conn)
1505 CURLcode result = CURLE_OK;
1507 /* Send the QUIT command */
1508 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "QUIT");
1511 state(conn, SMTP_QUIT);
1516 /***********************************************************************
1520 * Disconnect from an SMTP server. Cleanup protocol-specific per-connection
1521 * resources. BLOCKING.
1523 static CURLcode smtp_disconnect(struct connectdata *conn,
1524 bool dead_connection)
1526 struct smtp_conn *smtpc = &conn->proto.smtpc;
1528 /* We cannot send quit unconditionally. If this connection is stale or
1529 bad in any way, sending quit and waiting around here will make the
1530 disconnect wait in vain and cause more problems than we need to. */
1532 /* The SMTP session may or may not have been allocated/setup at this
1534 if(!dead_connection && smtpc->pp.conn)
1535 if(!smtp_quit(conn))
1536 (void)smtp_block_statemach(conn); /* ignore errors on QUIT */
1538 /* Disconnect from the server */
1539 Curl_pp_disconnect(&smtpc->pp);
1541 /* Cleanup the SASL module */
1542 Curl_sasl_cleanup(conn, smtpc->authused);
1544 /* Cleanup our connection based variables */
1545 Curl_safefree(smtpc->domain);
1550 /* Call this when the DO phase has completed */
1551 static CURLcode smtp_dophase_done(struct connectdata *conn, bool connected)
1553 struct SMTP *smtp = conn->data->state.proto.smtp;
1557 if(smtp->transfer != FTPTRANSFER_BODY)
1558 /* no data to transfer */
1559 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1564 /* Called from multi.c while DOing */
1565 static CURLcode smtp_doing(struct connectdata *conn, bool *dophase_done)
1567 CURLcode result = smtp_multi_statemach(conn, dophase_done);
1570 DEBUGF(infof(conn->data, "DO phase failed\n"));
1571 else if(*dophase_done) {
1572 result = smtp_dophase_done(conn, FALSE /* not connected */);
1574 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1580 /***********************************************************************
1582 * smtp_regular_transfer()
1584 * The input argument is already checked for validity.
1586 * Performs all commands done before a regular transfer between a local and a
1589 static CURLcode smtp_regular_transfer(struct connectdata *conn,
1592 CURLcode result = CURLE_OK;
1593 bool connected = FALSE;
1594 struct SessionHandle *data = conn->data;
1596 /* Make sure size is unknown at this point */
1597 data->req.size = -1;
1599 /* Set the progress data */
1600 Curl_pgrsSetUploadCounter(data, 0);
1601 Curl_pgrsSetDownloadCounter(data, 0);
1602 Curl_pgrsSetUploadSize(data, 0);
1603 Curl_pgrsSetDownloadSize(data, 0);
1605 /* Carry out the perform */
1606 result = smtp_perform(conn, &connected, dophase_done);
1608 /* Perform post DO phase operations if necessary */
1609 if(!result && *dophase_done)
1610 result = smtp_dophase_done(conn, connected);
1615 static CURLcode smtp_setup_connection(struct connectdata *conn)
1617 struct SessionHandle *data = conn->data;
1619 if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
1620 /* Unless we have asked to tunnel SMTP operations through the proxy, we
1621 switch and use HTTP operations only */
1622 #ifndef CURL_DISABLE_HTTP
1623 if(conn->handler == &Curl_handler_smtp)
1624 conn->handler = &Curl_handler_smtp_proxy;
1627 conn->handler = &Curl_handler_smtps_proxy;
1629 failf(data, "SMTPS not supported!");
1630 return CURLE_UNSUPPORTED_PROTOCOL;
1634 /* We explicitly mark this connection as persistent here as we're doing
1635 SMTP over HTTP and thus we accidentally avoid setting this value
1637 conn->bits.close = FALSE;
1639 failf(data, "SMTP over http proxy requires HTTP support built-in!");
1640 return CURLE_UNSUPPORTED_PROTOCOL;
1644 data->state.path++; /* don't include the initial slash */
1649 /***********************************************************************
1651 * smtp_parse_url_options()
1653 * Parse the URL login options.
1655 static CURLcode smtp_parse_url_options(struct connectdata *conn)
1657 CURLcode result = CURLE_OK;
1658 struct smtp_conn *smtpc = &conn->proto.smtpc;
1659 const char *options = conn->options;
1660 const char *ptr = options;
1663 const char *key = ptr;
1665 while(*ptr && *ptr != '=')
1668 if(strnequal(key, "AUTH", 4)) {
1669 const char *value = ptr + 1;
1671 if(strequal(value, "*"))
1672 smtpc->prefmech = SASL_AUTH_ANY;
1673 else if(strequal(value, "LOGIN"))
1674 smtpc->prefmech = SASL_MECH_LOGIN;
1675 else if(strequal(value, "PLAIN"))
1676 smtpc->prefmech = SASL_MECH_PLAIN;
1677 else if(strequal(value, "CRAM-MD5"))
1678 smtpc->prefmech = SASL_MECH_CRAM_MD5;
1679 else if(strequal(value, "DIGEST-MD5"))
1680 smtpc->prefmech = SASL_MECH_DIGEST_MD5;
1681 else if(strequal(value, "GSSAPI"))
1682 smtpc->prefmech = SASL_MECH_GSSAPI;
1683 else if(strequal(value, "NTLM"))
1684 smtpc->prefmech = SASL_MECH_NTLM;
1686 smtpc->prefmech = SASL_AUTH_NONE;
1689 result = CURLE_URL_MALFORMAT;
1695 /***********************************************************************
1697 * smtp_parse_url_path()
1699 * Parse the URL path into separate path components.
1701 static CURLcode smtp_parse_url_path(struct connectdata *conn)
1703 /* The SMTP struct is already initialised in smtp_connect() */
1704 struct SessionHandle *data = conn->data;
1705 struct smtp_conn *smtpc = &conn->proto.smtpc;
1706 const char *path = data->state.path;
1707 char localhost[HOSTNAME_MAX + 1];
1709 /* Calculate the path if necessary */
1711 if(!Curl_gethostname(localhost, sizeof(localhost)))
1717 /* URL decode the path and use it as the domain in our EHLO */
1718 return Curl_urldecode(conn->data, path, 0, &smtpc->domain, NULL, TRUE);
1721 CURLcode Curl_smtp_escape_eob(struct connectdata *conn, ssize_t nread)
1723 /* When sending a SMTP payload we must detect CRLF. sequences making sure
1724 they are sent as CRLF.. instead, as a . on the beginning of a line will
1725 be deleted by the server when not part of an EOB terminator and a
1726 genuine CRLF.CRLF which isn't escaped will wrongly be detected as end of
1731 struct smtp_conn *smtpc = &conn->proto.smtpc;
1732 struct SessionHandle *data = conn->data;
1734 /* Do we need to allocate the scatch buffer? */
1735 if(!data->state.scratch) {
1736 data->state.scratch = malloc(2 * BUFSIZE);
1738 if(!data->state.scratch) {
1739 failf (data, "Failed to alloc scratch buffer!");
1740 return CURLE_OUT_OF_MEMORY;
1744 /* This loop can be improved by some kind of Boyer-Moore style of
1745 approach but that is saved for later... */
1746 for(i = 0, si = 0; i < nread; i++) {
1747 if(SMTP_EOB[smtpc->eob] == data->req.upload_fromhere[i])
1749 else if(smtpc->eob) {
1750 /* A previous substring matched so output that first */
1751 memcpy(&data->state.scratch[si], SMTP_EOB, smtpc->eob);
1754 /* Then compare the first byte */
1755 if(SMTP_EOB[0] == data->req.upload_fromhere[i])
1761 /* Do we have a match for CRLF. as per RFC-2821, sect. 4.5.2 */
1762 if(SMTP_EOB_FIND_LEN == smtpc->eob) {
1763 /* Copy the replacement data to the target buffer */
1764 memcpy(&data->state.scratch[si], SMTP_EOB_REPL, SMTP_EOB_REPL_LEN);
1765 si += SMTP_EOB_REPL_LEN;
1768 else if(!smtpc->eob)
1769 data->state.scratch[si++] = data->req.upload_fromhere[i];
1773 /* A substring matched before processing ended so output that now */
1774 memcpy(&data->state.scratch[si], SMTP_EOB, smtpc->eob);
1780 /* Only use the new buffer if we replaced something */
1783 /* Upload from the new (replaced) buffer instead */
1784 data->req.upload_fromhere = data->state.scratch;
1786 /* Set the new amount too */
1787 data->req.upload_present = nread;
1793 #endif /* CURL_DISABLE_SMTP */