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
30 ***************************************************************************/
32 #include "curl_setup.h"
34 #ifndef CURL_DISABLE_SMTP
36 #ifdef HAVE_NETINET_IN_H
37 #include <netinet/in.h>
39 #ifdef HAVE_ARPA_INET_H
40 #include <arpa/inet.h>
43 #include <sys/utsname.h>
53 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
55 #define in_addr_t unsigned long
58 #include <curl/curl.h>
66 #include "http.h" /* for HTTP proxy tunnel stuff */
70 #include "strtoofft.h"
79 #include "curl_gethostname.h"
80 #include "curl_sasl.h"
83 #define _MPRINTF_REPLACE /* use our functions only */
84 #include <curl/mprintf.h>
86 #include "curl_memory.h"
87 /* The last #include file should be: */
90 /* Local API functions */
91 static CURLcode smtp_regular_transfer(struct connectdata *conn, bool *done);
92 static CURLcode smtp_do(struct connectdata *conn, bool *done);
93 static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
95 static CURLcode smtp_connect(struct connectdata *conn, bool *done);
96 static CURLcode smtp_disconnect(struct connectdata *conn, bool dead);
97 static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done);
98 static int smtp_getsock(struct connectdata *conn, curl_socket_t *socks,
100 static CURLcode smtp_doing(struct connectdata *conn, bool *dophase_done);
101 static CURLcode smtp_setup_connection(struct connectdata *conn);
104 * SMTP protocol handler.
107 const struct Curl_handler Curl_handler_smtp = {
109 smtp_setup_connection, /* setup_connection */
111 smtp_done, /* done */
112 ZERO_NULL, /* do_more */
113 smtp_connect, /* connect_it */
114 smtp_multi_statemach, /* connecting */
115 smtp_doing, /* doing */
116 smtp_getsock, /* proto_getsock */
117 smtp_getsock, /* doing_getsock */
118 ZERO_NULL, /* domore_getsock */
119 ZERO_NULL, /* perform_getsock */
120 smtp_disconnect, /* disconnect */
121 ZERO_NULL, /* readwrite */
122 PORT_SMTP, /* defport */
123 CURLPROTO_SMTP, /* protocol */
124 PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY /* flags */
129 * SMTPS protocol handler.
132 const struct Curl_handler Curl_handler_smtps = {
133 "SMTPS", /* scheme */
134 smtp_setup_connection, /* setup_connection */
136 smtp_done, /* done */
137 ZERO_NULL, /* do_more */
138 smtp_connect, /* connect_it */
139 smtp_multi_statemach, /* connecting */
140 smtp_doing, /* doing */
141 smtp_getsock, /* proto_getsock */
142 smtp_getsock, /* doing_getsock */
143 ZERO_NULL, /* domore_getsock */
144 ZERO_NULL, /* perform_getsock */
145 smtp_disconnect, /* disconnect */
146 ZERO_NULL, /* readwrite */
147 PORT_SMTPS, /* defport */
148 CURLPROTO_SMTP | CURLPROTO_SMTPS, /* protocol */
149 PROTOPT_CLOSEACTION | PROTOPT_SSL
150 | PROTOPT_NOURLQUERY /* flags */
154 #ifndef CURL_DISABLE_HTTP
156 * HTTP-proxyed SMTP protocol handler.
159 static const struct Curl_handler Curl_handler_smtp_proxy = {
161 ZERO_NULL, /* setup_connection */
162 Curl_http, /* do_it */
163 Curl_http_done, /* done */
164 ZERO_NULL, /* do_more */
165 ZERO_NULL, /* connect_it */
166 ZERO_NULL, /* connecting */
167 ZERO_NULL, /* doing */
168 ZERO_NULL, /* proto_getsock */
169 ZERO_NULL, /* doing_getsock */
170 ZERO_NULL, /* domore_getsock */
171 ZERO_NULL, /* perform_getsock */
172 ZERO_NULL, /* disconnect */
173 ZERO_NULL, /* readwrite */
174 PORT_SMTP, /* defport */
175 CURLPROTO_HTTP, /* protocol */
176 PROTOPT_NONE /* flags */
181 * HTTP-proxyed SMTPS protocol handler.
184 static const struct Curl_handler Curl_handler_smtps_proxy = {
185 "SMTPS", /* scheme */
186 ZERO_NULL, /* setup_connection */
187 Curl_http, /* do_it */
188 Curl_http_done, /* done */
189 ZERO_NULL, /* do_more */
190 ZERO_NULL, /* connect_it */
191 ZERO_NULL, /* connecting */
192 ZERO_NULL, /* doing */
193 ZERO_NULL, /* proto_getsock */
194 ZERO_NULL, /* doing_getsock */
195 ZERO_NULL, /* domore_getsock */
196 ZERO_NULL, /* perform_getsock */
197 ZERO_NULL, /* disconnect */
198 ZERO_NULL, /* readwrite */
199 PORT_SMTPS, /* defport */
200 CURLPROTO_HTTP, /* protocol */
201 PROTOPT_NONE /* flags */
207 static void smtp_to_smtps(struct connectdata *conn)
209 conn->handler = &Curl_handler_smtps;
212 #define smtp_to_smtps(x) Curl_nop_stmt
215 /* Function that checks for an ending SMTP status code at the start of the
216 given string, but also detects various capabilities from the EHLO response
217 including the supported authentication mechanisms. */
218 static bool smtp_endofresp(struct connectdata *conn, char *line, size_t len,
221 struct smtp_conn *smtpc = &conn->proto.smtpc;
225 if(len < 4 || !ISDIGIT(line[0]) || !ISDIGIT(line[1]) || !ISDIGIT(line[2]))
226 return FALSE; /* Nothing for us */
228 /* Do we have a command response? */
229 result = (line[3] == ' ') ? TRUE : FALSE;
231 *resp = curlx_sltosi(strtol(line, NULL, 10));
233 /* Are we processing EHLO command data? */
234 if(smtpc->state == SMTP_EHLO && (!result || (result && *resp/100 == 2))) {
238 /* Does the server support the STARTTLS capability? */
239 if(len >= 8 && !memcmp(line, "STARTTLS", 8))
240 smtpc->tls_supported = TRUE;
242 /* Does the server support the SIZE capability? */
243 else if(len >= 4 && !memcmp(line, "SIZE", 4))
244 smtpc->size_supported = TRUE;
246 /* Do we have the authentication mechanism list? */
247 else if(len >= 5 && !memcmp(line, "AUTH ", 5)) {
251 /* Loop through the data line */
254 (*line == ' ' || *line == '\t' ||
255 *line == '\r' || *line == '\n')) {
264 /* Extract the word */
265 for(wordlen = 0; wordlen < len && line[wordlen] != ' ' &&
266 line[wordlen] != '\t' && line[wordlen] != '\r' &&
267 line[wordlen] != '\n';)
270 /* Test the word for a matching authentication mechanism */
271 if(wordlen == 5 && !memcmp(line, "LOGIN", 5))
272 smtpc->authmechs |= SASL_MECH_LOGIN;
273 else if(wordlen == 5 && !memcmp(line, "PLAIN", 5))
274 smtpc->authmechs |= SASL_MECH_PLAIN;
275 else if(wordlen == 8 && !memcmp(line, "CRAM-MD5", 8))
276 smtpc->authmechs |= SASL_MECH_CRAM_MD5;
277 else if(wordlen == 10 && !memcmp(line, "DIGEST-MD5", 10))
278 smtpc->authmechs |= SASL_MECH_DIGEST_MD5;
279 else if(wordlen == 6 && !memcmp(line, "GSSAPI", 6))
280 smtpc->authmechs |= SASL_MECH_GSSAPI;
281 else if(wordlen == 8 && !memcmp(line, "EXTERNAL", 8))
282 smtpc->authmechs |= SASL_MECH_EXTERNAL;
283 else if(wordlen == 4 && !memcmp(line, "NTLM", 4))
284 smtpc->authmechs |= SASL_MECH_NTLM;
295 /* This is the ONLY way to change SMTP state! */
296 static void state(struct connectdata *conn, smtpstate newstate)
298 struct smtp_conn *smtpc = &conn->proto.smtpc;
299 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
300 /* for debug purposes */
301 static const char * const names[] = {
313 "AUTH_DIGESTMD5_RESP",
315 "AUTH_NTLM_TYPE2MSG",
325 if(smtpc->state != newstate)
326 infof(conn->data, "SMTP %p state change from %s to %s\n",
327 smtpc, names[smtpc->state], names[newstate]);
330 smtpc->state = newstate;
333 static CURLcode smtp_state_ehlo(struct connectdata *conn)
335 CURLcode result = CURLE_OK;
336 struct smtp_conn *smtpc = &conn->proto.smtpc;
338 smtpc->authmechs = 0; /* No known authentication mechanisms yet */
339 smtpc->authused = 0; /* Clear the authentication mechanism used
340 for esmtp connections */
341 smtpc->tls_supported = FALSE; /* Clear the TLS capability */
343 /* Send the EHLO command */
344 result = Curl_pp_sendf(&smtpc->pp, "EHLO %s", smtpc->domain);
349 state(conn, SMTP_EHLO);
354 static CURLcode smtp_state_helo(struct connectdata *conn)
356 CURLcode result = CURLE_OK;
357 struct smtp_conn *smtpc = &conn->proto.smtpc;
359 smtpc->authused = 0; /* No authentication mechanism used in smtp
362 /* Send the HELO command */
363 result = Curl_pp_sendf(&smtpc->pp, "HELO %s", smtpc->domain);
368 state(conn, SMTP_HELO);
373 static CURLcode smtp_state_starttls(struct connectdata *conn)
375 CURLcode result = CURLE_OK;
377 /* Send the STARTTLS command */
378 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "STARTTLS");
381 state(conn, SMTP_STARTTLS);
386 static CURLcode smtp_state_upgrade_tls(struct connectdata *conn)
388 CURLcode result = CURLE_OK;
389 struct smtp_conn *smtpc = &conn->proto.smtpc;
391 /* Start the SSL connection */
392 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone);
395 if(smtpc->state != SMTP_UPGRADETLS)
396 state(conn, SMTP_UPGRADETLS);
400 result = smtp_state_ehlo(conn);
407 static CURLcode smtp_authenticate(struct connectdata *conn)
409 CURLcode result = CURLE_OK;
410 struct smtp_conn *smtpc = &conn->proto.smtpc;
411 const char *mech = NULL;
412 char *initresp = NULL;
414 smtpstate state1 = SMTP_STOP;
415 smtpstate state2 = SMTP_STOP;
417 /* Check we have a username and password to authenticate with and end the
418 connect phase if we don't */
419 if(!conn->bits.user_passwd) {
420 state(conn, SMTP_STOP);
425 /* Calculate the supported authentication mechanism, by decreasing order of
426 security, as well as the initial response where appropriate */
427 #ifndef CURL_DISABLE_CRYPTO_AUTH
428 if(smtpc->authmechs & 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) {
435 state1 = SMTP_AUTH_CRAMMD5;
436 smtpc->authused = SASL_MECH_CRAM_MD5;
441 if(smtpc->authmechs & SASL_MECH_NTLM) {
443 state1 = SMTP_AUTH_NTLM;
444 state2 = SMTP_AUTH_NTLM_TYPE2MSG;
445 smtpc->authused = SASL_MECH_NTLM;
446 result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
452 if(smtpc->authmechs & SASL_MECH_LOGIN) {
454 state1 = SMTP_AUTH_LOGIN;
455 state2 = SMTP_AUTH_LOGIN_PASSWD;
456 smtpc->authused = SASL_MECH_LOGIN;
457 result = Curl_sasl_create_login_message(conn->data, conn->user,
460 else if(smtpc->authmechs & SASL_MECH_PLAIN) {
462 state1 = SMTP_AUTH_PLAIN;
464 smtpc->authused = SASL_MECH_PLAIN;
465 result = Curl_sasl_create_plain_message(conn->data, conn->user,
466 conn->passwd, &initresp, &len);
469 /* Other mechanisms not supported */
470 infof(conn->data, "No known authentication mechanisms supported!\n");
471 result = CURLE_LOGIN_DENIED;
475 /* Perform SASL based authentication */
477 strlen(mech) + len <= 512 - 8) { /* AUTH <mech> ...<crlf> */
478 result = Curl_pp_sendf(&smtpc->pp, "AUTH %s %s", mech, initresp);
484 result = Curl_pp_sendf(&smtpc->pp, "AUTH %s", mech);
490 Curl_safefree(initresp);
496 /* For the initial server greeting */
497 static CURLcode smtp_state_servergreet_resp(struct connectdata *conn,
501 CURLcode result = CURLE_OK;
502 struct SessionHandle *data = conn->data;
504 (void)instate; /* no use for this yet */
506 if(smtpcode/100 != 2) {
507 failf(data, "Got unexpected smtp-server response: %d", smtpcode);
508 return CURLE_FTP_WEIRD_SERVER_REPLY;
511 result = smtp_state_ehlo(conn);
516 /* For STARTTLS responses */
517 static CURLcode smtp_state_starttls_resp(struct connectdata *conn,
521 CURLcode result = CURLE_OK;
522 struct SessionHandle *data = conn->data;
524 (void)instate; /* no use for this yet */
526 if(smtpcode != 220) {
527 if(data->set.use_ssl != CURLUSESSL_TRY) {
528 failf(data, "STARTTLS denied. %c", smtpcode);
529 result = CURLE_USE_SSL_FAILED;
532 result = smtp_authenticate(conn);
535 result = smtp_state_upgrade_tls(conn);
540 /* For EHLO responses */
541 static CURLcode smtp_state_ehlo_resp(struct connectdata *conn, int smtpcode,
544 CURLcode result = CURLE_OK;
545 struct SessionHandle *data = conn->data;
546 struct smtp_conn *smtpc = &conn->proto.smtpc;
548 (void)instate; /* no use for this yet */
550 if(smtpcode/100 != 2) {
551 if((data->set.use_ssl <= CURLUSESSL_TRY || conn->ssl[FIRSTSOCKET].use) &&
552 !conn->bits.user_passwd)
553 result = smtp_state_helo(conn);
555 failf(data, "Remote access denied: %d", smtpcode);
556 result = CURLE_REMOTE_ACCESS_DENIED;
559 else if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
560 /* We don't have a SSL/TLS connection yet, but SSL is requested */
561 if(smtpc->tls_supported)
562 /* Switch to TLS connection now */
563 result = smtp_state_starttls(conn);
564 else if(data->set.use_ssl == CURLUSESSL_TRY)
565 /* Fallback and carry on with authentication */
566 result = smtp_authenticate(conn);
568 failf(data, "STARTTLS not supported.");
569 result = CURLE_USE_SSL_FAILED;
573 result = smtp_authenticate(conn);
578 /* For HELO responses */
579 static CURLcode smtp_state_helo_resp(struct connectdata *conn, int smtpcode,
582 CURLcode result = CURLE_OK;
583 struct SessionHandle *data = conn->data;
585 (void)instate; /* no use for this yet */
587 if(smtpcode/100 != 2) {
588 failf(data, "Remote access denied: %d", smtpcode);
589 result = CURLE_REMOTE_ACCESS_DENIED;
592 /* End of connect phase */
593 state(conn, SMTP_STOP);
598 /* For AUTH PLAIN (without initial response) responses */
599 static CURLcode smtp_state_auth_plain_resp(struct connectdata *conn,
603 CURLcode result = CURLE_OK;
604 struct SessionHandle *data = conn->data;
606 char *plainauth = NULL;
608 (void)instate; /* no use for this yet */
610 if(smtpcode != 334) {
611 failf(data, "Access denied: %d", smtpcode);
612 result = CURLE_LOGIN_DENIED;
615 /* Create the authorisation message */
616 result = Curl_sasl_create_plain_message(conn->data, conn->user,
617 conn->passwd, &plainauth, &len);
619 /* Send the message */
622 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", plainauth);
625 state(conn, SMTP_AUTH);
628 Curl_safefree(plainauth);
635 /* For AUTH LOGIN (without initial response) responses */
636 static CURLcode smtp_state_auth_login_resp(struct connectdata *conn,
640 CURLcode result = CURLE_OK;
641 struct SessionHandle *data = conn->data;
643 char *authuser = NULL;
645 (void)instate; /* no use for this yet */
647 if(smtpcode != 334) {
648 failf(data, "Access denied: %d", smtpcode);
649 result = CURLE_LOGIN_DENIED;
652 /* Create the user message */
653 result = Curl_sasl_create_login_message(conn->data, conn->user,
659 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", authuser);
662 state(conn, SMTP_AUTH_LOGIN_PASSWD);
665 Curl_safefree(authuser);
672 /* For AUTH LOGIN user entry responses */
673 static CURLcode smtp_state_auth_login_password_resp(struct connectdata *conn,
677 CURLcode result = CURLE_OK;
678 struct SessionHandle *data = conn->data;
680 char *authpasswd = NULL;
682 (void)instate; /* no use for this yet */
684 if(smtpcode != 334) {
685 failf(data, "Access denied: %d", smtpcode);
686 result = CURLE_LOGIN_DENIED;
689 /* Create the password message */
690 result = Curl_sasl_create_login_message(conn->data, conn->passwd,
693 /* Send the password */
696 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", authpasswd);
699 state(conn, SMTP_AUTH);
702 Curl_safefree(authpasswd);
709 #ifndef CURL_DISABLE_CRYPTO_AUTH
710 /* For AUTH CRAM-MD5 responses */
711 static CURLcode smtp_state_auth_cram_resp(struct connectdata *conn,
715 CURLcode result = CURLE_OK;
716 struct SessionHandle *data = conn->data;
717 char *chlg64 = data->state.buffer;
719 char *rplyb64 = NULL;
721 (void)instate; /* no use for this yet */
723 if(smtpcode != 334) {
724 failf(data, "Access denied: %d", smtpcode);
725 return CURLE_LOGIN_DENIED;
728 /* Get the challenge */
729 for(chlg64 += 4; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
732 /* Terminate the challenge */
734 for(len = strlen(chlg64); len--;)
735 if(chlg64[len] != '\r' && chlg64[len] != '\n' && chlg64[len] != ' ' &&
744 /* Create the response message */
745 result = Curl_sasl_create_cram_md5_message(data, chlg64, conn->user,
746 conn->passwd, &rplyb64, &len);
748 /* Send the response */
751 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", rplyb64);
754 state(conn, SMTP_AUTH);
757 Curl_safefree(rplyb64);
763 /* For AUTH DIGEST-MD5 challenge responses */
764 static CURLcode smtp_state_auth_digest_resp(struct connectdata *conn,
768 CURLcode result = CURLE_OK;
769 struct SessionHandle *data = conn->data;
770 char *chlg64 = data->state.buffer;
772 char *rplyb64 = NULL;
774 (void)instate; /* no use for this yet */
776 if(smtpcode != 334) {
777 failf(data, "Access denied: %d", smtpcode);
778 return CURLE_LOGIN_DENIED;
781 /* Get the challenge */
782 for(chlg64 += 4; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
785 /* Create the response message */
786 result = Curl_sasl_create_digest_md5_message(data, chlg64, conn->user,
787 conn->passwd, "smtp",
790 /* Send the response */
793 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", rplyb64);
796 state(conn, SMTP_AUTH_DIGESTMD5_RESP);
799 Curl_safefree(rplyb64);
805 /* For AUTH DIGEST-MD5 challenge-response responses */
806 static CURLcode smtp_state_auth_digest_resp_resp(struct connectdata *conn,
810 CURLcode result = CURLE_OK;
811 struct SessionHandle *data = conn->data;
813 (void)instate; /* no use for this yet */
815 if(smtpcode != 334) {
816 failf(data, "Authentication failed: %d", smtpcode);
817 result = CURLE_LOGIN_DENIED;
820 /* Send an empty response */
821 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "");
824 state(conn, SMTP_AUTH);
833 /* For AUTH NTLM (without initial response) responses */
834 static CURLcode smtp_state_auth_ntlm_resp(struct connectdata *conn,
838 CURLcode result = CURLE_OK;
839 struct SessionHandle *data = conn->data;
840 char *type1msg = NULL;
843 (void)instate; /* no use for this yet */
845 if(smtpcode != 334) {
846 failf(data, "Access denied: %d", smtpcode);
847 result = CURLE_LOGIN_DENIED;
850 /* Create the type-1 message */
851 result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
855 /* Send the message */
858 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", type1msg);
861 state(conn, SMTP_AUTH_NTLM_TYPE2MSG);
864 Curl_safefree(type1msg);
871 /* For NTLM type-2 responses (sent in reponse to our type-1 message) */
872 static CURLcode smtp_state_auth_ntlm_type2msg_resp(struct connectdata *conn,
876 CURLcode result = CURLE_OK;
877 struct SessionHandle *data = conn->data;
878 char *type3msg = NULL;
881 (void)instate; /* no use for this yet */
883 if(smtpcode != 334) {
884 failf(data, "Access denied: %d", smtpcode);
885 result = CURLE_LOGIN_DENIED;
888 /* Create the type-3 message */
889 result = Curl_sasl_create_ntlm_type3_message(data,
890 data->state.buffer + 4,
891 conn->user, conn->passwd,
895 /* Send the message */
898 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", type3msg);
901 state(conn, SMTP_AUTH);
904 Curl_safefree(type3msg);
912 /* For the final responses to the AUTH sequence */
913 static CURLcode smtp_state_auth_final_resp(struct connectdata *conn,
917 CURLcode result = CURLE_OK;
918 struct SessionHandle *data = conn->data;
920 (void)instate; /* no use for this yet */
922 if(smtpcode != 235) {
923 failf(data, "Authentication failed: %d", smtpcode);
924 result = CURLE_LOGIN_DENIED;
927 /* End of connect phase */
928 state(conn, SMTP_STOP);
933 /* Start the DO phase */
934 static CURLcode smtp_mail(struct connectdata *conn)
939 CURLcode result = CURLE_OK;
940 struct SessionHandle *data = conn->data;
942 /* Calculate the FROM parameter */
943 if(!data->set.str[STRING_MAIL_FROM])
944 /* Null reverse-path, RFC-2821, sect. 3.7 */
946 else if(data->set.str[STRING_MAIL_FROM][0] == '<')
947 from = aprintf("%s", data->set.str[STRING_MAIL_FROM]);
949 from = aprintf("<%s>", data->set.str[STRING_MAIL_FROM]);
952 return CURLE_OUT_OF_MEMORY;
954 /* Calculate the optional AUTH parameter */
955 if(data->set.str[STRING_MAIL_AUTH] && conn->proto.smtpc.authused) {
956 if(data->set.str[STRING_MAIL_AUTH][0] != '\0')
957 auth = aprintf("%s", data->set.str[STRING_MAIL_AUTH]);
959 /* Empty AUTH, RFC-2554, sect. 5 */
965 return CURLE_OUT_OF_MEMORY;
969 /* calculate the optional SIZE parameter */
970 if(conn->proto.smtpc.size_supported && conn->data->set.infilesize > 0) {
971 size = aprintf("%" FORMAT_OFF_T, data->set.infilesize);
977 return CURLE_OUT_OF_MEMORY;
981 /* Send the MAIL command */
983 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
984 "MAIL FROM:%s", from);
985 else if(auth && !size)
986 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
987 "MAIL FROM:%s AUTH=%s", from, auth);
988 else if(auth && size)
989 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
990 "MAIL FROM:%s AUTH=%s SIZE=%s", from, auth, size);
992 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
993 "MAIL FROM:%s SIZE=%s", from, size);
1002 state(conn, SMTP_MAIL);
1007 static CURLcode smtp_rcpt_to(struct connectdata *conn)
1009 CURLcode result = CURLE_OK;
1010 struct SessionHandle *data = conn->data;
1011 struct SMTP *smtp = data->state.proto.smtp;
1013 /* Send the RCPT TO command */
1015 if(smtp->rcpt->data[0] == '<')
1016 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:%s",
1019 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:<%s>",
1022 state(conn, SMTP_RCPT);
1028 /* For MAIL responses */
1029 static CURLcode smtp_state_mail_resp(struct connectdata *conn, int smtpcode,
1032 CURLcode result = CURLE_OK;
1033 struct SessionHandle *data = conn->data;
1034 struct SMTP *smtp = data->state.proto.smtp;
1036 (void)instate; /* no use for this yet */
1038 if(smtpcode/100 != 2) {
1039 failf(data, "MAIL failed: %d", smtpcode);
1040 result = CURLE_SEND_ERROR;
1041 state(conn, SMTP_STOP);
1044 smtp->rcpt = data->set.mail_rcpt;
1046 result = smtp_rcpt_to(conn);
1052 /* For RCPT responses */
1053 static CURLcode smtp_state_rcpt_resp(struct connectdata *conn, int smtpcode,
1056 CURLcode result = CURLE_OK;
1057 struct SessionHandle *data = conn->data;
1058 struct SMTP *smtp = data->state.proto.smtp;
1060 (void)instate; /* no use for this yet */
1062 if(smtpcode/100 != 2) {
1063 failf(data, "RCPT failed: %d", smtpcode);
1064 result = CURLE_SEND_ERROR;
1065 state(conn, SMTP_STOP);
1069 smtp->rcpt = smtp->rcpt->next;
1070 result = smtp_rcpt_to(conn);
1072 /* If we failed or still are sending RCPT data then return */
1073 if(result || smtp->rcpt)
1077 /* Send the DATA command */
1078 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,
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) {
1266 result = Curl_pp_statemach(&smtpc->pp, TRUE);
1274 /* Allocate and initialize the SMTP struct for the current SessionHandle if
1276 static CURLcode smtp_init(struct connectdata *conn)
1278 struct SessionHandle *data = conn->data;
1279 struct SMTP *smtp = data->state.proto.smtp;
1282 smtp = data->state.proto.smtp = calloc(sizeof(struct SMTP), 1);
1284 return CURLE_OUT_OF_MEMORY;
1290 /* For the SMTP "protocol connect" and "doing" phases only */
1291 static int smtp_getsock(struct connectdata *conn, curl_socket_t *socks,
1294 return Curl_pp_getsock(&conn->proto.smtpc.pp, socks, numsocks);
1297 /***********************************************************************
1301 * This function should do everything that is to be considered a part of
1302 * the connection phase.
1304 * The variable pointed to by 'done' will be TRUE if the protocol-layer
1305 * connect phase is done when this function returns, or FALSE if not. When
1306 * called as a part of the easy interface, it will always be TRUE.
1308 static CURLcode smtp_connect(struct connectdata *conn, bool *done)
1310 CURLcode result = CURLE_OK;
1311 struct smtp_conn *smtpc = &conn->proto.smtpc;
1312 struct pingpong *pp = &smtpc->pp;
1313 const char *path = conn->data->state.path;
1314 char localhost[HOSTNAME_MAX + 1];
1316 *done = FALSE; /* default to not done yet */
1318 /* If there already is a protocol-specific struct allocated for this
1319 sessionhandle, deal with it */
1320 Curl_reset_reqproto(conn);
1322 /* Initialise the SMTP layer */
1323 result = smtp_init(conn);
1327 /* We always support persistent connections in SMTP */
1328 conn->bits.close = FALSE;
1330 /* Set the default response time-out */
1331 pp->response_time = RESP_TIMEOUT;
1332 pp->statemach_act = smtp_statemach_act;
1333 pp->endofresp = smtp_endofresp;
1336 /* Initialise the pingpong layer */
1339 /* Calculate the path if necessary */
1341 if(!Curl_gethostname(localhost, sizeof(localhost)))
1347 /* URL decode the path and use it as the domain in our EHLO */
1348 result = Curl_urldecode(conn->data, path, 0, &smtpc->domain, NULL, TRUE);
1352 /* Start off waiting for the server greeting response */
1353 state(conn, SMTP_SERVERGREET);
1355 result = smtp_multi_statemach(conn, done);
1360 /***********************************************************************
1364 * The DONE function. This does what needs to be done after a single DO has
1367 * Input argument is already checked for validity.
1369 static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
1372 CURLcode result = CURLE_OK;
1373 struct SessionHandle *data = conn->data;
1374 struct SMTP *smtp = data->state.proto.smtp;
1375 ssize_t bytes_written;
1380 /* When the easy handle is removed from the multi interface while libcurl
1381 is still trying to resolve the host name, the SMTP struct is not yet
1382 initialized. However, the removal action calls Curl_done() which in
1383 turn calls this function, so we simply return success. */
1387 conn->bits.close = TRUE; /* marked for closure */
1388 result = status; /* use the already set error code */
1390 else if(!data->set.connect_only) {
1391 struct smtp_conn *smtpc = &conn->proto.smtpc;
1392 struct pingpong *pp = &smtpc->pp;
1394 /* Send the end of block data */
1395 result = Curl_write(conn,
1396 conn->writesockfd, /* socket to send to */
1397 SMTP_EOB, /* buffer pointer */
1398 SMTP_EOB_LEN, /* buffer size */
1399 &bytes_written); /* actually sent away */
1404 if(bytes_written != SMTP_EOB_LEN) {
1405 /* The whole chunk was not sent so keep it around and adjust the
1406 pingpong structure accordingly */
1407 pp->sendthis = strdup(SMTP_EOB);
1408 pp->sendsize = SMTP_EOB_LEN;
1409 pp->sendleft = SMTP_EOB_LEN - bytes_written;
1412 /* Successfully sent so adjust the response timeout relative to now */
1413 pp->response = Curl_tvnow();
1415 state(conn, SMTP_POSTDATA);
1417 /* Run the state-machine
1419 TODO: when the multi interface is used, this _really_ should be using
1420 the smtp_multi_statemach function but we have no general support for
1421 non-blocking DONE operations, not in the multi state machine and with
1422 Curl_done() invokes on several places in the code!
1424 result = smtp_block_statemach(conn);
1427 /* Clear the transfer mode for the next request */
1428 smtp->transfer = FTPTRANSFER_BODY;
1433 /***********************************************************************
1437 * This is the actual DO function for SMTP. Send a mail according to the
1438 * options previously setup.
1440 static CURLcode smtp_perform(struct connectdata *conn, bool *connected,
1443 /* This is SMTP and no proxy */
1444 CURLcode result = CURLE_OK;
1446 DEBUGF(infof(conn->data, "DO phase starts\n"));
1448 if(conn->data->set.opt_no_body) {
1449 /* Requested no body means no transfer */
1450 struct SMTP *smtp = conn->data->state.proto.smtp;
1451 smtp->transfer = FTPTRANSFER_INFO;
1454 *dophase_done = FALSE; /* not done yet */
1456 /* Start the first command in the DO phase */
1457 result = smtp_mail(conn);
1461 /* run the state-machine */
1462 result = smtp_multi_statemach(conn, dophase_done);
1464 *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1467 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1472 /***********************************************************************
1476 * This function is registered as 'curl_do' function. It decodes the path
1477 * parts etc as a wrapper to the actual DO function (smtp_perform).
1479 * The input argument is already checked for validity.
1481 static CURLcode smtp_do(struct connectdata *conn, bool *done)
1483 CURLcode result = CURLE_OK;
1485 *done = FALSE; /* default to false */
1487 /* Since connections can be re-used between SessionHandles, there might be a
1488 connection already existing but on a fresh SessionHandle struct. As such
1489 we make sure we have a good SMTP struct to play with. For new connections
1490 the SMTP struct is allocated and setup in the smtp_connect() function. */
1491 Curl_reset_reqproto(conn);
1492 result = smtp_init(conn);
1496 result = smtp_regular_transfer(conn, done);
1501 /***********************************************************************
1505 * This should be called before calling sclose(). We should then wait for the
1506 * response from the server before returning. The calling code should then try
1507 * to close the connection.
1509 static CURLcode smtp_quit(struct connectdata *conn)
1511 CURLcode result = CURLE_OK;
1513 /* Send the QUIT command */
1514 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "QUIT");
1518 state(conn, SMTP_QUIT);
1520 result = smtp_block_statemach(conn);
1525 /***********************************************************************
1529 * Disconnect from an SMTP server. Cleanup protocol-specific per-connection
1530 * resources. BLOCKING.
1532 static CURLcode smtp_disconnect(struct connectdata *conn,
1533 bool dead_connection)
1535 struct smtp_conn *smtpc = &conn->proto.smtpc;
1537 /* We cannot send quit unconditionally. If this connection is stale or
1538 bad in any way, sending quit and waiting around here will make the
1539 disconnect wait in vain and cause more problems than we need to. */
1541 /* The SMTP session may or may not have been allocated/setup at this
1543 if(!dead_connection && smtpc->pp.conn)
1544 (void)smtp_quit(conn); /* ignore errors on QUIT */
1546 /* Disconnect from the server */
1547 Curl_pp_disconnect(&smtpc->pp);
1549 /* Cleanup the SASL module */
1550 Curl_sasl_cleanup(conn, smtpc->authused);
1552 /* Cleanup our connection based variables */
1553 Curl_safefree(smtpc->domain);
1558 /* Call this when the DO phase has completed */
1559 static CURLcode smtp_dophase_done(struct connectdata *conn, bool connected)
1561 struct SMTP *smtp = conn->data->state.proto.smtp;
1565 if(smtp->transfer != FTPTRANSFER_BODY)
1566 /* no data to transfer */
1567 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1572 /* Called from multi.c while DOing */
1573 static CURLcode smtp_doing(struct connectdata *conn, bool *dophase_done)
1575 CURLcode result = smtp_multi_statemach(conn, dophase_done);
1578 DEBUGF(infof(conn->data, "DO phase failed\n"));
1581 result = smtp_dophase_done(conn, FALSE /* not connected */);
1583 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1590 /***********************************************************************
1592 * smtp_regular_transfer()
1594 * The input argument is already checked for validity.
1596 * Performs all commands done before a regular transfer between a local and a
1599 static CURLcode smtp_regular_transfer(struct connectdata *conn,
1602 CURLcode result = CURLE_OK;
1603 bool connected = FALSE;
1604 struct SessionHandle *data = conn->data;
1606 /* Make sure size is unknown at this point */
1607 data->req.size = -1;
1609 Curl_pgrsSetUploadCounter(data, 0);
1610 Curl_pgrsSetDownloadCounter(data, 0);
1611 Curl_pgrsSetUploadSize(data, 0);
1612 Curl_pgrsSetDownloadSize(data, 0);
1614 result = smtp_perform(conn, &connected, dophase_done);
1618 /* The DO phase has not completed yet */
1621 result = smtp_dophase_done(conn, connected);
1627 static CURLcode smtp_setup_connection(struct connectdata *conn)
1629 struct SessionHandle *data = conn->data;
1631 if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
1632 /* Unless we have asked to tunnel SMTP operations through the proxy, we
1633 switch and use HTTP operations only */
1634 #ifndef CURL_DISABLE_HTTP
1635 if(conn->handler == &Curl_handler_smtp)
1636 conn->handler = &Curl_handler_smtp_proxy;
1639 conn->handler = &Curl_handler_smtps_proxy;
1641 failf(data, "SMTPS not supported!");
1642 return CURLE_UNSUPPORTED_PROTOCOL;
1646 /* We explicitly mark this connection as persistent here as we're doing
1647 SMTP over HTTP and thus we accidentally avoid setting this value
1649 conn->bits.close = FALSE;
1651 failf(data, "SMTP over http proxy requires HTTP support built-in!");
1652 return CURLE_UNSUPPORTED_PROTOCOL;
1656 data->state.path++; /* don't include the initial slash */
1661 CURLcode Curl_smtp_escape_eob(struct connectdata *conn, ssize_t nread)
1663 /* When sending a SMTP payload we must detect CRLF. sequences making sure
1664 they are sent as CRLF.. instead, as a . on the beginning of a line will
1665 be deleted by the server when not part of an EOB terminator and a
1666 genuine CRLF.CRLF which isn't escaped will wrongly be detected as end of
1671 struct smtp_conn *smtpc = &conn->proto.smtpc;
1672 struct SessionHandle *data = conn->data;
1674 /* Do we need to allocate the scatch buffer? */
1675 if(!data->state.scratch) {
1676 data->state.scratch = malloc(2 * BUFSIZE);
1678 if(!data->state.scratch) {
1679 failf (data, "Failed to alloc scratch buffer!");
1680 return CURLE_OUT_OF_MEMORY;
1684 /* This loop can be improved by some kind of Boyer-Moore style of
1685 approach but that is saved for later... */
1686 for(i = 0, si = 0; i < nread; i++) {
1687 if(SMTP_EOB[smtpc->eob] == data->req.upload_fromhere[i])
1689 else if(smtpc->eob) {
1690 /* A previous substring matched so output that first */
1691 memcpy(&data->state.scratch[si], SMTP_EOB, smtpc->eob);
1694 /* Then compare the first byte */
1695 if(SMTP_EOB[0] == data->req.upload_fromhere[i])
1701 /* Do we have a match for CRLF. as per RFC-2821, sect. 4.5.2 */
1702 if(SMTP_EOB_FIND_LEN == smtpc->eob) {
1703 /* Copy the replacement data to the target buffer */
1704 memcpy(&data->state.scratch[si], SMTP_EOB_REPL, SMTP_EOB_REPL_LEN);
1705 si += SMTP_EOB_REPL_LEN;
1708 else if(!smtpc->eob)
1709 data->state.scratch[si++] = data->req.upload_fromhere[i];
1713 /* A substring matched before processing ended so output that now */
1714 memcpy(&data->state.scratch[si], SMTP_EOB, smtpc->eob);
1720 /* Only use the new buffer if we replaced something */
1723 /* Upload from the new (replaced) buffer instead */
1724 data->req.upload_fromhere = data->state.scratch;
1726 /* Set the new amount too */
1727 data->req.upload_present = nread;
1733 #endif /* CURL_DISABLE_SMTP */