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