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/>.
22 #include "claws-features.h"
30 #include <glib/gi18n.h>
33 # define MAP_FAILED ((char *) -1)
35 # include <sys/mman.h>
37 #include <sys/types.h>
48 #include "prefs_common.h"
49 #include "file-utils.h"
51 #if G_BYTE_ORDER == G_BIG_ENDIAN
53 ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \
54 (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24))
56 #define MMAP_TO_GUINT32(x) \
58 ((x[2]&0xff) << 8) | \
59 ((x[1]&0xff) << 16) | \
62 #define MMAP_TO_GUINT32_SWAPPED(x) \
64 ((x[1]&0xff) << 8) | \
65 ((x[2]&0xff) << 16) | \
68 static gboolean msgcache_use_mmap_read
= TRUE
;
71 #define bswap_32(x) (x)
73 #define MMAP_TO_GUINT32(x) \
75 ((x[1]&0xff) << 8) | \
76 ((x[2]&0xff) << 16) | \
79 #define MMAP_TO_GUINT32_SWAPPED(x) \
81 ((x[1]&0xff) << 8) | \
82 ((x[2]&0xff) << 16) | \
85 static gboolean msgcache_use_mmap_read
= TRUE
;
88 static gboolean swapping
= TRUE
;
98 GHashTable
*msgnum_table
;
99 GHashTable
*msgid_table
;
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
;
123 MsgCache
*msgcache_new(void)
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
);
135 static gboolean
msgcache_msginfo_free_func(gpointer num
, gpointer msginfo
, gpointer user_data
)
137 procmsg_msginfo_free((MsgInfo
**)&msginfo
);
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
);
151 void msgcache_add_msg(MsgCache
*cache
, MsgInfo
*msginfo
)
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
)
174 cm_return_if_fail(cache
!= NULL
);
176 msginfo
= (MsgInfo
*) g_hash_table_lookup(cache
->msgnum_table
, &msgnum
);
180 cache
->memusage
-= procmsg_msginfo_memusage(msginfo
);
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
);
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
;
224 MsgInfo
*msgcache_get_msg(MsgCache
*cache
, guint num
)
228 cm_return_val_if_fail(cache
!= NULL
, NULL
);
230 msginfo
= g_hash_table_lookup(cache
->msgnum_table
, &num
);
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
)
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
);
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
;
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
);
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); \
300 total_len += tmp_len; \
303 #define READ_CACHE_DATA_INT(n, fp) \
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); \
315 n = swapping ? bswap_32(idata) : (idata);\
318 #define GET_CACHE_DATA_INT(n) \
321 g_print("error at rem_len:%d\n", rem_len); \
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); \
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); \
343 total_len += tmp_len; \
344 walk_data += tmp_len; rem_len -= tmp_len; \
348 #define WRITE_CACHE_DATA_INT(n, fp) \
352 idata = (guint32)bswap_32(n); \
353 if (claws_fwrite(&idata, sizeof(idata), 1, fp) != 1) \
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; \
368 #define WRITE_CACHE_DATA(data, fp) \
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) \
383 #define PUT_CACHE_DATA(data) \
389 len = strlen(data); \
390 PUT_CACHE_DATA_INT(len); \
392 memcpy(walk_data, data, len); \
398 static FILE *msgcache_open_data_file(const gchar
*file
, guint version
,
400 gchar
*buf
, size_t buf_size
)
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");
413 if (change_file_mode_rw(fp
, file
) < 0)
414 FILE_OP_ERROR(file
, "chmod");
416 WRITE_CACHE_DATA_INT(version
, fp
);
418 g_warning("failed to write int");
426 if ((fp
= claws_fopen(file
, "rb")) == NULL
)
427 debug_print("Mark/Cache file '%s' not found\n", file
);
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
);
438 data_ver
= bswap_32(data_ver
);
441 if (mode
== DATA_READ
)
445 /* reopen with append mode */
447 if ((fp
= claws_fopen(file
, "ab")) == NULL
)
448 FILE_OP_ERROR(file
, "claws_fopen");
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
,
459 static gint
msgcache_read_cache_data_str(FILE *fp
, gchar
**str
,
460 StringConverter
*conv
)
462 gchar
*tmpstr
= NULL
;
468 if ((ni
= claws_fread(&len
, sizeof(len
), 1, fp
) != 1) ||
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
),
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
),
489 tmpstr
= g_try_malloc(len
+ 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",
505 *str
= conv
->convert(conv
, tmpstr
);
513 static gint
msgcache_get_cache_data_str(gchar
*src
, gchar
**str
, gint len
,
514 StringConverter
*conv
)
516 gchar
*tmpstr
= NULL
;
523 if(len
> 2*1024*1024) {
524 g_warning("read_data_str: refusing to allocate %d bytes.", len
);
528 tmpstr
= g_try_malloc(len
+ 1);
534 memcpy(tmpstr
, src
, len
);
538 *str
= conv
->convert(conv
, tmpstr
);
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
)
566 MsgTmpFlags tmp_flags
= 0;
567 gchar file_buf
[BUFFSIZE
];
570 gboolean error
= FALSE
;
571 StringConverter
*conv
= NULL
;
572 gchar
*srccharset
= NULL
;
573 const gchar
*dstcharset
= NULL
;
576 gint tmp_len
= 0, map_len
= -1;
577 char *cache_data
= NULL
;
580 cm_return_val_if_fail(cache_file
!= NULL
, NULL
);
581 cm_return_val_if_fail(item
!= NULL
, NULL
);
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
)
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) {
611 dstcharset
= CS_UTF_8
;
612 if (srccharset
== NULL
|| dstcharset
== NULL
) {
614 } else if (strcmp(srccharset
, dstcharset
) == 0) {
615 debug_print("using Noop Converter\n");
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
;
633 cache
= msgcache_new();
635 if (msgcache_use_mmap_read
== TRUE
) {
636 if (fstat(fileno(fp
), &st
) >= 0)
637 map_len
= st
.st_size
;
643 HANDLE hFile
, hMapping
;
644 hFile
= (HANDLE
) _get_osfhandle (fileno(fp
));
645 if (hFile
== (HANDLE
) -1)
647 hMapping
= CreateFileMapping(hFile
, NULL
, PAGE_WRITECOPY
, 0, 0, NULL
);
650 cache_data
= (unsigned char *)MapViewOfFile(hMapping
, FILE_MAP_COPY
, 0, 0, 0);
651 CloseHandle (hMapping
);
655 cache_data
= mmap(NULL
, map_len
, PROT_READ
, MAP_PRIVATE
, fileno(fp
), 0);
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
);
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
--) {
696 GET_CACHE_DATA(ref
, memusage
);
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
);
711 g_hash_table_insert(cache
->msgid_table
, msginfo
->msgid
, msginfo
);
714 while (claws_fread(&num
, sizeof(num
), 1, fp
) == 1) {
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
--) {
746 READ_CACHE_DATA(ref
, fp
, memusage
);
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
);
761 g_hash_table_insert(cache
->msgid_table
, msginfo
->msgid
, msginfo
);
765 if (cache_data
!= NULL
&& cache_data
!= MAP_FAILED
) {
767 UnmapViewOfFile((void*) cache_data
);
769 munmap(cache_data
, map_len
);
774 if (conv
->free
!= NULL
)
780 msgcache_destroy(cache
);
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
);
793 void msgcache_read_mark(MsgCache
*cache
, const gchar
*mark_file
)
797 MsgPermFlags perm_flags
;
800 char *cache_data
= NULL
;
802 gboolean error
= FALSE
;
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
)
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
;
828 HANDLE hFile
, hMapping
;
829 hFile
= (HANDLE
) _get_osfhandle (fileno(fp
));
830 if (hFile
== (HANDLE
) -1)
832 hMapping
= CreateFileMapping(hFile
, NULL
, PAGE_WRITECOPY
, 0, 0, NULL
);
835 cache_data
= (unsigned char *)MapViewOfFile(hMapping
, FILE_MAP_COPY
, 0, 0, 0);
836 CloseHandle (hMapping
);
840 cache_data
= mmap(NULL
, map_len
, PROT_READ
, MAP_PRIVATE
, fileno(fp
), 0);
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
);
851 GET_CACHE_DATA_INT(num
);
852 GET_CACHE_DATA_INT(perm_flags
);
853 msginfo
= g_hash_table_lookup(cache
->msgnum_table
, &num
);
855 msginfo
->flags
.perm_flags
= perm_flags
;
859 while (claws_fread(&num
, sizeof(num
), 1, fp
) == 1) {
862 if (claws_fread(&perm_flags
, sizeof(perm_flags
), 1, fp
) != 1) {
867 perm_flags
= bswap_32(perm_flags
);
868 msginfo
= g_hash_table_lookup(cache
->msgnum_table
, &num
);
870 msginfo
->flags
.perm_flags
= perm_flags
;
875 if (cache_data
!= NULL
&& cache_data
!= MAP_FAILED
) {
877 UnmapViewOfFile((void*) cache_data
);
879 munmap(cache_data
, map_len
);
884 debug_print("error reading cache mark from %s\n", mark_file
);
888 void msgcache_read_tags(MsgCache
*cache
, const gchar
*tags_file
)
894 char *cache_data
= NULL
;
896 gboolean error
= FALSE
;
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
)
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
;
922 HANDLE hFile
, hMapping
;
923 hFile
= (HANDLE
) _get_osfhandle (fileno(fp
));
924 if (hFile
== (HANDLE
) -1)
926 hMapping
= CreateFileMapping(hFile
, NULL
, PAGE_WRITECOPY
, 0, 0, NULL
);
929 cache_data
= (unsigned char *)MapViewOfFile(hMapping
, FILE_MAP_COPY
, 0, 0, 0);
930 CloseHandle (hMapping
);
934 cache_data
= mmap(NULL
, map_len
, PROT_READ
, MAP_PRIVATE
, fileno(fp
), 0);
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
);
946 GET_CACHE_DATA_INT(num
);
947 msginfo
= g_hash_table_lookup(cache
->msgnum_table
, &num
);
949 g_slist_free(msginfo
->tags
);
950 msginfo
->tags
= NULL
;
952 GET_CACHE_DATA_INT(id
);
954 msginfo
->tags
= g_slist_prepend(
956 GINT_TO_POINTER(id
));
959 msginfo
->tags
= g_slist_reverse(msginfo
->tags
);
963 while (claws_fread(&num
, sizeof(num
), 1, fp
) == 1) {
967 msginfo
= g_hash_table_lookup(cache
->msgnum_table
, &num
);
969 g_slist_free(msginfo
->tags
);
970 msginfo
->tags
= NULL
;
972 if (claws_fread(&id
, sizeof(id
), 1, fp
) != 1)
977 msginfo
->tags
= g_slist_prepend(
979 GINT_TO_POINTER(id
));
982 msginfo
->tags
= g_slist_reverse(msginfo
->tags
);
987 if (cache_data
!= NULL
&& cache_data
!= MAP_FAILED
) {
989 UnmapViewOfFile((void*) cache_data
);
991 munmap(cache_data
, map_len
);
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
;
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
;
1071 static void msgcache_write_func(gpointer key
, gpointer value
, gpointer user_data
)
1074 struct write_fps
*write_fps
;
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
);
1083 write_fps
->error
= 1;
1085 write_fps
->cache_size
+= tmp
;
1087 if (write_fps
->mark_fp
) {
1088 tmp
= msgcache_write_flags(msginfo
, write_fps
->mark_fp
);
1090 write_fps
->error
= 1;
1092 write_fps
->mark_size
+= tmp
;
1094 if (write_fps
->tags_fp
) {
1095 tmp
= msgcache_write_tags(msginfo
, write_fps
->tags_fp
);
1097 write_fps
->error
= 1;
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;
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 */
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
) {
1132 WRITE_CACHE_DATA(CS_UTF_8
, write_fps
.cache_fp
);
1134 write_fps
.cache_fp
= NULL
;
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
);
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
);
1161 write_fps
.mark_fp
= NULL
;
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
);
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
);
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
);
1220 move_file(new_cache
, cache_file
, TRUE
);
1222 move_file(new_mark
, mark_file
, TRUE
);
1224 move_file(new_tags
, tags_file
, TRUE
);
1225 cache
->last_access
= time(NULL
);
1231 debug_print("done.\n");