1/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
9 *
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.
13 *
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.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
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 * RFC8314 Use of TLS for Email Submission and Access
32 * Draft SMTP URL Interface <draft-earhart-url-smtp-00.txt>
33 * Draft LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt>
34 *
35 ***************************************************************************/
36
37#include "curl_setup.h"
38
39#ifndef CURL_DISABLE_SMTP
40
41#ifdef HAVE_NETINET_IN_H
42#include <netinet/in.h>
43#endif
44#ifdef HAVE_ARPA_INET_H
45#include <arpa/inet.h>
46#endif
47#ifdef HAVE_UTSNAME_H
48#include <sys/utsname.h>
49#endif
50#ifdef HAVE_NETDB_H
51#include <netdb.h>
52#endif
53#ifdef __VMS
54#include <in.h>
55#include <inet.h>
56#endif
57
58#if (defined(NETWARE) && defined(__NOVELL_LIBC__))
59#undef in_addr_t
60#define in_addr_t unsigned long
61#endif
62
63#include <curl/curl.h>
64#include "urldata.h"
65#include "sendf.h"
66#include "hostip.h"
67#include "progress.h"
68#include "transfer.h"
69#include "escape.h"
70#include "http.h" /* for HTTP proxy tunnel stuff */
71#include "mime.h"
72#include "socks.h"
73#include "smtp.h"
74#include "strtoofft.h"
75#include "strcase.h"
76#include "vtls/vtls.h"
77#include "connect.h"
78#include "strerror.h"
79#include "select.h"
80#include "multiif.h"
81#include "url.h"
82#include "curl_gethostname.h"
83#include "curl_sasl.h"
84#include "warnless.h"
85/* The last 3 #include files should be in this order */
86#include "curl_printf.h"
87#include "curl_memory.h"
88#include "memdebug.h"
89
90/* Local API functions */
91static CURLcode smtp_regular_transfer(struct connectdata *conn, bool *done);
92static CURLcode smtp_do(struct connectdata *conn, bool *done);
93static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
94 bool premature);
95static CURLcode smtp_connect(struct connectdata *conn, bool *done);
96static CURLcode smtp_disconnect(struct connectdata *conn, bool dead);
97static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done);
98static int smtp_getsock(struct connectdata *conn, curl_socket_t *socks);
99static CURLcode smtp_doing(struct connectdata *conn, bool *dophase_done);
100static CURLcode smtp_setup_connection(struct connectdata *conn);
101static CURLcode smtp_parse_url_options(struct connectdata *conn);
102static CURLcode smtp_parse_url_path(struct connectdata *conn);
103static CURLcode smtp_parse_custom_request(struct connectdata *conn);
104static CURLcode smtp_perform_auth(struct connectdata *conn, const char *mech,
105 const char *initresp);
106static CURLcode smtp_continue_auth(struct connectdata *conn, const char *resp);
107static void smtp_get_message(char *buffer, char **outptr);
108
109/*
110 * SMTP protocol handler.
111 */
112
113const struct Curl_handler Curl_handler_smtp = {
114 "SMTP", /* scheme */
115 smtp_setup_connection, /* setup_connection */
116 smtp_do, /* do_it */
117 smtp_done, /* done */
118 ZERO_NULL, /* do_more */
119 smtp_connect, /* connect_it */
120 smtp_multi_statemach, /* connecting */
121 smtp_doing, /* doing */
122 smtp_getsock, /* proto_getsock */
123 smtp_getsock, /* doing_getsock */
124 ZERO_NULL, /* domore_getsock */
125 ZERO_NULL, /* perform_getsock */
126 smtp_disconnect, /* disconnect */
127 ZERO_NULL, /* readwrite */
128 ZERO_NULL, /* connection_check */
129 PORT_SMTP, /* defport */
130 CURLPROTO_SMTP, /* protocol */
131 PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY | /* flags */
132 PROTOPT_URLOPTIONS
133};
134
135#ifdef USE_SSL
136/*
137 * SMTPS protocol handler.
138 */
139
140const struct Curl_handler Curl_handler_smtps = {
141 "SMTPS", /* scheme */
142 smtp_setup_connection, /* setup_connection */
143 smtp_do, /* do_it */
144 smtp_done, /* done */
145 ZERO_NULL, /* do_more */
146 smtp_connect, /* connect_it */
147 smtp_multi_statemach, /* connecting */
148 smtp_doing, /* doing */
149 smtp_getsock, /* proto_getsock */
150 smtp_getsock, /* doing_getsock */
151 ZERO_NULL, /* domore_getsock */
152 ZERO_NULL, /* perform_getsock */
153 smtp_disconnect, /* disconnect */
154 ZERO_NULL, /* readwrite */
155 ZERO_NULL, /* connection_check */
156 PORT_SMTPS, /* defport */
157 CURLPROTO_SMTPS, /* protocol */
158 PROTOPT_CLOSEACTION | PROTOPT_SSL
159 | PROTOPT_NOURLQUERY | PROTOPT_URLOPTIONS /* flags */
160};
161#endif
162
163/* SASL parameters for the smtp protocol */
164static const struct SASLproto saslsmtp = {
165 "smtp", /* The service name */
166 334, /* Code received when continuation is expected */
167 235, /* Code to receive upon authentication success */
168 512 - 8, /* Maximum initial response length (no max) */
169 smtp_perform_auth, /* Send authentication command */
170 smtp_continue_auth, /* Send authentication continuation */
171 smtp_get_message /* Get SASL response message */
172};
173
174#ifdef USE_SSL
175static void smtp_to_smtps(struct connectdata *conn)
176{
177 /* Change the connection handler */
178 conn->handler = &Curl_handler_smtps;
179
180 /* Set the connection's upgraded to TLS flag */
181 conn->tls_upgraded = TRUE;
182}
183#else
184#define smtp_to_smtps(x) Curl_nop_stmt
185#endif
186
187/***********************************************************************
188 *
189 * smtp_endofresp()
190 *
191 * Checks for an ending SMTP status code at the start of the given string, but
192 * also detects various capabilities from the EHLO response including the
193 * supported authentication mechanisms.
194 */
195static bool smtp_endofresp(struct connectdata *conn, char *line, size_t len,
196 int *resp)
197{
198 struct smtp_conn *smtpc = &conn->proto.smtpc;
199 bool result = FALSE;
200
201 /* Nothing for us */
202 if(len < 4 || !ISDIGIT(line[0]) || !ISDIGIT(line[1]) || !ISDIGIT(line[2]))
203 return FALSE;
204
205 /* Do we have a command response? This should be the response code followed
206 by a space and optionally some text as per RFC-5321 and as outlined in
207 Section 4. Examples of RFC-4954 but some e-mail servers ignore this and
208 only send the response code instead as per Section 4.2. */
209 if(line[3] == ' ' || len == 5) {
210 char tmpline[6];
211
212 result = TRUE;
213 memset(tmpline, '\0', sizeof(tmpline));
214 memcpy(tmpline, line, (len == 5 ? 5 : 3));
215 *resp = curlx_sltosi(strtol(tmpline, NULL, 10));
216
217 /* Make sure real server never sends internal value */
218 if(*resp == 1)
219 *resp = 0;
220 }
221 /* Do we have a multiline (continuation) response? */
222 else if(line[3] == '-' &&
223 (smtpc->state == SMTP_EHLO || smtpc->state == SMTP_COMMAND)) {
224 result = TRUE;
225 *resp = 1; /* Internal response code */
226 }
227
228 return result;
229}
230
231/***********************************************************************
232 *
233 * smtp_get_message()
234 *
235 * Gets the authentication message from the response buffer.
236 */
237static void smtp_get_message(char *buffer, char **outptr)
238{
239 size_t len = strlen(buffer);
240 char *message = NULL;
241
242 if(len > 4) {
243 /* Find the start of the message */
244 len -= 4;
245 for(message = buffer + 4; *message == ' ' || *message == '\t';
246 message++, len--)
247 ;
248
249 /* Find the end of the message */
250 for(; len--;)
251 if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' &&
252 message[len] != '\t')
253 break;
254
255 /* Terminate the message */
256 if(++len) {
257 message[len] = '\0';
258 }
259 }
260 else
261 /* junk input => zero length output */
262 message = &buffer[len];
263
264 *outptr = message;
265}
266
267/***********************************************************************
268 *
269 * state()
270 *
271 * This is the ONLY way to change SMTP state!
272 */
273static void state(struct connectdata *conn, smtpstate newstate)
274{
275 struct smtp_conn *smtpc = &conn->proto.smtpc;
276#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
277 /* for debug purposes */
278 static const char * const names[] = {
279 "STOP",
280 "SERVERGREET",
281 "EHLO",
282 "HELO",
283 "STARTTLS",
284 "UPGRADETLS",
285 "AUTH",
286 "COMMAND",
287 "MAIL",
288 "RCPT",
289 "DATA",
290 "POSTDATA",
291 "QUIT",
292 /* LAST */
293 };
294
295 if(smtpc->state != newstate)
296 infof(conn->data, "SMTP %p state change from %s to %s\n",
297 (void *)smtpc, names[smtpc->state], names[newstate]);
298#endif
299
300 smtpc->state = newstate;
301}
302
303/***********************************************************************
304 *
305 * smtp_perform_ehlo()
306 *
307 * Sends the EHLO command to not only initialise communication with the ESMTP
308 * server but to also obtain a list of server side supported capabilities.
309 */
310static CURLcode smtp_perform_ehlo(struct connectdata *conn)
311{
312 CURLcode result = CURLE_OK;
313 struct smtp_conn *smtpc = &conn->proto.smtpc;
314
315 smtpc->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanism yet */
316 smtpc->sasl.authused = SASL_AUTH_NONE; /* Clear the authentication mechanism
317 used for esmtp connections */
318 smtpc->tls_supported = FALSE; /* Clear the TLS capability */
319 smtpc->auth_supported = FALSE; /* Clear the AUTH capability */
320
321 /* Send the EHLO command */
322 result = Curl_pp_sendf(&smtpc->pp, "EHLO %s", smtpc->domain);
323
324 if(!result)
325 state(conn, SMTP_EHLO);
326
327 return result;
328}
329
330/***********************************************************************
331 *
332 * smtp_perform_helo()
333 *
334 * Sends the HELO command to initialise communication with the SMTP server.
335 */
336static CURLcode smtp_perform_helo(struct connectdata *conn)
337{
338 CURLcode result = CURLE_OK;
339 struct smtp_conn *smtpc = &conn->proto.smtpc;
340
341 smtpc->sasl.authused = SASL_AUTH_NONE; /* No authentication mechanism used
342 in smtp connections */
343
344 /* Send the HELO command */
345 result = Curl_pp_sendf(&smtpc->pp, "HELO %s", smtpc->domain);
346
347 if(!result)
348 state(conn, SMTP_HELO);
349
350 return result;
351}
352
353/***********************************************************************
354 *
355 * smtp_perform_starttls()
356 *
357 * Sends the STLS command to start the upgrade to TLS.
358 */
359static CURLcode smtp_perform_starttls(struct connectdata *conn)
360{
361 /* Send the STARTTLS command */
362 CURLcode result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "STARTTLS");
363
364 if(!result)
365 state(conn, SMTP_STARTTLS);
366
367 return result;
368}
369
370/***********************************************************************
371 *
372 * smtp_perform_upgrade_tls()
373 *
374 * Performs the upgrade to TLS.
375 */
376static CURLcode smtp_perform_upgrade_tls(struct connectdata *conn)
377{
378 /* Start the SSL connection */
379 struct smtp_conn *smtpc = &conn->proto.smtpc;
380 CURLcode result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET,
381 &smtpc->ssldone);
382
383 if(!result) {
384 if(smtpc->state != SMTP_UPGRADETLS)
385 state(conn, SMTP_UPGRADETLS);
386
387 if(smtpc->ssldone) {
388 smtp_to_smtps(conn);
389 result = smtp_perform_ehlo(conn);
390 }
391 }
392
393 return result;
394}
395
396/***********************************************************************
397 *
398 * smtp_perform_auth()
399 *
400 * Sends an AUTH command allowing the client to login with the given SASL
401 * authentication mechanism.
402 */
403static CURLcode smtp_perform_auth(struct connectdata *conn,
404 const char *mech,
405 const char *initresp)
406{
407 CURLcode result = CURLE_OK;
408 struct smtp_conn *smtpc = &conn->proto.smtpc;
409
410 if(initresp) { /* AUTH <mech> ...<crlf> */
411 /* Send the AUTH command with the initial response */
412 result = Curl_pp_sendf(&smtpc->pp, "AUTH %s %s", mech, initresp);
413 }
414 else {
415 /* Send the AUTH command */
416 result = Curl_pp_sendf(&smtpc->pp, "AUTH %s", mech);
417 }
418
419 return result;
420}
421
422/***********************************************************************
423 *
424 * smtp_continue_auth()
425 *
426 * Sends SASL continuation data or cancellation.
427 */
428static CURLcode smtp_continue_auth(struct connectdata *conn, const char *resp)
429{
430 struct smtp_conn *smtpc = &conn->proto.smtpc;
431
432 return Curl_pp_sendf(&smtpc->pp, "%s", resp);
433}
434
435/***********************************************************************
436 *
437 * smtp_perform_authentication()
438 *
439 * Initiates the authentication sequence, with the appropriate SASL
440 * authentication mechanism.
441 */
442static CURLcode smtp_perform_authentication(struct connectdata *conn)
443{
444 CURLcode result = CURLE_OK;
445 struct smtp_conn *smtpc = &conn->proto.smtpc;
446 saslprogress progress;
447
448 /* Check we have enough data to authenticate with, and the
449 server supports authentiation, and end the connect phase if not */
450 if(!smtpc->auth_supported ||
451 !Curl_sasl_can_authenticate(&smtpc->sasl, conn)) {
452 state(conn, SMTP_STOP);
453 return result;
454 }
455
456 /* Calculate the SASL login details */
457 result = Curl_sasl_start(&smtpc->sasl, conn, FALSE, &progress);
458
459 if(!result) {
460 if(progress == SASL_INPROGRESS)
461 state(conn, SMTP_AUTH);
462 else {
463 /* Other mechanisms not supported */
464 infof(conn->data, "No known authentication mechanisms supported!\n");
465 result = CURLE_LOGIN_DENIED;
466 }
467 }
468
469 return result;
470}
471
472/***********************************************************************
473 *
474 * smtp_perform_command()
475 *
476 * Sends a SMTP based command.
477 */
478static CURLcode smtp_perform_command(struct connectdata *conn)
479{
480 CURLcode result = CURLE_OK;
481 struct Curl_easy *data = conn->data;
482 struct SMTP *smtp = data->req.protop;
483
484 /* Send the command */
485 if(smtp->rcpt)
486 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s %s",
487 smtp->custom && smtp->custom[0] != '\0' ?
488 smtp->custom : "VRFY",
489 smtp->rcpt->data);
490 else
491 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s",
492 smtp->custom && smtp->custom[0] != '\0' ?
493 smtp->custom : "HELP");
494
495 if(!result)
496 state(conn, SMTP_COMMAND);
497
498 return result;
499}
500
501/***********************************************************************
502 *
503 * smtp_perform_mail()
504 *
505 * Sends an MAIL command to initiate the upload of a message.
506 */
507static CURLcode smtp_perform_mail(struct connectdata *conn)
508{
509 char *from = NULL;
510 char *auth = NULL;
511 char *size = NULL;
512 CURLcode result = CURLE_OK;
513 struct Curl_easy *data = conn->data;
514
515 /* Calculate the FROM parameter */
516 if(!data->set.str[STRING_MAIL_FROM])
517 /* Null reverse-path, RFC-5321, sect. 3.6.3 */
518 from = strdup("<>");
519 else if(data->set.str[STRING_MAIL_FROM][0] == '<')
520 from = aprintf("%s", data->set.str[STRING_MAIL_FROM]);
521 else
522 from = aprintf("<%s>", data->set.str[STRING_MAIL_FROM]);
523
524 if(!from)
525 return CURLE_OUT_OF_MEMORY;
526
527 /* Calculate the optional AUTH parameter */
528 if(data->set.str[STRING_MAIL_AUTH] && conn->proto.smtpc.sasl.authused) {
529 if(data->set.str[STRING_MAIL_AUTH][0] != '\0')
530 auth = aprintf("%s", data->set.str[STRING_MAIL_AUTH]);
531 else
532 /* Empty AUTH, RFC-2554, sect. 5 */
533 auth = strdup("<>");
534
535 if(!auth) {
536 free(from);
537
538 return CURLE_OUT_OF_MEMORY;
539 }
540 }
541
542 /* Prepare the mime data if some. */
543 if(data->set.mimepost.kind != MIMEKIND_NONE) {
544 /* Use the whole structure as data. */
545 data->set.mimepost.flags &= ~MIME_BODY_ONLY;
546
547 /* Add external headers and mime version. */
548 curl_mime_headers(&data->set.mimepost, data->set.headers, 0);
549 result = Curl_mime_prepare_headers(&data->set.mimepost, NULL,
550 NULL, MIMESTRATEGY_MAIL);
551
552 if(!result)
553 if(!Curl_checkheaders(conn, "Mime-Version"))
554 result = Curl_mime_add_header(&data->set.mimepost.curlheaders,
555 "Mime-Version: 1.0");
556
557 /* Make sure we will read the entire mime structure. */
558 if(!result)
559 result = Curl_mime_rewind(&data->set.mimepost);
560
561 if(result) {
562 free(from);
563 free(auth);
564 return result;
565 }
566
567 data->state.infilesize = Curl_mime_size(&data->set.mimepost);
568
569 /* Read from mime structure. */
570 data->state.fread_func = (curl_read_callback) Curl_mime_read;
571 data->state.in = (void *) &data->set.mimepost;
572 }
573
574 /* Calculate the optional SIZE parameter */
575 if(conn->proto.smtpc.size_supported && data->state.infilesize > 0) {
576 size = aprintf("%" CURL_FORMAT_CURL_OFF_T, data->state.infilesize);
577
578 if(!size) {
579 free(from);
580 free(auth);
581
582 return CURLE_OUT_OF_MEMORY;
583 }
584 }
585
586 /* Send the MAIL command */
587 if(!auth && !size)
588 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
589 "MAIL FROM:%s", from);
590 else if(auth && !size)
591 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
592 "MAIL FROM:%s AUTH=%s", from, auth);
593 else if(auth && size)
594 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
595 "MAIL FROM:%s AUTH=%s SIZE=%s", from, auth, size);
596 else
597 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
598 "MAIL FROM:%s SIZE=%s", from, size);
599
600 free(from);
601 free(auth);
602 free(size);
603
604 if(!result)
605 state(conn, SMTP_MAIL);
606
607 return result;
608}
609
610/***********************************************************************
611 *
612 * smtp_perform_rcpt_to()
613 *
614 * Sends a RCPT TO command for a given recipient as part of the message upload
615 * process.
616 */
617static CURLcode smtp_perform_rcpt_to(struct connectdata *conn)
618{
619 CURLcode result = CURLE_OK;
620 struct Curl_easy *data = conn->data;
621 struct SMTP *smtp = data->req.protop;
622
623 /* Send the RCPT TO command */
624 if(smtp->rcpt->data[0] == '<')
625 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:%s",
626 smtp->rcpt->data);
627 else
628 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:<%s>",
629 smtp->rcpt->data);
630 if(!result)
631 state(conn, SMTP_RCPT);
632
633 return result;
634}
635
636/***********************************************************************
637 *
638 * smtp_perform_quit()
639 *
640 * Performs the quit action prior to sclose() being called.
641 */
642static CURLcode smtp_perform_quit(struct connectdata *conn)
643{
644 /* Send the QUIT command */
645 CURLcode result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "QUIT");
646
647 if(!result)
648 state(conn, SMTP_QUIT);
649
650 return result;
651}
652
653/* For the initial server greeting */
654static CURLcode smtp_state_servergreet_resp(struct connectdata *conn,
655 int smtpcode,
656 smtpstate instate)
657{
658 CURLcode result = CURLE_OK;
659 struct Curl_easy *data = conn->data;
660
661 (void)instate; /* no use for this yet */
662
663 if(smtpcode/100 != 2) {
664 failf(data, "Got unexpected smtp-server response: %d", smtpcode);
665 result = CURLE_WEIRD_SERVER_REPLY;
666 }
667 else
668 result = smtp_perform_ehlo(conn);
669
670 return result;
671}
672
673/* For STARTTLS responses */
674static CURLcode smtp_state_starttls_resp(struct connectdata *conn,
675 int smtpcode,
676 smtpstate instate)
677{
678 CURLcode result = CURLE_OK;
679 struct Curl_easy *data = conn->data;
680
681 (void)instate; /* no use for this yet */
682
683 if(smtpcode != 220) {
684 if(data->set.use_ssl != CURLUSESSL_TRY) {
685 failf(data, "STARTTLS denied, code %d", smtpcode);
686 result = CURLE_USE_SSL_FAILED;
687 }
688 else
689 result = smtp_perform_authentication(conn);
690 }
691 else
692 result = smtp_perform_upgrade_tls(conn);
693
694 return result;
695}
696
697/* For EHLO responses */
698static CURLcode smtp_state_ehlo_resp(struct connectdata *conn, int smtpcode,
699 smtpstate instate)
700{
701 CURLcode result = CURLE_OK;
702 struct Curl_easy *data = conn->data;
703 struct smtp_conn *smtpc = &conn->proto.smtpc;
704 const char *line = data->state.buffer;
705 size_t len = strlen(line);
706
707 (void)instate; /* no use for this yet */
708
709 if(smtpcode/100 != 2 && smtpcode != 1) {
710 if(data->set.use_ssl <= CURLUSESSL_TRY || conn->ssl[FIRSTSOCKET].use)
711 result = smtp_perform_helo(conn);
712 else {
713 failf(data, "Remote access denied: %d", smtpcode);
714 result = CURLE_REMOTE_ACCESS_DENIED;
715 }
716 }
717 else if(len >= 4) {
718 line += 4;
719 len -= 4;
720
721 /* Does the server support the STARTTLS capability? */
722 if(len >= 8 && !memcmp(line, "STARTTLS", 8))
723 smtpc->tls_supported = TRUE;
724
725 /* Does the server support the SIZE capability? */
726 else if(len >= 4 && !memcmp(line, "SIZE", 4))
727 smtpc->size_supported = TRUE;
728
729 /* Does the server support authentication? */
730 else if(len >= 5 && !memcmp(line, "AUTH ", 5)) {
731 smtpc->auth_supported = TRUE;
732
733 /* Advance past the AUTH keyword */
734 line += 5;
735 len -= 5;
736
737 /* Loop through the data line */
738 for(;;) {
739 size_t llen;
740 size_t wordlen;
741 unsigned int mechbit;
742
743 while(len &&
744 (*line == ' ' || *line == '\t' ||
745 *line == '\r' || *line == '\n')) {
746
747 line++;
748 len--;
749 }
750
751 if(!len)
752 break;
753
754 /* Extract the word */
755 for(wordlen = 0; wordlen < len && line[wordlen] != ' ' &&
756 line[wordlen] != '\t' && line[wordlen] != '\r' &&
757 line[wordlen] != '\n';)
758 wordlen++;
759
760 /* Test the word for a matching authentication mechanism */
761 mechbit = Curl_sasl_decode_mech(line, wordlen, &llen);
762 if(mechbit && llen == wordlen)
763 smtpc->sasl.authmechs |= mechbit;
764
765 line += wordlen;
766 len -= wordlen;
767 }
768 }
769
770 if(smtpcode != 1) {
771 if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
772 /* We don't have a SSL/TLS connection yet, but SSL is requested */
773 if(smtpc->tls_supported)
774 /* Switch to TLS connection now */
775 result = smtp_perform_starttls(conn);
776 else if(data->set.use_ssl == CURLUSESSL_TRY)
777 /* Fallback and carry on with authentication */
778 result = smtp_perform_authentication(conn);
779 else {
780 failf(data, "STARTTLS not supported.");
781 result = CURLE_USE_SSL_FAILED;
782 }
783 }
784 else
785 result = smtp_perform_authentication(conn);
786 }
787 }
788 else {
789 failf(data, "Unexpectedly short EHLO response");
790 result = CURLE_WEIRD_SERVER_REPLY;
791 }
792
793 return result;
794}
795
796/* For HELO responses */
797static CURLcode smtp_state_helo_resp(struct connectdata *conn, int smtpcode,
798 smtpstate instate)
799{
800 CURLcode result = CURLE_OK;
801 struct Curl_easy *data = conn->data;
802
803 (void)instate; /* no use for this yet */
804
805 if(smtpcode/100 != 2) {
806 failf(data, "Remote access denied: %d", smtpcode);
807 result = CURLE_REMOTE_ACCESS_DENIED;
808 }
809 else
810 /* End of connect phase */
811 state(conn, SMTP_STOP);
812
813 return result;
814}
815
816/* For SASL authentication responses */
817static CURLcode smtp_state_auth_resp(struct connectdata *conn,
818 int smtpcode,
819 smtpstate instate)
820{
821 CURLcode result = CURLE_OK;
822 struct Curl_easy *data = conn->data;
823 struct smtp_conn *smtpc = &conn->proto.smtpc;
824 saslprogress progress;
825
826 (void)instate; /* no use for this yet */
827
828 result = Curl_sasl_continue(&smtpc->sasl, conn, smtpcode, &progress);
829 if(!result)
830 switch(progress) {
831 case SASL_DONE:
832 state(conn, SMTP_STOP); /* Authenticated */
833 break;
834 case SASL_IDLE: /* No mechanism left after cancellation */
835 failf(data, "Authentication cancelled");
836 result = CURLE_LOGIN_DENIED;
837 break;
838 default:
839 break;
840 }
841
842 return result;
843}
844
845/* For command responses */
846static CURLcode smtp_state_command_resp(struct connectdata *conn, int smtpcode,
847 smtpstate instate)
848{
849 CURLcode result = CURLE_OK;
850 struct Curl_easy *data = conn->data;
851 struct SMTP *smtp = data->req.protop;
852 char *line = data->state.buffer;
853 size_t len = strlen(line);
854
855 (void)instate; /* no use for this yet */
856
857 if((smtp->rcpt && smtpcode/100 != 2 && smtpcode != 553 && smtpcode != 1) ||
858 (!smtp->rcpt && smtpcode/100 != 2 && smtpcode != 1)) {
859 failf(data, "Command failed: %d", smtpcode);
860 result = CURLE_RECV_ERROR;
861 }
862 else {
863 /* Temporarily add the LF character back and send as body to the client */
864 if(!data->set.opt_no_body) {
865 line[len] = '\n';
866 result = Curl_client_write(conn, CLIENTWRITE_BODY, line, len + 1);
867 line[len] = '\0';
868 }
869
870 if(smtpcode != 1) {
871 if(smtp->rcpt) {
872 smtp->rcpt = smtp->rcpt->next;
873
874 if(smtp->rcpt) {
875 /* Send the next command */
876 result = smtp_perform_command(conn);
877 }
878 else
879 /* End of DO phase */
880 state(conn, SMTP_STOP);
881 }
882 else
883 /* End of DO phase */
884 state(conn, SMTP_STOP);
885 }
886 }
887
888 return result;
889}
890
891/* For MAIL responses */
892static CURLcode smtp_state_mail_resp(struct connectdata *conn, int smtpcode,
893 smtpstate instate)
894{
895 CURLcode result = CURLE_OK;
896 struct Curl_easy *data = conn->data;
897
898 (void)instate; /* no use for this yet */
899
900 if(smtpcode/100 != 2) {
901 failf(data, "MAIL failed: %d", smtpcode);
902 result = CURLE_SEND_ERROR;
903 }
904 else
905 /* Start the RCPT TO command */
906 result = smtp_perform_rcpt_to(conn);
907
908 return result;
909}
910
911/* For RCPT responses */
912static CURLcode smtp_state_rcpt_resp(struct connectdata *conn, int smtpcode,
913 smtpstate instate)
914{
915 CURLcode result = CURLE_OK;
916 struct Curl_easy *data = conn->data;
917 struct SMTP *smtp = data->req.protop;
918
919 (void)instate; /* no use for this yet */
920
921 if(smtpcode/100 != 2) {
922 failf(data, "RCPT failed: %d", smtpcode);
923 result = CURLE_SEND_ERROR;
924 }
925 else {
926 smtp->rcpt = smtp->rcpt->next;
927
928 if(smtp->rcpt)
929 /* Send the next RCPT TO command */
930 result = smtp_perform_rcpt_to(conn);
931 else {
932 /* Send the DATA command */
933 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "DATA");
934
935 if(!result)
936 state(conn, SMTP_DATA);
937 }
938 }
939
940 return result;
941}
942
943/* For DATA response */
944static CURLcode smtp_state_data_resp(struct connectdata *conn, int smtpcode,
945 smtpstate instate)
946{
947 CURLcode result = CURLE_OK;
948 struct Curl_easy *data = conn->data;
949
950 (void)instate; /* no use for this yet */
951
952 if(smtpcode != 354) {
953 failf(data, "DATA failed: %d", smtpcode);
954 result = CURLE_SEND_ERROR;
955 }
956 else {
957 /* Set the progress upload size */
958 Curl_pgrsSetUploadSize(data, data->state.infilesize);
959
960 /* SMTP upload */
961 Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET);
962
963 /* End of DO phase */
964 state(conn, SMTP_STOP);
965 }
966
967 return result;
968}
969
970/* For POSTDATA responses, which are received after the entire DATA
971 part has been sent to the server */
972static CURLcode smtp_state_postdata_resp(struct connectdata *conn,
973 int smtpcode,
974 smtpstate instate)
975{
976 CURLcode result = CURLE_OK;
977
978 (void)instate; /* no use for this yet */
979
980 if(smtpcode != 250)
981 result = CURLE_RECV_ERROR;
982
983 /* End of DONE phase */
984 state(conn, SMTP_STOP);
985
986 return result;
987}
988
989static CURLcode smtp_statemach_act(struct connectdata *conn)
990{
991 CURLcode result = CURLE_OK;
992 curl_socket_t sock = conn->sock[FIRSTSOCKET];
993 struct Curl_easy *data = conn->data;
994 int smtpcode;
995 struct smtp_conn *smtpc = &conn->proto.smtpc;
996 struct pingpong *pp = &smtpc->pp;
997 size_t nread = 0;
998
999 /* Busy upgrading the connection; right now all I/O is SSL/TLS, not SMTP */
1000 if(smtpc->state == SMTP_UPGRADETLS)
1001 return smtp_perform_upgrade_tls(conn);
1002
1003 /* Flush any data that needs to be sent */
1004 if(pp->sendleft)
1005 return Curl_pp_flushsend(pp);
1006
1007 do {
1008 /* Read the response from the server */
1009 result = Curl_pp_readresp(sock, pp, &smtpcode, &nread);
1010 if(result)
1011 return result;
1012
1013 /* Store the latest response for later retrieval if necessary */
1014 if(smtpc->state != SMTP_QUIT && smtpcode != 1)
1015 data->info.httpcode = smtpcode;
1016
1017 if(!smtpcode)
1018 break;
1019
1020 /* We have now received a full SMTP server response */
1021 switch(smtpc->state) {
1022 case SMTP_SERVERGREET:
1023 result = smtp_state_servergreet_resp(conn, smtpcode, smtpc->state);
1024 break;
1025
1026 case SMTP_EHLO:
1027 result = smtp_state_ehlo_resp(conn, smtpcode, smtpc->state);
1028 break;
1029
1030 case SMTP_HELO:
1031 result = smtp_state_helo_resp(conn, smtpcode, smtpc->state);
1032 break;
1033
1034 case SMTP_STARTTLS:
1035 result = smtp_state_starttls_resp(conn, smtpcode, smtpc->state);
1036 break;
1037
1038 case SMTP_AUTH:
1039 result = smtp_state_auth_resp(conn, smtpcode, smtpc->state);
1040 break;
1041
1042 case SMTP_COMMAND:
1043 result = smtp_state_command_resp(conn, smtpcode, smtpc->state);
1044 break;
1045
1046 case SMTP_MAIL:
1047 result = smtp_state_mail_resp(conn, smtpcode, smtpc->state);
1048 break;
1049
1050 case SMTP_RCPT:
1051 result = smtp_state_rcpt_resp(conn, smtpcode, smtpc->state);
1052 break;
1053
1054 case SMTP_DATA:
1055 result = smtp_state_data_resp(conn, smtpcode, smtpc->state);
1056 break;
1057
1058 case SMTP_POSTDATA:
1059 result = smtp_state_postdata_resp(conn, smtpcode, smtpc->state);
1060 break;
1061
1062 case SMTP_QUIT:
1063 /* fallthrough, just stop! */
1064 default:
1065 /* internal error */
1066 state(conn, SMTP_STOP);
1067 break;
1068 }
1069 } while(!result && smtpc->state != SMTP_STOP && Curl_pp_moredata(pp));
1070
1071 return result;
1072}
1073
1074/* Called repeatedly until done from multi.c */
1075static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done)
1076{
1077 CURLcode result = CURLE_OK;
1078 struct smtp_conn *smtpc = &conn->proto.smtpc;
1079
1080 if((conn->handler->flags & PROTOPT_SSL) && !smtpc->ssldone) {
1081 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone);
1082 if(result || !smtpc->ssldone)
1083 return result;
1084 }
1085
1086 result = Curl_pp_statemach(&smtpc->pp, FALSE, FALSE);
1087 *done = (smtpc->state == SMTP_STOP) ? TRUE : FALSE;
1088
1089 return result;
1090}
1091
1092static CURLcode smtp_block_statemach(struct connectdata *conn,
1093 bool disconnecting)
1094{
1095 CURLcode result = CURLE_OK;
1096 struct smtp_conn *smtpc = &conn->proto.smtpc;
1097
1098 while(smtpc->state != SMTP_STOP && !result)
1099 result = Curl_pp_statemach(&smtpc->pp, TRUE, disconnecting);
1100
1101 return result;
1102}
1103
1104/* Allocate and initialize the SMTP struct for the current Curl_easy if
1105 required */
1106static CURLcode smtp_init(struct connectdata *conn)
1107{
1108 CURLcode result = CURLE_OK;
1109 struct Curl_easy *data = conn->data;
1110 struct SMTP *smtp;
1111
1112 smtp = data->req.protop = calloc(sizeof(struct SMTP), 1);
1113 if(!smtp)
1114 result = CURLE_OUT_OF_MEMORY;
1115
1116 return result;
1117}
1118
1119/* For the SMTP "protocol connect" and "doing" phases only */
1120static int smtp_getsock(struct connectdata *conn, curl_socket_t *socks)
1121{
1122 return Curl_pp_getsock(&conn->proto.smtpc.pp, socks);
1123}
1124
1125/***********************************************************************
1126 *
1127 * smtp_connect()
1128 *
1129 * This function should do everything that is to be considered a part of
1130 * the connection phase.
1131 *
1132 * The variable pointed to by 'done' will be TRUE if the protocol-layer
1133 * connect phase is done when this function returns, or FALSE if not.
1134 */
1135static CURLcode smtp_connect(struct connectdata *conn, bool *done)
1136{
1137 CURLcode result = CURLE_OK;
1138 struct smtp_conn *smtpc = &conn->proto.smtpc;
1139 struct pingpong *pp = &smtpc->pp;
1140
1141 *done = FALSE; /* default to not done yet */
1142
1143 /* We always support persistent connections in SMTP */
1144 connkeep(conn, "SMTP default");
1145
1146 /* Set the default response time-out */
1147 pp->response_time = RESP_TIMEOUT;
1148 pp->statemach_act = smtp_statemach_act;
1149 pp->endofresp = smtp_endofresp;
1150 pp->conn = conn;
1151
1152 /* Initialize the SASL storage */
1153 Curl_sasl_init(&smtpc->sasl, &saslsmtp);
1154
1155 /* Initialise the pingpong layer */
1156 Curl_pp_init(pp);
1157
1158 /* Parse the URL options */
1159 result = smtp_parse_url_options(conn);
1160 if(result)
1161 return result;
1162
1163 /* Parse the URL path */
1164 result = smtp_parse_url_path(conn);
1165 if(result)
1166 return result;
1167
1168 /* Start off waiting for the server greeting response */
1169 state(conn, SMTP_SERVERGREET);
1170
1171 result = smtp_multi_statemach(conn, done);
1172
1173 return result;
1174}
1175
1176/***********************************************************************
1177 *
1178 * smtp_done()
1179 *
1180 * The DONE function. This does what needs to be done after a single DO has
1181 * performed.
1182 *
1183 * Input argument is already checked for validity.
1184 */
1185static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
1186 bool premature)
1187{
1188 CURLcode result = CURLE_OK;
1189 struct Curl_easy *data = conn->data;
1190 struct SMTP *smtp = data->req.protop;
1191 struct pingpong *pp = &conn->proto.smtpc.pp;
1192 char *eob;
1193 ssize_t len;
1194 ssize_t bytes_written;
1195
1196 (void)premature;
1197
1198 if(!smtp || !pp->conn)
1199 return CURLE_OK;
1200
1201 /* Cleanup our per-request based variables */
1202 Curl_safefree(smtp->custom);
1203
1204 if(status) {
1205 connclose(conn, "SMTP done with bad status"); /* marked for closure */
1206 result = status; /* use the already set error code */
1207 }
1208 else if(!data->set.connect_only && data->set.mail_rcpt &&
1209 (data->set.upload || data->set.mimepost.kind)) {
1210 /* Calculate the EOB taking into account any terminating CRLF from the
1211 previous line of the email or the CRLF of the DATA command when there
1212 is "no mail data". RFC-5321, sect. 4.1.1.4.
1213
1214 Note: As some SSL backends, such as OpenSSL, will cause Curl_write() to
1215 fail when using a different pointer following a previous write, that
1216 returned CURLE_AGAIN, we duplicate the EOB now rather than when the
1217 bytes written doesn't equal len. */
1218 if(smtp->trailing_crlf || !conn->data->state.infilesize) {
1219 eob = strdup(&SMTP_EOB[2]);
1220 len = SMTP_EOB_LEN - 2;
1221 }
1222 else {
1223 eob = strdup(SMTP_EOB);
1224 len = SMTP_EOB_LEN;
1225 }
1226
1227 if(!eob)
1228 return CURLE_OUT_OF_MEMORY;
1229
1230 /* Send the end of block data */
1231 result = Curl_write(conn, conn->writesockfd, eob, len, &bytes_written);
1232 if(result) {
1233 free(eob);
1234 return result;
1235 }
1236
1237 if(bytes_written != len) {
1238 /* The whole chunk was not sent so keep it around and adjust the
1239 pingpong structure accordingly */
1240 pp->sendthis = eob;
1241 pp->sendsize = len;
1242 pp->sendleft = len - bytes_written;
1243 }
1244 else {
1245 /* Successfully sent so adjust the response timeout relative to now */
1246 pp->response = Curl_now();
1247
1248 free(eob);
1249 }
1250
1251 state(conn, SMTP_POSTDATA);
1252
1253 /* Run the state-machine */
1254 result = smtp_block_statemach(conn, FALSE);
1255 }
1256
1257 /* Clear the transfer mode for the next request */
1258 smtp->transfer = FTPTRANSFER_BODY;
1259
1260 return result;
1261}
1262
1263/***********************************************************************
1264 *
1265 * smtp_perform()
1266 *
1267 * This is the actual DO function for SMTP. Transfer a mail, send a command
1268 * or get some data according to the options previously setup.
1269 */
1270static CURLcode smtp_perform(struct connectdata *conn, bool *connected,
1271 bool *dophase_done)
1272{
1273 /* This is SMTP and no proxy */
1274 CURLcode result = CURLE_OK;
1275 struct Curl_easy *data = conn->data;
1276 struct SMTP *smtp = data->req.protop;
1277
1278 DEBUGF(infof(conn->data, "DO phase starts\n"));
1279
1280 if(data->set.opt_no_body) {
1281 /* Requested no body means no transfer */
1282 smtp->transfer = FTPTRANSFER_INFO;
1283 }
1284
1285 *dophase_done = FALSE; /* not done yet */
1286
1287 /* Store the first recipient (or NULL if not specified) */
1288 smtp->rcpt = data->set.mail_rcpt;
1289
1290 /* Initial data character is the first character in line: it is implicitly
1291 preceded by a virtual CRLF. */
1292 smtp->trailing_crlf = TRUE;
1293 smtp->eob = 2;
1294
1295 /* Start the first command in the DO phase */
1296 if((data->set.upload || data->set.mimepost.kind) && data->set.mail_rcpt)
1297 /* MAIL transfer */
1298 result = smtp_perform_mail(conn);
1299 else
1300 /* SMTP based command (VRFY, EXPN, NOOP, RSET or HELP) */
1301 result = smtp_perform_command(conn);
1302
1303 if(result)
1304 return result;
1305
1306 /* Run the state-machine */
1307 result = smtp_multi_statemach(conn, dophase_done);
1308
1309 *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1310
1311 if(*dophase_done)
1312 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1313
1314 return result;
1315}
1316
1317/***********************************************************************
1318 *
1319 * smtp_do()
1320 *
1321 * This function is registered as 'curl_do' function. It decodes the path
1322 * parts etc as a wrapper to the actual DO function (smtp_perform).
1323 *
1324 * The input argument is already checked for validity.
1325 */
1326static CURLcode smtp_do(struct connectdata *conn, bool *done)
1327{
1328 CURLcode result = CURLE_OK;
1329
1330 *done = FALSE; /* default to false */
1331
1332 /* Parse the custom request */
1333 result = smtp_parse_custom_request(conn);
1334 if(result)
1335 return result;
1336
1337 result = smtp_regular_transfer(conn, done);
1338
1339 return result;
1340}
1341
1342/***********************************************************************
1343 *
1344 * smtp_disconnect()
1345 *
1346 * Disconnect from an SMTP server. Cleanup protocol-specific per-connection
1347 * resources. BLOCKING.
1348 */
1349static CURLcode smtp_disconnect(struct connectdata *conn, bool dead_connection)
1350{
1351 struct smtp_conn *smtpc = &conn->proto.smtpc;
1352
1353 /* We cannot send quit unconditionally. If this connection is stale or
1354 bad in any way, sending quit and waiting around here will make the
1355 disconnect wait in vain and cause more problems than we need to. */
1356
1357 /* The SMTP session may or may not have been allocated/setup at this
1358 point! */
1359 if(!dead_connection && smtpc->pp.conn && smtpc->pp.conn->bits.protoconnstart)
1360 if(!smtp_perform_quit(conn))
1361 (void)smtp_block_statemach(conn, TRUE); /* ignore errors on QUIT */
1362
1363 /* Disconnect from the server */
1364 Curl_pp_disconnect(&smtpc->pp);
1365
1366 /* Cleanup the SASL module */
1367 Curl_sasl_cleanup(conn, smtpc->sasl.authused);
1368
1369 /* Cleanup our connection based variables */
1370 Curl_safefree(smtpc->domain);
1371
1372 return CURLE_OK;
1373}
1374
1375/* Call this when the DO phase has completed */
1376static CURLcode smtp_dophase_done(struct connectdata *conn, bool connected)
1377{
1378 struct SMTP *smtp = conn->data->req.protop;
1379
1380 (void)connected;
1381
1382 if(smtp->transfer != FTPTRANSFER_BODY)
1383 /* no data to transfer */
1384 Curl_setup_transfer(conn->data, -1, -1, FALSE, -1);
1385
1386 return CURLE_OK;
1387}
1388
1389/* Called from multi.c while DOing */
1390static CURLcode smtp_doing(struct connectdata *conn, bool *dophase_done)
1391{
1392 CURLcode result = smtp_multi_statemach(conn, dophase_done);
1393
1394 if(result)
1395 DEBUGF(infof(conn->data, "DO phase failed\n"));
1396 else if(*dophase_done) {
1397 result = smtp_dophase_done(conn, FALSE /* not connected */);
1398
1399 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1400 }
1401
1402 return result;
1403}
1404
1405/***********************************************************************
1406 *
1407 * smtp_regular_transfer()
1408 *
1409 * The input argument is already checked for validity.
1410 *
1411 * Performs all commands done before a regular transfer between a local and a
1412 * remote host.
1413 */
1414static CURLcode smtp_regular_transfer(struct connectdata *conn,
1415 bool *dophase_done)
1416{
1417 CURLcode result = CURLE_OK;
1418 bool connected = FALSE;
1419 struct Curl_easy *data = conn->data;
1420
1421 /* Make sure size is unknown at this point */
1422 data->req.size = -1;
1423
1424 /* Set the progress data */
1425 Curl_pgrsSetUploadCounter(data, 0);
1426 Curl_pgrsSetDownloadCounter(data, 0);
1427 Curl_pgrsSetUploadSize(data, -1);
1428 Curl_pgrsSetDownloadSize(data, -1);
1429
1430 /* Carry out the perform */
1431 result = smtp_perform(conn, &connected, dophase_done);
1432
1433 /* Perform post DO phase operations if necessary */
1434 if(!result && *dophase_done)
1435 result = smtp_dophase_done(conn, connected);
1436
1437 return result;
1438}
1439
1440static CURLcode smtp_setup_connection(struct connectdata *conn)
1441{
1442 CURLcode result;
1443
1444 /* Clear the TLS upgraded flag */
1445 conn->tls_upgraded = FALSE;
1446
1447 /* Initialise the SMTP layer */
1448 result = smtp_init(conn);
1449 if(result)
1450 return result;
1451
1452 return CURLE_OK;
1453}
1454
1455/***********************************************************************
1456 *
1457 * smtp_parse_url_options()
1458 *
1459 * Parse the URL login options.
1460 */
1461static CURLcode smtp_parse_url_options(struct connectdata *conn)
1462{
1463 CURLcode result = CURLE_OK;
1464 struct smtp_conn *smtpc = &conn->proto.smtpc;
1465 const char *ptr = conn->options;
1466
1467 smtpc->sasl.resetprefs = TRUE;
1468
1469 while(!result && ptr && *ptr) {
1470 const char *key = ptr;
1471 const char *value;
1472
1473 while(*ptr && *ptr != '=')
1474 ptr++;
1475
1476 value = ptr + 1;
1477
1478 while(*ptr && *ptr != ';')
1479 ptr++;
1480
1481 if(strncasecompare(key, "AUTH=", 5))
1482 result = Curl_sasl_parse_url_auth_option(&smtpc->sasl,
1483 value, ptr - value);
1484 else
1485 result = CURLE_URL_MALFORMAT;
1486
1487 if(*ptr == ';')
1488 ptr++;
1489 }
1490
1491 return result;
1492}
1493
1494/***********************************************************************
1495 *
1496 * smtp_parse_url_path()
1497 *
1498 * Parse the URL path into separate path components.
1499 */
1500static CURLcode smtp_parse_url_path(struct connectdata *conn)
1501{
1502 /* The SMTP struct is already initialised in smtp_connect() */
1503 struct Curl_easy *data = conn->data;
1504 struct smtp_conn *smtpc = &conn->proto.smtpc;
1505 const char *path = &data->state.up.path[1]; /* skip leading path */
1506 char localhost[HOSTNAME_MAX + 1];
1507
1508 /* Calculate the path if necessary */
1509 if(!*path) {
1510 if(!Curl_gethostname(localhost, sizeof(localhost)))
1511 path = localhost;
1512 else
1513 path = "localhost";
1514 }
1515
1516 /* URL decode the path and use it as the domain in our EHLO */
1517 return Curl_urldecode(conn->data, path, 0, &smtpc->domain, NULL, TRUE);
1518}
1519
1520/***********************************************************************
1521 *
1522 * smtp_parse_custom_request()
1523 *
1524 * Parse the custom request.
1525 */
1526static CURLcode smtp_parse_custom_request(struct connectdata *conn)
1527{
1528 CURLcode result = CURLE_OK;
1529 struct Curl_easy *data = conn->data;
1530 struct SMTP *smtp = data->req.protop;
1531 const char *custom = data->set.str[STRING_CUSTOMREQUEST];
1532
1533 /* URL decode the custom request */
1534 if(custom)
1535 result = Curl_urldecode(data, custom, 0, &smtp->custom, NULL, TRUE);
1536
1537 return result;
1538}
1539
1540CURLcode Curl_smtp_escape_eob(struct connectdata *conn, const ssize_t nread)
1541{
1542 /* When sending a SMTP payload we must detect CRLF. sequences making sure
1543 they are sent as CRLF.. instead, as a . on the beginning of a line will
1544 be deleted by the server when not part of an EOB terminator and a
1545 genuine CRLF.CRLF which isn't escaped will wrongly be detected as end of
1546 data by the server
1547 */
1548 ssize_t i;
1549 ssize_t si;
1550 struct Curl_easy *data = conn->data;
1551 struct SMTP *smtp = data->req.protop;
1552 char *scratch = data->state.scratch;
1553 char *newscratch = NULL;
1554 char *oldscratch = NULL;
1555 size_t eob_sent;
1556
1557 /* Do we need to allocate a scratch buffer? */
1558 if(!scratch || data->set.crlf) {
1559 oldscratch = scratch;
1560
1561 scratch = newscratch = malloc(2 * data->set.upload_buffer_size);
1562 if(!newscratch) {
1563 failf(data, "Failed to alloc scratch buffer!");
1564
1565 return CURLE_OUT_OF_MEMORY;
1566 }
1567 }
1568 DEBUGASSERT(data->set.upload_buffer_size >= (size_t)nread);
1569
1570 /* Have we already sent part of the EOB? */
1571 eob_sent = smtp->eob;
1572
1573 /* This loop can be improved by some kind of Boyer-Moore style of
1574 approach but that is saved for later... */
1575 for(i = 0, si = 0; i < nread; i++) {
1576 if(SMTP_EOB[smtp->eob] == data->req.upload_fromhere[i]) {
1577 smtp->eob++;
1578
1579 /* Is the EOB potentially the terminating CRLF? */
1580 if(2 == smtp->eob || SMTP_EOB_LEN == smtp->eob)
1581 smtp->trailing_crlf = TRUE;
1582 else
1583 smtp->trailing_crlf = FALSE;
1584 }
1585 else if(smtp->eob) {
1586 /* A previous substring matched so output that first */
1587 memcpy(&scratch[si], &SMTP_EOB[eob_sent], smtp->eob - eob_sent);
1588 si += smtp->eob - eob_sent;
1589
1590 /* Then compare the first byte */
1591 if(SMTP_EOB[0] == data->req.upload_fromhere[i])
1592 smtp->eob = 1;
1593 else
1594 smtp->eob = 0;
1595
1596 eob_sent = 0;
1597
1598 /* Reset the trailing CRLF flag as there was more data */
1599 smtp->trailing_crlf = FALSE;
1600 }
1601
1602 /* Do we have a match for CRLF. as per RFC-5321, sect. 4.5.2 */
1603 if(SMTP_EOB_FIND_LEN == smtp->eob) {
1604 /* Copy the replacement data to the target buffer */
1605 memcpy(&scratch[si], &SMTP_EOB_REPL[eob_sent],
1606 SMTP_EOB_REPL_LEN - eob_sent);
1607 si += SMTP_EOB_REPL_LEN - eob_sent;
1608 smtp->eob = 0;
1609 eob_sent = 0;
1610 }
1611 else if(!smtp->eob)
1612 scratch[si++] = data->req.upload_fromhere[i];
1613 }
1614
1615 if(smtp->eob - eob_sent) {
1616 /* A substring matched before processing ended so output that now */
1617 memcpy(&scratch[si], &SMTP_EOB[eob_sent], smtp->eob - eob_sent);
1618 si += smtp->eob - eob_sent;
1619 }
1620
1621 /* Only use the new buffer if we replaced something */
1622 if(si != nread) {
1623 /* Upload from the new (replaced) buffer instead */
1624 data->req.upload_fromhere = scratch;
1625
1626 /* Save the buffer so it can be freed later */
1627 data->state.scratch = scratch;
1628
1629 /* Free the old scratch buffer */
1630 free(oldscratch);
1631
1632 /* Set the new amount too */
1633 data->req.upload_present = si;
1634 }
1635 else
1636 free(newscratch);
1637
1638 return CURLE_OK;
1639}
1640
1641#endif /* CURL_DISABLE_SMTP */
1642