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