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