Nico Golde:
[apps/madmutt.git] / hcache.c
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., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
19  */
20
21 # if HAVE_INTTYPES_H
22 #  include <inttypes.h>
23 # else
24 #  if HAVE_STDINT_H
25 #   include <stdint.h>
26 #  endif
27 # endif
28
29 #if HAVE_CONFIG_H
30 #include "config.h"
31 #endif                          /* HAVE_CONFIG_H */
32
33 #ifdef USE_HCACHE
34
35 #if HAVE_GDBM
36 #include <gdbm.h>
37 #elif HAVE_DB4
38 #include <db.h>
39 #endif
40
41 #include <errno.h>
42 #include <fcntl.h>
43 #if HAVE_SYS_TIME_H
44 #include <sys/time.h>
45 #endif
46 #include "mutt.h"
47 #ifdef USE_IMAP
48 #include "message.h"
49 #endif
50 #include "mime.h"
51 #include "mx.h"
52 #include "lib.h"
53 #include "md5.h"
54
55 #if HAVE_GDBM
56 static struct
57 header_cache
58 {
59         GDBM_FILE db;
60         char *folder;
61         unsigned int crc;
62 } HEADER_CACHE;
63 #elif HAVE_DB4
64 static struct
65 header_cache
66 {
67         DB_ENV *env;
68         DB *db;
69         unsigned int crc;
70         int fd;
71         char lockfile[_POSIX_PATH_MAX];
72 } HEADER_CACHE;
73 #endif
74
75 typedef union
76 {
77         struct timeval timeval;
78         uint64_t uid_validity;
79 } validate;
80
81 static void *
82 lazy_malloc(size_t siz)
83 {
84         if (0 < siz && siz < 4096) {
85                 siz = 4096;
86         }
87
88         return safe_malloc(siz);
89 }
90
91 static void
92 lazy_realloc(void *ptr, size_t siz)
93 {
94         void **p = (void **)ptr;
95
96         if ( p != NULL
97         &&   0 < siz
98         && siz < 4096) {
99                 return;
100         }
101
102         safe_realloc(ptr, siz);
103 }
104
105 static unsigned char *
106 dump_int(unsigned int i, unsigned char *d, int *off)
107 {
108         lazy_realloc(&d, *off + sizeof(int));
109         memcpy(d + *off, &i, sizeof(int));
110         (*off) += sizeof(int);
111
112         return d;
113 }
114
115 static void
116 restore_int(unsigned int *i, const unsigned char *d, int *off)
117 {
118         memcpy(i, d + *off, sizeof(int));
119         (*off) += sizeof(int);
120 }
121
122 static unsigned char *
123 dump_char(char *c, unsigned char *d, int *off)
124 {
125         unsigned int size;
126
127         if (c == NULL) {
128                 size = 0;
129                 d = dump_int(size, d, off);
130                 return d;
131         }
132
133         size = mutt_strlen(c) + 1;
134         d = dump_int(size, d, off);
135         lazy_realloc(&d, *off + size);
136         memcpy(d + *off, c, size);
137         *off += size;
138
139         return d;
140 }
141
142 #if 0
143 static unsigned char *
144 dump_char_size(char *c, unsigned char *d, int *off, ssize_t size)
145 {
146         if (c == NULL) {
147                 size = 0;
148                 d = dump_int(size, d, off);
149                 return d;
150         }
151
152         d = dump_int(size, d, off);
153         lazy_realloc(&d, *off + size);
154         memcpy(d + *off, c, size);
155         *off += size;
156
157         return d;
158 }
159 #endif
160
161 static void
162 restore_char(char **c, const unsigned char *d, int *off)
163 {
164         unsigned int size;
165         restore_int(&size, d, off);
166
167         if (size == 0) {
168                 *c = NULL;
169                 return;
170         }
171
172         *c = safe_malloc(size);
173         memcpy(*c, d + *off, size);
174         *off += size;
175 }
176
177 static unsigned char *
178 dump_address(ADDRESS *a, unsigned char *d, int *off)
179 {
180         unsigned int counter = 0;
181         unsigned int start_off = *off;
182
183         d = dump_int(0xdeadbeef, d, off);
184
185         while (a) {
186 #ifdef EXACT_ADDRESS
187                 d = dump_char(a->val, d, off);
188 #endif
189                 d = dump_char(a->personal, d, off);
190                 d = dump_char(a->mailbox, d, off);
191                 d = dump_int(a->group, d, off);
192                 a = a->next;
193                 counter++;
194         }
195
196         memcpy(d + start_off, &counter, sizeof(int));
197
198         return d;
199 }
200
201 static void
202 restore_address(ADDRESS **a, const unsigned char *d, int *off)
203 {
204         unsigned int counter;
205
206         restore_int(&counter, d, off);
207
208         while (counter) {
209                 *a = safe_malloc(sizeof(ADDRESS));
210 #ifdef EXACT_ADDRESS
211                 restore_char(&(*a)->val, d, off);
212 #endif
213                 restore_char(&(*a)->personal, d, off);
214                 restore_char(&(*a)->mailbox, d, off);
215                 restore_int((unsigned int *)&(*a)->group, d, off);
216                 a = &(*a)->next;
217                 counter--;
218         }
219
220         *a = NULL;
221 }
222
223 static unsigned char *
224 dump_list(LIST *l, unsigned char *d, int *off)
225 {
226         unsigned int counter = 0;
227         unsigned int start_off = *off;
228
229         d = dump_int(0xdeadbeef, d, off);
230
231         while (l) {
232                 d = dump_char(l->data, d, off);
233                 l = l->next;
234                 counter++;
235         }
236
237         memcpy(d + start_off, &counter, sizeof(int));
238
239         return d;
240 }
241
242 static void
243 restore_list(LIST **l, const unsigned char *d, int *off)
244 {
245         unsigned int counter;
246
247         restore_int(&counter, d, off);
248
249         while (counter) {
250                 *l = safe_malloc(sizeof(LIST));
251                 restore_char(&(*l)->data, d, off);
252                 l = &(*l)->next;
253                 counter--;
254         }
255
256         *l = NULL;
257 }
258
259 #if 0
260 static unsigned char *
261 dump_buffer(BUFFER *b, unsigned char *d, int *off)
262 {
263         if (! b) {
264                 d = dump_int(0, d, off);
265                 return d;
266         } else {
267                 d = dump_int(1, d, off);
268         }
269
270         d = dump_char_size(b->data, d, off, b->dsize + 1);
271         d = dump_int(b->dptr - b->data, d, off);
272         d = dump_int(b->dsize, d, off);
273         d = dump_int(b->destroy, d, off);
274
275         return d;
276 }
277
278 static void
279 restore_buffer(BUFFER **b, const unsigned char *d, int *off)
280 {
281         unsigned int used;
282         unsigned int offset;
283         restore_int(&used, d, off);
284         if (! used) {
285                 return;
286         }
287
288         *b = safe_malloc(sizeof(BUFFER));
289
290         restore_char(& (*b)->data, d, off);
291         restore_int(& offset, d, off);
292         (*b)->dptr = (*b)->data + offset;
293         restore_int(& (*b)->dsize, d, off);
294         restore_int((unsigned int *) & (*b)->destroy, d, off);
295 }
296 #endif
297
298 static unsigned char *
299 dump_parameter(PARAMETER *p, unsigned char *d, int *off)
300 {
301         unsigned int counter = 0;
302         unsigned int start_off = *off;
303
304         d = dump_int(0xdeadbeef, d, off);
305
306         while (p) {
307                 d = dump_char(p->attribute, d, off);
308                 d = dump_char(p->value, d, off);
309                 p = p->next;
310                 counter++;
311         }
312
313         memcpy(d + start_off, &counter, sizeof(int));
314
315         return d;
316 }
317
318 static void
319 restore_parameter(PARAMETER **p, const unsigned char *d, int *off)
320 {
321         unsigned int counter;
322
323         restore_int(&counter, d, off);
324
325         while (counter) {
326                 *p = safe_malloc(sizeof(PARAMETER));
327                 restore_char(&(*p)->attribute, d, off);
328                 restore_char(&(*p)->value, d, off);
329                 p = &(*p)->next;
330                 counter--;
331         }
332
333         *p = NULL;
334 }
335
336 static unsigned char *
337 dump_body(BODY *c, unsigned char *d, int *off)
338 {
339         lazy_realloc(&d, *off + sizeof(BODY));
340         memcpy(d + *off, c, sizeof(BODY));
341         *off += sizeof(BODY);
342
343         d = dump_char(c->xtype, d, off);
344         d = dump_char(c->subtype, d, off);
345
346         d = dump_parameter(c->parameter, d, off);
347
348         d = dump_char(c->description, d, off);
349         d = dump_char(c->form_name, d, off);
350         d = dump_char(c->filename, d, off);
351         d = dump_char(c->d_filename, d, off);
352
353         return d;
354 }
355
356 static void
357 restore_body(BODY *c, const unsigned char *d, int *off)
358 {
359         memcpy(c, d + *off, sizeof(BODY));
360         *off += sizeof(BODY);
361
362         restore_char(& c->xtype, d, off);
363         restore_char(& c->subtype, d, off);
364
365         restore_parameter(& c->parameter, d, off);
366
367         restore_char(& c->description, d, off);
368         restore_char(& c->form_name, d, off);
369         restore_char(& c->filename, d, off);
370         restore_char(& c->d_filename, d, off);
371 }
372
373 static unsigned char *
374 dump_envelope(ENVELOPE *e, unsigned char *d, int *off)
375 {
376         d = dump_address(e->return_path, d, off);
377         d = dump_address(e->from, d, off);
378         d = dump_address(e->to, d, off);
379         d = dump_address(e->cc, d, off);
380         d = dump_address(e->bcc, d, off);
381         d = dump_address(e->sender, d, off);
382         d = dump_address(e->reply_to, d, off);
383         d = dump_address(e->mail_followup_to, d, off);
384
385         d = dump_char(e->subject, d, off);
386         if (e->real_subj) {
387                 d = dump_int(e->real_subj - e->subject, d, off);
388         } else {
389                 d = dump_int(-1, d, off);
390         }
391         d = dump_char(e->message_id, d, off);
392         d = dump_char(e->supersedes, d, off);
393         d = dump_char(e->date, d, off);
394         d = dump_char(e->x_label, d, off);
395
396         d = dump_list(e->references, d, off);
397         d = dump_list(e->in_reply_to, d, off);
398         d = dump_list(e->userhdrs, d, off);
399
400         return d;
401 }
402
403 static void
404 restore_envelope(ENVELOPE *e, const unsigned char *d, int *off)
405 {
406         int real_subj_off;
407
408         restore_address(& e->return_path, d, off);
409         restore_address(& e->from, d, off);
410         restore_address(& e->to, d, off);
411         restore_address(& e->cc, d, off);
412         restore_address(& e->bcc, d, off);
413         restore_address(& e->sender, d, off);
414         restore_address(& e->reply_to, d, off);
415         restore_address(& e->mail_followup_to, d, off);
416
417         restore_char(& e->subject, d, off);
418         restore_int((unsigned int *) (& real_subj_off), d, off);
419         if (0 <= real_subj_off) {
420                 e->real_subj = e->subject + real_subj_off;
421         } else {
422                 e->real_subj = NULL;
423         }
424         restore_char(& e->message_id, d, off);
425         restore_char(& e->supersedes, d, off);
426         restore_char(& e->date, d, off);
427         restore_char(& e->x_label, d, off);
428
429         restore_list(& e->references, d, off);
430         restore_list(& e->in_reply_to, d, off);
431         restore_list(& e->userhdrs, d, off);
432 }
433
434 static
435 unsigned int crc32(unsigned int crc, unsigned char const *p, size_t len)
436 {
437         int i;
438         while (len--) {
439                 crc ^= *p++;
440                 for (i = 0; i < 8; i++)
441                         crc = (crc >> 1) ^ ((crc & 1) ? 0xedb88320 : 0);
442         }
443         return crc;
444 }
445
446 static int
447 generate_crc32()
448 {
449         int crc = 0;
450
451         crc = crc32(crc, (unsigned char const *) "sithglan@stud.uni-erlangen.de[sithglan]|hcache.c|20041108231548|29613", mutt_strlen("sithglan@stud.uni-erlangen.de[sithglan]|hcache.c|20041108231548|29613"));
452
453 #if HAVE_LANGINFO_CODESET
454         crc = crc32(crc, (unsigned char const *) Charset, mutt_strlen(Charset));
455         crc = crc32(crc, (unsigned char const *) "HAVE_LANGINFO_CODESET", mutt_strlen("HAVE_LANGINFO_CODESET"));
456 #endif
457
458 #if EXACT_ADDRESS
459         crc = crc32(crc, (unsigned char const *) "EXACT_ADDRESS", mutt_strlen("EXACT_ADDRESS"));
460 #endif
461
462 #ifdef USE_POP
463         crc = crc32(crc, (unsigned char const *) "USE_POP", mutt_strlen("USE_POP"));
464 #endif
465
466 #ifdef MIXMASTER
467         crc = crc32(crc, (unsigned char const *) "MIXMASTER", mutt_strlen("MIXMASTER"));
468 #endif
469
470 #ifdef USE_IMAP
471         crc = crc32(crc, (unsigned char const *) "USE_IMAP", mutt_strlen("USE_IMAP"));
472 #endif
473         return crc;
474 }
475
476 static int
477 crc32_matches(const char *d, unsigned int crc)
478 {
479         int off = sizeof(validate);
480         unsigned int mycrc = 0;
481
482         if (! d) {
483                 return 0;
484         }
485
486         restore_int(&mycrc, (unsigned char *) d, &off);
487
488         return (crc == mycrc);
489 }
490
491 /* Append md5sumed folder to path if path is a directory. */
492 static const char *
493 mutt_hcache_per_folder(const char *path, const char *folder)
494 {
495         static char mutt_hcache_per_folder_path[_POSIX_PATH_MAX];
496         struct stat path_stat;
497         MD5_CTX md5;
498         unsigned char md5sum[16];
499         int ret;
500
501         ret = stat(path, &path_stat);
502         if (ret < 0) {
503                 return path;
504         }
505
506         if (! S_ISDIR(path_stat.st_mode)) {
507                 return path;
508         }
509
510         MD5Init(&md5);
511         MD5Update(&md5, (unsigned char *) folder, strlen(folder));
512         MD5Final(md5sum, &md5);
513
514         ret = snprintf(mutt_hcache_per_folder_path, _POSIX_PATH_MAX,
515                         "%s/%02x%02x%02x%02x%02x%02x%02x%02x"
516                         "%02x%02x%02x%02x%02x%02x%02x%02x",
517                         path, md5sum[0], md5sum[1], md5sum[2], md5sum[3],
518                         md5sum[4], md5sum[5], md5sum[6], md5sum[7], md5sum[8],
519                         md5sum[9], md5sum[10], md5sum[11], md5sum[12],
520                         md5sum[13], md5sum[14], md5sum[15]);
521
522         if (ret <= 0) {
523                 return path;
524         }
525
526         return mutt_hcache_per_folder_path;
527 }
528
529 /* This function transforms a header into a char so that it is useable by
530  * db_store */
531 static void *
532 mutt_hcache_dump(void *_db, HEADER *h, int *off, uint64_t uid_validity)
533 {
534         struct header_cache *db = _db;
535         unsigned char *d = NULL;
536         *off             = 0;
537
538         d = lazy_malloc(sizeof(validate));
539
540         if (uid_validity) {
541                 memcpy(d, &uid_validity, sizeof(uint64_t));
542         } else {
543                 struct timeval now;
544                 gettimeofday(&now, NULL);
545                 memcpy(d, &now, sizeof(struct timeval));
546         }
547         *off += sizeof(validate);
548
549         d = dump_int(db->crc, d, off);
550
551         lazy_realloc(&d, *off + sizeof(HEADER));
552         memcpy(d + *off, h, sizeof(HEADER));
553         *off += sizeof(HEADER);
554
555         d = dump_envelope(h->env, d, off);
556         d = dump_body(h->content, d, off);
557         d = dump_char(h->maildir_flags, d, off);
558
559         return d;
560 }
561
562 HEADER *
563 mutt_hcache_restore(const unsigned char *d, HEADER **oh)
564 {
565         int off = 0;
566         HEADER *h        = mutt_new_header();
567
568         /* skip validate */
569         off += sizeof(validate);
570
571         /* skip crc */
572         off += sizeof(unsigned int);
573
574         memcpy(h, d + off, sizeof(HEADER));
575         off += sizeof(HEADER);
576
577         h->env = mutt_new_envelope();
578         restore_envelope(h->env, d, &off);
579
580         h->content = mutt_new_body();
581         restore_body(h->content, d, &off);
582
583         restore_char(&h->maildir_flags, d, &off);
584
585         /* this is needed for maildir style mailboxes */
586         if (oh) {
587                 h->old  = (*oh)->old;
588                 h->path = safe_strdup((*oh)->path);
589                 mutt_free_header (oh);
590         }
591
592         return h;
593 }
594
595 #if HAVE_GDBM
596
597 void *
598 mutt_hcache_open(const char *path, const char *folder)
599 {
600         struct header_cache *h = safe_calloc(1, sizeof(HEADER_CACHE));
601         int pagesize = atoi(HeaderCachePageSize) ? atoi(HeaderCachePageSize) : 16384;
602         h->db     = NULL;
603         h->folder = safe_strdup (folder);
604         h->crc    = generate_crc32();
605
606         if (! path || path[0] == '\0') {
607                 FREE(& h->folder);
608                 FREE(& h);
609                 return NULL;
610         }
611
612         path = mutt_hcache_per_folder(path, folder);
613
614         h->db = gdbm_open((char *) path, pagesize, GDBM_WRCREAT, 00600, NULL);
615         if (h->db) {
616                 return h;
617         }
618
619         /* if rw failed try ro */
620         h->db = gdbm_open((char *) path, pagesize, GDBM_READER, 00600, NULL);
621         if(h->db) {
622                 return h;
623         } else {
624                 FREE(& h->folder);
625                 FREE(& h);
626
627                 return NULL;
628         }
629 }
630
631 void
632 mutt_hcache_close(void *db)
633 {
634         struct header_cache *h = db;
635
636         if (! h) {
637                 return;
638         }
639
640         gdbm_close(h->db);
641         FREE(& h->folder);
642         FREE(& h);
643 }
644
645 void *
646 mutt_hcache_fetch(void *db, const char *filename, size_t (*keylen)(const char *fn))
647 {
648         struct header_cache *h = db;
649         datum key;
650         datum data;
651         char path[_POSIX_PATH_MAX];
652
653         if (! h) {
654                 return NULL;
655         }
656
657         strncpy(path, h->folder, sizeof(path));
658         strncat(path, filename, sizeof(path) - mutt_strlen(path));
659
660         key.dptr  = path;
661         key.dsize = keylen(path);
662
663         data = gdbm_fetch(h->db, key);
664
665         if (! crc32_matches(data.dptr, h->crc)) {
666                 free(data.dptr);
667                 return NULL;
668         }
669
670         return data.dptr;
671 }
672
673 int
674 mutt_hcache_store(void *db, const char *filename, HEADER *header, uint64_t uid_validity, size_t (*keylen)(const char *fn))
675 {
676         struct header_cache *h = db;
677         datum key;
678         datum data;
679         char path[_POSIX_PATH_MAX];
680         int ret;
681
682         if (! h) {
683                 return -1;
684         }
685
686         strncpy(path, h->folder, sizeof(path));
687         strncat(path, filename, sizeof(path) - mutt_strlen(path));
688
689         key.dptr  = path;
690         key.dsize = keylen(path);
691
692         data.dptr = mutt_hcache_dump(db, header, &data.dsize, uid_validity);
693
694         ret = gdbm_store(h->db, key, data, GDBM_REPLACE);
695
696         FREE(& data.dptr);
697
698         return ret;
699 }
700
701 int
702 mutt_hcache_delete(void *db, const char *filename, size_t (*keylen)(const char *fn))
703 {
704         datum key;
705         struct header_cache *h = db;
706         char path[_POSIX_PATH_MAX];
707
708         if (! h) {
709                 return -1;
710         }
711
712         strncpy(path, h->folder, sizeof(path));
713         strncat(path, filename, sizeof(path) - mutt_strlen(path));
714
715         key.dptr  = path;
716         key.dsize = keylen(path);
717
718         return gdbm_delete(h->db, key);
719 }
720 #elif HAVE_DB4
721
722 static void
723 mutt_hcache_dbt_init(DBT *dbt, void *data, size_t len)
724 {
725         dbt->data = data;
726         dbt->size = dbt->ulen = len;
727         dbt->dlen = dbt->doff = 0;
728         dbt->flags = DB_DBT_USERMEM;
729 }
730
731 static void
732 mutt_hcache_dbt_empty_init(DBT *dbt)
733 {
734         dbt->data = NULL;
735         dbt->size = dbt->ulen = dbt->dlen = dbt->doff = 0;
736         dbt->flags = 0;
737 }
738
739 void *
740 mutt_hcache_open(const char *path, const char *folder)
741 {
742         struct stat sb;
743         u_int32_t createflags = DB_CREATE;
744         int ret;
745         struct header_cache *h = calloc(1, sizeof(HEADER_CACHE));
746         int pagesize = atoi(HeaderCachePageSize);
747
748
749         h->crc = generate_crc32();
750
751         if (! path || path[0] == '\0') {
752                 FREE(& h);
753                 return NULL;
754         }
755
756         path = mutt_hcache_per_folder(path, folder);
757
758         snprintf (h->lockfile, _POSIX_PATH_MAX, "%s-lock-hack", path);
759
760         h->fd = open(h->lockfile, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
761         if (h->fd < 0) {
762                 FREE (&h);
763                 return NULL;
764         }
765
766         if (mx_lock_file(h->lockfile, h->fd, 1, 0, 5)) {
767                 close(h->fd);
768                 FREE (&h);
769                 return NULL;
770         }
771
772         ret = db_env_create(&h->env, 0);
773         if (ret) {
774                 mx_unlock_file(h->lockfile, h->fd, 0);
775                 close(h->fd);
776                 FREE(& h);
777                 return NULL;
778         }
779
780         ret = h->env->open(h->env, NULL, DB_INIT_MPOOL | DB_CREATE | DB_PRIVATE, 0600);
781         if (! ret) {
782                 ret = db_create(&h->db, h->env, 0);
783                 if (ret) {
784                         h->env->close(h->env, 0);
785                         mx_unlock_file(h->lockfile, h->fd, 0);
786                         close(h->fd);
787                         FREE(& h);
788                         return NULL;
789                 }
790         }
791
792         if (stat(path, &sb) != 0 && errno == ENOENT) {
793                 createflags |= DB_EXCL;
794                 h->db->set_pagesize(h->db, pagesize);
795         }
796
797         ret = h->db->open(h->db, NULL, path, folder, DB_BTREE, createflags, 0600);
798         if (ret) {
799                 h->db->close(h->db, 0);
800                 h->env->close(h->env, 0);
801                 mx_unlock_file(h->lockfile, h->fd, 0);
802                 close(h->fd);
803                 FREE(& h);
804                 return NULL;
805         }
806
807         return h;
808 }
809
810 void
811 mutt_hcache_close(void *db)
812 {
813         struct header_cache *h = db;
814         int ret;
815
816         if (! h) {
817                 return;
818         }
819
820         h->db->close(h->db, 0);
821         h->env->close(h->env, 0);
822         mx_unlock_file(h->lockfile, h->fd, 0);
823         close(h->fd);
824         FREE(& h);
825 }
826
827 void *
828 mutt_hcache_fetch(void *db, const char *filename, size_t (*keylen)(const char *fn))
829 {
830         DBT key;
831         DBT data;
832         struct header_cache *h = db;
833
834         if (! h) {
835                 return NULL;
836         }
837
838         filename++; /* skip '/' */
839
840         mutt_hcache_dbt_init(&key, (void *) filename, keylen(filename));
841         mutt_hcache_dbt_empty_init(&data);
842         data.flags = DB_DBT_MALLOC;
843
844         h->db->get(h->db, NULL, &key, &data, 0);
845
846         if (! crc32_matches(data.data, h->crc)) {
847                 free(data.data);
848                 return NULL;
849         }
850
851         return data.data;
852 }
853
854 int
855 mutt_hcache_store(void *db, const char *filename, HEADER *header, uint64_t uid_validity, size_t (*keylen)(const char *fn))
856 {
857         DBT key;
858         DBT data;
859         int ret;
860         struct header_cache *h = db;
861
862         if (! h) {
863                 return -1;
864         }
865
866         filename++; /* skip '/' */
867
868         mutt_hcache_dbt_init(&key, (void *) filename, keylen(filename));
869
870         mutt_hcache_dbt_empty_init(&data);
871         data.flags = DB_DBT_USERMEM;
872         data.data = mutt_hcache_dump(db, header, (signed int *) &data.size, uid_validity);
873         data.ulen = data.size;
874
875         ret = h->db->put(h->db, NULL, &key, &data, 0);
876
877         FREE(& data.data);
878
879         return ret;
880 }
881
882 int
883 mutt_hcache_delete(void *db, const char *filename, size_t (*keylen)(const char *fn))
884 {
885         DBT key;
886         struct header_cache *h = db;
887
888         if (! h) {
889                 return -1;
890         }
891
892         filename++; /* skip '/' */
893
894         mutt_hcache_dbt_init(&key, (void *) filename, keylen(filename));
895         return h->db->del(h->db, NULL, &key, 0);
896 }
897 #endif
898
899 #endif /* USE_HCACHE */