1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2012, 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 * RFC2821 SMTP protocol
22 * RFC3207 SMTP over TLS
23 * RFC4954 SMTP Authentication
24 * RFC2195 CRAM-MD5 authentication
25 * RFC2831 DIGEST-MD5 authentication
26 * RFC4616 PLAIN authentication
28 ***************************************************************************/
30 #include "curl_setup.h"
32 #ifndef CURL_DISABLE_SMTP
34 #ifdef HAVE_NETINET_IN_H
35 #include <netinet/in.h>
37 #ifdef HAVE_ARPA_INET_H
38 #include <arpa/inet.h>
41 #include <sys/utsname.h>
51 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
53 #define in_addr_t unsigned long
56 #include <curl/curl.h>
57 #include "curl_urldata.h"
58 #include "curl_sendf.h"
59 #include "curl_if2ip.h"
60 #include "curl_hostip.h"
61 #include "curl_progress.h"
62 #include "curl_transfer.h"
63 #include "curl_escape.h"
64 #include "curl_http.h" /* for HTTP proxy tunnel stuff */
65 #include "curl_socks.h"
66 #include "curl_smtp.h"
68 #include "curl_strtoofft.h"
69 #include "curl_strequal.h"
70 #include "curl_sslgen.h"
71 #include "curl_connect.h"
72 #include "curl_strerror.h"
73 #include "curl_select.h"
74 #include "curl_multiif.h"
76 #include "curl_rawstr.h"
77 #include "curl_gethostname.h"
78 #include "curl_sasl.h"
79 #include "curl_warnless.h"
81 #define _MPRINTF_REPLACE /* use our functions only */
82 #include <curl/mprintf.h>
84 #include "curl_memory.h"
85 /* The last #include file should be: */
86 #include "curl_memdebug.h"
88 /* Local API functions */
89 static CURLcode smtp_regular_transfer(struct connectdata *conn, bool *done);
90 static CURLcode smtp_do(struct connectdata *conn, bool *done);
91 static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
93 static CURLcode smtp_connect(struct connectdata *conn, bool *done);
94 static CURLcode smtp_disconnect(struct connectdata *conn, bool dead);
95 static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done);
96 static int smtp_getsock(struct connectdata *conn,
99 static CURLcode smtp_doing(struct connectdata *conn, bool *dophase_done);
100 static CURLcode smtp_setup_connection(struct connectdata *conn);
101 static CURLcode smtp_state_upgrade_tls(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 */
206 /* Function that checks for an ending smtp status code at the start of the
208 As a side effect, it also flags allowed authentication mechanisms according
209 to EHLO AUTH response. */
210 static int smtp_endofresp(struct pingpong *pp, int *resp)
212 char *line = pp->linestart_resp;
213 size_t len = pp->nread_resp;
214 struct connectdata *conn = pp->conn;
215 struct smtp_conn *smtpc = &conn->proto.smtpc;
219 if(len < 4 || !ISDIGIT(line[0]) || !ISDIGIT(line[1]) || !ISDIGIT(line[2]))
220 return FALSE; /* Nothing for us */
222 if((result = (line[3] == ' ')) != 0)
223 *resp = curlx_sltosi(strtol(line, NULL, 10));
228 if(smtpc->state == SMTP_EHLO && len >= 4 && !memcmp(line, "SIZE", 4)) {
229 DEBUGF(infof(conn->data, "Server supports SIZE extension.\n"));
230 smtpc->size_supported = true;
233 if(smtpc->state == SMTP_EHLO && len >= 5 && !memcmp(line, "AUTH ", 5)) {
239 (*line == ' ' || *line == '\t' ||
240 *line == '\r' || *line == '\n')) {
248 for(wordlen = 0; wordlen < len && line[wordlen] != ' ' &&
249 line[wordlen] != '\t' && line[wordlen] != '\r' &&
250 line[wordlen] != '\n';)
253 if(wordlen == 5 && !memcmp(line, "LOGIN", 5))
254 smtpc->authmechs |= SASL_MECH_LOGIN;
255 else if(wordlen == 5 && !memcmp(line, "PLAIN", 5))
256 smtpc->authmechs |= SASL_MECH_PLAIN;
257 else if(wordlen == 8 && !memcmp(line, "CRAM-MD5", 8))
258 smtpc->authmechs |= SASL_MECH_CRAM_MD5;
259 else if(wordlen == 10 && !memcmp(line, "DIGEST-MD5", 10))
260 smtpc->authmechs |= SASL_MECH_DIGEST_MD5;
261 else if(wordlen == 6 && !memcmp(line, "GSSAPI", 6))
262 smtpc->authmechs |= SASL_MECH_GSSAPI;
263 else if(wordlen == 8 && !memcmp(line, "EXTERNAL", 8))
264 smtpc->authmechs |= SASL_MECH_EXTERNAL;
265 else if(wordlen == 4 && !memcmp(line, "NTLM", 4))
266 smtpc->authmechs |= SASL_MECH_NTLM;
276 /* This is the ONLY way to change SMTP state! */
277 static void state(struct connectdata *conn, smtpstate newstate)
279 struct smtp_conn *smtpc = &conn->proto.smtpc;
280 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
281 /* for debug purposes */
282 static const char * const names[] = {
294 "AUTH_DIGESTMD5_RESP",
296 "AUTH_NTLM_TYPE2MSG",
305 if(smtpc->state != newstate)
306 infof(conn->data, "SMTP %p state change from %s to %s\n",
307 smtpc, names[smtpc->state], names[newstate]);
309 smtpc->state = newstate;
312 static CURLcode smtp_state_ehlo(struct connectdata *conn)
315 struct smtp_conn *smtpc = &conn->proto.smtpc;
317 smtpc->authmechs = 0; /* No known authentication mechanisms yet */
318 smtpc->authused = 0; /* Clear the authentication mechanism used
319 for esmtp connections */
322 result = Curl_pp_sendf(&smtpc->pp, "EHLO %s", smtpc->domain);
327 state(conn, SMTP_EHLO);
332 static CURLcode smtp_state_helo(struct connectdata *conn)
335 struct smtp_conn *smtpc = &conn->proto.smtpc;
337 smtpc->authused = 0; /* No authentication mechanism used in smtp
341 result = Curl_pp_sendf(&smtpc->pp, "HELO %s", smtpc->domain);
346 state(conn, SMTP_HELO);
351 static CURLcode smtp_authenticate(struct connectdata *conn)
353 CURLcode result = CURLE_OK;
354 struct smtp_conn *smtpc = &conn->proto.smtpc;
355 char *initresp = NULL;
356 const char *mech = NULL;
358 smtpstate state1 = SMTP_STOP;
359 smtpstate state2 = SMTP_STOP;
361 /* Check we have a username and password to authenticate with and end the
362 connect phase if we don't */
363 if(!conn->bits.user_passwd) {
364 state(conn, SMTP_STOP);
369 /* Check supported authentication mechanisms by decreasing order of
371 #ifndef CURL_DISABLE_CRYPTO_AUTH
372 if(smtpc->authmechs & SASL_MECH_DIGEST_MD5) {
374 state1 = SMTP_AUTH_DIGESTMD5;
375 smtpc->authused = SASL_MECH_DIGEST_MD5;
377 else if(smtpc->authmechs & SASL_MECH_CRAM_MD5) {
379 state1 = SMTP_AUTH_CRAMMD5;
380 smtpc->authused = SASL_MECH_CRAM_MD5;
385 if(smtpc->authmechs & SASL_MECH_NTLM) {
387 state1 = SMTP_AUTH_NTLM;
388 state2 = SMTP_AUTH_NTLM_TYPE2MSG;
389 smtpc->authused = SASL_MECH_NTLM;
390 result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
396 if(smtpc->authmechs & SASL_MECH_LOGIN) {
398 state1 = SMTP_AUTH_LOGIN;
399 state2 = SMTP_AUTH_PASSWD;
400 smtpc->authused = SASL_MECH_LOGIN;
401 result = Curl_sasl_create_login_message(conn->data, conn->user,
404 else if(smtpc->authmechs & SASL_MECH_PLAIN) {
406 state1 = SMTP_AUTH_PLAIN;
408 smtpc->authused = SASL_MECH_PLAIN;
409 result = Curl_sasl_create_plain_message(conn->data, conn->user,
410 conn->passwd, &initresp, &len);
413 infof(conn->data, "No known authentication mechanisms supported!\n");
414 result = CURLE_LOGIN_DENIED; /* Other mechanisms not supported */
419 strlen(mech) + len <= 512 - 8) { /* AUTH <mech> ...<crlf> */
420 result = Curl_pp_sendf(&smtpc->pp, "AUTH %s %s", mech, initresp);
426 result = Curl_pp_sendf(&smtpc->pp, "AUTH %s", mech);
431 Curl_safefree(initresp);
437 /* For the SMTP "protocol connect" and "doing" phases only */
438 static int smtp_getsock(struct connectdata *conn,
439 curl_socket_t *socks,
442 return Curl_pp_getsock(&conn->proto.smtpc.pp, socks, numsocks);
446 static void smtp_to_smtps(struct connectdata *conn)
448 conn->handler = &Curl_handler_smtps;
451 #define smtp_to_smtps(x) Curl_nop_stmt
454 /* For the initial server greeting */
455 static CURLcode smtp_state_servergreet_resp(struct connectdata *conn,
459 CURLcode result = CURLE_OK;
460 struct SessionHandle *data = conn->data;
462 (void)instate; /* no use for this yet */
464 if(smtpcode/100 != 2) {
465 failf(data, "Got unexpected smtp-server response: %d", smtpcode);
466 return CURLE_FTP_WEIRD_SERVER_REPLY;
469 result = smtp_state_ehlo(conn);
474 /* For STARTTLS responses */
475 static CURLcode smtp_state_starttls_resp(struct connectdata *conn,
479 CURLcode result = CURLE_OK;
480 struct SessionHandle *data = conn->data;
482 (void)instate; /* no use for this yet */
484 if(smtpcode != 220) {
485 if(data->set.use_ssl != CURLUSESSL_TRY) {
486 failf(data, "STARTTLS denied. %c", smtpcode);
487 result = CURLE_USE_SSL_FAILED;
490 result = smtp_authenticate(conn);
493 if(data->state.used_interface == Curl_if_multi) {
494 state(conn, SMTP_UPGRADETLS);
495 return smtp_state_upgrade_tls(conn);
498 result = Curl_ssl_connect(conn, FIRSTSOCKET);
499 if(CURLE_OK == result) {
501 result = smtp_state_ehlo(conn);
509 static CURLcode smtp_state_upgrade_tls(struct connectdata *conn)
511 struct smtp_conn *smtpc = &conn->proto.smtpc;
514 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone);
518 result = smtp_state_ehlo(conn);
524 /* For EHLO responses */
525 static CURLcode smtp_state_ehlo_resp(struct connectdata *conn,
529 CURLcode result = CURLE_OK;
530 struct SessionHandle *data = conn->data;
532 (void)instate; /* no use for this yet */
534 if(smtpcode/100 != 2) {
535 if((data->set.use_ssl <= CURLUSESSL_TRY || conn->ssl[FIRSTSOCKET].use) &&
536 !conn->bits.user_passwd)
537 result = smtp_state_helo(conn);
539 failf(data, "Remote access denied: %d", smtpcode);
540 result = CURLE_REMOTE_ACCESS_DENIED;
543 else if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
544 /* We don't have a SSL/TLS connection yet, but SSL is requested. Switch
545 to TLS connection now */
546 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "STARTTLS");
547 state(conn, SMTP_STARTTLS);
550 result = smtp_authenticate(conn);
555 /* For HELO responses */
556 static CURLcode smtp_state_helo_resp(struct connectdata *conn,
560 CURLcode result = CURLE_OK;
561 struct SessionHandle *data = conn->data;
563 (void)instate; /* no use for this yet */
565 if(smtpcode/100 != 2) {
566 failf(data, "Remote access denied: %d", smtpcode);
567 result = CURLE_REMOTE_ACCESS_DENIED;
570 /* End of connect phase */
571 state(conn, SMTP_STOP);
576 /* For AUTH PLAIN (without initial response) responses */
577 static CURLcode smtp_state_auth_plain_resp(struct connectdata *conn,
581 CURLcode result = CURLE_OK;
582 struct SessionHandle *data = conn->data;
584 char *plainauth = NULL;
586 (void)instate; /* no use for this yet */
588 if(smtpcode != 334) {
589 failf(data, "Access denied: %d", smtpcode);
590 result = CURLE_LOGIN_DENIED;
593 /* Create the authorisation message */
594 result = Curl_sasl_create_plain_message(conn->data, conn->user,
595 conn->passwd, &plainauth, &len);
597 /* Send the message */
600 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", plainauth);
603 state(conn, SMTP_AUTH);
605 Curl_safefree(plainauth);
612 /* For AUTH LOGIN (without initial response) responses */
613 static CURLcode smtp_state_auth_login_resp(struct connectdata *conn,
617 CURLcode result = CURLE_OK;
618 struct SessionHandle *data = conn->data;
620 char *authuser = NULL;
622 (void)instate; /* no use for this yet */
624 if(smtpcode != 334) {
625 failf(data, "Access denied: %d", smtpcode);
626 result = CURLE_LOGIN_DENIED;
629 /* Create the user message */
630 result = Curl_sasl_create_login_message(conn->data, conn->user,
636 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", authuser);
639 state(conn, SMTP_AUTH_PASSWD);
641 Curl_safefree(authuser);
648 /* For responses to user entry of AUTH LOGIN */
649 static CURLcode smtp_state_auth_passwd_resp(struct connectdata *conn,
653 CURLcode result = CURLE_OK;
654 struct SessionHandle *data = conn->data;
656 char *authpasswd = NULL;
658 (void)instate; /* no use for this yet */
660 if(smtpcode != 334) {
661 failf(data, "Access denied: %d", smtpcode);
662 result = CURLE_LOGIN_DENIED;
665 /* Create the password message */
666 result = Curl_sasl_create_login_message(conn->data, conn->passwd,
669 /* Send the password */
672 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", authpasswd);
675 state(conn, SMTP_AUTH);
677 Curl_safefree(authpasswd);
684 #ifndef CURL_DISABLE_CRYPTO_AUTH
685 /* For AUTH CRAM-MD5 responses */
686 static CURLcode smtp_state_auth_cram_resp(struct connectdata *conn,
690 CURLcode result = CURLE_OK;
691 struct SessionHandle *data = conn->data;
692 char *chlg64 = data->state.buffer;
694 char *rplyb64 = NULL;
696 (void)instate; /* no use for this yet */
698 if(smtpcode != 334) {
699 failf(data, "Access denied: %d", smtpcode);
700 return CURLE_LOGIN_DENIED;
703 /* Get the challenge */
704 for(chlg64 += 4; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
707 /* Terminate the challenge */
709 for(len = strlen(chlg64); len--;)
710 if(chlg64[len] != '\r' && chlg64[len] != '\n' && chlg64[len] != ' ' &&
719 /* Create the response message */
720 result = Curl_sasl_create_cram_md5_message(data, chlg64, conn->user,
721 conn->passwd, &rplyb64, &len);
723 /* Send the response */
726 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", rplyb64);
729 state(conn, SMTP_AUTH);
731 Curl_safefree(rplyb64);
737 /* For AUTH DIGEST-MD5 challenge responses */
738 static CURLcode smtp_state_auth_digest_resp(struct connectdata *conn,
742 CURLcode result = CURLE_OK;
743 struct SessionHandle *data = conn->data;
744 char *chlg64 = data->state.buffer;
746 char *rplyb64 = NULL;
748 (void)instate; /* no use for this yet */
750 if(smtpcode != 334) {
751 failf(data, "Access denied: %d", smtpcode);
752 return CURLE_LOGIN_DENIED;
755 /* Get the challenge */
756 for(chlg64 += 4; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
759 /* Create the response message */
760 result = Curl_sasl_create_digest_md5_message(data, chlg64, conn->user,
761 conn->passwd, "smtp",
764 /* Send the response */
767 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", rplyb64);
770 state(conn, SMTP_AUTH_DIGESTMD5_RESP);
773 Curl_safefree(rplyb64);
779 /* For AUTH DIGEST-MD5 challenge-response responses */
780 static CURLcode smtp_state_auth_digest_resp_resp(struct connectdata *conn,
784 CURLcode result = CURLE_OK;
785 struct SessionHandle *data = conn->data;
787 (void)instate; /* no use for this yet */
789 if(smtpcode != 334) {
790 failf(data, "Authentication failed: %d", smtpcode);
791 result = CURLE_LOGIN_DENIED;
794 /* Send an empty response */
795 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "");
798 state(conn, SMTP_AUTH);
807 /* For AUTH NTLM (without initial response) responses */
808 static CURLcode smtp_state_auth_ntlm_resp(struct connectdata *conn,
812 CURLcode result = CURLE_OK;
813 struct SessionHandle *data = conn->data;
814 char *type1msg = NULL;
817 (void)instate; /* no use for this yet */
819 if(smtpcode != 334) {
820 failf(data, "Access denied: %d", smtpcode);
821 result = CURLE_LOGIN_DENIED;
824 /* Create the type-1 message */
825 result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
829 /* Send the message */
832 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", type1msg);
835 state(conn, SMTP_AUTH_NTLM_TYPE2MSG);
838 Curl_safefree(type1msg);
845 /* For NTLM type-2 responses (sent in reponse to our type-1 message) */
846 static CURLcode smtp_state_auth_ntlm_type2msg_resp(struct connectdata *conn,
850 CURLcode result = CURLE_OK;
851 struct SessionHandle *data = conn->data;
852 char *type3msg = NULL;
855 (void)instate; /* no use for this yet */
857 if(smtpcode != 334) {
858 failf(data, "Access denied: %d", smtpcode);
859 result = CURLE_LOGIN_DENIED;
862 /* Create the type-3 message */
863 result = Curl_sasl_create_ntlm_type3_message(data,
864 data->state.buffer + 4,
865 conn->user, conn->passwd,
869 /* Send the message */
872 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", type3msg);
875 state(conn, SMTP_AUTH);
878 Curl_safefree(type3msg);
886 /* For the final responses to the AUTH sequence */
887 static CURLcode smtp_state_auth_resp(struct connectdata *conn,
891 CURLcode result = CURLE_OK;
892 struct SessionHandle *data = conn->data;
894 (void)instate; /* no use for this yet */
896 if(smtpcode != 235) {
897 failf(data, "Authentication failed: %d", smtpcode);
898 result = CURLE_LOGIN_DENIED;
901 /* End of connect phase */
902 state(conn, SMTP_STOP);
907 /* Start the DO phase */
908 static CURLcode smtp_mail(struct connectdata *conn)
913 CURLcode result = CURLE_OK;
914 struct SessionHandle *data = conn->data;
916 /* Calculate the FROM parameter */
917 if(!data->set.str[STRING_MAIL_FROM])
918 /* Null reverse-path, RFC-2821, sect. 3.7 */
920 else if(data->set.str[STRING_MAIL_FROM][0] == '<')
921 from = aprintf("%s", data->set.str[STRING_MAIL_FROM]);
923 from = aprintf("<%s>", data->set.str[STRING_MAIL_FROM]);
926 return CURLE_OUT_OF_MEMORY;
928 /* Calculate the optional AUTH parameter */
929 if(data->set.str[STRING_MAIL_AUTH] && conn->proto.smtpc.authused) {
930 if(data->set.str[STRING_MAIL_AUTH][0] != '\0')
931 auth = aprintf("%s", data->set.str[STRING_MAIL_AUTH]);
933 /* Empty AUTH, RFC-2554, sect. 5 */
939 return CURLE_OUT_OF_MEMORY;
943 /* calculate the optional SIZE parameter */
944 if(conn->proto.smtpc.size_supported && conn->data->set.infilesize > 0) {
945 size = aprintf("%" FORMAT_OFF_T, data->set.infilesize);
951 return CURLE_OUT_OF_MEMORY;
955 /* Send the MAIL command */
957 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
958 "MAIL FROM:%s", from);
959 else if(auth && !size)
960 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
961 "MAIL FROM:%s AUTH=%s", from, auth);
962 else if(auth && size)
963 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
964 "MAIL FROM:%s AUTH=%s SIZE=%s", from, auth, size);
966 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
967 "MAIL FROM:%s SIZE=%s", from, size);
976 state(conn, SMTP_MAIL);
981 static CURLcode smtp_rcpt_to(struct connectdata *conn)
983 CURLcode result = CURLE_OK;
984 struct smtp_conn *smtpc = &conn->proto.smtpc;
986 /* Send the RCPT TO command */
988 if(smtpc->rcpt->data[0] == '<')
989 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:%s",
992 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:<%s>",
995 state(conn, SMTP_RCPT);
1001 /* For MAIL responses */
1002 static CURLcode smtp_state_mail_resp(struct connectdata *conn,
1006 CURLcode result = CURLE_OK;
1007 struct SessionHandle *data = conn->data;
1009 (void)instate; /* no use for this yet */
1011 if(smtpcode/100 != 2) {
1012 failf(data, "MAIL failed: %d", smtpcode);
1013 result = CURLE_SEND_ERROR;
1014 state(conn, SMTP_STOP);
1017 struct smtp_conn *smtpc = &conn->proto.smtpc;
1018 smtpc->rcpt = data->set.mail_rcpt;
1020 result = smtp_rcpt_to(conn);
1026 /* For RCPT responses */
1027 static CURLcode smtp_state_rcpt_resp(struct connectdata *conn,
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, "RCPT failed: %d", smtpcode);
1038 result = CURLE_SEND_ERROR;
1039 state(conn, SMTP_STOP);
1042 struct smtp_conn *smtpc = &conn->proto.smtpc;
1045 smtpc->rcpt = smtpc->rcpt->next;
1046 result = smtp_rcpt_to(conn);
1048 /* If we failed or still are sending RCPT data then return */
1049 if(result || smtpc->rcpt)
1053 /* Send the DATA command */
1054 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "DATA");
1059 state(conn, SMTP_DATA);
1065 /* For DATA response */
1066 static CURLcode smtp_state_data_resp(struct connectdata *conn,
1070 struct SessionHandle *data = conn->data;
1071 struct FTP *smtp = data->state.proto.smtp;
1073 (void)instate; /* no use for this yet */
1075 if(smtpcode != 354) {
1076 state(conn, SMTP_STOP);
1077 return CURLE_SEND_ERROR;
1081 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, /* no download */
1082 FIRSTSOCKET, smtp->bytecountp);
1084 state(conn, SMTP_STOP);
1089 /* For POSTDATA responses, which are received after the entire DATA
1090 part has been sent to the server */
1091 static CURLcode smtp_state_postdata_resp(struct connectdata *conn,
1095 CURLcode result = CURLE_OK;
1097 (void)instate; /* no use for this yet */
1100 result = CURLE_RECV_ERROR;
1102 state(conn, SMTP_STOP);
1107 static CURLcode smtp_statemach_act(struct connectdata *conn)
1110 curl_socket_t sock = conn->sock[FIRSTSOCKET];
1111 struct SessionHandle *data = conn->data;
1113 struct smtp_conn *smtpc = &conn->proto.smtpc;
1114 struct pingpong *pp = &smtpc->pp;
1117 if(smtpc->state == SMTP_UPGRADETLS)
1118 return smtp_state_upgrade_tls(conn);
1120 /* Flush any data that needs to be sent */
1122 return Curl_pp_flushsend(pp);
1124 /* Read the response from the server */
1125 result = Curl_pp_readresp(sock, pp, &smtpcode, &nread);
1129 /* Store the latest response for later retrieval */
1130 if(smtpc->state != SMTP_QUIT)
1131 data->info.httpcode = smtpcode;
1134 /* we have now received a full SMTP server response */
1135 switch(smtpc->state) {
1136 case SMTP_SERVERGREET:
1137 result = smtp_state_servergreet_resp(conn, smtpcode, smtpc->state);
1141 result = smtp_state_ehlo_resp(conn, smtpcode, smtpc->state);
1145 result = smtp_state_helo_resp(conn, smtpcode, smtpc->state);
1149 result = smtp_state_starttls_resp(conn, smtpcode, smtpc->state);
1152 case SMTP_AUTH_PLAIN:
1153 result = smtp_state_auth_plain_resp(conn, smtpcode, smtpc->state);
1156 case SMTP_AUTH_LOGIN:
1157 result = smtp_state_auth_login_resp(conn, smtpcode, smtpc->state);
1160 case SMTP_AUTH_PASSWD:
1161 result = smtp_state_auth_passwd_resp(conn, smtpcode, smtpc->state);
1164 #ifndef CURL_DISABLE_CRYPTO_AUTH
1165 case SMTP_AUTH_CRAMMD5:
1166 result = smtp_state_auth_cram_resp(conn, smtpcode, smtpc->state);
1169 case SMTP_AUTH_DIGESTMD5:
1170 result = smtp_state_auth_digest_resp(conn, smtpcode, smtpc->state);
1173 case SMTP_AUTH_DIGESTMD5_RESP:
1174 result = smtp_state_auth_digest_resp_resp(conn, smtpcode, smtpc->state);
1179 case SMTP_AUTH_NTLM:
1180 result = smtp_state_auth_ntlm_resp(conn, smtpcode, smtpc->state);
1183 case SMTP_AUTH_NTLM_TYPE2MSG:
1184 result = smtp_state_auth_ntlm_type2msg_resp(conn, smtpcode,
1190 result = smtp_state_auth_resp(conn, smtpcode, smtpc->state);
1194 result = smtp_state_mail_resp(conn, smtpcode, smtpc->state);
1198 result = smtp_state_rcpt_resp(conn, smtpcode, smtpc->state);
1202 result = smtp_state_data_resp(conn, smtpcode, smtpc->state);
1206 result = smtp_state_postdata_resp(conn, smtpcode, smtpc->state);
1210 /* fallthrough, just stop! */
1212 /* internal error */
1213 state(conn, SMTP_STOP);
1221 /* Called repeatedly until done from multi.c */
1222 static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done)
1224 struct smtp_conn *smtpc = &conn->proto.smtpc;
1227 if((conn->handler->flags & PROTOPT_SSL) && !smtpc->ssldone)
1228 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone);
1230 result = Curl_pp_multi_statemach(&smtpc->pp);
1232 *done = (smtpc->state == SMTP_STOP) ? TRUE : FALSE;
1237 static CURLcode smtp_easy_statemach(struct connectdata *conn)
1239 struct smtp_conn *smtpc = &conn->proto.smtpc;
1240 struct pingpong *pp = &smtpc->pp;
1241 CURLcode result = CURLE_OK;
1243 while(smtpc->state != SMTP_STOP) {
1244 result = Curl_pp_easy_statemach(pp);
1252 /* Allocate and initialize the SMTP struct for the current SessionHandle if
1254 static CURLcode smtp_init(struct connectdata *conn)
1256 struct SessionHandle *data = conn->data;
1257 struct FTP *smtp = data->state.proto.smtp;
1260 smtp = data->state.proto.smtp = calloc(sizeof(struct FTP), 1);
1262 return CURLE_OUT_OF_MEMORY;
1265 /* Get some initial data into the smtp struct */
1266 smtp->bytecountp = &data->req.bytecount;
1268 /* No need to duplicate user+password, the connectdata struct won't change
1269 during a session, but we re-init them here since on subsequent inits
1270 since the conn struct may have changed or been replaced.
1272 smtp->user = conn->user;
1273 smtp->passwd = conn->passwd;
1278 /***********************************************************************
1282 * This function should do everything that is to be considered a part of
1283 * the connection phase.
1285 * The variable pointed to by 'done' will be TRUE if the protocol-layer
1286 * connect phase is done when this function returns, or FALSE if not. When
1287 * called as a part of the easy interface, it will always be TRUE.
1289 static CURLcode smtp_connect(struct connectdata *conn, bool *done)
1292 struct smtp_conn *smtpc = &conn->proto.smtpc;
1293 struct SessionHandle *data = conn->data;
1294 struct pingpong *pp = &smtpc->pp;
1295 const char *path = conn->data->state.path;
1296 char localhost[HOSTNAME_MAX + 1];
1298 *done = FALSE; /* default to not done yet */
1300 /* If there already is a protocol-specific struct allocated for this
1301 sessionhandle, deal with it */
1302 Curl_reset_reqproto(conn);
1304 result = smtp_init(conn);
1305 if(CURLE_OK != result)
1308 /* We always support persistent connections on smtp */
1309 conn->bits.close = FALSE;
1311 pp->response_time = RESP_TIMEOUT; /* set default response time-out */
1312 pp->statemach_act = smtp_statemach_act;
1313 pp->endofresp = smtp_endofresp;
1316 if((conn->handler->protocol & CURLPROTO_SMTPS) &&
1317 data->state.used_interface != Curl_if_multi) {
1318 /* SMTPS is simply smtp with SSL for the control channel */
1319 /* now, perform the SSL initialization for this socket */
1320 result = Curl_ssl_connect(conn, FIRSTSOCKET);
1325 /* Initialise the response reader stuff */
1328 pp->response_time = RESP_TIMEOUT; /* set default response time-out */
1329 pp->statemach_act = smtp_statemach_act;
1330 pp->endofresp = smtp_endofresp;
1334 if(!Curl_gethostname(localhost, sizeof(localhost)))
1340 /* URL decode the path and use it as the domain in our EHLO */
1341 result = Curl_urldecode(conn->data, path, 0, &smtpc->domain, NULL, TRUE);
1345 /* Set the state as we are waiting the server greeting */
1346 state(conn, SMTP_SERVERGREET);
1348 if(data->state.used_interface == Curl_if_multi)
1349 result = smtp_multi_statemach(conn, done);
1351 result = smtp_easy_statemach(conn);
1359 /***********************************************************************
1363 * The DONE function. This does what needs to be done after a single DO has
1366 * Input argument is already checked for validity.
1368 static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
1371 struct SessionHandle *data = conn->data;
1372 struct FTP *smtp = data->state.proto.smtp;
1373 CURLcode result = CURLE_OK;
1374 ssize_t bytes_written;
1378 /* When the easy handle is removed from the multi while libcurl is still
1379 * trying to resolve the host name, it seems that the smtp struct is not
1380 * yet initialized, but the removal action calls Curl_done() which calls
1381 * this function. So we simply return success if no smtp pointer is set.
1386 conn->bits.close = TRUE; /* marked for closure */
1387 result = status; /* use the already set error code */
1389 else if(!data->set.connect_only) {
1390 struct smtp_conn *smtpc = &conn->proto.smtpc;
1391 struct pingpong *pp = &smtpc->pp;
1393 /* Send the end of block data */
1394 result = Curl_write(conn,
1395 conn->writesockfd, /* socket to send to */
1396 SMTP_EOB, /* buffer pointer */
1397 SMTP_EOB_LEN, /* buffer size */
1398 &bytes_written); /* actually sent away */
1403 if(bytes_written != SMTP_EOB_LEN) {
1404 /* The whole chunk was not sent so keep it around and adjust the
1405 pingpong structure accordingly */
1406 pp->sendthis = strdup(SMTP_EOB);
1407 pp->sendsize = SMTP_EOB_LEN;
1408 pp->sendleft = SMTP_EOB_LEN - bytes_written;
1411 /* Successfully sent so adjust the response timeout relative to now */
1412 pp->response = Curl_tvnow();
1414 state(conn, SMTP_POSTDATA);
1416 /* Run the state-machine
1418 TODO: when the multi interface is used, this _really_ should be using
1419 the smtp_multi_statemach function but we have no general support for
1420 non-blocking DONE operations, not in the multi state machine and with
1421 Curl_done() invokes on several places in the code!
1423 result = smtp_easy_statemach(conn);
1426 /* Clear the transfer mode for the next connection */
1427 smtp->transfer = FTPTRANSFER_BODY;
1432 /***********************************************************************
1436 * This is the actual DO function for SMTP. Get a file/directory according to
1437 * the options previously setup.
1439 static CURLcode smtp_perform(struct connectdata *conn, bool *connected,
1442 /* This is SMTP and no proxy */
1443 CURLcode result = CURLE_OK;
1445 DEBUGF(infof(conn->data, "DO phase starts\n"));
1447 if(conn->data->set.opt_no_body) {
1448 /* Requested no body means no transfer */
1449 struct FTP *smtp = conn->data->state.proto.smtp;
1450 smtp->transfer = FTPTRANSFER_INFO;
1453 *dophase_done = FALSE; /* not done yet */
1455 /* Start the first command in the DO phase */
1456 result = smtp_mail(conn);
1460 /* run the state-machine */
1461 if(conn->data->state.used_interface == Curl_if_multi)
1462 result = smtp_multi_statemach(conn, dophase_done);
1464 result = smtp_easy_statemach(conn);
1465 *dophase_done = TRUE; /* with the easy interface we are done here */
1467 *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1470 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1475 /***********************************************************************
1479 * This function is registered as 'curl_do' function. It decodes the path
1480 * parts etc as a wrapper to the actual DO function (smtp_perform).
1482 * The input argument is already checked for validity.
1484 static CURLcode smtp_do(struct connectdata *conn, bool *done)
1486 CURLcode retcode = CURLE_OK;
1488 *done = FALSE; /* default to false */
1491 Since connections can be re-used between SessionHandles, this might be a
1492 connection already existing but on a fresh SessionHandle struct so we must
1493 make sure we have a good 'struct SMTP' to play with. For new connections,
1494 the struct SMTP is allocated and setup in the smtp_connect() function.
1496 Curl_reset_reqproto(conn);
1497 retcode = smtp_init(conn);
1501 retcode = smtp_regular_transfer(conn, done);
1506 /***********************************************************************
1510 * This should be called before calling sclose(). We should then wait for the
1511 * response from the server before returning. The calling code should then try
1512 * to close the connection.
1514 static CURLcode smtp_quit(struct connectdata *conn)
1516 CURLcode result = CURLE_OK;
1518 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "QUIT");
1522 state(conn, SMTP_QUIT);
1524 result = smtp_easy_statemach(conn);
1529 /***********************************************************************
1533 * Disconnect from an SMTP server. Cleanup protocol-specific per-connection
1534 * resources. BLOCKING.
1536 static CURLcode smtp_disconnect(struct connectdata *conn,
1537 bool dead_connection)
1539 struct smtp_conn *smtpc = &conn->proto.smtpc;
1541 /* We cannot send quit unconditionally. If this connection is stale or
1542 bad in any way, sending quit and waiting around here will make the
1543 disconnect wait in vain and cause more problems than we need to */
1545 /* The SMTP session may or may not have been allocated/setup at this
1547 if(!dead_connection && smtpc->pp.conn)
1548 (void)smtp_quit(conn); /* ignore errors on the LOGOUT */
1550 /* Disconnect from the server */
1551 Curl_pp_disconnect(&smtpc->pp);
1553 /* Cleanup the SASL module */
1554 Curl_sasl_cleanup(conn, smtpc->authused);
1556 /* Cleanup our connection based variables */
1557 Curl_safefree(smtpc->domain);
1562 /* Call this when the DO phase has completed */
1563 static CURLcode smtp_dophase_done(struct connectdata *conn, bool connected)
1565 struct FTP *smtp = conn->data->state.proto.smtp;
1569 if(smtp->transfer != FTPTRANSFER_BODY)
1570 /* no data to transfer */
1571 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1576 /* Called from multi.c while DOing */
1577 static CURLcode smtp_doing(struct connectdata *conn, bool *dophase_done)
1579 CURLcode result = smtp_multi_statemach(conn, dophase_done);
1582 DEBUGF(infof(conn->data, "DO phase failed\n"));
1584 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1587 smtp_dophase_done(conn, FALSE /* not connected */);
1592 /***********************************************************************
1594 * smtp_regular_transfer()
1596 * The input argument is already checked for validity.
1598 * Performs all commands done before a regular transfer between a local and a
1601 static CURLcode smtp_regular_transfer(struct connectdata *conn,
1604 CURLcode result = CURLE_OK;
1605 bool connected = FALSE;
1606 struct SessionHandle *data = conn->data;
1607 data->req.size = -1; /* make sure this is unknown at this point */
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);
1616 if(CURLE_OK == result) {
1619 /* The DO phase has not completed yet */
1622 result = smtp_dophase_done(conn, connected);
1630 static CURLcode smtp_setup_connection(struct connectdata *conn)
1632 struct SessionHandle *data = conn->data;
1634 if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
1635 /* Unless we have asked to tunnel smtp operations through the proxy, we
1636 switch and use HTTP operations only */
1637 #ifndef CURL_DISABLE_HTTP
1638 if(conn->handler == &Curl_handler_smtp)
1639 conn->handler = &Curl_handler_smtp_proxy;
1642 conn->handler = &Curl_handler_smtps_proxy;
1644 failf(data, "SMTPS not supported!");
1645 return CURLE_UNSUPPORTED_PROTOCOL;
1649 /* We explicitly mark this connection as persistent here as we're doing
1650 SMTP over HTTP and thus we accidentally avoid setting this value
1652 conn->bits.close = FALSE;
1654 failf(data, "SMTP over http proxy requires HTTP support built-in!");
1655 return CURLE_UNSUPPORTED_PROTOCOL;
1659 data->state.path++; /* don't include the initial slash */
1664 CURLcode Curl_smtp_escape_eob(struct connectdata *conn, ssize_t nread)
1666 /* When sending a SMTP payload we must detect CRLF. sequences making sure
1667 they are sent as CRLF.. instead, as a . on the beginning of a line will
1668 be deleted by the server when not part of an EOB terminator and a
1669 genuine CRLF.CRLF which isn't escaped will wrongly be detected as end of
1674 struct smtp_conn *smtpc = &conn->proto.smtpc;
1675 struct SessionHandle *data = conn->data;
1677 /* Do we need to allocate the scatch buffer? */
1678 if(!data->state.scratch) {
1679 data->state.scratch = malloc(2 * BUFSIZE);
1681 if(!data->state.scratch) {
1682 failf (data, "Failed to alloc scratch buffer!");
1683 return CURLE_OUT_OF_MEMORY;
1687 /* This loop can be improved by some kind of Boyer-Moore style of
1688 approach but that is saved for later... */
1689 for(i = 0, si = 0; i < nread; i++) {
1690 if(SMTP_EOB[smtpc->eob] == data->req.upload_fromhere[i])
1692 else if(smtpc->eob) {
1693 /* A previous substring matched so output that first */
1694 memcpy(&data->state.scratch[si], SMTP_EOB, smtpc->eob);
1697 /* Then compare the first byte */
1698 if(SMTP_EOB[0] == data->req.upload_fromhere[i])
1704 /* Do we have a match for CRLF. as per RFC-2821, sect. 4.5.2 */
1705 if(SMTP_EOB_FIND_LEN == smtpc->eob) {
1706 /* Copy the replacement data to the target buffer */
1707 memcpy(&data->state.scratch[si], SMTP_EOB_REPL, SMTP_EOB_REPL_LEN);
1708 si += SMTP_EOB_REPL_LEN;
1711 else if(!smtpc->eob)
1712 data->state.scratch[si++] = data->req.upload_fromhere[i];
1716 /* A substring matched before processing ended so output that now */
1717 memcpy(&data->state.scratch[si], SMTP_EOB, smtpc->eob);
1723 /* Only use the new buffer if we replaced something */
1726 /* Upload from the new (replaced) buffer instead */
1727 data->req.upload_fromhere = data->state.scratch;
1729 /* Set the new amount too */
1730 data->req.upload_present = nread;
1736 #endif /* CURL_DISABLE_SMTP */