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