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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 */
20
21 #if HAVE_CONFIG_H
22 #include "config.h"
23 #endif /* HAVE_CONFIG_H */
24
25 #if HAVE_QDBM
26 #include <depot.h>
27 #include <cabin.h>
28 #include <villa.h>
29 #elif HAVE_TC
30 #include <tcbdb.h>
31 #elif HAVE_GDBM
32 #include <gdbm.h>
33 #elif HAVE_DB4
34 #include <db.h>
35 #endif
36
37 #include <errno.h>
38 #include <fcntl.h>
39 #if HAVE_SYS_TIME_H
40 #include <sys/time.h>
41 #endif
42 #include "mutt.h"
43 #include "hcache.h"
44 #include "hcversion.h"
45 #include "mx.h"
46 #include "lib.h"
47 #include "md5.h"
48 #include "rfc822.h"
49
50 #if HAVE_QDBM
51 static struct header_cache
52 {
53 VILLA *db;
54 char *folder;
55 unsigned int crc;
56 } HEADER_CACHE;
57 #elif HAVE_TC
58 static struct header_cache
59 {
60 TCBDB *db;
61 char *folder;
62 unsigned int crc;
63 } HEADER_CACHE;
64 #elif HAVE_GDBM
65 static struct header_cache
66 {
67 GDBM_FILE db;
68 char *folder;
69 unsigned int crc;
70 } HEADER_CACHE;
71 #elif HAVE_DB4
72 static struct header_cache
73 {
74 DB_ENV *env;
75 DB *db;
76 char *folder;
77 unsigned int crc;
78 int fd;
79 char lockfile[_POSIX_PATH_MAX];
80 } HEADER_CACHE;
81
82 static void mutt_hcache_dbt_init(DBT * dbt, void *data, size_t len);
83 static void mutt_hcache_dbt_empty_init(DBT * dbt);
84 #endif
85
86 typedef union
87 {
88 struct timeval timeval;
89 unsigned int uidvalidity;
90 } validate;
91
92 static void *
lazy_malloc(size_t siz)93 lazy_malloc(size_t siz)
94 {
95 if (0 < siz && siz < 4096)
96 siz = 4096;
97
98 return safe_malloc(siz);
99 }
100
101 static void
lazy_realloc(void * ptr,size_t siz)102 lazy_realloc(void *ptr, size_t siz)
103 {
104 void **p = (void **) ptr;
105
106 if (p != NULL && 0 < siz && siz < 4096)
107 return;
108
109 safe_realloc(ptr, siz);
110 }
111
112 static unsigned char *
dump_int(unsigned int i,unsigned char * d,int * off)113 dump_int(unsigned int i, unsigned char *d, int *off)
114 {
115 lazy_realloc(&d, *off + sizeof (int));
116 memcpy(d + *off, &i, sizeof (int));
117 (*off) += sizeof (int);
118
119 return d;
120 }
121
122 static void
restore_int(unsigned int * i,const unsigned char * d,int * off)123 restore_int(unsigned int *i, const unsigned char *d, int *off)
124 {
125 memcpy(i, d + *off, sizeof (int));
126 (*off) += sizeof (int);
127 }
128
is_ascii(const char * p,size_t len)129 static inline int is_ascii (const char *p, size_t len) {
130 register const char *s = p;
131 while (s && (unsigned) (s - p) < len) {
132 if ((*s & 0x80) != 0)
133 return 0;
134 s++;
135 }
136 return 1;
137 }
138
139 static unsigned char *
dump_char_size(char * c,unsigned char * d,int * off,ssize_t size,int convert)140 dump_char_size(char *c, unsigned char *d, int *off, ssize_t size, int convert)
141 {
142 char *p = c;
143
144 if (c == NULL)
145 {
146 size = 0;
147 d = dump_int(size, d, off);
148 return d;
149 }
150
151 if (convert && !is_ascii (c, size)) {
152 p = mutt_substrdup (c, c + size);
153 if (mutt_convert_string (&p, Charset, "utf-8", 0) == 0) {
154 c = p;
155 size = mutt_strlen (c) + 1;
156 }
157 }
158
159 d = dump_int(size, d, off);
160 lazy_realloc(&d, *off + size);
161 memcpy(d + *off, p, size);
162 *off += size;
163
164 if (p != c)
165 FREE(&p);
166
167 return d;
168 }
169
170 static unsigned char *
dump_char(char * c,unsigned char * d,int * off,int convert)171 dump_char(char *c, unsigned char *d, int *off, int convert)
172 {
173 return dump_char_size (c, d, off, mutt_strlen (c) + 1, convert);
174 }
175
176 static void
restore_char(char ** c,const unsigned char * d,int * off,int convert)177 restore_char(char **c, const unsigned char *d, int *off, int convert)
178 {
179 unsigned int size;
180 restore_int(&size, d, off);
181
182 if (size == 0)
183 {
184 *c = NULL;
185 return;
186 }
187
188 *c = safe_malloc(size);
189 memcpy(*c, d + *off, size);
190 if (convert && !is_ascii (*c, size)) {
191 char *tmp = safe_strdup (*c);
192 if (mutt_convert_string (&tmp, "utf-8", Charset, 0) == 0) {
193 mutt_str_replace (c, tmp);
194 } else {
195 FREE(&tmp);
196 }
197 }
198 *off += size;
199 }
200
201 static unsigned char *
dump_address(ADDRESS * a,unsigned char * d,int * off,int convert)202 dump_address(ADDRESS * a, unsigned char *d, int *off, int convert)
203 {
204 unsigned int counter = 0;
205 unsigned int start_off = *off;
206
207 d = dump_int(0xdeadbeef, d, off);
208
209 while (a)
210 {
211 #ifdef EXACT_ADDRESS
212 d = dump_char(a->val, d, off, convert);
213 #endif
214 d = dump_char(a->personal, d, off, convert);
215 d = dump_char(a->mailbox, d, off, 0);
216 d = dump_int(a->group, d, off);
217 a = a->next;
218 counter++;
219 }
220
221 memcpy(d + start_off, &counter, sizeof (int));
222
223 return d;
224 }
225
226 static void
restore_address(ADDRESS ** a,const unsigned char * d,int * off,int convert)227 restore_address(ADDRESS ** a, const unsigned char *d, int *off, int convert)
228 {
229 unsigned int counter;
230
231 restore_int(&counter, d, off);
232
233 while (counter)
234 {
235 *a = rfc822_new_address();
236 #ifdef EXACT_ADDRESS
237 restore_char(&(*a)->val, d, off, convert);
238 #endif
239 restore_char(&(*a)->personal, d, off, convert);
240 restore_char(&(*a)->mailbox, d, off, 0);
241 restore_int((unsigned int *) &(*a)->group, d, off);
242 a = &(*a)->next;
243 counter--;
244 }
245
246 *a = NULL;
247 }
248
249 static unsigned char *
dump_list(LIST * l,unsigned char * d,int * off,int convert)250 dump_list(LIST * l, unsigned char *d, int *off, int convert)
251 {
252 unsigned int counter = 0;
253 unsigned int start_off = *off;
254
255 d = dump_int(0xdeadbeef, d, off);
256
257 while (l)
258 {
259 d = dump_char(l->data, d, off, convert);
260 l = l->next;
261 counter++;
262 }
263
264 memcpy(d + start_off, &counter, sizeof (int));
265
266 return d;
267 }
268
269 static void
restore_list(LIST ** l,const unsigned char * d,int * off,int convert)270 restore_list(LIST ** l, const unsigned char *d, int *off, int convert)
271 {
272 unsigned int counter;
273
274 restore_int(&counter, d, off);
275
276 while (counter)
277 {
278 *l = safe_malloc(sizeof (LIST));
279 restore_char(&(*l)->data, d, off, convert);
280 l = &(*l)->next;
281 counter--;
282 }
283
284 *l = NULL;
285 }
286
287 static unsigned char *
dump_buffer(BUFFER * b,unsigned char * d,int * off,int convert)288 dump_buffer(BUFFER * b, unsigned char *d, int *off, int convert)
289 {
290 if (!b)
291 {
292 d = dump_int(0, d, off);
293 return d;
294 }
295 else
296 d = dump_int(1, d, off);
297
298 d = dump_char_size(b->data, d, off, b->dsize + 1, convert);
299 d = dump_int(b->dptr - b->data, d, off);
300 d = dump_int(b->dsize, d, off);
301 d = dump_int(b->destroy, d, off);
302
303 return d;
304 }
305
306 static void
restore_buffer(BUFFER ** b,const unsigned char * d,int * off,int convert)307 restore_buffer(BUFFER ** b, const unsigned char *d, int *off, int convert)
308 {
309 unsigned int used;
310 unsigned int offset;
311 restore_int(&used, d, off);
312 if (!used)
313 {
314 return;
315 }
316
317 *b = safe_malloc(sizeof (BUFFER));
318
319 restore_char(&(*b)->data, d, off, convert);
320 restore_int(&offset, d, off);
321 (*b)->dptr = (*b)->data + offset;
322 restore_int (&used, d, off);
323 (*b)->dsize = used;
324 restore_int (&used, d, off);
325 (*b)->destroy = used;
326 }
327
328 static unsigned char *
dump_parameter(PARAMETER * p,unsigned char * d,int * off,int convert)329 dump_parameter(PARAMETER * p, unsigned char *d, int *off, int convert)
330 {
331 unsigned int counter = 0;
332 unsigned int start_off = *off;
333
334 d = dump_int(0xdeadbeef, d, off);
335
336 while (p)
337 {
338 d = dump_char(p->attribute, d, off, 0);
339 d = dump_char(p->value, d, off, convert);
340 p = p->next;
341 counter++;
342 }
343
344 memcpy(d + start_off, &counter, sizeof (int));
345
346 return d;
347 }
348
349 static void
restore_parameter(PARAMETER ** p,const unsigned char * d,int * off,int convert)350 restore_parameter(PARAMETER ** p, const unsigned char *d, int *off, int convert)
351 {
352 unsigned int counter;
353
354 restore_int(&counter, d, off);
355
356 while (counter)
357 {
358 *p = safe_malloc(sizeof (PARAMETER));
359 restore_char(&(*p)->attribute, d, off, 0);
360 restore_char(&(*p)->value, d, off, convert);
361 p = &(*p)->next;
362 counter--;
363 }
364
365 *p = NULL;
366 }
367
368 static unsigned char *
dump_body(BODY * c,unsigned char * d,int * off,int convert)369 dump_body(BODY * c, unsigned char *d, int *off, int convert)
370 {
371 BODY nb;
372
373 memcpy (&nb, c, sizeof (BODY));
374
375 /* some fields are not safe to cache */
376 nb.content = NULL;
377 nb.charset = NULL;
378 nb.next = NULL;
379 nb.parts = NULL;
380 nb.hdr = NULL;
381 nb.aptr = NULL;
382
383 lazy_realloc(&d, *off + sizeof (BODY));
384 memcpy(d + *off, &nb, sizeof (BODY));
385 *off += sizeof (BODY);
386
387 d = dump_char(nb.xtype, d, off, 0);
388 d = dump_char(nb.subtype, d, off, 0);
389
390 d = dump_parameter(nb.parameter, d, off, convert);
391
392 d = dump_char(nb.description, d, off, convert);
393 d = dump_char(nb.form_name, d, off, convert);
394 d = dump_char(nb.filename, d, off, convert);
395 d = dump_char(nb.d_filename, d, off, convert);
396
397 return d;
398 }
399
400 static void
restore_body(BODY * c,const unsigned char * d,int * off,int convert)401 restore_body(BODY * c, const unsigned char *d, int *off, int convert)
402 {
403 memcpy(c, d + *off, sizeof (BODY));
404 *off += sizeof (BODY);
405
406 restore_char(&c->xtype, d, off, 0);
407 restore_char(&c->subtype, d, off, 0);
408
409 restore_parameter(&c->parameter, d, off, convert);
410
411 restore_char(&c->description, d, off, convert);
412 restore_char(&c->form_name, d, off, convert);
413 restore_char(&c->filename, d, off, convert);
414 restore_char(&c->d_filename, d, off, convert);
415 }
416
417 static unsigned char *
dump_envelope(ENVELOPE * e,unsigned char * d,int * off,int convert)418 dump_envelope(ENVELOPE * e, unsigned char *d, int *off, int convert)
419 {
420 d = dump_address(e->return_path, d, off, convert);
421 d = dump_address(e->from, d, off, convert);
422 d = dump_address(e->to, d, off, convert);
423 d = dump_address(e->cc, d, off, convert);
424 d = dump_address(e->bcc, d, off, convert);
425 d = dump_address(e->sender, d, off, convert);
426 d = dump_address(e->reply_to, d, off, convert);
427 d = dump_address(e->mail_followup_to, d, off, convert);
428
429 d = dump_char(e->list_post, d, off, convert);
430 d = dump_char(e->subject, d, off, convert);
431
432 if (e->real_subj)
433 d = dump_int(e->real_subj - e->subject, d, off);
434 else
435 d = dump_int(-1, d, off);
436
437 d = dump_char(e->message_id, d, off, 0);
438 d = dump_char(e->supersedes, d, off, 0);
439 d = dump_char(e->date, d, off, 0);
440 d = dump_char(e->x_label, d, off, convert);
441
442 d = dump_buffer(e->spam, d, off, convert);
443
444 d = dump_list(e->references, d, off, 0);
445 d = dump_list(e->in_reply_to, d, off, 0);
446 d = dump_list(e->userhdrs, d, off, convert);
447
448 return d;
449 }
450
451 static void
restore_envelope(ENVELOPE * e,const unsigned char * d,int * off,int convert)452 restore_envelope(ENVELOPE * e, const unsigned char *d, int *off, int convert)
453 {
454 int real_subj_off;
455
456 restore_address(&e->return_path, d, off, convert);
457 restore_address(&e->from, d, off, convert);
458 restore_address(&e->to, d, off, convert);
459 restore_address(&e->cc, d, off, convert);
460 restore_address(&e->bcc, d, off, convert);
461 restore_address(&e->sender, d, off, convert);
462 restore_address(&e->reply_to, d, off, convert);
463 restore_address(&e->mail_followup_to, d, off, convert);
464
465 restore_char(&e->list_post, d, off, convert);
466 restore_char(&e->subject, d, off, convert);
467 restore_int((unsigned int *) (&real_subj_off), d, off);
468
469 if (0 <= real_subj_off)
470 e->real_subj = e->subject + real_subj_off;
471 else
472 e->real_subj = NULL;
473
474 restore_char(&e->message_id, d, off, 0);
475 restore_char(&e->supersedes, d, off, 0);
476 restore_char(&e->date, d, off, 0);
477 restore_char(&e->x_label, d, off, convert);
478
479 restore_buffer(&e->spam, d, off, convert);
480
481 restore_list(&e->references, d, off, 0);
482 restore_list(&e->in_reply_to, d, off, 0);
483 restore_list(&e->userhdrs, d, off, convert);
484 }
485
486 static int
crc_matches(const char * d,unsigned int crc)487 crc_matches(const char *d, unsigned int crc)
488 {
489 int off = sizeof (validate);
490 unsigned int mycrc = 0;
491
492 if (!d)
493 return 0;
494
495 restore_int(&mycrc, (unsigned char *) d, &off);
496
497 return (crc == mycrc);
498 }
499
500 /* Append md5sumed folder to path if path is a directory. */
501 static const char *
mutt_hcache_per_folder(const char * path,const char * folder,hcache_namer_t namer)502 mutt_hcache_per_folder(const char *path, const char *folder,
503 hcache_namer_t namer)
504 {
505 static char hcpath[_POSIX_PATH_MAX];
506 struct stat sb;
507 unsigned char md5sum[16];
508 char* s;
509 int ret, plen;
510 #ifndef HAVE_ICONV
511 const char *chs = Charset && *Charset ? Charset :
512 mutt_get_default_charset ();
513 #endif
514
515 plen = mutt_strlen (path);
516
517 ret = stat(path, &sb);
518 if (ret < 0 && path[plen-1] != '/')
519 {
520 #ifdef HAVE_ICONV
521 return path;
522 #else
523 snprintf (hcpath, _POSIX_PATH_MAX, "%s-%s", path, chs);
524 return hcpath;
525 #endif
526 }
527
528 if (ret >= 0 && !S_ISDIR(sb.st_mode))
529 {
530 #ifdef HAVE_ICONV
531 return path;
532 #else
533 snprintf (hcpath, _POSIX_PATH_MAX, "%s-%s", path, chs);
534 return hcpath;
535 #endif
536 }
537
538 if (namer)
539 {
540 snprintf (hcpath, sizeof (hcpath), "%s%s", path,
541 path[plen-1] == '/' ? "" : "/");
542 if (path[plen-1] != '/')
543 plen++;
544
545 ret = namer (folder, hcpath + plen, sizeof (hcpath) - plen);
546 }
547 else
548 {
549 md5_buffer (folder, strlen (folder), &md5sum);
550
551 ret = snprintf(hcpath, _POSIX_PATH_MAX,
552 "%s/%02x%02x%02x%02x%02x%02x%02x%02x"
553 "%02x%02x%02x%02x%02x%02x%02x%02x"
554 #ifndef HAVE_ICONV
555 "-%s"
556 #endif
557 ,
558 path, md5sum[0], md5sum[1], md5sum[2], md5sum[3],
559 md5sum[4], md5sum[5], md5sum[6], md5sum[7], md5sum[8],
560 md5sum[9], md5sum[10], md5sum[11], md5sum[12],
561 md5sum[13], md5sum[14], md5sum[15]
562 #ifndef HAVE_ICONV
563 ,chs
564 #endif
565 );
566 }
567
568 if (ret <= 0)
569 return path;
570
571 if (stat (hcpath, &sb) >= 0)
572 return hcpath;
573
574 s = strchr (hcpath + 1, '/');
575 while (s)
576 {
577 /* create missing path components */
578 *s = '\0';
579 if (stat (hcpath, &sb) < 0 && (errno != ENOENT || mkdir (hcpath, 0777) < 0))
580 return path;
581 *s = '/';
582 s = strchr (s + 1, '/');
583 }
584
585 return hcpath;
586 }
587
588 /* This function transforms a header into a char so that it is useable by
589 * db_store */
590 static void *
mutt_hcache_dump(header_cache_t * h,HEADER * header,int * off,unsigned int uidvalidity)591 mutt_hcache_dump(header_cache_t *h, HEADER * header, int *off,
592 unsigned int uidvalidity)
593 {
594 unsigned char *d = NULL;
595 HEADER nh;
596 int convert = !Charset_is_utf8;
597
598 *off = 0;
599 d = lazy_malloc(sizeof (validate));
600
601 if (uidvalidity)
602 memcpy(d, &uidvalidity, sizeof (uidvalidity));
603 else
604 {
605 struct timeval now;
606 gettimeofday(&now, NULL);
607 memcpy(d, &now, sizeof (struct timeval));
608 }
609 *off += sizeof (validate);
610
611 d = dump_int(h->crc, d, off);
612
613 lazy_realloc(&d, *off + sizeof (HEADER));
614 memcpy(&nh, header, sizeof (HEADER));
615
616 /* some fields are not safe to cache */
617 nh.tagged = 0;
618 nh.changed = 0;
619 nh.threaded = 0;
620 nh.recip_valid = 0;
621 nh.searched = 0;
622 nh.matched = 0;
623 nh.collapsed = 0;
624 nh.limited = 0;
625 nh.num_hidden = 0;
626 nh.recipient = 0;
627 nh.pair = 0;
628 nh.attach_valid = 0;
629 nh.path = NULL;
630 nh.tree = NULL;
631 nh.thread = NULL;
632 #ifdef MIXMASTER
633 nh.chain = NULL;
634 #endif
635 #if defined USE_POP || defined USE_IMAP
636 nh.data = NULL;
637 #endif
638
639 memcpy(d + *off, &nh, sizeof (HEADER));
640 *off += sizeof (HEADER);
641
642 d = dump_envelope(nh.env, d, off, convert);
643 d = dump_body(nh.content, d, off, convert);
644 d = dump_char(nh.maildir_flags, d, off, convert);
645
646 return d;
647 }
648
649 HEADER *
mutt_hcache_restore(const unsigned char * d,HEADER ** oh)650 mutt_hcache_restore(const unsigned char *d, HEADER ** oh)
651 {
652 int off = 0;
653 HEADER *h = mutt_new_header();
654 int convert = !Charset_is_utf8;
655
656 /* skip validate */
657 off += sizeof (validate);
658
659 /* skip crc */
660 off += sizeof (unsigned int);
661
662 memcpy(h, d + off, sizeof (HEADER));
663 off += sizeof (HEADER);
664
665 h->env = mutt_new_envelope();
666 restore_envelope(h->env, d, &off, convert);
667
668 h->content = mutt_new_body();
669 restore_body(h->content, d, &off, convert);
670
671 restore_char(&h->maildir_flags, d, &off, convert);
672
673 /* this is needed for maildir style mailboxes */
674 if (oh)
675 {
676 h->old = (*oh)->old;
677 h->path = safe_strdup((*oh)->path);
678 mutt_free_header(oh);
679 }
680
681 return h;
682 }
683
684 void *
mutt_hcache_fetch(header_cache_t * h,const char * filename,size_t (* keylen)(const char * fn))685 mutt_hcache_fetch(header_cache_t *h, const char *filename,
686 size_t(*keylen) (const char *fn))
687 {
688 void* data;
689
690 data = mutt_hcache_fetch_raw (h, filename, keylen);
691
692 if (!data || !crc_matches(data, h->crc))
693 {
694 FREE(&data);
695 return NULL;
696 }
697
698 return data;
699 }
700
701 void *
mutt_hcache_fetch_raw(header_cache_t * h,const char * filename,size_t (* keylen)(const char * fn))702 mutt_hcache_fetch_raw (header_cache_t *h, const char *filename,
703 size_t(*keylen) (const char *fn))
704 {
705 #ifndef HAVE_DB4
706 char path[_POSIX_PATH_MAX];
707 int ksize;
708 #endif
709 #ifdef HAVE_QDBM
710 char *data = NULL;
711 #elif HAVE_TC
712 void *data;
713 int sp;
714 #elif HAVE_GDBM
715 datum key;
716 datum data;
717 #elif HAVE_DB4
718 DBT key;
719 DBT data;
720 #endif
721
722 if (!h)
723 return NULL;
724
725 #ifdef HAVE_DB4
726 if (filename[0] == '/')
727 filename++;
728
729 mutt_hcache_dbt_init(&key, (void *) filename, keylen(filename));
730 mutt_hcache_dbt_empty_init(&data);
731 data.flags = DB_DBT_MALLOC;
732
733 h->db->get(h->db, NULL, &key, &data, 0);
734
735 return data.data;
736 #else
737 strncpy(path, h->folder, sizeof (path));
738 safe_strcat(path, sizeof (path), filename);
739
740 ksize = strlen (h->folder) + keylen (path + strlen (h->folder));
741 #endif
742 #ifdef HAVE_QDBM
743 data = vlget(h->db, path, ksize, NULL);
744
745 return data;
746 #elif HAVE_TC
747 data = tcbdbget(h->db, path, ksize, &sp);
748
749 return data;
750 #elif HAVE_GDBM
751 key.dptr = path;
752 key.dsize = ksize;
753
754 data = gdbm_fetch(h->db, key);
755
756 return data.dptr;
757 #endif
758 }
759
760 int
mutt_hcache_store(header_cache_t * h,const char * filename,HEADER * header,unsigned int uidvalidity,size_t (* keylen)(const char * fn))761 mutt_hcache_store(header_cache_t *h, const char *filename, HEADER * header,
762 unsigned int uidvalidity,
763 size_t(*keylen) (const char *fn))
764 {
765 char* data;
766 int dlen;
767 int ret;
768
769 if (!h)
770 return -1;
771
772 data = mutt_hcache_dump(h, header, &dlen, uidvalidity);
773 ret = mutt_hcache_store_raw (h, filename, data, dlen, keylen);
774
775 FREE(&data);
776
777 return ret;
778 }
779
780 int
mutt_hcache_store_raw(header_cache_t * h,const char * filename,void * data,size_t dlen,size_t (* keylen)(const char * fn))781 mutt_hcache_store_raw (header_cache_t* h, const char* filename, void* data,
782 size_t dlen, size_t(*keylen) (const char* fn))
783 {
784 #ifndef HAVE_DB4
785 char path[_POSIX_PATH_MAX];
786 int ksize;
787 #endif
788 #if HAVE_GDBM
789 datum key;
790 datum databuf;
791 #elif HAVE_DB4
792 DBT key;
793 DBT databuf;
794 #endif
795
796 if (!h)
797 return -1;
798
799 #if HAVE_DB4
800 if (filename[0] == '/')
801 filename++;
802
803 mutt_hcache_dbt_init(&key, (void *) filename, keylen(filename));
804
805 mutt_hcache_dbt_empty_init(&databuf);
806 databuf.flags = DB_DBT_USERMEM;
807 databuf.data = data;
808 databuf.size = dlen;
809 databuf.ulen = dlen;
810
811 return h->db->put(h->db, NULL, &key, &databuf, 0);
812 #else
813 strncpy(path, h->folder, sizeof (path));
814 safe_strcat(path, sizeof (path), filename);
815
816 ksize = strlen(h->folder) + keylen(path + strlen(h->folder));
817 #endif
818 #if HAVE_QDBM
819 return vlput(h->db, path, ksize, data, dlen, VL_DOVER);
820 #elif HAVE_TC
821 return tcbdbput(h->db, path, ksize, data, dlen);
822 #elif HAVE_GDBM
823 key.dptr = path;
824 key.dsize = ksize;
825
826 databuf.dsize = dlen;
827 databuf.dptr = data;
828
829 return gdbm_store(h->db, key, databuf, GDBM_REPLACE);
830 #endif
831 }
832
get_foldername(const char * folder)833 static char* get_foldername(const char *folder)
834 {
835 char *p = NULL;
836 char path[_POSIX_PATH_MAX];
837 struct stat st;
838
839 mutt_encode_path (path, sizeof (path), folder);
840
841 /* if the folder is local, canonify the path to avoid
842 * to ensure equivalent paths share the hcache */
843 if (stat (path, &st) == 0)
844 {
845 p = safe_malloc (PATH_MAX+1);
846 if (!realpath (path, p))
847 mutt_str_replace (&p, path);
848 } else
849 p = safe_strdup (path);
850
851 return p;
852 }
853
854 #if HAVE_QDBM
855 static int
hcache_open_qdbm(struct header_cache * h,const char * path)856 hcache_open_qdbm (struct header_cache* h, const char* path)
857 {
858 int flags = VL_OWRITER | VL_OCREAT;
859
860 if (option(OPTHCACHECOMPRESS))
861 flags |= VL_OZCOMP;
862
863 h->db = vlopen (path, flags, VL_CMPLEX);
864 if (h->db)
865 return 0;
866 else
867 return -1;
868 }
869
870 void
mutt_hcache_close(header_cache_t * h)871 mutt_hcache_close(header_cache_t *h)
872 {
873 if (!h)
874 return;
875
876 vlclose(h->db);
877 FREE(&h->folder);
878 FREE(&h);
879 }
880
881 int
mutt_hcache_delete(header_cache_t * h,const char * filename,size_t (* keylen)(const char * fn))882 mutt_hcache_delete(header_cache_t *h, const char *filename,
883 size_t(*keylen) (const char *fn))
884 {
885 char path[_POSIX_PATH_MAX];
886 int ksize;
887
888 if (!h)
889 return -1;
890
891 strncpy(path, h->folder, sizeof (path));
892 safe_strcat(path, sizeof (path), filename);
893
894 ksize = strlen(h->folder) + keylen(path + strlen(h->folder));
895
896 return vlout(h->db, path, ksize);
897 }
898
899 #elif HAVE_TC
900 static int
hcache_open_tc(struct header_cache * h,const char * path)901 hcache_open_tc (struct header_cache* h, const char* path)
902 {
903 h->db = tcbdbnew();
904 if (option(OPTHCACHECOMPRESS))
905 tcbdbtune(h->db, 0, 0, 0, -1, -1, BDBTDEFLATE);
906 if (tcbdbopen(h->db, path, BDBOWRITER | BDBOCREAT))
907 return 0;
908 else
909 {
910 tcbdbdel(h->db);
911 return -1;
912 }
913 }
914
915 void
mutt_hcache_close(header_cache_t * h)916 mutt_hcache_close(header_cache_t *h)
917 {
918 if (!h)
919 return;
920
921 tcbdbclose(h->db);
922 tcbdbdel(h->db);
923 FREE(&h->folder);
924 FREE(&h);
925 }
926
927 int
mutt_hcache_delete(header_cache_t * h,const char * filename,size_t (* keylen)(const char * fn))928 mutt_hcache_delete(header_cache_t *h, const char *filename,
929 size_t(*keylen) (const char *fn))
930 {
931 char path[_POSIX_PATH_MAX];
932 int ksize;
933
934 if (!h)
935 return -1;
936
937 strncpy(path, h->folder, sizeof (path));
938 safe_strcat(path, sizeof (path), filename);
939
940 ksize = strlen(h->folder) + keylen(path + strlen(h->folder));
941
942 return tcbdbout(h->db, path, ksize);
943 }
944
945 #elif HAVE_GDBM
946 static int
hcache_open_gdbm(struct header_cache * h,const char * path)947 hcache_open_gdbm (struct header_cache* h, const char* path)
948 {
949 int pagesize;
950
951 if (mutt_atoi (HeaderCachePageSize, &pagesize) < 0 || pagesize <= 0)
952 pagesize = 16384;
953
954 h->db = gdbm_open((char *) path, pagesize, GDBM_WRCREAT, 00600, NULL);
955 if (h->db)
956 return 0;
957
958 /* if rw failed try ro */
959 h->db = gdbm_open((char *) path, pagesize, GDBM_READER, 00600, NULL);
960 if (h->db)
961 return 0;
962
963 return -1;
964 }
965
966 void
mutt_hcache_close(header_cache_t * h)967 mutt_hcache_close(header_cache_t *h)
968 {
969 if (!h)
970 return;
971
972 gdbm_close(h->db);
973 FREE(&h->folder);
974 FREE(&h);
975 }
976
977 int
mutt_hcache_delete(header_cache_t * h,const char * filename,size_t (* keylen)(const char * fn))978 mutt_hcache_delete(header_cache_t *h, const char *filename,
979 size_t(*keylen) (const char *fn))
980 {
981 datum key;
982 char path[_POSIX_PATH_MAX];
983
984 if (!h)
985 return -1;
986
987 strncpy(path, h->folder, sizeof (path));
988 safe_strcat(path, sizeof (path), filename);
989
990 key.dptr = path;
991 key.dsize = strlen(h->folder) + keylen(path + strlen(h->folder));
992
993 return gdbm_delete(h->db, key);
994 }
995 #elif HAVE_DB4
996
997 static void
mutt_hcache_dbt_init(DBT * dbt,void * data,size_t len)998 mutt_hcache_dbt_init(DBT * dbt, void *data, size_t len)
999 {
1000 dbt->data = data;
1001 dbt->size = dbt->ulen = len;
1002 dbt->dlen = dbt->doff = 0;
1003 dbt->flags = DB_DBT_USERMEM;
1004 }
1005
1006 static void
mutt_hcache_dbt_empty_init(DBT * dbt)1007 mutt_hcache_dbt_empty_init(DBT * dbt)
1008 {
1009 dbt->data = NULL;
1010 dbt->size = dbt->ulen = dbt->dlen = dbt->doff = 0;
1011 dbt->flags = 0;
1012 }
1013
1014 static int
hcache_open_db4(struct header_cache * h,const char * path)1015 hcache_open_db4 (struct header_cache* h, const char* path)
1016 {
1017 struct stat sb;
1018 int ret;
1019 u_int32_t createflags = DB_CREATE;
1020 int pagesize;
1021
1022 if (mutt_atoi (HeaderCachePageSize, &pagesize) < 0 || pagesize <= 0)
1023 pagesize = 16384;
1024
1025 snprintf (h->lockfile, _POSIX_PATH_MAX, "%s-lock-hack", path);
1026
1027 h->fd = open (h->lockfile, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
1028 if (h->fd < 0)
1029 return -1;
1030
1031 if (mx_lock_file (h->lockfile, h->fd, 1, 0, 5))
1032 goto fail_close;
1033
1034 ret = db_env_create (&h->env, 0);
1035 if (ret)
1036 goto fail_unlock;
1037
1038 ret = (*h->env->open)(h->env, NULL, DB_INIT_MPOOL | DB_CREATE | DB_PRIVATE,
1039 0600);
1040 if (ret)
1041 goto fail_env;
1042
1043 ret = db_create (&h->db, h->env, 0);
1044 if (ret)
1045 goto fail_env;
1046
1047 if (stat(path, &sb) != 0 && errno == ENOENT)
1048 {
1049 createflags |= DB_EXCL;
1050 h->db->set_pagesize(h->db, pagesize);
1051 }
1052
1053 ret = (*h->db->open)(h->db, NULL, path, h->folder, DB_BTREE, createflags,
1054 0600);
1055 if (ret)
1056 goto fail_db;
1057
1058 return 0;
1059
1060 fail_db:
1061 h->db->close (h->db, 0);
1062 fail_env:
1063 h->env->close (h->env, 0);
1064 fail_unlock:
1065 mx_unlock_file (h->lockfile, h->fd, 0);
1066 fail_close:
1067 close (h->fd);
1068 unlink (h->lockfile);
1069
1070 return -1;
1071 }
1072
1073 void
mutt_hcache_close(header_cache_t * h)1074 mutt_hcache_close(header_cache_t *h)
1075 {
1076 if (!h)
1077 return;
1078
1079 h->db->close (h->db, 0);
1080 h->env->close (h->env, 0);
1081 mx_unlock_file (h->lockfile, h->fd, 0);
1082 close (h->fd);
1083 unlink (h->lockfile);
1084 FREE (&h->folder);
1085 FREE (&h);
1086 }
1087
1088 int
mutt_hcache_delete(header_cache_t * h,const char * filename,size_t (* keylen)(const char * fn))1089 mutt_hcache_delete(header_cache_t *h, const char *filename,
1090 size_t(*keylen) (const char *fn))
1091 {
1092 DBT key;
1093
1094 if (!h)
1095 return -1;
1096
1097 if (filename[0] == '/')
1098 filename++;
1099
1100 mutt_hcache_dbt_init(&key, (void *) filename, keylen(filename));
1101 return h->db->del(h->db, NULL, &key, 0);
1102 }
1103 #endif
1104
1105 header_cache_t *
mutt_hcache_open(const char * path,const char * folder,hcache_namer_t namer)1106 mutt_hcache_open(const char *path, const char *folder, hcache_namer_t namer)
1107 {
1108 struct header_cache *h = safe_calloc(1, sizeof (HEADER_CACHE));
1109 int (*hcache_open) (struct header_cache* h, const char* path);
1110 struct stat sb;
1111
1112 #if HAVE_QDBM
1113 hcache_open = hcache_open_qdbm;
1114 #elif HAVE_TC
1115 hcache_open= hcache_open_tc;
1116 #elif HAVE_GDBM
1117 hcache_open = hcache_open_gdbm;
1118 #elif HAVE_DB4
1119 hcache_open = hcache_open_db4;
1120 #endif
1121
1122 h->db = NULL;
1123 h->folder = get_foldername(folder);
1124 h->crc = HCACHEVER;
1125
1126 if (!path || path[0] == '\0')
1127 {
1128 FREE(&h->folder);
1129 FREE(&h);
1130 return NULL;
1131 }
1132
1133 path = mutt_hcache_per_folder(path, h->folder, namer);
1134
1135 if (!hcache_open (h, path))
1136 return h;
1137 else
1138 {
1139 /* remove a possibly incompatible version */
1140 if (!stat (path, &sb) && !unlink (path))
1141 {
1142 if (!hcache_open (h, path))
1143 return h;
1144 }
1145 FREE(&h->folder);
1146 FREE(&h);
1147
1148 return NULL;
1149 }
1150 }
1151
1152 #if HAVE_DB4
mutt_hcache_backend(void)1153 const char *mutt_hcache_backend (void)
1154 {
1155 return DB_VERSION_STRING;
1156 }
1157 #elif HAVE_GDBM
mutt_hcache_backend(void)1158 const char *mutt_hcache_backend (void)
1159 {
1160 return gdbm_version;
1161 }
1162 #elif HAVE_QDBM
mutt_hcache_backend(void)1163 const char *mutt_hcache_backend (void)
1164 {
1165 return "qdbm " _QDBM_VERSION;
1166 }
1167 #elif HAVE_TC
mutt_hcache_backend(void)1168 const char *mutt_hcache_backend (void)
1169 {
1170 return "tokyocabinet " _TC_VERSION;
1171 }
1172 #endif
1173