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