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

Contents of /imapfilter/action.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.10 - (show annotations)
Mon Feb 9 20:49:01 2004 UTC (20 years, 2 months ago) by lefcha
Branch: MAIN
Changes since 1.9: +6 -3 lines
File MIME type: text/plain
Wrap some long >80 lines.

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

webmaster@linux.gr
ViewVC Help
Powered by ViewVC 1.1.26