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

Contents of /imapfilter/request.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.22 - (show annotations)
Wed Jul 3 17:04:50 2002 UTC (21 years, 9 months ago) by lefcha
Branch: MAIN
Changes since 1.21: +10 -7 lines
File MIME type: text/plain
Fixed a bug with action_copy.

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;
424
425 action_list(mesgs, args);
426
427 convert_messages(mesgs);
428
429 m = mcp = xstrdup(mesgs);
430
431 while ((tok = strsep(&m, " ")))
432 server_response(&sockpri, imap_store(&sockpri, tok, "\\Deleted"));
433
434 xfree(mcp);
435
436 return 0;
437 }
438
439
440 /*
441 * Copy messages to specified mailbox.
442 */
443 int action_copy(char *mesgs, char *destmbox, char *args)
444 {
445 int r = 0;
446 char *tok = NULL, *mcp, *m;
447
448 action_list(mesgs, args);
449
450 convert_messages(mesgs);
451
452 m = mcp = xstrdup(mesgs);
453
454 while ((tok = strsep(&m, " "))) {
455 if ((r = copy_response(&sockpri,
456 imap_copy(&sockpri, tok, destmbox))) ==
457 RESPONSE_TRYCREATE)
458 if (!server_response(&sockpri, imap_create(&sockpri, destmbox))) {
459 server_response(&sockpri, imap_subscribe(&sockpri, destmbox));
460 r = copy_response(&sockpri, imap_copy(&sockpri, tok, destmbox));
461 }
462 }
463
464 xfree(mcp);
465
466 return r;
467 }
468
469
470 /*
471 * Move messages to specified mailbox.
472 */
473 int action_move(char *mesgs, char *destmbox, char *args)
474 {
475 if (!action_copy(mesgs, destmbox, args))
476 action_delete(mesgs, "\0");
477
478 /* CLOSE -> SELECT much faster than EXPUNGE -> SELECT */
479 /* server_response(imap_expunge()); */
480
481 return 0;
482 }
483
484
485 /*
486 * Copy messages to the specified mailbox of another mail server.
487 */
488 int action_rcopy(char *mesgs, account_t *destacc, char *destmbox, char *args)
489 {
490 int r;
491 char *tok, *m, *mcp, *ndm;
492 unsigned int n, t = 0;
493 char buf[RESPONSE_BUF];
494
495 if (init_connection(&sockaux, destacc->server, destacc->port,
496 destacc->ssl))
497 return ERROR_NETWORK;
498
499 r = greeting_response(&sockaux);
500
501 if (r == RESPONSE_BYE || check_capabilities(&sockaux))
502 return ERROR_NETWORK;
503
504 #ifdef DEBUG
505 test(&sockaux);
506 #endif
507
508 if (r != RESPONSE_PREAUTH) {
509 if (destacc->passwdattr == PASSWORD_NONE) {
510 printf("Enter password for %s@%s: ", destacc->username,
511 destacc->server);
512 get_password(destacc->password, PASSWORD_LEN);
513 destacc->passwdattr = PASSWORD_PLAIN;
514 }
515 if (login(&sockaux, destacc->username,
516 destacc->password) == RESPONSE_NO) {
517 error("imapfilter: username %s or password rejected at %s\n",
518 destacc->username, destacc->server);
519 return ERROR_NETWORK;
520 }
521 }
522 check_namespace(&sockaux, &nspaux);
523
524 /* apply_namespace() returns a pointer to a static buffer. */
525 ndm = apply_namespace(destmbox, nspaux.prefix, nspaux.delim);
526
527 r = check_mailbox(&sockaux, ndm, &nspaux);
528
529 if (r == RESPONSE_OK)
530 close_mailbox(&sockaux);
531 else if (r == RESPONSE_NO) {
532 server_response(&sockaux, imap_create(&sockaux, ndm));
533 server_response(&sockaux, imap_subscribe(&sockaux, ndm));
534 }
535
536 m = mcp = xstrdup(mesgs);
537
538 while ((tok = strsep(&m, " "))) {
539 fetchsize_response(&sockpri, &n,
540 imap_fetch(&sockpri, tok, "RFC822.SIZE"));
541
542 t = imap_append(&sockaux, ndm, n);
543
544 do {
545 r = fetch_response(&sockpri, 0, buf, imap_fetch(&sockpri, tok,
546 "RFC822.HEADER"));
547
548 socket_write(&sockaux, buf);
549 } while (r == RESPONSE_NONE);
550
551 socket_write(&sockaux, "\r\n");
552
553 fetch_response(&sockpri, 1, NULL, 0);
554 do {
555 r = fetch_response(&sockpri, 0, buf, imap_fetch(&sockpri, tok,
556 "BODY[TEXT]"));
557
558 socket_write(&sockaux, buf);
559 } while (r == RESPONSE_NONE);
560 }
561
562 socket_write(&sockaux, "\r\n\r\n");
563
564 append_response(&sockaux, t);
565
566 logout(&sockaux);
567
568 action_list(mesgs, args);
569
570 xfree(mcp);
571
572 return 0;
573 }
574
575
576 /*
577 * Move messages to the specified mailbox of another mail server.
578 */
579 int action_rmove(char *mesgs, account_t *destacc, char *destmbox, char *args)
580 {
581 if (!action_rcopy(mesgs, destacc, destmbox, args))
582 action_delete(mesgs, "\0");
583
584 return 0;
585 }
586
587
588 /*
589 * List user selected headers of messages.
590 */
591 int action_list(char *mesgs, char *args)
592 {
593 int r;
594 char *tok, *mcp, *m;
595 char s[ARGS_LEN + 27];
596 char hdrs[RESPONSE_BUF];
597
598 if (!*args)
599 return 0;
600
601 m = mcp = xstrdup(mesgs);
602
603 snprintf(s, ARGS_LEN + 27 - 1, "BODY.PEEK[HEADER.FIELDS (%s)]", args);
604
605 while ((tok = strsep(&m, " "))) {
606 /* Reset internal fetch counter. */
607 fetch_response(&sockpri, 1, NULL, 0);
608
609 do {
610 r = fetch_response(&sockpri, 0, hdrs, imap_fetch(&sockpri, tok, s));
611
612 if (*hdrs) {
613 if (options & OPTION_HEADERS)
614 info("%s\n", hdrs);
615 log_info(LOG_WRITE, hdrs);
616 } else {
617 log_info(LOG_WRITE, NULL);
618 }
619 } while (r == RESPONSE_NONE);
620 }
621
622 xfree(mcp);
623
624 return 0;
625 }
626
627
628 /*
629 * Count how many messages matched the filter.
630 */
631 unsigned int count_messages(char *mesgs)
632 {
633 unsigned int cnt = 0;
634 char *c = mesgs;
635
636 while ((c = strchr(c, ' '))) {
637 cnt++;
638 c++;
639 }
640
641 return ++cnt;
642 }
643
644
645 /*
646 * Convert messages with contiguous sequence number to the corresponding
647 * number range, eg. 1 2 3 5 7 8 --> 1:3 5 7:8
648 */
649 void convert_messages(char *mesgs)
650 {
651 unsigned int maxlen;
652 unsigned int start, end, tmp;
653 char *cp, *tail, *m;
654
655 start = end = tmp = 0;
656 maxlen = strlen(mesgs) + 1;
657 tail = NULL;
658
659 cp = xstrdup(mesgs);
660 m = mesgs;
661
662 start = (unsigned int) strtoul(cp, &tail, 10);
663 end = start;
664
665 do {
666 if (tail) {
667 tmp = (unsigned int) strtoul(tail, &tail, 10);
668 if (!tmp)
669 tail = NULL;
670 }
671 if (tmp == end + 1)
672 end++;
673 else {
674 if (start == end) {
675 xstrncpy(m, ultostr(start, 10), maxlen);
676 m += strlen(m);
677 } else {
678 xstrncpy(m, ultostr(start, 10), maxlen - 1);
679 m += strlen(m);
680 *m = ':';
681 *++m = 0;
682 xstrncpy(m, ultostr(end, 10), maxlen);
683 m += strlen(m);
684 }
685
686 if (tail && m - mesgs < maxlen) {
687 *m = ' ';
688 *++m = 0;
689 }
690 start = end = tmp;
691 }
692 } while (tmp);
693
694 xfree(cp);
695 }

webmaster@linux.gr
ViewVC Help
Powered by ViewVC 1.1.26