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