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