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