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

Annotation of /imapfilter/action.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.16 - (hide 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 lefcha 1.1 #include <stdio.h>
2 lefcha 1.2 #include <stdlib.h>
3 lefcha 1.1 #include <string.h>
4 lefcha 1.3 #include <sys/types.h>
5 lefcha 1.1 #include <time.h>
6    
7     #include "config.h"
8     #include "imapfilter.h"
9 lefcha 1.13 #include "filter.h"
10 lefcha 1.1
11    
12 lefcha 1.13 extern connection_t connpri, connaux;
13     extern options_t opts;
14 lefcha 1.1
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 lefcha 1.15 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 lefcha 1.11 int action_flag(char *mesgs, unsigned int *type, unsigned int *msgflags, char *args);
22 lefcha 1.1 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 lefcha 1.15 apply_action(char *mbox, char *mesgs, unsigned int *type, account_t * destacct,
37 lefcha 1.1 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 lefcha 1.15 log_info(LOG_DEST_ACCOUNT, destacct->key);
46 lefcha 1.13 log_info(LOG_DEST_MBOX, destmbox);
47 lefcha 1.1
48     cnt = count_messages(mesgs);
49    
50     switch (*type) {
51 lefcha 1.13 case ACTION_DELETE:
52 lefcha 1.1 info("%d message%s deleted.\n", cnt, plural(cnt));
53     action_delete(mesgs, args);
54     break;
55 lefcha 1.13 case ACTION_COPY:
56 lefcha 1.1 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 lefcha 1.15 connpri.ns.prefix, connpri.ns.delim), args);
60 lefcha 1.1 break;
61 lefcha 1.13 case ACTION_MOVE:
62 lefcha 1.1 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 lefcha 1.15 connpri.ns.prefix, connpri.ns.delim), args);
66 lefcha 1.1 break;
67 lefcha 1.13 case ACTION_RCOPY:
68 lefcha 1.1 info("%d message%s copied from \"%s\" to mailbox "
69 lefcha 1.15 "\"%s\" at account %s.\n", cnt, plural(cnt), mbox,
70     destmbox, destacct->key);
71     action_rcopy(mbox, mesgs, destacct, destmbox, args);
72 lefcha 1.1 break;
73 lefcha 1.13 case ACTION_RMOVE:
74 lefcha 1.1 info("%d message%s moved from \"%s\" to mailbox "
75 lefcha 1.15 "\"%s\" at account %s.\n", cnt, plural(cnt), mbox,
76     destmbox, destacct->key);
77     action_rmove(mbox, mesgs, destacct, destmbox, args);
78 lefcha 1.1 break;
79 lefcha 1.13 case ACTION_FLAG_REPLACE:
80     case ACTION_FLAG_ADD:
81     case ACTION_FLAG_REMOVE:
82 lefcha 1.1 info("%d message%s flagged.\n", cnt, plural(cnt));
83     action_flag(mesgs, type, msgflags, args);
84     break;
85 lefcha 1.13 case ACTION_LIST:
86 lefcha 1.1 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 lefcha 1.13 response_generic(&connpri, imap_store(&connpri, tok,
113 lefcha 1.15 ACTION_FLAG_ADD, "\\Deleted"));
114 lefcha 1.1
115     tok = strtok_r(NULL, " ", &m);
116     }
117    
118 lefcha 1.12 if (opts.expunge)
119 lefcha 1.13 response_generic(&connpri, imap_expunge(&connpri));
120 lefcha 1.1
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 lefcha 1.13 char dm[2][MBOX_LEN];
136 lefcha 1.1
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 lefcha 1.13 xstrncpy(dm[0], destmbox, MBOX_LEN - 1);
148 lefcha 1.1 default_variables(mbox, dm[0]);
149     current_date(dm[0]);
150     tok = strtok_r(m, " ", &m);
151     while (tok != NULL) {
152 lefcha 1.13 xstrncpy(dm[1], dm[0], MBOX_LEN - 1);
153 lefcha 1.1 message_date(tok, dm[1]);
154    
155 lefcha 1.13 if ((r = response_copy(&connpri, imap_copy(&connpri, tok,
156 lefcha 1.15 dm[1]))) == RESPONSE_TRYCREATE)
157 lefcha 1.13 if (!response_generic(&connpri, imap_create(&connpri,
158 lefcha 1.15 dm[1]))) {
159 lefcha 1.12 if (opts.subscribe)
160 lefcha 1.13 response_generic(&connpri,
161 lefcha 1.1 imap_subscribe(&connpri, dm[1]));
162 lefcha 1.13 r = response_copy(&connpri,
163 lefcha 1.1 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 lefcha 1.14
181 lefcha 1.1 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 lefcha 1.15 action_rcopy(char *mbox, char *mesgs, account_t * destacct, char *destmbox,
193 lefcha 1.1 char *args)
194     {
195     int r, ta, tf;
196     char *tok, *m, *mcp, *ndm;
197 lefcha 1.7 char *flags, *date;
198     unsigned int size;
199 lefcha 1.1 char buf[RESPONSE_BUF * 2 + 1];
200 lefcha 1.13 char dm[3][MBOX_LEN];
201 lefcha 1.1
202     *dm[0] = *dm[1] = *dm[2] = '\0';
203 lefcha 1.7 flags = date = NULL;
204 lefcha 1.1
205 lefcha 1.15 if (init_connection(&connaux, destacct->server, destacct->port,
206     destacct->ssl))
207 lefcha 1.1 return ERROR_NETWORK;
208    
209 lefcha 1.13 r = response_greeting(&connaux);
210 lefcha 1.1
211 lefcha 1.12 if (opts.debug)
212 lefcha 1.9 test(&connaux);
213 lefcha 1.1
214     if (r == RESPONSE_BYE || check_capabilities(&connaux))
215     return ERROR_NETWORK;
216    
217     #ifdef SSL_TLS
218 lefcha 1.16 if (destacct->ssl == SSL_DISABLED && connaux.caps & CAPABILITY_STARTTLS)
219 lefcha 1.1 if (negotiate_tls(&connaux) == RESPONSE_OK)
220     check_capabilities(&connaux);
221     #endif
222    
223     if (r != RESPONSE_PREAUTH) {
224 lefcha 1.15 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 lefcha 1.1 }
230     #ifdef CRAM_MD5
231 lefcha 1.16 if (connaux.caps & CAPABILITY_CRAMMD5)
232 lefcha 1.15 r = auth_cram_md5(&connaux, destacct->user,
233     destacct->pass);
234 lefcha 1.1 else
235     #endif
236 lefcha 1.15 r = login(&connaux, destacct->user, destacct->pass);
237 lefcha 1.1
238     if (r == RESPONSE_NO) {
239     error("username %s or password rejected at %s\n",
240 lefcha 1.15 destacct->user, destacct->server);
241 lefcha 1.1 return ERROR_NETWORK;
242     }
243     }
244     check_namespace(&connaux);
245    
246     m = mcp = xstrdup(mesgs);
247    
248 lefcha 1.13 xstrncpy(dm[1], destmbox, MBOX_LEN - 1);
249 lefcha 1.4 default_variables(mbox, dm[1]);
250 lefcha 1.1 current_date(dm[1]);
251    
252     tok = strtok_r(m, " ", &m);
253     while (tok != NULL) {
254 lefcha 1.13 xstrncpy(dm[2], dm[1], MBOX_LEN - 1);
255 lefcha 1.1 message_date(tok, dm[2]);
256    
257     /* apply_namespace() returns a pointer to a static buffer. */
258 lefcha 1.13 ndm = apply_namespace(dm[2], connaux.ns.prefix,
259     connaux.ns.delim);
260 lefcha 1.1
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 lefcha 1.13 response_generic(&connaux,
266 lefcha 1.1 imap_create(&connaux, ndm));
267 lefcha 1.12 if (opts.subscribe)
268 lefcha 1.13 response_generic(&connaux,
269 lefcha 1.1 imap_subscribe(&connaux, ndm));
270     }
271     }
272 lefcha 1.13 xstrncpy(dm[0], dm[2], MBOX_LEN - 1);
273 lefcha 1.1
274 lefcha 1.13 response_fetchfast(&connpri, &flags, &date, &size,
275 lefcha 1.7 imap_fetch(&connpri, tok, "FAST"));
276 lefcha 1.1
277 lefcha 1.7 ta = imap_append(&connaux, ndm, flags, date, size);
278    
279     xfree(flags);
280     xfree(date);
281 lefcha 1.1
282 lefcha 1.13 response_fetch(&connpri, 0, 1, NULL);
283 lefcha 1.12 tf = imap_fetch(&connpri, tok, opts.peek ?
284 lefcha 1.5 "BODY.PEEK[HEADER]" : "BODY[HEADER]");
285 lefcha 1.1 do {
286 lefcha 1.13 r = response_fetch(&connpri, tf, 0, buf);
287 lefcha 1.1 socket_write(&connaux, buf);
288     } while (r == RESPONSE_NONE);
289    
290     socket_write(&connaux, "\r\n");
291    
292 lefcha 1.13 response_fetch(&connpri, 0, 1, NULL);
293 lefcha 1.12 tf = imap_fetch(&connpri, tok, opts.peek ?
294 lefcha 1.5 "BODY.PEEK[TEXT]" : "BODY[TEXT]");
295 lefcha 1.1 do {
296 lefcha 1.13 r = response_fetch(&connpri, tf, 0, buf);
297 lefcha 1.1 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 lefcha 1.13 response_append(&connaux, ta);
307 lefcha 1.1
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 lefcha 1.15 action_rmove(char *mbox, char *mesgs, account_t * destacct, char *destmbox,
326 lefcha 1.1 char *args)
327     {
328 lefcha 1.14
329 lefcha 1.15 if (!action_rcopy(mbox, mesgs, destacct, destmbox, args))
330 lefcha 1.1 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 lefcha 1.13 if ((*msgflags != MSG_FLAG_NONE)) {
349     if ((*msgflags & MSG_FLAG_SEEN))
350 lefcha 1.1 strncat(s, "\\Seen ", STORE_FLAGS_BUF - strlen(s) - 1);
351 lefcha 1.13 if ((*msgflags & MSG_FLAG_ANSWERED))
352 lefcha 1.1 strncat(s, "\\Answered ",
353     STORE_FLAGS_BUF - strlen(s) - 1);
354 lefcha 1.13 if ((*msgflags & MSG_FLAG_FLAGGED))
355 lefcha 1.1 strncat(s, "\\Flagged ",
356     STORE_FLAGS_BUF - strlen(s) - 1);
357 lefcha 1.13 if ((*msgflags & MSG_FLAG_DELETED))
358 lefcha 1.1 strncat(s, "\\Deleted ",
359     STORE_FLAGS_BUF - strlen(s) - 1);
360 lefcha 1.13 if ((*msgflags & MSG_FLAG_DRAFT))
361 lefcha 1.1 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 lefcha 1.13 response_generic(&connpri, imap_store(&connpri, tok, *type, s));
372 lefcha 1.1 tok = strtok_r(NULL, " ", &m);
373     }
374    
375 lefcha 1.12 if (opts.expunge)
376 lefcha 1.13 response_generic(&connpri, imap_expunge(&connpri));
377 lefcha 1.1
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 lefcha 1.12 snprintf(s, ARGS_LEN + 27 - 1, opts.peek ?
400 lefcha 1.5 "BODY.PEEK[HEADER.FIELDS (%s)]" :
401 lefcha 1.6 "BODY[HEADER.FIELDS (%s)]", args);
402 lefcha 1.1
403     tok = strtok_r(m, " ", &m);
404     while (tok != NULL) {
405     /* Reset internal fetch counter. */
406 lefcha 1.13 response_fetch(&connpri, 0, 1, NULL);
407 lefcha 1.1 t = imap_fetch(&connpri, tok, s);
408    
409     log_info(LOG_PREAMBLE, NULL);
410     do {
411 lefcha 1.13 r = response_fetch(&connpri, t, 0, hdrs);
412 lefcha 1.1
413     if (*hdrs != '\0') {
414 lefcha 1.12 if (opts.headers)
415 lefcha 1.1 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 lefcha 1.15 * 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 lefcha 1.1 */
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 lefcha 1.15 * 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 lefcha 1.1 */
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 lefcha 1.13 char s[MBOX_LEN];
550 lefcha 1.1 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 lefcha 1.13 if (strftime(s, MBOX_LEN - 1, destmbox, tl))
560     xstrncpy(destmbox, s, MBOX_LEN - 1);
561 lefcha 1.1 }
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 lefcha 1.13 char s[MBOX_LEN];
573 lefcha 1.1 struct tm tl;
574     char dbuf[RESPONSE_BUF + 1];
575    
576     if (!strchr(destmbox, '@'))
577     return;
578    
579     substitute_date(destmbox);
580    
581 lefcha 1.13 response_fetch(&connpri, 0, 1, NULL);
582 lefcha 1.12 t = imap_fetch(&connpri, mesg, opts.peek ?
583 lefcha 1.5 "BODY.PEEK[HEADER.FIELDS (DATE)]" :
584     "BODY[HEADER.FIELDS (DATE)]");
585 lefcha 1.1
586 lefcha 1.13 while (response_fetch(&connpri, t, 0, dbuf) == RESPONSE_NONE);
587 lefcha 1.1
588 lefcha 1.8 if (((strptime(dbuf, "Date: %a, %d %b %Y %H:%M:%S", &tl) ||
589 lefcha 1.15 strptime(dbuf, "Date: %d %b %Y %H:%M:%S", &tl)) &&
590     strftime(s, MBOX_LEN - 1, destmbox, &tl)))
591 lefcha 1.13 xstrncpy(destmbox, s, MBOX_LEN - 1);
592 lefcha 1.1 }
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 lefcha 1.13 if (w - destmbox >= MBOX_LEN - 1)
610 lefcha 1.1 break;
611     if (*r == '$') {
612     switch (*(r + 1)) {
613     case '_':
614     if (w + strlen(mbox) - destmbox >
615 lefcha 1.13 MBOX_LEN - 1) {
616 lefcha 1.1 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