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 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, "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);
522 /* Other mechanisms not supported */
523 infof(conn->data, "No known authentication mechanisms supported!\n");
524 result = CURLE_LOGIN_DENIED;
528 /* Perform SASL based authentication */
530 strlen(mech) + len <= 512 - 8) { /* AUTH <mech> ...<crlf> */
531 result = Curl_pp_sendf(&smtpc->pp, "AUTH %s %s", mech, initresp);
537 result = Curl_pp_sendf(&smtpc->pp, "AUTH %s", mech);
543 Curl_safefree(initresp);
549 /***********************************************************************
551 * smtp_perform_mail()
553 * Sends an MAIL command to initiate the upload of a message.
555 static CURLcode smtp_perform_mail(struct connectdata *conn)
560 CURLcode result = CURLE_OK;
561 struct SessionHandle *data = conn->data;
563 /* Calculate the FROM parameter */
564 if(!data->set.str[STRING_MAIL_FROM])
565 /* Null reverse-path, RFC-2821, sect. 3.7 */
567 else if(data->set.str[STRING_MAIL_FROM][0] == '<')
568 from = aprintf("%s", data->set.str[STRING_MAIL_FROM]);
570 from = aprintf("<%s>", data->set.str[STRING_MAIL_FROM]);
573 return CURLE_OUT_OF_MEMORY;
575 /* Calculate the optional AUTH parameter */
576 if(data->set.str[STRING_MAIL_AUTH] && conn->proto.smtpc.authused) {
577 if(data->set.str[STRING_MAIL_AUTH][0] != '\0')
578 auth = aprintf("%s", data->set.str[STRING_MAIL_AUTH]);
580 /* Empty AUTH, RFC-2554, sect. 5 */
586 return CURLE_OUT_OF_MEMORY;
590 /* calculate the optional SIZE parameter */
591 if(conn->proto.smtpc.size_supported && conn->data->set.infilesize > 0) {
592 size = aprintf("%" FORMAT_OFF_T, data->set.infilesize);
598 return CURLE_OUT_OF_MEMORY;
602 /* Send the MAIL command */
604 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
605 "MAIL FROM:%s", from);
606 else if(auth && !size)
607 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
608 "MAIL FROM:%s AUTH=%s", from, auth);
609 else if(auth && size)
610 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
611 "MAIL FROM:%s AUTH=%s SIZE=%s", from, auth, size);
613 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
614 "MAIL FROM:%s SIZE=%s", from, size);
621 state(conn, SMTP_MAIL);
626 /***********************************************************************
628 * smtp_perform_rcpt_to()
630 * Sends a RCPT TO command for a given recipient as part of the message upload
633 static CURLcode smtp_perform_rcpt_to(struct connectdata *conn)
635 CURLcode result = CURLE_OK;
636 struct SessionHandle *data = conn->data;
637 struct SMTP *smtp = data->state.proto.smtp;
639 /* Send the RCPT TO command */
641 if(smtp->rcpt->data[0] == '<')
642 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:%s",
645 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:<%s>",
648 state(conn, SMTP_RCPT);
654 /***********************************************************************
656 * smtp_perform_quit()
658 * Performs the quit action prior to sclose() being called.
660 static CURLcode smtp_perform_quit(struct connectdata *conn)
662 CURLcode result = CURLE_OK;
664 /* Send the QUIT command */
665 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "QUIT");
668 state(conn, SMTP_QUIT);
673 /* For the initial server greeting */
674 static CURLcode smtp_state_servergreet_resp(struct connectdata *conn,
678 CURLcode result = CURLE_OK;
679 struct SessionHandle *data = conn->data;
681 (void)instate; /* no use for this yet */
683 if(smtpcode/100 != 2) {
684 failf(data, "Got unexpected smtp-server response: %d", smtpcode);
685 result = CURLE_FTP_WEIRD_SERVER_REPLY;
688 result = smtp_perform_ehlo(conn);
693 /* For STARTTLS responses */
694 static CURLcode smtp_state_starttls_resp(struct connectdata *conn,
698 CURLcode result = CURLE_OK;
699 struct SessionHandle *data = conn->data;
701 (void)instate; /* no use for this yet */
703 if(smtpcode != 220) {
704 if(data->set.use_ssl != CURLUSESSL_TRY) {
705 failf(data, "STARTTLS denied. %c", smtpcode);
706 result = CURLE_USE_SSL_FAILED;
709 result = smtp_perform_authenticate(conn);
712 result = smtp_perform_upgrade_tls(conn);
717 /* For EHLO responses */
718 static CURLcode smtp_state_ehlo_resp(struct connectdata *conn, int smtpcode,
721 CURLcode result = CURLE_OK;
722 struct SessionHandle *data = conn->data;
723 struct smtp_conn *smtpc = &conn->proto.smtpc;
725 (void)instate; /* no use for this yet */
727 if(smtpcode/100 != 2) {
728 if((data->set.use_ssl <= CURLUSESSL_TRY || conn->ssl[FIRSTSOCKET].use) &&
729 !conn->bits.user_passwd)
730 result = smtp_perform_helo(conn);
732 failf(data, "Remote access denied: %d", smtpcode);
733 result = CURLE_REMOTE_ACCESS_DENIED;
736 else if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
737 /* We don't have a SSL/TLS connection yet, but SSL is requested */
738 if(smtpc->tls_supported)
739 /* Switch to TLS connection now */
740 result = smtp_perform_starttls(conn);
741 else if(data->set.use_ssl == CURLUSESSL_TRY)
742 /* Fallback and carry on with authentication */
743 result = smtp_perform_authenticate(conn);
745 failf(data, "STARTTLS not supported.");
746 result = CURLE_USE_SSL_FAILED;
750 result = smtp_perform_authenticate(conn);
755 /* For HELO responses */
756 static CURLcode smtp_state_helo_resp(struct connectdata *conn, int smtpcode,
759 CURLcode result = CURLE_OK;
760 struct SessionHandle *data = conn->data;
762 (void)instate; /* no use for this yet */
764 if(smtpcode/100 != 2) {
765 failf(data, "Remote access denied: %d", smtpcode);
766 result = CURLE_REMOTE_ACCESS_DENIED;
769 /* End of connect phase */
770 state(conn, SMTP_STOP);
775 /* For AUTH PLAIN (without initial response) responses */
776 static CURLcode smtp_state_auth_plain_resp(struct connectdata *conn,
780 CURLcode result = CURLE_OK;
781 struct SessionHandle *data = conn->data;
783 char *plainauth = NULL;
785 (void)instate; /* no use for this yet */
787 if(smtpcode != 334) {
788 failf(data, "Access denied: %d", smtpcode);
789 result = CURLE_LOGIN_DENIED;
792 /* Create the authorisation message */
793 result = Curl_sasl_create_plain_message(conn->data, conn->user,
794 conn->passwd, &plainauth, &len);
796 /* Send the message */
799 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", plainauth);
802 state(conn, SMTP_AUTH_FINAL);
805 Curl_safefree(plainauth);
812 /* For AUTH LOGIN (without initial response) responses */
813 static CURLcode smtp_state_auth_login_resp(struct connectdata *conn,
817 CURLcode result = CURLE_OK;
818 struct SessionHandle *data = conn->data;
820 char *authuser = NULL;
822 (void)instate; /* no use for this yet */
824 if(smtpcode != 334) {
825 failf(data, "Access denied: %d", smtpcode);
826 result = CURLE_LOGIN_DENIED;
829 /* Create the user message */
830 result = Curl_sasl_create_login_message(conn->data, conn->user,
836 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", authuser);
839 state(conn, SMTP_AUTH_LOGIN_PASSWD);
842 Curl_safefree(authuser);
849 /* For AUTH LOGIN user entry responses */
850 static CURLcode smtp_state_auth_login_password_resp(struct connectdata *conn,
854 CURLcode result = CURLE_OK;
855 struct SessionHandle *data = conn->data;
857 char *authpasswd = NULL;
859 (void)instate; /* no use for this yet */
861 if(smtpcode != 334) {
862 failf(data, "Access denied: %d", smtpcode);
863 result = CURLE_LOGIN_DENIED;
866 /* Create the password message */
867 result = Curl_sasl_create_login_message(conn->data, conn->passwd,
870 /* Send the password */
873 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", authpasswd);
876 state(conn, SMTP_AUTH_FINAL);
879 Curl_safefree(authpasswd);
886 #ifndef CURL_DISABLE_CRYPTO_AUTH
887 /* For AUTH CRAM-MD5 responses */
888 static CURLcode smtp_state_auth_cram_resp(struct connectdata *conn,
892 CURLcode result = CURLE_OK;
893 struct SessionHandle *data = conn->data;
894 char *chlg64 = data->state.buffer;
896 char *rplyb64 = NULL;
898 (void)instate; /* no use for this yet */
900 if(smtpcode != 334) {
901 failf(data, "Access denied: %d", smtpcode);
902 return CURLE_LOGIN_DENIED;
905 /* Get the challenge */
906 for(chlg64 += 4; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
909 /* Terminate the challenge */
911 for(len = strlen(chlg64); len--;)
912 if(chlg64[len] != '\r' && chlg64[len] != '\n' && chlg64[len] != ' ' &&
921 /* Create the response message */
922 result = Curl_sasl_create_cram_md5_message(data, chlg64, conn->user,
923 conn->passwd, &rplyb64, &len);
925 /* Send the response */
928 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", rplyb64);
931 state(conn, SMTP_AUTH_FINAL);
934 Curl_safefree(rplyb64);
940 /* For AUTH DIGEST-MD5 challenge responses */
941 static CURLcode smtp_state_auth_digest_resp(struct connectdata *conn,
945 CURLcode result = CURLE_OK;
946 struct SessionHandle *data = conn->data;
947 char *chlg64 = data->state.buffer;
949 char *rplyb64 = NULL;
951 (void)instate; /* no use for this yet */
953 if(smtpcode != 334) {
954 failf(data, "Access denied: %d", smtpcode);
955 return CURLE_LOGIN_DENIED;
958 /* Get the challenge */
959 for(chlg64 += 4; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
962 /* Create the response message */
963 result = Curl_sasl_create_digest_md5_message(data, chlg64, conn->user,
964 conn->passwd, "smtp",
967 /* Send the response */
970 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", rplyb64);
973 state(conn, SMTP_AUTH_DIGESTMD5_RESP);
976 Curl_safefree(rplyb64);
982 /* For AUTH DIGEST-MD5 challenge-response responses */
983 static CURLcode smtp_state_auth_digest_resp_resp(struct connectdata *conn,
987 CURLcode result = CURLE_OK;
988 struct SessionHandle *data = conn->data;
990 (void)instate; /* no use for this yet */
992 if(smtpcode != 334) {
993 failf(data, "Authentication failed: %d", smtpcode);
994 result = CURLE_LOGIN_DENIED;
997 /* Send an empty response */
998 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "");
1001 state(conn, SMTP_AUTH_FINAL);
1010 /* For AUTH NTLM (without initial response) responses */
1011 static CURLcode smtp_state_auth_ntlm_resp(struct connectdata *conn,
1015 CURLcode result = CURLE_OK;
1016 struct SessionHandle *data = conn->data;
1017 char *type1msg = NULL;
1020 (void)instate; /* no use for this yet */
1022 if(smtpcode != 334) {
1023 failf(data, "Access denied: %d", smtpcode);
1024 result = CURLE_LOGIN_DENIED;
1027 /* Create the type-1 message */
1028 result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
1032 /* Send the message */
1035 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", type1msg);
1038 state(conn, SMTP_AUTH_NTLM_TYPE2MSG);
1041 Curl_safefree(type1msg);
1048 /* For NTLM type-2 responses (sent in reponse to our type-1 message) */
1049 static CURLcode smtp_state_auth_ntlm_type2msg_resp(struct connectdata *conn,
1053 CURLcode result = CURLE_OK;
1054 struct SessionHandle *data = conn->data;
1055 char *type3msg = NULL;
1058 (void)instate; /* no use for this yet */
1060 if(smtpcode != 334) {
1061 failf(data, "Access denied: %d", smtpcode);
1062 result = CURLE_LOGIN_DENIED;
1065 /* Create the type-3 message */
1066 result = Curl_sasl_create_ntlm_type3_message(data,
1067 data->state.buffer + 4,
1068 conn->user, conn->passwd,
1072 /* Send the message */
1075 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", type3msg);
1078 state(conn, SMTP_AUTH_FINAL);
1081 Curl_safefree(type3msg);
1089 /* For the final responses to the AUTH sequence */
1090 static CURLcode smtp_state_auth_final_resp(struct connectdata *conn,
1094 CURLcode result = CURLE_OK;
1095 struct SessionHandle *data = conn->data;
1097 (void)instate; /* no use for this yet */
1099 if(smtpcode != 235) {
1100 failf(data, "Authentication failed: %d", smtpcode);
1101 result = CURLE_LOGIN_DENIED;
1104 /* End of connect phase */
1105 state(conn, SMTP_STOP);
1110 /* For MAIL responses */
1111 static CURLcode smtp_state_mail_resp(struct connectdata *conn, int smtpcode,
1114 CURLcode result = CURLE_OK;
1115 struct SessionHandle *data = conn->data;
1116 struct SMTP *smtp = data->state.proto.smtp;
1118 (void)instate; /* no use for this yet */
1120 if(smtpcode/100 != 2) {
1121 failf(data, "MAIL failed: %d", smtpcode);
1122 result = CURLE_SEND_ERROR;
1123 state(conn, SMTP_STOP);
1126 smtp->rcpt = data->set.mail_rcpt;
1128 result = smtp_perform_rcpt_to(conn);
1134 /* For RCPT responses */
1135 static CURLcode smtp_state_rcpt_resp(struct connectdata *conn, int smtpcode,
1138 CURLcode result = CURLE_OK;
1139 struct SessionHandle *data = conn->data;
1140 struct SMTP *smtp = data->state.proto.smtp;
1142 (void)instate; /* no use for this yet */
1144 if(smtpcode/100 != 2) {
1145 failf(data, "RCPT failed: %d", smtpcode);
1146 result = CURLE_SEND_ERROR;
1147 state(conn, SMTP_STOP);
1151 smtp->rcpt = smtp->rcpt->next;
1152 result = smtp_perform_rcpt_to(conn);
1154 /* If we failed or still are sending RCPT data then return */
1155 if(result || smtp->rcpt)
1159 /* Send the DATA command */
1160 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "DATA");
1163 state(conn, SMTP_DATA);
1169 /* For DATA response */
1170 static CURLcode smtp_state_data_resp(struct connectdata *conn, int smtpcode,
1173 (void)instate; /* no use for this yet */
1175 if(smtpcode != 354) {
1176 state(conn, SMTP_STOP);
1177 return CURLE_SEND_ERROR;
1181 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL);
1183 /* End of DO phase */
1184 state(conn, SMTP_STOP);
1189 /* For POSTDATA responses, which are received after the entire DATA
1190 part has been sent to the server */
1191 static CURLcode smtp_state_postdata_resp(struct connectdata *conn,
1195 CURLcode result = CURLE_OK;
1197 (void)instate; /* no use for this yet */
1200 result = CURLE_RECV_ERROR;
1202 /* End of DONE phase */
1203 state(conn, SMTP_STOP);
1208 static CURLcode smtp_statemach_act(struct connectdata *conn)
1210 CURLcode result = CURLE_OK;
1211 curl_socket_t sock = conn->sock[FIRSTSOCKET];
1212 struct SessionHandle *data = conn->data;
1214 struct smtp_conn *smtpc = &conn->proto.smtpc;
1215 struct pingpong *pp = &smtpc->pp;
1218 /* Busy upgrading the connection; right now all I/O is SSL/TLS, not SMTP */
1219 if(smtpc->state == SMTP_UPGRADETLS)
1220 return smtp_perform_upgrade_tls(conn);
1222 /* Flush any data that needs to be sent */
1224 return Curl_pp_flushsend(pp);
1226 /* Read the response from the server */
1227 result = Curl_pp_readresp(sock, pp, &smtpcode, &nread);
1231 /* Store the latest response for later retrieval */
1232 if(smtpc->state != SMTP_QUIT)
1233 data->info.httpcode = smtpcode;
1236 /* We have now received a full SMTP server response */
1237 switch(smtpc->state) {
1238 case SMTP_SERVERGREET:
1239 result = smtp_state_servergreet_resp(conn, smtpcode, smtpc->state);
1243 result = smtp_state_ehlo_resp(conn, smtpcode, smtpc->state);
1247 result = smtp_state_helo_resp(conn, smtpcode, smtpc->state);
1251 result = smtp_state_starttls_resp(conn, smtpcode, smtpc->state);
1254 case SMTP_AUTH_PLAIN:
1255 result = smtp_state_auth_plain_resp(conn, smtpcode, smtpc->state);
1258 case SMTP_AUTH_LOGIN:
1259 result = smtp_state_auth_login_resp(conn, smtpcode, smtpc->state);
1262 case SMTP_AUTH_LOGIN_PASSWD:
1263 result = smtp_state_auth_login_password_resp(conn, smtpcode,
1267 #ifndef CURL_DISABLE_CRYPTO_AUTH
1268 case SMTP_AUTH_CRAMMD5:
1269 result = smtp_state_auth_cram_resp(conn, smtpcode, smtpc->state);
1272 case SMTP_AUTH_DIGESTMD5:
1273 result = smtp_state_auth_digest_resp(conn, smtpcode, smtpc->state);
1276 case SMTP_AUTH_DIGESTMD5_RESP:
1277 result = smtp_state_auth_digest_resp_resp(conn, smtpcode, smtpc->state);
1282 case SMTP_AUTH_NTLM:
1283 result = smtp_state_auth_ntlm_resp(conn, smtpcode, smtpc->state);
1286 case SMTP_AUTH_NTLM_TYPE2MSG:
1287 result = smtp_state_auth_ntlm_type2msg_resp(conn, smtpcode,
1292 case SMTP_AUTH_FINAL:
1293 result = smtp_state_auth_final_resp(conn, smtpcode, smtpc->state);
1297 result = smtp_state_mail_resp(conn, smtpcode, smtpc->state);
1301 result = smtp_state_rcpt_resp(conn, smtpcode, smtpc->state);
1305 result = smtp_state_data_resp(conn, smtpcode, smtpc->state);
1309 result = smtp_state_postdata_resp(conn, smtpcode, smtpc->state);
1313 /* fallthrough, just stop! */
1315 /* internal error */
1316 state(conn, SMTP_STOP);
1324 /* Called repeatedly until done from multi.c */
1325 static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done)
1327 CURLcode result = CURLE_OK;
1328 struct smtp_conn *smtpc = &conn->proto.smtpc;
1330 if((conn->handler->flags & PROTOPT_SSL) && !smtpc->ssldone)
1331 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone);
1333 result = Curl_pp_statemach(&smtpc->pp, FALSE);
1335 *done = (smtpc->state == SMTP_STOP) ? TRUE : FALSE;
1340 static CURLcode smtp_block_statemach(struct connectdata *conn)
1342 CURLcode result = CURLE_OK;
1343 struct smtp_conn *smtpc = &conn->proto.smtpc;
1345 while(smtpc->state != SMTP_STOP && !result)
1346 result = Curl_pp_statemach(&smtpc->pp, TRUE);
1351 /* Allocate and initialize the SMTP struct for the current SessionHandle if
1353 static CURLcode smtp_init(struct connectdata *conn)
1355 CURLcode result = CURLE_OK;
1356 struct SessionHandle *data = conn->data;
1357 struct SMTP *smtp = data->state.proto.smtp;
1360 smtp = data->state.proto.smtp = calloc(sizeof(struct SMTP), 1);
1362 result = CURLE_OUT_OF_MEMORY;
1368 /* For the SMTP "protocol connect" and "doing" phases only */
1369 static int smtp_getsock(struct connectdata *conn, curl_socket_t *socks,
1372 return Curl_pp_getsock(&conn->proto.smtpc.pp, socks, numsocks);
1375 /***********************************************************************
1379 * This function should do everything that is to be considered a part of
1380 * the connection phase.
1382 * The variable pointed to by 'done' will be TRUE if the protocol-layer
1383 * connect phase is done when this function returns, or FALSE if not. When
1384 * called as a part of the easy interface, it will always be TRUE.
1386 static CURLcode smtp_connect(struct connectdata *conn, bool *done)
1388 CURLcode result = CURLE_OK;
1389 struct smtp_conn *smtpc = &conn->proto.smtpc;
1390 struct pingpong *pp = &smtpc->pp;
1392 *done = FALSE; /* default to not done yet */
1394 /* If there already is a protocol-specific struct allocated for this
1395 sessionhandle, deal with it */
1396 Curl_reset_reqproto(conn);
1398 /* Initialise the SMTP layer */
1399 result = smtp_init(conn);
1403 /* We always support persistent connections in SMTP */
1404 conn->bits.close = FALSE;
1406 /* Set the default response time-out */
1407 pp->response_time = RESP_TIMEOUT;
1408 pp->statemach_act = smtp_statemach_act;
1409 pp->endofresp = smtp_endofresp;
1412 /* Set the default preferred authentication mechanism */
1413 smtpc->prefmech = SASL_AUTH_ANY;
1415 /* Initialise the pingpong layer */
1418 /* Parse the URL options */
1419 result = smtp_parse_url_options(conn);
1423 /* Parse the URL path */
1424 result = smtp_parse_url_path(conn);
1428 /* Start off waiting for the server greeting response */
1429 state(conn, SMTP_SERVERGREET);
1431 result = smtp_multi_statemach(conn, done);
1436 /***********************************************************************
1440 * The DONE function. This does what needs to be done after a single DO has
1443 * Input argument is already checked for validity.
1445 static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
1448 CURLcode result = CURLE_OK;
1449 struct SessionHandle *data = conn->data;
1450 struct SMTP *smtp = data->state.proto.smtp;
1451 ssize_t bytes_written;
1456 /* When the easy handle is removed from the multi interface while libcurl
1457 is still trying to resolve the host name, the SMTP struct is not yet
1458 initialized. However, the removal action calls Curl_done() which in
1459 turn calls this function, so we simply return success. */
1463 conn->bits.close = TRUE; /* marked for closure */
1464 result = status; /* use the already set error code */
1466 else if(!data->set.connect_only) {
1467 struct smtp_conn *smtpc = &conn->proto.smtpc;
1468 struct pingpong *pp = &smtpc->pp;
1470 /* Send the end of block data */
1471 result = Curl_write(conn,
1472 conn->writesockfd, /* socket to send to */
1473 SMTP_EOB, /* buffer pointer */
1474 SMTP_EOB_LEN, /* buffer size */
1475 &bytes_written); /* actually sent away */
1480 if(bytes_written != SMTP_EOB_LEN) {
1481 /* The whole chunk was not sent so keep it around and adjust the
1482 pingpong structure accordingly */
1483 pp->sendthis = strdup(SMTP_EOB);
1484 pp->sendsize = SMTP_EOB_LEN;
1485 pp->sendleft = SMTP_EOB_LEN - bytes_written;
1488 /* Successfully sent so adjust the response timeout relative to now */
1489 pp->response = Curl_tvnow();
1491 state(conn, SMTP_POSTDATA);
1493 /* Run the state-machine
1495 TODO: when the multi interface is used, this _really_ should be using
1496 the smtp_multi_statemach function but we have no general support for
1497 non-blocking DONE operations, not in the multi state machine and with
1498 Curl_done() invokes on several places in the code!
1500 result = smtp_block_statemach(conn);
1503 /* Clear the transfer mode for the next request */
1504 smtp->transfer = FTPTRANSFER_BODY;
1509 /***********************************************************************
1513 * This is the actual DO function for SMTP. Send a mail according to the
1514 * options previously setup.
1516 static CURLcode smtp_perform(struct connectdata *conn, bool *connected,
1519 /* This is SMTP and no proxy */
1520 CURLcode result = CURLE_OK;
1522 DEBUGF(infof(conn->data, "DO phase starts\n"));
1524 if(conn->data->set.opt_no_body) {
1525 /* Requested no body means no transfer */
1526 struct SMTP *smtp = conn->data->state.proto.smtp;
1527 smtp->transfer = FTPTRANSFER_INFO;
1530 *dophase_done = FALSE; /* not done yet */
1532 /* Start the first command in the DO phase */
1533 result = smtp_perform_mail(conn);
1537 /* run the state-machine */
1538 result = smtp_multi_statemach(conn, dophase_done);
1540 *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1543 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1548 /***********************************************************************
1552 * This function is registered as 'curl_do' function. It decodes the path
1553 * parts etc as a wrapper to the actual DO function (smtp_perform).
1555 * The input argument is already checked for validity.
1557 static CURLcode smtp_do(struct connectdata *conn, bool *done)
1559 CURLcode result = CURLE_OK;
1561 *done = FALSE; /* default to false */
1563 /* Since connections can be re-used between SessionHandles, there might be a
1564 connection already existing but on a fresh SessionHandle struct. As such
1565 we make sure we have a good SMTP struct to play with. For new connections
1566 the SMTP struct is allocated and setup in the smtp_connect() function. */
1567 Curl_reset_reqproto(conn);
1568 result = smtp_init(conn);
1572 result = smtp_regular_transfer(conn, done);
1577 /***********************************************************************
1581 * Disconnect from an SMTP server. Cleanup protocol-specific per-connection
1582 * resources. BLOCKING.
1584 static CURLcode smtp_disconnect(struct connectdata *conn,
1585 bool dead_connection)
1587 struct smtp_conn *smtpc = &conn->proto.smtpc;
1589 /* We cannot send quit unconditionally. If this connection is stale or
1590 bad in any way, sending quit and waiting around here will make the
1591 disconnect wait in vain and cause more problems than we need to. */
1593 /* The SMTP session may or may not have been allocated/setup at this
1595 if(!dead_connection && smtpc->pp.conn)
1596 if(!smtp_perform_quit(conn))
1597 (void)smtp_block_statemach(conn); /* ignore errors on QUIT */
1599 /* Disconnect from the server */
1600 Curl_pp_disconnect(&smtpc->pp);
1602 /* Cleanup the SASL module */
1603 Curl_sasl_cleanup(conn, smtpc->authused);
1605 /* Cleanup our connection based variables */
1606 Curl_safefree(smtpc->domain);
1611 /* Call this when the DO phase has completed */
1612 static CURLcode smtp_dophase_done(struct connectdata *conn, bool connected)
1614 struct SMTP *smtp = conn->data->state.proto.smtp;
1618 if(smtp->transfer != FTPTRANSFER_BODY)
1619 /* no data to transfer */
1620 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1625 /* Called from multi.c while DOing */
1626 static CURLcode smtp_doing(struct connectdata *conn, bool *dophase_done)
1628 CURLcode result = smtp_multi_statemach(conn, dophase_done);
1631 DEBUGF(infof(conn->data, "DO phase failed\n"));
1632 else if(*dophase_done) {
1633 result = smtp_dophase_done(conn, FALSE /* not connected */);
1635 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1641 /***********************************************************************
1643 * smtp_regular_transfer()
1645 * The input argument is already checked for validity.
1647 * Performs all commands done before a regular transfer between a local and a
1650 static CURLcode smtp_regular_transfer(struct connectdata *conn,
1653 CURLcode result = CURLE_OK;
1654 bool connected = FALSE;
1655 struct SessionHandle *data = conn->data;
1657 /* Make sure size is unknown at this point */
1658 data->req.size = -1;
1660 /* Set the progress data */
1661 Curl_pgrsSetUploadCounter(data, 0);
1662 Curl_pgrsSetDownloadCounter(data, 0);
1663 Curl_pgrsSetUploadSize(data, 0);
1664 Curl_pgrsSetDownloadSize(data, 0);
1666 /* Carry out the perform */
1667 result = smtp_perform(conn, &connected, dophase_done);
1669 /* Perform post DO phase operations if necessary */
1670 if(!result && *dophase_done)
1671 result = smtp_dophase_done(conn, connected);
1676 static CURLcode smtp_setup_connection(struct connectdata *conn)
1678 struct SessionHandle *data = conn->data;
1680 if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
1681 /* Unless we have asked to tunnel SMTP operations through the proxy, we
1682 switch and use HTTP operations only */
1683 #ifndef CURL_DISABLE_HTTP
1684 if(conn->handler == &Curl_handler_smtp)
1685 conn->handler = &Curl_handler_smtp_proxy;
1688 conn->handler = &Curl_handler_smtps_proxy;
1690 failf(data, "SMTPS not supported!");
1691 return CURLE_UNSUPPORTED_PROTOCOL;
1695 /* We explicitly mark this connection as persistent here as we're doing
1696 SMTP over HTTP and thus we accidentally avoid setting this value
1698 conn->bits.close = FALSE;
1700 failf(data, "SMTP over http proxy requires HTTP support built-in!");
1701 return CURLE_UNSUPPORTED_PROTOCOL;
1705 data->state.path++; /* don't include the initial slash */
1710 /***********************************************************************
1712 * smtp_parse_url_options()
1714 * Parse the URL login options.
1716 static CURLcode smtp_parse_url_options(struct connectdata *conn)
1718 CURLcode result = CURLE_OK;
1719 struct smtp_conn *smtpc = &conn->proto.smtpc;
1720 const char *options = conn->options;
1721 const char *ptr = options;
1724 const char *key = ptr;
1726 while(*ptr && *ptr != '=')
1729 if(strnequal(key, "AUTH", 4)) {
1730 const char *value = ptr + 1;
1732 if(strequal(value, "*"))
1733 smtpc->prefmech = SASL_AUTH_ANY;
1734 else if(strequal(value, "LOGIN"))
1735 smtpc->prefmech = SASL_MECH_LOGIN;
1736 else if(strequal(value, "PLAIN"))
1737 smtpc->prefmech = SASL_MECH_PLAIN;
1738 else if(strequal(value, "CRAM-MD5"))
1739 smtpc->prefmech = SASL_MECH_CRAM_MD5;
1740 else if(strequal(value, "DIGEST-MD5"))
1741 smtpc->prefmech = SASL_MECH_DIGEST_MD5;
1742 else if(strequal(value, "GSSAPI"))
1743 smtpc->prefmech = SASL_MECH_GSSAPI;
1744 else if(strequal(value, "NTLM"))
1745 smtpc->prefmech = SASL_MECH_NTLM;
1747 smtpc->prefmech = SASL_AUTH_NONE;
1750 result = CURLE_URL_MALFORMAT;
1756 /***********************************************************************
1758 * smtp_parse_url_path()
1760 * Parse the URL path into separate path components.
1762 static CURLcode smtp_parse_url_path(struct connectdata *conn)
1764 /* The SMTP struct is already initialised in smtp_connect() */
1765 struct SessionHandle *data = conn->data;
1766 struct smtp_conn *smtpc = &conn->proto.smtpc;
1767 const char *path = data->state.path;
1768 char localhost[HOSTNAME_MAX + 1];
1770 /* Calculate the path if necessary */
1772 if(!Curl_gethostname(localhost, sizeof(localhost)))
1778 /* URL decode the path and use it as the domain in our EHLO */
1779 return Curl_urldecode(conn->data, path, 0, &smtpc->domain, NULL, TRUE);
1782 CURLcode Curl_smtp_escape_eob(struct connectdata *conn, ssize_t nread)
1784 /* When sending a SMTP payload we must detect CRLF. sequences making sure
1785 they are sent as CRLF.. instead, as a . on the beginning of a line will
1786 be deleted by the server when not part of an EOB terminator and a
1787 genuine CRLF.CRLF which isn't escaped will wrongly be detected as end of
1792 struct smtp_conn *smtpc = &conn->proto.smtpc;
1793 struct SessionHandle *data = conn->data;
1795 /* Do we need to allocate the scatch buffer? */
1796 if(!data->state.scratch) {
1797 data->state.scratch = malloc(2 * BUFSIZE);
1799 if(!data->state.scratch) {
1800 failf (data, "Failed to alloc scratch buffer!");
1801 return CURLE_OUT_OF_MEMORY;
1805 /* This loop can be improved by some kind of Boyer-Moore style of
1806 approach but that is saved for later... */
1807 for(i = 0, si = 0; i < nread; i++) {
1808 if(SMTP_EOB[smtpc->eob] == data->req.upload_fromhere[i])
1810 else if(smtpc->eob) {
1811 /* A previous substring matched so output that first */
1812 memcpy(&data->state.scratch[si], SMTP_EOB, smtpc->eob);
1815 /* Then compare the first byte */
1816 if(SMTP_EOB[0] == data->req.upload_fromhere[i])
1822 /* Do we have a match for CRLF. as per RFC-2821, sect. 4.5.2 */
1823 if(SMTP_EOB_FIND_LEN == smtpc->eob) {
1824 /* Copy the replacement data to the target buffer */
1825 memcpy(&data->state.scratch[si], SMTP_EOB_REPL, SMTP_EOB_REPL_LEN);
1826 si += SMTP_EOB_REPL_LEN;
1829 else if(!smtpc->eob)
1830 data->state.scratch[si++] = data->req.upload_fromhere[i];
1834 /* A substring matched before processing ended so output that now */
1835 memcpy(&data->state.scratch[si], SMTP_EOB, smtpc->eob);
1841 /* Only use the new buffer if we replaced something */
1844 /* Upload from the new (replaced) buffer instead */
1845 data->req.upload_fromhere = data->state.scratch;
1847 /* Set the new amount too */
1848 data->req.upload_present = nread;
1854 #endif /* CURL_DISABLE_SMTP */