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