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