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