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

Annotation of /imapfilter/action.c

Parent Directory Parent Directory | Revision Log Revision Log


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

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    
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 lefcha 1.10 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 lefcha 1.1 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 lefcha 1.7 char *flags, *date;
199     unsigned int size;
200 lefcha 1.1 char buf[RESPONSE_BUF * 2 + 1];
201     char dm[3][MBOX_NAME_LEN];
202    
203     *dm[0] = *dm[1] = *dm[2] = '\0';
204 lefcha 1.7 flags = date = NULL;
205 lefcha 1.1
206     if (init_connection(&connaux, destacc->server, destacc->port,
207     destacc->ssl))
208     return ERROR_NETWORK;
209    
210     r = greeting_response(&connaux);
211    
212 lefcha 1.9 if (options & OPTION_DEBUG)
213     test(&connaux);
214 lefcha 1.1
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 lefcha 1.4 default_variables(mbox, dm[1]);
252 lefcha 1.1 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 lefcha 1.7 fetchfast_response(&connpri, &flags, &date, &size,
277     imap_fetch(&connpri, tok, "FAST"));
278 lefcha 1.1
279 lefcha 1.7 ta = imap_append(&connaux, ndm, flags, date, size);
280    
281     xfree(flags);
282     xfree(date);
283 lefcha 1.1
284     fetch_response(&connpri, 0, 1, NULL);
285 lefcha 1.5 tf = imap_fetch(&connpri, tok, options & OPTION_PEEK ?
286     "BODY.PEEK[HEADER]" : "BODY[HEADER]");
287 lefcha 1.1 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 lefcha 1.5 tf = imap_fetch(&connpri, tok, options & OPTION_PEEK ?
296     "BODY.PEEK[TEXT]" : "BODY[TEXT]");
297 lefcha 1.1 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 lefcha 1.5 snprintf(s, ARGS_LEN + 27 - 1, options & OPTION_PEEK ?
414     "BODY.PEEK[HEADER.FIELDS (%s)]" :
415 lefcha 1.6 "BODY[HEADER.FIELDS (%s)]", args);
416 lefcha 1.1
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 lefcha 1.5 t = imap_fetch(&connpri, mesg, options & OPTION_PEEK ?
597     "BODY.PEEK[HEADER.FIELDS (DATE)]" :
598     "BODY[HEADER.FIELDS (DATE)]");
599 lefcha 1.1
600     while (fetch_response(&connpri, t, 0, dbuf) == RESPONSE_NONE);
601    
602 lefcha 1.8 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 lefcha 1.1 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