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