1 /* $Id$ */
2
3 /*
4 * Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include <sys/param.h>
20
21 #include <ctype.h>
22 #include <errno.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27
28 #include "fdm.h"
29 #include "deliver.h"
30 #include "fetch.h"
31
32 /*
33 * This file is a bit of a mishmash, so that we can use some bits from
34 * the IMAP fetching code.
35 *
36 * All needs to be straightened out sometime.
37 */
38
39 int deliver_imap_deliver(struct deliver_ctx *, struct actitem *);
40 void deliver_imap_desc(struct actitem *, char *, size_t);
41
42 int deliver_imap_poll(struct account *, struct io *);
43 int deliver_imap_pollto(int (*)(struct account *, struct fetch_ctx *),
44 struct account *, struct io *, struct fetch_ctx *);
45 int deliver_imap_waitokay(struct account *, struct fetch_ctx *,
46 struct io *, char **);
47 int deliver_imap_waitcontinue(struct account *, struct fetch_ctx *,
48 struct io *, char **);
49 int deliver_imap_waitappend(struct account *, struct fetch_ctx *,
50 struct io *, char **);
51
52 struct deliver deliver_imap = {
53 "imap",
54 DELIVER_ASUSER,
55 deliver_imap_deliver,
56 deliver_imap_desc
57 };
58
59 /* Poll for data from/to server. */
60 int
deliver_imap_poll(struct account * a,struct io * io)61 deliver_imap_poll(struct account *a, struct io *io)
62 {
63 char *cause;
64
65 switch (io_poll(io, conf.timeout, &cause)) {
66 case 0:
67 log_warnx("%s: connection unexpectedly closed", a->name);
68 return (1);
69 case -1:
70 log_warnx("%s: %s", a->name, cause);
71 xfree(cause);
72 return (1);
73 }
74 return (0);
75 }
76
77 /* Poll through the IMAP fetch states until a particular one is reached. */
78 int
deliver_imap_pollto(int (* state)(struct account *,struct fetch_ctx *),struct account * a,struct io * io,struct fetch_ctx * fctx)79 deliver_imap_pollto(int (*state)(struct account *, struct fetch_ctx *),
80 struct account *a, struct io *io, struct fetch_ctx *fctx)
81 {
82 while (state == NULL || fctx->state != state) {
83 switch (fctx->state(a, fctx)) {
84 case FETCH_AGAIN:
85 continue;
86 case FETCH_ERROR:
87 return (1);
88 case FETCH_EXIT:
89 return (0);
90 }
91 if (deliver_imap_poll(a, io) != 0)
92 return (1);
93 }
94 return (0);
95 }
96
97 /* Wait for okay. */
98 int
deliver_imap_waitokay(struct account * a,struct fetch_ctx * fctx,struct io * io,char ** line)99 deliver_imap_waitokay(struct account *a, struct fetch_ctx *fctx, struct io *io,
100 char **line)
101 {
102 do {
103 if (deliver_imap_poll(a, io) != 0)
104 return (1);
105 if (imap_getln(a, fctx, IMAP_TAGGED, line) != 0)
106 return (1);
107 } while (*line == NULL);
108
109 if (!imap_okay(*line)) {
110 imap_bad(a, *line);
111 return (1);
112 }
113 return (0);
114 }
115
116 /* Wait for continuation. */
117 int
deliver_imap_waitcontinue(struct account * a,struct fetch_ctx * fctx,struct io * io,char ** line)118 deliver_imap_waitcontinue(struct account *a, struct fetch_ctx *fctx,
119 struct io *io, char **line)
120 {
121 do {
122 if (deliver_imap_poll(a, io) != 0)
123 return (1);
124 if (imap_getln(a, fctx, IMAP_CONTINUE, line) != 0)
125 return (1);
126 } while (*line == NULL);
127
128 return (0);
129 }
130
131 /* Wait for append response. */
132 int
deliver_imap_waitappend(struct account * a,struct fetch_ctx * fctx,struct io * io,char ** line)133 deliver_imap_waitappend(struct account *a, struct fetch_ctx *fctx,
134 struct io *io, char **line)
135 {
136 struct fetch_imap_data *data = a->data;
137 int tag;
138
139 for (;;) {
140 if (deliver_imap_poll(a, io) != 0) {
141 line = NULL;
142 return (IMAP_TAG_ERROR);
143 }
144 if (data->getln(a, fctx, line) != 0) {
145 line = NULL;
146 return (IMAP_TAG_ERROR);
147 }
148 if (*line == NULL)
149 continue;
150
151 tag = imap_tag(*line);
152 if (tag != IMAP_TAG_NONE)
153 break;
154 }
155
156 if (tag == IMAP_TAG_CONTINUE)
157 return (IMAP_TAG_CONTINUE);
158 if (tag != data->tag)
159 return (IMAP_TAG_ERROR);
160 return (tag);
161 }
162
163 int
deliver_imap_deliver(struct deliver_ctx * dctx,struct actitem * ti)164 deliver_imap_deliver(struct deliver_ctx *dctx, struct actitem *ti)
165 {
166 struct account *a = dctx->account;
167 struct mail *m = dctx->mail;
168 struct deliver_imap_data *data = ti->data;
169 struct io *io;
170 struct fetch_ctx fctx;
171 struct fetch_imap_data fdata;
172 char *cause, *folder, *ptr, *line;
173 size_t len, maillen;
174 u_int total, body;
175
176 /* Connect to the IMAP server. */
177 io = connectproxy(&data->server,
178 conf.verify_certs, conf.proxy, IO_CRLF, conf.timeout, &cause);
179 if (io == NULL) {
180 log_warnx("%s: %s", a->name, cause);
181 xfree(cause);
182 return (DELIVER_FAILURE);
183 }
184 if (conf.debug > 3 && !conf.syslog)
185 io->dup_fd = STDOUT_FILENO;
186
187 /* Work out the folder name. */
188 folder = replacestr(&data->folder, m->tags, m, &m->rml);
189 if (folder == NULL || *folder == '\0') {
190 log_warnx("%s: empty folder", a->name);
191 goto error;
192 }
193
194 /* Fake up the fetch context for the fetch code. */
195 memset(&fdata, 0, sizeof fdata);
196 fdata.user = data->user;
197 fdata.pass = data->pass;
198 fdata.nocrammd5 = data->nocrammd5;
199 fdata.nologin = data->nologin;
200 memcpy(&fdata.server, &data->server, sizeof fdata.server);
201 fdata.io = io;
202 fdata.only = FETCH_ONLY_ALL;
203 a->data = &fdata;
204 fetch_imap_state_init(a, &fctx);
205 fctx.state = imap_state_connected;
206 fctx.llen = IO_LINESIZE;
207 fctx.lbuf = xmalloc(fctx.llen);
208
209 /* Use the fetch code until the select1 state is reached. */
210 if (deliver_imap_pollto(imap_state_select1, a, io, &fctx) != 0)
211 goto error;
212
213 retry:
214 /* Send an append command. */
215 if (imap_putln(a, "%u APPEND {%zu}", ++fdata.tag, strlen(folder)) != 0)
216 goto error;
217 switch (deliver_imap_waitappend(a, &fctx, io, &line)) {
218 case IMAP_TAG_ERROR:
219 if (line != NULL)
220 imap_invalid(a, line);
221 goto error;
222 case IMAP_TAG_CONTINUE:
223 break;
224 default:
225 if (imap_no(line) && strstr(line, "[TRYCREATE]") != NULL)
226 goto try_create;
227 imap_invalid(a, line);
228 goto error;
229 }
230
231 /*
232 * Send the mail size, not forgetting lines are CRLF terminated. The
233 * Google IMAP server is written strangely, so send the size as if
234 * every CRLF was a CR if the server has XYZZY.
235 */
236 count_lines(m, &total, &body);
237 maillen = m->size + total - 1;
238 if (fdata.capa & IMAP_CAPA_XYZZY) {
239 log_debug2("%s: adjusting size: actual %zu", a->name, maillen);
240 maillen = m->size;
241 }
242 if (fdata.capa & IMAP_CAPA_NOSPACE) {
243 if (imap_putln(a, "%s{%zu}", folder, maillen) != 0)
244 goto error;
245 } else {
246 if (imap_putln(a, "%s {%zu}", folder, maillen) != 0)
247 goto error;
248 }
249 switch (deliver_imap_waitappend(a, &fctx, io, &line)) {
250 case IMAP_TAG_ERROR:
251 if (line != NULL)
252 imap_invalid(a, line);
253 goto error;
254 case IMAP_TAG_CONTINUE:
255 break;
256 default:
257 if (imap_no(line) && strstr(line, "[TRYCREATE]") != NULL)
258 goto try_create;
259 imap_invalid(a, line);
260 goto error;
261 }
262
263 /* Send the mail data. */
264 line_init(m, &ptr, &len);
265 while (ptr != NULL) {
266 if (len > 1)
267 io_write(io, ptr, len - 1);
268 io_writeline(io, NULL);
269
270 /* Update if necessary. */
271 if (io_update(io, conf.timeout, &cause) != 1) {
272 log_warnx("%s: %s", a->name, cause);
273 xfree(cause);
274 goto error;
275 }
276
277 line_next(m, &ptr, &len);
278 }
279
280 /* Wait for an okay from the server. */
281 switch (deliver_imap_waitappend(a, &fctx, io, &line)) {
282 case IMAP_TAG_ERROR:
283 case IMAP_TAG_CONTINUE:
284 if (line != NULL)
285 imap_invalid(a, line);
286 goto error;
287 default:
288 if (imap_okay(line))
289 break;
290 if (strstr(line, "[TRYCREATE]") != NULL)
291 goto try_create;
292 imap_invalid(a, line);
293 goto error;
294 }
295
296 if (imap_putln(a, "%u LOGOUT", ++fdata.tag) != 0)
297 goto error;
298 if (deliver_imap_waitokay(a, &fctx, io, &line) != 0)
299 goto error;
300
301 xfree(fctx.lbuf);
302 xfree(folder);
303
304 fdata.disconnect(a);
305 return (DELIVER_SUCCESS);
306
307 try_create: /* XXX function? */
308 /* Try to create the folder. */
309 if (imap_putln(a, "%u CREATE {%zu}", ++fdata.tag, strlen(folder)) != 0)
310 goto error;
311 if (deliver_imap_waitcontinue(a, &fctx, io, &line) != 0)
312 goto error;
313 if (imap_putln(a, "%s", folder) != 0)
314 goto error;
315 if (deliver_imap_waitokay(a, &fctx, io, &line) != 0)
316 goto error;
317 goto retry;
318
319 error:
320 io_writeline(io, "QUIT");
321 io_flush(io, conf.timeout, NULL);
322
323 xfree(fctx.lbuf);
324 if (folder != NULL)
325 xfree(folder);
326
327 fdata.disconnect(a);
328 return (DELIVER_FAILURE);
329 }
330
331 void
deliver_imap_desc(struct actitem * ti,char * buf,size_t len)332 deliver_imap_desc(struct actitem *ti, char *buf, size_t len)
333 {
334 struct deliver_imap_data *data = ti->data;
335
336 xsnprintf(buf, len, "imap%s server \"%s\" port %s folder \"%s\"",
337 data->server.ssl ? "s" : "", data->server.host, data->server.port,
338 data->folder.str);
339 }
340