1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2010, 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
42 #ifdef HAVE_SYS_SOCKET_H
43 #include <sys/socket.h>
45 #ifdef HAVE_NETINET_IN_H
46 #include <netinet/in.h>
48 #ifdef HAVE_ARPA_INET_H
49 #include <arpa/inet.h>
52 #include <sys/utsname.h>
62 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
64 #define in_addr_t unsigned long
67 #include <curl/curl.h>
70 #include "easyif.h" /* for Curl_convert_... prototypes */
77 #include "http.h" /* for HTTP proxy tunnel stuff */
81 #include "strtoofft.h"
90 #include "strtoofft.h"
91 #include "curl_base64.h"
93 #include "curl_hmac.h"
94 #include "curl_gethostname.h"
97 #define _MPRINTF_REPLACE /* use our functions only */
98 #include <curl/mprintf.h>
100 #include "curl_memory.h"
101 /* The last #include file should be: */
102 #include "memdebug.h"
104 /* Local API functions */
105 static CURLcode smtp_regular_transfer(struct connectdata *conn, bool *done);
106 static CURLcode smtp_do(struct connectdata *conn, bool *done);
107 static CURLcode smtp_done(struct connectdata *conn,
108 CURLcode, bool premature);
109 static CURLcode smtp_connect(struct connectdata *conn, bool *done);
110 static CURLcode smtp_disconnect(struct connectdata *conn, bool dead_connection);
111 static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done);
112 static int smtp_getsock(struct connectdata *conn,
113 curl_socket_t *socks,
115 static CURLcode smtp_doing(struct connectdata *conn,
117 static CURLcode smtp_setup_connection(struct connectdata * conn);
121 * SMTP protocol handler.
124 const struct Curl_handler Curl_handler_smtp = {
126 smtp_setup_connection, /* setup_connection */
128 smtp_done, /* done */
129 ZERO_NULL, /* do_more */
130 smtp_connect, /* connect_it */
131 smtp_multi_statemach, /* connecting */
132 smtp_doing, /* doing */
133 smtp_getsock, /* proto_getsock */
134 smtp_getsock, /* doing_getsock */
135 ZERO_NULL, /* perform_getsock */
136 smtp_disconnect, /* disconnect */
137 PORT_SMTP, /* defport */
138 PROT_SMTP /* protocol */
144 * SMTPS protocol handler.
147 const struct Curl_handler Curl_handler_smtps = {
148 "SMTPS", /* scheme */
149 smtp_setup_connection, /* setup_connection */
151 smtp_done, /* done */
152 ZERO_NULL, /* do_more */
153 smtp_connect, /* connect_it */
154 smtp_multi_statemach, /* connecting */
155 smtp_doing, /* doing */
156 smtp_getsock, /* proto_getsock */
157 smtp_getsock, /* doing_getsock */
158 ZERO_NULL, /* perform_getsock */
159 smtp_disconnect, /* disconnect */
160 PORT_SMTPS, /* defport */
161 PROT_SMTP | PROT_SMTPS | PROT_SSL /* protocol */
165 #ifndef CURL_DISABLE_HTTP
167 * HTTP-proxyed SMTP protocol handler.
170 static const struct Curl_handler Curl_handler_smtp_proxy = {
172 ZERO_NULL, /* setup_connection */
173 Curl_http, /* do_it */
174 Curl_http_done, /* done */
175 ZERO_NULL, /* do_more */
176 ZERO_NULL, /* connect_it */
177 ZERO_NULL, /* connecting */
178 ZERO_NULL, /* doing */
179 ZERO_NULL, /* proto_getsock */
180 ZERO_NULL, /* doing_getsock */
181 ZERO_NULL, /* perform_getsock */
182 ZERO_NULL, /* disconnect */
183 PORT_SMTP, /* defport */
184 PROT_HTTP /* protocol */
190 * HTTP-proxyed SMTPS protocol handler.
193 static const struct Curl_handler Curl_handler_smtps_proxy = {
194 "SMTPS", /* scheme */
195 ZERO_NULL, /* setup_connection */
196 Curl_http, /* do_it */
197 Curl_http_done, /* done */
198 ZERO_NULL, /* do_more */
199 ZERO_NULL, /* connect_it */
200 ZERO_NULL, /* connecting */
201 ZERO_NULL, /* doing */
202 ZERO_NULL, /* proto_getsock */
203 ZERO_NULL, /* doing_getsock */
204 ZERO_NULL, /* perform_getsock */
205 ZERO_NULL, /* disconnect */
206 PORT_SMTPS, /* defport */
207 PROT_HTTP /* protocol */
213 /* Function that checks for an ending smtp status code at the start of the
215 As a side effect, it also flags allowed authentication mechanisms according
216 to EHLO AUTH response. */
217 static int smtp_endofresp(struct pingpong *pp, int *resp)
219 char *line = pp->linestart_resp;
220 size_t len = pp->nread_resp;
221 struct connectdata *conn = pp->conn;
222 struct smtp_conn *smtpc = &conn->proto.smtpc;
226 if(len < 4 || !ISDIGIT(line[0]) || !ISDIGIT(line[1]) || !ISDIGIT(line[2]))
227 return FALSE; /* Nothing for us. */
229 if((result = (line[3] == ' ')) != 0)
230 *resp = curlx_sltosi(strtol(line, NULL, 10));
235 if(smtpc->state == SMTP_EHLO && len >= 5 && !memcmp(line, "AUTH ", 5)) {
241 (*line == ' ' || *line == '\t' ||
242 *line == '\r' || *line == '\n')) {
250 for (wordlen = 0; wordlen < len && line[wordlen] != ' ' &&
251 line[wordlen] != '\t' && line[wordlen] != '\r' &&
252 line[wordlen] != '\n';)
255 if(wordlen == 5 && !memcmp(line, "LOGIN", 5))
256 smtpc->authmechs |= SMTP_AUTH_LOGIN;
257 else if(wordlen == 5 && !memcmp(line, "PLAIN", 5))
258 smtpc->authmechs |= SMTP_AUTH_PLAIN;
259 else if(wordlen == 8 && !memcmp(line, "CRAM-MD5", 8))
260 smtpc->authmechs |= SMTP_AUTH_CRAM_MD5;
261 else if(wordlen == 10 && !memcmp(line, "DIGEST-MD5", 10))
262 smtpc->authmechs |= SMTP_AUTH_DIGEST_MD5;
263 else if(wordlen == 6 && !memcmp(line, "GSSAPI", 6))
264 smtpc->authmechs |= SMTP_AUTH_GSSAPI;
265 else if(wordlen == 8 && !memcmp(line, "EXTERNAL", 8))
266 smtpc->authmechs |= SMTP_AUTH_EXTERNAL;
276 /* This is the ONLY way to change SMTP state! */
277 static void state(struct connectdata *conn,
280 struct smtp_conn *smtpc = &conn->proto.smtpc;
281 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
282 /* for debug purposes */
283 static const char * const names[]={
301 if(smtpc->state != newstate)
302 infof(conn->data, "SMTP %p state change from %s to %s\n",
303 smtpc, names[smtpc->state], names[newstate]);
305 smtpc->state = newstate;
308 static CURLcode smtp_state_ehlo(struct connectdata *conn)
311 struct smtp_conn *smtpc = &conn->proto.smtpc;
313 smtpc->authmechs = 0; /* No known authentication mechanisms yet. */
316 result = Curl_pp_sendf(&smtpc->pp, "EHLO %s", smtpc->domain);
321 state(conn, SMTP_EHLO);
325 static CURLcode smtp_state_helo(struct connectdata *conn)
328 struct smtp_conn *smtpc = &conn->proto.smtpc;
331 result = Curl_pp_sendf(&smtpc->pp, "HELO %s", smtpc->domain);
336 state(conn, SMTP_HELO);
340 static size_t smtp_auth_plain_data(struct connectdata * conn, char * * outptr)
342 char plainauth[2 * MAX_CURL_USER_LENGTH + MAX_CURL_PASSWORD_LENGTH];
346 ulen = strlen(conn->user);
347 plen = strlen(conn->passwd);
349 if(2 * ulen + plen + 2 > sizeof plainauth)
352 memcpy(plainauth, conn->user, ulen);
353 plainauth[ulen] = '\0';
354 memcpy(plainauth + ulen + 1, conn->user, ulen);
355 plainauth[2 * ulen + 1] = '\0';
356 memcpy(plainauth + 2 * ulen + 2, conn->passwd, plen);
357 return Curl_base64_encode(conn->data, plainauth, 2 * ulen + plen + 2, outptr);
360 static size_t smtp_auth_login_user(struct connectdata * conn, char * * outptr)
364 ulen = strlen(conn->user);
367 *outptr = strdup("=");
368 return *outptr? 1: 0;
371 return Curl_base64_encode(conn->data, conn->user, ulen, outptr);
374 static CURLcode smtp_authenticate(struct connectdata *conn)
376 CURLcode result = CURLE_OK;
377 struct smtp_conn *smtpc = &conn->proto.smtpc;
384 if(!conn->bits.user_passwd)
385 state(conn, SMTP_STOP); /* End of connect phase. */
387 initresp = (char *) NULL;
390 /* Check supported authentication mechanisms by decreasing order of
392 mech = (const char *) NULL; /* Avoid compiler warnings. */
396 #ifndef CURL_DISABLE_CRYPTO_AUTH
397 if(smtpc->authmechs & SMTP_AUTH_CRAM_MD5) {
399 state1 = SMTP_AUTHCRAM;
403 if(smtpc->authmechs & SMTP_AUTH_PLAIN) {
405 state1 = SMTP_AUTHPLAIN;
407 l = smtp_auth_plain_data(conn, &initresp);
409 else if(smtpc->authmechs & SMTP_AUTH_LOGIN) {
411 state1 = SMTP_AUTHLOGIN;
412 state2 = SMTP_AUTHPASSWD;
413 l = smtp_auth_login_user(conn, &initresp);
416 infof(conn->data, "No known auth mechanisms supported!\n");
417 result = CURLE_LOGIN_DENIED; /* Other mechanisms not supported. */
422 result = CURLE_OUT_OF_MEMORY;
424 l + strlen(mech) <= 512 - 8) { /* AUTH <mech> ...<crlf> */
425 result = Curl_pp_sendf(&smtpc->pp, "AUTH %s %s", mech, initresp);
432 Curl_safefree(initresp);
434 result = Curl_pp_sendf(&smtpc->pp, "AUTH %s", mech);
445 /* For the SMTP "protocol connect" and "doing" phases only */
446 static int smtp_getsock(struct connectdata *conn,
447 curl_socket_t *socks,
450 return Curl_pp_getsock(&conn->proto.smtpc.pp, socks, numsocks);
453 /* for STARTTLS responses */
454 static CURLcode smtp_state_starttls_resp(struct connectdata *conn,
458 CURLcode result = CURLE_OK;
459 struct SessionHandle *data = conn->data;
460 (void)instate; /* no use for this yet */
462 if(smtpcode != 220) {
463 if(data->set.ftp_ssl != CURLUSESSL_TRY) {
464 failf(data, "STARTTLS denied. %c", smtpcode);
465 result = CURLE_LOGIN_DENIED;
468 result = smtp_authenticate(conn);
471 /* Curl_ssl_connect is BLOCKING */
472 result = Curl_ssl_connect(conn, FIRSTSOCKET);
473 if(CURLE_OK == result) {
474 conn->protocol |= PROT_SMTPS;
475 result = smtp_state_ehlo(conn);
481 /* for EHLO responses */
482 static CURLcode smtp_state_ehlo_resp(struct connectdata *conn,
486 CURLcode result = CURLE_OK;
487 struct SessionHandle *data = conn->data;
489 (void)instate; /* no use for this yet */
491 if(smtpcode/100 != 2) {
492 if((data->set.ftp_ssl <= CURLUSESSL_TRY || conn->ssl[FIRSTSOCKET].use) &&
493 !conn->bits.user_passwd)
494 result = smtp_state_helo(conn);
496 failf(data, "Access denied: %d", smtpcode);
497 result = CURLE_LOGIN_DENIED;
500 else if(data->set.ftp_ssl && !conn->ssl[FIRSTSOCKET].use) {
501 /* We don't have a SSL/TLS connection yet, but SSL is requested. Switch
502 to TLS connection now */
503 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "STARTTLS");
504 state(conn, SMTP_STARTTLS);
507 result = smtp_authenticate(conn);
512 /* for HELO responses */
513 static CURLcode smtp_state_helo_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/100 != 2) {
523 failf(data, "Access denied: %d", smtpcode);
524 result = CURLE_LOGIN_DENIED;
527 /* end the connect phase */
528 state(conn, SMTP_STOP);
533 /* for AUTH PLAIN (without initial response) responses */
534 static CURLcode smtp_state_authplain_resp(struct connectdata *conn,
538 CURLcode result = CURLE_OK;
539 struct SessionHandle *data = conn->data;
543 (void)instate; /* no use for this yet */
545 if(smtpcode != 334) {
546 failf(data, "Access denied: %d", smtpcode);
547 result = CURLE_LOGIN_DENIED;
550 l = smtp_auth_plain_data(conn, &plainauth);
553 result = CURLE_OUT_OF_MEMORY;
555 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", plainauth);
559 state(conn, SMTP_AUTH);
566 /* for AUTH LOGIN (without initial response) responses */
567 static CURLcode smtp_state_authlogin_resp(struct connectdata *conn,
571 CURLcode result = CURLE_OK;
572 struct SessionHandle *data = conn->data;
576 (void)instate; /* no use for this yet */
578 if(smtpcode != 334) {
579 failf(data, "Access denied: %d", smtpcode);
580 result = CURLE_LOGIN_DENIED;
583 l = smtp_auth_login_user(conn, &authuser);
586 result = CURLE_OUT_OF_MEMORY;
588 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", authuser);
592 state(conn, SMTP_AUTHPASSWD);
599 /* for responses to user entry of AUTH LOGIN. */
600 static CURLcode smtp_state_authpasswd_resp(struct connectdata *conn,
604 CURLcode result = CURLE_OK;
605 struct SessionHandle *data = conn->data;
610 (void)instate; /* no use for this yet */
612 if(smtpcode != 334) {
613 failf(data, "Access denied: %d", smtpcode);
614 result = CURLE_LOGIN_DENIED;
617 plen = strlen(conn->passwd);
620 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "=");
622 l = Curl_base64_encode(data, conn->passwd, plen, &authpasswd);
625 result = CURLE_OUT_OF_MEMORY;
627 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", authpasswd);
631 state(conn, SMTP_AUTH);
639 #ifndef CURL_DISABLE_CRYPTO_AUTH
641 /* for AUTH CRAM-MD5 responses. */
642 static CURLcode smtp_state_authcram_resp(struct connectdata *conn,
646 CURLcode result = CURLE_OK;
647 struct SessionHandle *data = conn->data;
648 char * chlg64 = data->state.buffer;
649 unsigned char * chlg;
654 unsigned char digest[16];
655 char reply[MAX_CURL_USER_LENGTH + 32 /* 2 * size of MD5 digest */ + 1];
657 (void)instate; /* no use for this yet */
659 if(smtpcode != 334) {
660 failf(data, "Access denied: %d", smtpcode);
661 return CURLE_LOGIN_DENIED;
664 /* Get the challenge. */
665 for (chlg64 += 4; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
668 chlg = (unsigned char *) NULL;
672 for (l = strlen(chlg64); l--;)
673 if(chlg64[l] != '\r' && chlg64[l] != '\n' && chlg64[l] != ' ' &&
680 chlglen = Curl_base64_decode(chlg64, &chlg);
682 return CURLE_OUT_OF_MEMORY;
686 /* Compute digest. */
687 ctxt = Curl_HMAC_init(Curl_HMAC_MD5,
688 (const unsigned char *) conn->passwd,
689 (unsigned int)(strlen(conn->passwd)));
695 return CURLE_OUT_OF_MEMORY;
699 Curl_HMAC_update(ctxt, chlg, (unsigned int)(chlglen));
704 Curl_HMAC_final(ctxt, digest);
706 /* Prepare the reply. */
707 snprintf(reply, sizeof reply,
708 "%s %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
709 conn->user, digest[0], digest[1], digest[2], digest[3], digest[4], digest[5],
710 digest[6], digest[7], digest[8], digest[9], digest[10], digest[11],
711 digest[12], digest[13], digest[14], digest[15]);
713 /* Encode it to base64 and send it. */
714 l = Curl_base64_encode(data, reply, 0, &rplyb64);
717 result = CURLE_OUT_OF_MEMORY;
719 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", rplyb64);
723 state(conn, SMTP_AUTH);
731 /* for final responses to AUTH sequences. */
732 static CURLcode smtp_state_auth_resp(struct connectdata *conn,
736 CURLcode result = CURLE_OK;
737 struct SessionHandle *data = conn->data;
739 (void)instate; /* no use for this yet */
741 if(smtpcode != 235) {
742 failf(data, "Authentication failed: %d", smtpcode);
743 result = CURLE_LOGIN_DENIED;
746 state(conn, SMTP_STOP); /* End of connect phase. */
751 /* start the DO phase */
752 static CURLcode smtp_mail(struct connectdata *conn)
754 CURLcode result = CURLE_OK;
755 struct SessionHandle *data = conn->data;
758 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "MAIL FROM:%s",
759 data->set.str[STRING_MAIL_FROM]);
763 state(conn, SMTP_MAIL);
767 static CURLcode smtp_rcpt_to(struct connectdata *conn)
769 CURLcode result = CURLE_OK;
770 struct smtp_conn *smtpc = &conn->proto.smtpc;
774 if(smtpc->rcpt->data[0] == '<')
775 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:%s",
778 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:<%s>",
781 state(conn, SMTP_RCPT);
786 /* for MAIL responses */
787 static CURLcode smtp_state_mail_resp(struct connectdata *conn,
791 CURLcode result = CURLE_OK;
792 struct SessionHandle *data = conn->data;
793 (void)instate; /* no use for this yet */
795 if(smtpcode/100 != 2) {
796 failf(data, "Access denied: %d", smtpcode);
797 result = CURLE_LOGIN_DENIED;
798 state(conn, SMTP_STOP);
801 struct smtp_conn *smtpc = &conn->proto.smtpc;
802 smtpc->rcpt = data->set.mail_rcpt;
804 result = smtp_rcpt_to(conn);
810 /* for RCPT responses */
811 static CURLcode smtp_state_rcpt_resp(struct connectdata *conn,
815 CURLcode result = CURLE_OK;
816 struct SessionHandle *data = conn->data;
817 (void)instate; /* no use for this yet */
819 if(smtpcode/100 != 2) {
820 failf(data, "Access denied: %d", smtpcode);
821 result = CURLE_LOGIN_DENIED;
822 state(conn, SMTP_STOP);
825 struct smtp_conn *smtpc = &conn->proto.smtpc;
828 smtpc->rcpt = smtpc->rcpt->next;
829 result = smtp_rcpt_to(conn);
831 /* if we failed or still is in RCPT sending, return */
832 if(result || smtpc->rcpt)
837 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "DATA");
841 state(conn, SMTP_DATA);
846 /* for the DATA response */
847 static CURLcode smtp_state_data_resp(struct connectdata *conn,
851 struct SessionHandle *data = conn->data;
852 struct FTP *smtp = data->state.proto.smtp;
854 (void)instate; /* no use for this yet */
856 if(smtpcode != 354) {
857 state(conn, SMTP_STOP);
858 return CURLE_RECV_ERROR;
862 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, /* no download */
863 FIRSTSOCKET, smtp->bytecountp);
865 state(conn, SMTP_STOP);
869 /* for the POSTDATA response, which is received after the entire DATA
870 part has been sent off to the server */
871 static CURLcode smtp_state_postdata_resp(struct connectdata *conn,
875 CURLcode result = CURLE_OK;
877 (void)instate; /* no use for this yet */
880 result = CURLE_RECV_ERROR;
882 state(conn, SMTP_STOP);
886 static CURLcode smtp_statemach_act(struct connectdata *conn)
889 curl_socket_t sock = conn->sock[FIRSTSOCKET];
890 struct SessionHandle *data=conn->data;
892 struct smtp_conn *smtpc = &conn->proto.smtpc;
893 struct pingpong *pp = &smtpc->pp;
897 /* we have a piece of a command still left to send */
898 return Curl_pp_flushsend(pp);
900 /* we read a piece of response */
901 result = Curl_pp_readresp(sock, pp, &smtpcode, &nread);
906 /* we have now received a full SMTP server response */
907 switch(smtpc->state) {
908 case SMTP_SERVERGREET:
909 if(smtpcode/100 != 2) {
910 failf(data, "Got unexpected smtp-server response: %d", smtpcode);
911 return CURLE_FTP_WEIRD_SERVER_REPLY;
914 result = smtp_state_ehlo(conn);
920 result = smtp_state_ehlo_resp(conn, smtpcode, smtpc->state);
924 result = smtp_state_helo_resp(conn, smtpcode, smtpc->state);
928 result = smtp_state_starttls_resp(conn, smtpcode, smtpc->state);
932 result = smtp_state_authplain_resp(conn, smtpcode, smtpc->state);
936 result = smtp_state_authlogin_resp(conn, smtpcode, smtpc->state);
939 case SMTP_AUTHPASSWD:
940 result = smtp_state_authpasswd_resp(conn, smtpcode, smtpc->state);
943 #ifndef CURL_DISABLE_CRYPTO_AUTH
945 result = smtp_state_authcram_resp(conn, smtpcode, smtpc->state);
950 result = smtp_state_auth_resp(conn, smtpcode, smtpc->state);
954 result = smtp_state_mail_resp(conn, smtpcode, smtpc->state);
958 result = smtp_state_rcpt_resp(conn, smtpcode, smtpc->state);
962 result = smtp_state_data_resp(conn, smtpcode, smtpc->state);
966 result = smtp_state_postdata_resp(conn, smtpcode, smtpc->state);
970 /* fallthrough, just stop! */
973 state(conn, SMTP_STOP);
980 /* called repeatedly until done from multi.c */
981 static CURLcode smtp_multi_statemach(struct connectdata *conn,
984 struct smtp_conn *smtpc = &conn->proto.smtpc;
985 CURLcode result = Curl_pp_multi_statemach(&smtpc->pp);
987 *done = (bool)(smtpc->state == SMTP_STOP);
992 static CURLcode smtp_easy_statemach(struct connectdata *conn)
994 struct smtp_conn *smtpc = &conn->proto.smtpc;
995 struct pingpong *pp = &smtpc->pp;
996 CURLcode result = CURLE_OK;
998 while(smtpc->state != SMTP_STOP) {
999 result = Curl_pp_easy_statemach(pp);
1008 * Allocate and initialize the struct SMTP for the current SessionHandle. If
1011 static CURLcode smtp_init(struct connectdata *conn)
1013 struct SessionHandle *data = conn->data;
1014 struct FTP *smtp = data->state.proto.smtp;
1016 smtp = data->state.proto.smtp = calloc(sizeof(struct FTP), 1);
1018 return CURLE_OUT_OF_MEMORY;
1021 /* get some initial data into the smtp struct */
1022 smtp->bytecountp = &data->req.bytecount;
1024 /* No need to duplicate user+password, the connectdata struct won't change
1025 during a session, but we re-init them here since on subsequent inits
1026 since the conn struct may have changed or been replaced.
1028 smtp->user = conn->user;
1029 smtp->passwd = conn->passwd;
1035 * smtp_connect() should do everything that is to be considered a part of
1036 * the connection phase.
1038 * The variable 'done' points to will be TRUE if the protocol-layer connect
1039 * phase is done when this function returns, or FALSE is not. When called as
1040 * a part of the easy interface, it will always be TRUE.
1042 static CURLcode smtp_connect(struct connectdata *conn,
1043 bool *done) /* see description above */
1046 struct smtp_conn *smtpc = &conn->proto.smtpc;
1047 struct SessionHandle *data=conn->data;
1048 struct pingpong *pp=&smtpc->pp;
1049 const char *path = conn->data->state.path;
1051 char localhost[1024 + 1];
1053 *done = FALSE; /* default to not done yet */
1055 /* If there already is a protocol-specific struct allocated for this
1056 sessionhandle, deal with it */
1057 Curl_reset_reqproto(conn);
1059 result = smtp_init(conn);
1060 if(CURLE_OK != result)
1063 /* We always support persistant connections on smtp */
1064 conn->bits.close = FALSE;
1066 pp->response_time = RESP_TIMEOUT; /* set default response time-out */
1067 pp->statemach_act = smtp_statemach_act;
1068 pp->endofresp = smtp_endofresp;
1071 #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_PROXY)
1072 if(conn->bits.tunnel_proxy && conn->bits.httpproxy) {
1073 /* for SMTP over HTTP proxy */
1074 struct HTTP http_proxy;
1075 struct FTP *smtp_save;
1078 /* We want "seamless" SMTP operations through HTTP proxy tunnel */
1080 /* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the member
1081 * conn->proto.http; we want SMTP through HTTP and we have to change the
1082 * member temporarily for connecting to the HTTP proxy. After
1083 * Curl_proxyCONNECT we have to set back the member to the original struct
1086 smtp_save = data->state.proto.smtp;
1087 memset(&http_proxy, 0, sizeof(http_proxy));
1088 data->state.proto.http = &http_proxy;
1090 result = Curl_proxyCONNECT(conn, FIRSTSOCKET,
1091 conn->host.name, conn->remote_port);
1093 data->state.proto.smtp = smtp_save;
1095 if(CURLE_OK != result)
1098 #endif /* !CURL_DISABLE_HTTP && !CURL_DISABLE_PROXY */
1100 if(conn->protocol & PROT_SMTPS) {
1102 /* SMTPS is simply smtp with SSL for the control channel */
1103 /* now, perform the SSL initialization for this socket */
1104 result = Curl_ssl_connect(conn, FIRSTSOCKET);
1109 Curl_pp_init(pp); /* init the response reader stuff */
1111 pp->response_time = RESP_TIMEOUT; /* set default response time-out */
1112 pp->statemach_act = smtp_statemach_act;
1113 pp->endofresp = smtp_endofresp;
1117 if(!Curl_gethostname(localhost, sizeof localhost))
1123 /* url decode the path and use it as domain with EHLO */
1124 smtpc->domain = curl_easy_unescape(conn->data, path, 0, &len);
1126 return CURLE_OUT_OF_MEMORY;
1128 /* When we connect, we start in the state where we await the server greeting
1130 state(conn, SMTP_SERVERGREET);
1132 if(data->state.used_interface == Curl_if_multi)
1133 result = smtp_multi_statemach(conn, done);
1135 result = smtp_easy_statemach(conn);
1143 /***********************************************************************
1147 * The DONE function. This does what needs to be done after a single DO has
1150 * Input argument is already checked for validity.
1152 static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
1155 struct SessionHandle *data = conn->data;
1156 struct FTP *smtp = data->state.proto.smtp;
1157 CURLcode result=CURLE_OK;
1158 ssize_t bytes_written;
1162 /* When the easy handle is removed from the multi while libcurl is still
1163 * trying to resolve the host name, it seems that the smtp struct is not
1164 * yet initialized, but the removal action calls Curl_done() which calls
1165 * this function. So we simply return success if no smtp pointer is set.
1170 conn->bits.close = TRUE; /* marked for closure */
1171 result = status; /* use the already set error code */
1174 /* TODO: make this work even when the socket is EWOULDBLOCK in this call! */
1176 /* write to socket (send away data) */
1177 result = Curl_write(conn,
1178 conn->writesockfd, /* socket to send to */
1179 SMTP_EOB, /* buffer pointer */
1180 SMTP_EOB_LEN, /* buffer size */
1181 &bytes_written); /* actually sent away */
1184 if(status == CURLE_OK) {
1185 struct smtp_conn *smtpc = &conn->proto.smtpc;
1186 struct pingpong *pp= &smtpc->pp;
1187 pp->response = Curl_tvnow(); /* timeout relative now */
1189 state(conn, SMTP_POSTDATA);
1190 /* run the state-machine
1192 TODO: when the multi interface is used, this _really_ should be using
1193 the smtp_multi_statemach function but we have no general support for
1194 non-blocking DONE operations, not in the multi state machine and with
1195 Curl_done() invokes on several places in the code!
1197 result = smtp_easy_statemach(conn);
1200 /* clear these for next connection */
1201 smtp->transfer = FTPTRANSFER_BODY;
1206 /***********************************************************************
1210 * This is the actual DO function for SMTP. Get a file/directory according to
1211 * the options previously setup.
1215 CURLcode smtp_perform(struct connectdata *conn,
1216 bool *connected, /* connect status after PASV / PORT */
1219 /* this is SMTP and no proxy */
1220 CURLcode result=CURLE_OK;
1222 DEBUGF(infof(conn->data, "DO phase starts\n"));
1224 if(conn->data->set.opt_no_body) {
1225 /* requested no body means no transfer... */
1226 struct FTP *smtp = conn->data->state.proto.smtp;
1227 smtp->transfer = FTPTRANSFER_INFO;
1230 *dophase_done = FALSE; /* not done yet */
1232 /* start the first command in the DO phase */
1233 result = smtp_mail(conn);
1237 /* run the state-machine */
1238 if(conn->data->state.used_interface == Curl_if_multi)
1239 result = smtp_multi_statemach(conn, dophase_done);
1241 result = smtp_easy_statemach(conn);
1242 *dophase_done = TRUE; /* with the easy interface we are done here */
1244 *connected = conn->bits.tcpconnect;
1247 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1252 /***********************************************************************
1256 * This function is registered as 'curl_do' function. It decodes the path
1257 * parts etc as a wrapper to the actual DO function (smtp_perform).
1259 * The input argument is already checked for validity.
1261 static CURLcode smtp_do(struct connectdata *conn, bool *done)
1263 CURLcode retcode = CURLE_OK;
1265 *done = FALSE; /* default to false */
1268 Since connections can be re-used between SessionHandles, this might be a
1269 connection already existing but on a fresh SessionHandle struct so we must
1270 make sure we have a good 'struct SMTP' to play with. For new connections,
1271 the struct SMTP is allocated and setup in the smtp_connect() function.
1273 Curl_reset_reqproto(conn);
1274 retcode = smtp_init(conn);
1278 retcode = smtp_regular_transfer(conn, done);
1283 /***********************************************************************
1287 * This should be called before calling sclose(). We should then wait for the
1288 * response from the server before returning. The calling code should then try
1289 * to close the connection.
1292 static CURLcode smtp_quit(struct connectdata *conn)
1294 CURLcode result = CURLE_OK;
1296 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "QUIT");
1299 state(conn, SMTP_QUIT);
1301 result = smtp_easy_statemach(conn);
1306 /***********************************************************************
1310 * Disconnect from an SMTP server. Cleanup protocol-specific per-connection
1311 * resources. BLOCKING.
1313 static CURLcode smtp_disconnect(struct connectdata *conn, bool dead_connection)
1315 struct smtp_conn *smtpc= &conn->proto.smtpc;
1317 /* We cannot send quit unconditionally. If this connection is stale or
1318 bad in any way, sending quit and waiting around here will make the
1319 disconnect wait in vain and cause more problems than we need to.
1322 /* The SMTP session may or may not have been allocated/setup at this
1324 if(!dead_connection && smtpc->pp.conn)
1325 (void)smtp_quit(conn); /* ignore errors on the LOGOUT */
1327 Curl_pp_disconnect(&smtpc->pp);
1329 /* This won't already be freed in some error cases */
1330 Curl_safefree(smtpc->domain);
1331 smtpc->domain = NULL;
1336 /* call this when the DO phase has completed */
1337 static CURLcode smtp_dophase_done(struct connectdata *conn,
1340 struct FTP *smtp = conn->data->state.proto.smtp;
1341 struct smtp_conn *smtpc= &conn->proto.smtpc;
1344 if(smtp->transfer != FTPTRANSFER_BODY)
1345 /* no data to transfer */
1346 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1348 free(smtpc->domain);
1349 smtpc->domain = NULL;
1354 /* called from multi.c while DOing */
1355 static CURLcode smtp_doing(struct connectdata *conn,
1359 result = smtp_multi_statemach(conn, dophase_done);
1362 result = smtp_dophase_done(conn, FALSE /* not connected */);
1364 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1369 /***********************************************************************
1371 * smtp_regular_transfer()
1373 * The input argument is already checked for validity.
1375 * Performs all commands done before a regular transfer between a local and a
1379 CURLcode smtp_regular_transfer(struct connectdata *conn,
1382 CURLcode result=CURLE_OK;
1383 bool connected=FALSE;
1384 struct SessionHandle *data = conn->data;
1385 data->req.size = -1; /* make sure this is unknown at this point */
1387 Curl_pgrsSetUploadCounter(data, 0);
1388 Curl_pgrsSetDownloadCounter(data, 0);
1389 Curl_pgrsSetUploadSize(data, 0);
1390 Curl_pgrsSetDownloadSize(data, 0);
1392 result = smtp_perform(conn,
1393 &connected, /* have we connected after PASV/PORT */
1394 dophase_done); /* all commands in the DO-phase done? */
1396 if(CURLE_OK == result) {
1399 /* the DO phase has not completed yet */
1402 result = smtp_dophase_done(conn, connected);
1410 static CURLcode smtp_setup_connection(struct connectdata * conn)
1412 struct SessionHandle *data = conn->data;
1414 if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
1415 /* Unless we have asked to tunnel smtp operations through the proxy, we
1416 switch and use HTTP operations only */
1417 #ifndef CURL_DISABLE_HTTP
1418 if(conn->handler == &Curl_handler_smtp)
1419 conn->handler = &Curl_handler_smtp_proxy;
1422 conn->handler = &Curl_handler_smtps_proxy;
1424 failf(data, "SMTPS not supported!");
1425 return CURLE_UNSUPPORTED_PROTOCOL;
1429 * We explicitly mark this connection as persistent here as we're doing
1430 * SMTP over HTTP and thus we accidentally avoid setting this value
1433 conn->bits.close = FALSE;
1435 failf(data, "SMTP over http proxy requires HTTP support built-in!");
1436 return CURLE_UNSUPPORTED_PROTOCOL;
1440 data->state.path++; /* don't include the initial slash */
1445 CURLcode Curl_smtp_escape_eob(struct connectdata *conn, ssize_t nread)
1447 /* When sending SMTP payload, we must detect CRLF.CRLF sequences in
1448 * the data and make sure it is sent as CRLF..CRLF instead, as
1449 * otherwise it will wrongly be detected as end of data by the server.
1453 struct smtp_conn *smtpc = &conn->proto.smtpc;
1454 struct SessionHandle *data = conn->data;
1456 if(data->state.scratch == NULL)
1457 data->state.scratch = malloc(2*BUFSIZE);
1458 if(data->state.scratch == NULL) {
1459 failf (data, "Failed to alloc scratch buffer!");
1460 return CURLE_OUT_OF_MEMORY;
1462 /* This loop can be improved by some kind of Boyer-Moore style of
1463 approach but that is saved for later... */
1464 for(i = 0, si = 0; i < nread; i++, si++) {
1465 ssize_t left = nread - i;
1467 if(left>= (ssize_t)(SMTP_EOB_LEN-smtpc->eob)) {
1468 if(!memcmp(SMTP_EOB+smtpc->eob, &data->req.upload_fromhere[i],
1469 SMTP_EOB_LEN-smtpc->eob)) {
1470 /* It matched, copy the replacement data to the target buffer
1471 instead. Note that the replacement does not contain the
1472 trailing CRLF but we instead continue to match on that one
1473 to deal with repeated sequences. Like CRLF.CRLF.CRLF etc
1475 memcpy(&data->state.scratch[si], SMTP_EOB_REPL,
1477 si+=SMTP_EOB_REPL_LEN-1; /* minus one since the for() increments
1479 i+=SMTP_EOB_LEN-smtpc->eob-1-2;
1480 smtpc->eob = 0; /* start over */
1484 else if(!memcmp(SMTP_EOB+smtpc->eob, &data->req.upload_fromhere[i],
1486 /* the last piece of the data matches the EOB so we can't send that
1487 until we know the rest of it */
1492 data->state.scratch[si] = data->req.upload_fromhere[i];
1496 /* only use the new buffer if we replaced something */
1499 /* upload from the new (replaced) buffer instead */
1500 data->req.upload_fromhere = data->state.scratch;
1502 /* set the new amount too */
1503 data->req.upload_present = nread;
1508 #endif /* CURL_DISABLE_SMTP */