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

Contents of /imapfilter/action.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.2 - (show annotations)
Sun Aug 3 16:01:53 2003 UTC (20 years, 8 months ago) by lefcha
Branch: MAIN
CVS Tags: release-0_9
Branch point for: release-0_9-patches
Changes since 1.1: +1 -0 lines
File MIME type: text/plain
Added missing stdlib.h header.

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

webmaster@linux.gr
ViewVC Help
Powered by ViewVC 1.1.26