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>
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.
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.
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.
23 #endif /* HAVE_CONFIG_H */
44 #include "hcversion.h"
50 static struct header_cache
57 static struct header_cache
64 static struct header_cache
71 static struct header_cache
78 char lockfile[_POSIX_PATH_MAX];
81 static void mutt_hcache_dbt_init(DBT * dbt, void *data, size_t len);
82 static void mutt_hcache_dbt_empty_init(DBT * dbt);
87 struct timeval timeval;
88 unsigned long uid_validity;
92 lazy_malloc(size_t siz)
94 if (0 < siz && siz < 4096)
97 return safe_malloc(siz);
101 lazy_realloc(void *ptr, size_t siz)
103 void **p = (void **) ptr;
105 if (p != NULL && 0 < siz && siz < 4096)
108 safe_realloc(ptr, siz);
111 static unsigned char *
112 dump_int(unsigned int i, unsigned char *d, int *off)
114 lazy_realloc(&d, *off + sizeof (int));
115 memcpy(d + *off, &i, sizeof (int));
116 (*off) += sizeof (int);
122 restore_int(unsigned int *i, const unsigned char *d, int *off)
124 memcpy(i, d + *off, sizeof (int));
125 (*off) += sizeof (int);
128 static inline int is_ascii (const char *p, size_t len) {
129 register const char *s = p;
130 while (s && (unsigned) (s - p) < len) {
131 if ((*s & 0x80) != 0)
138 static unsigned char *
139 dump_char_size(char *c, unsigned char *d, int *off, ssize_t size, int convert)
146 d = dump_int(size, d, off);
150 if (convert && !is_ascii (c, size)) {
151 p = mutt_substrdup (c, c + size);
152 if (mutt_convert_string (&p, Charset, "utf-8", 0) == 0) {
154 size = mutt_strlen (c) + 1;
158 d = dump_int(size, d, off);
159 lazy_realloc(&d, *off + size);
160 memcpy(d + *off, p, size);
169 static unsigned char *
170 dump_char(char *c, unsigned char *d, int *off, int convert)
172 return dump_char_size (c, d, off, mutt_strlen (c) + 1, convert);
176 restore_char(char **c, const unsigned char *d, int *off, int convert)
179 restore_int(&size, d, off);
187 *c = safe_malloc(size);
188 memcpy(*c, d + *off, size);
189 if (convert && !is_ascii (*c, size)) {
190 char *tmp = safe_strdup (*c);
191 if (mutt_convert_string (&tmp, "utf-8", Charset, 0) == 0) {
192 mutt_str_replace (c, tmp);
200 static unsigned char *
201 dump_address(ADDRESS * a, unsigned char *d, int *off, int convert)
203 unsigned int counter = 0;
204 unsigned int start_off = *off;
206 d = dump_int(0xdeadbeef, d, off);
211 d = dump_char(a->val, d, off, convert);
213 d = dump_char(a->personal, d, off, convert);
214 d = dump_char(a->mailbox, d, off, 0);
215 d = dump_int(a->group, d, off);
220 memcpy(d + start_off, &counter, sizeof (int));
226 restore_address(ADDRESS ** a, const unsigned char *d, int *off, int convert)
228 unsigned int counter;
230 restore_int(&counter, d, off);
234 *a = safe_malloc(sizeof (ADDRESS));
236 restore_char(&(*a)->val, d, off, convert);
238 restore_char(&(*a)->personal, d, off, convert);
239 restore_char(&(*a)->mailbox, d, off, 0);
240 restore_int((unsigned int *) &(*a)->group, d, off);
248 static unsigned char *
249 dump_list(LIST * l, unsigned char *d, int *off, int convert)
251 unsigned int counter = 0;
252 unsigned int start_off = *off;
254 d = dump_int(0xdeadbeef, d, off);
258 d = dump_char(l->data, d, off, convert);
263 memcpy(d + start_off, &counter, sizeof (int));
269 restore_list(LIST ** l, const unsigned char *d, int *off, int convert)
271 unsigned int counter;
273 restore_int(&counter, d, off);
277 *l = safe_malloc(sizeof (LIST));
278 restore_char(&(*l)->data, d, off, convert);
286 static unsigned char *
287 dump_buffer(BUFFER * b, unsigned char *d, int *off, int convert)
291 d = dump_int(0, d, off);
295 d = dump_int(1, d, off);
297 d = dump_char_size(b->data, d, off, b->dsize + 1, convert);
298 d = dump_int(b->dptr - b->data, d, off);
299 d = dump_int(b->dsize, d, off);
300 d = dump_int(b->destroy, d, off);
306 restore_buffer(BUFFER ** b, const unsigned char *d, int *off, int convert)
310 restore_int(&used, d, off);
316 *b = safe_malloc(sizeof (BUFFER));
318 restore_char(&(*b)->data, d, off, convert);
319 restore_int(&offset, d, off);
320 (*b)->dptr = (*b)->data + offset;
321 restore_int (&used, d, off);
323 restore_int (&used, d, off);
324 (*b)->destroy = used;
327 static unsigned char *
328 dump_parameter(PARAMETER * p, unsigned char *d, int *off, int convert)
330 unsigned int counter = 0;
331 unsigned int start_off = *off;
333 d = dump_int(0xdeadbeef, d, off);
337 d = dump_char(p->attribute, d, off, 0);
338 d = dump_char(p->value, d, off, convert);
343 memcpy(d + start_off, &counter, sizeof (int));
349 restore_parameter(PARAMETER ** p, const unsigned char *d, int *off, int convert)
351 unsigned int counter;
353 restore_int(&counter, d, off);
357 *p = safe_malloc(sizeof (PARAMETER));
358 restore_char(&(*p)->attribute, d, off, 0);
359 restore_char(&(*p)->value, d, off, convert);
367 static unsigned char *
368 dump_body(BODY * c, unsigned char *d, int *off, int convert)
372 memcpy (&nb, c, sizeof (BODY));
374 /* some fields are not safe to cache */
382 lazy_realloc(&d, *off + sizeof (BODY));
383 memcpy(d + *off, &nb, sizeof (BODY));
384 *off += sizeof (BODY);
386 d = dump_char(nb.xtype, d, off, 0);
387 d = dump_char(nb.subtype, d, off, 0);
389 d = dump_parameter(nb.parameter, d, off, convert);
391 d = dump_char(nb.description, d, off, convert);
392 d = dump_char(nb.form_name, d, off, convert);
393 d = dump_char(nb.filename, d, off, convert);
394 d = dump_char(nb.d_filename, d, off, convert);
400 restore_body(BODY * c, const unsigned char *d, int *off, int convert)
402 memcpy(c, d + *off, sizeof (BODY));
403 *off += sizeof (BODY);
405 restore_char(&c->xtype, d, off, 0);
406 restore_char(&c->subtype, d, off, 0);
408 restore_parameter(&c->parameter, d, off, convert);
410 restore_char(&c->description, d, off, convert);
411 restore_char(&c->form_name, d, off, convert);
412 restore_char(&c->filename, d, off, convert);
413 restore_char(&c->d_filename, d, off, convert);
416 static unsigned char *
417 dump_envelope(ENVELOPE * e, unsigned char *d, int *off, int convert)
419 d = dump_address(e->return_path, d, off, convert);
420 d = dump_address(e->from, d, off, convert);
421 d = dump_address(e->to, d, off, convert);
422 d = dump_address(e->cc, d, off, convert);
423 d = dump_address(e->bcc, d, off, convert);
424 d = dump_address(e->sender, d, off, convert);
425 d = dump_address(e->reply_to, d, off, convert);
426 d = dump_address(e->mail_followup_to, d, off, convert);
428 d = dump_char(e->list_post, d, off, convert);
429 d = dump_char(e->subject, d, off, convert);
432 d = dump_int(e->real_subj - e->subject, d, off);
434 d = dump_int(-1, d, off);
436 d = dump_char(e->message_id, d, off, 0);
437 d = dump_char(e->supersedes, d, off, 0);
438 d = dump_char(e->date, d, off, 0);
439 d = dump_char(e->x_label, d, off, convert);
441 d = dump_buffer(e->spam, d, off, convert);
443 d = dump_list(e->references, d, off, 0);
444 d = dump_list(e->in_reply_to, d, off, 0);
445 d = dump_list(e->userhdrs, d, off, convert);
451 restore_envelope(ENVELOPE * e, const unsigned char *d, int *off, int convert)
455 restore_address(&e->return_path, d, off, convert);
456 restore_address(&e->from, d, off, convert);
457 restore_address(&e->to, d, off, convert);
458 restore_address(&e->cc, d, off, convert);
459 restore_address(&e->bcc, d, off, convert);
460 restore_address(&e->sender, d, off, convert);
461 restore_address(&e->reply_to, d, off, convert);
462 restore_address(&e->mail_followup_to, d, off, convert);
464 restore_char(&e->list_post, d, off, convert);
465 restore_char(&e->subject, d, off, convert);
466 restore_int((unsigned int *) (&real_subj_off), d, off);
468 if (0 <= real_subj_off)
469 e->real_subj = e->subject + real_subj_off;
473 restore_char(&e->message_id, d, off, 0);
474 restore_char(&e->supersedes, d, off, 0);
475 restore_char(&e->date, d, off, 0);
476 restore_char(&e->x_label, d, off, convert);
478 restore_buffer(&e->spam, d, off, convert);
480 restore_list(&e->references, d, off, 0);
481 restore_list(&e->in_reply_to, d, off, 0);
482 restore_list(&e->userhdrs, d, off, convert);
486 crc_matches(const char *d, unsigned int crc)
488 int off = sizeof (validate);
489 unsigned int mycrc = 0;
494 restore_int(&mycrc, (unsigned char *) d, &off);
496 return (crc == mycrc);
499 /* Append md5sumed folder to path if path is a directory. */
501 mutt_hcache_per_folder(const char *path, const char *folder,
502 hcache_namer_t namer)
504 static char hcpath[_POSIX_PATH_MAX];
506 unsigned char md5sum[16];
510 const char *chs = Charset && *Charset ? Charset :
511 mutt_get_default_charset ();
514 plen = mutt_strlen (path);
516 ret = stat(path, &sb);
517 if (ret < 0 && path[plen-1] != '/')
522 snprintf (hcpath, _POSIX_PATH_MAX, "%s-%s", path, chs);
527 if (ret >= 0 && !S_ISDIR(sb.st_mode))
532 snprintf (hcpath, _POSIX_PATH_MAX, "%s-%s", path, chs);
539 snprintf (hcpath, sizeof (hcpath), "%s%s", path,
540 path[plen-1] == '/' ? "" : "/");
541 if (path[plen-1] != '/')
544 ret = namer (folder, hcpath + plen, sizeof (hcpath) - plen);
548 md5_buffer (folder, strlen (folder), &md5sum);
550 ret = snprintf(hcpath, _POSIX_PATH_MAX,
551 "%s/%02x%02x%02x%02x%02x%02x%02x%02x"
552 "%02x%02x%02x%02x%02x%02x%02x%02x"
557 path, md5sum[0], md5sum[1], md5sum[2], md5sum[3],
558 md5sum[4], md5sum[5], md5sum[6], md5sum[7], md5sum[8],
559 md5sum[9], md5sum[10], md5sum[11], md5sum[12],
560 md5sum[13], md5sum[14], md5sum[15]
570 if (stat (hcpath, &sb) >= 0)
573 s = strchr (hcpath + 1, '/');
576 /* create missing path components */
578 if (stat (hcpath, &sb) < 0 && (errno != ENOENT || mkdir (hcpath, 0777) < 0))
581 s = strchr (s + 1, '/');
587 /* This function transforms a header into a char so that it is useable by
590 mutt_hcache_dump(header_cache_t *h, HEADER * header, int *off,
591 unsigned long uid_validity)
593 unsigned char *d = NULL;
595 int convert = !Charset_is_utf8;
598 d = lazy_malloc(sizeof (validate));
601 memcpy(d, &uid_validity, sizeof (unsigned long));
605 gettimeofday(&now, NULL);
606 memcpy(d, &now, sizeof (struct timeval));
608 *off += sizeof (validate);
610 d = dump_int(h->crc, d, off);
612 lazy_realloc(&d, *off + sizeof (HEADER));
613 memcpy(&nh, header, sizeof (HEADER));
615 /* some fields are not safe to cache */
633 #if defined USE_POP || defined USE_IMAP
637 memcpy(d + *off, &nh, sizeof (HEADER));
638 *off += sizeof (HEADER);
640 d = dump_envelope(nh.env, d, off, convert);
641 d = dump_body(nh.content, d, off, convert);
642 d = dump_char(nh.maildir_flags, d, off, convert);
648 mutt_hcache_restore(const unsigned char *d, HEADER ** oh)
651 HEADER *h = mutt_new_header();
652 int convert = !Charset_is_utf8;
655 off += sizeof (validate);
658 off += sizeof (unsigned int);
660 memcpy(h, d + off, sizeof (HEADER));
661 off += sizeof (HEADER);
663 h->env = mutt_new_envelope();
664 restore_envelope(h->env, d, &off, convert);
666 h->content = mutt_new_body();
667 restore_body(h->content, d, &off, convert);
669 restore_char(&h->maildir_flags, d, &off, convert);
671 /* this is needed for maildir style mailboxes */
675 h->path = safe_strdup((*oh)->path);
676 mutt_free_header(oh);
683 mutt_hcache_fetch(header_cache_t *h, const char *filename,
684 size_t(*keylen) (const char *fn))
688 data = mutt_hcache_fetch_raw (h, filename, keylen);
690 if (!data || !crc_matches(data, h->crc))
700 mutt_hcache_fetch_raw (header_cache_t *h, const char *filename,
701 size_t(*keylen) (const char *fn))
704 char path[_POSIX_PATH_MAX];
724 if (filename[0] == '/')
727 mutt_hcache_dbt_init(&key, (void *) filename, keylen(filename));
728 mutt_hcache_dbt_empty_init(&data);
729 data.flags = DB_DBT_MALLOC;
731 h->db->get(h->db, NULL, &key, &data, 0);
735 strncpy(path, h->folder, sizeof (path));
736 safe_strcat(path, sizeof (path), filename);
738 ksize = strlen (h->folder) + keylen (path + strlen (h->folder));
741 data = vlget(h->db, path, ksize, NULL);
745 data = tcbdbget(h->db, path, ksize, &sp);
752 data = gdbm_fetch(h->db, key);
759 mutt_hcache_store(header_cache_t *h, const char *filename, HEADER * header,
760 unsigned long uid_validity,
761 size_t(*keylen) (const char *fn))
770 data = mutt_hcache_dump(h, header, &dlen, uid_validity);
771 ret = mutt_hcache_store_raw (h, filename, data, dlen, keylen);
779 mutt_hcache_store_raw (header_cache_t* h, const char* filename, void* data,
780 size_t dlen, size_t(*keylen) (const char* fn))
783 char path[_POSIX_PATH_MAX];
798 if (filename[0] == '/')
801 mutt_hcache_dbt_init(&key, (void *) filename, keylen(filename));
803 mutt_hcache_dbt_empty_init(&databuf);
804 databuf.flags = DB_DBT_USERMEM;
809 return h->db->put(h->db, NULL, &key, &databuf, 0);
811 strncpy(path, h->folder, sizeof (path));
812 safe_strcat(path, sizeof (path), filename);
814 ksize = strlen(h->folder) + keylen(path + strlen(h->folder));
817 return vlput(h->db, path, ksize, data, dlen, VL_DOVER);
819 return tcbdbput(h->db, path, ksize, data, dlen);
824 databuf.dsize = dlen;
827 return gdbm_store(h->db, key, databuf, GDBM_REPLACE);
831 static char* get_foldername(const char *folder) {
835 /* if the folder is local, canonify the path to avoid
836 * to ensure equivalent paths share the hcache */
837 if (stat (folder, &st) == 0)
839 p = safe_malloc (PATH_MAX+1);
840 if (!realpath (folder, p))
841 mutt_str_replace (&p, folder);
843 p = safe_strdup (folder);
850 hcache_open_qdbm (struct header_cache* h, const char* path)
852 int flags = VL_OWRITER | VL_OCREAT;
854 if (option(OPTHCACHECOMPRESS))
857 h->db = vlopen (path, flags, VL_CMPLEX);
865 mutt_hcache_close(header_cache_t *h)
876 mutt_hcache_delete(header_cache_t *h, const char *filename,
877 size_t(*keylen) (const char *fn))
879 char path[_POSIX_PATH_MAX];
885 strncpy(path, h->folder, sizeof (path));
886 safe_strcat(path, sizeof (path), filename);
888 ksize = strlen(h->folder) + keylen(path + strlen(h->folder));
890 return vlout(h->db, path, ksize);
895 hcache_open_tc (struct header_cache* h, const char* path)
898 if (option(OPTHCACHECOMPRESS))
899 tcbdbtune(h->db, 0, 0, 0, -1, -1, BDBTDEFLATE);
900 if (tcbdbopen(h->db, path, BDBOWRITER | BDBOCREAT))
910 mutt_hcache_close(header_cache_t *h)
922 mutt_hcache_delete(header_cache_t *h, const char *filename,
923 size_t(*keylen) (const char *fn))
925 char path[_POSIX_PATH_MAX];
931 strncpy(path, h->folder, sizeof (path));
932 safe_strcat(path, sizeof (path), filename);
934 ksize = strlen(h->folder) + keylen(path + strlen(h->folder));
936 return tcbdbout(h->db, path, ksize);
941 hcache_open_gdbm (struct header_cache* h, const char* path)
945 if (mutt_atoi (HeaderCachePageSize, &pagesize) < 0 || pagesize <= 0)
948 h->db = gdbm_open((char *) path, pagesize, GDBM_WRCREAT, 00600, NULL);
952 /* if rw failed try ro */
953 h->db = gdbm_open((char *) path, pagesize, GDBM_READER, 00600, NULL);
961 mutt_hcache_close(header_cache_t *h)
972 mutt_hcache_delete(header_cache_t *h, const char *filename,
973 size_t(*keylen) (const char *fn))
976 char path[_POSIX_PATH_MAX];
981 strncpy(path, h->folder, sizeof (path));
982 safe_strcat(path, sizeof (path), filename);
985 key.dsize = strlen(h->folder) + keylen(path + strlen(h->folder));
987 return gdbm_delete(h->db, key);
992 mutt_hcache_dbt_init(DBT * dbt, void *data, size_t len)
995 dbt->size = dbt->ulen = len;
996 dbt->dlen = dbt->doff = 0;
997 dbt->flags = DB_DBT_USERMEM;
1001 mutt_hcache_dbt_empty_init(DBT * dbt)
1004 dbt->size = dbt->ulen = dbt->dlen = dbt->doff = 0;
1009 hcache_open_db4 (struct header_cache* h, const char* path)
1013 u_int32_t createflags = DB_CREATE;
1016 if (mutt_atoi (HeaderCachePageSize, &pagesize) < 0 || pagesize <= 0)
1019 snprintf (h->lockfile, _POSIX_PATH_MAX, "%s-lock-hack", path);
1021 h->fd = open (h->lockfile, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
1025 if (mx_lock_file (h->lockfile, h->fd, 1, 0, 5))
1028 ret = db_env_create (&h->env, 0);
1032 ret = (*h->env->open)(h->env, NULL, DB_INIT_MPOOL | DB_CREATE | DB_PRIVATE,
1037 ret = db_create (&h->db, h->env, 0);
1041 if (stat(path, &sb) != 0 && errno == ENOENT)
1043 createflags |= DB_EXCL;
1044 h->db->set_pagesize(h->db, pagesize);
1047 ret = (*h->db->open)(h->db, NULL, path, h->folder, DB_BTREE, createflags,
1055 h->db->close (h->db, 0);
1057 h->env->close (h->env, 0);
1059 mx_unlock_file (h->lockfile, h->fd, 0);
1062 unlink (h->lockfile);
1068 mutt_hcache_close(header_cache_t *h)
1073 h->db->close (h->db, 0);
1074 h->env->close (h->env, 0);
1075 mx_unlock_file (h->lockfile, h->fd, 0);
1077 unlink (h->lockfile);
1083 mutt_hcache_delete(header_cache_t *h, const char *filename,
1084 size_t(*keylen) (const char *fn))
1091 if (filename[0] == '/')
1094 mutt_hcache_dbt_init(&key, (void *) filename, keylen(filename));
1095 return h->db->del(h->db, NULL, &key, 0);
1100 mutt_hcache_open(const char *path, const char *folder, hcache_namer_t namer)
1102 struct header_cache *h = safe_calloc(1, sizeof (HEADER_CACHE));
1103 int (*hcache_open) (struct header_cache* h, const char* path);
1107 hcache_open = hcache_open_qdbm;
1109 hcache_open= hcache_open_tc;
1111 hcache_open = hcache_open_gdbm;
1113 hcache_open = hcache_open_db4;
1117 h->folder = get_foldername(folder);
1120 if (!path || path[0] == '\0')
1127 path = mutt_hcache_per_folder(path, h->folder, namer);
1129 if (!hcache_open (h, path))
1133 /* remove a possibly incompatible version */
1134 if (!stat (path, &sb) && !unlink (path))
1136 if (!hcache_open (h, path))
1147 const char *mutt_hcache_backend (void)
1149 return DB_VERSION_STRING;
1152 const char *mutt_hcache_backend (void)
1154 return gdbm_version;
1157 const char *mutt_hcache_backend (void)
1159 return "qdbm " _QDBM_VERSION;
1162 const char *mutt_hcache_backend (void)
1164 return "tokyocabinet " _TC_VERSION;