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

Annotation of /imapfilter/action.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.2 - (hide annotations)
Sun Aug 3 16:01:53 2003 UTC (20 years, 7 months ago) by lefcha
Branch: MAIN
CVS Tags: release-0_9
Branch point for: release-0_9-patches
Changes since 1.1: +1 -0 lines
File MIME type: text/plain
Added missing stdlib.h header.

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

webmaster@linux.gr
ViewVC Help
Powered by ViewVC 1.1.26