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

Contents of /imapfilter/action.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.13 - (show annotations)
Fri Feb 13 12:17:15 2004 UTC (20 years, 1 month ago) by lefcha
Branch: MAIN
Changes since 1.12: +72 -84 lines
File MIME type: text/plain
Stylistic changes.

1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <sys/types.h>
5 #include <time.h>
6
7 #include "config.h"
8 #include "imapfilter.h"
9 #include "filter.h"
10
11
12 extern connection_t connpri, connaux;
13 extern options_t opts;
14
15
16 int action_delete(char *mesgs, char *args);
17 int action_copy(char *mbox, char *mesgs, char *destmbox, char *args);
18 int action_move(char *mbox, char *mesgs, char *destmbox, char *args);
19 int action_rcopy(char *mbox, char *mesgs, account_t * destacc, char *destmbox, char *args);
20 int action_rmove(char *mbox, char *mesgs, account_t * destacc, char *destmbox, char *args);
21 int action_flag(char *mesgs, unsigned int *type, unsigned int *msgflags, char *args);
22 int action_list(char *mesgs, char *args);
23
24 unsigned int count_messages(char *mesgs);
25 char *convert_messages(char *mesgs);
26 int substitute_date(char *str);
27 void current_date(char *destmbox);
28 void message_date(char *mesg, char *destmbox);
29 void default_variables(char *mbox, char *destmbox);
30
31
32 /*
33 * Apply the appropriate action.
34 */
35 int
36 apply_action(char *mbox, char *mesgs, unsigned int *type, account_t * raccount,
37 char *destmbox, unsigned int *msgflags, char *args)
38 {
39 unsigned int cnt;
40
41 if (*mesgs == '\0')
42 return 0;
43
44 log_info(LOG_ACTION, type);
45 log_info(LOG_DEST_ACCOUNT, raccount->key);
46 log_info(LOG_DEST_MBOX, destmbox);
47
48 cnt = count_messages(mesgs);
49
50 switch (*type) {
51 case ACTION_DELETE:
52 info("%d message%s deleted.\n", cnt, plural(cnt));
53 action_delete(mesgs, args);
54 break;
55 case ACTION_COPY:
56 info("%d message%s copied from \"%s\" to mailbox \"%s\".\n",
57 cnt, plural(cnt), mbox, destmbox);
58 action_copy(mbox, mesgs, apply_namespace(destmbox,
59 connpri.ns.prefix, connpri.ns.delim), args);
60 break;
61 case ACTION_MOVE:
62 info("%d message%s moved from \"%s\" to mailbox \"%s\".\n",
63 cnt, plural(cnt), mbox, destmbox);
64 action_move(mbox, mesgs, apply_namespace(destmbox,
65 connpri.ns.prefix, connpri.ns.delim), args);
66 break;
67 case ACTION_RCOPY:
68 info("%d message%s copied from \"%s\" to mailbox "
69 "\"%s\" at account %s.\n", cnt, plural(cnt),
70 mbox, destmbox, raccount->key);
71 action_rcopy(mbox, mesgs, raccount, destmbox, args);
72 break;
73 case ACTION_RMOVE:
74 info("%d message%s moved from \"%s\" to mailbox "
75 "\"%s\" at account %s.\n", cnt, plural(cnt),
76 mbox, destmbox, raccount->key);
77 action_rmove(mbox, mesgs, raccount, destmbox, args);
78 break;
79 case ACTION_FLAG_REPLACE:
80 case ACTION_FLAG_ADD:
81 case ACTION_FLAG_REMOVE:
82 info("%d message%s flagged.\n", cnt, plural(cnt));
83 action_flag(mesgs, type, msgflags, args);
84 break;
85 case ACTION_LIST:
86 info("%d message%s listed.\n", cnt, plural(cnt));
87 action_list(mesgs, args);
88 break;
89 }
90
91 if (*args == '\0')
92 log_info(LOG_PREAMBLE, NULL);
93
94 return 0;
95 }
96
97
98 /*
99 * Delete messages and optionally list some of their headers.
100 */
101 int
102 action_delete(char *mesgs, char *args)
103 {
104 char *tok, *m, *mcp;
105
106 action_list(mesgs, args);
107
108 m = mcp = convert_messages(mesgs);
109
110 tok = strtok_r(m, " ", &m);
111 while (tok) {
112 response_generic(&connpri, imap_store(&connpri, tok,
113 ACTION_FLAG_ADD, "\\Deleted"));
114
115 tok = strtok_r(NULL, " ", &m);
116 }
117
118 if (opts.expunge)
119 response_generic(&connpri, imap_expunge(&connpri));
120
121 xfree(mcp);
122
123 return 0;
124 }
125
126
127 /*
128 * Copy messages to specified mailbox.
129 */
130 int
131 action_copy(char *mbox, char *mesgs, char *destmbox, char *args)
132 {
133 int r;
134 char *tok, *mcp, *m;
135 char dm[2][MBOX_LEN];
136
137 r = 0;
138 tok = NULL;
139
140 action_list(mesgs, args);
141
142 if (strchr(destmbox, '@'))
143 m = mcp = xstrdup(mesgs);
144 else
145 m = mcp = convert_messages(mesgs);
146
147 xstrncpy(dm[0], destmbox, MBOX_LEN - 1);
148 default_variables(mbox, dm[0]);
149 current_date(dm[0]);
150 tok = strtok_r(m, " ", &m);
151 while (tok != NULL) {
152 xstrncpy(dm[1], dm[0], MBOX_LEN - 1);
153 message_date(tok, dm[1]);
154
155 if ((r = response_copy(&connpri, imap_copy(&connpri, tok,
156 dm[1]))) == RESPONSE_TRYCREATE)
157 if (!response_generic(&connpri, imap_create(&connpri,
158 dm[1]))) {
159 if (opts.subscribe)
160 response_generic(&connpri,
161 imap_subscribe(&connpri, dm[1]));
162 r = response_copy(&connpri,
163 imap_copy(&connpri, tok, dm[1]));
164 }
165 tok = strtok_r(NULL, " ", &m);
166 }
167
168 xfree(mcp);
169
170 return r;
171 }
172
173
174 /*
175 * Move messages to specified mailbox.
176 */
177 int
178 action_move(char *mbox, char *mesgs, char *destmbox, char *args)
179 {
180 if (!action_copy(mbox, mesgs, destmbox, args))
181 action_delete(mesgs, "\0");
182
183 return 0;
184 }
185
186
187 /*
188 * Copy messages to the specified mailbox of another mail server.
189 */
190 int
191 action_rcopy(char *mbox, char *mesgs, account_t * destacc, char *destmbox,
192 char *args)
193 {
194 int r, ta, tf;
195 char *tok, *m, *mcp, *ndm;
196 char *flags, *date;
197 unsigned int size;
198 char buf[RESPONSE_BUF * 2 + 1];
199 char dm[3][MBOX_LEN];
200
201 *dm[0] = *dm[1] = *dm[2] = '\0';
202 flags = date = NULL;
203
204 if (init_connection(&connaux, destacc->server, destacc->port,
205 destacc->ssl))
206 return ERROR_NETWORK;
207
208 r = response_greeting(&connaux);
209
210 if (opts.debug)
211 test(&connaux);
212
213 if (r == RESPONSE_BYE || check_capabilities(&connaux))
214 return ERROR_NETWORK;
215
216 #ifdef SSL_TLS
217 if (destacc->ssl == SSL_DISABLED && connaux.caps & CAPS_STARTTLS)
218 if (negotiate_tls(&connaux) == RESPONSE_OK)
219 check_capabilities(&connaux);
220 #endif
221
222 if (r != RESPONSE_PREAUTH) {
223 if (destacc->pass_attr == PASS_ATTR_NONE) {
224 printf("Enter password for %s@%s: ", destacc->user,
225 destacc->server);
226 get_password(destacc->pass, PASS_LEN);
227 destacc->pass_attr = PASS_ATTR_PLAIN;
228 }
229 #ifdef CRAM_MD5
230 if (connaux.caps & CAPS_CRAMMD5)
231 r = auth_cram_md5(&connaux, destacc->user,
232 destacc->pass);
233 else
234 #endif
235 r = login(&connaux, destacc->user,
236 destacc->pass);
237
238 if (r == RESPONSE_NO) {
239 error("username %s or password rejected at %s\n",
240 destacc->user, destacc->server);
241 return ERROR_NETWORK;
242 }
243 }
244 check_namespace(&connaux);
245
246 m = mcp = xstrdup(mesgs);
247
248 xstrncpy(dm[1], destmbox, MBOX_LEN - 1);
249 default_variables(mbox, dm[1]);
250 current_date(dm[1]);
251
252 tok = strtok_r(m, " ", &m);
253 while (tok != NULL) {
254 xstrncpy(dm[2], dm[1], MBOX_LEN - 1);
255 message_date(tok, dm[2]);
256
257 /* apply_namespace() returns a pointer to a static buffer. */
258 ndm = apply_namespace(dm[2], connaux.ns.prefix,
259 connaux.ns.delim);
260
261 /* Check only if mailbox name is different from last one. */
262 if (strncmp(dm[0], dm[2], strlen(dm[2]))) {
263 r = check_mailbox(&connaux, ndm);
264 if (r == RESPONSE_NO) {
265 response_generic(&connaux,
266 imap_create(&connaux, ndm));
267 if (opts.subscribe)
268 response_generic(&connaux,
269 imap_subscribe(&connaux, ndm));
270 }
271 }
272 xstrncpy(dm[0], dm[2], MBOX_LEN - 1);
273
274 response_fetchfast(&connpri, &flags, &date, &size,
275 imap_fetch(&connpri, tok, "FAST"));
276
277 ta = imap_append(&connaux, ndm, flags, date, size);
278
279 xfree(flags);
280 xfree(date);
281
282 response_fetch(&connpri, 0, 1, NULL);
283 tf = imap_fetch(&connpri, tok, opts.peek ?
284 "BODY.PEEK[HEADER]" : "BODY[HEADER]");
285 do {
286 r = response_fetch(&connpri, tf, 0, buf);
287 socket_write(&connaux, buf);
288 } while (r == RESPONSE_NONE);
289
290 socket_write(&connaux, "\r\n");
291
292 response_fetch(&connpri, 0, 1, NULL);
293 tf = imap_fetch(&connpri, tok, opts.peek ?
294 "BODY.PEEK[TEXT]" : "BODY[TEXT]");
295 do {
296 r = response_fetch(&connpri, tf, 0, buf);
297 if (r != RESPONSE_NULLBODY)
298 socket_write(&connaux, buf);
299 } while (r == RESPONSE_NONE);
300
301 if (r != RESPONSE_NULLBODY)
302 socket_write(&connaux, "\r\n\r\n");
303 else
304 socket_write(&connaux, "\r\n");
305
306 response_append(&connaux, ta);
307
308 tok = strtok_r(NULL, " ", &m);
309 }
310
311 logout(&connaux);
312
313 action_list(mesgs, args);
314
315 xfree(mcp);
316
317 return 0;
318 }
319
320
321 /*
322 * Move messages to the specified mailbox of another mail server.
323 */
324 int
325 action_rmove(char *mbox, char *mesgs, account_t * destacc, char *destmbox,
326 char *args)
327 {
328 if (!action_rcopy(mbox, mesgs, destacc, destmbox, args))
329 action_delete(mesgs, "\0");
330
331 return 0;
332 }
333
334
335 /*
336 * Flag messages by replacing, adding or removing specified flags.
337 */
338 int
339 action_flag(char *mesgs, unsigned int *type, unsigned int *msgflags,
340 char *args)
341 {
342 char s[STORE_FLAGS_BUF];
343 char *tok, *m, *mcp;
344
345 *s = 0;
346
347 if ((*msgflags != MSG_FLAG_NONE)) {
348 if ((*msgflags & MSG_FLAG_SEEN))
349 strncat(s, "\\Seen ", STORE_FLAGS_BUF - strlen(s) - 1);
350 if ((*msgflags & MSG_FLAG_ANSWERED))
351 strncat(s, "\\Answered ",
352 STORE_FLAGS_BUF - strlen(s) - 1);
353 if ((*msgflags & MSG_FLAG_FLAGGED))
354 strncat(s, "\\Flagged ",
355 STORE_FLAGS_BUF - strlen(s) - 1);
356 if ((*msgflags & MSG_FLAG_DELETED))
357 strncat(s, "\\Deleted ",
358 STORE_FLAGS_BUF - strlen(s) - 1);
359 if ((*msgflags & MSG_FLAG_DRAFT))
360 strncat(s, "\\Draft", STORE_FLAGS_BUF - strlen(s) - 1);
361 if ((s[strlen(s) - 1] == ' '))
362 s[strlen(s) - 1] = 0;
363 }
364 action_list(mesgs, args);
365
366 m = mcp = convert_messages(mesgs);
367
368 tok = strtok_r(m, " ", &m);
369 while (tok != NULL) {
370 response_generic(&connpri, imap_store(&connpri, tok, *type, s));
371 tok = strtok_r(NULL, " ", &m);
372 }
373
374 if (opts.expunge)
375 response_generic(&connpri, imap_expunge(&connpri));
376
377 xfree(mcp);
378
379 return 0;
380 }
381
382 /*
383 * List user selected headers of messages.
384 */
385 int
386 action_list(char *mesgs, char *args)
387 {
388 int r, t;
389 char *tok, *mcp, *m;
390 char s[ARGS_LEN + 27];
391 char hdrs[RESPONSE_BUF * 2 + 1];
392
393 if (*args == '\0')
394 return 0;
395
396 m = mcp = xstrdup(mesgs);
397
398 snprintf(s, ARGS_LEN + 27 - 1, opts.peek ?
399 "BODY.PEEK[HEADER.FIELDS (%s)]" :
400 "BODY[HEADER.FIELDS (%s)]", args);
401
402 tok = strtok_r(m, " ", &m);
403 while (tok != NULL) {
404 /* Reset internal fetch counter. */
405 response_fetch(&connpri, 0, 1, NULL);
406 t = imap_fetch(&connpri, tok, s);
407
408 log_info(LOG_PREAMBLE, NULL);
409 do {
410 r = response_fetch(&connpri, t, 0, hdrs);
411
412 if (*hdrs != '\0') {
413 if (opts.headers)
414 info("%s\n", hdrs);
415 log_info(LOG_HEADER, hdrs);
416 }
417 } while (r == RESPONSE_NONE);
418
419 tok = strtok_r(NULL, " ", &m);
420 }
421
422 xfree(mcp);
423
424 return 0;
425 }
426
427
428 /*
429 * Count how many messages matched the filter.
430 */
431 unsigned int
432 count_messages(char *mesgs)
433 {
434 unsigned int cnt;
435 char *c;
436
437 cnt = 0;
438 c = mesgs;
439
440 while ((c = strchr(c, ' '))) {
441 cnt++;
442 c++;
443 }
444
445 return ++cnt;
446 }
447
448
449 /*
450 * Convert messages with contiguous sequence number to the corresponding
451 * number range, eg. 1 2 3 5 7 8 --> 1:3 5 7:8 and return a newly allocated
452 * buffer with the results.
453 */
454 char *
455 convert_messages(char *mesgs)
456 {
457 int maxlen;
458 unsigned int start, end, tmp;
459 char *c, *cp, *tail;
460
461 start = end = tmp = 0;
462 maxlen = strlen(mesgs) + 1;
463 tail = NULL;
464
465 c = cp = xstrdup(mesgs);
466
467 start = (unsigned int)strtoul(mesgs, &tail, 10);
468 end = start;
469
470 do {
471 if (tail != NULL) {
472 tmp = (unsigned int)strtoul(tail, &tail, 10);
473 if (tmp == 0)
474 tail = NULL;
475 }
476 if (tmp == end + 1)
477 end++;
478 else {
479 if (start == end) {
480 xstrncpy(c, ultostr(start, 10), maxlen);
481 c += strlen(c);
482 } else {
483 xstrncpy(c, ultostr(start, 10), maxlen - 1);
484 c += strlen(c);
485 *c = ':';
486 *++c = 0;
487 xstrncpy(c, ultostr(end, 10), maxlen);
488 c += strlen(c);
489 }
490
491 if (tail != NULL && c - cp < maxlen) {
492 *c = ' ';
493 *++c = 0;
494 }
495 start = end = tmp;
496 }
497 } while (tmp != 0);
498
499 return cp;
500 }
501
502
503 /*
504 * Substitute all occurences of the '@' character with the '%' character,
505 * and any double '@' characters with a signle '@' character, returning the
506 * number of replacements.
507 */
508 int
509 substitute_date(char *str)
510 {
511 int n;
512 char *r, *w, *s;
513
514 n = 0;
515
516 s = xstrdup(str);
517
518 for (r = s, w = str; *r != '\0'; r++, w++) {
519 if (*r == '%') {
520 *w++ = '%';
521 *w = '%';
522 } else if (*r == '@') {
523 if (*(r + 1) == '@') {
524 *w = *r++;
525 } else {
526 *w = '%';
527 n++;
528 }
529 } else {
530 *w = *r;
531 }
532 }
533 *w = '\0';
534
535 xfree(s);
536
537 return n;
538 }
539
540
541 /*
542 * Format the destination mailbox according to the current date conversion
543 * specifiers.
544 */
545 void
546 current_date(char *destmbox)
547 {
548 char s[MBOX_LEN];
549 time_t te;
550 struct tm *tl;
551
552 if (!strchr(destmbox, '%'))
553 return;
554
555 te = time(NULL);
556 tl = localtime(&te);
557
558 if (strftime(s, MBOX_LEN - 1, destmbox, tl))
559 xstrncpy(destmbox, s, MBOX_LEN - 1);
560 }
561
562
563 /*
564 * Format the destination mailbox according to the message date conversion
565 * specifiers.
566 */
567 void
568 message_date(char *mesg, char *destmbox)
569 {
570 unsigned int t;
571 char s[MBOX_LEN];
572 struct tm tl;
573 char dbuf[RESPONSE_BUF + 1];
574
575 if (!strchr(destmbox, '@'))
576 return;
577
578 substitute_date(destmbox);
579
580 response_fetch(&connpri, 0, 1, NULL);
581 t = imap_fetch(&connpri, mesg, opts.peek ?
582 "BODY.PEEK[HEADER.FIELDS (DATE)]" :
583 "BODY[HEADER.FIELDS (DATE)]");
584
585 while (response_fetch(&connpri, t, 0, dbuf) == RESPONSE_NONE);
586
587 if (((strptime(dbuf, "Date: %a, %d %b %Y %H:%M:%S", &tl) ||
588 strptime(dbuf, "Date: %d %b %Y %H:%M:%S", &tl)) &&
589 strftime(s, MBOX_LEN - 1, destmbox, &tl)))
590 xstrncpy(destmbox, s, MBOX_LEN - 1);
591 }
592
593
594 /*
595 * Format the destination mailbox according to "default variables" specifiers.
596 */
597 void
598 default_variables(char *mbox, char *destmbox)
599 {
600 char *m, *r, *w, *s;
601
602 if (!strchr(destmbox, '$'))
603 return;
604
605 s = xstrdup(destmbox);
606
607 for (r = s, w = destmbox; *r != '\0';) {
608 if (w - destmbox >= MBOX_LEN - 1)
609 break;
610 if (*r == '$') {
611 switch (*(r + 1)) {
612 case '_':
613 if (w + strlen(mbox) - destmbox >
614 MBOX_LEN - 1) {
615 r += 2;
616 break;
617 }
618 for (m = mbox; *m != '\0'; m++, w++)
619 *w = *m;
620 r += 2;
621 break;
622 case '$':
623 *w++ = '$';
624 r += 2;
625 break;
626 default:
627 *w++ = *r++;
628 break;
629 }
630 } else {
631 *w++ = *r++;
632 }
633 }
634 *w = '\0';
635
636 xfree(s);
637 }

webmaster@linux.gr
ViewVC Help
Powered by ViewVC 1.1.26