1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2018, 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 https://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 * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism
28 * RFC4954 SMTP Authentication
29 * RFC5321 SMTP protocol
30 * RFC6749 OAuth 2.0 Authorization Framework
31 * Draft SMTP URL Interface <draft-earhart-url-smtp-00.txt>
32 * Draft LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt>
34 ***************************************************************************/
36 #include "curl_setup.h"
38 #ifndef CURL_DISABLE_SMTP
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>
69 #include "http.h" /* for HTTP proxy tunnel stuff */
73 #include "strtoofft.h"
75 #include "vtls/vtls.h"
81 #include "curl_gethostname.h"
82 #include "curl_sasl.h"
84 /* The last 3 #include files should be in this order */
85 #include "curl_printf.h"
86 #include "curl_memory.h"
89 /* Local API functions */
90 static CURLcode smtp_regular_transfer(struct connectdata *conn, bool *done);
91 static CURLcode smtp_do(struct connectdata *conn, bool *done);
92 static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
94 static CURLcode smtp_connect(struct connectdata *conn, bool *done);
95 static CURLcode smtp_disconnect(struct connectdata *conn, bool dead);
96 static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done);
97 static int smtp_getsock(struct connectdata *conn, curl_socket_t *socks,
99 static CURLcode smtp_doing(struct connectdata *conn, bool *dophase_done);
100 static CURLcode smtp_setup_connection(struct connectdata *conn);
101 static CURLcode smtp_parse_url_options(struct connectdata *conn);
102 static CURLcode smtp_parse_url_path(struct connectdata *conn);
103 static CURLcode smtp_parse_custom_request(struct connectdata *conn);
104 static CURLcode smtp_perform_auth(struct connectdata *conn, const char *mech,
105 const char *initresp);
106 static CURLcode smtp_continue_auth(struct connectdata *conn, const char *resp);
107 static void smtp_get_message(char *buffer, char **outptr);
110 * SMTP protocol handler.
113 const struct Curl_handler Curl_handler_smtp = {
115 smtp_setup_connection, /* setup_connection */
117 smtp_done, /* done */
118 ZERO_NULL, /* do_more */
119 smtp_connect, /* connect_it */
120 smtp_multi_statemach, /* connecting */
121 smtp_doing, /* doing */
122 smtp_getsock, /* proto_getsock */
123 smtp_getsock, /* doing_getsock */
124 ZERO_NULL, /* domore_getsock */
125 ZERO_NULL, /* perform_getsock */
126 smtp_disconnect, /* disconnect */
127 ZERO_NULL, /* readwrite */
128 ZERO_NULL, /* connection_check */
129 PORT_SMTP, /* defport */
130 CURLPROTO_SMTP, /* protocol */
131 PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY | /* flags */
137 * SMTPS protocol handler.
140 const struct Curl_handler Curl_handler_smtps = {
141 "SMTPS", /* scheme */
142 smtp_setup_connection, /* setup_connection */
144 smtp_done, /* done */
145 ZERO_NULL, /* do_more */
146 smtp_connect, /* connect_it */
147 smtp_multi_statemach, /* connecting */
148 smtp_doing, /* doing */
149 smtp_getsock, /* proto_getsock */
150 smtp_getsock, /* doing_getsock */
151 ZERO_NULL, /* domore_getsock */
152 ZERO_NULL, /* perform_getsock */
153 smtp_disconnect, /* disconnect */
154 ZERO_NULL, /* readwrite */
155 ZERO_NULL, /* connection_check */
156 PORT_SMTPS, /* defport */
157 CURLPROTO_SMTPS, /* protocol */
158 PROTOPT_CLOSEACTION | PROTOPT_SSL
159 | PROTOPT_NOURLQUERY | PROTOPT_URLOPTIONS /* flags */
163 /* SASL parameters for the smtp protocol */
164 static const struct SASLproto saslsmtp = {
165 "smtp", /* The service name */
166 334, /* Code received when continuation is expected */
167 235, /* Code to receive upon authentication success */
168 512 - 8, /* Maximum initial response length (no max) */
169 smtp_perform_auth, /* Send authentication command */
170 smtp_continue_auth, /* Send authentication continuation */
171 smtp_get_message /* Get SASL response message */
175 static void smtp_to_smtps(struct connectdata *conn)
177 /* Change the connection handler */
178 conn->handler = &Curl_handler_smtps;
180 /* Set the connection's upgraded to TLS flag */
181 conn->tls_upgraded = TRUE;
184 #define smtp_to_smtps(x) Curl_nop_stmt
187 /***********************************************************************
191 * Checks for an ending SMTP status code at the start of the given string, but
192 * also detects various capabilities from the EHLO response including the
193 * supported authentication mechanisms.
195 static bool smtp_endofresp(struct connectdata *conn, char *line, size_t len,
198 struct smtp_conn *smtpc = &conn->proto.smtpc;
202 if(len < 4 || !ISDIGIT(line[0]) || !ISDIGIT(line[1]) || !ISDIGIT(line[2]))
205 /* Do we have a command response? This should be the response code followed
206 by a space and optionally some text as per RFC-5321 and as outlined in
207 Section 4. Examples of RFC-4954 but some e-mail servers ignore this and
208 only send the response code instead as per Section 4.2. */
209 if(line[3] == ' ' || len == 5) {
211 *resp = curlx_sltosi(strtol(line, NULL, 10));
213 /* Make sure real server never sends internal value */
217 /* Do we have a multiline (continuation) response? */
218 else if(line[3] == '-' &&
219 (smtpc->state == SMTP_EHLO || smtpc->state == SMTP_COMMAND)) {
221 *resp = 1; /* Internal response code */
227 /***********************************************************************
231 * Gets the authentication message from the response buffer.
233 static void smtp_get_message(char *buffer, char **outptr)
235 size_t len = strlen(buffer);
236 char *message = NULL;
239 /* Find the start of the message */
241 for(message = buffer + 4; *message == ' ' || *message == '\t';
245 /* Find the end of the message */
247 if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' &&
248 message[len] != '\t')
251 /* Terminate the message */
257 /* junk input => zero length output */
258 message = &buffer[len];
263 /***********************************************************************
267 * This is the ONLY way to change SMTP state!
269 static void state(struct connectdata *conn, smtpstate newstate)
271 struct smtp_conn *smtpc = &conn->proto.smtpc;
272 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
273 /* for debug purposes */
274 static const char * const names[] = {
291 if(smtpc->state != newstate)
292 infof(conn->data, "SMTP %p state change from %s to %s\n",
293 (void *)smtpc, names[smtpc->state], names[newstate]);
296 smtpc->state = newstate;
299 /***********************************************************************
301 * smtp_perform_ehlo()
303 * Sends the EHLO command to not only initialise communication with the ESMTP
304 * server but to also obtain a list of server side supported capabilities.
306 static CURLcode smtp_perform_ehlo(struct connectdata *conn)
308 CURLcode result = CURLE_OK;
309 struct smtp_conn *smtpc = &conn->proto.smtpc;
311 smtpc->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanism yet */
312 smtpc->sasl.authused = SASL_AUTH_NONE; /* Clear the authentication mechanism
313 used for esmtp connections */
314 smtpc->tls_supported = FALSE; /* Clear the TLS capability */
315 smtpc->auth_supported = FALSE; /* Clear the AUTH capability */
317 /* Send the EHLO command */
318 result = Curl_pp_sendf(&smtpc->pp, "EHLO %s", smtpc->domain);
321 state(conn, SMTP_EHLO);
326 /***********************************************************************
328 * smtp_perform_helo()
330 * Sends the HELO command to initialise communication with the SMTP server.
332 static CURLcode smtp_perform_helo(struct connectdata *conn)
334 CURLcode result = CURLE_OK;
335 struct smtp_conn *smtpc = &conn->proto.smtpc;
337 smtpc->sasl.authused = SASL_AUTH_NONE; /* No authentication mechanism used
338 in smtp connections */
340 /* Send the HELO command */
341 result = Curl_pp_sendf(&smtpc->pp, "HELO %s", smtpc->domain);
344 state(conn, SMTP_HELO);
349 /***********************************************************************
351 * smtp_perform_starttls()
353 * Sends the STLS command to start the upgrade to TLS.
355 static CURLcode smtp_perform_starttls(struct connectdata *conn)
357 CURLcode result = CURLE_OK;
359 /* Send the STARTTLS command */
360 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "STARTTLS");
363 state(conn, SMTP_STARTTLS);
368 /***********************************************************************
370 * smtp_perform_upgrade_tls()
372 * Performs the upgrade to TLS.
374 static CURLcode smtp_perform_upgrade_tls(struct connectdata *conn)
376 CURLcode result = CURLE_OK;
377 struct smtp_conn *smtpc = &conn->proto.smtpc;
379 /* Start the SSL connection */
380 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone);
383 if(smtpc->state != SMTP_UPGRADETLS)
384 state(conn, SMTP_UPGRADETLS);
388 result = smtp_perform_ehlo(conn);
395 /***********************************************************************
397 * smtp_perform_auth()
399 * Sends an AUTH command allowing the client to login with the given SASL
400 * authentication mechanism.
402 static CURLcode smtp_perform_auth(struct connectdata *conn,
404 const char *initresp)
406 CURLcode result = CURLE_OK;
407 struct smtp_conn *smtpc = &conn->proto.smtpc;
409 if(initresp) { /* AUTH <mech> ...<crlf> */
410 /* Send the AUTH command with the initial response */
411 result = Curl_pp_sendf(&smtpc->pp, "AUTH %s %s", mech, initresp);
414 /* Send the AUTH command */
415 result = Curl_pp_sendf(&smtpc->pp, "AUTH %s", mech);
421 /***********************************************************************
423 * smtp_continue_auth()
425 * Sends SASL continuation data or cancellation.
427 static CURLcode smtp_continue_auth(struct connectdata *conn, const char *resp)
429 struct smtp_conn *smtpc = &conn->proto.smtpc;
431 return Curl_pp_sendf(&smtpc->pp, "%s", resp);
434 /***********************************************************************
436 * smtp_perform_authentication()
438 * Initiates the authentication sequence, with the appropriate SASL
439 * authentication mechanism.
441 static CURLcode smtp_perform_authentication(struct connectdata *conn)
443 CURLcode result = CURLE_OK;
444 struct smtp_conn *smtpc = &conn->proto.smtpc;
445 saslprogress progress;
447 /* Check we have enough data to authenticate with, and the
448 server supports authentiation, and end the connect phase if not */
449 if(!smtpc->auth_supported ||
450 !Curl_sasl_can_authenticate(&smtpc->sasl, conn)) {
451 state(conn, SMTP_STOP);
455 /* Calculate the SASL login details */
456 result = Curl_sasl_start(&smtpc->sasl, conn, FALSE, &progress);
459 if(progress == SASL_INPROGRESS)
460 state(conn, SMTP_AUTH);
462 /* Other mechanisms not supported */
463 infof(conn->data, "No known authentication mechanisms supported!\n");
464 result = CURLE_LOGIN_DENIED;
471 /***********************************************************************
473 * smtp_perform_command()
475 * Sends a SMTP based command.
477 static CURLcode smtp_perform_command(struct connectdata *conn)
479 CURLcode result = CURLE_OK;
480 struct Curl_easy *data = conn->data;
481 struct SMTP *smtp = data->req.protop;
483 /* Send the command */
485 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s %s",
486 smtp->custom && smtp->custom[0] != '\0' ?
487 smtp->custom : "VRFY",
490 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s",
491 smtp->custom && smtp->custom[0] != '\0' ?
492 smtp->custom : "HELP");
495 state(conn, SMTP_COMMAND);
500 /***********************************************************************
502 * smtp_perform_mail()
504 * Sends an MAIL command to initiate the upload of a message.
506 static CURLcode smtp_perform_mail(struct connectdata *conn)
511 CURLcode result = CURLE_OK;
512 struct Curl_easy *data = conn->data;
514 /* Calculate the FROM parameter */
515 if(!data->set.str[STRING_MAIL_FROM])
516 /* Null reverse-path, RFC-5321, sect. 3.6.3 */
518 else if(data->set.str[STRING_MAIL_FROM][0] == '<')
519 from = aprintf("%s", data->set.str[STRING_MAIL_FROM]);
521 from = aprintf("<%s>", data->set.str[STRING_MAIL_FROM]);
524 return CURLE_OUT_OF_MEMORY;
526 /* Calculate the optional AUTH parameter */
527 if(data->set.str[STRING_MAIL_AUTH] && conn->proto.smtpc.sasl.authused) {
528 if(data->set.str[STRING_MAIL_AUTH][0] != '\0')
529 auth = aprintf("%s", data->set.str[STRING_MAIL_AUTH]);
531 /* Empty AUTH, RFC-2554, sect. 5 */
537 return CURLE_OUT_OF_MEMORY;
541 /* Prepare the mime data if some. */
542 if(data->set.mimepost.kind != MIMEKIND_NONE) {
543 /* Use the whole structure as data. */
544 data->set.mimepost.flags &= ~MIME_BODY_ONLY;
546 /* Add external headers and mime version. */
547 curl_mime_headers(&data->set.mimepost, data->set.headers, 0);
548 result = Curl_mime_prepare_headers(&data->set.mimepost, NULL,
549 NULL, MIMESTRATEGY_MAIL);
552 if(!Curl_checkheaders(conn, "Mime-Version"))
553 result = Curl_mime_add_header(&data->set.mimepost.curlheaders,
554 "Mime-Version: 1.0");
556 /* Make sure we will read the entire mime structure. */
558 result = Curl_mime_rewind(&data->set.mimepost);
566 data->state.infilesize = Curl_mime_size(&data->set.mimepost);
568 /* Read from mime structure. */
569 data->state.fread_func = (curl_read_callback) Curl_mime_read;
570 data->state.in = (void *) &data->set.mimepost;
573 /* Calculate the optional SIZE parameter */
574 if(conn->proto.smtpc.size_supported && data->state.infilesize > 0) {
575 size = aprintf("%" CURL_FORMAT_CURL_OFF_T, data->state.infilesize);
581 return CURLE_OUT_OF_MEMORY;
585 /* Send the MAIL command */
587 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
588 "MAIL FROM:%s", from);
589 else if(auth && !size)
590 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
591 "MAIL FROM:%s AUTH=%s", from, auth);
592 else if(auth && size)
593 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
594 "MAIL FROM:%s AUTH=%s SIZE=%s", from, auth, size);
596 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
597 "MAIL FROM:%s SIZE=%s", from, size);
604 state(conn, SMTP_MAIL);
609 /***********************************************************************
611 * smtp_perform_rcpt_to()
613 * Sends a RCPT TO command for a given recipient as part of the message upload
616 static CURLcode smtp_perform_rcpt_to(struct connectdata *conn)
618 CURLcode result = CURLE_OK;
619 struct Curl_easy *data = conn->data;
620 struct SMTP *smtp = data->req.protop;
622 /* Send the RCPT TO command */
623 if(smtp->rcpt->data[0] == '<')
624 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:%s",
627 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:<%s>",
630 state(conn, SMTP_RCPT);
635 /***********************************************************************
637 * smtp_perform_quit()
639 * Performs the quit action prior to sclose() being called.
641 static CURLcode smtp_perform_quit(struct connectdata *conn)
643 CURLcode result = CURLE_OK;
645 /* Send the QUIT command */
646 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "QUIT");
649 state(conn, SMTP_QUIT);
654 /* For the initial server greeting */
655 static CURLcode smtp_state_servergreet_resp(struct connectdata *conn,
659 CURLcode result = CURLE_OK;
660 struct Curl_easy *data = conn->data;
662 (void)instate; /* no use for this yet */
664 if(smtpcode/100 != 2) {
665 failf(data, "Got unexpected smtp-server response: %d", smtpcode);
666 result = CURLE_WEIRD_SERVER_REPLY;
669 result = smtp_perform_ehlo(conn);
674 /* For STARTTLS responses */
675 static CURLcode smtp_state_starttls_resp(struct connectdata *conn,
679 CURLcode result = CURLE_OK;
680 struct Curl_easy *data = conn->data;
682 (void)instate; /* no use for this yet */
684 if(smtpcode != 220) {
685 if(data->set.use_ssl != CURLUSESSL_TRY) {
686 failf(data, "STARTTLS denied, code %d", smtpcode);
687 result = CURLE_USE_SSL_FAILED;
690 result = smtp_perform_authentication(conn);
693 result = smtp_perform_upgrade_tls(conn);
698 /* For EHLO responses */
699 static CURLcode smtp_state_ehlo_resp(struct connectdata *conn, int smtpcode,
702 CURLcode result = CURLE_OK;
703 struct Curl_easy *data = conn->data;
704 struct smtp_conn *smtpc = &conn->proto.smtpc;
705 const char *line = data->state.buffer;
706 size_t len = strlen(line);
709 (void)instate; /* no use for this yet */
711 if(smtpcode/100 != 2 && smtpcode != 1) {
712 if(data->set.use_ssl <= CURLUSESSL_TRY || conn->ssl[FIRSTSOCKET].use)
713 result = smtp_perform_helo(conn);
715 failf(data, "Remote access denied: %d", smtpcode);
716 result = CURLE_REMOTE_ACCESS_DENIED;
723 /* Does the server support the STARTTLS capability? */
724 if(len >= 8 && !memcmp(line, "STARTTLS", 8))
725 smtpc->tls_supported = TRUE;
727 /* Does the server support the SIZE capability? */
728 else if(len >= 4 && !memcmp(line, "SIZE", 4))
729 smtpc->size_supported = TRUE;
731 /* Does the server support authentication? */
732 else if(len >= 5 && !memcmp(line, "AUTH ", 5)) {
733 smtpc->auth_supported = TRUE;
735 /* Advance past the AUTH keyword */
739 /* Loop through the data line */
742 unsigned int mechbit;
745 (*line == ' ' || *line == '\t' ||
746 *line == '\r' || *line == '\n')) {
755 /* Extract the word */
756 for(wordlen = 0; wordlen < len && line[wordlen] != ' ' &&
757 line[wordlen] != '\t' && line[wordlen] != '\r' &&
758 line[wordlen] != '\n';)
761 /* Test the word for a matching authentication mechanism */
762 mechbit = Curl_sasl_decode_mech(line, wordlen, &llen);
763 if(mechbit && llen == wordlen)
764 smtpc->sasl.authmechs |= mechbit;
772 if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
773 /* We don't have a SSL/TLS connection yet, but SSL is requested */
774 if(smtpc->tls_supported)
775 /* Switch to TLS connection now */
776 result = smtp_perform_starttls(conn);
777 else if(data->set.use_ssl == CURLUSESSL_TRY)
778 /* Fallback and carry on with authentication */
779 result = smtp_perform_authentication(conn);
781 failf(data, "STARTTLS not supported.");
782 result = CURLE_USE_SSL_FAILED;
786 result = smtp_perform_authentication(conn);
793 /* For HELO responses */
794 static CURLcode smtp_state_helo_resp(struct connectdata *conn, int smtpcode,
797 CURLcode result = CURLE_OK;
798 struct Curl_easy *data = conn->data;
800 (void)instate; /* no use for this yet */
802 if(smtpcode/100 != 2) {
803 failf(data, "Remote access denied: %d", smtpcode);
804 result = CURLE_REMOTE_ACCESS_DENIED;
807 /* End of connect phase */
808 state(conn, SMTP_STOP);
813 /* For SASL authentication responses */
814 static CURLcode smtp_state_auth_resp(struct connectdata *conn,
818 CURLcode result = CURLE_OK;
819 struct Curl_easy *data = conn->data;
820 struct smtp_conn *smtpc = &conn->proto.smtpc;
821 saslprogress progress;
823 (void)instate; /* no use for this yet */
825 result = Curl_sasl_continue(&smtpc->sasl, conn, smtpcode, &progress);
829 state(conn, SMTP_STOP); /* Authenticated */
831 case SASL_IDLE: /* No mechanism left after cancellation */
832 failf(data, "Authentication cancelled");
833 result = CURLE_LOGIN_DENIED;
842 /* For command responses */
843 static CURLcode smtp_state_command_resp(struct connectdata *conn, int smtpcode,
846 CURLcode result = CURLE_OK;
847 struct Curl_easy *data = conn->data;
848 struct SMTP *smtp = data->req.protop;
849 char *line = data->state.buffer;
850 size_t len = strlen(line);
852 (void)instate; /* no use for this yet */
854 if((smtp->rcpt && smtpcode/100 != 2 && smtpcode != 553 && smtpcode != 1) ||
855 (!smtp->rcpt && smtpcode/100 != 2 && smtpcode != 1)) {
856 failf(data, "Command failed: %d", smtpcode);
857 result = CURLE_RECV_ERROR;
860 /* Temporarily add the LF character back and send as body to the client */
861 if(!data->set.opt_no_body) {
863 result = Curl_client_write(conn, CLIENTWRITE_BODY, line, len + 1);
869 smtp->rcpt = smtp->rcpt->next;
872 /* Send the next command */
873 result = smtp_perform_command(conn);
876 /* End of DO phase */
877 state(conn, SMTP_STOP);
880 /* End of DO phase */
881 state(conn, SMTP_STOP);
888 /* For MAIL responses */
889 static CURLcode smtp_state_mail_resp(struct connectdata *conn, int smtpcode,
892 CURLcode result = CURLE_OK;
893 struct Curl_easy *data = conn->data;
895 (void)instate; /* no use for this yet */
897 if(smtpcode/100 != 2) {
898 failf(data, "MAIL failed: %d", smtpcode);
899 result = CURLE_SEND_ERROR;
902 /* Start the RCPT TO command */
903 result = smtp_perform_rcpt_to(conn);
908 /* For RCPT responses */
909 static CURLcode smtp_state_rcpt_resp(struct connectdata *conn, int smtpcode,
912 CURLcode result = CURLE_OK;
913 struct Curl_easy *data = conn->data;
914 struct SMTP *smtp = data->req.protop;
916 (void)instate; /* no use for this yet */
918 if(smtpcode/100 != 2) {
919 failf(data, "RCPT failed: %d", smtpcode);
920 result = CURLE_SEND_ERROR;
923 smtp->rcpt = smtp->rcpt->next;
926 /* Send the next RCPT TO command */
927 result = smtp_perform_rcpt_to(conn);
929 /* Send the DATA command */
930 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "DATA");
933 state(conn, SMTP_DATA);
940 /* For DATA response */
941 static CURLcode smtp_state_data_resp(struct connectdata *conn, int smtpcode,
944 CURLcode result = CURLE_OK;
945 struct Curl_easy *data = conn->data;
947 (void)instate; /* no use for this yet */
949 if(smtpcode != 354) {
950 failf(data, "DATA failed: %d", smtpcode);
951 result = CURLE_SEND_ERROR;
954 /* Set the progress upload size */
955 Curl_pgrsSetUploadSize(data, data->state.infilesize);
958 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL);
960 /* End of DO phase */
961 state(conn, SMTP_STOP);
967 /* For POSTDATA responses, which are received after the entire DATA
968 part has been sent to the server */
969 static CURLcode smtp_state_postdata_resp(struct connectdata *conn,
973 CURLcode result = CURLE_OK;
975 (void)instate; /* no use for this yet */
978 result = CURLE_RECV_ERROR;
980 /* End of DONE phase */
981 state(conn, SMTP_STOP);
986 static CURLcode smtp_statemach_act(struct connectdata *conn)
988 CURLcode result = CURLE_OK;
989 curl_socket_t sock = conn->sock[FIRSTSOCKET];
990 struct Curl_easy *data = conn->data;
992 struct smtp_conn *smtpc = &conn->proto.smtpc;
993 struct pingpong *pp = &smtpc->pp;
996 /* Busy upgrading the connection; right now all I/O is SSL/TLS, not SMTP */
997 if(smtpc->state == SMTP_UPGRADETLS)
998 return smtp_perform_upgrade_tls(conn);
1000 /* Flush any data that needs to be sent */
1002 return Curl_pp_flushsend(pp);
1005 /* Read the response from the server */
1006 result = Curl_pp_readresp(sock, pp, &smtpcode, &nread);
1010 /* Store the latest response for later retrieval if necessary */
1011 if(smtpc->state != SMTP_QUIT && smtpcode != 1)
1012 data->info.httpcode = smtpcode;
1017 /* We have now received a full SMTP server response */
1018 switch(smtpc->state) {
1019 case SMTP_SERVERGREET:
1020 result = smtp_state_servergreet_resp(conn, smtpcode, smtpc->state);
1024 result = smtp_state_ehlo_resp(conn, smtpcode, smtpc->state);
1028 result = smtp_state_helo_resp(conn, smtpcode, smtpc->state);
1032 result = smtp_state_starttls_resp(conn, smtpcode, smtpc->state);
1036 result = smtp_state_auth_resp(conn, smtpcode, smtpc->state);
1040 result = smtp_state_command_resp(conn, smtpcode, smtpc->state);
1044 result = smtp_state_mail_resp(conn, smtpcode, smtpc->state);
1048 result = smtp_state_rcpt_resp(conn, smtpcode, smtpc->state);
1052 result = smtp_state_data_resp(conn, smtpcode, smtpc->state);
1056 result = smtp_state_postdata_resp(conn, smtpcode, smtpc->state);
1060 /* fallthrough, just stop! */
1062 /* internal error */
1063 state(conn, SMTP_STOP);
1066 } while(!result && smtpc->state != SMTP_STOP && Curl_pp_moredata(pp));
1071 /* Called repeatedly until done from multi.c */
1072 static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done)
1074 CURLcode result = CURLE_OK;
1075 struct smtp_conn *smtpc = &conn->proto.smtpc;
1077 if((conn->handler->flags & PROTOPT_SSL) && !smtpc->ssldone) {
1078 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone);
1079 if(result || !smtpc->ssldone)
1083 result = Curl_pp_statemach(&smtpc->pp, FALSE);
1084 *done = (smtpc->state == SMTP_STOP) ? TRUE : FALSE;
1089 static CURLcode smtp_block_statemach(struct connectdata *conn)
1091 CURLcode result = CURLE_OK;
1092 struct smtp_conn *smtpc = &conn->proto.smtpc;
1094 while(smtpc->state != SMTP_STOP && !result)
1095 result = Curl_pp_statemach(&smtpc->pp, TRUE);
1100 /* Allocate and initialize the SMTP struct for the current Curl_easy if
1102 static CURLcode smtp_init(struct connectdata *conn)
1104 CURLcode result = CURLE_OK;
1105 struct Curl_easy *data = conn->data;
1108 smtp = data->req.protop = calloc(sizeof(struct SMTP), 1);
1110 result = CURLE_OUT_OF_MEMORY;
1115 /* For the SMTP "protocol connect" and "doing" phases only */
1116 static int smtp_getsock(struct connectdata *conn, curl_socket_t *socks,
1119 return Curl_pp_getsock(&conn->proto.smtpc.pp, socks, numsocks);
1122 /***********************************************************************
1126 * This function should do everything that is to be considered a part of
1127 * the connection phase.
1129 * The variable pointed to by 'done' will be TRUE if the protocol-layer
1130 * connect phase is done when this function returns, or FALSE if not.
1132 static CURLcode smtp_connect(struct connectdata *conn, bool *done)
1134 CURLcode result = CURLE_OK;
1135 struct smtp_conn *smtpc = &conn->proto.smtpc;
1136 struct pingpong *pp = &smtpc->pp;
1138 *done = FALSE; /* default to not done yet */
1140 /* We always support persistent connections in SMTP */
1141 connkeep(conn, "SMTP default");
1143 /* Set the default response time-out */
1144 pp->response_time = RESP_TIMEOUT;
1145 pp->statemach_act = smtp_statemach_act;
1146 pp->endofresp = smtp_endofresp;
1149 /* Initialize the SASL storage */
1150 Curl_sasl_init(&smtpc->sasl, &saslsmtp);
1152 /* Initialise the pingpong layer */
1155 /* Parse the URL options */
1156 result = smtp_parse_url_options(conn);
1160 /* Parse the URL path */
1161 result = smtp_parse_url_path(conn);
1165 /* Start off waiting for the server greeting response */
1166 state(conn, SMTP_SERVERGREET);
1168 result = smtp_multi_statemach(conn, done);
1173 /***********************************************************************
1177 * The DONE function. This does what needs to be done after a single DO has
1180 * Input argument is already checked for validity.
1182 static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
1185 CURLcode result = CURLE_OK;
1186 struct Curl_easy *data = conn->data;
1187 struct SMTP *smtp = data->req.protop;
1188 struct pingpong *pp = &conn->proto.smtpc.pp;
1191 ssize_t bytes_written;
1195 if(!smtp || !pp->conn)
1198 /* Cleanup our per-request based variables */
1199 Curl_safefree(smtp->custom);
1202 connclose(conn, "SMTP done with bad status"); /* marked for closure */
1203 result = status; /* use the already set error code */
1205 else if(!data->set.connect_only && data->set.mail_rcpt &&
1206 (data->set.upload || data->set.mimepost.kind)) {
1207 /* Calculate the EOB taking into account any terminating CRLF from the
1208 previous line of the email or the CRLF of the DATA command when there
1209 is "no mail data". RFC-5321, sect. 4.1.1.4.
1211 Note: As some SSL backends, such as OpenSSL, will cause Curl_write() to
1212 fail when using a different pointer following a previous write, that
1213 returned CURLE_AGAIN, we duplicate the EOB now rather than when the
1214 bytes written doesn't equal len. */
1215 if(smtp->trailing_crlf || !conn->data->state.infilesize) {
1216 eob = strdup(SMTP_EOB + 2);
1217 len = SMTP_EOB_LEN - 2;
1220 eob = strdup(SMTP_EOB);
1225 return CURLE_OUT_OF_MEMORY;
1227 /* Send the end of block data */
1228 result = Curl_write(conn, conn->writesockfd, eob, len, &bytes_written);
1234 if(bytes_written != len) {
1235 /* The whole chunk was not sent so keep it around and adjust the
1236 pingpong structure accordingly */
1239 pp->sendleft = len - bytes_written;
1242 /* Successfully sent so adjust the response timeout relative to now */
1243 pp->response = Curl_now();
1248 state(conn, SMTP_POSTDATA);
1250 /* Run the state-machine
1252 TODO: when the multi interface is used, this _really_ should be using
1253 the smtp_multi_statemach function but we have no general support for
1254 non-blocking DONE operations!
1256 result = smtp_block_statemach(conn);
1259 /* Clear the transfer mode for the next request */
1260 smtp->transfer = FTPTRANSFER_BODY;
1265 /***********************************************************************
1269 * This is the actual DO function for SMTP. Transfer a mail, send a command
1270 * or get some data according to the options previously setup.
1272 static CURLcode smtp_perform(struct connectdata *conn, bool *connected,
1275 /* This is SMTP and no proxy */
1276 CURLcode result = CURLE_OK;
1277 struct Curl_easy *data = conn->data;
1278 struct SMTP *smtp = data->req.protop;
1280 DEBUGF(infof(conn->data, "DO phase starts\n"));
1282 if(data->set.opt_no_body) {
1283 /* Requested no body means no transfer */
1284 smtp->transfer = FTPTRANSFER_INFO;
1287 *dophase_done = FALSE; /* not done yet */
1289 /* Store the first recipient (or NULL if not specified) */
1290 smtp->rcpt = data->set.mail_rcpt;
1292 /* Initial data character is the first character in line: it is implicitly
1293 preceded by a virtual CRLF. */
1294 smtp->trailing_crlf = TRUE;
1297 /* Start the first command in the DO phase */
1298 if((data->set.upload || data->set.mimepost.kind) && data->set.mail_rcpt)
1300 result = smtp_perform_mail(conn);
1302 /* SMTP based command (VRFY, EXPN, NOOP, RSET or HELP) */
1303 result = smtp_perform_command(conn);
1308 /* Run the state-machine */
1309 result = smtp_multi_statemach(conn, dophase_done);
1311 *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1314 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1319 /***********************************************************************
1323 * This function is registered as 'curl_do' function. It decodes the path
1324 * parts etc as a wrapper to the actual DO function (smtp_perform).
1326 * The input argument is already checked for validity.
1328 static CURLcode smtp_do(struct connectdata *conn, bool *done)
1330 CURLcode result = CURLE_OK;
1332 *done = FALSE; /* default to false */
1334 /* Parse the custom request */
1335 result = smtp_parse_custom_request(conn);
1339 result = smtp_regular_transfer(conn, done);
1344 /***********************************************************************
1348 * Disconnect from an SMTP server. Cleanup protocol-specific per-connection
1349 * resources. BLOCKING.
1351 static CURLcode smtp_disconnect(struct connectdata *conn, bool dead_connection)
1353 struct smtp_conn *smtpc = &conn->proto.smtpc;
1355 /* We cannot send quit unconditionally. If this connection is stale or
1356 bad in any way, sending quit and waiting around here will make the
1357 disconnect wait in vain and cause more problems than we need to. */
1359 /* The SMTP session may or may not have been allocated/setup at this
1361 if(!dead_connection && smtpc->pp.conn && smtpc->pp.conn->bits.protoconnstart)
1362 if(!smtp_perform_quit(conn))
1363 (void)smtp_block_statemach(conn); /* ignore errors on QUIT */
1365 /* Disconnect from the server */
1366 Curl_pp_disconnect(&smtpc->pp);
1368 /* Cleanup the SASL module */
1369 Curl_sasl_cleanup(conn, smtpc->sasl.authused);
1371 /* Cleanup our connection based variables */
1372 Curl_safefree(smtpc->domain);
1377 /* Call this when the DO phase has completed */
1378 static CURLcode smtp_dophase_done(struct connectdata *conn, bool connected)
1380 struct SMTP *smtp = conn->data->req.protop;
1384 if(smtp->transfer != FTPTRANSFER_BODY)
1385 /* no data to transfer */
1386 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1391 /* Called from multi.c while DOing */
1392 static CURLcode smtp_doing(struct connectdata *conn, bool *dophase_done)
1394 CURLcode result = smtp_multi_statemach(conn, dophase_done);
1397 DEBUGF(infof(conn->data, "DO phase failed\n"));
1398 else if(*dophase_done) {
1399 result = smtp_dophase_done(conn, FALSE /* not connected */);
1401 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1407 /***********************************************************************
1409 * smtp_regular_transfer()
1411 * The input argument is already checked for validity.
1413 * Performs all commands done before a regular transfer between a local and a
1416 static CURLcode smtp_regular_transfer(struct connectdata *conn,
1419 CURLcode result = CURLE_OK;
1420 bool connected = FALSE;
1421 struct Curl_easy *data = conn->data;
1423 /* Make sure size is unknown at this point */
1424 data->req.size = -1;
1426 /* Set the progress data */
1427 Curl_pgrsSetUploadCounter(data, 0);
1428 Curl_pgrsSetDownloadCounter(data, 0);
1429 Curl_pgrsSetUploadSize(data, -1);
1430 Curl_pgrsSetDownloadSize(data, -1);
1432 /* Carry out the perform */
1433 result = smtp_perform(conn, &connected, dophase_done);
1435 /* Perform post DO phase operations if necessary */
1436 if(!result && *dophase_done)
1437 result = smtp_dophase_done(conn, connected);
1442 static CURLcode smtp_setup_connection(struct connectdata *conn)
1444 struct Curl_easy *data = conn->data;
1447 /* Clear the TLS upgraded flag */
1448 conn->tls_upgraded = FALSE;
1450 /* Initialise the SMTP layer */
1451 result = smtp_init(conn);
1455 data->state.path++; /* don't include the initial slash */
1460 /***********************************************************************
1462 * smtp_parse_url_options()
1464 * Parse the URL login options.
1466 static CURLcode smtp_parse_url_options(struct connectdata *conn)
1468 CURLcode result = CURLE_OK;
1469 struct smtp_conn *smtpc = &conn->proto.smtpc;
1470 const char *ptr = conn->options;
1472 smtpc->sasl.resetprefs = TRUE;
1474 while(!result && ptr && *ptr) {
1475 const char *key = ptr;
1478 while(*ptr && *ptr != '=')
1483 while(*ptr && *ptr != ';')
1486 if(strncasecompare(key, "AUTH=", 5))
1487 result = Curl_sasl_parse_url_auth_option(&smtpc->sasl,
1488 value, ptr - value);
1490 result = CURLE_URL_MALFORMAT;
1499 /***********************************************************************
1501 * smtp_parse_url_path()
1503 * Parse the URL path into separate path components.
1505 static CURLcode smtp_parse_url_path(struct connectdata *conn)
1507 /* The SMTP struct is already initialised in smtp_connect() */
1508 struct Curl_easy *data = conn->data;
1509 struct smtp_conn *smtpc = &conn->proto.smtpc;
1510 const char *path = data->state.path;
1511 char localhost[HOSTNAME_MAX + 1];
1513 /* Calculate the path if necessary */
1515 if(!Curl_gethostname(localhost, sizeof(localhost)))
1521 /* URL decode the path and use it as the domain in our EHLO */
1522 return Curl_urldecode(conn->data, path, 0, &smtpc->domain, NULL, TRUE);
1525 /***********************************************************************
1527 * smtp_parse_custom_request()
1529 * Parse the custom request.
1531 static CURLcode smtp_parse_custom_request(struct connectdata *conn)
1533 CURLcode result = CURLE_OK;
1534 struct Curl_easy *data = conn->data;
1535 struct SMTP *smtp = data->req.protop;
1536 const char *custom = data->set.str[STRING_CUSTOMREQUEST];
1538 /* URL decode the custom request */
1540 result = Curl_urldecode(data, custom, 0, &smtp->custom, NULL, TRUE);
1545 CURLcode Curl_smtp_escape_eob(struct connectdata *conn, const ssize_t nread)
1547 /* When sending a SMTP payload we must detect CRLF. sequences making sure
1548 they are sent as CRLF.. instead, as a . on the beginning of a line will
1549 be deleted by the server when not part of an EOB terminator and a
1550 genuine CRLF.CRLF which isn't escaped will wrongly be detected as end of
1555 struct Curl_easy *data = conn->data;
1556 struct SMTP *smtp = data->req.protop;
1557 char *scratch = data->state.scratch;
1558 char *newscratch = NULL;
1559 char *oldscratch = NULL;
1562 /* Do we need to allocate a scratch buffer? */
1563 if(!scratch || data->set.crlf) {
1564 oldscratch = scratch;
1566 scratch = newscratch = malloc(2 * data->set.buffer_size);
1568 failf(data, "Failed to alloc scratch buffer!");
1570 return CURLE_OUT_OF_MEMORY;
1574 /* Have we already sent part of the EOB? */
1575 eob_sent = smtp->eob;
1577 /* This loop can be improved by some kind of Boyer-Moore style of
1578 approach but that is saved for later... */
1579 for(i = 0, si = 0; i < nread; i++) {
1580 if(SMTP_EOB[smtp->eob] == data->req.upload_fromhere[i]) {
1583 /* Is the EOB potentially the terminating CRLF? */
1584 if(2 == smtp->eob || SMTP_EOB_LEN == smtp->eob)
1585 smtp->trailing_crlf = TRUE;
1587 smtp->trailing_crlf = FALSE;
1589 else if(smtp->eob) {
1590 /* A previous substring matched so output that first */
1591 memcpy(&scratch[si], &SMTP_EOB[eob_sent], smtp->eob - eob_sent);
1592 si += smtp->eob - eob_sent;
1594 /* Then compare the first byte */
1595 if(SMTP_EOB[0] == data->req.upload_fromhere[i])
1602 /* Reset the trailing CRLF flag as there was more data */
1603 smtp->trailing_crlf = FALSE;
1606 /* Do we have a match for CRLF. as per RFC-5321, sect. 4.5.2 */
1607 if(SMTP_EOB_FIND_LEN == smtp->eob) {
1608 /* Copy the replacement data to the target buffer */
1609 memcpy(&scratch[si], &SMTP_EOB_REPL[eob_sent],
1610 SMTP_EOB_REPL_LEN - eob_sent);
1611 si += SMTP_EOB_REPL_LEN - eob_sent;
1616 scratch[si++] = data->req.upload_fromhere[i];
1619 if(smtp->eob - eob_sent) {
1620 /* A substring matched before processing ended so output that now */
1621 memcpy(&scratch[si], &SMTP_EOB[eob_sent], smtp->eob - eob_sent);
1622 si += smtp->eob - eob_sent;
1625 /* Only use the new buffer if we replaced something */
1627 /* Upload from the new (replaced) buffer instead */
1628 data->req.upload_fromhere = scratch;
1630 /* Save the buffer so it can be freed later */
1631 data->state.scratch = scratch;
1633 /* Free the old scratch buffer */
1636 /* Set the new amount too */
1637 data->req.upload_present = si;
1645 #endif /* CURL_DISABLE_SMTP */