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

Contents of /imapfilter/action.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.8 - (show annotations)
Fri Feb 6 01:08:39 2004 UTC (20 years, 1 month ago) by lefcha
Branch: MAIN
Changes since 1.7: +3 -2 lines
File MIME type: text/plain
Fixed bug related to RFC822 Date: header and optional day %a specification.

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
10
11 extern conn_t connpri, connaux;
12 extern unsigned int options;
13
14
15 int action_delete(char *mesgs, char *args);
16 int action_copy(char *mbox, char *mesgs, char *destmbox, char *args);
17 int action_move(char *mbox, char *mesgs, char *destmbox, char *args);
18 int action_rcopy(char *mbox, char *mesgs, account_t * destacc, char *destmbox, char *args);
19 int action_rmove(char *mbox, char *mesgs, account_t * destacc, char *destmbox, char *args);
20 int action_flag(char *mesgs, unsigned int *type, unsigned int *msgflags, char *args);
21 int action_list(char *mesgs, char *args);
22
23 unsigned int count_messages(char *mesgs);
24 char *convert_messages(char *mesgs);
25 int substitute_date(char *str);
26 void current_date(char *destmbox);
27 void message_date(char *mesg, char *destmbox);
28 void default_variables(char *mbox, char *destmbox);
29
30
31 /*
32 * Apply the appropriate action.
33 */
34 int
35 apply_action(char *mbox, char *mesgs, unsigned int *type, account_t * raccount,
36 char *destmbox, unsigned int *msgflags, char *args)
37 {
38 unsigned int cnt;
39
40 if (*mesgs == '\0')
41 return 0;
42
43 log_info(LOG_ACTION, type);
44 log_info(LOG_DESTINATION_ACCOUNT, raccount->key);
45 log_info(LOG_DESTINATION_MAILBOX, destmbox);
46
47 cnt = count_messages(mesgs);
48
49 switch (*type) {
50 case FILTER_ACTION_DELETE:
51 info("%d message%s deleted.\n", cnt, plural(cnt));
52 action_delete(mesgs, args);
53 break;
54 case FILTER_ACTION_COPY:
55 info("%d message%s copied from \"%s\" to mailbox \"%s\".\n",
56 cnt, plural(cnt), mbox, destmbox);
57 action_copy(mbox, mesgs, apply_namespace(destmbox,
58 connpri.nsp.prefix, connpri.nsp.delim), args);
59 break;
60 case FILTER_ACTION_MOVE:
61 info("%d message%s moved from \"%s\" to mailbox \"%s\".\n",
62 cnt, plural(cnt), mbox, destmbox);
63 action_move(mbox, mesgs, apply_namespace(destmbox,
64 connpri.nsp.prefix, connpri.nsp.delim), args);
65 break;
66 case FILTER_ACTION_RCOPY:
67 info("%d message%s copied from \"%s\" to mailbox "
68 "\"%s\" at account %s.\n", cnt, plural(cnt),
69 mbox, destmbox, raccount->key);
70 action_rcopy(mbox, mesgs, raccount, destmbox, args);
71 break;
72 case FILTER_ACTION_RMOVE:
73 info("%d message%s moved from \"%s\" to mailbox "
74 "\"%s\" at account %s.\n", cnt, plural(cnt),
75 mbox, destmbox, raccount->key);
76 action_rmove(mbox, mesgs, raccount, destmbox, args);
77 break;
78 case FILTER_ACTION_FLAG_REPLACE:
79 case FILTER_ACTION_FLAG_ADD:
80 case FILTER_ACTION_FLAG_REMOVE:
81 info("%d message%s flagged.\n", cnt, plural(cnt));
82 action_flag(mesgs, type, msgflags, args);
83 break;
84 case FILTER_ACTION_LIST:
85 info("%d message%s listed.\n", cnt, plural(cnt));
86 action_list(mesgs, args);
87 break;
88 }
89
90 if (*args == '\0')
91 log_info(LOG_PREAMBLE, NULL);
92
93 return 0;
94 }
95
96
97 /*
98 * Delete messages and optionally list some of their headers.
99 */
100 int
101 action_delete(char *mesgs, char *args)
102 {
103 char *tok, *m, *mcp;
104
105 action_list(mesgs, args);
106
107 m = mcp = convert_messages(mesgs);
108
109 tok = strtok_r(m, " ", &m);
110 while (tok) {
111 server_response(&connpri, imap_store(&connpri, tok,
112 STORE_FLAG_ADD, "\\Deleted"));
113
114 tok = strtok_r(NULL, " ", &m);
115 }
116
117 if (options & OPTION_EXPUNGE)
118 server_response(&connpri, imap_expunge(&connpri));
119
120 xfree(mcp);
121
122 return 0;
123 }
124
125
126 /*
127 * Copy messages to specified mailbox.
128 */
129 int
130 action_copy(char *mbox, char *mesgs, char *destmbox, char *args)
131 {
132 int r;
133 char *tok, *mcp, *m;
134 char dm[2][MBOX_NAME_LEN];
135
136 r = 0;
137 tok = NULL;
138
139 action_list(mesgs, args);
140
141 if (strchr(destmbox, '@'))
142 m = mcp = xstrdup(mesgs);
143 else
144 m = mcp = convert_messages(mesgs);
145
146 xstrncpy(dm[0], destmbox, MBOX_NAME_LEN - 1);
147 default_variables(mbox, dm[0]);
148 current_date(dm[0]);
149 tok = strtok_r(m, " ", &m);
150 while (tok != NULL) {
151 xstrncpy(dm[1], dm[0], MBOX_NAME_LEN - 1);
152 message_date(tok, dm[1]);
153
154 if ((r = copy_response(&connpri, imap_copy(&connpri, tok,
155 dm[1]))) == RESPONSE_TRYCREATE)
156 if (!server_response(&connpri, imap_create(&connpri,
157 dm[1]))) {
158 if ((options & OPTION_SUBSCRIBE))
159 server_response(&connpri,
160 imap_subscribe(&connpri, dm[1]));
161 r = copy_response(&connpri,
162 imap_copy(&connpri, tok, dm[1]));
163 }
164 tok = strtok_r(NULL, " ", &m);
165 }
166
167 xfree(mcp);
168
169 return r;
170 }
171
172
173 /*
174 * Move messages to specified mailbox.
175 */
176 int
177 action_move(char *mbox, char *mesgs, char *destmbox, char *args)
178 {
179 if (!action_copy(mbox, mesgs, destmbox, args))
180 action_delete(mesgs, "\0");
181
182 return 0;
183 }
184
185
186 /*
187 * Copy messages to the specified mailbox of another mail server.
188 */
189 int
190 action_rcopy(char *mbox, char *mesgs, account_t * destacc, char *destmbox,
191 char *args)
192 {
193 int r, ta, tf;
194 char *tok, *m, *mcp, *ndm;
195 char *flags, *date;
196 unsigned int size;
197 char buf[RESPONSE_BUF * 2 + 1];
198 char dm[3][MBOX_NAME_LEN];
199
200 *dm[0] = *dm[1] = *dm[2] = '\0';
201 flags = date = NULL;
202
203 if (init_connection(&connaux, destacc->server, destacc->port,
204 destacc->ssl))
205 return ERROR_NETWORK;
206
207 r = greeting_response(&connaux);
208
209 #ifdef DEBUG
210 test(&connaux);
211 #endif
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 & CAPABILITY_STARTTLS)
218 if (negotiate_tls(&connaux) == RESPONSE_OK)
219 check_capabilities(&connaux);
220 #endif
221
222 if (r != RESPONSE_PREAUTH) {
223 if (destacc->passwdattr == PASSWORD_NONE) {
224 printf("Enter password for %s@%s: ", destacc->username,
225 destacc->server);
226 get_password(destacc->password, PASSWORD_LEN);
227 destacc->passwdattr = PASSWORD_PLAIN;
228 }
229 #ifdef CRAM_MD5
230 if (connaux.caps & CAPABILITY_AUTH_CRAM_MD5)
231 r = auth_cram_md5(&connaux, destacc->username,
232 destacc->password);
233 else
234 #endif
235 r = login(&connaux, destacc->username,
236 destacc->password);
237
238 if (r == RESPONSE_NO) {
239 error("username %s or password rejected at %s\n",
240 destacc->username, 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_NAME_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_NAME_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.nsp.prefix,
259 connaux.nsp.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 server_response(&connaux,
266 imap_create(&connaux, ndm));
267 if ((options & OPTION_SUBSCRIBE))
268 server_response(&connaux,
269 imap_subscribe(&connaux, ndm));
270 }
271 }
272 xstrncpy(dm[0], dm[2], MBOX_NAME_LEN - 1);
273
274 fetchfast_response(&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 fetch_response(&connpri, 0, 1, NULL);
283 tf = imap_fetch(&connpri, tok, options & OPTION_PEEK ?
284 "BODY.PEEK[HEADER]" : "BODY[HEADER]");
285 do {
286 r = fetch_response(&connpri, tf, 0, buf);
287 socket_write(&connaux, buf);
288 } while (r == RESPONSE_NONE);
289
290 socket_write(&connaux, "\r\n");
291
292 fetch_response(&connpri, 0, 1, NULL);
293 tf = imap_fetch(&connpri, tok, options & OPTION_PEEK ?
294 "BODY.PEEK[TEXT]" : "BODY[TEXT]");
295 do {
296 r = fetch_response(&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 append_response(&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 unsigned int t;
343 char s[STORE_FLAGS_BUF];
344 char *tok, *m, *mcp;
345
346 *s = 0;
347
348 switch (*type) {
349 case FILTER_ACTION_FLAG_ADD:
350 t = STORE_FLAG_ADD;
351 break;
352 case FILTER_ACTION_FLAG_REMOVE:
353 t = STORE_FLAG_REMOVE;
354 break;
355 default:
356 t = STORE_FLAG_REPLACE;
357 }
358
359 if ((*msgflags != MESSAGE_FLAG_NONE)) {
360 if ((*msgflags & MESSAGE_FLAG_SEEN))
361 strncat(s, "\\Seen ", STORE_FLAGS_BUF - strlen(s) - 1);
362 if ((*msgflags & MESSAGE_FLAG_ANSWERED))
363 strncat(s, "\\Answered ",
364 STORE_FLAGS_BUF - strlen(s) - 1);
365 if ((*msgflags & MESSAGE_FLAG_FLAGGED))
366 strncat(s, "\\Flagged ",
367 STORE_FLAGS_BUF - strlen(s) - 1);
368 if ((*msgflags & MESSAGE_FLAG_DELETED))
369 strncat(s, "\\Deleted ",
370 STORE_FLAGS_BUF - strlen(s) - 1);
371 if ((*msgflags & MESSAGE_FLAG_DRAFT))
372 strncat(s, "\\Draft", STORE_FLAGS_BUF - strlen(s) - 1);
373 if ((s[strlen(s) - 1] == ' '))
374 s[strlen(s) - 1] = 0;
375 }
376 action_list(mesgs, args);
377
378 m = mcp = convert_messages(mesgs);
379
380 tok = strtok_r(m, " ", &m);
381 while (tok != NULL) {
382 server_response(&connpri, imap_store(&connpri, tok, t, s));
383
384 tok = strtok_r(NULL, " ", &m);
385 }
386
387 if (options & OPTION_EXPUNGE)
388 server_response(&connpri, imap_expunge(&connpri));
389
390 xfree(mcp);
391
392 return 0;
393 }
394
395 /*
396 * List user selected headers of messages.
397 */
398 int
399 action_list(char *mesgs, char *args)
400 {
401 int r, t;
402 char *tok, *mcp, *m;
403 char s[ARGS_LEN + 27];
404 char hdrs[RESPONSE_BUF * 2 + 1];
405
406 if (*args == '\0')
407 return 0;
408
409 m = mcp = xstrdup(mesgs);
410
411 snprintf(s, ARGS_LEN + 27 - 1, options & OPTION_PEEK ?
412 "BODY.PEEK[HEADER.FIELDS (%s)]" :
413 "BODY[HEADER.FIELDS (%s)]", args);
414
415 tok = strtok_r(m, " ", &m);
416 while (tok != NULL) {
417 /* Reset internal fetch counter. */
418 fetch_response(&connpri, 0, 1, NULL);
419 t = imap_fetch(&connpri, tok, s);
420
421 log_info(LOG_PREAMBLE, NULL);
422 do {
423 r = fetch_response(&connpri, t, 0, hdrs);
424
425 if (*hdrs != '\0') {
426 if (options & OPTION_HEADERS)
427 info("%s\n", hdrs);
428 log_info(LOG_HEADER, hdrs);
429 }
430 } while (r == RESPONSE_NONE);
431
432 tok = strtok_r(NULL, " ", &m);
433 }
434
435 xfree(mcp);
436
437 return 0;
438 }
439
440
441 /*
442 * Count how many messages matched the filter.
443 */
444 unsigned int
445 count_messages(char *mesgs)
446 {
447 unsigned int cnt;
448 char *c;
449
450 cnt = 0;
451 c = mesgs;
452
453 while ((c = strchr(c, ' '))) {
454 cnt++;
455 c++;
456 }
457
458 return ++cnt;
459 }
460
461
462 /*
463 * Convert messages with contiguous sequence number to the corresponding
464 * number range, eg. 1 2 3 5 7 8 --> 1:3 5 7:8 and return a newly allocated
465 * buffer with the results.
466 */
467 char *
468 convert_messages(char *mesgs)
469 {
470 int maxlen;
471 unsigned int start, end, tmp;
472 char *c, *cp, *tail;
473
474 start = end = tmp = 0;
475 maxlen = strlen(mesgs) + 1;
476 tail = NULL;
477
478 c = cp = xstrdup(mesgs);
479
480 start = (unsigned int)strtoul(mesgs, &tail, 10);
481 end = start;
482
483 do {
484 if (tail != NULL) {
485 tmp = (unsigned int)strtoul(tail, &tail, 10);
486 if (tmp == 0)
487 tail = NULL;
488 }
489 if (tmp == end + 1)
490 end++;
491 else {
492 if (start == end) {
493 xstrncpy(c, ultostr(start, 10), maxlen);
494 c += strlen(c);
495 } else {
496 xstrncpy(c, ultostr(start, 10), maxlen - 1);
497 c += strlen(c);
498 *c = ':';
499 *++c = 0;
500 xstrncpy(c, ultostr(end, 10), maxlen);
501 c += strlen(c);
502 }
503
504 if (tail != NULL && c - cp < maxlen) {
505 *c = ' ';
506 *++c = 0;
507 }
508 start = end = tmp;
509 }
510 } while (tmp != 0);
511
512 return cp;
513 }
514
515
516 /*
517 * Substitute all occurences of the '@' character with the '%' character,
518 * and any double '@' characters with a signle '@' character, returning the
519 * number of replacements.
520 */
521 int
522 substitute_date(char *str)
523 {
524 int n;
525 char *r, *w, *s;
526
527 n = 0;
528
529 s = xstrdup(str);
530
531 for (r = s, w = str; *r != '\0'; r++, w++) {
532 if (*r == '%') {
533 *w++ = '%';
534 *w = '%';
535 } else if (*r == '@') {
536 if (*(r + 1) == '@') {
537 *w = *r++;
538 } else {
539 *w = '%';
540 n++;
541 }
542 } else {
543 *w = *r;
544 }
545 }
546 *w = '\0';
547
548 xfree(s);
549
550 return n;
551 }
552
553
554 /*
555 * Format the destination mailbox according to the current date conversion
556 * specifiers.
557 */
558 void
559 current_date(char *destmbox)
560 {
561 char s[MBOX_NAME_LEN];
562 time_t te;
563 struct tm *tl;
564
565 if (!strchr(destmbox, '%'))
566 return;
567
568 te = time(NULL);
569 tl = localtime(&te);
570
571 if (strftime(s, MBOX_NAME_LEN - 1, destmbox, tl))
572 xstrncpy(destmbox, s, MBOX_NAME_LEN - 1);
573 }
574
575
576 /*
577 * Format the destination mailbox according to the message date conversion
578 * specifiers.
579 */
580 void
581 message_date(char *mesg, char *destmbox)
582 {
583 unsigned int t;
584 char s[MBOX_NAME_LEN];
585 struct tm tl;
586 char dbuf[RESPONSE_BUF + 1];
587
588 if (!strchr(destmbox, '@'))
589 return;
590
591 substitute_date(destmbox);
592
593 fetch_response(&connpri, 0, 1, NULL);
594 t = imap_fetch(&connpri, mesg, options & OPTION_PEEK ?
595 "BODY.PEEK[HEADER.FIELDS (DATE)]" :
596 "BODY[HEADER.FIELDS (DATE)]");
597
598 while (fetch_response(&connpri, t, 0, dbuf) == RESPONSE_NONE);
599
600 if (((strptime(dbuf, "Date: %a, %d %b %Y %H:%M:%S", &tl) ||
601 strptime(dbuf, "Date: %d %b %Y %H:%M:%S", &tl)) &&
602 strftime(s, MBOX_NAME_LEN - 1, destmbox, &tl)))
603 xstrncpy(destmbox, s, MBOX_NAME_LEN - 1);
604 }
605
606
607 /*
608 * Format the destination mailbox according to "default variables" specifiers.
609 */
610 void
611 default_variables(char *mbox, char *destmbox)
612 {
613 char *m, *r, *w, *s;
614
615 if (!strchr(destmbox, '$'))
616 return;
617
618 s = xstrdup(destmbox);
619
620 for (r = s, w = destmbox; *r != '\0';) {
621 if (w - destmbox >= MBOX_NAME_LEN - 1)
622 break;
623 if (*r == '$') {
624 switch (*(r + 1)) {
625 case '_':
626 if (w + strlen(mbox) - destmbox >
627 MBOX_NAME_LEN - 1) {
628 r += 2;
629 break;
630 }
631 for (m = mbox; *m != '\0'; m++, w++)
632 *w = *m;
633 r += 2;
634 break;
635 case '$':
636 *w++ = '$';
637 r += 2;
638 break;
639 default:
640 *w++ = *r++;
641 break;
642 }
643 } else {
644 *w++ = *r++;
645 }
646 }
647 *w = '\0';
648
649 xfree(s);
650 }

webmaster@linux.gr
ViewVC Help
Powered by ViewVC 1.1.26