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"
31 #include <glib/gi18n.h>
34 # define MAP_FAILED ((char *) -1)
36 # include <sys/mman.h>
38 #include <sys/types.h>
49 #include "prefs_common.h"
51 #ifdef HAVE_FWRITE_UNLOCKED
52 #define SC_FWRITE fwrite_unlocked
54 #define SC_FWRITE fwrite
57 #if G_BYTE_ORDER == G_BIG_ENDIAN
59 ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \
60 (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24))
62 #define MMAP_TO_GUINT32(x) \
64 ((x[2]&0xff) << 8) | \
65 ((x[1]&0xff) << 16) | \
68 #define MMAP_TO_GUINT32_SWAPPED(x) \
70 ((x[1]&0xff) << 8) | \
71 ((x[2]&0xff) << 16) | \
74 static gboolean msgcache_use_mmap_read
= TRUE
;
77 #define bswap_32(x) (x)
79 #define MMAP_TO_GUINT32(x) \
81 ((x[1]&0xff) << 8) | \
82 ((x[2]&0xff) << 16) | \
85 #define MMAP_TO_GUINT32_SWAPPED(x) \
87 ((x[1]&0xff) << 8) | \
88 ((x[2]&0xff) << 16) | \
91 static gboolean msgcache_use_mmap_read
= TRUE
;
94 static gboolean swapping
= TRUE
;
104 GHashTable
*msgnum_table
;
105 GHashTable
*msgid_table
;
110 typedef struct _StringConverter StringConverter
;
111 struct _StringConverter
{
112 gchar
*(*convert
) (StringConverter
*converter
, gchar
*srcstr
);
113 void (*free
) (StringConverter
*converter
);
116 typedef struct _StrdupConverter StrdupConverter
;
117 struct _StrdupConverter
{
118 StringConverter converter
;
121 typedef struct _CharsetConverter CharsetConverter
;
122 struct _CharsetConverter
{
123 StringConverter converter
;
129 MsgCache
*msgcache_new(void)
133 cache
= g_new0(MsgCache
, 1),
134 cache
->msgnum_table
= g_hash_table_new(g_int_hash
, g_int_equal
);
135 cache
->msgid_table
= g_hash_table_new(g_str_hash
, g_str_equal
);
136 cache
->last_access
= time(NULL
);
141 static gboolean
msgcache_msginfo_free_func(gpointer num
, gpointer msginfo
, gpointer user_data
)
143 procmsg_msginfo_free((MsgInfo
**)&msginfo
);
147 void msgcache_destroy(MsgCache
*cache
)
149 cm_return_if_fail(cache
!= NULL
);
151 g_hash_table_foreach_remove(cache
->msgnum_table
, msgcache_msginfo_free_func
, NULL
);
152 g_hash_table_destroy(cache
->msgid_table
);
153 g_hash_table_destroy(cache
->msgnum_table
);
157 void msgcache_add_msg(MsgCache
*cache
, MsgInfo
*msginfo
)
161 cm_return_if_fail(cache
!= NULL
);
162 cm_return_if_fail(msginfo
!= NULL
);
164 newmsginfo
= procmsg_msginfo_new_ref(msginfo
);
165 g_hash_table_insert(cache
->msgnum_table
, &newmsginfo
->msgnum
, newmsginfo
);
166 if(newmsginfo
->msgid
!= NULL
)
167 g_hash_table_insert(cache
->msgid_table
, newmsginfo
->msgid
, newmsginfo
);
168 cache
->memusage
+= procmsg_msginfo_memusage(msginfo
);
169 cache
->last_access
= time(NULL
);
171 msginfo
->folder
->cache_dirty
= TRUE
;
173 debug_print("Cache size: %d messages, %u bytes\n", g_hash_table_size(cache
->msgnum_table
), cache
->memusage
);
176 void msgcache_remove_msg(MsgCache
*cache
, guint msgnum
)
180 cm_return_if_fail(cache
!= NULL
);
182 msginfo
= (MsgInfo
*) g_hash_table_lookup(cache
->msgnum_table
, &msgnum
);
186 cache
->memusage
-= procmsg_msginfo_memusage(msginfo
);
188 g_hash_table_remove(cache
->msgid_table
, msginfo
->msgid
);
189 g_hash_table_remove(cache
->msgnum_table
, &msginfo
->msgnum
);
191 msginfo
->folder
->cache_dirty
= TRUE
;
193 procmsg_msginfo_free(&msginfo
);
194 cache
->last_access
= time(NULL
);
197 debug_print("Cache size: %d messages, %u bytes\n", g_hash_table_size(cache
->msgnum_table
), cache
->memusage
);
200 void msgcache_update_msg(MsgCache
*cache
, MsgInfo
*msginfo
)
202 MsgInfo
*oldmsginfo
, *newmsginfo
;
204 cm_return_if_fail(cache
!= NULL
);
205 cm_return_if_fail(msginfo
!= NULL
);
207 oldmsginfo
= g_hash_table_lookup(cache
->msgnum_table
, &msginfo
->msgnum
);
208 if(oldmsginfo
&& oldmsginfo
->msgid
)
209 g_hash_table_remove(cache
->msgid_table
, oldmsginfo
->msgid
);
211 g_hash_table_remove(cache
->msgnum_table
, &oldmsginfo
->msgnum
);
212 cache
->memusage
-= procmsg_msginfo_memusage(oldmsginfo
);
213 procmsg_msginfo_free(&oldmsginfo
);
216 newmsginfo
= procmsg_msginfo_new_ref(msginfo
);
217 g_hash_table_insert(cache
->msgnum_table
, &newmsginfo
->msgnum
, newmsginfo
);
218 if(newmsginfo
->msgid
)
219 g_hash_table_insert(cache
->msgid_table
, newmsginfo
->msgid
, newmsginfo
);
220 cache
->memusage
+= procmsg_msginfo_memusage(newmsginfo
);
221 cache
->last_access
= time(NULL
);
223 debug_print("Cache size: %d messages, %u bytes\n", g_hash_table_size(cache
->msgnum_table
), cache
->memusage
);
225 msginfo
->folder
->cache_dirty
= TRUE
;
230 MsgInfo
*msgcache_get_msg(MsgCache
*cache
, guint num
)
234 cm_return_val_if_fail(cache
!= NULL
, NULL
);
236 msginfo
= g_hash_table_lookup(cache
->msgnum_table
, &num
);
239 cache
->last_access
= time(NULL
);
241 return procmsg_msginfo_new_ref(msginfo
);
244 MsgInfo
*msgcache_get_msg_by_id(MsgCache
*cache
, const gchar
*msgid
)
248 cm_return_val_if_fail(cache
!= NULL
, NULL
);
249 cm_return_val_if_fail(msgid
!= NULL
, NULL
);
251 msginfo
= g_hash_table_lookup(cache
->msgid_table
, msgid
);
254 cache
->last_access
= time(NULL
);
256 return procmsg_msginfo_new_ref(msginfo
);
259 static void msgcache_get_msg_list_func(gpointer key
, gpointer value
, gpointer user_data
)
261 MsgInfoList
**listptr
= user_data
;
262 MsgInfo
*msginfo
= value
;
264 *listptr
= g_slist_prepend(*listptr
, procmsg_msginfo_new_ref(msginfo
));
267 MsgInfoList
*msgcache_get_msg_list(MsgCache
*cache
)
269 MsgInfoList
*msg_list
= NULL
;
271 cm_return_val_if_fail(cache
!= NULL
, NULL
);
273 g_hash_table_foreach((GHashTable
*)cache
->msgnum_table
, msgcache_get_msg_list_func
, (gpointer
)&msg_list
);
274 cache
->last_access
= time(NULL
);
276 msg_list
= g_slist_reverse(msg_list
);
281 time_t msgcache_get_last_access_time(MsgCache
*cache
)
283 cm_return_val_if_fail(cache
!= NULL
, 0);
285 return cache
->last_access
;
288 gint
msgcache_get_memory_usage(MsgCache
*cache
)
290 cm_return_val_if_fail(cache
!= NULL
, 0);
292 return cache
->memusage
;
296 * Cache saving functions
299 #define READ_CACHE_DATA(data, fp, total_len) \
301 if ((tmp_len = msgcache_read_cache_data_str(fp, &data, conv)) < 0) { \
302 procmsg_msginfo_free(&msginfo); \
306 total_len += tmp_len; \
309 #define READ_CACHE_DATA_INT(n, fp) \
314 if ((ni = fread(&idata, sizeof(idata), 1, fp)) != 1) { \
315 g_warning("read_int: Cache data corrupted, read %zd of %zd at " \
316 "offset %ld", ni, sizeof(idata), ftell(fp)); \
317 procmsg_msginfo_free(&msginfo); \
321 n = swapping ? bswap_32(idata) : (idata);\
324 #define GET_CACHE_DATA_INT(n) \
327 g_print("error at rem_len:%d\n", rem_len); \
331 n = (swapping ? (MMAP_TO_GUINT32_SWAPPED(walk_data)):(MMAP_TO_GUINT32(walk_data))); \
332 walk_data += 4; rem_len -= 4; \
335 #define GET_CACHE_DATA(data, total_len) \
337 GET_CACHE_DATA_INT(tmp_len); \
338 if (rem_len < tmp_len) { \
339 g_print("error at rem_len:%d (tmp_len %d)\n", rem_len, tmp_len); \
343 if ((tmp_len = msgcache_get_cache_data_str(walk_data, &data, tmp_len, conv)) < 0) { \
344 g_print("error at rem_len:%d\n", rem_len);\
345 procmsg_msginfo_free(&msginfo); \
349 total_len += tmp_len; \
350 walk_data += tmp_len; rem_len -= tmp_len; \
354 #define WRITE_CACHE_DATA_INT(n, fp) \
358 idata = (guint32)bswap_32(n); \
359 if (SC_FWRITE(&idata, sizeof(idata), 1, fp) != 1) \
364 #define PUT_CACHE_DATA_INT(n) \
366 walk_data[0]=(((guint32)n)&0x000000ff); \
367 walk_data[1]=(((guint32)n)&0x0000ff00)>>8; \
368 walk_data[2]=(((guint32)n)&0x00ff0000)>>16; \
369 walk_data[3]=(((guint32)n)&0xff000000)>>24; \
374 #define WRITE_CACHE_DATA(data, fp) \
380 len = strlen(data); \
381 WRITE_CACHE_DATA_INT(len, fp); \
382 if (w_err == 0 && len > 0) { \
383 if (SC_FWRITE(data, 1, len, fp) != len) \
389 #define PUT_CACHE_DATA(data) \
395 len = strlen(data); \
396 PUT_CACHE_DATA_INT(len); \
398 memcpy(walk_data, data, len); \
404 static FILE *msgcache_open_data_file(const gchar
*file
, guint version
,
406 gchar
*buf
, size_t buf_size
)
411 cm_return_val_if_fail(file
!= NULL
, NULL
);
413 if (mode
== DATA_WRITE
) {
414 int w_err
= 0, wrote
= 0;
415 if ((fp
= g_fopen(file
, "wb")) == NULL
) {
416 FILE_OP_ERROR(file
, "fopen");
419 if (change_file_mode_rw(fp
, file
) < 0)
420 FILE_OP_ERROR(file
, "chmod");
422 WRITE_CACHE_DATA_INT(version
, fp
);
424 g_warning("failed to write int");
432 if ((fp
= g_fopen(file
, "rb")) == NULL
)
433 debug_print("Mark/Cache file '%s' not found\n", file
);
435 if (buf
&& buf_size
> 0)
436 setvbuf(fp
, buf
, _IOFBF
, buf_size
);
437 if (fread(&data_ver
, sizeof(data_ver
), 1, fp
) != 1 ||
438 version
!= bswap_32(data_ver
)) {
439 g_message("%s: Mark/Cache version is different (%u != %u).\n",
440 file
, bswap_32(data_ver
), version
);
444 data_ver
= bswap_32(data_ver
);
447 if (mode
== DATA_READ
)
451 /* reopen with append mode */
453 if ((fp
= g_fopen(file
, "ab")) == NULL
)
454 FILE_OP_ERROR(file
, "fopen");
456 /* open with overwrite mode if mark file doesn't exist or
457 version is different */
458 fp
= msgcache_open_data_file(file
, version
, DATA_WRITE
, buf
,
465 static gint
msgcache_read_cache_data_str(FILE *fp
, gchar
**str
,
466 StringConverter
*conv
)
468 gchar
*tmpstr
= NULL
;
474 if ((ni
= fread(&len
, sizeof(len
), 1, fp
) != 1) ||
476 g_warning("read_data_str: Cache data (len) corrupted, read %zd "
477 "of %zd bytes at offset %ld", ni
, sizeof(len
),
482 if ((ni
= fread(&len
, sizeof(len
), 1, fp
) != 1) ||
483 bswap_32(len
) > G_MAXINT
) {
484 g_warning("read_data_str: Cache data (len) corrupted, read %zd "
485 "of %zd bytes at offset %ld", ni
, sizeof(len
),
495 tmpstr
= g_try_malloc(len
+ 1);
501 if ((ni
= fread(tmpstr
, 1, len
, fp
)) != len
) {
502 g_warning("read_data_str: Cache data corrupted, read %zd of %u "
503 "bytes at offset %ld",
511 *str
= conv
->convert(conv
, tmpstr
);
519 static gint
msgcache_get_cache_data_str(gchar
*src
, gchar
**str
, gint len
,
520 StringConverter
*conv
)
522 gchar
*tmpstr
= NULL
;
529 if(len
> 2*1024*1024) {
530 g_warning("read_data_str: refusing to allocate %d bytes.", len
);
534 tmpstr
= g_try_malloc(len
+ 1);
540 memcpy(tmpstr
, src
, len
);
544 *str
= conv
->convert(conv
, tmpstr
);
552 static gchar
*strconv_charset_convert(StringConverter
*conv
, gchar
*srcstr
)
554 CharsetConverter
*charsetconv
= (CharsetConverter
*) conv
;
556 return conv_codeset_strdup(srcstr
, charsetconv
->srccharset
, charsetconv
->dstcharset
);
559 static void strconv_charset_free(StringConverter
*conv
)
561 CharsetConverter
*charsetconv
= (CharsetConverter
*) conv
;
563 g_free(charsetconv
->srccharset
);
564 g_free(charsetconv
->dstcharset
);
567 MsgCache
*msgcache_read_cache(FolderItem
*item
, const gchar
*cache_file
)
572 MsgTmpFlags tmp_flags
= 0;
573 gchar file_buf
[BUFFSIZE
];
576 gboolean error
= FALSE
;
577 StringConverter
*conv
= NULL
;
578 gchar
*srccharset
= NULL
;
579 const gchar
*dstcharset
= NULL
;
582 gint tmp_len
= 0, map_len
= -1;
583 char *cache_data
= NULL
;
586 cm_return_val_if_fail(cache_file
!= NULL
, NULL
);
587 cm_return_val_if_fail(item
!= NULL
, NULL
);
591 /* In case we can't open the mark file with MARK_VERSION, check if we can open it with the
592 * swapped MARK_VERSION. As msgcache_open_data_file swaps it too, if this succeeds,
593 * it means it's the old version (not little-endian) on a big-endian machine. The code has
594 * no effect on x86 as their file doesn't change. */
596 if ((fp
= msgcache_open_data_file
597 (cache_file
, CACHE_VERSION
, DATA_READ
, file_buf
, sizeof(file_buf
))) == NULL
) {
598 if ((fp
= msgcache_open_data_file
599 (cache_file
, bswap_32(CACHE_VERSION
), DATA_READ
, file_buf
, sizeof(file_buf
))) == NULL
)
605 debug_print("\tReading %sswapped message cache from %s...\n", swapping
?"":"un", cache_file
);
607 if (folder_has_parent_of_type(item
, F_QUEUE
)) {
608 tmp_flags
|= MSG_QUEUED
;
609 } else if (folder_has_parent_of_type(item
, F_DRAFT
)) {
610 tmp_flags
|= MSG_DRAFT
;
613 if (msgcache_read_cache_data_str(fp
, &srccharset
, NULL
) < 0) {
617 dstcharset
= CS_UTF_8
;
618 if (srccharset
== NULL
|| dstcharset
== NULL
) {
620 } else if (strcmp(srccharset
, dstcharset
) == 0) {
621 debug_print("using Noop Converter\n");
625 CharsetConverter
*charsetconv
;
627 debug_print("using CharsetConverter\n");
629 charsetconv
= g_new0(CharsetConverter
, 1);
630 charsetconv
->converter
.convert
= strconv_charset_convert
;
631 charsetconv
->converter
.free
= strconv_charset_free
;
632 charsetconv
->srccharset
= g_strdup(srccharset
);
633 charsetconv
->dstcharset
= g_strdup(dstcharset
);
635 conv
= (StringConverter
*) charsetconv
;
639 cache
= msgcache_new();
641 if (msgcache_use_mmap_read
== TRUE
) {
642 if (fstat(fileno(fp
), &st
) >= 0)
643 map_len
= st
.st_size
;
649 HANDLE hFile
, hMapping
;
650 hFile
= (HANDLE
) _get_osfhandle (fileno(fp
));
651 if (hFile
== (HANDLE
) -1)
653 hMapping
= CreateFileMapping(hFile
, NULL
, PAGE_WRITECOPY
, 0, 0, NULL
);
656 cache_data
= (unsigned char *)MapViewOfFile(hMapping
, FILE_MAP_COPY
, 0, 0, 0);
657 CloseHandle (hMapping
);
661 cache_data
= mmap(NULL
, map_len
, PROT_READ
, MAP_PRIVATE
, fileno(fp
), 0);
667 if (cache_data
!= NULL
&& cache_data
!= MAP_FAILED
) {
668 int rem_len
= map_len
-ftell(fp
);
669 char *walk_data
= cache_data
+ftell(fp
);
672 GET_CACHE_DATA_INT(num
);
674 msginfo
= procmsg_msginfo_new();
675 msginfo
->msgnum
= num
;
676 memusage
+= sizeof(MsgInfo
);
678 GET_CACHE_DATA_INT(msginfo
->size
);
679 GET_CACHE_DATA_INT(msginfo
->mtime
);
680 GET_CACHE_DATA_INT(msginfo
->date_t
);
681 GET_CACHE_DATA_INT(msginfo
->flags
.tmp_flags
);
683 GET_CACHE_DATA(msginfo
->fromname
, memusage
);
685 GET_CACHE_DATA(msginfo
->date
, memusage
);
686 GET_CACHE_DATA(msginfo
->from
, memusage
);
687 GET_CACHE_DATA(msginfo
->to
, memusage
);
688 GET_CACHE_DATA(msginfo
->cc
, memusage
);
689 GET_CACHE_DATA(msginfo
->newsgroups
, memusage
);
690 GET_CACHE_DATA(msginfo
->subject
, memusage
);
691 GET_CACHE_DATA(msginfo
->msgid
, memusage
);
692 GET_CACHE_DATA(msginfo
->inreplyto
, memusage
);
693 GET_CACHE_DATA(msginfo
->xref
, memusage
);
695 GET_CACHE_DATA_INT(msginfo
->planned_download
);
696 GET_CACHE_DATA_INT(msginfo
->total_size
);
697 GET_CACHE_DATA_INT(refnum
);
699 for (; refnum
!= 0; refnum
--) {
702 GET_CACHE_DATA(ref
, memusage
);
705 msginfo
->references
=
706 g_slist_prepend(msginfo
->references
, ref
);
708 if (msginfo
->references
)
709 msginfo
->references
=
710 g_slist_reverse(msginfo
->references
);
712 msginfo
->folder
= item
;
713 msginfo
->flags
.tmp_flags
|= tmp_flags
;
715 g_hash_table_insert(cache
->msgnum_table
, &msginfo
->msgnum
, msginfo
);
717 g_hash_table_insert(cache
->msgid_table
, msginfo
->msgid
, msginfo
);
720 while (fread(&num
, sizeof(num
), 1, fp
) == 1) {
724 msginfo
= procmsg_msginfo_new();
725 msginfo
->msgnum
= num
;
726 memusage
+= sizeof(MsgInfo
);
728 READ_CACHE_DATA_INT(msginfo
->size
, fp
);
729 READ_CACHE_DATA_INT(msginfo
->mtime
, fp
);
730 READ_CACHE_DATA_INT(msginfo
->date_t
, fp
);
731 READ_CACHE_DATA_INT(msginfo
->flags
.tmp_flags
, fp
);
733 READ_CACHE_DATA(msginfo
->fromname
, fp
, memusage
);
735 READ_CACHE_DATA(msginfo
->date
, fp
, memusage
);
736 READ_CACHE_DATA(msginfo
->from
, fp
, memusage
);
737 READ_CACHE_DATA(msginfo
->to
, fp
, memusage
);
738 READ_CACHE_DATA(msginfo
->cc
, fp
, memusage
);
739 READ_CACHE_DATA(msginfo
->newsgroups
, fp
, memusage
);
740 READ_CACHE_DATA(msginfo
->subject
, fp
, memusage
);
741 READ_CACHE_DATA(msginfo
->msgid
, fp
, memusage
);
742 READ_CACHE_DATA(msginfo
->inreplyto
, fp
, memusage
);
743 READ_CACHE_DATA(msginfo
->xref
, fp
, memusage
);
745 READ_CACHE_DATA_INT(msginfo
->planned_download
, fp
);
746 READ_CACHE_DATA_INT(msginfo
->total_size
, fp
);
747 READ_CACHE_DATA_INT(refnum
, fp
);
749 for (; refnum
!= 0; refnum
--) {
752 READ_CACHE_DATA(ref
, fp
, memusage
);
755 msginfo
->references
=
756 g_slist_prepend(msginfo
->references
, ref
);
758 if (msginfo
->references
)
759 msginfo
->references
=
760 g_slist_reverse(msginfo
->references
);
762 msginfo
->folder
= item
;
763 msginfo
->flags
.tmp_flags
|= tmp_flags
;
765 g_hash_table_insert(cache
->msgnum_table
, &msginfo
->msgnum
, msginfo
);
767 g_hash_table_insert(cache
->msgid_table
, msginfo
->msgid
, msginfo
);
771 if (cache_data
!= NULL
&& cache_data
!= MAP_FAILED
) {
773 UnmapViewOfFile((void*) cache_data
);
775 munmap(cache_data
, map_len
);
780 if (conv
->free
!= NULL
)
786 msgcache_destroy(cache
);
790 cache
->last_access
= time(NULL
);
791 cache
->memusage
= memusage
;
793 debug_print("done. (%d items read)\n", g_hash_table_size(cache
->msgnum_table
));
794 debug_print("Cache size: %d messages, %u bytes\n", g_hash_table_size(cache
->msgnum_table
), cache
->memusage
);
799 void msgcache_read_mark(MsgCache
*cache
, const gchar
*mark_file
)
803 MsgPermFlags perm_flags
;
806 char *cache_data
= NULL
;
808 gboolean error
= FALSE
;
812 /* In case we can't open the mark file with MARK_VERSION, check if we can open it with the
813 * swapped MARK_VERSION. As msgcache_open_data_file swaps it too, if this succeeds,
814 * it means it's the old version (not little-endian) on a big-endian machine. The code has
815 * no effect on x86 as their file doesn't change. */
817 if ((fp
= msgcache_open_data_file(mark_file
, MARK_VERSION
, DATA_READ
, NULL
, 0)) == NULL
) {
818 /* see if it isn't swapped ? */
819 if ((fp
= msgcache_open_data_file(mark_file
, bswap_32(MARK_VERSION
), DATA_READ
, NULL
, 0)) == NULL
)
822 swapping
= FALSE
; /* yay */
824 debug_print("reading %sswapped mark file.\n", swapping
?"":"un");
826 if (msgcache_use_mmap_read
) {
827 if (fstat(fileno(fp
), &st
) >= 0)
828 map_len
= st
.st_size
;
834 HANDLE hFile
, hMapping
;
835 hFile
= (HANDLE
) _get_osfhandle (fileno(fp
));
836 if (hFile
== (HANDLE
) -1)
838 hMapping
= CreateFileMapping(hFile
, NULL
, PAGE_WRITECOPY
, 0, 0, NULL
);
841 cache_data
= (unsigned char *)MapViewOfFile(hMapping
, FILE_MAP_COPY
, 0, 0, 0);
842 CloseHandle (hMapping
);
846 cache_data
= mmap(NULL
, map_len
, PROT_READ
, MAP_PRIVATE
, fileno(fp
), 0);
852 if (cache_data
!= NULL
&& cache_data
!= MAP_FAILED
) {
853 int rem_len
= map_len
-ftell(fp
);
854 char *walk_data
= cache_data
+ftell(fp
);
857 GET_CACHE_DATA_INT(num
);
858 GET_CACHE_DATA_INT(perm_flags
);
859 msginfo
= g_hash_table_lookup(cache
->msgnum_table
, &num
);
861 msginfo
->flags
.perm_flags
= perm_flags
;
865 while (fread(&num
, sizeof(num
), 1, fp
) == 1) {
868 if (fread(&perm_flags
, sizeof(perm_flags
), 1, fp
) != 1) {
873 perm_flags
= bswap_32(perm_flags
);
874 msginfo
= g_hash_table_lookup(cache
->msgnum_table
, &num
);
876 msginfo
->flags
.perm_flags
= perm_flags
;
881 if (cache_data
!= NULL
&& cache_data
!= MAP_FAILED
) {
883 UnmapViewOfFile((void*) cache_data
);
885 munmap(cache_data
, map_len
);
890 debug_print("error reading cache mark from %s\n", mark_file
);
894 void msgcache_read_tags(MsgCache
*cache
, const gchar
*tags_file
)
900 char *cache_data
= NULL
;
902 gboolean error
= FALSE
;
906 /* In case we can't open the mark file with MARK_VERSION, check if we can open it with the
907 * swapped MARK_VERSION. As msgcache_open_data_file swaps it too, if this succeeds,
908 * it means it's the old version (not little-endian) on a big-endian machine. The code has
909 * no effect on x86 as their file doesn't change. */
911 if ((fp
= msgcache_open_data_file(tags_file
, TAGS_VERSION
, DATA_READ
, NULL
, 0)) == NULL
) {
912 /* see if it isn't swapped ? */
913 if ((fp
= msgcache_open_data_file(tags_file
, bswap_32(TAGS_VERSION
), DATA_READ
, NULL
, 0)) == NULL
)
916 swapping
= FALSE
; /* yay */
918 debug_print("reading %sswapped tags file.\n", swapping
?"":"un");
920 if (msgcache_use_mmap_read
) {
921 if (fstat(fileno(fp
), &st
) >= 0)
922 map_len
= st
.st_size
;
928 HANDLE hFile
, hMapping
;
929 hFile
= (HANDLE
) _get_osfhandle (fileno(fp
));
930 if (hFile
== (HANDLE
) -1)
932 hMapping
= CreateFileMapping(hFile
, NULL
, PAGE_WRITECOPY
, 0, 0, NULL
);
935 cache_data
= (unsigned char *)MapViewOfFile(hMapping
, FILE_MAP_COPY
, 0, 0, 0);
936 CloseHandle (hMapping
);
940 cache_data
= mmap(NULL
, map_len
, PROT_READ
, MAP_PRIVATE
, fileno(fp
), 0);
946 if (cache_data
!= NULL
&& cache_data
!= MAP_FAILED
) {
947 int rem_len
= map_len
-ftell(fp
);
948 char *walk_data
= cache_data
+ftell(fp
);
952 GET_CACHE_DATA_INT(num
);
953 msginfo
= g_hash_table_lookup(cache
->msgnum_table
, &num
);
955 g_slist_free(msginfo
->tags
);
956 msginfo
->tags
= NULL
;
958 GET_CACHE_DATA_INT(id
);
960 msginfo
->tags
= g_slist_prepend(
962 GINT_TO_POINTER(id
));
965 msginfo
->tags
= g_slist_reverse(msginfo
->tags
);
969 while (fread(&num
, sizeof(num
), 1, fp
) == 1) {
973 msginfo
= g_hash_table_lookup(cache
->msgnum_table
, &num
);
975 g_slist_free(msginfo
->tags
);
976 msginfo
->tags
= NULL
;
978 if (fread(&id
, sizeof(id
), 1, fp
) != 1)
983 msginfo
->tags
= g_slist_prepend(
985 GINT_TO_POINTER(id
));
988 msginfo
->tags
= g_slist_reverse(msginfo
->tags
);
993 if (cache_data
!= NULL
&& cache_data
!= MAP_FAILED
) {
995 UnmapViewOfFile((void*) cache_data
);
997 munmap(cache_data
, map_len
);
1002 debug_print("error reading cache tags from %s\n", tags_file
);
1006 static int msgcache_write_cache(MsgInfo
*msginfo
, FILE *fp
)
1008 MsgTmpFlags flags
= msginfo
->flags
.tmp_flags
& MSG_CACHED_FLAG_MASK
;
1010 int w_err
= 0, wrote
= 0;
1012 WRITE_CACHE_DATA_INT(msginfo
->msgnum
, fp
);
1013 WRITE_CACHE_DATA_INT(msginfo
->size
, fp
);
1014 WRITE_CACHE_DATA_INT(msginfo
->mtime
, fp
);
1015 WRITE_CACHE_DATA_INT(msginfo
->date_t
, fp
);
1016 WRITE_CACHE_DATA_INT(flags
, fp
);
1018 WRITE_CACHE_DATA(msginfo
->fromname
, fp
);
1020 WRITE_CACHE_DATA(msginfo
->date
, fp
);
1021 WRITE_CACHE_DATA(msginfo
->from
, fp
);
1022 WRITE_CACHE_DATA(msginfo
->to
, fp
);
1023 WRITE_CACHE_DATA(msginfo
->cc
, fp
);
1024 WRITE_CACHE_DATA(msginfo
->newsgroups
, fp
);
1025 WRITE_CACHE_DATA(msginfo
->subject
, fp
);
1026 WRITE_CACHE_DATA(msginfo
->msgid
, fp
);
1027 WRITE_CACHE_DATA(msginfo
->inreplyto
, fp
);
1028 WRITE_CACHE_DATA(msginfo
->xref
, fp
);
1029 WRITE_CACHE_DATA_INT(msginfo
->planned_download
, fp
);
1030 WRITE_CACHE_DATA_INT(msginfo
->total_size
, fp
);
1032 WRITE_CACHE_DATA_INT(g_slist_length(msginfo
->references
), fp
);
1034 for (cur
= msginfo
->references
; cur
!= NULL
; cur
= cur
->next
) {
1035 WRITE_CACHE_DATA((gchar
*)cur
->data
, fp
);
1037 return w_err
? -1 : wrote
;
1040 static int msgcache_write_flags(MsgInfo
*msginfo
, FILE *fp
)
1042 MsgPermFlags flags
= msginfo
->flags
.perm_flags
;
1043 int w_err
= 0, wrote
= 0;
1044 WRITE_CACHE_DATA_INT(msginfo
->msgnum
, fp
);
1045 WRITE_CACHE_DATA_INT(flags
, fp
);
1046 return w_err
? -1 : wrote
;
1049 static int msgcache_write_tags(MsgInfo
*msginfo
, FILE *fp
)
1051 GSList
*cur
= msginfo
->tags
;
1052 int w_err
= 0, wrote
= 0;
1054 WRITE_CACHE_DATA_INT(msginfo
->msgnum
, fp
);
1055 for (; cur
; cur
= cur
->next
) {
1056 gint id
= GPOINTER_TO_INT(cur
->data
);
1057 if (tags_get_tag(id
) != NULL
) {
1058 WRITE_CACHE_DATA_INT(id
, fp
);
1061 WRITE_CACHE_DATA_INT(-1, fp
);
1063 return w_err
? -1 : wrote
;
1077 static void msgcache_write_func(gpointer key
, gpointer value
, gpointer user_data
)
1080 struct write_fps
*write_fps
;
1083 msginfo
= (MsgInfo
*)value
;
1084 write_fps
= user_data
;
1086 if (write_fps
->cache_fp
) {
1087 tmp
= msgcache_write_cache(msginfo
, write_fps
->cache_fp
);
1089 write_fps
->error
= 1;
1091 write_fps
->cache_size
+= tmp
;
1093 if (write_fps
->mark_fp
) {
1094 tmp
= msgcache_write_flags(msginfo
, write_fps
->mark_fp
);
1096 write_fps
->error
= 1;
1098 write_fps
->mark_size
+= tmp
;
1100 if (write_fps
->tags_fp
) {
1101 tmp
= msgcache_write_tags(msginfo
, write_fps
->tags_fp
);
1103 write_fps
->error
= 1;
1105 write_fps
->tags_size
+= tmp
;
1109 gint
msgcache_write(const gchar
*cache_file
, const gchar
*mark_file
, const gchar
*tags_file
, MsgCache
*cache
)
1111 struct write_fps write_fps
;
1112 gchar
*new_cache
, *new_mark
, *new_tags
;
1113 int w_err
= 0, wrote
= 0;
1116 cm_return_val_if_fail(cache
!= NULL
, -1);
1118 new_cache
= g_strconcat(cache_file
, ".new", NULL
);
1119 new_mark
= g_strconcat(mark_file
, ".new", NULL
);
1120 new_tags
= g_strconcat(tags_file
, ".new", NULL
);
1122 write_fps
.error
= 0;
1123 write_fps
.cache_size
= 0;
1124 write_fps
.mark_size
= 0;
1125 write_fps
.tags_size
= 0;
1127 /* open files and write headers */
1130 write_fps
.cache_fp
= msgcache_open_data_file(new_cache
, CACHE_VERSION
,
1131 DATA_WRITE
, NULL
, 0);
1132 if (write_fps
.cache_fp
== NULL
) {
1138 WRITE_CACHE_DATA(CS_UTF_8
, write_fps
.cache_fp
);
1140 write_fps
.cache_fp
= NULL
;
1144 g_warning("failed to write charset");
1145 if (write_fps
.cache_fp
)
1146 fclose(write_fps
.cache_fp
);
1147 claws_unlink(new_cache
);
1155 write_fps
.mark_fp
= msgcache_open_data_file(new_mark
, MARK_VERSION
,
1156 DATA_WRITE
, NULL
, 0);
1157 if (write_fps
.mark_fp
== NULL
) {
1158 if (write_fps
.cache_fp
)
1159 fclose(write_fps
.cache_fp
);
1160 claws_unlink(new_cache
);
1167 write_fps
.mark_fp
= NULL
;
1171 write_fps
.tags_fp
= msgcache_open_data_file(new_tags
, TAGS_VERSION
,
1172 DATA_WRITE
, NULL
, 0);
1173 if (write_fps
.tags_fp
== NULL
) {
1174 if (write_fps
.cache_fp
)
1175 fclose(write_fps
.cache_fp
);
1176 if (write_fps
.mark_fp
)
1177 fclose(write_fps
.mark_fp
);
1178 claws_unlink(new_cache
);
1179 claws_unlink(new_mark
);
1186 write_fps
.tags_fp
= NULL
;
1189 debug_print("\tWriting message cache to %s and %s...\n", new_cache
, new_mark
);
1191 if (write_fps
.cache_fp
&& change_file_mode_rw(write_fps
.cache_fp
, new_cache
) < 0)
1192 FILE_OP_ERROR(new_cache
, "chmod");
1194 /* headers written, note file size */
1195 if (write_fps
.cache_fp
)
1196 write_fps
.cache_size
= ftell(write_fps
.cache_fp
);
1197 if (write_fps
.mark_fp
)
1198 write_fps
.mark_size
= ftell(write_fps
.mark_fp
);
1199 if (write_fps
.tags_fp
)
1200 write_fps
.tags_size
= ftell(write_fps
.tags_fp
);
1202 #ifdef HAVE_FWRITE_UNLOCKED
1203 /* lock files for write once (instead of once per fwrite) */
1204 if (write_fps
.cache_fp
)
1205 flockfile(write_fps
.cache_fp
);
1206 if (write_fps
.mark_fp
)
1207 flockfile(write_fps
.mark_fp
);
1208 if (write_fps
.tags_fp
)
1209 flockfile(write_fps
.tags_fp
);
1211 /* write data to the files */
1212 g_hash_table_foreach(cache
->msgnum_table
, msgcache_write_func
, (gpointer
)&write_fps
);
1213 #ifdef HAVE_FWRITE_UNLOCKED
1215 if (write_fps
.cache_fp
)
1216 funlockfile(write_fps
.cache_fp
);
1217 if (write_fps
.mark_fp
)
1218 funlockfile(write_fps
.mark_fp
);
1219 if (write_fps
.tags_fp
)
1220 funlockfile(write_fps
.tags_fp
);
1223 if (write_fps
.cache_fp
)
1224 write_fps
.error
|= (fflush(write_fps
.cache_fp
) != 0);
1225 if (write_fps
.mark_fp
)
1226 write_fps
.error
|= (fflush(write_fps
.mark_fp
) != 0);
1227 if (write_fps
.tags_fp
)
1228 write_fps
.error
|= (fflush(write_fps
.tags_fp
) != 0);
1230 /* sync to filesystem */
1231 if (prefs_common
.flush_metadata
&& write_fps
.cache_fp
)
1232 write_fps
.error
|= (fsync(fileno(write_fps
.cache_fp
)) != 0);
1233 if (prefs_common
.flush_metadata
&& write_fps
.mark_fp
)
1234 write_fps
.error
|= (fsync(fileno(write_fps
.mark_fp
)) != 0);
1235 if (prefs_common
.flush_metadata
&& write_fps
.tags_fp
)
1236 write_fps
.error
|= (fsync(fileno(write_fps
.tags_fp
)) != 0);
1239 if (write_fps
.cache_fp
)
1240 write_fps
.error
|= (fclose(write_fps
.cache_fp
) != 0);
1241 if (write_fps
.mark_fp
)
1242 write_fps
.error
|= (fclose(write_fps
.mark_fp
) != 0);
1243 if (write_fps
.tags_fp
)
1244 write_fps
.error
|= (fclose(write_fps
.tags_fp
) != 0);
1247 if (write_fps
.error
!= 0) {
1248 /* in case of error, forget all */
1249 claws_unlink(new_cache
);
1250 claws_unlink(new_mark
);
1251 claws_unlink(new_tags
);
1259 move_file(new_cache
, cache_file
, TRUE
);
1261 move_file(new_mark
, mark_file
, TRUE
);
1263 move_file(new_tags
, tags_file
, TRUE
);
1264 cache
->last_access
= time(NULL
);
1270 debug_print("done.\n");