mutt stable branch with some hacks
1/* mutt - text oriented MIME mail user agent
2 * Copyright (C) 2002 Michael R. Elkins <me@mutt.org>
3 * Copyright (C) 2005-2009 Brendan Cully <brendan@kublai.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
18 */
19
20/* This file contains code for direct SMTP delivery of email messages. */
21
22#if HAVE_CONFIG_H
23#include "config.h"
24#endif
25
26#include "mutt.h"
27#include "mutt_curses.h"
28#include "mutt_socket.h"
29#ifdef USE_SSL
30# include "mutt_ssl.h"
31#endif
32#ifdef USE_SASL
33#include "mutt_sasl.h"
34
35#include <sasl/sasl.h>
36#include <sasl/saslutil.h>
37#endif
38
39#include <netdb.h>
40#include <netinet/in.h>
41#include <stdio.h>
42#include <sys/stat.h>
43
44#define smtp_success(x) ((x)/100 == 2)
45#define smtp_ready 334
46#define smtp_continue 354
47
48#define smtp_err_read -2
49#define smtp_err_write -3
50#define smtp_err_code -4
51
52#define SMTP_PORT 25
53#define SMTPS_PORT 465
54
55#define SMTP_AUTH_SUCCESS 0
56#define SMTP_AUTH_UNAVAIL 1
57#define SMTP_AUTH_FAIL -1
58
59enum {
60 STARTTLS,
61 AUTH,
62 DSN,
63 EIGHTBITMIME,
64 SMTPUTF8,
65
66 CAPMAX
67};
68
69static int smtp_auth (CONNECTION* conn);
70static int smtp_auth_oauth (CONNECTION* conn);
71#ifdef USE_SASL
72static int smtp_auth_sasl (CONNECTION* conn, const char* mechanisms);
73#endif
74
75static int smtp_fill_account (ACCOUNT* account);
76static int smtp_open (CONNECTION* conn);
77
78static int Esmtp = 0;
79static char* AuthMechs = NULL;
80static unsigned char Capabilities[(CAPMAX + 7)/ 8];
81
82static int smtp_code (char *buf, size_t len, int *n)
83{
84 char code[4];
85
86 if (len < 4)
87 return -1;
88 code[0] = buf[0];
89 code[1] = buf[1];
90 code[2] = buf[2];
91 code[3] = 0;
92 if (mutt_atoi (code, n) < 0)
93 return -1;
94 return 0;
95}
96
97/* Reads a command response from the SMTP server.
98 * Returns:
99 * 0 on success (2xx code) or continue (354 code)
100 * -1 write error, or any other response code
101 */
102static int
103smtp_get_resp (CONNECTION * conn)
104{
105 int n;
106 char buf[1024];
107
108 do
109 {
110 n = mutt_socket_readln (buf, sizeof (buf), conn);
111 if (n < 4)
112 {
113 /* read error, or no response code */
114 return smtp_err_read;
115 }
116
117 if (!ascii_strncasecmp ("8BITMIME", buf + 4, 8))
118 mutt_bit_set (Capabilities, EIGHTBITMIME);
119 else if (!ascii_strncasecmp ("AUTH ", buf + 4, 5))
120 {
121 mutt_bit_set (Capabilities, AUTH);
122 FREE (&AuthMechs);
123 AuthMechs = safe_strdup (buf + 9);
124 }
125 else if (!ascii_strncasecmp ("DSN", buf + 4, 3))
126 mutt_bit_set (Capabilities, DSN);
127 else if (!ascii_strncasecmp ("STARTTLS", buf + 4, 8))
128 mutt_bit_set (Capabilities, STARTTLS);
129 else if (!ascii_strncasecmp ("SMTPUTF8", buf + 4, 8))
130 mutt_bit_set (Capabilities, SMTPUTF8);
131
132 if (smtp_code (buf, n, &n) < 0)
133 return smtp_err_code;
134
135 } while (buf[3] == '-');
136
137 if (smtp_success (n) || n == smtp_continue)
138 return 0;
139
140 mutt_error (_("SMTP session failed: %s"), buf);
141 return -1;
142}
143
144static int
145smtp_rcpt_to (CONNECTION * conn, const ADDRESS * a)
146{
147 char buf[1024];
148 int r;
149
150 while (a)
151 {
152 /* weed out group mailboxes, since those are for display only */
153 if (!a->mailbox || a->group)
154 {
155 a = a->next;
156 continue;
157 }
158 if (mutt_bit_isset (Capabilities, DSN) && DsnNotify)
159 snprintf (buf, sizeof (buf), "RCPT TO:<%s> NOTIFY=%s\r\n",
160 a->mailbox, DsnNotify);
161 else
162 snprintf (buf, sizeof (buf), "RCPT TO:<%s>\r\n", a->mailbox);
163 if (mutt_socket_write (conn, buf) == -1)
164 return smtp_err_write;
165 if ((r = smtp_get_resp (conn)))
166 return r;
167 a = a->next;
168 }
169
170 return 0;
171}
172
173static int
174smtp_data (CONNECTION * conn, const char *msgfile)
175{
176 char buf[1024];
177 FILE *fp = 0;
178 progress_t progress;
179 struct stat st;
180 int r, term = 0;
181 size_t buflen = 0;
182
183 fp = fopen (msgfile, "r");
184 if (!fp)
185 {
186 mutt_error (_("SMTP session failed: unable to open %s"), msgfile);
187 return -1;
188 }
189 stat (msgfile, &st);
190 unlink (msgfile);
191 mutt_progress_init (&progress, _("Sending message..."), MUTT_PROGRESS_SIZE,
192 NetInc, st.st_size);
193
194 snprintf (buf, sizeof (buf), "DATA\r\n");
195 if (mutt_socket_write (conn, buf) == -1)
196 {
197 safe_fclose (&fp);
198 return smtp_err_write;
199 }
200 if ((r = smtp_get_resp (conn)))
201 {
202 safe_fclose (&fp);
203 return r;
204 }
205
206 while (fgets (buf, sizeof (buf) - 1, fp))
207 {
208 buflen = mutt_strlen (buf);
209 term = buflen && buf[buflen-1] == '\n';
210 if (term && (buflen == 1 || buf[buflen - 2] != '\r'))
211 snprintf (buf + buflen - 1, sizeof (buf) - buflen + 1, "\r\n");
212 if (buf[0] == '.')
213 {
214 if (mutt_socket_write_d (conn, ".", -1, MUTT_SOCK_LOG_FULL) == -1)
215 {
216 safe_fclose (&fp);
217 return smtp_err_write;
218 }
219 }
220 if (mutt_socket_write_d (conn, buf, -1, MUTT_SOCK_LOG_FULL) == -1)
221 {
222 safe_fclose (&fp);
223 return smtp_err_write;
224 }
225 mutt_progress_update (&progress, ftell (fp), -1);
226 }
227 if (!term && buflen &&
228 mutt_socket_write_d (conn, "\r\n", -1, MUTT_SOCK_LOG_FULL) == -1)
229 {
230 safe_fclose (&fp);
231 return smtp_err_write;
232 }
233 safe_fclose (&fp);
234
235 /* terminate the message body */
236 if (mutt_socket_write (conn, ".\r\n") == -1)
237 return smtp_err_write;
238
239 if ((r = smtp_get_resp (conn)))
240 return r;
241
242 return 0;
243}
244
245
246/* Returns 1 if a contains at least one 8-bit character, 0 if none do.
247 */
248static int address_uses_unicode(const char *a)
249{
250 if (!a)
251 return 0;
252
253 while (*a)
254 {
255 if ((unsigned char) *a & (1<<7))
256 return 1;
257 a++;
258 }
259
260 return 0;
261}
262
263
264/* Returns 1 if any address in a contains at least one 8-bit
265 * character, 0 if none do.
266 */
267static int addresses_use_unicode(const ADDRESS* a)
268{
269 while (a)
270 {
271 if (a->mailbox && !a->group && address_uses_unicode(a->mailbox))
272 return 1;
273 a = a->next;
274 }
275 return 0;
276}
277
278
279int
280mutt_smtp_send (const ADDRESS* from, const ADDRESS* to, const ADDRESS* cc,
281 const ADDRESS* bcc, const char *msgfile, int eightbit)
282{
283 CONNECTION *conn;
284 ACCOUNT account;
285 const char* envfrom;
286 char buf[1024];
287 int ret = -1;
288
289 /* it might be better to synthesize an envelope from from user and host
290 * but this condition is most likely arrived at accidentally */
291 if (EnvFrom)
292 envfrom = EnvFrom->mailbox;
293 else if (from)
294 envfrom = from->mailbox;
295 else
296 {
297 mutt_error (_("No from address given"));
298 return -1;
299 }
300
301 if (smtp_fill_account (&account) < 0)
302 return ret;
303
304 if (!(conn = mutt_conn_find (NULL, &account)))
305 return -1;
306
307 Esmtp = eightbit;
308
309 do
310 {
311 /* send our greeting */
312 if (( ret = smtp_open (conn)))
313 break;
314 FREE (&AuthMechs);
315
316 /* send the sender's address */
317 ret = snprintf (buf, sizeof (buf), "MAIL FROM:<%s>", envfrom);
318 if (eightbit && mutt_bit_isset (Capabilities, EIGHTBITMIME))
319 {
320 safe_strncat (buf, sizeof (buf), " BODY=8BITMIME", 15);
321 ret += 14;
322 }
323 if (DsnReturn && mutt_bit_isset (Capabilities, DSN))
324 ret += snprintf (buf + ret, sizeof (buf) - ret, " RET=%s", DsnReturn);
325 if (mutt_bit_isset (Capabilities, SMTPUTF8) &&
326 (address_uses_unicode(envfrom) ||
327 addresses_use_unicode(to) ||
328 addresses_use_unicode(cc) ||
329 addresses_use_unicode(bcc)))
330 ret += snprintf (buf + ret, sizeof (buf) - ret, " SMTPUTF8");
331 safe_strncat (buf, sizeof (buf), "\r\n", 3);
332 if (mutt_socket_write (conn, buf) == -1)
333 {
334 ret = smtp_err_write;
335 break;
336 }
337 if ((ret = smtp_get_resp (conn)))
338 break;
339
340 /* send the recipient list */
341 if ((ret = smtp_rcpt_to (conn, to)) || (ret = smtp_rcpt_to (conn, cc))
342 || (ret = smtp_rcpt_to (conn, bcc)))
343 break;
344
345 /* send the message data */
346 if ((ret = smtp_data (conn, msgfile)))
347 break;
348
349 mutt_socket_write (conn, "QUIT\r\n");
350
351 ret = 0;
352 }
353 while (0);
354
355 if (conn)
356 mutt_socket_close (conn);
357
358 if (ret == smtp_err_read)
359 mutt_error (_("SMTP session failed: read error"));
360 else if (ret == smtp_err_write)
361 mutt_error (_("SMTP session failed: write error"));
362 else if (ret == smtp_err_code)
363 mutt_error (_("Invalid server response"));
364
365 return ret;
366}
367
368static int smtp_fill_account (ACCOUNT* account)
369{
370 static unsigned short SmtpPort = 0;
371
372 struct servent* service;
373 ciss_url_t url;
374 char* urlstr;
375
376 account->flags = 0;
377 account->port = 0;
378 account->type = MUTT_ACCT_TYPE_SMTP;
379
380 urlstr = safe_strdup (SmtpUrl);
381 url_parse_ciss (&url, urlstr);
382 if ((url.scheme != U_SMTP && url.scheme != U_SMTPS)
383 || mutt_account_fromurl (account, &url) < 0)
384 {
385 FREE (&urlstr);
386 mutt_error (_("Invalid SMTP URL: %s"), SmtpUrl);
387 mutt_sleep (1);
388 return -1;
389 }
390 FREE (&urlstr);
391
392 if (url.scheme == U_SMTPS)
393 account->flags |= MUTT_ACCT_SSL;
394
395 if (!account->port)
396 {
397 if (account->flags & MUTT_ACCT_SSL)
398 account->port = SMTPS_PORT;
399 else
400 {
401 if (!SmtpPort)
402 {
403 service = getservbyname ("smtp", "tcp");
404 if (service)
405 SmtpPort = ntohs (service->s_port);
406 else
407 SmtpPort = SMTP_PORT;
408 dprint (3, (debugfile, "Using default SMTP port %d\n", SmtpPort));
409 }
410 account->port = SmtpPort;
411 }
412 }
413
414 return 0;
415}
416
417static int smtp_helo (CONNECTION* conn)
418{
419 char buf[LONG_STRING];
420 const char* fqdn;
421
422 memset (Capabilities, 0, sizeof (Capabilities));
423
424 if (!Esmtp)
425 {
426 /* if TLS or AUTH are requested, use EHLO */
427 if (conn->account.flags & MUTT_ACCT_USER)
428 Esmtp = 1;
429#ifdef USE_SSL
430 if (option (OPTSSLFORCETLS) || quadoption (OPT_SSLSTARTTLS) != MUTT_NO)
431 Esmtp = 1;
432#endif
433 }
434
435 if (!(fqdn = mutt_fqdn (0)))
436 fqdn = NONULL (Hostname);
437
438 snprintf (buf, sizeof (buf), "%s %s\r\n", Esmtp ? "EHLO" : "HELO", fqdn);
439 /* XXX there should probably be a wrapper in mutt_socket.c that
440 * repeatedly calls conn->write until all data is sent. This
441 * currently doesn't check for a short write.
442 */
443 if (mutt_socket_write (conn, buf) == -1)
444 return smtp_err_write;
445 return smtp_get_resp (conn);
446}
447
448static int smtp_open (CONNECTION* conn)
449{
450 int rc;
451
452 if (mutt_socket_open (conn))
453 return -1;
454
455 /* get greeting string */
456 if ((rc = smtp_get_resp (conn)))
457 return rc;
458
459 if ((rc = smtp_helo (conn)))
460 return rc;
461
462#ifdef USE_SSL
463 if (conn->ssf)
464 rc = MUTT_NO;
465 else if (option (OPTSSLFORCETLS))
466 rc = MUTT_YES;
467 else if (mutt_bit_isset (Capabilities, STARTTLS) &&
468 (rc = query_quadoption (OPT_SSLSTARTTLS,
469 _("Secure connection with TLS?"))) == -1)
470 return rc;
471
472 if (rc == MUTT_YES)
473 {
474 if (mutt_socket_write (conn, "STARTTLS\r\n") < 0)
475 return smtp_err_write;
476 if ((rc = smtp_get_resp (conn)))
477 return rc;
478
479 if (mutt_ssl_starttls (conn))
480 {
481 mutt_error (_("Could not negotiate TLS connection"));
482 mutt_sleep (1);
483 return -1;
484 }
485
486 /* re-EHLO to get authentication mechanisms */
487 if ((rc = smtp_helo (conn)))
488 return rc;
489 }
490#endif
491
492 if (conn->account.flags & MUTT_ACCT_USER)
493 {
494 if (!mutt_bit_isset (Capabilities, AUTH))
495 {
496 mutt_error (_("SMTP server does not support authentication"));
497 mutt_sleep (1);
498 return -1;
499 }
500
501 return smtp_auth (conn);
502 }
503
504 return 0;
505}
506
507static int smtp_auth (CONNECTION* conn)
508{
509 int r = SMTP_AUTH_UNAVAIL;
510
511 if (SmtpAuthenticators)
512 {
513 char* methods = safe_strdup (SmtpAuthenticators);
514 char* method;
515 char* delim;
516
517 for (method = methods; method; method = delim)
518 {
519 delim = strchr (method, ':');
520 if (delim)
521 *delim++ = '\0';
522 if (! method[0])
523 continue;
524
525 dprint (2, (debugfile, "smtp_authenticate: Trying method %s\n", method));
526
527 if (!strcmp (method, "oauthbearer"))
528 {
529 r = smtp_auth_oauth (conn);
530 }
531 else
532 {
533#ifdef USE_SASL
534 r = smtp_auth_sasl (conn, method);
535#else
536 mutt_error (_("SMTP authentication method %s requires SASL"), method);
537 mutt_sleep (1);
538 continue;
539#endif
540 }
541 if (r == SMTP_AUTH_FAIL && delim)
542 {
543 mutt_error (_("%s authentication failed, trying next method"), method);
544 mutt_sleep (1);
545 }
546 else if (r != SMTP_AUTH_UNAVAIL)
547 break;
548 }
549
550 FREE (&methods);
551 }
552 else
553 {
554#ifdef USE_SASL
555 r = smtp_auth_sasl (conn, AuthMechs);
556#else
557 mutt_error (_("SMTP authentication requires SASL"));
558 mutt_sleep (1);
559 r = SMTP_AUTH_UNAVAIL;
560#endif
561 }
562
563 if (r != SMTP_AUTH_SUCCESS)
564 mutt_account_unsetpass (&conn->account);
565
566 if (r == SMTP_AUTH_FAIL)
567 {
568 mutt_error (_("SASL authentication failed"));
569 mutt_sleep (1);
570 }
571 else if (r == SMTP_AUTH_UNAVAIL)
572 {
573 mutt_error (_("No authenticators available"));
574 mutt_sleep (1);
575 }
576
577 return r == SMTP_AUTH_SUCCESS ? 0 : -1;
578}
579
580#ifdef USE_SASL
581static int smtp_auth_sasl (CONNECTION* conn, const char* mechlist)
582{
583 sasl_conn_t* saslconn;
584 sasl_interact_t* interaction = NULL;
585 const char* mech;
586 const char* data = NULL;
587 unsigned int len;
588 char *buf = NULL;
589 size_t bufsize = 0;
590 int rc, saslrc;
591
592 if (mutt_sasl_client_new (conn, &saslconn) < 0)
593 return SMTP_AUTH_FAIL;
594
595 do
596 {
597 rc = sasl_client_start (saslconn, mechlist, &interaction, &data, &len, &mech);
598 if (rc == SASL_INTERACT)
599 mutt_sasl_interact (interaction);
600 }
601 while (rc == SASL_INTERACT);
602
603 if (rc != SASL_OK && rc != SASL_CONTINUE)
604 {
605 dprint (2, (debugfile, "smtp_auth_sasl: %s unavailable\n", mech));
606 sasl_dispose (&saslconn);
607 return SMTP_AUTH_UNAVAIL;
608 }
609
610 if (!option(OPTNOCURSES))
611 mutt_message (_("Authenticating (%s)..."), mech);
612
613 bufsize = ((len * 2) > LONG_STRING) ? (len * 2) : LONG_STRING;
614 buf = safe_malloc (bufsize);
615
616 snprintf (buf, bufsize, "AUTH %s", mech);
617 if (len)
618 {
619 safe_strcat (buf, bufsize, " ");
620 if (sasl_encode64 (data, len, buf + mutt_strlen (buf),
621 bufsize - mutt_strlen (buf), &len) != SASL_OK)
622 {
623 dprint (1, (debugfile, "smtp_auth_sasl: error base64-encoding client response.\n"));
624 goto fail;
625 }
626 }
627 safe_strcat (buf, bufsize, "\r\n");
628
629 do
630 {
631 if (mutt_socket_write (conn, buf) < 0)
632 goto fail;
633 if ((rc = mutt_socket_readln (buf, bufsize, conn)) < 0)
634 goto fail;
635 if (smtp_code (buf, rc, &rc) < 0)
636 goto fail;
637
638 if (rc != smtp_ready)
639 break;
640
641 if (sasl_decode64 (buf+4, strlen (buf+4), buf, bufsize - 1, &len) != SASL_OK)
642 {
643 dprint (1, (debugfile, "smtp_auth_sasl: error base64-decoding server response.\n"));
644 goto fail;
645 }
646
647 do
648 {
649 saslrc = sasl_client_step (saslconn, buf, len, &interaction, &data, &len);
650 if (saslrc == SASL_INTERACT)
651 mutt_sasl_interact (interaction);
652 }
653 while (saslrc == SASL_INTERACT);
654
655 if (len)
656 {
657 if ((len * 2) > bufsize)
658 {
659 bufsize = len * 2;
660 safe_realloc (&buf, bufsize);
661 }
662 if (sasl_encode64 (data, len, buf, bufsize, &len) != SASL_OK)
663 {
664 dprint (1, (debugfile, "smtp_auth_sasl: error base64-encoding client response.\n"));
665 goto fail;
666 }
667 }
668 strfcpy (buf + len, "\r\n", bufsize - len);
669 } while (rc == smtp_ready && saslrc != SASL_FAIL);
670
671 if (smtp_success (rc))
672 {
673 mutt_sasl_setup_conn (conn, saslconn);
674 FREE (&buf);
675 return SMTP_AUTH_SUCCESS;
676 }
677
678fail:
679 sasl_dispose (&saslconn);
680 FREE (&buf);
681 return SMTP_AUTH_FAIL;
682}
683#endif /* USE_SASL */
684
685
686/* smtp_auth_oauth: AUTH=OAUTHBEARER support. See RFC 7628 */
687static int smtp_auth_oauth (CONNECTION* conn)
688{
689 char* ibuf = NULL;
690 char* oauthbearer = NULL;
691 int ilen;
692 int rc;
693
694 mutt_message _("Authenticating (OAUTHBEARER)...");
695
696 /* We get the access token from the smtp_oauth_refresh_command */
697 oauthbearer = mutt_account_getoauthbearer (&conn->account);
698 if (oauthbearer == NULL)
699 return SMTP_AUTH_FAIL;
700
701 ilen = strlen (oauthbearer) + 30;
702 ibuf = safe_malloc (ilen);
703
704 snprintf (ibuf, ilen, "AUTH OAUTHBEARER %s\r\n", oauthbearer);
705
706 rc = mutt_socket_write (conn, ibuf);
707 FREE (&oauthbearer);
708 FREE (&ibuf);
709
710 if (rc == -1)
711 return SMTP_AUTH_FAIL;
712 if (smtp_get_resp (conn) != 0)
713 return SMTP_AUTH_FAIL;
714
715 return SMTP_AUTH_SUCCESS;
716}