/[imapfilter]/imapfilter/request.c
ViewVC logotype

Contents of /imapfilter/request.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.25 - (show annotations)
Sat Jul 13 14:11:08 2002 UTC (21 years, 8 months ago) by lefcha
Branch: MAIN
Changes since 1.24: +50 -51 lines
File MIME type: text/plain
Added variable to enable direct expunge of marked deleted mail.

1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <errno.h>
5
6 #include "config.h"
7 #include "imapfilter.h"
8 #include "data.h"
9
10
11 extern int sockpri, sockaux;
12 extern unsigned int options;
13 extern unsigned int capabilities;
14
15 namesp_t nsppri, nspaux; /* Primary and auxiliary namespace. */
16
17
18 #ifdef DEBUG
19 /*
20 * Test/ping server.
21 */
22 int test(int *sock)
23 {
24 return server_response(sock, imap_noop(sock));
25 }
26
27 #endif
28
29
30 /*
31 * Check server's capabilities.
32 */
33 int check_capabilities(int *sock)
34 {
35 capabilities = CAPABILITY_NONE;
36
37 return capability_response(sock, imap_capability(sock));
38 }
39
40
41 /*
42 * Get namespace of mail server's mailboxes.
43 */
44 int check_namespace(int *sock, namesp_t * nsp)
45 {
46 nsp->prefix[0] = nsp->delim = 0;
47
48 if (!(options & OPTION_NAMESPACE) ||
49 !(capabilities & CAPABILITY_NAMESPACE))
50 return 0;
51 else
52 return namespace_response(sock, imap_namespace(sock), nsp);
53 }
54
55
56 /*
57 * Login to server.
58 */
59 int login(int *sock, char *user, char *pass)
60 {
61 log_info(LOG_USERNAME, user);
62
63 return server_response(sock, imap_login(sock, user, pass));
64 }
65
66
67
68 /*
69 * Check if a mailbox exists.
70 */
71 int check_mailbox(int *sock, char *mbox, namesp_t * nsp)
72 {
73 return server_response(sock, imap_examine(sock,
74 apply_namespace(mbox,
75 nsp->prefix,
76 nsp->delim)));
77 }
78
79
80 /*
81 * Open mailbox in read-write mode.
82 */
83 int select_mailbox(int *sock, char *mbox, namesp_t * nsp)
84 {
85 int r;
86
87 if (mailbox_status(sock, mbox, nsp) == -2)
88 return -2; /* No messages exist. No filters need to be
89 applied. */
90
91 r = select_response(sock, imap_select(sock,
92 apply_namespace(mbox, nsp->prefix,
93 nsp->delim)));
94
95 log_info(LOG_MAILBOX, mbox);
96
97 return r;
98 }
99
100
101 /*
102 * Get mailbox's status.
103 */
104 int mailbox_status(int *sock, char *mbox, namesp_t * nsp)
105 {
106 return status_response(sock, imap_status(sock,
107 apply_namespace(mbox, nsp->prefix,
108 nsp->delim),
109 "MESSAGES RECENT UNSEEN"), mbox);
110 }
111
112
113 /*
114 * Close examined/selected mailbox.
115 */
116 int close_mailbox(int *sock)
117 {
118 return server_response(sock, imap_close(sock));
119 }
120
121
122 /*
123 * Logout from server.
124 */
125 int logout(int *sock)
126 {
127 return server_response(sock, imap_logout(sock));
128 }
129
130
131 /*
132 * Match and apply filters assigned to a mailbox.
133 */
134 int apply_filters(filter_t ** filters)
135 {
136 int i;
137 char *mesgs;
138
139 for (i = 0; filters[i]; i++) {
140 mesgs = NULL;
141
142 if (match_filter(filters[i], &mesgs))
143 continue;
144
145 log_info(LOG_FILTER, filters[i]->key);
146
147 apply_action(mesgs, &(filters[i]->action.type),
148 filters[i]->action.raccount, filters[i]->action.destmbox,
149 filters[i]->action.args);
150
151 xfree(mesgs);
152 }
153
154 return 0;
155 }
156
157
158 /*
159 * Generate the search request by the masks of the filter and try to
160 * match the generated filter.
161 */
162 int match_filter(filter_t * filter, char **mesgs)
163 {
164 char *search;
165
166 if (filter->mode == FILTER_MODE_OR)
167 search = generate_filter_or(filter->masks, filter->masknum,
168 filter->masklen);
169 else
170 search = generate_filter_and(filter->masks, filter->masknum,
171 filter->masklen);
172
173 search_response(&sockpri, imap_search(&sockpri, search), mesgs);
174
175 xfree(search);
176
177 if (!*mesgs)
178 return 1;
179
180 return 0;
181 }
182
183
184 /*
185 * Empty the FIFO inventory.
186 */
187 void empty_fifo(mask_t ** mfifo)
188 {
189 mfifo[0] = NULL;
190
191 queue_fifo(NULL, NULL);
192 dequeue_fifo(NULL);
193 }
194
195
196 /*
197 * Add item to FIFO inventory.
198 */
199 void queue_fifo(mask_t ** mfifo, mask_t * mask)
200 {
201 static unsigned int i;
202
203 if (!mfifo) {
204 i = 0;
205 return;
206 }
207 mfifo[i++] = mask;
208 mfifo[i] = NULL;
209 }
210
211
212 /*
213 * Get next item from FIFO inventory.
214 */
215 mask_t *dequeue_fifo(mask_t ** mfifo)
216 {
217 static unsigned int j;
218
219 if (!mfifo) {
220 j = 0;
221 return NULL;
222 }
223 return mfifo[j++];
224 }
225
226
227 /*
228 * Generate the filter search command from the masks, assuming that
229 * masks are AND-ed.
230 */
231 char *generate_filter_and(mask_t * mask, unsigned int masknum,
232 unsigned int masklen)
233 {
234 const unsigned int searchbuf = masklen + masknum * 6 + 8;
235 unsigned int len = 0;
236 char *search;
237 mask_t *tmp;
238
239 search = (char *)xmalloc(sizeof(char) * searchbuf);
240
241 search[0] = 0;
242
243 tmp = mask;
244 if (!tmp) {
245 strncat(search, "ALL ", searchbuf - len - 1);
246 len += 4;
247 } else
248 while ((tmp = tmp->next)) {
249 if (tmp->type != MASK_TYPE_OR) {
250 strncat(search, "ALL ", searchbuf - len - 1);
251 len += 4;
252 break;
253 }
254 }
255
256 tmp = NULL;
257 while (mask) {
258 tmp = mask;
259 mask = mask->next;
260
261 if (mask && mask->type == MASK_TYPE_OR) {
262 strncat(search, "OR (", searchbuf - len - 1);
263 len += 4;
264
265 strncat(search, tmp->body, searchbuf - len - 1);
266 len = strlen(search);
267 search[len] = ' ';
268 search[++len] = 0;
269
270 search[len - 1] = ')';
271 search[len] = ' ';
272 search[++len] = 0;
273
274 if (!mask->next || mask->next->type != MASK_TYPE_OR) {
275 search[len] = '(';
276 search[++len] = 0;
277 strncat(search, mask->body, searchbuf - len - 1);
278 len = strlen(search);
279 search[len] = ')';
280 search[++len] = ' ';
281 search[++len] = 0;
282 mask = mask->next;
283 }
284 } else {
285 strncat(search, tmp->body, searchbuf - len - 1);
286 len = strlen(search);
287 search[len] = ' ';
288 search[++len] = 0;
289 }
290 }
291
292 search[len - 1] = 0;
293
294 return search;
295 }
296
297
298 /*
299 * Generate the filter search command from the masks, assuming that
300 * masks are OR-ed.
301 */
302 char *generate_filter_or(mask_t * mask, unsigned int masknum,
303 unsigned int masklen)
304 {
305 const unsigned int searchbuf = masklen + masknum * 6 + 8;
306 unsigned int len = 0;
307 char *search;
308 mask_t **mfifo; /* Mailbox FIFO queue. */
309 mask_t *mf; /* Mask returned from FIFO. */
310
311 search = (char *)xmalloc(sizeof(char) * searchbuf);
312 mfifo = (mask_t **) xmalloc(sizeof(mask_t *) * (masknum + 1));
313
314 search[0] = 0;
315 empty_fifo(mfifo);
316
317 strncat(search, "ALL ", searchbuf - len - 1);
318 len += 4;
319
320 while (mask) {
321 queue_fifo(mfifo, mask);
322 mask = mask->next;
323
324 while (mask && mask->type == MASK_TYPE_AND) {
325 queue_fifo(mfifo, mask);
326 mask = mask->next;
327 }
328
329 if (mask) {
330 if (len == 4 && search[0] == 'A')
331 search[0] = len = 0;
332
333 strncat(search, "OR ", searchbuf - len - 1);
334 len += 3;
335 }
336 if (search[0] != 'A') {
337 search[len] = '(';
338 search[++len] = 0;
339 }
340 while ((mf = dequeue_fifo(mfifo))) {
341 strncat(search, mf->body, searchbuf - len - 1);
342 len = strlen(search);
343 search[len] = ' ';
344 search[++len] = 0;
345 }
346
347 if (strchr(search, '(')) {
348 search[len - 1] = ')';
349 search[len] = ' ';
350 search[++len] = 0;
351 }
352 empty_fifo(mfifo);
353 }
354
355 search[len - 1] = 0;
356
357 xfree(mfifo);
358
359 return search;
360 }
361
362
363 /*
364 * Apply the appropriate action.
365 */
366 int apply_action(char *mesgs, unsigned int *type, account_t * raccount,
367 char *destmbox, char *args)
368 {
369 unsigned int cnt;
370
371 if (!*mesgs)
372 return 0;
373
374 log_info(LOG_ACTION, type);
375 log_info(LOG_DESTINATION_MAILBOX, destmbox);
376
377 cnt = count_messages(mesgs);
378
379 switch (*type) {
380 case FILTER_ACTION_DELETE:
381 info("%d message%s deleted.\n", cnt, plural(cnt));
382 action_delete(mesgs, args);
383 break;
384 case FILTER_ACTION_COPY:
385 info("%d message%s copied to mailbox \"%s\".\n", cnt, plural(cnt),
386 destmbox);
387 action_copy(mesgs, apply_namespace(destmbox, nsppri.prefix,
388 nsppri.delim), args);
389 break;
390 case FILTER_ACTION_MOVE:
391 info("%d message%s moved to mailbox \"%s\".\n", cnt, plural(cnt),
392 destmbox);
393 action_move(mesgs, apply_namespace(destmbox, nsppri.prefix,
394 nsppri.delim), args);
395 break;
396 case FILTER_ACTION_RCOPY:
397 info("%d message%s copied to mailbox \"%s\" at account %s\n", cnt,
398 plural(cnt), destmbox, raccount->key);
399 action_rcopy(mesgs, raccount, destmbox, args);
400 break;
401 case FILTER_ACTION_RMOVE:
402 info("%d messages%s moved to mailbox \"%s\" at account %s\n", cnt,
403 plural(cnt), destmbox, raccount->key);
404 action_rmove(mesgs, raccount, destmbox, args);
405 break;
406 case FILTER_ACTION_LIST:
407 info("%d message%s listed.\n", cnt, plural(cnt));
408 action_list(mesgs, args);
409 break;
410 }
411
412 if (!*args)
413 log_info(LOG_WRITE, NULL);
414
415 return 0;
416 }
417
418
419 /*
420 * Delete messages and optionally list some of their headers.
421 */
422 int action_delete(char *mesgs, char *args)
423 {
424 char *tok, *mcp, *m, *cm;
425
426 action_list(mesgs, args);
427
428 cm = convert_messages(mesgs);
429
430 m = mcp = xstrdup(cm);
431
432 while ((tok = strsep(&m, " ")))
433 server_response(&sockpri, imap_store(&sockpri, tok, "\\Deleted"));
434
435 if (options & OPTION_EXPUNGE)
436 server_response(&sockpri, imap_expunge(&sockpri));
437
438 xfree(cm);
439 xfree(mcp);
440
441 return 0;
442 }
443
444
445 /*
446 * Copy messages to specified mailbox.
447 */
448 int action_copy(char *mesgs, char *destmbox, char *args)
449 {
450 int r = 0;
451 char *tok = NULL, *mcp, *m, *cm;
452
453 action_list(mesgs, args);
454
455 cm = convert_messages(mesgs);
456
457 m = mcp = xstrdup(cm);
458
459 while ((tok = strsep(&m, " "))) {
460 if ((r = copy_response(&sockpri,
461 imap_copy(&sockpri, tok, destmbox))) ==
462 RESPONSE_TRYCREATE)
463 if (!server_response(&sockpri, imap_create(&sockpri, destmbox))) {
464 server_response(&sockpri, imap_subscribe(&sockpri, destmbox));
465 r = copy_response(&sockpri, imap_copy(&sockpri, tok, destmbox));
466 }
467 }
468
469 xfree(cm);
470 xfree(mcp);
471
472 return r;
473 }
474
475
476 /*
477 * Move messages to specified mailbox.
478 */
479 int action_move(char *mesgs, char *destmbox, char *args)
480 {
481 if (!action_copy(mesgs, destmbox, args))
482 action_delete(mesgs, "\0");
483
484 return 0;
485 }
486
487
488 /*
489 * Copy messages to the specified mailbox of another mail server.
490 */
491 int action_rcopy(char *mesgs, account_t * destacc, char *destmbox, char *args)
492 {
493 int r;
494 char *tok, *m, *mcp, *ndm;
495 unsigned int n, t = 0;
496 char buf[RESPONSE_BUF];
497
498 if (init_connection(&sockaux, destacc->server, destacc->port,
499 destacc->ssl))
500 return ERROR_NETWORK;
501
502 r = greeting_response(&sockaux);
503
504 if (r == RESPONSE_BYE || check_capabilities(&sockaux))
505 return ERROR_NETWORK;
506
507 #ifdef DEBUG
508 test(&sockaux);
509 #endif
510
511 if (r != RESPONSE_PREAUTH) {
512 if (destacc->passwdattr == PASSWORD_NONE) {
513 printf("Enter password for %s@%s: ", destacc->username,
514 destacc->server);
515 get_password(destacc->password, PASSWORD_LEN);
516 destacc->passwdattr = PASSWORD_PLAIN;
517 }
518 if (login(&sockaux, destacc->username,
519 destacc->password) == RESPONSE_NO) {
520 error("imapfilter: username %s or password rejected at %s\n",
521 destacc->username, destacc->server);
522 return ERROR_NETWORK;
523 }
524 }
525 check_namespace(&sockaux, &nspaux);
526
527 /* apply_namespace() returns a pointer to a static buffer. */
528 ndm = apply_namespace(destmbox, nspaux.prefix, nspaux.delim);
529
530 r = check_mailbox(&sockaux, ndm, &nspaux);
531
532 if (r == RESPONSE_OK)
533 close_mailbox(&sockaux);
534 else if (r == RESPONSE_NO) {
535 server_response(&sockaux, imap_create(&sockaux, ndm));
536 server_response(&sockaux, imap_subscribe(&sockaux, ndm));
537 }
538 m = mcp = xstrdup(mesgs);
539
540 while ((tok = strsep(&m, " "))) {
541 fetchsize_response(&sockpri, &n,
542 imap_fetch(&sockpri, tok, "RFC822.SIZE"));
543
544 t = imap_append(&sockaux, ndm, n);
545
546 fetch_response(&sockpri, 1, NULL, 0);
547 do {
548 r = fetch_response(&sockpri, 0, buf, imap_fetch(&sockpri, tok,
549 "RFC822.HEADER"));
550
551 socket_write(&sockaux, buf);
552 } while (r == RESPONSE_NONE);
553
554 socket_write(&sockaux, "\r\n");
555
556 fetch_response(&sockpri, 1, NULL, 0);
557 do {
558 r = fetch_response(&sockpri, 0, buf, imap_fetch(&sockpri, tok,
559 "BODY[TEXT]"));
560
561 socket_write(&sockaux, buf);
562 } while (r == RESPONSE_NONE);
563
564 socket_write(&sockaux, "\r\n\r\n");
565
566 append_response(&sockaux, t);
567 }
568
569 logout(&sockaux);
570
571 action_list(mesgs, args);
572
573 xfree(mcp);
574
575 return 0;
576 }
577
578
579 /*
580 * Move messages to the specified mailbox of another mail server.
581 */
582 int action_rmove(char *mesgs, account_t * destacc, char *destmbox, char *args)
583 {
584 if (!action_rcopy(mesgs, destacc, destmbox, args))
585 action_delete(mesgs, "\0");
586
587 return 0;
588 }
589
590
591 /*
592 * List user selected headers of messages.
593 */
594 int action_list(char *mesgs, char *args)
595 {
596 int r;
597 char *tok, *mcp, *m;
598 char s[ARGS_LEN + 27];
599 char hdrs[RESPONSE_BUF];
600
601 if (!*args)
602 return 0;
603
604 m = mcp = xstrdup(mesgs);
605
606 snprintf(s, ARGS_LEN + 27 - 1, "BODY.PEEK[HEADER.FIELDS (%s)]", args);
607
608 while ((tok = strsep(&m, " "))) {
609 /* Reset internal fetch counter. */
610 fetch_response(&sockpri, 1, NULL, 0);
611
612 do {
613 r = fetch_response(&sockpri, 0, hdrs, imap_fetch(&sockpri, tok, s));
614
615 if (*hdrs) {
616 if (options & OPTION_HEADERS)
617 info("%s\n", hdrs);
618 log_info(LOG_WRITE, hdrs);
619 } else {
620 log_info(LOG_WRITE, NULL);
621 }
622 } while (r == RESPONSE_NONE);
623 }
624
625 xfree(mcp);
626
627 return 0;
628 }
629
630
631 /*
632 * Count how many messages matched the filter.
633 */
634 unsigned int count_messages(char *mesgs)
635 {
636 unsigned int cnt = 0;
637 char *c = mesgs;
638
639 while ((c = strchr(c, ' '))) {
640 cnt++;
641 c++;
642 }
643
644 return ++cnt;
645 }
646
647
648 /*
649 * Convert messages with contiguous sequence number to the corresponding
650 * number range, eg. 1 2 3 5 7 8 --> 1:3 5 7:8
651 */
652 char *convert_messages(char *mesgs)
653 {
654 unsigned int maxlen;
655 unsigned int start, end, tmp;
656 char *c, *cp, *tail;
657
658 start = end = tmp = 0;
659 maxlen = strlen(mesgs) + 1;
660 tail = NULL;
661
662 c = cp = xstrdup(mesgs);
663
664 start = (unsigned int)strtoul(mesgs, &tail, 10);
665 end = start;
666
667 do {
668 if (tail) {
669 tmp = (unsigned int)strtoul(tail, &tail, 10);
670 if (!tmp)
671 tail = NULL;
672 }
673 if (tmp == end + 1)
674 end++;
675 else {
676 if (start == end) {
677 xstrncpy(c, ultostr(start, 10), maxlen);
678 c += strlen(c);
679 } else {
680 xstrncpy(c, ultostr(start, 10), maxlen - 1);
681 c += strlen(c);
682 *c = ':';
683 *++c = 0;
684 xstrncpy(c, ultostr(end, 10), maxlen);
685 c += strlen(c);
686 }
687
688 if (tail && c - cp < maxlen) {
689 *c = ' ';
690 *++c = 0;
691 }
692 start = end = tmp;
693 }
694 } while (tmp);
695
696 return cp;
697 }

webmaster@linux.gr
ViewVC Help
Powered by ViewVC 1.1.26