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

Contents of /imapfilter/request.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.32.2.8 - (show annotations)
Fri Mar 28 16:57:52 2003 UTC (21 years ago) by lefcha
Branch: release-0_8-patches
Changes since 1.32.2.7: +16 -18 lines
File MIME type: text/plain
Correct bug with long headers along with action list and logger.

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

webmaster@linux.gr
ViewVC Help
Powered by ViewVC 1.1.26