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

Contents of /imapfilter/action.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.16 - (show annotations)
Sat Feb 14 22:48:04 2004 UTC (20 years, 1 month ago) by lefcha
Branch: MAIN
Changes since 1.15: +2 -2 lines
File MIME type: text/plain
Added IMAP4 protocol support.

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 * destacct, char *destmbox, char *args);
20 int action_rmove(char *mbox, char *mesgs, account_t * destacct, 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 * destacct,
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, destacct->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), mbox,
70 destmbox, destacct->key);
71 action_rcopy(mbox, mesgs, destacct, 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), mbox,
76 destmbox, destacct->key);
77 action_rmove(mbox, mesgs, destacct, 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
181 if (!action_copy(mbox, mesgs, destmbox, args))
182 action_delete(mesgs, "\0");
183
184 return 0;
185 }
186
187
188 /*
189 * Copy messages to the specified mailbox of another mail server.
190 */
191 int
192 action_rcopy(char *mbox, char *mesgs, account_t * destacct, char *destmbox,
193 char *args)
194 {
195 int r, ta, tf;
196 char *tok, *m, *mcp, *ndm;
197 char *flags, *date;
198 unsigned int size;
199 char buf[RESPONSE_BUF * 2 + 1];
200 char dm[3][MBOX_LEN];
201
202 *dm[0] = *dm[1] = *dm[2] = '\0';
203 flags = date = NULL;
204
205 if (init_connection(&connaux, destacct->server, destacct->port,
206 destacct->ssl))
207 return ERROR_NETWORK;
208
209 r = response_greeting(&connaux);
210
211 if (opts.debug)
212 test(&connaux);
213
214 if (r == RESPONSE_BYE || check_capabilities(&connaux))
215 return ERROR_NETWORK;
216
217 #ifdef SSL_TLS
218 if (destacct->ssl == SSL_DISABLED && connaux.caps & CAPABILITY_STARTTLS)
219 if (negotiate_tls(&connaux) == RESPONSE_OK)
220 check_capabilities(&connaux);
221 #endif
222
223 if (r != RESPONSE_PREAUTH) {
224 if (destacct->pass_attr == PASS_ATTR_NONE) {
225 printf("Enter password for %s@%s: ", destacct->user,
226 destacct->server);
227 get_password(destacct->pass, PASS_LEN);
228 destacct->pass_attr = PASS_ATTR_PLAIN;
229 }
230 #ifdef CRAM_MD5
231 if (connaux.caps & CAPABILITY_CRAMMD5)
232 r = auth_cram_md5(&connaux, destacct->user,
233 destacct->pass);
234 else
235 #endif
236 r = login(&connaux, destacct->user, destacct->pass);
237
238 if (r == RESPONSE_NO) {
239 error("username %s or password rejected at %s\n",
240 destacct->user, destacct->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 * destacct, char *destmbox,
326 char *args)
327 {
328
329 if (!action_rcopy(mbox, mesgs, destacct, destmbox, args))
330 action_delete(mesgs, "\0");
331
332 return 0;
333 }
334
335
336 /*
337 * Flag messages by replacing, adding or removing specified flags.
338 */
339 int
340 action_flag(char *mesgs, unsigned int *type, unsigned int *msgflags,
341 char *args)
342 {
343 char s[STORE_FLAGS_BUF];
344 char *tok, *m, *mcp;
345
346 *s = 0;
347
348 if ((*msgflags != MSG_FLAG_NONE)) {
349 if ((*msgflags & MSG_FLAG_SEEN))
350 strncat(s, "\\Seen ", STORE_FLAGS_BUF - strlen(s) - 1);
351 if ((*msgflags & MSG_FLAG_ANSWERED))
352 strncat(s, "\\Answered ",
353 STORE_FLAGS_BUF - strlen(s) - 1);
354 if ((*msgflags & MSG_FLAG_FLAGGED))
355 strncat(s, "\\Flagged ",
356 STORE_FLAGS_BUF - strlen(s) - 1);
357 if ((*msgflags & MSG_FLAG_DELETED))
358 strncat(s, "\\Deleted ",
359 STORE_FLAGS_BUF - strlen(s) - 1);
360 if ((*msgflags & MSG_FLAG_DRAFT))
361 strncat(s, "\\Draft", STORE_FLAGS_BUF - strlen(s) - 1);
362 if ((s[strlen(s) - 1] == ' '))
363 s[strlen(s) - 1] = 0;
364 }
365 action_list(mesgs, args);
366
367 m = mcp = convert_messages(mesgs);
368
369 tok = strtok_r(m, " ", &m);
370 while (tok != NULL) {
371 response_generic(&connpri, imap_store(&connpri, tok, *type, s));
372 tok = strtok_r(NULL, " ", &m);
373 }
374
375 if (opts.expunge)
376 response_generic(&connpri, imap_expunge(&connpri));
377
378 xfree(mcp);
379
380 return 0;
381 }
382
383 /*
384 * List user selected headers of messages.
385 */
386 int
387 action_list(char *mesgs, char *args)
388 {
389 int r, t;
390 char *tok, *mcp, *m;
391 char s[ARGS_LEN + 27];
392 char hdrs[RESPONSE_BUF * 2 + 1];
393
394 if (*args == '\0')
395 return 0;
396
397 m = mcp = xstrdup(mesgs);
398
399 snprintf(s, ARGS_LEN + 27 - 1, opts.peek ?
400 "BODY.PEEK[HEADER.FIELDS (%s)]" :
401 "BODY[HEADER.FIELDS (%s)]", args);
402
403 tok = strtok_r(m, " ", &m);
404 while (tok != NULL) {
405 /* Reset internal fetch counter. */
406 response_fetch(&connpri, 0, 1, NULL);
407 t = imap_fetch(&connpri, tok, s);
408
409 log_info(LOG_PREAMBLE, NULL);
410 do {
411 r = response_fetch(&connpri, t, 0, hdrs);
412
413 if (*hdrs != '\0') {
414 if (opts.headers)
415 info("%s\n", hdrs);
416 log_info(LOG_HEADER, hdrs);
417 }
418 } while (r == RESPONSE_NONE);
419
420 tok = strtok_r(NULL, " ", &m);
421 }
422
423 xfree(mcp);
424
425 return 0;
426 }
427
428
429 /*
430 * Count how many messages matched the filter.
431 */
432 unsigned int
433 count_messages(char *mesgs)
434 {
435 unsigned int cnt;
436 char *c;
437
438 cnt = 0;
439 c = mesgs;
440
441 while ((c = strchr(c, ' '))) {
442 cnt++;
443 c++;
444 }
445
446 return ++cnt;
447 }
448
449
450 /*
451 * Convert messages with contiguous sequence number to the corresponding number
452 * range, eg. 1 2 3 5 7 8 --> 1:3 5 7:8 and return a newly allocated buffer
453 * with the results.
454 */
455 char *
456 convert_messages(char *mesgs)
457 {
458 int maxlen;
459 unsigned int start, end, tmp;
460 char *c, *cp, *tail;
461
462 start = end = tmp = 0;
463 maxlen = strlen(mesgs) + 1;
464 tail = NULL;
465
466 c = cp = xstrdup(mesgs);
467
468 start = (unsigned int)strtoul(mesgs, &tail, 10);
469 end = start;
470
471 do {
472 if (tail != NULL) {
473 tmp = (unsigned int)strtoul(tail, &tail, 10);
474 if (tmp == 0)
475 tail = NULL;
476 }
477 if (tmp == end + 1)
478 end++;
479 else {
480 if (start == end) {
481 xstrncpy(c, ultostr(start, 10), maxlen);
482 c += strlen(c);
483 } else {
484 xstrncpy(c, ultostr(start, 10), maxlen - 1);
485 c += strlen(c);
486 *c = ':';
487 *++c = 0;
488 xstrncpy(c, ultostr(end, 10), maxlen);
489 c += strlen(c);
490 }
491
492 if (tail != NULL && c - cp < maxlen) {
493 *c = ' ';
494 *++c = 0;
495 }
496 start = end = tmp;
497 }
498 } while (tmp != 0);
499
500 return cp;
501 }
502
503
504 /*
505 * Substitute all occurences of the '@' character with the '%' character, and
506 * any double '@' characters with a signle '@' character, returning the number
507 * of replacements.
508 */
509 int
510 substitute_date(char *str)
511 {
512 int n;
513 char *r, *w, *s;
514
515 n = 0;
516
517 s = xstrdup(str);
518
519 for (r = s, w = str; *r != '\0'; r++, w++) {
520 if (*r == '%') {
521 *w++ = '%';
522 *w = '%';
523 } else if (*r == '@') {
524 if (*(r + 1) == '@') {
525 *w = *r++;
526 } else {
527 *w = '%';
528 n++;
529 }
530 } else {
531 *w = *r;
532 }
533 }
534 *w = '\0';
535
536 xfree(s);
537
538 return n;
539 }
540
541
542 /*
543 * Format the destination mailbox according to the current date conversion
544 * specifiers.
545 */
546 void
547 current_date(char *destmbox)
548 {
549 char s[MBOX_LEN];
550 time_t te;
551 struct tm *tl;
552
553 if (!strchr(destmbox, '%'))
554 return;
555
556 te = time(NULL);
557 tl = localtime(&te);
558
559 if (strftime(s, MBOX_LEN - 1, destmbox, tl))
560 xstrncpy(destmbox, s, MBOX_LEN - 1);
561 }
562
563
564 /*
565 * Format the destination mailbox according to the message date conversion
566 * specifiers.
567 */
568 void
569 message_date(char *mesg, char *destmbox)
570 {
571 unsigned int t;
572 char s[MBOX_LEN];
573 struct tm tl;
574 char dbuf[RESPONSE_BUF + 1];
575
576 if (!strchr(destmbox, '@'))
577 return;
578
579 substitute_date(destmbox);
580
581 response_fetch(&connpri, 0, 1, NULL);
582 t = imap_fetch(&connpri, mesg, opts.peek ?
583 "BODY.PEEK[HEADER.FIELDS (DATE)]" :
584 "BODY[HEADER.FIELDS (DATE)]");
585
586 while (response_fetch(&connpri, t, 0, dbuf) == RESPONSE_NONE);
587
588 if (((strptime(dbuf, "Date: %a, %d %b %Y %H:%M:%S", &tl) ||
589 strptime(dbuf, "Date: %d %b %Y %H:%M:%S", &tl)) &&
590 strftime(s, MBOX_LEN - 1, destmbox, &tl)))
591 xstrncpy(destmbox, s, MBOX_LEN - 1);
592 }
593
594
595 /*
596 * Format the destination mailbox according to "default variables" specifiers.
597 */
598 void
599 default_variables(char *mbox, char *destmbox)
600 {
601 char *m, *r, *w, *s;
602
603 if (!strchr(destmbox, '$'))
604 return;
605
606 s = xstrdup(destmbox);
607
608 for (r = s, w = destmbox; *r != '\0';) {
609 if (w - destmbox >= MBOX_LEN - 1)
610 break;
611 if (*r == '$') {
612 switch (*(r + 1)) {
613 case '_':
614 if (w + strlen(mbox) - destmbox >
615 MBOX_LEN - 1) {
616 r += 2;
617 break;
618 }
619 for (m = mbox; *m != '\0'; m++, w++)
620 *w = *m;
621 r += 2;
622 break;
623 case '$':
624 *w++ = '$';
625 r += 2;
626 break;
627 default:
628 *w++ = *r++;
629 break;
630 }
631 } else {
632 *w++ = *r++;
633 }
634 }
635 *w = '\0';
636
637 xfree(s);
638 }

webmaster@linux.gr
ViewVC Help
Powered by ViewVC 1.1.26