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