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