add some lua in the mime module as well.
[apps/madmutt.git] / mutt_libesmtp.c
1 /*
2  * Copyright notice from original mutt:
3  * [none]
4  *
5  * Parts were written/modified by:
6  * Christian Gall <cg@cgall.de>
7  * Rocco Rutte <pdmef@cs.tu-berlin.de>
8  *
9  * This file is part of mutt-ng, see http://www.muttng.org/.
10  * It's licensed under the GNU General Public License,
11  * please see the file GPL in the top level source directory.
12  */
13
14 #include <lib-lib/lib-lib.h>
15
16 #include <lib-ui/enter.h>
17 #include <lib-ui/curses.h>
18
19 #include "mutt.h"
20 #include "mutt_libesmtp.h"
21
22 #if defined (USE_SSL) || (defined (USE_GNUTLS) && defined (HAVE_GNUTLS_OPENSSL_H))
23 #include <openssl/ssl.h>
24 #endif
25
26 #include <auth-client.h>
27 #include <libesmtp.h>
28
29 static char authpass[STRING] = "";
30
31 #define FAIL() \
32   do { \
33     ret = -1; \
34     goto Done; \
35   } while (0)
36 #define MSGFAIL(msg) \
37   do { \
38     mutt_error("%s", msg); \
39     FAIL(); \
40   } while (0)
41 #define LIBCFAIL(msg) \
42   do { \
43     mutt_error("%s: %s", msg, strerror(errno)); \
44     FAIL(); \
45   } while (0)
46 #define SMTPFAIL(msg) \
47   do { \
48     _mutt_libesmtp_perror(msg); \
49     FAIL(); \
50   } while (0)
51 #define extna(msg) { mutt_error (_("SMTP Extension '%s' not supported by MTA."), \
52                                  msg); sleep (1); }
53
54 /*
55  * _mutt_libesmtp_ensure_init
56  *   Make sure the libESMTP support in mutt is initialized at some time.
57  */
58 static void _mutt_libesmtp_ensure_init ()
59 {
60   static int libesmtp_init = 0;
61
62   if (!libesmtp_init) {
63     if (SmtpAuthUser)
64       auth_client_init ();
65     libesmtp_init = 1;
66   }
67 }
68
69 /*
70  * _mutt_libesmtp_perror
71  *   Prints 'msg', a colon, and then a string representation of the
72  *   libesmtp errno as a mutt error.
73  */
74 static void _mutt_libesmtp_perror (const char *msg)
75 {
76   char buf[512];
77
78   mutt_error ("%s: %s", msg,
79               smtp_strerror (smtp_errno (), buf, sizeof (buf)));
80 }
81
82 /*
83  * _mutt_libesmtp_add_recipients
84  *   Adds every address in 'addr' as a recipient to the smtp message
85  *   'message'.  Note that this does not mean that they will necessarily
86  *   show up in the mail headers (e.g., when bcc'ing).  Returns 0 upon
87  *   success, -1 upon failure (and prints an error message).
88  *
89  *   Very similar to sendlib.c::add_args
90  */
91 static int
92 _mutt_libesmtp_add_recipients (smtp_message_t message, address_t * addr)
93 {
94   int ret = 0;
95
96   for (; addr; addr = addr->next) {
97     /* weed out group mailboxes, since those are for display only */
98     if (addr->mailbox && !addr->group) {
99       if (!smtp_add_recipient (message, addr->mailbox))
100         SMTPFAIL ("smtp_add_recipient");
101     }
102   }
103
104 Done:
105   return ret;
106 }
107
108 static int
109 _mutt_libesmtp_auth_interact (auth_client_request_t request,
110                               char **result, int fields,
111                               void *arg __attribute__ ((unused)))
112 {
113   int i;
114
115   for (i = 0; i < fields; i++) {
116     if (request[i].flags & AUTH_USER) {
117       result[i] = SmtpAuthUser;
118     }
119     else if (request[i].flags & AUTH_PASS) {
120       if (SmtpAuthPass) {
121         result[i] = SmtpAuthPass;
122       }
123       else {
124         if (authpass[0] == '\0') {
125           char prompt[STRING];
126
127           snprintf (prompt, sizeof (prompt), "%s%s: ", request[i].prompt,
128                     (request[i].
129                      flags & AUTH_CLEARTEXT) ? " (not encrypted)" : "");
130           mutt_get_field_unbuffered (prompt, authpass, sizeof (authpass),
131                                      M_PASS);
132         }
133         result[i] = authpass;
134       }
135     }
136   }
137
138   return 1;
139 }
140
141 #define BUFLEN 8192
142
143 static const char *_mutt_libesmtp_messagefp_cb (void **buf, int *len,
144                                                 void *arg)
145 {
146   int octets;
147
148   if (*buf == NULL)
149     *buf = xmalloc(BUFLEN);
150
151   if (len == NULL) {
152     rewind ((FILE *) arg);
153     return NULL;
154   }
155
156   if (fgets (*buf, BUFLEN - 2, (FILE *) arg) == NULL) {
157     octets = 0;
158   }
159   else {
160     char *p = strchr (*buf, '\0');
161
162     if (p[-1] == '\n' && p[-2] != '\r') {
163       m_strcpy(p - 1, (char *) *buf + BUFLEN - p + 1, "\r\n");
164       p++;
165     }
166     octets = p - (char *) *buf;
167   }
168
169   *len = octets;
170   return *buf;
171 }
172
173 static int handle_invalid_peer_certificate (long vfy_result) {
174 #if defined (USE_SSL) || (defined (USE_GNUTLS) && defined (HAVE_GNUTLS_OPENSSL_H))
175   mutt_error (_("Error verifying certificate: %s"),
176               NONULL (X509_verify_cert_error_string (vfy_result)));
177 #else
178   mutt_error (_("Error verifying certificate. Error Code: %lu"), vfy_result);
179 #endif
180   sleep(2);
181   return 1; /* Accept the problem */
182 }
183
184 static void event_cb (smtp_session_t session __attribute__ ((unused)),
185                       int event_no, void *arg,...)
186
187   va_list alist;
188   int *ok;
189
190   va_start(alist, arg);
191   switch(event_no) {
192   case SMTP_EV_CONNECT:
193   case SMTP_EV_MAILSTATUS:
194   case SMTP_EV_RCPTSTATUS:
195   case SMTP_EV_MESSAGEDATA:
196   case SMTP_EV_MESSAGESENT:
197   case SMTP_EV_DISCONNECT: break;
198   case SMTP_EV_WEAK_CIPHER: {
199     int bits;
200     bits = va_arg(alist, long); ok = va_arg(alist, int*);
201     mutt_message (_("SMTP_EV_WEAK_CIPHER, bits=%d - accepted."), bits);
202     sleep(1);
203     *ok = 1; break;
204   } 
205   case SMTP_EV_STARTTLS_OK:
206     mutt_message (_("Using TLS"));
207     sleep(1);
208     break;
209   case SMTP_EV_INVALID_PEER_CERTIFICATE: {
210     long vfy_result;
211     vfy_result = va_arg(alist, long); ok = va_arg(alist, int*);
212     *ok = handle_invalid_peer_certificate(vfy_result);
213     sleep(1);
214     break;
215   } 
216   case SMTP_EV_NO_PEER_CERTIFICATE: {
217     ok = va_arg(alist, int*); 
218     mutt_message (_("SMTP_EV_NO_PEER_CERTIFICATE - accepted."));
219     sleep(1);
220     *ok = 1; break;
221   }
222   case SMTP_EV_WRONG_PEER_CERTIFICATE: {
223     ok = va_arg(alist, int*);
224     mutt_message (_("SMTP_EV_WRONG_PEER_CERTIFICATE - accepted."));
225     sleep(1);
226     *ok = 1; break;
227   }
228   case SMTP_EV_NO_CLIENT_CERTIFICATE: {
229     ok = va_arg(alist, int*);
230     mutt_message (_("SMTP_EV_NO_CLIENT_CERTIFICATE - accepted."));
231     sleep(1);
232     *ok = 1; break;
233   }
234   case SMTP_EV_EXTNA_DSN:
235     extna ("DSN");
236     break;
237   case SMTP_EV_EXTNA_STARTTLS:
238     extna ("StartTLS");
239     break;
240   case SMTP_EV_EXTNA_8BITMIME:
241     extna ("8BITMIME");
242     break;
243   default:
244     mutt_message(_("Got unhandled event ID = %d - ignored."), event_no);
245     sleep(1);
246   }
247   va_end(alist);
248 }
249
250 static void do_dsn_notify (smtp_message_t message, const char* from) {
251   int flags = Notify_NOTSET;
252   smtp_recipient_t self = NULL;
253
254   if (m_strisempty(DsnNotify) || !message || m_strisempty(from) || 
255       strstr (DsnNotify, "never") != NULL)
256     return;
257
258   if (strstr (DsnNotify, "failure") != NULL)
259     flags |= Notify_FAILURE;
260   if (strstr (DsnNotify, "delay") != NULL)
261     flags |= Notify_DELAY;
262   if (strstr (DsnNotify, "success") != NULL)
263     flags |= Notify_SUCCESS;
264
265   if (flags != Notify_NOTSET) {
266     if (!(self = smtp_add_recipient (message, from)))
267       return;
268     smtp_dsn_set_notify (self, flags);
269   }
270 }
271
272 static void do_dsn_ret (smtp_message_t message) {
273   if (m_strisempty(DsnReturn) || !message)
274     return;
275   if (ascii_strncasecmp (DsnReturn, "hdrs", 4) == 0)
276     smtp_dsn_set_ret (message, Ret_HDRS);
277   else if (ascii_strncasecmp (DsnReturn, "full", 4) == 0)
278     smtp_dsn_set_ret (message, Ret_FULL);
279 }
280
281 #if defined (USE_LIBESMTP) && (defined (USE_SSL) || defined (USE_GNUTLS))
282 int mutt_libesmtp_check_usetls (const char* option, unsigned long p,
283                                 char* errbuf, size_t errlen) {
284   char* val = (char*) p;
285   if (m_strisempty(val))
286     return (1);
287   if (m_strncmp(val, "enabled", 7) != 0 &&
288       m_strncmp(val, "required", 8) != 0) {
289     if (errbuf)
290       snprintf (errbuf, errlen, _("'%s' is invalid for %s"), val, option);
291     return (0);
292   }
293   return (1);
294 }
295 #endif
296
297 /*
298  * mutt_libesmtp_invoke
299  *   Sends a mail message to the provided recipients using libesmtp.
300  *   Returns 0 upon success, -1 upon failure (and prints an error
301  *   message).
302  */
303 int mutt_libesmtp_invoke (address_t * from,       /* the sender */
304                           address_t * to, address_t * cc, address_t * bcc,    /* recips */
305                           const char *msg,      /* file containing message */
306                           int eightbit)
307 {                               /* message contains 8bit chars */
308   int ret = 0;                  /* return value, default = success */
309   smtp_session_t session;
310   smtp_message_t message;
311   char *hostportstr = NULL;
312   size_t hostportlen;
313   FILE *fp = NULL;
314   auth_context_t authctx = NULL;
315   const smtp_status_t *status;
316   char* envfrom = from->mailbox;
317
318   _mutt_libesmtp_ensure_init ();
319
320   if ((session = smtp_create_session ()) == NULL)
321     SMTPFAIL ("smtp_create_session");
322
323 #if defined (USE_SSL) || (defined (USE_GNUTLS) && defined (HAVE_GNUTLS_OPENSSL_H))
324   if (SmtpUseTLS != NULL && ascii_strncasecmp("enabled", SmtpUseTLS, 7) == 0) {
325     smtp_starttls_enable(session, Starttls_ENABLED);
326   } else if (SmtpUseTLS != NULL && ascii_strncasecmp("required", SmtpUseTLS, 8) == 0) {
327     smtp_starttls_enable(session, Starttls_REQUIRED);
328   }
329 #endif
330
331   /* Create hostname:port string and tell libesmtp */
332   /* len = SmtpHost len + colon + max port (65536 => 5 chars) + terminator */
333   hostportlen = m_strlen(SmtpHost) + 7;
334   hostportstr = p_new(char, hostportlen);
335   snprintf (hostportstr, hostportlen, "%s:%d", SmtpHost, SmtpPort);
336   if (!smtp_set_server (session, hostportstr))
337     SMTPFAIL ("smtp_set_server");
338
339   if (SmtpAuthUser) {
340     if ((authctx = auth_create_context ()) == NULL)
341       MSGFAIL ("auth_create_context failed");
342     auth_set_mechanism_flags (authctx, AUTH_PLUGIN_PLAIN, 0);
343     auth_set_interact_cb (authctx, _mutt_libesmtp_auth_interact, NULL);
344
345     if (!smtp_auth_set_context (session, authctx))
346       SMTPFAIL ("smtp_auth_set_context");
347   }
348
349 #if defined (USE_SSL) || (defined (USE_GNUTLS) && defined (HAVE_GNUTLS_OPENSSL_H))
350   smtp_starttls_set_ctx (session, NULL);
351 #endif
352   smtp_set_eventcb (session, event_cb, NULL);
353
354   if ((message = smtp_add_message (session)) == NULL)
355     SMTPFAIL ("smtp_add_message");
356
357   /*  Initialize envelope sender */
358   if (MTransport.use_envelope_fromoption && MTransport.envelope_from_address)
359     envfrom = MTransport.envelope_from_address->mailbox;
360   if (!smtp_set_reverse_path (message, envfrom))
361     SMTPFAIL ("smtp_set_reverse_path");
362
363   /* set up DSN for message */
364   do_dsn_notify (message, envfrom);
365   do_dsn_ret (message);
366
367   /* set up 8bitmime flag */
368   if (eightbit && MTransport.use_8bitmime)
369     smtp_8bitmime_set_body (message, E8bitmime_8BITMIME);
370
371   if ((fp = fopen (msg, "r")) == NULL)
372     LIBCFAIL ("fopen");
373   if (!smtp_set_messagecb (message, _mutt_libesmtp_messagefp_cb, fp))
374     SMTPFAIL ("smtp_set_messagecb");
375   if (_mutt_libesmtp_add_recipients (message, to))
376     FAIL ();
377   if (_mutt_libesmtp_add_recipients (message, cc))
378     FAIL ();
379   if (_mutt_libesmtp_add_recipients (message, bcc))
380     FAIL ();
381   if (!smtp_start_session (session))
382     SMTPFAIL ("smtp_start_session");
383
384   status = smtp_message_transfer_status (message);
385   if (status->code < 200 || status->code > 299) {
386     char buf[256];
387
388     snprintf (buf, sizeof (buf), "SMTP error while sending: %d %s",
389               status->code, status->text);
390     MSGFAIL (buf);
391   }
392
393 Done:
394   m_fclose(&fp);
395   if (hostportstr != NULL)
396     p_delete(&hostportstr);
397   if (session != NULL)
398     smtp_destroy_session (session);
399   if (authctx != NULL)
400     auth_destroy_context (authctx);
401
402   /* Forget user-entered SMTP AUTH password if send fails */
403   if (ret != 0)
404     authpass[0] = '\0';
405
406   return ret;
407 }