1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at http://curl.haxx.se/docs/copyright.html.
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 * RFC1870 SMTP Service Extension for Message Size
22 * RFC2195 CRAM-MD5 authentication
23 * RFC2831 DIGEST-MD5 authentication
24 * RFC3207 SMTP over TLS
25 * RFC4422 Simple Authentication and Security Layer (SASL)
26 * RFC4616 PLAIN authentication
27 * RFC4954 SMTP Authentication
28 * RFC5321 SMTP protocol
29 * RFC6749 OAuth 2.0 Authorization Framework
30 * Draft SMTP URL Interface <draft-earhart-url-smtp-00.txt>
31 * Draft LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt>
33 ***************************************************************************/
35 #include "curl_setup.h"
37 #ifndef CURL_DISABLE_SMTP
39 #ifdef HAVE_NETINET_IN_H
40 #include <netinet/in.h>
42 #ifdef HAVE_ARPA_INET_H
43 #include <arpa/inet.h>
46 #include <sys/utsname.h>
56 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
58 #define in_addr_t unsigned long
61 #include <curl/curl.h>
69 #include "http.h" /* for HTTP proxy tunnel stuff */
73 #include "strtoofft.h"
82 #include "curl_gethostname.h"
83 #include "curl_sasl.h"
86 #define _MPRINTF_REPLACE /* use our functions only */
87 #include <curl/mprintf.h>
89 #include "curl_memory.h"
90 /* The last #include file should be: */
93 /* Local API functions */
94 static CURLcode smtp_regular_transfer(struct connectdata *conn, bool *done);
95 static CURLcode smtp_do(struct connectdata *conn, bool *done);
96 static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
98 static CURLcode smtp_connect(struct connectdata *conn, bool *done);
99 static CURLcode smtp_disconnect(struct connectdata *conn, bool dead);
100 static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done);
101 static int smtp_getsock(struct connectdata *conn, curl_socket_t *socks,
103 static CURLcode smtp_doing(struct connectdata *conn, bool *dophase_done);
104 static CURLcode smtp_setup_connection(struct connectdata *conn);
105 static CURLcode smtp_parse_url_options(struct connectdata *conn);
106 static CURLcode smtp_parse_url_path(struct connectdata *conn);
109 * SMTP protocol handler.
112 const struct Curl_handler Curl_handler_smtp = {
114 smtp_setup_connection, /* setup_connection */
116 smtp_done, /* done */
117 ZERO_NULL, /* do_more */
118 smtp_connect, /* connect_it */
119 smtp_multi_statemach, /* connecting */
120 smtp_doing, /* doing */
121 smtp_getsock, /* proto_getsock */
122 smtp_getsock, /* doing_getsock */
123 ZERO_NULL, /* domore_getsock */
124 ZERO_NULL, /* perform_getsock */
125 smtp_disconnect, /* disconnect */
126 ZERO_NULL, /* readwrite */
127 PORT_SMTP, /* defport */
128 CURLPROTO_SMTP, /* protocol */
129 PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY /* flags */
134 * SMTPS protocol handler.
137 const struct Curl_handler Curl_handler_smtps = {
138 "SMTPS", /* scheme */
139 smtp_setup_connection, /* setup_connection */
141 smtp_done, /* done */
142 ZERO_NULL, /* do_more */
143 smtp_connect, /* connect_it */
144 smtp_multi_statemach, /* connecting */
145 smtp_doing, /* doing */
146 smtp_getsock, /* proto_getsock */
147 smtp_getsock, /* doing_getsock */
148 ZERO_NULL, /* domore_getsock */
149 ZERO_NULL, /* perform_getsock */
150 smtp_disconnect, /* disconnect */
151 ZERO_NULL, /* readwrite */
152 PORT_SMTPS, /* defport */
153 CURLPROTO_SMTP | CURLPROTO_SMTPS, /* protocol */
154 PROTOPT_CLOSEACTION | PROTOPT_SSL
155 | PROTOPT_NOURLQUERY /* flags */
159 #ifndef CURL_DISABLE_HTTP
161 * HTTP-proxyed SMTP protocol handler.
164 static const struct Curl_handler Curl_handler_smtp_proxy = {
166 Curl_http_setup_conn, /* setup_connection */
167 Curl_http, /* do_it */
168 Curl_http_done, /* done */
169 ZERO_NULL, /* do_more */
170 ZERO_NULL, /* connect_it */
171 ZERO_NULL, /* connecting */
172 ZERO_NULL, /* doing */
173 ZERO_NULL, /* proto_getsock */
174 ZERO_NULL, /* doing_getsock */
175 ZERO_NULL, /* domore_getsock */
176 ZERO_NULL, /* perform_getsock */
177 ZERO_NULL, /* disconnect */
178 ZERO_NULL, /* readwrite */
179 PORT_SMTP, /* defport */
180 CURLPROTO_HTTP, /* protocol */
181 PROTOPT_NONE /* flags */
186 * HTTP-proxyed SMTPS protocol handler.
189 static const struct Curl_handler Curl_handler_smtps_proxy = {
190 "SMTPS", /* scheme */
191 Curl_http_setup_conn, /* setup_connection */
192 Curl_http, /* do_it */
193 Curl_http_done, /* done */
194 ZERO_NULL, /* do_more */
195 ZERO_NULL, /* connect_it */
196 ZERO_NULL, /* connecting */
197 ZERO_NULL, /* doing */
198 ZERO_NULL, /* proto_getsock */
199 ZERO_NULL, /* doing_getsock */
200 ZERO_NULL, /* domore_getsock */
201 ZERO_NULL, /* perform_getsock */
202 ZERO_NULL, /* disconnect */
203 ZERO_NULL, /* readwrite */
204 PORT_SMTPS, /* defport */
205 CURLPROTO_HTTP, /* protocol */
206 PROTOPT_NONE /* flags */
212 static void smtp_to_smtps(struct connectdata *conn)
214 conn->handler = &Curl_handler_smtps;
217 #define smtp_to_smtps(x) Curl_nop_stmt
220 /***********************************************************************
224 * Checks for an ending SMTP status code at the start of the given string, but
225 * also detects various capabilities from the EHLO response including the
226 * supported authentication mechanisms.
228 static bool smtp_endofresp(struct connectdata *conn, char *line, size_t len,
234 if(len < 4 || !ISDIGIT(line[0]) || !ISDIGIT(line[1]) || !ISDIGIT(line[2]))
237 /* Do we have a command response? This should be the response code followed
238 by a space and optionally some text as per RFC-5321 and as outlined in
239 Section 4. Examples of RFC-4954 but some e-mail servers ignore this and
240 only send the response code instead as per Section 4.2. */
241 if(line[3] == ' ' || len == 5) {
243 *resp = curlx_sltosi(strtol(line, NULL, 10));
245 /* Make sure real server never sends internal value */
249 /* Do we have a multiline (continuation) response? */
250 else if(line[3] == '-') {
252 *resp = 1; /* Internal response code */
258 /***********************************************************************
262 * Gets the authentication message from the response buffer.
264 static void smtp_get_message(char *buffer, char** outptr)
267 char* message = NULL;
269 /* Find the start of the message */
270 for(message = buffer + 4; *message == ' ' || *message == '\t'; message++)
273 /* Find the end of the message */
274 for(len = strlen(message); len--;)
275 if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' &&
276 message[len] != '\t')
279 /* Terminate the message */
287 /***********************************************************************
291 * This is the ONLY way to change SMTP state!
293 static void state(struct connectdata *conn, smtpstate newstate)
295 struct smtp_conn *smtpc = &conn->proto.smtpc;
296 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
297 /* for debug purposes */
298 static const char * const names[] = {
310 "AUTH_DIGESTMD5_RESP",
312 "AUTH_NTLM_TYPE2MSG",
324 if(smtpc->state != newstate)
325 infof(conn->data, "SMTP %p state change from %s to %s\n",
326 (void *)smtpc, names[smtpc->state], names[newstate]);
329 smtpc->state = newstate;
332 /***********************************************************************
334 * smtp_perform_ehlo()
336 * Sends the EHLO command to not only initialise communication with the ESMTP
337 * server but to also obtain a list of server side supported capabilities.
339 static CURLcode smtp_perform_ehlo(struct connectdata *conn)
341 CURLcode result = CURLE_OK;
342 struct smtp_conn *smtpc = &conn->proto.smtpc;
344 smtpc->authmechs = 0; /* No known authentication mechanisms yet */
345 smtpc->authused = 0; /* Clear the authentication mechanism used
346 for esmtp connections */
347 smtpc->tls_supported = FALSE; /* Clear the TLS capability */
349 /* Send the EHLO command */
350 result = Curl_pp_sendf(&smtpc->pp, "EHLO %s", smtpc->domain);
353 state(conn, SMTP_EHLO);
358 /***********************************************************************
360 * smtp_perform_helo()
362 * Sends the HELO command to initialise communication with the SMTP server.
364 static CURLcode smtp_perform_helo(struct connectdata *conn)
366 CURLcode result = CURLE_OK;
367 struct smtp_conn *smtpc = &conn->proto.smtpc;
369 smtpc->authused = 0; /* No authentication mechanism used in smtp
372 /* Send the HELO command */
373 result = Curl_pp_sendf(&smtpc->pp, "HELO %s", smtpc->domain);
376 state(conn, SMTP_HELO);
381 /***********************************************************************
383 * smtp_perform_starttls()
385 * Sends the STLS command to start the upgrade to TLS.
387 static CURLcode smtp_perform_starttls(struct connectdata *conn)
389 CURLcode result = CURLE_OK;
391 /* Send the STARTTLS command */
392 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "STARTTLS");
395 state(conn, SMTP_STARTTLS);
400 /***********************************************************************
402 * smtp_perform_upgrade_tls()
404 * Performs the upgrade to TLS.
406 static CURLcode smtp_perform_upgrade_tls(struct connectdata *conn)
408 CURLcode result = CURLE_OK;
409 struct smtp_conn *smtpc = &conn->proto.smtpc;
411 /* Start the SSL connection */
412 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone);
415 if(smtpc->state != SMTP_UPGRADETLS)
416 state(conn, SMTP_UPGRADETLS);
420 result = smtp_perform_ehlo(conn);
427 /***********************************************************************
429 * smtp_perform_authenticate()
431 * Sends an AUTH command allowing the client to login with the appropriate
432 * SASL authentication mechanism.
434 static CURLcode smtp_perform_authenticate(struct connectdata *conn)
436 CURLcode result = CURLE_OK;
437 struct SessionHandle *data = conn->data;
438 struct smtp_conn *smtpc = &conn->proto.smtpc;
439 const char *mech = NULL;
440 char *initresp = NULL;
442 smtpstate state1 = SMTP_STOP;
443 smtpstate state2 = SMTP_STOP;
445 /* Check we have a username and password to authenticate with and end the
446 connect phase if we don't */
447 if(!conn->bits.user_passwd) {
448 state(conn, SMTP_STOP);
453 /* Calculate the supported authentication mechanism, by decreasing order of
454 security, as well as the initial response where appropriate */
455 #ifndef CURL_DISABLE_CRYPTO_AUTH
456 if((smtpc->authmechs & SASL_MECH_DIGEST_MD5) &&
457 (smtpc->prefmech & SASL_MECH_DIGEST_MD5)) {
458 mech = SASL_MECH_STRING_DIGEST_MD5;
459 state1 = SMTP_AUTH_DIGESTMD5;
460 smtpc->authused = SASL_MECH_DIGEST_MD5;
462 else if((smtpc->authmechs & SASL_MECH_CRAM_MD5) &&
463 (smtpc->prefmech & SASL_MECH_CRAM_MD5)) {
464 mech = SASL_MECH_STRING_CRAM_MD5;
465 state1 = SMTP_AUTH_CRAMMD5;
466 smtpc->authused = SASL_MECH_CRAM_MD5;
471 if((smtpc->authmechs & SASL_MECH_NTLM) &&
472 (smtpc->prefmech & SASL_MECH_NTLM)) {
473 mech = SASL_MECH_STRING_NTLM;
474 state1 = SMTP_AUTH_NTLM;
475 state2 = SMTP_AUTH_NTLM_TYPE2MSG;
476 smtpc->authused = SASL_MECH_NTLM;
478 if(data->set.sasl_ir)
479 result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
485 if(((smtpc->authmechs & SASL_MECH_XOAUTH2) &&
486 (smtpc->prefmech & SASL_MECH_XOAUTH2) &&
487 (smtpc->prefmech != SASL_AUTH_ANY)) || conn->xoauth2_bearer) {
488 mech = SASL_MECH_STRING_XOAUTH2;
489 state1 = SMTP_AUTH_XOAUTH2;
490 state2 = SMTP_AUTH_FINAL;
491 smtpc->authused = SASL_MECH_XOAUTH2;
493 if(data->set.sasl_ir)
494 result = Curl_sasl_create_xoauth2_message(conn->data, conn->user,
495 conn->xoauth2_bearer,
498 else if((smtpc->authmechs & SASL_MECH_LOGIN) &&
499 (smtpc->prefmech & SASL_MECH_LOGIN)) {
500 mech = SASL_MECH_STRING_LOGIN;
501 state1 = SMTP_AUTH_LOGIN;
502 state2 = SMTP_AUTH_LOGIN_PASSWD;
503 smtpc->authused = SASL_MECH_LOGIN;
505 if(data->set.sasl_ir)
506 result = Curl_sasl_create_login_message(conn->data, conn->user,
509 else if((smtpc->authmechs & SASL_MECH_PLAIN) &&
510 (smtpc->prefmech & SASL_MECH_PLAIN)) {
511 mech = SASL_MECH_STRING_PLAIN;
512 state1 = SMTP_AUTH_PLAIN;
513 state2 = SMTP_AUTH_FINAL;
514 smtpc->authused = SASL_MECH_PLAIN;
516 if(data->set.sasl_ir)
517 result = Curl_sasl_create_plain_message(conn->data, conn->user,
518 conn->passwd, &initresp, &len);
523 /* Perform SASL based authentication */
525 8 + strlen(mech) + len <= 512) { /* AUTH <mech> ...<crlf> */
526 result = Curl_pp_sendf(&smtpc->pp, "AUTH %s %s", mech, initresp);
532 result = Curl_pp_sendf(&smtpc->pp, "AUTH %s", mech);
538 Curl_safefree(initresp);
541 /* Other mechanisms not supported */
542 infof(conn->data, "No known authentication mechanisms supported!\n");
543 result = CURLE_LOGIN_DENIED;
550 /***********************************************************************
552 * smtp_perform_mail()
554 * Sends an MAIL command to initiate the upload of a message.
556 static CURLcode smtp_perform_mail(struct connectdata *conn)
561 CURLcode result = CURLE_OK;
562 struct SessionHandle *data = conn->data;
564 /* Calculate the FROM parameter */
565 if(!data->set.str[STRING_MAIL_FROM])
566 /* Null reverse-path, RFC-5321, sect. 3.6.3 */
568 else if(data->set.str[STRING_MAIL_FROM][0] == '<')
569 from = aprintf("%s", data->set.str[STRING_MAIL_FROM]);
571 from = aprintf("<%s>", data->set.str[STRING_MAIL_FROM]);
574 return CURLE_OUT_OF_MEMORY;
576 /* Calculate the optional AUTH parameter */
577 if(data->set.str[STRING_MAIL_AUTH] && conn->proto.smtpc.authused) {
578 if(data->set.str[STRING_MAIL_AUTH][0] != '\0')
579 auth = aprintf("%s", data->set.str[STRING_MAIL_AUTH]);
581 /* Empty AUTH, RFC-2554, sect. 5 */
587 return CURLE_OUT_OF_MEMORY;
591 /* Calculate the optional SIZE parameter */
592 if(conn->proto.smtpc.size_supported && conn->data->set.infilesize > 0) {
593 size = aprintf("%" FORMAT_OFF_T, data->set.infilesize);
599 return CURLE_OUT_OF_MEMORY;
603 /* Send the MAIL command */
605 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
606 "MAIL FROM:%s", from);
607 else if(auth && !size)
608 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
609 "MAIL FROM:%s AUTH=%s", from, auth);
610 else if(auth && size)
611 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
612 "MAIL FROM:%s AUTH=%s SIZE=%s", from, auth, size);
614 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
615 "MAIL FROM:%s SIZE=%s", from, size);
622 state(conn, SMTP_MAIL);
627 /***********************************************************************
629 * smtp_perform_rcpt_to()
631 * Sends a RCPT TO command for a given recipient as part of the message upload
634 static CURLcode smtp_perform_rcpt_to(struct connectdata *conn)
636 CURLcode result = CURLE_OK;
637 struct SessionHandle *data = conn->data;
638 struct SMTP *smtp = data->req.protop;
640 /* Send the RCPT TO command */
642 if(smtp->rcpt->data[0] == '<')
643 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:%s",
646 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:<%s>",
649 state(conn, SMTP_RCPT);
655 /***********************************************************************
657 * smtp_perform_quit()
659 * Performs the quit action prior to sclose() being called.
661 static CURLcode smtp_perform_quit(struct connectdata *conn)
663 CURLcode result = CURLE_OK;
665 /* Send the QUIT command */
666 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "QUIT");
669 state(conn, SMTP_QUIT);
674 /* For the initial server greeting */
675 static CURLcode smtp_state_servergreet_resp(struct connectdata *conn,
679 CURLcode result = CURLE_OK;
680 struct SessionHandle *data = conn->data;
682 (void)instate; /* no use for this yet */
684 if(smtpcode/100 != 2) {
685 failf(data, "Got unexpected smtp-server response: %d", smtpcode);
686 result = CURLE_FTP_WEIRD_SERVER_REPLY;
689 result = smtp_perform_ehlo(conn);
694 /* For STARTTLS responses */
695 static CURLcode smtp_state_starttls_resp(struct connectdata *conn,
699 CURLcode result = CURLE_OK;
700 struct SessionHandle *data = conn->data;
702 (void)instate; /* no use for this yet */
704 if(smtpcode != 220) {
705 if(data->set.use_ssl != CURLUSESSL_TRY) {
706 failf(data, "STARTTLS denied. %c", smtpcode);
707 result = CURLE_USE_SSL_FAILED;
710 result = smtp_perform_authenticate(conn);
713 result = smtp_perform_upgrade_tls(conn);
718 /* For EHLO responses */
719 static CURLcode smtp_state_ehlo_resp(struct connectdata *conn, int smtpcode,
722 CURLcode result = CURLE_OK;
723 struct SessionHandle *data = conn->data;
724 struct smtp_conn *smtpc = &conn->proto.smtpc;
725 const char *line = data->state.buffer;
726 size_t len = strlen(line);
729 (void)instate; /* no use for this yet */
731 if(smtpcode/100 != 2 && smtpcode != 1) {
732 if((data->set.use_ssl <= CURLUSESSL_TRY || conn->ssl[FIRSTSOCKET].use) &&
733 !conn->bits.user_passwd)
734 result = smtp_perform_helo(conn);
736 failf(data, "Remote access denied: %d", smtpcode);
737 result = CURLE_REMOTE_ACCESS_DENIED;
744 /* Does the server support the STARTTLS capability? */
745 if(len >= 8 && !memcmp(line, "STARTTLS", 8))
746 smtpc->tls_supported = TRUE;
748 /* Does the server support the SIZE capability? */
749 else if(len >= 4 && !memcmp(line, "SIZE", 4))
750 smtpc->size_supported = TRUE;
752 /* Do we have the authentication mechanism list? */
753 else if(len >= 5 && !memcmp(line, "AUTH ", 5)) {
757 /* Loop through the data line */
760 (*line == ' ' || *line == '\t' ||
761 *line == '\r' || *line == '\n')) {
770 /* Extract the word */
771 for(wordlen = 0; wordlen < len && line[wordlen] != ' ' &&
772 line[wordlen] != '\t' && line[wordlen] != '\r' &&
773 line[wordlen] != '\n';)
776 /* Test the word for a matching authentication mechanism */
777 if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_LOGIN))
778 smtpc->authmechs |= SASL_MECH_LOGIN;
779 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_PLAIN))
780 smtpc->authmechs |= SASL_MECH_PLAIN;
781 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_CRAM_MD5))
782 smtpc->authmechs |= SASL_MECH_CRAM_MD5;
783 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_DIGEST_MD5))
784 smtpc->authmechs |= SASL_MECH_DIGEST_MD5;
785 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_GSSAPI))
786 smtpc->authmechs |= SASL_MECH_GSSAPI;
787 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_EXTERNAL))
788 smtpc->authmechs |= SASL_MECH_EXTERNAL;
789 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_NTLM))
790 smtpc->authmechs |= SASL_MECH_NTLM;
791 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_XOAUTH2))
792 smtpc->authmechs |= SASL_MECH_XOAUTH2;
800 if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
801 /* We don't have a SSL/TLS connection yet, but SSL is requested */
802 if(smtpc->tls_supported)
803 /* Switch to TLS connection now */
804 result = smtp_perform_starttls(conn);
805 else if(data->set.use_ssl == CURLUSESSL_TRY)
806 /* Fallback and carry on with authentication */
807 result = smtp_perform_authenticate(conn);
809 failf(data, "STARTTLS not supported.");
810 result = CURLE_USE_SSL_FAILED;
814 result = smtp_perform_authenticate(conn);
821 /* For HELO responses */
822 static CURLcode smtp_state_helo_resp(struct connectdata *conn, int smtpcode,
825 CURLcode result = CURLE_OK;
826 struct SessionHandle *data = conn->data;
828 (void)instate; /* no use for this yet */
830 if(smtpcode/100 != 2) {
831 failf(data, "Remote access denied: %d", smtpcode);
832 result = CURLE_REMOTE_ACCESS_DENIED;
835 /* End of connect phase */
836 state(conn, SMTP_STOP);
841 /* For AUTH PLAIN (without initial response) responses */
842 static CURLcode smtp_state_auth_plain_resp(struct connectdata *conn,
846 CURLcode result = CURLE_OK;
847 struct SessionHandle *data = conn->data;
849 char *plainauth = NULL;
851 (void)instate; /* no use for this yet */
853 if(smtpcode != 334) {
854 failf(data, "Access denied: %d", smtpcode);
855 result = CURLE_LOGIN_DENIED;
858 /* Create the authorisation message */
859 result = Curl_sasl_create_plain_message(conn->data, conn->user,
860 conn->passwd, &plainauth, &len);
861 if(!result && plainauth) {
862 /* Send the message */
863 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", plainauth);
866 state(conn, SMTP_AUTH_FINAL);
870 Curl_safefree(plainauth);
875 /* For AUTH LOGIN (without initial response) responses */
876 static CURLcode smtp_state_auth_login_resp(struct connectdata *conn,
880 CURLcode result = CURLE_OK;
881 struct SessionHandle *data = conn->data;
883 char *authuser = NULL;
885 (void)instate; /* no use for this yet */
887 if(smtpcode != 334) {
888 failf(data, "Access denied: %d", smtpcode);
889 result = CURLE_LOGIN_DENIED;
892 /* Create the user message */
893 result = Curl_sasl_create_login_message(conn->data, conn->user,
895 if(!result && authuser) {
897 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", authuser);
900 state(conn, SMTP_AUTH_LOGIN_PASSWD);
904 Curl_safefree(authuser);
909 /* For AUTH LOGIN user entry responses */
910 static CURLcode smtp_state_auth_login_password_resp(struct connectdata *conn,
914 CURLcode result = CURLE_OK;
915 struct SessionHandle *data = conn->data;
917 char *authpasswd = NULL;
919 (void)instate; /* no use for this yet */
921 if(smtpcode != 334) {
922 failf(data, "Access denied: %d", smtpcode);
923 result = CURLE_LOGIN_DENIED;
926 /* Create the password message */
927 result = Curl_sasl_create_login_message(conn->data, conn->passwd,
929 if(!result && authpasswd) {
930 /* Send the password */
931 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", authpasswd);
934 state(conn, SMTP_AUTH_FINAL);
938 Curl_safefree(authpasswd);
943 #ifndef CURL_DISABLE_CRYPTO_AUTH
944 /* For AUTH CRAM-MD5 responses */
945 static CURLcode smtp_state_auth_cram_resp(struct connectdata *conn,
949 CURLcode result = CURLE_OK;
950 struct SessionHandle *data = conn->data;
953 char *rplyb64 = NULL;
956 (void)instate; /* no use for this yet */
958 if(smtpcode != 334) {
959 failf(data, "Access denied: %d", smtpcode);
960 return CURLE_LOGIN_DENIED;
963 /* Get the challenge message */
964 smtp_get_message(data->state.buffer, &chlg64);
966 /* Decode the challenge message */
967 result = Curl_sasl_decode_cram_md5_message(chlg64, &chlg, &len);
969 /* Send the cancellation */
970 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "*");
973 state(conn, SMTP_AUTH_CANCEL);
976 /* Create the response message */
977 result = Curl_sasl_create_cram_md5_message(data, chlg, conn->user,
978 conn->passwd, &rplyb64, &len);
979 if(!result && rplyb64) {
980 /* Send the response */
981 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", rplyb64);
984 state(conn, SMTP_AUTH_FINAL);
989 Curl_safefree(rplyb64);
994 /* For AUTH DIGEST-MD5 challenge responses */
995 static CURLcode smtp_state_auth_digest_resp(struct connectdata *conn,
999 CURLcode result = CURLE_OK;
1000 struct SessionHandle *data = conn->data;
1001 char *chlg64 = NULL;
1002 char *rplyb64 = NULL;
1009 (void)instate; /* no use for this yet */
1011 if(smtpcode != 334) {
1012 failf(data, "Access denied: %d", smtpcode);
1013 return CURLE_LOGIN_DENIED;
1016 /* Get the challenge message */
1017 smtp_get_message(data->state.buffer, &chlg64);
1019 /* Decode the challange message */
1020 result = Curl_sasl_decode_digest_md5_message(chlg64, nonce, sizeof(nonce),
1021 realm, sizeof(realm),
1022 algorithm, sizeof(algorithm));
1023 if(result || strcmp(algorithm, "md5-sess") != 0) {
1024 /* Send the cancellation */
1025 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "*");
1028 state(conn, SMTP_AUTH_CANCEL);
1031 /* Create the response message */
1032 result = Curl_sasl_create_digest_md5_message(data, nonce, realm,
1033 conn->user, conn->passwd,
1034 "smtp", &rplyb64, &len);
1035 if(!result && rplyb64) {
1036 /* Send the response */
1037 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", rplyb64);
1040 state(conn, SMTP_AUTH_DIGESTMD5_RESP);
1044 Curl_safefree(rplyb64);
1049 /* For AUTH DIGEST-MD5 challenge-response responses */
1050 static CURLcode smtp_state_auth_digest_resp_resp(struct connectdata *conn,
1054 CURLcode result = CURLE_OK;
1055 struct SessionHandle *data = conn->data;
1057 (void)instate; /* no use for this yet */
1059 if(smtpcode != 334) {
1060 failf(data, "Authentication failed: %d", smtpcode);
1061 result = CURLE_LOGIN_DENIED;
1064 /* Send an empty response */
1065 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "");
1068 state(conn, SMTP_AUTH_FINAL);
1077 /* For AUTH NTLM (without initial response) responses */
1078 static CURLcode smtp_state_auth_ntlm_resp(struct connectdata *conn,
1082 CURLcode result = CURLE_OK;
1083 struct SessionHandle *data = conn->data;
1084 char *type1msg = NULL;
1087 (void)instate; /* no use for this yet */
1089 if(smtpcode != 334) {
1090 failf(data, "Access denied: %d", smtpcode);
1091 result = CURLE_LOGIN_DENIED;
1094 /* Create the type-1 message */
1095 result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
1098 if(!result && type1msg) {
1099 /* Send the message */
1100 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", type1msg);
1103 state(conn, SMTP_AUTH_NTLM_TYPE2MSG);
1107 Curl_safefree(type1msg);
1112 /* For NTLM type-2 responses (sent in reponse to our type-1 message) */
1113 static CURLcode smtp_state_auth_ntlm_type2msg_resp(struct connectdata *conn,
1117 CURLcode result = CURLE_OK;
1118 struct SessionHandle *data = conn->data;
1119 char *type2msg = NULL;
1120 char *type3msg = NULL;
1123 (void)instate; /* no use for this yet */
1125 if(smtpcode != 334) {
1126 failf(data, "Access denied: %d", smtpcode);
1127 result = CURLE_LOGIN_DENIED;
1130 /* Get the type-2 message */
1131 smtp_get_message(data->state.buffer, &type2msg);
1133 /* Decode the type-2 message */
1134 result = Curl_sasl_decode_ntlm_type2_message(data, type2msg, &conn->ntlm);
1136 /* Send the cancellation */
1137 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "*");
1140 state(conn, SMTP_AUTH_CANCEL);
1143 /* Create the type-3 message */
1144 result = Curl_sasl_create_ntlm_type3_message(data, conn->user,
1145 conn->passwd, &conn->ntlm,
1147 if(!result && type3msg) {
1148 /* Send the message */
1149 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", type3msg);
1152 state(conn, SMTP_AUTH_FINAL);
1157 Curl_safefree(type3msg);
1163 /* For AUTH XOAUTH2 (without initial response) responses */
1164 static CURLcode smtp_state_auth_xoauth2_resp(struct connectdata *conn,
1165 int smtpcode, smtpstate instate)
1167 CURLcode result = CURLE_OK;
1168 struct SessionHandle *data = conn->data;
1170 char *xoauth = NULL;
1172 (void)instate; /* no use for this yet */
1174 if(smtpcode != 334) {
1175 failf(data, "Access denied: %d", smtpcode);
1176 result = CURLE_LOGIN_DENIED;
1179 /* Create the authorisation message */
1180 result = Curl_sasl_create_xoauth2_message(conn->data, conn->user,
1181 conn->xoauth2_bearer,
1183 if(!result && xoauth) {
1184 /* Send the message */
1185 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", xoauth);
1188 state(conn, SMTP_AUTH_FINAL);
1192 Curl_safefree(xoauth);
1197 /* For AUTH cancellation responses */
1198 static CURLcode smtp_state_auth_cancel_resp(struct connectdata *conn,
1202 struct SessionHandle *data = conn->data;
1205 (void)instate; /* no use for this yet */
1207 failf(data, "Authentication cancelled");
1209 return CURLE_LOGIN_DENIED;
1212 /* For final responses in the AUTH sequence */
1213 static CURLcode smtp_state_auth_final_resp(struct connectdata *conn,
1217 CURLcode result = CURLE_OK;
1218 struct SessionHandle *data = conn->data;
1220 (void)instate; /* no use for this yet */
1222 if(smtpcode != 235) {
1223 failf(data, "Authentication failed: %d", smtpcode);
1224 result = CURLE_LOGIN_DENIED;
1227 /* End of connect phase */
1228 state(conn, SMTP_STOP);
1233 /* For MAIL responses */
1234 static CURLcode smtp_state_mail_resp(struct connectdata *conn, int smtpcode,
1237 CURLcode result = CURLE_OK;
1238 struct SessionHandle *data = conn->data;
1239 struct SMTP *smtp = data->req.protop;
1241 (void)instate; /* no use for this yet */
1243 if(smtpcode/100 != 2) {
1244 failf(data, "MAIL failed: %d", smtpcode);
1245 result = CURLE_SEND_ERROR;
1246 state(conn, SMTP_STOP);
1249 smtp->rcpt = data->set.mail_rcpt;
1251 result = smtp_perform_rcpt_to(conn);
1257 /* For RCPT responses */
1258 static CURLcode smtp_state_rcpt_resp(struct connectdata *conn, int smtpcode,
1261 CURLcode result = CURLE_OK;
1262 struct SessionHandle *data = conn->data;
1263 struct SMTP *smtp = data->req.protop;
1265 (void)instate; /* no use for this yet */
1267 if(smtpcode/100 != 2) {
1268 failf(data, "RCPT failed: %d", smtpcode);
1269 result = CURLE_SEND_ERROR;
1270 state(conn, SMTP_STOP);
1274 smtp->rcpt = smtp->rcpt->next;
1275 result = smtp_perform_rcpt_to(conn);
1277 /* If we failed or still are sending RCPT data then return */
1278 if(result || smtp->rcpt)
1282 /* Send the DATA command */
1283 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "DATA");
1286 state(conn, SMTP_DATA);
1292 /* For DATA response */
1293 static CURLcode smtp_state_data_resp(struct connectdata *conn, int smtpcode,
1296 struct SessionHandle *data = conn->data;
1298 (void)instate; /* no use for this yet */
1300 if(smtpcode != 354) {
1301 state(conn, SMTP_STOP);
1302 return CURLE_SEND_ERROR;
1305 /* Set the progress upload size */
1306 Curl_pgrsSetUploadSize(data, data->set.infilesize);
1309 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL);
1311 /* End of DO phase */
1312 state(conn, SMTP_STOP);
1317 /* For POSTDATA responses, which are received after the entire DATA
1318 part has been sent to the server */
1319 static CURLcode smtp_state_postdata_resp(struct connectdata *conn,
1323 CURLcode result = CURLE_OK;
1325 (void)instate; /* no use for this yet */
1328 result = CURLE_RECV_ERROR;
1330 /* End of DONE phase */
1331 state(conn, SMTP_STOP);
1336 static CURLcode smtp_statemach_act(struct connectdata *conn)
1338 CURLcode result = CURLE_OK;
1339 curl_socket_t sock = conn->sock[FIRSTSOCKET];
1340 struct SessionHandle *data = conn->data;
1342 struct smtp_conn *smtpc = &conn->proto.smtpc;
1343 struct pingpong *pp = &smtpc->pp;
1346 /* Busy upgrading the connection; right now all I/O is SSL/TLS, not SMTP */
1347 if(smtpc->state == SMTP_UPGRADETLS)
1348 return smtp_perform_upgrade_tls(conn);
1350 /* Flush any data that needs to be sent */
1352 return Curl_pp_flushsend(pp);
1355 /* Read the response from the server */
1356 result = Curl_pp_readresp(sock, pp, &smtpcode, &nread);
1360 /* Store the latest response for later retrieval if necessary */
1361 if(smtpc->state != SMTP_QUIT && smtpcode != 1)
1362 data->info.httpcode = smtpcode;
1367 /* We have now received a full SMTP server response */
1368 switch(smtpc->state) {
1369 case SMTP_SERVERGREET:
1370 result = smtp_state_servergreet_resp(conn, smtpcode, smtpc->state);
1374 result = smtp_state_ehlo_resp(conn, smtpcode, smtpc->state);
1378 result = smtp_state_helo_resp(conn, smtpcode, smtpc->state);
1382 result = smtp_state_starttls_resp(conn, smtpcode, smtpc->state);
1385 case SMTP_AUTH_PLAIN:
1386 result = smtp_state_auth_plain_resp(conn, smtpcode, smtpc->state);
1389 case SMTP_AUTH_LOGIN:
1390 result = smtp_state_auth_login_resp(conn, smtpcode, smtpc->state);
1393 case SMTP_AUTH_LOGIN_PASSWD:
1394 result = smtp_state_auth_login_password_resp(conn, smtpcode,
1398 #ifndef CURL_DISABLE_CRYPTO_AUTH
1399 case SMTP_AUTH_CRAMMD5:
1400 result = smtp_state_auth_cram_resp(conn, smtpcode, smtpc->state);
1403 case SMTP_AUTH_DIGESTMD5:
1404 result = smtp_state_auth_digest_resp(conn, smtpcode, smtpc->state);
1407 case SMTP_AUTH_DIGESTMD5_RESP:
1408 result = smtp_state_auth_digest_resp_resp(conn, smtpcode, smtpc->state);
1413 case SMTP_AUTH_NTLM:
1414 result = smtp_state_auth_ntlm_resp(conn, smtpcode, smtpc->state);
1417 case SMTP_AUTH_NTLM_TYPE2MSG:
1418 result = smtp_state_auth_ntlm_type2msg_resp(conn, smtpcode,
1423 case SMTP_AUTH_XOAUTH2:
1424 result = smtp_state_auth_xoauth2_resp(conn, smtpcode, smtpc->state);
1427 case SMTP_AUTH_CANCEL:
1428 result = smtp_state_auth_cancel_resp(conn, smtpcode, smtpc->state);
1431 case SMTP_AUTH_FINAL:
1432 result = smtp_state_auth_final_resp(conn, smtpcode, smtpc->state);
1436 result = smtp_state_mail_resp(conn, smtpcode, smtpc->state);
1440 result = smtp_state_rcpt_resp(conn, smtpcode, smtpc->state);
1444 result = smtp_state_data_resp(conn, smtpcode, smtpc->state);
1448 result = smtp_state_postdata_resp(conn, smtpcode, smtpc->state);
1452 /* fallthrough, just stop! */
1454 /* internal error */
1455 state(conn, SMTP_STOP);
1458 } while(!result && smtpc->state != SMTP_STOP && Curl_pp_moredata(pp));
1463 /* Called repeatedly until done from multi.c */
1464 static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done)
1466 CURLcode result = CURLE_OK;
1467 struct smtp_conn *smtpc = &conn->proto.smtpc;
1469 if((conn->handler->flags & PROTOPT_SSL) && !smtpc->ssldone) {
1470 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone);
1471 if(result || !smtpc->ssldone)
1475 result = Curl_pp_statemach(&smtpc->pp, FALSE);
1476 *done = (smtpc->state == SMTP_STOP) ? TRUE : FALSE;
1481 static CURLcode smtp_block_statemach(struct connectdata *conn)
1483 CURLcode result = CURLE_OK;
1484 struct smtp_conn *smtpc = &conn->proto.smtpc;
1486 while(smtpc->state != SMTP_STOP && !result)
1487 result = Curl_pp_statemach(&smtpc->pp, TRUE);
1492 /* Allocate and initialize the SMTP struct for the current SessionHandle if
1494 static CURLcode smtp_init(struct connectdata *conn)
1496 CURLcode result = CURLE_OK;
1497 struct SessionHandle *data = conn->data;
1500 smtp = data->req.protop = calloc(sizeof(struct SMTP), 1);
1502 result = CURLE_OUT_OF_MEMORY;
1507 /* For the SMTP "protocol connect" and "doing" phases only */
1508 static int smtp_getsock(struct connectdata *conn, curl_socket_t *socks,
1511 return Curl_pp_getsock(&conn->proto.smtpc.pp, socks, numsocks);
1514 /***********************************************************************
1518 * This function should do everything that is to be considered a part of
1519 * the connection phase.
1521 * The variable pointed to by 'done' will be TRUE if the protocol-layer
1522 * connect phase is done when this function returns, or FALSE if not.
1524 static CURLcode smtp_connect(struct connectdata *conn, bool *done)
1526 CURLcode result = CURLE_OK;
1527 struct smtp_conn *smtpc = &conn->proto.smtpc;
1528 struct pingpong *pp = &smtpc->pp;
1530 *done = FALSE; /* default to not done yet */
1532 /* We always support persistent connections in SMTP */
1533 conn->bits.close = FALSE;
1535 /* Set the default response time-out */
1536 pp->response_time = RESP_TIMEOUT;
1537 pp->statemach_act = smtp_statemach_act;
1538 pp->endofresp = smtp_endofresp;
1541 /* Set the default preferred authentication mechanism */
1542 smtpc->prefmech = SASL_AUTH_ANY;
1544 /* Initialise the pingpong layer */
1547 /* Parse the URL options */
1548 result = smtp_parse_url_options(conn);
1552 /* Parse the URL path */
1553 result = smtp_parse_url_path(conn);
1557 /* Start off waiting for the server greeting response */
1558 state(conn, SMTP_SERVERGREET);
1560 result = smtp_multi_statemach(conn, done);
1565 /***********************************************************************
1569 * The DONE function. This does what needs to be done after a single DO has
1572 * Input argument is already checked for validity.
1574 static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
1577 CURLcode result = CURLE_OK;
1578 struct SessionHandle *data = conn->data;
1579 struct SMTP *smtp = data->req.protop;
1580 struct pingpong *pp = &conn->proto.smtpc.pp;
1583 ssize_t bytes_written;
1588 /* When the easy handle is removed from the multi interface while libcurl
1589 is still trying to resolve the host name, the SMTP struct is not yet
1590 initialized. However, the removal action calls Curl_done() which in
1591 turn calls this function, so we simply return success. */
1595 conn->bits.close = TRUE; /* marked for closure */
1596 result = status; /* use the already set error code */
1598 else if(!data->set.connect_only) {
1599 /* Calculate the EOB taking into account any terminating CRLF from the
1600 previous line of the email or the CRLF of the DATA command when there
1601 is "no mail data". RFC-5321, sect. 4.1.1.4. */
1604 if(smtp->trailing_crlf || !conn->data->set.infilesize) {
1609 /* Send the end of block data */
1610 result = Curl_write(conn, conn->writesockfd, eob, len, &bytes_written);
1614 if(bytes_written != len) {
1615 /* The whole chunk was not sent so keep it around and adjust the
1616 pingpong structure accordingly */
1617 pp->sendthis = strdup(eob);
1619 pp->sendleft = len - bytes_written;
1622 /* Successfully sent so adjust the response timeout relative to now */
1623 pp->response = Curl_tvnow();
1625 state(conn, SMTP_POSTDATA);
1627 /* Run the state-machine
1629 TODO: when the multi interface is used, this _really_ should be using
1630 the smtp_multi_statemach function but we have no general support for
1631 non-blocking DONE operations, not in the multi state machine and with
1632 Curl_done() invokes on several places in the code!
1634 result = smtp_block_statemach(conn);
1637 /* Clear the transfer mode for the next request */
1638 smtp->transfer = FTPTRANSFER_BODY;
1643 /***********************************************************************
1647 * This is the actual DO function for SMTP. Send a mail according to the
1648 * options previously setup.
1650 static CURLcode smtp_perform(struct connectdata *conn, bool *connected,
1653 /* This is SMTP and no proxy */
1654 CURLcode result = CURLE_OK;
1656 DEBUGF(infof(conn->data, "DO phase starts\n"));
1658 if(conn->data->set.opt_no_body) {
1659 /* Requested no body means no transfer */
1660 struct SMTP *smtp = conn->data->req.protop;
1661 smtp->transfer = FTPTRANSFER_INFO;
1664 *dophase_done = FALSE; /* not done yet */
1666 /* Start the first command in the DO phase */
1667 result = smtp_perform_mail(conn);
1671 /* run the state-machine */
1672 result = smtp_multi_statemach(conn, dophase_done);
1674 *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1677 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1682 /***********************************************************************
1686 * This function is registered as 'curl_do' function. It decodes the path
1687 * parts etc as a wrapper to the actual DO function (smtp_perform).
1689 * The input argument is already checked for validity.
1691 static CURLcode smtp_do(struct connectdata *conn, bool *done)
1693 CURLcode result = CURLE_OK;
1695 *done = FALSE; /* default to false */
1697 result = smtp_regular_transfer(conn, done);
1702 /***********************************************************************
1706 * Disconnect from an SMTP server. Cleanup protocol-specific per-connection
1707 * resources. BLOCKING.
1709 static CURLcode smtp_disconnect(struct connectdata *conn, bool dead_connection)
1711 struct smtp_conn *smtpc = &conn->proto.smtpc;
1713 /* We cannot send quit unconditionally. If this connection is stale or
1714 bad in any way, sending quit and waiting around here will make the
1715 disconnect wait in vain and cause more problems than we need to. */
1717 /* The SMTP session may or may not have been allocated/setup at this
1719 if(!dead_connection && smtpc->pp.conn && smtpc->pp.conn->bits.protoconnstart)
1720 if(!smtp_perform_quit(conn))
1721 (void)smtp_block_statemach(conn); /* ignore errors on QUIT */
1723 /* Disconnect from the server */
1724 Curl_pp_disconnect(&smtpc->pp);
1726 /* Cleanup the SASL module */
1727 Curl_sasl_cleanup(conn, smtpc->authused);
1729 /* Cleanup our connection based variables */
1730 Curl_safefree(smtpc->domain);
1735 /* Call this when the DO phase has completed */
1736 static CURLcode smtp_dophase_done(struct connectdata *conn, bool connected)
1738 struct SMTP *smtp = conn->data->req.protop;
1742 if(smtp->transfer != FTPTRANSFER_BODY)
1743 /* no data to transfer */
1744 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1749 /* Called from multi.c while DOing */
1750 static CURLcode smtp_doing(struct connectdata *conn, bool *dophase_done)
1752 CURLcode result = smtp_multi_statemach(conn, dophase_done);
1755 DEBUGF(infof(conn->data, "DO phase failed\n"));
1756 else if(*dophase_done) {
1757 result = smtp_dophase_done(conn, FALSE /* not connected */);
1759 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1765 /***********************************************************************
1767 * smtp_regular_transfer()
1769 * The input argument is already checked for validity.
1771 * Performs all commands done before a regular transfer between a local and a
1774 static CURLcode smtp_regular_transfer(struct connectdata *conn,
1777 CURLcode result = CURLE_OK;
1778 bool connected = FALSE;
1779 struct SessionHandle *data = conn->data;
1781 /* Make sure size is unknown at this point */
1782 data->req.size = -1;
1784 /* Set the progress data */
1785 Curl_pgrsSetUploadCounter(data, 0);
1786 Curl_pgrsSetDownloadCounter(data, 0);
1787 Curl_pgrsSetUploadSize(data, 0);
1788 Curl_pgrsSetDownloadSize(data, 0);
1790 /* Carry out the perform */
1791 result = smtp_perform(conn, &connected, dophase_done);
1793 /* Perform post DO phase operations if necessary */
1794 if(!result && *dophase_done)
1795 result = smtp_dophase_done(conn, connected);
1800 static CURLcode smtp_setup_connection(struct connectdata *conn)
1802 struct SessionHandle *data = conn->data;
1805 if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
1806 /* Unless we have asked to tunnel SMTP operations through the proxy, we
1807 switch and use HTTP operations only */
1808 #ifndef CURL_DISABLE_HTTP
1809 if(conn->handler == &Curl_handler_smtp)
1810 conn->handler = &Curl_handler_smtp_proxy;
1813 conn->handler = &Curl_handler_smtps_proxy;
1815 failf(data, "SMTPS not supported!");
1816 return CURLE_UNSUPPORTED_PROTOCOL;
1819 /* set it up as a HTTP connection instead */
1820 return conn->handler->setup_connection(conn);
1823 failf(data, "SMTP over http proxy requires HTTP support built-in!");
1824 return CURLE_UNSUPPORTED_PROTOCOL;
1828 /* Initialise the SMTP layer */
1829 result = smtp_init(conn);
1833 data->state.path++; /* don't include the initial slash */
1838 /***********************************************************************
1840 * smtp_parse_url_options()
1842 * Parse the URL login options.
1844 static CURLcode smtp_parse_url_options(struct connectdata *conn)
1846 CURLcode result = CURLE_OK;
1847 struct smtp_conn *smtpc = &conn->proto.smtpc;
1848 const char *options = conn->options;
1849 const char *ptr = options;
1852 const char *key = ptr;
1854 while(*ptr && *ptr != '=')
1857 if(strnequal(key, "AUTH", 4)) {
1858 const char *value = ptr + 1;
1860 if(strequal(value, "*"))
1861 smtpc->prefmech = SASL_AUTH_ANY;
1862 else if(strequal(value, SASL_MECH_STRING_LOGIN))
1863 smtpc->prefmech = SASL_MECH_LOGIN;
1864 else if(strequal(value, SASL_MECH_STRING_PLAIN))
1865 smtpc->prefmech = SASL_MECH_PLAIN;
1866 else if(strequal(value, SASL_MECH_STRING_CRAM_MD5))
1867 smtpc->prefmech = SASL_MECH_CRAM_MD5;
1868 else if(strequal(value, SASL_MECH_STRING_DIGEST_MD5))
1869 smtpc->prefmech = SASL_MECH_DIGEST_MD5;
1870 else if(strequal(value, SASL_MECH_STRING_GSSAPI))
1871 smtpc->prefmech = SASL_MECH_GSSAPI;
1872 else if(strequal(value, SASL_MECH_STRING_NTLM))
1873 smtpc->prefmech = SASL_MECH_NTLM;
1874 else if(strequal(value, SASL_MECH_STRING_XOAUTH2))
1875 smtpc->prefmech = SASL_MECH_XOAUTH2;
1877 smtpc->prefmech = SASL_AUTH_NONE;
1880 result = CURLE_URL_MALFORMAT;
1886 /***********************************************************************
1888 * smtp_parse_url_path()
1890 * Parse the URL path into separate path components.
1892 static CURLcode smtp_parse_url_path(struct connectdata *conn)
1894 /* The SMTP struct is already initialised in smtp_connect() */
1895 struct SessionHandle *data = conn->data;
1896 struct smtp_conn *smtpc = &conn->proto.smtpc;
1897 const char *path = data->state.path;
1898 char localhost[HOSTNAME_MAX + 1];
1900 /* Calculate the path if necessary */
1902 if(!Curl_gethostname(localhost, sizeof(localhost)))
1908 /* URL decode the path and use it as the domain in our EHLO */
1909 return Curl_urldecode(conn->data, path, 0, &smtpc->domain, NULL, TRUE);
1912 CURLcode Curl_smtp_escape_eob(struct connectdata *conn, ssize_t nread)
1914 /* When sending a SMTP payload we must detect CRLF. sequences making sure
1915 they are sent as CRLF.. instead, as a . on the beginning of a line will
1916 be deleted by the server when not part of an EOB terminator and a
1917 genuine CRLF.CRLF which isn't escaped will wrongly be detected as end of
1922 struct SessionHandle *data = conn->data;
1923 struct SMTP *smtp = data->req.protop;
1925 /* Do we need to allocate the scatch buffer? */
1926 if(!data->state.scratch) {
1927 data->state.scratch = malloc(2 * BUFSIZE);
1929 if(!data->state.scratch) {
1930 failf (data, "Failed to alloc scratch buffer!");
1931 return CURLE_OUT_OF_MEMORY;
1935 /* This loop can be improved by some kind of Boyer-Moore style of
1936 approach but that is saved for later... */
1937 for(i = 0, si = 0; i < nread; i++) {
1938 if(SMTP_EOB[smtp->eob] == data->req.upload_fromhere[i]) {
1941 /* Is the EOB potentially the terminating CRLF? */
1942 if(2 == smtp->eob || SMTP_EOB_LEN == smtp->eob)
1943 smtp->trailing_crlf = TRUE;
1945 smtp->trailing_crlf = FALSE;
1947 else if(smtp->eob) {
1948 /* A previous substring matched so output that first */
1949 memcpy(&data->state.scratch[si], SMTP_EOB, smtp->eob);
1952 /* Then compare the first byte */
1953 if(SMTP_EOB[0] == data->req.upload_fromhere[i])
1958 /* Reset the trailing CRLF flag as there was more data */
1959 smtp->trailing_crlf = FALSE;
1962 /* Do we have a match for CRLF. as per RFC-5321, sect. 4.5.2 */
1963 if(SMTP_EOB_FIND_LEN == smtp->eob) {
1964 /* Copy the replacement data to the target buffer */
1965 memcpy(&data->state.scratch[si], SMTP_EOB_REPL, SMTP_EOB_REPL_LEN);
1966 si += SMTP_EOB_REPL_LEN;
1970 data->state.scratch[si++] = data->req.upload_fromhere[i];
1974 /* A substring matched before processing ended so output that now */
1975 memcpy(&data->state.scratch[si], SMTP_EOB, smtp->eob);
1981 /* Only use the new buffer if we replaced something */
1984 /* Upload from the new (replaced) buffer instead */
1985 data->req.upload_fromhere = data->state.scratch;
1987 /* Set the new amount too */
1988 data->req.upload_present = nread;
1994 #endif /* CURL_DISABLE_SMTP */