2 * Claws Mail -- 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/>.
22 #include "claws-features.h"
30 #include <glib/gi18n.h>
32 # define MAP_FAILED ((char *) -1)
34 # include <sys/mman.h>
36 #include <sys/types.h>
47 #include "prefs_common.h"
48 #include "file-utils.h"
50 #if G_BYTE_ORDER == G_BIG_ENDIAN
52 ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \
53 (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24))
55 #define MMAP_TO_GUINT32(x) \
57 ((x[2]&0xff) << 8) | \
58 ((x[1]&0xff) << 16) | \
61 #define MMAP_TO_GUINT32_SWAPPED(x) \
63 ((x[1]&0xff) << 8) | \
64 ((x[2]&0xff) << 16) | \
67 static gboolean msgcache_use_mmap_read
= TRUE
;
70 #define bswap_32(x) (x)
72 #define MMAP_TO_GUINT32(x) \
74 ((x[1]&0xff) << 8) | \
75 ((x[2]&0xff) << 16) | \
78 #define MMAP_TO_GUINT32_SWAPPED(x) \
80 ((x[1]&0xff) << 8) | \
81 ((x[2]&0xff) << 16) | \
84 static gboolean msgcache_use_mmap_read
= TRUE
;
87 static gboolean swapping
= TRUE
;
97 GHashTable
*msgnum_table
;
98 GHashTable
*msgid_table
;
103 typedef struct _StringConverter StringConverter
;
104 struct _StringConverter
{
105 gchar
*(*convert
) (StringConverter
*converter
, gchar
*srcstr
);
106 void (*free
) (StringConverter
*converter
);
109 typedef struct _StrdupConverter StrdupConverter
;
110 struct _StrdupConverter
{
111 StringConverter converter
;
114 typedef struct _CharsetConverter CharsetConverter
;
115 struct _CharsetConverter
{
116 StringConverter converter
;
122 MsgCache
*msgcache_new(void)
126 cache
= g_new0(MsgCache
, 1),
127 cache
->msgnum_table
= g_hash_table_new(g_int_hash
, g_int_equal
);
128 cache
->msgid_table
= g_hash_table_new(g_str_hash
, g_str_equal
);
129 cache
->last_access
= time(NULL
);
134 static gboolean
msgcache_msginfo_free_func(gpointer num
, gpointer msginfo
, gpointer user_data
)
136 procmsg_msginfo_free((MsgInfo
**)&msginfo
);
140 void msgcache_destroy(MsgCache
*cache
)
142 cm_return_if_fail(cache
!= NULL
);
144 g_hash_table_foreach_remove(cache
->msgnum_table
, msgcache_msginfo_free_func
, NULL
);
145 g_hash_table_destroy(cache
->msgid_table
);
146 g_hash_table_destroy(cache
->msgnum_table
);
150 void msgcache_add_msg(MsgCache
*cache
, MsgInfo
*msginfo
)
154 cm_return_if_fail(cache
!= NULL
);
155 cm_return_if_fail(msginfo
!= NULL
);
157 newmsginfo
= procmsg_msginfo_new_ref(msginfo
);
158 g_hash_table_insert(cache
->msgnum_table
, &newmsginfo
->msgnum
, newmsginfo
);
159 if(newmsginfo
->msgid
!= NULL
)
160 g_hash_table_insert(cache
->msgid_table
, newmsginfo
->msgid
, newmsginfo
);
161 cache
->memusage
+= procmsg_msginfo_memusage(msginfo
);
162 cache
->last_access
= time(NULL
);
164 msginfo
->folder
->cache_dirty
= TRUE
;
166 debug_print("Cache size: %d messages, %u bytes\n", g_hash_table_size(cache
->msgnum_table
), cache
->memusage
);
169 void msgcache_remove_msg(MsgCache
*cache
, guint msgnum
)
173 cm_return_if_fail(cache
!= NULL
);
175 msginfo
= (MsgInfo
*) g_hash_table_lookup(cache
->msgnum_table
, &msgnum
);
179 cache
->memusage
-= procmsg_msginfo_memusage(msginfo
);
181 g_hash_table_remove(cache
->msgid_table
, msginfo
->msgid
);
182 g_hash_table_remove(cache
->msgnum_table
, &msginfo
->msgnum
);
184 msginfo
->folder
->cache_dirty
= TRUE
;
186 procmsg_msginfo_free(&msginfo
);
187 cache
->last_access
= time(NULL
);
190 debug_print("Cache size: %d messages, %u bytes\n", g_hash_table_size(cache
->msgnum_table
), cache
->memusage
);
193 void msgcache_update_msg(MsgCache
*cache
, MsgInfo
*msginfo
)
195 MsgInfo
*oldmsginfo
, *newmsginfo
;
197 cm_return_if_fail(cache
!= NULL
);
198 cm_return_if_fail(msginfo
!= NULL
);
200 oldmsginfo
= g_hash_table_lookup(cache
->msgnum_table
, &msginfo
->msgnum
);
201 if(oldmsginfo
&& oldmsginfo
->msgid
)
202 g_hash_table_remove(cache
->msgid_table
, oldmsginfo
->msgid
);
204 g_hash_table_remove(cache
->msgnum_table
, &oldmsginfo
->msgnum
);
205 cache
->memusage
-= procmsg_msginfo_memusage(oldmsginfo
);
206 procmsg_msginfo_free(&oldmsginfo
);
209 newmsginfo
= procmsg_msginfo_new_ref(msginfo
);
210 g_hash_table_insert(cache
->msgnum_table
, &newmsginfo
->msgnum
, newmsginfo
);
211 if(newmsginfo
->msgid
)
212 g_hash_table_insert(cache
->msgid_table
, newmsginfo
->msgid
, newmsginfo
);
213 cache
->memusage
+= procmsg_msginfo_memusage(newmsginfo
);
214 cache
->last_access
= time(NULL
);
216 debug_print("Cache size: %d messages, %u bytes\n", g_hash_table_size(cache
->msgnum_table
), cache
->memusage
);
218 msginfo
->folder
->cache_dirty
= TRUE
;
223 MsgInfo
*msgcache_get_msg(MsgCache
*cache
, guint num
)
227 cm_return_val_if_fail(cache
!= NULL
, NULL
);
229 msginfo
= g_hash_table_lookup(cache
->msgnum_table
, &num
);
232 cache
->last_access
= time(NULL
);
234 return procmsg_msginfo_new_ref(msginfo
);
237 MsgInfo
*msgcache_get_msg_by_id(MsgCache
*cache
, const gchar
*msgid
)
241 cm_return_val_if_fail(cache
!= NULL
, NULL
);
242 cm_return_val_if_fail(msgid
!= NULL
, NULL
);
244 msginfo
= g_hash_table_lookup(cache
->msgid_table
, msgid
);
247 cache
->last_access
= time(NULL
);
249 return procmsg_msginfo_new_ref(msginfo
);
252 static void msgcache_get_msg_list_func(gpointer key
, gpointer value
, gpointer user_data
)
254 MsgInfoList
**listptr
= user_data
;
255 MsgInfo
*msginfo
= value
;
257 *listptr
= g_slist_prepend(*listptr
, procmsg_msginfo_new_ref(msginfo
));
260 MsgInfoList
*msgcache_get_msg_list(MsgCache
*cache
)
262 MsgInfoList
*msg_list
= NULL
;
264 cm_return_val_if_fail(cache
!= NULL
, NULL
);
266 g_hash_table_foreach((GHashTable
*)cache
->msgnum_table
, msgcache_get_msg_list_func
, (gpointer
)&msg_list
);
267 cache
->last_access
= time(NULL
);
269 msg_list
= g_slist_reverse(msg_list
);
274 time_t msgcache_get_last_access_time(MsgCache
*cache
)
276 cm_return_val_if_fail(cache
!= NULL
, 0);
278 return cache
->last_access
;
281 gint
msgcache_get_memory_usage(MsgCache
*cache
)
283 cm_return_val_if_fail(cache
!= NULL
, 0);
285 return cache
->memusage
;
289 * Cache saving functions
292 #define READ_CACHE_DATA(data, fp, total_len) \
294 if ((tmp_len = msgcache_read_cache_data_str(fp, &data, conv)) < 0) { \
295 procmsg_msginfo_free(&msginfo); \
299 total_len += tmp_len; \
302 #define READ_CACHE_DATA_INT(n, fp) \
307 if ((ni = claws_fread(&idata, sizeof(idata), 1, fp)) != 1) { \
308 g_warning("read_int: cache data corrupted, read %"G_GSIZE_FORMAT" of %"G_GSIZE_FORMAT" at " \
309 "offset %ld", ni, sizeof(idata), ftell(fp)); \
310 procmsg_msginfo_free(&msginfo); \
314 n = swapping ? bswap_32(idata) : (idata);\
317 #define GET_CACHE_DATA_INT(n) \
320 g_print("error at rem_len:%d\n", rem_len); \
321 procmsg_msginfo_free(&msginfo); \
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 procmsg_msginfo_free(&msginfo); \
338 if ((tmp_len = msgcache_get_cache_data_str(walk_data, &data, tmp_len, conv)) < 0) { \
339 g_print("error at rem_len:%d\n", rem_len);\
340 procmsg_msginfo_free(&msginfo); \
344 total_len += tmp_len; \
345 walk_data += tmp_len; rem_len -= tmp_len; \
349 #define WRITE_CACHE_DATA_INT(n, fp) \
353 idata = (guint32)bswap_32(n); \
354 if (claws_fwrite(&idata, sizeof(idata), 1, fp) != 1) \
359 #define PUT_CACHE_DATA_INT(n) \
361 walk_data[0]=(((guint32)n)&0x000000ff); \
362 walk_data[1]=(((guint32)n)&0x0000ff00)>>8; \
363 walk_data[2]=(((guint32)n)&0x00ff0000)>>16; \
364 walk_data[3]=(((guint32)n)&0xff000000)>>24; \
369 #define WRITE_CACHE_DATA(data, fp) \
375 len = strlen(data); \
376 WRITE_CACHE_DATA_INT(len, fp); \
377 if (w_err == 0 && len > 0) { \
378 if (claws_fwrite(data, 1, len, fp) != len) \
384 #define PUT_CACHE_DATA(data) \
390 len = strlen(data); \
391 PUT_CACHE_DATA_INT(len); \
393 memcpy(walk_data, data, len); \
399 static FILE *msgcache_open_data_file(const gchar
*file
, guint version
,
401 gchar
*buf
, size_t buf_size
)
406 cm_return_val_if_fail(file
!= NULL
, NULL
);
408 if (mode
== DATA_WRITE
) {
409 int w_err
= 0, wrote
= 0;
410 if ((fp
= claws_fopen(file
, "wb")) == NULL
) {
411 FILE_OP_ERROR(file
, "claws_fopen");
414 if (change_file_mode_rw(fp
, file
) < 0)
415 FILE_OP_ERROR(file
, "chmod");
417 WRITE_CACHE_DATA_INT(version
, fp
);
419 g_warning("failed to write int");
427 if ((fp
= claws_fopen(file
, "rb")) == NULL
)
428 debug_print("Mark/Cache file '%s' not found\n", file
);
430 if (buf
&& buf_size
> 0)
431 setvbuf(fp
, buf
, _IOFBF
, buf_size
);
432 if (claws_fread(&data_ver
, sizeof(data_ver
), 1, fp
) != 1 ||
433 version
!= bswap_32(data_ver
)) {
434 g_message("%s: Mark/Cache version is different (%u != %u).\n",
435 file
, bswap_32(data_ver
), version
);
439 data_ver
= bswap_32(data_ver
);
442 if (mode
== DATA_READ
)
446 /* reopen with append mode */
448 if ((fp
= claws_fopen(file
, "ab")) == NULL
)
449 FILE_OP_ERROR(file
, "claws_fopen");
451 /* open with overwrite mode if mark file doesn't exist or
452 version is different */
453 fp
= msgcache_open_data_file(file
, version
, DATA_WRITE
, buf
,
460 static gint
msgcache_read_cache_data_str(FILE *fp
, gchar
**str
,
461 StringConverter
*conv
)
463 gchar
*tmpstr
= NULL
;
469 if ((ni
= claws_fread(&len
, sizeof(len
), 1, fp
) != 1) ||
471 g_warning("read_data_str: cache data (len) corrupted, read %"G_GSIZE_FORMAT
472 " of %"G_GSIZE_FORMAT
" bytes at offset %ld", ni
, sizeof(len
),
477 if ((ni
= claws_fread(&len
, sizeof(len
), 1, fp
) != 1) ||
478 bswap_32(len
) > G_MAXINT
) {
479 g_warning("read_data_str: cache data (len) corrupted, read %"G_GSIZE_FORMAT
480 " of %"G_GSIZE_FORMAT
" bytes at offset %ld", ni
, sizeof(len
),
490 tmpstr
= g_try_malloc(len
+ 1);
496 if ((ni
= claws_fread(tmpstr
, 1, len
, fp
)) != len
) {
497 g_warning("read_data_str: cache data corrupted, read %"G_GSIZE_FORMAT
" of %u "
498 "bytes at offset %ld",
506 *str
= conv
->convert(conv
, tmpstr
);
514 static gint
msgcache_get_cache_data_str(gchar
*src
, gchar
**str
, gint len
,
515 StringConverter
*conv
)
517 gchar
*tmpstr
= NULL
;
524 if(len
> 2*1024*1024) {
525 g_warning("read_data_str: refusing to allocate %d bytes", len
);
529 tmpstr
= g_try_malloc(len
+ 1);
535 memcpy(tmpstr
, src
, len
);
539 *str
= conv
->convert(conv
, tmpstr
);
547 static gchar
*strconv_charset_convert(StringConverter
*conv
, gchar
*srcstr
)
549 CharsetConverter
*charsetconv
= (CharsetConverter
*) conv
;
551 return conv_codeset_strdup(srcstr
, charsetconv
->srccharset
, charsetconv
->dstcharset
);
554 static void strconv_charset_free(StringConverter
*conv
)
556 CharsetConverter
*charsetconv
= (CharsetConverter
*) conv
;
558 g_free(charsetconv
->srccharset
);
559 g_free(charsetconv
->dstcharset
);
562 MsgCache
*msgcache_read_cache(FolderItem
*item
, const gchar
*cache_file
)
567 MsgTmpFlags tmp_flags
= 0;
568 gchar file_buf
[BUFFSIZE
];
571 gboolean error
= FALSE
;
572 StringConverter
*conv
= NULL
;
573 gchar
*srccharset
= NULL
;
574 const gchar
*dstcharset
= NULL
;
577 gint tmp_len
= 0, map_len
= -1;
578 char *cache_data
= NULL
;
581 cm_return_val_if_fail(cache_file
!= NULL
, NULL
);
582 cm_return_val_if_fail(item
!= NULL
, NULL
);
586 /* In case we can't open the mark file with MARK_VERSION, check if we can open it with the
587 * swapped MARK_VERSION. As msgcache_open_data_file swaps it too, if this succeeds,
588 * it means it's the old version (not little-endian) on a big-endian machine. The code has
589 * no effect on x86 as their file doesn't change. */
591 if ((fp
= msgcache_open_data_file
592 (cache_file
, CACHE_VERSION
, DATA_READ
, file_buf
, sizeof(file_buf
))) == NULL
) {
593 if ((fp
= msgcache_open_data_file
594 (cache_file
, bswap_32(CACHE_VERSION
), DATA_READ
, file_buf
, sizeof(file_buf
))) == NULL
)
600 debug_print("\tReading %sswapped message cache from %s...\n", swapping
?"":"un", cache_file
);
602 if (folder_has_parent_of_type(item
, F_QUEUE
)) {
603 tmp_flags
|= MSG_QUEUED
;
604 } else if (folder_has_parent_of_type(item
, F_DRAFT
)) {
605 tmp_flags
|= MSG_DRAFT
;
608 if (msgcache_read_cache_data_str(fp
, &srccharset
, NULL
) < 0) {
612 dstcharset
= CS_UTF_8
;
613 if (srccharset
== NULL
|| dstcharset
== NULL
) {
615 } else if (strcmp(srccharset
, dstcharset
) == 0) {
616 debug_print("using Noop Converter\n");
620 CharsetConverter
*charsetconv
;
622 debug_print("using CharsetConverter\n");
624 charsetconv
= g_new0(CharsetConverter
, 1);
625 charsetconv
->converter
.convert
= strconv_charset_convert
;
626 charsetconv
->converter
.free
= strconv_charset_free
;
627 charsetconv
->srccharset
= g_strdup(srccharset
);
628 charsetconv
->dstcharset
= g_strdup(dstcharset
);
630 conv
= (StringConverter
*) charsetconv
;
634 cache
= msgcache_new();
636 if (msgcache_use_mmap_read
== TRUE
) {
637 if (fstat(fileno(fp
), &st
) >= 0)
638 map_len
= st
.st_size
;
644 HANDLE hFile
, hMapping
;
645 hFile
= (HANDLE
) _get_osfhandle (fileno(fp
));
646 if (hFile
== (HANDLE
) -1)
648 hMapping
= CreateFileMapping(hFile
, NULL
, PAGE_WRITECOPY
, 0, 0, NULL
);
651 cache_data
= (unsigned char *)MapViewOfFile(hMapping
, FILE_MAP_COPY
, 0, 0, 0);
652 CloseHandle (hMapping
);
656 cache_data
= mmap(NULL
, map_len
, PROT_READ
, MAP_PRIVATE
, fileno(fp
), 0);
662 if (cache_data
!= NULL
&& cache_data
!= MAP_FAILED
) {
663 int rem_len
= map_len
-ftell(fp
);
664 char *walk_data
= cache_data
+ftell(fp
);
667 msginfo
= procmsg_msginfo_new();
669 GET_CACHE_DATA_INT(num
);
671 msginfo
->msgnum
= num
;
672 memusage
+= sizeof(MsgInfo
);
674 GET_CACHE_DATA_INT(msginfo
->size
);
675 GET_CACHE_DATA_INT(msginfo
->mtime
);
676 GET_CACHE_DATA_INT(msginfo
->date_t
);
677 GET_CACHE_DATA_INT(msginfo
->flags
.tmp_flags
);
679 GET_CACHE_DATA(msginfo
->fromname
, memusage
);
681 GET_CACHE_DATA(msginfo
->date
, memusage
);
682 GET_CACHE_DATA(msginfo
->from
, memusage
);
683 GET_CACHE_DATA(msginfo
->to
, memusage
);
684 GET_CACHE_DATA(msginfo
->cc
, memusage
);
685 GET_CACHE_DATA(msginfo
->newsgroups
, memusage
);
686 GET_CACHE_DATA(msginfo
->subject
, memusage
);
687 GET_CACHE_DATA(msginfo
->msgid
, memusage
);
688 GET_CACHE_DATA(msginfo
->inreplyto
, memusage
);
689 GET_CACHE_DATA(msginfo
->xref
, memusage
);
691 GET_CACHE_DATA_INT(msginfo
->planned_download
);
692 GET_CACHE_DATA_INT(msginfo
->total_size
);
693 GET_CACHE_DATA_INT(refnum
);
695 for (; refnum
!= 0; refnum
--) {
698 GET_CACHE_DATA(ref
, memusage
);
701 msginfo
->references
=
702 g_slist_prepend(msginfo
->references
, ref
);
704 if (msginfo
->references
)
705 msginfo
->references
=
706 g_slist_reverse(msginfo
->references
);
708 msginfo
->folder
= item
;
709 msginfo
->flags
.tmp_flags
|= tmp_flags
;
711 g_hash_table_insert(cache
->msgnum_table
, &msginfo
->msgnum
, msginfo
);
713 g_hash_table_insert(cache
->msgid_table
, msginfo
->msgid
, msginfo
);
716 while (claws_fread(&num
, sizeof(num
), 1, fp
) == 1) {
720 msginfo
= procmsg_msginfo_new();
721 msginfo
->msgnum
= num
;
722 memusage
+= sizeof(MsgInfo
);
724 READ_CACHE_DATA_INT(msginfo
->size
, fp
);
725 READ_CACHE_DATA_INT(msginfo
->mtime
, fp
);
726 READ_CACHE_DATA_INT(msginfo
->date_t
, fp
);
727 READ_CACHE_DATA_INT(msginfo
->flags
.tmp_flags
, fp
);
729 READ_CACHE_DATA(msginfo
->fromname
, fp
, memusage
);
731 READ_CACHE_DATA(msginfo
->date
, fp
, memusage
);
732 READ_CACHE_DATA(msginfo
->from
, fp
, memusage
);
733 READ_CACHE_DATA(msginfo
->to
, fp
, memusage
);
734 READ_CACHE_DATA(msginfo
->cc
, fp
, memusage
);
735 READ_CACHE_DATA(msginfo
->newsgroups
, fp
, memusage
);
736 READ_CACHE_DATA(msginfo
->subject
, fp
, memusage
);
737 READ_CACHE_DATA(msginfo
->msgid
, fp
, memusage
);
738 READ_CACHE_DATA(msginfo
->inreplyto
, fp
, memusage
);
739 READ_CACHE_DATA(msginfo
->xref
, fp
, memusage
);
741 READ_CACHE_DATA_INT(msginfo
->planned_download
, fp
);
742 READ_CACHE_DATA_INT(msginfo
->total_size
, fp
);
743 READ_CACHE_DATA_INT(refnum
, fp
);
745 for (; refnum
!= 0; refnum
--) {
748 READ_CACHE_DATA(ref
, fp
, memusage
);
751 msginfo
->references
=
752 g_slist_prepend(msginfo
->references
, ref
);
754 if (msginfo
->references
)
755 msginfo
->references
=
756 g_slist_reverse(msginfo
->references
);
758 msginfo
->folder
= item
;
759 msginfo
->flags
.tmp_flags
|= tmp_flags
;
761 g_hash_table_insert(cache
->msgnum_table
, &msginfo
->msgnum
, msginfo
);
763 g_hash_table_insert(cache
->msgid_table
, msginfo
->msgid
, msginfo
);
767 if (cache_data
!= NULL
&& cache_data
!= MAP_FAILED
) {
769 UnmapViewOfFile((void*) cache_data
);
771 munmap(cache_data
, map_len
);
776 if (conv
->free
!= NULL
)
782 msgcache_destroy(cache
);
786 cache
->last_access
= time(NULL
);
787 cache
->memusage
= memusage
;
789 debug_print("done. (%d items read)\n", g_hash_table_size(cache
->msgnum_table
));
790 debug_print("Cache size: %d messages, %u bytes\n", g_hash_table_size(cache
->msgnum_table
), cache
->memusage
);
795 void msgcache_read_mark(MsgCache
*cache
, const gchar
*mark_file
)
799 MsgPermFlags perm_flags
;
802 char *cache_data
= NULL
;
804 gboolean error
= FALSE
;
808 /* In case we can't open the mark file with MARK_VERSION, check if we can open it with the
809 * swapped MARK_VERSION. As msgcache_open_data_file swaps it too, if this succeeds,
810 * it means it's the old version (not little-endian) on a big-endian machine. The code has
811 * no effect on x86 as their file doesn't change. */
813 if ((fp
= msgcache_open_data_file(mark_file
, MARK_VERSION
, DATA_READ
, NULL
, 0)) == NULL
) {
814 /* see if it isn't swapped ? */
815 if ((fp
= msgcache_open_data_file(mark_file
, bswap_32(MARK_VERSION
), DATA_READ
, NULL
, 0)) == NULL
)
818 swapping
= FALSE
; /* yay */
820 debug_print("reading %sswapped mark file.\n", swapping
?"":"un");
822 if (msgcache_use_mmap_read
) {
823 if (fstat(fileno(fp
), &st
) >= 0)
824 map_len
= st
.st_size
;
830 HANDLE hFile
, hMapping
;
831 hFile
= (HANDLE
) _get_osfhandle (fileno(fp
));
832 if (hFile
== (HANDLE
) -1)
834 hMapping
= CreateFileMapping(hFile
, NULL
, PAGE_WRITECOPY
, 0, 0, NULL
);
837 cache_data
= (unsigned char *)MapViewOfFile(hMapping
, FILE_MAP_COPY
, 0, 0, 0);
838 CloseHandle (hMapping
);
842 cache_data
= mmap(NULL
, map_len
, PROT_READ
, MAP_PRIVATE
, fileno(fp
), 0);
848 if (cache_data
!= NULL
&& cache_data
!= MAP_FAILED
) {
849 int rem_len
= map_len
-ftell(fp
);
850 char *walk_data
= cache_data
+ftell(fp
);
853 GET_CACHE_DATA_INT(num
);
854 GET_CACHE_DATA_INT(perm_flags
);
855 msginfo
= g_hash_table_lookup(cache
->msgnum_table
, &num
);
857 msginfo
->flags
.perm_flags
= perm_flags
;
861 while (claws_fread(&num
, sizeof(num
), 1, fp
) == 1) {
864 if (claws_fread(&perm_flags
, sizeof(perm_flags
), 1, fp
) != 1) {
869 perm_flags
= bswap_32(perm_flags
);
870 msginfo
= g_hash_table_lookup(cache
->msgnum_table
, &num
);
872 msginfo
->flags
.perm_flags
= perm_flags
;
877 if (cache_data
!= NULL
&& cache_data
!= MAP_FAILED
) {
879 UnmapViewOfFile((void*) cache_data
);
881 munmap(cache_data
, map_len
);
886 debug_print("error reading cache mark from %s\n", mark_file
);
890 void msgcache_read_tags(MsgCache
*cache
, const gchar
*tags_file
)
896 char *cache_data
= NULL
;
898 gboolean error
= FALSE
;
902 /* In case we can't open the mark file with MARK_VERSION, check if we can open it with the
903 * swapped MARK_VERSION. As msgcache_open_data_file swaps it too, if this succeeds,
904 * it means it's the old version (not little-endian) on a big-endian machine. The code has
905 * no effect on x86 as their file doesn't change. */
907 if ((fp
= msgcache_open_data_file(tags_file
, TAGS_VERSION
, DATA_READ
, NULL
, 0)) == NULL
) {
908 /* see if it isn't swapped ? */
909 if ((fp
= msgcache_open_data_file(tags_file
, bswap_32(TAGS_VERSION
), DATA_READ
, NULL
, 0)) == NULL
)
912 swapping
= FALSE
; /* yay */
914 debug_print("reading %sswapped tags file.\n", swapping
?"":"un");
916 if (msgcache_use_mmap_read
) {
917 if (fstat(fileno(fp
), &st
) >= 0)
918 map_len
= st
.st_size
;
924 HANDLE hFile
, hMapping
;
925 hFile
= (HANDLE
) _get_osfhandle (fileno(fp
));
926 if (hFile
== (HANDLE
) -1)
928 hMapping
= CreateFileMapping(hFile
, NULL
, PAGE_WRITECOPY
, 0, 0, NULL
);
931 cache_data
= (unsigned char *)MapViewOfFile(hMapping
, FILE_MAP_COPY
, 0, 0, 0);
932 CloseHandle (hMapping
);
936 cache_data
= mmap(NULL
, map_len
, PROT_READ
, MAP_PRIVATE
, fileno(fp
), 0);
942 if (cache_data
!= NULL
&& cache_data
!= MAP_FAILED
) {
943 int rem_len
= map_len
-ftell(fp
);
944 char *walk_data
= cache_data
+ftell(fp
);
948 GET_CACHE_DATA_INT(num
);
949 msginfo
= g_hash_table_lookup(cache
->msgnum_table
, &num
);
951 g_slist_free(msginfo
->tags
);
952 msginfo
->tags
= NULL
;
954 GET_CACHE_DATA_INT(id
);
956 msginfo
->tags
= g_slist_prepend(
958 GINT_TO_POINTER(id
));
961 msginfo
->tags
= g_slist_reverse(msginfo
->tags
);
965 while (claws_fread(&num
, sizeof(num
), 1, fp
) == 1) {
969 msginfo
= g_hash_table_lookup(cache
->msgnum_table
, &num
);
971 g_slist_free(msginfo
->tags
);
972 msginfo
->tags
= NULL
;
974 if (claws_fread(&id
, sizeof(id
), 1, fp
) != 1)
979 msginfo
->tags
= g_slist_prepend(
981 GINT_TO_POINTER(id
));
984 msginfo
->tags
= g_slist_reverse(msginfo
->tags
);
989 if (cache_data
!= NULL
&& cache_data
!= MAP_FAILED
) {
991 UnmapViewOfFile((void*) cache_data
);
993 munmap(cache_data
, map_len
);
998 debug_print("error reading cache tags from %s\n", tags_file
);
1002 static int msgcache_write_cache(MsgInfo
*msginfo
, FILE *fp
)
1004 MsgTmpFlags flags
= msginfo
->flags
.tmp_flags
& MSG_CACHED_FLAG_MASK
;
1006 int w_err
= 0, wrote
= 0;
1008 WRITE_CACHE_DATA_INT(msginfo
->msgnum
, fp
);
1009 WRITE_CACHE_DATA_INT(msginfo
->size
, fp
);
1010 WRITE_CACHE_DATA_INT(msginfo
->mtime
, fp
);
1011 WRITE_CACHE_DATA_INT(msginfo
->date_t
, fp
);
1012 WRITE_CACHE_DATA_INT(flags
, fp
);
1014 WRITE_CACHE_DATA(msginfo
->fromname
, fp
);
1016 WRITE_CACHE_DATA(msginfo
->date
, fp
);
1017 WRITE_CACHE_DATA(msginfo
->from
, fp
);
1018 WRITE_CACHE_DATA(msginfo
->to
, fp
);
1019 WRITE_CACHE_DATA(msginfo
->cc
, fp
);
1020 WRITE_CACHE_DATA(msginfo
->newsgroups
, fp
);
1021 WRITE_CACHE_DATA(msginfo
->subject
, fp
);
1022 WRITE_CACHE_DATA(msginfo
->msgid
, fp
);
1023 WRITE_CACHE_DATA(msginfo
->inreplyto
, fp
);
1024 WRITE_CACHE_DATA(msginfo
->xref
, fp
);
1025 WRITE_CACHE_DATA_INT(msginfo
->planned_download
, fp
);
1026 WRITE_CACHE_DATA_INT(msginfo
->total_size
, fp
);
1028 WRITE_CACHE_DATA_INT(g_slist_length(msginfo
->references
), fp
);
1030 for (cur
= msginfo
->references
; cur
!= NULL
; cur
= cur
->next
) {
1031 WRITE_CACHE_DATA((gchar
*)cur
->data
, fp
);
1033 return w_err
? -1 : wrote
;
1036 static int msgcache_write_flags(MsgInfo
*msginfo
, FILE *fp
)
1038 MsgPermFlags flags
= msginfo
->flags
.perm_flags
;
1039 int w_err
= 0, wrote
= 0;
1040 WRITE_CACHE_DATA_INT(msginfo
->msgnum
, fp
);
1041 WRITE_CACHE_DATA_INT(flags
, fp
);
1042 return w_err
? -1 : wrote
;
1045 static int msgcache_write_tags(MsgInfo
*msginfo
, FILE *fp
)
1047 GSList
*cur
= msginfo
->tags
;
1048 int w_err
= 0, wrote
= 0;
1050 WRITE_CACHE_DATA_INT(msginfo
->msgnum
, fp
);
1051 for (; cur
; cur
= cur
->next
) {
1052 gint id
= GPOINTER_TO_INT(cur
->data
);
1053 if (tags_get_tag(id
) != NULL
) {
1054 WRITE_CACHE_DATA_INT(id
, fp
);
1057 WRITE_CACHE_DATA_INT(-1, fp
);
1059 return w_err
? -1 : wrote
;
1073 static void msgcache_write_func(gpointer key
, gpointer value
, gpointer user_data
)
1076 struct write_fps
*write_fps
;
1079 msginfo
= (MsgInfo
*)value
;
1080 write_fps
= user_data
;
1082 if (write_fps
->cache_fp
) {
1083 tmp
= msgcache_write_cache(msginfo
, write_fps
->cache_fp
);
1085 write_fps
->error
= 1;
1087 write_fps
->cache_size
+= tmp
;
1089 if (write_fps
->mark_fp
) {
1090 tmp
= msgcache_write_flags(msginfo
, write_fps
->mark_fp
);
1092 write_fps
->error
= 1;
1094 write_fps
->mark_size
+= tmp
;
1096 if (write_fps
->tags_fp
) {
1097 tmp
= msgcache_write_tags(msginfo
, write_fps
->tags_fp
);
1099 write_fps
->error
= 1;
1101 write_fps
->tags_size
+= tmp
;
1105 gint
msgcache_write(const gchar
*cache_file
, const gchar
*mark_file
, const gchar
*tags_file
, MsgCache
*cache
)
1107 struct write_fps write_fps
;
1108 gchar
*new_cache
, *new_mark
, *new_tags
;
1109 int w_err
= 0, wrote
= 0;
1112 cm_return_val_if_fail(cache
!= NULL
, -1);
1114 new_cache
= g_strconcat(cache_file
, ".new", NULL
);
1115 new_mark
= g_strconcat(mark_file
, ".new", NULL
);
1116 new_tags
= g_strconcat(tags_file
, ".new", NULL
);
1118 write_fps
.error
= 0;
1119 write_fps
.cache_size
= 0;
1120 write_fps
.mark_size
= 0;
1121 write_fps
.tags_size
= 0;
1123 /* open files and write headers */
1126 write_fps
.cache_fp
= msgcache_open_data_file(new_cache
, CACHE_VERSION
,
1127 DATA_WRITE
, NULL
, 0);
1128 if (write_fps
.cache_fp
== NULL
) {
1134 WRITE_CACHE_DATA(CS_UTF_8
, write_fps
.cache_fp
);
1136 write_fps
.cache_fp
= NULL
;
1140 g_warning("failed to write charset");
1141 if (write_fps
.cache_fp
)
1142 claws_fclose(write_fps
.cache_fp
);
1143 claws_unlink(new_cache
);
1151 write_fps
.mark_fp
= msgcache_open_data_file(new_mark
, MARK_VERSION
,
1152 DATA_WRITE
, NULL
, 0);
1153 if (write_fps
.mark_fp
== NULL
) {
1154 if (write_fps
.cache_fp
)
1155 claws_fclose(write_fps
.cache_fp
);
1156 claws_unlink(new_cache
);
1163 write_fps
.mark_fp
= NULL
;
1167 write_fps
.tags_fp
= msgcache_open_data_file(new_tags
, TAGS_VERSION
,
1168 DATA_WRITE
, NULL
, 0);
1169 if (write_fps
.tags_fp
== NULL
) {
1170 if (write_fps
.cache_fp
)
1171 claws_fclose(write_fps
.cache_fp
);
1172 if (write_fps
.mark_fp
)
1173 claws_fclose(write_fps
.mark_fp
);
1174 claws_unlink(new_cache
);
1175 claws_unlink(new_mark
);
1182 write_fps
.tags_fp
= NULL
;
1185 debug_print("\tWriting message cache to %s and %s...\n", new_cache
, new_mark
);
1187 if (write_fps
.cache_fp
&& change_file_mode_rw(write_fps
.cache_fp
, new_cache
) < 0)
1188 FILE_OP_ERROR(new_cache
, "chmod");
1190 /* headers written, note file size */
1191 if (write_fps
.cache_fp
)
1192 write_fps
.cache_size
= ftell(write_fps
.cache_fp
);
1193 if (write_fps
.mark_fp
)
1194 write_fps
.mark_size
= ftell(write_fps
.mark_fp
);
1195 if (write_fps
.tags_fp
)
1196 write_fps
.tags_size
= ftell(write_fps
.tags_fp
);
1198 /* write data to the files */
1199 g_hash_table_foreach(cache
->msgnum_table
, msgcache_write_func
, (gpointer
)&write_fps
);
1202 if (write_fps
.cache_fp
)
1203 write_fps
.error
|= (claws_safe_fclose(write_fps
.cache_fp
) != 0);
1204 if (write_fps
.mark_fp
)
1205 write_fps
.error
|= (claws_safe_fclose(write_fps
.mark_fp
) != 0);
1206 if (write_fps
.tags_fp
)
1207 write_fps
.error
|= (claws_safe_fclose(write_fps
.tags_fp
) != 0);
1210 if (write_fps
.error
!= 0) {
1211 /* in case of error, forget all */
1212 claws_unlink(new_cache
);
1213 claws_unlink(new_mark
);
1214 claws_unlink(new_tags
);
1222 move_file(new_cache
, cache_file
, TRUE
);
1224 move_file(new_mark
, mark_file
, TRUE
);
1226 move_file(new_tags
, tags_file
, TRUE
);
1227 cache
->last_access
= time(NULL
);
1233 debug_print("done.\n");