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 smtp_conn *smtpc = &conn->proto.smtpc;
1012 /* Send the RCPT TO command */
1014 if(smtpc->rcpt->data[0] == '<')
1015 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:%s",
1018 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:<%s>",
1021 state(conn, SMTP_RCPT);
1027 /* For MAIL responses */
1028 static CURLcode smtp_state_mail_resp(struct connectdata *conn, int smtpcode,
1031 CURLcode result = CURLE_OK;
1032 struct SessionHandle *data = conn->data;
1034 (void)instate; /* no use for this yet */
1036 if(smtpcode/100 != 2) {
1037 failf(data, "MAIL failed: %d", smtpcode);
1038 result = CURLE_SEND_ERROR;
1039 state(conn, SMTP_STOP);
1042 struct smtp_conn *smtpc = &conn->proto.smtpc;
1043 smtpc->rcpt = data->set.mail_rcpt;
1045 result = smtp_rcpt_to(conn);
1051 /* For RCPT responses */
1052 static CURLcode smtp_state_rcpt_resp(struct connectdata *conn, int smtpcode,
1055 CURLcode result = CURLE_OK;
1056 struct SessionHandle *data = conn->data;
1058 (void)instate; /* no use for this yet */
1060 if(smtpcode/100 != 2) {
1061 failf(data, "RCPT failed: %d", smtpcode);
1062 result = CURLE_SEND_ERROR;
1063 state(conn, SMTP_STOP);
1066 struct smtp_conn *smtpc = &conn->proto.smtpc;
1069 smtpc->rcpt = smtpc->rcpt->next;
1070 result = smtp_rcpt_to(conn);
1072 /* If we failed or still are sending RCPT data then return */
1073 if(result || smtpc->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 struct SessionHandle *data = conn->data;
1094 struct SMTP *smtp = data->state.proto.smtp;
1096 (void)instate; /* no use for this yet */
1098 if(smtpcode != 354) {
1099 state(conn, SMTP_STOP);
1100 return CURLE_SEND_ERROR;
1104 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, /* no download */
1105 FIRSTSOCKET, smtp->bytecountp);
1107 /* End of do phase */
1108 state(conn, SMTP_STOP);
1113 /* For POSTDATA responses, which are received after the entire DATA
1114 part has been sent to the server */
1115 static CURLcode smtp_state_postdata_resp(struct connectdata *conn,
1119 CURLcode result = CURLE_OK;
1121 (void)instate; /* no use for this yet */
1124 result = CURLE_RECV_ERROR;
1126 /* End of done phase */
1127 state(conn, SMTP_STOP);
1132 static CURLcode smtp_statemach_act(struct connectdata *conn)
1134 CURLcode result = CURLE_OK;
1135 curl_socket_t sock = conn->sock[FIRSTSOCKET];
1136 struct SessionHandle *data = conn->data;
1138 struct smtp_conn *smtpc = &conn->proto.smtpc;
1139 struct pingpong *pp = &smtpc->pp;
1142 /* Busy upgrading the connection; right now all I/O is SSL/TLS, not SMTP */
1143 if(smtpc->state == SMTP_UPGRADETLS)
1144 return smtp_state_upgrade_tls(conn);
1146 /* Flush any data that needs to be sent */
1148 return Curl_pp_flushsend(pp);
1150 /* Read the response from the server */
1151 result = Curl_pp_readresp(sock, pp, &smtpcode, &nread);
1155 /* Store the latest response for later retrieval */
1156 if(smtpc->state != SMTP_QUIT)
1157 data->info.httpcode = smtpcode;
1160 /* We have now received a full SMTP server response */
1161 switch(smtpc->state) {
1162 case SMTP_SERVERGREET:
1163 result = smtp_state_servergreet_resp(conn, smtpcode, smtpc->state);
1167 result = smtp_state_ehlo_resp(conn, smtpcode, smtpc->state);
1171 result = smtp_state_helo_resp(conn, smtpcode, smtpc->state);
1175 result = smtp_state_starttls_resp(conn, smtpcode, smtpc->state);
1178 case SMTP_AUTH_PLAIN:
1179 result = smtp_state_auth_plain_resp(conn, smtpcode, smtpc->state);
1182 case SMTP_AUTH_LOGIN:
1183 result = smtp_state_auth_login_resp(conn, smtpcode, smtpc->state);
1186 case SMTP_AUTH_LOGIN_PASSWD:
1187 result = smtp_state_auth_login_password_resp(conn, smtpcode,
1191 #ifndef CURL_DISABLE_CRYPTO_AUTH
1192 case SMTP_AUTH_CRAMMD5:
1193 result = smtp_state_auth_cram_resp(conn, smtpcode, smtpc->state);
1196 case SMTP_AUTH_DIGESTMD5:
1197 result = smtp_state_auth_digest_resp(conn, smtpcode, smtpc->state);
1200 case SMTP_AUTH_DIGESTMD5_RESP:
1201 result = smtp_state_auth_digest_resp_resp(conn, smtpcode, smtpc->state);
1206 case SMTP_AUTH_NTLM:
1207 result = smtp_state_auth_ntlm_resp(conn, smtpcode, smtpc->state);
1210 case SMTP_AUTH_NTLM_TYPE2MSG:
1211 result = smtp_state_auth_ntlm_type2msg_resp(conn, smtpcode,
1217 result = smtp_state_auth_final_resp(conn, smtpcode, smtpc->state);
1221 result = smtp_state_mail_resp(conn, smtpcode, smtpc->state);
1225 result = smtp_state_rcpt_resp(conn, smtpcode, smtpc->state);
1229 result = smtp_state_data_resp(conn, smtpcode, smtpc->state);
1233 result = smtp_state_postdata_resp(conn, smtpcode, smtpc->state);
1237 /* fallthrough, just stop! */
1239 /* internal error */
1240 state(conn, SMTP_STOP);
1248 /* Called repeatedly until done from multi.c */
1249 static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done)
1251 CURLcode result = CURLE_OK;
1252 struct smtp_conn *smtpc = &conn->proto.smtpc;
1254 if((conn->handler->flags & PROTOPT_SSL) && !smtpc->ssldone)
1255 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone);
1257 result = Curl_pp_statemach(&smtpc->pp, FALSE);
1259 *done = (smtpc->state == SMTP_STOP) ? TRUE : FALSE;
1264 static CURLcode smtp_block_statemach(struct connectdata *conn)
1266 CURLcode result = CURLE_OK;
1267 struct smtp_conn *smtpc = &conn->proto.smtpc;
1269 while(smtpc->state != SMTP_STOP) {
1270 result = Curl_pp_statemach(&smtpc->pp, TRUE);
1278 /* Allocate and initialize the SMTP struct for the current SessionHandle if
1280 static CURLcode smtp_init(struct connectdata *conn)
1282 struct SessionHandle *data = conn->data;
1283 struct SMTP *smtp = data->state.proto.smtp;
1286 smtp = data->state.proto.smtp = calloc(sizeof(struct SMTP), 1);
1288 return CURLE_OUT_OF_MEMORY;
1291 /* Get some initial data into the smtp struct */
1292 smtp->bytecountp = &data->req.bytecount;
1294 /* No need to duplicate user+password, the connectdata struct won't change
1295 during a session, but we re-init them here since on subsequent inits
1296 since the conn struct may have changed or been replaced.
1298 smtp->user = conn->user;
1299 smtp->passwd = conn->passwd;
1304 /* For the SMTP "protocol connect" and "doing" phases only */
1305 static int smtp_getsock(struct connectdata *conn, curl_socket_t *socks,
1308 return Curl_pp_getsock(&conn->proto.smtpc.pp, socks, numsocks);
1311 /***********************************************************************
1315 * This function should do everything that is to be considered a part of
1316 * the connection phase.
1318 * The variable pointed to by 'done' will be TRUE if the protocol-layer
1319 * connect phase is done when this function returns, or FALSE if not. When
1320 * called as a part of the easy interface, it will always be TRUE.
1322 static CURLcode smtp_connect(struct connectdata *conn, bool *done)
1324 CURLcode result = CURLE_OK;
1325 struct smtp_conn *smtpc = &conn->proto.smtpc;
1326 struct pingpong *pp = &smtpc->pp;
1327 const char *path = conn->data->state.path;
1328 char localhost[HOSTNAME_MAX + 1];
1330 *done = FALSE; /* default to not done yet */
1332 /* If there already is a protocol-specific struct allocated for this
1333 sessionhandle, deal with it */
1334 Curl_reset_reqproto(conn);
1336 /* Initialise the SMTP layer */
1337 result = smtp_init(conn);
1341 /* We always support persistent connections in SMTP */
1342 conn->bits.close = FALSE;
1344 /* Set the default response time-out */
1345 pp->response_time = RESP_TIMEOUT;
1346 pp->statemach_act = smtp_statemach_act;
1347 pp->endofresp = smtp_endofresp;
1350 /* Initialise the pingpong layer */
1353 /* Calculate the path if necessary */
1355 if(!Curl_gethostname(localhost, sizeof(localhost)))
1361 /* URL decode the path and use it as the domain in our EHLO */
1362 result = Curl_urldecode(conn->data, path, 0, &smtpc->domain, NULL, TRUE);
1366 /* Start off waiting for the server greeting response */
1367 state(conn, SMTP_SERVERGREET);
1369 result = smtp_multi_statemach(conn, done);
1374 /***********************************************************************
1378 * The DONE function. This does what needs to be done after a single DO has
1381 * Input argument is already checked for validity.
1383 static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
1386 CURLcode result = CURLE_OK;
1387 struct SessionHandle *data = conn->data;
1388 struct SMTP *smtp = data->state.proto.smtp;
1389 ssize_t bytes_written;
1394 /* When the easy handle is removed from the multi while libcurl is still
1395 * trying to resolve the host name, it seems that the SMTP struct is not
1396 * yet initialized, but the removal action calls Curl_done() which calls
1397 * this function. So we simply return success if no SMTP pointer is set.
1402 conn->bits.close = TRUE; /* marked for closure */
1403 result = status; /* use the already set error code */
1405 else if(!data->set.connect_only) {
1406 struct smtp_conn *smtpc = &conn->proto.smtpc;
1407 struct pingpong *pp = &smtpc->pp;
1409 /* Send the end of block data */
1410 result = Curl_write(conn,
1411 conn->writesockfd, /* socket to send to */
1412 SMTP_EOB, /* buffer pointer */
1413 SMTP_EOB_LEN, /* buffer size */
1414 &bytes_written); /* actually sent away */
1419 if(bytes_written != SMTP_EOB_LEN) {
1420 /* The whole chunk was not sent so keep it around and adjust the
1421 pingpong structure accordingly */
1422 pp->sendthis = strdup(SMTP_EOB);
1423 pp->sendsize = SMTP_EOB_LEN;
1424 pp->sendleft = SMTP_EOB_LEN - bytes_written;
1427 /* Successfully sent so adjust the response timeout relative to now */
1428 pp->response = Curl_tvnow();
1430 state(conn, SMTP_POSTDATA);
1432 /* Run the state-machine
1434 TODO: when the multi interface is used, this _really_ should be using
1435 the smtp_multi_statemach function but we have no general support for
1436 non-blocking DONE operations, not in the multi state machine and with
1437 Curl_done() invokes on several places in the code!
1439 result = smtp_block_statemach(conn);
1442 /* Clear the transfer mode for the next request */
1443 smtp->transfer = FTPTRANSFER_BODY;
1448 /***********************************************************************
1452 * This is the actual DO function for SMTP. Get a file/directory according to
1453 * the options previously setup.
1455 static CURLcode smtp_perform(struct connectdata *conn, bool *connected,
1458 /* This is SMTP and no proxy */
1459 CURLcode result = CURLE_OK;
1461 DEBUGF(infof(conn->data, "DO phase starts\n"));
1463 if(conn->data->set.opt_no_body) {
1464 /* Requested no body means no transfer */
1465 struct SMTP *smtp = conn->data->state.proto.smtp;
1466 smtp->transfer = FTPTRANSFER_INFO;
1469 *dophase_done = FALSE; /* not done yet */
1471 /* Start the first command in the DO phase */
1472 result = smtp_mail(conn);
1476 /* run the state-machine */
1477 result = smtp_multi_statemach(conn, dophase_done);
1479 *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1482 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1487 /***********************************************************************
1491 * This function is registered as 'curl_do' function. It decodes the path
1492 * parts etc as a wrapper to the actual DO function (smtp_perform).
1494 * The input argument is already checked for validity.
1496 static CURLcode smtp_do(struct connectdata *conn, bool *done)
1498 CURLcode result = CURLE_OK;
1500 *done = FALSE; /* default to false */
1503 Since connections can be re-used between SessionHandles, this might be a
1504 connection already existing but on a fresh SessionHandle struct so we must
1505 make sure we have a good 'struct SMTP' to play with. For new connections,
1506 the struct SMTP is allocated and setup in the smtp_connect() function.
1508 Curl_reset_reqproto(conn);
1509 result = smtp_init(conn);
1513 result = smtp_regular_transfer(conn, done);
1518 /***********************************************************************
1522 * This should be called before calling sclose(). We should then wait for the
1523 * response from the server before returning. The calling code should then try
1524 * to close the connection.
1526 static CURLcode smtp_quit(struct connectdata *conn)
1528 CURLcode result = CURLE_OK;
1530 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "QUIT");
1534 state(conn, SMTP_QUIT);
1536 result = smtp_block_statemach(conn);
1541 /***********************************************************************
1545 * Disconnect from an SMTP server. Cleanup protocol-specific per-connection
1546 * resources. BLOCKING.
1548 static CURLcode smtp_disconnect(struct connectdata *conn,
1549 bool dead_connection)
1551 struct smtp_conn *smtpc = &conn->proto.smtpc;
1553 /* We cannot send quit unconditionally. If this connection is stale or
1554 bad in any way, sending quit and waiting around here will make the
1555 disconnect wait in vain and cause more problems than we need to */
1557 /* The SMTP session may or may not have been allocated/setup at this
1559 if(!dead_connection && smtpc->pp.conn)
1560 (void)smtp_quit(conn); /* ignore errors on QUIT */
1562 /* Disconnect from the server */
1563 Curl_pp_disconnect(&smtpc->pp);
1565 /* Cleanup the SASL module */
1566 Curl_sasl_cleanup(conn, smtpc->authused);
1568 /* Cleanup our connection based variables */
1569 Curl_safefree(smtpc->domain);
1574 /* Call this when the DO phase has completed */
1575 static CURLcode smtp_dophase_done(struct connectdata *conn, bool connected)
1577 struct SMTP *smtp = conn->data->state.proto.smtp;
1581 if(smtp->transfer != FTPTRANSFER_BODY)
1582 /* no data to transfer */
1583 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1588 /* Called from multi.c while DOing */
1589 static CURLcode smtp_doing(struct connectdata *conn, bool *dophase_done)
1591 CURLcode result = smtp_multi_statemach(conn, dophase_done);
1594 DEBUGF(infof(conn->data, "DO phase failed\n"));
1597 result = smtp_dophase_done(conn, FALSE /* not connected */);
1599 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1606 /***********************************************************************
1608 * smtp_regular_transfer()
1610 * The input argument is already checked for validity.
1612 * Performs all commands done before a regular transfer between a local and a
1615 static CURLcode smtp_regular_transfer(struct connectdata *conn,
1618 CURLcode result = CURLE_OK;
1619 bool connected = FALSE;
1620 struct SessionHandle *data = conn->data;
1622 /* Make sure size is unknown at this point */
1623 data->req.size = -1;
1625 Curl_pgrsSetUploadCounter(data, 0);
1626 Curl_pgrsSetDownloadCounter(data, 0);
1627 Curl_pgrsSetUploadSize(data, 0);
1628 Curl_pgrsSetDownloadSize(data, 0);
1630 result = smtp_perform(conn, &connected, dophase_done);
1634 /* The DO phase has not completed yet */
1637 result = smtp_dophase_done(conn, connected);
1643 static CURLcode smtp_setup_connection(struct connectdata *conn)
1645 struct SessionHandle *data = conn->data;
1647 if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
1648 /* Unless we have asked to tunnel SMTP operations through the proxy, we
1649 switch and use HTTP operations only */
1650 #ifndef CURL_DISABLE_HTTP
1651 if(conn->handler == &Curl_handler_smtp)
1652 conn->handler = &Curl_handler_smtp_proxy;
1655 conn->handler = &Curl_handler_smtps_proxy;
1657 failf(data, "SMTPS not supported!");
1658 return CURLE_UNSUPPORTED_PROTOCOL;
1662 /* We explicitly mark this connection as persistent here as we're doing
1663 SMTP over HTTP and thus we accidentally avoid setting this value
1665 conn->bits.close = FALSE;
1667 failf(data, "SMTP over http proxy requires HTTP support built-in!");
1668 return CURLE_UNSUPPORTED_PROTOCOL;
1672 data->state.path++; /* don't include the initial slash */
1677 CURLcode Curl_smtp_escape_eob(struct connectdata *conn, ssize_t nread)
1679 /* When sending a SMTP payload we must detect CRLF. sequences making sure
1680 they are sent as CRLF.. instead, as a . on the beginning of a line will
1681 be deleted by the server when not part of an EOB terminator and a
1682 genuine CRLF.CRLF which isn't escaped will wrongly be detected as end of
1687 struct smtp_conn *smtpc = &conn->proto.smtpc;
1688 struct SessionHandle *data = conn->data;
1690 /* Do we need to allocate the scatch buffer? */
1691 if(!data->state.scratch) {
1692 data->state.scratch = malloc(2 * BUFSIZE);
1694 if(!data->state.scratch) {
1695 failf (data, "Failed to alloc scratch buffer!");
1696 return CURLE_OUT_OF_MEMORY;
1700 /* This loop can be improved by some kind of Boyer-Moore style of
1701 approach but that is saved for later... */
1702 for(i = 0, si = 0; i < nread; i++) {
1703 if(SMTP_EOB[smtpc->eob] == data->req.upload_fromhere[i])
1705 else if(smtpc->eob) {
1706 /* A previous substring matched so output that first */
1707 memcpy(&data->state.scratch[si], SMTP_EOB, smtpc->eob);
1710 /* Then compare the first byte */
1711 if(SMTP_EOB[0] == data->req.upload_fromhere[i])
1717 /* Do we have a match for CRLF. as per RFC-2821, sect. 4.5.2 */
1718 if(SMTP_EOB_FIND_LEN == smtpc->eob) {
1719 /* Copy the replacement data to the target buffer */
1720 memcpy(&data->state.scratch[si], SMTP_EOB_REPL, SMTP_EOB_REPL_LEN);
1721 si += SMTP_EOB_REPL_LEN;
1724 else if(!smtpc->eob)
1725 data->state.scratch[si++] = data->req.upload_fromhere[i];
1729 /* A substring matched before processing ended so output that now */
1730 memcpy(&data->state.scratch[si], SMTP_EOB, smtpc->eob);
1736 /* Only use the new buffer if we replaced something */
1739 /* Upload from the new (replaced) buffer instead */
1740 data->req.upload_fromhere = data->state.scratch;
1742 /* Set the new amount too */
1743 data->req.upload_present = nread;
1749 #endif /* CURL_DISABLE_SMTP */