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);
102 static CURLcode smtp_state_upgrade_tls(struct connectdata *conn);
105 * SMTP protocol handler.
108 const struct Curl_handler Curl_handler_smtp = {
110 smtp_setup_connection, /* setup_connection */
112 smtp_done, /* done */
113 ZERO_NULL, /* do_more */
114 smtp_connect, /* connect_it */
115 smtp_multi_statemach, /* connecting */
116 smtp_doing, /* doing */
117 smtp_getsock, /* proto_getsock */
118 smtp_getsock, /* doing_getsock */
119 ZERO_NULL, /* domore_getsock */
120 ZERO_NULL, /* perform_getsock */
121 smtp_disconnect, /* disconnect */
122 ZERO_NULL, /* readwrite */
123 PORT_SMTP, /* defport */
124 CURLPROTO_SMTP, /* protocol */
125 PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY /* flags */
130 * SMTPS protocol handler.
133 const struct Curl_handler Curl_handler_smtps = {
134 "SMTPS", /* scheme */
135 smtp_setup_connection, /* setup_connection */
137 smtp_done, /* done */
138 ZERO_NULL, /* do_more */
139 smtp_connect, /* connect_it */
140 smtp_multi_statemach, /* connecting */
141 smtp_doing, /* doing */
142 smtp_getsock, /* proto_getsock */
143 smtp_getsock, /* doing_getsock */
144 ZERO_NULL, /* domore_getsock */
145 ZERO_NULL, /* perform_getsock */
146 smtp_disconnect, /* disconnect */
147 ZERO_NULL, /* readwrite */
148 PORT_SMTPS, /* defport */
149 CURLPROTO_SMTP | CURLPROTO_SMTPS, /* protocol */
150 PROTOPT_CLOSEACTION | PROTOPT_SSL
151 | PROTOPT_NOURLQUERY /* flags */
155 #ifndef CURL_DISABLE_HTTP
157 * HTTP-proxyed SMTP protocol handler.
160 static const struct Curl_handler Curl_handler_smtp_proxy = {
162 ZERO_NULL, /* setup_connection */
163 Curl_http, /* do_it */
164 Curl_http_done, /* done */
165 ZERO_NULL, /* do_more */
166 ZERO_NULL, /* connect_it */
167 ZERO_NULL, /* connecting */
168 ZERO_NULL, /* doing */
169 ZERO_NULL, /* proto_getsock */
170 ZERO_NULL, /* doing_getsock */
171 ZERO_NULL, /* domore_getsock */
172 ZERO_NULL, /* perform_getsock */
173 ZERO_NULL, /* disconnect */
174 ZERO_NULL, /* readwrite */
175 PORT_SMTP, /* defport */
176 CURLPROTO_HTTP, /* protocol */
177 PROTOPT_NONE /* flags */
182 * HTTP-proxyed SMTPS protocol handler.
185 static const struct Curl_handler Curl_handler_smtps_proxy = {
186 "SMTPS", /* scheme */
187 ZERO_NULL, /* setup_connection */
188 Curl_http, /* do_it */
189 Curl_http_done, /* done */
190 ZERO_NULL, /* do_more */
191 ZERO_NULL, /* connect_it */
192 ZERO_NULL, /* connecting */
193 ZERO_NULL, /* doing */
194 ZERO_NULL, /* proto_getsock */
195 ZERO_NULL, /* doing_getsock */
196 ZERO_NULL, /* domore_getsock */
197 ZERO_NULL, /* perform_getsock */
198 ZERO_NULL, /* disconnect */
199 ZERO_NULL, /* readwrite */
200 PORT_SMTPS, /* defport */
201 CURLPROTO_HTTP, /* protocol */
202 PROTOPT_NONE /* flags */
207 /* Function that checks for an ending smtp status code at the start of the
208 given string, but also detects the supported authentication mechanisms
209 from the 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 /* Extract the response code if necessary */
223 if((result = (line[3] == ' ')) != 0)
224 *resp = curlx_sltosi(strtol(line, NULL, 10));
229 /* Does the server support the SIZE capability? */
230 if(smtpc->state == SMTP_EHLO && len >= 4 && !memcmp(line, "SIZE", 4)) {
231 DEBUGF(infof(conn->data, "Server supports SIZE extension.\n"));
232 smtpc->size_supported = true;
235 /* Do we have the authentication mechanism list? */
236 if(smtpc->state == SMTP_EHLO && len >= 5 && !memcmp(line, "AUTH ", 5)) {
242 (*line == ' ' || *line == '\t' ||
243 *line == '\r' || *line == '\n')) {
251 /* Extract the word */
252 for(wordlen = 0; wordlen < len && line[wordlen] != ' ' &&
253 line[wordlen] != '\t' && line[wordlen] != '\r' &&
254 line[wordlen] != '\n';)
257 /* Test the word for a matching authentication mechanism */
258 if(wordlen == 5 && !memcmp(line, "LOGIN", 5))
259 smtpc->authmechs |= SASL_MECH_LOGIN;
260 else if(wordlen == 5 && !memcmp(line, "PLAIN", 5))
261 smtpc->authmechs |= SASL_MECH_PLAIN;
262 else if(wordlen == 8 && !memcmp(line, "CRAM-MD5", 8))
263 smtpc->authmechs |= SASL_MECH_CRAM_MD5;
264 else if(wordlen == 10 && !memcmp(line, "DIGEST-MD5", 10))
265 smtpc->authmechs |= SASL_MECH_DIGEST_MD5;
266 else if(wordlen == 6 && !memcmp(line, "GSSAPI", 6))
267 smtpc->authmechs |= SASL_MECH_GSSAPI;
268 else if(wordlen == 8 && !memcmp(line, "EXTERNAL", 8))
269 smtpc->authmechs |= SASL_MECH_EXTERNAL;
270 else if(wordlen == 4 && !memcmp(line, "NTLM", 4))
271 smtpc->authmechs |= SASL_MECH_NTLM;
281 /* This is the ONLY way to change SMTP state! */
282 static void state(struct connectdata *conn, smtpstate newstate)
284 struct smtp_conn *smtpc = &conn->proto.smtpc;
285 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
286 /* for debug purposes */
287 static const char * const names[] = {
299 "AUTH_DIGESTMD5_RESP",
301 "AUTH_NTLM_TYPE2MSG",
311 if(smtpc->state != newstate)
312 infof(conn->data, "SMTP %p state change from %s to %s\n",
313 smtpc, names[smtpc->state], names[newstate]);
316 smtpc->state = newstate;
319 static CURLcode smtp_state_ehlo(struct connectdata *conn)
322 struct smtp_conn *smtpc = &conn->proto.smtpc;
324 smtpc->authmechs = 0; /* No known authentication mechanisms yet */
325 smtpc->authused = 0; /* Clear the authentication mechanism used
326 for esmtp connections */
328 /* Send the EHLO command */
329 result = Curl_pp_sendf(&smtpc->pp, "EHLO %s", smtpc->domain);
334 state(conn, SMTP_EHLO);
339 static CURLcode smtp_state_helo(struct connectdata *conn)
342 struct smtp_conn *smtpc = &conn->proto.smtpc;
344 smtpc->authused = 0; /* No authentication mechanism used in smtp
347 /* Send the HELO command */
348 result = Curl_pp_sendf(&smtpc->pp, "HELO %s", smtpc->domain);
353 state(conn, SMTP_HELO);
358 static CURLcode smtp_state_starttls(struct connectdata *conn)
360 CURLcode result = CURLE_OK;
362 /* Send the STARTTLS command */
363 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "STARTTLS");
366 state(conn, SMTP_STARTTLS);
371 static CURLcode smtp_authenticate(struct connectdata *conn)
373 CURLcode result = CURLE_OK;
374 struct smtp_conn *smtpc = &conn->proto.smtpc;
375 char *initresp = NULL;
376 const char *mech = NULL;
378 smtpstate state1 = SMTP_STOP;
379 smtpstate state2 = SMTP_STOP;
381 /* Check we have a username and password to authenticate with and end the
382 connect phase if we don't */
383 if(!conn->bits.user_passwd) {
384 state(conn, SMTP_STOP);
389 /* Calculate the supported authentication mechanism, by decreasing order of
390 security, as well as the initial response where appropriate */
391 #ifndef CURL_DISABLE_CRYPTO_AUTH
392 if(smtpc->authmechs & SASL_MECH_DIGEST_MD5) {
394 state1 = SMTP_AUTH_DIGESTMD5;
395 smtpc->authused = SASL_MECH_DIGEST_MD5;
397 else if(smtpc->authmechs & SASL_MECH_CRAM_MD5) {
399 state1 = SMTP_AUTH_CRAMMD5;
400 smtpc->authused = SASL_MECH_CRAM_MD5;
405 if(smtpc->authmechs & SASL_MECH_NTLM) {
407 state1 = SMTP_AUTH_NTLM;
408 state2 = SMTP_AUTH_NTLM_TYPE2MSG;
409 smtpc->authused = SASL_MECH_NTLM;
410 result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
416 if(smtpc->authmechs & SASL_MECH_LOGIN) {
418 state1 = SMTP_AUTH_LOGIN;
419 state2 = SMTP_AUTH_PASSWD;
420 smtpc->authused = SASL_MECH_LOGIN;
421 result = Curl_sasl_create_login_message(conn->data, conn->user,
424 else if(smtpc->authmechs & SASL_MECH_PLAIN) {
426 state1 = SMTP_AUTH_PLAIN;
428 smtpc->authused = SASL_MECH_PLAIN;
429 result = Curl_sasl_create_plain_message(conn->data, conn->user,
430 conn->passwd, &initresp, &len);
433 /* Other mechanisms not supported */
434 infof(conn->data, "No known authentication mechanisms supported!\n");
435 result = CURLE_LOGIN_DENIED;
439 /* Perform SASL based authentication */
441 strlen(mech) + len <= 512 - 8) { /* AUTH <mech> ...<crlf> */
442 result = Curl_pp_sendf(&smtpc->pp, "AUTH %s %s", mech, initresp);
448 result = Curl_pp_sendf(&smtpc->pp, "AUTH %s", mech);
454 Curl_safefree(initresp);
460 /* For the SMTP "protocol connect" and "doing" phases only */
461 static int smtp_getsock(struct connectdata *conn, curl_socket_t *socks,
464 return Curl_pp_getsock(&conn->proto.smtpc.pp, socks, numsocks);
468 static void smtp_to_smtps(struct connectdata *conn)
470 conn->handler = &Curl_handler_smtps;
473 #define smtp_to_smtps(x) Curl_nop_stmt
476 /* For the initial server greeting */
477 static CURLcode smtp_state_servergreet_resp(struct connectdata *conn,
481 CURLcode result = CURLE_OK;
482 struct SessionHandle *data = conn->data;
484 (void)instate; /* no use for this yet */
486 if(smtpcode/100 != 2) {
487 failf(data, "Got unexpected smtp-server response: %d", smtpcode);
488 return CURLE_FTP_WEIRD_SERVER_REPLY;
491 result = smtp_state_ehlo(conn);
496 /* For STARTTLS responses */
497 static CURLcode smtp_state_starttls_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 != 220) {
507 if(data->set.use_ssl != CURLUSESSL_TRY) {
508 failf(data, "STARTTLS denied. %c", smtpcode);
509 result = CURLE_USE_SSL_FAILED;
512 result = smtp_authenticate(conn);
515 result = smtp_state_upgrade_tls(conn);
520 static CURLcode smtp_state_upgrade_tls(struct connectdata *conn)
522 struct smtp_conn *smtpc = &conn->proto.smtpc;
525 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone);
528 if(smtpc->state != SMTP_UPGRADETLS)
529 state(conn, SMTP_UPGRADETLS);
533 result = smtp_state_ehlo(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;
547 (void)instate; /* no use for this yet */
549 if(smtpcode/100 != 2) {
550 if((data->set.use_ssl <= CURLUSESSL_TRY || conn->ssl[FIRSTSOCKET].use) &&
551 !conn->bits.user_passwd)
552 result = smtp_state_helo(conn);
554 failf(data, "Remote access denied: %d", smtpcode);
555 result = CURLE_REMOTE_ACCESS_DENIED;
558 else if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
559 /* We don't have a SSL/TLS connection yet, but SSL is requested. Switch
560 to TLS connection now */
561 result = smtp_state_starttls(conn);
564 result = smtp_authenticate(conn);
569 /* For HELO responses */
570 static CURLcode smtp_state_helo_resp(struct connectdata *conn, int smtpcode,
573 CURLcode result = CURLE_OK;
574 struct SessionHandle *data = conn->data;
576 (void)instate; /* no use for this yet */
578 if(smtpcode/100 != 2) {
579 failf(data, "Remote access denied: %d", smtpcode);
580 result = CURLE_REMOTE_ACCESS_DENIED;
583 /* End of connect phase */
584 state(conn, SMTP_STOP);
589 /* For AUTH PLAIN (without initial response) responses */
590 static CURLcode smtp_state_auth_plain_resp(struct connectdata *conn,
594 CURLcode result = CURLE_OK;
595 struct SessionHandle *data = conn->data;
597 char *plainauth = NULL;
599 (void)instate; /* no use for this yet */
601 if(smtpcode != 334) {
602 failf(data, "Access denied: %d", smtpcode);
603 result = CURLE_LOGIN_DENIED;
606 /* Create the authorisation message */
607 result = Curl_sasl_create_plain_message(conn->data, conn->user,
608 conn->passwd, &plainauth, &len);
610 /* Send the message */
613 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", plainauth);
616 state(conn, SMTP_AUTH);
619 Curl_safefree(plainauth);
626 /* For AUTH LOGIN (without initial response) responses */
627 static CURLcode smtp_state_auth_login_resp(struct connectdata *conn,
631 CURLcode result = CURLE_OK;
632 struct SessionHandle *data = conn->data;
634 char *authuser = NULL;
636 (void)instate; /* no use for this yet */
638 if(smtpcode != 334) {
639 failf(data, "Access denied: %d", smtpcode);
640 result = CURLE_LOGIN_DENIED;
643 /* Create the user message */
644 result = Curl_sasl_create_login_message(conn->data, conn->user,
650 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", authuser);
653 state(conn, SMTP_AUTH_PASSWD);
656 Curl_safefree(authuser);
663 /* For responses to user entry of AUTH LOGIN */
664 static CURLcode smtp_state_auth_passwd_resp(struct connectdata *conn,
668 CURLcode result = CURLE_OK;
669 struct SessionHandle *data = conn->data;
671 char *authpasswd = NULL;
673 (void)instate; /* no use for this yet */
675 if(smtpcode != 334) {
676 failf(data, "Access denied: %d", smtpcode);
677 result = CURLE_LOGIN_DENIED;
680 /* Create the password message */
681 result = Curl_sasl_create_login_message(conn->data, conn->passwd,
684 /* Send the password */
687 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", authpasswd);
690 state(conn, SMTP_AUTH);
693 Curl_safefree(authpasswd);
700 #ifndef CURL_DISABLE_CRYPTO_AUTH
701 /* For AUTH CRAM-MD5 responses */
702 static CURLcode smtp_state_auth_cram_resp(struct connectdata *conn,
706 CURLcode result = CURLE_OK;
707 struct SessionHandle *data = conn->data;
708 char *chlg64 = data->state.buffer;
710 char *rplyb64 = NULL;
712 (void)instate; /* no use for this yet */
714 if(smtpcode != 334) {
715 failf(data, "Access denied: %d", smtpcode);
716 return CURLE_LOGIN_DENIED;
719 /* Get the challenge */
720 for(chlg64 += 4; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
723 /* Terminate the challenge */
725 for(len = strlen(chlg64); len--;)
726 if(chlg64[len] != '\r' && chlg64[len] != '\n' && chlg64[len] != ' ' &&
735 /* Create the response message */
736 result = Curl_sasl_create_cram_md5_message(data, chlg64, conn->user,
737 conn->passwd, &rplyb64, &len);
739 /* Send the response */
742 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", rplyb64);
745 state(conn, SMTP_AUTH);
748 Curl_safefree(rplyb64);
754 /* For AUTH DIGEST-MD5 challenge responses */
755 static CURLcode smtp_state_auth_digest_resp(struct connectdata *conn,
759 CURLcode result = CURLE_OK;
760 struct SessionHandle *data = conn->data;
761 char *chlg64 = data->state.buffer;
763 char *rplyb64 = NULL;
765 (void)instate; /* no use for this yet */
767 if(smtpcode != 334) {
768 failf(data, "Access denied: %d", smtpcode);
769 return CURLE_LOGIN_DENIED;
772 /* Get the challenge */
773 for(chlg64 += 4; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
776 /* Create the response message */
777 result = Curl_sasl_create_digest_md5_message(data, chlg64, conn->user,
778 conn->passwd, "smtp",
781 /* Send the response */
784 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", rplyb64);
787 state(conn, SMTP_AUTH_DIGESTMD5_RESP);
790 Curl_safefree(rplyb64);
796 /* For AUTH DIGEST-MD5 challenge-response responses */
797 static CURLcode smtp_state_auth_digest_resp_resp(struct connectdata *conn,
801 CURLcode result = CURLE_OK;
802 struct SessionHandle *data = conn->data;
804 (void)instate; /* no use for this yet */
806 if(smtpcode != 334) {
807 failf(data, "Authentication failed: %d", smtpcode);
808 result = CURLE_LOGIN_DENIED;
811 /* Send an empty response */
812 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "");
815 state(conn, SMTP_AUTH);
824 /* For AUTH NTLM (without initial response) responses */
825 static CURLcode smtp_state_auth_ntlm_resp(struct connectdata *conn,
829 CURLcode result = CURLE_OK;
830 struct SessionHandle *data = conn->data;
831 char *type1msg = NULL;
834 (void)instate; /* no use for this yet */
836 if(smtpcode != 334) {
837 failf(data, "Access denied: %d", smtpcode);
838 result = CURLE_LOGIN_DENIED;
841 /* Create the type-1 message */
842 result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
846 /* Send the message */
849 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", type1msg);
852 state(conn, SMTP_AUTH_NTLM_TYPE2MSG);
855 Curl_safefree(type1msg);
862 /* For NTLM type-2 responses (sent in reponse to our type-1 message) */
863 static CURLcode smtp_state_auth_ntlm_type2msg_resp(struct connectdata *conn,
867 CURLcode result = CURLE_OK;
868 struct SessionHandle *data = conn->data;
869 char *type3msg = NULL;
872 (void)instate; /* no use for this yet */
874 if(smtpcode != 334) {
875 failf(data, "Access denied: %d", smtpcode);
876 result = CURLE_LOGIN_DENIED;
879 /* Create the type-3 message */
880 result = Curl_sasl_create_ntlm_type3_message(data,
881 data->state.buffer + 4,
882 conn->user, conn->passwd,
886 /* Send the message */
889 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", type3msg);
892 state(conn, SMTP_AUTH);
895 Curl_safefree(type3msg);
903 /* For the final responses to the AUTH sequence */
904 static CURLcode smtp_state_auth_resp(struct connectdata *conn, int smtpcode,
907 CURLcode result = CURLE_OK;
908 struct SessionHandle *data = conn->data;
910 (void)instate; /* no use for this yet */
912 if(smtpcode != 235) {
913 failf(data, "Authentication failed: %d", smtpcode);
914 result = CURLE_LOGIN_DENIED;
917 /* End of connect phase */
918 state(conn, SMTP_STOP);
923 /* Start the DO phase */
924 static CURLcode smtp_mail(struct connectdata *conn)
929 CURLcode result = CURLE_OK;
930 struct SessionHandle *data = conn->data;
932 /* Calculate the FROM parameter */
933 if(!data->set.str[STRING_MAIL_FROM])
934 /* Null reverse-path, RFC-2821, sect. 3.7 */
936 else if(data->set.str[STRING_MAIL_FROM][0] == '<')
937 from = aprintf("%s", data->set.str[STRING_MAIL_FROM]);
939 from = aprintf("<%s>", data->set.str[STRING_MAIL_FROM]);
942 return CURLE_OUT_OF_MEMORY;
944 /* Calculate the optional AUTH parameter */
945 if(data->set.str[STRING_MAIL_AUTH] && conn->proto.smtpc.authused) {
946 if(data->set.str[STRING_MAIL_AUTH][0] != '\0')
947 auth = aprintf("%s", data->set.str[STRING_MAIL_AUTH]);
949 /* Empty AUTH, RFC-2554, sect. 5 */
955 return CURLE_OUT_OF_MEMORY;
959 /* calculate the optional SIZE parameter */
960 if(conn->proto.smtpc.size_supported && conn->data->set.infilesize > 0) {
961 size = aprintf("%" FORMAT_OFF_T, data->set.infilesize);
967 return CURLE_OUT_OF_MEMORY;
971 /* Send the MAIL command */
973 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
974 "MAIL FROM:%s", from);
975 else if(auth && !size)
976 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
977 "MAIL FROM:%s AUTH=%s", from, auth);
978 else if(auth && size)
979 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
980 "MAIL FROM:%s AUTH=%s SIZE=%s", from, auth, size);
982 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
983 "MAIL FROM:%s SIZE=%s", from, size);
992 state(conn, SMTP_MAIL);
997 static CURLcode smtp_rcpt_to(struct connectdata *conn)
999 CURLcode result = CURLE_OK;
1000 struct smtp_conn *smtpc = &conn->proto.smtpc;
1002 /* Send the RCPT TO command */
1004 if(smtpc->rcpt->data[0] == '<')
1005 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:%s",
1008 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:<%s>",
1011 state(conn, SMTP_RCPT);
1017 /* For MAIL responses */
1018 static CURLcode smtp_state_mail_resp(struct connectdata *conn, int smtpcode,
1021 CURLcode result = CURLE_OK;
1022 struct SessionHandle *data = conn->data;
1024 (void)instate; /* no use for this yet */
1026 if(smtpcode/100 != 2) {
1027 failf(data, "MAIL failed: %d", smtpcode);
1028 result = CURLE_SEND_ERROR;
1029 state(conn, SMTP_STOP);
1032 struct smtp_conn *smtpc = &conn->proto.smtpc;
1033 smtpc->rcpt = data->set.mail_rcpt;
1035 result = smtp_rcpt_to(conn);
1041 /* For RCPT responses */
1042 static CURLcode smtp_state_rcpt_resp(struct connectdata *conn, int smtpcode,
1045 CURLcode result = CURLE_OK;
1046 struct SessionHandle *data = conn->data;
1048 (void)instate; /* no use for this yet */
1050 if(smtpcode/100 != 2) {
1051 failf(data, "RCPT failed: %d", smtpcode);
1052 result = CURLE_SEND_ERROR;
1053 state(conn, SMTP_STOP);
1056 struct smtp_conn *smtpc = &conn->proto.smtpc;
1059 smtpc->rcpt = smtpc->rcpt->next;
1060 result = smtp_rcpt_to(conn);
1062 /* If we failed or still are sending RCPT data then return */
1063 if(result || smtpc->rcpt)
1067 /* Send the DATA command */
1068 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "DATA");
1073 state(conn, SMTP_DATA);
1079 /* For DATA response */
1080 static CURLcode smtp_state_data_resp(struct connectdata *conn, int smtpcode,
1083 struct SessionHandle *data = conn->data;
1084 struct FTP *smtp = data->state.proto.smtp;
1086 (void)instate; /* no use for this yet */
1088 if(smtpcode != 354) {
1089 state(conn, SMTP_STOP);
1090 return CURLE_SEND_ERROR;
1094 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, /* no download */
1095 FIRSTSOCKET, smtp->bytecountp);
1097 /* End of do phase */
1098 state(conn, SMTP_STOP);
1103 /* For POSTDATA responses, which are received after the entire DATA
1104 part has been sent to the server */
1105 static CURLcode smtp_state_postdata_resp(struct connectdata *conn,
1109 CURLcode result = CURLE_OK;
1111 (void)instate; /* no use for this yet */
1114 result = CURLE_RECV_ERROR;
1116 /* End of done phase */
1117 state(conn, SMTP_STOP);
1122 static CURLcode smtp_statemach_act(struct connectdata *conn)
1125 curl_socket_t sock = conn->sock[FIRSTSOCKET];
1126 struct SessionHandle *data = conn->data;
1128 struct smtp_conn *smtpc = &conn->proto.smtpc;
1129 struct pingpong *pp = &smtpc->pp;
1132 /* Busy upgrading the connection; right now all I/O is SSL/TLS, not SMTP */
1133 if(smtpc->state == SMTP_UPGRADETLS)
1134 return smtp_state_upgrade_tls(conn);
1136 /* Flush any data that needs to be sent */
1138 return Curl_pp_flushsend(pp);
1140 /* Read the response from the server */
1141 result = Curl_pp_readresp(sock, pp, &smtpcode, &nread);
1145 /* Store the latest response for later retrieval */
1146 if(smtpc->state != SMTP_QUIT)
1147 data->info.httpcode = smtpcode;
1150 /* We have now received a full SMTP server response */
1151 switch(smtpc->state) {
1152 case SMTP_SERVERGREET:
1153 result = smtp_state_servergreet_resp(conn, smtpcode, smtpc->state);
1157 result = smtp_state_ehlo_resp(conn, smtpcode, smtpc->state);
1161 result = smtp_state_helo_resp(conn, smtpcode, smtpc->state);
1165 result = smtp_state_starttls_resp(conn, smtpcode, smtpc->state);
1168 case SMTP_AUTH_PLAIN:
1169 result = smtp_state_auth_plain_resp(conn, smtpcode, smtpc->state);
1172 case SMTP_AUTH_LOGIN:
1173 result = smtp_state_auth_login_resp(conn, smtpcode, smtpc->state);
1176 case SMTP_AUTH_PASSWD:
1177 result = smtp_state_auth_passwd_resp(conn, smtpcode, smtpc->state);
1180 #ifndef CURL_DISABLE_CRYPTO_AUTH
1181 case SMTP_AUTH_CRAMMD5:
1182 result = smtp_state_auth_cram_resp(conn, smtpcode, smtpc->state);
1185 case SMTP_AUTH_DIGESTMD5:
1186 result = smtp_state_auth_digest_resp(conn, smtpcode, smtpc->state);
1189 case SMTP_AUTH_DIGESTMD5_RESP:
1190 result = smtp_state_auth_digest_resp_resp(conn, smtpcode, smtpc->state);
1195 case SMTP_AUTH_NTLM:
1196 result = smtp_state_auth_ntlm_resp(conn, smtpcode, smtpc->state);
1199 case SMTP_AUTH_NTLM_TYPE2MSG:
1200 result = smtp_state_auth_ntlm_type2msg_resp(conn, smtpcode,
1206 result = smtp_state_auth_resp(conn, smtpcode, smtpc->state);
1210 result = smtp_state_mail_resp(conn, smtpcode, smtpc->state);
1214 result = smtp_state_rcpt_resp(conn, smtpcode, smtpc->state);
1218 result = smtp_state_data_resp(conn, smtpcode, smtpc->state);
1222 result = smtp_state_postdata_resp(conn, smtpcode, smtpc->state);
1226 /* fallthrough, just stop! */
1228 /* internal error */
1229 state(conn, SMTP_STOP);
1237 /* Called repeatedly until done from multi.c */
1238 static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done)
1240 struct smtp_conn *smtpc = &conn->proto.smtpc;
1243 if((conn->handler->flags & PROTOPT_SSL) && !smtpc->ssldone)
1244 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone);
1246 result = Curl_pp_multi_statemach(&smtpc->pp);
1248 *done = (smtpc->state == SMTP_STOP) ? TRUE : FALSE;
1253 static CURLcode smtp_easy_statemach(struct connectdata *conn)
1255 struct smtp_conn *smtpc = &conn->proto.smtpc;
1256 struct pingpong *pp = &smtpc->pp;
1257 CURLcode result = CURLE_OK;
1259 while(smtpc->state != SMTP_STOP) {
1260 result = Curl_pp_easy_statemach(pp);
1268 /* Allocate and initialize the SMTP struct for the current SessionHandle if
1270 static CURLcode smtp_init(struct connectdata *conn)
1272 struct SessionHandle *data = conn->data;
1273 struct FTP *smtp = data->state.proto.smtp;
1276 smtp = data->state.proto.smtp = calloc(sizeof(struct FTP), 1);
1278 return CURLE_OUT_OF_MEMORY;
1281 /* Get some initial data into the smtp struct */
1282 smtp->bytecountp = &data->req.bytecount;
1284 /* No need to duplicate user+password, the connectdata struct won't change
1285 during a session, but we re-init them here since on subsequent inits
1286 since the conn struct may have changed or been replaced.
1288 smtp->user = conn->user;
1289 smtp->passwd = conn->passwd;
1294 /***********************************************************************
1298 * This function should do everything that is to be considered a part of
1299 * the connection phase.
1301 * The variable pointed to by 'done' will be TRUE if the protocol-layer
1302 * connect phase is done when this function returns, or FALSE if not. When
1303 * called as a part of the easy interface, it will always be TRUE.
1305 static CURLcode smtp_connect(struct connectdata *conn, bool *done)
1308 struct smtp_conn *smtpc = &conn->proto.smtpc;
1309 struct pingpong *pp = &smtpc->pp;
1310 const char *path = conn->data->state.path;
1311 char localhost[HOSTNAME_MAX + 1];
1313 *done = FALSE; /* default to not done yet */
1315 /* If there already is a protocol-specific struct allocated for this
1316 sessionhandle, deal with it */
1317 Curl_reset_reqproto(conn);
1319 result = smtp_init(conn);
1320 if(CURLE_OK != result)
1323 /* We always support persistent connections on smtp */
1324 conn->bits.close = FALSE;
1326 pp->response_time = RESP_TIMEOUT; /* set default response time-out */
1327 pp->statemach_act = smtp_statemach_act;
1328 pp->endofresp = smtp_endofresp;
1331 /* Initialise the response reader stuff */
1334 /* Set the default response time-out */
1335 pp->response_time = RESP_TIMEOUT;
1336 pp->statemach_act = smtp_statemach_act;
1337 pp->endofresp = smtp_endofresp;
1340 /* Calculate the path if necessary */
1342 if(!Curl_gethostname(localhost, sizeof(localhost)))
1348 /* URL decode the path and use it as the domain in our EHLO */
1349 result = Curl_urldecode(conn->data, path, 0, &smtpc->domain, NULL, TRUE);
1353 /* Start off waiting for the server greeting response */
1354 state(conn, SMTP_SERVERGREET);
1356 result = smtp_multi_statemach(conn, done);
1361 /***********************************************************************
1365 * The DONE function. This does what needs to be done after a single DO has
1368 * Input argument is already checked for validity.
1370 static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
1373 struct SessionHandle *data = conn->data;
1374 struct FTP *smtp = data->state.proto.smtp;
1375 CURLcode result = CURLE_OK;
1376 ssize_t bytes_written;
1381 /* When the easy handle is removed from the multi while libcurl is still
1382 * trying to resolve the host name, it seems that the smtp struct is not
1383 * yet initialized, but the removal action calls Curl_done() which calls
1384 * this function. So we simply return success if no smtp pointer is set.
1389 conn->bits.close = TRUE; /* marked for closure */
1390 result = status; /* use the already set error code */
1392 else if(!data->set.connect_only) {
1393 struct smtp_conn *smtpc = &conn->proto.smtpc;
1394 struct pingpong *pp = &smtpc->pp;
1396 /* Send the end of block data */
1397 result = Curl_write(conn,
1398 conn->writesockfd, /* socket to send to */
1399 SMTP_EOB, /* buffer pointer */
1400 SMTP_EOB_LEN, /* buffer size */
1401 &bytes_written); /* actually sent away */
1406 if(bytes_written != SMTP_EOB_LEN) {
1407 /* The whole chunk was not sent so keep it around and adjust the
1408 pingpong structure accordingly */
1409 pp->sendthis = strdup(SMTP_EOB);
1410 pp->sendsize = SMTP_EOB_LEN;
1411 pp->sendleft = SMTP_EOB_LEN - bytes_written;
1414 /* Successfully sent so adjust the response timeout relative to now */
1415 pp->response = Curl_tvnow();
1417 state(conn, SMTP_POSTDATA);
1419 /* Run the state-machine
1421 TODO: when the multi interface is used, this _really_ should be using
1422 the smtp_multi_statemach function but we have no general support for
1423 non-blocking DONE operations, not in the multi state machine and with
1424 Curl_done() invokes on several places in the code!
1426 result = smtp_easy_statemach(conn);
1429 /* Clear the transfer mode for the next connection */
1430 smtp->transfer = FTPTRANSFER_BODY;
1435 /***********************************************************************
1439 * This is the actual DO function for SMTP. Get a file/directory according to
1440 * the options previously setup.
1442 static CURLcode smtp_perform(struct connectdata *conn, bool *connected,
1445 /* This is SMTP and no proxy */
1446 CURLcode result = CURLE_OK;
1448 DEBUGF(infof(conn->data, "DO phase starts\n"));
1450 if(conn->data->set.opt_no_body) {
1451 /* Requested no body means no transfer */
1452 struct FTP *smtp = conn->data->state.proto.smtp;
1453 smtp->transfer = FTPTRANSFER_INFO;
1456 *dophase_done = FALSE; /* not done yet */
1458 /* Start the first command in the DO phase */
1459 result = smtp_mail(conn);
1463 /* run the state-machine */
1464 result = smtp_multi_statemach(conn, dophase_done);
1466 *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1469 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1474 /***********************************************************************
1478 * This function is registered as 'curl_do' function. It decodes the path
1479 * parts etc as a wrapper to the actual DO function (smtp_perform).
1481 * The input argument is already checked for validity.
1483 static CURLcode smtp_do(struct connectdata *conn, bool *done)
1485 CURLcode retcode = CURLE_OK;
1487 *done = FALSE; /* default to false */
1490 Since connections can be re-used between SessionHandles, this might be a
1491 connection already existing but on a fresh SessionHandle struct so we must
1492 make sure we have a good 'struct SMTP' to play with. For new connections,
1493 the struct SMTP is allocated and setup in the smtp_connect() function.
1495 Curl_reset_reqproto(conn);
1496 retcode = smtp_init(conn);
1500 retcode = smtp_regular_transfer(conn, done);
1505 /***********************************************************************
1509 * This should be called before calling sclose(). We should then wait for the
1510 * response from the server before returning. The calling code should then try
1511 * to close the connection.
1513 static CURLcode smtp_quit(struct connectdata *conn)
1515 CURLcode result = CURLE_OK;
1517 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "QUIT");
1521 state(conn, SMTP_QUIT);
1523 result = smtp_easy_statemach(conn);
1528 /***********************************************************************
1532 * Disconnect from an SMTP server. Cleanup protocol-specific per-connection
1533 * resources. BLOCKING.
1535 static CURLcode smtp_disconnect(struct connectdata *conn,
1536 bool dead_connection)
1538 struct smtp_conn *smtpc = &conn->proto.smtpc;
1540 /* We cannot send quit unconditionally. If this connection is stale or
1541 bad in any way, sending quit and waiting around here will make the
1542 disconnect wait in vain and cause more problems than we need to */
1544 /* The SMTP session may or may not have been allocated/setup at this
1546 if(!dead_connection && smtpc->pp.conn)
1547 (void)smtp_quit(conn); /* ignore errors on the LOGOUT */
1549 /* Disconnect from the server */
1550 Curl_pp_disconnect(&smtpc->pp);
1552 /* Cleanup the SASL module */
1553 Curl_sasl_cleanup(conn, smtpc->authused);
1555 /* Cleanup our connection based variables */
1556 Curl_safefree(smtpc->domain);
1561 /* Call this when the DO phase has completed */
1562 static CURLcode smtp_dophase_done(struct connectdata *conn, bool connected)
1564 struct FTP *smtp = conn->data->state.proto.smtp;
1568 if(smtp->transfer != FTPTRANSFER_BODY)
1569 /* no data to transfer */
1570 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1575 /* Called from multi.c while DOing */
1576 static CURLcode smtp_doing(struct connectdata *conn, bool *dophase_done)
1578 CURLcode result = smtp_multi_statemach(conn, dophase_done);
1581 DEBUGF(infof(conn->data, "DO phase failed\n"));
1584 result = smtp_dophase_done(conn, FALSE /* not connected */);
1586 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1593 /***********************************************************************
1595 * smtp_regular_transfer()
1597 * The input argument is already checked for validity.
1599 * Performs all commands done before a regular transfer between a local and a
1602 static CURLcode smtp_regular_transfer(struct connectdata *conn,
1605 CURLcode result = CURLE_OK;
1606 bool connected = FALSE;
1607 struct SessionHandle *data = conn->data;
1609 /* Make sure size is unknown at this point */
1610 data->req.size = -1;
1612 Curl_pgrsSetUploadCounter(data, 0);
1613 Curl_pgrsSetDownloadCounter(data, 0);
1614 Curl_pgrsSetUploadSize(data, 0);
1615 Curl_pgrsSetDownloadSize(data, 0);
1617 result = smtp_perform(conn, &connected, dophase_done);
1619 if(CURLE_OK == result) {
1621 /* The DO phase has not completed yet */
1624 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 */