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