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