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

Contents of /imapfilter/request.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.51 - (show annotations)
Sun Jul 27 10:08:42 2003 UTC (20 years, 8 months ago) by lefcha
Branch: MAIN
Changes since 1.50: +25 -0 lines
File MIME type: text/plain
Added STARTTLS support.

1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <errno.h>
5 #include <time.h>
6
7 #include "config.h"
8 #include "imapfilter.h"
9 #include "data.h"
10
11
12 extern int sockpri, sockaux;
13 extern unsigned int options;
14 extern char charset[CHARSET_LEN];
15 extern unsigned int capspri, capsaux;
16
17 namesp_t nsppri, nspaux; /* Primary and auxiliary namespace. */
18
19
20 #ifdef DEBUG
21 /*
22 * Test/ping server.
23 */
24 int
25 test(int *sock)
26 {
27 return server_response(sock, imap_noop(sock));
28 }
29
30 #endif
31
32
33 /*
34 * Check server's capabilities.
35 */
36 int
37 check_capabilities(int *sock)
38 {
39 if (sock == &sockpri)
40 capspri = CAPABILITY_NONE;
41 else
42 capsaux = CAPABILITY_NONE;
43
44 return capability_response(sock, imap_capability(sock));
45 }
46
47
48 /*
49 * Get namespace of mail server's mailboxes.
50 */
51 int
52 check_namespace(int *sock, namesp_t * nsp)
53 {
54 unsigned int *caps;
55
56 caps = (sock == &sockpri ? &capspri : &capsaux);
57 nsp->prefix[0] = nsp->delim = '\0';
58
59 if (!(options & OPTION_NAMESPACE) ||
60 !(*caps & CAPABILITY_NAMESPACE))
61 return 0;
62 else
63 return namespace_response(sock, imap_namespace(sock), nsp);
64 }
65
66
67 #ifdef SSL_TLS
68 /*
69 * Begin TLS negotiation (STARTTLS).
70 */
71 int
72 imf_starttls(int *sock)
73 {
74 unsigned int t;
75 int r;
76
77 r = server_response(sock, imap_starttls(sock));
78 imf_ssl_init(sock, SSL_TLS_V1);
79
80 return r;
81 }
82
83 #endif
84
85
86 /*
87 * Login to server.
88 */
89 int
90 login(int *sock, char *user, char *pass)
91 {
92 return server_response(sock, imap_login(sock, user, pass));
93 }
94
95
96
97 /*
98 * Check if a mailbox exists.
99 */
100 int
101 check_mailbox(int *sock, char *mbox)
102 {
103 return server_response(sock, imap_status(sock, mbox, "MESSAGES"));
104 }
105
106
107 /*
108 * Open mailbox in read-write mode.
109 */
110 int
111 select_mailbox(int *sock, char *mbox, namesp_t * nsp)
112 {
113 int r;
114
115 if (mailbox_status(sock, mbox, nsp) == -2)
116 return -2; /* No messages exist. No filters need to be
117 * applied. */
118
119 r = select_response(sock, imap_select(sock,
120 apply_namespace(mbox, nsp->prefix,
121 nsp->delim)));
122
123 log_info(LOG_MAILBOX, mbox);
124
125 return r;
126 }
127
128
129 /*
130 * Get mailbox's status.
131 */
132 int
133 mailbox_status(int *sock, char *mbox, namesp_t * nsp)
134 {
135 return status_response(sock, imap_status(sock,
136 apply_namespace(mbox, nsp->prefix, nsp->delim),
137 "MESSAGES RECENT UNSEEN"), mbox);
138 }
139
140
141 /*
142 * Close examined/selected mailbox.
143 */
144 int
145 close_mailbox(int *sock)
146 {
147 return server_response(sock, imap_close(sock));
148 }
149
150
151 /*
152 * Logout from server.
153 */
154 int
155 logout(int *sock)
156 {
157 return logout_response(sock, imap_logout(sock));
158 }
159
160
161 /*
162 * Match and apply filters assigned to a mailbox.
163 */
164 int
165 apply_filters(char *mbox, filter_t ** filters)
166 {
167 int i;
168 char *mesgs;
169
170 for (i = 0; filters[i] != NULL; i++) {
171 mesgs = NULL;
172
173 if (match_filter(filters[i], &mesgs))
174 continue;
175
176 log_info(LOG_FILTER, filters[i]->key);
177
178 apply_action(mbox, mesgs, &(filters[i]->action.type),
179 filters[i]->action.raccount, filters[i]->action.destmbox,
180 &filters[i]->action.msgflags, filters[i]->action.args);
181
182 xfree(mesgs);
183 }
184
185 return 0;
186 }
187
188
189 /*
190 * Generate the search request by the masks of the filter and try to
191 * match the generated filter.
192 */
193 int
194 match_filter(filter_t * filter, char **mesgs)
195 {
196 char *search;
197
198 if (filter->mode == FILTER_MODE_OR)
199 search = generate_filter_or(filter->masks, filter->masknum,
200 filter->masklen);
201 else
202 search = generate_filter_and(filter->masks, filter->masknum,
203 filter->masklen);
204
205 search_response(&sockpri, imap_search(&sockpri, charset, search),
206 mesgs);
207
208 xfree(search);
209
210 if (*mesgs == '\0')
211 return 1;
212
213 return 0;
214 }
215
216
217 /*
218 * Empty the FIFO inventory.
219 */
220 void
221 empty_fifo(mask_t ** mfifo)
222 {
223 mfifo[0] = NULL;
224
225 queue_fifo(NULL, NULL);
226 dequeue_fifo(NULL);
227 }
228
229
230 /*
231 * Add item to FIFO inventory.
232 */
233 void
234 queue_fifo(mask_t ** mfifo, mask_t * mask)
235 {
236 static unsigned int i;
237
238 if (mfifo == NULL) {
239 i = 0;
240 return;
241 }
242 mfifo[i++] = mask;
243 mfifo[i] = NULL;
244 }
245
246
247 /*
248 * Get next item from FIFO inventory.
249 */
250 mask_t *
251 dequeue_fifo(mask_t ** mfifo)
252 {
253 static unsigned int j;
254
255 if (mfifo == NULL) {
256 j = 0;
257 return NULL;
258 }
259 return mfifo[j++];
260 }
261
262
263 /*
264 * Generate the filter search command from the masks, assuming that
265 * masks are AND-ed.
266 */
267 char *
268 generate_filter_and(mask_t * mask, unsigned int masknum,
269 unsigned int masklen)
270 {
271 const unsigned int searchbuf = masklen + masknum * 6 + 8;
272 unsigned int len;
273 char *search;
274 mask_t *tmp;
275
276 len = 0;
277
278 search = (char *)xmalloc(sizeof(char) * searchbuf);
279 search[0] = '\0';
280
281 tmp = mask;
282 if (tmp == NULL) {
283 strncat(search, "ALL ", searchbuf - len - 1);
284 len += 4;
285 } else
286 while ((tmp = tmp->next) != NULL) {
287 if (tmp->type != MASK_TYPE_OR) {
288 strncat(search, "ALL ", searchbuf - len - 1);
289 len += 4;
290 break;
291 }
292 }
293
294 tmp = NULL;
295 while (mask != NULL) {
296 tmp = mask;
297 mask = mask->next;
298
299 if (mask != NULL && mask->type == MASK_TYPE_OR) {
300 strncat(search, "OR (", searchbuf - len - 1);
301 len += 4;
302
303 strncat(search, tmp->body, searchbuf - len - 1);
304 len = strlen(search);
305 search[len] = ' ';
306 search[++len] = '\0';
307
308 search[len - 1] = ')';
309 search[len] = ' ';
310 search[++len] = '\0';
311
312 if (mask->next == NULL ||
313 mask->next->type != MASK_TYPE_OR) {
314 search[len] = '(';
315 search[++len] = '\0';
316 strncat(search, mask->body,
317 searchbuf - len - 1);
318 len = strlen(search);
319 search[len] = ')';
320 search[++len] = ' ';
321 search[++len] = '\0';
322 mask = mask->next;
323 }
324 } else {
325 strncat(search, tmp->body, searchbuf - len - 1);
326 len = strlen(search);
327 search[len] = ' ';
328 search[++len] = '\0';
329 }
330 }
331
332 search[len - 1] = '\0';
333
334 return search;
335 }
336
337
338 /*
339 * Generate the filter search command from the masks, assuming that
340 * masks are OR-ed.
341 */
342 char *
343 generate_filter_or(mask_t * mask, unsigned int masknum,
344 unsigned int masklen)
345 {
346 const unsigned int searchbuf = masklen + masknum * 6 + 8;
347 unsigned int len;
348 char *search;
349 mask_t **mfifo; /* Mailbox FIFO queue. */
350 mask_t *mf; /* Mask returned from FIFO. */
351
352 len = 0;
353
354 search = (char *)xmalloc(sizeof(char) * searchbuf);
355 mfifo = (mask_t **) xmalloc(sizeof(mask_t *) * (masknum + 1));
356
357 search[0] = '\0';
358 empty_fifo(mfifo);
359
360 strncat(search, "ALL ", searchbuf - len - 1);
361 len += 4;
362
363 while (mask != NULL) {
364 queue_fifo(mfifo, mask);
365 mask = mask->next;
366
367 while (mask != NULL && mask->type == MASK_TYPE_AND) {
368 queue_fifo(mfifo, mask);
369 mask = mask->next;
370 }
371
372 if (mask != NULL) {
373 if (len == 4 && search[0] == 'A') {
374 search[0] = '\0';
375 len = 0;
376 }
377 strncat(search, "OR ", searchbuf - len - 1);
378 len += 3;
379 }
380 if (search[0] != 'A') {
381 search[len] = '(';
382 search[++len] = '\0';
383 }
384 while ((mf = dequeue_fifo(mfifo)) != NULL) {
385 strncat(search, mf->body, searchbuf - len - 1);
386 len = strlen(search);
387 search[len] = ' ';
388 search[++len] = '\0';
389 }
390
391 if (strchr(search, '(')) {
392 search[len - 1] = ')';
393 search[len] = ' ';
394 search[++len] = '\0';
395 }
396 empty_fifo(mfifo);
397 }
398
399 search[len - 1] = '\0';
400
401 xfree(mfifo);
402
403 return search;
404 }
405
406
407 /*
408 * Apply the appropriate action.
409 */
410 int
411 apply_action(char *mbox, char *mesgs, unsigned int *type, account_t * raccount,
412 char *destmbox, unsigned int *msgflags, char *args)
413 {
414 unsigned int cnt;
415
416 if (*mesgs == '\0')
417 return 0;
418
419 log_info(LOG_ACTION, type);
420 log_info(LOG_DESTINATION_ACCOUNT, raccount->key);
421 log_info(LOG_DESTINATION_MAILBOX, destmbox);
422
423 cnt = count_messages(mesgs);
424
425 switch (*type) {
426 case FILTER_ACTION_DELETE:
427 info("%d message%s deleted.\n", cnt, plural(cnt));
428 action_delete(mesgs, args);
429 break;
430 case FILTER_ACTION_COPY:
431 info("%d message%s copied from \"%s\" to mailbox \"%s\".\n",
432 cnt, plural(cnt), mbox, destmbox);
433 action_copy(mbox, mesgs, apply_namespace(destmbox,
434 nsppri.prefix, nsppri.delim), args);
435 break;
436 case FILTER_ACTION_MOVE:
437 info("%d message%s moved from \"%s\" to mailbox \"%s\".\n",
438 cnt, plural(cnt), mbox, destmbox);
439 action_move(mbox, mesgs, apply_namespace(destmbox,
440 nsppri.prefix, nsppri.delim), args);
441 break;
442 case FILTER_ACTION_RCOPY:
443 info("%d message%s copied from \"%s\" to mailbox "
444 "\"%s\" at account %s.\n", cnt, plural(cnt),
445 mbox, destmbox, raccount->key);
446 action_rcopy(mbox, mesgs, raccount, destmbox, args);
447 break;
448 case FILTER_ACTION_RMOVE:
449 info("%d message%s moved from \"%s\" to mailbox "
450 "\"%s\" at account %s.\n", cnt, plural(cnt),
451 mbox, destmbox, raccount->key);
452 action_rmove(mbox, mesgs, raccount, destmbox, args);
453 break;
454 case FILTER_ACTION_FLAG_REPLACE:
455 case FILTER_ACTION_FLAG_ADD:
456 case FILTER_ACTION_FLAG_REMOVE:
457 info("%d message%s flagged.\n", cnt, plural(cnt));
458 action_flag(mesgs, type, msgflags, args);
459 break;
460 case FILTER_ACTION_LIST:
461 info("%d message%s listed.\n", cnt, plural(cnt));
462 action_list(mesgs, args);
463 break;
464 }
465
466 if (*args == '\0')
467 log_info(LOG_PREAMBLE, NULL);
468
469 return 0;
470 }
471
472
473 /*
474 * Delete messages and optionally list some of their headers.
475 */
476 int
477 action_delete(char *mesgs, char *args)
478 {
479 char *tok, *m, *mcp;
480
481 action_list(mesgs, args);
482
483 m = mcp = convert_messages(mesgs);
484
485 tok = strtok_r(m, " ", &m);
486 while (tok) {
487 server_response(&sockpri, imap_store(&sockpri, tok,
488 STORE_FLAG_ADD, "\\Deleted"));
489
490 tok = strtok_r(NULL, " ", &m);
491 }
492
493 if (options & OPTION_EXPUNGE)
494 server_response(&sockpri, imap_expunge(&sockpri));
495
496 xfree(mcp);
497
498 return 0;
499 }
500
501
502 /*
503 * Copy messages to specified mailbox.
504 */
505 int
506 action_copy(char *mbox, char *mesgs, char *destmbox, char *args)
507 {
508 int r;
509 char *tok, *mcp, *m;
510 char dm[2][MBOX_NAME_LEN];
511
512 r = 0;
513 tok = NULL;
514
515 action_list(mesgs, args);
516
517 if (strchr(destmbox, '@'))
518 m = mcp = xstrdup(mesgs);
519 else
520 m = mcp = convert_messages(mesgs);
521
522 xstrncpy(dm[0], destmbox, MBOX_NAME_LEN - 1);
523 default_variables(mbox, dm[0]);
524 current_date(dm[0]);
525 tok = strtok_r(m, " ", &m);
526 while (tok != NULL) {
527 xstrncpy(dm[1], dm[0], MBOX_NAME_LEN - 1);
528 message_date(tok, dm[1]);
529
530 if ((r = copy_response(&sockpri, imap_copy(&sockpri, tok,
531 dm[1]))) == RESPONSE_TRYCREATE)
532 if (!server_response(&sockpri, imap_create(&sockpri,
533 dm[1]))) {
534 if ((options & OPTION_SUBSCRIBE))
535 server_response(&sockpri,
536 imap_subscribe(&sockpri, dm[1]));
537 r = copy_response(&sockpri,
538 imap_copy(&sockpri, tok, dm[1]));
539 }
540 tok = strtok_r(NULL, " ", &m);
541 }
542
543 xfree(mcp);
544
545 return r;
546 }
547
548
549 /*
550 * Move messages to specified mailbox.
551 */
552 int
553 action_move(char *mbox, char *mesgs, char *destmbox, char *args)
554 {
555 if (!action_copy(mbox, mesgs, destmbox, args))
556 action_delete(mesgs, "\0");
557
558 return 0;
559 }
560
561
562 /*
563 * Copy messages to the specified mailbox of another mail server.
564 */
565 int
566 action_rcopy(char *mbox, char *mesgs, account_t * destacc, char *destmbox,
567 char *args)
568 {
569 int r, ta, tf;
570 char *tok, *m, *mcp, *ndm;
571 unsigned int n;
572 char buf[RESPONSE_BUF * 2 + 1];
573 char dm[3][MBOX_NAME_LEN];
574
575 *dm[0] = *dm[1] = *dm[2] = '\0';
576
577 if (init_connection(&sockaux, destacc->server, destacc->port,
578 destacc->ssl))
579 return ERROR_NETWORK;
580
581 r = greeting_response(&sockaux);
582
583 #ifdef DEBUG
584 test(&sockaux);
585 #endif
586
587 if (r == RESPONSE_BYE || check_capabilities(&sockaux))
588 return ERROR_NETWORK;
589
590 #ifdef SSL_TLS
591 if (destacc->ssl == SSL_DISABLED && capsaux & CAPABILITY_STARTTLS)
592 if (imf_starttls(&sockaux) == RESPONSE_OK)
593 check_capabilities(&sockaux);
594 #endif
595
596 if (r != RESPONSE_PREAUTH) {
597 if (destacc->passwdattr == PASSWORD_NONE) {
598 printf("Enter password for %s@%s: ", destacc->username,
599 destacc->server);
600 get_password(destacc->password, PASSWORD_LEN);
601 destacc->passwdattr = PASSWORD_PLAIN;
602 }
603 #ifdef CRAM_MD5
604 if (capsaux & CAPABILITY_AUTH_CRAM_MD5)
605 r = imf_cram_md5(&sockaux, destacc->username,
606 destacc->password);
607 else
608 #endif
609 r = login(&sockaux, destacc->username,
610 destacc->password);
611
612 if (r == RESPONSE_NO) {
613 error("username %s or password rejected at %s\n",
614 destacc->username, destacc->server);
615 return ERROR_NETWORK;
616 }
617 }
618 check_namespace(&sockaux, &nspaux);
619
620 m = mcp = xstrdup(mesgs);
621
622 xstrncpy(dm[1], destmbox, MBOX_NAME_LEN - 1);
623 current_date(dm[1]);
624
625 tok = strtok_r(m, " ", &m);
626 while (tok != NULL) {
627 xstrncpy(dm[2], dm[1], MBOX_NAME_LEN - 1);
628 message_date(tok, dm[2]);
629
630 /* apply_namespace() returns a pointer to a static buffer. */
631 ndm = apply_namespace(dm[2], nspaux.prefix, nspaux.delim);
632
633 /* Check only if mailbox name is different from last one. */
634 if (strncmp(dm[0], dm[2], strlen(dm[2]))) {
635 r = check_mailbox(&sockaux, ndm);
636 if (r == RESPONSE_NO) {
637 server_response(&sockaux,
638 imap_create(&sockaux, ndm));
639 if ((options & OPTION_SUBSCRIBE))
640 server_response(&sockaux,
641 imap_subscribe(&sockaux, ndm));
642 }
643 }
644 xstrncpy(dm[0], dm[2], MBOX_NAME_LEN - 1);
645
646 fetchsize_response(&sockpri, &n,
647 imap_fetch(&sockpri, tok, "RFC822.SIZE"));
648
649 ta = imap_append(&sockaux, ndm, n);
650
651 fetch_response(&sockpri, 0, 1, NULL);
652 tf = imap_fetch(&sockpri, tok, "RFC822.HEADER");
653 do {
654 r = fetch_response(&sockpri, tf, 0, buf);
655 socket_write(&sockaux, buf);
656 } while (r == RESPONSE_NONE);
657
658 socket_write(&sockaux, "\r\n");
659
660 fetch_response(&sockpri, 0, 1, NULL);
661 tf = imap_fetch(&sockpri, tok, "BODY[TEXT]");
662 do {
663 r = fetch_response(&sockpri, tf, 0, buf);
664 if (r != RESPONSE_NULLBODY)
665 socket_write(&sockaux, buf);
666 } while (r == RESPONSE_NONE);
667
668 if (r != RESPONSE_NULLBODY)
669 socket_write(&sockaux, "\r\n\r\n");
670 else
671 socket_write(&sockaux, "\r\n");
672
673 append_response(&sockaux, ta);
674
675 tok = strtok_r(NULL, " ", &m);
676 }
677
678 logout(&sockaux);
679
680 action_list(mesgs, args);
681
682 xfree(mcp);
683
684 return 0;
685 }
686
687
688 /*
689 * Move messages to the specified mailbox of another mail server.
690 */
691 int
692 action_rmove(char *mbox, char *mesgs, account_t * destacc, char *destmbox,
693 char *args)
694 {
695 if (!action_rcopy(mbox, mesgs, destacc, destmbox, args))
696 action_delete(mesgs, "\0");
697
698 return 0;
699 }
700
701
702 /*
703 * Flag messages by replacing, adding or removing specified flags.
704 */
705 int
706 action_flag(char *mesgs, unsigned int *type, unsigned int *msgflags,
707 char *args)
708 {
709 unsigned int t;
710 char s[STORE_FLAGS_BUF];
711 char *tok, *m, *mcp;
712
713 *s = 0;
714
715 switch (*type) {
716 case FILTER_ACTION_FLAG_ADD:
717 t = STORE_FLAG_ADD;
718 break;
719 case FILTER_ACTION_FLAG_REMOVE:
720 t = STORE_FLAG_REMOVE;
721 break;
722 default:
723 t = STORE_FLAG_REPLACE;
724 }
725
726 if ((*msgflags != MESSAGE_FLAG_NONE)) {
727 if ((*msgflags & MESSAGE_FLAG_SEEN))
728 strncat(s, "\\Seen ", STORE_FLAGS_BUF - strlen(s) - 1);
729 if ((*msgflags & MESSAGE_FLAG_ANSWERED))
730 strncat(s, "\\Answered ",
731 STORE_FLAGS_BUF - strlen(s) - 1);
732 if ((*msgflags & MESSAGE_FLAG_FLAGGED))
733 strncat(s, "\\Flagged ",
734 STORE_FLAGS_BUF - strlen(s) - 1);
735 if ((*msgflags & MESSAGE_FLAG_DELETED))
736 strncat(s, "\\Deleted ",
737 STORE_FLAGS_BUF - strlen(s) - 1);
738 if ((*msgflags & MESSAGE_FLAG_DRAFT))
739 strncat(s, "\\Draft", STORE_FLAGS_BUF - strlen(s) - 1);
740 if ((s[strlen(s) - 1] == ' '))
741 s[strlen(s) - 1] = 0;
742 }
743 action_list(mesgs, args);
744
745 m = mcp = convert_messages(mesgs);
746
747 tok = strtok_r(m, " ", &m);
748 while (tok != NULL) {
749 server_response(&sockpri, imap_store(&sockpri, tok, t, s));
750
751 tok = strtok_r(NULL, " ", &m);
752 }
753
754 if (options & OPTION_EXPUNGE)
755 server_response(&sockpri, imap_expunge(&sockpri));
756
757 xfree(mcp);
758
759 return 0;
760 }
761
762 /*
763 * List user selected headers of messages.
764 */
765 int
766 action_list(char *mesgs, char *args)
767 {
768 int r, t;
769 char *tok, *mcp, *m;
770 char s[ARGS_LEN + 27];
771 char hdrs[RESPONSE_BUF * 2 + 1];
772
773 if (*args == '\0')
774 return 0;
775
776 m = mcp = xstrdup(mesgs);
777
778 snprintf(s, ARGS_LEN + 27 - 1, "BODY.PEEK[HEADER.FIELDS (%s)]", args);
779
780 tok = strtok_r(m, " ", &m);
781 while (tok != NULL) {
782 /* Reset internal fetch counter. */
783 fetch_response(&sockpri, 0, 1, NULL);
784 t = imap_fetch(&sockpri, tok, s);
785
786 log_info(LOG_PREAMBLE, NULL);
787 do {
788 r = fetch_response(&sockpri, t, 0, hdrs);
789
790 if (*hdrs != '\0') {
791 if (options & OPTION_HEADERS)
792 info("%s\n", hdrs);
793 log_info(LOG_HEADER, hdrs);
794 }
795 } while (r == RESPONSE_NONE);
796
797 tok = strtok_r(NULL, " ", &m);
798 }
799
800 xfree(mcp);
801
802 return 0;
803 }
804
805
806 /*
807 * Count how many messages matched the filter.
808 */
809 unsigned int
810 count_messages(char *mesgs)
811 {
812 unsigned int cnt;
813 char *c;
814
815 cnt = 0;
816 c = mesgs;
817
818 while ((c = strchr(c, ' '))) {
819 cnt++;
820 c++;
821 }
822
823 return ++cnt;
824 }
825
826
827 /*
828 * Convert messages with contiguous sequence number to the corresponding
829 * number range, eg. 1 2 3 5 7 8 --> 1:3 5 7:8 and return a newly allocated
830 * buffer with the results.
831 */
832 char *
833 convert_messages(char *mesgs)
834 {
835 int maxlen;
836 unsigned int start, end, tmp;
837 char *c, *cp, *tail;
838
839 start = end = tmp = 0;
840 maxlen = strlen(mesgs) + 1;
841 tail = NULL;
842
843 c = cp = xstrdup(mesgs);
844
845 start = (unsigned int)strtoul(mesgs, &tail, 10);
846 end = start;
847
848 do {
849 if (tail != NULL) {
850 tmp = (unsigned int)strtoul(tail, &tail, 10);
851 if (tmp == 0)
852 tail = NULL;
853 }
854 if (tmp == end + 1)
855 end++;
856 else {
857 if (start == end) {
858 xstrncpy(c, ultostr(start, 10), maxlen);
859 c += strlen(c);
860 } else {
861 xstrncpy(c, ultostr(start, 10), maxlen - 1);
862 c += strlen(c);
863 *c = ':';
864 *++c = 0;
865 xstrncpy(c, ultostr(end, 10), maxlen);
866 c += strlen(c);
867 }
868
869 if (tail != NULL && c - cp < maxlen) {
870 *c = ' ';
871 *++c = 0;
872 }
873 start = end = tmp;
874 }
875 } while (tmp != 0);
876
877 return cp;
878 }
879
880
881 /*
882 * Substitute all occurences of the '@' character with the '%' character,
883 * and any double '@' characters with a signle '@' character, returning the
884 * number of replacements.
885 */
886 int
887 substitute_date(char *str)
888 {
889 int n;
890 char *r, *w, *s;
891
892 n = 0;
893
894 s = xstrdup(str);
895
896 for (r = s, w = str; *r != '\0'; r++, w++) {
897 if (*r == '%') {
898 *w++ = '%';
899 *w = '%';
900 } else if (*r == '@') {
901 if (*(r + 1) == '@') {
902 *w = *r++;
903 } else {
904 *w = '%';
905 n++;
906 }
907 } else {
908 *w = *r;
909 }
910 }
911 *w = '\0';
912
913 xfree(s);
914
915 return n;
916 }
917
918
919 /*
920 * Format the destination mailbox according to the current date conversion
921 * specifiers.
922 */
923 void
924 current_date(char *destmbox)
925 {
926 char s[MBOX_NAME_LEN];
927 time_t te;
928 struct tm *tl;
929
930 if (!strchr(destmbox, '%'))
931 return;
932
933 te = time(NULL);
934 tl = localtime(&te);
935
936 if (strftime(s, MBOX_NAME_LEN - 1, destmbox, tl))
937 xstrncpy(destmbox, s, MBOX_NAME_LEN - 1);
938 }
939
940
941 /*
942 * Format the destination mailbox according to the message date conversion
943 * specifiers.
944 */
945 void
946 message_date(char *mesg, char *destmbox)
947 {
948 unsigned int t;
949 char s[MBOX_NAME_LEN];
950 struct tm tl;
951 char dbuf[RESPONSE_BUF + 1];
952
953 if (!strchr(destmbox, '@'))
954 return;
955
956 substitute_date(destmbox);
957
958 fetch_response(&sockpri, 0, 1, NULL);
959 t = imap_fetch(&sockpri, mesg, "BODY.PEEK[HEADER.FIELDS (DATE)]");
960
961 while (fetch_response(&sockpri, t, 0, dbuf) == RESPONSE_NONE);
962
963 if (strptime(dbuf, "Date: %a, %d %b %Y %H:%M:%S", &tl) &&
964 strftime(s, MBOX_NAME_LEN - 1, destmbox, &tl))
965 xstrncpy(destmbox, s, MBOX_NAME_LEN - 1);
966 }
967
968
969 /*
970 * Format the destination mailbox according to "default variables" specifiers.
971 */
972 void
973 default_variables(char *mbox, char *destmbox)
974 {
975 char *m, *r, *w, *s;
976
977 if (!strchr(destmbox, '$'))
978 return;
979
980 s = xstrdup(destmbox);
981
982 for (r = s, w = destmbox; *r != '\0';) {
983 if (w - destmbox >= MBOX_NAME_LEN - 1)
984 break;
985 if (*r == '$') {
986 switch (*(r + 1)) {
987 case '_':
988 if (w + strlen(mbox) - destmbox >
989 MBOX_NAME_LEN - 1) {
990 r += 2;
991 break;
992 }
993 for (m = mbox; *m != '\0'; m++, w++)
994 *w = *m;
995 r += 2;
996 break;
997 case '$':
998 *w++ = '$';
999 r += 2;
1000 break;
1001 default:
1002 *w++ = *r++;
1003 break;
1004 }
1005 } else {
1006 *w++ = *r++;
1007 }
1008 }
1009 *w = '\0';
1010
1011 xfree(s);
1012 }

webmaster@linux.gr
ViewVC Help
Powered by ViewVC 1.1.26