2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2007 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>
49 #ifdef HAVE_FWRITE_UNLOCKED
50 #define SC_FWRITE fwrite_unlocked
52 #define SC_FWRITE fwrite
55 #if G_BYTE_ORDER == G_BIG_ENDIAN
57 ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \
58 (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24))
60 #define MMAP_TO_GUINT32(x) \
62 ((x[2]&0xff) << 8) | \
63 ((x[1]&0xff) << 16) | \
66 #define MMAP_TO_GUINT32_SWAPPED(x) \
68 ((x[1]&0xff) << 8) | \
69 ((x[2]&0xff) << 16) | \
72 static gboolean msgcache_use_mmap_read
= TRUE
;
73 static gboolean msgcache_use_mmap_write
= FALSE
;
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
;
91 static gboolean msgcache_use_mmap_write
= FALSE
;
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 g_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 g_return_if_fail(cache
!= NULL
);
162 g_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 debug_print("Cache size: %d messages, %d bytes\n", g_hash_table_size(cache
->msgnum_table
), cache
->memusage
);
174 void msgcache_remove_msg(MsgCache
*cache
, guint msgnum
)
178 g_return_if_fail(cache
!= NULL
);
179 g_return_if_fail(msgnum
> 0);
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 debug_print("Cache size: %d messages, %d byte\n", g_hash_table_size(cache
->msgnum_table
), cache
->memusage
);
195 void msgcache_update_msg(MsgCache
*cache
, MsgInfo
*msginfo
)
197 MsgInfo
*oldmsginfo
, *newmsginfo
;
199 g_return_if_fail(cache
!= NULL
);
200 g_return_if_fail(msginfo
!= NULL
);
202 oldmsginfo
= g_hash_table_lookup(cache
->msgnum_table
, &msginfo
->msgnum
);
203 if(oldmsginfo
&& oldmsginfo
->msgid
)
204 g_hash_table_remove(cache
->msgid_table
, oldmsginfo
->msgid
);
206 g_hash_table_remove(cache
->msgnum_table
, &oldmsginfo
->msgnum
);
207 cache
->memusage
-= procmsg_msginfo_memusage(oldmsginfo
);
208 procmsg_msginfo_free(oldmsginfo
);
211 newmsginfo
= procmsg_msginfo_new_ref(msginfo
);
212 g_hash_table_insert(cache
->msgnum_table
, &newmsginfo
->msgnum
, newmsginfo
);
213 if(newmsginfo
->msgid
)
214 g_hash_table_insert(cache
->msgid_table
, newmsginfo
->msgid
, newmsginfo
);
215 cache
->memusage
+= procmsg_msginfo_memusage(newmsginfo
);
216 cache
->last_access
= time(NULL
);
218 debug_print("Cache size: %d messages, %d byte\n", g_hash_table_size(cache
->msgnum_table
), cache
->memusage
);
223 MsgInfo
*msgcache_get_msg(MsgCache
*cache
, guint num
)
227 g_return_val_if_fail(cache
!= NULL
, NULL
);
229 msginfo
= g_hash_table_lookup(cache
->msgnum_table
, &num
);
232 cache
->last_access
= time(NULL
);
234 return procmsg_msginfo_new_ref(msginfo
);
237 MsgInfo
*msgcache_get_msg_by_id(MsgCache
*cache
, const gchar
*msgid
)
241 g_return_val_if_fail(cache
!= NULL
, NULL
);
242 g_return_val_if_fail(msgid
!= NULL
, NULL
);
244 msginfo
= g_hash_table_lookup(cache
->msgid_table
, msgid
);
247 cache
->last_access
= time(NULL
);
249 return procmsg_msginfo_new_ref(msginfo
);
252 static void msgcache_get_msg_list_func(gpointer key
, gpointer value
, gpointer user_data
)
254 MsgInfoList
**listptr
= user_data
;
255 MsgInfo
*msginfo
= value
;
257 *listptr
= g_slist_prepend(*listptr
, procmsg_msginfo_new_ref(msginfo
));
260 MsgInfoList
*msgcache_get_msg_list(MsgCache
*cache
)
262 MsgInfoList
*msg_list
= NULL
;
264 g_return_val_if_fail(cache
!= NULL
, NULL
);
266 g_hash_table_foreach((GHashTable
*)cache
->msgnum_table
, msgcache_get_msg_list_func
, (gpointer
)&msg_list
);
267 cache
->last_access
= time(NULL
);
269 msg_list
= g_slist_reverse(msg_list
);
274 time_t msgcache_get_last_access_time(MsgCache
*cache
)
276 g_return_val_if_fail(cache
!= NULL
, 0);
278 return cache
->last_access
;
281 gint
msgcache_get_memory_usage(MsgCache
*cache
)
283 g_return_val_if_fail(cache
!= NULL
, 0);
285 return cache
->memusage
;
289 * Cache saving functions
292 #define READ_CACHE_DATA(data, fp, total_len) \
294 if ((tmp_len = msgcache_read_cache_data_str(fp, &data, conv)) < 0) { \
295 procmsg_msginfo_free(msginfo); \
299 total_len += tmp_len; \
302 #define READ_CACHE_DATA_INT(n, fp) \
307 if ((ni = fread(&idata, sizeof(idata), 1, fp)) != 1) { \
308 g_warning("read_int: Cache data corrupted, read %d of %d at " \
309 "offset %ld\n", ni, sizeof(idata), ftell(fp)); \
310 procmsg_msginfo_free(msginfo); \
314 n = swapping ? bswap_32(idata) : (idata);\
317 #define GET_CACHE_DATA_INT(n) \
319 n = (swapping ? (MMAP_TO_GUINT32_SWAPPED(walk_data)):(MMAP_TO_GUINT32(walk_data))); \
320 walk_data += 4; rem_len -= 4; \
323 #define GET_CACHE_DATA(data, total_len) \
325 GET_CACHE_DATA_INT(tmp_len); \
326 if ((tmp_len = msgcache_get_cache_data_str(walk_data, &data, tmp_len, conv)) < 0) { \
327 g_print("error at rem_len:%d\n", rem_len);\
328 procmsg_msginfo_free(msginfo); \
332 total_len += tmp_len; \
333 walk_data += tmp_len; rem_len -= tmp_len; \
337 #define WRITE_CACHE_DATA_INT(n, fp) \
341 idata = (guint32)bswap_32(n); \
342 if (SC_FWRITE(&idata, sizeof(idata), 1, fp) != 1) \
347 #define PUT_CACHE_DATA_INT(n) \
349 walk_data[0]=(((guint32)n)&0x000000ff); \
350 walk_data[1]=(((guint32)n)&0x0000ff00)>>8; \
351 walk_data[2]=(((guint32)n)&0x00ff0000)>>16; \
352 walk_data[3]=(((guint32)n)&0xff000000)>>24; \
357 #define WRITE_CACHE_DATA(data, fp) \
363 len = strlen(data); \
364 WRITE_CACHE_DATA_INT(len, fp); \
365 if (w_err == 0 && len > 0) { \
366 if (SC_FWRITE(data, 1, len, fp) != len) \
372 #define PUT_CACHE_DATA(data) \
378 len = strlen(data); \
379 PUT_CACHE_DATA_INT(len); \
381 memcpy(walk_data, data, len); \
387 static FILE *msgcache_open_data_file(const gchar
*file
, guint version
,
389 gchar
*buf
, size_t buf_size
)
394 g_return_val_if_fail(file
!= NULL
, NULL
);
396 if (mode
== DATA_WRITE
) {
397 int w_err
= 0, wrote
= 0;
398 if ((fp
= g_fopen(file
, "wb")) == NULL
) {
399 FILE_OP_ERROR(file
, "fopen");
402 if (change_file_mode_rw(fp
, file
) < 0)
403 FILE_OP_ERROR(file
, "chmod");
405 WRITE_CACHE_DATA_INT(version
, fp
);
407 g_warning("failed to write int\n");
415 if ((fp
= g_fopen(file
, "rb")) == NULL
)
416 debug_print("Mark/Cache file '%s' not found\n", file
);
418 if (buf
&& buf_size
> 0)
419 setvbuf(fp
, buf
, _IOFBF
, buf_size
);
420 if (fread(&data_ver
, sizeof(data_ver
), 1, fp
) != 1 ||
421 version
!= bswap_32(data_ver
)) {
422 g_message("%s: Mark/Cache version is different (%u != %u).\n",
423 file
, bswap_32(data_ver
), version
);
427 data_ver
= bswap_32(data_ver
);
430 if (mode
== DATA_READ
)
434 /* reopen with append mode */
436 if ((fp
= g_fopen(file
, "ab")) == NULL
)
437 FILE_OP_ERROR(file
, "fopen");
439 /* open with overwrite mode if mark file doesn't exist or
440 version is different */
441 fp
= msgcache_open_data_file(file
, version
, DATA_WRITE
, buf
,
448 static gint
msgcache_read_cache_data_str(FILE *fp
, gchar
**str
,
449 StringConverter
*conv
)
451 gchar
*tmpstr
= NULL
;
457 if ((ni
= fread(&len
, sizeof(len
), 1, fp
) != 1) ||
459 g_warning("read_data_str: Cache data (len) corrupted, read %d "
460 "of %d bytes at offset %ld\n", ni
, sizeof(len
),
465 if ((ni
= fread(&len
, sizeof(len
), 1, fp
) != 1) ||
466 bswap_32(len
) > G_MAXINT
) {
467 g_warning("read_data_str: Cache data (len) corrupted, read %d "
468 "of %d bytes at offset %ld\n", ni
, sizeof(len
),
478 tmpstr
= g_try_malloc(len
+ 1);
484 if ((ni
= fread(tmpstr
, 1, len
, fp
)) != len
) {
485 g_warning("read_data_str: Cache data corrupted, read %d of %d "
486 "bytes at offset %ld\n",
494 *str
= conv
->convert(conv
, tmpstr
);
502 static gint
msgcache_get_cache_data_str(gchar
*src
, gchar
**str
, gint len
,
503 StringConverter
*conv
)
505 gchar
*tmpstr
= NULL
;
512 if(len
> 2*1024*1024) {
513 g_warning("read_data_str: refusing to allocate %d bytes.\n", len
);
517 tmpstr
= g_try_malloc(len
+ 1);
523 strncpy(tmpstr
, src
, len
);
528 *str
= conv
->convert(conv
, tmpstr
);
536 static gchar
*strconv_charset_convert(StringConverter
*conv
, gchar
*srcstr
)
538 CharsetConverter
*charsetconv
= (CharsetConverter
*) conv
;
540 return conv_codeset_strdup(srcstr
, charsetconv
->srccharset
, charsetconv
->dstcharset
);
543 static void strconv_charset_free(StringConverter
*conv
)
545 CharsetConverter
*charsetconv
= (CharsetConverter
*) conv
;
547 g_free(charsetconv
->srccharset
);
548 g_free(charsetconv
->dstcharset
);
551 MsgCache
*msgcache_read_cache(FolderItem
*item
, const gchar
*cache_file
)
556 MsgTmpFlags tmp_flags
= 0;
557 gchar file_buf
[BUFFSIZE
];
560 gboolean error
= FALSE
;
561 StringConverter
*conv
= NULL
;
562 gchar
*srccharset
= NULL
;
563 const gchar
*dstcharset
= NULL
;
566 gint tmp_len
= 0, map_len
= -1;
567 char *cache_data
= NULL
;
570 g_return_val_if_fail(cache_file
!= NULL
, NULL
);
571 g_return_val_if_fail(item
!= NULL
, NULL
);
575 /* In case we can't open the mark file with MARK_VERSION, check if we can open it with the
576 * swapped MARK_VERSION. As msgcache_open_data_file swaps it too, if this succeeds,
577 * it means it's the old version (not little-endian) on a big-endian machine. The code has
578 * no effect on x86 as their file doesn't change. */
580 if ((fp
= msgcache_open_data_file
581 (cache_file
, CACHE_VERSION
, DATA_READ
, file_buf
, sizeof(file_buf
))) == NULL
) {
582 if ((fp
= msgcache_open_data_file
583 (cache_file
, bswap_32(CACHE_VERSION
), DATA_READ
, file_buf
, sizeof(file_buf
))) == NULL
)
589 debug_print("\tReading %sswapped message cache from %s...\n", swapping
?"":"un", cache_file
);
591 if (folder_has_parent_of_type(item
, F_QUEUE
)) {
592 tmp_flags
|= MSG_QUEUED
;
593 } else if (folder_has_parent_of_type(item
, F_DRAFT
)) {
594 tmp_flags
|= MSG_DRAFT
;
597 if (msgcache_read_cache_data_str(fp
, &srccharset
, NULL
) < 0) {
601 dstcharset
= CS_UTF_8
;
602 if (srccharset
== NULL
|| dstcharset
== NULL
) {
604 } else if (strcmp(srccharset
, dstcharset
) == 0) {
605 debug_print("using Noop Converter\n");
609 CharsetConverter
*charsetconv
;
611 debug_print("using CharsetConverter\n");
613 charsetconv
= g_new0(CharsetConverter
, 1);
614 charsetconv
->converter
.convert
= strconv_charset_convert
;
615 charsetconv
->converter
.free
= strconv_charset_free
;
616 charsetconv
->srccharset
= g_strdup(srccharset
);
617 charsetconv
->dstcharset
= g_strdup(dstcharset
);
619 conv
= (StringConverter
*) charsetconv
;
623 cache
= msgcache_new();
625 if (msgcache_use_mmap_read
== TRUE
) {
626 if (fstat(fileno(fp
), &st
) >= 0)
627 map_len
= st
.st_size
;
633 HANDLE hFile
, hMapping
;
634 hFile
= (HANDLE
) _get_osfhandle (fileno(fp
));
635 if (hFile
== (HANDLE
) -1)
637 hMapping
= CreateFileMapping(hFile
, NULL
, PAGE_WRITECOPY
, 0, 0, NULL
);
640 cache_data
= (unsigned char *)MapViewOfFile(hMapping
, FILE_MAP_COPY
, 0, 0, 0);
641 CloseHandle (hMapping
);
645 cache_data
= mmap(NULL
, map_len
, PROT_READ
, MAP_PRIVATE
, fileno(fp
), 0);
651 if (cache_data
!= NULL
&& cache_data
!= MAP_FAILED
) {
652 int rem_len
= map_len
-ftell(fp
);
653 char *walk_data
= cache_data
+ftell(fp
);
656 GET_CACHE_DATA_INT(num
);
658 msginfo
= procmsg_msginfo_new();
659 msginfo
->msgnum
= num
;
660 memusage
+= sizeof(MsgInfo
);
662 GET_CACHE_DATA_INT(msginfo
->size
);
663 GET_CACHE_DATA_INT(msginfo
->mtime
);
664 GET_CACHE_DATA_INT(msginfo
->date_t
);
665 GET_CACHE_DATA_INT(msginfo
->flags
.tmp_flags
);
667 GET_CACHE_DATA(msginfo
->fromname
, memusage
);
669 GET_CACHE_DATA(msginfo
->date
, memusage
);
670 GET_CACHE_DATA(msginfo
->from
, memusage
);
671 GET_CACHE_DATA(msginfo
->to
, memusage
);
672 GET_CACHE_DATA(msginfo
->cc
, memusage
);
673 GET_CACHE_DATA(msginfo
->newsgroups
, memusage
);
674 GET_CACHE_DATA(msginfo
->subject
, memusage
);
675 GET_CACHE_DATA(msginfo
->msgid
, memusage
);
676 GET_CACHE_DATA(msginfo
->inreplyto
, memusage
);
677 GET_CACHE_DATA(msginfo
->xref
, memusage
);
679 GET_CACHE_DATA_INT(msginfo
->planned_download
);
680 GET_CACHE_DATA_INT(msginfo
->total_size
);
681 GET_CACHE_DATA_INT(refnum
);
683 for (; refnum
!= 0; refnum
--) {
686 GET_CACHE_DATA(ref
, memusage
);
689 msginfo
->references
=
690 g_slist_prepend(msginfo
->references
, ref
);
692 if (msginfo
->references
)
693 msginfo
->references
=
694 g_slist_reverse(msginfo
->references
);
696 msginfo
->folder
= item
;
697 msginfo
->flags
.tmp_flags
|= tmp_flags
;
699 g_hash_table_insert(cache
->msgnum_table
, &msginfo
->msgnum
, msginfo
);
701 g_hash_table_insert(cache
->msgid_table
, msginfo
->msgid
, msginfo
);
705 UnmapViewOfFile((void*) cache_data
);
707 munmap(cache_data
, map_len
);
710 while (fread(&num
, sizeof(num
), 1, fp
) == 1) {
714 msginfo
= procmsg_msginfo_new();
715 msginfo
->msgnum
= num
;
716 memusage
+= sizeof(MsgInfo
);
718 READ_CACHE_DATA_INT(msginfo
->size
, fp
);
719 READ_CACHE_DATA_INT(msginfo
->mtime
, fp
);
720 READ_CACHE_DATA_INT(msginfo
->date_t
, fp
);
721 READ_CACHE_DATA_INT(msginfo
->flags
.tmp_flags
, fp
);
723 READ_CACHE_DATA(msginfo
->fromname
, fp
, memusage
);
725 READ_CACHE_DATA(msginfo
->date
, fp
, memusage
);
726 READ_CACHE_DATA(msginfo
->from
, fp
, memusage
);
727 READ_CACHE_DATA(msginfo
->to
, fp
, memusage
);
728 READ_CACHE_DATA(msginfo
->cc
, fp
, memusage
);
729 READ_CACHE_DATA(msginfo
->newsgroups
, fp
, memusage
);
730 READ_CACHE_DATA(msginfo
->subject
, fp
, memusage
);
731 READ_CACHE_DATA(msginfo
->msgid
, fp
, memusage
);
732 READ_CACHE_DATA(msginfo
->inreplyto
, fp
, memusage
);
733 READ_CACHE_DATA(msginfo
->xref
, fp
, memusage
);
735 READ_CACHE_DATA_INT(msginfo
->planned_download
, fp
);
736 READ_CACHE_DATA_INT(msginfo
->total_size
, fp
);
737 READ_CACHE_DATA_INT(refnum
, fp
);
739 for (; refnum
!= 0; refnum
--) {
742 READ_CACHE_DATA(ref
, fp
, memusage
);
745 msginfo
->references
=
746 g_slist_prepend(msginfo
->references
, ref
);
748 if (msginfo
->references
)
749 msginfo
->references
=
750 g_slist_reverse(msginfo
->references
);
752 msginfo
->folder
= item
;
753 msginfo
->flags
.tmp_flags
|= tmp_flags
;
755 g_hash_table_insert(cache
->msgnum_table
, &msginfo
->msgnum
, msginfo
);
757 g_hash_table_insert(cache
->msgid_table
, msginfo
->msgid
, msginfo
);
764 if (conv
->free
!= NULL
)
770 msgcache_destroy(cache
);
774 cache
->last_access
= time(NULL
);
775 cache
->memusage
= memusage
;
777 debug_print("done. (%d items read)\n", g_hash_table_size(cache
->msgnum_table
));
778 debug_print("Cache size: %d messages, %d byte\n", g_hash_table_size(cache
->msgnum_table
), cache
->memusage
);
782 void msgcache_read_mark(MsgCache
*cache
, const gchar
*mark_file
)
786 MsgPermFlags perm_flags
;
789 char *cache_data
= NULL
;
794 /* In case we can't open the mark file with MARK_VERSION, check if we can open it with the
795 * swapped MARK_VERSION. As msgcache_open_data_file swaps it too, if this succeeds,
796 * it means it's the old version (not little-endian) on a big-endian machine. The code has
797 * no effect on x86 as their file doesn't change. */
799 if ((fp
= msgcache_open_data_file(mark_file
, MARK_VERSION
, DATA_READ
, NULL
, 0)) == NULL
) {
800 /* see if it isn't swapped ? */
801 if ((fp
= msgcache_open_data_file(mark_file
, bswap_32(MARK_VERSION
), DATA_READ
, NULL
, 0)) == NULL
)
804 swapping
= FALSE
; /* yay */
806 debug_print("reading %sswapped mark file.\n", swapping
?"":"un");
808 if (msgcache_use_mmap_read
) {
809 if (fstat(fileno(fp
), &st
) >= 0)
810 map_len
= st
.st_size
;
816 HANDLE hFile
, hMapping
;
817 hFile
= (HANDLE
) _get_osfhandle (fileno(fp
));
818 if (hFile
== (HANDLE
) -1)
820 hMapping
= CreateFileMapping(hFile
, NULL
, PAGE_WRITECOPY
, 0, 0, NULL
);
823 cache_data
= (unsigned char *)MapViewOfFile(hMapping
, FILE_MAP_COPY
, 0, 0, 0);
824 CloseHandle (hMapping
);
828 cache_data
= mmap(NULL
, map_len
, PROT_READ
, MAP_PRIVATE
, fileno(fp
), 0);
834 if (cache_data
!= NULL
&& cache_data
!= MAP_FAILED
) {
835 int rem_len
= map_len
-ftell(fp
);
836 char *walk_data
= cache_data
+ftell(fp
);
839 GET_CACHE_DATA_INT(num
);
840 GET_CACHE_DATA_INT(perm_flags
);
841 msginfo
= g_hash_table_lookup(cache
->msgnum_table
, &num
);
843 msginfo
->flags
.perm_flags
= perm_flags
;
847 UnmapViewOfFile((void*) cache_data
);
849 munmap(cache_data
, map_len
);
852 while (fread(&num
, sizeof(num
), 1, fp
) == 1) {
855 if (fread(&perm_flags
, sizeof(perm_flags
), 1, fp
) != 1) break;
857 perm_flags
= bswap_32(perm_flags
);
858 msginfo
= g_hash_table_lookup(cache
->msgnum_table
, &num
);
860 msginfo
->flags
.perm_flags
= perm_flags
;
867 void msgcache_read_tags(MsgCache
*cache
, const gchar
*tags_file
)
873 char *cache_data
= NULL
;
878 /* In case we can't open the mark file with MARK_VERSION, check if we can open it with the
879 * swapped MARK_VERSION. As msgcache_open_data_file swaps it too, if this succeeds,
880 * it means it's the old version (not little-endian) on a big-endian machine. The code has
881 * no effect on x86 as their file doesn't change. */
883 if ((fp
= msgcache_open_data_file(tags_file
, TAGS_VERSION
, DATA_READ
, NULL
, 0)) == NULL
) {
884 /* see if it isn't swapped ? */
885 if ((fp
= msgcache_open_data_file(tags_file
, bswap_32(TAGS_VERSION
), DATA_READ
, NULL
, 0)) == NULL
)
888 swapping
= FALSE
; /* yay */
890 debug_print("reading %sswapped tags file.\n", swapping
?"":"un");
892 if (msgcache_use_mmap_read
) {
893 if (fstat(fileno(fp
), &st
) >= 0)
894 map_len
= st
.st_size
;
900 HANDLE hFile
, hMapping
;
901 hFile
= (HANDLE
) _get_osfhandle (fileno(fp
));
902 if (hFile
== (HANDLE
) -1)
904 hMapping
= CreateFileMapping(hFile
, NULL
, PAGE_WRITECOPY
, 0, 0, NULL
);
907 cache_data
= (unsigned char *)MapViewOfFile(hMapping
, FILE_MAP_COPY
, 0, 0, 0);
908 CloseHandle (hMapping
);
912 cache_data
= mmap(NULL
, map_len
, PROT_READ
, MAP_PRIVATE
, fileno(fp
), 0);
918 if (cache_data
!= NULL
&& cache_data
!= MAP_FAILED
) {
919 int rem_len
= map_len
-ftell(fp
);
920 char *walk_data
= cache_data
+ftell(fp
);
924 GET_CACHE_DATA_INT(num
);
925 msginfo
= g_hash_table_lookup(cache
->msgnum_table
, &num
);
927 g_slist_free(msginfo
->tags
);
928 msginfo
->tags
= NULL
;
930 GET_CACHE_DATA_INT(id
);
932 msginfo
->tags
= g_slist_prepend(
934 GINT_TO_POINTER(id
));
937 msginfo
->tags
= g_slist_reverse(msginfo
->tags
);
941 UnmapViewOfFile((void*) cache_data
);
943 munmap(cache_data
, map_len
);
946 while (fread(&num
, sizeof(num
), 1, fp
) == 1) {
950 msginfo
= g_hash_table_lookup(cache
->msgnum_table
, &num
);
952 g_slist_free(msginfo
->tags
);
953 msginfo
->tags
= NULL
;
955 if (fread(&id
, sizeof(id
), 1, fp
) != 1)
960 msginfo
->tags
= g_slist_prepend(
962 GINT_TO_POINTER(id
));
965 msginfo
->tags
= g_slist_reverse(msginfo
->tags
);
972 static int msgcache_write_cache(MsgInfo
*msginfo
, FILE *fp
)
974 MsgTmpFlags flags
= msginfo
->flags
.tmp_flags
& MSG_CACHED_FLAG_MASK
;
976 int w_err
= 0, wrote
= 0;
978 WRITE_CACHE_DATA_INT(msginfo
->msgnum
, fp
);
979 WRITE_CACHE_DATA_INT(msginfo
->size
, fp
);
980 WRITE_CACHE_DATA_INT(msginfo
->mtime
, fp
);
981 WRITE_CACHE_DATA_INT(msginfo
->date_t
, fp
);
982 WRITE_CACHE_DATA_INT(flags
, fp
);
984 WRITE_CACHE_DATA(msginfo
->fromname
, fp
);
986 WRITE_CACHE_DATA(msginfo
->date
, fp
);
987 WRITE_CACHE_DATA(msginfo
->from
, fp
);
988 WRITE_CACHE_DATA(msginfo
->to
, fp
);
989 WRITE_CACHE_DATA(msginfo
->cc
, fp
);
990 WRITE_CACHE_DATA(msginfo
->newsgroups
, fp
);
991 WRITE_CACHE_DATA(msginfo
->subject
, fp
);
992 WRITE_CACHE_DATA(msginfo
->msgid
, fp
);
993 WRITE_CACHE_DATA(msginfo
->inreplyto
, fp
);
994 WRITE_CACHE_DATA(msginfo
->xref
, fp
);
995 WRITE_CACHE_DATA_INT(msginfo
->planned_download
, fp
);
996 WRITE_CACHE_DATA_INT(msginfo
->total_size
, fp
);
998 WRITE_CACHE_DATA_INT(g_slist_length(msginfo
->references
), fp
);
1000 for (cur
= msginfo
->references
; cur
!= NULL
; cur
= cur
->next
) {
1001 WRITE_CACHE_DATA((gchar
*)cur
->data
, fp
);
1003 return w_err
? -1 : wrote
;
1006 static int msgcache_write_mmap_cache(MsgInfo
*msginfo
, char *walk_data
)
1008 MsgTmpFlags flags
= msginfo
->flags
.tmp_flags
& MSG_CACHED_FLAG_MASK
;
1012 PUT_CACHE_DATA_INT(msginfo
->msgnum
);
1013 PUT_CACHE_DATA_INT(msginfo
->size
);
1014 PUT_CACHE_DATA_INT(msginfo
->mtime
);
1015 PUT_CACHE_DATA_INT(msginfo
->date_t
);
1016 PUT_CACHE_DATA_INT(flags
);
1017 PUT_CACHE_DATA(msginfo
->fromname
);
1019 PUT_CACHE_DATA(msginfo
->date
);
1020 PUT_CACHE_DATA(msginfo
->from
);
1021 PUT_CACHE_DATA(msginfo
->to
);
1022 PUT_CACHE_DATA(msginfo
->cc
);
1023 PUT_CACHE_DATA(msginfo
->newsgroups
);
1024 PUT_CACHE_DATA(msginfo
->subject
);
1025 PUT_CACHE_DATA(msginfo
->msgid
);
1026 PUT_CACHE_DATA(msginfo
->inreplyto
);
1027 PUT_CACHE_DATA(msginfo
->xref
);
1028 PUT_CACHE_DATA_INT(msginfo
->planned_download
);
1029 PUT_CACHE_DATA_INT(msginfo
->total_size
);
1031 PUT_CACHE_DATA_INT(g_slist_length(msginfo
->references
));
1033 for (cur
= msginfo
->references
; cur
!= NULL
; cur
= cur
->next
) {
1034 PUT_CACHE_DATA((gchar
*)cur
->data
);
1039 static int msgcache_write_flags(MsgInfo
*msginfo
, FILE *fp
)
1041 MsgPermFlags flags
= msginfo
->flags
.perm_flags
;
1042 int w_err
= 0, wrote
= 0;
1043 WRITE_CACHE_DATA_INT(msginfo
->msgnum
, fp
);
1044 WRITE_CACHE_DATA_INT(flags
, fp
);
1045 return w_err
? -1 : wrote
;
1048 static int msgcache_write_tags(MsgInfo
*msginfo
, FILE *fp
)
1050 GSList
*cur
= msginfo
->tags
;
1051 int w_err
= 0, wrote
= 0;
1053 WRITE_CACHE_DATA_INT(msginfo
->msgnum
, fp
);
1054 for (; cur
; cur
= cur
->next
) {
1055 gint id
= GPOINTER_TO_INT(cur
->data
);
1056 if (tags_get_tag(id
) != NULL
) {
1057 WRITE_CACHE_DATA_INT(id
, fp
);
1060 WRITE_CACHE_DATA_INT(-1, fp
);
1062 return w_err
? -1 : wrote
;
1065 static int msgcache_write_mmap_flags(MsgInfo
*msginfo
, char *walk_data
)
1067 MsgPermFlags flags
= msginfo
->flags
.perm_flags
;
1070 PUT_CACHE_DATA_INT(msginfo
->msgnum
);
1071 PUT_CACHE_DATA_INT(flags
);
1075 static int msgcache_write_mmap_tags(MsgInfo
*msginfo
, char *walk_data
)
1077 GSList
*cur
= msginfo
->tags
;
1080 PUT_CACHE_DATA_INT(msginfo
->msgnum
);
1081 for (; cur
; cur
= cur
->next
) {
1082 gint id
= GPOINTER_TO_INT(cur
->data
);
1083 if (tags_get_tag(id
) != NULL
) {
1084 PUT_CACHE_DATA_INT(id
);
1087 PUT_CACHE_DATA_INT(-1);
1105 static void msgcache_write_func(gpointer key
, gpointer value
, gpointer user_data
)
1108 struct write_fps
*write_fps
;
1111 msginfo
= (MsgInfo
*)value
;
1112 write_fps
= user_data
;
1114 tmp
= msgcache_write_cache(msginfo
, write_fps
->cache_fp
);
1116 write_fps
->error
= 1;
1118 write_fps
->cache_size
+= tmp
;
1119 tmp
= msgcache_write_flags(msginfo
, write_fps
->mark_fp
);
1121 write_fps
->error
= 1;
1123 write_fps
->mark_size
+= tmp
;
1124 tmp
= msgcache_write_tags(msginfo
, write_fps
->tags_fp
);
1126 write_fps
->error
= 1;
1128 write_fps
->tags_size
+= tmp
;
1131 static void msgcache_write_mmap_func(gpointer key
, gpointer value
, gpointer user_data
)
1134 struct write_fps
*write_fps
;
1137 msginfo
= (MsgInfo
*)value
;
1138 write_fps
= user_data
;
1140 tmp
= msgcache_write_mmap_cache(msginfo
, write_fps
->cache_data
);
1141 write_fps
->cache_size
+= tmp
;
1142 write_fps
->cache_data
+= tmp
;
1143 tmp
= msgcache_write_mmap_flags(msginfo
, write_fps
->mark_data
);
1144 write_fps
->mark_size
+= tmp
;
1145 write_fps
->mark_data
+= tmp
;
1146 tmp
= msgcache_write_mmap_tags(msginfo
, write_fps
->tags_data
);
1147 write_fps
->tags_size
+= tmp
;
1148 write_fps
->tags_data
+= tmp
;
1151 gint
msgcache_write(const gchar
*cache_file
, const gchar
*mark_file
, const gchar
*tags_file
, MsgCache
*cache
)
1153 struct write_fps write_fps
;
1154 gchar
*new_cache
, *new_mark
, *new_tags
;
1155 int w_err
= 0, wrote
= 0;
1157 char *cache_data
= NULL
;
1158 char *mark_data
= NULL
;
1159 char *tags_data
= NULL
;
1162 g_return_val_if_fail(cache_file
!= NULL
, -1);
1163 g_return_val_if_fail(mark_file
!= NULL
, -1);
1164 g_return_val_if_fail(tags_file
!= NULL
, -1);
1165 g_return_val_if_fail(cache
!= NULL
, -1);
1167 new_cache
= g_strconcat(cache_file
, ".new", NULL
);
1168 new_mark
= g_strconcat(mark_file
, ".new", NULL
);
1169 new_tags
= g_strconcat(tags_file
, ".new", NULL
);
1171 write_fps
.error
= 0;
1172 write_fps
.cache_size
= 0;
1173 write_fps
.mark_size
= 0;
1174 write_fps
.tags_size
= 0;
1176 write_fps
.cache_fp
= msgcache_open_data_file(new_cache
, CACHE_VERSION
,
1177 DATA_WRITE
, NULL
, 0);
1178 if (write_fps
.cache_fp
== NULL
) {
1185 WRITE_CACHE_DATA(CS_UTF_8
, write_fps
.cache_fp
);
1188 g_warning("failed to write charset\n");
1189 fclose(write_fps
.cache_fp
);
1190 g_unlink(new_cache
);
1197 write_fps
.mark_fp
= msgcache_open_data_file(new_mark
, MARK_VERSION
,
1198 DATA_WRITE
, NULL
, 0);
1199 if (write_fps
.mark_fp
== NULL
) {
1200 fclose(write_fps
.cache_fp
);
1201 g_unlink(new_cache
);
1208 write_fps
.tags_fp
= msgcache_open_data_file(new_tags
, TAGS_VERSION
,
1209 DATA_WRITE
, NULL
, 0);
1210 if (write_fps
.tags_fp
== NULL
) {
1211 fclose(write_fps
.cache_fp
);
1212 fclose(write_fps
.mark_fp
);
1213 g_unlink(new_cache
);
1221 debug_print("\tWriting message cache to %s and %s...\n", new_cache
, new_mark
);
1223 if (change_file_mode_rw(write_fps
.cache_fp
, new_cache
) < 0)
1224 FILE_OP_ERROR(new_cache
, "chmod");
1226 write_fps
.cache_size
= ftell(write_fps
.cache_fp
);
1227 write_fps
.mark_size
= ftell(write_fps
.mark_fp
);
1228 write_fps
.tags_size
= ftell(write_fps
.tags_fp
);
1230 if (msgcache_use_mmap_write
&& cache
->memusage
> 0) {
1231 map_len
= cache
->memusage
;
1232 if (ftruncate(fileno(write_fps
.cache_fp
), (off_t
)map_len
) == 0) {
1236 HANDLE hFile
, hMapping
;
1237 hFile
= (HANDLE
) _get_osfhandle (fileno(write_fps
.cache_fp
));
1238 if (hFile
== (HANDLE
) -1)
1240 hMapping
= CreateFileMapping(hFile
, NULL
, PAGE_READWRITE
, 0, 0, NULL
);
1243 cache_data
= (unsigned char *)MapViewOfFile(hMapping
, FILE_MAP_COPY
, 0, 0, 0);
1244 CloseHandle (hMapping
);
1248 cache_data
= mmap(NULL
, map_len
, PROT_WRITE
, MAP_SHARED
,
1249 fileno(write_fps
.cache_fp
), 0);
1252 if (cache_data
!= NULL
&& cache_data
!= MAP_FAILED
) {
1253 if (ftruncate(fileno(write_fps
.mark_fp
), (off_t
)map_len
) == 0) {
1256 HANDLE hFile
, hMapping
;
1257 hFile
= (HANDLE
) _get_osfhandle (fileno(write_fps
.mark_fp
));
1258 if (hFile
== (HANDLE
) -1)
1260 hMapping
= CreateFileMapping(hFile
, NULL
, PAGE_READWRITE
, 0, 0, NULL
);
1263 mark_data
= (unsigned char *)MapViewOfFile(hMapping
, FILE_MAP_COPY
, 0, 0, 0);
1264 CloseHandle (hMapping
);
1268 mark_data
= mmap(NULL
, map_len
, PROT_WRITE
, MAP_SHARED
,
1269 fileno(write_fps
.mark_fp
), 0);
1272 if (mark_data
== NULL
|| mark_data
== MAP_FAILED
) {
1274 UnmapViewOfFile((void*) cache_data
);
1276 munmap(cache_data
, map_len
);
1280 if (ftruncate(fileno(write_fps
.tags_fp
), (off_t
)map_len
) == 0) {
1283 HANDLE hFile
, hMapping
;
1284 hFile
= (HANDLE
) _get_osfhandle (fileno(write_fps
.tags_fp
));
1285 if (hFile
== (HANDLE
) -1)
1287 hMapping
= CreateFileMapping(hFile
, NULL
, PAGE_READWRITE
, 0, 0, NULL
);
1290 tags_data
= (unsigned char *)MapViewOfFile(hMapping
, FILE_MAP_COPY
, 0, 0, 0);
1291 CloseHandle (hMapping
);
1295 tags_data
= mmap(NULL
, map_len
, PROT_WRITE
, MAP_SHARED
,
1296 fileno(write_fps
.tags_fp
), 0);
1299 if (tags_data
== NULL
|| tags_data
== MAP_FAILED
) {
1301 UnmapViewOfFile((void*) cache_data
);
1302 UnmapViewOfFile((void*) mark_data
);
1304 munmap(cache_data
, map_len
);
1305 munmap(mark_data
, map_len
);
1314 if (cache_data
!= NULL
&& cache_data
!= MAP_FAILED
) {
1315 write_fps
.cache_data
= cache_data
+ ftell(write_fps
.cache_fp
);
1316 write_fps
.mark_data
= mark_data
+ ftell(write_fps
.mark_fp
);
1317 write_fps
.tags_data
= mark_data
+ ftell(write_fps
.tags_fp
);
1318 g_hash_table_foreach(cache
->msgnum_table
, msgcache_write_mmap_func
, (gpointer
)&write_fps
);
1320 UnmapViewOfFile((void*) cache_data
);
1321 UnmapViewOfFile((void*) mark_data
);
1322 UnmapViewOfFile((void*) tags_data
);
1324 munmap(cache_data
, map_len
);
1325 munmap(mark_data
, map_len
);
1326 munmap(tags_data
, map_len
);
1328 ftruncate(fileno(write_fps
.cache_fp
), write_fps
.cache_size
);
1329 ftruncate(fileno(write_fps
.mark_fp
), write_fps
.mark_size
);
1330 ftruncate(fileno(write_fps
.tags_fp
), write_fps
.tags_size
);
1332 #ifdef HAVE_FWRITE_UNLOCKED
1333 flockfile(write_fps
.cache_fp
);
1334 flockfile(write_fps
.mark_fp
);
1335 flockfile(write_fps
.tags_fp
);
1337 g_hash_table_foreach(cache
->msgnum_table
, msgcache_write_func
, (gpointer
)&write_fps
);
1338 #ifdef HAVE_FWRITE_UNLOCKED
1339 funlockfile(write_fps
.mark_fp
);
1340 funlockfile(write_fps
.cache_fp
);
1341 funlockfile(write_fps
.tags_fp
);
1345 fflush(write_fps
.cache_fp
);
1346 fflush(write_fps
.mark_fp
);
1347 fflush(write_fps
.tags_fp
);
1350 fsync(fileno(write_fps
.cache_fp
));
1351 fsync(fileno(write_fps
.mark_fp
));
1352 fsync(fileno(write_fps
.tags_fp
));
1355 fclose(write_fps
.cache_fp
);
1356 fclose(write_fps
.mark_fp
);
1357 fclose(write_fps
.tags_fp
);
1360 if (write_fps
.error
!= 0) {
1361 g_unlink(new_cache
);
1369 move_file(new_cache
, cache_file
, TRUE
);
1370 move_file(new_mark
, mark_file
, TRUE
);
1371 move_file(new_tags
, tags_file
, TRUE
);
1372 cache
->last_access
= time(NULL
);
1378 debug_print("done.\n");