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

Contents of /imapfilter/action.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.2.2.3 - (show annotations)
Sun Nov 23 22:52:54 2003 UTC (20 years, 4 months ago) by lefcha
Branch: release-0_9-patches
Changes since 1.2.2.2: +10 -4 lines
File MIME type: text/plain
dded "peek" option that causes use of BODY or BODY.PEEK when doing FETCH.

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

webmaster@linux.gr
ViewVC Help
Powered by ViewVC 1.1.26