Rocco Rutte:
[apps/madmutt.git] / mutt_libesmtp.c
1 /*
2  * Copyright notice from original mutt:
3  * [none]
4  *
5  * This file is part of mutt-ng, see http://www.muttng.org/.
6  * It's licensed under the GNU General Public License,
7  * please see the file GPL in the top level source directory.
8  */
9 #if HAVE_CONFIG_H
10 #include "config.h"
11 #endif
12
13 #include "mutt.h"
14 #include "ascii.h"
15 #include "enter.h"
16
17 #include "lib/mem.h"
18 #include "lib/intl.h"
19 #include "lib/str.h"
20
21 #if defined (USE_SSL) || (defined (USE_GNUTLS) && defined (HAVE_GNUTLS_OPENSSL_H))
22 #include <openssl/ssl.h>
23 #endif
24
25 #include <errno.h>
26
27 #include <auth-client.h>
28 #include <libesmtp.h>
29
30 static char authpass[STRING] = "";
31
32 #define FAIL() \
33   do { \
34     ret = -1; \
35     goto Done; \
36   } while (0)
37 #define MSGFAIL(msg) \
38   do { \
39     mutt_error("%s", msg); \
40     FAIL(); \
41   } while (0)
42 #define LIBCFAIL(msg) \
43   do { \
44     mutt_error("%s: %s", msg, strerror(errno)); \
45     FAIL(); \
46   } while (0)
47 #define SMTPFAIL(msg) \
48   do { \
49     _mutt_libesmtp_perror(msg); \
50     FAIL(); \
51   } while (0)
52
53 /*
54  * _mutt_libesmtp_ensure_init
55  *   Make sure the libESMTP support in mutt is initialized at some time.
56  */
57 static void _mutt_libesmtp_ensure_init ()
58 {
59   static int libesmtp_init = 0;
60
61   if (!libesmtp_init) {
62     if (SmtpAuthUser)
63       auth_client_init ();
64     libesmtp_init = 1;
65   }
66 }
67
68 /*
69  * _mutt_libesmtp_perror
70  *   Prints 'msg', a colon, and then a string representation of the
71  *   libesmtp errno as a mutt error.
72  */
73 static void _mutt_libesmtp_perror (const char *msg)
74 {
75   char buf[512];
76
77   mutt_error ("%s: %s", msg,
78               smtp_strerror (smtp_errno (), buf, sizeof (buf)));
79 }
80
81 /*
82  * _mutt_libesmtp_add_recipients
83  *   Adds every address in 'addr' as a recipient to the smtp message
84  *   'message'.  Note that this does not mean that they will necessarily
85  *   show up in the mail headers (e.g., when bcc'ing).  Returns 0 upon
86  *   success, -1 upon failure (and prints an error message).
87  *
88  *   Very similar to sendlib.c::add_args
89  */
90 static int
91 _mutt_libesmtp_add_recipients (smtp_message_t message, ADDRESS * addr)
92 {
93   int ret = 0;
94
95   for (; addr; addr = addr->next) {
96     /* weed out group mailboxes, since those are for display only */
97     if (addr->mailbox && !addr->group) {
98       if (!smtp_add_recipient (message, addr->mailbox))
99         SMTPFAIL ("smtp_add_recipient");
100     }
101   }
102
103 Done:
104   return ret;
105 }
106
107 static int
108 _mutt_libesmtp_auth_interact (auth_client_request_t request,
109                               char **result, int fields, void *arg)
110 {
111   int i;
112
113   for (i = 0; i < fields; i++) {
114     if (request[i].flags & AUTH_USER) {
115       result[i] = SmtpAuthUser;
116     }
117     else if (request[i].flags & AUTH_PASS) {
118       if (SmtpAuthPass) {
119         result[i] = SmtpAuthPass;
120       }
121       else {
122         if (authpass[0] == '\0') {
123           char prompt[STRING];
124
125           snprintf (prompt, sizeof (prompt), "%s%s: ", request[i].prompt,
126                     (request[i].
127                      flags & AUTH_CLEARTEXT) ? " (not encrypted)" : "");
128           mutt_get_field_unbuffered (prompt, authpass, sizeof (authpass),
129                                      M_PASS);
130         }
131         result[i] = authpass;
132       }
133     }
134   }
135
136   return 1;
137 }
138
139 #define BUFLEN 8192
140
141 static const char *_mutt_libesmtp_messagefp_cb (void **buf, int *len,
142                                                 void *arg)
143 {
144   int octets;
145
146   if (*buf == NULL)
147     *buf = malloc (BUFLEN);
148
149   if (len == NULL) {
150     rewind ((FILE *) arg);
151     return NULL;
152   }
153
154   if (fgets (*buf, BUFLEN - 2, (FILE *) arg) == NULL) {
155     octets = 0;
156   }
157   else {
158     char *p = strchr (*buf, '\0');
159
160     if (p[-1] == '\n' && p[-2] != '\r') {
161       strcpy (p - 1, "\r\n");
162       p++;
163     }
164     octets = p - (char *) *buf;
165   }
166
167   *len = octets;
168   return *buf;
169 }
170
171 #if defined (USE_SSL) || (defined (USE_GNUTLS) && defined (HAVE_GNUTLS_OPENSSL_H))
172 static int handle_invalid_peer_certificate (long vfy_result) {
173   mutt_error (_("Error verifying certificate: %s"),
174               NONULL (X509_verify_cert_error_string (vfy_result)));
175   sleep(2);
176   return 1; /* Accept the problem */
177 }
178 #endif
179
180 void event_cb (smtp_session_t session, int event_no, void *arg,...)
181
182   va_list alist;
183   int *ok;
184
185   va_start(alist, arg);
186   switch(event_no) {
187   case SMTP_EV_CONNECT:
188   case SMTP_EV_MAILSTATUS:
189   case SMTP_EV_RCPTSTATUS:
190   case SMTP_EV_MESSAGEDATA:
191   case SMTP_EV_MESSAGESENT:
192   case SMTP_EV_DISCONNECT: break;
193   case SMTP_EV_WEAK_CIPHER: {
194     int bits;
195     bits = va_arg(alist, long); ok = va_arg(alist, int*);
196     mutt_message (_("SMTP_EV_WEAK_CIPHER, bits=%d - accepted."), bits);
197     sleep(1);
198     *ok = 1; break;
199   } 
200   case SMTP_EV_STARTTLS_OK:
201     mutt_message (_("Using TLS"));
202     sleep(1);
203     break;
204   case SMTP_EV_INVALID_PEER_CERTIFICATE: {
205     long vfy_result;
206     vfy_result = va_arg(alist, long); ok = va_arg(alist, int*);
207     *ok = handle_invalid_peer_certificate(vfy_result);
208     sleep(1);
209     break;
210   } 
211   case SMTP_EV_NO_PEER_CERTIFICATE: {
212     ok = va_arg(alist, int*); 
213     mutt_message (_("SMTP_EV_NO_PEER_CERTIFICATE - accepted."));
214     sleep(1);
215     *ok = 1; break;
216   }
217   case SMTP_EV_WRONG_PEER_CERTIFICATE: {
218     ok = va_arg(alist, int*);
219     mutt_message (_("SMTP_EV_WRONG_PEER_CERTIFICATE - accepted."));
220     sleep(1);
221     *ok = 1; break;
222   }
223   case SMTP_EV_NO_CLIENT_CERTIFICATE: {
224     ok = va_arg(alist, int*);
225     mutt_message (_("SMTP_EV_NO_CLIENT_CERTIFICATE - accepted."));
226     sleep(1);
227     *ok = 1; break;
228   }
229   default:
230     mutt_message(_("Got event: %d - ignored."), event_no);
231     sleep(1);
232   }
233   va_end(alist);
234 }
235
236 /*
237  * mutt_invoke_libesmtp
238  *   Sends a mail message to the provided recipients using libesmtp.
239  *   Returns 0 upon success, -1 upon failure (and prints an error
240  *   message).
241  */
242 int mutt_invoke_libesmtp (ADDRESS * from,       /* the sender */
243                           ADDRESS * to, ADDRESS * cc, ADDRESS * bcc,    /* recips */
244                           const char *msg,      /* file containing message */
245                           int eightbit)
246 {                               /* message contains 8bit chars */
247   int ret = 0;                  /* return value, default = success */
248   smtp_session_t session;
249   smtp_message_t message;
250   char *hostportstr = NULL;
251   size_t hostportlen;
252   FILE *fp = NULL;
253   auth_context_t authctx = NULL;
254   const smtp_status_t *status;
255
256   _mutt_libesmtp_ensure_init ();
257
258   if ((session = smtp_create_session ()) == NULL)
259     SMTPFAIL ("smtp_create_session");
260
261 #if defined (USE_SSL) || (defined (USE_GNUTLS) && defined (HAVE_GNUTLS_OPENSSL_H))
262   if (SmtpUseTLS != NULL && ascii_strncasecmp("enabled", SmtpUseTLS, 7) == 0) {
263     smtp_starttls_enable(session, Starttls_ENABLED);
264   } else if (SmtpUseTLS != NULL && ascii_strncasecmp("required", SmtpUseTLS, 8) == 0) {
265     smtp_starttls_enable(session, Starttls_REQUIRED);
266   }
267 #endif
268
269   /* Create hostname:port string and tell libesmtp */
270   /* len = SmtpHost len + colon + max port (65536 => 5 chars) + terminator */
271   hostportlen = str_len (SmtpHost) + 7;
272   hostportstr = mem_malloc (hostportlen);
273   snprintf (hostportstr, hostportlen, "%s:%d", SmtpHost, SmtpPort);
274   if (!smtp_set_server (session, hostportstr))
275     SMTPFAIL ("smtp_set_server");
276
277   if (SmtpAuthUser) {
278     if ((authctx = auth_create_context ()) == NULL)
279       MSGFAIL ("auth_create_context failed");
280     auth_set_mechanism_flags (authctx, AUTH_PLUGIN_PLAIN, 0);
281     auth_set_interact_cb (authctx, _mutt_libesmtp_auth_interact, NULL);
282
283     if (!smtp_auth_set_context (session, authctx))
284       SMTPFAIL ("smtp_auth_set_context");
285   }
286
287 #if defined (USE_SSL) || (defined (USE_GNUTLS) && defined (HAVE_GNUTLS_OPENSSL_H))
288   smtp_starttls_set_ctx (session, NULL);
289 #endif
290   smtp_set_eventcb (session, event_cb, NULL);
291
292   if ((message = smtp_add_message (session)) == NULL)
293     SMTPFAIL ("smtp_add_message");
294   /*  Initialize envelope sender */
295   if (!smtp_set_reverse_path (message, from->mailbox))
296     SMTPFAIL ("smtp_set_reverse_path");
297
298   if ((fp = fopen (msg, "r")) == NULL)
299     LIBCFAIL ("fopen");
300   if (!smtp_set_messagecb (message, _mutt_libesmtp_messagefp_cb, fp))
301     SMTPFAIL ("smtp_set_messagecb");
302   if (_mutt_libesmtp_add_recipients (message, to))
303     FAIL ();
304   if (_mutt_libesmtp_add_recipients (message, cc))
305     FAIL ();
306   if (_mutt_libesmtp_add_recipients (message, bcc))
307     FAIL ();
308   if (!smtp_start_session (session))
309     SMTPFAIL ("smtp_start_session");
310
311   status = smtp_message_transfer_status (message);
312   if (status->code < 200 || status->code > 299) {
313     char buf[256];
314
315     snprintf (buf, sizeof (buf), "SMTP error while sending: %d %s",
316               status->code, status->text);
317     MSGFAIL (buf);
318   }
319
320 Done:
321   if (fp != NULL)
322     fclose (fp);
323   if (hostportstr != NULL)
324     free (hostportstr);
325   if (session != NULL)
326     smtp_destroy_session (session);
327   if (authctx != NULL)
328     auth_destroy_context (authctx);
329
330   /* Forget user-entered SMTP AUTH password if send fails */
331   if (ret != 0)
332     authpass[0] = '\0';
333
334   return ret;
335 }