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