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   return d;
391 }
392
393 static void restore_envelope (ENVELOPE * e, const unsigned char *d, int *off)
394 {
395   int real_subj_off;
396
397   restore_address (&e->return_path, d, off);
398   restore_address (&e->from, d, off);
399   restore_address (&e->to, d, off);
400   restore_address (&e->cc, d, off);
401   restore_address (&e->bcc, d, off);
402   restore_address (&e->sender, d, off);
403   restore_address (&e->reply_to, d, off);
404   restore_address (&e->mail_followup_to, d, off);
405
406   restore_char (&e->subject, d, off);
407   restore_int ((unsigned int *) (&real_subj_off), d, off);
408   if (0 <= real_subj_off) {
409     e->real_subj = e->subject + real_subj_off;
410   }
411   else {
412     e->real_subj = NULL;
413   }
414   restore_char (&e->message_id, d, off);
415   restore_char (&e->supersedes, d, off);
416   restore_char (&e->date, d, off);
417   restore_char (&e->x_label, d, off);
418   
419 #ifdef USE_NNTP
420   restore_char (&e->newsgroups, d, off);
421   restore_char (&e->xref, d, off);
422   restore_char (&e->followup_to, d, off);
423   restore_char (&e->x_comment_to, d, off);
424 #endif
425
426   restore_list (&e->references, d, off);
427   restore_list (&e->in_reply_to, d, off);
428   restore_list (&e->userhdrs, d, off);
429 }
430
431 static
432 unsigned int crc32 (unsigned int crc, unsigned char const *p, size_t len)
433 {
434   int i;
435
436   while (len--) {
437     crc ^= *p++;
438     for (i = 0; i < 8; i++)
439       crc = (crc >> 1) ^ ((crc & 1) ? 0xedb88320 : 0);
440   }
441   return crc;
442 }
443
444 static int generate_crc32 ()
445 {
446   int crc = 0;
447
448   crc = crc32 (crc, (unsigned char const *)
449                "sithglan@stud.uni-erlangen.de[sithglan]|hcache.c|20041108231548|29613",
450                mutt_strlen
451                ("sithglan@stud.uni-erlangen.de[sithglan]|hcache.c|20041108231548|29613"));
452
453 #if HAVE_LANGINFO_CODESET
454   crc = crc32 (crc, (unsigned char const *) Charset, mutt_strlen (Charset));
455   crc =
456     crc32 (crc, (unsigned char const *) "HAVE_LANGINFO_CODESET",
457            mutt_strlen ("HAVE_LANGINFO_CODESET"));
458 #endif
459
460 #ifdef USE_POP
461   crc =
462     crc32 (crc, (unsigned char const *) "USE_POP", mutt_strlen ("USE_POP"));
463 #endif
464
465 #ifdef MIXMASTER
466   crc =
467     crc32 (crc, (unsigned char const *) "MIXMASTER",
468            mutt_strlen ("MIXMASTER"));
469 #endif
470
471 #ifdef USE_IMAP
472   crc =
473     crc32 (crc, (unsigned char const *) "USE_IMAP", mutt_strlen ("USE_IMAP"));
474 #endif
475
476 #ifdef USE_NNTP
477   crc =
478     crc32 (crc, (unsigned char const *) "USE_NNTP", mutt_strlen ("USE_NNTP"));
479 #endif
480   return crc;
481 }
482
483 static int crc32_matches (const char *d, unsigned int crc)
484 {
485   int off = sizeof (validate);
486   unsigned int mycrc = 0;
487
488   if (!d) {
489     return 0;
490   }
491
492   restore_int (&mycrc, (unsigned char *) d, &off);
493
494   return (crc == mycrc);
495 }
496
497 /* Append md5sumed folder to path if path is a directory. */
498 static const char *mutt_hcache_per_folder (const char *path,
499                                            const char *folder)
500 {
501   static char mutt_hcache_per_folder_path[_POSIX_PATH_MAX];
502   struct stat path_stat;
503   MD5_CTX md5;
504   unsigned char md5sum[16];
505   int ret;
506
507   ret = stat (path, &path_stat);
508   if (ret < 0) {
509     return path;
510   }
511
512   if (!S_ISDIR (path_stat.st_mode)) {
513     return path;
514   }
515
516   MD5Init (&md5);
517   MD5Update (&md5, (unsigned char *) folder, mutt_strlen (folder));
518   MD5Final (md5sum, &md5);
519
520   ret = snprintf (mutt_hcache_per_folder_path, _POSIX_PATH_MAX,
521                   "%s/%02x%02x%02x%02x%02x%02x%02x%02x"
522                   "%02x%02x%02x%02x%02x%02x%02x%02x",
523                   path, md5sum[0], md5sum[1], md5sum[2], md5sum[3],
524                   md5sum[4], md5sum[5], md5sum[6], md5sum[7], md5sum[8],
525                   md5sum[9], md5sum[10], md5sum[11], md5sum[12],
526                   md5sum[13], md5sum[14], md5sum[15]);
527
528   if (ret <= 0) {
529     return path;
530   }
531
532   return mutt_hcache_per_folder_path;
533 }
534
535 /* This function transforms a header into a char so that it is useable by
536  * db_store */
537 static void *mutt_hcache_dump (void *_db, HEADER * h, int *off,
538                                unsigned long uid_validity)
539 {
540   struct header_cache *db = _db;
541   unsigned char *d = NULL;
542
543   *off = 0;
544
545   d = lazy_malloc (sizeof (validate));
546
547   if (uid_validity) {
548     memcpy (d, &uid_validity, sizeof (unsigned long));
549   }
550   else {
551     struct timeval now;
552
553     gettimeofday (&now, NULL);
554     memcpy (d, &now, sizeof (struct timeval));
555   }
556   *off += sizeof (validate);
557
558   d = dump_int (db->crc, d, off);
559
560   lazy_realloc (&d, *off + sizeof (HEADER));
561   memcpy (d + *off, h, sizeof (HEADER));
562   *off += sizeof (HEADER);
563
564   d = dump_envelope (h->env, d, off);
565   d = dump_body (h->content, d, off);
566   d = dump_char (h->maildir_flags, d, off);
567
568   return d;
569 }
570
571 HEADER *mutt_hcache_restore (const unsigned char *d, HEADER ** oh)
572 {
573   int off = 0;
574   HEADER *h = mutt_new_header ();
575
576   /* skip validate */
577   off += sizeof (validate);
578
579   /* skip crc */
580   off += sizeof (unsigned int);
581
582   memcpy (h, d + off, sizeof (HEADER));
583   off += sizeof (HEADER);
584
585   h->env = mutt_new_envelope ();
586   restore_envelope (h->env, d, &off);
587
588   h->content = mutt_new_body ();
589   restore_body (h->content, d, &off);
590
591   restore_char (&h->maildir_flags, d, &off);
592
593   /* this is needed for maildir style mailboxes */
594   if (oh) {
595     h->old = (*oh)->old;
596     h->path = safe_strdup ((*oh)->path);
597     mutt_free_header (oh);
598   }
599
600   return h;
601 }
602
603 #if HAVE_QDBM
604 void *
605 mutt_hcache_open(const char *path, const char *folder)
606 {
607   struct header_cache *h = safe_calloc(1, sizeof (HEADER_CACHE));
608   int    flags = VL_OWRITER | VL_OCREAT;
609   h->db = NULL;
610   h->folder = safe_strdup(folder);
611   h->crc = generate_crc32();
612
613   if (!path || path[0] == '\0')
614   {
615     FREE(&h->folder);
616     FREE(&h);
617     return NULL;
618   }
619
620   path = mutt_hcache_per_folder(path, folder);
621
622   if (option(OPTHCACHECOMPRESS))
623     flags |= VL_OZCOMP;
624
625   h->db = vlopen(path, flags, VL_CMPLEX);
626   if (h->db)
627     return h;
628   else
629   {
630     FREE(&h->folder);
631     FREE(&h);
632
633     return NULL;
634   }
635 }
636
637 void
638 mutt_hcache_close(void *db)
639 {
640   struct header_cache *h = db;
641
642   if (!h)
643     return;
644
645   vlclose(h->db);
646   FREE(&h->folder);
647   FREE(&h);
648 }
649
650 void *
651 mutt_hcache_fetch(void *db, const char *filename,
652                   size_t(*keylen) (const char *fn))
653 {
654   struct header_cache *h = db;
655   char path[_POSIX_PATH_MAX];
656   int ksize;
657   char *data = NULL;
658
659   if (!h)
660     return NULL;
661
662   strncpy(path, h->folder, sizeof (path));
663   safe_strcat(path, sizeof (path), filename);
664
665   ksize = strlen(h->folder) + keylen(path + strlen(h->folder));
666
667   data = vlget(h->db, path, ksize, NULL);
668
669   if (! crc32_matches(data, h->crc))
670   {
671     FREE(&data);
672     return NULL;
673   }
674
675   return data;
676 }
677
678 int
679 mutt_hcache_store(void *db, const char *filename, HEADER * header,
680                   unsigned long uid_validity,
681                   size_t(*keylen) (const char *fn))
682 {
683   struct header_cache *h = db;
684   char path[_POSIX_PATH_MAX];
685   int ret;
686   int ksize, dsize;
687   char *data = NULL;
688
689   if (!h)
690     return -1;
691
692   strncpy(path, h->folder, sizeof (path));
693   safe_strcat(path, sizeof (path), filename);
694
695   ksize = strlen(h->folder) + keylen(path + strlen(h->folder));
696
697   data  = mutt_hcache_dump(db, header, &dsize, uid_validity);
698
699   ret = vlput(h->db, path, ksize, data, dsize, VL_DOVER);
700
701   FREE(&data);
702
703   return ret;
704 }
705
706 int
707 mutt_hcache_delete(void *db, const char *filename,
708                    size_t(*keylen) (const char *fn))
709 {
710   struct header_cache *h = db;
711   char path[_POSIX_PATH_MAX];
712   int ksize;
713
714   if (!h)
715     return -1;
716
717   strncpy(path, h->folder, sizeof (path));
718   safe_strcat(path, sizeof (path), filename);
719
720   ksize = strlen(h->folder) + keylen(path + strlen(h->folder));
721
722   return vlout(h->db, path, ksize);
723 }
724
725 #elif HAVE_GDBM
726
727 void *mutt_hcache_open (const char *path, const char *folder)
728 {
729   struct header_cache *h = safe_calloc (1, sizeof (HEADER_CACHE));
730   int pagesize =
731     atoi (HeaderCachePageSize) ? atoi (HeaderCachePageSize) : 16384;
732   h->db = NULL;
733   h->folder = safe_strdup (folder);
734   h->crc = generate_crc32 ();
735
736   if (!path || path[0] == '\0') {
737     FREE (&h->folder);
738     FREE (&h);
739     return NULL;
740   }
741
742   path = mutt_hcache_per_folder (path, folder);
743
744   h->db = gdbm_open ((char *) path, pagesize, GDBM_WRCREAT, 00600, NULL);
745   if (h->db) {
746     return h;
747   }
748
749   /* if rw failed try ro */
750   h->db = gdbm_open ((char *) path, pagesize, GDBM_READER, 00600, NULL);
751   if (h->db) {
752     return h;
753   }
754   else {
755     FREE (&h->folder);
756     FREE (&h);
757
758     return NULL;
759   }
760 }
761
762 void mutt_hcache_close (void *db)
763 {
764   struct header_cache *h = db;
765
766   if (!h) {
767     return;
768   }
769
770   gdbm_close (h->db);
771   FREE (&h->folder);
772   FREE (&h);
773 }
774
775 void *mutt_hcache_fetch (void *db, const char *filename,
776                          size_t (*keylen) (const char *fn))
777 {
778   struct header_cache *h = db;
779   datum key;
780   datum data;
781   char path[_POSIX_PATH_MAX];
782
783   if (!h) {
784     return NULL;
785   }
786
787   strncpy (path, h->folder, sizeof (path));
788   strncat (path, filename, sizeof (path) - mutt_strlen (path));
789
790   key.dptr = path;
791   key.dsize = keylen (path);
792
793   data = gdbm_fetch (h->db, key);
794
795   if (!crc32_matches (data.dptr, h->crc)) {
796     FREE(&data.dptr);
797     return NULL;
798   }
799
800   return data.dptr;
801 }
802
803 int
804 mutt_hcache_store (void *db, const char *filename, HEADER * header,
805                    unsigned long uid_validity, size_t (*keylen) (const char *fn))
806 {
807   struct header_cache *h = db;
808   datum key;
809   datum data;
810   char path[_POSIX_PATH_MAX];
811   int ret;
812
813   if (!h) {
814     return -1;
815   }
816
817   strncpy (path, h->folder, sizeof (path));
818   strncat (path, filename, sizeof (path) - mutt_strlen (path));
819
820   key.dptr = path;
821   key.dsize = keylen (path);
822
823   data.dptr = mutt_hcache_dump (db, header, &data.dsize, uid_validity);
824
825   ret = gdbm_store (h->db, key, data, GDBM_REPLACE);
826
827   FREE (&data.dptr);
828
829   return ret;
830 }
831
832 int
833 mutt_hcache_delete (void *db, const char *filename,
834                     size_t (*keylen) (const char *fn))
835 {
836   datum key;
837   struct header_cache *h = db;
838   char path[_POSIX_PATH_MAX];
839
840   if (!h) {
841     return -1;
842   }
843
844   strncpy (path, h->folder, sizeof (path));
845   strncat (path, filename, sizeof (path) - mutt_strlen (path));
846
847   key.dptr = path;
848   key.dsize = keylen (path);
849
850   return gdbm_delete (h->db, key);
851 }
852 #elif HAVE_DB4
853
854 static void mutt_hcache_dbt_init (DBT * dbt, void *data, size_t len)
855 {
856   dbt->data = data;
857   dbt->size = dbt->ulen = len;
858   dbt->dlen = dbt->doff = 0;
859   dbt->flags = DB_DBT_USERMEM;
860 }
861
862 static void mutt_hcache_dbt_empty_init (DBT * dbt)
863 {
864   dbt->data = NULL;
865   dbt->size = dbt->ulen = dbt->dlen = dbt->doff = 0;
866   dbt->flags = 0;
867 }
868
869 void *mutt_hcache_open (const char *path, const char *folder)
870 {
871   struct stat sb;
872   u_int32_t createflags = DB_CREATE;
873   int ret;
874   struct header_cache *h = calloc (1, sizeof (HEADER_CACHE));
875   int pagesize = atoi (HeaderCachePageSize);
876
877
878   h->crc = generate_crc32 ();
879
880   if (!path || path[0] == '\0') {
881     FREE (&h);
882     return NULL;
883   }
884
885   path = mutt_hcache_per_folder (path, folder);
886
887   snprintf (h->lockfile, _POSIX_PATH_MAX, "%s-lock-hack", path);
888
889   h->fd = open (h->lockfile, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
890   if (h->fd < 0) {
891     FREE (&h);
892     return NULL;
893   }
894
895   if (mx_lock_file (h->lockfile, h->fd, 1, 0, 5)) {
896     close (h->fd);
897     FREE (&h);
898     return NULL;
899   }
900
901   ret = db_env_create (&h->env, 0);
902   if (ret) {
903     mx_unlock_file (h->lockfile, h->fd, 0);
904     close (h->fd);
905     FREE (&h);
906     return NULL;
907   }
908
909   ret =
910     (h->env->open)(h->env, NULL, DB_INIT_MPOOL | DB_CREATE | DB_PRIVATE, 0600);
911   if (!ret) {
912     ret = db_create (&h->db, h->env, 0);
913     if (ret) {
914       h->env->close (h->env, 0);
915       mx_unlock_file (h->lockfile, h->fd, 0);
916       close (h->fd);
917       FREE (&h);
918       return NULL;
919     }
920   }
921
922   if (stat (path, &sb) != 0 && errno == ENOENT) {
923     createflags |= DB_EXCL;
924     h->db->set_pagesize (h->db, pagesize);
925   }
926
927   ret = (h->db->open)(h->db, NULL, path, folder, DB_BTREE, createflags, 0600);
928   if (ret) {
929     h->db->close (h->db, 0);
930     h->env->close (h->env, 0);
931     mx_unlock_file (h->lockfile, h->fd, 0);
932     close (h->fd);
933     FREE (&h);
934     return NULL;
935   }
936
937   return h;
938 }
939
940 void mutt_hcache_close (void *db)
941 {
942   struct header_cache *h = db;
943
944   if (!h) {
945     return;
946   }
947
948   h->db->close (h->db, 0);
949   h->env->close (h->env, 0);
950   mx_unlock_file (h->lockfile, h->fd, 0);
951   close (h->fd);
952   FREE (&h);
953 }
954
955 void *mutt_hcache_fetch (void *db, const char *filename,
956                          size_t (*keylen) (const char *fn))
957 {
958   DBT key;
959   DBT data;
960   struct header_cache *h = db;
961
962   if (!h) {
963     return NULL;
964   }
965
966   filename++;                   /* skip '/' */
967
968   mutt_hcache_dbt_init (&key, (void *) filename, keylen (filename));
969   mutt_hcache_dbt_empty_init (&data);
970   data.flags = DB_DBT_MALLOC;
971
972   h->db->get (h->db, NULL, &key, &data, 0);
973
974   if (!crc32_matches (data.data, h->crc)) {
975     FREE(&data.data);
976     return NULL;
977   }
978
979   return data.data;
980 }
981
982 int
983 mutt_hcache_store (void *db, const char *filename, HEADER * header,
984                    unsigned long uid_validity, size_t (*keylen) (const char *fn))
985 {
986   DBT key;
987   DBT data;
988   int ret;
989   struct header_cache *h = db;
990
991   if (!h) {
992     return -1;
993   }
994
995   filename++;                   /* skip '/' */
996
997   mutt_hcache_dbt_init (&key, (void *) filename, keylen (filename));
998
999   mutt_hcache_dbt_empty_init (&data);
1000   data.flags = DB_DBT_USERMEM;
1001   data.data =
1002     mutt_hcache_dump (db, header, (signed int *) &data.size, uid_validity);
1003   data.ulen = data.size;
1004
1005   ret = h->db->put (h->db, NULL, &key, &data, 0);
1006
1007   FREE (&data.data);
1008
1009   return ret;
1010 }
1011
1012 int
1013 mutt_hcache_delete (void *db, const char *filename,
1014                     size_t (*keylen) (const char *fn))
1015 {
1016   DBT key;
1017   struct header_cache *h = db;
1018
1019   if (!h) {
1020     return -1;
1021   }
1022
1023   filename++;                   /* skip '/' */
1024
1025   mutt_hcache_dbt_init (&key, (void *) filename, keylen (filename));
1026   return h->db->del (h->db, NULL, &key, 0);
1027 }
1028 #endif
1029
1030 #endif /* USE_HCACHE */