2008-12-21 [paul] 3.7.0cvs2
[claws.git] / src / msgcache.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2007 Hiroyuki Yamamoto & The Claws Mail Team
4  *
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.
9  *
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.
14  *
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/>.
17  * 
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #include "defs.h"
25
26 #define _GNU_SOURCE
27 #include <stdio.h>
28
29 #include <glib.h>
30 #include <glib/gi18n.h>
31 #ifdef _WIN32
32 # include <w32lib.h>
33 # define MAP_FAILED     ((char *) -1)
34 #else
35 # include <sys/mman.h>
36 #endif
37 #include <sys/types.h>
38 #include <sys/stat.h>
39
40 #include <time.h>
41
42 #include "msgcache.h"
43 #include "utils.h"
44 #include "procmsg.h"
45 #include "codeconv.h"
46 #include "timing.h"
47 #include "tags.h"
48
49 #ifdef HAVE_FWRITE_UNLOCKED
50 #define SC_FWRITE fwrite_unlocked
51 #else
52 #define SC_FWRITE fwrite
53 #endif
54
55 #if G_BYTE_ORDER == G_BIG_ENDIAN
56 #define bswap_32(x) \
57      ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >>  8) | \
58       (((x) & 0x0000ff00) <<  8) | (((x) & 0x000000ff) << 24))
59      
60 #define MMAP_TO_GUINT32(x)      \
61         (((x[3]&0xff)) |        \
62          ((x[2]&0xff) << 8) |   \
63          ((x[1]&0xff) << 16) |  \
64          ((x[0]&0xff) << 24))
65
66 #define MMAP_TO_GUINT32_SWAPPED(x)      \
67         (((x[0]&0xff)) |                \
68          ((x[1]&0xff) << 8) |           \
69          ((x[2]&0xff) << 16) |          \
70          ((x[3]&0xff) << 24))
71
72 static gboolean msgcache_use_mmap_read = TRUE;
73 static gboolean msgcache_use_mmap_write = FALSE;
74
75 #else
76 #define bswap_32(x) (x)
77
78 #define MMAP_TO_GUINT32(x)      \
79         (((x[0]&0xff)) |        \
80          ((x[1]&0xff) << 8) |   \
81          ((x[2]&0xff) << 16) |  \
82          ((x[3]&0xff) << 24))
83
84 #define MMAP_TO_GUINT32_SWAPPED(x)      \
85         (((x[0]&0xff)) |                \
86          ((x[1]&0xff) << 8) |           \
87          ((x[2]&0xff) << 16) |          \
88          ((x[3]&0xff) << 24))
89
90 static gboolean msgcache_use_mmap_read = TRUE;
91 static gboolean msgcache_use_mmap_write = FALSE;
92 #endif
93
94 static gboolean swapping = TRUE;
95
96 typedef enum
97 {
98         DATA_READ,
99         DATA_WRITE,
100         DATA_APPEND
101 } DataOpenMode;
102
103 struct _MsgCache {
104         GHashTable      *msgnum_table;
105         GHashTable      *msgid_table;
106         guint            memusage;
107         time_t           last_access;
108 };
109
110 typedef struct _StringConverter StringConverter;
111 struct _StringConverter {
112         gchar *(*convert) (StringConverter *converter, gchar *srcstr);
113         void   (*free)    (StringConverter *converter);
114 };
115
116 typedef struct _StrdupConverter StrdupConverter;
117 struct _StrdupConverter {
118         StringConverter converter;
119 };
120
121 typedef struct _CharsetConverter CharsetConverter;
122 struct _CharsetConverter {
123         StringConverter converter;
124
125         gchar *srccharset;
126         gchar *dstcharset;
127 };
128
129 MsgCache *msgcache_new(void)
130 {
131         MsgCache *cache;
132         
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);
137
138         return cache;
139 }
140
141 static gboolean msgcache_msginfo_free_func(gpointer num, gpointer msginfo, gpointer user_data)
142 {
143         procmsg_msginfo_free((MsgInfo *)msginfo);
144         return TRUE;
145 }                                                                                         
146
147 void msgcache_destroy(MsgCache *cache)
148 {
149         g_return_if_fail(cache != NULL);
150
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);
154         g_free(cache);
155 }
156
157 void msgcache_add_msg(MsgCache *cache, MsgInfo *msginfo) 
158 {
159         MsgInfo *newmsginfo;
160
161         g_return_if_fail(cache != NULL);
162         g_return_if_fail(msginfo != NULL);
163
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);
170
171         debug_print("Cache size: %d messages, %u bytes\n", g_hash_table_size(cache->msgnum_table), cache->memusage);
172 }
173
174 void msgcache_remove_msg(MsgCache *cache, guint msgnum)
175 {
176         MsgInfo *msginfo;
177
178         g_return_if_fail(cache != NULL);
179         g_return_if_fail(msgnum > 0);
180
181         msginfo = (MsgInfo *) g_hash_table_lookup(cache->msgnum_table, &msgnum);
182         if(!msginfo)
183                 return;
184
185         cache->memusage -= procmsg_msginfo_memusage(msginfo);
186         if(msginfo->msgid)
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);
191
192         debug_print("Cache size: %d messages, %u bytes\n", g_hash_table_size(cache->msgnum_table), cache->memusage);
193 }
194
195 void msgcache_update_msg(MsgCache *cache, MsgInfo *msginfo)
196 {
197         MsgInfo *oldmsginfo, *newmsginfo;
198         
199         g_return_if_fail(cache != NULL);
200         g_return_if_fail(msginfo != NULL);
201
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);
205         if (oldmsginfo) {
206                 g_hash_table_remove(cache->msgnum_table, &oldmsginfo->msgnum);
207                 cache->memusage -= procmsg_msginfo_memusage(oldmsginfo);
208                 procmsg_msginfo_free(oldmsginfo);
209         }
210
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);
217         
218         debug_print("Cache size: %d messages, %u bytes\n", g_hash_table_size(cache->msgnum_table), cache->memusage);
219
220         return;
221 }
222
223 MsgInfo *msgcache_get_msg(MsgCache *cache, guint num)
224 {
225         MsgInfo *msginfo;
226
227         g_return_val_if_fail(cache != NULL, NULL);
228
229         msginfo = g_hash_table_lookup(cache->msgnum_table, &num);
230         if(!msginfo)
231                 return NULL;
232         cache->last_access = time(NULL);
233         
234         return procmsg_msginfo_new_ref(msginfo);
235 }
236
237 MsgInfo *msgcache_get_msg_by_id(MsgCache *cache, const gchar *msgid)
238 {
239         MsgInfo *msginfo;
240         
241         g_return_val_if_fail(cache != NULL, NULL);
242         g_return_val_if_fail(msgid != NULL, NULL);
243
244         msginfo = g_hash_table_lookup(cache->msgid_table, msgid);
245         if(!msginfo)
246                 return NULL;
247         cache->last_access = time(NULL);
248         
249         return procmsg_msginfo_new_ref(msginfo);        
250 }
251
252 static void msgcache_get_msg_list_func(gpointer key, gpointer value, gpointer user_data)
253 {
254         MsgInfoList **listptr = user_data;
255         MsgInfo *msginfo = value;
256
257         *listptr = g_slist_prepend(*listptr, procmsg_msginfo_new_ref(msginfo));
258 }
259
260 MsgInfoList *msgcache_get_msg_list(MsgCache *cache)
261 {
262         MsgInfoList *msg_list = NULL;
263         START_TIMING("");
264         g_return_val_if_fail(cache != NULL, NULL);
265
266         g_hash_table_foreach((GHashTable *)cache->msgnum_table, msgcache_get_msg_list_func, (gpointer)&msg_list);       
267         cache->last_access = time(NULL);
268         
269         msg_list = g_slist_reverse(msg_list);
270         END_TIMING();
271         return msg_list;
272 }
273
274 time_t msgcache_get_last_access_time(MsgCache *cache)
275 {
276         g_return_val_if_fail(cache != NULL, 0);
277         
278         return cache->last_access;
279 }
280
281 gint msgcache_get_memory_usage(MsgCache *cache)
282 {
283         g_return_val_if_fail(cache != NULL, 0);
284
285         return cache->memusage;
286 }
287
288 /*
289  *  Cache saving functions
290  */
291
292 #define READ_CACHE_DATA(data, fp, total_len) \
293 { \
294         if ((tmp_len = msgcache_read_cache_data_str(fp, &data, conv)) < 0) { \
295                 procmsg_msginfo_free(msginfo); \
296                 error = TRUE; \
297                 goto bail_err; \
298         } \
299         total_len += tmp_len; \
300 }
301
302 #define READ_CACHE_DATA_INT(n, fp) \
303 { \
304         guint32 idata; \
305         size_t ni; \
306  \
307         if ((ni = fread(&idata, sizeof(idata), 1, fp)) != 1) { \
308                 g_warning("read_int: Cache data corrupted, read %zd of %zd at " \
309                           "offset %ld\n", ni, sizeof(idata), ftell(fp)); \
310                 procmsg_msginfo_free(msginfo); \
311                 error = TRUE; \
312                 goto bail_err; \
313         } else \
314                 n = swapping ? bswap_32(idata) : (idata);\
315 }
316
317 #define GET_CACHE_DATA_INT(n) \
318 { \
319         n = (swapping ? (MMAP_TO_GUINT32_SWAPPED(walk_data)):(MMAP_TO_GUINT32(walk_data))); \
320         walk_data += 4; rem_len -= 4;                   \
321 }
322
323 #define GET_CACHE_DATA(data, total_len) \
324 { \
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); \
329                 error = TRUE; \
330                 goto bail_err; \
331         } \
332         total_len += tmp_len; \
333         walk_data += tmp_len; rem_len -= tmp_len; \
334 }
335
336
337 #define WRITE_CACHE_DATA_INT(n, fp)                     \
338 {                                                       \
339         guint32 idata;                                  \
340                                                         \
341         idata = (guint32)bswap_32(n);                   \
342         if (SC_FWRITE(&idata, sizeof(idata), 1, fp) != 1)       \
343                 w_err = 1;                              \
344         wrote += 4;                                     \
345 }
346
347 #define PUT_CACHE_DATA_INT(n)                           \
348 {                                                       \
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;             \
353         walk_data += 4;                                 \
354         wrote += 4;                                     \
355 }
356
357 #define WRITE_CACHE_DATA(data, fp) \
358 { \
359         size_t len;                                     \
360         if (data == NULL)                               \
361                 len = 0;                                \
362         else                                            \
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) \
367                         w_err = 1;                      \
368                 wrote += len;                           \
369         } \
370 }
371
372 #define PUT_CACHE_DATA(data)                            \
373 {                                                       \
374         size_t len;                                     \
375         if (data == NULL)                               \
376                 len = 0;                                \
377         else                                            \
378                 len = strlen(data);                     \
379         PUT_CACHE_DATA_INT(len);                        \
380         if (len > 0) {                                  \
381                 memcpy(walk_data, data, len);           \
382                 walk_data += len;                       \
383                 wrote += len;                           \
384         }                                               \
385 }
386
387 static FILE *msgcache_open_data_file(const gchar *file, guint version,
388                                      DataOpenMode mode,
389                                      gchar *buf, size_t buf_size)
390 {
391         FILE *fp;
392         gint32 data_ver;
393
394         g_return_val_if_fail(file != NULL, NULL);
395
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");
400                         return NULL;
401                 }
402                 if (change_file_mode_rw(fp, file) < 0)
403                         FILE_OP_ERROR(file, "chmod");
404
405                 WRITE_CACHE_DATA_INT(version, fp);
406                 if (w_err != 0) {
407                         g_warning("failed to write int\n");
408                         fclose(fp);
409                         return NULL;
410                 }
411                 return fp;
412         }
413
414         /* check version */
415         if ((fp = g_fopen(file, "rb")) == NULL)
416                 debug_print("Mark/Cache file '%s' not found\n", file);
417         else {
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);
424                         fclose(fp);
425                         fp = NULL;
426                 }
427                 data_ver = bswap_32(data_ver);
428         }
429         
430         if (mode == DATA_READ)
431                 return fp;
432
433         if (fp) {
434                 /* reopen with append mode */
435                 fclose(fp);
436                 if ((fp = g_fopen(file, "ab")) == NULL)
437                         FILE_OP_ERROR(file, "fopen");
438         } else {
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,
442                                             buf_size);
443         }
444
445         return fp;
446 }
447
448 static gint msgcache_read_cache_data_str(FILE *fp, gchar **str, 
449                                          StringConverter *conv)
450 {
451         gchar *tmpstr = NULL;
452         size_t ni;
453         guint32 len;
454
455         *str = NULL;
456         if (!swapping) {
457                 if ((ni = fread(&len, sizeof(len), 1, fp) != 1) ||
458                     len > G_MAXINT) {
459                         g_warning("read_data_str: Cache data (len) corrupted, read %zd "
460                                   "of %zd bytes at offset %ld\n", ni, sizeof(len), 
461                                   ftell(fp));
462                         return -1;
463                 }
464         } else {
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 %zd "
468                                   "of %zd bytes at offset %ld\n", ni, sizeof(len), 
469                                   ftell(fp));
470                         return -1;
471                 }
472                 len = bswap_32(len);
473         }
474
475         if (len == 0)
476                 return 0;
477
478         tmpstr = g_try_malloc(len + 1);
479
480         if(tmpstr == NULL) {
481                 return -1;
482         }
483
484         if ((ni = fread(tmpstr, 1, len, fp)) != len) {
485                 g_warning("read_data_str: Cache data corrupted, read %zd of %u "
486                           "bytes at offset %ld\n", 
487                           ni, len, ftell(fp));
488                 g_free(tmpstr);
489                 return -1;
490         }
491         tmpstr[len] = 0;
492
493         if (conv != NULL) {
494                 *str = conv->convert(conv, tmpstr);
495                 g_free(tmpstr);
496         } else 
497                 *str = tmpstr;
498
499         return len;
500 }
501
502 static gint msgcache_get_cache_data_str(gchar *src, gchar **str, gint len,
503                                          StringConverter *conv)
504 {
505         gchar *tmpstr = NULL;
506
507         *str = NULL;
508
509         if (len == 0)
510                 return 0;
511
512         if(len > 2*1024*1024) {
513                 g_warning("read_data_str: refusing to allocate %d bytes.\n", len);
514                 return -1;
515         }
516
517         tmpstr = g_try_malloc(len + 1);
518
519         if(tmpstr == NULL) {
520                 return -1;
521         }
522
523         memcpy(tmpstr, src, len);
524         tmpstr[len] = 0;
525
526         if (conv != NULL) {
527                 *str = conv->convert(conv, tmpstr);
528                 g_free(tmpstr);
529         } else 
530                 *str = tmpstr;
531
532         return len;
533 }
534
535 static gchar *strconv_charset_convert(StringConverter *conv, gchar *srcstr)
536 {
537         CharsetConverter *charsetconv = (CharsetConverter *) conv;
538
539         return conv_codeset_strdup(srcstr, charsetconv->srccharset, charsetconv->dstcharset);
540 }
541
542 static void strconv_charset_free(StringConverter *conv)
543 {
544         CharsetConverter *charsetconv = (CharsetConverter *) conv;
545
546         g_free(charsetconv->srccharset);
547         g_free(charsetconv->dstcharset);
548 }
549
550 MsgCache *msgcache_read_cache(FolderItem *item, const gchar *cache_file)
551 {
552         MsgCache *cache;
553         FILE *fp;
554         MsgInfo *msginfo;
555         MsgTmpFlags tmp_flags = 0;
556         gchar file_buf[BUFFSIZE];
557         guint32 num;
558         guint refnum;
559         gboolean error = FALSE;
560         StringConverter *conv = NULL;
561         gchar *srccharset = NULL;
562         const gchar *dstcharset = NULL;
563         gchar *ref = NULL;
564         guint memusage = 0;
565         gint tmp_len = 0, map_len = -1;
566         char *cache_data = NULL;
567         struct stat st;
568
569         g_return_val_if_fail(cache_file != NULL, NULL);
570         g_return_val_if_fail(item != NULL, NULL);
571
572         swapping = TRUE;
573
574         /* In case we can't open the mark file with MARK_VERSION, check if we can open it with the
575          * swapped MARK_VERSION. As msgcache_open_data_file swaps it too, if this succeeds, 
576          * it means it's the old version (not little-endian) on a big-endian machine. The code has
577          * no effect on x86 as their file doesn't change. */
578
579         if ((fp = msgcache_open_data_file
580                 (cache_file, CACHE_VERSION, DATA_READ, file_buf, sizeof(file_buf))) == NULL) {
581                 if ((fp = msgcache_open_data_file
582                 (cache_file, bswap_32(CACHE_VERSION), DATA_READ, file_buf, sizeof(file_buf))) == NULL)
583                         return NULL;
584                 else
585                         swapping = FALSE;
586         }
587
588         debug_print("\tReading %sswapped message cache from %s...\n", swapping?"":"un", cache_file);
589
590         if (folder_has_parent_of_type(item, F_QUEUE)) {
591                 tmp_flags |= MSG_QUEUED;
592         } else if (folder_has_parent_of_type(item, F_DRAFT)) {
593                 tmp_flags |= MSG_DRAFT;
594         }
595
596         if (msgcache_read_cache_data_str(fp, &srccharset, NULL) < 0) {
597                 fclose(fp);
598                 return NULL;
599         }
600         dstcharset = CS_UTF_8;
601         if (srccharset == NULL || dstcharset == NULL) {
602                 conv = NULL;
603         } else if (strcmp(srccharset, dstcharset) == 0) {
604                 debug_print("using Noop Converter\n");
605
606                 conv = NULL;
607         } else {
608                 CharsetConverter *charsetconv;
609
610                 debug_print("using CharsetConverter\n");
611
612                 charsetconv = g_new0(CharsetConverter, 1);
613                 charsetconv->converter.convert = strconv_charset_convert;
614                 charsetconv->converter.free = strconv_charset_free;
615                 charsetconv->srccharset = g_strdup(srccharset);
616                 charsetconv->dstcharset = g_strdup(dstcharset);
617
618                 conv = (StringConverter *) charsetconv;
619         }
620         g_free(srccharset);
621
622         cache = msgcache_new();
623
624         if (msgcache_use_mmap_read == TRUE) {
625                 if (fstat(fileno(fp), &st) >= 0)
626                         map_len = st.st_size;
627                 else
628                         map_len = -1;
629                 if (map_len > 0) {
630 #ifdef G_OS_WIN32
631                         cache_data = NULL;
632                         HANDLE hFile, hMapping;
633                         hFile = (HANDLE) _get_osfhandle (fileno(fp));
634                         if (hFile == (HANDLE) -1)
635                                 goto w32_fail;
636                         hMapping = CreateFileMapping(hFile, NULL, PAGE_WRITECOPY, 0, 0, NULL);
637                         if (!hMapping)
638                                 goto w32_fail;
639                         cache_data = (unsigned char *)MapViewOfFile(hMapping, FILE_MAP_COPY, 0, 0, 0);
640                         CloseHandle (hMapping);
641                 w32_fail:
642                         ;
643 #else
644                         cache_data = mmap(NULL, map_len, PROT_READ, MAP_PRIVATE, fileno(fp), 0);
645 #endif
646                 }
647         } else {
648                 cache_data = NULL;
649         }
650         if (cache_data != NULL && cache_data != MAP_FAILED) {
651                 int rem_len = map_len-ftell(fp);
652                 char *walk_data = cache_data+ftell(fp);
653
654                 while(rem_len > 0) {
655                         GET_CACHE_DATA_INT(num);
656                         
657                         msginfo = procmsg_msginfo_new();
658                         msginfo->msgnum = num;
659                         memusage += sizeof(MsgInfo);
660
661                         GET_CACHE_DATA_INT(msginfo->size);
662                         GET_CACHE_DATA_INT(msginfo->mtime);
663                         GET_CACHE_DATA_INT(msginfo->date_t);
664                         GET_CACHE_DATA_INT(msginfo->flags.tmp_flags);
665
666                         GET_CACHE_DATA(msginfo->fromname, memusage);
667
668                         GET_CACHE_DATA(msginfo->date, memusage);
669                         GET_CACHE_DATA(msginfo->from, memusage);
670                         GET_CACHE_DATA(msginfo->to, memusage);
671                         GET_CACHE_DATA(msginfo->cc, memusage);
672                         GET_CACHE_DATA(msginfo->newsgroups, memusage);
673                         GET_CACHE_DATA(msginfo->subject, memusage);
674                         GET_CACHE_DATA(msginfo->msgid, memusage);
675                         GET_CACHE_DATA(msginfo->inreplyto, memusage);
676                         GET_CACHE_DATA(msginfo->xref, memusage);
677
678                         GET_CACHE_DATA_INT(msginfo->planned_download);
679                         GET_CACHE_DATA_INT(msginfo->total_size);
680                         GET_CACHE_DATA_INT(refnum);
681
682                         for (; refnum != 0; refnum--) {
683                                 ref = NULL;
684
685                                 GET_CACHE_DATA(ref, memusage);
686
687                                 if (ref && *ref)
688                                         msginfo->references =
689                                                 g_slist_prepend(msginfo->references, ref);
690                         }
691                         if (msginfo->references)
692                                 msginfo->references =
693                                         g_slist_reverse(msginfo->references);
694
695                         msginfo->folder = item;
696                         msginfo->flags.tmp_flags |= tmp_flags;
697
698                         g_hash_table_insert(cache->msgnum_table, &msginfo->msgnum, msginfo);
699                         if(msginfo->msgid)
700                                 g_hash_table_insert(cache->msgid_table, msginfo->msgid, msginfo);
701                 }
702
703 #ifdef G_OS_WIN32
704                 UnmapViewOfFile((void*) cache_data);
705 #else
706                 munmap(cache_data, map_len);
707 #endif
708         } else {
709                 while (fread(&num, sizeof(num), 1, fp) == 1) {
710                         if (swapping)
711                                 num = bswap_32(num);
712
713                         msginfo = procmsg_msginfo_new();
714                         msginfo->msgnum = num;
715                         memusage += sizeof(MsgInfo);
716
717                         READ_CACHE_DATA_INT(msginfo->size, fp);
718                         READ_CACHE_DATA_INT(msginfo->mtime, fp);
719                         READ_CACHE_DATA_INT(msginfo->date_t, fp);
720                         READ_CACHE_DATA_INT(msginfo->flags.tmp_flags, fp);
721
722                         READ_CACHE_DATA(msginfo->fromname, fp, memusage);
723
724                         READ_CACHE_DATA(msginfo->date, fp, memusage);
725                         READ_CACHE_DATA(msginfo->from, fp, memusage);
726                         READ_CACHE_DATA(msginfo->to, fp, memusage);
727                         READ_CACHE_DATA(msginfo->cc, fp, memusage);
728                         READ_CACHE_DATA(msginfo->newsgroups, fp, memusage);
729                         READ_CACHE_DATA(msginfo->subject, fp, memusage);
730                         READ_CACHE_DATA(msginfo->msgid, fp, memusage);
731                         READ_CACHE_DATA(msginfo->inreplyto, fp, memusage);
732                         READ_CACHE_DATA(msginfo->xref, fp, memusage);
733
734                         READ_CACHE_DATA_INT(msginfo->planned_download, fp);
735                         READ_CACHE_DATA_INT(msginfo->total_size, fp);
736                         READ_CACHE_DATA_INT(refnum, fp);
737
738                         for (; refnum != 0; refnum--) {
739                                 ref = NULL;
740
741                                 READ_CACHE_DATA(ref, fp, memusage);
742
743                                 if (ref && *ref)
744                                         msginfo->references =
745                                                 g_slist_prepend(msginfo->references, ref);
746                         }
747                         if (msginfo->references)
748                                 msginfo->references =
749                                         g_slist_reverse(msginfo->references);
750
751                         msginfo->folder = item;
752                         msginfo->flags.tmp_flags |= tmp_flags;
753
754                         g_hash_table_insert(cache->msgnum_table, &msginfo->msgnum, msginfo);
755                         if(msginfo->msgid)
756                                 g_hash_table_insert(cache->msgid_table, msginfo->msgid, msginfo);
757                 }
758         }
759 bail_err:
760         fclose(fp);
761
762         if (conv != NULL) {
763                 if (conv->free != NULL)
764                         conv->free(conv);
765                 g_free(conv);
766         }
767
768         if(error) {
769                 msgcache_destroy(cache);
770                 return NULL;
771         }
772
773         cache->last_access = time(NULL);
774         cache->memusage = memusage;
775
776         debug_print("done. (%d items read)\n", g_hash_table_size(cache->msgnum_table));
777         debug_print("Cache size: %d messages, %u bytes\n", g_hash_table_size(cache->msgnum_table), cache->memusage);
778         return cache;
779 }
780
781 void msgcache_read_mark(MsgCache *cache, const gchar *mark_file)
782 {
783         FILE *fp;
784         MsgInfo *msginfo;
785         MsgPermFlags perm_flags;
786         guint32 num;
787         gint map_len = -1;
788         char *cache_data = NULL;
789         struct stat st;
790         
791         swapping = TRUE;
792
793         /* In case we can't open the mark file with MARK_VERSION, check if we can open it with the
794          * swapped MARK_VERSION. As msgcache_open_data_file swaps it too, if this succeeds, 
795          * it means it's the old version (not little-endian) on a big-endian machine. The code has
796          * no effect on x86 as their file doesn't change. */
797
798         if ((fp = msgcache_open_data_file(mark_file, MARK_VERSION, DATA_READ, NULL, 0)) == NULL) {
799                 /* see if it isn't swapped ? */
800                 if ((fp = msgcache_open_data_file(mark_file, bswap_32(MARK_VERSION), DATA_READ, NULL, 0)) == NULL)
801                         return;
802                 else
803                         swapping = FALSE; /* yay */
804         }
805         debug_print("reading %sswapped mark file.\n", swapping?"":"un");
806         
807         if (msgcache_use_mmap_read) {
808                 if (fstat(fileno(fp), &st) >= 0)
809                         map_len = st.st_size;
810                 else
811                         map_len = -1;
812                 if (map_len > 0) {
813 #ifdef G_OS_WIN32
814                         cache_data = NULL;
815                         HANDLE hFile, hMapping;
816                         hFile = (HANDLE) _get_osfhandle (fileno(fp));
817                         if (hFile == (HANDLE) -1)
818                                 goto w32_fail2;
819                         hMapping = CreateFileMapping(hFile, NULL, PAGE_WRITECOPY, 0, 0, NULL);
820                         if (!hMapping)
821                                 goto w32_fail2;
822                         cache_data = (unsigned char *)MapViewOfFile(hMapping, FILE_MAP_COPY, 0, 0, 0);
823                         CloseHandle (hMapping);
824                 w32_fail2:
825                         ;
826 #else
827                         cache_data = mmap(NULL, map_len, PROT_READ, MAP_PRIVATE, fileno(fp), 0);
828 #endif
829                 }
830         } else {
831                 cache_data = NULL;
832         }
833         if (cache_data != NULL && cache_data != MAP_FAILED) {
834                 int rem_len = map_len-ftell(fp);
835                 char *walk_data = cache_data+ftell(fp);
836
837                 while(rem_len > 0) {
838                         GET_CACHE_DATA_INT(num);
839                         GET_CACHE_DATA_INT(perm_flags);
840                         msginfo = g_hash_table_lookup(cache->msgnum_table, &num);
841                         if(msginfo) {
842                                 msginfo->flags.perm_flags = perm_flags;
843                         }
844                 }
845 #ifdef G_OS_WIN32
846                 UnmapViewOfFile((void*) cache_data);
847 #else
848                 munmap(cache_data, map_len);
849 #endif
850         } else {
851                 while (fread(&num, sizeof(num), 1, fp) == 1) {
852                         if (swapping)
853                                 num = bswap_32(num);
854                         if (fread(&perm_flags, sizeof(perm_flags), 1, fp) != 1) break;
855                         if (swapping)
856                                 perm_flags = bswap_32(perm_flags);
857                         msginfo = g_hash_table_lookup(cache->msgnum_table, &num);
858                         if(msginfo) {
859                                 msginfo->flags.perm_flags = perm_flags;
860                         }
861                 }       
862         }
863         fclose(fp);
864 }
865
866 void msgcache_read_tags(MsgCache *cache, const gchar *tags_file)
867 {
868         FILE *fp;
869         MsgInfo *msginfo;
870         guint32 num;
871         gint map_len = -1;
872         char *cache_data = NULL;
873         struct stat st;
874         
875         swapping = TRUE;
876
877         /* In case we can't open the mark file with MARK_VERSION, check if we can open it with the
878          * swapped MARK_VERSION. As msgcache_open_data_file swaps it too, if this succeeds, 
879          * it means it's the old version (not little-endian) on a big-endian machine. The code has
880          * no effect on x86 as their file doesn't change. */
881
882         if ((fp = msgcache_open_data_file(tags_file, TAGS_VERSION, DATA_READ, NULL, 0)) == NULL) {
883                 /* see if it isn't swapped ? */
884                 if ((fp = msgcache_open_data_file(tags_file, bswap_32(TAGS_VERSION), DATA_READ, NULL, 0)) == NULL)
885                         return;
886                 else
887                         swapping = FALSE; /* yay */
888         }
889         debug_print("reading %sswapped tags file.\n", swapping?"":"un");
890         
891         if (msgcache_use_mmap_read) {
892                 if (fstat(fileno(fp), &st) >= 0)
893                         map_len = st.st_size;
894                 else
895                         map_len = -1;
896                 if (map_len > 0) {
897 #ifdef G_OS_WIN32
898                         cache_data = NULL;
899                         HANDLE hFile, hMapping;
900                         hFile = (HANDLE) _get_osfhandle (fileno(fp));
901                         if (hFile == (HANDLE) -1)
902                                 goto w32_fail6;
903                         hMapping = CreateFileMapping(hFile, NULL, PAGE_WRITECOPY, 0, 0, NULL);
904                         if (!hMapping)
905                                 goto w32_fail6;
906                         cache_data = (unsigned char *)MapViewOfFile(hMapping, FILE_MAP_COPY, 0, 0, 0);
907                         CloseHandle (hMapping);
908                 w32_fail6:
909                         ;
910 #else
911                         cache_data = mmap(NULL, map_len, PROT_READ, MAP_PRIVATE, fileno(fp), 0);
912 #endif
913                 }
914         } else {
915                 cache_data = NULL;
916         }
917         if (cache_data != NULL && cache_data != MAP_FAILED) {
918                 int rem_len = map_len-ftell(fp);
919                 char *walk_data = cache_data+ftell(fp);
920
921                 while(rem_len > 0) {
922                         gint id = -1;
923                         GET_CACHE_DATA_INT(num);
924                         msginfo = g_hash_table_lookup(cache->msgnum_table, &num);
925                         if(msginfo) {
926                                 g_slist_free(msginfo->tags);
927                                 msginfo->tags = NULL;
928                                 do {
929                                         GET_CACHE_DATA_INT(id);
930                                         if (id > 0) {
931                                                 msginfo->tags = g_slist_prepend(
932                                                         msginfo->tags, 
933                                                         GINT_TO_POINTER(id));
934                                         }
935                                 } while (id > 0);
936                                 msginfo->tags = g_slist_reverse(msginfo->tags);
937                         }
938                 }
939 #ifdef G_OS_WIN32
940                 UnmapViewOfFile((void*) cache_data);
941 #else
942                 munmap(cache_data, map_len);
943 #endif
944         } else {
945                 while (fread(&num, sizeof(num), 1, fp) == 1) {
946                         gint id = -1;
947                         if (swapping)
948                                 num = bswap_32(num);
949                         msginfo = g_hash_table_lookup(cache->msgnum_table, &num);
950                         if(msginfo) {
951                                 g_slist_free(msginfo->tags);
952                                 msginfo->tags = NULL;
953                                 do {
954                                         if (fread(&id, sizeof(id), 1, fp) != 1) 
955                                                 id = -1;
956                                         if (swapping)
957                                                 id = bswap_32(id);
958                                         if (id > 0) {
959                                                 msginfo->tags = g_slist_prepend(
960                                                         msginfo->tags, 
961                                                         GINT_TO_POINTER(id));
962                                         }
963                                 } while (id > 0);
964                                 msginfo->tags = g_slist_reverse(msginfo->tags);
965                         }
966                 }
967         }
968         fclose(fp);
969 }
970
971 static int msgcache_write_cache(MsgInfo *msginfo, FILE *fp)
972 {
973         MsgTmpFlags flags = msginfo->flags.tmp_flags & MSG_CACHED_FLAG_MASK;
974         GSList *cur;
975         int w_err = 0, wrote = 0;
976
977         WRITE_CACHE_DATA_INT(msginfo->msgnum, fp);
978         WRITE_CACHE_DATA_INT(msginfo->size, fp);
979         WRITE_CACHE_DATA_INT(msginfo->mtime, fp);
980         WRITE_CACHE_DATA_INT(msginfo->date_t, fp);
981         WRITE_CACHE_DATA_INT(flags, fp);
982
983         WRITE_CACHE_DATA(msginfo->fromname, fp);
984
985         WRITE_CACHE_DATA(msginfo->date, fp);
986         WRITE_CACHE_DATA(msginfo->from, fp);
987         WRITE_CACHE_DATA(msginfo->to, fp);
988         WRITE_CACHE_DATA(msginfo->cc, fp);
989         WRITE_CACHE_DATA(msginfo->newsgroups, fp);
990         WRITE_CACHE_DATA(msginfo->subject, fp);
991         WRITE_CACHE_DATA(msginfo->msgid, fp);
992         WRITE_CACHE_DATA(msginfo->inreplyto, fp);
993         WRITE_CACHE_DATA(msginfo->xref, fp);
994         WRITE_CACHE_DATA_INT(msginfo->planned_download, fp);
995         WRITE_CACHE_DATA_INT(msginfo->total_size, fp);
996         
997         WRITE_CACHE_DATA_INT(g_slist_length(msginfo->references), fp);
998
999         for (cur = msginfo->references; cur != NULL; cur = cur->next) {
1000                 WRITE_CACHE_DATA((gchar *)cur->data, fp);
1001         }
1002         return w_err ? -1 : wrote;
1003 }
1004
1005 static int msgcache_write_mmap_cache(MsgInfo *msginfo, char *walk_data)
1006 {
1007         MsgTmpFlags flags = msginfo->flags.tmp_flags & MSG_CACHED_FLAG_MASK;
1008         GSList *cur;
1009         int wrote = 0;
1010
1011         PUT_CACHE_DATA_INT(msginfo->msgnum);
1012         PUT_CACHE_DATA_INT(msginfo->size);
1013         PUT_CACHE_DATA_INT(msginfo->mtime);
1014         PUT_CACHE_DATA_INT(msginfo->date_t);
1015         PUT_CACHE_DATA_INT(flags);
1016         PUT_CACHE_DATA(msginfo->fromname);
1017
1018         PUT_CACHE_DATA(msginfo->date);
1019         PUT_CACHE_DATA(msginfo->from);
1020         PUT_CACHE_DATA(msginfo->to);
1021         PUT_CACHE_DATA(msginfo->cc);
1022         PUT_CACHE_DATA(msginfo->newsgroups);
1023         PUT_CACHE_DATA(msginfo->subject);
1024         PUT_CACHE_DATA(msginfo->msgid);
1025         PUT_CACHE_DATA(msginfo->inreplyto);
1026         PUT_CACHE_DATA(msginfo->xref);
1027         PUT_CACHE_DATA_INT(msginfo->planned_download);
1028         PUT_CACHE_DATA_INT(msginfo->total_size);
1029         
1030         PUT_CACHE_DATA_INT(g_slist_length(msginfo->references));
1031
1032         for (cur = msginfo->references; cur != NULL; cur = cur->next) {
1033                 PUT_CACHE_DATA((gchar *)cur->data);
1034         }
1035         return wrote;
1036 }
1037
1038 static int msgcache_write_flags(MsgInfo *msginfo, FILE *fp)
1039 {
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;
1045 }
1046
1047 static int msgcache_write_tags(MsgInfo *msginfo, FILE *fp)
1048 {
1049         GSList *cur = msginfo->tags;
1050         int w_err = 0, wrote = 0;
1051
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);
1057                 }
1058         }
1059         WRITE_CACHE_DATA_INT(-1, fp);
1060
1061         return w_err ? -1 : wrote;
1062 }
1063
1064 static int msgcache_write_mmap_flags(MsgInfo *msginfo, char *walk_data)
1065 {
1066         MsgPermFlags flags = msginfo->flags.perm_flags;
1067         int wrote = 0;
1068
1069         PUT_CACHE_DATA_INT(msginfo->msgnum);
1070         PUT_CACHE_DATA_INT(flags);
1071         return wrote;
1072 }
1073
1074 static int msgcache_write_mmap_tags(MsgInfo *msginfo, char *walk_data)
1075 {
1076         GSList *cur = msginfo->tags;
1077         int wrote = 0;
1078         
1079         PUT_CACHE_DATA_INT(msginfo->msgnum);
1080         for (; cur; cur = cur->next) {
1081                 gint id = GPOINTER_TO_INT(cur->data);
1082                 if (tags_get_tag(id) != NULL) {
1083                         PUT_CACHE_DATA_INT(id);
1084                 }
1085         }
1086         PUT_CACHE_DATA_INT(-1);
1087         return wrote;
1088 }
1089
1090 struct write_fps
1091 {
1092         FILE *cache_fp;
1093         FILE *mark_fp;
1094         FILE *tags_fp;
1095         char *cache_data;
1096         char *mark_data;
1097         char *tags_data;
1098         int error;
1099         guint cache_size;
1100         guint mark_size;
1101         guint tags_size;
1102 };
1103
1104 static void msgcache_write_func(gpointer key, gpointer value, gpointer user_data)
1105 {
1106         MsgInfo *msginfo;
1107         struct write_fps *write_fps;
1108         int tmp;
1109
1110         msginfo = (MsgInfo *)value;
1111         write_fps = user_data;
1112
1113         tmp = msgcache_write_cache(msginfo, write_fps->cache_fp);
1114         if (tmp < 0)
1115                 write_fps->error = 1;
1116         else
1117                 write_fps->cache_size += tmp;
1118         tmp= msgcache_write_flags(msginfo, write_fps->mark_fp);
1119         if (tmp < 0)
1120                 write_fps->error = 1;
1121         else
1122                 write_fps->mark_size += tmp;
1123         tmp = msgcache_write_tags(msginfo, write_fps->tags_fp);
1124         if (tmp < 0)
1125                 write_fps->error = 1;
1126         else
1127                 write_fps->tags_size += tmp;
1128 }
1129
1130 static void msgcache_write_mmap_func(gpointer key, gpointer value, gpointer user_data)
1131 {
1132         MsgInfo *msginfo;
1133         struct write_fps *write_fps;
1134         int tmp;
1135
1136         msginfo = (MsgInfo *)value;
1137         write_fps = user_data;
1138
1139         tmp = msgcache_write_mmap_cache(msginfo, write_fps->cache_data);
1140         write_fps->cache_size += tmp;
1141         write_fps->cache_data += tmp;
1142         tmp = msgcache_write_mmap_flags(msginfo, write_fps->mark_data);
1143         write_fps->mark_size += tmp;
1144         write_fps->mark_data += tmp;
1145         tmp = msgcache_write_mmap_tags(msginfo, write_fps->tags_data);
1146         write_fps->tags_size += tmp;
1147         write_fps->tags_data += tmp;
1148 }
1149
1150 gint msgcache_write(const gchar *cache_file, const gchar *mark_file, const gchar *tags_file, MsgCache *cache)
1151 {
1152         struct write_fps write_fps;
1153         gchar *new_cache, *new_mark, *new_tags;
1154         int w_err = 0, wrote = 0;
1155         gint map_len = -1;
1156         char *cache_data = NULL;
1157         char *mark_data = NULL;
1158         char *tags_data = NULL;
1159
1160         START_TIMING("");
1161         g_return_val_if_fail(cache_file != NULL, -1);
1162         g_return_val_if_fail(mark_file != NULL, -1);
1163         g_return_val_if_fail(tags_file != NULL, -1);
1164         g_return_val_if_fail(cache != NULL, -1);
1165
1166         new_cache = g_strconcat(cache_file, ".new", NULL);
1167         new_mark  = g_strconcat(mark_file, ".new", NULL);
1168         new_tags  = g_strconcat(tags_file, ".new", NULL);
1169
1170         write_fps.error = 0;
1171         write_fps.cache_size = 0;
1172         write_fps.mark_size = 0;
1173         write_fps.tags_size = 0;
1174
1175         write_fps.cache_fp = msgcache_open_data_file(new_cache, CACHE_VERSION,
1176                 DATA_WRITE, NULL, 0);
1177         if (write_fps.cache_fp == NULL) {
1178                 g_free(new_cache);
1179                 g_free(new_mark);
1180                 g_free(new_tags);
1181                 return -1;
1182         }
1183
1184         WRITE_CACHE_DATA(CS_UTF_8, write_fps.cache_fp);
1185
1186         if (w_err != 0) {
1187                 g_warning("failed to write charset\n");
1188                 fclose(write_fps.cache_fp);
1189                 claws_unlink(new_cache);
1190                 g_free(new_cache);
1191                 g_free(new_mark);
1192                 g_free(new_tags);
1193                 return -1;
1194         }
1195
1196         write_fps.mark_fp = msgcache_open_data_file(new_mark, MARK_VERSION,
1197                 DATA_WRITE, NULL, 0);
1198         if (write_fps.mark_fp == NULL) {
1199                 fclose(write_fps.cache_fp);
1200                 claws_unlink(new_cache);
1201                 g_free(new_cache);
1202                 g_free(new_mark);
1203                 g_free(new_tags);
1204                 return -1;
1205         }
1206
1207         write_fps.tags_fp = msgcache_open_data_file(new_tags, TAGS_VERSION,
1208                 DATA_WRITE, NULL, 0);
1209         if (write_fps.tags_fp == NULL) {
1210                 fclose(write_fps.cache_fp);
1211                 fclose(write_fps.mark_fp);
1212                 claws_unlink(new_cache);
1213                 claws_unlink(new_mark);
1214                 g_free(new_cache);
1215                 g_free(new_mark);
1216                 g_free(new_tags);
1217                 return -1;
1218         }
1219
1220         debug_print("\tWriting message cache to %s and %s...\n", new_cache, new_mark);
1221
1222         if (change_file_mode_rw(write_fps.cache_fp, new_cache) < 0)
1223                 FILE_OP_ERROR(new_cache, "chmod");
1224
1225         write_fps.cache_size = ftell(write_fps.cache_fp);
1226         write_fps.mark_size = ftell(write_fps.mark_fp);
1227         write_fps.tags_size = ftell(write_fps.tags_fp);
1228
1229         if (msgcache_use_mmap_write && cache->memusage > 0) {
1230                 map_len = cache->memusage;
1231                 if (ftruncate(fileno(write_fps.cache_fp), (off_t)map_len) == 0) {
1232
1233 #ifdef G_OS_WIN32
1234                         cache_data = NULL;
1235                         HANDLE hFile, hMapping;
1236                         hFile = (HANDLE) _get_osfhandle (fileno(write_fps.cache_fp));
1237                         if (hFile == (HANDLE) -1)
1238                                 goto w32_fail3;
1239                         hMapping = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, NULL);
1240                         if (!hMapping)
1241                                 goto w32_fail3;
1242                         cache_data = (unsigned char *)MapViewOfFile(hMapping, FILE_MAP_COPY, 0, 0, 0);
1243                         CloseHandle (hMapping);
1244                 w32_fail3:
1245                         ;
1246 #else
1247                         cache_data = mmap(NULL, map_len, PROT_WRITE, MAP_SHARED, 
1248                                 fileno(write_fps.cache_fp), 0);
1249 #endif
1250                 }
1251                 if (cache_data != NULL && cache_data != MAP_FAILED) {
1252                         if (ftruncate(fileno(write_fps.mark_fp), (off_t)map_len) == 0) {
1253 #ifdef G_OS_WIN32
1254                                 mark_data = NULL;
1255                                 HANDLE hFile, hMapping;
1256                                 hFile = (HANDLE) _get_osfhandle (fileno(write_fps.mark_fp));
1257                                 if (hFile == (HANDLE) -1)
1258                                         goto w32_fail4;
1259                                 hMapping = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, NULL);
1260                                 if (!hMapping)
1261                                         goto w32_fail4;
1262                                 mark_data = (unsigned char *)MapViewOfFile(hMapping, FILE_MAP_COPY, 0, 0, 0);
1263                                 CloseHandle (hMapping);
1264                         w32_fail4:
1265                                 ;
1266 #else
1267                                 mark_data = mmap(NULL, map_len, PROT_WRITE, MAP_SHARED, 
1268                                         fileno(write_fps.mark_fp), 0);
1269 #endif
1270                         } 
1271                         if (mark_data == NULL || mark_data == MAP_FAILED) {
1272 #ifdef G_OS_WIN32
1273                                 UnmapViewOfFile((void*) cache_data);
1274 #else
1275                                 munmap(cache_data, map_len);
1276 #endif
1277                                 cache_data = NULL;
1278                         } else {
1279                                 if (ftruncate(fileno(write_fps.tags_fp), (off_t)map_len) == 0) {
1280 #ifdef G_OS_WIN32
1281                                                 tags_data = NULL;
1282                                                 HANDLE hFile, hMapping;
1283                                                 hFile = (HANDLE) _get_osfhandle (fileno(write_fps.tags_fp));
1284                                                 if (hFile == (HANDLE) -1)
1285                                                         goto w32_fail5;
1286                                                 hMapping = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, NULL);
1287                                                 if (!hMapping)
1288                                                         goto w32_fail5;
1289                                                 tags_data = (unsigned char *)MapViewOfFile(hMapping, FILE_MAP_COPY, 0, 0, 0);
1290                                                 CloseHandle (hMapping);
1291                                         w32_fail5:
1292                                                 ;
1293 #else
1294                                         tags_data = mmap(NULL, map_len, PROT_WRITE, MAP_SHARED, 
1295                                                 fileno(write_fps.tags_fp), 0);
1296 #endif
1297                                 } 
1298                                 if (tags_data == NULL || tags_data == MAP_FAILED) {
1299 #ifdef G_OS_WIN32
1300                                         UnmapViewOfFile((void*) cache_data);
1301                                         UnmapViewOfFile((void*) mark_data);
1302 #else
1303                                         munmap(cache_data, map_len);
1304                                         munmap(mark_data, map_len);
1305 #endif
1306                                         cache_data = NULL;
1307                                         mark_data = NULL;
1308                                 } 
1309                         }
1310                 }
1311         }
1312
1313         if (cache_data != NULL && cache_data != MAP_FAILED) {
1314                 write_fps.cache_data = cache_data + ftell(write_fps.cache_fp);
1315                 write_fps.mark_data = mark_data + ftell(write_fps.mark_fp);
1316                 write_fps.tags_data = mark_data + ftell(write_fps.tags_fp);
1317                 g_hash_table_foreach(cache->msgnum_table, msgcache_write_mmap_func, (gpointer)&write_fps);
1318 #ifdef G_OS_WIN32
1319                 UnmapViewOfFile((void*) cache_data);
1320                 UnmapViewOfFile((void*) mark_data);
1321                 UnmapViewOfFile((void*) tags_data);
1322 #else
1323                 munmap(cache_data, map_len);
1324                 munmap(mark_data, map_len);
1325                 munmap(tags_data, map_len);
1326 #endif
1327                 ftruncate(fileno(write_fps.cache_fp), write_fps.cache_size);
1328                 ftruncate(fileno(write_fps.mark_fp), write_fps.mark_size);
1329                 ftruncate(fileno(write_fps.tags_fp), write_fps.tags_size);
1330         } else {
1331 #ifdef HAVE_FWRITE_UNLOCKED
1332                 flockfile(write_fps.cache_fp);
1333                 flockfile(write_fps.mark_fp);
1334                 flockfile(write_fps.tags_fp);
1335 #endif
1336                 g_hash_table_foreach(cache->msgnum_table, msgcache_write_func, (gpointer)&write_fps);
1337 #ifdef HAVE_FWRITE_UNLOCKED
1338                 funlockfile(write_fps.mark_fp);
1339                 funlockfile(write_fps.cache_fp);
1340                 funlockfile(write_fps.tags_fp);
1341 #endif
1342         }
1343         
1344         fflush(write_fps.cache_fp);
1345         fflush(write_fps.mark_fp);
1346         fflush(write_fps.tags_fp);
1347
1348 #if 0
1349         fsync(fileno(write_fps.cache_fp));
1350         fsync(fileno(write_fps.mark_fp));
1351         fsync(fileno(write_fps.tags_fp));
1352 #endif
1353
1354         fclose(write_fps.cache_fp);
1355         fclose(write_fps.mark_fp);
1356         fclose(write_fps.tags_fp);
1357
1358
1359         if (write_fps.error != 0) {
1360                 claws_unlink(new_cache);
1361                 claws_unlink(new_mark);
1362                 claws_unlink(new_tags);
1363                 g_free(new_cache);
1364                 g_free(new_mark);
1365                 g_free(new_tags);
1366                 return -1;
1367         } else {
1368                 move_file(new_cache, cache_file, TRUE);
1369                 move_file(new_mark, mark_file, TRUE);
1370                 move_file(new_tags, tags_file, TRUE);
1371                 cache->last_access = time(NULL);
1372         }
1373
1374         g_free(new_cache);
1375         g_free(new_mark);
1376         g_free(new_tags);
1377         debug_print("done.\n");
1378         END_TIMING();
1379         return 0;
1380 }
1381