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