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"
51 static struct header_cache
58 static struct header_cache
65 static struct header_cache
72 static struct header_cache
79 char lockfile[_POSIX_PATH_MAX];
82 static void mutt_hcache_dbt_init(DBT * dbt, void *data, size_t len);
83 static void mutt_hcache_dbt_empty_init(DBT * dbt);
88 struct timeval timeval;
89 unsigned int uidvalidity;
93 lazy_malloc(size_t siz)
95 if (0 < siz && siz < 4096)
98 return safe_malloc(siz);
102 lazy_realloc(void *ptr, size_t siz)
104 void **p = (void **) ptr;
106 if (p != NULL && 0 < siz && siz < 4096)
109 safe_realloc(ptr, siz);
112 static unsigned char *
113 dump_int(unsigned int i, unsigned char *d, int *off)
115 lazy_realloc(&d, *off + sizeof (int));
116 memcpy(d + *off, &i, sizeof (int));
117 (*off) += sizeof (int);
123 restore_int(unsigned int *i, const unsigned char *d, int *off)
125 memcpy(i, d + *off, sizeof (int));
126 (*off) += sizeof (int);
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)
139 static unsigned char *
140 dump_char_size(char *c, unsigned char *d, int *off, ssize_t size, int convert)
147 d = dump_int(size, d, off);
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) {
155 size = mutt_strlen (c) + 1;
159 d = dump_int(size, d, off);
160 lazy_realloc(&d, *off + size);
161 memcpy(d + *off, p, size);
170 static unsigned char *
171 dump_char(char *c, unsigned char *d, int *off, int convert)
173 return dump_char_size (c, d, off, mutt_strlen (c) + 1, convert);
177 restore_char(char **c, const unsigned char *d, int *off, int convert)
180 restore_int(&size, d, off);
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);
201 static unsigned char *
202 dump_address(ADDRESS * a, unsigned char *d, int *off, int convert)
204 unsigned int counter = 0;
205 unsigned int start_off = *off;
207 d = dump_int(0xdeadbeef, d, off);
212 d = dump_char(a->val, d, off, convert);
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);
221 memcpy(d + start_off, &counter, sizeof (int));
227 restore_address(ADDRESS ** a, const unsigned char *d, int *off, int convert)
229 unsigned int counter;
231 restore_int(&counter, d, off);
235 *a = rfc822_new_address();
237 restore_char(&(*a)->val, d, off, convert);
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);
249 static unsigned char *
250 dump_list(LIST * l, unsigned char *d, int *off, int convert)
252 unsigned int counter = 0;
253 unsigned int start_off = *off;
255 d = dump_int(0xdeadbeef, d, off);
259 d = dump_char(l->data, d, off, convert);
264 memcpy(d + start_off, &counter, sizeof (int));
270 restore_list(LIST ** l, const unsigned char *d, int *off, int convert)
272 unsigned int counter;
274 restore_int(&counter, d, off);
278 *l = safe_malloc(sizeof (LIST));
279 restore_char(&(*l)->data, d, off, convert);
287 static unsigned char *
288 dump_buffer(BUFFER * b, unsigned char *d, int *off, int convert)
292 d = dump_int(0, d, off);
296 d = dump_int(1, d, off);
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);
307 restore_buffer(BUFFER ** b, const unsigned char *d, int *off, int convert)
311 restore_int(&used, d, off);
317 *b = safe_malloc(sizeof (BUFFER));
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);
324 restore_int (&used, d, off);
325 (*b)->destroy = used;
328 static unsigned char *
329 dump_parameter(PARAMETER * p, unsigned char *d, int *off, int convert)
331 unsigned int counter = 0;
332 unsigned int start_off = *off;
334 d = dump_int(0xdeadbeef, d, off);
338 d = dump_char(p->attribute, d, off, 0);
339 d = dump_char(p->value, d, off, convert);
344 memcpy(d + start_off, &counter, sizeof (int));
350 restore_parameter(PARAMETER ** p, const unsigned char *d, int *off, int convert)
352 unsigned int counter;
354 restore_int(&counter, d, off);
358 *p = safe_malloc(sizeof (PARAMETER));
359 restore_char(&(*p)->attribute, d, off, 0);
360 restore_char(&(*p)->value, d, off, convert);
368 static unsigned char *
369 dump_body(BODY * c, unsigned char *d, int *off, int convert)
373 memcpy (&nb, c, sizeof (BODY));
375 /* some fields are not safe to cache */
383 lazy_realloc(&d, *off + sizeof (BODY));
384 memcpy(d + *off, &nb, sizeof (BODY));
385 *off += sizeof (BODY);
387 d = dump_char(nb.xtype, d, off, 0);
388 d = dump_char(nb.subtype, d, off, 0);
390 d = dump_parameter(nb.parameter, d, off, convert);
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);
401 restore_body(BODY * c, const unsigned char *d, int *off, int convert)
403 memcpy(c, d + *off, sizeof (BODY));
404 *off += sizeof (BODY);
406 restore_char(&c->xtype, d, off, 0);
407 restore_char(&c->subtype, d, off, 0);
409 restore_parameter(&c->parameter, d, off, convert);
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);
417 static unsigned char *
418 dump_envelope(ENVELOPE * e, unsigned char *d, int *off, int convert)
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);
429 d = dump_char(e->list_post, d, off, convert);
430 d = dump_char(e->subject, d, off, convert);
433 d = dump_int(e->real_subj - e->subject, d, off);
435 d = dump_int(-1, d, off);
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);
442 d = dump_buffer(e->spam, d, off, convert);
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);
452 restore_envelope(ENVELOPE * e, const unsigned char *d, int *off, int convert)
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);
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);
469 if (0 <= real_subj_off)
470 e->real_subj = e->subject + real_subj_off;
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);
479 restore_buffer(&e->spam, d, off, convert);
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);
487 crc_matches(const char *d, unsigned int crc)
489 int off = sizeof (validate);
490 unsigned int mycrc = 0;
495 restore_int(&mycrc, (unsigned char *) d, &off);
497 return (crc == mycrc);
500 /* Append md5sumed folder to path if path is a directory. */
502 mutt_hcache_per_folder(const char *path, const char *folder,
503 hcache_namer_t namer)
505 static char hcpath[_POSIX_PATH_MAX];
507 unsigned char md5sum[16];
511 const char *chs = Charset && *Charset ? Charset :
512 mutt_get_default_charset ();
515 plen = mutt_strlen (path);
517 ret = stat(path, &sb);
518 if (ret < 0 && path[plen-1] != '/')
523 snprintf (hcpath, _POSIX_PATH_MAX, "%s-%s", path, chs);
528 if (ret >= 0 && !S_ISDIR(sb.st_mode))
533 snprintf (hcpath, _POSIX_PATH_MAX, "%s-%s", path, chs);
540 snprintf (hcpath, sizeof (hcpath), "%s%s", path,
541 path[plen-1] == '/' ? "" : "/");
542 if (path[plen-1] != '/')
545 ret = namer (folder, hcpath + plen, sizeof (hcpath) - plen);
549 md5_buffer (folder, strlen (folder), &md5sum);
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"
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]
571 if (stat (hcpath, &sb) >= 0)
574 s = strchr (hcpath + 1, '/');
577 /* create missing path components */
579 if (stat (hcpath, &sb) < 0 && (errno != ENOENT || mkdir (hcpath, 0777) < 0))
582 s = strchr (s + 1, '/');
588 /* This function transforms a header into a char so that it is useable by
591 mutt_hcache_dump(header_cache_t *h, HEADER * header, int *off,
592 unsigned int uidvalidity)
594 unsigned char *d = NULL;
596 int convert = !Charset_is_utf8;
599 d = lazy_malloc(sizeof (validate));
602 memcpy(d, &uidvalidity, sizeof (uidvalidity));
606 gettimeofday(&now, NULL);
607 memcpy(d, &now, sizeof (struct timeval));
609 *off += sizeof (validate);
611 d = dump_int(h->crc, d, off);
613 lazy_realloc(&d, *off + sizeof (HEADER));
614 memcpy(&nh, header, sizeof (HEADER));
616 /* some fields are not safe to cache */
635 #if defined USE_POP || defined USE_IMAP
639 memcpy(d + *off, &nh, sizeof (HEADER));
640 *off += sizeof (HEADER);
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);
650 mutt_hcache_restore(const unsigned char *d, HEADER ** oh)
653 HEADER *h = mutt_new_header();
654 int convert = !Charset_is_utf8;
657 off += sizeof (validate);
660 off += sizeof (unsigned int);
662 memcpy(h, d + off, sizeof (HEADER));
663 off += sizeof (HEADER);
665 h->env = mutt_new_envelope();
666 restore_envelope(h->env, d, &off, convert);
668 h->content = mutt_new_body();
669 restore_body(h->content, d, &off, convert);
671 restore_char(&h->maildir_flags, d, &off, convert);
673 /* this is needed for maildir style mailboxes */
677 h->path = safe_strdup((*oh)->path);
678 mutt_free_header(oh);
685 mutt_hcache_fetch(header_cache_t *h, const char *filename,
686 size_t(*keylen) (const char *fn))
690 data = mutt_hcache_fetch_raw (h, filename, keylen);
692 if (!data || !crc_matches(data, h->crc))
702 mutt_hcache_fetch_raw (header_cache_t *h, const char *filename,
703 size_t(*keylen) (const char *fn))
706 char path[_POSIX_PATH_MAX];
726 if (filename[0] == '/')
729 mutt_hcache_dbt_init(&key, (void *) filename, keylen(filename));
730 mutt_hcache_dbt_empty_init(&data);
731 data.flags = DB_DBT_MALLOC;
733 h->db->get(h->db, NULL, &key, &data, 0);
737 strncpy(path, h->folder, sizeof (path));
738 safe_strcat(path, sizeof (path), filename);
740 ksize = strlen (h->folder) + keylen (path + strlen (h->folder));
743 data = vlget(h->db, path, ksize, NULL);
747 data = tcbdbget(h->db, path, ksize, &sp);
754 data = gdbm_fetch(h->db, key);
761 mutt_hcache_store(header_cache_t *h, const char *filename, HEADER * header,
762 unsigned int uidvalidity,
763 size_t(*keylen) (const char *fn))
772 data = mutt_hcache_dump(h, header, &dlen, uidvalidity);
773 ret = mutt_hcache_store_raw (h, filename, data, dlen, keylen);
781 mutt_hcache_store_raw (header_cache_t* h, const char* filename, void* data,
782 size_t dlen, size_t(*keylen) (const char* fn))
785 char path[_POSIX_PATH_MAX];
800 if (filename[0] == '/')
803 mutt_hcache_dbt_init(&key, (void *) filename, keylen(filename));
805 mutt_hcache_dbt_empty_init(&databuf);
806 databuf.flags = DB_DBT_USERMEM;
811 return h->db->put(h->db, NULL, &key, &databuf, 0);
813 strncpy(path, h->folder, sizeof (path));
814 safe_strcat(path, sizeof (path), filename);
816 ksize = strlen(h->folder) + keylen(path + strlen(h->folder));
819 return vlput(h->db, path, ksize, data, dlen, VL_DOVER);
821 return tcbdbput(h->db, path, ksize, data, dlen);
826 databuf.dsize = dlen;
829 return gdbm_store(h->db, key, databuf, GDBM_REPLACE);
833 static char* get_foldername(const char *folder)
836 char path[_POSIX_PATH_MAX];
839 mutt_encode_path (path, sizeof (path), folder);
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)
845 p = safe_malloc (PATH_MAX+1);
846 if (!realpath (path, p))
847 mutt_str_replace (&p, path);
849 p = safe_strdup (path);
856 hcache_open_qdbm (struct header_cache* h, const char* path)
858 int flags = VL_OWRITER | VL_OCREAT;
860 if (option(OPTHCACHECOMPRESS))
863 h->db = vlopen (path, flags, VL_CMPLEX);
871 mutt_hcache_close(header_cache_t *h)
882 mutt_hcache_delete(header_cache_t *h, const char *filename,
883 size_t(*keylen) (const char *fn))
885 char path[_POSIX_PATH_MAX];
891 strncpy(path, h->folder, sizeof (path));
892 safe_strcat(path, sizeof (path), filename);
894 ksize = strlen(h->folder) + keylen(path + strlen(h->folder));
896 return vlout(h->db, path, ksize);
901 hcache_open_tc (struct header_cache* h, const char* path)
904 if (option(OPTHCACHECOMPRESS))
905 tcbdbtune(h->db, 0, 0, 0, -1, -1, BDBTDEFLATE);
906 if (tcbdbopen(h->db, path, BDBOWRITER | BDBOCREAT))
916 mutt_hcache_close(header_cache_t *h)
928 mutt_hcache_delete(header_cache_t *h, const char *filename,
929 size_t(*keylen) (const char *fn))
931 char path[_POSIX_PATH_MAX];
937 strncpy(path, h->folder, sizeof (path));
938 safe_strcat(path, sizeof (path), filename);
940 ksize = strlen(h->folder) + keylen(path + strlen(h->folder));
942 return tcbdbout(h->db, path, ksize);
947 hcache_open_gdbm (struct header_cache* h, const char* path)
951 if (mutt_atoi (HeaderCachePageSize, &pagesize) < 0 || pagesize <= 0)
954 h->db = gdbm_open((char *) path, pagesize, GDBM_WRCREAT, 00600, NULL);
958 /* if rw failed try ro */
959 h->db = gdbm_open((char *) path, pagesize, GDBM_READER, 00600, NULL);
967 mutt_hcache_close(header_cache_t *h)
978 mutt_hcache_delete(header_cache_t *h, const char *filename,
979 size_t(*keylen) (const char *fn))
982 char path[_POSIX_PATH_MAX];
987 strncpy(path, h->folder, sizeof (path));
988 safe_strcat(path, sizeof (path), filename);
991 key.dsize = strlen(h->folder) + keylen(path + strlen(h->folder));
993 return gdbm_delete(h->db, key);
998 mutt_hcache_dbt_init(DBT * dbt, void *data, size_t len)
1001 dbt->size = dbt->ulen = len;
1002 dbt->dlen = dbt->doff = 0;
1003 dbt->flags = DB_DBT_USERMEM;
1007 mutt_hcache_dbt_empty_init(DBT * dbt)
1010 dbt->size = dbt->ulen = dbt->dlen = dbt->doff = 0;
1015 hcache_open_db4 (struct header_cache* h, const char* path)
1019 u_int32_t createflags = DB_CREATE;
1022 if (mutt_atoi (HeaderCachePageSize, &pagesize) < 0 || pagesize <= 0)
1025 snprintf (h->lockfile, _POSIX_PATH_MAX, "%s-lock-hack", path);
1027 h->fd = open (h->lockfile, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
1031 if (mx_lock_file (h->lockfile, h->fd, 1, 0, 5))
1034 ret = db_env_create (&h->env, 0);
1038 ret = (*h->env->open)(h->env, NULL, DB_INIT_MPOOL | DB_CREATE | DB_PRIVATE,
1043 ret = db_create (&h->db, h->env, 0);
1047 if (stat(path, &sb) != 0 && errno == ENOENT)
1049 createflags |= DB_EXCL;
1050 h->db->set_pagesize(h->db, pagesize);
1053 ret = (*h->db->open)(h->db, NULL, path, h->folder, DB_BTREE, createflags,
1061 h->db->close (h->db, 0);
1063 h->env->close (h->env, 0);
1065 mx_unlock_file (h->lockfile, h->fd, 0);
1068 unlink (h->lockfile);
1074 mutt_hcache_close(header_cache_t *h)
1079 h->db->close (h->db, 0);
1080 h->env->close (h->env, 0);
1081 mx_unlock_file (h->lockfile, h->fd, 0);
1083 unlink (h->lockfile);
1089 mutt_hcache_delete(header_cache_t *h, const char *filename,
1090 size_t(*keylen) (const char *fn))
1097 if (filename[0] == '/')
1100 mutt_hcache_dbt_init(&key, (void *) filename, keylen(filename));
1101 return h->db->del(h->db, NULL, &key, 0);
1106 mutt_hcache_open(const char *path, const char *folder, hcache_namer_t namer)
1108 struct header_cache *h = safe_calloc(1, sizeof (HEADER_CACHE));
1109 int (*hcache_open) (struct header_cache* h, const char* path);
1113 hcache_open = hcache_open_qdbm;
1115 hcache_open= hcache_open_tc;
1117 hcache_open = hcache_open_gdbm;
1119 hcache_open = hcache_open_db4;
1123 h->folder = get_foldername(folder);
1126 if (!path || path[0] == '\0')
1133 path = mutt_hcache_per_folder(path, h->folder, namer);
1135 if (!hcache_open (h, path))
1139 /* remove a possibly incompatible version */
1140 if (!stat (path, &sb) && !unlink (path))
1142 if (!hcache_open (h, path))
1153 const char *mutt_hcache_backend (void)
1155 return DB_VERSION_STRING;
1158 const char *mutt_hcache_backend (void)
1160 return gdbm_version;
1163 const char *mutt_hcache_backend (void)
1165 return "qdbm " _QDBM_VERSION;
1168 const char *mutt_hcache_backend (void)
1170 return "tokyocabinet " _TC_VERSION;