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