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