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

Contents of /imapfilter/request.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.41 - (show annotations)
Mon Feb 3 20:22:01 2003 UTC (21 years, 2 months ago) by lefcha
Branch: MAIN
Changes since 1.40: +2 -1 lines
File MIME type: text/plain
Added i18n support, variable charset to declare character set to the server.

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

webmaster@linux.gr
ViewVC Help
Powered by ViewVC 1.1.26