mutt stable branch with some hacks
1/*
2 * Copyright (C) 2004 Thomas Glanzmann <sithglan@stud.uni-erlangen.de>
3 * Copyright (C) 2004 Tobias Werth <sitowert@stud.uni-erlangen.de>
4 * Copyright (C) 2004 Brian Fundakowski Feldman <green@FreeBSD.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 */
20
21#if HAVE_CONFIG_H
22#include "config.h"
23#endif /* HAVE_CONFIG_H */
24
25#if HAVE_QDBM
26#include <depot.h>
27#include <cabin.h>
28#include <villa.h>
29#elif HAVE_TC
30#include <tcbdb.h>
31#elif HAVE_GDBM
32#include <gdbm.h>
33#elif HAVE_DB4
34#include <db.h>
35#endif
36
37#include <errno.h>
38#include <fcntl.h>
39#if HAVE_SYS_TIME_H
40#include <sys/time.h>
41#endif
42#include "mutt.h"
43#include "hcache.h"
44#include "hcversion.h"
45#include "mx.h"
46#include "lib.h"
47#include "md5.h"
48#include "rfc822.h"
49
50unsigned int hcachever = 0x0;
51
52#if HAVE_QDBM
53struct header_cache
54{
55 VILLA *db;
56 char *folder;
57 unsigned int crc;
58};
59#elif HAVE_TC
60struct header_cache
61{
62 TCBDB *db;
63 char *folder;
64 unsigned int crc;
65};
66#elif HAVE_GDBM
67struct header_cache
68{
69 GDBM_FILE db;
70 char *folder;
71 unsigned int crc;
72};
73#elif HAVE_DB4
74struct header_cache
75{
76 DB_ENV *env;
77 DB *db;
78 char *folder;
79 unsigned int crc;
80 int fd;
81 char lockfile[_POSIX_PATH_MAX];
82};
83
84static void mutt_hcache_dbt_init(DBT * dbt, void *data, size_t len);
85static void mutt_hcache_dbt_empty_init(DBT * dbt);
86#endif
87
88typedef union
89{
90 struct timeval timeval;
91 unsigned int uidvalidity;
92} validate;
93
94static void *
95lazy_malloc(size_t siz)
96{
97 if (0 < siz && siz < 4096)
98 siz = 4096;
99
100 return safe_malloc(siz);
101}
102
103static void
104lazy_realloc(void *ptr, size_t siz)
105{
106 void **p = (void **) ptr;
107
108 if (p != NULL && 0 < siz && siz < 4096)
109 return;
110
111 safe_realloc(ptr, siz);
112}
113
114static unsigned char *
115dump_int(unsigned int i, unsigned char *d, int *off)
116{
117 lazy_realloc(&d, *off + sizeof (int));
118 memcpy(d + *off, &i, sizeof (int));
119 (*off) += sizeof (int);
120
121 return d;
122}
123
124static void
125restore_int(unsigned int *i, const unsigned char *d, int *off)
126{
127 memcpy(i, d + *off, sizeof (int));
128 (*off) += sizeof (int);
129}
130
131static inline int is_ascii (const char *p, size_t len) {
132 register const char *s = p;
133 while (s && (unsigned) (s - p) < len) {
134 if ((*s & 0x80) != 0)
135 return 0;
136 s++;
137 }
138 return 1;
139}
140
141static unsigned char *
142dump_char_size(char *c, unsigned char *d, int *off, ssize_t size, int convert)
143{
144 char *p = c;
145
146 if (c == NULL)
147 {
148 size = 0;
149 d = dump_int(size, d, off);
150 return d;
151 }
152
153 if (convert && !is_ascii (c, size)) {
154 p = mutt_substrdup (c, c + size);
155 if (mutt_convert_string (&p, Charset, "utf-8", 0) == 0) {
156 c = p;
157 size = mutt_strlen (c) + 1;
158 }
159 }
160
161 d = dump_int(size, d, off);
162 lazy_realloc(&d, *off + size);
163 memcpy(d + *off, p, size);
164 *off += size;
165
166 if (p != c)
167 FREE(&p);
168
169 return d;
170}
171
172static unsigned char *
173dump_char(char *c, unsigned char *d, int *off, int convert)
174{
175 return dump_char_size (c, d, off, mutt_strlen (c) + 1, convert);
176}
177
178static void
179restore_char(char **c, const unsigned char *d, int *off, int convert)
180{
181 unsigned int size;
182 restore_int(&size, d, off);
183
184 if (size == 0)
185 {
186 *c = NULL;
187 return;
188 }
189
190 *c = safe_malloc(size);
191 memcpy(*c, d + *off, size);
192 if (convert && !is_ascii (*c, size)) {
193 char *tmp = safe_strdup (*c);
194 if (mutt_convert_string (&tmp, "utf-8", Charset, 0) == 0) {
195 mutt_str_replace (c, tmp);
196 } else {
197 FREE(&tmp);
198 }
199 }
200 *off += size;
201}
202
203static unsigned char *
204dump_address(ADDRESS * a, unsigned char *d, int *off, int convert)
205{
206 unsigned int counter = 0;
207 unsigned int start_off = *off;
208
209 d = dump_int(0xdeadbeef, d, off);
210
211 while (a)
212 {
213#ifdef EXACT_ADDRESS
214 d = dump_char(a->val, d, off, convert);
215#endif
216 d = dump_char(a->personal, d, off, convert);
217 d = dump_char(a->mailbox, d, off, 0);
218 d = dump_int(a->group, d, off);
219 a = a->next;
220 counter++;
221 }
222
223 memcpy(d + start_off, &counter, sizeof (int));
224
225 return d;
226}
227
228static void
229restore_address(ADDRESS ** a, const unsigned char *d, int *off, int convert)
230{
231 unsigned int counter;
232
233 restore_int(&counter, d, off);
234
235 while (counter)
236 {
237 *a = rfc822_new_address();
238#ifdef EXACT_ADDRESS
239 restore_char(&(*a)->val, d, off, convert);
240#endif
241 restore_char(&(*a)->personal, d, off, convert);
242 restore_char(&(*a)->mailbox, d, off, 0);
243 restore_int((unsigned int *) &(*a)->group, d, off);
244 a = &(*a)->next;
245 counter--;
246 }
247
248 *a = NULL;
249}
250
251static unsigned char *
252dump_list(LIST * l, unsigned char *d, int *off, int convert)
253{
254 unsigned int counter = 0;
255 unsigned int start_off = *off;
256
257 d = dump_int(0xdeadbeef, d, off);
258
259 while (l)
260 {
261 d = dump_char(l->data, d, off, convert);
262 l = l->next;
263 counter++;
264 }
265
266 memcpy(d + start_off, &counter, sizeof (int));
267
268 return d;
269}
270
271static void
272restore_list(LIST ** l, const unsigned char *d, int *off, int convert)
273{
274 unsigned int counter;
275
276 restore_int(&counter, d, off);
277
278 while (counter)
279 {
280 *l = safe_malloc(sizeof (LIST));
281 restore_char(&(*l)->data, d, off, convert);
282 l = &(*l)->next;
283 counter--;
284 }
285
286 *l = NULL;
287}
288
289static unsigned char *
290dump_buffer(BUFFER * b, unsigned char *d, int *off, int convert)
291{
292 if (!b)
293 {
294 d = dump_int(0, d, off);
295 return d;
296 }
297 else
298 d = dump_int(1, d, off);
299
300 d = dump_char_size(b->data, d, off, b->dsize + 1, convert);
301 d = dump_int(b->dptr - b->data, d, off);
302 d = dump_int(b->dsize, d, off);
303 d = dump_int(b->destroy, d, off);
304
305 return d;
306}
307
308static void
309restore_buffer(BUFFER ** b, const unsigned char *d, int *off, int convert)
310{
311 unsigned int used;
312 unsigned int offset;
313 restore_int(&used, d, off);
314 if (!used)
315 {
316 return;
317 }
318
319 *b = safe_malloc(sizeof (BUFFER));
320
321 restore_char(&(*b)->data, d, off, convert);
322 restore_int(&offset, d, off);
323 (*b)->dptr = (*b)->data + offset;
324 restore_int (&used, d, off);
325 (*b)->dsize = used;
326 restore_int (&used, d, off);
327 (*b)->destroy = used;
328}
329
330static unsigned char *
331dump_parameter(PARAMETER * p, unsigned char *d, int *off, int convert)
332{
333 unsigned int counter = 0;
334 unsigned int start_off = *off;
335
336 d = dump_int(0xdeadbeef, d, off);
337
338 while (p)
339 {
340 d = dump_char(p->attribute, d, off, 0);
341 d = dump_char(p->value, d, off, convert);
342 p = p->next;
343 counter++;
344 }
345
346 memcpy(d + start_off, &counter, sizeof (int));
347
348 return d;
349}
350
351static void
352restore_parameter(PARAMETER ** p, const unsigned char *d, int *off, int convert)
353{
354 unsigned int counter;
355
356 restore_int(&counter, d, off);
357
358 while (counter)
359 {
360 *p = safe_malloc(sizeof (PARAMETER));
361 restore_char(&(*p)->attribute, d, off, 0);
362 restore_char(&(*p)->value, d, off, convert);
363 p = &(*p)->next;
364 counter--;
365 }
366
367 *p = NULL;
368}
369
370static unsigned char *
371dump_body(BODY * c, unsigned char *d, int *off, int convert)
372{
373 BODY nb;
374
375 memcpy (&nb, c, sizeof (BODY));
376
377 /* some fields are not safe to cache */
378 nb.content = NULL;
379 nb.charset = NULL;
380 nb.next = NULL;
381 nb.parts = NULL;
382 nb.hdr = NULL;
383 nb.aptr = NULL;
384
385 lazy_realloc(&d, *off + sizeof (BODY));
386 memcpy(d + *off, &nb, sizeof (BODY));
387 *off += sizeof (BODY);
388
389 d = dump_char(nb.xtype, d, off, 0);
390 d = dump_char(nb.subtype, d, off, 0);
391
392 d = dump_parameter(nb.parameter, d, off, convert);
393
394 d = dump_char(nb.description, d, off, convert);
395 d = dump_char(nb.form_name, d, off, convert);
396 d = dump_char(nb.filename, d, off, convert);
397 d = dump_char(nb.d_filename, d, off, convert);
398
399 return d;
400}
401
402static void
403restore_body(BODY * c, const unsigned char *d, int *off, int convert)
404{
405 memcpy(c, d + *off, sizeof (BODY));
406 *off += sizeof (BODY);
407
408 restore_char(&c->xtype, d, off, 0);
409 restore_char(&c->subtype, d, off, 0);
410
411 restore_parameter(&c->parameter, d, off, convert);
412
413 restore_char(&c->description, d, off, convert);
414 restore_char(&c->form_name, d, off, convert);
415 restore_char(&c->filename, d, off, convert);
416 restore_char(&c->d_filename, d, off, convert);
417}
418
419static unsigned char *
420dump_envelope(ENVELOPE * e, unsigned char *d, int *off, int convert)
421{
422 d = dump_address(e->return_path, d, off, convert);
423 d = dump_address(e->from, d, off, convert);
424 d = dump_address(e->to, d, off, convert);
425 d = dump_address(e->cc, d, off, convert);
426 d = dump_address(e->bcc, d, off, convert);
427 d = dump_address(e->sender, d, off, convert);
428 d = dump_address(e->reply_to, d, off, convert);
429 d = dump_address(e->mail_followup_to, d, off, convert);
430
431 d = dump_char(e->list_post, d, off, convert);
432 d = dump_char(e->subject, d, off, convert);
433
434 if (e->real_subj)
435 d = dump_int(e->real_subj - e->subject, d, off);
436 else
437 d = dump_int(-1, d, off);
438
439 d = dump_char(e->message_id, d, off, 0);
440 d = dump_char(e->supersedes, d, off, 0);
441 d = dump_char(e->date, d, off, 0);
442 d = dump_char(e->x_label, d, off, convert);
443
444 d = dump_buffer(e->spam, d, off, convert);
445
446 d = dump_list(e->references, d, off, 0);
447 d = dump_list(e->in_reply_to, d, off, 0);
448 d = dump_list(e->userhdrs, d, off, convert);
449
450 return d;
451}
452
453static void
454restore_envelope(ENVELOPE * e, const unsigned char *d, int *off, int convert)
455{
456 int real_subj_off;
457
458 restore_address(&e->return_path, d, off, convert);
459 restore_address(&e->from, d, off, convert);
460 restore_address(&e->to, d, off, convert);
461 restore_address(&e->cc, d, off, convert);
462 restore_address(&e->bcc, d, off, convert);
463 restore_address(&e->sender, d, off, convert);
464 restore_address(&e->reply_to, d, off, convert);
465 restore_address(&e->mail_followup_to, d, off, convert);
466
467 restore_char(&e->list_post, d, off, convert);
468 restore_char(&e->subject, d, off, convert);
469 restore_int((unsigned int *) (&real_subj_off), d, off);
470
471 if (0 <= real_subj_off)
472 e->real_subj = e->subject + real_subj_off;
473 else
474 e->real_subj = NULL;
475
476 restore_char(&e->message_id, d, off, 0);
477 restore_char(&e->supersedes, d, off, 0);
478 restore_char(&e->date, d, off, 0);
479 restore_char(&e->x_label, d, off, convert);
480
481 restore_buffer(&e->spam, d, off, convert);
482
483 restore_list(&e->references, d, off, 0);
484 restore_list(&e->in_reply_to, d, off, 0);
485 restore_list(&e->userhdrs, d, off, convert);
486}
487
488static int
489crc_matches(const char *d, unsigned int crc)
490{
491 int off = sizeof (validate);
492 unsigned int mycrc = 0;
493
494 if (!d)
495 return 0;
496
497 restore_int(&mycrc, (unsigned char *) d, &off);
498
499 return (crc == mycrc);
500}
501
502/* Append md5sumed folder to path if path is a directory. */
503static const char *
504mutt_hcache_per_folder(const char *path, const char *folder,
505 hcache_namer_t namer)
506{
507 static char hcpath[_POSIX_PATH_MAX];
508 struct stat sb;
509 unsigned char md5sum[16];
510 char* s;
511 int ret, plen;
512#ifndef HAVE_ICONV
513 const char *chs = Charset && *Charset ? Charset :
514 mutt_get_default_charset ();
515#endif
516
517 plen = mutt_strlen (path);
518
519 ret = stat(path, &sb);
520 if (ret < 0 && path[plen-1] != '/')
521 {
522#ifdef HAVE_ICONV
523 return path;
524#else
525 snprintf (hcpath, _POSIX_PATH_MAX, "%s-%s", path, chs);
526 return hcpath;
527#endif
528 }
529
530 if (ret >= 0 && !S_ISDIR(sb.st_mode))
531 {
532#ifdef HAVE_ICONV
533 return path;
534#else
535 snprintf (hcpath, _POSIX_PATH_MAX, "%s-%s", path, chs);
536 return hcpath;
537#endif
538 }
539
540 if (namer)
541 {
542 snprintf (hcpath, sizeof (hcpath), "%s%s", path,
543 path[plen-1] == '/' ? "" : "/");
544 if (path[plen-1] != '/')
545 plen++;
546
547 ret = namer (folder, hcpath + plen, sizeof (hcpath) - plen);
548 }
549 else
550 {
551 md5_buffer (folder, strlen (folder), &md5sum);
552
553 /* On some systems (e.g. OS X), snprintf is defined as a macro.
554 * Embedding directives inside macros is undefined, so we have to duplicate
555 * the whole call:
556 */
557#ifndef HAVE_ICONV
558 ret = snprintf(hcpath, _POSIX_PATH_MAX,
559 "%s/%02x%02x%02x%02x%02x%02x%02x%02x"
560 "%02x%02x%02x%02x%02x%02x%02x%02x"
561 "-%s"
562 ,
563 path, md5sum[0], md5sum[1], md5sum[2], md5sum[3],
564 md5sum[4], md5sum[5], md5sum[6], md5sum[7], md5sum[8],
565 md5sum[9], md5sum[10], md5sum[11], md5sum[12],
566 md5sum[13], md5sum[14], md5sum[15]
567 ,chs
568 );
569#else
570 ret = snprintf(hcpath, _POSIX_PATH_MAX,
571 "%s/%02x%02x%02x%02x%02x%02x%02x%02x"
572 "%02x%02x%02x%02x%02x%02x%02x%02x"
573 ,
574 path, md5sum[0], md5sum[1], md5sum[2], md5sum[3],
575 md5sum[4], md5sum[5], md5sum[6], md5sum[7], md5sum[8],
576 md5sum[9], md5sum[10], md5sum[11], md5sum[12],
577 md5sum[13], md5sum[14], md5sum[15]
578 );
579#endif
580 }
581
582 if (ret <= 0)
583 return path;
584
585 if (stat (hcpath, &sb) >= 0)
586 return hcpath;
587
588 s = strchr (hcpath + 1, '/');
589 while (s)
590 {
591 /* create missing path components */
592 *s = '\0';
593 if (stat (hcpath, &sb) < 0 && (errno != ENOENT || mkdir (hcpath, 0777) < 0))
594 return path;
595 *s = '/';
596 s = strchr (s + 1, '/');
597 }
598
599 return hcpath;
600}
601
602/* This function transforms a header into a char so that it is useable by
603 * db_store.
604 */
605static void *
606mutt_hcache_dump(header_cache_t *h, HEADER * header, int *off,
607 unsigned int uidvalidity, mutt_hcache_store_flags_t flags)
608{
609 unsigned char *d = NULL;
610 HEADER nh;
611 int convert = !Charset_is_utf8;
612
613 *off = 0;
614 d = lazy_malloc(sizeof (validate));
615
616 if (flags & MUTT_GENERATE_UIDVALIDITY)
617 {
618 struct timeval now;
619 gettimeofday(&now, NULL);
620 memcpy(d, &now, sizeof (struct timeval));
621 }
622 else
623 memcpy(d, &uidvalidity, sizeof (uidvalidity));
624 *off += sizeof (validate);
625
626 d = dump_int(h->crc, d, off);
627
628 lazy_realloc(&d, *off + sizeof (HEADER));
629 memcpy(&nh, header, sizeof (HEADER));
630
631 /* some fields are not safe to cache */
632 nh.tagged = 0;
633 nh.changed = 0;
634 nh.threaded = 0;
635 nh.recip_valid = 0;
636 nh.searched = 0;
637 nh.matched = 0;
638 nh.collapsed = 0;
639 nh.limited = 0;
640 nh.num_hidden = 0;
641 nh.recipient = 0;
642 nh.pair = 0;
643 nh.attach_valid = 0;
644 nh.path = NULL;
645 nh.tree = NULL;
646 nh.thread = NULL;
647#ifdef MIXMASTER
648 nh.chain = NULL;
649#endif
650#if defined USE_POP || defined USE_IMAP
651 nh.data = NULL;
652#endif
653
654 memcpy(d + *off, &nh, sizeof (HEADER));
655 *off += sizeof (HEADER);
656
657 d = dump_envelope(nh.env, d, off, convert);
658 d = dump_body(nh.content, d, off, convert);
659 d = dump_char(nh.maildir_flags, d, off, convert);
660
661 return d;
662}
663
664HEADER *
665mutt_hcache_restore(const unsigned char *d, HEADER ** oh)
666{
667 int off = 0;
668 HEADER *h = mutt_new_header();
669 int convert = !Charset_is_utf8;
670
671 /* skip validate */
672 off += sizeof (validate);
673
674 /* skip crc */
675 off += sizeof (unsigned int);
676
677 memcpy(h, d + off, sizeof (HEADER));
678 off += sizeof (HEADER);
679
680 h->env = mutt_new_envelope();
681 restore_envelope(h->env, d, &off, convert);
682
683 h->content = mutt_new_body();
684 restore_body(h->content, d, &off, convert);
685
686 restore_char(&h->maildir_flags, d, &off, convert);
687
688 /* this is needed for maildir style mailboxes */
689 if (oh)
690 {
691 h->old = (*oh)->old;
692 h->path = safe_strdup((*oh)->path);
693 mutt_free_header(oh);
694 }
695
696 return h;
697}
698
699void *
700mutt_hcache_fetch(header_cache_t *h, const char *filename,
701 size_t(*keylen) (const char *fn))
702{
703 void* data;
704
705 data = mutt_hcache_fetch_raw (h, filename, keylen);
706
707 if (!data || !crc_matches(data, h->crc))
708 {
709 FREE(&data);
710 return NULL;
711 }
712
713 return data;
714}
715
716void *
717mutt_hcache_fetch_raw (header_cache_t *h, const char *filename,
718 size_t(*keylen) (const char *fn))
719{
720#ifndef HAVE_DB4
721 char path[_POSIX_PATH_MAX];
722 int ksize;
723#endif
724#ifdef HAVE_QDBM
725 char *data = NULL;
726#elif HAVE_TC
727 void *data;
728 int sp;
729#elif HAVE_GDBM
730 datum key;
731 datum data;
732#elif HAVE_DB4
733 DBT key;
734 DBT data;
735#endif
736
737 if (!h)
738 return NULL;
739
740#ifdef HAVE_DB4
741 if (filename[0] == '/')
742 filename++;
743
744 mutt_hcache_dbt_init(&key, (void *) filename, keylen(filename));
745 mutt_hcache_dbt_empty_init(&data);
746 data.flags = DB_DBT_MALLOC;
747
748 h->db->get(h->db, NULL, &key, &data, 0);
749
750 return data.data;
751#else
752 strncpy(path, h->folder, sizeof (path));
753 safe_strcat(path, sizeof (path), filename);
754
755 ksize = strlen (h->folder) + keylen (path + strlen (h->folder));
756#endif
757#ifdef HAVE_QDBM
758 data = vlget(h->db, path, ksize, NULL);
759
760 return data;
761#elif HAVE_TC
762 data = tcbdbget(h->db, path, ksize, &sp);
763
764 return data;
765#elif HAVE_GDBM
766 key.dptr = path;
767 key.dsize = ksize;
768
769 data = gdbm_fetch(h->db, key);
770
771 return data.dptr;
772#endif
773}
774
775/*
776 * flags
777 *
778 * MUTT_GENERATE_UIDVALIDITY
779 * ignore uidvalidity param and store gettimeofday() as the value
780 */
781int
782mutt_hcache_store(header_cache_t *h, const char *filename, HEADER * header,
783 unsigned int uidvalidity,
784 size_t(*keylen) (const char *fn),
785 mutt_hcache_store_flags_t flags)
786{
787 char* data;
788 int dlen;
789 int ret;
790
791 if (!h)
792 return -1;
793
794 data = mutt_hcache_dump(h, header, &dlen, uidvalidity, flags);
795 ret = mutt_hcache_store_raw (h, filename, data, dlen, keylen);
796
797 FREE(&data);
798
799 return ret;
800}
801
802int
803mutt_hcache_store_raw (header_cache_t* h, const char* filename, void* data,
804 size_t dlen, size_t(*keylen) (const char* fn))
805{
806#ifndef HAVE_DB4
807 char path[_POSIX_PATH_MAX];
808 int ksize;
809#endif
810#if HAVE_GDBM
811 datum key;
812 datum databuf;
813#elif HAVE_DB4
814 DBT key;
815 DBT databuf;
816#endif
817
818 if (!h)
819 return -1;
820
821#if HAVE_DB4
822 if (filename[0] == '/')
823 filename++;
824
825 mutt_hcache_dbt_init(&key, (void *) filename, keylen(filename));
826
827 mutt_hcache_dbt_empty_init(&databuf);
828 databuf.flags = DB_DBT_USERMEM;
829 databuf.data = data;
830 databuf.size = dlen;
831 databuf.ulen = dlen;
832
833 return h->db->put(h->db, NULL, &key, &databuf, 0);
834#else
835 strncpy(path, h->folder, sizeof (path));
836 safe_strcat(path, sizeof (path), filename);
837
838 ksize = strlen(h->folder) + keylen(path + strlen(h->folder));
839#endif
840#if HAVE_QDBM
841 return vlput(h->db, path, ksize, data, dlen, VL_DOVER);
842#elif HAVE_TC
843 return tcbdbput(h->db, path, ksize, data, dlen);
844#elif HAVE_GDBM
845 key.dptr = path;
846 key.dsize = ksize;
847
848 databuf.dsize = dlen;
849 databuf.dptr = data;
850
851 return gdbm_store(h->db, key, databuf, GDBM_REPLACE);
852#endif
853}
854
855static char* get_foldername(const char *folder)
856{
857 char *p = NULL;
858 char path[_POSIX_PATH_MAX];
859 struct stat st;
860
861 mutt_encode_path (path, sizeof (path), folder);
862
863 /* if the folder is local, canonify the path to avoid
864 * to ensure equivalent paths share the hcache */
865 if (stat (path, &st) == 0)
866 {
867 p = safe_malloc (PATH_MAX+1);
868 if (!realpath (path, p))
869 mutt_str_replace (&p, path);
870 } else
871 p = safe_strdup (path);
872
873 return p;
874}
875
876#if HAVE_QDBM
877static int
878hcache_open_qdbm (struct header_cache* h, const char* path)
879{
880 int flags = VL_OWRITER | VL_OCREAT;
881
882 if (option(OPTHCACHECOMPRESS))
883 flags |= VL_OZCOMP;
884
885 h->db = vlopen (path, flags, VL_CMPLEX);
886 if (h->db)
887 return 0;
888 else
889 return -1;
890}
891
892void
893mutt_hcache_close(header_cache_t *h)
894{
895 if (!h)
896 return;
897
898 vlclose(h->db);
899 FREE(&h->folder);
900 FREE(&h);
901}
902
903int
904mutt_hcache_delete(header_cache_t *h, const char *filename,
905 size_t(*keylen) (const char *fn))
906{
907 char path[_POSIX_PATH_MAX];
908 int ksize;
909
910 if (!h)
911 return -1;
912
913 strncpy(path, h->folder, sizeof (path));
914 safe_strcat(path, sizeof (path), filename);
915
916 ksize = strlen(h->folder) + keylen(path + strlen(h->folder));
917
918 return vlout(h->db, path, ksize);
919}
920
921#elif HAVE_TC
922static int
923hcache_open_tc (struct header_cache* h, const char* path)
924{
925 h->db = tcbdbnew();
926 if (!h->db)
927 return -1;
928 if (option(OPTHCACHECOMPRESS))
929 tcbdbtune(h->db, 0, 0, 0, -1, -1, BDBTDEFLATE);
930 if (tcbdbopen(h->db, path, BDBOWRITER | BDBOCREAT))
931 return 0;
932 else
933 {
934#ifdef DEBUG
935 int ecode = tcbdbecode (h->db);
936 dprint (2, (debugfile, "tcbdbopen failed for %s: %s (ecode %d)\n", path, tcbdberrmsg (ecode), ecode));
937#endif
938 tcbdbdel(h->db);
939 return -1;
940 }
941}
942
943void
944mutt_hcache_close(header_cache_t *h)
945{
946 if (!h)
947 return;
948
949 if (!tcbdbclose(h->db))
950 {
951#ifdef DEBUG
952 int ecode = tcbdbecode (h->db);
953 dprint (2, (debugfile, "tcbdbclose failed for %s: %s (ecode %d)\n", h->folder, tcbdberrmsg (ecode), ecode));
954#endif
955 }
956 tcbdbdel(h->db);
957 FREE(&h->folder);
958 FREE(&h);
959}
960
961int
962mutt_hcache_delete(header_cache_t *h, const char *filename,
963 size_t(*keylen) (const char *fn))
964{
965 char path[_POSIX_PATH_MAX];
966 int ksize;
967
968 if (!h)
969 return -1;
970
971 strncpy(path, h->folder, sizeof (path));
972 safe_strcat(path, sizeof (path), filename);
973
974 ksize = strlen(h->folder) + keylen(path + strlen(h->folder));
975
976 return tcbdbout(h->db, path, ksize);
977}
978
979#elif HAVE_GDBM
980static int
981hcache_open_gdbm (struct header_cache* h, const char* path)
982{
983 int pagesize;
984
985 if (mutt_atoi (HeaderCachePageSize, &pagesize) < 0 || pagesize <= 0)
986 pagesize = 16384;
987
988 h->db = gdbm_open((char *) path, pagesize, GDBM_WRCREAT, 00600, NULL);
989 if (h->db)
990 return 0;
991
992 /* if rw failed try ro */
993 h->db = gdbm_open((char *) path, pagesize, GDBM_READER, 00600, NULL);
994 if (h->db)
995 return 0;
996
997 return -1;
998}
999
1000void
1001mutt_hcache_close(header_cache_t *h)
1002{
1003 if (!h)
1004 return;
1005
1006 gdbm_close(h->db);
1007 FREE(&h->folder);
1008 FREE(&h);
1009}
1010
1011int
1012mutt_hcache_delete(header_cache_t *h, const char *filename,
1013 size_t(*keylen) (const char *fn))
1014{
1015 datum key;
1016 char path[_POSIX_PATH_MAX];
1017
1018 if (!h)
1019 return -1;
1020
1021 strncpy(path, h->folder, sizeof (path));
1022 safe_strcat(path, sizeof (path), filename);
1023
1024 key.dptr = path;
1025 key.dsize = strlen(h->folder) + keylen(path + strlen(h->folder));
1026
1027 return gdbm_delete(h->db, key);
1028}
1029#elif HAVE_DB4
1030
1031static void
1032mutt_hcache_dbt_init(DBT * dbt, void *data, size_t len)
1033{
1034 dbt->data = data;
1035 dbt->size = dbt->ulen = len;
1036 dbt->dlen = dbt->doff = 0;
1037 dbt->flags = DB_DBT_USERMEM;
1038}
1039
1040static void
1041mutt_hcache_dbt_empty_init(DBT * dbt)
1042{
1043 dbt->data = NULL;
1044 dbt->size = dbt->ulen = dbt->dlen = dbt->doff = 0;
1045 dbt->flags = 0;
1046}
1047
1048static int
1049hcache_open_db4 (struct header_cache* h, const char* path)
1050{
1051 struct stat sb;
1052 int ret;
1053 u_int32_t createflags = DB_CREATE;
1054 int pagesize;
1055
1056 if (mutt_atoi (HeaderCachePageSize, &pagesize) < 0 || pagesize <= 0)
1057 pagesize = 16384;
1058
1059 snprintf (h->lockfile, _POSIX_PATH_MAX, "%s-lock-hack", path);
1060
1061 h->fd = open (h->lockfile, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
1062 if (h->fd < 0)
1063 return -1;
1064
1065 if (mx_lock_file (h->lockfile, h->fd, 1, 0, 5))
1066 goto fail_close;
1067
1068 ret = db_env_create (&h->env, 0);
1069 if (ret)
1070 goto fail_unlock;
1071
1072 ret = (*h->env->open)(h->env, NULL, DB_INIT_MPOOL | DB_CREATE | DB_PRIVATE,
1073 0600);
1074 if (ret)
1075 goto fail_env;
1076
1077 ret = db_create (&h->db, h->env, 0);
1078 if (ret)
1079 goto fail_env;
1080
1081 if (stat(path, &sb) != 0 && errno == ENOENT)
1082 {
1083 createflags |= DB_EXCL;
1084 h->db->set_pagesize(h->db, pagesize);
1085 }
1086
1087 ret = (*h->db->open)(h->db, NULL, path, h->folder, DB_BTREE, createflags,
1088 0600);
1089 if (ret)
1090 goto fail_db;
1091
1092 return 0;
1093
1094 fail_db:
1095 h->db->close (h->db, 0);
1096 fail_env:
1097 h->env->close (h->env, 0);
1098 fail_unlock:
1099 mx_unlock_file (h->lockfile, h->fd, 0);
1100 fail_close:
1101 close (h->fd);
1102 unlink (h->lockfile);
1103
1104 return -1;
1105}
1106
1107void
1108mutt_hcache_close(header_cache_t *h)
1109{
1110 if (!h)
1111 return;
1112
1113 h->db->close (h->db, 0);
1114 h->env->close (h->env, 0);
1115 mx_unlock_file (h->lockfile, h->fd, 0);
1116 close (h->fd);
1117 unlink (h->lockfile);
1118 FREE (&h->folder);
1119 FREE (&h);
1120}
1121
1122int
1123mutt_hcache_delete(header_cache_t *h, const char *filename,
1124 size_t(*keylen) (const char *fn))
1125{
1126 DBT key;
1127
1128 if (!h)
1129 return -1;
1130
1131 if (filename[0] == '/')
1132 filename++;
1133
1134 mutt_hcache_dbt_init(&key, (void *) filename, keylen(filename));
1135 return h->db->del(h->db, NULL, &key, 0);
1136}
1137#endif
1138
1139header_cache_t *
1140mutt_hcache_open(const char *path, const char *folder, hcache_namer_t namer)
1141{
1142 struct header_cache *h = safe_calloc(1, sizeof (struct header_cache));
1143 int (*hcache_open) (struct header_cache* h, const char* path);
1144 struct stat sb;
1145
1146#if HAVE_QDBM
1147 hcache_open = hcache_open_qdbm;
1148#elif HAVE_TC
1149 hcache_open= hcache_open_tc;
1150#elif HAVE_GDBM
1151 hcache_open = hcache_open_gdbm;
1152#elif HAVE_DB4
1153 hcache_open = hcache_open_db4;
1154#endif
1155
1156 /* Calculate the current hcache version from dynamic configuration */
1157 if (hcachever == 0x0) {
1158 union {
1159 unsigned char charval[16];
1160 unsigned int intval;
1161 } digest;
1162 struct md5_ctx ctx;
1163 SPAM_LIST *spam;
1164 RX_LIST *nospam;
1165
1166 hcachever = HCACHEVER;
1167
1168 md5_init_ctx(&ctx);
1169
1170 /* Seed with the compiled-in header structure hash */
1171 md5_process_bytes(&hcachever, sizeof(hcachever), &ctx);
1172
1173 /* Mix in user's spam list */
1174 for (spam = SpamList; spam; spam = spam->next)
1175 {
1176 md5_process_bytes(spam->rx->pattern, strlen(spam->rx->pattern), &ctx);
1177 md5_process_bytes(spam->template, strlen(spam->template), &ctx);
1178 }
1179
1180 /* Mix in user's nospam list */
1181 for (nospam = NoSpamList; nospam; nospam = nospam->next)
1182 {
1183 md5_process_bytes(nospam->rx->pattern, strlen(nospam->rx->pattern), &ctx);
1184 }
1185
1186 /* Get a hash and take its bytes as an (unsigned int) hash version */
1187 md5_finish_ctx(&ctx, digest.charval);
1188 hcachever = digest.intval;
1189 }
1190
1191 h->db = NULL;
1192 h->folder = get_foldername(folder);
1193 h->crc = hcachever;
1194
1195 if (!path || path[0] == '\0')
1196 {
1197 FREE(&h->folder);
1198 FREE(&h);
1199 return NULL;
1200 }
1201
1202 path = mutt_hcache_per_folder(path, h->folder, namer);
1203
1204 if (!hcache_open (h, path))
1205 return h;
1206 else
1207 {
1208 /* remove a possibly incompatible version */
1209 if (!stat (path, &sb) && !unlink (path))
1210 {
1211 if (!hcache_open (h, path))
1212 return h;
1213 }
1214 FREE(&h->folder);
1215 FREE(&h);
1216
1217 return NULL;
1218 }
1219}
1220
1221#if HAVE_DB4
1222const char *mutt_hcache_backend (void)
1223{
1224 return DB_VERSION_STRING;
1225}
1226#elif HAVE_GDBM
1227const char *mutt_hcache_backend (void)
1228{
1229 return gdbm_version;
1230}
1231#elif HAVE_QDBM
1232const char *mutt_hcache_backend (void)
1233{
1234 return "qdbm " _QDBM_VERSION;
1235}
1236#elif HAVE_TC
1237const char *mutt_hcache_backend (void)
1238{
1239 return "tokyocabinet " _TC_VERSION;
1240}
1241#endif