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