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