2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2009 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/>.
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"
50 #ifdef HAVE_FWRITE_UNLOCKED
51 #define SC_FWRITE fwrite_unlocked
53 #define SC_FWRITE fwrite
56 #if G_BYTE_ORDER == G_BIG_ENDIAN
58 ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \
59 (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24))
61 #define MMAP_TO_GUINT32(x) \
63 ((x[2]&0xff) << 8) | \
64 ((x[1]&0xff) << 16) | \
67 #define MMAP_TO_GUINT32_SWAPPED(x) \
69 ((x[1]&0xff) << 8) | \
70 ((x[2]&0xff) << 16) | \
73 static gboolean msgcache_use_mmap_read
= TRUE
;
76 #define bswap_32(x) (x)
78 #define MMAP_TO_GUINT32(x) \
80 ((x[1]&0xff) << 8) | \
81 ((x[2]&0xff) << 16) | \
84 #define MMAP_TO_GUINT32_SWAPPED(x) \
86 ((x[1]&0xff) << 8) | \
87 ((x[2]&0xff) << 16) | \
90 static gboolean msgcache_use_mmap_read
= TRUE
;
93 static gboolean swapping
= TRUE
;
103 GHashTable
*msgnum_table
;
104 GHashTable
*msgid_table
;
109 typedef struct _StringConverter StringConverter
;
110 struct _StringConverter
{
111 gchar
*(*convert
) (StringConverter
*converter
, gchar
*srcstr
);
112 void (*free
) (StringConverter
*converter
);
115 typedef struct _StrdupConverter StrdupConverter
;
116 struct _StrdupConverter
{
117 StringConverter converter
;
120 typedef struct _CharsetConverter CharsetConverter
;
121 struct _CharsetConverter
{
122 StringConverter converter
;
128 MsgCache
*msgcache_new(void)
132 cache
= g_new0(MsgCache
, 1),
133 cache
->msgnum_table
= g_hash_table_new(g_int_hash
, g_int_equal
);
134 cache
->msgid_table
= g_hash_table_new(g_str_hash
, g_str_equal
);
135 cache
->last_access
= time(NULL
);
140 static gboolean
msgcache_msginfo_free_func(gpointer num
, gpointer msginfo
, gpointer user_data
)
142 procmsg_msginfo_free((MsgInfo
*)msginfo
);
146 void msgcache_destroy(MsgCache
*cache
)
148 cm_return_if_fail(cache
!= NULL
);
150 g_hash_table_foreach_remove(cache
->msgnum_table
, msgcache_msginfo_free_func
, NULL
);
151 g_hash_table_destroy(cache
->msgid_table
);
152 g_hash_table_destroy(cache
->msgnum_table
);
156 void msgcache_add_msg(MsgCache
*cache
, MsgInfo
*msginfo
)
160 cm_return_if_fail(cache
!= NULL
);
161 cm_return_if_fail(msginfo
!= NULL
);
163 newmsginfo
= procmsg_msginfo_new_ref(msginfo
);
164 g_hash_table_insert(cache
->msgnum_table
, &newmsginfo
->msgnum
, newmsginfo
);
165 if(newmsginfo
->msgid
!= NULL
)
166 g_hash_table_insert(cache
->msgid_table
, newmsginfo
->msgid
, newmsginfo
);
167 cache
->memusage
+= procmsg_msginfo_memusage(msginfo
);
168 cache
->last_access
= time(NULL
);
170 msginfo
->folder
->cache_dirty
= TRUE
;
172 debug_print("Cache size: %d messages, %u bytes\n", g_hash_table_size(cache
->msgnum_table
), cache
->memusage
);
175 void msgcache_remove_msg(MsgCache
*cache
, guint msgnum
)
179 cm_return_if_fail(cache
!= NULL
);
181 msginfo
= (MsgInfo
*) g_hash_table_lookup(cache
->msgnum_table
, &msgnum
);
185 cache
->memusage
-= procmsg_msginfo_memusage(msginfo
);
187 g_hash_table_remove(cache
->msgid_table
, msginfo
->msgid
);
188 g_hash_table_remove(cache
->msgnum_table
, &msginfo
->msgnum
);
189 procmsg_msginfo_free(msginfo
);
190 cache
->last_access
= time(NULL
);
192 msginfo
->folder
->cache_dirty
= TRUE
;
194 debug_print("Cache size: %d messages, %u bytes\n", g_hash_table_size(cache
->msgnum_table
), cache
->memusage
);
197 void msgcache_update_msg(MsgCache
*cache
, MsgInfo
*msginfo
)
199 MsgInfo
*oldmsginfo
, *newmsginfo
;
201 cm_return_if_fail(cache
!= NULL
);
202 cm_return_if_fail(msginfo
!= NULL
);
204 oldmsginfo
= g_hash_table_lookup(cache
->msgnum_table
, &msginfo
->msgnum
);
205 if(oldmsginfo
&& oldmsginfo
->msgid
)
206 g_hash_table_remove(cache
->msgid_table
, oldmsginfo
->msgid
);
208 g_hash_table_remove(cache
->msgnum_table
, &oldmsginfo
->msgnum
);
209 cache
->memusage
-= procmsg_msginfo_memusage(oldmsginfo
);
210 procmsg_msginfo_free(oldmsginfo
);
213 newmsginfo
= procmsg_msginfo_new_ref(msginfo
);
214 g_hash_table_insert(cache
->msgnum_table
, &newmsginfo
->msgnum
, newmsginfo
);
215 if(newmsginfo
->msgid
)
216 g_hash_table_insert(cache
->msgid_table
, newmsginfo
->msgid
, newmsginfo
);
217 cache
->memusage
+= procmsg_msginfo_memusage(newmsginfo
);
218 cache
->last_access
= time(NULL
);
220 debug_print("Cache size: %d messages, %u bytes\n", g_hash_table_size(cache
->msgnum_table
), cache
->memusage
);
222 msginfo
->folder
->cache_dirty
= TRUE
;
227 MsgInfo
*msgcache_get_msg(MsgCache
*cache
, guint num
)
231 cm_return_val_if_fail(cache
!= NULL
, NULL
);
233 msginfo
= g_hash_table_lookup(cache
->msgnum_table
, &num
);
236 cache
->last_access
= time(NULL
);
238 return procmsg_msginfo_new_ref(msginfo
);
241 MsgInfo
*msgcache_get_msg_by_id(MsgCache
*cache
, const gchar
*msgid
)
245 cm_return_val_if_fail(cache
!= NULL
, NULL
);
246 cm_return_val_if_fail(msgid
!= NULL
, NULL
);
248 msginfo
= g_hash_table_lookup(cache
->msgid_table
, msgid
);
251 cache
->last_access
= time(NULL
);
253 return procmsg_msginfo_new_ref(msginfo
);
256 static void msgcache_get_msg_list_func(gpointer key
, gpointer value
, gpointer user_data
)
258 MsgInfoList
**listptr
= user_data
;
259 MsgInfo
*msginfo
= value
;
261 *listptr
= g_slist_prepend(*listptr
, procmsg_msginfo_new_ref(msginfo
));
264 MsgInfoList
*msgcache_get_msg_list(MsgCache
*cache
)
266 MsgInfoList
*msg_list
= NULL
;
268 cm_return_val_if_fail(cache
!= NULL
, NULL
);
270 g_hash_table_foreach((GHashTable
*)cache
->msgnum_table
, msgcache_get_msg_list_func
, (gpointer
)&msg_list
);
271 cache
->last_access
= time(NULL
);
273 msg_list
= g_slist_reverse(msg_list
);
278 time_t msgcache_get_last_access_time(MsgCache
*cache
)
280 cm_return_val_if_fail(cache
!= NULL
, 0);
282 return cache
->last_access
;
285 gint
msgcache_get_memory_usage(MsgCache
*cache
)
287 cm_return_val_if_fail(cache
!= NULL
, 0);
289 return cache
->memusage
;
293 * Cache saving functions
296 #define READ_CACHE_DATA(data, fp, total_len) \
298 if ((tmp_len = msgcache_read_cache_data_str(fp, &data, conv)) < 0) { \
299 procmsg_msginfo_free(msginfo); \
303 total_len += tmp_len; \
306 #define READ_CACHE_DATA_INT(n, fp) \
311 if ((ni = fread(&idata, sizeof(idata), 1, fp)) != 1) { \
312 g_warning("read_int: Cache data corrupted, read %zd of %zd at " \
313 "offset %ld\n", ni, sizeof(idata), ftell(fp)); \
314 procmsg_msginfo_free(msginfo); \
318 n = swapping ? bswap_32(idata) : (idata);\
321 #define GET_CACHE_DATA_INT(n) \
324 g_print("error at rem_len:%d\n", rem_len); \
328 n = (swapping ? (MMAP_TO_GUINT32_SWAPPED(walk_data)):(MMAP_TO_GUINT32(walk_data))); \
329 walk_data += 4; rem_len -= 4; \
332 #define GET_CACHE_DATA(data, total_len) \
334 GET_CACHE_DATA_INT(tmp_len); \
335 if (rem_len < tmp_len) { \
336 g_print("error at rem_len:%d (tmp_len %d)\n", rem_len, tmp_len); \
340 if ((tmp_len = msgcache_get_cache_data_str(walk_data, &data, tmp_len, conv)) < 0) { \
341 g_print("error at rem_len:%d\n", rem_len);\
342 procmsg_msginfo_free(msginfo); \
346 total_len += tmp_len; \
347 walk_data += tmp_len; rem_len -= tmp_len; \
351 #define WRITE_CACHE_DATA_INT(n, fp) \
355 idata = (guint32)bswap_32(n); \
356 if (SC_FWRITE(&idata, sizeof(idata), 1, fp) != 1) \
361 #define PUT_CACHE_DATA_INT(n) \
363 walk_data[0]=(((guint32)n)&0x000000ff); \
364 walk_data[1]=(((guint32)n)&0x0000ff00)>>8; \
365 walk_data[2]=(((guint32)n)&0x00ff0000)>>16; \
366 walk_data[3]=(((guint32)n)&0xff000000)>>24; \
371 #define WRITE_CACHE_DATA(data, fp) \
377 len = strlen(data); \
378 WRITE_CACHE_DATA_INT(len, fp); \
379 if (w_err == 0 && len > 0) { \
380 if (SC_FWRITE(data, 1, len, fp) != len) \
386 #define PUT_CACHE_DATA(data) \
392 len = strlen(data); \
393 PUT_CACHE_DATA_INT(len); \
395 memcpy(walk_data, data, len); \
401 static FILE *msgcache_open_data_file(const gchar
*file
, guint version
,
403 gchar
*buf
, size_t buf_size
)
408 cm_return_val_if_fail(file
!= NULL
, NULL
);
410 if (mode
== DATA_WRITE
) {
411 int w_err
= 0, wrote
= 0;
412 if ((fp
= g_fopen(file
, "wb")) == NULL
) {
413 FILE_OP_ERROR(file
, "fopen");
416 if (change_file_mode_rw(fp
, file
) < 0)
417 FILE_OP_ERROR(file
, "chmod");
419 WRITE_CACHE_DATA_INT(version
, fp
);
421 g_warning("failed to write int\n");
429 if ((fp
= g_fopen(file
, "rb")) == NULL
)
430 debug_print("Mark/Cache file '%s' not found\n", file
);
432 if (buf
&& buf_size
> 0)
433 setvbuf(fp
, buf
, _IOFBF
, buf_size
);
434 if (fread(&data_ver
, sizeof(data_ver
), 1, fp
) != 1 ||
435 version
!= bswap_32(data_ver
)) {
436 g_message("%s: Mark/Cache version is different (%u != %u).\n",
437 file
, bswap_32(data_ver
), version
);
441 data_ver
= bswap_32(data_ver
);
444 if (mode
== DATA_READ
)
448 /* reopen with append mode */
450 if ((fp
= g_fopen(file
, "ab")) == NULL
)
451 FILE_OP_ERROR(file
, "fopen");
453 /* open with overwrite mode if mark file doesn't exist or
454 version is different */
455 fp
= msgcache_open_data_file(file
, version
, DATA_WRITE
, buf
,
462 static gint
msgcache_read_cache_data_str(FILE *fp
, gchar
**str
,
463 StringConverter
*conv
)
465 gchar
*tmpstr
= NULL
;
471 if ((ni
= fread(&len
, sizeof(len
), 1, fp
) != 1) ||
473 g_warning("read_data_str: Cache data (len) corrupted, read %zd "
474 "of %zd bytes at offset %ld\n", ni
, sizeof(len
),
479 if ((ni
= fread(&len
, sizeof(len
), 1, fp
) != 1) ||
480 bswap_32(len
) > G_MAXINT
) {
481 g_warning("read_data_str: Cache data (len) corrupted, read %zd "
482 "of %zd bytes at offset %ld\n", ni
, sizeof(len
),
492 tmpstr
= g_try_malloc(len
+ 1);
498 if ((ni
= fread(tmpstr
, 1, len
, fp
)) != len
) {
499 g_warning("read_data_str: Cache data corrupted, read %zd of %u "
500 "bytes at offset %ld\n",
508 *str
= conv
->convert(conv
, tmpstr
);
516 static gint
msgcache_get_cache_data_str(gchar
*src
, gchar
**str
, gint len
,
517 StringConverter
*conv
)
519 gchar
*tmpstr
= NULL
;
526 if(len
> 2*1024*1024) {
527 g_warning("read_data_str: refusing to allocate %d bytes.\n", len
);
531 tmpstr
= g_try_malloc(len
+ 1);
537 memcpy(tmpstr
, src
, len
);
541 *str
= conv
->convert(conv
, tmpstr
);
549 static gchar
*strconv_charset_convert(StringConverter
*conv
, gchar
*srcstr
)
551 CharsetConverter
*charsetconv
= (CharsetConverter
*) conv
;
553 return conv_codeset_strdup(srcstr
, charsetconv
->srccharset
, charsetconv
->dstcharset
);
556 static void strconv_charset_free(StringConverter
*conv
)
558 CharsetConverter
*charsetconv
= (CharsetConverter
*) conv
;
560 g_free(charsetconv
->srccharset
);
561 g_free(charsetconv
->dstcharset
);
564 MsgCache
*msgcache_read_cache(FolderItem
*item
, const gchar
*cache_file
)
569 MsgTmpFlags tmp_flags
= 0;
570 gchar file_buf
[BUFFSIZE
];
573 gboolean error
= FALSE
;
574 StringConverter
*conv
= NULL
;
575 gchar
*srccharset
= NULL
;
576 const gchar
*dstcharset
= NULL
;
579 gint tmp_len
= 0, map_len
= -1;
580 char *cache_data
= NULL
;
583 cm_return_val_if_fail(cache_file
!= NULL
, NULL
);
584 cm_return_val_if_fail(item
!= NULL
, NULL
);
588 /* In case we can't open the mark file with MARK_VERSION, check if we can open it with the
589 * swapped MARK_VERSION. As msgcache_open_data_file swaps it too, if this succeeds,
590 * it means it's the old version (not little-endian) on a big-endian machine. The code has
591 * no effect on x86 as their file doesn't change. */
593 if ((fp
= msgcache_open_data_file
594 (cache_file
, CACHE_VERSION
, DATA_READ
, file_buf
, sizeof(file_buf
))) == NULL
) {
595 if ((fp
= msgcache_open_data_file
596 (cache_file
, bswap_32(CACHE_VERSION
), DATA_READ
, file_buf
, sizeof(file_buf
))) == NULL
)
602 debug_print("\tReading %sswapped message cache from %s...\n", swapping
?"":"un", cache_file
);
604 if (folder_has_parent_of_type(item
, F_QUEUE
)) {
605 tmp_flags
|= MSG_QUEUED
;
606 } else if (folder_has_parent_of_type(item
, F_DRAFT
)) {
607 tmp_flags
|= MSG_DRAFT
;
610 if (msgcache_read_cache_data_str(fp
, &srccharset
, NULL
) < 0) {
614 dstcharset
= CS_UTF_8
;
615 if (srccharset
== NULL
|| dstcharset
== NULL
) {
617 } else if (strcmp(srccharset
, dstcharset
) == 0) {
618 debug_print("using Noop Converter\n");
622 CharsetConverter
*charsetconv
;
624 debug_print("using CharsetConverter\n");
626 charsetconv
= g_new0(CharsetConverter
, 1);
627 charsetconv
->converter
.convert
= strconv_charset_convert
;
628 charsetconv
->converter
.free
= strconv_charset_free
;
629 charsetconv
->srccharset
= g_strdup(srccharset
);
630 charsetconv
->dstcharset
= g_strdup(dstcharset
);
632 conv
= (StringConverter
*) charsetconv
;
636 cache
= msgcache_new();
638 if (msgcache_use_mmap_read
== TRUE
) {
639 if (fstat(fileno(fp
), &st
) >= 0)
640 map_len
= st
.st_size
;
646 HANDLE hFile
, hMapping
;
647 hFile
= (HANDLE
) _get_osfhandle (fileno(fp
));
648 if (hFile
== (HANDLE
) -1)
650 hMapping
= CreateFileMapping(hFile
, NULL
, PAGE_WRITECOPY
, 0, 0, NULL
);
653 cache_data
= (unsigned char *)MapViewOfFile(hMapping
, FILE_MAP_COPY
, 0, 0, 0);
654 CloseHandle (hMapping
);
658 cache_data
= mmap(NULL
, map_len
, PROT_READ
, MAP_PRIVATE
, fileno(fp
), 0);
664 if (cache_data
!= NULL
&& cache_data
!= MAP_FAILED
) {
665 int rem_len
= map_len
-ftell(fp
);
666 char *walk_data
= cache_data
+ftell(fp
);
669 GET_CACHE_DATA_INT(num
);
671 msginfo
= procmsg_msginfo_new();
672 msginfo
->msgnum
= num
;
673 memusage
+= sizeof(MsgInfo
);
675 GET_CACHE_DATA_INT(msginfo
->size
);
676 GET_CACHE_DATA_INT(msginfo
->mtime
);
677 GET_CACHE_DATA_INT(msginfo
->date_t
);
678 GET_CACHE_DATA_INT(msginfo
->flags
.tmp_flags
);
680 GET_CACHE_DATA(msginfo
->fromname
, memusage
);
682 GET_CACHE_DATA(msginfo
->date
, memusage
);
683 GET_CACHE_DATA(msginfo
->from
, memusage
);
684 GET_CACHE_DATA(msginfo
->to
, memusage
);
685 GET_CACHE_DATA(msginfo
->cc
, memusage
);
686 GET_CACHE_DATA(msginfo
->newsgroups
, memusage
);
687 GET_CACHE_DATA(msginfo
->subject
, memusage
);
688 GET_CACHE_DATA(msginfo
->msgid
, memusage
);
689 GET_CACHE_DATA(msginfo
->inreplyto
, memusage
);
690 GET_CACHE_DATA(msginfo
->xref
, memusage
);
692 GET_CACHE_DATA_INT(msginfo
->planned_download
);
693 GET_CACHE_DATA_INT(msginfo
->total_size
);
694 GET_CACHE_DATA_INT(refnum
);
696 for (; refnum
!= 0; refnum
--) {
699 GET_CACHE_DATA(ref
, memusage
);
702 msginfo
->references
=
703 g_slist_prepend(msginfo
->references
, ref
);
705 if (msginfo
->references
)
706 msginfo
->references
=
707 g_slist_reverse(msginfo
->references
);
709 msginfo
->folder
= item
;
710 msginfo
->flags
.tmp_flags
|= tmp_flags
;
712 g_hash_table_insert(cache
->msgnum_table
, &msginfo
->msgnum
, msginfo
);
714 g_hash_table_insert(cache
->msgid_table
, msginfo
->msgid
, msginfo
);
718 UnmapViewOfFile((void*) cache_data
);
720 munmap(cache_data
, map_len
);
723 while (fread(&num
, sizeof(num
), 1, fp
) == 1) {
727 msginfo
= procmsg_msginfo_new();
728 msginfo
->msgnum
= num
;
729 memusage
+= sizeof(MsgInfo
);
731 READ_CACHE_DATA_INT(msginfo
->size
, fp
);
732 READ_CACHE_DATA_INT(msginfo
->mtime
, fp
);
733 READ_CACHE_DATA_INT(msginfo
->date_t
, fp
);
734 READ_CACHE_DATA_INT(msginfo
->flags
.tmp_flags
, fp
);
736 READ_CACHE_DATA(msginfo
->fromname
, fp
, memusage
);
738 READ_CACHE_DATA(msginfo
->date
, fp
, memusage
);
739 READ_CACHE_DATA(msginfo
->from
, fp
, memusage
);
740 READ_CACHE_DATA(msginfo
->to
, fp
, memusage
);
741 READ_CACHE_DATA(msginfo
->cc
, fp
, memusage
);
742 READ_CACHE_DATA(msginfo
->newsgroups
, fp
, memusage
);
743 READ_CACHE_DATA(msginfo
->subject
, fp
, memusage
);
744 READ_CACHE_DATA(msginfo
->msgid
, fp
, memusage
);
745 READ_CACHE_DATA(msginfo
->inreplyto
, fp
, memusage
);
746 READ_CACHE_DATA(msginfo
->xref
, fp
, memusage
);
748 READ_CACHE_DATA_INT(msginfo
->planned_download
, fp
);
749 READ_CACHE_DATA_INT(msginfo
->total_size
, fp
);
750 READ_CACHE_DATA_INT(refnum
, fp
);
752 for (; refnum
!= 0; refnum
--) {
755 READ_CACHE_DATA(ref
, fp
, memusage
);
758 msginfo
->references
=
759 g_slist_prepend(msginfo
->references
, ref
);
761 if (msginfo
->references
)
762 msginfo
->references
=
763 g_slist_reverse(msginfo
->references
);
765 msginfo
->folder
= item
;
766 msginfo
->flags
.tmp_flags
|= tmp_flags
;
768 g_hash_table_insert(cache
->msgnum_table
, &msginfo
->msgnum
, msginfo
);
770 g_hash_table_insert(cache
->msgid_table
, msginfo
->msgid
, msginfo
);
777 if (conv
->free
!= NULL
)
783 msgcache_destroy(cache
);
787 cache
->last_access
= time(NULL
);
788 cache
->memusage
= memusage
;
790 debug_print("done. (%d items read)\n", g_hash_table_size(cache
->msgnum_table
));
791 debug_print("Cache size: %d messages, %u bytes\n", g_hash_table_size(cache
->msgnum_table
), cache
->memusage
);
796 void msgcache_read_mark(MsgCache
*cache
, const gchar
*mark_file
)
800 MsgPermFlags perm_flags
;
803 char *cache_data
= NULL
;
809 /* In case we can't open the mark file with MARK_VERSION, check if we can open it with the
810 * swapped MARK_VERSION. As msgcache_open_data_file swaps it too, if this succeeds,
811 * it means it's the old version (not little-endian) on a big-endian machine. The code has
812 * no effect on x86 as their file doesn't change. */
814 if ((fp
= msgcache_open_data_file(mark_file
, MARK_VERSION
, DATA_READ
, NULL
, 0)) == NULL
) {
815 /* see if it isn't swapped ? */
816 if ((fp
= msgcache_open_data_file(mark_file
, bswap_32(MARK_VERSION
), DATA_READ
, NULL
, 0)) == NULL
)
819 swapping
= FALSE
; /* yay */
821 debug_print("reading %sswapped mark file.\n", swapping
?"":"un");
823 if (msgcache_use_mmap_read
) {
824 if (fstat(fileno(fp
), &st
) >= 0)
825 map_len
= st
.st_size
;
831 HANDLE hFile
, hMapping
;
832 hFile
= (HANDLE
) _get_osfhandle (fileno(fp
));
833 if (hFile
== (HANDLE
) -1)
835 hMapping
= CreateFileMapping(hFile
, NULL
, PAGE_WRITECOPY
, 0, 0, NULL
);
838 cache_data
= (unsigned char *)MapViewOfFile(hMapping
, FILE_MAP_COPY
, 0, 0, 0);
839 CloseHandle (hMapping
);
843 cache_data
= mmap(NULL
, map_len
, PROT_READ
, MAP_PRIVATE
, fileno(fp
), 0);
849 if (cache_data
!= NULL
&& cache_data
!= MAP_FAILED
) {
850 int rem_len
= map_len
-ftell(fp
);
851 char *walk_data
= cache_data
+ftell(fp
);
854 GET_CACHE_DATA_INT(num
);
855 GET_CACHE_DATA_INT(perm_flags
);
856 msginfo
= g_hash_table_lookup(cache
->msgnum_table
, &num
);
858 msginfo
->flags
.perm_flags
= perm_flags
;
862 UnmapViewOfFile((void*) cache_data
);
864 munmap(cache_data
, map_len
);
867 while (fread(&num
, sizeof(num
), 1, fp
) == 1) {
870 if (fread(&perm_flags
, sizeof(perm_flags
), 1, fp
) != 1) break;
872 perm_flags
= bswap_32(perm_flags
);
873 msginfo
= g_hash_table_lookup(cache
->msgnum_table
, &num
);
875 msginfo
->flags
.perm_flags
= perm_flags
;
883 void msgcache_read_tags(MsgCache
*cache
, const gchar
*tags_file
)
889 char *cache_data
= NULL
;
891 gboolean error
= FALSE
;
895 /* In case we can't open the mark file with MARK_VERSION, check if we can open it with the
896 * swapped MARK_VERSION. As msgcache_open_data_file swaps it too, if this succeeds,
897 * it means it's the old version (not little-endian) on a big-endian machine. The code has
898 * no effect on x86 as their file doesn't change. */
900 if ((fp
= msgcache_open_data_file(tags_file
, TAGS_VERSION
, DATA_READ
, NULL
, 0)) == NULL
) {
901 /* see if it isn't swapped ? */
902 if ((fp
= msgcache_open_data_file(tags_file
, bswap_32(TAGS_VERSION
), DATA_READ
, NULL
, 0)) == NULL
)
905 swapping
= FALSE
; /* yay */
907 debug_print("reading %sswapped tags file.\n", swapping
?"":"un");
909 if (msgcache_use_mmap_read
) {
910 if (fstat(fileno(fp
), &st
) >= 0)
911 map_len
= st
.st_size
;
917 HANDLE hFile
, hMapping
;
918 hFile
= (HANDLE
) _get_osfhandle (fileno(fp
));
919 if (hFile
== (HANDLE
) -1)
921 hMapping
= CreateFileMapping(hFile
, NULL
, PAGE_WRITECOPY
, 0, 0, NULL
);
924 cache_data
= (unsigned char *)MapViewOfFile(hMapping
, FILE_MAP_COPY
, 0, 0, 0);
925 CloseHandle (hMapping
);
929 cache_data
= mmap(NULL
, map_len
, PROT_READ
, MAP_PRIVATE
, fileno(fp
), 0);
935 if (cache_data
!= NULL
&& cache_data
!= MAP_FAILED
) {
936 int rem_len
= map_len
-ftell(fp
);
937 char *walk_data
= cache_data
+ftell(fp
);
941 GET_CACHE_DATA_INT(num
);
942 msginfo
= g_hash_table_lookup(cache
->msgnum_table
, &num
);
944 g_slist_free(msginfo
->tags
);
945 msginfo
->tags
= NULL
;
947 GET_CACHE_DATA_INT(id
);
949 msginfo
->tags
= g_slist_prepend(
951 GINT_TO_POINTER(id
));
954 msginfo
->tags
= g_slist_reverse(msginfo
->tags
);
958 UnmapViewOfFile((void*) cache_data
);
960 munmap(cache_data
, map_len
);
963 while (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 (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
);
990 static int msgcache_write_cache(MsgInfo
*msginfo
, FILE *fp
)
992 MsgTmpFlags flags
= msginfo
->flags
.tmp_flags
& MSG_CACHED_FLAG_MASK
;
994 int w_err
= 0, wrote
= 0;
996 WRITE_CACHE_DATA_INT(msginfo
->msgnum
, fp
);
997 WRITE_CACHE_DATA_INT(msginfo
->size
, fp
);
998 WRITE_CACHE_DATA_INT(msginfo
->mtime
, fp
);
999 WRITE_CACHE_DATA_INT(msginfo
->date_t
, fp
);
1000 WRITE_CACHE_DATA_INT(flags
, fp
);
1002 WRITE_CACHE_DATA(msginfo
->fromname
, fp
);
1004 WRITE_CACHE_DATA(msginfo
->date
, fp
);
1005 WRITE_CACHE_DATA(msginfo
->from
, fp
);
1006 WRITE_CACHE_DATA(msginfo
->to
, fp
);
1007 WRITE_CACHE_DATA(msginfo
->cc
, fp
);
1008 WRITE_CACHE_DATA(msginfo
->newsgroups
, fp
);
1009 WRITE_CACHE_DATA(msginfo
->subject
, fp
);
1010 WRITE_CACHE_DATA(msginfo
->msgid
, fp
);
1011 WRITE_CACHE_DATA(msginfo
->inreplyto
, fp
);
1012 WRITE_CACHE_DATA(msginfo
->xref
, fp
);
1013 WRITE_CACHE_DATA_INT(msginfo
->planned_download
, fp
);
1014 WRITE_CACHE_DATA_INT(msginfo
->total_size
, fp
);
1016 WRITE_CACHE_DATA_INT(g_slist_length(msginfo
->references
), fp
);
1018 for (cur
= msginfo
->references
; cur
!= NULL
; cur
= cur
->next
) {
1019 WRITE_CACHE_DATA((gchar
*)cur
->data
, fp
);
1021 return w_err
? -1 : wrote
;
1024 static int msgcache_write_flags(MsgInfo
*msginfo
, FILE *fp
)
1026 MsgPermFlags flags
= msginfo
->flags
.perm_flags
;
1027 int w_err
= 0, wrote
= 0;
1028 WRITE_CACHE_DATA_INT(msginfo
->msgnum
, fp
);
1029 WRITE_CACHE_DATA_INT(flags
, fp
);
1030 return w_err
? -1 : wrote
;
1033 static int msgcache_write_tags(MsgInfo
*msginfo
, FILE *fp
)
1035 GSList
*cur
= msginfo
->tags
;
1036 int w_err
= 0, wrote
= 0;
1038 WRITE_CACHE_DATA_INT(msginfo
->msgnum
, fp
);
1039 for (; cur
; cur
= cur
->next
) {
1040 gint id
= GPOINTER_TO_INT(cur
->data
);
1041 if (tags_get_tag(id
) != NULL
) {
1042 WRITE_CACHE_DATA_INT(id
, fp
);
1045 WRITE_CACHE_DATA_INT(-1, fp
);
1047 return w_err
? -1 : wrote
;
1061 static void msgcache_write_func(gpointer key
, gpointer value
, gpointer user_data
)
1064 struct write_fps
*write_fps
;
1067 msginfo
= (MsgInfo
*)value
;
1068 write_fps
= user_data
;
1070 if (write_fps
->cache_fp
) {
1071 tmp
= msgcache_write_cache(msginfo
, write_fps
->cache_fp
);
1073 write_fps
->error
= 1;
1075 write_fps
->cache_size
+= tmp
;
1077 if (write_fps
->mark_fp
) {
1078 tmp
= msgcache_write_flags(msginfo
, write_fps
->mark_fp
);
1080 write_fps
->error
= 1;
1082 write_fps
->mark_size
+= tmp
;
1084 if (write_fps
->tags_fp
) {
1085 tmp
= msgcache_write_tags(msginfo
, write_fps
->tags_fp
);
1087 write_fps
->error
= 1;
1089 write_fps
->tags_size
+= tmp
;
1093 gint
msgcache_write(const gchar
*cache_file
, const gchar
*mark_file
, const gchar
*tags_file
, MsgCache
*cache
)
1095 struct write_fps write_fps
;
1096 gchar
*new_cache
, *new_mark
, *new_tags
;
1097 int w_err
= 0, wrote
= 0;
1100 cm_return_val_if_fail(cache
!= NULL
, -1);
1102 new_cache
= g_strconcat(cache_file
, ".new", NULL
);
1103 new_mark
= g_strconcat(mark_file
, ".new", NULL
);
1104 new_tags
= g_strconcat(tags_file
, ".new", NULL
);
1106 write_fps
.error
= 0;
1107 write_fps
.cache_size
= 0;
1108 write_fps
.mark_size
= 0;
1109 write_fps
.tags_size
= 0;
1111 /* open files and write headers */
1114 write_fps
.cache_fp
= msgcache_open_data_file(new_cache
, CACHE_VERSION
,
1115 DATA_WRITE
, NULL
, 0);
1116 if (write_fps
.cache_fp
== NULL
) {
1122 WRITE_CACHE_DATA(CS_UTF_8
, write_fps
.cache_fp
);
1124 write_fps
.cache_fp
= NULL
;
1128 g_warning("failed to write charset\n");
1129 fclose(write_fps
.cache_fp
);
1130 claws_unlink(new_cache
);
1138 write_fps
.mark_fp
= msgcache_open_data_file(new_mark
, MARK_VERSION
,
1139 DATA_WRITE
, NULL
, 0);
1140 if (write_fps
.mark_fp
== NULL
) {
1141 fclose(write_fps
.cache_fp
);
1142 claws_unlink(new_cache
);
1149 write_fps
.mark_fp
= NULL
;
1153 write_fps
.tags_fp
= msgcache_open_data_file(new_tags
, TAGS_VERSION
,
1154 DATA_WRITE
, NULL
, 0);
1155 if (write_fps
.tags_fp
== NULL
) {
1156 fclose(write_fps
.cache_fp
);
1157 fclose(write_fps
.mark_fp
);
1158 claws_unlink(new_cache
);
1159 claws_unlink(new_mark
);
1166 write_fps
.tags_fp
= NULL
;
1169 debug_print("\tWriting message cache to %s and %s...\n", new_cache
, new_mark
);
1171 if (write_fps
.cache_fp
&& change_file_mode_rw(write_fps
.cache_fp
, new_cache
) < 0)
1172 FILE_OP_ERROR(new_cache
, "chmod");
1174 /* headers written, note file size */
1175 if (write_fps
.cache_fp
)
1176 write_fps
.cache_size
= ftell(write_fps
.cache_fp
);
1177 if (write_fps
.mark_fp
)
1178 write_fps
.mark_size
= ftell(write_fps
.mark_fp
);
1179 if (write_fps
.tags_fp
)
1180 write_fps
.tags_size
= ftell(write_fps
.tags_fp
);
1182 #ifdef HAVE_FWRITE_UNLOCKED
1183 /* lock files for write once (instead of once per fwrite) */
1184 if (write_fps
.cache_fp
)
1185 flockfile(write_fps
.cache_fp
);
1186 if (write_fps
.mark_fp
)
1187 flockfile(write_fps
.mark_fp
);
1188 if (write_fps
.tags_fp
)
1189 flockfile(write_fps
.tags_fp
);
1191 /* write data to the files */
1192 g_hash_table_foreach(cache
->msgnum_table
, msgcache_write_func
, (gpointer
)&write_fps
);
1193 #ifdef HAVE_FWRITE_UNLOCKED
1195 if (write_fps
.cache_fp
)
1196 funlockfile(write_fps
.cache_fp
);
1197 if (write_fps
.mark_fp
)
1198 funlockfile(write_fps
.mark_fp
);
1199 if (write_fps
.tags_fp
)
1200 funlockfile(write_fps
.tags_fp
);
1203 if (write_fps
.cache_fp
)
1204 write_fps
.error
|= (fflush(write_fps
.cache_fp
) != 0);
1205 if (write_fps
.mark_fp
)
1206 write_fps
.error
|= (fflush(write_fps
.mark_fp
) != 0);
1207 if (write_fps
.tags_fp
)
1208 write_fps
.error
|= (fflush(write_fps
.tags_fp
) != 0);
1210 /* sync to filesystem */
1211 if (prefs_common
.flush_metadata
&& write_fps
.cache_fp
)
1212 write_fps
.error
|= (fsync(fileno(write_fps
.cache_fp
)) != 0);
1213 if (prefs_common
.flush_metadata
&& write_fps
.mark_fp
)
1214 write_fps
.error
|= (fsync(fileno(write_fps
.mark_fp
)) != 0);
1215 if (prefs_common
.flush_metadata
&& write_fps
.tags_fp
)
1216 write_fps
.error
|= (fsync(fileno(write_fps
.tags_fp
)) != 0);
1219 if (write_fps
.cache_fp
)
1220 write_fps
.error
|= (fclose(write_fps
.cache_fp
) != 0);
1221 if (write_fps
.mark_fp
)
1222 write_fps
.error
|= (fclose(write_fps
.mark_fp
) != 0);
1223 if (write_fps
.tags_fp
)
1224 write_fps
.error
|= (fclose(write_fps
.tags_fp
) != 0);
1227 if (write_fps
.error
!= 0) {
1228 /* in case of error, forget all */
1229 claws_unlink(new_cache
);
1230 claws_unlink(new_mark
);
1231 claws_unlink(new_tags
);
1239 move_file(new_cache
, cache_file
, TRUE
);
1241 move_file(new_mark
, mark_file
, TRUE
);
1243 move_file(new_tags
, tags_file
, TRUE
);
1244 cache
->last_access
= time(NULL
);
1250 debug_print("done.\n");