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 * Draft SMTP URL Interface
31 ***************************************************************************/
33 #include "curl_setup.h"
35 #ifndef CURL_DISABLE_SMTP
37 #ifdef HAVE_NETINET_IN_H
38 #include <netinet/in.h>
40 #ifdef HAVE_ARPA_INET_H
41 #include <arpa/inet.h>
44 #include <sys/utsname.h>
54 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
56 #define in_addr_t unsigned long
59 #include <curl/curl.h>
67 #include "http.h" /* for HTTP proxy tunnel stuff */
71 #include "strtoofft.h"
80 #include "curl_gethostname.h"
81 #include "curl_sasl.h"
84 #define _MPRINTF_REPLACE /* use our functions only */
85 #include <curl/mprintf.h>
87 #include "curl_memory.h"
88 /* The last #include file should be: */
91 /* Local API functions */
92 static CURLcode smtp_regular_transfer(struct connectdata *conn, bool *done);
93 static CURLcode smtp_do(struct connectdata *conn, bool *done);
94 static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
96 static CURLcode smtp_connect(struct connectdata *conn, bool *done);
97 static CURLcode smtp_disconnect(struct connectdata *conn, bool dead);
98 static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done);
99 static int smtp_getsock(struct connectdata *conn, curl_socket_t *socks,
101 static CURLcode smtp_doing(struct connectdata *conn, bool *dophase_done);
102 static CURLcode smtp_setup_connection(struct connectdata *conn);
103 static CURLcode smtp_parse_url_options(struct connectdata *conn);
104 static CURLcode smtp_parse_url_path(struct connectdata *conn);
107 * SMTP protocol handler.
110 const struct Curl_handler Curl_handler_smtp = {
112 smtp_setup_connection, /* setup_connection */
114 smtp_done, /* done */
115 ZERO_NULL, /* do_more */
116 smtp_connect, /* connect_it */
117 smtp_multi_statemach, /* connecting */
118 smtp_doing, /* doing */
119 smtp_getsock, /* proto_getsock */
120 smtp_getsock, /* doing_getsock */
121 ZERO_NULL, /* domore_getsock */
122 ZERO_NULL, /* perform_getsock */
123 smtp_disconnect, /* disconnect */
124 ZERO_NULL, /* readwrite */
125 PORT_SMTP, /* defport */
126 CURLPROTO_SMTP, /* protocol */
127 PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY /* flags */
132 * SMTPS protocol handler.
135 const struct Curl_handler Curl_handler_smtps = {
136 "SMTPS", /* scheme */
137 smtp_setup_connection, /* setup_connection */
139 smtp_done, /* done */
140 ZERO_NULL, /* do_more */
141 smtp_connect, /* connect_it */
142 smtp_multi_statemach, /* connecting */
143 smtp_doing, /* doing */
144 smtp_getsock, /* proto_getsock */
145 smtp_getsock, /* doing_getsock */
146 ZERO_NULL, /* domore_getsock */
147 ZERO_NULL, /* perform_getsock */
148 smtp_disconnect, /* disconnect */
149 ZERO_NULL, /* readwrite */
150 PORT_SMTPS, /* defport */
151 CURLPROTO_SMTP | CURLPROTO_SMTPS, /* protocol */
152 PROTOPT_CLOSEACTION | PROTOPT_SSL
153 | PROTOPT_NOURLQUERY /* flags */
157 #ifndef CURL_DISABLE_HTTP
159 * HTTP-proxyed SMTP protocol handler.
162 static const struct Curl_handler Curl_handler_smtp_proxy = {
164 ZERO_NULL, /* setup_connection */
165 Curl_http, /* do_it */
166 Curl_http_done, /* done */
167 ZERO_NULL, /* do_more */
168 ZERO_NULL, /* connect_it */
169 ZERO_NULL, /* connecting */
170 ZERO_NULL, /* doing */
171 ZERO_NULL, /* proto_getsock */
172 ZERO_NULL, /* doing_getsock */
173 ZERO_NULL, /* domore_getsock */
174 ZERO_NULL, /* perform_getsock */
175 ZERO_NULL, /* disconnect */
176 ZERO_NULL, /* readwrite */
177 PORT_SMTP, /* defport */
178 CURLPROTO_HTTP, /* protocol */
179 PROTOPT_NONE /* flags */
184 * HTTP-proxyed SMTPS protocol handler.
187 static const struct Curl_handler Curl_handler_smtps_proxy = {
188 "SMTPS", /* scheme */
189 ZERO_NULL, /* setup_connection */
190 Curl_http, /* do_it */
191 Curl_http_done, /* done */
192 ZERO_NULL, /* do_more */
193 ZERO_NULL, /* connect_it */
194 ZERO_NULL, /* connecting */
195 ZERO_NULL, /* doing */
196 ZERO_NULL, /* proto_getsock */
197 ZERO_NULL, /* doing_getsock */
198 ZERO_NULL, /* domore_getsock */
199 ZERO_NULL, /* perform_getsock */
200 ZERO_NULL, /* disconnect */
201 ZERO_NULL, /* readwrite */
202 PORT_SMTPS, /* defport */
203 CURLPROTO_HTTP, /* protocol */
204 PROTOPT_NONE /* flags */
210 static void smtp_to_smtps(struct connectdata *conn)
212 conn->handler = &Curl_handler_smtps;
215 #define smtp_to_smtps(x) Curl_nop_stmt
218 /***********************************************************************
222 * Checks for an ending SMTP status code at the start of the given string, but
223 * also detects various capabilities from the EHLO response including the
224 * supported authentication mechanisms.
226 static bool smtp_endofresp(struct connectdata *conn, char *line, size_t len,
229 struct smtp_conn *smtpc = &conn->proto.smtpc;
233 if(len < 4 || !ISDIGIT(line[0]) || !ISDIGIT(line[1]) || !ISDIGIT(line[2]))
234 return FALSE; /* Nothing for us */
236 /* Do we have a command response? */
237 result = (line[3] == ' ') ? TRUE : FALSE;
239 *resp = curlx_sltosi(strtol(line, NULL, 10));
241 /* Are we processing EHLO command data? */
242 if(smtpc->state == SMTP_EHLO && (!result || (result && *resp/100 == 2))) {
246 /* Does the server support the STARTTLS capability? */
247 if(len >= 8 && !memcmp(line, "STARTTLS", 8))
248 smtpc->tls_supported = TRUE;
250 /* Does the server support the SIZE capability? */
251 else if(len >= 4 && !memcmp(line, "SIZE", 4))
252 smtpc->size_supported = TRUE;
254 /* Do we have the authentication mechanism list? */
255 else if(len >= 5 && !memcmp(line, "AUTH ", 5)) {
259 /* Loop through the data line */
262 (*line == ' ' || *line == '\t' ||
263 *line == '\r' || *line == '\n')) {
272 /* Extract the word */
273 for(wordlen = 0; wordlen < len && line[wordlen] != ' ' &&
274 line[wordlen] != '\t' && line[wordlen] != '\r' &&
275 line[wordlen] != '\n';)
278 /* Test the word for a matching authentication mechanism */
279 if(wordlen == 5 && !memcmp(line, "LOGIN", 5))
280 smtpc->authmechs |= SASL_MECH_LOGIN;
281 else if(wordlen == 5 && !memcmp(line, "PLAIN", 5))
282 smtpc->authmechs |= SASL_MECH_PLAIN;
283 else if(wordlen == 8 && !memcmp(line, "CRAM-MD5", 8))
284 smtpc->authmechs |= SASL_MECH_CRAM_MD5;
285 else if(wordlen == 10 && !memcmp(line, "DIGEST-MD5", 10))
286 smtpc->authmechs |= SASL_MECH_DIGEST_MD5;
287 else if(wordlen == 6 && !memcmp(line, "GSSAPI", 6))
288 smtpc->authmechs |= SASL_MECH_GSSAPI;
289 else if(wordlen == 8 && !memcmp(line, "EXTERNAL", 8))
290 smtpc->authmechs |= SASL_MECH_EXTERNAL;
291 else if(wordlen == 4 && !memcmp(line, "NTLM", 4))
292 smtpc->authmechs |= SASL_MECH_NTLM;
303 /***********************************************************************
307 * This is the ONLY way to change SMTP state!
309 static void state(struct connectdata *conn, smtpstate newstate)
311 struct smtp_conn *smtpc = &conn->proto.smtpc;
312 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
313 /* for debug purposes */
314 static const char * const names[] = {
326 "AUTH_DIGESTMD5_RESP",
328 "AUTH_NTLM_TYPE2MSG",
338 if(smtpc->state != newstate)
339 infof(conn->data, "SMTP %p state change from %s to %s\n",
340 (void *)smtpc, names[smtpc->state], names[newstate]);
343 smtpc->state = newstate;
346 /***********************************************************************
348 * smtp_perform_ehlo()
350 * Sends the EHLO command to not only initialise communication with the ESMTP
351 * server but to also obtain a list of server side supported capabilities.
353 static CURLcode smtp_perform_ehlo(struct connectdata *conn)
355 CURLcode result = CURLE_OK;
356 struct smtp_conn *smtpc = &conn->proto.smtpc;
358 smtpc->authmechs = 0; /* No known authentication mechanisms yet */
359 smtpc->authused = 0; /* Clear the authentication mechanism used
360 for esmtp connections */
361 smtpc->tls_supported = FALSE; /* Clear the TLS capability */
363 /* Send the EHLO command */
364 result = Curl_pp_sendf(&smtpc->pp, "EHLO %s", smtpc->domain);
367 state(conn, SMTP_EHLO);
372 /***********************************************************************
374 * smtp_perform_helo()
376 * Sends the HELO command to initialise communication with the SMTP server.
378 static CURLcode smtp_perform_helo(struct connectdata *conn)
380 CURLcode result = CURLE_OK;
381 struct smtp_conn *smtpc = &conn->proto.smtpc;
383 smtpc->authused = 0; /* No authentication mechanism used in smtp
386 /* Send the HELO command */
387 result = Curl_pp_sendf(&smtpc->pp, "HELO %s", smtpc->domain);
390 state(conn, SMTP_HELO);
395 /***********************************************************************
397 * smtp_perform_starttls()
399 * Sends the STLS command to start the upgrade to TLS.
401 static CURLcode smtp_perform_starttls(struct connectdata *conn)
403 CURLcode result = CURLE_OK;
405 /* Send the STARTTLS command */
406 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "STARTTLS");
409 state(conn, SMTP_STARTTLS);
414 /***********************************************************************
416 * smtp_perform_upgrade_tls()
418 * Performs the upgrade to TLS.
420 static CURLcode smtp_perform_upgrade_tls(struct connectdata *conn)
422 CURLcode result = CURLE_OK;
423 struct smtp_conn *smtpc = &conn->proto.smtpc;
425 /* Start the SSL connection */
426 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone);
429 if(smtpc->state != SMTP_UPGRADETLS)
430 state(conn, SMTP_UPGRADETLS);
434 result = smtp_perform_ehlo(conn);
441 /***********************************************************************
443 * smtp_perform_authenticate()
445 * Sends an AUTH command allowing the client to login with the appropriate
446 * SASL authentication mechanism.
448 static CURLcode smtp_perform_authenticate(struct connectdata *conn)
450 CURLcode result = CURLE_OK;
451 struct SessionHandle *data = conn->data;
452 struct smtp_conn *smtpc = &conn->proto.smtpc;
453 const char *mech = NULL;
454 char *initresp = NULL;
456 smtpstate state1 = SMTP_STOP;
457 smtpstate state2 = SMTP_STOP;
459 /* Check we have a username and password to authenticate with and end the
460 connect phase if we don't */
461 if(!conn->bits.user_passwd) {
462 state(conn, SMTP_STOP);
467 /* Calculate the supported authentication mechanism, by decreasing order of
468 security, as well as the initial response where appropriate */
469 #ifndef CURL_DISABLE_CRYPTO_AUTH
470 if((smtpc->authmechs & SASL_MECH_DIGEST_MD5) &&
471 (smtpc->prefmech & SASL_MECH_DIGEST_MD5)) {
473 state1 = SMTP_AUTH_DIGESTMD5;
474 smtpc->authused = SASL_MECH_DIGEST_MD5;
476 else if((smtpc->authmechs & SASL_MECH_CRAM_MD5) &&
477 (smtpc->prefmech & SASL_MECH_CRAM_MD5)) {
479 state1 = SMTP_AUTH_CRAMMD5;
480 smtpc->authused = SASL_MECH_CRAM_MD5;
485 if((smtpc->authmechs & SASL_MECH_NTLM) &&
486 (smtpc->prefmech & SASL_MECH_NTLM)) {
488 state1 = SMTP_AUTH_NTLM;
489 state2 = SMTP_AUTH_NTLM_TYPE2MSG;
490 smtpc->authused = SASL_MECH_NTLM;
492 if(data->set.sasl_ir)
493 result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
499 if((smtpc->authmechs & SASL_MECH_LOGIN) &&
500 (smtpc->prefmech & SASL_MECH_LOGIN)) {
502 state1 = SMTP_AUTH_LOGIN;
503 state2 = SMTP_AUTH_LOGIN_PASSWD;
504 smtpc->authused = SASL_MECH_LOGIN;
506 if(data->set.sasl_ir)
507 result = Curl_sasl_create_login_message(conn->data, conn->user,
510 else if((smtpc->authmechs & SASL_MECH_PLAIN) &&
511 (smtpc->prefmech & SASL_MECH_PLAIN)) {
513 state1 = SMTP_AUTH_PLAIN;
514 state2 = SMTP_AUTH_FINAL;
515 smtpc->authused = SASL_MECH_PLAIN;
517 if(data->set.sasl_ir)
518 result = Curl_sasl_create_plain_message(conn->data, conn->user,
519 conn->passwd, &initresp, &len);
524 /* Perform SASL based authentication */
526 8 + strlen(mech) + len <= 512) { /* AUTH <mech> ...<crlf> */
527 result = Curl_pp_sendf(&smtpc->pp, "AUTH %s %s", mech, initresp);
533 result = Curl_pp_sendf(&smtpc->pp, "AUTH %s", mech);
539 Curl_safefree(initresp);
542 /* Other mechanisms not supported */
543 infof(conn->data, "No known authentication mechanisms supported!\n");
544 result = CURLE_LOGIN_DENIED;
551 /***********************************************************************
553 * smtp_perform_mail()
555 * Sends an MAIL command to initiate the upload of a message.
557 static CURLcode smtp_perform_mail(struct connectdata *conn)
562 CURLcode result = CURLE_OK;
563 struct SessionHandle *data = conn->data;
565 /* Calculate the FROM parameter */
566 if(!data->set.str[STRING_MAIL_FROM])
567 /* Null reverse-path, RFC-5321, sect. 3.6.3 */
569 else if(data->set.str[STRING_MAIL_FROM][0] == '<')
570 from = aprintf("%s", data->set.str[STRING_MAIL_FROM]);
572 from = aprintf("<%s>", data->set.str[STRING_MAIL_FROM]);
575 return CURLE_OUT_OF_MEMORY;
577 /* Calculate the optional AUTH parameter */
578 if(data->set.str[STRING_MAIL_AUTH] && conn->proto.smtpc.authused) {
579 if(data->set.str[STRING_MAIL_AUTH][0] != '\0')
580 auth = aprintf("%s", data->set.str[STRING_MAIL_AUTH]);
582 /* Empty AUTH, RFC-2554, sect. 5 */
588 return CURLE_OUT_OF_MEMORY;
592 /* Calculate the optional SIZE parameter */
593 if(conn->proto.smtpc.size_supported && conn->data->set.infilesize > 0) {
594 size = aprintf("%" FORMAT_OFF_T, data->set.infilesize);
600 return CURLE_OUT_OF_MEMORY;
604 /* Send the MAIL command */
606 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
607 "MAIL FROM:%s", from);
608 else if(auth && !size)
609 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
610 "MAIL FROM:%s AUTH=%s", from, auth);
611 else if(auth && size)
612 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
613 "MAIL FROM:%s AUTH=%s SIZE=%s", from, auth, size);
615 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
616 "MAIL FROM:%s SIZE=%s", from, size);
623 state(conn, SMTP_MAIL);
628 /***********************************************************************
630 * smtp_perform_rcpt_to()
632 * Sends a RCPT TO command for a given recipient as part of the message upload
635 static CURLcode smtp_perform_rcpt_to(struct connectdata *conn)
637 CURLcode result = CURLE_OK;
638 struct SessionHandle *data = conn->data;
639 struct SMTP *smtp = data->state.proto.smtp;
641 /* Send the RCPT TO command */
643 if(smtp->rcpt->data[0] == '<')
644 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:%s",
647 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:<%s>",
650 state(conn, SMTP_RCPT);
656 /***********************************************************************
658 * smtp_perform_quit()
660 * Performs the quit action prior to sclose() being called.
662 static CURLcode smtp_perform_quit(struct connectdata *conn)
664 CURLcode result = CURLE_OK;
666 /* Send the QUIT command */
667 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "QUIT");
670 state(conn, SMTP_QUIT);
675 /* For the initial server greeting */
676 static CURLcode smtp_state_servergreet_resp(struct connectdata *conn,
680 CURLcode result = CURLE_OK;
681 struct SessionHandle *data = conn->data;
683 (void)instate; /* no use for this yet */
685 if(smtpcode/100 != 2) {
686 failf(data, "Got unexpected smtp-server response: %d", smtpcode);
687 result = CURLE_FTP_WEIRD_SERVER_REPLY;
690 result = smtp_perform_ehlo(conn);
695 /* For STARTTLS responses */
696 static CURLcode smtp_state_starttls_resp(struct connectdata *conn,
700 CURLcode result = CURLE_OK;
701 struct SessionHandle *data = conn->data;
703 (void)instate; /* no use for this yet */
705 if(smtpcode != 220) {
706 if(data->set.use_ssl != CURLUSESSL_TRY) {
707 failf(data, "STARTTLS denied. %c", smtpcode);
708 result = CURLE_USE_SSL_FAILED;
711 result = smtp_perform_authenticate(conn);
714 result = smtp_perform_upgrade_tls(conn);
719 /* For EHLO responses */
720 static CURLcode smtp_state_ehlo_resp(struct connectdata *conn, int smtpcode,
723 CURLcode result = CURLE_OK;
724 struct SessionHandle *data = conn->data;
725 struct smtp_conn *smtpc = &conn->proto.smtpc;
727 (void)instate; /* no use for this yet */
729 if(smtpcode/100 != 2) {
730 if((data->set.use_ssl <= CURLUSESSL_TRY || conn->ssl[FIRSTSOCKET].use) &&
731 !conn->bits.user_passwd)
732 result = smtp_perform_helo(conn);
734 failf(data, "Remote access denied: %d", smtpcode);
735 result = CURLE_REMOTE_ACCESS_DENIED;
738 else if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
739 /* We don't have a SSL/TLS connection yet, but SSL is requested */
740 if(smtpc->tls_supported)
741 /* Switch to TLS connection now */
742 result = smtp_perform_starttls(conn);
743 else if(data->set.use_ssl == CURLUSESSL_TRY)
744 /* Fallback and carry on with authentication */
745 result = smtp_perform_authenticate(conn);
747 failf(data, "STARTTLS not supported.");
748 result = CURLE_USE_SSL_FAILED;
752 result = smtp_perform_authenticate(conn);
757 /* For HELO responses */
758 static CURLcode smtp_state_helo_resp(struct connectdata *conn, int smtpcode,
761 CURLcode result = CURLE_OK;
762 struct SessionHandle *data = conn->data;
764 (void)instate; /* no use for this yet */
766 if(smtpcode/100 != 2) {
767 failf(data, "Remote access denied: %d", smtpcode);
768 result = CURLE_REMOTE_ACCESS_DENIED;
771 /* End of connect phase */
772 state(conn, SMTP_STOP);
777 /* For AUTH PLAIN (without initial response) responses */
778 static CURLcode smtp_state_auth_plain_resp(struct connectdata *conn,
782 CURLcode result = CURLE_OK;
783 struct SessionHandle *data = conn->data;
785 char *plainauth = NULL;
787 (void)instate; /* no use for this yet */
789 if(smtpcode != 334) {
790 failf(data, "Access denied: %d", smtpcode);
791 result = CURLE_LOGIN_DENIED;
794 /* Create the authorisation message */
795 result = Curl_sasl_create_plain_message(conn->data, conn->user,
796 conn->passwd, &plainauth, &len);
798 /* Send the message */
801 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", plainauth);
804 state(conn, SMTP_AUTH_FINAL);
807 Curl_safefree(plainauth);
814 /* For AUTH LOGIN (without initial response) responses */
815 static CURLcode smtp_state_auth_login_resp(struct connectdata *conn,
819 CURLcode result = CURLE_OK;
820 struct SessionHandle *data = conn->data;
822 char *authuser = NULL;
824 (void)instate; /* no use for this yet */
826 if(smtpcode != 334) {
827 failf(data, "Access denied: %d", smtpcode);
828 result = CURLE_LOGIN_DENIED;
831 /* Create the user message */
832 result = Curl_sasl_create_login_message(conn->data, conn->user,
838 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", authuser);
841 state(conn, SMTP_AUTH_LOGIN_PASSWD);
844 Curl_safefree(authuser);
851 /* For AUTH LOGIN user entry responses */
852 static CURLcode smtp_state_auth_login_password_resp(struct connectdata *conn,
856 CURLcode result = CURLE_OK;
857 struct SessionHandle *data = conn->data;
859 char *authpasswd = NULL;
861 (void)instate; /* no use for this yet */
863 if(smtpcode != 334) {
864 failf(data, "Access denied: %d", smtpcode);
865 result = CURLE_LOGIN_DENIED;
868 /* Create the password message */
869 result = Curl_sasl_create_login_message(conn->data, conn->passwd,
872 /* Send the password */
875 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", authpasswd);
878 state(conn, SMTP_AUTH_FINAL);
881 Curl_safefree(authpasswd);
888 #ifndef CURL_DISABLE_CRYPTO_AUTH
889 /* For AUTH CRAM-MD5 responses */
890 static CURLcode smtp_state_auth_cram_resp(struct connectdata *conn,
894 CURLcode result = CURLE_OK;
895 struct SessionHandle *data = conn->data;
896 char *chlg64 = data->state.buffer;
898 char *rplyb64 = NULL;
900 (void)instate; /* no use for this yet */
902 if(smtpcode != 334) {
903 failf(data, "Access denied: %d", smtpcode);
904 return CURLE_LOGIN_DENIED;
907 /* Get the challenge */
908 for(chlg64 += 4; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
911 /* Terminate the challenge */
913 for(len = strlen(chlg64); len--;)
914 if(chlg64[len] != '\r' && chlg64[len] != '\n' && chlg64[len] != ' ' &&
923 /* Create the response message */
924 result = Curl_sasl_create_cram_md5_message(data, chlg64, conn->user,
925 conn->passwd, &rplyb64, &len);
927 /* Send the response */
930 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", rplyb64);
933 state(conn, SMTP_AUTH_FINAL);
936 Curl_safefree(rplyb64);
942 /* For AUTH DIGEST-MD5 challenge responses */
943 static CURLcode smtp_state_auth_digest_resp(struct connectdata *conn,
947 CURLcode result = CURLE_OK;
948 struct SessionHandle *data = conn->data;
949 char *chlg64 = data->state.buffer;
951 char *rplyb64 = NULL;
953 (void)instate; /* no use for this yet */
955 if(smtpcode != 334) {
956 failf(data, "Access denied: %d", smtpcode);
957 return CURLE_LOGIN_DENIED;
960 /* Get the challenge */
961 for(chlg64 += 4; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
964 /* Create the response message */
965 result = Curl_sasl_create_digest_md5_message(data, chlg64, conn->user,
966 conn->passwd, "smtp",
969 /* Send the response */
972 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", rplyb64);
975 state(conn, SMTP_AUTH_DIGESTMD5_RESP);
978 Curl_safefree(rplyb64);
984 /* For AUTH DIGEST-MD5 challenge-response responses */
985 static CURLcode smtp_state_auth_digest_resp_resp(struct connectdata *conn,
989 CURLcode result = CURLE_OK;
990 struct SessionHandle *data = conn->data;
992 (void)instate; /* no use for this yet */
994 if(smtpcode != 334) {
995 failf(data, "Authentication failed: %d", smtpcode);
996 result = CURLE_LOGIN_DENIED;
999 /* Send an empty response */
1000 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "");
1003 state(conn, SMTP_AUTH_FINAL);
1012 /* For AUTH NTLM (without initial response) responses */
1013 static CURLcode smtp_state_auth_ntlm_resp(struct connectdata *conn,
1017 CURLcode result = CURLE_OK;
1018 struct SessionHandle *data = conn->data;
1019 char *type1msg = NULL;
1022 (void)instate; /* no use for this yet */
1024 if(smtpcode != 334) {
1025 failf(data, "Access denied: %d", smtpcode);
1026 result = CURLE_LOGIN_DENIED;
1029 /* Create the type-1 message */
1030 result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
1034 /* Send the message */
1037 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", type1msg);
1040 state(conn, SMTP_AUTH_NTLM_TYPE2MSG);
1043 Curl_safefree(type1msg);
1050 /* For NTLM type-2 responses (sent in reponse to our type-1 message) */
1051 static CURLcode smtp_state_auth_ntlm_type2msg_resp(struct connectdata *conn,
1055 CURLcode result = CURLE_OK;
1056 struct SessionHandle *data = conn->data;
1057 char *type3msg = NULL;
1060 (void)instate; /* no use for this yet */
1062 if(smtpcode != 334) {
1063 failf(data, "Access denied: %d", smtpcode);
1064 result = CURLE_LOGIN_DENIED;
1067 /* Create the type-3 message */
1068 result = Curl_sasl_create_ntlm_type3_message(data,
1069 data->state.buffer + 4,
1070 conn->user, conn->passwd,
1074 /* Send the message */
1077 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", type3msg);
1080 state(conn, SMTP_AUTH_FINAL);
1083 Curl_safefree(type3msg);
1091 /* For the final responses to the AUTH sequence */
1092 static CURLcode smtp_state_auth_final_resp(struct connectdata *conn,
1096 CURLcode result = CURLE_OK;
1097 struct SessionHandle *data = conn->data;
1099 (void)instate; /* no use for this yet */
1101 if(smtpcode != 235) {
1102 failf(data, "Authentication failed: %d", smtpcode);
1103 result = CURLE_LOGIN_DENIED;
1106 /* End of connect phase */
1107 state(conn, SMTP_STOP);
1112 /* For MAIL responses */
1113 static CURLcode smtp_state_mail_resp(struct connectdata *conn, int smtpcode,
1116 CURLcode result = CURLE_OK;
1117 struct SessionHandle *data = conn->data;
1118 struct SMTP *smtp = data->state.proto.smtp;
1120 (void)instate; /* no use for this yet */
1122 if(smtpcode/100 != 2) {
1123 failf(data, "MAIL failed: %d", smtpcode);
1124 result = CURLE_SEND_ERROR;
1125 state(conn, SMTP_STOP);
1128 smtp->rcpt = data->set.mail_rcpt;
1130 result = smtp_perform_rcpt_to(conn);
1136 /* For RCPT responses */
1137 static CURLcode smtp_state_rcpt_resp(struct connectdata *conn, int smtpcode,
1140 CURLcode result = CURLE_OK;
1141 struct SessionHandle *data = conn->data;
1142 struct SMTP *smtp = data->state.proto.smtp;
1144 (void)instate; /* no use for this yet */
1146 if(smtpcode/100 != 2) {
1147 failf(data, "RCPT failed: %d", smtpcode);
1148 result = CURLE_SEND_ERROR;
1149 state(conn, SMTP_STOP);
1153 smtp->rcpt = smtp->rcpt->next;
1154 result = smtp_perform_rcpt_to(conn);
1156 /* If we failed or still are sending RCPT data then return */
1157 if(result || smtp->rcpt)
1161 /* Send the DATA command */
1162 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "DATA");
1165 state(conn, SMTP_DATA);
1171 /* For DATA response */
1172 static CURLcode smtp_state_data_resp(struct connectdata *conn, int smtpcode,
1175 struct SessionHandle *data = conn->data;
1177 (void)instate; /* no use for this yet */
1179 if(smtpcode != 354) {
1180 state(conn, SMTP_STOP);
1181 return CURLE_SEND_ERROR;
1184 /* Set the progress upload size */
1185 Curl_pgrsSetUploadSize(data, data->set.infilesize);
1188 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL);
1190 /* End of DO phase */
1191 state(conn, SMTP_STOP);
1196 /* For POSTDATA responses, which are received after the entire DATA
1197 part has been sent to the server */
1198 static CURLcode smtp_state_postdata_resp(struct connectdata *conn,
1202 CURLcode result = CURLE_OK;
1204 (void)instate; /* no use for this yet */
1207 result = CURLE_RECV_ERROR;
1209 /* End of DONE phase */
1210 state(conn, SMTP_STOP);
1215 static CURLcode smtp_statemach_act(struct connectdata *conn)
1217 CURLcode result = CURLE_OK;
1218 curl_socket_t sock = conn->sock[FIRSTSOCKET];
1219 struct SessionHandle *data = conn->data;
1221 struct smtp_conn *smtpc = &conn->proto.smtpc;
1222 struct pingpong *pp = &smtpc->pp;
1225 /* Busy upgrading the connection; right now all I/O is SSL/TLS, not SMTP */
1226 if(smtpc->state == SMTP_UPGRADETLS)
1227 return smtp_perform_upgrade_tls(conn);
1229 /* Flush any data that needs to be sent */
1231 return Curl_pp_flushsend(pp);
1233 /* Read the response from the server */
1234 result = Curl_pp_readresp(sock, pp, &smtpcode, &nread);
1238 /* Store the latest response for later retrieval */
1239 if(smtpc->state != SMTP_QUIT)
1240 data->info.httpcode = smtpcode;
1243 /* We have now received a full SMTP server response */
1244 switch(smtpc->state) {
1245 case SMTP_SERVERGREET:
1246 result = smtp_state_servergreet_resp(conn, smtpcode, smtpc->state);
1250 result = smtp_state_ehlo_resp(conn, smtpcode, smtpc->state);
1254 result = smtp_state_helo_resp(conn, smtpcode, smtpc->state);
1258 result = smtp_state_starttls_resp(conn, smtpcode, smtpc->state);
1261 case SMTP_AUTH_PLAIN:
1262 result = smtp_state_auth_plain_resp(conn, smtpcode, smtpc->state);
1265 case SMTP_AUTH_LOGIN:
1266 result = smtp_state_auth_login_resp(conn, smtpcode, smtpc->state);
1269 case SMTP_AUTH_LOGIN_PASSWD:
1270 result = smtp_state_auth_login_password_resp(conn, smtpcode,
1274 #ifndef CURL_DISABLE_CRYPTO_AUTH
1275 case SMTP_AUTH_CRAMMD5:
1276 result = smtp_state_auth_cram_resp(conn, smtpcode, smtpc->state);
1279 case SMTP_AUTH_DIGESTMD5:
1280 result = smtp_state_auth_digest_resp(conn, smtpcode, smtpc->state);
1283 case SMTP_AUTH_DIGESTMD5_RESP:
1284 result = smtp_state_auth_digest_resp_resp(conn, smtpcode, smtpc->state);
1289 case SMTP_AUTH_NTLM:
1290 result = smtp_state_auth_ntlm_resp(conn, smtpcode, smtpc->state);
1293 case SMTP_AUTH_NTLM_TYPE2MSG:
1294 result = smtp_state_auth_ntlm_type2msg_resp(conn, smtpcode,
1299 case SMTP_AUTH_FINAL:
1300 result = smtp_state_auth_final_resp(conn, smtpcode, smtpc->state);
1304 result = smtp_state_mail_resp(conn, smtpcode, smtpc->state);
1308 result = smtp_state_rcpt_resp(conn, smtpcode, smtpc->state);
1312 result = smtp_state_data_resp(conn, smtpcode, smtpc->state);
1316 result = smtp_state_postdata_resp(conn, smtpcode, smtpc->state);
1320 /* fallthrough, just stop! */
1322 /* internal error */
1323 state(conn, SMTP_STOP);
1331 /* Called repeatedly until done from multi.c */
1332 static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done)
1334 CURLcode result = CURLE_OK;
1335 struct smtp_conn *smtpc = &conn->proto.smtpc;
1337 if((conn->handler->flags & PROTOPT_SSL) && !smtpc->ssldone)
1338 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone);
1340 result = Curl_pp_statemach(&smtpc->pp, FALSE);
1342 *done = (smtpc->state == SMTP_STOP) ? TRUE : FALSE;
1347 static CURLcode smtp_block_statemach(struct connectdata *conn)
1349 CURLcode result = CURLE_OK;
1350 struct smtp_conn *smtpc = &conn->proto.smtpc;
1352 while(smtpc->state != SMTP_STOP && !result)
1353 result = Curl_pp_statemach(&smtpc->pp, TRUE);
1358 /* Allocate and initialize the SMTP struct for the current SessionHandle if
1360 static CURLcode smtp_init(struct connectdata *conn)
1362 CURLcode result = CURLE_OK;
1363 struct SessionHandle *data = conn->data;
1364 struct SMTP *smtp = data->state.proto.smtp;
1367 smtp = data->state.proto.smtp = calloc(sizeof(struct SMTP), 1);
1369 result = CURLE_OUT_OF_MEMORY;
1375 /* For the SMTP "protocol connect" and "doing" phases only */
1376 static int smtp_getsock(struct connectdata *conn, curl_socket_t *socks,
1379 return Curl_pp_getsock(&conn->proto.smtpc.pp, socks, numsocks);
1382 /***********************************************************************
1386 * This function should do everything that is to be considered a part of
1387 * the connection phase.
1389 * The variable pointed to by 'done' will be TRUE if the protocol-layer
1390 * connect phase is done when this function returns, or FALSE if not.
1392 static CURLcode smtp_connect(struct connectdata *conn, bool *done)
1394 CURLcode result = CURLE_OK;
1395 struct smtp_conn *smtpc = &conn->proto.smtpc;
1396 struct pingpong *pp = &smtpc->pp;
1398 *done = FALSE; /* default to not done yet */
1400 /* If there already is a protocol-specific struct allocated for this
1401 sessionhandle, deal with it */
1402 Curl_reset_reqproto(conn);
1404 /* Initialise the SMTP layer */
1405 result = smtp_init(conn);
1409 /* We always support persistent connections in SMTP */
1410 conn->bits.close = FALSE;
1412 /* Set the default response time-out */
1413 pp->response_time = RESP_TIMEOUT;
1414 pp->statemach_act = smtp_statemach_act;
1415 pp->endofresp = smtp_endofresp;
1418 /* Set the default preferred authentication mechanism */
1419 smtpc->prefmech = SASL_AUTH_ANY;
1421 /* Initialise the pingpong layer */
1424 /* Parse the URL options */
1425 result = smtp_parse_url_options(conn);
1429 /* Parse the URL path */
1430 result = smtp_parse_url_path(conn);
1434 /* Start off waiting for the server greeting response */
1435 state(conn, SMTP_SERVERGREET);
1437 result = smtp_multi_statemach(conn, done);
1442 /***********************************************************************
1446 * The DONE function. This does what needs to be done after a single DO has
1449 * Input argument is already checked for validity.
1451 static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
1454 CURLcode result = CURLE_OK;
1455 struct SessionHandle *data = conn->data;
1456 struct SMTP *smtp = data->state.proto.smtp;
1457 struct pingpong *pp = &conn->proto.smtpc.pp;
1460 ssize_t bytes_written;
1465 /* When the easy handle is removed from the multi interface while libcurl
1466 is still trying to resolve the host name, the SMTP struct is not yet
1467 initialized. However, the removal action calls Curl_done() which in
1468 turn calls this function, so we simply return success. */
1472 conn->bits.close = TRUE; /* marked for closure */
1473 result = status; /* use the already set error code */
1475 else if(!data->set.connect_only) {
1476 /* Calculate the EOB taking into account any terminating CRLF from the
1477 previous line of the email or the CRLF of the DATA command when there
1478 is "no mail data". RFC-5321, sect. 4.1.1.4. */
1481 if(smtp->trailing_crlf || !conn->data->set.infilesize) {
1486 /* Send the end of block data */
1487 result = Curl_write(conn, conn->writesockfd, eob, len, &bytes_written);
1491 if(bytes_written != len) {
1492 /* The whole chunk was not sent so keep it around and adjust the
1493 pingpong structure accordingly */
1494 pp->sendthis = strdup(eob);
1496 pp->sendleft = len - bytes_written;
1499 /* Successfully sent so adjust the response timeout relative to now */
1500 pp->response = Curl_tvnow();
1502 state(conn, SMTP_POSTDATA);
1504 /* Run the state-machine
1506 TODO: when the multi interface is used, this _really_ should be using
1507 the smtp_multi_statemach function but we have no general support for
1508 non-blocking DONE operations, not in the multi state machine and with
1509 Curl_done() invokes on several places in the code!
1511 result = smtp_block_statemach(conn);
1514 /* Clear the transfer mode for the next request */
1515 smtp->transfer = FTPTRANSFER_BODY;
1520 /***********************************************************************
1524 * This is the actual DO function for SMTP. Send a mail according to the
1525 * options previously setup.
1527 static CURLcode smtp_perform(struct connectdata *conn, bool *connected,
1530 /* This is SMTP and no proxy */
1531 CURLcode result = CURLE_OK;
1533 DEBUGF(infof(conn->data, "DO phase starts\n"));
1535 if(conn->data->set.opt_no_body) {
1536 /* Requested no body means no transfer */
1537 struct SMTP *smtp = conn->data->state.proto.smtp;
1538 smtp->transfer = FTPTRANSFER_INFO;
1541 *dophase_done = FALSE; /* not done yet */
1543 /* Start the first command in the DO phase */
1544 result = smtp_perform_mail(conn);
1548 /* run the state-machine */
1549 result = smtp_multi_statemach(conn, dophase_done);
1551 *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1554 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1559 /***********************************************************************
1563 * This function is registered as 'curl_do' function. It decodes the path
1564 * parts etc as a wrapper to the actual DO function (smtp_perform).
1566 * The input argument is already checked for validity.
1568 static CURLcode smtp_do(struct connectdata *conn, bool *done)
1570 CURLcode result = CURLE_OK;
1572 *done = FALSE; /* default to false */
1574 /* Since connections can be re-used between SessionHandles, there might be a
1575 connection already existing but on a fresh SessionHandle struct. As such
1576 we make sure we have a good SMTP struct to play with. For new connections
1577 the SMTP struct is allocated and setup in the smtp_connect() function. */
1578 Curl_reset_reqproto(conn);
1579 result = smtp_init(conn);
1583 result = smtp_regular_transfer(conn, done);
1588 /***********************************************************************
1592 * Disconnect from an SMTP server. Cleanup protocol-specific per-connection
1593 * resources. BLOCKING.
1595 static CURLcode smtp_disconnect(struct connectdata *conn,
1596 bool dead_connection)
1598 struct smtp_conn *smtpc = &conn->proto.smtpc;
1600 /* We cannot send quit unconditionally. If this connection is stale or
1601 bad in any way, sending quit and waiting around here will make the
1602 disconnect wait in vain and cause more problems than we need to. */
1604 /* The SMTP session may or may not have been allocated/setup at this
1606 if(!dead_connection && smtpc->pp.conn)
1607 if(!smtp_perform_quit(conn))
1608 (void)smtp_block_statemach(conn); /* ignore errors on QUIT */
1610 /* Disconnect from the server */
1611 Curl_pp_disconnect(&smtpc->pp);
1613 /* Cleanup the SASL module */
1614 Curl_sasl_cleanup(conn, smtpc->authused);
1616 /* Cleanup our connection based variables */
1617 Curl_safefree(smtpc->domain);
1622 /* Call this when the DO phase has completed */
1623 static CURLcode smtp_dophase_done(struct connectdata *conn, bool connected)
1625 struct SMTP *smtp = conn->data->state.proto.smtp;
1629 if(smtp->transfer != FTPTRANSFER_BODY)
1630 /* no data to transfer */
1631 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1636 /* Called from multi.c while DOing */
1637 static CURLcode smtp_doing(struct connectdata *conn, bool *dophase_done)
1639 CURLcode result = smtp_multi_statemach(conn, dophase_done);
1642 DEBUGF(infof(conn->data, "DO phase failed\n"));
1643 else if(*dophase_done) {
1644 result = smtp_dophase_done(conn, FALSE /* not connected */);
1646 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1652 /***********************************************************************
1654 * smtp_regular_transfer()
1656 * The input argument is already checked for validity.
1658 * Performs all commands done before a regular transfer between a local and a
1661 static CURLcode smtp_regular_transfer(struct connectdata *conn,
1664 CURLcode result = CURLE_OK;
1665 bool connected = FALSE;
1666 struct SessionHandle *data = conn->data;
1668 /* Make sure size is unknown at this point */
1669 data->req.size = -1;
1671 /* Set the progress data */
1672 Curl_pgrsSetUploadCounter(data, 0);
1673 Curl_pgrsSetDownloadCounter(data, 0);
1674 Curl_pgrsSetUploadSize(data, 0);
1675 Curl_pgrsSetDownloadSize(data, 0);
1677 /* Carry out the perform */
1678 result = smtp_perform(conn, &connected, dophase_done);
1680 /* Perform post DO phase operations if necessary */
1681 if(!result && *dophase_done)
1682 result = smtp_dophase_done(conn, connected);
1687 static CURLcode smtp_setup_connection(struct connectdata *conn)
1689 struct SessionHandle *data = conn->data;
1691 if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
1692 /* Unless we have asked to tunnel SMTP operations through the proxy, we
1693 switch and use HTTP operations only */
1694 #ifndef CURL_DISABLE_HTTP
1695 if(conn->handler == &Curl_handler_smtp)
1696 conn->handler = &Curl_handler_smtp_proxy;
1699 conn->handler = &Curl_handler_smtps_proxy;
1701 failf(data, "SMTPS not supported!");
1702 return CURLE_UNSUPPORTED_PROTOCOL;
1706 /* We explicitly mark this connection as persistent here as we're doing
1707 SMTP over HTTP and thus we accidentally avoid setting this value
1709 conn->bits.close = FALSE;
1711 failf(data, "SMTP over http proxy requires HTTP support built-in!");
1712 return CURLE_UNSUPPORTED_PROTOCOL;
1716 data->state.path++; /* don't include the initial slash */
1721 /***********************************************************************
1723 * smtp_parse_url_options()
1725 * Parse the URL login options.
1727 static CURLcode smtp_parse_url_options(struct connectdata *conn)
1729 CURLcode result = CURLE_OK;
1730 struct smtp_conn *smtpc = &conn->proto.smtpc;
1731 const char *options = conn->options;
1732 const char *ptr = options;
1735 const char *key = ptr;
1737 while(*ptr && *ptr != '=')
1740 if(strnequal(key, "AUTH", 4)) {
1741 const char *value = ptr + 1;
1743 if(strequal(value, "*"))
1744 smtpc->prefmech = SASL_AUTH_ANY;
1745 else if(strequal(value, "LOGIN"))
1746 smtpc->prefmech = SASL_MECH_LOGIN;
1747 else if(strequal(value, "PLAIN"))
1748 smtpc->prefmech = SASL_MECH_PLAIN;
1749 else if(strequal(value, "CRAM-MD5"))
1750 smtpc->prefmech = SASL_MECH_CRAM_MD5;
1751 else if(strequal(value, "DIGEST-MD5"))
1752 smtpc->prefmech = SASL_MECH_DIGEST_MD5;
1753 else if(strequal(value, "GSSAPI"))
1754 smtpc->prefmech = SASL_MECH_GSSAPI;
1755 else if(strequal(value, "NTLM"))
1756 smtpc->prefmech = SASL_MECH_NTLM;
1758 smtpc->prefmech = SASL_AUTH_NONE;
1761 result = CURLE_URL_MALFORMAT;
1767 /***********************************************************************
1769 * smtp_parse_url_path()
1771 * Parse the URL path into separate path components.
1773 static CURLcode smtp_parse_url_path(struct connectdata *conn)
1775 /* The SMTP struct is already initialised in smtp_connect() */
1776 struct SessionHandle *data = conn->data;
1777 struct smtp_conn *smtpc = &conn->proto.smtpc;
1778 const char *path = data->state.path;
1779 char localhost[HOSTNAME_MAX + 1];
1781 /* Calculate the path if necessary */
1783 if(!Curl_gethostname(localhost, sizeof(localhost)))
1789 /* URL decode the path and use it as the domain in our EHLO */
1790 return Curl_urldecode(conn->data, path, 0, &smtpc->domain, NULL, TRUE);
1793 CURLcode Curl_smtp_escape_eob(struct connectdata *conn, ssize_t nread)
1795 /* When sending a SMTP payload we must detect CRLF. sequences making sure
1796 they are sent as CRLF.. instead, as a . on the beginning of a line will
1797 be deleted by the server when not part of an EOB terminator and a
1798 genuine CRLF.CRLF which isn't escaped will wrongly be detected as end of
1803 struct SessionHandle *data = conn->data;
1804 struct SMTP *smtp = data->state.proto.smtp;
1806 /* Do we need to allocate the scatch buffer? */
1807 if(!data->state.scratch) {
1808 data->state.scratch = malloc(2 * BUFSIZE);
1810 if(!data->state.scratch) {
1811 failf (data, "Failed to alloc scratch buffer!");
1812 return CURLE_OUT_OF_MEMORY;
1816 /* This loop can be improved by some kind of Boyer-Moore style of
1817 approach but that is saved for later... */
1818 for(i = 0, si = 0; i < nread; i++) {
1819 if(SMTP_EOB[smtp->eob] == data->req.upload_fromhere[i]) {
1822 /* Is the EOB potentially the terminating CRLF? */
1823 if(2 == smtp->eob || SMTP_EOB_LEN == smtp->eob)
1824 smtp->trailing_crlf = TRUE;
1826 smtp->trailing_crlf = FALSE;
1828 else if(smtp->eob) {
1829 /* A previous substring matched so output that first */
1830 memcpy(&data->state.scratch[si], SMTP_EOB, smtp->eob);
1833 /* Then compare the first byte */
1834 if(SMTP_EOB[0] == data->req.upload_fromhere[i])
1839 /* Reset the trailing CRLF flag as there was more data */
1840 smtp->trailing_crlf = FALSE;
1843 /* Do we have a match for CRLF. as per RFC-5321, sect. 4.5.2 */
1844 if(SMTP_EOB_FIND_LEN == smtp->eob) {
1845 /* Copy the replacement data to the target buffer */
1846 memcpy(&data->state.scratch[si], SMTP_EOB_REPL, SMTP_EOB_REPL_LEN);
1847 si += SMTP_EOB_REPL_LEN;
1851 data->state.scratch[si++] = data->req.upload_fromhere[i];
1855 /* A substring matched before processing ended so output that now */
1856 memcpy(&data->state.scratch[si], SMTP_EOB, smtp->eob);
1862 /* Only use the new buffer if we replaced something */
1865 /* Upload from the new (replaced) buffer instead */
1866 data->req.upload_fromhere = data->state.scratch;
1868 /* Set the new amount too */
1869 data->req.upload_present = nread;
1875 #endif /* CURL_DISABLE_SMTP */