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

Contents of /imapfilter/request.c

Parent Directory Parent Directory | Revision Log Revision Log


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

webmaster@linux.gr
ViewVC Help
Powered by ViewVC 1.1.26