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