1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2016, 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"
82 #include "curl_gethostname.h"
83 #include "curl_sasl.h"
85 /* The last 3 #include files should be in this order */
86 #include "curl_printf.h"
87 #include "curl_memory.h"
90 /* Local API functions */
91 static CURLcode
smtp_regular_transfer(struct connectdata
*conn
, bool *done
);
92 static CURLcode
smtp_do(struct connectdata
*conn
, bool *done
);
93 static CURLcode
smtp_done(struct connectdata
*conn
, CURLcode status
,
95 static CURLcode
smtp_connect(struct connectdata
*conn
, bool *done
);
96 static CURLcode
smtp_disconnect(struct connectdata
*conn
, bool dead
);
97 static CURLcode
smtp_multi_statemach(struct connectdata
*conn
, bool *done
);
98 static int smtp_getsock(struct connectdata
*conn
, curl_socket_t
*socks
,
100 static CURLcode
smtp_doing(struct connectdata
*conn
, bool *dophase_done
);
101 static CURLcode
smtp_setup_connection(struct connectdata
*conn
);
102 static CURLcode
smtp_parse_url_options(struct connectdata
*conn
);
103 static CURLcode
smtp_parse_url_path(struct connectdata
*conn
);
104 static CURLcode
smtp_parse_custom_request(struct connectdata
*conn
);
105 static CURLcode
smtp_perform_auth(struct connectdata
*conn
, const char *mech
,
106 const char *initresp
);
107 static CURLcode
smtp_continue_auth(struct connectdata
*conn
, const char *resp
);
108 static void smtp_get_message(char *buffer
, char** outptr
);
111 * SMTP protocol handler.
114 const struct Curl_handler Curl_handler_smtp
= {
116 smtp_setup_connection
, /* setup_connection */
118 smtp_done
, /* done */
119 ZERO_NULL
, /* do_more */
120 smtp_connect
, /* connect_it */
121 smtp_multi_statemach
, /* connecting */
122 smtp_doing
, /* doing */
123 smtp_getsock
, /* proto_getsock */
124 smtp_getsock
, /* doing_getsock */
125 ZERO_NULL
, /* domore_getsock */
126 ZERO_NULL
, /* perform_getsock */
127 smtp_disconnect
, /* disconnect */
128 ZERO_NULL
, /* readwrite */
129 PORT_SMTP
, /* defport */
130 CURLPROTO_SMTP
, /* protocol */
131 PROTOPT_CLOSEACTION
| PROTOPT_NOURLQUERY
/* flags */
136 * SMTPS protocol handler.
139 const struct Curl_handler Curl_handler_smtps
= {
140 "SMTPS", /* scheme */
141 smtp_setup_connection
, /* setup_connection */
143 smtp_done
, /* done */
144 ZERO_NULL
, /* do_more */
145 smtp_connect
, /* connect_it */
146 smtp_multi_statemach
, /* connecting */
147 smtp_doing
, /* doing */
148 smtp_getsock
, /* proto_getsock */
149 smtp_getsock
, /* doing_getsock */
150 ZERO_NULL
, /* domore_getsock */
151 ZERO_NULL
, /* perform_getsock */
152 smtp_disconnect
, /* disconnect */
153 ZERO_NULL
, /* readwrite */
154 PORT_SMTPS
, /* defport */
155 CURLPROTO_SMTPS
, /* protocol */
156 PROTOPT_CLOSEACTION
| PROTOPT_SSL
157 | PROTOPT_NOURLQUERY
/* flags */
161 #ifndef CURL_DISABLE_HTTP
163 * HTTP-proxyed SMTP protocol handler.
166 static const struct Curl_handler Curl_handler_smtp_proxy
= {
168 Curl_http_setup_conn
, /* setup_connection */
169 Curl_http
, /* do_it */
170 Curl_http_done
, /* done */
171 ZERO_NULL
, /* do_more */
172 ZERO_NULL
, /* connect_it */
173 ZERO_NULL
, /* connecting */
174 ZERO_NULL
, /* doing */
175 ZERO_NULL
, /* proto_getsock */
176 ZERO_NULL
, /* doing_getsock */
177 ZERO_NULL
, /* domore_getsock */
178 ZERO_NULL
, /* perform_getsock */
179 ZERO_NULL
, /* disconnect */
180 ZERO_NULL
, /* readwrite */
181 PORT_SMTP
, /* defport */
182 CURLPROTO_HTTP
, /* protocol */
183 PROTOPT_NONE
/* flags */
188 * HTTP-proxyed SMTPS protocol handler.
191 static const struct Curl_handler Curl_handler_smtps_proxy
= {
192 "SMTPS", /* scheme */
193 Curl_http_setup_conn
, /* setup_connection */
194 Curl_http
, /* do_it */
195 Curl_http_done
, /* done */
196 ZERO_NULL
, /* do_more */
197 ZERO_NULL
, /* connect_it */
198 ZERO_NULL
, /* connecting */
199 ZERO_NULL
, /* doing */
200 ZERO_NULL
, /* proto_getsock */
201 ZERO_NULL
, /* doing_getsock */
202 ZERO_NULL
, /* domore_getsock */
203 ZERO_NULL
, /* perform_getsock */
204 ZERO_NULL
, /* disconnect */
205 ZERO_NULL
, /* readwrite */
206 PORT_SMTPS
, /* defport */
207 CURLPROTO_HTTP
, /* protocol */
208 PROTOPT_NONE
/* flags */
213 /* SASL parameters for the smtp protocol */
214 static const struct SASLproto saslsmtp
= {
215 "smtp", /* The service name */
216 334, /* Code received when continuation is expected */
217 235, /* Code to receive upon authentication success */
218 512 - 8, /* Maximum initial response length (no max) */
219 smtp_perform_auth
, /* Send authentication command */
220 smtp_continue_auth
, /* Send authentication continuation */
221 smtp_get_message
/* Get SASL response message */
225 static void smtp_to_smtps(struct connectdata
*conn
)
227 /* Change the connection handler */
228 conn
->handler
= &Curl_handler_smtps
;
230 /* Set the connection's upgraded to TLS flag */
231 conn
->tls_upgraded
= TRUE
;
234 #define smtp_to_smtps(x) Curl_nop_stmt
237 /***********************************************************************
241 * Checks for an ending SMTP status code at the start of the given string, but
242 * also detects various capabilities from the EHLO response including the
243 * supported authentication mechanisms.
245 static bool smtp_endofresp(struct connectdata
*conn
, char *line
, size_t len
,
248 struct smtp_conn
*smtpc
= &conn
->proto
.smtpc
;
252 if(len
< 4 || !ISDIGIT(line
[0]) || !ISDIGIT(line
[1]) || !ISDIGIT(line
[2]))
255 /* Do we have a command response? This should be the response code followed
256 by a space and optionally some text as per RFC-5321 and as outlined in
257 Section 4. Examples of RFC-4954 but some e-mail servers ignore this and
258 only send the response code instead as per Section 4.2. */
259 if(line
[3] == ' ' || len
== 5) {
261 *resp
= curlx_sltosi(strtol(line
, NULL
, 10));
263 /* Make sure real server never sends internal value */
267 /* Do we have a multiline (continuation) response? */
268 else if(line
[3] == '-' &&
269 (smtpc
->state
== SMTP_EHLO
|| smtpc
->state
== SMTP_COMMAND
)) {
271 *resp
= 1; /* Internal response code */
277 /***********************************************************************
281 * Gets the authentication message from the response buffer.
283 static void smtp_get_message(char *buffer
, char** outptr
)
286 char* message
= NULL
;
288 /* Find the start of the message */
289 for(message
= buffer
+ 4; *message
== ' ' || *message
== '\t'; message
++)
292 /* Find the end of the message */
293 for(len
= strlen(message
); len
--;)
294 if(message
[len
] != '\r' && message
[len
] != '\n' && message
[len
] != ' ' &&
295 message
[len
] != '\t')
298 /* Terminate the message */
306 /***********************************************************************
310 * This is the ONLY way to change SMTP state!
312 static void state(struct connectdata
*conn
, smtpstate newstate
)
314 struct smtp_conn
*smtpc
= &conn
->proto
.smtpc
;
315 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
316 /* for debug purposes */
317 static const char * const names
[] = {
334 if(smtpc
->state
!= newstate
)
335 infof(conn
->data
, "SMTP %p state change from %s to %s\n",
336 (void *)smtpc
, names
[smtpc
->state
], names
[newstate
]);
339 smtpc
->state
= newstate
;
342 /***********************************************************************
344 * smtp_perform_ehlo()
346 * Sends the EHLO command to not only initialise communication with the ESMTP
347 * server but to also obtain a list of server side supported capabilities.
349 static CURLcode
smtp_perform_ehlo(struct connectdata
*conn
)
351 CURLcode result
= CURLE_OK
;
352 struct smtp_conn
*smtpc
= &conn
->proto
.smtpc
;
354 smtpc
->sasl
.authmechs
= SASL_AUTH_NONE
; /* No known auth. mechanism yet */
355 smtpc
->sasl
.authused
= SASL_AUTH_NONE
; /* Clear the authentication mechanism
356 used for esmtp connections */
357 smtpc
->tls_supported
= FALSE
; /* Clear the TLS capability */
358 smtpc
->auth_supported
= FALSE
; /* Clear the AUTH capability */
360 /* Send the EHLO command */
361 result
= Curl_pp_sendf(&smtpc
->pp
, "EHLO %s", smtpc
->domain
);
364 state(conn
, SMTP_EHLO
);
369 /***********************************************************************
371 * smtp_perform_helo()
373 * Sends the HELO command to initialise communication with the SMTP server.
375 static CURLcode
smtp_perform_helo(struct connectdata
*conn
)
377 CURLcode result
= CURLE_OK
;
378 struct smtp_conn
*smtpc
= &conn
->proto
.smtpc
;
380 smtpc
->sasl
.authused
= SASL_AUTH_NONE
; /* No authentication mechanism used
381 in smtp connections */
383 /* Send the HELO command */
384 result
= Curl_pp_sendf(&smtpc
->pp
, "HELO %s", smtpc
->domain
);
387 state(conn
, SMTP_HELO
);
392 /***********************************************************************
394 * smtp_perform_starttls()
396 * Sends the STLS command to start the upgrade to TLS.
398 static CURLcode
smtp_perform_starttls(struct connectdata
*conn
)
400 CURLcode result
= CURLE_OK
;
402 /* Send the STARTTLS command */
403 result
= Curl_pp_sendf(&conn
->proto
.smtpc
.pp
, "%s", "STARTTLS");
406 state(conn
, SMTP_STARTTLS
);
411 /***********************************************************************
413 * smtp_perform_upgrade_tls()
415 * Performs the upgrade to TLS.
417 static CURLcode
smtp_perform_upgrade_tls(struct connectdata
*conn
)
419 CURLcode result
= CURLE_OK
;
420 struct smtp_conn
*smtpc
= &conn
->proto
.smtpc
;
422 /* Start the SSL connection */
423 result
= Curl_ssl_connect_nonblocking(conn
, FIRSTSOCKET
, &smtpc
->ssldone
);
426 if(smtpc
->state
!= SMTP_UPGRADETLS
)
427 state(conn
, SMTP_UPGRADETLS
);
431 result
= smtp_perform_ehlo(conn
);
438 /***********************************************************************
440 * smtp_perform_auth()
442 * Sends an AUTH command allowing the client to login with the given SASL
443 * authentication mechanism.
445 static CURLcode
smtp_perform_auth(struct connectdata
*conn
,
447 const char *initresp
)
449 CURLcode result
= CURLE_OK
;
450 struct smtp_conn
*smtpc
= &conn
->proto
.smtpc
;
452 if(initresp
) { /* AUTH <mech> ...<crlf> */
453 /* Send the AUTH command with the initial response */
454 result
= Curl_pp_sendf(&smtpc
->pp
, "AUTH %s %s", mech
, initresp
);
457 /* Send the AUTH command */
458 result
= Curl_pp_sendf(&smtpc
->pp
, "AUTH %s", mech
);
464 /***********************************************************************
466 * smtp_continue_auth()
468 * Sends SASL continuation data or cancellation.
470 static CURLcode
smtp_continue_auth(struct connectdata
*conn
, const char *resp
)
472 struct smtp_conn
*smtpc
= &conn
->proto
.smtpc
;
474 return Curl_pp_sendf(&smtpc
->pp
, "%s", resp
);
477 /***********************************************************************
479 * smtp_perform_authentication()
481 * Initiates the authentication sequence, with the appropriate SASL
482 * authentication mechanism.
484 static CURLcode
smtp_perform_authentication(struct connectdata
*conn
)
486 CURLcode result
= CURLE_OK
;
487 struct smtp_conn
*smtpc
= &conn
->proto
.smtpc
;
488 saslprogress progress
;
490 /* Check we have enough data to authenticate with, and the
491 server supports authentiation, and end the connect phase if not */
492 if(!smtpc
->auth_supported
||
493 !Curl_sasl_can_authenticate(&smtpc
->sasl
, conn
)) {
494 state(conn
, SMTP_STOP
);
498 /* Calculate the SASL login details */
499 result
= Curl_sasl_start(&smtpc
->sasl
, conn
, FALSE
, &progress
);
502 if(progress
== SASL_INPROGRESS
)
503 state(conn
, SMTP_AUTH
);
505 /* Other mechanisms not supported */
506 infof(conn
->data
, "No known authentication mechanisms supported!\n");
507 result
= CURLE_LOGIN_DENIED
;
514 /***********************************************************************
516 * smtp_perform_command()
518 * Sends a SMTP based command.
520 static CURLcode
smtp_perform_command(struct connectdata
*conn
)
522 CURLcode result
= CURLE_OK
;
523 struct SessionHandle
*data
= conn
->data
;
524 struct SMTP
*smtp
= data
->req
.protop
;
526 /* Send the command */
528 result
= Curl_pp_sendf(&conn
->proto
.smtpc
.pp
, "%s %s",
529 smtp
->custom
&& smtp
->custom
[0] != '\0' ?
530 smtp
->custom
: "VRFY",
533 result
= Curl_pp_sendf(&conn
->proto
.smtpc
.pp
, "%s",
534 smtp
->custom
&& smtp
->custom
[0] != '\0' ?
535 smtp
->custom
: "HELP");
538 state(conn
, SMTP_COMMAND
);
543 /***********************************************************************
545 * smtp_perform_mail()
547 * Sends an MAIL command to initiate the upload of a message.
549 static CURLcode
smtp_perform_mail(struct connectdata
*conn
)
554 CURLcode result
= CURLE_OK
;
555 struct SessionHandle
*data
= conn
->data
;
557 /* Calculate the FROM parameter */
558 if(!data
->set
.str
[STRING_MAIL_FROM
])
559 /* Null reverse-path, RFC-5321, sect. 3.6.3 */
561 else if(data
->set
.str
[STRING_MAIL_FROM
][0] == '<')
562 from
= aprintf("%s", data
->set
.str
[STRING_MAIL_FROM
]);
564 from
= aprintf("<%s>", data
->set
.str
[STRING_MAIL_FROM
]);
567 return CURLE_OUT_OF_MEMORY
;
569 /* Calculate the optional AUTH parameter */
570 if(data
->set
.str
[STRING_MAIL_AUTH
] && conn
->proto
.smtpc
.sasl
.authused
) {
571 if(data
->set
.str
[STRING_MAIL_AUTH
][0] != '\0')
572 auth
= aprintf("%s", data
->set
.str
[STRING_MAIL_AUTH
]);
574 /* Empty AUTH, RFC-2554, sect. 5 */
580 return CURLE_OUT_OF_MEMORY
;
584 /* Calculate the optional SIZE parameter */
585 if(conn
->proto
.smtpc
.size_supported
&& conn
->data
->state
.infilesize
> 0) {
586 size
= aprintf("%" CURL_FORMAT_CURL_OFF_T
, data
->state
.infilesize
);
592 return CURLE_OUT_OF_MEMORY
;
596 /* Send the MAIL command */
598 result
= Curl_pp_sendf(&conn
->proto
.smtpc
.pp
,
599 "MAIL FROM:%s", from
);
600 else if(auth
&& !size
)
601 result
= Curl_pp_sendf(&conn
->proto
.smtpc
.pp
,
602 "MAIL FROM:%s AUTH=%s", from
, auth
);
603 else if(auth
&& size
)
604 result
= Curl_pp_sendf(&conn
->proto
.smtpc
.pp
,
605 "MAIL FROM:%s AUTH=%s SIZE=%s", from
, auth
, size
);
607 result
= Curl_pp_sendf(&conn
->proto
.smtpc
.pp
,
608 "MAIL FROM:%s SIZE=%s", from
, size
);
615 state(conn
, SMTP_MAIL
);
620 /***********************************************************************
622 * smtp_perform_rcpt_to()
624 * Sends a RCPT TO command for a given recipient as part of the message upload
627 static CURLcode
smtp_perform_rcpt_to(struct connectdata
*conn
)
629 CURLcode result
= CURLE_OK
;
630 struct SessionHandle
*data
= conn
->data
;
631 struct SMTP
*smtp
= data
->req
.protop
;
633 /* Send the RCPT TO command */
634 if(smtp
->rcpt
->data
[0] == '<')
635 result
= Curl_pp_sendf(&conn
->proto
.smtpc
.pp
, "RCPT TO:%s",
638 result
= Curl_pp_sendf(&conn
->proto
.smtpc
.pp
, "RCPT TO:<%s>",
641 state(conn
, SMTP_RCPT
);
646 /***********************************************************************
648 * smtp_perform_quit()
650 * Performs the quit action prior to sclose() being called.
652 static CURLcode
smtp_perform_quit(struct connectdata
*conn
)
654 CURLcode result
= CURLE_OK
;
656 /* Send the QUIT command */
657 result
= Curl_pp_sendf(&conn
->proto
.smtpc
.pp
, "%s", "QUIT");
660 state(conn
, SMTP_QUIT
);
665 /* For the initial server greeting */
666 static CURLcode
smtp_state_servergreet_resp(struct connectdata
*conn
,
670 CURLcode result
= CURLE_OK
;
671 struct SessionHandle
*data
= conn
->data
;
673 (void)instate
; /* no use for this yet */
675 if(smtpcode
/100 != 2) {
676 failf(data
, "Got unexpected smtp-server response: %d", smtpcode
);
677 result
= CURLE_FTP_WEIRD_SERVER_REPLY
;
680 result
= smtp_perform_ehlo(conn
);
685 /* For STARTTLS responses */
686 static CURLcode
smtp_state_starttls_resp(struct connectdata
*conn
,
690 CURLcode result
= CURLE_OK
;
691 struct SessionHandle
*data
= conn
->data
;
693 (void)instate
; /* no use for this yet */
695 if(smtpcode
!= 220) {
696 if(data
->set
.use_ssl
!= CURLUSESSL_TRY
) {
697 failf(data
, "STARTTLS denied. %c", smtpcode
);
698 result
= CURLE_USE_SSL_FAILED
;
701 result
= smtp_perform_authentication(conn
);
704 result
= smtp_perform_upgrade_tls(conn
);
709 /* For EHLO responses */
710 static CURLcode
smtp_state_ehlo_resp(struct connectdata
*conn
, int smtpcode
,
713 CURLcode result
= CURLE_OK
;
714 struct SessionHandle
*data
= conn
->data
;
715 struct smtp_conn
*smtpc
= &conn
->proto
.smtpc
;
716 const char *line
= data
->state
.buffer
;
717 size_t len
= strlen(line
);
720 (void)instate
; /* no use for this yet */
722 if(smtpcode
/100 != 2 && smtpcode
!= 1) {
723 if(data
->set
.use_ssl
<= CURLUSESSL_TRY
|| conn
->ssl
[FIRSTSOCKET
].use
)
724 result
= smtp_perform_helo(conn
);
726 failf(data
, "Remote access denied: %d", smtpcode
);
727 result
= CURLE_REMOTE_ACCESS_DENIED
;
734 /* Does the server support the STARTTLS capability? */
735 if(len
>= 8 && !memcmp(line
, "STARTTLS", 8))
736 smtpc
->tls_supported
= TRUE
;
738 /* Does the server support the SIZE capability? */
739 else if(len
>= 4 && !memcmp(line
, "SIZE", 4))
740 smtpc
->size_supported
= TRUE
;
742 /* Does the server support authentication? */
743 else if(len
>= 5 && !memcmp(line
, "AUTH ", 5)) {
744 smtpc
->auth_supported
= TRUE
;
746 /* Advance past the AUTH keyword */
750 /* Loop through the data line */
753 unsigned int mechbit
;
756 (*line
== ' ' || *line
== '\t' ||
757 *line
== '\r' || *line
== '\n')) {
766 /* Extract the word */
767 for(wordlen
= 0; wordlen
< len
&& line
[wordlen
] != ' ' &&
768 line
[wordlen
] != '\t' && line
[wordlen
] != '\r' &&
769 line
[wordlen
] != '\n';)
772 /* Test the word for a matching authentication mechanism */
773 mechbit
= Curl_sasl_decode_mech(line
, wordlen
, &llen
);
774 if(mechbit
&& llen
== wordlen
)
775 smtpc
->sasl
.authmechs
|= mechbit
;
783 if(data
->set
.use_ssl
&& !conn
->ssl
[FIRSTSOCKET
].use
) {
784 /* We don't have a SSL/TLS connection yet, but SSL is requested */
785 if(smtpc
->tls_supported
)
786 /* Switch to TLS connection now */
787 result
= smtp_perform_starttls(conn
);
788 else if(data
->set
.use_ssl
== CURLUSESSL_TRY
)
789 /* Fallback and carry on with authentication */
790 result
= smtp_perform_authentication(conn
);
792 failf(data
, "STARTTLS not supported.");
793 result
= CURLE_USE_SSL_FAILED
;
797 result
= smtp_perform_authentication(conn
);
804 /* For HELO responses */
805 static CURLcode
smtp_state_helo_resp(struct connectdata
*conn
, int smtpcode
,
808 CURLcode result
= CURLE_OK
;
809 struct SessionHandle
*data
= conn
->data
;
811 (void)instate
; /* no use for this yet */
813 if(smtpcode
/100 != 2) {
814 failf(data
, "Remote access denied: %d", smtpcode
);
815 result
= CURLE_REMOTE_ACCESS_DENIED
;
818 /* End of connect phase */
819 state(conn
, SMTP_STOP
);
824 /* For SASL authentication responses */
825 static CURLcode
smtp_state_auth_resp(struct connectdata
*conn
,
829 CURLcode result
= CURLE_OK
;
830 struct SessionHandle
*data
= conn
->data
;
831 struct smtp_conn
*smtpc
= &conn
->proto
.smtpc
;
832 saslprogress progress
;
834 (void)instate
; /* no use for this yet */
836 result
= Curl_sasl_continue(&smtpc
->sasl
, conn
, smtpcode
, &progress
);
840 state(conn
, SMTP_STOP
); /* Authenticated */
842 case SASL_IDLE
: /* No mechanism left after cancellation */
843 failf(data
, "Authentication cancelled");
844 result
= CURLE_LOGIN_DENIED
;
853 /* For command responses */
854 static CURLcode
smtp_state_command_resp(struct connectdata
*conn
, int smtpcode
,
857 CURLcode result
= CURLE_OK
;
858 struct SessionHandle
*data
= conn
->data
;
859 struct SMTP
*smtp
= data
->req
.protop
;
860 char *line
= data
->state
.buffer
;
861 size_t len
= strlen(line
);
863 (void)instate
; /* no use for this yet */
865 if((smtp
->rcpt
&& smtpcode
/100 != 2 && smtpcode
!= 553 && smtpcode
!= 1) ||
866 (!smtp
->rcpt
&& smtpcode
/100 != 2 && smtpcode
!= 1)) {
867 failf(data
, "Command failed: %d", smtpcode
);
868 result
= CURLE_RECV_ERROR
;
871 /* Temporarily add the LF character back and send as body to the client */
872 if(!data
->set
.opt_no_body
) {
874 result
= Curl_client_write(conn
, CLIENTWRITE_BODY
, line
, len
+ 1);
880 smtp
->rcpt
= smtp
->rcpt
->next
;
883 /* Send the next command */
884 result
= smtp_perform_command(conn
);
887 /* End of DO phase */
888 state(conn
, SMTP_STOP
);
891 /* End of DO phase */
892 state(conn
, SMTP_STOP
);
899 /* For MAIL responses */
900 static CURLcode
smtp_state_mail_resp(struct connectdata
*conn
, int smtpcode
,
903 CURLcode result
= CURLE_OK
;
904 struct SessionHandle
*data
= conn
->data
;
906 (void)instate
; /* no use for this yet */
908 if(smtpcode
/100 != 2) {
909 failf(data
, "MAIL failed: %d", smtpcode
);
910 result
= CURLE_SEND_ERROR
;
913 /* Start the RCPT TO command */
914 result
= smtp_perform_rcpt_to(conn
);
919 /* For RCPT responses */
920 static CURLcode
smtp_state_rcpt_resp(struct connectdata
*conn
, int smtpcode
,
923 CURLcode result
= CURLE_OK
;
924 struct SessionHandle
*data
= conn
->data
;
925 struct SMTP
*smtp
= data
->req
.protop
;
927 (void)instate
; /* no use for this yet */
929 if(smtpcode
/100 != 2) {
930 failf(data
, "RCPT failed: %d", smtpcode
);
931 result
= CURLE_SEND_ERROR
;
934 smtp
->rcpt
= smtp
->rcpt
->next
;
937 /* Send the next RCPT TO command */
938 result
= smtp_perform_rcpt_to(conn
);
940 /* Send the DATA command */
941 result
= Curl_pp_sendf(&conn
->proto
.smtpc
.pp
, "%s", "DATA");
944 state(conn
, SMTP_DATA
);
951 /* For DATA response */
952 static CURLcode
smtp_state_data_resp(struct connectdata
*conn
, int smtpcode
,
955 CURLcode result
= CURLE_OK
;
956 struct SessionHandle
*data
= conn
->data
;
958 (void)instate
; /* no use for this yet */
960 if(smtpcode
!= 354) {
961 failf(data
, "DATA failed: %d", smtpcode
);
962 result
= CURLE_SEND_ERROR
;
965 /* Set the progress upload size */
966 Curl_pgrsSetUploadSize(data
, data
->state
.infilesize
);
969 Curl_setup_transfer(conn
, -1, -1, FALSE
, NULL
, FIRSTSOCKET
, NULL
);
971 /* End of DO phase */
972 state(conn
, SMTP_STOP
);
978 /* For POSTDATA responses, which are received after the entire DATA
979 part has been sent to the server */
980 static CURLcode
smtp_state_postdata_resp(struct connectdata
*conn
,
984 CURLcode result
= CURLE_OK
;
986 (void)instate
; /* no use for this yet */
989 result
= CURLE_RECV_ERROR
;
991 /* End of DONE phase */
992 state(conn
, SMTP_STOP
);
997 static CURLcode
smtp_statemach_act(struct connectdata
*conn
)
999 CURLcode result
= CURLE_OK
;
1000 curl_socket_t sock
= conn
->sock
[FIRSTSOCKET
];
1001 struct SessionHandle
*data
= conn
->data
;
1003 struct smtp_conn
*smtpc
= &conn
->proto
.smtpc
;
1004 struct pingpong
*pp
= &smtpc
->pp
;
1007 /* Busy upgrading the connection; right now all I/O is SSL/TLS, not SMTP */
1008 if(smtpc
->state
== SMTP_UPGRADETLS
)
1009 return smtp_perform_upgrade_tls(conn
);
1011 /* Flush any data that needs to be sent */
1013 return Curl_pp_flushsend(pp
);
1016 /* Read the response from the server */
1017 result
= Curl_pp_readresp(sock
, pp
, &smtpcode
, &nread
);
1021 /* Store the latest response for later retrieval if necessary */
1022 if(smtpc
->state
!= SMTP_QUIT
&& smtpcode
!= 1)
1023 data
->info
.httpcode
= smtpcode
;
1028 /* We have now received a full SMTP server response */
1029 switch(smtpc
->state
) {
1030 case SMTP_SERVERGREET
:
1031 result
= smtp_state_servergreet_resp(conn
, smtpcode
, smtpc
->state
);
1035 result
= smtp_state_ehlo_resp(conn
, smtpcode
, smtpc
->state
);
1039 result
= smtp_state_helo_resp(conn
, smtpcode
, smtpc
->state
);
1043 result
= smtp_state_starttls_resp(conn
, smtpcode
, smtpc
->state
);
1047 result
= smtp_state_auth_resp(conn
, smtpcode
, smtpc
->state
);
1051 result
= smtp_state_command_resp(conn
, smtpcode
, smtpc
->state
);
1055 result
= smtp_state_mail_resp(conn
, smtpcode
, smtpc
->state
);
1059 result
= smtp_state_rcpt_resp(conn
, smtpcode
, smtpc
->state
);
1063 result
= smtp_state_data_resp(conn
, smtpcode
, smtpc
->state
);
1067 result
= smtp_state_postdata_resp(conn
, smtpcode
, smtpc
->state
);
1071 /* fallthrough, just stop! */
1073 /* internal error */
1074 state(conn
, SMTP_STOP
);
1077 } while(!result
&& smtpc
->state
!= SMTP_STOP
&& Curl_pp_moredata(pp
));
1082 /* Called repeatedly until done from multi.c */
1083 static CURLcode
smtp_multi_statemach(struct connectdata
*conn
, bool *done
)
1085 CURLcode result
= CURLE_OK
;
1086 struct smtp_conn
*smtpc
= &conn
->proto
.smtpc
;
1088 if((conn
->handler
->flags
& PROTOPT_SSL
) && !smtpc
->ssldone
) {
1089 result
= Curl_ssl_connect_nonblocking(conn
, FIRSTSOCKET
, &smtpc
->ssldone
);
1090 if(result
|| !smtpc
->ssldone
)
1094 result
= Curl_pp_statemach(&smtpc
->pp
, FALSE
);
1095 *done
= (smtpc
->state
== SMTP_STOP
) ? TRUE
: FALSE
;
1100 static CURLcode
smtp_block_statemach(struct connectdata
*conn
)
1102 CURLcode result
= CURLE_OK
;
1103 struct smtp_conn
*smtpc
= &conn
->proto
.smtpc
;
1105 while(smtpc
->state
!= SMTP_STOP
&& !result
)
1106 result
= Curl_pp_statemach(&smtpc
->pp
, TRUE
);
1111 /* Allocate and initialize the SMTP struct for the current SessionHandle if
1113 static CURLcode
smtp_init(struct connectdata
*conn
)
1115 CURLcode result
= CURLE_OK
;
1116 struct SessionHandle
*data
= conn
->data
;
1119 smtp
= data
->req
.protop
= calloc(sizeof(struct SMTP
), 1);
1121 result
= CURLE_OUT_OF_MEMORY
;
1126 /* For the SMTP "protocol connect" and "doing" phases only */
1127 static int smtp_getsock(struct connectdata
*conn
, curl_socket_t
*socks
,
1130 return Curl_pp_getsock(&conn
->proto
.smtpc
.pp
, socks
, numsocks
);
1133 /***********************************************************************
1137 * This function should do everything that is to be considered a part of
1138 * the connection phase.
1140 * The variable pointed to by 'done' will be TRUE if the protocol-layer
1141 * connect phase is done when this function returns, or FALSE if not.
1143 static CURLcode
smtp_connect(struct connectdata
*conn
, bool *done
)
1145 CURLcode result
= CURLE_OK
;
1146 struct smtp_conn
*smtpc
= &conn
->proto
.smtpc
;
1147 struct pingpong
*pp
= &smtpc
->pp
;
1149 *done
= FALSE
; /* default to not done yet */
1151 /* We always support persistent connections in SMTP */
1152 connkeep(conn
, "SMTP default");
1154 /* Set the default response time-out */
1155 pp
->response_time
= RESP_TIMEOUT
;
1156 pp
->statemach_act
= smtp_statemach_act
;
1157 pp
->endofresp
= smtp_endofresp
;
1160 /* Initialize the SASL storage */
1161 Curl_sasl_init(&smtpc
->sasl
, &saslsmtp
);
1163 /* Initialise the pingpong layer */
1166 /* Parse the URL options */
1167 result
= smtp_parse_url_options(conn
);
1171 /* Parse the URL path */
1172 result
= smtp_parse_url_path(conn
);
1176 /* Start off waiting for the server greeting response */
1177 state(conn
, SMTP_SERVERGREET
);
1179 result
= smtp_multi_statemach(conn
, done
);
1184 /***********************************************************************
1188 * The DONE function. This does what needs to be done after a single DO has
1191 * Input argument is already checked for validity.
1193 static CURLcode
smtp_done(struct connectdata
*conn
, CURLcode status
,
1196 CURLcode result
= CURLE_OK
;
1197 struct SessionHandle
*data
= conn
->data
;
1198 struct SMTP
*smtp
= data
->req
.protop
;
1199 struct pingpong
*pp
= &conn
->proto
.smtpc
.pp
;
1202 ssize_t bytes_written
;
1206 if(!smtp
|| !pp
->conn
)
1210 connclose(conn
, "SMTP done with bad status"); /* marked for closure */
1211 result
= status
; /* use the already set error code */
1213 else if(!data
->set
.connect_only
&& data
->set
.upload
&& data
->set
.mail_rcpt
) {
1214 /* Calculate the EOB taking into account any terminating CRLF from the
1215 previous line of the email or the CRLF of the DATA command when there
1216 is "no mail data". RFC-5321, sect. 4.1.1.4.
1218 Note: As some SSL backends, such as OpenSSL, will cause Curl_write() to
1219 fail when using a different pointer following a previous write, that
1220 returned CURLE_AGAIN, we duplicate the EOB now rather than when the
1221 bytes written doesn't equal len. */
1222 if(smtp
->trailing_crlf
|| !conn
->data
->state
.infilesize
) {
1223 eob
= strdup(SMTP_EOB
+ 2);
1224 len
= SMTP_EOB_LEN
- 2;
1227 eob
= strdup(SMTP_EOB
);
1232 return CURLE_OUT_OF_MEMORY
;
1234 /* Send the end of block data */
1235 result
= Curl_write(conn
, conn
->writesockfd
, eob
, len
, &bytes_written
);
1241 if(bytes_written
!= len
) {
1242 /* The whole chunk was not sent so keep it around and adjust the
1243 pingpong structure accordingly */
1246 pp
->sendleft
= len
- bytes_written
;
1249 /* Successfully sent so adjust the response timeout relative to now */
1250 pp
->response
= Curl_tvnow();
1255 state(conn
, SMTP_POSTDATA
);
1257 /* Run the state-machine
1259 TODO: when the multi interface is used, this _really_ should be using
1260 the smtp_multi_statemach function but we have no general support for
1261 non-blocking DONE operations!
1263 result
= smtp_block_statemach(conn
);
1266 /* Cleanup our per-request based variables */
1267 Curl_safefree(smtp
->custom
);
1269 /* Clear the transfer mode for the next request */
1270 smtp
->transfer
= FTPTRANSFER_BODY
;
1275 /***********************************************************************
1279 * This is the actual DO function for SMTP. Transfer a mail, send a command
1280 * or get some data according to the options previously setup.
1282 static CURLcode
smtp_perform(struct connectdata
*conn
, bool *connected
,
1285 /* This is SMTP and no proxy */
1286 CURLcode result
= CURLE_OK
;
1287 struct SessionHandle
*data
= conn
->data
;
1288 struct SMTP
*smtp
= data
->req
.protop
;
1290 DEBUGF(infof(conn
->data
, "DO phase starts\n"));
1292 if(data
->set
.opt_no_body
) {
1293 /* Requested no body means no transfer */
1294 smtp
->transfer
= FTPTRANSFER_INFO
;
1297 *dophase_done
= FALSE
; /* not done yet */
1299 /* Store the first recipient (or NULL if not specified) */
1300 smtp
->rcpt
= data
->set
.mail_rcpt
;
1302 /* Start the first command in the DO phase */
1303 if(data
->set
.upload
&& data
->set
.mail_rcpt
)
1305 result
= smtp_perform_mail(conn
);
1307 /* SMTP based command (VRFY, EXPN, NOOP, RSET or HELP) */
1308 result
= smtp_perform_command(conn
);
1313 /* Run the state-machine */
1314 result
= smtp_multi_statemach(conn
, dophase_done
);
1316 *connected
= conn
->bits
.tcpconnect
[FIRSTSOCKET
];
1319 DEBUGF(infof(conn
->data
, "DO phase is complete\n"));
1324 /***********************************************************************
1328 * This function is registered as 'curl_do' function. It decodes the path
1329 * parts etc as a wrapper to the actual DO function (smtp_perform).
1331 * The input argument is already checked for validity.
1333 static CURLcode
smtp_do(struct connectdata
*conn
, bool *done
)
1335 CURLcode result
= CURLE_OK
;
1337 *done
= FALSE
; /* default to false */
1339 /* Parse the custom request */
1340 result
= smtp_parse_custom_request(conn
);
1344 result
= smtp_regular_transfer(conn
, done
);
1349 /***********************************************************************
1353 * Disconnect from an SMTP server. Cleanup protocol-specific per-connection
1354 * resources. BLOCKING.
1356 static CURLcode
smtp_disconnect(struct connectdata
*conn
, bool dead_connection
)
1358 struct smtp_conn
*smtpc
= &conn
->proto
.smtpc
;
1360 /* We cannot send quit unconditionally. If this connection is stale or
1361 bad in any way, sending quit and waiting around here will make the
1362 disconnect wait in vain and cause more problems than we need to. */
1364 /* The SMTP session may or may not have been allocated/setup at this
1366 if(!dead_connection
&& smtpc
->pp
.conn
&& smtpc
->pp
.conn
->bits
.protoconnstart
)
1367 if(!smtp_perform_quit(conn
))
1368 (void)smtp_block_statemach(conn
); /* ignore errors on QUIT */
1370 /* Disconnect from the server */
1371 Curl_pp_disconnect(&smtpc
->pp
);
1373 /* Cleanup the SASL module */
1374 Curl_sasl_cleanup(conn
, smtpc
->sasl
.authused
);
1376 /* Cleanup our connection based variables */
1377 Curl_safefree(smtpc
->domain
);
1382 /* Call this when the DO phase has completed */
1383 static CURLcode
smtp_dophase_done(struct connectdata
*conn
, bool connected
)
1385 struct SMTP
*smtp
= conn
->data
->req
.protop
;
1389 if(smtp
->transfer
!= FTPTRANSFER_BODY
)
1390 /* no data to transfer */
1391 Curl_setup_transfer(conn
, -1, -1, FALSE
, NULL
, -1, NULL
);
1396 /* Called from multi.c while DOing */
1397 static CURLcode
smtp_doing(struct connectdata
*conn
, bool *dophase_done
)
1399 CURLcode result
= smtp_multi_statemach(conn
, dophase_done
);
1402 DEBUGF(infof(conn
->data
, "DO phase failed\n"));
1403 else if(*dophase_done
) {
1404 result
= smtp_dophase_done(conn
, FALSE
/* not connected */);
1406 DEBUGF(infof(conn
->data
, "DO phase is complete\n"));
1412 /***********************************************************************
1414 * smtp_regular_transfer()
1416 * The input argument is already checked for validity.
1418 * Performs all commands done before a regular transfer between a local and a
1421 static CURLcode
smtp_regular_transfer(struct connectdata
*conn
,
1424 CURLcode result
= CURLE_OK
;
1425 bool connected
= FALSE
;
1426 struct SessionHandle
*data
= conn
->data
;
1428 /* Make sure size is unknown at this point */
1429 data
->req
.size
= -1;
1431 /* Set the progress data */
1432 Curl_pgrsSetUploadCounter(data
, 0);
1433 Curl_pgrsSetDownloadCounter(data
, 0);
1434 Curl_pgrsSetUploadSize(data
, -1);
1435 Curl_pgrsSetDownloadSize(data
, -1);
1437 /* Carry out the perform */
1438 result
= smtp_perform(conn
, &connected
, dophase_done
);
1440 /* Perform post DO phase operations if necessary */
1441 if(!result
&& *dophase_done
)
1442 result
= smtp_dophase_done(conn
, connected
);
1447 static CURLcode
smtp_setup_connection(struct connectdata
*conn
)
1449 struct SessionHandle
*data
= conn
->data
;
1452 /* Clear the TLS upgraded flag */
1453 conn
->tls_upgraded
= FALSE
;
1455 /* Set up the proxy if necessary */
1456 if(conn
->bits
.httpproxy
&& !data
->set
.tunnel_thru_httpproxy
) {
1457 /* Unless we have asked to tunnel SMTP operations through the proxy, we
1458 switch and use HTTP operations only */
1459 #ifndef CURL_DISABLE_HTTP
1460 if(conn
->handler
== &Curl_handler_smtp
)
1461 conn
->handler
= &Curl_handler_smtp_proxy
;
1464 conn
->handler
= &Curl_handler_smtps_proxy
;
1466 failf(data
, "SMTPS not supported!");
1467 return CURLE_UNSUPPORTED_PROTOCOL
;
1470 /* set it up as a HTTP connection instead */
1471 return conn
->handler
->setup_connection(conn
);
1474 failf(data
, "SMTP over http proxy requires HTTP support built-in!");
1475 return CURLE_UNSUPPORTED_PROTOCOL
;
1479 /* Initialise the SMTP layer */
1480 result
= smtp_init(conn
);
1484 data
->state
.path
++; /* don't include the initial slash */
1489 /***********************************************************************
1491 * smtp_parse_url_options()
1493 * Parse the URL login options.
1495 static CURLcode
smtp_parse_url_options(struct connectdata
*conn
)
1497 CURLcode result
= CURLE_OK
;
1498 struct smtp_conn
*smtpc
= &conn
->proto
.smtpc
;
1499 const char *ptr
= conn
->options
;
1501 smtpc
->sasl
.resetprefs
= TRUE
;
1503 while(!result
&& ptr
&& *ptr
) {
1504 const char *key
= ptr
;
1507 while(*ptr
&& *ptr
!= '=')
1512 while(*ptr
&& *ptr
!= ';')
1515 if(strnequal(key
, "AUTH=", 5))
1516 result
= Curl_sasl_parse_url_auth_option(&smtpc
->sasl
,
1517 value
, ptr
- value
);
1519 result
= CURLE_URL_MALFORMAT
;
1528 /***********************************************************************
1530 * smtp_parse_url_path()
1532 * Parse the URL path into separate path components.
1534 static CURLcode
smtp_parse_url_path(struct connectdata
*conn
)
1536 /* The SMTP struct is already initialised in smtp_connect() */
1537 struct SessionHandle
*data
= conn
->data
;
1538 struct smtp_conn
*smtpc
= &conn
->proto
.smtpc
;
1539 const char *path
= data
->state
.path
;
1540 char localhost
[HOSTNAME_MAX
+ 1];
1542 /* Calculate the path if necessary */
1544 if(!Curl_gethostname(localhost
, sizeof(localhost
)))
1550 /* URL decode the path and use it as the domain in our EHLO */
1551 return Curl_urldecode(conn
->data
, path
, 0, &smtpc
->domain
, NULL
, TRUE
);
1554 /***********************************************************************
1556 * smtp_parse_custom_request()
1558 * Parse the custom request.
1560 static CURLcode
smtp_parse_custom_request(struct connectdata
*conn
)
1562 CURLcode result
= CURLE_OK
;
1563 struct SessionHandle
*data
= conn
->data
;
1564 struct SMTP
*smtp
= data
->req
.protop
;
1565 const char *custom
= data
->set
.str
[STRING_CUSTOMREQUEST
];
1567 /* URL decode the custom request */
1569 result
= Curl_urldecode(data
, custom
, 0, &smtp
->custom
, NULL
, TRUE
);
1574 CURLcode
Curl_smtp_escape_eob(struct connectdata
*conn
, const ssize_t nread
)
1576 /* When sending a SMTP payload we must detect CRLF. sequences making sure
1577 they are sent as CRLF.. instead, as a . on the beginning of a line will
1578 be deleted by the server when not part of an EOB terminator and a
1579 genuine CRLF.CRLF which isn't escaped will wrongly be detected as end of
1584 struct SessionHandle
*data
= conn
->data
;
1585 struct SMTP
*smtp
= data
->req
.protop
;
1586 char *scratch
= data
->state
.scratch
;
1587 char *newscratch
= NULL
;
1588 char *oldscratch
= NULL
;
1591 /* Do we need to allocate a scratch buffer? */
1592 if(!scratch
|| data
->set
.crlf
) {
1593 oldscratch
= scratch
;
1595 scratch
= newscratch
= malloc(2 * BUFSIZE
);
1597 failf(data
, "Failed to alloc scratch buffer!");
1599 return CURLE_OUT_OF_MEMORY
;
1603 /* Have we already sent part of the EOB? */
1604 eob_sent
= smtp
->eob
;
1606 /* This loop can be improved by some kind of Boyer-Moore style of
1607 approach but that is saved for later... */
1608 for(i
= 0, si
= 0; i
< nread
; i
++) {
1609 if(SMTP_EOB
[smtp
->eob
] == data
->req
.upload_fromhere
[i
]) {
1612 /* Is the EOB potentially the terminating CRLF? */
1613 if(2 == smtp
->eob
|| SMTP_EOB_LEN
== smtp
->eob
)
1614 smtp
->trailing_crlf
= TRUE
;
1616 smtp
->trailing_crlf
= FALSE
;
1618 else if(smtp
->eob
) {
1619 /* A previous substring matched so output that first */
1620 memcpy(&scratch
[si
], &SMTP_EOB
[eob_sent
], smtp
->eob
- eob_sent
);
1621 si
+= smtp
->eob
- eob_sent
;
1623 /* Then compare the first byte */
1624 if(SMTP_EOB
[0] == data
->req
.upload_fromhere
[i
])
1631 /* Reset the trailing CRLF flag as there was more data */
1632 smtp
->trailing_crlf
= FALSE
;
1635 /* Do we have a match for CRLF. as per RFC-5321, sect. 4.5.2 */
1636 if(SMTP_EOB_FIND_LEN
== smtp
->eob
) {
1637 /* Copy the replacement data to the target buffer */
1638 memcpy(&scratch
[si
], &SMTP_EOB_REPL
[eob_sent
],
1639 SMTP_EOB_REPL_LEN
- eob_sent
);
1640 si
+= SMTP_EOB_REPL_LEN
- eob_sent
;
1645 scratch
[si
++] = data
->req
.upload_fromhere
[i
];
1648 if(smtp
->eob
- eob_sent
) {
1649 /* A substring matched before processing ended so output that now */
1650 memcpy(&scratch
[si
], &SMTP_EOB
[eob_sent
], smtp
->eob
- eob_sent
);
1651 si
+= smtp
->eob
- eob_sent
;
1654 /* Only use the new buffer if we replaced something */
1656 /* Upload from the new (replaced) buffer instead */
1657 data
->req
.upload_fromhere
= scratch
;
1659 /* Save the buffer so it can be freed later */
1660 data
->state
.scratch
= scratch
;
1662 /* Free the old scratch buffer */
1665 /* Set the new amount too */
1666 data
->req
.upload_present
= si
;
1674 #endif /* CURL_DISABLE_SMTP */