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