1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2011, 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 * RFC4616 PLAIN authentication
27 ***************************************************************************/
31 #ifndef CURL_DISABLE_SMTP
37 #ifdef HAVE_SYS_SOCKET_H
38 #include <sys/socket.h>
40 #ifdef HAVE_NETINET_IN_H
41 #include <netinet/in.h>
43 #ifdef HAVE_ARPA_INET_H
44 #include <arpa/inet.h>
47 #include <sys/utsname.h>
57 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
59 #define in_addr_t unsigned long
62 #include <curl/curl.h>
70 #include "http.h" /* for HTTP proxy tunnel stuff */
74 #include "strtoofft.h"
83 #include "strtoofft.h"
84 #include "curl_base64.h"
86 #include "curl_hmac.h"
87 #include "curl_gethostname.h"
88 #include "curl_ntlm_msgs.h"
90 #include "http_proxy.h"
92 #define _MPRINTF_REPLACE /* use our functions only */
93 #include <curl/mprintf.h>
95 #include "curl_memory.h"
96 /* The last #include file should be: */
99 /* Local API functions */
100 static CURLcode smtp_regular_transfer(struct connectdata *conn, bool *done);
101 static CURLcode smtp_do(struct connectdata *conn, bool *done);
102 static CURLcode smtp_done(struct connectdata *conn,
103 CURLcode, bool premature);
104 static CURLcode smtp_connect(struct connectdata *conn, bool *done);
105 static CURLcode smtp_disconnect(struct connectdata *conn, bool dead);
106 static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done);
107 static int smtp_getsock(struct connectdata *conn,
108 curl_socket_t *socks,
110 static CURLcode smtp_doing(struct connectdata *conn,
112 static CURLcode smtp_setup_connection(struct connectdata * conn);
113 static CURLcode smtp_state_upgrade_tls(struct connectdata *conn);
116 * SMTP protocol handler.
119 const struct Curl_handler Curl_handler_smtp = {
121 smtp_setup_connection, /* setup_connection */
123 smtp_done, /* done */
124 ZERO_NULL, /* do_more */
125 smtp_connect, /* connect_it */
126 smtp_multi_statemach, /* connecting */
127 smtp_doing, /* doing */
128 smtp_getsock, /* proto_getsock */
129 smtp_getsock, /* doing_getsock */
130 ZERO_NULL, /* domore_getsock */
131 ZERO_NULL, /* perform_getsock */
132 smtp_disconnect, /* disconnect */
133 ZERO_NULL, /* readwrite */
134 PORT_SMTP, /* defport */
135 CURLPROTO_SMTP, /* protocol */
136 PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY /* flags */
141 * SMTPS protocol handler.
144 const struct Curl_handler Curl_handler_smtps = {
145 "SMTPS", /* scheme */
146 smtp_setup_connection, /* setup_connection */
148 smtp_done, /* done */
149 ZERO_NULL, /* do_more */
150 smtp_connect, /* connect_it */
151 smtp_multi_statemach, /* connecting */
152 smtp_doing, /* doing */
153 smtp_getsock, /* proto_getsock */
154 smtp_getsock, /* doing_getsock */
155 ZERO_NULL, /* domore_getsock */
156 ZERO_NULL, /* perform_getsock */
157 smtp_disconnect, /* disconnect */
158 ZERO_NULL, /* readwrite */
159 PORT_SMTPS, /* defport */
160 CURLPROTO_SMTP | CURLPROTO_SMTPS, /* protocol */
161 PROTOPT_CLOSEACTION | PROTOPT_SSL
162 | PROTOPT_NOURLQUERY /* flags */
166 #ifndef CURL_DISABLE_HTTP
168 * HTTP-proxyed SMTP protocol handler.
171 static const struct Curl_handler Curl_handler_smtp_proxy = {
173 ZERO_NULL, /* setup_connection */
174 Curl_http, /* do_it */
175 Curl_http_done, /* done */
176 ZERO_NULL, /* do_more */
177 ZERO_NULL, /* connect_it */
178 ZERO_NULL, /* connecting */
179 ZERO_NULL, /* doing */
180 ZERO_NULL, /* proto_getsock */
181 ZERO_NULL, /* doing_getsock */
182 ZERO_NULL, /* domore_getsock */
183 ZERO_NULL, /* perform_getsock */
184 ZERO_NULL, /* disconnect */
185 ZERO_NULL, /* readwrite */
186 PORT_SMTP, /* defport */
187 CURLPROTO_HTTP, /* protocol */
188 PROTOPT_NONE /* flags */
193 * HTTP-proxyed SMTPS protocol handler.
196 static const struct Curl_handler Curl_handler_smtps_proxy = {
197 "SMTPS", /* scheme */
198 ZERO_NULL, /* setup_connection */
199 Curl_http, /* do_it */
200 Curl_http_done, /* done */
201 ZERO_NULL, /* do_more */
202 ZERO_NULL, /* connect_it */
203 ZERO_NULL, /* connecting */
204 ZERO_NULL, /* doing */
205 ZERO_NULL, /* proto_getsock */
206 ZERO_NULL, /* doing_getsock */
207 ZERO_NULL, /* domore_getsock */
208 ZERO_NULL, /* perform_getsock */
209 ZERO_NULL, /* disconnect */
210 ZERO_NULL, /* readwrite */
211 PORT_SMTPS, /* defport */
212 CURLPROTO_HTTP, /* protocol */
213 PROTOPT_NONE /* flags */
218 /* Function that checks for an ending smtp status code at the start of the
220 As a side effect, it also flags allowed authentication mechanisms according
221 to EHLO AUTH response. */
222 static int smtp_endofresp(struct pingpong *pp, int *resp)
224 char *line = pp->linestart_resp;
225 size_t len = pp->nread_resp;
226 struct connectdata *conn = pp->conn;
227 struct smtp_conn *smtpc = &conn->proto.smtpc;
231 if(len < 4 || !ISDIGIT(line[0]) || !ISDIGIT(line[1]) || !ISDIGIT(line[2]))
232 return FALSE; /* Nothing for us. */
234 if((result = (line[3] == ' ')) != 0)
235 *resp = curlx_sltosi(strtol(line, NULL, 10));
240 if(smtpc->state == SMTP_EHLO && len >= 5 && !memcmp(line, "AUTH ", 5)) {
246 (*line == ' ' || *line == '\t' ||
247 *line == '\r' || *line == '\n')) {
255 for(wordlen = 0; wordlen < len && line[wordlen] != ' ' &&
256 line[wordlen] != '\t' && line[wordlen] != '\r' &&
257 line[wordlen] != '\n';)
260 if(wordlen == 5 && !memcmp(line, "LOGIN", 5))
261 smtpc->authmechs |= SMTP_AUTH_LOGIN;
262 else if(wordlen == 5 && !memcmp(line, "PLAIN", 5))
263 smtpc->authmechs |= SMTP_AUTH_PLAIN;
264 else if(wordlen == 8 && !memcmp(line, "CRAM-MD5", 8))
265 smtpc->authmechs |= SMTP_AUTH_CRAM_MD5;
266 else if(wordlen == 10 && !memcmp(line, "DIGEST-MD5", 10))
267 smtpc->authmechs |= SMTP_AUTH_DIGEST_MD5;
268 else if(wordlen == 6 && !memcmp(line, "GSSAPI", 6))
269 smtpc->authmechs |= SMTP_AUTH_GSSAPI;
270 else if(wordlen == 8 && !memcmp(line, "EXTERNAL", 8))
271 smtpc->authmechs |= SMTP_AUTH_EXTERNAL;
272 else if(wordlen == 4 && !memcmp(line, "NTLM", 4))
273 smtpc->authmechs |= SMTP_AUTH_NTLM;
283 /* This is the ONLY way to change SMTP state! */
284 static void state(struct connectdata *conn,
287 struct smtp_conn *smtpc = &conn->proto.smtpc;
288 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
289 /* for debug purposes */
290 static const char * const names[] = {
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]);
315 smtpc->state = newstate;
318 static CURLcode smtp_state_ehlo(struct connectdata *conn)
321 struct smtp_conn *smtpc = &conn->proto.smtpc;
323 smtpc->authmechs = 0; /* No known authentication mechanisms yet. */
324 smtpc->authused = 0; /* Clear the authentication mechanism used
325 for esmtp connections */
328 result = Curl_pp_sendf(&smtpc->pp, "EHLO %s", smtpc->domain);
333 state(conn, SMTP_EHLO);
337 static CURLcode smtp_state_helo(struct connectdata *conn)
340 struct smtp_conn *smtpc = &conn->proto.smtpc;
342 smtpc->authused = 0; /* No authentication mechanism used in smtp
346 result = Curl_pp_sendf(&smtpc->pp, "HELO %s", smtpc->domain);
351 state(conn, SMTP_HELO);
355 static CURLcode smtp_auth_plain_data(struct connectdata *conn,
356 char **outptr, size_t *outlen)
358 char plainauth[2 * MAX_CURL_USER_LENGTH + MAX_CURL_PASSWORD_LENGTH];
362 ulen = strlen(conn->user);
363 plen = strlen(conn->passwd);
365 if(2 * ulen + plen + 2 > sizeof plainauth) {
368 return CURLE_OUT_OF_MEMORY; /* plainauth too small */
371 memcpy(plainauth, conn->user, ulen);
372 plainauth[ulen] = '\0';
373 memcpy(plainauth + ulen + 1, conn->user, ulen);
374 plainauth[2 * ulen + 1] = '\0';
375 memcpy(plainauth + 2 * ulen + 2, conn->passwd, plen);
376 return Curl_base64_encode(conn->data, plainauth, 2 * ulen + plen + 2,
380 static CURLcode smtp_auth_login_user(struct connectdata *conn,
381 char **outptr, size_t *outlen)
383 size_t ulen = strlen(conn->user);
386 *outptr = strdup("=");
388 *outlen = (size_t) 1;
392 return CURLE_OUT_OF_MEMORY;
395 return Curl_base64_encode(conn->data, conn->user, ulen, outptr, outlen);
399 static CURLcode smtp_auth_ntlm_type1_message(struct connectdata *conn,
400 char **outptr, size_t *outlen)
402 return Curl_ntlm_create_type1_message(conn->user, conn->passwd,
403 &conn->ntlm, outptr, outlen);
407 static CURLcode smtp_authenticate(struct connectdata *conn)
409 CURLcode result = CURLE_OK;
410 struct smtp_conn *smtpc = &conn->proto.smtpc;
411 char *initresp = NULL;
412 const char *mech = NULL;
414 smtpstate state1 = SMTP_STOP;
415 smtpstate state2 = SMTP_STOP;
417 /* Check we have a username and password to authenticate with and end the
418 connect phase if we don't. */
419 if(!conn->bits.user_passwd) {
420 state(conn, SMTP_STOP);
425 /* Check supported authentication mechanisms by decreasing order of
427 #ifndef CURL_DISABLE_CRYPTO_AUTH
428 if(smtpc->authmechs & SMTP_AUTH_CRAM_MD5) {
430 state1 = SMTP_AUTHCRAM;
431 smtpc->authused = SMTP_AUTH_CRAM_MD5;
436 if(smtpc->authmechs & SMTP_AUTH_NTLM) {
438 state1 = SMTP_AUTHNTLM;
439 state2 = SMTP_AUTHNTLM_TYPE2MSG;
440 smtpc->authused = SMTP_AUTH_NTLM;
441 result = smtp_auth_ntlm_type1_message(conn, &initresp, &len);
445 if(smtpc->authmechs & SMTP_AUTH_LOGIN) {
447 state1 = SMTP_AUTHLOGIN;
448 state2 = SMTP_AUTHPASSWD;
449 smtpc->authused = SMTP_AUTH_LOGIN;
450 result = smtp_auth_login_user(conn, &initresp, &len);
452 else if(smtpc->authmechs & SMTP_AUTH_PLAIN) {
454 state1 = SMTP_AUTHPLAIN;
456 smtpc->authused = SMTP_AUTH_PLAIN;
457 result = smtp_auth_plain_data(conn, &initresp, &len);
460 infof(conn->data, "No known auth mechanisms supported!\n");
461 result = CURLE_LOGIN_DENIED; /* Other mechanisms not supported. */
466 strlen(mech) + len <= 512 - 8) { /* AUTH <mech> ...<crlf> */
467 result = Curl_pp_sendf(&smtpc->pp, "AUTH %s %s", mech, initresp);
473 result = Curl_pp_sendf(&smtpc->pp, "AUTH %s", mech);
478 Curl_safefree(initresp);
484 /* For the SMTP "protocol connect" and "doing" phases only */
485 static int smtp_getsock(struct connectdata *conn,
486 curl_socket_t *socks,
489 return Curl_pp_getsock(&conn->proto.smtpc.pp, socks, numsocks);
493 static void smtp_to_smtps(struct connectdata *conn)
495 conn->handler = &Curl_handler_smtps;
498 #define smtp_to_smtps(x) Curl_nop_stmt
501 /* for STARTTLS responses */
502 static CURLcode smtp_state_starttls_resp(struct connectdata *conn,
506 CURLcode result = CURLE_OK;
507 struct SessionHandle *data = conn->data;
508 (void)instate; /* no use for this yet */
510 if(smtpcode != 220) {
511 if(data->set.use_ssl != CURLUSESSL_TRY) {
512 failf(data, "STARTTLS denied. %c", smtpcode);
513 result = CURLE_USE_SSL_FAILED;
516 result = smtp_authenticate(conn);
519 if(data->state.used_interface == Curl_if_multi) {
520 state(conn, SMTP_UPGRADETLS);
521 return smtp_state_upgrade_tls(conn);
524 result = Curl_ssl_connect(conn, FIRSTSOCKET);
525 if(CURLE_OK == result) {
527 result = smtp_state_ehlo(conn);
535 static CURLcode smtp_state_upgrade_tls(struct connectdata *conn)
537 struct smtp_conn *smtpc = &conn->proto.smtpc;
540 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone);
544 result = smtp_state_ehlo(conn);
550 /* for EHLO responses */
551 static CURLcode smtp_state_ehlo_resp(struct connectdata *conn,
555 CURLcode result = CURLE_OK;
556 struct SessionHandle *data = conn->data;
558 (void)instate; /* no use for this yet */
560 if(smtpcode/100 != 2) {
561 if((data->set.use_ssl <= CURLUSESSL_TRY || conn->ssl[FIRSTSOCKET].use) &&
562 !conn->bits.user_passwd)
563 result = smtp_state_helo(conn);
565 failf(data, "Access denied: %d", smtpcode);
566 result = CURLE_LOGIN_DENIED;
569 else if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
570 /* We don't have a SSL/TLS connection yet, but SSL is requested. Switch
571 to TLS connection now */
572 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "STARTTLS");
573 state(conn, SMTP_STARTTLS);
576 result = smtp_authenticate(conn);
581 /* for HELO responses */
582 static CURLcode smtp_state_helo_resp(struct connectdata *conn,
586 CURLcode result = CURLE_OK;
587 struct SessionHandle *data = conn->data;
589 (void)instate; /* no use for this yet */
591 if(smtpcode/100 != 2) {
592 failf(data, "Access denied: %d", smtpcode);
593 result = CURLE_LOGIN_DENIED;
596 /* end the connect phase */
597 state(conn, SMTP_STOP);
603 /* for AUTH PLAIN (without initial response) responses */
604 static CURLcode smtp_state_authplain_resp(struct connectdata *conn,
608 CURLcode result = CURLE_OK;
609 struct SessionHandle *data = conn->data;
611 char *plainauth = NULL;
613 (void)instate; /* no use for this yet */
615 if(smtpcode != 334) {
616 failf(data, "Access denied: %d", smtpcode);
617 result = CURLE_LOGIN_DENIED;
620 result = smtp_auth_plain_data(conn, &plainauth, &len);
624 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", plainauth);
627 state(conn, SMTP_AUTH);
629 Curl_safefree(plainauth);
636 /* for AUTH LOGIN (without initial response) responses */
637 static CURLcode smtp_state_authlogin_resp(struct connectdata *conn,
641 CURLcode result = CURLE_OK;
642 struct SessionHandle *data = conn->data;
644 char *authuser = NULL;
646 (void)instate; /* no use for this yet */
648 if(smtpcode != 334) {
649 failf(data, "Access denied: %d", smtpcode);
650 result = CURLE_LOGIN_DENIED;
653 result = smtp_auth_login_user(conn, &authuser, &len);
657 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", authuser);
660 state(conn, SMTP_AUTHPASSWD);
662 Curl_safefree(authuser);
669 /* for responses to user entry of AUTH LOGIN. */
670 static CURLcode smtp_state_authpasswd_resp(struct connectdata *conn,
674 CURLcode result = CURLE_OK;
675 struct SessionHandle *data = conn->data;
678 char *authpasswd = NULL;
680 (void)instate; /* no use for this yet */
682 if(smtpcode != 334) {
683 failf(data, "Access denied: %d", smtpcode);
684 result = CURLE_LOGIN_DENIED;
687 plen = strlen(conn->passwd);
690 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "=");
692 result = Curl_base64_encode(data, conn->passwd, plen, &authpasswd, &len);
696 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", authpasswd);
699 state(conn, SMTP_AUTH);
701 Curl_safefree(authpasswd);
709 #ifndef CURL_DISABLE_CRYPTO_AUTH
711 /* for AUTH CRAM-MD5 responses. */
712 static CURLcode smtp_state_authcram_resp(struct connectdata *conn,
716 CURLcode result = CURLE_OK;
717 struct SessionHandle *data = conn->data;
718 char * chlg64 = data->state.buffer;
719 unsigned char * chlg;
722 char *rplyb64 = NULL;
724 unsigned char digest[16];
725 char reply[MAX_CURL_USER_LENGTH + 32 /* 2 * size of MD5 digest */ + 1];
727 (void)instate; /* no use for this yet */
729 if(smtpcode != 334) {
730 failf(data, "Access denied: %d", smtpcode);
731 return CURLE_LOGIN_DENIED;
734 /* Get the challenge. */
735 for(chlg64 += 4; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
738 chlg = (unsigned char *) NULL;
742 for(len = strlen(chlg64); len--;)
743 if(chlg64[len] != '\r' && chlg64[len] != '\n' && chlg64[len] != ' ' &&
750 result = Curl_base64_decode(chlg64, &chlg, &chlglen);
756 /* Compute digest. */
757 ctxt = Curl_HMAC_init(Curl_HMAC_MD5,
758 (const unsigned char *) conn->passwd,
759 (unsigned int)(strlen(conn->passwd)));
763 return CURLE_OUT_OF_MEMORY;
767 Curl_HMAC_update(ctxt, chlg, (unsigned int)(chlglen));
771 Curl_HMAC_final(ctxt, digest);
773 /* Prepare the reply. */
774 snprintf(reply, sizeof reply,
775 "%s %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
776 conn->user, digest[0], digest[1], digest[2], digest[3], digest[4],
778 digest[6], digest[7], digest[8], digest[9], digest[10], digest[11],
779 digest[12], digest[13], digest[14], digest[15]);
781 /* Encode it to base64 and send it. */
782 result = Curl_base64_encode(data, reply, 0, &rplyb64, &len);
786 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", rplyb64);
789 state(conn, SMTP_AUTH);
791 Curl_safefree(rplyb64);
800 /* for the AUTH NTLM (without initial response) response. */
801 static CURLcode smtp_state_auth_ntlm_resp(struct connectdata *conn,
805 CURLcode result = CURLE_OK;
806 struct SessionHandle *data = conn->data;
807 char *type1msg = NULL;
810 (void)instate; /* no use for this yet */
812 if(smtpcode != 334) {
813 failf(data, "Access denied: %d", smtpcode);
814 result = CURLE_LOGIN_DENIED;
817 result = smtp_auth_ntlm_type1_message(conn, &type1msg, &len);
821 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", type1msg);
824 state(conn, SMTP_AUTHNTLM_TYPE2MSG);
826 Curl_safefree(type1msg);
833 /* for the NTLM type-2 response (sent in reponse to our type-1 message). */
834 static CURLcode smtp_state_auth_ntlm_type2msg_resp(struct connectdata *conn,
838 CURLcode result = CURLE_OK;
839 struct SessionHandle *data = conn->data;
840 char *type3msg = NULL;
843 (void)instate; /* no use for this yet */
845 if(smtpcode != 334) {
846 failf(data, "Access denied: %d", smtpcode);
847 result = CURLE_LOGIN_DENIED;
850 result = Curl_ntlm_decode_type2_message(data, data->state.buffer + 4,
853 result = Curl_ntlm_create_type3_message(conn->data, conn->user,
854 conn->passwd, &conn->ntlm,
858 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", type3msg);
861 state(conn, SMTP_AUTH);
863 Curl_safefree(type3msg);
872 /* for final responses to AUTH sequences. */
873 static CURLcode smtp_state_auth_resp(struct connectdata *conn,
877 CURLcode result = CURLE_OK;
878 struct SessionHandle *data = conn->data;
880 (void)instate; /* no use for this yet */
882 if(smtpcode != 235) {
883 failf(data, "Authentication failed: %d", smtpcode);
884 result = CURLE_LOGIN_DENIED;
887 state(conn, SMTP_STOP); /* End of connect phase. */
892 /* start the DO phase */
893 static CURLcode smtp_mail(struct connectdata *conn)
897 CURLcode result = CURLE_OK;
898 struct SessionHandle *data = conn->data;
900 /* calculate the FROM parameter */
901 if(!data->set.str[STRING_MAIL_FROM])
902 /* null reverse-path, RFC-2821, sect. 3.7 */
904 else if(data->set.str[STRING_MAIL_FROM][0] == '<')
905 from = aprintf("%s", data->set.str[STRING_MAIL_FROM]);
907 from = aprintf("<%s>", data->set.str[STRING_MAIL_FROM]);
910 return CURLE_OUT_OF_MEMORY;
912 /* calculate the optional SIZE parameter */
913 if(conn->data->set.infilesize > 0) {
914 size = aprintf("%" FORMAT_OFF_T, data->set.infilesize);
919 return CURLE_OUT_OF_MEMORY;
925 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "MAIL FROM:%s", from);
927 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "MAIL FROM:%s SIZE=%s",
936 state(conn, SMTP_MAIL);
941 static CURLcode smtp_rcpt_to(struct connectdata *conn)
943 CURLcode result = CURLE_OK;
944 struct smtp_conn *smtpc = &conn->proto.smtpc;
948 if(smtpc->rcpt->data[0] == '<')
949 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:%s",
952 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:<%s>",
955 state(conn, SMTP_RCPT);
961 /* for MAIL responses */
962 static CURLcode smtp_state_mail_resp(struct connectdata *conn,
966 CURLcode result = CURLE_OK;
967 struct SessionHandle *data = conn->data;
968 (void)instate; /* no use for this yet */
970 if(smtpcode/100 != 2) {
971 failf(data, "Access denied: %d", smtpcode);
972 result = CURLE_LOGIN_DENIED;
973 state(conn, SMTP_STOP);
976 struct smtp_conn *smtpc = &conn->proto.smtpc;
977 smtpc->rcpt = data->set.mail_rcpt;
979 result = smtp_rcpt_to(conn);
985 /* for RCPT responses */
986 static CURLcode smtp_state_rcpt_resp(struct connectdata *conn,
990 CURLcode result = CURLE_OK;
991 struct SessionHandle *data = conn->data;
992 (void)instate; /* no use for this yet */
994 if(smtpcode/100 != 2) {
995 failf(data, "Access denied: %d", smtpcode);
996 result = CURLE_LOGIN_DENIED;
997 state(conn, SMTP_STOP);
1000 struct smtp_conn *smtpc = &conn->proto.smtpc;
1003 smtpc->rcpt = smtpc->rcpt->next;
1004 result = smtp_rcpt_to(conn);
1006 /* if we failed or still is in RCPT sending, return */
1007 if(result || smtpc->rcpt)
1012 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "DATA");
1016 state(conn, SMTP_DATA);
1022 /* for the DATA response */
1023 static CURLcode smtp_state_data_resp(struct connectdata *conn,
1027 struct SessionHandle *data = conn->data;
1028 struct FTP *smtp = data->state.proto.smtp;
1030 (void)instate; /* no use for this yet */
1032 if(smtpcode != 354) {
1033 state(conn, SMTP_STOP);
1034 return CURLE_RECV_ERROR;
1038 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, /* no download */
1039 FIRSTSOCKET, smtp->bytecountp);
1041 state(conn, SMTP_STOP);
1045 /* for the POSTDATA response, which is received after the entire DATA
1046 part has been sent off to the server */
1047 static CURLcode smtp_state_postdata_resp(struct connectdata *conn,
1051 CURLcode result = CURLE_OK;
1053 (void)instate; /* no use for this yet */
1056 result = CURLE_RECV_ERROR;
1058 state(conn, SMTP_STOP);
1063 static CURLcode smtp_statemach_act(struct connectdata *conn)
1066 curl_socket_t sock = conn->sock[FIRSTSOCKET];
1067 struct SessionHandle *data = conn->data;
1069 struct smtp_conn *smtpc = &conn->proto.smtpc;
1070 struct pingpong *pp = &smtpc->pp;
1073 if(smtpc->state == SMTP_UPGRADETLS)
1074 return smtp_state_upgrade_tls(conn);
1077 /* we have a piece of a command still left to send */
1078 return Curl_pp_flushsend(pp);
1080 /* we read a piece of response */
1081 result = Curl_pp_readresp(sock, pp, &smtpcode, &nread);
1086 /* we have now received a full SMTP server response */
1087 switch(smtpc->state) {
1088 case SMTP_SERVERGREET:
1089 if(smtpcode/100 != 2) {
1090 failf(data, "Got unexpected smtp-server response: %d", smtpcode);
1091 return CURLE_FTP_WEIRD_SERVER_REPLY;
1094 result = smtp_state_ehlo(conn);
1100 result = smtp_state_ehlo_resp(conn, smtpcode, smtpc->state);
1104 result = smtp_state_helo_resp(conn, smtpcode, smtpc->state);
1108 result = smtp_state_starttls_resp(conn, smtpcode, smtpc->state);
1111 case SMTP_AUTHPLAIN:
1112 result = smtp_state_authplain_resp(conn, smtpcode, smtpc->state);
1115 case SMTP_AUTHLOGIN:
1116 result = smtp_state_authlogin_resp(conn, smtpcode, smtpc->state);
1119 case SMTP_AUTHPASSWD:
1120 result = smtp_state_authpasswd_resp(conn, smtpcode, smtpc->state);
1123 #ifndef CURL_DISABLE_CRYPTO_AUTH
1125 result = smtp_state_authcram_resp(conn, smtpcode, smtpc->state);
1131 result = smtp_state_auth_ntlm_resp(conn, smtpcode, smtpc->state);
1134 case SMTP_AUTHNTLM_TYPE2MSG:
1135 result = smtp_state_auth_ntlm_type2msg_resp(conn, smtpcode,
1141 result = smtp_state_auth_resp(conn, smtpcode, smtpc->state);
1145 result = smtp_state_mail_resp(conn, smtpcode, smtpc->state);
1149 result = smtp_state_rcpt_resp(conn, smtpcode, smtpc->state);
1153 result = smtp_state_data_resp(conn, smtpcode, smtpc->state);
1157 result = smtp_state_postdata_resp(conn, smtpcode, smtpc->state);
1161 /* fallthrough, just stop! */
1163 /* internal error */
1164 state(conn, SMTP_STOP);
1172 /* called repeatedly until done from multi.c */
1173 static CURLcode smtp_multi_statemach(struct connectdata *conn,
1176 struct smtp_conn *smtpc = &conn->proto.smtpc;
1179 if((conn->handler->flags & PROTOPT_SSL) && !smtpc->ssldone)
1180 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone);
1182 result = Curl_pp_multi_statemach(&smtpc->pp);
1184 *done = (smtpc->state == SMTP_STOP) ? TRUE : FALSE;
1189 static CURLcode smtp_easy_statemach(struct connectdata *conn)
1191 struct smtp_conn *smtpc = &conn->proto.smtpc;
1192 struct pingpong *pp = &smtpc->pp;
1193 CURLcode result = CURLE_OK;
1195 while(smtpc->state != SMTP_STOP) {
1196 result = Curl_pp_easy_statemach(pp);
1205 * Allocate and initialize the struct SMTP for the current SessionHandle. If
1208 static CURLcode smtp_init(struct connectdata *conn)
1210 struct SessionHandle *data = conn->data;
1211 struct FTP *smtp = data->state.proto.smtp;
1213 smtp = data->state.proto.smtp = calloc(sizeof(struct FTP), 1);
1215 return CURLE_OUT_OF_MEMORY;
1218 /* get some initial data into the smtp struct */
1219 smtp->bytecountp = &data->req.bytecount;
1221 /* No need to duplicate user+password, the connectdata struct won't change
1222 during a session, but we re-init them here since on subsequent inits
1223 since the conn struct may have changed or been replaced.
1225 smtp->user = conn->user;
1226 smtp->passwd = conn->passwd;
1232 * smtp_connect() should do everything that is to be considered a part of
1233 * the connection phase.
1235 * The variable pointed to by 'done' will be TRUE if the protocol-layer
1236 * connect phase is done when this function returns, or FALSE if not. When
1237 * called as a part of the easy interface, it will always be TRUE.
1239 static CURLcode smtp_connect(struct connectdata *conn,
1240 bool *done) /* see description above */
1243 struct smtp_conn *smtpc = &conn->proto.smtpc;
1244 struct SessionHandle *data = conn->data;
1245 struct pingpong *pp = &smtpc->pp;
1246 const char *path = conn->data->state.path;
1247 char localhost[HOSTNAME_MAX + 1];
1249 *done = FALSE; /* default to not done yet */
1251 /* If there already is a protocol-specific struct allocated for this
1252 sessionhandle, deal with it */
1253 Curl_reset_reqproto(conn);
1255 result = smtp_init(conn);
1256 if(CURLE_OK != result)
1259 /* We always support persistent connections on smtp */
1260 conn->bits.close = FALSE;
1262 pp->response_time = RESP_TIMEOUT; /* set default response time-out */
1263 pp->statemach_act = smtp_statemach_act;
1264 pp->endofresp = smtp_endofresp;
1267 if(conn->bits.tunnel_proxy && conn->bits.httpproxy) {
1268 /* for SMTP over HTTP proxy */
1269 struct HTTP http_proxy;
1270 struct FTP *smtp_save;
1273 /* We want "seamless" SMTP operations through HTTP proxy tunnel */
1275 /* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the member
1276 * conn->proto.http; we want SMTP through HTTP and we have to change the
1277 * member temporarily for connecting to the HTTP proxy. After
1278 * Curl_proxyCONNECT we have to set back the member to the original struct
1281 smtp_save = data->state.proto.smtp;
1282 memset(&http_proxy, 0, sizeof(http_proxy));
1283 data->state.proto.http = &http_proxy;
1285 result = Curl_proxyCONNECT(conn, FIRSTSOCKET,
1286 conn->host.name, conn->remote_port);
1288 data->state.proto.smtp = smtp_save;
1290 if(CURLE_OK != result)
1294 if((conn->handler->protocol & CURLPROTO_SMTPS) &&
1295 data->state.used_interface != Curl_if_multi) {
1296 /* SMTPS is simply smtp with SSL for the control channel */
1297 /* now, perform the SSL initialization for this socket */
1298 result = Curl_ssl_connect(conn, FIRSTSOCKET);
1303 Curl_pp_init(pp); /* init the response reader stuff */
1305 pp->response_time = RESP_TIMEOUT; /* set default response time-out */
1306 pp->statemach_act = smtp_statemach_act;
1307 pp->endofresp = smtp_endofresp;
1311 if(!Curl_gethostname(localhost, sizeof localhost))
1317 /* url decode the path and use it as domain with EHLO */
1318 result = Curl_urldecode(conn->data, path, 0, &smtpc->domain, NULL, TRUE);
1322 /* When we connect, we start in the state where we await the server greeting
1324 state(conn, SMTP_SERVERGREET);
1326 if(data->state.used_interface == Curl_if_multi)
1327 result = smtp_multi_statemach(conn, done);
1329 result = smtp_easy_statemach(conn);
1337 /***********************************************************************
1341 * The DONE function. This does what needs to be done after a single DO has
1344 * Input argument is already checked for validity.
1346 static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
1349 struct SessionHandle *data = conn->data;
1350 struct FTP *smtp = data->state.proto.smtp;
1351 CURLcode result = CURLE_OK;
1352 ssize_t bytes_written;
1356 /* When the easy handle is removed from the multi while libcurl is still
1357 * trying to resolve the host name, it seems that the smtp struct is not
1358 * yet initialized, but the removal action calls Curl_done() which calls
1359 * this function. So we simply return success if no smtp pointer is set.
1364 conn->bits.close = TRUE; /* marked for closure */
1365 result = status; /* use the already set error code */
1368 /* TODO: make this work even when the socket is EWOULDBLOCK in this
1371 /* write to socket (send away data) */
1372 result = Curl_write(conn,
1373 conn->writesockfd, /* socket to send to */
1374 SMTP_EOB, /* buffer pointer */
1375 SMTP_EOB_LEN, /* buffer size */
1376 &bytes_written); /* actually sent away */
1379 if(status == CURLE_OK) {
1380 struct smtp_conn *smtpc = &conn->proto.smtpc;
1381 struct pingpong *pp = &smtpc->pp;
1382 pp->response = Curl_tvnow(); /* timeout relative now */
1384 state(conn, SMTP_POSTDATA);
1385 /* run the state-machine
1387 TODO: when the multi interface is used, this _really_ should be using
1388 the smtp_multi_statemach function but we have no general support for
1389 non-blocking DONE operations, not in the multi state machine and with
1390 Curl_done() invokes on several places in the code!
1392 result = smtp_easy_statemach(conn);
1395 /* clear these for next connection */
1396 smtp->transfer = FTPTRANSFER_BODY;
1401 /***********************************************************************
1405 * This is the actual DO function for SMTP. Get a file/directory according to
1406 * the options previously setup.
1410 CURLcode smtp_perform(struct connectdata *conn,
1411 bool *connected, /* connect status after PASV / PORT */
1414 /* this is SMTP and no proxy */
1415 CURLcode result = CURLE_OK;
1417 DEBUGF(infof(conn->data, "DO phase starts\n"));
1419 if(conn->data->set.opt_no_body) {
1420 /* requested no body means no transfer... */
1421 struct FTP *smtp = conn->data->state.proto.smtp;
1422 smtp->transfer = FTPTRANSFER_INFO;
1425 *dophase_done = FALSE; /* not done yet */
1427 /* start the first command in the DO phase */
1428 result = smtp_mail(conn);
1432 /* run the state-machine */
1433 if(conn->data->state.used_interface == Curl_if_multi)
1434 result = smtp_multi_statemach(conn, dophase_done);
1436 result = smtp_easy_statemach(conn);
1437 *dophase_done = TRUE; /* with the easy interface we are done here */
1439 *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1442 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1447 /***********************************************************************
1451 * This function is registered as 'curl_do' function. It decodes the path
1452 * parts etc as a wrapper to the actual DO function (smtp_perform).
1454 * The input argument is already checked for validity.
1456 static CURLcode smtp_do(struct connectdata *conn, bool *done)
1458 CURLcode retcode = CURLE_OK;
1460 *done = FALSE; /* default to false */
1463 Since connections can be re-used between SessionHandles, this might be a
1464 connection already existing but on a fresh SessionHandle struct so we must
1465 make sure we have a good 'struct SMTP' to play with. For new connections,
1466 the struct SMTP is allocated and setup in the smtp_connect() function.
1468 Curl_reset_reqproto(conn);
1469 retcode = smtp_init(conn);
1473 retcode = smtp_regular_transfer(conn, done);
1478 /***********************************************************************
1482 * This should be called before calling sclose(). We should then wait for the
1483 * response from the server before returning. The calling code should then try
1484 * to close the connection.
1487 static CURLcode smtp_quit(struct connectdata *conn)
1489 CURLcode result = CURLE_OK;
1491 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "QUIT");
1494 state(conn, SMTP_QUIT);
1496 result = smtp_easy_statemach(conn);
1501 /***********************************************************************
1505 * Disconnect from an SMTP server. Cleanup protocol-specific per-connection
1506 * resources. BLOCKING.
1508 static CURLcode smtp_disconnect(struct connectdata *conn,
1509 bool dead_connection)
1511 struct smtp_conn *smtpc = &conn->proto.smtpc;
1513 /* We cannot send quit unconditionally. If this connection is stale or
1514 bad in any way, sending quit and waiting around here will make the
1515 disconnect wait in vain and cause more problems than we need to.
1518 /* The SMTP session may or may not have been allocated/setup at this
1520 if(!dead_connection && smtpc->pp.conn)
1521 (void)smtp_quit(conn); /* ignore errors on the LOGOUT */
1523 Curl_pp_disconnect(&smtpc->pp);
1526 /* Cleanup the ntlm structure */
1527 if(smtpc->authused == SMTP_AUTH_NTLM) {
1528 Curl_ntlm_sspi_cleanup(&conn->ntlm);
1532 /* This won't already be freed in some error cases */
1533 Curl_safefree(smtpc->domain);
1534 smtpc->domain = NULL;
1539 /* call this when the DO phase has completed */
1540 static CURLcode smtp_dophase_done(struct connectdata *conn,
1543 struct FTP *smtp = conn->data->state.proto.smtp;
1544 struct smtp_conn *smtpc = &conn->proto.smtpc;
1547 if(smtp->transfer != FTPTRANSFER_BODY)
1548 /* no data to transfer */
1549 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1551 free(smtpc->domain);
1552 smtpc->domain = NULL;
1557 /* called from multi.c while DOing */
1558 static CURLcode smtp_doing(struct connectdata *conn,
1562 result = smtp_multi_statemach(conn, dophase_done);
1565 result = smtp_dophase_done(conn, FALSE /* not connected */);
1567 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1573 /***********************************************************************
1575 * smtp_regular_transfer()
1577 * The input argument is already checked for validity.
1579 * Performs all commands done before a regular transfer between a local and a
1583 CURLcode smtp_regular_transfer(struct connectdata *conn,
1586 CURLcode result = CURLE_OK;
1587 bool connected = FALSE;
1588 struct SessionHandle *data = conn->data;
1589 data->req.size = -1; /* make sure this is unknown at this point */
1591 Curl_pgrsSetUploadCounter(data, 0);
1592 Curl_pgrsSetDownloadCounter(data, 0);
1593 Curl_pgrsSetUploadSize(data, 0);
1594 Curl_pgrsSetDownloadSize(data, 0);
1596 result = smtp_perform(conn,
1597 &connected, /* have we connected after PASV/PORT */
1598 dophase_done); /* all commands in the DO-phase done? */
1600 if(CURLE_OK == result) {
1603 /* the DO phase has not completed yet */
1606 result = smtp_dophase_done(conn, connected);
1614 static CURLcode smtp_setup_connection(struct connectdata *conn)
1616 struct SessionHandle *data = conn->data;
1618 if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
1619 /* Unless we have asked to tunnel smtp operations through the proxy, we
1620 switch and use HTTP operations only */
1621 #ifndef CURL_DISABLE_HTTP
1622 if(conn->handler == &Curl_handler_smtp)
1623 conn->handler = &Curl_handler_smtp_proxy;
1626 conn->handler = &Curl_handler_smtps_proxy;
1628 failf(data, "SMTPS not supported!");
1629 return CURLE_UNSUPPORTED_PROTOCOL;
1633 * We explicitly mark this connection as persistent here as we're doing
1634 * SMTP over HTTP and thus we accidentally avoid setting this value
1637 conn->bits.close = FALSE;
1639 failf(data, "SMTP over http proxy requires HTTP support built-in!");
1640 return CURLE_UNSUPPORTED_PROTOCOL;
1644 data->state.path++; /* don't include the initial slash */
1649 CURLcode Curl_smtp_escape_eob(struct connectdata *conn, ssize_t nread)
1651 /* When sending SMTP payload, we must detect CRLF.CRLF sequences in
1652 * the data and make sure it is sent as CRLF..CRLF instead, as
1653 * otherwise it will wrongly be detected as end of data by the server.
1657 struct smtp_conn *smtpc = &conn->proto.smtpc;
1658 struct SessionHandle *data = conn->data;
1660 if(data->state.scratch == NULL)
1661 data->state.scratch = malloc(2 * BUFSIZE);
1662 if(data->state.scratch == NULL) {
1663 failf (data, "Failed to alloc scratch buffer!");
1664 return CURLE_OUT_OF_MEMORY;
1666 /* This loop can be improved by some kind of Boyer-Moore style of
1667 approach but that is saved for later... */
1668 for(i = 0, si = 0; i < nread; i++) {
1670 if(SMTP_EOB[smtpc->eob] == data->req.upload_fromhere[i])
1672 else if(smtpc->eob) {
1673 /* previously a substring matched, output that first */
1674 memcpy(&data->state.scratch[si], SMTP_EOB, smtpc->eob);
1677 /* then compare the first byte */
1678 if(SMTP_EOB[0] == data->req.upload_fromhere[i])
1684 if(SMTP_EOB_LEN == smtpc->eob) {
1685 /* It matched, copy the replacement data to the target buffer
1686 instead. Note that the replacement does not contain the
1687 trailing CRLF but we instead continue to match on that one
1688 to deal with repeated sequences. Like CRLF.CRLF.CRLF etc
1690 memcpy(&data->state.scratch[si], SMTP_EOB_REPL,
1692 si += SMTP_EOB_REPL_LEN;
1693 smtpc->eob = 2; /* start over at two bytes */
1695 else if(!smtpc->eob)
1696 data->state.scratch[si++] = data->req.upload_fromhere[i];
1701 /* only use the new buffer if we replaced something */
1704 /* upload from the new (replaced) buffer instead */
1705 data->req.upload_fromhere = data->state.scratch;
1707 /* set the new amount too */
1708 data->req.upload_present = nread;
1714 #endif /* CURL_DISABLE_SMTP */