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