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