msgcache.c (25086B)
1 /* 2 * Claws Mail -- a GTK based, lightweight, and fast e-mail client 3 * Copyright (C) 1999-2025 the Claws Mail team & Hiroyuki Yamamoto 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 3 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 #include "defs.h" 21 22 #include <stdio.h> 23 24 #include <glib.h> 25 #include <glib/gi18n.h> 26 #include <sys/mman.h> 27 #include <sys/types.h> 28 #include <sys/stat.h> 29 30 #include <time.h> 31 32 #include "msgcache.h" 33 #include "utils.h" 34 #include "procmsg.h" 35 #include "codeconv.h" 36 #include "prefs_common.h" 37 #include "file-utils.h" 38 39 #if G_BYTE_ORDER == G_BIG_ENDIAN 40 #define bswap_32(x) \ 41 ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \ 42 (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24)) 43 44 #define MMAP_TO_GUINT32(x) \ 45 (((x[3]&0xff)) | \ 46 ((x[2]&0xff) << 8) | \ 47 ((x[1]&0xff) << 16) | \ 48 ((x[0]&0xff) << 24)) 49 50 #define MMAP_TO_GUINT32_SWAPPED(x) \ 51 (((x[0]&0xff)) | \ 52 ((x[1]&0xff) << 8) | \ 53 ((x[2]&0xff) << 16) | \ 54 ((x[3]&0xff) << 24)) 55 56 static gboolean msgcache_use_mmap_read = TRUE; 57 58 #else 59 #define bswap_32(x) (x) 60 61 #define MMAP_TO_GUINT32(x) \ 62 (((x[0]&0xff)) | \ 63 ((x[1]&0xff) << 8) | \ 64 ((x[2]&0xff) << 16) | \ 65 ((x[3]&0xff) << 24)) 66 67 #define MMAP_TO_GUINT32_SWAPPED(x) \ 68 (((x[0]&0xff)) | \ 69 ((x[1]&0xff) << 8) | \ 70 ((x[2]&0xff) << 16) | \ 71 ((x[3]&0xff) << 24)) 72 73 static gboolean msgcache_use_mmap_read = TRUE; 74 #endif 75 76 static gboolean swapping = TRUE; 77 78 typedef enum 79 { 80 DATA_READ, 81 DATA_WRITE, 82 DATA_APPEND 83 } DataOpenMode; 84 85 struct _MsgCache { 86 GHashTable *msgnum_table; 87 GHashTable *msgid_table; 88 guint memusage; 89 time_t last_access; 90 }; 91 92 typedef struct _StringConverter StringConverter; 93 struct _StringConverter { 94 gchar *(*convert) (StringConverter *converter, gchar *srcstr); 95 void (*free) (StringConverter *converter); 96 }; 97 98 typedef struct _StrdupConverter StrdupConverter; 99 struct _StrdupConverter { 100 StringConverter converter; 101 }; 102 103 typedef struct _CharsetConverter CharsetConverter; 104 struct _CharsetConverter { 105 StringConverter converter; 106 107 gchar *srccharset; 108 gchar *dstcharset; 109 }; 110 111 MsgCache *msgcache_new(void) 112 { 113 MsgCache *cache; 114 115 cache = g_new0(MsgCache, 1), 116 cache->msgnum_table = g_hash_table_new(g_int_hash, g_int_equal); 117 cache->msgid_table = g_hash_table_new(g_str_hash, g_str_equal); 118 cache->last_access = time(NULL); 119 120 return cache; 121 } 122 123 static gboolean msgcache_msginfo_free_func(gpointer num, gpointer msginfo, gpointer user_data) 124 { 125 procmsg_msginfo_free((MsgInfo **)&msginfo); 126 return TRUE; 127 } 128 129 void msgcache_destroy(MsgCache *cache) 130 { 131 g_hash_table_foreach_remove(cache->msgnum_table, msgcache_msginfo_free_func, NULL); 132 g_hash_table_destroy(cache->msgid_table); 133 g_hash_table_destroy(cache->msgnum_table); 134 g_free(cache); 135 } 136 137 void msgcache_add_msg(MsgCache *cache, MsgInfo *msginfo) 138 { 139 MsgInfo *newmsginfo; 140 141 newmsginfo = procmsg_msginfo_new_ref(msginfo); 142 g_hash_table_insert(cache->msgnum_table, &newmsginfo->msgnum, newmsginfo); 143 if(newmsginfo->msgid != NULL) 144 g_hash_table_insert(cache->msgid_table, newmsginfo->msgid, newmsginfo); 145 cache->memusage += procmsg_msginfo_memusage(msginfo); 146 cache->last_access = time(NULL); 147 148 msginfo->folder->cache_dirty = TRUE; 149 150 debug_print("Cache size: %d messages, %u bytes\n", g_hash_table_size(cache->msgnum_table), cache->memusage); 151 } 152 153 void msgcache_remove_msg(MsgCache *cache, guint msgnum) 154 { 155 MsgInfo *msginfo; 156 157 msginfo = (MsgInfo *) g_hash_table_lookup(cache->msgnum_table, &msgnum); 158 if(!msginfo) 159 return; 160 161 cache->memusage -= procmsg_msginfo_memusage(msginfo); 162 if(msginfo->msgid) 163 g_hash_table_remove(cache->msgid_table, msginfo->msgid); 164 g_hash_table_remove(cache->msgnum_table, &msginfo->msgnum); 165 166 msginfo->folder->cache_dirty = TRUE; 167 168 procmsg_msginfo_free(&msginfo); 169 cache->last_access = time(NULL); 170 171 172 debug_print("Cache size: %d messages, %u bytes\n", g_hash_table_size(cache->msgnum_table), cache->memusage); 173 } 174 175 void msgcache_update_msg(MsgCache *cache, MsgInfo *msginfo) 176 { 177 MsgInfo *oldmsginfo, *newmsginfo; 178 179 oldmsginfo = g_hash_table_lookup(cache->msgnum_table, &msginfo->msgnum); 180 if(oldmsginfo && oldmsginfo->msgid) 181 g_hash_table_remove(cache->msgid_table, oldmsginfo->msgid); 182 if (oldmsginfo) { 183 g_hash_table_remove(cache->msgnum_table, &oldmsginfo->msgnum); 184 cache->memusage -= procmsg_msginfo_memusage(oldmsginfo); 185 procmsg_msginfo_free(&oldmsginfo); 186 } 187 188 newmsginfo = procmsg_msginfo_new_ref(msginfo); 189 g_hash_table_insert(cache->msgnum_table, &newmsginfo->msgnum, newmsginfo); 190 if(newmsginfo->msgid) 191 g_hash_table_insert(cache->msgid_table, newmsginfo->msgid, newmsginfo); 192 cache->memusage += procmsg_msginfo_memusage(newmsginfo); 193 cache->last_access = time(NULL); 194 195 debug_print("Cache size: %d messages, %u bytes\n", g_hash_table_size(cache->msgnum_table), cache->memusage); 196 197 msginfo->folder->cache_dirty = TRUE; 198 199 return; 200 } 201 202 MsgInfo *msgcache_get_msg(MsgCache *cache, guint num) 203 { 204 MsgInfo *msginfo; 205 206 msginfo = g_hash_table_lookup(cache->msgnum_table, &num); 207 if(!msginfo) 208 return NULL; 209 cache->last_access = time(NULL); 210 211 return procmsg_msginfo_new_ref(msginfo); 212 } 213 214 MsgInfo *msgcache_get_msg_by_id(MsgCache *cache, const gchar *msgid) 215 { 216 MsgInfo *msginfo; 217 218 msginfo = g_hash_table_lookup(cache->msgid_table, msgid); 219 if(!msginfo) 220 return NULL; 221 cache->last_access = time(NULL); 222 223 return procmsg_msginfo_new_ref(msginfo); 224 } 225 226 static void msgcache_get_msg_list_func(gpointer key, gpointer value, gpointer user_data) 227 { 228 MsgInfoList **listptr = user_data; 229 MsgInfo *msginfo = value; 230 231 *listptr = g_slist_prepend(*listptr, procmsg_msginfo_new_ref(msginfo)); 232 } 233 234 MsgInfoList *msgcache_get_msg_list(MsgCache *cache) 235 { 236 MsgInfoList *msg_list = NULL; 237 g_hash_table_foreach((GHashTable *)cache->msgnum_table, msgcache_get_msg_list_func, (gpointer)&msg_list); 238 cache->last_access = time(NULL); 239 240 msg_list = g_slist_reverse(msg_list); 241 return msg_list; 242 } 243 244 time_t msgcache_get_last_access_time(MsgCache *cache) 245 { 246 247 return cache->last_access; 248 } 249 250 gint msgcache_get_memory_usage(MsgCache *cache) 251 { 252 253 return cache->memusage; 254 } 255 256 /* 257 * Cache saving functions 258 */ 259 260 #define READ_CACHE_DATA(data, fp, total_len) \ 261 { \ 262 if ((tmp_len = msgcache_read_cache_data_str(fp, &data, conv)) < 0) { \ 263 procmsg_msginfo_free(&msginfo); \ 264 error = TRUE; \ 265 goto bail_err; \ 266 } \ 267 total_len += tmp_len; \ 268 } 269 270 #define READ_CACHE_DATA_INT(n, fp) \ 271 { \ 272 guint32 idata; \ 273 size_t ni; \ 274 \ 275 if ((ni = fread(&idata, sizeof(idata), 1, fp)) != 1) { \ 276 g_warning("read_int: cache data corrupted, read %"G_GSIZE_FORMAT" of %"G_GSIZE_FORMAT" at " \ 277 "offset %ld", ni, sizeof(idata), ftell(fp)); \ 278 procmsg_msginfo_free(&msginfo); \ 279 error = TRUE; \ 280 goto bail_err; \ 281 } else \ 282 n = swapping ? bswap_32(idata) : (idata);\ 283 } 284 285 #define GET_CACHE_DATA_INT(n) \ 286 { \ 287 if (rem_len < 4) { \ 288 g_print("error at rem_len:%d\n", rem_len); \ 289 procmsg_msginfo_free(&msginfo); \ 290 error = TRUE; \ 291 goto bail_err; \ 292 } \ 293 n = (swapping ? (MMAP_TO_GUINT32_SWAPPED(walk_data)):(MMAP_TO_GUINT32(walk_data))); \ 294 walk_data += 4; rem_len -= 4; \ 295 } 296 297 #define GET_CACHE_DATA(data, total_len) \ 298 { \ 299 GET_CACHE_DATA_INT(tmp_len); \ 300 if (rem_len < tmp_len) { \ 301 g_print("error at rem_len:%d (tmp_len %d)\n", rem_len, tmp_len); \ 302 procmsg_msginfo_free(&msginfo); \ 303 error = TRUE; \ 304 goto bail_err; \ 305 } \ 306 if ((tmp_len = msgcache_get_cache_data_str(walk_data, &data, tmp_len, conv)) < 0) { \ 307 g_print("error at rem_len:%d\n", rem_len);\ 308 procmsg_msginfo_free(&msginfo); \ 309 error = TRUE; \ 310 goto bail_err; \ 311 } \ 312 total_len += tmp_len; \ 313 walk_data += tmp_len; rem_len -= tmp_len; \ 314 } 315 316 317 #define WRITE_CACHE_DATA_INT(n, fp) \ 318 { \ 319 guint32 idata; \ 320 \ 321 idata = (guint32)bswap_32(n); \ 322 if (fwrite(&idata, sizeof(idata), 1, fp) != 1) \ 323 w_err = 1; \ 324 wrote += 4; \ 325 } 326 327 #define PUT_CACHE_DATA_INT(n) \ 328 { \ 329 walk_data[0]=(((guint32)n)&0x000000ff); \ 330 walk_data[1]=(((guint32)n)&0x0000ff00)>>8; \ 331 walk_data[2]=(((guint32)n)&0x00ff0000)>>16; \ 332 walk_data[3]=(((guint32)n)&0xff000000)>>24; \ 333 walk_data += 4; \ 334 wrote += 4; \ 335 } 336 337 #define WRITE_CACHE_DATA(data, fp) \ 338 { \ 339 size_t len; \ 340 if (data == NULL) \ 341 len = 0; \ 342 else \ 343 len = strlen(data); \ 344 WRITE_CACHE_DATA_INT(len, fp); \ 345 if (w_err == 0 && len > 0) { \ 346 if (fwrite(data, 1, len, fp) != len) \ 347 w_err = 1; \ 348 wrote += len; \ 349 } \ 350 } 351 352 #define PUT_CACHE_DATA(data) \ 353 { \ 354 size_t len; \ 355 if (data == NULL) \ 356 len = 0; \ 357 else \ 358 len = strlen(data); \ 359 PUT_CACHE_DATA_INT(len); \ 360 if (len > 0) { \ 361 memcpy(walk_data, data, len); \ 362 walk_data += len; \ 363 wrote += len; \ 364 } \ 365 } 366 367 static FILE *msgcache_open_data_file(const gchar *file, guint version, 368 DataOpenMode mode, 369 gchar *buf, size_t buf_size) 370 { 371 FILE *fp; 372 gint32 data_ver = 0; 373 374 if (mode == DATA_WRITE) { 375 int w_err = 0, wrote = 0; 376 if ((fp = g_fopen(file, "wb")) == NULL) { 377 FILE_OP_ERROR(file, "g_fopen"); 378 return NULL; 379 } 380 WRITE_CACHE_DATA_INT(version, fp); 381 if (w_err != 0) { 382 g_warning("failed to write int"); 383 fclose(fp); 384 return NULL; 385 } 386 return fp; 387 } 388 389 /* check version */ 390 if ((fp = g_fopen(file, "rb")) == NULL) 391 debug_print("Mark/Cache file '%s' not found\n", file); 392 else { 393 if (buf && buf_size > 0) 394 setvbuf(fp, buf, _IOFBF, buf_size); 395 if (fread(&data_ver, sizeof(data_ver), 1, fp) != 1 || 396 version != bswap_32(data_ver)) { 397 g_message("%s: Mark/Cache version is different (%u != %u).\n", 398 file, bswap_32(data_ver), version); 399 fclose(fp); 400 fp = NULL; 401 } 402 data_ver = bswap_32(data_ver); 403 } 404 405 if (mode == DATA_READ) 406 return fp; 407 408 if (fp) { 409 /* reopen with append mode */ 410 fclose(fp); 411 if ((fp = g_fopen(file, "ab")) == NULL) 412 FILE_OP_ERROR(file, "g_fopen"); 413 } else { 414 /* open with overwrite mode if mark file doesn't exist or 415 version is different */ 416 fp = msgcache_open_data_file(file, version, DATA_WRITE, buf, 417 buf_size); 418 } 419 420 return fp; 421 } 422 423 static gint msgcache_read_cache_data_str(FILE *fp, gchar **str, 424 StringConverter *conv) 425 { 426 gchar *tmpstr = NULL; 427 size_t ni; 428 guint32 len; 429 430 *str = NULL; 431 if (!swapping) { 432 if ((ni = fread(&len, sizeof(len), 1, fp) != 1) || 433 len > G_MAXINT) { 434 g_warning("read_data_str: cache data (len) corrupted, read %"G_GSIZE_FORMAT 435 " of %"G_GSIZE_FORMAT" bytes at offset %ld", ni, sizeof(len), 436 ftell(fp)); 437 return -1; 438 } 439 } else { 440 if ((ni = fread(&len, sizeof(len), 1, fp) != 1) || 441 bswap_32(len) > G_MAXINT) { 442 g_warning("read_data_str: cache data (len) corrupted, read %"G_GSIZE_FORMAT 443 " of %"G_GSIZE_FORMAT" bytes at offset %ld", ni, sizeof(len), 444 ftell(fp)); 445 return -1; 446 } 447 len = bswap_32(len); 448 } 449 450 if (len == 0) 451 return 0; 452 453 tmpstr = g_try_malloc(len + 1); 454 455 if(tmpstr == NULL) { 456 return -1; 457 } 458 459 if ((ni = fread(tmpstr, 1, len, fp)) != len) { 460 g_warning("read_data_str: cache data corrupted, read %"G_GSIZE_FORMAT" of %u " 461 "bytes at offset %ld", 462 ni, len, ftell(fp)); 463 g_free(tmpstr); 464 return -1; 465 } 466 tmpstr[len] = 0; 467 468 if (conv != NULL) { 469 *str = conv->convert(conv, tmpstr); 470 g_free(tmpstr); 471 } else 472 *str = tmpstr; 473 474 return len; 475 } 476 477 static gint msgcache_get_cache_data_str(gchar *src, gchar **str, gint len, 478 StringConverter *conv) 479 { 480 gchar *tmpstr = NULL; 481 482 *str = NULL; 483 484 if (len == 0) 485 return 0; 486 487 if(len > 2*1024*1024) { 488 g_warning("read_data_str: refusing to allocate %d bytes", len); 489 return -1; 490 } 491 492 tmpstr = g_try_malloc(len + 1); 493 494 if(tmpstr == NULL) { 495 return -1; 496 } 497 498 memcpy(tmpstr, src, len); 499 tmpstr[len] = 0; 500 501 if (conv != NULL) { 502 *str = conv->convert(conv, tmpstr); 503 g_free(tmpstr); 504 } else 505 *str = tmpstr; 506 507 return len; 508 } 509 510 static gchar *strconv_charset_convert(StringConverter *conv, gchar *srcstr) 511 { 512 CharsetConverter *charsetconv = (CharsetConverter *) conv; 513 514 return conv_codeset_strdup(srcstr, charsetconv->srccharset, charsetconv->dstcharset); 515 } 516 517 static void strconv_charset_free(StringConverter *conv) 518 { 519 CharsetConverter *charsetconv = (CharsetConverter *) conv; 520 521 g_free(charsetconv->srccharset); 522 g_free(charsetconv->dstcharset); 523 } 524 525 MsgCache *msgcache_read_cache(FolderItem *item, const gchar *cache_file) 526 { 527 MsgCache *cache; 528 FILE *fp; 529 MsgInfo *msginfo; 530 MsgTmpFlags tmp_flags = 0; 531 gchar file_buf[BUFFSIZE]; 532 guint32 num; 533 guint refnum; 534 gboolean error = FALSE; 535 StringConverter *conv = NULL; 536 gchar *srccharset = NULL; 537 const gchar *dstcharset = NULL; 538 gchar *ref = NULL; 539 guint memusage = 0; 540 gint tmp_len = 0, map_len = -1; 541 char *cache_data = NULL; 542 struct stat st; 543 544 swapping = TRUE; 545 546 /* In case we can't open the mark file with MARK_VERSION, check if we can open it with the 547 * swapped MARK_VERSION. As msgcache_open_data_file swaps it too, if this succeeds, 548 * it means it's the old version (not little-endian) on a big-endian machine. The code has 549 * no effect on x86 as their file doesn't change. */ 550 551 if ((fp = msgcache_open_data_file 552 (cache_file, CACHE_VERSION, DATA_READ, file_buf, sizeof(file_buf))) == NULL) { 553 if ((fp = msgcache_open_data_file 554 (cache_file, bswap_32(CACHE_VERSION), DATA_READ, file_buf, sizeof(file_buf))) == NULL) 555 return NULL; 556 else 557 swapping = FALSE; 558 } 559 560 debug_print("\tReading %sswapped message cache from %s...\n", swapping?"":"un", cache_file); 561 562 if (folder_has_parent_of_type(item, F_QUEUE)) { 563 tmp_flags |= MSG_QUEUED; 564 } else if (folder_has_parent_of_type(item, F_DRAFT)) { 565 tmp_flags |= MSG_DRAFT; 566 } 567 568 if (msgcache_read_cache_data_str(fp, &srccharset, NULL) < 0) { 569 fclose(fp); 570 return NULL; 571 } 572 dstcharset = CS_UTF_8; 573 if (srccharset == NULL || dstcharset == NULL) { 574 conv = NULL; 575 } else if (strcmp(srccharset, dstcharset) == 0) { 576 debug_print("using Noop Converter\n"); 577 578 conv = NULL; 579 } else { 580 CharsetConverter *charsetconv; 581 582 debug_print("using CharsetConverter\n"); 583 584 charsetconv = g_new0(CharsetConverter, 1); 585 charsetconv->converter.convert = strconv_charset_convert; 586 charsetconv->converter.free = strconv_charset_free; 587 charsetconv->srccharset = g_strdup(srccharset); 588 charsetconv->dstcharset = g_strdup(dstcharset); 589 590 conv = (StringConverter *) charsetconv; 591 } 592 g_free(srccharset); 593 594 cache = msgcache_new(); 595 596 if (msgcache_use_mmap_read == TRUE) { 597 if (fstat(fileno(fp), &st) >= 0) 598 map_len = st.st_size; 599 else 600 map_len = -1; 601 if (map_len > 0) { 602 cache_data = mmap(NULL, map_len, PROT_READ, MAP_PRIVATE, fileno(fp), 0); 603 } 604 } else { 605 cache_data = NULL; 606 } 607 if (cache_data != NULL && cache_data != MAP_FAILED) { 608 int rem_len = map_len-ftell(fp); 609 char *walk_data = cache_data+ftell(fp); 610 611 while(rem_len > 0) { 612 msginfo = procmsg_msginfo_new(); 613 614 GET_CACHE_DATA_INT(num); 615 616 msginfo->msgnum = num; 617 memusage += sizeof(MsgInfo); 618 619 GET_CACHE_DATA_INT(msginfo->size); 620 GET_CACHE_DATA_INT(msginfo->mtime); 621 GET_CACHE_DATA_INT(msginfo->date_t); 622 GET_CACHE_DATA_INT(msginfo->flags.tmp_flags); 623 624 GET_CACHE_DATA(msginfo->fromname, memusage); 625 626 GET_CACHE_DATA(msginfo->date, memusage); 627 GET_CACHE_DATA(msginfo->from, memusage); 628 GET_CACHE_DATA(msginfo->to, memusage); 629 GET_CACHE_DATA(msginfo->cc, memusage); 630 GET_CACHE_DATA(msginfo->subject, memusage); 631 GET_CACHE_DATA(msginfo->msgid, memusage); 632 GET_CACHE_DATA(msginfo->inreplyto, memusage); 633 GET_CACHE_DATA(msginfo->xref, memusage); 634 635 GET_CACHE_DATA_INT(msginfo->planned_download); 636 GET_CACHE_DATA_INT(msginfo->total_size); 637 GET_CACHE_DATA_INT(refnum); 638 639 for (; refnum != 0; refnum--) { 640 ref = NULL; 641 642 GET_CACHE_DATA(ref, memusage); 643 644 if (ref) { 645 if (*ref) { 646 msginfo->references = 647 g_slist_prepend(msginfo->references, ref); 648 } else { 649 g_free(ref); 650 ref = NULL; 651 } 652 } 653 } 654 if (msginfo->references) 655 msginfo->references = 656 g_slist_reverse(msginfo->references); 657 658 msginfo->folder = item; 659 msginfo->flags.tmp_flags |= tmp_flags; 660 661 g_hash_table_insert(cache->msgnum_table, &msginfo->msgnum, msginfo); 662 if(msginfo->msgid) 663 g_hash_table_insert(cache->msgid_table, msginfo->msgid, msginfo); 664 } 665 } else { 666 while (fread(&num, sizeof(num), 1, fp) == 1) { 667 if (swapping) 668 num = bswap_32(num); 669 670 msginfo = procmsg_msginfo_new(); 671 msginfo->msgnum = num; 672 memusage += sizeof(MsgInfo); 673 674 READ_CACHE_DATA_INT(msginfo->size, fp); 675 READ_CACHE_DATA_INT(msginfo->mtime, fp); 676 READ_CACHE_DATA_INT(msginfo->date_t, fp); 677 READ_CACHE_DATA_INT(msginfo->flags.tmp_flags, fp); 678 679 READ_CACHE_DATA(msginfo->fromname, fp, memusage); 680 681 READ_CACHE_DATA(msginfo->date, fp, memusage); 682 READ_CACHE_DATA(msginfo->from, fp, memusage); 683 READ_CACHE_DATA(msginfo->to, fp, memusage); 684 READ_CACHE_DATA(msginfo->cc, fp, memusage); 685 READ_CACHE_DATA(msginfo->subject, fp, memusage); 686 READ_CACHE_DATA(msginfo->msgid, fp, memusage); 687 READ_CACHE_DATA(msginfo->inreplyto, fp, memusage); 688 READ_CACHE_DATA(msginfo->xref, fp, memusage); 689 690 READ_CACHE_DATA_INT(msginfo->planned_download, fp); 691 READ_CACHE_DATA_INT(msginfo->total_size, fp); 692 READ_CACHE_DATA_INT(refnum, fp); 693 694 for (; refnum != 0; refnum--) { 695 ref = NULL; 696 697 READ_CACHE_DATA(ref, fp, memusage); 698 699 if (ref) { 700 if (*ref) { 701 msginfo->references = 702 g_slist_prepend(msginfo->references, ref); 703 } else { 704 g_free(ref); 705 ref = NULL; 706 } 707 } 708 } 709 if (msginfo->references) 710 msginfo->references = 711 g_slist_reverse(msginfo->references); 712 713 msginfo->folder = item; 714 msginfo->flags.tmp_flags |= tmp_flags; 715 716 g_hash_table_insert(cache->msgnum_table, &msginfo->msgnum, msginfo); 717 if(msginfo->msgid) 718 g_hash_table_insert(cache->msgid_table, msginfo->msgid, msginfo); 719 } 720 } 721 bail_err: 722 if (cache_data != NULL && cache_data != MAP_FAILED) { 723 munmap(cache_data, map_len); 724 } 725 fclose(fp); 726 if (conv != NULL) { 727 if (conv->free != NULL) 728 conv->free(conv); 729 g_free(conv); 730 } 731 732 if(error) { 733 msgcache_destroy(cache); 734 return NULL; 735 } 736 737 cache->last_access = time(NULL); 738 cache->memusage = memusage; 739 740 debug_print("done. (%d items read)\n", g_hash_table_size(cache->msgnum_table)); 741 debug_print("Cache size: %d messages, %u bytes\n", g_hash_table_size(cache->msgnum_table), cache->memusage); 742 743 return cache; 744 } 745 746 void msgcache_read_mark(MsgCache *cache, const gchar *mark_file) 747 { 748 FILE *fp; 749 MsgInfo *msginfo; 750 MsgPermFlags perm_flags; 751 guint32 num; 752 gint map_len = -1; 753 char *cache_data = NULL; 754 struct stat st; 755 gboolean error = FALSE; 756 757 swapping = TRUE; 758 759 /* In case we can't open the mark file with MARK_VERSION, check if we can open it with the 760 * swapped MARK_VERSION. As msgcache_open_data_file swaps it too, if this succeeds, 761 * it means it's the old version (not little-endian) on a big-endian machine. The code has 762 * no effect on x86 as their file doesn't change. */ 763 764 if ((fp = msgcache_open_data_file(mark_file, MARK_VERSION, DATA_READ, NULL, 0)) == NULL) { 765 /* see if it isn't swapped ? */ 766 if ((fp = msgcache_open_data_file(mark_file, bswap_32(MARK_VERSION), DATA_READ, NULL, 0)) == NULL) 767 return; 768 else 769 swapping = FALSE; /* yay */ 770 } 771 debug_print("reading %sswapped mark file.\n", swapping?"":"un"); 772 773 if (msgcache_use_mmap_read) { 774 if (fstat(fileno(fp), &st) >= 0) 775 map_len = st.st_size; 776 else 777 map_len = -1; 778 if (map_len > 0) { 779 cache_data = mmap(NULL, map_len, PROT_READ, MAP_PRIVATE, fileno(fp), 0); 780 } 781 } else { 782 cache_data = NULL; 783 } 784 if (cache_data != NULL && cache_data != MAP_FAILED) { 785 int rem_len = map_len-ftell(fp); 786 char *walk_data = cache_data+ftell(fp); 787 788 while(rem_len > 0) { 789 GET_CACHE_DATA_INT(num); 790 GET_CACHE_DATA_INT(perm_flags); 791 msginfo = g_hash_table_lookup(cache->msgnum_table, &num); 792 if(msginfo) { 793 msginfo->flags.perm_flags = perm_flags; 794 } 795 } 796 } else { 797 while (fread(&num, sizeof(num), 1, fp) == 1) { 798 if (swapping) 799 num = bswap_32(num); 800 if (fread(&perm_flags, sizeof(perm_flags), 1, fp) != 1) { 801 error = TRUE; 802 break; 803 } 804 if (swapping) 805 perm_flags = bswap_32(perm_flags); 806 msginfo = g_hash_table_lookup(cache->msgnum_table, &num); 807 if(msginfo) { 808 msginfo->flags.perm_flags = perm_flags; 809 } 810 } 811 } 812 bail_err: 813 if (cache_data != NULL && cache_data != MAP_FAILED) { 814 munmap(cache_data, map_len); 815 } 816 fclose(fp); 817 if (error) { 818 debug_print("error reading cache mark from %s\n", mark_file); 819 } 820 } 821 822 static int msgcache_write_cache(MsgInfo *msginfo, FILE *fp) 823 { 824 MsgTmpFlags flags = msginfo->flags.tmp_flags & MSG_CACHED_FLAG_MASK; 825 GSList *cur; 826 int w_err = 0, wrote = 0; 827 828 WRITE_CACHE_DATA_INT(msginfo->msgnum, fp); 829 WRITE_CACHE_DATA_INT(msginfo->size, fp); 830 WRITE_CACHE_DATA_INT(msginfo->mtime, fp); 831 WRITE_CACHE_DATA_INT(msginfo->date_t, fp); 832 WRITE_CACHE_DATA_INT(flags, fp); 833 834 WRITE_CACHE_DATA(msginfo->fromname, fp); 835 836 WRITE_CACHE_DATA(msginfo->date, fp); 837 WRITE_CACHE_DATA(msginfo->from, fp); 838 WRITE_CACHE_DATA(msginfo->to, fp); 839 WRITE_CACHE_DATA(msginfo->cc, fp); 840 WRITE_CACHE_DATA(msginfo->subject, fp); 841 WRITE_CACHE_DATA(msginfo->msgid, fp); 842 WRITE_CACHE_DATA(msginfo->inreplyto, fp); 843 WRITE_CACHE_DATA(msginfo->xref, fp); 844 WRITE_CACHE_DATA_INT(msginfo->planned_download, fp); 845 WRITE_CACHE_DATA_INT(msginfo->total_size, fp); 846 847 WRITE_CACHE_DATA_INT(g_slist_length(msginfo->references), fp); 848 849 for (cur = msginfo->references; cur != NULL; cur = cur->next) { 850 WRITE_CACHE_DATA((gchar *)cur->data, fp); 851 } 852 return w_err ? -1 : wrote; 853 } 854 855 static int msgcache_write_flags(MsgInfo *msginfo, FILE *fp) 856 { 857 MsgPermFlags flags = msginfo->flags.perm_flags; 858 int w_err = 0, wrote = 0; 859 WRITE_CACHE_DATA_INT(msginfo->msgnum, fp); 860 WRITE_CACHE_DATA_INT(flags, fp); 861 return w_err ? -1 : wrote; 862 } 863 864 struct write_fps 865 { 866 FILE *cache_fp; 867 FILE *mark_fp; 868 FILE *tags_fp; 869 int error; 870 guint cache_size; 871 guint mark_size; 872 guint tags_size; 873 }; 874 875 static void msgcache_write_func(gpointer key, gpointer value, gpointer user_data) 876 { 877 MsgInfo *msginfo; 878 struct write_fps *write_fps; 879 int tmp; 880 881 msginfo = (MsgInfo *)value; 882 write_fps = user_data; 883 884 if (write_fps->cache_fp) { 885 tmp = msgcache_write_cache(msginfo, write_fps->cache_fp); 886 if (tmp < 0) 887 write_fps->error = 1; 888 else 889 write_fps->cache_size += tmp; 890 } 891 if (write_fps->mark_fp) { 892 tmp = msgcache_write_flags(msginfo, write_fps->mark_fp); 893 if (tmp < 0) 894 write_fps->error = 1; 895 else 896 write_fps->mark_size += tmp; 897 } 898 } 899 900 gint msgcache_write(const gchar *cache_file, const gchar *mark_file, const gchar *tags_file, MsgCache *cache) 901 { 902 struct write_fps write_fps; 903 gchar *new_cache = NULL, *new_mark = NULL; 904 int w_err = 0, wrote = 0; 905 906 if (cache_file) 907 new_cache = g_strconcat(cache_file, ".new", NULL); 908 if (mark_file) 909 new_mark = g_strconcat(mark_file, ".new", NULL); 910 911 write_fps.error = 0; 912 write_fps.cache_size = 0; 913 write_fps.mark_size = 0; 914 write_fps.tags_size = 0; 915 916 /* open files and write headers */ 917 918 if (cache_file) { 919 write_fps.cache_fp = msgcache_open_data_file(new_cache, CACHE_VERSION, 920 DATA_WRITE, NULL, 0); 921 if (write_fps.cache_fp == NULL) { 922 g_free(new_cache); 923 g_free(new_mark); 924 return -1; 925 } 926 WRITE_CACHE_DATA(CS_UTF_8, write_fps.cache_fp); 927 } else { 928 write_fps.cache_fp = NULL; 929 } 930 931 if (w_err != 0) { 932 g_warning("failed to write charset"); 933 if (write_fps.cache_fp) 934 fclose(write_fps.cache_fp); 935 unlink(new_cache); 936 g_free(new_cache); 937 g_free(new_mark); 938 return -1; 939 } 940 941 if (mark_file) { 942 write_fps.mark_fp = msgcache_open_data_file(new_mark, MARK_VERSION, 943 DATA_WRITE, NULL, 0); 944 if (write_fps.mark_fp == NULL) { 945 if (write_fps.cache_fp) 946 fclose(write_fps.cache_fp); 947 unlink(new_cache); 948 g_free(new_cache); 949 g_free(new_mark); 950 return -1; 951 } 952 } else { 953 write_fps.mark_fp = NULL; 954 } 955 956 write_fps.tags_fp = NULL; 957 958 /* headers written, note file size */ 959 if (write_fps.cache_fp) 960 write_fps.cache_size = ftell(write_fps.cache_fp); 961 if (write_fps.mark_fp) 962 write_fps.mark_size = ftell(write_fps.mark_fp); 963 964 /* write data to the files */ 965 g_hash_table_foreach(cache->msgnum_table, msgcache_write_func, (gpointer)&write_fps); 966 967 /* close files */ 968 if (write_fps.cache_fp) 969 write_fps.error |= (fclose(write_fps.cache_fp) != 0); 970 if (write_fps.mark_fp) 971 write_fps.error |= (fclose(write_fps.mark_fp) != 0); 972 973 if (write_fps.error != 0) { 974 /* in case of error, forget all */ 975 unlink(new_cache); 976 unlink(new_mark); 977 g_free(new_cache); 978 g_free(new_mark); 979 return -1; 980 } else { 981 /* switch files */ 982 if (cache_file) 983 rename(new_cache, cache_file); 984 if (mark_file) 985 rename(new_mark, mark_file); 986 cache->last_access = time(NULL); 987 } 988 989 g_free(new_cache); 990 g_free(new_mark); 991 debug_print("msgcache_write() done.\n"); 992 return 0; 993 } 994