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

Contents of /imapfilter/action.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.12 - (show annotations)
Tue Feb 10 22:21:08 2004 UTC (20 years, 1 month ago) by lefcha
Branch: MAIN
Changes since 1.11: +11 -11 lines
File MIME type: text/plain
Replaced integer options and bitwise OPTION_* with an options struct.

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 opts_t opts;
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 (opts.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 (opts.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 if (opts.debug)
210 test(&connaux);
211
212 if (r == RESPONSE_BYE || check_capabilities(&connaux))
213 return ERROR_NETWORK;
214
215 #ifdef SSL_TLS
216 if (destacc->ssl == SSL_DISABLED && connaux.caps & CAPABILITY_STARTTLS)
217 if (negotiate_tls(&connaux) == RESPONSE_OK)
218 check_capabilities(&connaux);
219 #endif
220
221 if (r != RESPONSE_PREAUTH) {
222 if (destacc->passwdattr == PASSWORD_NONE) {
223 printf("Enter password for %s@%s: ", destacc->username,
224 destacc->server);
225 get_password(destacc->password, PASSWORD_LEN);
226 destacc->passwdattr = PASSWORD_PLAIN;
227 }
228 #ifdef CRAM_MD5
229 if (connaux.caps & CAPABILITY_AUTH_CRAM_MD5)
230 r = auth_cram_md5(&connaux, destacc->username,
231 destacc->password);
232 else
233 #endif
234 r = login(&connaux, destacc->username,
235 destacc->password);
236
237 if (r == RESPONSE_NO) {
238 error("username %s or password rejected at %s\n",
239 destacc->username, destacc->server);
240 return ERROR_NETWORK;
241 }
242 }
243 check_namespace(&connaux);
244
245 m = mcp = xstrdup(mesgs);
246
247 xstrncpy(dm[1], destmbox, MBOX_NAME_LEN - 1);
248 default_variables(mbox, dm[1]);
249 current_date(dm[1]);
250
251 tok = strtok_r(m, " ", &m);
252 while (tok != NULL) {
253 xstrncpy(dm[2], dm[1], MBOX_NAME_LEN - 1);
254 message_date(tok, dm[2]);
255
256 /* apply_namespace() returns a pointer to a static buffer. */
257 ndm = apply_namespace(dm[2], connaux.nsp.prefix,
258 connaux.nsp.delim);
259
260 /* Check only if mailbox name is different from last one. */
261 if (strncmp(dm[0], dm[2], strlen(dm[2]))) {
262 r = check_mailbox(&connaux, ndm);
263 if (r == RESPONSE_NO) {
264 server_response(&connaux,
265 imap_create(&connaux, ndm));
266 if (opts.subscribe)
267 server_response(&connaux,
268 imap_subscribe(&connaux, ndm));
269 }
270 }
271 xstrncpy(dm[0], dm[2], MBOX_NAME_LEN - 1);
272
273 fetchfast_response(&connpri, &flags, &date, &size,
274 imap_fetch(&connpri, tok, "FAST"));
275
276 ta = imap_append(&connaux, ndm, flags, date, size);
277
278 xfree(flags);
279 xfree(date);
280
281 fetch_response(&connpri, 0, 1, NULL);
282 tf = imap_fetch(&connpri, tok, opts.peek ?
283 "BODY.PEEK[HEADER]" : "BODY[HEADER]");
284 do {
285 r = fetch_response(&connpri, tf, 0, buf);
286 socket_write(&connaux, buf);
287 } while (r == RESPONSE_NONE);
288
289 socket_write(&connaux, "\r\n");
290
291 fetch_response(&connpri, 0, 1, NULL);
292 tf = imap_fetch(&connpri, tok, opts.peek ?
293 "BODY.PEEK[TEXT]" : "BODY[TEXT]");
294 do {
295 r = fetch_response(&connpri, tf, 0, buf);
296 if (r != RESPONSE_NULLBODY)
297 socket_write(&connaux, buf);
298 } while (r == RESPONSE_NONE);
299
300 if (r != RESPONSE_NULLBODY)
301 socket_write(&connaux, "\r\n\r\n");
302 else
303 socket_write(&connaux, "\r\n");
304
305 append_response(&connaux, ta);
306
307 tok = strtok_r(NULL, " ", &m);
308 }
309
310 logout(&connaux);
311
312 action_list(mesgs, args);
313
314 xfree(mcp);
315
316 return 0;
317 }
318
319
320 /*
321 * Move messages to the specified mailbox of another mail server.
322 */
323 int
324 action_rmove(char *mbox, char *mesgs, account_t * destacc, char *destmbox,
325 char *args)
326 {
327 if (!action_rcopy(mbox, mesgs, destacc, destmbox, args))
328 action_delete(mesgs, "\0");
329
330 return 0;
331 }
332
333
334 /*
335 * Flag messages by replacing, adding or removing specified flags.
336 */
337 int
338 action_flag(char *mesgs, unsigned int *type, unsigned int *msgflags,
339 char *args)
340 {
341 unsigned int t;
342 char s[STORE_FLAGS_BUF];
343 char *tok, *m, *mcp;
344
345 *s = 0;
346
347 switch (*type) {
348 case FILTER_ACTION_FLAG_ADD:
349 t = STORE_FLAG_ADD;
350 break;
351 case FILTER_ACTION_FLAG_REMOVE:
352 t = STORE_FLAG_REMOVE;
353 break;
354 default:
355 t = STORE_FLAG_REPLACE;
356 }
357
358 if ((*msgflags != MESSAGE_FLAG_NONE)) {
359 if ((*msgflags & MESSAGE_FLAG_SEEN))
360 strncat(s, "\\Seen ", STORE_FLAGS_BUF - strlen(s) - 1);
361 if ((*msgflags & MESSAGE_FLAG_ANSWERED))
362 strncat(s, "\\Answered ",
363 STORE_FLAGS_BUF - strlen(s) - 1);
364 if ((*msgflags & MESSAGE_FLAG_FLAGGED))
365 strncat(s, "\\Flagged ",
366 STORE_FLAGS_BUF - strlen(s) - 1);
367 if ((*msgflags & MESSAGE_FLAG_DELETED))
368 strncat(s, "\\Deleted ",
369 STORE_FLAGS_BUF - strlen(s) - 1);
370 if ((*msgflags & MESSAGE_FLAG_DRAFT))
371 strncat(s, "\\Draft", STORE_FLAGS_BUF - strlen(s) - 1);
372 if ((s[strlen(s) - 1] == ' '))
373 s[strlen(s) - 1] = 0;
374 }
375 action_list(mesgs, args);
376
377 m = mcp = convert_messages(mesgs);
378
379 tok = strtok_r(m, " ", &m);
380 while (tok != NULL) {
381 server_response(&connpri, imap_store(&connpri, tok, t, s));
382
383 tok = strtok_r(NULL, " ", &m);
384 }
385
386 if (opts.expunge)
387 server_response(&connpri, imap_expunge(&connpri));
388
389 xfree(mcp);
390
391 return 0;
392 }
393
394 /*
395 * List user selected headers of messages.
396 */
397 int
398 action_list(char *mesgs, char *args)
399 {
400 int r, t;
401 char *tok, *mcp, *m;
402 char s[ARGS_LEN + 27];
403 char hdrs[RESPONSE_BUF * 2 + 1];
404
405 if (*args == '\0')
406 return 0;
407
408 m = mcp = xstrdup(mesgs);
409
410 snprintf(s, ARGS_LEN + 27 - 1, opts.peek ?
411 "BODY.PEEK[HEADER.FIELDS (%s)]" :
412 "BODY[HEADER.FIELDS (%s)]", args);
413
414 tok = strtok_r(m, " ", &m);
415 while (tok != NULL) {
416 /* Reset internal fetch counter. */
417 fetch_response(&connpri, 0, 1, NULL);
418 t = imap_fetch(&connpri, tok, s);
419
420 log_info(LOG_PREAMBLE, NULL);
421 do {
422 r = fetch_response(&connpri, t, 0, hdrs);
423
424 if (*hdrs != '\0') {
425 if (opts.headers)
426 info("%s\n", hdrs);
427 log_info(LOG_HEADER, hdrs);
428 }
429 } while (r == RESPONSE_NONE);
430
431 tok = strtok_r(NULL, " ", &m);
432 }
433
434 xfree(mcp);
435
436 return 0;
437 }
438
439
440 /*
441 * Count how many messages matched the filter.
442 */
443 unsigned int
444 count_messages(char *mesgs)
445 {
446 unsigned int cnt;
447 char *c;
448
449 cnt = 0;
450 c = mesgs;
451
452 while ((c = strchr(c, ' '))) {
453 cnt++;
454 c++;
455 }
456
457 return ++cnt;
458 }
459
460
461 /*
462 * Convert messages with contiguous sequence number to the corresponding
463 * number range, eg. 1 2 3 5 7 8 --> 1:3 5 7:8 and return a newly allocated
464 * buffer with the results.
465 */
466 char *
467 convert_messages(char *mesgs)
468 {
469 int maxlen;
470 unsigned int start, end, tmp;
471 char *c, *cp, *tail;
472
473 start = end = tmp = 0;
474 maxlen = strlen(mesgs) + 1;
475 tail = NULL;
476
477 c = cp = xstrdup(mesgs);
478
479 start = (unsigned int)strtoul(mesgs, &tail, 10);
480 end = start;
481
482 do {
483 if (tail != NULL) {
484 tmp = (unsigned int)strtoul(tail, &tail, 10);
485 if (tmp == 0)
486 tail = NULL;
487 }
488 if (tmp == end + 1)
489 end++;
490 else {
491 if (start == end) {
492 xstrncpy(c, ultostr(start, 10), maxlen);
493 c += strlen(c);
494 } else {
495 xstrncpy(c, ultostr(start, 10), maxlen - 1);
496 c += strlen(c);
497 *c = ':';
498 *++c = 0;
499 xstrncpy(c, ultostr(end, 10), maxlen);
500 c += strlen(c);
501 }
502
503 if (tail != NULL && c - cp < maxlen) {
504 *c = ' ';
505 *++c = 0;
506 }
507 start = end = tmp;
508 }
509 } while (tmp != 0);
510
511 return cp;
512 }
513
514
515 /*
516 * Substitute all occurences of the '@' character with the '%' character,
517 * and any double '@' characters with a signle '@' character, returning the
518 * number of replacements.
519 */
520 int
521 substitute_date(char *str)
522 {
523 int n;
524 char *r, *w, *s;
525
526 n = 0;
527
528 s = xstrdup(str);
529
530 for (r = s, w = str; *r != '\0'; r++, w++) {
531 if (*r == '%') {
532 *w++ = '%';
533 *w = '%';
534 } else if (*r == '@') {
535 if (*(r + 1) == '@') {
536 *w = *r++;
537 } else {
538 *w = '%';
539 n++;
540 }
541 } else {
542 *w = *r;
543 }
544 }
545 *w = '\0';
546
547 xfree(s);
548
549 return n;
550 }
551
552
553 /*
554 * Format the destination mailbox according to the current date conversion
555 * specifiers.
556 */
557 void
558 current_date(char *destmbox)
559 {
560 char s[MBOX_NAME_LEN];
561 time_t te;
562 struct tm *tl;
563
564 if (!strchr(destmbox, '%'))
565 return;
566
567 te = time(NULL);
568 tl = localtime(&te);
569
570 if (strftime(s, MBOX_NAME_LEN - 1, destmbox, tl))
571 xstrncpy(destmbox, s, MBOX_NAME_LEN - 1);
572 }
573
574
575 /*
576 * Format the destination mailbox according to the message date conversion
577 * specifiers.
578 */
579 void
580 message_date(char *mesg, char *destmbox)
581 {
582 unsigned int t;
583 char s[MBOX_NAME_LEN];
584 struct tm tl;
585 char dbuf[RESPONSE_BUF + 1];
586
587 if (!strchr(destmbox, '@'))
588 return;
589
590 substitute_date(destmbox);
591
592 fetch_response(&connpri, 0, 1, NULL);
593 t = imap_fetch(&connpri, mesg, opts.peek ?
594 "BODY.PEEK[HEADER.FIELDS (DATE)]" :
595 "BODY[HEADER.FIELDS (DATE)]");
596
597 while (fetch_response(&connpri, t, 0, dbuf) == RESPONSE_NONE);
598
599 if (((strptime(dbuf, "Date: %a, %d %b %Y %H:%M:%S", &tl) ||
600 strptime(dbuf, "Date: %d %b %Y %H:%M:%S", &tl)) &&
601 strftime(s, MBOX_NAME_LEN - 1, destmbox, &tl)))
602 xstrncpy(destmbox, s, MBOX_NAME_LEN - 1);
603 }
604
605
606 /*
607 * Format the destination mailbox according to "default variables" specifiers.
608 */
609 void
610 default_variables(char *mbox, char *destmbox)
611 {
612 char *m, *r, *w, *s;
613
614 if (!strchr(destmbox, '$'))
615 return;
616
617 s = xstrdup(destmbox);
618
619 for (r = s, w = destmbox; *r != '\0';) {
620 if (w - destmbox >= MBOX_NAME_LEN - 1)
621 break;
622 if (*r == '$') {
623 switch (*(r + 1)) {
624 case '_':
625 if (w + strlen(mbox) - destmbox >
626 MBOX_NAME_LEN - 1) {
627 r += 2;
628 break;
629 }
630 for (m = mbox; *m != '\0'; m++, w++)
631 *w = *m;
632 r += 2;
633 break;
634 case '$':
635 *w++ = '$';
636 r += 2;
637 break;
638 default:
639 *w++ = *r++;
640 break;
641 }
642 } else {
643 *w++ = *r++;
644 }
645 }
646 *w = '\0';
647
648 xfree(s);
649 }

webmaster@linux.gr
ViewVC Help
Powered by ViewVC 1.1.26