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