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