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