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

Contents of /imapfilter/request.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.23 - (show annotations)
Mon Jul 8 17:59:57 2002 UTC (21 years, 8 months ago) by lefcha
Branch: MAIN
Changes since 1.22: +30 -28 lines
File MIME type: text/plain
Fixed convert_messages()/actions bug.

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

webmaster@linux.gr
ViewVC Help
Powered by ViewVC 1.1.26