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
15 #include "lib/mem.h"
16 #include "lib/intl.h"
17 #include "lib/str.h"
18
19 #include <errno.h>
20 #include <auth-client.h>
21 #include <libesmtp.h>
22
23 static char authpass[STRING] = "";
24
25 #define FAIL() \
26   do { \
27     ret = -1; \
28     goto Done; \
29   } while (0)
30 #define MSGFAIL(msg) \
31   do { \
32     mutt_error("%s", msg); \
33     FAIL(); \
34   } while (0)
35 #define LIBCFAIL(msg) \
36   do { \
37     mutt_error("%s: %s", msg, strerror(errno)); \
38     FAIL(); \
39   } while (0)
40 #define SMTPFAIL(msg) \
41   do { \
42     _mutt_libesmtp_perror(msg); \
43     FAIL(); \
44   } while (0)
45
46 /*
47  * _mutt_libesmtp_ensure_init
48  *   Make sure the libESMTP support in mutt is initialized at some time.
49  */
50 static void _mutt_libesmtp_ensure_init ()
51 {
52   static int libesmtp_init = 0;
53
54   if (!libesmtp_init) {
55     if (SmtpAuthUser)
56       auth_client_init ();
57     libesmtp_init = 1;
58   }
59 }
60
61 /*
62  * _mutt_libesmtp_perror
63  *   Prints 'msg', a colon, and then a string representation of the
64  *   libesmtp errno as a mutt error.
65  */
66 static void _mutt_libesmtp_perror (const char *msg)
67 {
68   char buf[512];
69
70   mutt_error ("%s: %s", msg,
71               smtp_strerror (smtp_errno (), buf, sizeof (buf)));
72 }
73
74 /*
75  * _mutt_libesmtp_add_recipients
76  *   Adds every address in 'addr' as a recipient to the smtp message
77  *   'message'.  Note that this does not mean that they will necessarily
78  *   show up in the mail headers (e.g., when bcc'ing).  Returns 0 upon
79  *   success, -1 upon failure (and prints an error message).
80  *
81  *   Very similar to sendlib.c::add_args
82  */
83 static int
84 _mutt_libesmtp_add_recipients (smtp_message_t message, ADDRESS * addr)
85 {
86   int ret = 0;
87
88   for (; addr; addr = addr->next) {
89     /* weed out group mailboxes, since those are for display only */
90     if (addr->mailbox && !addr->group) {
91       if (!smtp_add_recipient (message, addr->mailbox))
92         SMTPFAIL ("smtp_add_recipient");
93     }
94   }
95
96 Done:
97   return ret;
98 }
99
100 static int
101 _mutt_libesmtp_auth_interact (auth_client_request_t request,
102                               char **result, int fields, void *arg)
103 {
104   int i;
105
106   for (i = 0; i < fields; i++) {
107     if (request[i].flags & AUTH_USER) {
108       result[i] = SmtpAuthUser;
109     }
110     else if (request[i].flags & AUTH_PASS) {
111       if (SmtpAuthPass) {
112         result[i] = SmtpAuthPass;
113       }
114       else {
115         if (authpass[0] == '\0') {
116           char prompt[STRING];
117
118           snprintf (prompt, sizeof (prompt), "%s%s: ", request[i].prompt,
119                     (request[i].
120                      flags & AUTH_CLEARTEXT) ? " (not encrypted)" : "");
121           mutt_get_password (prompt, authpass, sizeof (authpass));
122         }
123         result[i] = authpass;
124       }
125     }
126   }
127
128   return 1;
129 }
130
131 #define BUFLEN 8192
132
133 static const char *_mutt_libesmtp_messagefp_cb (void **buf, int *len,
134                                                 void *arg)
135 {
136   int octets;
137
138   if (*buf == NULL)
139     *buf = malloc (BUFLEN);
140
141   if (len == NULL) {
142     rewind ((FILE *) arg);
143     return NULL;
144   }
145
146   if (fgets (*buf, BUFLEN - 2, (FILE *) arg) == NULL) {
147     octets = 0;
148   }
149   else {
150     char *p = strchr (*buf, '\0');
151
152     if (p[-1] == '\n' && p[-2] != '\r') {
153       strcpy (p - 1, "\r\n");
154       p++;
155     }
156     octets = p - (char *) *buf;
157   }
158
159   *len = octets;
160   return *buf;
161 }
162
163 /*
164  * mutt_invoke_libesmtp
165  *   Sends a mail message to the provided recipients using libesmtp.
166  *   Returns 0 upon success, -1 upon failure (and prints an error
167  *   message).
168  */
169 int mutt_invoke_libesmtp (ADDRESS * from,       /* the sender */
170                           ADDRESS * to, ADDRESS * cc, ADDRESS * bcc,    /* recips */
171                           const char *msg,      /* file containing message */
172                           int eightbit)
173 {                               /* message contains 8bit chars */
174   int ret = 0;                  /* return value, default = success */
175   smtp_session_t session;
176   smtp_message_t message;
177   char *hostportstr = NULL;
178   size_t hostportlen;
179   FILE *fp = NULL;
180   auth_context_t authctx = NULL;
181   const smtp_status_t *status;
182
183   _mutt_libesmtp_ensure_init ();
184
185   if ((session = smtp_create_session ()) == NULL)
186     SMTPFAIL ("smtp_create_session");
187
188   /* Create hostname:port string and tell libesmtp */
189   /* len = SmtpHost len + colon + max port (65536 => 5 chars) + terminator */
190   hostportlen = safe_strlen (SmtpHost) + 7;
191   hostportstr = safe_malloc (hostportlen);
192   snprintf (hostportstr, hostportlen, "%s:%d", SmtpHost, SmtpPort);
193   if (!smtp_set_server (session, hostportstr))
194     SMTPFAIL ("smtp_set_server");
195
196   if (SmtpAuthUser) {
197     if ((authctx = auth_create_context ()) == NULL)
198       MSGFAIL ("auth_create_context failed");
199     auth_set_mechanism_flags (authctx, AUTH_PLUGIN_PLAIN, 0);
200     auth_set_interact_cb (authctx, _mutt_libesmtp_auth_interact, NULL);
201
202     if (!smtp_auth_set_context (session, authctx))
203       SMTPFAIL ("smtp_auth_set_context");
204   }
205
206   if ((message = smtp_add_message (session)) == NULL)
207     SMTPFAIL ("smtp_add_message");
208   /*  Initialize envelope sender */
209   if (!smtp_set_reverse_path (message, from->mailbox))
210     SMTPFAIL ("smtp_set_reverse_path");
211
212   if ((fp = fopen (msg, "r")) == NULL)
213     LIBCFAIL ("fopen");
214   if (!smtp_set_messagecb (message, _mutt_libesmtp_messagefp_cb, fp))
215     SMTPFAIL ("smtp_set_messagecb");
216   if (_mutt_libesmtp_add_recipients (message, to))
217     FAIL ();
218   if (_mutt_libesmtp_add_recipients (message, cc))
219     FAIL ();
220   if (_mutt_libesmtp_add_recipients (message, bcc))
221     FAIL ();
222   if (!smtp_start_session (session))
223     SMTPFAIL ("smtp_start_session");
224
225   status = smtp_message_transfer_status (message);
226   if (status->code < 200 || status->code > 299) {
227     char buf[256];
228
229     snprintf (buf, sizeof (buf), "SMTP error while sending: %d %s",
230               status->code, status->text);
231     MSGFAIL (buf);
232   }
233
234 Done:
235   if (fp != NULL)
236     fclose (fp);
237   if (hostportstr != NULL)
238     free (hostportstr);
239   if (session != NULL)
240     smtp_destroy_session (session);
241   if (authctx != NULL)
242     auth_destroy_context (authctx);
243
244   /* Forget user-entered SMTP AUTH password if send fails */
245   if (ret != 0)
246     authpass[0] = '\0';
247
248   return ret;
249 }