cef8fc16e6dc0f38e5f396f8c6637ecf73fb6991
[apps/madmutt.git] / hcache.c
1 /*
2  * Copyright (C) 2004 Thomas Glanzmann <sithglan@stud.uni-erlangen.de>
3  * Copyright (C) 2004 Tobias Werth <sitowert@stud.uni-erlangen.de>
4  * Copyright (C) 2004 Brian Fundakowski Feldman <green@FreeBSD.org>
5  *
6  *     This program is free software; you can redistribute it and/or modify
7  *     it under the terms of the GNU General Public License as published by
8  *     the Free Software Foundation; either version 2 of the License, or
9  *     (at your option) any later version.
10  *
11  *     This program is distributed in the hope that it will be useful,
12  *     but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *     GNU General Public License for more details.
15  *
16  *     You should have received a copy of the GNU General Public License
17  *     along with this program; if not, write to the Free Software
18  *     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
19  */
20
21 # if HAVE_INTTYPES_H
22 #  include <inttypes.h>
23 # else
24 #  if HAVE_STDINT_H
25 #   include <stdint.h>
26 #  endif
27 # endif
28
29 #if HAVE_CONFIG_H
30 #include "config.h"
31 #endif /* HAVE_CONFIG_H */
32
33 #ifdef USE_HCACHE
34
35 #if HAVE_GDBM
36 #include <gdbm.h>
37 #elif HAVE_DB4
38 #include <db.h>
39 #endif
40
41 #include <errno.h>
42 #include <fcntl.h>
43 #if HAVE_SYS_TIME_H
44 #include <sys/time.h>
45 #endif
46 #include "mutt.h"
47 #ifdef USE_IMAP
48 #include "message.h"
49 #endif
50 #include "mime.h"
51 #include "mx.h"
52 #include "lib.h"
53 #include "md5.h"
54
55 #if HAVE_GDBM
56 static struct
57   header_cache {
58   GDBM_FILE db;
59   char *folder;
60   unsigned int crc;
61 } HEADER_CACHE;
62 #elif HAVE_DB4
63 static struct
64   header_cache {
65   DB_ENV *env;
66   DB *db;
67   unsigned int crc;
68   int fd;
69   char lockfile[_POSIX_PATH_MAX];
70 } HEADER_CACHE;
71 #endif
72
73 typedef union {
74   struct timeval timeval;
75   uint64_t uid_validity;
76 } validate;
77
78 static void *lazy_malloc (size_t siz)
79 {
80   if (0 < siz && siz < 4096) {
81     siz = 4096;
82   }
83
84   return safe_malloc (siz);
85 }
86
87 static void lazy_realloc (void *ptr, size_t siz)
88 {
89   void **p = (void **) ptr;
90
91   if (p != NULL && 0 < siz && siz < 4096) {
92     return;
93   }
94
95   safe_realloc (ptr, siz);
96 }
97
98 static unsigned char *dump_int (unsigned int i, unsigned char *d, int *off)
99 {
100   lazy_realloc (&d, *off + sizeof (int));
101   memcpy (d + *off, &i, sizeof (int));
102   (*off) += sizeof (int);
103
104   return d;
105 }
106
107 static void restore_int (unsigned int *i, const unsigned char *d, int *off)
108 {
109   memcpy (i, d + *off, sizeof (int));
110   (*off) += sizeof (int);
111 }
112
113 static unsigned char *dump_char (char *c, unsigned char *d, int *off)
114 {
115   unsigned int size;
116
117   if (c == NULL) {
118     size = 0;
119     d = dump_int (size, d, off);
120     return d;
121   }
122
123   size = mutt_strlen (c) + 1;
124   d = dump_int (size, d, off);
125   lazy_realloc (&d, *off + size);
126   memcpy (d + *off, c, size);
127   *off += size;
128
129   return d;
130 }
131
132 #if 0
133 static unsigned char *dump_char_size (char *c, unsigned char *d, int *off,
134                                       ssize_t size)
135 {
136   if (c == NULL) {
137     size = 0;
138     d = dump_int (size, d, off);
139     return d;
140   }
141
142   d = dump_int (size, d, off);
143   lazy_realloc (&d, *off + size);
144   memcpy (d + *off, c, size);
145   *off += size;
146
147   return d;
148 }
149 #endif
150
151 static void restore_char (char **c, const unsigned char *d, int *off)
152 {
153   unsigned int size;
154
155   restore_int (&size, d, off);
156
157   if (size == 0) {
158     *c = NULL;
159     return;
160   }
161
162   *c = safe_malloc (size);
163   memcpy (*c, d + *off, size);
164   *off += size;
165 }
166
167 static unsigned char *dump_address (ADDRESS * a, unsigned char *d, int *off)
168 {
169   unsigned int counter = 0;
170   unsigned int start_off = *off;
171
172   d = dump_int (0xdeadbeef, d, off);
173
174   while (a) {
175 #ifdef EXACT_ADDRESS
176     d = dump_char (a->val, d, off);
177 #endif
178     d = dump_char (a->personal, d, off);
179     d = dump_char (a->mailbox, d, off);
180     d = dump_int (a->group, d, off);
181     a = a->next;
182     counter++;
183   }
184
185   memcpy (d + start_off, &counter, sizeof (int));
186
187   return d;
188 }
189
190 static void restore_address (ADDRESS ** a, const unsigned char *d, int *off)
191 {
192   unsigned int counter;
193
194   restore_int (&counter, d, off);
195
196   while (counter) {
197     *a = safe_malloc (sizeof (ADDRESS));
198 #ifdef EXACT_ADDRESS
199     restore_char (&(*a)->val, d, off);
200 #endif
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 = safe_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 = safe_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 = safe_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   d = dump_list (e->references, d, off);
381   d = dump_list (e->in_reply_to, d, off);
382   d = dump_list (e->userhdrs, d, off);
383
384   return d;
385 }
386
387 static void restore_envelope (ENVELOPE * e, const unsigned char *d, int *off)
388 {
389   int real_subj_off;
390
391   restore_address (&e->return_path, d, off);
392   restore_address (&e->from, d, off);
393   restore_address (&e->to, d, off);
394   restore_address (&e->cc, d, off);
395   restore_address (&e->bcc, d, off);
396   restore_address (&e->sender, d, off);
397   restore_address (&e->reply_to, d, off);
398   restore_address (&e->mail_followup_to, d, off);
399
400   restore_char (&e->subject, d, off);
401   restore_int ((unsigned int *) (&real_subj_off), d, off);
402   if (0 <= real_subj_off) {
403     e->real_subj = e->subject + real_subj_off;
404   }
405   else {
406     e->real_subj = NULL;
407   }
408   restore_char (&e->message_id, d, off);
409   restore_char (&e->supersedes, d, off);
410   restore_char (&e->date, d, off);
411   restore_char (&e->x_label, d, off);
412
413   restore_list (&e->references, d, off);
414   restore_list (&e->in_reply_to, d, off);
415   restore_list (&e->userhdrs, d, off);
416 }
417
418 static
419 unsigned int crc32 (unsigned int crc, unsigned char const *p, size_t len)
420 {
421   int i;
422
423   while (len--) {
424     crc ^= *p++;
425     for (i = 0; i < 8; i++)
426       crc = (crc >> 1) ^ ((crc & 1) ? 0xedb88320 : 0);
427   }
428   return crc;
429 }
430
431 static int generate_crc32 ()
432 {
433   int crc = 0;
434
435   crc = crc32 (crc, (unsigned char const *)
436                "sithglan@stud.uni-erlangen.de[sithglan]|hcache.c|20041108231548|29613",
437                mutt_strlen
438                ("sithglan@stud.uni-erlangen.de[sithglan]|hcache.c|20041108231548|29613"));
439
440 #if HAVE_LANGINFO_CODESET
441   crc = crc32 (crc, (unsigned char const *) Charset, mutt_strlen (Charset));
442   crc =
443     crc32 (crc, (unsigned char const *) "HAVE_LANGINFO_CODESET",
444            mutt_strlen ("HAVE_LANGINFO_CODESET"));
445 #endif
446
447 #if EXACT_ADDRESS
448   crc =
449     crc32 (crc, (unsigned char const *) "EXACT_ADDRESS",
450            mutt_strlen ("EXACT_ADDRESS"));
451 #endif
452
453 #ifdef USE_POP
454   crc =
455     crc32 (crc, (unsigned char const *) "USE_POP", mutt_strlen ("USE_POP"));
456 #endif
457
458 #ifdef MIXMASTER
459   crc =
460     crc32 (crc, (unsigned char const *) "MIXMASTER",
461            mutt_strlen ("MIXMASTER"));
462 #endif
463
464 #ifdef USE_IMAP
465   crc =
466     crc32 (crc, (unsigned char const *) "USE_IMAP", mutt_strlen ("USE_IMAP"));
467 #endif
468   return crc;
469 }
470
471 static int crc32_matches (const char *d, unsigned int crc)
472 {
473   int off = sizeof (validate);
474   unsigned int mycrc = 0;
475
476   if (!d) {
477     return 0;
478   }
479
480   restore_int (&mycrc, (unsigned char *) d, &off);
481
482   return (crc == mycrc);
483 }
484
485 /* Append md5sumed folder to path if path is a directory. */
486 static const char *mutt_hcache_per_folder (const char *path,
487                                            const char *folder)
488 {
489   static char mutt_hcache_per_folder_path[_POSIX_PATH_MAX];
490   struct stat path_stat;
491   MD5_CTX md5;
492   unsigned char md5sum[16];
493   int ret;
494
495   ret = stat (path, &path_stat);
496   if (ret < 0) {
497     return path;
498   }
499
500   if (!S_ISDIR (path_stat.st_mode)) {
501     return path;
502   }
503
504   MD5Init (&md5);
505   MD5Update (&md5, (unsigned char *) folder, strlen (folder));
506   MD5Final (md5sum, &md5);
507
508   ret = snprintf (mutt_hcache_per_folder_path, _POSIX_PATH_MAX,
509                   "%s/%02x%02x%02x%02x%02x%02x%02x%02x"
510                   "%02x%02x%02x%02x%02x%02x%02x%02x",
511                   path, md5sum[0], md5sum[1], md5sum[2], md5sum[3],
512                   md5sum[4], md5sum[5], md5sum[6], md5sum[7], md5sum[8],
513                   md5sum[9], md5sum[10], md5sum[11], md5sum[12],
514                   md5sum[13], md5sum[14], md5sum[15]);
515
516   if (ret <= 0) {
517     return path;
518   }
519
520   return mutt_hcache_per_folder_path;
521 }
522
523 /* This function transforms a header into a char so that it is useable by
524  * db_store */
525 static void *mutt_hcache_dump (void *_db, HEADER * h, int *off,
526                                uint64_t uid_validity)
527 {
528   struct header_cache *db = _db;
529   unsigned char *d = NULL;
530
531   *off = 0;
532
533   d = lazy_malloc (sizeof (validate));
534
535   if (uid_validity) {
536     memcpy (d, &uid_validity, sizeof (uint64_t));
537   }
538   else {
539     struct timeval now;
540
541     gettimeofday (&now, NULL);
542     memcpy (d, &now, sizeof (struct timeval));
543   }
544   *off += sizeof (validate);
545
546   d = dump_int (db->crc, d, off);
547
548   lazy_realloc (&d, *off + sizeof (HEADER));
549   memcpy (d + *off, h, sizeof (HEADER));
550   *off += sizeof (HEADER);
551
552   d = dump_envelope (h->env, d, off);
553   d = dump_body (h->content, d, off);
554   d = dump_char (h->maildir_flags, d, off);
555
556   return d;
557 }
558
559 HEADER *mutt_hcache_restore (const unsigned char *d, HEADER ** oh)
560 {
561   int off = 0;
562   HEADER *h = mutt_new_header ();
563
564   /* skip validate */
565   off += sizeof (validate);
566
567   /* skip crc */
568   off += sizeof (unsigned int);
569
570   memcpy (h, d + off, sizeof (HEADER));
571   off += sizeof (HEADER);
572
573   h->env = mutt_new_envelope ();
574   restore_envelope (h->env, d, &off);
575
576   h->content = mutt_new_body ();
577   restore_body (h->content, d, &off);
578
579   restore_char (&h->maildir_flags, d, &off);
580
581   /* this is needed for maildir style mailboxes */
582   if (oh) {
583     h->old = (*oh)->old;
584     h->path = safe_strdup ((*oh)->path);
585     mutt_free_header (oh);
586   }
587
588   return h;
589 }
590
591 #if HAVE_GDBM
592
593 void *mutt_hcache_open (const char *path, const char *folder)
594 {
595   struct header_cache *h = safe_calloc (1, sizeof (HEADER_CACHE));
596   int pagesize =
597     atoi (HeaderCachePageSize) ? atoi (HeaderCachePageSize) : 16384;
598   h->db = NULL;
599   h->folder = safe_strdup (folder);
600   h->crc = generate_crc32 ();
601
602   if (!path || path[0] == '\0') {
603     FREE (&h->folder);
604     FREE (&h);
605     return NULL;
606   }
607
608   path = mutt_hcache_per_folder (path, folder);
609
610   h->db = gdbm_open ((char *) path, pagesize, GDBM_WRCREAT, 00600, NULL);
611   if (h->db) {
612     return h;
613   }
614
615   /* if rw failed try ro */
616   h->db = gdbm_open ((char *) path, pagesize, GDBM_READER, 00600, NULL);
617   if (h->db) {
618     return h;
619   }
620   else {
621     FREE (&h->folder);
622     FREE (&h);
623
624     return NULL;
625   }
626 }
627
628 void mutt_hcache_close (void *db)
629 {
630   struct header_cache *h = db;
631
632   if (!h) {
633     return;
634   }
635
636   gdbm_close (h->db);
637   FREE (&h->folder);
638   FREE (&h);
639 }
640
641 void *mutt_hcache_fetch (void *db, const char *filename,
642                          size_t (*keylen) (const char *fn))
643 {
644   struct header_cache *h = db;
645   datum key;
646   datum data;
647   char path[_POSIX_PATH_MAX];
648
649   if (!h) {
650     return NULL;
651   }
652
653   strncpy (path, h->folder, sizeof (path));
654   strncat (path, filename, sizeof (path) - mutt_strlen (path));
655
656   key.dptr = path;
657   key.dsize = keylen (path);
658
659   data = gdbm_fetch (h->db, key);
660
661   if (!crc32_matches (data.dptr, h->crc)) {
662     free (data.dptr);
663     return NULL;
664   }
665
666   return data.dptr;
667 }
668
669 int
670 mutt_hcache_store (void *db, const char *filename, HEADER * header,
671                    uint64_t uid_validity, size_t (*keylen) (const char *fn))
672 {
673   struct header_cache *h = db;
674   datum key;
675   datum data;
676   char path[_POSIX_PATH_MAX];
677   int ret;
678
679   if (!h) {
680     return -1;
681   }
682
683   strncpy (path, h->folder, sizeof (path));
684   strncat (path, filename, sizeof (path) - mutt_strlen (path));
685
686   key.dptr = path;
687   key.dsize = keylen (path);
688
689   data.dptr = mutt_hcache_dump (db, header, &data.dsize, uid_validity);
690
691   ret = gdbm_store (h->db, key, data, GDBM_REPLACE);
692
693   FREE (&data.dptr);
694
695   return ret;
696 }
697
698 int
699 mutt_hcache_delete (void *db, const char *filename,
700                     size_t (*keylen) (const char *fn))
701 {
702   datum key;
703   struct header_cache *h = db;
704   char path[_POSIX_PATH_MAX];
705
706   if (!h) {
707     return -1;
708   }
709
710   strncpy (path, h->folder, sizeof (path));
711   strncat (path, filename, sizeof (path) - mutt_strlen (path));
712
713   key.dptr = path;
714   key.dsize = keylen (path);
715
716   return gdbm_delete (h->db, key);
717 }
718 #elif HAVE_DB4
719
720 static void mutt_hcache_dbt_init (DBT * dbt, void *data, size_t len)
721 {
722   dbt->data = data;
723   dbt->size = dbt->ulen = len;
724   dbt->dlen = dbt->doff = 0;
725   dbt->flags = DB_DBT_USERMEM;
726 }
727
728 static void mutt_hcache_dbt_empty_init (DBT * dbt)
729 {
730   dbt->data = NULL;
731   dbt->size = dbt->ulen = dbt->dlen = dbt->doff = 0;
732   dbt->flags = 0;
733 }
734
735 void *mutt_hcache_open (const char *path, const char *folder)
736 {
737   struct stat sb;
738   u_int32_t createflags = DB_CREATE;
739   int ret;
740   struct header_cache *h = calloc (1, sizeof (HEADER_CACHE));
741   int pagesize = atoi (HeaderCachePageSize);
742
743
744   h->crc = generate_crc32 ();
745
746   if (!path || path[0] == '\0') {
747     FREE (&h);
748     return NULL;
749   }
750
751   path = mutt_hcache_per_folder (path, folder);
752
753   snprintf (h->lockfile, _POSIX_PATH_MAX, "%s-lock-hack", path);
754
755   h->fd = open (h->lockfile, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
756   if (h->fd < 0) {
757     FREE (&h);
758     return NULL;
759   }
760
761   if (mx_lock_file (h->lockfile, h->fd, 1, 0, 5)) {
762     close (h->fd);
763     FREE (&h);
764     return NULL;
765   }
766
767   ret = db_env_create (&h->env, 0);
768   if (ret) {
769     mx_unlock_file (h->lockfile, h->fd, 0);
770     close (h->fd);
771     FREE (&h);
772     return NULL;
773   }
774
775   ret =
776     h->env->open (h->env, NULL, DB_INIT_MPOOL | DB_CREATE | DB_PRIVATE, 0600);
777   if (!ret) {
778     ret = db_create (&h->db, h->env, 0);
779     if (ret) {
780       h->env->close (h->env, 0);
781       mx_unlock_file (h->lockfile, h->fd, 0);
782       close (h->fd);
783       FREE (&h);
784       return NULL;
785     }
786   }
787
788   if (stat (path, &sb) != 0 && errno == ENOENT) {
789     createflags |= DB_EXCL;
790     h->db->set_pagesize (h->db, pagesize);
791   }
792
793   ret = h->db->open (h->db, NULL, path, folder, DB_BTREE, createflags, 0600);
794   if (ret) {
795     h->db->close (h->db, 0);
796     h->env->close (h->env, 0);
797     mx_unlock_file (h->lockfile, h->fd, 0);
798     close (h->fd);
799     FREE (&h);
800     return NULL;
801   }
802
803   return h;
804 }
805
806 void mutt_hcache_close (void *db)
807 {
808   struct header_cache *h = db;
809   int ret;
810
811   if (!h) {
812     return;
813   }
814
815   h->db->close (h->db, 0);
816   h->env->close (h->env, 0);
817   mx_unlock_file (h->lockfile, h->fd, 0);
818   close (h->fd);
819   FREE (&h);
820 }
821
822 void *mutt_hcache_fetch (void *db, const char *filename,
823                          size_t (*keylen) (const char *fn))
824 {
825   DBT key;
826   DBT data;
827   struct header_cache *h = db;
828
829   if (!h) {
830     return NULL;
831   }
832
833   filename++;                   /* skip '/' */
834
835   mutt_hcache_dbt_init (&key, (void *) filename, keylen (filename));
836   mutt_hcache_dbt_empty_init (&data);
837   data.flags = DB_DBT_MALLOC;
838
839   h->db->get (h->db, NULL, &key, &data, 0);
840
841   if (!crc32_matches (data.data, h->crc)) {
842     free (data.data);
843     return NULL;
844   }
845
846   return data.data;
847 }
848
849 int
850 mutt_hcache_store (void *db, const char *filename, HEADER * header,
851                    uint64_t uid_validity, size_t (*keylen) (const char *fn))
852 {
853   DBT key;
854   DBT data;
855   int ret;
856   struct header_cache *h = db;
857
858   if (!h) {
859     return -1;
860   }
861
862   filename++;                   /* skip '/' */
863
864   mutt_hcache_dbt_init (&key, (void *) filename, keylen (filename));
865
866   mutt_hcache_dbt_empty_init (&data);
867   data.flags = DB_DBT_USERMEM;
868   data.data =
869     mutt_hcache_dump (db, header, (signed int *) &data.size, uid_validity);
870   data.ulen = data.size;
871
872   ret = h->db->put (h->db, NULL, &key, &data, 0);
873
874   FREE (&data.data);
875
876   return ret;
877 }
878
879 int
880 mutt_hcache_delete (void *db, const char *filename,
881                     size_t (*keylen) (const char *fn))
882 {
883   DBT key;
884   struct header_cache *h = db;
885
886   if (!h) {
887     return -1;
888   }
889
890   filename++;                   /* skip '/' */
891
892   mutt_hcache_dbt_init (&key, (void *) filename, keylen (filename));
893   return h->db->del (h->db, NULL, &key, 0);
894 }
895 #endif
896
897 #endif /* USE_HCACHE */