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

Contents of /imapfilter/request.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.39 - (show annotations)
Thu Dec 5 07:33:50 2002 UTC (21 years, 3 months ago) by lefcha
Branch: MAIN
Changes since 1.38: +2 -2 lines
File MIME type: text/plain
Added response when the message body is null.

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 logout_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.msgflags, 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, unsigned int *msgflags, 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 message%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_FLAG_REPLACE:
407 case FILTER_ACTION_FLAG_ADD:
408 case FILTER_ACTION_FLAG_REMOVE:
409 info("%d message%s flagged.\n", cnt, plural(cnt));
410 action_flag(mesgs, type, msgflags, args);
411 break;
412 case FILTER_ACTION_LIST:
413 info("%d message%s listed.\n", cnt, plural(cnt));
414 action_list(mesgs, args);
415 break;
416 }
417
418 if (!*args)
419 log_info(LOG_WRITE, NULL);
420
421 return 0;
422 }
423
424
425 /*
426 * Delete messages and optionally list some of their headers.
427 */
428 int action_delete(char *mesgs, char *args)
429 {
430 char *tok, *m, *mcp;
431
432 action_list(mesgs, args);
433
434 m = mcp = convert_messages(mesgs);
435
436 tok = strtok_r(m, " ", &m);
437 while (tok) {
438 server_response(&sockpri, imap_store(&sockpri, tok,
439 STORE_FLAG_ADD, "\\Deleted"));
440
441 tok = strtok_r(NULL, " ", &m);
442 }
443
444 if (options & OPTION_EXPUNGE)
445 server_response(&sockpri, imap_expunge(&sockpri));
446
447 xfree(mcp);
448
449 return 0;
450 }
451
452
453 /*
454 * Copy messages to specified mailbox.
455 */
456 int action_copy(char *mesgs, char *destmbox, char *args)
457 {
458 int r = 0;
459 char *tok = NULL, *mcp, *m;
460
461 action_list(mesgs, args);
462
463 m = mcp = convert_messages(mesgs);
464
465 tok = strtok_r(m, " ", &m);
466 while (tok) {
467 if ((r = copy_response(&sockpri,
468 imap_copy(&sockpri, tok, destmbox))) ==
469 RESPONSE_TRYCREATE)
470 if (!server_response(&sockpri, imap_create(&sockpri, destmbox))) {
471 if ((options & OPTION_SUBSCRIBE))
472 server_response(&sockpri,
473 imap_subscribe(&sockpri, destmbox));
474 r = copy_response(&sockpri,
475 imap_copy(&sockpri, tok, destmbox));
476 }
477 tok = strtok_r(NULL, " ", &m);
478 }
479
480 xfree(mcp);
481
482 return r;
483 }
484
485
486 /*
487 * Move messages to specified mailbox.
488 */
489 int action_move(char *mesgs, char *destmbox, char *args)
490 {
491 if (!action_copy(mesgs, destmbox, args))
492 action_delete(mesgs, "\0");
493
494 return 0;
495 }
496
497
498 /*
499 * Copy messages to the specified mailbox of another mail server.
500 */
501 int action_rcopy(char *mesgs, account_t * destacc, char *destmbox, char *args)
502 {
503 int r, ta, tf;
504 char *tok, *m, *mcp, *ndm;
505 unsigned int n;
506 char buf[RESPONSE_BUF * 2];
507
508 if (init_connection(&sockaux, destacc->server, destacc->port,
509 destacc->ssl))
510 return ERROR_NETWORK;
511
512 r = greeting_response(&sockaux);
513
514 if (r == RESPONSE_BYE || check_capabilities(&sockaux))
515 return ERROR_NETWORK;
516
517 #ifdef DEBUG
518 test(&sockaux);
519 #endif
520
521 if (r != RESPONSE_PREAUTH) {
522 if (destacc->passwdattr == PASSWORD_NONE) {
523 printf("Enter password for %s@%s: ", destacc->username,
524 destacc->server);
525 get_password(destacc->password, PASSWORD_LEN);
526 destacc->passwdattr = PASSWORD_PLAIN;
527 }
528 if (login(&sockaux, destacc->username,
529 destacc->password) == RESPONSE_NO) {
530 error("imapfilter: username %s or password rejected at %s\n",
531 destacc->username, destacc->server);
532 return ERROR_NETWORK;
533 }
534 }
535 check_namespace(&sockaux, &nspaux);
536
537 /* apply_namespace() returns a pointer to a static buffer. */
538 ndm = apply_namespace(destmbox, nspaux.prefix, nspaux.delim);
539
540 r = check_mailbox(&sockaux, ndm, &nspaux);
541
542 if (r == RESPONSE_OK)
543 close_mailbox(&sockaux);
544 else if (r == RESPONSE_NO) {
545 server_response(&sockaux, imap_create(&sockaux, ndm));
546 if ((options & OPTION_SUBSCRIBE))
547 server_response(&sockaux, imap_subscribe(&sockaux, ndm));
548 }
549 m = mcp = xstrdup(mesgs);
550
551 tok = strtok_r(m, " ", &m);
552 while (tok) {
553 fetchsize_response(&sockpri, &n,
554 imap_fetch(&sockpri, tok, "RFC822.SIZE"));
555
556 ta = imap_append(&sockaux, ndm, n);
557
558 fetch_response(&sockpri, 0, 1, NULL);
559 tf = imap_fetch(&sockpri, tok, "RFC822.HEADER");
560 do {
561 r = fetch_response(&sockpri, tf, 0, buf);
562 socket_write(&sockaux, buf);
563 } while (r == RESPONSE_NONE);
564
565 socket_write(&sockaux, "\r\n");
566
567 fetch_response(&sockpri, 0, 1, NULL);
568 tf = imap_fetch(&sockpri, tok, "BODY[TEXT]");
569 do {
570 r = fetch_response(&sockpri, tf, 0, buf);
571 if (r != RESPONSE_NULLBODY)
572 socket_write(&sockaux, buf);
573 } while (r == RESPONSE_NONE);
574
575 if (r != RESPONSE_NULLBODY)
576 socket_write(&sockaux, "\r\n\r\n");
577 else
578 socket_write(&sockaux, "\r\n");
579
580 append_response(&sockaux, ta);
581
582 tok = strtok_r(NULL, " ", &m);
583 }
584
585 logout(&sockaux);
586
587 action_list(mesgs, args);
588
589 xfree(mcp);
590
591 return 0;
592 }
593
594
595 /*
596 * Move messages to the specified mailbox of another mail server.
597 */
598 int action_rmove(char *mesgs, account_t * destacc, char *destmbox, char *args)
599 {
600 if (!action_rcopy(mesgs, destacc, destmbox, args))
601 action_delete(mesgs, "\0");
602
603 return 0;
604 }
605
606
607 /*
608 * Flag messages by replacing, adding or removing specified flags.
609 */
610 int action_flag(char *mesgs, unsigned int *type, unsigned int *msgflags,
611 char *args)
612 {
613 unsigned int t;
614 char s[STORE_FLAGS_BUF];
615 char *tok, *m, *mcp;
616
617 *s = 0;
618
619 switch (*type) {
620 case FILTER_ACTION_FLAG_ADD:
621 t = STORE_FLAG_ADD;
622 break;
623 case FILTER_ACTION_FLAG_REMOVE:
624 t = STORE_FLAG_REMOVE;
625 break;
626 default:
627 t = STORE_FLAG_REPLACE;
628 }
629
630 if ((*msgflags != MESSAGE_FLAG_NONE)) {
631 if ((*msgflags & MESSAGE_FLAG_SEEN))
632 strncat(s, "\\Seen ", STORE_FLAGS_BUF - strlen(s) - 1);
633 if ((*msgflags & MESSAGE_FLAG_ANSWERED))
634 strncat(s, "\\Answered ", STORE_FLAGS_BUF - strlen(s) - 1);
635 if ((*msgflags & MESSAGE_FLAG_FLAGGED))
636 strncat(s, "\\Flagged ", STORE_FLAGS_BUF - strlen(s) - 1);
637 if ((*msgflags & MESSAGE_FLAG_DELETED))
638 strncat(s, "\\Deleted ", STORE_FLAGS_BUF - strlen(s) - 1);
639 if ((*msgflags & MESSAGE_FLAG_DRAFT))
640 strncat(s, "\\Draft", STORE_FLAGS_BUF - strlen(s) - 1);
641 if ((s[strlen(s) - 1] == ' '))
642 s[strlen(s) - 1] = 0;
643 }
644 action_list(mesgs, args);
645
646 m = mcp = convert_messages(mesgs);
647
648 tok = strtok_r(m, " ", &m);
649 while (tok) {
650 server_response(&sockpri, imap_store(&sockpri, tok, t, s));
651
652 tok = strtok_r(NULL, " ", &m);
653 }
654
655 if (options & OPTION_EXPUNGE)
656 server_response(&sockpri, imap_expunge(&sockpri));
657
658 xfree(mcp);
659
660 return 0;
661 }
662
663 /*
664 * List user selected headers of messages.
665 */
666 int action_list(char *mesgs, char *args)
667 {
668 int r, t;
669 char *tok, *mcp, *m;
670 char s[ARGS_LEN + 27];
671 char hdrs[RESPONSE_BUF];
672
673 if (!*args)
674 return 0;
675
676 m = mcp = xstrdup(mesgs);
677
678 snprintf(s, ARGS_LEN + 27 - 1, "BODY.PEEK[HEADER.FIELDS (%s)]", args);
679
680 tok = strtok_r(m, " ", &m);
681 while (tok) {
682 /* Reset internal fetch counter. */
683 fetch_response(&sockpri, 0, 1, NULL);
684 t = imap_fetch(&sockpri, tok, s);
685 do
686 r = fetch_response(&sockpri, t, 0, hdrs);
687 while (r == RESPONSE_NONE);
688
689 if (*hdrs) {
690 if (options & OPTION_HEADERS)
691 info("%s\n", hdrs);
692 log_info(LOG_WRITE, hdrs);
693 } else {
694 log_info(LOG_WRITE, NULL);
695 }
696
697 tok = strtok_r(NULL, " ", &m);
698 }
699
700 xfree(mcp);
701
702 return 0;
703 }
704
705
706 /*
707 * Count how many messages matched the filter.
708 */
709 unsigned int count_messages(char *mesgs)
710 {
711 unsigned int cnt = 0;
712 char *c = mesgs;
713
714 while ((c = strchr(c, ' '))) {
715 cnt++;
716 c++;
717 }
718
719 return ++cnt;
720 }
721
722
723 /*
724 * Convert messages with contiguous sequence number to the corresponding
725 * number range, eg. 1 2 3 5 7 8 --> 1:3 5 7:8 and return a newly allocated
726 * buffer with the results.
727 */
728 char *convert_messages(char *mesgs)
729 {
730 int maxlen;
731 unsigned int start, end, tmp;
732 char *c, *cp, *tail;
733
734 start = end = tmp = 0;
735 maxlen = strlen(mesgs) + 1;
736 tail = NULL;
737
738 c = cp = xstrdup(mesgs);
739
740 start = (unsigned int)strtoul(mesgs, &tail, 10);
741 end = start;
742
743 do {
744 if (tail) {
745 tmp = (unsigned int)strtoul(tail, &tail, 10);
746 if (!tmp)
747 tail = NULL;
748 }
749 if (tmp == end + 1)
750 end++;
751 else {
752 if (start == end) {
753 xstrncpy(c, ultostr(start, 10), maxlen);
754 c += strlen(c);
755 } else {
756 xstrncpy(c, ultostr(start, 10), maxlen - 1);
757 c += strlen(c);
758 *c = ':';
759 *++c = 0;
760 xstrncpy(c, ultostr(end, 10), maxlen);
761 c += strlen(c);
762 }
763
764 if (tail && c - cp < maxlen) {
765 *c = ' ';
766 *++c = 0;
767 }
768 start = end = tmp;
769 }
770 } while (tmp);
771
772 return cp;
773 }

webmaster@linux.gr
ViewVC Help
Powered by ViewVC 1.1.26