VirtualBox

source: vbox/trunk/src/libs/curl-8.0.1/lib/smtp.c@ 99874

Last change on this file since 99874 was 99344, checked in by vboxsync, 2 years ago

curl-8.0.1: Applied and adjusted our curl changes to 7.87.0 bugref:10417

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette