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 /* For the initial server greeting */
501 static CURLcode smtp_state_servergreet_resp(struct connectdata *conn,
505 CURLcode result = CURLE_OK;
506 struct SessionHandle *data = conn->data;
508 (void)instate; /* no use for this yet */
510 if(smtpcode/100 != 2) {
511 failf(data, "Got unexpected smtp-server response: %d", smtpcode);
512 return CURLE_FTP_WEIRD_SERVER_REPLY;
515 result = smtp_state_ehlo(conn);
520 /* For STARTTLS responses */
521 static CURLcode smtp_state_starttls_resp(struct connectdata *conn,
525 CURLcode result = CURLE_OK;
526 struct SessionHandle *data = conn->data;
528 (void)instate; /* no use for this yet */
530 if(smtpcode != 220) {
531 if(data->set.use_ssl != CURLUSESSL_TRY) {
532 failf(data, "STARTTLS denied. %c", smtpcode);
533 result = CURLE_USE_SSL_FAILED;
536 result = smtp_authenticate(conn);
539 result = smtp_state_upgrade_tls(conn);
544 /* For EHLO responses */
545 static CURLcode smtp_state_ehlo_resp(struct connectdata *conn, int smtpcode,
548 CURLcode result = CURLE_OK;
549 struct SessionHandle *data = conn->data;
550 struct smtp_conn *smtpc = &conn->proto.smtpc;
552 (void)instate; /* no use for this yet */
554 if(smtpcode/100 != 2) {
555 if((data->set.use_ssl <= CURLUSESSL_TRY || conn->ssl[FIRSTSOCKET].use) &&
556 !conn->bits.user_passwd)
557 result = smtp_state_helo(conn);
559 failf(data, "Remote access denied: %d", smtpcode);
560 result = CURLE_REMOTE_ACCESS_DENIED;
563 else if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
564 /* We don't have a SSL/TLS connection yet, but SSL is requested */
565 if(smtpc->tls_supported)
566 /* Switch to TLS connection now */
567 result = smtp_state_starttls(conn);
568 else if(data->set.use_ssl == CURLUSESSL_TRY)
569 /* Fallback and carry on with authentication */
570 result = smtp_authenticate(conn);
572 failf(data, "STARTTLS not supported.");
573 result = CURLE_USE_SSL_FAILED;
577 result = smtp_authenticate(conn);
582 /* For HELO responses */
583 static CURLcode smtp_state_helo_resp(struct connectdata *conn, int smtpcode,
586 CURLcode result = CURLE_OK;
587 struct SessionHandle *data = conn->data;
589 (void)instate; /* no use for this yet */
591 if(smtpcode/100 != 2) {
592 failf(data, "Remote access denied: %d", smtpcode);
593 result = CURLE_REMOTE_ACCESS_DENIED;
596 /* End of connect phase */
597 state(conn, SMTP_STOP);
602 /* For AUTH PLAIN (without initial response) responses */
603 static CURLcode smtp_state_auth_plain_resp(struct connectdata *conn,
607 CURLcode result = CURLE_OK;
608 struct SessionHandle *data = conn->data;
610 char *plainauth = NULL;
612 (void)instate; /* no use for this yet */
614 if(smtpcode != 334) {
615 failf(data, "Access denied: %d", smtpcode);
616 result = CURLE_LOGIN_DENIED;
619 /* Create the authorisation message */
620 result = Curl_sasl_create_plain_message(conn->data, conn->user,
621 conn->passwd, &plainauth, &len);
623 /* Send the message */
626 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", plainauth);
629 state(conn, SMTP_AUTH_FINAL);
632 Curl_safefree(plainauth);
639 /* For AUTH LOGIN (without initial response) responses */
640 static CURLcode smtp_state_auth_login_resp(struct connectdata *conn,
644 CURLcode result = CURLE_OK;
645 struct SessionHandle *data = conn->data;
647 char *authuser = NULL;
649 (void)instate; /* no use for this yet */
651 if(smtpcode != 334) {
652 failf(data, "Access denied: %d", smtpcode);
653 result = CURLE_LOGIN_DENIED;
656 /* Create the user message */
657 result = Curl_sasl_create_login_message(conn->data, conn->user,
663 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", authuser);
666 state(conn, SMTP_AUTH_LOGIN_PASSWD);
669 Curl_safefree(authuser);
676 /* For AUTH LOGIN user entry responses */
677 static CURLcode smtp_state_auth_login_password_resp(struct connectdata *conn,
681 CURLcode result = CURLE_OK;
682 struct SessionHandle *data = conn->data;
684 char *authpasswd = NULL;
686 (void)instate; /* no use for this yet */
688 if(smtpcode != 334) {
689 failf(data, "Access denied: %d", smtpcode);
690 result = CURLE_LOGIN_DENIED;
693 /* Create the password message */
694 result = Curl_sasl_create_login_message(conn->data, conn->passwd,
697 /* Send the password */
700 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", authpasswd);
703 state(conn, SMTP_AUTH_FINAL);
706 Curl_safefree(authpasswd);
713 #ifndef CURL_DISABLE_CRYPTO_AUTH
714 /* For AUTH CRAM-MD5 responses */
715 static CURLcode smtp_state_auth_cram_resp(struct connectdata *conn,
719 CURLcode result = CURLE_OK;
720 struct SessionHandle *data = conn->data;
721 char *chlg64 = data->state.buffer;
723 char *rplyb64 = NULL;
725 (void)instate; /* no use for this yet */
727 if(smtpcode != 334) {
728 failf(data, "Access denied: %d", smtpcode);
729 return CURLE_LOGIN_DENIED;
732 /* Get the challenge */
733 for(chlg64 += 4; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
736 /* Terminate the challenge */
738 for(len = strlen(chlg64); len--;)
739 if(chlg64[len] != '\r' && chlg64[len] != '\n' && chlg64[len] != ' ' &&
748 /* Create the response message */
749 result = Curl_sasl_create_cram_md5_message(data, chlg64, conn->user,
750 conn->passwd, &rplyb64, &len);
752 /* Send the response */
755 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", rplyb64);
758 state(conn, SMTP_AUTH_FINAL);
761 Curl_safefree(rplyb64);
767 /* For AUTH DIGEST-MD5 challenge responses */
768 static CURLcode smtp_state_auth_digest_resp(struct connectdata *conn,
772 CURLcode result = CURLE_OK;
773 struct SessionHandle *data = conn->data;
774 char *chlg64 = data->state.buffer;
776 char *rplyb64 = NULL;
778 (void)instate; /* no use for this yet */
780 if(smtpcode != 334) {
781 failf(data, "Access denied: %d", smtpcode);
782 return CURLE_LOGIN_DENIED;
785 /* Get the challenge */
786 for(chlg64 += 4; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
789 /* Create the response message */
790 result = Curl_sasl_create_digest_md5_message(data, chlg64, conn->user,
791 conn->passwd, "smtp",
794 /* Send the response */
797 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", rplyb64);
800 state(conn, SMTP_AUTH_DIGESTMD5_RESP);
803 Curl_safefree(rplyb64);
809 /* For AUTH DIGEST-MD5 challenge-response responses */
810 static CURLcode smtp_state_auth_digest_resp_resp(struct connectdata *conn,
814 CURLcode result = CURLE_OK;
815 struct SessionHandle *data = conn->data;
817 (void)instate; /* no use for this yet */
819 if(smtpcode != 334) {
820 failf(data, "Authentication failed: %d", smtpcode);
821 result = CURLE_LOGIN_DENIED;
824 /* Send an empty response */
825 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "");
828 state(conn, SMTP_AUTH_FINAL);
837 /* For AUTH NTLM (without initial response) responses */
838 static CURLcode smtp_state_auth_ntlm_resp(struct connectdata *conn,
842 CURLcode result = CURLE_OK;
843 struct SessionHandle *data = conn->data;
844 char *type1msg = NULL;
847 (void)instate; /* no use for this yet */
849 if(smtpcode != 334) {
850 failf(data, "Access denied: %d", smtpcode);
851 result = CURLE_LOGIN_DENIED;
854 /* Create the type-1 message */
855 result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
859 /* Send the message */
862 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", type1msg);
865 state(conn, SMTP_AUTH_NTLM_TYPE2MSG);
868 Curl_safefree(type1msg);
875 /* For NTLM type-2 responses (sent in reponse to our type-1 message) */
876 static CURLcode smtp_state_auth_ntlm_type2msg_resp(struct connectdata *conn,
880 CURLcode result = CURLE_OK;
881 struct SessionHandle *data = conn->data;
882 char *type3msg = NULL;
885 (void)instate; /* no use for this yet */
887 if(smtpcode != 334) {
888 failf(data, "Access denied: %d", smtpcode);
889 result = CURLE_LOGIN_DENIED;
892 /* Create the type-3 message */
893 result = Curl_sasl_create_ntlm_type3_message(data,
894 data->state.buffer + 4,
895 conn->user, conn->passwd,
899 /* Send the message */
902 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", type3msg);
905 state(conn, SMTP_AUTH_FINAL);
908 Curl_safefree(type3msg);
916 /* For the final responses to the AUTH sequence */
917 static CURLcode smtp_state_auth_final_resp(struct connectdata *conn,
921 CURLcode result = CURLE_OK;
922 struct SessionHandle *data = conn->data;
924 (void)instate; /* no use for this yet */
926 if(smtpcode != 235) {
927 failf(data, "Authentication failed: %d", smtpcode);
928 result = CURLE_LOGIN_DENIED;
931 /* End of connect phase */
932 state(conn, SMTP_STOP);
937 /* Start the DO phase */
938 static CURLcode smtp_mail(struct connectdata *conn)
943 CURLcode result = CURLE_OK;
944 struct SessionHandle *data = conn->data;
946 /* Calculate the FROM parameter */
947 if(!data->set.str[STRING_MAIL_FROM])
948 /* Null reverse-path, RFC-2821, sect. 3.7 */
950 else if(data->set.str[STRING_MAIL_FROM][0] == '<')
951 from = aprintf("%s", data->set.str[STRING_MAIL_FROM]);
953 from = aprintf("<%s>", data->set.str[STRING_MAIL_FROM]);
956 return CURLE_OUT_OF_MEMORY;
958 /* Calculate the optional AUTH parameter */
959 if(data->set.str[STRING_MAIL_AUTH] && conn->proto.smtpc.authused) {
960 if(data->set.str[STRING_MAIL_AUTH][0] != '\0')
961 auth = aprintf("%s", data->set.str[STRING_MAIL_AUTH]);
963 /* Empty AUTH, RFC-2554, sect. 5 */
969 return CURLE_OUT_OF_MEMORY;
973 /* calculate the optional SIZE parameter */
974 if(conn->proto.smtpc.size_supported && conn->data->set.infilesize > 0) {
975 size = aprintf("%" FORMAT_OFF_T, data->set.infilesize);
981 return CURLE_OUT_OF_MEMORY;
985 /* Send the MAIL command */
987 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
988 "MAIL FROM:%s", from);
989 else if(auth && !size)
990 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
991 "MAIL FROM:%s AUTH=%s", from, auth);
992 else if(auth && size)
993 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
994 "MAIL FROM:%s AUTH=%s SIZE=%s", from, auth, size);
996 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
997 "MAIL FROM:%s SIZE=%s", from, size);
1000 Curl_safefree(auth);
1001 Curl_safefree(size);
1004 state(conn, SMTP_MAIL);
1009 static CURLcode smtp_rcpt_to(struct connectdata *conn)
1011 CURLcode result = CURLE_OK;
1012 struct SessionHandle *data = conn->data;
1013 struct SMTP *smtp = data->state.proto.smtp;
1015 /* Send the RCPT TO command */
1017 if(smtp->rcpt->data[0] == '<')
1018 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:%s",
1021 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:<%s>",
1024 state(conn, SMTP_RCPT);
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 */