last rewrite in rfc822parse.c
[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 #include <imap/message.h>
50 #include "mx.h"
51 #include "lib.h"
52
53 #include "lib/debug.h"
54
55 #if HAVE_QDBM
56 static struct
57   header_cache {
58   VILLA *db;
59   char *folder;
60   unsigned int crc;
61 } HEADER_CACHE;
62 #elif HAVE_GDBM
63 static struct
64   header_cache {
65   GDBM_FILE db;
66   char *folder;
67   unsigned int crc;
68 } HEADER_CACHE;
69 #elif HAVE_DB4
70 static struct
71   header_cache {
72   DB_ENV *env;
73   DB *db;
74   unsigned int crc;
75   int fd;
76   char lockfile[_POSIX_PATH_MAX];
77 } HEADER_CACHE;
78 #endif
79
80 typedef union {
81   struct timeval timeval;
82   unsigned long uid_validity;
83 } validate;
84
85 static void *lazy_malloc (size_t siz)
86 {
87   if (0 < siz && siz < 4096) {
88     siz = 4096;
89   }
90
91   return xmalloc(siz);
92 }
93
94 static void lazy_realloc (void *ptr, size_t siz)
95 {
96   void **p = (void **) ptr;
97
98   if (p != NULL && 0 < siz && siz < 4096) {
99     return;
100   }
101
102   p_realloc(ptr, siz);
103 }
104
105 static unsigned char *dump_int (unsigned int i, unsigned char *d, int *off)
106 {
107   lazy_realloc (&d, *off + sizeof (int));
108   memcpy (d + *off, &i, sizeof (int));
109   (*off) += sizeof (int);
110
111   return d;
112 }
113
114 static void restore_int (unsigned int *i, const unsigned char *d, int *off)
115 {
116   memcpy (i, d + *off, sizeof (int));
117   (*off) += sizeof (int);
118 }
119
120 static unsigned char *dump_char (char *c, unsigned char *d, int *off)
121 {
122   unsigned int size;
123
124   if (c == NULL) {
125     size = 0;
126     d = dump_int (size, d, off);
127     return d;
128   }
129
130   size = m_strlen(c) + 1;
131   d = dump_int (size, d, off);
132   lazy_realloc (&d, *off + size);
133   memcpy (d + *off, c, size);
134   *off += size;
135
136   return d;
137 }
138
139 #if 0
140 static unsigned char *dump_char_size (char *c, unsigned char *d, int *off,
141                                       ssize_t size)
142 {
143   if (c == NULL) {
144     size = 0;
145     d = dump_int (size, d, off);
146     return d;
147   }
148
149   d = dump_int (size, d, off);
150   lazy_realloc (&d, *off + size);
151   memcpy (d + *off, c, size);
152   *off += size;
153
154   return d;
155 }
156 #endif
157
158 static void restore_char (char **c, const unsigned char *d, int *off)
159 {
160   unsigned int size;
161
162   restore_int (&size, d, off);
163
164   if (size == 0) {
165     *c = NULL;
166     return;
167   }
168
169   *c = p_dup(d + *off, size);
170   *off += size;
171 }
172
173 static unsigned char *dump_address (address_t * 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_t ** 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 = p_new(address_t, 1);
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 = p_new(LIST, 1);
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 = p_new(BUFFER, 1);
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 = p_new(PARAMETER, 1);
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                m_strlen
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, m_strlen(Charset));
458   crc = crc32(crc, (unsigned char const *) "HAVE_LANGINFO_CODESET",
459               m_strlen("HAVE_LANGINFO_CODESET"));
460 #endif
461
462   crc = crc32(crc, (unsigned char const *) "USE_POP", m_strlen("USE_POP"));
463
464 #ifdef MIXMASTER
465   crc = crc32(crc, (unsigned char const *) "MIXMASTER",
466               m_strlen("MIXMASTER"));
467 #endif
468
469   crc = crc32(crc, (unsigned char const *) "USE_IMAP", m_strlen("USE_IMAP"));
470
471 #ifdef USE_NNTP
472   crc = crc32(crc, (unsigned char const *) "USE_NNTP", m_strlen("USE_NNTP"));
473 #endif
474   return crc;
475 }
476
477 static int crc32_matches (const char *d, unsigned int crc)
478 {
479   int off = sizeof (validate);
480   unsigned int mycrc = 0;
481
482   if (!d) {
483     return 0;
484   }
485
486   restore_int (&mycrc, (unsigned char *) d, &off);
487
488   return (crc == mycrc);
489 }
490
491 /* Append md5sumed folder to path if path is a directory. */
492 static const char *mutt_hcache_per_folder (const char *path,
493                                            const char *folder)
494 {
495   static char mutt_hcache_per_folder_path[_POSIX_PATH_MAX];
496   struct stat path_stat;
497   MD5_CTX md5;
498   unsigned char md5sum[16];
499   int ret;
500
501   ret = stat (path, &path_stat);
502   if (ret < 0) {
503     return path;
504   }
505
506   if (!S_ISDIR (path_stat.st_mode)) {
507     return path;
508   }
509
510   MD5Init (&md5);
511   MD5Update (&md5, (unsigned char *) folder, m_strlen(folder));
512   MD5Final (md5sum, &md5);
513
514   ret = snprintf (mutt_hcache_per_folder_path, _POSIX_PATH_MAX,
515                   "%s/%02x%02x%02x%02x%02x%02x%02x%02x"
516                   "%02x%02x%02x%02x%02x%02x%02x%02x",
517                   path, md5sum[0], md5sum[1], md5sum[2], md5sum[3],
518                   md5sum[4], md5sum[5], md5sum[6], md5sum[7], md5sum[8],
519                   md5sum[9], md5sum[10], md5sum[11], md5sum[12],
520                   md5sum[13], md5sum[14], md5sum[15]);
521
522   if (ret <= 0) {
523     return path;
524   }
525
526   return mutt_hcache_per_folder_path;
527 }
528
529 /* This function transforms a header into a char so that it is useable by
530  * db_store */
531 static void *mutt_hcache_dump (void *_db, HEADER * h, int *off,
532                                unsigned long uid_validity)
533 {
534   struct header_cache *db = _db;
535   unsigned char *d = NULL;
536
537   *off = 0;
538
539   d = lazy_malloc (sizeof (validate));
540
541   if (uid_validity) {
542     memcpy (d, &uid_validity, sizeof (unsigned long));
543   }
544   else {
545     struct timeval now;
546
547     gettimeofday (&now, NULL);
548     memcpy (d, &now, sizeof (struct timeval));
549   }
550   *off += sizeof (validate);
551
552   d = dump_int (db->crc, d, off);
553
554   lazy_realloc (&d, *off + sizeof (HEADER));
555   memcpy (d + *off, h, sizeof (HEADER));
556   *off += sizeof (HEADER);
557
558   d = dump_envelope (h->env, d, off);
559   d = dump_body (h->content, d, off);
560   d = dump_char (h->maildir_flags, d, off);
561
562   return d;
563 }
564
565 HEADER *mutt_hcache_restore (const unsigned char *d, HEADER ** oh)
566 {
567   int off = 0;
568   HEADER *h = header_new();
569
570   /* skip validate */
571   off += sizeof (validate);
572
573   /* skip crc */
574   off += sizeof (unsigned int);
575
576   memcpy (h, d + off, sizeof (HEADER));
577   off += sizeof (HEADER);
578
579   h->env = envelope_new();
580   restore_envelope (h->env, d, &off);
581
582   h->content = mutt_new_body ();
583   restore_body (h->content, d, &off);
584
585   restore_char (&h->maildir_flags, d, &off);
586
587   /* this is needed for maildir style mailboxes */
588   if (oh) {
589     h->old = (*oh)->old;
590     h->path = m_strdup((*oh)->path);
591     header_delete(oh);
592   }
593
594   return h;
595 }
596
597 #if HAVE_QDBM
598 void *
599 mutt_hcache_open(const char *path, const char *folder)
600 {
601   struct header_cache *h = p_new(HEADER_CACHE, 1);
602   int    flags = VL_OWRITER | VL_OCREAT;
603   h->db = NULL;
604   h->folder = m_strdup(folder);
605   h->crc = generate_crc32();
606
607   if (!path || path[0] == '\0')
608   {
609     p_delete(&h->folder);
610     p_delete(&h);
611     return NULL;
612   }
613
614   path = mutt_hcache_per_folder(path, folder);
615
616   if (option(OPTHCACHECOMPRESS))
617     flags |= VL_OZCOMP;
618
619   h->db = vlopen(path, flags, VL_CMPLEX);
620   if (h->db)
621     return h;
622   else
623   {
624     p_delete(&h->folder);
625     p_delete(&h);
626
627     return NULL;
628   }
629 }
630
631 void
632 mutt_hcache_close(void *db)
633 {
634   struct header_cache *h = db;
635
636   if (!h)
637     return;
638
639   vlclose(h->db);
640   p_delete(&h->folder);
641   p_delete(&h);
642 }
643
644 void *
645 mutt_hcache_fetch(void *db, const char *filename,
646                   size_t(*keylen) (const char *fn))
647 {
648   struct header_cache *h = db;
649   char path[_POSIX_PATH_MAX];
650   int ksize, len;
651   char *data = NULL;
652
653   if (!h)
654     return NULL;
655
656   m_strcpy(path, sizeof(path), h->folder);
657   m_strcat(path, sizeof(path), filename);
658
659   ksize = strlen(h->folder) + keylen(path + strlen(h->folder));
660
661   data = vlget(h->db, path, ksize, NULL);
662
663   if (!crc32_matches(data, h->crc))
664   {
665     p_delete(&data);
666     return NULL;
667   }
668
669   return data;
670 }
671
672 int
673 mutt_hcache_store(void *db, const char *filename, HEADER * header,
674                   unsigned long uid_validity,
675                   size_t(*keylen) (const char *fn))
676 {
677   struct header_cache *h = db;
678   char path[_POSIX_PATH_MAX];
679   int ret;
680   int ksize, dsize;
681   char *data = NULL;
682
683   if (!h)
684     return -1;
685
686   m_strcpy(path, sizeof(path), h->folder);
687   m_strcat(path, sizeof(path), filename);
688
689   ksize = strlen(h->folder) + keylen(path + strlen(h->folder));
690
691   data  = mutt_hcache_dump(db, header, &dsize, uid_validity);
692
693   ret = vlput(h->db, path, ksize, data, dsize, VL_DOVER);
694
695   p_delete(&data);
696
697   return ret;
698 }
699
700 int
701 mutt_hcache_delete(void *db, const char *filename,
702                    size_t(*keylen) (const char *fn))
703 {
704   struct header_cache *h = db;
705   char path[_POSIX_PATH_MAX];
706   int ksize;
707
708   if (!h)
709     return -1;
710
711   m_strcpy(path, sizeof(path), h->folder);
712   m_strcat(path, sizeof(path), filename);
713
714   ksize = strlen(h->folder) + keylen(path + strlen(h->folder));
715
716   return vlout(h->db, path, ksize);
717 }
718
719 #elif HAVE_GDBM
720
721 void *mutt_hcache_open (const char *path, const char *folder)
722 {
723   struct header_cache *h = p_new(HEADER_CACHE, 1);
724   int pagesize =
725     atoi (HeaderCachePageSize) ? atoi (HeaderCachePageSize) : 16384;
726   h->db = NULL;
727   h->folder = m_strdup(folder);
728   h->crc = generate_crc32 ();
729
730   if (!path || path[0] == '\0') {
731     p_delete(&h->folder);
732     p_delete(&h);
733     return NULL;
734   }
735
736   path = mutt_hcache_per_folder (path, folder);
737
738   h->db = gdbm_open ((char *) path, pagesize, GDBM_WRCREAT, 00600, NULL);
739   if (h->db) {
740     return h;
741   }
742
743   /* if rw failed try ro */
744   h->db = gdbm_open ((char *) path, pagesize, GDBM_READER, 00600, NULL);
745   if (h->db) {
746     return h;
747   }
748   else {
749     p_delete(&h->folder);
750     p_delete(&h);
751
752     return NULL;
753   }
754 }
755
756 void mutt_hcache_close (void *db)
757 {
758   struct header_cache *h = db;
759
760   if (!h) {
761     return;
762   }
763
764   gdbm_close (h->db);
765   p_delete(&h->folder);
766   p_delete(&h);
767 }
768
769 void *mutt_hcache_fetch (void *db, const char *filename,
770                          size_t (*keylen) (const char *fn))
771 {
772   struct header_cache *h = db;
773   datum key;
774   datum data;
775   char path[_POSIX_PATH_MAX];
776
777   if (!h) {
778     return NULL;
779   }
780
781   m_strcpy(path, sizeof(path), h->folder);
782   strncat (path, filename, sizeof (path) - m_strlen(path));
783
784   key.dptr = path;
785   key.dsize = keylen (path);
786
787   data = gdbm_fetch (h->db, key);
788
789   if (!crc32_matches (data.dptr, h->crc)) {
790     p_delete(&data.dptr);
791     return NULL;
792   }
793
794   return data.dptr;
795 }
796
797 int
798 mutt_hcache_store (void *db, const char *filename, HEADER * header,
799                    unsigned long uid_validity, size_t (*keylen) (const char *fn))
800 {
801   struct header_cache *h = db;
802   datum key;
803   datum data;
804   char path[_POSIX_PATH_MAX];
805   int ret;
806
807   if (!h) {
808     return -1;
809   }
810
811   m_strcpy(path, sizeof(path), h->folder);
812   strncat (path, filename, sizeof (path) - m_strlen(path));
813
814   key.dptr = path;
815   key.dsize = keylen (path);
816
817   data.dptr = mutt_hcache_dump (db, header, &data.dsize, uid_validity);
818
819   ret = gdbm_store (h->db, key, data, GDBM_REPLACE);
820
821   p_delete(&data.dptr);
822
823   return ret;
824 }
825
826 int
827 mutt_hcache_delete (void *db, const char *filename,
828                     size_t (*keylen) (const char *fn))
829 {
830   datum key;
831   struct header_cache *h = db;
832   char path[_POSIX_PATH_MAX];
833
834   if (!h) {
835     return -1;
836   }
837
838   m_strcpy(path, sizeof(path), h->folder);
839   strncat (path, filename, sizeof (path) - m_strlen(path));
840
841   key.dptr = path;
842   key.dsize = keylen (path);
843
844   return gdbm_delete (h->db, key);
845 }
846 #elif HAVE_DB4
847
848 static void mutt_hcache_dbt_init (DBT * dbt, void *data, size_t len)
849 {
850   dbt->data = data;
851   dbt->size = dbt->ulen = len;
852   dbt->dlen = dbt->doff = 0;
853   dbt->flags = DB_DBT_USERMEM;
854 }
855
856 static void mutt_hcache_dbt_empty_init (DBT * dbt)
857 {
858   dbt->data = NULL;
859   dbt->size = dbt->ulen = dbt->dlen = dbt->doff = 0;
860   dbt->flags = 0;
861 }
862
863 void *mutt_hcache_open (const char *path, const char *folder)
864 {
865   struct stat sb;
866   u_int32_t createflags = DB_CREATE;
867   int ret;
868   struct header_cache *h = calloc (1, sizeof (HEADER_CACHE));
869   int pagesize = atoi (HeaderCachePageSize);
870
871
872   h->crc = generate_crc32 ();
873
874   if (!path || path[0] == '\0') {
875     p_delete(&h);
876     return NULL;
877   }
878
879   path = mutt_hcache_per_folder (path, folder);
880
881   snprintf (h->lockfile, _POSIX_PATH_MAX, "%s-lock-hack", path);
882
883   h->fd = open (h->lockfile, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
884   if (h->fd < 0) {
885     p_delete(&h);
886     return NULL;
887   }
888
889   if (mx_lock_file (h->lockfile, h->fd, 1, 0, 5)) {
890     close (h->fd);
891     p_delete(&h);
892     return NULL;
893   }
894
895   ret = db_env_create (&h->env, 0);
896   if (ret) {
897     mx_unlock_file (h->lockfile, h->fd, 0);
898     close (h->fd);
899     p_delete(&h);
900     return NULL;
901   }
902
903   ret =
904     (h->env->open)(h->env, NULL, DB_INIT_MPOOL | DB_CREATE | DB_PRIVATE, 0600);
905   if (!ret) {
906     ret = db_create (&h->db, h->env, 0);
907     if (ret) {
908       h->env->close (h->env, 0);
909       mx_unlock_file (h->lockfile, h->fd, 0);
910       close (h->fd);
911       p_delete(&h);
912       return NULL;
913     }
914   }
915
916   if (stat (path, &sb) != 0 && errno == ENOENT) {
917     createflags |= DB_EXCL;
918     h->db->set_pagesize (h->db, pagesize);
919   }
920
921   ret = (h->db->open)(h->db, NULL, path, folder, DB_BTREE, createflags, 0600);
922   if (ret) {
923     h->db->close (h->db, 0);
924     h->env->close (h->env, 0);
925     mx_unlock_file (h->lockfile, h->fd, 0);
926     close (h->fd);
927     p_delete(&h);
928     return NULL;
929   }
930
931   return h;
932 }
933
934 void mutt_hcache_close (void *db)
935 {
936   struct header_cache *h = db;
937
938   if (!h) {
939     return;
940   }
941
942   h->db->close (h->db, 0);
943   h->env->close (h->env, 0);
944   mx_unlock_file (h->lockfile, h->fd, 0);
945   close (h->fd);
946   p_delete(&h);
947 }
948
949 void *mutt_hcache_fetch (void *db, const char *filename,
950                          size_t (*keylen) (const char *fn))
951 {
952   DBT key;
953   DBT data;
954   struct header_cache *h = db;
955
956   if (!h) {
957     return NULL;
958   }
959
960   filename++;                   /* skip '/' */
961
962   mutt_hcache_dbt_init (&key, (void *) filename, keylen (filename));
963   mutt_hcache_dbt_empty_init (&data);
964   data.flags = DB_DBT_MALLOC;
965
966   h->db->get (h->db, NULL, &key, &data, 0);
967
968   if (!crc32_matches (data.data, h->crc)) {
969     p_delete(&data.data);
970     return NULL;
971   }
972
973   return data.data;
974 }
975
976 int
977 mutt_hcache_store (void *db, const char *filename, HEADER * header,
978                    unsigned long uid_validity, size_t (*keylen) (const char *fn))
979 {
980   DBT key;
981   DBT data;
982   int ret;
983   struct header_cache *h = db;
984
985   if (!h) {
986     return -1;
987   }
988
989   filename++;                   /* skip '/' */
990
991   mutt_hcache_dbt_init (&key, (void *) filename, keylen (filename));
992
993   mutt_hcache_dbt_empty_init (&data);
994   data.flags = DB_DBT_USERMEM;
995   data.data =
996     mutt_hcache_dump (db, header, (signed int *) &data.size, uid_validity);
997   data.ulen = data.size;
998
999   ret = h->db->put (h->db, NULL, &key, &data, 0);
1000
1001   p_delete(&data.data);
1002
1003   return ret;
1004 }
1005
1006 int
1007 mutt_hcache_delete (void *db, const char *filename,
1008                     size_t (*keylen) (const char *fn))
1009 {
1010   DBT key;
1011   struct header_cache *h = db;
1012
1013   if (!h) {
1014     return -1;
1015   }
1016
1017   filename++;                   /* skip '/' */
1018
1019   mutt_hcache_dbt_init (&key, (void *) filename, keylen (filename));
1020   return h->db->del (h->db, NULL, &key, 0);
1021 }
1022 #endif
1023
1024 #endif /* USE_HCACHE */