VirtualBox

source: vbox/trunk/src/libs/curl-7.64.0/lib/smtp.c@ 95219

Last change on this file since 95219 was 85671, checked in by vboxsync, 4 years ago

Export out internal curl copy to make it a lot simpler to build VBox (OSE) on Windows. bugref:9814

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

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