2007-08-19 [colin] 2.10.0cvs133
[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, %d 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, %d byte\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, %d byte\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 %d of %d 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                 printf("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 %d "
460                                   "of %d 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 %d "
468                                   "of %d 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                 g_warning("read_data_str: can't g_malloc %d bytes - cache data probably corrupted.\n", len);
482                 return -1;
483         }
484
485         if ((ni = fread(tmpstr, 1, len, fp)) != len) {
486                 g_warning("read_data_str: Cache data corrupted, read %d of %d "
487                           "bytes at offset %ld\n", 
488                           ni, len, ftell(fp));
489                 g_free(tmpstr);
490                 return -1;
491         }
492         tmpstr[len] = 0;
493
494         if (conv != NULL) {
495                 *str = conv->convert(conv, tmpstr);
496                 g_free(tmpstr);
497         } else 
498                 *str = tmpstr;
499
500         return len;
501 }
502
503 static gint msgcache_get_cache_data_str(gchar *src, gchar **str, gint len,
504                                          StringConverter *conv)
505 {
506         gchar *tmpstr = NULL;
507
508         *str = NULL;
509
510         if (len == 0)
511                 return 0;
512
513         if(len > 2*1024*1024) {
514                 g_warning("read_data_str: refusing to allocate %d bytes.\n", len);
515                 return -1;
516         }
517
518         tmpstr = g_try_malloc(len + 1);
519
520         if(tmpstr == NULL) {
521                 g_warning("read_data_str: can't g_malloc %d bytes - cache data probably corrupted.\n", len);
522                 return -1;
523         }
524
525         strncpy(tmpstr, src, len);
526
527         tmpstr[len] = 0;
528
529         if (conv != NULL) {
530                 *str = conv->convert(conv, tmpstr);
531                 g_free(tmpstr);
532         } else 
533                 *str = tmpstr;
534
535         return len;
536 }
537
538 static gchar *strconv_charset_convert(StringConverter *conv, gchar *srcstr)
539 {
540         CharsetConverter *charsetconv = (CharsetConverter *) conv;
541
542         return conv_codeset_strdup(srcstr, charsetconv->srccharset, charsetconv->dstcharset);
543 }
544
545 static void strconv_charset_free(StringConverter *conv)
546 {
547         CharsetConverter *charsetconv = (CharsetConverter *) conv;
548
549         g_free(charsetconv->srccharset);
550         g_free(charsetconv->dstcharset);
551 }
552
553 MsgCache *msgcache_read_cache(FolderItem *item, const gchar *cache_file)
554 {
555         MsgCache *cache;
556         FILE *fp;
557         MsgInfo *msginfo;
558         MsgTmpFlags tmp_flags = 0;
559         gchar file_buf[BUFFSIZE];
560         guint32 num;
561         guint refnum;
562         gboolean error = FALSE;
563         StringConverter *conv = NULL;
564         gchar *srccharset = NULL;
565         const gchar *dstcharset = NULL;
566         gchar *ref = NULL;
567         guint memusage = 0;
568         gint tmp_len = 0, map_len = -1;
569         char *cache_data = NULL;
570         struct stat st;
571
572         g_return_val_if_fail(cache_file != NULL, NULL);
573         g_return_val_if_fail(item != NULL, NULL);
574
575         swapping = TRUE;
576
577         /* In case we can't open the mark file with MARK_VERSION, check if we can open it with the
578          * swapped MARK_VERSION. As msgcache_open_data_file swaps it too, if this succeeds, 
579          * it means it's the old version (not little-endian) on a big-endian machine. The code has
580          * no effect on x86 as their file doesn't change. */
581
582         if ((fp = msgcache_open_data_file
583                 (cache_file, CACHE_VERSION, DATA_READ, file_buf, sizeof(file_buf))) == NULL) {
584                 if ((fp = msgcache_open_data_file
585                 (cache_file, bswap_32(CACHE_VERSION), DATA_READ, file_buf, sizeof(file_buf))) == NULL)
586                         return NULL;
587                 else
588                         swapping = FALSE;
589         }
590
591         debug_print("\tReading %sswapped message cache from %s...\n", swapping?"":"un", cache_file);
592
593         if (folder_has_parent_of_type(item, F_QUEUE)) {
594                 tmp_flags |= MSG_QUEUED;
595         } else if (folder_has_parent_of_type(item, F_DRAFT)) {
596                 tmp_flags |= MSG_DRAFT;
597         }
598
599         if (msgcache_read_cache_data_str(fp, &srccharset, NULL) < 0) {
600                 fclose(fp);
601                 return NULL;
602         }
603         dstcharset = CS_UTF_8;
604         if (srccharset == NULL || dstcharset == NULL) {
605                 conv = NULL;
606         } else if (strcmp(srccharset, dstcharset) == 0) {
607                 debug_print("using Noop Converter\n");
608
609                 conv = NULL;
610         } else {
611                 CharsetConverter *charsetconv;
612
613                 debug_print("using CharsetConverter\n");
614
615                 charsetconv = g_new0(CharsetConverter, 1);
616                 charsetconv->converter.convert = strconv_charset_convert;
617                 charsetconv->converter.free = strconv_charset_free;
618                 charsetconv->srccharset = g_strdup(srccharset);
619                 charsetconv->dstcharset = g_strdup(dstcharset);
620
621                 conv = (StringConverter *) charsetconv;
622         }
623         g_free(srccharset);
624
625         cache = msgcache_new();
626
627         if (msgcache_use_mmap_read == TRUE) {
628                 if (fstat(fileno(fp), &st) >= 0)
629                         map_len = st.st_size;
630                 else
631                         map_len = -1;
632                 if (map_len > 0) {
633 #ifdef G_OS_WIN32
634                         cache_data = NULL;
635                         HANDLE hFile, hMapping;
636                         hFile = (HANDLE) _get_osfhandle (fileno(fp));
637                         if (hFile == (HANDLE) -1)
638                                 goto w32_fail;
639                         hMapping = CreateFileMapping(hFile, NULL, PAGE_WRITECOPY, 0, 0, NULL);
640                         if (!hMapping)
641                                 goto w32_fail;
642                         cache_data = (unsigned char *)MapViewOfFile(hMapping, FILE_MAP_COPY, 0, 0, 0);
643                         CloseHandle (hMapping);
644                 w32_fail:
645                         ;
646 #else
647                         cache_data = mmap(NULL, map_len, PROT_READ, MAP_PRIVATE, fileno(fp), 0);
648 #endif
649                 }
650         } else {
651                 cache_data = NULL;
652         }
653         if (cache_data != NULL && cache_data != MAP_FAILED) {
654                 int rem_len = map_len-ftell(fp);
655                 char *walk_data = cache_data+ftell(fp);
656
657                 while(rem_len > 0) {
658                         GET_CACHE_DATA_INT(num);
659                         
660                         msginfo = procmsg_msginfo_new();
661                         msginfo->msgnum = num;
662                         memusage += sizeof(MsgInfo);
663
664                         GET_CACHE_DATA_INT(msginfo->size);
665                         GET_CACHE_DATA_INT(msginfo->mtime);
666                         GET_CACHE_DATA_INT(msginfo->date_t);
667                         GET_CACHE_DATA_INT(msginfo->flags.tmp_flags);
668
669                         GET_CACHE_DATA(msginfo->fromname, memusage);
670
671                         GET_CACHE_DATA(msginfo->date, memusage);
672                         GET_CACHE_DATA(msginfo->from, memusage);
673                         GET_CACHE_DATA(msginfo->to, memusage);
674                         GET_CACHE_DATA(msginfo->cc, memusage);
675                         GET_CACHE_DATA(msginfo->newsgroups, memusage);
676                         GET_CACHE_DATA(msginfo->subject, memusage);
677                         GET_CACHE_DATA(msginfo->msgid, memusage);
678                         GET_CACHE_DATA(msginfo->inreplyto, memusage);
679                         GET_CACHE_DATA(msginfo->xref, memusage);
680
681                         GET_CACHE_DATA_INT(msginfo->planned_download);
682                         GET_CACHE_DATA_INT(msginfo->total_size);
683                         GET_CACHE_DATA_INT(refnum);
684
685                         for (; refnum != 0; refnum--) {
686                                 ref = NULL;
687
688                                 GET_CACHE_DATA(ref, memusage);
689
690                                 if (ref && *ref)
691                                         msginfo->references =
692                                                 g_slist_prepend(msginfo->references, ref);
693                         }
694                         if (msginfo->references)
695                                 msginfo->references =
696                                         g_slist_reverse(msginfo->references);
697
698                         msginfo->folder = item;
699                         msginfo->flags.tmp_flags |= tmp_flags;
700
701                         g_hash_table_insert(cache->msgnum_table, &msginfo->msgnum, msginfo);
702                         if(msginfo->msgid)
703                                 g_hash_table_insert(cache->msgid_table, msginfo->msgid, msginfo);
704                 }
705
706 #ifdef G_OS_WIN32
707                 UnmapViewOfFile((void*) cache_data);
708 #else
709                 munmap(cache_data, map_len);
710 #endif
711         } else {
712                 while (fread(&num, sizeof(num), 1, fp) == 1) {
713                         if (swapping)
714                                 num = bswap_32(num);
715
716                         msginfo = procmsg_msginfo_new();
717                         msginfo->msgnum = num;
718                         memusage += sizeof(MsgInfo);
719
720                         READ_CACHE_DATA_INT(msginfo->size, fp);
721                         READ_CACHE_DATA_INT(msginfo->mtime, fp);
722                         READ_CACHE_DATA_INT(msginfo->date_t, fp);
723                         READ_CACHE_DATA_INT(msginfo->flags.tmp_flags, fp);
724
725                         READ_CACHE_DATA(msginfo->fromname, fp, memusage);
726
727                         READ_CACHE_DATA(msginfo->date, fp, memusage);
728                         READ_CACHE_DATA(msginfo->from, fp, memusage);
729                         READ_CACHE_DATA(msginfo->to, fp, memusage);
730                         READ_CACHE_DATA(msginfo->cc, fp, memusage);
731                         READ_CACHE_DATA(msginfo->newsgroups, fp, memusage);
732                         READ_CACHE_DATA(msginfo->subject, fp, memusage);
733                         READ_CACHE_DATA(msginfo->msgid, fp, memusage);
734                         READ_CACHE_DATA(msginfo->inreplyto, fp, memusage);
735                         READ_CACHE_DATA(msginfo->xref, fp, memusage);
736
737                         READ_CACHE_DATA_INT(msginfo->planned_download, fp);
738                         READ_CACHE_DATA_INT(msginfo->total_size, fp);
739                         READ_CACHE_DATA_INT(refnum, fp);
740
741                         for (; refnum != 0; refnum--) {
742                                 ref = NULL;
743
744                                 READ_CACHE_DATA(ref, fp, memusage);
745
746                                 if (ref && *ref)
747                                         msginfo->references =
748                                                 g_slist_prepend(msginfo->references, ref);
749                         }
750                         if (msginfo->references)
751                                 msginfo->references =
752                                         g_slist_reverse(msginfo->references);
753
754                         msginfo->folder = item;
755                         msginfo->flags.tmp_flags |= tmp_flags;
756
757                         g_hash_table_insert(cache->msgnum_table, &msginfo->msgnum, msginfo);
758                         if(msginfo->msgid)
759                                 g_hash_table_insert(cache->msgid_table, msginfo->msgid, msginfo);
760                 }
761         }
762 bail_err:
763         fclose(fp);
764
765         if (conv != NULL) {
766                 if (conv->free != NULL)
767                         conv->free(conv);
768                 g_free(conv);
769         }
770
771         if(error) {
772                 msgcache_destroy(cache);
773                 return NULL;
774         }
775
776         cache->last_access = time(NULL);
777         cache->memusage = memusage;
778
779         debug_print("done. (%d items read)\n", g_hash_table_size(cache->msgnum_table));
780         debug_print("Cache size: %d messages, %d byte\n", g_hash_table_size(cache->msgnum_table), cache->memusage);
781         return cache;
782 }
783
784 void msgcache_read_mark(MsgCache *cache, const gchar *mark_file)
785 {
786         FILE *fp;
787         MsgInfo *msginfo;
788         MsgPermFlags perm_flags;
789         guint32 num;
790         gint map_len = -1;
791         char *cache_data = NULL;
792         struct stat st;
793         
794         swapping = TRUE;
795
796         /* In case we can't open the mark file with MARK_VERSION, check if we can open it with the
797          * swapped MARK_VERSION. As msgcache_open_data_file swaps it too, if this succeeds, 
798          * it means it's the old version (not little-endian) on a big-endian machine. The code has
799          * no effect on x86 as their file doesn't change. */
800
801         if ((fp = msgcache_open_data_file(mark_file, MARK_VERSION, DATA_READ, NULL, 0)) == NULL) {
802                 /* see if it isn't swapped ? */
803                 if ((fp = msgcache_open_data_file(mark_file, bswap_32(MARK_VERSION), DATA_READ, NULL, 0)) == NULL)
804                         return;
805                 else
806                         swapping = FALSE; /* yay */
807         }
808         debug_print("reading %sswapped mark file.\n", swapping?"":"un");
809         
810         if (msgcache_use_mmap_read) {
811                 if (fstat(fileno(fp), &st) >= 0)
812                         map_len = st.st_size;
813                 else
814                         map_len = -1;
815                 if (map_len > 0) {
816 #ifdef G_OS_WIN32
817                         cache_data = NULL;
818                         HANDLE hFile, hMapping;
819                         hFile = (HANDLE) _get_osfhandle (fileno(fp));
820                         if (hFile == (HANDLE) -1)
821                                 goto w32_fail2;
822                         hMapping = CreateFileMapping(hFile, NULL, PAGE_WRITECOPY, 0, 0, NULL);
823                         if (!hMapping)
824                                 goto w32_fail2;
825                         cache_data = (unsigned char *)MapViewOfFile(hMapping, FILE_MAP_COPY, 0, 0, 0);
826                         CloseHandle (hMapping);
827                 w32_fail2:
828                         ;
829 #else
830                         cache_data = mmap(NULL, map_len, PROT_READ, MAP_PRIVATE, fileno(fp), 0);
831 #endif
832                 }
833         } else {
834                 cache_data = NULL;
835         }
836         if (cache_data != NULL && cache_data != MAP_FAILED) {
837                 int rem_len = map_len-ftell(fp);
838                 char *walk_data = cache_data+ftell(fp);
839
840                 while(rem_len > 0) {
841                         GET_CACHE_DATA_INT(num);
842                         GET_CACHE_DATA_INT(perm_flags);
843                         msginfo = g_hash_table_lookup(cache->msgnum_table, &num);
844                         if(msginfo) {
845                                 msginfo->flags.perm_flags = perm_flags;
846                         }
847                 }
848 #ifdef G_OS_WIN32
849                 UnmapViewOfFile((void*) cache_data);
850 #else
851                 munmap(cache_data, map_len);
852 #endif
853         } else {
854                 while (fread(&num, sizeof(num), 1, fp) == 1) {
855                         if (swapping)
856                                 num = bswap_32(num);
857                         if (fread(&perm_flags, sizeof(perm_flags), 1, fp) != 1) break;
858                         if (swapping)
859                                 perm_flags = bswap_32(perm_flags);
860                         msginfo = g_hash_table_lookup(cache->msgnum_table, &num);
861                         if(msginfo) {
862                                 msginfo->flags.perm_flags = perm_flags;
863                         }
864                 }       
865         }
866         fclose(fp);
867 }
868
869 void msgcache_read_tags(MsgCache *cache, const gchar *tags_file)
870 {
871         FILE *fp;
872         MsgInfo *msginfo;
873         guint32 num;
874         gint map_len = -1;
875         char *cache_data = NULL;
876         struct stat st;
877         
878         swapping = TRUE;
879
880         /* In case we can't open the mark file with MARK_VERSION, check if we can open it with the
881          * swapped MARK_VERSION. As msgcache_open_data_file swaps it too, if this succeeds, 
882          * it means it's the old version (not little-endian) on a big-endian machine. The code has
883          * no effect on x86 as their file doesn't change. */
884
885         if ((fp = msgcache_open_data_file(tags_file, TAGS_VERSION, DATA_READ, NULL, 0)) == NULL) {
886                 /* see if it isn't swapped ? */
887                 if ((fp = msgcache_open_data_file(tags_file, bswap_32(TAGS_VERSION), DATA_READ, NULL, 0)) == NULL)
888                         return;
889                 else
890                         swapping = FALSE; /* yay */
891         }
892         debug_print("reading %sswapped tags file.\n", swapping?"":"un");
893         
894         if (msgcache_use_mmap_read) {
895                 if (fstat(fileno(fp), &st) >= 0)
896                         map_len = st.st_size;
897                 else
898                         map_len = -1;
899                 if (map_len > 0) {
900 #ifdef G_OS_WIN32
901                         cache_data = NULL;
902                         HANDLE hFile, hMapping;
903                         hFile = (HANDLE) _get_osfhandle (fileno(fp));
904                         if (hFile == (HANDLE) -1)
905                                 goto w32_fail6;
906                         hMapping = CreateFileMapping(hFile, NULL, PAGE_WRITECOPY, 0, 0, NULL);
907                         if (!hMapping)
908                                 goto w32_fail6;
909                         cache_data = (unsigned char *)MapViewOfFile(hMapping, FILE_MAP_COPY, 0, 0, 0);
910                         CloseHandle (hMapping);
911                 w32_fail6:
912                         ;
913 #else
914                         cache_data = mmap(NULL, map_len, PROT_READ, MAP_PRIVATE, fileno(fp), 0);
915 #endif
916                 }
917         } else {
918                 cache_data = NULL;
919         }
920         if (cache_data != NULL && cache_data != MAP_FAILED) {
921                 int rem_len = map_len-ftell(fp);
922                 char *walk_data = cache_data+ftell(fp);
923
924                 while(rem_len > 0) {
925                         gint id = -1;
926                         GET_CACHE_DATA_INT(num);
927                         msginfo = g_hash_table_lookup(cache->msgnum_table, &num);
928                         if(msginfo) {
929                                 g_slist_free(msginfo->tags);
930                                 msginfo->tags = NULL;
931                                 do {
932                                         GET_CACHE_DATA_INT(id);
933                                         if (id > 0) {
934                                                 msginfo->tags = g_slist_prepend(
935                                                         msginfo->tags, 
936                                                         GINT_TO_POINTER(id));
937                                         }
938                                 } while (id > 0);
939                                 msginfo->tags = g_slist_reverse(msginfo->tags);
940                         }
941                 }
942 #ifdef G_OS_WIN32
943                 UnmapViewOfFile((void*) cache_data);
944 #else
945                 munmap(cache_data, map_len);
946 #endif
947         } else {
948                 while (fread(&num, sizeof(num), 1, fp) == 1) {
949                         gint id = -1;
950                         if (swapping)
951                                 num = bswap_32(num);
952                         msginfo = g_hash_table_lookup(cache->msgnum_table, &num);
953                         if(msginfo) {
954                                 g_slist_free(msginfo->tags);
955                                 msginfo->tags = NULL;
956                                 do {
957                                         if (fread(&id, sizeof(id), 1, fp) != 1) 
958                                                 id = -1;
959                                         if (swapping)
960                                                 id = bswap_32(id);
961                                         if (id > 0) {
962                                                 msginfo->tags = g_slist_prepend(
963                                                         msginfo->tags, 
964                                                         GINT_TO_POINTER(id));
965                                         }
966                                 } while (id > 0);
967                                 msginfo->tags = g_slist_reverse(msginfo->tags);
968                         }
969                 }
970         }
971         fclose(fp);
972 }
973
974 static int msgcache_write_cache(MsgInfo *msginfo, FILE *fp)
975 {
976         MsgTmpFlags flags = msginfo->flags.tmp_flags & MSG_CACHED_FLAG_MASK;
977         GSList *cur;
978         int w_err = 0, wrote = 0;
979
980         WRITE_CACHE_DATA_INT(msginfo->msgnum, fp);
981         WRITE_CACHE_DATA_INT(msginfo->size, fp);
982         WRITE_CACHE_DATA_INT(msginfo->mtime, fp);
983         WRITE_CACHE_DATA_INT(msginfo->date_t, fp);
984         WRITE_CACHE_DATA_INT(flags, fp);
985
986         WRITE_CACHE_DATA(msginfo->fromname, fp);
987
988         WRITE_CACHE_DATA(msginfo->date, fp);
989         WRITE_CACHE_DATA(msginfo->from, fp);
990         WRITE_CACHE_DATA(msginfo->to, fp);
991         WRITE_CACHE_DATA(msginfo->cc, fp);
992         WRITE_CACHE_DATA(msginfo->newsgroups, fp);
993         WRITE_CACHE_DATA(msginfo->subject, fp);
994         WRITE_CACHE_DATA(msginfo->msgid, fp);
995         WRITE_CACHE_DATA(msginfo->inreplyto, fp);
996         WRITE_CACHE_DATA(msginfo->xref, fp);
997         WRITE_CACHE_DATA_INT(msginfo->planned_download, fp);
998         WRITE_CACHE_DATA_INT(msginfo->total_size, fp);
999         
1000         WRITE_CACHE_DATA_INT(g_slist_length(msginfo->references), fp);
1001
1002         for (cur = msginfo->references; cur != NULL; cur = cur->next) {
1003                 WRITE_CACHE_DATA((gchar *)cur->data, fp);
1004         }
1005         return w_err ? -1 : wrote;
1006 }
1007
1008 static int msgcache_write_mmap_cache(MsgInfo *msginfo, char *walk_data)
1009 {
1010         MsgTmpFlags flags = msginfo->flags.tmp_flags & MSG_CACHED_FLAG_MASK;
1011         GSList *cur;
1012         int wrote = 0;
1013
1014         PUT_CACHE_DATA_INT(msginfo->msgnum);
1015         PUT_CACHE_DATA_INT(msginfo->size);
1016         PUT_CACHE_DATA_INT(msginfo->mtime);
1017         PUT_CACHE_DATA_INT(msginfo->date_t);
1018         PUT_CACHE_DATA_INT(flags);
1019         PUT_CACHE_DATA(msginfo->fromname);
1020
1021         PUT_CACHE_DATA(msginfo->date);
1022         PUT_CACHE_DATA(msginfo->from);
1023         PUT_CACHE_DATA(msginfo->to);
1024         PUT_CACHE_DATA(msginfo->cc);
1025         PUT_CACHE_DATA(msginfo->newsgroups);
1026         PUT_CACHE_DATA(msginfo->subject);
1027         PUT_CACHE_DATA(msginfo->msgid);
1028         PUT_CACHE_DATA(msginfo->inreplyto);
1029         PUT_CACHE_DATA(msginfo->xref);
1030         PUT_CACHE_DATA_INT(msginfo->planned_download);
1031         PUT_CACHE_DATA_INT(msginfo->total_size);
1032         
1033         PUT_CACHE_DATA_INT(g_slist_length(msginfo->references));
1034
1035         for (cur = msginfo->references; cur != NULL; cur = cur->next) {
1036                 PUT_CACHE_DATA((gchar *)cur->data);
1037         }
1038         return wrote;
1039 }
1040
1041 static int msgcache_write_flags(MsgInfo *msginfo, FILE *fp)
1042 {
1043         MsgPermFlags flags = msginfo->flags.perm_flags;
1044         int w_err = 0, wrote = 0;
1045         WRITE_CACHE_DATA_INT(msginfo->msgnum, fp);
1046         WRITE_CACHE_DATA_INT(flags, fp);
1047         return w_err ? -1 : wrote;
1048 }
1049
1050 static int msgcache_write_tags(MsgInfo *msginfo, FILE *fp)
1051 {
1052         GSList *cur = msginfo->tags;
1053         int w_err = 0, wrote = 0;
1054
1055         WRITE_CACHE_DATA_INT(msginfo->msgnum, fp);
1056         for (; cur; cur = cur->next) {
1057                 gint id = GPOINTER_TO_INT(cur->data);
1058                 if (tags_get_tag(id) != NULL) {
1059                         WRITE_CACHE_DATA_INT(id, fp);
1060                 }
1061         }
1062         WRITE_CACHE_DATA_INT(-1, fp);
1063
1064         return w_err ? -1 : wrote;
1065 }
1066
1067 static int msgcache_write_mmap_flags(MsgInfo *msginfo, char *walk_data)
1068 {
1069         MsgPermFlags flags = msginfo->flags.perm_flags;
1070         int wrote = 0;
1071
1072         PUT_CACHE_DATA_INT(msginfo->msgnum);
1073         PUT_CACHE_DATA_INT(flags);
1074         return wrote;
1075 }
1076
1077 static int msgcache_write_mmap_tags(MsgInfo *msginfo, char *walk_data)
1078 {
1079         GSList *cur = msginfo->tags;
1080         int wrote = 0;
1081         
1082         PUT_CACHE_DATA_INT(msginfo->msgnum);
1083         for (; cur; cur = cur->next) {
1084                 gint id = GPOINTER_TO_INT(cur->data);
1085                 if (tags_get_tag(id) != NULL) {
1086                         PUT_CACHE_DATA_INT(id);
1087                 }
1088         }
1089         PUT_CACHE_DATA_INT(-1);
1090         return wrote;
1091 }
1092
1093 struct write_fps
1094 {
1095         FILE *cache_fp;
1096         FILE *mark_fp;
1097         FILE *tags_fp;
1098         char *cache_data;
1099         char *mark_data;
1100         char *tags_data;
1101         int error;
1102         guint cache_size;
1103         guint mark_size;
1104         guint tags_size;
1105 };
1106
1107 static void msgcache_write_func(gpointer key, gpointer value, gpointer user_data)
1108 {
1109         MsgInfo *msginfo;
1110         struct write_fps *write_fps;
1111         int tmp;
1112
1113         msginfo = (MsgInfo *)value;
1114         write_fps = user_data;
1115
1116         tmp = msgcache_write_cache(msginfo, write_fps->cache_fp);
1117         if (tmp < 0)
1118                 write_fps->error = 1;
1119         else
1120                 write_fps->cache_size += tmp;
1121         tmp= msgcache_write_flags(msginfo, write_fps->mark_fp);
1122         if (tmp < 0)
1123                 write_fps->error = 1;
1124         else
1125                 write_fps->mark_size += tmp;
1126         tmp = msgcache_write_tags(msginfo, write_fps->tags_fp);
1127         if (tmp < 0)
1128                 write_fps->error = 1;
1129         else
1130                 write_fps->tags_size += tmp;
1131 }
1132
1133 static void msgcache_write_mmap_func(gpointer key, gpointer value, gpointer user_data)
1134 {
1135         MsgInfo *msginfo;
1136         struct write_fps *write_fps;
1137         int tmp;
1138
1139         msginfo = (MsgInfo *)value;
1140         write_fps = user_data;
1141
1142         tmp = msgcache_write_mmap_cache(msginfo, write_fps->cache_data);
1143         write_fps->cache_size += tmp;
1144         write_fps->cache_data += tmp;
1145         tmp = msgcache_write_mmap_flags(msginfo, write_fps->mark_data);
1146         write_fps->mark_size += tmp;
1147         write_fps->mark_data += tmp;
1148         tmp = msgcache_write_mmap_tags(msginfo, write_fps->tags_data);
1149         write_fps->tags_size += tmp;
1150         write_fps->tags_data += tmp;
1151 }
1152
1153 gint msgcache_write(const gchar *cache_file, const gchar *mark_file, const gchar *tags_file, MsgCache *cache)
1154 {
1155         struct write_fps write_fps;
1156         gchar *new_cache, *new_mark, *new_tags;
1157         int w_err = 0, wrote = 0;
1158         gint map_len = -1;
1159         char *cache_data = NULL;
1160         char *mark_data = NULL;
1161         char *tags_data = NULL;
1162
1163         START_TIMING("");
1164         g_return_val_if_fail(cache_file != NULL, -1);
1165         g_return_val_if_fail(mark_file != NULL, -1);
1166         g_return_val_if_fail(tags_file != NULL, -1);
1167         g_return_val_if_fail(cache != NULL, -1);
1168
1169         new_cache = g_strconcat(cache_file, ".new", NULL);
1170         new_mark  = g_strconcat(mark_file, ".new", NULL);
1171         new_tags  = g_strconcat(tags_file, ".new", NULL);
1172
1173         write_fps.error = 0;
1174         write_fps.cache_size = 0;
1175         write_fps.mark_size = 0;
1176         write_fps.tags_size = 0;
1177
1178         write_fps.cache_fp = msgcache_open_data_file(new_cache, CACHE_VERSION,
1179                 DATA_WRITE, NULL, 0);
1180         if (write_fps.cache_fp == NULL) {
1181                 g_free(new_cache);
1182                 g_free(new_mark);
1183                 g_free(new_tags);
1184                 return -1;
1185         }
1186
1187         WRITE_CACHE_DATA(CS_UTF_8, write_fps.cache_fp);
1188
1189         if (w_err != 0) {
1190                 g_warning("failed to write charset\n");
1191                 fclose(write_fps.cache_fp);
1192                 g_unlink(new_cache);
1193                 g_free(new_cache);
1194                 g_free(new_mark);
1195                 g_free(new_tags);
1196                 return -1;
1197         }
1198
1199         write_fps.mark_fp = msgcache_open_data_file(new_mark, MARK_VERSION,
1200                 DATA_WRITE, NULL, 0);
1201         if (write_fps.mark_fp == NULL) {
1202                 fclose(write_fps.cache_fp);
1203                 g_unlink(new_cache);
1204                 g_free(new_cache);
1205                 g_free(new_mark);
1206                 g_free(new_tags);
1207                 return -1;
1208         }
1209
1210         write_fps.tags_fp = msgcache_open_data_file(new_tags, TAGS_VERSION,
1211                 DATA_WRITE, NULL, 0);
1212         if (write_fps.tags_fp == NULL) {
1213                 fclose(write_fps.cache_fp);
1214                 fclose(write_fps.mark_fp);
1215                 g_unlink(new_cache);
1216                 g_unlink(new_mark);
1217                 g_free(new_cache);
1218                 g_free(new_mark);
1219                 g_free(new_tags);
1220                 return -1;
1221         }
1222
1223         debug_print("\tWriting message cache to %s and %s...\n", new_cache, new_mark);
1224
1225         if (change_file_mode_rw(write_fps.cache_fp, new_cache) < 0)
1226                 FILE_OP_ERROR(new_cache, "chmod");
1227
1228         write_fps.cache_size = ftell(write_fps.cache_fp);
1229         write_fps.mark_size = ftell(write_fps.mark_fp);
1230         write_fps.tags_size = ftell(write_fps.tags_fp);
1231
1232         if (msgcache_use_mmap_write && cache->memusage > 0) {
1233                 map_len = cache->memusage;
1234                 if (ftruncate(fileno(write_fps.cache_fp), (off_t)map_len) == 0) {
1235
1236 #ifdef G_OS_WIN32
1237                         cache_data = NULL;
1238                         HANDLE hFile, hMapping;
1239                         hFile = (HANDLE) _get_osfhandle (fileno(write_fps.cache_fp));
1240                         if (hFile == (HANDLE) -1)
1241                                 goto w32_fail3;
1242                         hMapping = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, NULL);
1243                         if (!hMapping)
1244                                 goto w32_fail3;
1245                         cache_data = (unsigned char *)MapViewOfFile(hMapping, FILE_MAP_COPY, 0, 0, 0);
1246                         CloseHandle (hMapping);
1247                 w32_fail3:
1248                         ;
1249 #else
1250                         cache_data = mmap(NULL, map_len, PROT_WRITE, MAP_SHARED, 
1251                                 fileno(write_fps.cache_fp), 0);
1252 #endif
1253                 }
1254                 if (cache_data != NULL && cache_data != MAP_FAILED) {
1255                         if (ftruncate(fileno(write_fps.mark_fp), (off_t)map_len) == 0) {
1256 #ifdef G_OS_WIN32
1257                                 mark_data = NULL;
1258                                 HANDLE hFile, hMapping;
1259                                 hFile = (HANDLE) _get_osfhandle (fileno(write_fps.mark_fp));
1260                                 if (hFile == (HANDLE) -1)
1261                                         goto w32_fail4;
1262                                 hMapping = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, NULL);
1263                                 if (!hMapping)
1264                                         goto w32_fail4;
1265                                 mark_data = (unsigned char *)MapViewOfFile(hMapping, FILE_MAP_COPY, 0, 0, 0);
1266                                 CloseHandle (hMapping);
1267                         w32_fail4:
1268                                 ;
1269 #else
1270                                 mark_data = mmap(NULL, map_len, PROT_WRITE, MAP_SHARED, 
1271                                         fileno(write_fps.mark_fp), 0);
1272 #endif
1273                         } 
1274                         if (mark_data == NULL || mark_data == MAP_FAILED) {
1275 #ifdef G_OS_WIN32
1276                                 UnmapViewOfFile((void*) cache_data);
1277 #else
1278                                 munmap(cache_data, map_len);
1279 #endif
1280                                 cache_data = NULL;
1281                         } else {
1282                                 if (ftruncate(fileno(write_fps.tags_fp), (off_t)map_len) == 0) {
1283 #ifdef G_OS_WIN32
1284                                                 tags_data = NULL;
1285                                                 HANDLE hFile, hMapping;
1286                                                 hFile = (HANDLE) _get_osfhandle (fileno(write_fps.tags_fp));
1287                                                 if (hFile == (HANDLE) -1)
1288                                                         goto w32_fail5;
1289                                                 hMapping = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, NULL);
1290                                                 if (!hMapping)
1291                                                         goto w32_fail5;
1292                                                 tags_data = (unsigned char *)MapViewOfFile(hMapping, FILE_MAP_COPY, 0, 0, 0);
1293                                                 CloseHandle (hMapping);
1294                                         w32_fail5:
1295                                                 ;
1296 #else
1297                                         tags_data = mmap(NULL, map_len, PROT_WRITE, MAP_SHARED, 
1298                                                 fileno(write_fps.tags_fp), 0);
1299 #endif
1300                                 } 
1301                                 if (tags_data == NULL || tags_data == MAP_FAILED) {
1302 #ifdef G_OS_WIN32
1303                                         UnmapViewOfFile((void*) cache_data);
1304                                         UnmapViewOfFile((void*) mark_data);
1305 #else
1306                                         munmap(cache_data, map_len);
1307                                         munmap(mark_data, map_len);
1308 #endif
1309                                         cache_data = NULL;
1310                                         mark_data = NULL;
1311                                 } 
1312                         }
1313                 }
1314         }
1315
1316         if (cache_data != NULL && cache_data != MAP_FAILED) {
1317                 write_fps.cache_data = cache_data + ftell(write_fps.cache_fp);
1318                 write_fps.mark_data = mark_data + ftell(write_fps.mark_fp);
1319                 write_fps.tags_data = mark_data + ftell(write_fps.tags_fp);
1320                 g_hash_table_foreach(cache->msgnum_table, msgcache_write_mmap_func, (gpointer)&write_fps);
1321 #ifdef G_OS_WIN32
1322                 UnmapViewOfFile((void*) cache_data);
1323                 UnmapViewOfFile((void*) mark_data);
1324                 UnmapViewOfFile((void*) tags_data);
1325 #else
1326                 munmap(cache_data, map_len);
1327                 munmap(mark_data, map_len);
1328                 munmap(tags_data, map_len);
1329 #endif
1330                 ftruncate(fileno(write_fps.cache_fp), write_fps.cache_size);
1331                 ftruncate(fileno(write_fps.mark_fp), write_fps.mark_size);
1332                 ftruncate(fileno(write_fps.tags_fp), write_fps.tags_size);
1333         } else {
1334 #ifdef HAVE_FWRITE_UNLOCKED
1335                 flockfile(write_fps.cache_fp);
1336                 flockfile(write_fps.mark_fp);
1337                 flockfile(write_fps.tags_fp);
1338 #endif
1339                 g_hash_table_foreach(cache->msgnum_table, msgcache_write_func, (gpointer)&write_fps);
1340 #ifdef HAVE_FWRITE_UNLOCKED
1341                 funlockfile(write_fps.mark_fp);
1342                 funlockfile(write_fps.cache_fp);
1343                 funlockfile(write_fps.tags_fp);
1344 #endif
1345         }
1346         
1347         fflush(write_fps.cache_fp);
1348         fflush(write_fps.mark_fp);
1349         fflush(write_fps.tags_fp);
1350
1351 #if 0
1352         fsync(fileno(write_fps.cache_fp));
1353         fsync(fileno(write_fps.mark_fp));
1354         fsync(fileno(write_fps.tags_fp));
1355 #endif
1356
1357         fclose(write_fps.cache_fp);
1358         fclose(write_fps.mark_fp);
1359         fclose(write_fps.tags_fp);
1360
1361
1362         if (write_fps.error != 0) {
1363                 g_unlink(new_cache);
1364                 g_unlink(new_mark);
1365                 g_unlink(new_tags);
1366                 g_free(new_cache);
1367                 g_free(new_mark);
1368                 g_free(new_tags);
1369                 return -1;
1370         } else {
1371                 move_file(new_cache, cache_file, TRUE);
1372                 move_file(new_mark, mark_file, TRUE);
1373                 move_file(new_tags, tags_file, TRUE);
1374                 cache->last_access = time(NULL);
1375         }
1376
1377         g_free(new_cache);
1378         g_free(new_mark);
1379         g_free(new_tags);
1380         debug_print("done.\n");
1381         END_TIMING();
1382         return 0;
1383 }
1384