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);
347 state(conn, SMTP_EHLO);
352 static CURLcode smtp_state_helo(struct connectdata *conn)
354 CURLcode result = CURLE_OK;
355 struct smtp_conn *smtpc = &conn->proto.smtpc;
357 smtpc->authused = 0; /* No authentication mechanism used in smtp
360 /* Send the HELO command */
361 result = Curl_pp_sendf(&smtpc->pp, "HELO %s", smtpc->domain);
364 state(conn, SMTP_HELO);
369 static CURLcode smtp_state_starttls(struct connectdata *conn)
371 CURLcode result = CURLE_OK;
373 /* Send the STARTTLS command */
374 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "STARTTLS");
377 state(conn, SMTP_STARTTLS);
382 static CURLcode smtp_state_upgrade_tls(struct connectdata *conn)
384 CURLcode result = CURLE_OK;
385 struct smtp_conn *smtpc = &conn->proto.smtpc;
387 /* Start the SSL connection */
388 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone);
391 if(smtpc->state != SMTP_UPGRADETLS)
392 state(conn, SMTP_UPGRADETLS);
396 result = smtp_state_ehlo(conn);
403 static CURLcode smtp_authenticate(struct connectdata *conn)
405 CURLcode result = CURLE_OK;
406 struct smtp_conn *smtpc = &conn->proto.smtpc;
407 const char *mech = NULL;
408 char *initresp = NULL;
410 smtpstate state1 = SMTP_STOP;
411 smtpstate state2 = SMTP_STOP;
413 /* Check we have a username and password to authenticate with and end the
414 connect phase if we don't */
415 if(!conn->bits.user_passwd) {
416 state(conn, SMTP_STOP);
421 /* Calculate the supported authentication mechanism, by decreasing order of
422 security, as well as the initial response where appropriate */
423 #ifndef CURL_DISABLE_CRYPTO_AUTH
424 if(smtpc->authmechs & SASL_MECH_DIGEST_MD5) {
426 state1 = SMTP_AUTH_DIGESTMD5;
427 smtpc->authused = SASL_MECH_DIGEST_MD5;
429 else if(smtpc->authmechs & SASL_MECH_CRAM_MD5) {
431 state1 = SMTP_AUTH_CRAMMD5;
432 smtpc->authused = SASL_MECH_CRAM_MD5;
437 if(smtpc->authmechs & SASL_MECH_NTLM) {
439 state1 = SMTP_AUTH_NTLM;
440 state2 = SMTP_AUTH_NTLM_TYPE2MSG;
441 smtpc->authused = SASL_MECH_NTLM;
442 result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
448 if(smtpc->authmechs & SASL_MECH_LOGIN) {
450 state1 = SMTP_AUTH_LOGIN;
451 state2 = SMTP_AUTH_LOGIN_PASSWD;
452 smtpc->authused = SASL_MECH_LOGIN;
453 result = Curl_sasl_create_login_message(conn->data, conn->user,
456 else if(smtpc->authmechs & SASL_MECH_PLAIN) {
458 state1 = SMTP_AUTH_PLAIN;
459 state2 = SMTP_AUTH_FINAL;
460 smtpc->authused = SASL_MECH_PLAIN;
461 result = Curl_sasl_create_plain_message(conn->data, conn->user,
462 conn->passwd, &initresp, &len);
465 /* Other mechanisms not supported */
466 infof(conn->data, "No known authentication mechanisms supported!\n");
467 result = CURLE_LOGIN_DENIED;
471 /* Perform SASL based authentication */
473 strlen(mech) + len <= 512 - 8) { /* AUTH <mech> ...<crlf> */
474 result = Curl_pp_sendf(&smtpc->pp, "AUTH %s %s", mech, initresp);
480 result = Curl_pp_sendf(&smtpc->pp, "AUTH %s", mech);
486 Curl_safefree(initresp);
492 /* For the initial server greeting */
493 static CURLcode smtp_state_servergreet_resp(struct connectdata *conn,
497 CURLcode result = CURLE_OK;
498 struct SessionHandle *data = conn->data;
500 (void)instate; /* no use for this yet */
502 if(smtpcode/100 != 2) {
503 failf(data, "Got unexpected smtp-server response: %d", smtpcode);
504 return CURLE_FTP_WEIRD_SERVER_REPLY;
507 result = smtp_state_ehlo(conn);
512 /* For STARTTLS responses */
513 static CURLcode smtp_state_starttls_resp(struct connectdata *conn,
517 CURLcode result = CURLE_OK;
518 struct SessionHandle *data = conn->data;
520 (void)instate; /* no use for this yet */
522 if(smtpcode != 220) {
523 if(data->set.use_ssl != CURLUSESSL_TRY) {
524 failf(data, "STARTTLS denied. %c", smtpcode);
525 result = CURLE_USE_SSL_FAILED;
528 result = smtp_authenticate(conn);
531 result = smtp_state_upgrade_tls(conn);
536 /* For EHLO responses */
537 static CURLcode smtp_state_ehlo_resp(struct connectdata *conn, int smtpcode,
540 CURLcode result = CURLE_OK;
541 struct SessionHandle *data = conn->data;
542 struct smtp_conn *smtpc = &conn->proto.smtpc;
544 (void)instate; /* no use for this yet */
546 if(smtpcode/100 != 2) {
547 if((data->set.use_ssl <= CURLUSESSL_TRY || conn->ssl[FIRSTSOCKET].use) &&
548 !conn->bits.user_passwd)
549 result = smtp_state_helo(conn);
551 failf(data, "Remote access denied: %d", smtpcode);
552 result = CURLE_REMOTE_ACCESS_DENIED;
555 else if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
556 /* We don't have a SSL/TLS connection yet, but SSL is requested */
557 if(smtpc->tls_supported)
558 /* Switch to TLS connection now */
559 result = smtp_state_starttls(conn);
560 else if(data->set.use_ssl == CURLUSESSL_TRY)
561 /* Fallback and carry on with authentication */
562 result = smtp_authenticate(conn);
564 failf(data, "STARTTLS not supported.");
565 result = CURLE_USE_SSL_FAILED;
569 result = smtp_authenticate(conn);
574 /* For HELO responses */
575 static CURLcode smtp_state_helo_resp(struct connectdata *conn, int smtpcode,
578 CURLcode result = CURLE_OK;
579 struct SessionHandle *data = conn->data;
581 (void)instate; /* no use for this yet */
583 if(smtpcode/100 != 2) {
584 failf(data, "Remote access denied: %d", smtpcode);
585 result = CURLE_REMOTE_ACCESS_DENIED;
588 /* End of connect phase */
589 state(conn, SMTP_STOP);
594 /* For AUTH PLAIN (without initial response) responses */
595 static CURLcode smtp_state_auth_plain_resp(struct connectdata *conn,
599 CURLcode result = CURLE_OK;
600 struct SessionHandle *data = conn->data;
602 char *plainauth = NULL;
604 (void)instate; /* no use for this yet */
606 if(smtpcode != 334) {
607 failf(data, "Access denied: %d", smtpcode);
608 result = CURLE_LOGIN_DENIED;
611 /* Create the authorisation message */
612 result = Curl_sasl_create_plain_message(conn->data, conn->user,
613 conn->passwd, &plainauth, &len);
615 /* Send the message */
618 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", plainauth);
621 state(conn, SMTP_AUTH_FINAL);
624 Curl_safefree(plainauth);
631 /* For AUTH LOGIN (without initial response) responses */
632 static CURLcode smtp_state_auth_login_resp(struct connectdata *conn,
636 CURLcode result = CURLE_OK;
637 struct SessionHandle *data = conn->data;
639 char *authuser = NULL;
641 (void)instate; /* no use for this yet */
643 if(smtpcode != 334) {
644 failf(data, "Access denied: %d", smtpcode);
645 result = CURLE_LOGIN_DENIED;
648 /* Create the user message */
649 result = Curl_sasl_create_login_message(conn->data, conn->user,
655 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", authuser);
658 state(conn, SMTP_AUTH_LOGIN_PASSWD);
661 Curl_safefree(authuser);
668 /* For AUTH LOGIN user entry responses */
669 static CURLcode smtp_state_auth_login_password_resp(struct connectdata *conn,
673 CURLcode result = CURLE_OK;
674 struct SessionHandle *data = conn->data;
676 char *authpasswd = NULL;
678 (void)instate; /* no use for this yet */
680 if(smtpcode != 334) {
681 failf(data, "Access denied: %d", smtpcode);
682 result = CURLE_LOGIN_DENIED;
685 /* Create the password message */
686 result = Curl_sasl_create_login_message(conn->data, conn->passwd,
689 /* Send the password */
692 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", authpasswd);
695 state(conn, SMTP_AUTH_FINAL);
698 Curl_safefree(authpasswd);
705 #ifndef CURL_DISABLE_CRYPTO_AUTH
706 /* For AUTH CRAM-MD5 responses */
707 static CURLcode smtp_state_auth_cram_resp(struct connectdata *conn,
711 CURLcode result = CURLE_OK;
712 struct SessionHandle *data = conn->data;
713 char *chlg64 = data->state.buffer;
715 char *rplyb64 = NULL;
717 (void)instate; /* no use for this yet */
719 if(smtpcode != 334) {
720 failf(data, "Access denied: %d", smtpcode);
721 return CURLE_LOGIN_DENIED;
724 /* Get the challenge */
725 for(chlg64 += 4; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
728 /* Terminate the challenge */
730 for(len = strlen(chlg64); len--;)
731 if(chlg64[len] != '\r' && chlg64[len] != '\n' && chlg64[len] != ' ' &&
740 /* Create the response message */
741 result = Curl_sasl_create_cram_md5_message(data, chlg64, conn->user,
742 conn->passwd, &rplyb64, &len);
744 /* Send the response */
747 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", rplyb64);
750 state(conn, SMTP_AUTH_FINAL);
753 Curl_safefree(rplyb64);
759 /* For AUTH DIGEST-MD5 challenge responses */
760 static CURLcode smtp_state_auth_digest_resp(struct connectdata *conn,
764 CURLcode result = CURLE_OK;
765 struct SessionHandle *data = conn->data;
766 char *chlg64 = data->state.buffer;
768 char *rplyb64 = NULL;
770 (void)instate; /* no use for this yet */
772 if(smtpcode != 334) {
773 failf(data, "Access denied: %d", smtpcode);
774 return CURLE_LOGIN_DENIED;
777 /* Get the challenge */
778 for(chlg64 += 4; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
781 /* Create the response message */
782 result = Curl_sasl_create_digest_md5_message(data, chlg64, conn->user,
783 conn->passwd, "smtp",
786 /* Send the response */
789 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", rplyb64);
792 state(conn, SMTP_AUTH_DIGESTMD5_RESP);
795 Curl_safefree(rplyb64);
801 /* For AUTH DIGEST-MD5 challenge-response responses */
802 static CURLcode smtp_state_auth_digest_resp_resp(struct connectdata *conn,
806 CURLcode result = CURLE_OK;
807 struct SessionHandle *data = conn->data;
809 (void)instate; /* no use for this yet */
811 if(smtpcode != 334) {
812 failf(data, "Authentication failed: %d", smtpcode);
813 result = CURLE_LOGIN_DENIED;
816 /* Send an empty response */
817 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "");
820 state(conn, SMTP_AUTH_FINAL);
829 /* For AUTH NTLM (without initial response) responses */
830 static CURLcode smtp_state_auth_ntlm_resp(struct connectdata *conn,
834 CURLcode result = CURLE_OK;
835 struct SessionHandle *data = conn->data;
836 char *type1msg = NULL;
839 (void)instate; /* no use for this yet */
841 if(smtpcode != 334) {
842 failf(data, "Access denied: %d", smtpcode);
843 result = CURLE_LOGIN_DENIED;
846 /* Create the type-1 message */
847 result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
851 /* Send the message */
854 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", type1msg);
857 state(conn, SMTP_AUTH_NTLM_TYPE2MSG);
860 Curl_safefree(type1msg);
867 /* For NTLM type-2 responses (sent in reponse to our type-1 message) */
868 static CURLcode smtp_state_auth_ntlm_type2msg_resp(struct connectdata *conn,
872 CURLcode result = CURLE_OK;
873 struct SessionHandle *data = conn->data;
874 char *type3msg = NULL;
877 (void)instate; /* no use for this yet */
879 if(smtpcode != 334) {
880 failf(data, "Access denied: %d", smtpcode);
881 result = CURLE_LOGIN_DENIED;
884 /* Create the type-3 message */
885 result = Curl_sasl_create_ntlm_type3_message(data,
886 data->state.buffer + 4,
887 conn->user, conn->passwd,
891 /* Send the message */
894 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", type3msg);
897 state(conn, SMTP_AUTH_FINAL);
900 Curl_safefree(type3msg);
908 /* For the final responses to the AUTH sequence */
909 static CURLcode smtp_state_auth_final_resp(struct connectdata *conn,
913 CURLcode result = CURLE_OK;
914 struct SessionHandle *data = conn->data;
916 (void)instate; /* no use for this yet */
918 if(smtpcode != 235) {
919 failf(data, "Authentication failed: %d", smtpcode);
920 result = CURLE_LOGIN_DENIED;
923 /* End of connect phase */
924 state(conn, SMTP_STOP);
929 /* Start the DO phase */
930 static CURLcode smtp_mail(struct connectdata *conn)
935 CURLcode result = CURLE_OK;
936 struct SessionHandle *data = conn->data;
938 /* Calculate the FROM parameter */
939 if(!data->set.str[STRING_MAIL_FROM])
940 /* Null reverse-path, RFC-2821, sect. 3.7 */
942 else if(data->set.str[STRING_MAIL_FROM][0] == '<')
943 from = aprintf("%s", data->set.str[STRING_MAIL_FROM]);
945 from = aprintf("<%s>", data->set.str[STRING_MAIL_FROM]);
948 return CURLE_OUT_OF_MEMORY;
950 /* Calculate the optional AUTH parameter */
951 if(data->set.str[STRING_MAIL_AUTH] && conn->proto.smtpc.authused) {
952 if(data->set.str[STRING_MAIL_AUTH][0] != '\0')
953 auth = aprintf("%s", data->set.str[STRING_MAIL_AUTH]);
955 /* Empty AUTH, RFC-2554, sect. 5 */
961 return CURLE_OUT_OF_MEMORY;
965 /* calculate the optional SIZE parameter */
966 if(conn->proto.smtpc.size_supported && conn->data->set.infilesize > 0) {
967 size = aprintf("%" FORMAT_OFF_T, data->set.infilesize);
973 return CURLE_OUT_OF_MEMORY;
977 /* Send the MAIL command */
979 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
980 "MAIL FROM:%s", from);
981 else if(auth && !size)
982 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
983 "MAIL FROM:%s AUTH=%s", from, auth);
984 else if(auth && size)
985 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
986 "MAIL FROM:%s AUTH=%s SIZE=%s", from, auth, size);
988 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
989 "MAIL FROM:%s SIZE=%s", from, size);
996 state(conn, SMTP_MAIL);
1001 static CURLcode smtp_rcpt_to(struct connectdata *conn)
1003 CURLcode result = CURLE_OK;
1004 struct SessionHandle *data = conn->data;
1005 struct SMTP *smtp = data->state.proto.smtp;
1007 /* Send the RCPT TO command */
1009 if(smtp->rcpt->data[0] == '<')
1010 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:%s",
1013 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:<%s>",
1016 state(conn, SMTP_RCPT);
1022 /* For MAIL responses */
1023 static CURLcode smtp_state_mail_resp(struct connectdata *conn, int smtpcode,
1026 CURLcode result = CURLE_OK;
1027 struct SessionHandle *data = conn->data;
1028 struct SMTP *smtp = data->state.proto.smtp;
1030 (void)instate; /* no use for this yet */
1032 if(smtpcode/100 != 2) {
1033 failf(data, "MAIL failed: %d", smtpcode);
1034 result = CURLE_SEND_ERROR;
1035 state(conn, SMTP_STOP);
1038 smtp->rcpt = data->set.mail_rcpt;
1040 result = smtp_rcpt_to(conn);
1046 /* For RCPT responses */
1047 static CURLcode smtp_state_rcpt_resp(struct connectdata *conn, int smtpcode,
1050 CURLcode result = CURLE_OK;
1051 struct SessionHandle *data = conn->data;
1052 struct SMTP *smtp = data->state.proto.smtp;
1054 (void)instate; /* no use for this yet */
1056 if(smtpcode/100 != 2) {
1057 failf(data, "RCPT failed: %d", smtpcode);
1058 result = CURLE_SEND_ERROR;
1059 state(conn, SMTP_STOP);
1063 smtp->rcpt = smtp->rcpt->next;
1064 result = smtp_rcpt_to(conn);
1066 /* If we failed or still are sending RCPT data then return */
1067 if(result || smtp->rcpt)
1071 /* Send the DATA command */
1072 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "DATA");
1075 state(conn, SMTP_DATA);
1081 /* For DATA response */
1082 static CURLcode smtp_state_data_resp(struct connectdata *conn, int smtpcode,
1085 (void)instate; /* no use for this yet */
1087 if(smtpcode != 354) {
1088 state(conn, SMTP_STOP);
1089 return CURLE_SEND_ERROR;
1093 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL);
1095 /* End of DO phase */
1096 state(conn, SMTP_STOP);
1101 /* For POSTDATA responses, which are received after the entire DATA
1102 part has been sent to the server */
1103 static CURLcode smtp_state_postdata_resp(struct connectdata *conn,
1107 CURLcode result = CURLE_OK;
1109 (void)instate; /* no use for this yet */
1112 result = CURLE_RECV_ERROR;
1114 /* End of DONE phase */
1115 state(conn, SMTP_STOP);
1120 static CURLcode smtp_statemach_act(struct connectdata *conn)
1122 CURLcode result = CURLE_OK;
1123 curl_socket_t sock = conn->sock[FIRSTSOCKET];
1124 struct SessionHandle *data = conn->data;
1126 struct smtp_conn *smtpc = &conn->proto.smtpc;
1127 struct pingpong *pp = &smtpc->pp;
1130 /* Busy upgrading the connection; right now all I/O is SSL/TLS, not SMTP */
1131 if(smtpc->state == SMTP_UPGRADETLS)
1132 return smtp_state_upgrade_tls(conn);
1134 /* Flush any data that needs to be sent */
1136 return Curl_pp_flushsend(pp);
1138 /* Read the response from the server */
1139 result = Curl_pp_readresp(sock, pp, &smtpcode, &nread);
1143 /* Store the latest response for later retrieval */
1144 if(smtpc->state != SMTP_QUIT)
1145 data->info.httpcode = smtpcode;
1148 /* We have now received a full SMTP server response */
1149 switch(smtpc->state) {
1150 case SMTP_SERVERGREET:
1151 result = smtp_state_servergreet_resp(conn, smtpcode, smtpc->state);
1155 result = smtp_state_ehlo_resp(conn, smtpcode, smtpc->state);
1159 result = smtp_state_helo_resp(conn, smtpcode, smtpc->state);
1163 result = smtp_state_starttls_resp(conn, smtpcode, smtpc->state);
1166 case SMTP_AUTH_PLAIN:
1167 result = smtp_state_auth_plain_resp(conn, smtpcode, smtpc->state);
1170 case SMTP_AUTH_LOGIN:
1171 result = smtp_state_auth_login_resp(conn, smtpcode, smtpc->state);
1174 case SMTP_AUTH_LOGIN_PASSWD:
1175 result = smtp_state_auth_login_password_resp(conn, smtpcode,
1179 #ifndef CURL_DISABLE_CRYPTO_AUTH
1180 case SMTP_AUTH_CRAMMD5:
1181 result = smtp_state_auth_cram_resp(conn, smtpcode, smtpc->state);
1184 case SMTP_AUTH_DIGESTMD5:
1185 result = smtp_state_auth_digest_resp(conn, smtpcode, smtpc->state);
1188 case SMTP_AUTH_DIGESTMD5_RESP:
1189 result = smtp_state_auth_digest_resp_resp(conn, smtpcode, smtpc->state);
1194 case SMTP_AUTH_NTLM:
1195 result = smtp_state_auth_ntlm_resp(conn, smtpcode, smtpc->state);
1198 case SMTP_AUTH_NTLM_TYPE2MSG:
1199 result = smtp_state_auth_ntlm_type2msg_resp(conn, smtpcode,
1204 case SMTP_AUTH_FINAL:
1205 result = smtp_state_auth_final_resp(conn, smtpcode, smtpc->state);
1209 result = smtp_state_mail_resp(conn, smtpcode, smtpc->state);
1213 result = smtp_state_rcpt_resp(conn, smtpcode, smtpc->state);
1217 result = smtp_state_data_resp(conn, smtpcode, smtpc->state);
1221 result = smtp_state_postdata_resp(conn, smtpcode, smtpc->state);
1225 /* fallthrough, just stop! */
1227 /* internal error */
1228 state(conn, SMTP_STOP);
1236 /* Called repeatedly until done from multi.c */
1237 static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done)
1239 CURLcode result = CURLE_OK;
1240 struct smtp_conn *smtpc = &conn->proto.smtpc;
1242 if((conn->handler->flags & PROTOPT_SSL) && !smtpc->ssldone)
1243 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone);
1245 result = Curl_pp_statemach(&smtpc->pp, FALSE);
1247 *done = (smtpc->state == SMTP_STOP) ? TRUE : FALSE;
1252 static CURLcode smtp_block_statemach(struct connectdata *conn)
1254 CURLcode result = CURLE_OK;
1255 struct smtp_conn *smtpc = &conn->proto.smtpc;
1257 while(smtpc->state != SMTP_STOP && !result)
1258 result = Curl_pp_statemach(&smtpc->pp, TRUE);
1263 /* Allocate and initialize the SMTP struct for the current SessionHandle if
1265 static CURLcode smtp_init(struct connectdata *conn)
1267 struct SessionHandle *data = conn->data;
1268 struct SMTP *smtp = data->state.proto.smtp;
1271 smtp = data->state.proto.smtp = calloc(sizeof(struct SMTP), 1);
1273 return CURLE_OUT_OF_MEMORY;
1279 /* For the SMTP "protocol connect" and "doing" phases only */
1280 static int smtp_getsock(struct connectdata *conn, curl_socket_t *socks,
1283 return Curl_pp_getsock(&conn->proto.smtpc.pp, socks, numsocks);
1286 /***********************************************************************
1290 * This function should do everything that is to be considered a part of
1291 * the connection phase.
1293 * The variable pointed to by 'done' will be TRUE if the protocol-layer
1294 * connect phase is done when this function returns, or FALSE if not. When
1295 * called as a part of the easy interface, it will always be TRUE.
1297 static CURLcode smtp_connect(struct connectdata *conn, bool *done)
1299 CURLcode result = CURLE_OK;
1300 struct smtp_conn *smtpc = &conn->proto.smtpc;
1301 struct pingpong *pp = &smtpc->pp;
1302 const char *path = conn->data->state.path;
1303 char localhost[HOSTNAME_MAX + 1];
1305 *done = FALSE; /* default to not done yet */
1307 /* If there already is a protocol-specific struct allocated for this
1308 sessionhandle, deal with it */
1309 Curl_reset_reqproto(conn);
1311 /* Initialise the SMTP layer */
1312 result = smtp_init(conn);
1316 /* We always support persistent connections in SMTP */
1317 conn->bits.close = FALSE;
1319 /* Set the default response time-out */
1320 pp->response_time = RESP_TIMEOUT;
1321 pp->statemach_act = smtp_statemach_act;
1322 pp->endofresp = smtp_endofresp;
1325 /* Initialise the pingpong layer */
1328 /* Calculate the path if necessary */
1330 if(!Curl_gethostname(localhost, sizeof(localhost)))
1336 /* URL decode the path and use it as the domain in our EHLO */
1337 result = Curl_urldecode(conn->data, path, 0, &smtpc->domain, NULL, TRUE);
1341 /* Start off waiting for the server greeting response */
1342 state(conn, SMTP_SERVERGREET);
1344 result = smtp_multi_statemach(conn, done);
1349 /***********************************************************************
1353 * The DONE function. This does what needs to be done after a single DO has
1356 * Input argument is already checked for validity.
1358 static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
1361 CURLcode result = CURLE_OK;
1362 struct SessionHandle *data = conn->data;
1363 struct SMTP *smtp = data->state.proto.smtp;
1364 ssize_t bytes_written;
1369 /* When the easy handle is removed from the multi interface while libcurl
1370 is still trying to resolve the host name, the SMTP struct is not yet
1371 initialized. However, the removal action calls Curl_done() which in
1372 turn calls this function, so we simply return success. */
1376 conn->bits.close = TRUE; /* marked for closure */
1377 result = status; /* use the already set error code */
1379 else if(!data->set.connect_only) {
1380 struct smtp_conn *smtpc = &conn->proto.smtpc;
1381 struct pingpong *pp = &smtpc->pp;
1383 /* Send the end of block data */
1384 result = Curl_write(conn,
1385 conn->writesockfd, /* socket to send to */
1386 SMTP_EOB, /* buffer pointer */
1387 SMTP_EOB_LEN, /* buffer size */
1388 &bytes_written); /* actually sent away */
1393 if(bytes_written != SMTP_EOB_LEN) {
1394 /* The whole chunk was not sent so keep it around and adjust the
1395 pingpong structure accordingly */
1396 pp->sendthis = strdup(SMTP_EOB);
1397 pp->sendsize = SMTP_EOB_LEN;
1398 pp->sendleft = SMTP_EOB_LEN - bytes_written;
1401 /* Successfully sent so adjust the response timeout relative to now */
1402 pp->response = Curl_tvnow();
1404 state(conn, SMTP_POSTDATA);
1406 /* Run the state-machine
1408 TODO: when the multi interface is used, this _really_ should be using
1409 the smtp_multi_statemach function but we have no general support for
1410 non-blocking DONE operations, not in the multi state machine and with
1411 Curl_done() invokes on several places in the code!
1413 result = smtp_block_statemach(conn);
1416 /* Clear the transfer mode for the next request */
1417 smtp->transfer = FTPTRANSFER_BODY;
1422 /***********************************************************************
1426 * This is the actual DO function for SMTP. Send a mail according to the
1427 * options previously setup.
1429 static CURLcode smtp_perform(struct connectdata *conn, bool *connected,
1432 /* This is SMTP and no proxy */
1433 CURLcode result = CURLE_OK;
1435 DEBUGF(infof(conn->data, "DO phase starts\n"));
1437 if(conn->data->set.opt_no_body) {
1438 /* Requested no body means no transfer */
1439 struct SMTP *smtp = conn->data->state.proto.smtp;
1440 smtp->transfer = FTPTRANSFER_INFO;
1443 *dophase_done = FALSE; /* not done yet */
1445 /* Start the first command in the DO phase */
1446 result = smtp_mail(conn);
1450 /* run the state-machine */
1451 result = smtp_multi_statemach(conn, dophase_done);
1453 *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1456 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1461 /***********************************************************************
1465 * This function is registered as 'curl_do' function. It decodes the path
1466 * parts etc as a wrapper to the actual DO function (smtp_perform).
1468 * The input argument is already checked for validity.
1470 static CURLcode smtp_do(struct connectdata *conn, bool *done)
1472 CURLcode result = CURLE_OK;
1474 *done = FALSE; /* default to false */
1476 /* Since connections can be re-used between SessionHandles, there might be a
1477 connection already existing but on a fresh SessionHandle struct. As such
1478 we make sure we have a good SMTP struct to play with. For new connections
1479 the SMTP struct is allocated and setup in the smtp_connect() function. */
1480 Curl_reset_reqproto(conn);
1481 result = smtp_init(conn);
1485 result = smtp_regular_transfer(conn, done);
1490 /***********************************************************************
1494 * This should be called before calling sclose(). We should then wait for the
1495 * response from the server before returning. The calling code should then try
1496 * to close the connection.
1498 static CURLcode smtp_quit(struct connectdata *conn)
1500 CURLcode result = CURLE_OK;
1502 /* Send the QUIT command */
1503 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "QUIT");
1507 state(conn, SMTP_QUIT);
1509 result = smtp_block_statemach(conn);
1514 /***********************************************************************
1518 * Disconnect from an SMTP server. Cleanup protocol-specific per-connection
1519 * resources. BLOCKING.
1521 static CURLcode smtp_disconnect(struct connectdata *conn,
1522 bool dead_connection)
1524 struct smtp_conn *smtpc = &conn->proto.smtpc;
1526 /* We cannot send quit unconditionally. If this connection is stale or
1527 bad in any way, sending quit and waiting around here will make the
1528 disconnect wait in vain and cause more problems than we need to. */
1530 /* The SMTP session may or may not have been allocated/setup at this
1532 if(!dead_connection && smtpc->pp.conn)
1533 (void)smtp_quit(conn); /* ignore errors on QUIT */
1535 /* Disconnect from the server */
1536 Curl_pp_disconnect(&smtpc->pp);
1538 /* Cleanup the SASL module */
1539 Curl_sasl_cleanup(conn, smtpc->authused);
1541 /* Cleanup our connection based variables */
1542 Curl_safefree(smtpc->domain);
1547 /* Call this when the DO phase has completed */
1548 static CURLcode smtp_dophase_done(struct connectdata *conn, bool connected)
1550 struct SMTP *smtp = conn->data->state.proto.smtp;
1554 if(smtp->transfer != FTPTRANSFER_BODY)
1555 /* no data to transfer */
1556 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1561 /* Called from multi.c while DOing */
1562 static CURLcode smtp_doing(struct connectdata *conn, bool *dophase_done)
1564 CURLcode result = smtp_multi_statemach(conn, dophase_done);
1567 DEBUGF(infof(conn->data, "DO phase failed\n"));
1568 else if(*dophase_done) {
1569 result = smtp_dophase_done(conn, FALSE /* not connected */);
1571 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1577 /***********************************************************************
1579 * smtp_regular_transfer()
1581 * The input argument is already checked for validity.
1583 * Performs all commands done before a regular transfer between a local and a
1586 static CURLcode smtp_regular_transfer(struct connectdata *conn,
1589 CURLcode result = CURLE_OK;
1590 bool connected = FALSE;
1591 struct SessionHandle *data = conn->data;
1593 /* Make sure size is unknown at this point */
1594 data->req.size = -1;
1596 /* Set the progress data */
1597 Curl_pgrsSetUploadCounter(data, 0);
1598 Curl_pgrsSetDownloadCounter(data, 0);
1599 Curl_pgrsSetUploadSize(data, 0);
1600 Curl_pgrsSetDownloadSize(data, 0);
1602 /* Carry out the perform */
1603 result = smtp_perform(conn, &connected, dophase_done);
1605 /* Perform post DO phase operations if necessary */
1606 if(!result && *dophase_done)
1607 result = smtp_dophase_done(conn, connected);
1612 static CURLcode smtp_setup_connection(struct connectdata *conn)
1614 struct SessionHandle *data = conn->data;
1616 if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
1617 /* Unless we have asked to tunnel SMTP operations through the proxy, we
1618 switch and use HTTP operations only */
1619 #ifndef CURL_DISABLE_HTTP
1620 if(conn->handler == &Curl_handler_smtp)
1621 conn->handler = &Curl_handler_smtp_proxy;
1624 conn->handler = &Curl_handler_smtps_proxy;
1626 failf(data, "SMTPS not supported!");
1627 return CURLE_UNSUPPORTED_PROTOCOL;
1631 /* We explicitly mark this connection as persistent here as we're doing
1632 SMTP over HTTP and thus we accidentally avoid setting this value
1634 conn->bits.close = FALSE;
1636 failf(data, "SMTP over http proxy requires HTTP support built-in!");
1637 return CURLE_UNSUPPORTED_PROTOCOL;
1641 data->state.path++; /* don't include the initial slash */
1646 CURLcode Curl_smtp_escape_eob(struct connectdata *conn, ssize_t nread)
1648 /* When sending a SMTP payload we must detect CRLF. sequences making sure
1649 they are sent as CRLF.. instead, as a . on the beginning of a line will
1650 be deleted by the server when not part of an EOB terminator and a
1651 genuine CRLF.CRLF which isn't escaped will wrongly be detected as end of
1656 struct smtp_conn *smtpc = &conn->proto.smtpc;
1657 struct SessionHandle *data = conn->data;
1659 /* Do we need to allocate the scatch buffer? */
1660 if(!data->state.scratch) {
1661 data->state.scratch = malloc(2 * BUFSIZE);
1663 if(!data->state.scratch) {
1664 failf (data, "Failed to alloc scratch buffer!");
1665 return CURLE_OUT_OF_MEMORY;
1669 /* This loop can be improved by some kind of Boyer-Moore style of
1670 approach but that is saved for later... */
1671 for(i = 0, si = 0; i < nread; i++) {
1672 if(SMTP_EOB[smtpc->eob] == data->req.upload_fromhere[i])
1674 else if(smtpc->eob) {
1675 /* A previous substring matched so output that first */
1676 memcpy(&data->state.scratch[si], SMTP_EOB, smtpc->eob);
1679 /* Then compare the first byte */
1680 if(SMTP_EOB[0] == data->req.upload_fromhere[i])
1686 /* Do we have a match for CRLF. as per RFC-2821, sect. 4.5.2 */
1687 if(SMTP_EOB_FIND_LEN == smtpc->eob) {
1688 /* Copy the replacement data to the target buffer */
1689 memcpy(&data->state.scratch[si], SMTP_EOB_REPL, SMTP_EOB_REPL_LEN);
1690 si += SMTP_EOB_REPL_LEN;
1693 else if(!smtpc->eob)
1694 data->state.scratch[si++] = data->req.upload_fromhere[i];
1698 /* A substring matched before processing ended so output that now */
1699 memcpy(&data->state.scratch[si], SMTP_EOB, smtpc->eob);
1705 /* Only use the new buffer if we replaced something */
1708 /* Upload from the new (replaced) buffer instead */
1709 data->req.upload_fromhere = data->state.scratch;
1711 /* Set the new amount too */
1712 data->req.upload_present = nread;
1718 #endif /* CURL_DISABLE_SMTP */