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

Contents of /imapfilter/request.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.43 - (show annotations)
Sun Feb 23 00:16:02 2003 UTC (21 years, 1 month ago) by lefcha
Branch: MAIN
Changes since 1.42: +3 -3 lines
File MIME type: text/plain
Type mismatch corrections.

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

webmaster@linux.gr
ViewVC Help
Powered by ViewVC 1.1.26