always build POP support, we do a /mail/ client, right ?
[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        "0x004"
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
43 #include <lib-lib/mem.h>
44 #include <lib-hash/hash.h>
45
46 #include <lib-mime/mime.h>
47
48 #include "mutt.h"
49 #ifdef USE_IMAP
50 #include "message.h"
51 #endif
52 #include "mx.h"
53 #include "lib.h"
54
55 #include "lib/debug.h"
56
57 #if HAVE_QDBM
58 static struct
59   header_cache {
60   VILLA *db;
61   char *folder;
62   unsigned int crc;
63 } HEADER_CACHE;
64 #elif HAVE_GDBM
65 static struct
66   header_cache {
67   GDBM_FILE db;
68   char *folder;
69   unsigned int crc;
70 } HEADER_CACHE;
71 #elif HAVE_DB4
72 static struct
73   header_cache {
74   DB_ENV *env;
75   DB *db;
76   unsigned int crc;
77   int fd;
78   char lockfile[_POSIX_PATH_MAX];
79 } HEADER_CACHE;
80 #endif
81
82 typedef union {
83   struct timeval timeval;
84   unsigned long uid_validity;
85 } validate;
86
87 static void *lazy_malloc (size_t siz)
88 {
89   if (0 < siz && siz < 4096) {
90     siz = 4096;
91   }
92
93   return xmalloc(siz);
94 }
95
96 static void lazy_realloc (void *ptr, size_t siz)
97 {
98   void **p = (void **) ptr;
99
100   if (p != NULL && 0 < siz && siz < 4096) {
101     return;
102   }
103
104   p_realloc(ptr, siz);
105 }
106
107 static unsigned char *dump_int (unsigned int i, unsigned char *d, int *off)
108 {
109   lazy_realloc (&d, *off + sizeof (int));
110   memcpy (d + *off, &i, sizeof (int));
111   (*off) += sizeof (int);
112
113   return d;
114 }
115
116 static void restore_int (unsigned int *i, const unsigned char *d, int *off)
117 {
118   memcpy (i, d + *off, sizeof (int));
119   (*off) += sizeof (int);
120 }
121
122 static unsigned char *dump_char (char *c, unsigned char *d, int *off)
123 {
124   unsigned int size;
125
126   if (c == NULL) {
127     size = 0;
128     d = dump_int (size, d, off);
129     return d;
130   }
131
132   size = m_strlen(c) + 1;
133   d = dump_int (size, d, off);
134   lazy_realloc (&d, *off + size);
135   memcpy (d + *off, c, size);
136   *off += size;
137
138   return d;
139 }
140
141 #if 0
142 static unsigned char *dump_char_size (char *c, unsigned char *d, int *off,
143                                       ssize_t size)
144 {
145   if (c == NULL) {
146     size = 0;
147     d = dump_int (size, d, off);
148     return d;
149   }
150
151   d = dump_int (size, d, off);
152   lazy_realloc (&d, *off + size);
153   memcpy (d + *off, c, size);
154   *off += size;
155
156   return d;
157 }
158 #endif
159
160 static void restore_char (char **c, const unsigned char *d, int *off)
161 {
162   unsigned int size;
163
164   restore_int (&size, d, off);
165
166   if (size == 0) {
167     *c = NULL;
168     return;
169   }
170
171   *c = p_dup(d + *off, size);
172   *off += size;
173 }
174
175 static unsigned char *dump_address (address_t * a, unsigned char *d, int *off)
176 {
177   unsigned int counter = 0;
178   unsigned int start_off = *off;
179
180   d = dump_int (0xdeadbeef, d, off);
181
182   while (a) {
183     d = dump_char (a->personal, d, off);
184     d = dump_char (a->mailbox, d, off);
185     d = dump_int (a->group, d, off);
186     a = a->next;
187     counter++;
188   }
189
190   memcpy (d + start_off, &counter, sizeof (int));
191
192   return d;
193 }
194
195 static void restore_address (address_t ** a, const unsigned char *d, int *off)
196 {
197   unsigned int counter;
198
199   restore_int (&counter, d, off);
200
201   while (counter) {
202     *a = p_new(address_t, 1);
203     restore_char (&(*a)->personal, d, off);
204     restore_char (&(*a)->mailbox, d, off);
205     restore_int ((unsigned int *) &(*a)->group, d, off);
206     a = &(*a)->next;
207     counter--;
208   }
209
210   *a = NULL;
211 }
212
213 static unsigned char *dump_list (LIST * l, unsigned char *d, int *off)
214 {
215   unsigned int counter = 0;
216   unsigned int start_off = *off;
217
218   d = dump_int (0xdeadbeef, d, off);
219
220   while (l) {
221     d = dump_char (l->data, d, off);
222     l = l->next;
223     counter++;
224   }
225
226   memcpy (d + start_off, &counter, sizeof (int));
227
228   return d;
229 }
230
231 static void 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 = p_new(LIST, 1);
239     restore_char (&(*l)->data, d, off);
240     l = &(*l)->next;
241     counter--;
242   }
243
244   *l = NULL;
245 }
246
247 #if 0
248 static unsigned char *dump_buffer (BUFFER * b, unsigned char *d, int *off)
249 {
250   if (!b) {
251     d = dump_int (0, d, off);
252     return d;
253   }
254   else {
255     d = dump_int (1, d, off);
256   }
257
258   d = dump_char_size (b->data, d, off, b->dsize + 1);
259   d = dump_int (b->dptr - b->data, d, off);
260   d = dump_int (b->dsize, d, off);
261   d = dump_int (b->destroy, d, off);
262
263   return d;
264 }
265
266 static void restore_buffer (BUFFER ** b, const unsigned char *d, int *off)
267 {
268   unsigned int used;
269   unsigned int offset;
270
271   restore_int (&used, d, off);
272   if (!used) {
273     return;
274   }
275
276   *b = p_new(BUFFER, 1);
277
278   restore_char (&(*b)->data, d, off);
279   restore_int (&offset, d, off);
280   (*b)->dptr = (*b)->data + offset;
281   restore_int (&(*b)->dsize, d, off);
282   restore_int ((unsigned int *) &(*b)->destroy, d, off);
283 }
284 #endif
285
286 static unsigned char *dump_parameter (PARAMETER * p, unsigned char *d,
287                                       int *off)
288 {
289   unsigned int counter = 0;
290   unsigned int start_off = *off;
291
292   d = dump_int (0xdeadbeef, d, off);
293
294   while (p) {
295     d = dump_char (p->attribute, d, off);
296     d = dump_char (p->value, d, off);
297     p = p->next;
298     counter++;
299   }
300
301   memcpy (d + start_off, &counter, sizeof (int));
302
303   return d;
304 }
305
306 static void
307 restore_parameter (PARAMETER ** p, const unsigned char *d, int *off)
308 {
309   unsigned int counter;
310
311   restore_int (&counter, d, off);
312
313   while (counter) {
314     *p = p_new(PARAMETER, 1);
315     restore_char (&(*p)->attribute, d, off);
316     restore_char (&(*p)->value, d, off);
317     p = &(*p)->next;
318     counter--;
319   }
320
321   *p = NULL;
322 }
323
324 static unsigned char *dump_body (BODY * c, unsigned char *d, int *off)
325 {
326   lazy_realloc (&d, *off + sizeof (BODY));
327   memcpy (d + *off, c, sizeof (BODY));
328   *off += sizeof (BODY);
329
330   d = dump_char (c->xtype, d, off);
331   d = dump_char (c->subtype, d, off);
332
333   d = dump_parameter (c->parameter, d, off);
334
335   d = dump_char (c->description, d, off);
336   d = dump_char (c->form_name, d, off);
337   d = dump_char (c->filename, d, off);
338   d = dump_char (c->d_filename, d, off);
339
340   return d;
341 }
342
343 static void 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 *dump_envelope (ENVELOPE * e, unsigned char *d, int *off)
360 {
361   d = dump_address (e->return_path, d, off);
362   d = dump_address (e->from, d, off);
363   d = dump_address (e->to, d, off);
364   d = dump_address (e->cc, d, off);
365   d = dump_address (e->bcc, d, off);
366   d = dump_address (e->sender, d, off);
367   d = dump_address (e->reply_to, d, off);
368   d = dump_address (e->mail_followup_to, d, off);
369
370   d = dump_char (e->subject, d, off);
371   if (e->real_subj) {
372     d = dump_int (e->real_subj - e->subject, d, off);
373   }
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   d = dump_char (e->list_post, d, off);
382
383 #ifdef USE_NNTP
384   d = dump_char (e->newsgroups, d, off);
385   d = dump_char (e->xref, d, off);
386   d = dump_char (e->followup_to, d, off);
387   d = dump_char (e->x_comment_to, d, off);
388 #endif
389
390   d = dump_list (e->references, d, off);
391   d = dump_list (e->in_reply_to, d, off);
392   d = dump_list (e->userhdrs, d, off);
393
394   return d;
395 }
396
397 static void restore_envelope (ENVELOPE * e, const unsigned char *d, int *off)
398 {
399   int real_subj_off;
400
401   restore_address (&e->return_path, d, off);
402   restore_address (&e->from, d, off);
403   restore_address (&e->to, d, off);
404   restore_address (&e->cc, d, off);
405   restore_address (&e->bcc, d, off);
406   restore_address (&e->sender, d, off);
407   restore_address (&e->reply_to, d, off);
408   restore_address (&e->mail_followup_to, d, off);
409
410   restore_char (&e->subject, d, off);
411   restore_int ((unsigned int *) (&real_subj_off), d, off);
412   if (0 <= real_subj_off) {
413     e->real_subj = e->subject + real_subj_off;
414   }
415   else {
416     e->real_subj = NULL;
417   }
418   restore_char (&e->message_id, d, off);
419   restore_char (&e->supersedes, d, off);
420   restore_char (&e->date, d, off);
421   restore_char (&e->x_label, d, off);
422   restore_char (&e->list_post, d, off);
423
424 #ifdef USE_NNTP
425   restore_char (&e->newsgroups, d, off);
426   restore_char (&e->xref, d, off);
427   restore_char (&e->followup_to, d, off);
428   restore_char (&e->x_comment_to, d, off);
429 #endif
430
431   restore_list (&e->references, d, off);
432   restore_list (&e->in_reply_to, d, off);
433   restore_list (&e->userhdrs, d, off);
434 }
435
436 static
437 unsigned int crc32 (unsigned int crc, unsigned char const *p, size_t len)
438 {
439   int i;
440
441   while (len--) {
442     crc ^= *p++;
443     for (i = 0; i < 8; i++)
444       crc = (crc >> 1) ^ ((crc & 1) ? 0xedb88320 : 0);
445   }
446   return crc;
447 }
448
449 static int generate_crc32 ()
450 {
451   int crc = 0;
452
453   crc = crc32 (crc, (unsigned char const *)
454                MUTTNG_HCACHE_ID "sithglan@stud.uni-erlangen.de[sithglan]|hcache.c|20041108231548|29613",
455                m_strlen
456                (MUTTNG_HCACHE_ID "sithglan@stud.uni-erlangen.de[sithglan]|hcache.c|20041108231548|29613"));
457
458 #if HAVE_LANGINFO_CODESET
459   crc = crc32 (crc, (unsigned char const *) Charset, m_strlen(Charset));
460   crc = crc32 (crc, (unsigned char const *) "HAVE_LANGINFO_CODESET",
461                m_strlen("HAVE_LANGINFO_CODESET"));
462 #endif
463
464   crc = crc32(crc, (unsigned char const *) "USE_POP", m_strlen("USE_POP"));
465
466 #ifdef MIXMASTER
467   crc =
468     crc32 (crc, (unsigned char const *) "MIXMASTER",
469            m_strlen("MIXMASTER"));
470 #endif
471
472 #ifdef USE_IMAP
473   crc =
474     crc32 (crc, (unsigned char const *) "USE_IMAP", m_strlen("USE_IMAP"));
475 #endif
476
477 #ifdef USE_NNTP
478   crc =
479     crc32 (crc, (unsigned char const *) "USE_NNTP", m_strlen("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, m_strlen(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 = m_strdup((*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 = p_new(HEADER_CACHE, 1);
609   int    flags = VL_OWRITER | VL_OCREAT;
610   h->db = NULL;
611   h->folder = m_strdup(folder);
612   h->crc = generate_crc32();
613
614   if (!path || path[0] == '\0')
615   {
616     p_delete(&h->folder);
617     p_delete(&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     p_delete(&h->folder);
632     p_delete(&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   p_delete(&h->folder);
648   p_delete(&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, len;
658   char *data = NULL;
659
660   if (!h)
661     return NULL;
662
663   m_strcpy(path, sizeof(path), h->folder);
664   m_strcat(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     p_delete(&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   m_strcpy(path, sizeof(path), h->folder);
694   m_strcat(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   p_delete(&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   m_strcpy(path, sizeof(path), h->folder);
719   m_strcat(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 = p_new(HEADER_CACHE, 1);
731   int pagesize =
732     atoi (HeaderCachePageSize) ? atoi (HeaderCachePageSize) : 16384;
733   h->db = NULL;
734   h->folder = m_strdup(folder);
735   h->crc = generate_crc32 ();
736
737   if (!path || path[0] == '\0') {
738     p_delete(&h->folder);
739     p_delete(&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     p_delete(&h->folder);
757     p_delete(&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   p_delete(&h->folder);
773   p_delete(&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   m_strcpy(path, sizeof(path), h->folder);
789   strncat (path, filename, sizeof (path) - m_strlen(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     p_delete(&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   m_strcpy(path, sizeof(path), h->folder);
819   strncat (path, filename, sizeof (path) - m_strlen(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   p_delete(&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   m_strcpy(path, sizeof(path), h->folder);
846   strncat (path, filename, sizeof (path) - m_strlen(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     p_delete(&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     p_delete(&h);
893     return NULL;
894   }
895
896   if (mx_lock_file (h->lockfile, h->fd, 1, 0, 5)) {
897     close (h->fd);
898     p_delete(&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     p_delete(&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       p_delete(&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     p_delete(&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   p_delete(&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     p_delete(&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   p_delete(&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 */