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