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