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