update en_GB translation
[claws.git] / src / msgcache.c
blobfd0739773c7abd3a5ae564e1da3351a6867f72fd
1 /*
2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2012 Hiroyuki Yamamoto & The Claws Mail Team
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.
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.
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/>.
20 #ifdef HAVE_CONFIG_H
21 # include "config.h"
22 #include "claws-features.h"
23 #endif
25 #include "defs.h"
27 #include <stdio.h>
29 #include <glib.h>
30 #include <glib/gi18n.h>
31 #ifdef _WIN32
32 # include <w32lib.h>
33 # define MAP_FAILED ((char *) -1)
34 #else
35 # include <sys/mman.h>
36 #endif
37 #include <sys/types.h>
38 #include <sys/stat.h>
40 #include <time.h>
42 #include "msgcache.h"
43 #include "utils.h"
44 #include "procmsg.h"
45 #include "codeconv.h"
46 #include "timing.h"
47 #include "tags.h"
48 #include "prefs_common.h"
49 #include "file-utils.h"
51 #if G_BYTE_ORDER == G_BIG_ENDIAN
52 #define bswap_32(x) \
53 ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \
54 (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24))
56 #define MMAP_TO_GUINT32(x) \
57 (((x[3]&0xff)) | \
58 ((x[2]&0xff) << 8) | \
59 ((x[1]&0xff) << 16) | \
60 ((x[0]&0xff) << 24))
62 #define MMAP_TO_GUINT32_SWAPPED(x) \
63 (((x[0]&0xff)) | \
64 ((x[1]&0xff) << 8) | \
65 ((x[2]&0xff) << 16) | \
66 ((x[3]&0xff) << 24))
68 static gboolean msgcache_use_mmap_read = TRUE;
70 #else
71 #define bswap_32(x) (x)
73 #define MMAP_TO_GUINT32(x) \
74 (((x[0]&0xff)) | \
75 ((x[1]&0xff) << 8) | \
76 ((x[2]&0xff) << 16) | \
77 ((x[3]&0xff) << 24))
79 #define MMAP_TO_GUINT32_SWAPPED(x) \
80 (((x[0]&0xff)) | \
81 ((x[1]&0xff) << 8) | \
82 ((x[2]&0xff) << 16) | \
83 ((x[3]&0xff) << 24))
85 static gboolean msgcache_use_mmap_read = TRUE;
86 #endif
88 static gboolean swapping = TRUE;
90 typedef enum
92 DATA_READ,
93 DATA_WRITE,
94 DATA_APPEND
95 } DataOpenMode;
97 struct _MsgCache {
98 GHashTable *msgnum_table;
99 GHashTable *msgid_table;
100 guint memusage;
101 time_t last_access;
104 typedef struct _StringConverter StringConverter;
105 struct _StringConverter {
106 gchar *(*convert) (StringConverter *converter, gchar *srcstr);
107 void (*free) (StringConverter *converter);
110 typedef struct _StrdupConverter StrdupConverter;
111 struct _StrdupConverter {
112 StringConverter converter;
115 typedef struct _CharsetConverter CharsetConverter;
116 struct _CharsetConverter {
117 StringConverter converter;
119 gchar *srccharset;
120 gchar *dstcharset;
123 MsgCache *msgcache_new(void)
125 MsgCache *cache;
127 cache = g_new0(MsgCache, 1),
128 cache->msgnum_table = g_hash_table_new(g_int_hash, g_int_equal);
129 cache->msgid_table = g_hash_table_new(g_str_hash, g_str_equal);
130 cache->last_access = time(NULL);
132 return cache;
135 static gboolean msgcache_msginfo_free_func(gpointer num, gpointer msginfo, gpointer user_data)
137 procmsg_msginfo_free((MsgInfo **)&msginfo);
138 return TRUE;
141 void msgcache_destroy(MsgCache *cache)
143 cm_return_if_fail(cache != NULL);
145 g_hash_table_foreach_remove(cache->msgnum_table, msgcache_msginfo_free_func, NULL);
146 g_hash_table_destroy(cache->msgid_table);
147 g_hash_table_destroy(cache->msgnum_table);
148 g_free(cache);
151 void msgcache_add_msg(MsgCache *cache, MsgInfo *msginfo)
153 MsgInfo *newmsginfo;
155 cm_return_if_fail(cache != NULL);
156 cm_return_if_fail(msginfo != NULL);
158 newmsginfo = procmsg_msginfo_new_ref(msginfo);
159 g_hash_table_insert(cache->msgnum_table, &newmsginfo->msgnum, newmsginfo);
160 if(newmsginfo->msgid != NULL)
161 g_hash_table_insert(cache->msgid_table, newmsginfo->msgid, newmsginfo);
162 cache->memusage += procmsg_msginfo_memusage(msginfo);
163 cache->last_access = time(NULL);
165 msginfo->folder->cache_dirty = TRUE;
167 debug_print("Cache size: %d messages, %u bytes\n", g_hash_table_size(cache->msgnum_table), cache->memusage);
170 void msgcache_remove_msg(MsgCache *cache, guint msgnum)
172 MsgInfo *msginfo;
174 cm_return_if_fail(cache != NULL);
176 msginfo = (MsgInfo *) g_hash_table_lookup(cache->msgnum_table, &msgnum);
177 if(!msginfo)
178 return;
180 cache->memusage -= procmsg_msginfo_memusage(msginfo);
181 if(msginfo->msgid)
182 g_hash_table_remove(cache->msgid_table, msginfo->msgid);
183 g_hash_table_remove(cache->msgnum_table, &msginfo->msgnum);
185 msginfo->folder->cache_dirty = TRUE;
187 procmsg_msginfo_free(&msginfo);
188 cache->last_access = time(NULL);
191 debug_print("Cache size: %d messages, %u bytes\n", g_hash_table_size(cache->msgnum_table), cache->memusage);
194 void msgcache_update_msg(MsgCache *cache, MsgInfo *msginfo)
196 MsgInfo *oldmsginfo, *newmsginfo;
198 cm_return_if_fail(cache != NULL);
199 cm_return_if_fail(msginfo != NULL);
201 oldmsginfo = g_hash_table_lookup(cache->msgnum_table, &msginfo->msgnum);
202 if(oldmsginfo && oldmsginfo->msgid)
203 g_hash_table_remove(cache->msgid_table, oldmsginfo->msgid);
204 if (oldmsginfo) {
205 g_hash_table_remove(cache->msgnum_table, &oldmsginfo->msgnum);
206 cache->memusage -= procmsg_msginfo_memusage(oldmsginfo);
207 procmsg_msginfo_free(&oldmsginfo);
210 newmsginfo = procmsg_msginfo_new_ref(msginfo);
211 g_hash_table_insert(cache->msgnum_table, &newmsginfo->msgnum, newmsginfo);
212 if(newmsginfo->msgid)
213 g_hash_table_insert(cache->msgid_table, newmsginfo->msgid, newmsginfo);
214 cache->memusage += procmsg_msginfo_memusage(newmsginfo);
215 cache->last_access = time(NULL);
217 debug_print("Cache size: %d messages, %u bytes\n", g_hash_table_size(cache->msgnum_table), cache->memusage);
219 msginfo->folder->cache_dirty = TRUE;
221 return;
224 MsgInfo *msgcache_get_msg(MsgCache *cache, guint num)
226 MsgInfo *msginfo;
228 cm_return_val_if_fail(cache != NULL, NULL);
230 msginfo = g_hash_table_lookup(cache->msgnum_table, &num);
231 if(!msginfo)
232 return NULL;
233 cache->last_access = time(NULL);
235 return procmsg_msginfo_new_ref(msginfo);
238 MsgInfo *msgcache_get_msg_by_id(MsgCache *cache, const gchar *msgid)
240 MsgInfo *msginfo;
242 cm_return_val_if_fail(cache != NULL, NULL);
243 cm_return_val_if_fail(msgid != NULL, NULL);
245 msginfo = g_hash_table_lookup(cache->msgid_table, msgid);
246 if(!msginfo)
247 return NULL;
248 cache->last_access = time(NULL);
250 return procmsg_msginfo_new_ref(msginfo);
253 static void msgcache_get_msg_list_func(gpointer key, gpointer value, gpointer user_data)
255 MsgInfoList **listptr = user_data;
256 MsgInfo *msginfo = value;
258 *listptr = g_slist_prepend(*listptr, procmsg_msginfo_new_ref(msginfo));
261 MsgInfoList *msgcache_get_msg_list(MsgCache *cache)
263 MsgInfoList *msg_list = NULL;
264 START_TIMING("");
265 cm_return_val_if_fail(cache != NULL, NULL);
267 g_hash_table_foreach((GHashTable *)cache->msgnum_table, msgcache_get_msg_list_func, (gpointer)&msg_list);
268 cache->last_access = time(NULL);
270 msg_list = g_slist_reverse(msg_list);
271 END_TIMING();
272 return msg_list;
275 time_t msgcache_get_last_access_time(MsgCache *cache)
277 cm_return_val_if_fail(cache != NULL, 0);
279 return cache->last_access;
282 gint msgcache_get_memory_usage(MsgCache *cache)
284 cm_return_val_if_fail(cache != NULL, 0);
286 return cache->memusage;
290 * Cache saving functions
293 #define READ_CACHE_DATA(data, fp, total_len) \
295 if ((tmp_len = msgcache_read_cache_data_str(fp, &data, conv)) < 0) { \
296 procmsg_msginfo_free(&msginfo); \
297 error = TRUE; \
298 goto bail_err; \
300 total_len += tmp_len; \
303 #define READ_CACHE_DATA_INT(n, fp) \
305 guint32 idata; \
306 size_t ni; \
308 if ((ni = claws_fread(&idata, sizeof(idata), 1, fp)) != 1) { \
309 g_warning("read_int: Cache data corrupted, read %"G_GSIZE_FORMAT" of %"G_GSIZE_FORMAT" at " \
310 "offset %ld", ni, sizeof(idata), ftell(fp)); \
311 procmsg_msginfo_free(&msginfo); \
312 error = TRUE; \
313 goto bail_err; \
314 } else \
315 n = swapping ? bswap_32(idata) : (idata);\
318 #define GET_CACHE_DATA_INT(n) \
320 if (rem_len < 4) { \
321 g_print("error at rem_len:%d\n", rem_len); \
322 error = TRUE; \
323 goto bail_err; \
325 n = (swapping ? (MMAP_TO_GUINT32_SWAPPED(walk_data)):(MMAP_TO_GUINT32(walk_data))); \
326 walk_data += 4; rem_len -= 4; \
329 #define GET_CACHE_DATA(data, total_len) \
331 GET_CACHE_DATA_INT(tmp_len); \
332 if (rem_len < tmp_len) { \
333 g_print("error at rem_len:%d (tmp_len %d)\n", rem_len, tmp_len); \
334 error = TRUE; \
335 goto bail_err; \
337 if ((tmp_len = msgcache_get_cache_data_str(walk_data, &data, tmp_len, conv)) < 0) { \
338 g_print("error at rem_len:%d\n", rem_len);\
339 procmsg_msginfo_free(&msginfo); \
340 error = TRUE; \
341 goto bail_err; \
343 total_len += tmp_len; \
344 walk_data += tmp_len; rem_len -= tmp_len; \
348 #define WRITE_CACHE_DATA_INT(n, fp) \
350 guint32 idata; \
352 idata = (guint32)bswap_32(n); \
353 if (claws_fwrite(&idata, sizeof(idata), 1, fp) != 1) \
354 w_err = 1; \
355 wrote += 4; \
358 #define PUT_CACHE_DATA_INT(n) \
360 walk_data[0]=(((guint32)n)&0x000000ff); \
361 walk_data[1]=(((guint32)n)&0x0000ff00)>>8; \
362 walk_data[2]=(((guint32)n)&0x00ff0000)>>16; \
363 walk_data[3]=(((guint32)n)&0xff000000)>>24; \
364 walk_data += 4; \
365 wrote += 4; \
368 #define WRITE_CACHE_DATA(data, fp) \
370 size_t len; \
371 if (data == NULL) \
372 len = 0; \
373 else \
374 len = strlen(data); \
375 WRITE_CACHE_DATA_INT(len, fp); \
376 if (w_err == 0 && len > 0) { \
377 if (claws_fwrite(data, 1, len, fp) != len) \
378 w_err = 1; \
379 wrote += len; \
383 #define PUT_CACHE_DATA(data) \
385 size_t len; \
386 if (data == NULL) \
387 len = 0; \
388 else \
389 len = strlen(data); \
390 PUT_CACHE_DATA_INT(len); \
391 if (len > 0) { \
392 memcpy(walk_data, data, len); \
393 walk_data += len; \
394 wrote += len; \
398 static FILE *msgcache_open_data_file(const gchar *file, guint version,
399 DataOpenMode mode,
400 gchar *buf, size_t buf_size)
402 FILE *fp;
403 gint32 data_ver;
405 cm_return_val_if_fail(file != NULL, NULL);
407 if (mode == DATA_WRITE) {
408 int w_err = 0, wrote = 0;
409 if ((fp = claws_fopen(file, "wb")) == NULL) {
410 FILE_OP_ERROR(file, "claws_fopen");
411 return NULL;
413 if (change_file_mode_rw(fp, file) < 0)
414 FILE_OP_ERROR(file, "chmod");
416 WRITE_CACHE_DATA_INT(version, fp);
417 if (w_err != 0) {
418 g_warning("failed to write int");
419 claws_fclose(fp);
420 return NULL;
422 return fp;
425 /* check version */
426 if ((fp = claws_fopen(file, "rb")) == NULL)
427 debug_print("Mark/Cache file '%s' not found\n", file);
428 else {
429 if (buf && buf_size > 0)
430 setvbuf(fp, buf, _IOFBF, buf_size);
431 if (claws_fread(&data_ver, sizeof(data_ver), 1, fp) != 1 ||
432 version != bswap_32(data_ver)) {
433 g_message("%s: Mark/Cache version is different (%u != %u).\n",
434 file, bswap_32(data_ver), version);
435 claws_fclose(fp);
436 fp = NULL;
438 data_ver = bswap_32(data_ver);
441 if (mode == DATA_READ)
442 return fp;
444 if (fp) {
445 /* reopen with append mode */
446 claws_fclose(fp);
447 if ((fp = claws_fopen(file, "ab")) == NULL)
448 FILE_OP_ERROR(file, "claws_fopen");
449 } else {
450 /* open with overwrite mode if mark file doesn't exist or
451 version is different */
452 fp = msgcache_open_data_file(file, version, DATA_WRITE, buf,
453 buf_size);
456 return fp;
459 static gint msgcache_read_cache_data_str(FILE *fp, gchar **str,
460 StringConverter *conv)
462 gchar *tmpstr = NULL;
463 size_t ni;
464 guint32 len;
466 *str = NULL;
467 if (!swapping) {
468 if ((ni = claws_fread(&len, sizeof(len), 1, fp) != 1) ||
469 len > G_MAXINT) {
470 g_warning("read_data_str: Cache data (len) corrupted, read %"G_GSIZE_FORMAT
471 " of %"G_GSIZE_FORMAT" bytes at offset %ld", ni, sizeof(len),
472 ftell(fp));
473 return -1;
475 } else {
476 if ((ni = claws_fread(&len, sizeof(len), 1, fp) != 1) ||
477 bswap_32(len) > G_MAXINT) {
478 g_warning("read_data_str: Cache data (len) corrupted, read %"G_GSIZE_FORMAT
479 " of %"G_GSIZE_FORMAT" bytes at offset %ld", ni, sizeof(len),
480 ftell(fp));
481 return -1;
483 len = bswap_32(len);
486 if (len == 0)
487 return 0;
489 tmpstr = g_try_malloc(len + 1);
491 if(tmpstr == NULL) {
492 return -1;
495 if ((ni = claws_fread(tmpstr, 1, len, fp)) != len) {
496 g_warning("read_data_str: Cache data corrupted, read %"G_GSIZE_FORMAT" of %u "
497 "bytes at offset %ld",
498 ni, len, ftell(fp));
499 g_free(tmpstr);
500 return -1;
502 tmpstr[len] = 0;
504 if (conv != NULL) {
505 *str = conv->convert(conv, tmpstr);
506 g_free(tmpstr);
507 } else
508 *str = tmpstr;
510 return len;
513 static gint msgcache_get_cache_data_str(gchar *src, gchar **str, gint len,
514 StringConverter *conv)
516 gchar *tmpstr = NULL;
518 *str = NULL;
520 if (len == 0)
521 return 0;
523 if(len > 2*1024*1024) {
524 g_warning("read_data_str: refusing to allocate %d bytes.", len);
525 return -1;
528 tmpstr = g_try_malloc(len + 1);
530 if(tmpstr == NULL) {
531 return -1;
534 memcpy(tmpstr, src, len);
535 tmpstr[len] = 0;
537 if (conv != NULL) {
538 *str = conv->convert(conv, tmpstr);
539 g_free(tmpstr);
540 } else
541 *str = tmpstr;
543 return len;
546 static gchar *strconv_charset_convert(StringConverter *conv, gchar *srcstr)
548 CharsetConverter *charsetconv = (CharsetConverter *) conv;
550 return conv_codeset_strdup(srcstr, charsetconv->srccharset, charsetconv->dstcharset);
553 static void strconv_charset_free(StringConverter *conv)
555 CharsetConverter *charsetconv = (CharsetConverter *) conv;
557 g_free(charsetconv->srccharset);
558 g_free(charsetconv->dstcharset);
561 MsgCache *msgcache_read_cache(FolderItem *item, const gchar *cache_file)
563 MsgCache *cache;
564 FILE *fp;
565 MsgInfo *msginfo;
566 MsgTmpFlags tmp_flags = 0;
567 gchar file_buf[BUFFSIZE];
568 guint32 num;
569 guint refnum;
570 gboolean error = FALSE;
571 StringConverter *conv = NULL;
572 gchar *srccharset = NULL;
573 const gchar *dstcharset = NULL;
574 gchar *ref = NULL;
575 guint memusage = 0;
576 gint tmp_len = 0, map_len = -1;
577 char *cache_data = NULL;
578 struct stat st;
580 cm_return_val_if_fail(cache_file != NULL, NULL);
581 cm_return_val_if_fail(item != NULL, NULL);
583 swapping = TRUE;
585 /* In case we can't open the mark file with MARK_VERSION, check if we can open it with the
586 * swapped MARK_VERSION. As msgcache_open_data_file swaps it too, if this succeeds,
587 * it means it's the old version (not little-endian) on a big-endian machine. The code has
588 * no effect on x86 as their file doesn't change. */
590 if ((fp = msgcache_open_data_file
591 (cache_file, CACHE_VERSION, DATA_READ, file_buf, sizeof(file_buf))) == NULL) {
592 if ((fp = msgcache_open_data_file
593 (cache_file, bswap_32(CACHE_VERSION), DATA_READ, file_buf, sizeof(file_buf))) == NULL)
594 return NULL;
595 else
596 swapping = FALSE;
599 debug_print("\tReading %sswapped message cache from %s...\n", swapping?"":"un", cache_file);
601 if (folder_has_parent_of_type(item, F_QUEUE)) {
602 tmp_flags |= MSG_QUEUED;
603 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
604 tmp_flags |= MSG_DRAFT;
607 if (msgcache_read_cache_data_str(fp, &srccharset, NULL) < 0) {
608 claws_fclose(fp);
609 return NULL;
611 dstcharset = CS_UTF_8;
612 if (srccharset == NULL || dstcharset == NULL) {
613 conv = NULL;
614 } else if (strcmp(srccharset, dstcharset) == 0) {
615 debug_print("using Noop Converter\n");
617 conv = NULL;
618 } else {
619 CharsetConverter *charsetconv;
621 debug_print("using CharsetConverter\n");
623 charsetconv = g_new0(CharsetConverter, 1);
624 charsetconv->converter.convert = strconv_charset_convert;
625 charsetconv->converter.free = strconv_charset_free;
626 charsetconv->srccharset = g_strdup(srccharset);
627 charsetconv->dstcharset = g_strdup(dstcharset);
629 conv = (StringConverter *) charsetconv;
631 g_free(srccharset);
633 cache = msgcache_new();
635 if (msgcache_use_mmap_read == TRUE) {
636 if (fstat(fileno(fp), &st) >= 0)
637 map_len = st.st_size;
638 else
639 map_len = -1;
640 if (map_len > 0) {
641 #ifdef G_OS_WIN32
642 cache_data = NULL;
643 HANDLE hFile, hMapping;
644 hFile = (HANDLE) _get_osfhandle (fileno(fp));
645 if (hFile == (HANDLE) -1)
646 goto w32_fail;
647 hMapping = CreateFileMapping(hFile, NULL, PAGE_WRITECOPY, 0, 0, NULL);
648 if (!hMapping)
649 goto w32_fail;
650 cache_data = (unsigned char *)MapViewOfFile(hMapping, FILE_MAP_COPY, 0, 0, 0);
651 CloseHandle (hMapping);
652 w32_fail:
654 #else
655 cache_data = mmap(NULL, map_len, PROT_READ, MAP_PRIVATE, fileno(fp), 0);
656 #endif
658 } else {
659 cache_data = NULL;
661 if (cache_data != NULL && cache_data != MAP_FAILED) {
662 int rem_len = map_len-ftell(fp);
663 char *walk_data = cache_data+ftell(fp);
665 while(rem_len > 0) {
666 GET_CACHE_DATA_INT(num);
668 msginfo = procmsg_msginfo_new();
669 msginfo->msgnum = num;
670 memusage += sizeof(MsgInfo);
672 GET_CACHE_DATA_INT(msginfo->size);
673 GET_CACHE_DATA_INT(msginfo->mtime);
674 GET_CACHE_DATA_INT(msginfo->date_t);
675 GET_CACHE_DATA_INT(msginfo->flags.tmp_flags);
677 GET_CACHE_DATA(msginfo->fromname, memusage);
679 GET_CACHE_DATA(msginfo->date, memusage);
680 GET_CACHE_DATA(msginfo->from, memusage);
681 GET_CACHE_DATA(msginfo->to, memusage);
682 GET_CACHE_DATA(msginfo->cc, memusage);
683 GET_CACHE_DATA(msginfo->newsgroups, memusage);
684 GET_CACHE_DATA(msginfo->subject, memusage);
685 GET_CACHE_DATA(msginfo->msgid, memusage);
686 GET_CACHE_DATA(msginfo->inreplyto, memusage);
687 GET_CACHE_DATA(msginfo->xref, memusage);
689 GET_CACHE_DATA_INT(msginfo->planned_download);
690 GET_CACHE_DATA_INT(msginfo->total_size);
691 GET_CACHE_DATA_INT(refnum);
693 for (; refnum != 0; refnum--) {
694 ref = NULL;
696 GET_CACHE_DATA(ref, memusage);
698 if (ref && *ref)
699 msginfo->references =
700 g_slist_prepend(msginfo->references, ref);
702 if (msginfo->references)
703 msginfo->references =
704 g_slist_reverse(msginfo->references);
706 msginfo->folder = item;
707 msginfo->flags.tmp_flags |= tmp_flags;
709 g_hash_table_insert(cache->msgnum_table, &msginfo->msgnum, msginfo);
710 if(msginfo->msgid)
711 g_hash_table_insert(cache->msgid_table, msginfo->msgid, msginfo);
713 } else {
714 while (claws_fread(&num, sizeof(num), 1, fp) == 1) {
715 if (swapping)
716 num = bswap_32(num);
718 msginfo = procmsg_msginfo_new();
719 msginfo->msgnum = num;
720 memusage += sizeof(MsgInfo);
722 READ_CACHE_DATA_INT(msginfo->size, fp);
723 READ_CACHE_DATA_INT(msginfo->mtime, fp);
724 READ_CACHE_DATA_INT(msginfo->date_t, fp);
725 READ_CACHE_DATA_INT(msginfo->flags.tmp_flags, fp);
727 READ_CACHE_DATA(msginfo->fromname, fp, memusage);
729 READ_CACHE_DATA(msginfo->date, fp, memusage);
730 READ_CACHE_DATA(msginfo->from, fp, memusage);
731 READ_CACHE_DATA(msginfo->to, fp, memusage);
732 READ_CACHE_DATA(msginfo->cc, fp, memusage);
733 READ_CACHE_DATA(msginfo->newsgroups, fp, memusage);
734 READ_CACHE_DATA(msginfo->subject, fp, memusage);
735 READ_CACHE_DATA(msginfo->msgid, fp, memusage);
736 READ_CACHE_DATA(msginfo->inreplyto, fp, memusage);
737 READ_CACHE_DATA(msginfo->xref, fp, memusage);
739 READ_CACHE_DATA_INT(msginfo->planned_download, fp);
740 READ_CACHE_DATA_INT(msginfo->total_size, fp);
741 READ_CACHE_DATA_INT(refnum, fp);
743 for (; refnum != 0; refnum--) {
744 ref = NULL;
746 READ_CACHE_DATA(ref, fp, memusage);
748 if (ref && *ref)
749 msginfo->references =
750 g_slist_prepend(msginfo->references, ref);
752 if (msginfo->references)
753 msginfo->references =
754 g_slist_reverse(msginfo->references);
756 msginfo->folder = item;
757 msginfo->flags.tmp_flags |= tmp_flags;
759 g_hash_table_insert(cache->msgnum_table, &msginfo->msgnum, msginfo);
760 if(msginfo->msgid)
761 g_hash_table_insert(cache->msgid_table, msginfo->msgid, msginfo);
764 bail_err:
765 if (cache_data != NULL && cache_data != MAP_FAILED) {
766 #ifdef G_OS_WIN32
767 UnmapViewOfFile((void*) cache_data);
768 #else
769 munmap(cache_data, map_len);
770 #endif
772 claws_fclose(fp);
773 if (conv != NULL) {
774 if (conv->free != NULL)
775 conv->free(conv);
776 g_free(conv);
779 if(error) {
780 msgcache_destroy(cache);
781 return NULL;
784 cache->last_access = time(NULL);
785 cache->memusage = memusage;
787 debug_print("done. (%d items read)\n", g_hash_table_size(cache->msgnum_table));
788 debug_print("Cache size: %d messages, %u bytes\n", g_hash_table_size(cache->msgnum_table), cache->memusage);
790 return cache;
793 void msgcache_read_mark(MsgCache *cache, const gchar *mark_file)
795 FILE *fp;
796 MsgInfo *msginfo;
797 MsgPermFlags perm_flags;
798 guint32 num;
799 gint map_len = -1;
800 char *cache_data = NULL;
801 struct stat st;
802 gboolean error = FALSE;
804 swapping = TRUE;
806 /* In case we can't open the mark file with MARK_VERSION, check if we can open it with the
807 * swapped MARK_VERSION. As msgcache_open_data_file swaps it too, if this succeeds,
808 * it means it's the old version (not little-endian) on a big-endian machine. The code has
809 * no effect on x86 as their file doesn't change. */
811 if ((fp = msgcache_open_data_file(mark_file, MARK_VERSION, DATA_READ, NULL, 0)) == NULL) {
812 /* see if it isn't swapped ? */
813 if ((fp = msgcache_open_data_file(mark_file, bswap_32(MARK_VERSION), DATA_READ, NULL, 0)) == NULL)
814 return;
815 else
816 swapping = FALSE; /* yay */
818 debug_print("reading %sswapped mark file.\n", swapping?"":"un");
820 if (msgcache_use_mmap_read) {
821 if (fstat(fileno(fp), &st) >= 0)
822 map_len = st.st_size;
823 else
824 map_len = -1;
825 if (map_len > 0) {
826 #ifdef G_OS_WIN32
827 cache_data = NULL;
828 HANDLE hFile, hMapping;
829 hFile = (HANDLE) _get_osfhandle (fileno(fp));
830 if (hFile == (HANDLE) -1)
831 goto w32_fail2;
832 hMapping = CreateFileMapping(hFile, NULL, PAGE_WRITECOPY, 0, 0, NULL);
833 if (!hMapping)
834 goto w32_fail2;
835 cache_data = (unsigned char *)MapViewOfFile(hMapping, FILE_MAP_COPY, 0, 0, 0);
836 CloseHandle (hMapping);
837 w32_fail2:
839 #else
840 cache_data = mmap(NULL, map_len, PROT_READ, MAP_PRIVATE, fileno(fp), 0);
841 #endif
843 } else {
844 cache_data = NULL;
846 if (cache_data != NULL && cache_data != MAP_FAILED) {
847 int rem_len = map_len-ftell(fp);
848 char *walk_data = cache_data+ftell(fp);
850 while(rem_len > 0) {
851 GET_CACHE_DATA_INT(num);
852 GET_CACHE_DATA_INT(perm_flags);
853 msginfo = g_hash_table_lookup(cache->msgnum_table, &num);
854 if(msginfo) {
855 msginfo->flags.perm_flags = perm_flags;
858 } else {
859 while (claws_fread(&num, sizeof(num), 1, fp) == 1) {
860 if (swapping)
861 num = bswap_32(num);
862 if (claws_fread(&perm_flags, sizeof(perm_flags), 1, fp) != 1) {
863 error = TRUE;
864 break;
866 if (swapping)
867 perm_flags = bswap_32(perm_flags);
868 msginfo = g_hash_table_lookup(cache->msgnum_table, &num);
869 if(msginfo) {
870 msginfo->flags.perm_flags = perm_flags;
874 bail_err:
875 if (cache_data != NULL && cache_data != MAP_FAILED) {
876 #ifdef G_OS_WIN32
877 UnmapViewOfFile((void*) cache_data);
878 #else
879 munmap(cache_data, map_len);
880 #endif
882 claws_fclose(fp);
883 if (error) {
884 debug_print("error reading cache mark from %s\n", mark_file);
888 void msgcache_read_tags(MsgCache *cache, const gchar *tags_file)
890 FILE *fp;
891 MsgInfo *msginfo;
892 guint32 num;
893 gint map_len = -1;
894 char *cache_data = NULL;
895 struct stat st;
896 gboolean error = FALSE;
898 swapping = TRUE;
900 /* In case we can't open the mark file with MARK_VERSION, check if we can open it with the
901 * swapped MARK_VERSION. As msgcache_open_data_file swaps it too, if this succeeds,
902 * it means it's the old version (not little-endian) on a big-endian machine. The code has
903 * no effect on x86 as their file doesn't change. */
905 if ((fp = msgcache_open_data_file(tags_file, TAGS_VERSION, DATA_READ, NULL, 0)) == NULL) {
906 /* see if it isn't swapped ? */
907 if ((fp = msgcache_open_data_file(tags_file, bswap_32(TAGS_VERSION), DATA_READ, NULL, 0)) == NULL)
908 return;
909 else
910 swapping = FALSE; /* yay */
912 debug_print("reading %sswapped tags file.\n", swapping?"":"un");
914 if (msgcache_use_mmap_read) {
915 if (fstat(fileno(fp), &st) >= 0)
916 map_len = st.st_size;
917 else
918 map_len = -1;
919 if (map_len > 0) {
920 #ifdef G_OS_WIN32
921 cache_data = NULL;
922 HANDLE hFile, hMapping;
923 hFile = (HANDLE) _get_osfhandle (fileno(fp));
924 if (hFile == (HANDLE) -1)
925 goto w32_fail6;
926 hMapping = CreateFileMapping(hFile, NULL, PAGE_WRITECOPY, 0, 0, NULL);
927 if (!hMapping)
928 goto w32_fail6;
929 cache_data = (unsigned char *)MapViewOfFile(hMapping, FILE_MAP_COPY, 0, 0, 0);
930 CloseHandle (hMapping);
931 w32_fail6:
933 #else
934 cache_data = mmap(NULL, map_len, PROT_READ, MAP_PRIVATE, fileno(fp), 0);
935 #endif
937 } else {
938 cache_data = NULL;
940 if (cache_data != NULL && cache_data != MAP_FAILED) {
941 int rem_len = map_len-ftell(fp);
942 char *walk_data = cache_data+ftell(fp);
944 while(rem_len > 0) {
945 gint id = -1;
946 GET_CACHE_DATA_INT(num);
947 msginfo = g_hash_table_lookup(cache->msgnum_table, &num);
948 if(msginfo) {
949 g_slist_free(msginfo->tags);
950 msginfo->tags = NULL;
951 do {
952 GET_CACHE_DATA_INT(id);
953 if (id > 0) {
954 msginfo->tags = g_slist_prepend(
955 msginfo->tags,
956 GINT_TO_POINTER(id));
958 } while (id > 0);
959 msginfo->tags = g_slist_reverse(msginfo->tags);
962 } else {
963 while (claws_fread(&num, sizeof(num), 1, fp) == 1) {
964 gint id = -1;
965 if (swapping)
966 num = bswap_32(num);
967 msginfo = g_hash_table_lookup(cache->msgnum_table, &num);
968 if(msginfo) {
969 g_slist_free(msginfo->tags);
970 msginfo->tags = NULL;
971 do {
972 if (claws_fread(&id, sizeof(id), 1, fp) != 1)
973 id = -1;
974 if (swapping)
975 id = bswap_32(id);
976 if (id > 0) {
977 msginfo->tags = g_slist_prepend(
978 msginfo->tags,
979 GINT_TO_POINTER(id));
981 } while (id > 0);
982 msginfo->tags = g_slist_reverse(msginfo->tags);
986 bail_err:
987 if (cache_data != NULL && cache_data != MAP_FAILED) {
988 #ifdef G_OS_WIN32
989 UnmapViewOfFile((void*) cache_data);
990 #else
991 munmap(cache_data, map_len);
992 #endif
994 claws_fclose(fp);
995 if (error) {
996 debug_print("error reading cache tags from %s\n", tags_file);
1000 static int msgcache_write_cache(MsgInfo *msginfo, FILE *fp)
1002 MsgTmpFlags flags = msginfo->flags.tmp_flags & MSG_CACHED_FLAG_MASK;
1003 GSList *cur;
1004 int w_err = 0, wrote = 0;
1006 WRITE_CACHE_DATA_INT(msginfo->msgnum, fp);
1007 WRITE_CACHE_DATA_INT(msginfo->size, fp);
1008 WRITE_CACHE_DATA_INT(msginfo->mtime, fp);
1009 WRITE_CACHE_DATA_INT(msginfo->date_t, fp);
1010 WRITE_CACHE_DATA_INT(flags, fp);
1012 WRITE_CACHE_DATA(msginfo->fromname, fp);
1014 WRITE_CACHE_DATA(msginfo->date, fp);
1015 WRITE_CACHE_DATA(msginfo->from, fp);
1016 WRITE_CACHE_DATA(msginfo->to, fp);
1017 WRITE_CACHE_DATA(msginfo->cc, fp);
1018 WRITE_CACHE_DATA(msginfo->newsgroups, fp);
1019 WRITE_CACHE_DATA(msginfo->subject, fp);
1020 WRITE_CACHE_DATA(msginfo->msgid, fp);
1021 WRITE_CACHE_DATA(msginfo->inreplyto, fp);
1022 WRITE_CACHE_DATA(msginfo->xref, fp);
1023 WRITE_CACHE_DATA_INT(msginfo->planned_download, fp);
1024 WRITE_CACHE_DATA_INT(msginfo->total_size, fp);
1026 WRITE_CACHE_DATA_INT(g_slist_length(msginfo->references), fp);
1028 for (cur = msginfo->references; cur != NULL; cur = cur->next) {
1029 WRITE_CACHE_DATA((gchar *)cur->data, fp);
1031 return w_err ? -1 : wrote;
1034 static int msgcache_write_flags(MsgInfo *msginfo, FILE *fp)
1036 MsgPermFlags flags = msginfo->flags.perm_flags;
1037 int w_err = 0, wrote = 0;
1038 WRITE_CACHE_DATA_INT(msginfo->msgnum, fp);
1039 WRITE_CACHE_DATA_INT(flags, fp);
1040 return w_err ? -1 : wrote;
1043 static int msgcache_write_tags(MsgInfo *msginfo, FILE *fp)
1045 GSList *cur = msginfo->tags;
1046 int w_err = 0, wrote = 0;
1048 WRITE_CACHE_DATA_INT(msginfo->msgnum, fp);
1049 for (; cur; cur = cur->next) {
1050 gint id = GPOINTER_TO_INT(cur->data);
1051 if (tags_get_tag(id) != NULL) {
1052 WRITE_CACHE_DATA_INT(id, fp);
1055 WRITE_CACHE_DATA_INT(-1, fp);
1057 return w_err ? -1 : wrote;
1060 struct write_fps
1062 FILE *cache_fp;
1063 FILE *mark_fp;
1064 FILE *tags_fp;
1065 int error;
1066 guint cache_size;
1067 guint mark_size;
1068 guint tags_size;
1071 static void msgcache_write_func(gpointer key, gpointer value, gpointer user_data)
1073 MsgInfo *msginfo;
1074 struct write_fps *write_fps;
1075 int tmp;
1077 msginfo = (MsgInfo *)value;
1078 write_fps = user_data;
1080 if (write_fps->cache_fp) {
1081 tmp = msgcache_write_cache(msginfo, write_fps->cache_fp);
1082 if (tmp < 0)
1083 write_fps->error = 1;
1084 else
1085 write_fps->cache_size += tmp;
1087 if (write_fps->mark_fp) {
1088 tmp= msgcache_write_flags(msginfo, write_fps->mark_fp);
1089 if (tmp < 0)
1090 write_fps->error = 1;
1091 else
1092 write_fps->mark_size += tmp;
1094 if (write_fps->tags_fp) {
1095 tmp = msgcache_write_tags(msginfo, write_fps->tags_fp);
1096 if (tmp < 0)
1097 write_fps->error = 1;
1098 else
1099 write_fps->tags_size += tmp;
1103 gint msgcache_write(const gchar *cache_file, const gchar *mark_file, const gchar *tags_file, MsgCache *cache)
1105 struct write_fps write_fps;
1106 gchar *new_cache, *new_mark, *new_tags;
1107 int w_err = 0, wrote = 0;
1109 START_TIMING("");
1110 cm_return_val_if_fail(cache != NULL, -1);
1112 new_cache = g_strconcat(cache_file, ".new", NULL);
1113 new_mark = g_strconcat(mark_file, ".new", NULL);
1114 new_tags = g_strconcat(tags_file, ".new", NULL);
1116 write_fps.error = 0;
1117 write_fps.cache_size = 0;
1118 write_fps.mark_size = 0;
1119 write_fps.tags_size = 0;
1121 /* open files and write headers */
1123 if (cache_file) {
1124 write_fps.cache_fp = msgcache_open_data_file(new_cache, CACHE_VERSION,
1125 DATA_WRITE, NULL, 0);
1126 if (write_fps.cache_fp == NULL) {
1127 g_free(new_cache);
1128 g_free(new_mark);
1129 g_free(new_tags);
1130 return -1;
1132 WRITE_CACHE_DATA(CS_UTF_8, write_fps.cache_fp);
1133 } else {
1134 write_fps.cache_fp = NULL;
1137 if (w_err != 0) {
1138 g_warning("failed to write charset");
1139 if (write_fps.cache_fp)
1140 claws_fclose(write_fps.cache_fp);
1141 claws_unlink(new_cache);
1142 g_free(new_cache);
1143 g_free(new_mark);
1144 g_free(new_tags);
1145 return -1;
1148 if (mark_file) {
1149 write_fps.mark_fp = msgcache_open_data_file(new_mark, MARK_VERSION,
1150 DATA_WRITE, NULL, 0);
1151 if (write_fps.mark_fp == NULL) {
1152 if (write_fps.cache_fp)
1153 claws_fclose(write_fps.cache_fp);
1154 claws_unlink(new_cache);
1155 g_free(new_cache);
1156 g_free(new_mark);
1157 g_free(new_tags);
1158 return -1;
1160 } else {
1161 write_fps.mark_fp = NULL;
1164 if (tags_file) {
1165 write_fps.tags_fp = msgcache_open_data_file(new_tags, TAGS_VERSION,
1166 DATA_WRITE, NULL, 0);
1167 if (write_fps.tags_fp == NULL) {
1168 if (write_fps.cache_fp)
1169 claws_fclose(write_fps.cache_fp);
1170 if (write_fps.mark_fp)
1171 claws_fclose(write_fps.mark_fp);
1172 claws_unlink(new_cache);
1173 claws_unlink(new_mark);
1174 g_free(new_cache);
1175 g_free(new_mark);
1176 g_free(new_tags);
1177 return -1;
1179 } else {
1180 write_fps.tags_fp = NULL;
1183 debug_print("\tWriting message cache to %s and %s...\n", new_cache, new_mark);
1185 if (write_fps.cache_fp && change_file_mode_rw(write_fps.cache_fp, new_cache) < 0)
1186 FILE_OP_ERROR(new_cache, "chmod");
1188 /* headers written, note file size */
1189 if (write_fps.cache_fp)
1190 write_fps.cache_size = ftell(write_fps.cache_fp);
1191 if (write_fps.mark_fp)
1192 write_fps.mark_size = ftell(write_fps.mark_fp);
1193 if (write_fps.tags_fp)
1194 write_fps.tags_size = ftell(write_fps.tags_fp);
1196 /* write data to the files */
1197 g_hash_table_foreach(cache->msgnum_table, msgcache_write_func, (gpointer)&write_fps);
1199 /* close files */
1200 if (write_fps.cache_fp)
1201 write_fps.error |= (claws_safe_fclose(write_fps.cache_fp) != 0);
1202 if (write_fps.mark_fp)
1203 write_fps.error |= (claws_safe_fclose(write_fps.mark_fp) != 0);
1204 if (write_fps.tags_fp)
1205 write_fps.error |= (claws_safe_fclose(write_fps.tags_fp) != 0);
1208 if (write_fps.error != 0) {
1209 /* in case of error, forget all */
1210 claws_unlink(new_cache);
1211 claws_unlink(new_mark);
1212 claws_unlink(new_tags);
1213 g_free(new_cache);
1214 g_free(new_mark);
1215 g_free(new_tags);
1216 return -1;
1217 } else {
1218 /* switch files */
1219 if (cache_file)
1220 move_file(new_cache, cache_file, TRUE);
1221 if (mark_file)
1222 move_file(new_mark, mark_file, TRUE);
1223 if (tags_file)
1224 move_file(new_tags, tags_file, TRUE);
1225 cache->last_access = time(NULL);
1228 g_free(new_cache);
1229 g_free(new_mark);
1230 g_free(new_tags);
1231 debug_print("done.\n");
1232 END_TIMING();
1233 return 0;