1 /* $Id$ */
2
3 /*
4 * Copyright (c) 2006 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/types.h>
20 #include <sys/stat.h>
21 #include <sys/time.h>
22 #include <sys/wait.h>
23
24 #include <fcntl.h>
25 #include <fnmatch.h>
26 #include <limits.h>
27 #include <string.h>
28 #include <time.h>
29 #include <unistd.h>
30
31 #include "fdm.h"
32 #include "deliver.h"
33 #include "fetch.h"
34 #include "match.h"
35
36 void fetch_status(struct account *, double);
37 int fetch_account(struct account *, struct io *, int, double);
38 int fetch_match(struct account *, struct msg *, struct msgbuf *);
39 int fetch_deliver(struct account *, struct msg *, struct msgbuf *);
40 int fetch_poll(struct account *, struct iolist *, struct io *, int);
41 int fetch_purge(struct account *);
42 void fetch_free(void);
43 void fetch_free1(struct mail_ctx *);
44
45 int fetch_enqueue(struct account *, struct io *, struct mail *);
46 int fetch_dequeue(struct account *, struct mail_ctx *);
47
48 const char *account_get_method(struct account *);
49
50 struct mail_queue fetch_matchq;
51 struct mail_queue fetch_deliverq;
52
53 u_int fetch_dropped;
54 u_int fetch_kept;
55
56 u_int fetch_queued; /* number of mails queued */
57 u_int fetch_blocked; /* blocked for parent */
58
59 int
open_cache(struct account * a,struct cache * cache)60 open_cache(struct account *a, struct cache *cache)
61 {
62 int n;
63
64 if (cache->db != NULL)
65 return (0);
66
67 if ((cache->db = db_open(cache->path)) == NULL) {
68 log_warn("%s: %s", a->name, cache->path);
69 return (-1);
70 }
71
72 n = db_size(cache->db);
73 log_debug3("%s: opened cache %s: %d keys", a->name, cache->path, n);
74
75 if (cache->expire == 0)
76 return (0);
77 if (db_expire(cache->db, cache->expire) != 0) {
78 log_warnx("%s: %s: expiry failed", a->name, cache->path);
79 return (-1);
80 }
81
82 n -= db_size(cache->db);
83 if (n < 0)
84 n = 0;
85 log_debug3("%s: cache %s: expired %d keys", a->name, cache->path, n);
86
87 return (0);
88 }
89
90 int
child_fetch(struct child * child,struct io * pio)91 child_fetch(struct child *child, struct io *pio)
92 {
93 struct child_fetch_data *data = child->data;
94 enum fdmop op = data->op;
95 struct account *a = data->account;
96 struct msg msg;
97 int error, flags;
98 double tim;
99
100 log_debug2("%s: fetch started, pid %ld", a->name, (long) getpid());
101
102 #ifdef HAVE_SETPROCTITLE
103 setproctitle("child: %s", a->name);
104 #endif
105
106 log_debug2("%s: user is %lu", a->name, (u_long) geteuid());
107 tim = get_time();
108
109 /* Process fetch or poll. */
110 log_debug2("%s: started processing", a->name);
111 flags = 0;
112 if (op == FDMOP_POLL)
113 flags |= FETCH_POLL;
114 error = fetch_account(a, pio, flags, tim);
115 log_debug2("%s: finished processing. exiting", a->name);
116
117 memset(&msg, 0, sizeof msg);
118 msg.type = MSG_EXIT;
119 log_debug3("%s: sending exit message to parent", a->name);
120 if (privsep_send(pio, &msg, NULL) != 0)
121 fatalx("privsep_send error");
122 do {
123 log_debug3("%s: waiting for exit message from parent", a->name);
124 if (privsep_recv(pio, &msg, NULL) != 0)
125 fatalx("privsep_recv error");
126 } while (msg.type != MSG_EXIT);
127
128 return (error);
129 }
130
131 int
fetch_poll(struct account * a,struct iolist * iol,struct io * pio,int timeout)132 fetch_poll(struct account *a, struct iolist *iol, struct io *pio, int timeout)
133 {
134 struct io *rio;
135 char *cause;
136 double tim;
137
138 log_debug3(
139 "%s: polling: %u, timeout=%d", a->name, ARRAY_LENGTH(iol), timeout);
140 tim = get_time();
141 switch (io_polln(
142 ARRAY_DATA(iol), ARRAY_LENGTH(iol), &rio, timeout, &cause)) {
143 case 0:
144 if (rio == pio)
145 fatalx("parent socket closed");
146 log_warnx("%s: connection closed", a->name);
147 return (-1);
148 case -1:
149 if (errno == EAGAIN)
150 break;
151 if (rio == pio)
152 fatalx("parent socket error");
153 log_warnx("%s: %s", a->name, cause);
154 xfree(cause);
155 return (-1);
156 }
157 tim = get_time() - tim;
158
159 return (0);
160 }
161
162 int
fetch_match(struct account * a,struct msg * msg,struct msgbuf * msgbuf)163 fetch_match(struct account *a, struct msg *msg, struct msgbuf *msgbuf)
164 {
165 struct mail_ctx *mctx, *this;
166
167 if (TAILQ_EMPTY(&fetch_matchq))
168 return (0);
169
170 mctx = TAILQ_FIRST(&fetch_matchq);
171 while (mctx != NULL) {
172 this = mctx;
173 mctx = TAILQ_NEXT(this, entry);
174
175 log_debug3("%s: "
176 "trying (match) message %u", a->name, this->mail->idx);
177 switch (mail_match(this, msg, msgbuf)) {
178 case MAIL_ERROR:
179 log_debug3("%s: match"
180 " message %u, error", a->name, this->mail->idx);
181 return (-1);
182 case MAIL_DELIVER:
183 log_debug3("%s: match"
184 " message %u, deliver", a->name, this->mail->idx);
185 TAILQ_REMOVE(&fetch_matchq, this, entry);
186 TAILQ_INSERT_TAIL(&fetch_deliverq, this, entry);
187 break;
188 case MAIL_DONE:
189 log_debug3("%s: match"
190 " message %u, done", a->name, this->mail->idx);
191 if (fetch_dequeue(a, this) != 0)
192 return (-1);
193 break;
194 case MAIL_BLOCKED:
195 log_debug3("%s: match"
196 " message %u, blocked", a->name, this->mail->idx);
197 fetch_blocked++;
198 break;
199 }
200 }
201
202 return (0);
203 }
204
205 int
fetch_deliver(struct account * a,struct msg * msg,struct msgbuf * msgbuf)206 fetch_deliver(struct account *a, struct msg *msg, struct msgbuf *msgbuf)
207 {
208 struct mail_ctx *mctx, *this;
209
210 if (TAILQ_EMPTY(&fetch_deliverq))
211 return (0);
212
213 mctx = TAILQ_FIRST(&fetch_deliverq);
214 while (mctx != NULL) {
215 this = mctx;
216 mctx = TAILQ_NEXT(this, entry);
217
218 log_debug3("%s:"
219 " trying (deliver) message %u", a->name, this->mail->idx);
220 switch (mail_deliver(this, msg, msgbuf)) {
221 case MAIL_ERROR:
222 log_debug3("%s: deliver"
223 " message %u, error", a->name, this->mail->idx);
224
225 if (conf.ignore_errors) {
226 log_warnx("%s: fetching error. ignored",
227 a->name);
228
229 TAILQ_REMOVE(&fetch_deliverq, this, entry);
230 TAILQ_INSERT_TAIL(&fetch_matchq, this, entry);
231
232 this->mail->decision = DECISION_KEEP;
233 if (fetch_dequeue(a, this) != 0)
234 return (-1);
235 break;
236 }
237
238 return (-1);
239 case MAIL_MATCH:
240 log_debug3("%s: deliver"
241 " message %u, match", a->name, this->mail->idx);
242 TAILQ_REMOVE(&fetch_deliverq, this, entry);
243 TAILQ_INSERT_TAIL(&fetch_matchq, this, entry);
244 break;
245 case MAIL_BLOCKED:
246 log_debug3("%s: deliver"
247 " message %u, blocked", a->name, this->mail->idx);
248 fetch_blocked++;
249 break;
250 }
251 }
252
253 return (0);
254 }
255
256 void
fetch_free1(struct mail_ctx * mctx)257 fetch_free1(struct mail_ctx *mctx)
258 {
259 struct deliver_ctx *dctx;
260
261 while (!TAILQ_EMPTY(&mctx->dqueue)) {
262 dctx = TAILQ_FIRST(&mctx->dqueue);
263 TAILQ_REMOVE(&mctx->dqueue, dctx, entry);
264 user_free(dctx->udata);
265 xfree(dctx);
266 }
267
268 ARRAY_FREE(&mctx->stack);
269 mail_destroy(mctx->mail);
270 xfree(mctx->mail);
271 xfree(mctx);
272 }
273
274 void
fetch_free(void)275 fetch_free(void)
276 {
277 struct mail_ctx *mctx;
278
279 while (!TAILQ_EMPTY(&fetch_matchq)) {
280 mctx = TAILQ_FIRST(&fetch_matchq);
281 TAILQ_REMOVE(&fetch_matchq, mctx, entry);
282 fetch_free1(mctx);
283 }
284
285 while (!TAILQ_EMPTY(&fetch_deliverq)) {
286 mctx = TAILQ_FIRST(&fetch_deliverq);
287 TAILQ_REMOVE(&fetch_deliverq, mctx, entry);
288 fetch_free1(mctx);
289 }
290 }
291
292 int
fetch_purge(struct account * a)293 fetch_purge(struct account *a)
294 {
295 static u_int last_total = 0, last_dropped = 0;
296 u_int n;
297
298 if (conf.purge_after == 0)
299 return (0);
300
301 n = fetch_dropped + fetch_kept;
302 if (n == last_total || n % conf.purge_after != 0)
303 return (0);
304 last_total = n;
305
306 if (last_dropped == fetch_dropped) {
307 log_debug("%s: not purging, no mails dropped", a->name);
308 return (0);
309 }
310 last_dropped = fetch_dropped;
311
312 log_debug("%s: purging after %u mails", a->name, n);
313 return (1);
314 }
315
316 void
fetch_status(struct account * a,double tim)317 fetch_status(struct account *a, double tim)
318 {
319 u_int n;
320
321 tim = get_time() - tim;
322 n = fetch_dropped + fetch_kept;
323 if (n > 0) {
324 log_info("%s: %u messages processed (%u kept) in %.3f seconds "
325 "(average %.3f)", a->name, n, fetch_kept, tim, tim / n);
326 } else {
327 log_info("%s: 0 messages processed in %.3f seconds",
328 a->name, tim);
329 }
330 }
331
332 int
fetch_account(struct account * a,struct io * pio,int nflags,double tim)333 fetch_account(struct account *a, struct io *pio, int nflags, double tim)
334 {
335 struct msg msg, *msgp;
336 struct msgbuf msgbuf;
337 struct fetch_ctx fctx;
338 struct cache *cache;
339 struct iolist iol;
340 int aborted, complete, holding, timeout;
341
342 log_debug2("%s: fetching", a->name);
343
344 TAILQ_INIT(&fetch_matchq);
345 TAILQ_INIT(&fetch_deliverq);
346 fetch_queued = fetch_dropped = fetch_kept = 0;
347
348 if (nflags & FETCH_POLL && a->fetch->total == NULL) {
349 log_info("%s: polling not supported", a->name);
350 return (0);
351 }
352
353 fctx.llen = IO_LINESIZE;
354 fctx.lbuf = xmalloc(fctx.llen);
355 fctx.flags = nflags;
356
357 fctx.mail = xcalloc(1, sizeof *fctx.mail);
358 fctx.state = a->fetch->first;
359
360 ARRAY_INIT(&iol);
361
362 aborted = complete = holding = 0;
363 for (;;) {
364 log_debug3("%s: fetch loop start", a->name);
365
366 if (sigusr1) {
367 log_debug("%s: caught SIGUSR1", a->name);
368 if (!(nflags & FETCH_POLL))
369 fetch_status(a, tim);
370 sigusr1 = 0;
371 }
372
373 fetch_blocked = 0;
374
375 /* Check for new privsep messages. */
376 msgp = NULL;
377 if (privsep_check(pio)) {
378 if (privsep_recv(pio, &msg, &msgbuf) != 0)
379 fatalx("privsep_recv error");
380 log_debug3("%s: got message type %d, id %u", a->name,
381 msg.type, msg.id);
382 msgp = &msg;
383 }
384
385 /* Match and deliver mail. */
386 if (fetch_match(a, msgp, &msgbuf) != 0)
387 goto abort;
388 if (fetch_deliver(a, msgp, &msgbuf) != 0)
389 goto abort;
390
391 /* Check for purge and set flag if necessary. */
392 if (fetch_purge(a))
393 fctx.flags |= FETCH_PURGE;
394
395 /* Update the holding flag. */
396 if (fetch_queued <= (u_int) conf.queue_low)
397 holding = 0;
398 if (fetch_queued >= (u_int) conf.queue_high)
399 holding = 1;
400
401 /* If not holding and not finished, call the fetch handler. */
402 if (!holding && !complete) {
403 /*
404 * Set the empty flag if queues are empty. Purging
405 * shouldn't happen if this is clear.
406 */
407 fctx.flags &= ~FETCH_EMPTY;
408 if (fetch_queued == 0)
409 fctx.flags |= FETCH_EMPTY;
410
411 /* Call the fetch function. */
412 log_debug3("%s: calling fetch state (%p, flags 0x%02x)",
413 a->name, fctx.state, fctx.flags);
414 switch (fctx.state(a, &fctx)) {
415 case FETCH_ERROR:
416 /* Fetch error. */
417 log_debug3("%s: fetch, error", a->name);
418 goto abort;
419 case FETCH_EXIT:
420 /* Fetch completed. */
421 log_debug3("%s: fetch, exit", a->name);
422 complete = 1;
423 break;
424 case FETCH_AGAIN:
425 /* Fetch again - no blocking. */
426 log_debug3("%s: fetch, again", a->name);
427 continue;
428 case FETCH_BLOCK:
429 /* Fetch again - allow blocking. */
430 log_debug3("%s: fetch, block", a->name);
431 break;
432 case FETCH_MAIL:
433 /* Mail ready. */
434 log_debug3("%s: fetch, mail", a->name);
435 if (fetch_enqueue(a, pio, fctx.mail) != 0)
436 goto abort;
437 fctx.mail = xcalloc(1, sizeof *fctx.mail);
438 continue;
439 default:
440 fatalx("unexpected fetch return");
441 }
442 }
443
444 /* If fetch finished and no more mails queued, exit. */
445 if (complete && fetch_queued == 0)
446 goto finished;
447
448 /* Prepare for poll. */
449 ARRAY_CLEAR(&iol);
450 ARRAY_ADD(&iol, pio);
451 if (a->fetch->fill != NULL)
452 a->fetch->fill(a, &iol);
453
454 /*
455 * Work out timeout. If the queues are empty, we can block,
456 * unless this fetch type doesn't have any sockets to poll -
457 * then we would block forever. Otherwise, if the queues are
458 * non-empty, we can block unless there are mails that aren't
459 * blocked (these mails can continue to be processed).
460 */
461 timeout = conf.timeout;
462 if (fetch_queued == 0 && ARRAY_LENGTH(&iol) == 1)
463 timeout = 0;
464 else if (fetch_queued != 0 && fetch_blocked != fetch_queued)
465 timeout = 0;
466
467 /* Poll for fetch data or privsep messages. */
468 log_debug3("%s: queued %u; blocked %u; flags 0x%02x", a->name,
469 fetch_queued, fetch_blocked, fctx.flags);
470 if (fetch_poll(a, &iol, pio, timeout) != 0)
471 goto abort;
472 }
473
474 abort:
475 a->fetch->abort(a);
476
477 if (nflags & FETCH_POLL)
478 log_warnx("%s: polling error. aborted", a->name);
479 else
480 log_warnx("%s: fetching error. aborted", a->name);
481
482 aborted = 1;
483
484 finished:
485 if (fctx.mail != NULL) {
486 mail_destroy(fctx.mail);
487 xfree(fctx.mail);
488 }
489
490 xfree(fctx.lbuf);
491 fetch_free();
492 ARRAY_FREE(&iol);
493
494 /* Close caches. */
495 TAILQ_FOREACH(cache, &conf.caches, entry) {
496 if (cache->db != NULL)
497 db_close(cache->db);
498 }
499
500 /* Print results. */
501 if (nflags & FETCH_POLL)
502 log_info("%s: %u messages found", a->name, a->fetch->total(a));
503 else
504 fetch_status(a, tim);
505 return (aborted);
506 }
507
508 /*
509 * Returns a string describing the connection method, including cipher used (if
510 * any).
511 */
512 const char *
account_get_method(struct account * a)513 account_get_method(struct account *a)
514 {
515 struct fetch_imap_data *idata;
516 struct fetch_pop3_data *pdata;
517 const SSL_CIPHER *cipher = NULL;
518 static char s[128];
519 char tmp[128];
520
521 if (a->fetch == &fetch_imap) {
522 idata = a->data;
523 if (idata->io->ssl != NULL)
524 cipher = SSL_get_current_cipher(idata->io->ssl);
525 } else if (a->fetch == &fetch_pop3) {
526 pdata = a->data;
527 if (pdata->io->ssl != NULL)
528 cipher = SSL_get_current_cipher(pdata->io->ssl);
529 }
530 if (cipher != NULL) {
531 snprintf(tmp, sizeof tmp, "version=%s %s %d bits",
532 SSL_CIPHER_get_version(cipher),
533 SSL_CIPHER_get_name(cipher),
534 SSL_CIPHER_get_bits(cipher, NULL));
535 } else
536 snprintf(tmp, sizeof tmp, "unencrypted");
537
538 snprintf(s, sizeof s, "with %s (%s)", a->fetch->name, tmp);
539 return (s);
540 }
541
542 /*
543 * Check mail for various problems, add headers and fill tags, then create an
544 * mctx and enqueue it onto the fetch queue.
545 */
546 int
fetch_enqueue(struct account * a,struct io * pio,struct mail * m)547 fetch_enqueue(struct account *a, struct io *pio, struct mail *m)
548 {
549 struct mail_ctx *mctx;
550 char *hdr, rtime[128], *rhost, total[16];
551 u_int n, b;
552 size_t size;
553 int error;
554 struct tm *tm;
555 time_t t;
556 const char *tptr;
557
558 /*
559 * Check for oversize mails. This must be first since there is no
560 * guarantee anything other than size is valid if oversize.
561 */
562 if (m->size > conf.max_size) {
563 log_warnx("%s: message too big: %zu bytes", a->name, m->size);
564 if (!conf.del_big)
565 return (-1);
566
567 /* Delete the mail. */
568 m->decision = DECISION_DROP;
569 if (a->fetch->commit != NULL &&
570 a->fetch->commit(a, m) == FETCH_ERROR)
571 return (-1);
572
573 mail_destroy(m);
574 xfree(m);
575 return (0);
576 }
577
578 /*
579 * Find the mail body (needed by trim_from). This is probably slower
580 * than doing it during fetching but it guarantees consistency.
581 */
582 m->body = find_body(m);
583
584 /* Trim "From" line, if any. */
585 trim_from(m);
586
587 /* Check for empty mails. */
588 if (m->size == 0) {
589 log_warnx("%s: empty message", a->name);
590 return (-1);
591 }
592
593 /* Fill in standard mail attributes. */
594 m->decision = DECISION_DROP;
595 m->idx = ++a->idx;
596 m->tim = get_time();
597
598 /* Add account name tag. */
599 add_tag(&m->tags, "account", "%s", a->name);
600
601 /* Add mail time tags. */
602 if (mailtime(m, &t) != 0) {
603 log_debug2("%s: bad date header, using current time", a->name);
604 t = time(NULL);
605 }
606 if ((tm = localtime(&t)) != NULL) {
607 add_tag(&m->tags, "mail_hour", "%.2d", tm->tm_hour);
608 add_tag(&m->tags, "mail_minute", "%.2d", tm->tm_min);
609 add_tag(&m->tags, "mail_second", "%.2d", tm->tm_sec);
610 add_tag(&m->tags, "mail_day", "%.2d", tm->tm_mday);
611 add_tag(&m->tags, "mail_month", "%.2d", tm->tm_mon + 1);
612 add_tag(&m->tags, "mail_year", "%.4d", 1900 + tm->tm_year);
613 add_tag(&m->tags, "mail_year2", "%.2d", tm->tm_year % 100);
614 add_tag(&m->tags, "mail_dayofweek", "%d", tm->tm_wday);
615 add_tag(&m->tags, "mail_dayofyear", "%.2d", tm->tm_yday + 1);
616 add_tag(&m->tags,
617 "mail_quarter", "%d", tm->tm_mon / 3 + 1);
618 }
619 if (rfc822time(t, rtime, sizeof rtime) != NULL)
620 add_tag(&m->tags, "mail_rfc822date", "%s", rtime);
621
622 /* Fill in lines tags. */
623 count_lines(m, &n, &b);
624 log_debug2("%s: found %u lines, %u in body", a->name, n, b);
625 add_tag(&m->tags, "lines", "%u", n);
626 add_tag(&m->tags, "body_lines", "%u", b);
627 if (n - b != 0)
628 b++; /* don't include the separator */
629 add_tag(&m->tags, "header_lines", "%u", n - b);
630
631 /* Insert message-id tag. */
632 hdr = find_header(m, "message-id", &size, 1);
633 if (hdr == NULL || size == 0 || size > INT_MAX)
634 log_debug2("%s: message-id not found", a->name);
635 else {
636 log_debug2("%s: message-id is: %.*s", a->name, (int) size, hdr);
637 add_tag(&m->tags, "message_id", "%.*s", (int) size, hdr);
638 }
639
640 /*
641 * Insert received header.
642 *
643 * No header line must exceed 998 bytes. Limiting the user-supplied
644 * stuff to 900 bytes gives plenty of space for the other stuff, and if
645 * it gets truncated, who cares?
646 */
647 if (!conf.no_received) {
648 error = 1;
649 if (rfc822time(time(NULL), rtime, sizeof rtime) != NULL) {
650 rhost = conf.host_fqdn;
651 if (rhost == NULL)
652 rhost = conf.host_name;
653
654 error = insert_header(m, "received", "Received: by "
655 "%.350s (%s " VERSION ", account \"%.350s\") \n\t%s\n\t%s",
656 rhost, __progname, a->name, account_get_method(a), rtime);
657 }
658 if (error != 0)
659 log_debug3("%s: couldn't add received header", a->name);
660 }
661
662 /* Insert Gmail-specific headers. */
663 if ((tptr = find_tag(m->tags, "gmail_msgid")) != NULL &&
664 insert_header(m, "message-id", "X-GM-MSGID: %s", tptr) != 0)
665 log_warnx("%s: failed to add header X-GM-MSGID", a->name);
666 if ((tptr = find_tag(m->tags, "gmail_thrid")) != NULL &&
667 insert_header(m, "message-id", "X-GM-THRID: %s", tptr) != 0)
668 log_warnx("%s: failed to add header X-GM-THRID", a->name);
669 if ((tptr = find_tag(m->tags, "gmail_labels")) != NULL &&
670 insert_header(m, "message-id", "X-GM-LABELS: %s", tptr) != 0)
671 log_warnx("%s: failed to add header X-GM-LABELS", a->name);
672
673 /* Fill wrapped line list. */
674 n = fill_wrapped(m);
675 log_debug2("%s: found %u wrapped lines", a->name, n);
676
677 /* Create the mctx. */
678 mctx = xcalloc(1, sizeof *mctx);
679 mctx->account = a;
680 mctx->io = pio;
681 mctx->mail = m;
682 mctx->msgid = 0;
683 mctx->done = 0;
684
685 mctx->matched = 0;
686
687 mctx->rule = TAILQ_FIRST(&conf.rules);
688 TAILQ_INIT(&mctx->dqueue);
689 ARRAY_INIT(&mctx->stack);
690
691 /* And enqueue it. */
692 TAILQ_INSERT_TAIL(&fetch_matchq, mctx, entry);
693 fetch_queued++;
694
695 *total = '\0';
696 if (a->fetch->total != NULL && a->fetch->total(a) != 0)
697 xsnprintf(total, sizeof total, " of %u", a->fetch->total(a));
698 log_debug("%s: got message %u%s: size %zu, body %zu", a->name, m->idx,
699 total, m->size, m->body);
700 return (0);
701 }
702
703 /* Resolve final decision and dequeue mail. */
704 int
fetch_dequeue(struct account * a,struct mail_ctx * mctx)705 fetch_dequeue(struct account *a, struct mail_ctx *mctx)
706 {
707 struct mail *m = mctx->mail;
708
709 if (conf.keep_all || a->keep)
710 m->decision = DECISION_KEEP;
711
712 switch (m->decision) {
713 case DECISION_DROP:
714 fetch_dropped++;
715 log_debug("%s: deleting message %u", a->name, m->idx);
716 break;
717 case DECISION_KEEP:
718 fetch_kept++;
719 log_debug("%s: keeping message %u", a->name, m->idx);
720 break;
721 default:
722 fatalx("invalid decision");
723 }
724
725 if (a->fetch->commit != NULL && a->fetch->commit(a, m) == FETCH_ERROR)
726 return (-1);
727
728 TAILQ_REMOVE(&fetch_matchq, mctx, entry);
729 fetch_queued--;
730
731 fetch_free1(mctx);
732
733 return (0);
734 }
735