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