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
);
190 procmsg_msginfo_free(msginfo
);
191 cache
->last_access
= time(NULL
);
193 msginfo
->folder
->cache_dirty
= TRUE
;
195 debug_print("Cache size: %d messages, %u bytes\n", g_hash_table_size(cache
->msgnum_table
), cache
->memusage
);
198 void msgcache_update_msg(MsgCache
*cache
, MsgInfo
*msginfo
)
200 MsgInfo
*oldmsginfo
, *newmsginfo
;
202 cm_return_if_fail(cache
!= NULL
);
203 cm_return_if_fail(msginfo
!= NULL
);
205 oldmsginfo
= g_hash_table_lookup(cache
->msgnum_table
, &msginfo
->msgnum
);
206 if(oldmsginfo
&& oldmsginfo
->msgid
)
207 g_hash_table_remove(cache
->msgid_table
, oldmsginfo
->msgid
);
209 g_hash_table_remove(cache
->msgnum_table
, &oldmsginfo
->msgnum
);
210 cache
->memusage
-= procmsg_msginfo_memusage(oldmsginfo
);
211 procmsg_msginfo_free(oldmsginfo
);
214 newmsginfo
= procmsg_msginfo_new_ref(msginfo
);
215 g_hash_table_insert(cache
->msgnum_table
, &newmsginfo
->msgnum
, newmsginfo
);
216 if(newmsginfo
->msgid
)
217 g_hash_table_insert(cache
->msgid_table
, newmsginfo
->msgid
, newmsginfo
);
218 cache
->memusage
+= procmsg_msginfo_memusage(newmsginfo
);
219 cache
->last_access
= time(NULL
);
221 debug_print("Cache size: %d messages, %u bytes\n", g_hash_table_size(cache
->msgnum_table
), cache
->memusage
);
223 msginfo
->folder
->cache_dirty
= TRUE
;
228 MsgInfo
*msgcache_get_msg(MsgCache
*cache
, guint num
)
232 cm_return_val_if_fail(cache
!= NULL
, NULL
);
234 msginfo
= g_hash_table_lookup(cache
->msgnum_table
, &num
);
237 cache
->last_access
= time(NULL
);
239 return procmsg_msginfo_new_ref(msginfo
);
242 MsgInfo
*msgcache_get_msg_by_id(MsgCache
*cache
, const gchar
*msgid
)
246 cm_return_val_if_fail(cache
!= NULL
, NULL
);
247 cm_return_val_if_fail(msgid
!= NULL
, NULL
);
249 msginfo
= g_hash_table_lookup(cache
->msgid_table
, msgid
);
252 cache
->last_access
= time(NULL
);
254 return procmsg_msginfo_new_ref(msginfo
);
257 static void msgcache_get_msg_list_func(gpointer key
, gpointer value
, gpointer user_data
)
259 MsgInfoList
**listptr
= user_data
;
260 MsgInfo
*msginfo
= value
;
262 *listptr
= g_slist_prepend(*listptr
, procmsg_msginfo_new_ref(msginfo
));
265 MsgInfoList
*msgcache_get_msg_list(MsgCache
*cache
)
267 MsgInfoList
*msg_list
= NULL
;
269 cm_return_val_if_fail(cache
!= NULL
, NULL
);
271 g_hash_table_foreach((GHashTable
*)cache
->msgnum_table
, msgcache_get_msg_list_func
, (gpointer
)&msg_list
);
272 cache
->last_access
= time(NULL
);
274 msg_list
= g_slist_reverse(msg_list
);
279 time_t msgcache_get_last_access_time(MsgCache
*cache
)
281 cm_return_val_if_fail(cache
!= NULL
, 0);
283 return cache
->last_access
;
286 gint
msgcache_get_memory_usage(MsgCache
*cache
)
288 cm_return_val_if_fail(cache
!= NULL
, 0);
290 return cache
->memusage
;
294 * Cache saving functions
297 #define READ_CACHE_DATA(data, fp, total_len) \
299 if ((tmp_len = msgcache_read_cache_data_str(fp, &data, conv)) < 0) { \
300 procmsg_msginfo_free(msginfo); \
304 total_len += tmp_len; \
307 #define READ_CACHE_DATA_INT(n, fp) \
312 if ((ni = fread(&idata, sizeof(idata), 1, fp)) != 1) { \
313 g_warning("read_int: Cache data corrupted, read %zd of %zd at " \
314 "offset %ld\n", ni, sizeof(idata), ftell(fp)); \
315 procmsg_msginfo_free(msginfo); \
319 n = swapping ? bswap_32(idata) : (idata);\
322 #define GET_CACHE_DATA_INT(n) \
325 g_print("error at rem_len:%d\n", rem_len); \
329 n = (swapping ? (MMAP_TO_GUINT32_SWAPPED(walk_data)):(MMAP_TO_GUINT32(walk_data))); \
330 walk_data += 4; rem_len -= 4; \
333 #define GET_CACHE_DATA(data, total_len) \
335 GET_CACHE_DATA_INT(tmp_len); \
336 if (rem_len < tmp_len) { \
337 g_print("error at rem_len:%d (tmp_len %d)\n", rem_len, tmp_len); \
341 if ((tmp_len = msgcache_get_cache_data_str(walk_data, &data, tmp_len, conv)) < 0) { \
342 g_print("error at rem_len:%d\n", rem_len);\
343 procmsg_msginfo_free(msginfo); \
347 total_len += tmp_len; \
348 walk_data += tmp_len; rem_len -= tmp_len; \
352 #define WRITE_CACHE_DATA_INT(n, fp) \
356 idata = (guint32)bswap_32(n); \
357 if (SC_FWRITE(&idata, sizeof(idata), 1, fp) != 1) \
362 #define PUT_CACHE_DATA_INT(n) \
364 walk_data[0]=(((guint32)n)&0x000000ff); \
365 walk_data[1]=(((guint32)n)&0x0000ff00)>>8; \
366 walk_data[2]=(((guint32)n)&0x00ff0000)>>16; \
367 walk_data[3]=(((guint32)n)&0xff000000)>>24; \
372 #define WRITE_CACHE_DATA(data, fp) \
378 len = strlen(data); \
379 WRITE_CACHE_DATA_INT(len, fp); \
380 if (w_err == 0 && len > 0) { \
381 if (SC_FWRITE(data, 1, len, fp) != len) \
387 #define PUT_CACHE_DATA(data) \
393 len = strlen(data); \
394 PUT_CACHE_DATA_INT(len); \
396 memcpy(walk_data, data, len); \
402 static FILE *msgcache_open_data_file(const gchar
*file
, guint version
,
404 gchar
*buf
, size_t buf_size
)
409 cm_return_val_if_fail(file
!= NULL
, NULL
);
411 if (mode
== DATA_WRITE
) {
412 int w_err
= 0, wrote
= 0;
413 if ((fp
= g_fopen(file
, "wb")) == NULL
) {
414 FILE_OP_ERROR(file
, "fopen");
417 if (change_file_mode_rw(fp
, file
) < 0)
418 FILE_OP_ERROR(file
, "chmod");
420 WRITE_CACHE_DATA_INT(version
, fp
);
422 g_warning("failed to write int\n");
430 if ((fp
= g_fopen(file
, "rb")) == NULL
)
431 debug_print("Mark/Cache file '%s' not found\n", file
);
433 if (buf
&& buf_size
> 0)
434 setvbuf(fp
, buf
, _IOFBF
, buf_size
);
435 if (fread(&data_ver
, sizeof(data_ver
), 1, fp
) != 1 ||
436 version
!= bswap_32(data_ver
)) {
437 g_message("%s: Mark/Cache version is different (%u != %u).\n",
438 file
, bswap_32(data_ver
), version
);
442 data_ver
= bswap_32(data_ver
);
445 if (mode
== DATA_READ
)
449 /* reopen with append mode */
451 if ((fp
= g_fopen(file
, "ab")) == NULL
)
452 FILE_OP_ERROR(file
, "fopen");
454 /* open with overwrite mode if mark file doesn't exist or
455 version is different */
456 fp
= msgcache_open_data_file(file
, version
, DATA_WRITE
, buf
,
463 static gint
msgcache_read_cache_data_str(FILE *fp
, gchar
**str
,
464 StringConverter
*conv
)
466 gchar
*tmpstr
= NULL
;
472 if ((ni
= fread(&len
, sizeof(len
), 1, fp
) != 1) ||
474 g_warning("read_data_str: Cache data (len) corrupted, read %zd "
475 "of %zd bytes at offset %ld\n", ni
, sizeof(len
),
480 if ((ni
= fread(&len
, sizeof(len
), 1, fp
) != 1) ||
481 bswap_32(len
) > G_MAXINT
) {
482 g_warning("read_data_str: Cache data (len) corrupted, read %zd "
483 "of %zd bytes at offset %ld\n", ni
, sizeof(len
),
493 tmpstr
= g_try_malloc(len
+ 1);
499 if ((ni
= fread(tmpstr
, 1, len
, fp
)) != len
) {
500 g_warning("read_data_str: Cache data corrupted, read %zd of %u "
501 "bytes at offset %ld\n",
509 *str
= conv
->convert(conv
, tmpstr
);
517 static gint
msgcache_get_cache_data_str(gchar
*src
, gchar
**str
, gint len
,
518 StringConverter
*conv
)
520 gchar
*tmpstr
= NULL
;
527 if(len
> 2*1024*1024) {
528 g_warning("read_data_str: refusing to allocate %d bytes.\n", len
);
532 tmpstr
= g_try_malloc(len
+ 1);
538 memcpy(tmpstr
, src
, len
);
542 *str
= conv
->convert(conv
, tmpstr
);
550 static gchar
*strconv_charset_convert(StringConverter
*conv
, gchar
*srcstr
)
552 CharsetConverter
*charsetconv
= (CharsetConverter
*) conv
;
554 return conv_codeset_strdup(srcstr
, charsetconv
->srccharset
, charsetconv
->dstcharset
);
557 static void strconv_charset_free(StringConverter
*conv
)
559 CharsetConverter
*charsetconv
= (CharsetConverter
*) conv
;
561 g_free(charsetconv
->srccharset
);
562 g_free(charsetconv
->dstcharset
);
565 MsgCache
*msgcache_read_cache(FolderItem
*item
, const gchar
*cache_file
)
570 MsgTmpFlags tmp_flags
= 0;
571 gchar file_buf
[BUFFSIZE
];
574 gboolean error
= FALSE
;
575 StringConverter
*conv
= NULL
;
576 gchar
*srccharset
= NULL
;
577 const gchar
*dstcharset
= NULL
;
580 gint tmp_len
= 0, map_len
= -1;
581 char *cache_data
= NULL
;
584 cm_return_val_if_fail(cache_file
!= NULL
, NULL
);
585 cm_return_val_if_fail(item
!= NULL
, NULL
);
589 /* In case we can't open the mark file with MARK_VERSION, check if we can open it with the
590 * swapped MARK_VERSION. As msgcache_open_data_file swaps it too, if this succeeds,
591 * it means it's the old version (not little-endian) on a big-endian machine. The code has
592 * no effect on x86 as their file doesn't change. */
594 if ((fp
= msgcache_open_data_file
595 (cache_file
, CACHE_VERSION
, DATA_READ
, file_buf
, sizeof(file_buf
))) == NULL
) {
596 if ((fp
= msgcache_open_data_file
597 (cache_file
, bswap_32(CACHE_VERSION
), DATA_READ
, file_buf
, sizeof(file_buf
))) == NULL
)
603 debug_print("\tReading %sswapped message cache from %s...\n", swapping
?"":"un", cache_file
);
605 if (folder_has_parent_of_type(item
, F_QUEUE
)) {
606 tmp_flags
|= MSG_QUEUED
;
607 } else if (folder_has_parent_of_type(item
, F_DRAFT
)) {
608 tmp_flags
|= MSG_DRAFT
;
611 if (msgcache_read_cache_data_str(fp
, &srccharset
, NULL
) < 0) {
615 dstcharset
= CS_UTF_8
;
616 if (srccharset
== NULL
|| dstcharset
== NULL
) {
618 } else if (strcmp(srccharset
, dstcharset
) == 0) {
619 debug_print("using Noop Converter\n");
623 CharsetConverter
*charsetconv
;
625 debug_print("using CharsetConverter\n");
627 charsetconv
= g_new0(CharsetConverter
, 1);
628 charsetconv
->converter
.convert
= strconv_charset_convert
;
629 charsetconv
->converter
.free
= strconv_charset_free
;
630 charsetconv
->srccharset
= g_strdup(srccharset
);
631 charsetconv
->dstcharset
= g_strdup(dstcharset
);
633 conv
= (StringConverter
*) charsetconv
;
637 cache
= msgcache_new();
639 if (msgcache_use_mmap_read
== TRUE
) {
640 if (fstat(fileno(fp
), &st
) >= 0)
641 map_len
= st
.st_size
;
647 HANDLE hFile
, hMapping
;
648 hFile
= (HANDLE
) _get_osfhandle (fileno(fp
));
649 if (hFile
== (HANDLE
) -1)
651 hMapping
= CreateFileMapping(hFile
, NULL
, PAGE_WRITECOPY
, 0, 0, NULL
);
654 cache_data
= (unsigned char *)MapViewOfFile(hMapping
, FILE_MAP_COPY
, 0, 0, 0);
655 CloseHandle (hMapping
);
659 cache_data
= mmap(NULL
, map_len
, PROT_READ
, MAP_PRIVATE
, fileno(fp
), 0);
665 if (cache_data
!= NULL
&& cache_data
!= MAP_FAILED
) {
666 int rem_len
= map_len
-ftell(fp
);
667 char *walk_data
= cache_data
+ftell(fp
);
670 GET_CACHE_DATA_INT(num
);
672 msginfo
= procmsg_msginfo_new();
673 msginfo
->msgnum
= num
;
674 memusage
+= sizeof(MsgInfo
);
676 GET_CACHE_DATA_INT(msginfo
->size
);
677 GET_CACHE_DATA_INT(msginfo
->mtime
);
678 GET_CACHE_DATA_INT(msginfo
->date_t
);
679 GET_CACHE_DATA_INT(msginfo
->flags
.tmp_flags
);
681 GET_CACHE_DATA(msginfo
->fromname
, memusage
);
683 GET_CACHE_DATA(msginfo
->date
, memusage
);
684 GET_CACHE_DATA(msginfo
->from
, memusage
);
685 GET_CACHE_DATA(msginfo
->to
, memusage
);
686 GET_CACHE_DATA(msginfo
->cc
, memusage
);
687 GET_CACHE_DATA(msginfo
->newsgroups
, memusage
);
688 GET_CACHE_DATA(msginfo
->subject
, memusage
);
689 GET_CACHE_DATA(msginfo
->msgid
, memusage
);
690 GET_CACHE_DATA(msginfo
->inreplyto
, memusage
);
691 GET_CACHE_DATA(msginfo
->xref
, memusage
);
693 GET_CACHE_DATA_INT(msginfo
->planned_download
);
694 GET_CACHE_DATA_INT(msginfo
->total_size
);
695 GET_CACHE_DATA_INT(refnum
);
697 for (; refnum
!= 0; refnum
--) {
700 GET_CACHE_DATA(ref
, memusage
);
703 msginfo
->references
=
704 g_slist_prepend(msginfo
->references
, ref
);
706 if (msginfo
->references
)
707 msginfo
->references
=
708 g_slist_reverse(msginfo
->references
);
710 msginfo
->folder
= item
;
711 msginfo
->flags
.tmp_flags
|= tmp_flags
;
713 g_hash_table_insert(cache
->msgnum_table
, &msginfo
->msgnum
, msginfo
);
715 g_hash_table_insert(cache
->msgid_table
, msginfo
->msgid
, msginfo
);
718 while (fread(&num
, sizeof(num
), 1, fp
) == 1) {
722 msginfo
= procmsg_msginfo_new();
723 msginfo
->msgnum
= num
;
724 memusage
+= sizeof(MsgInfo
);
726 READ_CACHE_DATA_INT(msginfo
->size
, fp
);
727 READ_CACHE_DATA_INT(msginfo
->mtime
, fp
);
728 READ_CACHE_DATA_INT(msginfo
->date_t
, fp
);
729 READ_CACHE_DATA_INT(msginfo
->flags
.tmp_flags
, fp
);
731 READ_CACHE_DATA(msginfo
->fromname
, fp
, memusage
);
733 READ_CACHE_DATA(msginfo
->date
, fp
, memusage
);
734 READ_CACHE_DATA(msginfo
->from
, fp
, memusage
);
735 READ_CACHE_DATA(msginfo
->to
, fp
, memusage
);
736 READ_CACHE_DATA(msginfo
->cc
, fp
, memusage
);
737 READ_CACHE_DATA(msginfo
->newsgroups
, fp
, memusage
);
738 READ_CACHE_DATA(msginfo
->subject
, fp
, memusage
);
739 READ_CACHE_DATA(msginfo
->msgid
, fp
, memusage
);
740 READ_CACHE_DATA(msginfo
->inreplyto
, fp
, memusage
);
741 READ_CACHE_DATA(msginfo
->xref
, fp
, memusage
);
743 READ_CACHE_DATA_INT(msginfo
->planned_download
, fp
);
744 READ_CACHE_DATA_INT(msginfo
->total_size
, fp
);
745 READ_CACHE_DATA_INT(refnum
, fp
);
747 for (; refnum
!= 0; refnum
--) {
750 READ_CACHE_DATA(ref
, fp
, memusage
);
753 msginfo
->references
=
754 g_slist_prepend(msginfo
->references
, ref
);
756 if (msginfo
->references
)
757 msginfo
->references
=
758 g_slist_reverse(msginfo
->references
);
760 msginfo
->folder
= item
;
761 msginfo
->flags
.tmp_flags
|= tmp_flags
;
763 g_hash_table_insert(cache
->msgnum_table
, &msginfo
->msgnum
, msginfo
);
765 g_hash_table_insert(cache
->msgid_table
, msginfo
->msgid
, msginfo
);
769 if (cache_data
!= NULL
&& cache_data
!= MAP_FAILED
) {
771 UnmapViewOfFile((void*) cache_data
);
773 munmap(cache_data
, map_len
);
778 if (conv
->free
!= NULL
)
784 msgcache_destroy(cache
);
788 cache
->last_access
= time(NULL
);
789 cache
->memusage
= memusage
;
791 debug_print("done. (%d items read)\n", g_hash_table_size(cache
->msgnum_table
));
792 debug_print("Cache size: %d messages, %u bytes\n", g_hash_table_size(cache
->msgnum_table
), cache
->memusage
);
797 void msgcache_read_mark(MsgCache
*cache
, const gchar
*mark_file
)
801 MsgPermFlags perm_flags
;
804 char *cache_data
= NULL
;
806 gboolean error
= FALSE
;
810 /* In case we can't open the mark file with MARK_VERSION, check if we can open it with the
811 * swapped MARK_VERSION. As msgcache_open_data_file swaps it too, if this succeeds,
812 * it means it's the old version (not little-endian) on a big-endian machine. The code has
813 * no effect on x86 as their file doesn't change. */
815 if ((fp
= msgcache_open_data_file(mark_file
, MARK_VERSION
, DATA_READ
, NULL
, 0)) == NULL
) {
816 /* see if it isn't swapped ? */
817 if ((fp
= msgcache_open_data_file(mark_file
, bswap_32(MARK_VERSION
), DATA_READ
, NULL
, 0)) == NULL
)
820 swapping
= FALSE
; /* yay */
822 debug_print("reading %sswapped mark file.\n", swapping
?"":"un");
824 if (msgcache_use_mmap_read
) {
825 if (fstat(fileno(fp
), &st
) >= 0)
826 map_len
= st
.st_size
;
832 HANDLE hFile
, hMapping
;
833 hFile
= (HANDLE
) _get_osfhandle (fileno(fp
));
834 if (hFile
== (HANDLE
) -1)
836 hMapping
= CreateFileMapping(hFile
, NULL
, PAGE_WRITECOPY
, 0, 0, NULL
);
839 cache_data
= (unsigned char *)MapViewOfFile(hMapping
, FILE_MAP_COPY
, 0, 0, 0);
840 CloseHandle (hMapping
);
844 cache_data
= mmap(NULL
, map_len
, PROT_READ
, MAP_PRIVATE
, fileno(fp
), 0);
850 if (cache_data
!= NULL
&& cache_data
!= MAP_FAILED
) {
851 int rem_len
= map_len
-ftell(fp
);
852 char *walk_data
= cache_data
+ftell(fp
);
855 GET_CACHE_DATA_INT(num
);
856 GET_CACHE_DATA_INT(perm_flags
);
857 msginfo
= g_hash_table_lookup(cache
->msgnum_table
, &num
);
859 msginfo
->flags
.perm_flags
= perm_flags
;
863 while (fread(&num
, sizeof(num
), 1, fp
) == 1) {
866 if (fread(&perm_flags
, sizeof(perm_flags
), 1, fp
) != 1) {
871 perm_flags
= bswap_32(perm_flags
);
872 msginfo
= g_hash_table_lookup(cache
->msgnum_table
, &num
);
874 msginfo
->flags
.perm_flags
= perm_flags
;
879 if (cache_data
!= NULL
&& cache_data
!= MAP_FAILED
) {
881 UnmapViewOfFile((void*) cache_data
);
883 munmap(cache_data
, map_len
);
888 debug_print("error reading cache mark from %s\n", mark_file
);
892 void msgcache_read_tags(MsgCache
*cache
, const gchar
*tags_file
)
898 char *cache_data
= NULL
;
900 gboolean error
= FALSE
;
904 /* In case we can't open the mark file with MARK_VERSION, check if we can open it with the
905 * swapped MARK_VERSION. As msgcache_open_data_file swaps it too, if this succeeds,
906 * it means it's the old version (not little-endian) on a big-endian machine. The code has
907 * no effect on x86 as their file doesn't change. */
909 if ((fp
= msgcache_open_data_file(tags_file
, TAGS_VERSION
, DATA_READ
, NULL
, 0)) == NULL
) {
910 /* see if it isn't swapped ? */
911 if ((fp
= msgcache_open_data_file(tags_file
, bswap_32(TAGS_VERSION
), DATA_READ
, NULL
, 0)) == NULL
)
914 swapping
= FALSE
; /* yay */
916 debug_print("reading %sswapped tags file.\n", swapping
?"":"un");
918 if (msgcache_use_mmap_read
) {
919 if (fstat(fileno(fp
), &st
) >= 0)
920 map_len
= st
.st_size
;
926 HANDLE hFile
, hMapping
;
927 hFile
= (HANDLE
) _get_osfhandle (fileno(fp
));
928 if (hFile
== (HANDLE
) -1)
930 hMapping
= CreateFileMapping(hFile
, NULL
, PAGE_WRITECOPY
, 0, 0, NULL
);
933 cache_data
= (unsigned char *)MapViewOfFile(hMapping
, FILE_MAP_COPY
, 0, 0, 0);
934 CloseHandle (hMapping
);
938 cache_data
= mmap(NULL
, map_len
, PROT_READ
, MAP_PRIVATE
, fileno(fp
), 0);
944 if (cache_data
!= NULL
&& cache_data
!= MAP_FAILED
) {
945 int rem_len
= map_len
-ftell(fp
);
946 char *walk_data
= cache_data
+ftell(fp
);
950 GET_CACHE_DATA_INT(num
);
951 msginfo
= g_hash_table_lookup(cache
->msgnum_table
, &num
);
953 g_slist_free(msginfo
->tags
);
954 msginfo
->tags
= NULL
;
956 GET_CACHE_DATA_INT(id
);
958 msginfo
->tags
= g_slist_prepend(
960 GINT_TO_POINTER(id
));
963 msginfo
->tags
= g_slist_reverse(msginfo
->tags
);
967 while (fread(&num
, sizeof(num
), 1, fp
) == 1) {
971 msginfo
= g_hash_table_lookup(cache
->msgnum_table
, &num
);
973 g_slist_free(msginfo
->tags
);
974 msginfo
->tags
= NULL
;
976 if (fread(&id
, sizeof(id
), 1, fp
) != 1)
981 msginfo
->tags
= g_slist_prepend(
983 GINT_TO_POINTER(id
));
986 msginfo
->tags
= g_slist_reverse(msginfo
->tags
);
991 if (cache_data
!= NULL
&& cache_data
!= MAP_FAILED
) {
993 UnmapViewOfFile((void*) cache_data
);
995 munmap(cache_data
, map_len
);
1000 debug_print("error reading cache tags from %s\n", tags_file
);
1004 static int msgcache_write_cache(MsgInfo
*msginfo
, FILE *fp
)
1006 MsgTmpFlags flags
= msginfo
->flags
.tmp_flags
& MSG_CACHED_FLAG_MASK
;
1008 int w_err
= 0, wrote
= 0;
1010 WRITE_CACHE_DATA_INT(msginfo
->msgnum
, fp
);
1011 WRITE_CACHE_DATA_INT(msginfo
->size
, fp
);
1012 WRITE_CACHE_DATA_INT(msginfo
->mtime
, fp
);
1013 WRITE_CACHE_DATA_INT(msginfo
->date_t
, fp
);
1014 WRITE_CACHE_DATA_INT(flags
, fp
);
1016 WRITE_CACHE_DATA(msginfo
->fromname
, fp
);
1018 WRITE_CACHE_DATA(msginfo
->date
, fp
);
1019 WRITE_CACHE_DATA(msginfo
->from
, fp
);
1020 WRITE_CACHE_DATA(msginfo
->to
, fp
);
1021 WRITE_CACHE_DATA(msginfo
->cc
, fp
);
1022 WRITE_CACHE_DATA(msginfo
->newsgroups
, fp
);
1023 WRITE_CACHE_DATA(msginfo
->subject
, fp
);
1024 WRITE_CACHE_DATA(msginfo
->msgid
, fp
);
1025 WRITE_CACHE_DATA(msginfo
->inreplyto
, fp
);
1026 WRITE_CACHE_DATA(msginfo
->xref
, fp
);
1027 WRITE_CACHE_DATA_INT(msginfo
->planned_download
, fp
);
1028 WRITE_CACHE_DATA_INT(msginfo
->total_size
, fp
);
1030 WRITE_CACHE_DATA_INT(g_slist_length(msginfo
->references
), fp
);
1032 for (cur
= msginfo
->references
; cur
!= NULL
; cur
= cur
->next
) {
1033 WRITE_CACHE_DATA((gchar
*)cur
->data
, fp
);
1035 return w_err
? -1 : wrote
;
1038 static int msgcache_write_flags(MsgInfo
*msginfo
, FILE *fp
)
1040 MsgPermFlags flags
= msginfo
->flags
.perm_flags
;
1041 int w_err
= 0, wrote
= 0;
1042 WRITE_CACHE_DATA_INT(msginfo
->msgnum
, fp
);
1043 WRITE_CACHE_DATA_INT(flags
, fp
);
1044 return w_err
? -1 : wrote
;
1047 static int msgcache_write_tags(MsgInfo
*msginfo
, FILE *fp
)
1049 GSList
*cur
= msginfo
->tags
;
1050 int w_err
= 0, wrote
= 0;
1052 WRITE_CACHE_DATA_INT(msginfo
->msgnum
, fp
);
1053 for (; cur
; cur
= cur
->next
) {
1054 gint id
= GPOINTER_TO_INT(cur
->data
);
1055 if (tags_get_tag(id
) != NULL
) {
1056 WRITE_CACHE_DATA_INT(id
, fp
);
1059 WRITE_CACHE_DATA_INT(-1, fp
);
1061 return w_err
? -1 : wrote
;
1075 static void msgcache_write_func(gpointer key
, gpointer value
, gpointer user_data
)
1078 struct write_fps
*write_fps
;
1081 msginfo
= (MsgInfo
*)value
;
1082 write_fps
= user_data
;
1084 if (write_fps
->cache_fp
) {
1085 tmp
= msgcache_write_cache(msginfo
, write_fps
->cache_fp
);
1087 write_fps
->error
= 1;
1089 write_fps
->cache_size
+= tmp
;
1091 if (write_fps
->mark_fp
) {
1092 tmp
= msgcache_write_flags(msginfo
, write_fps
->mark_fp
);
1094 write_fps
->error
= 1;
1096 write_fps
->mark_size
+= tmp
;
1098 if (write_fps
->tags_fp
) {
1099 tmp
= msgcache_write_tags(msginfo
, write_fps
->tags_fp
);
1101 write_fps
->error
= 1;
1103 write_fps
->tags_size
+= tmp
;
1107 gint
msgcache_write(const gchar
*cache_file
, const gchar
*mark_file
, const gchar
*tags_file
, MsgCache
*cache
)
1109 struct write_fps write_fps
;
1110 gchar
*new_cache
, *new_mark
, *new_tags
;
1111 int w_err
= 0, wrote
= 0;
1114 cm_return_val_if_fail(cache
!= NULL
, -1);
1116 new_cache
= g_strconcat(cache_file
, ".new", NULL
);
1117 new_mark
= g_strconcat(mark_file
, ".new", NULL
);
1118 new_tags
= g_strconcat(tags_file
, ".new", NULL
);
1120 write_fps
.error
= 0;
1121 write_fps
.cache_size
= 0;
1122 write_fps
.mark_size
= 0;
1123 write_fps
.tags_size
= 0;
1125 /* open files and write headers */
1128 write_fps
.cache_fp
= msgcache_open_data_file(new_cache
, CACHE_VERSION
,
1129 DATA_WRITE
, NULL
, 0);
1130 if (write_fps
.cache_fp
== NULL
) {
1136 WRITE_CACHE_DATA(CS_UTF_8
, write_fps
.cache_fp
);
1138 write_fps
.cache_fp
= NULL
;
1142 g_warning("failed to write charset\n");
1143 if (write_fps
.cache_fp
)
1144 fclose(write_fps
.cache_fp
);
1145 claws_unlink(new_cache
);
1153 write_fps
.mark_fp
= msgcache_open_data_file(new_mark
, MARK_VERSION
,
1154 DATA_WRITE
, NULL
, 0);
1155 if (write_fps
.mark_fp
== NULL
) {
1156 if (write_fps
.cache_fp
)
1157 fclose(write_fps
.cache_fp
);
1158 claws_unlink(new_cache
);
1165 write_fps
.mark_fp
= NULL
;
1169 write_fps
.tags_fp
= msgcache_open_data_file(new_tags
, TAGS_VERSION
,
1170 DATA_WRITE
, NULL
, 0);
1171 if (write_fps
.tags_fp
== NULL
) {
1172 if (write_fps
.cache_fp
)
1173 fclose(write_fps
.cache_fp
);
1174 if (write_fps
.mark_fp
)
1175 fclose(write_fps
.mark_fp
);
1176 claws_unlink(new_cache
);
1177 claws_unlink(new_mark
);
1184 write_fps
.tags_fp
= NULL
;
1187 debug_print("\tWriting message cache to %s and %s...\n", new_cache
, new_mark
);
1189 if (write_fps
.cache_fp
&& change_file_mode_rw(write_fps
.cache_fp
, new_cache
) < 0)
1190 FILE_OP_ERROR(new_cache
, "chmod");
1192 /* headers written, note file size */
1193 if (write_fps
.cache_fp
)
1194 write_fps
.cache_size
= ftell(write_fps
.cache_fp
);
1195 if (write_fps
.mark_fp
)
1196 write_fps
.mark_size
= ftell(write_fps
.mark_fp
);
1197 if (write_fps
.tags_fp
)
1198 write_fps
.tags_size
= ftell(write_fps
.tags_fp
);
1200 #ifdef HAVE_FWRITE_UNLOCKED
1201 /* lock files for write once (instead of once per fwrite) */
1202 if (write_fps
.cache_fp
)
1203 flockfile(write_fps
.cache_fp
);
1204 if (write_fps
.mark_fp
)
1205 flockfile(write_fps
.mark_fp
);
1206 if (write_fps
.tags_fp
)
1207 flockfile(write_fps
.tags_fp
);
1209 /* write data to the files */
1210 g_hash_table_foreach(cache
->msgnum_table
, msgcache_write_func
, (gpointer
)&write_fps
);
1211 #ifdef HAVE_FWRITE_UNLOCKED
1213 if (write_fps
.cache_fp
)
1214 funlockfile(write_fps
.cache_fp
);
1215 if (write_fps
.mark_fp
)
1216 funlockfile(write_fps
.mark_fp
);
1217 if (write_fps
.tags_fp
)
1218 funlockfile(write_fps
.tags_fp
);
1221 if (write_fps
.cache_fp
)
1222 write_fps
.error
|= (fflush(write_fps
.cache_fp
) != 0);
1223 if (write_fps
.mark_fp
)
1224 write_fps
.error
|= (fflush(write_fps
.mark_fp
) != 0);
1225 if (write_fps
.tags_fp
)
1226 write_fps
.error
|= (fflush(write_fps
.tags_fp
) != 0);
1228 /* sync to filesystem */
1229 if (prefs_common
.flush_metadata
&& write_fps
.cache_fp
)
1230 write_fps
.error
|= (fsync(fileno(write_fps
.cache_fp
)) != 0);
1231 if (prefs_common
.flush_metadata
&& write_fps
.mark_fp
)
1232 write_fps
.error
|= (fsync(fileno(write_fps
.mark_fp
)) != 0);
1233 if (prefs_common
.flush_metadata
&& write_fps
.tags_fp
)
1234 write_fps
.error
|= (fsync(fileno(write_fps
.tags_fp
)) != 0);
1237 if (write_fps
.cache_fp
)
1238 write_fps
.error
|= (fclose(write_fps
.cache_fp
) != 0);
1239 if (write_fps
.mark_fp
)
1240 write_fps
.error
|= (fclose(write_fps
.mark_fp
) != 0);
1241 if (write_fps
.tags_fp
)
1242 write_fps
.error
|= (fclose(write_fps
.tags_fp
) != 0);
1245 if (write_fps
.error
!= 0) {
1246 /* in case of error, forget all */
1247 claws_unlink(new_cache
);
1248 claws_unlink(new_mark
);
1249 claws_unlink(new_tags
);
1257 move_file(new_cache
, cache_file
, TRUE
);
1259 move_file(new_mark
, mark_file
, TRUE
);
1261 move_file(new_tags
, tags_file
, TRUE
);
1262 cache
->last_access
= time(NULL
);
1268 debug_print("done.\n");