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