2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2001 Hiroyuki Yamamoto
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
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.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
32 GHashTable
*msgnum_table
;
33 GHashTable
*msgid_table
;
38 MsgCache
*msgcache_new()
42 cache
= g_new0(MsgCache
, 1),
43 cache
->msgnum_table
= g_hash_table_new(g_int_hash
, g_int_equal
);
44 cache
->msgid_table
= g_hash_table_new(g_str_hash
, g_str_equal
);
45 cache
->last_access
= time(NULL
);
50 static gboolean
msgcache_msginfo_free_func(gpointer num
, gpointer msginfo
, gpointer user_data
)
52 procmsg_msginfo_free((MsgInfo
*)msginfo
);
56 void msgcache_destroy(MsgCache
*cache
)
58 g_return_if_fail(cache
!= NULL
);
60 g_hash_table_foreach_remove(cache
->msgnum_table
, msgcache_msginfo_free_func
, NULL
);
61 g_hash_table_destroy(cache
->msgid_table
);
62 g_hash_table_destroy(cache
->msgnum_table
);
66 void msgcache_add_msg(MsgCache
*cache
, MsgInfo
*msginfo
)
70 g_return_if_fail(cache
!= NULL
);
71 g_return_if_fail(msginfo
!= NULL
);
73 newmsginfo
= procmsg_msginfo_new_ref(msginfo
);
74 g_hash_table_insert(cache
->msgnum_table
, &newmsginfo
->msgnum
, newmsginfo
);
75 if(newmsginfo
->msgid
!= NULL
)
76 g_hash_table_insert(cache
->msgid_table
, newmsginfo
->msgid
, newmsginfo
);
77 cache
->memusage
+= procmsg_msginfo_memusage(msginfo
);
78 cache
->last_access
= time(NULL
);
80 debug_print("Cache size: %d messages, %d byte\n", g_hash_table_size(cache
->msgnum_table
), cache
->memusage
);
83 void msgcache_remove_msg(MsgCache
*cache
, guint msgnum
)
87 g_return_if_fail(cache
!= NULL
);
88 g_return_if_fail(msgnum
> 0);
90 msginfo
= (MsgInfo
*) g_hash_table_lookup(cache
->msgnum_table
, &msgnum
);
94 cache
->memusage
-= procmsg_msginfo_memusage(msginfo
);
96 g_hash_table_remove(cache
->msgid_table
, msginfo
->msgid
);
97 g_hash_table_remove(cache
->msgnum_table
, &msginfo
->msgnum
);
98 procmsg_msginfo_free(msginfo
);
99 cache
->last_access
= time(NULL
);
101 debug_print("Cache size: %d messages, %d byte\n", g_hash_table_size(cache
->msgnum_table
), cache
->memusage
);
104 void msgcache_update_msg(MsgCache
*cache
, MsgInfo
*msginfo
)
106 MsgInfo
*oldmsginfo
, *newmsginfo
;
108 g_return_if_fail(cache
!= NULL
);
109 g_return_if_fail(msginfo
!= NULL
);
111 oldmsginfo
= g_hash_table_lookup(cache
->msgnum_table
, &msginfo
->msgnum
);
113 g_hash_table_remove(cache
->msgid_table
, oldmsginfo
->msgid
);
114 g_hash_table_remove(cache
->msgnum_table
, &oldmsginfo
->msgnum
);
115 procmsg_msginfo_free(oldmsginfo
);
117 cache
->memusage
-= procmsg_msginfo_memusage(oldmsginfo
);
119 newmsginfo
= procmsg_msginfo_new_ref(msginfo
);
120 g_hash_table_insert(cache
->msgnum_table
, &newmsginfo
->msgnum
, newmsginfo
);
121 if(newmsginfo
->msgid
)
122 g_hash_table_insert(cache
->msgid_table
, newmsginfo
->msgid
, newmsginfo
);
123 cache
->memusage
+= procmsg_msginfo_memusage(newmsginfo
);
124 cache
->last_access
= time(NULL
);
126 debug_print("Cache size: %d messages, %d byte\n", g_hash_table_size(cache
->msgnum_table
), cache
->memusage
);
131 static gint
msgcache_read_cache_data_str(FILE *fp
, gchar
**str
)
137 if (fread(&len
, sizeof(len
), 1, fp
) == 1) {
144 size_t size
= MIN(len
, BUFFSIZE
- 1);
146 if (fread(buf
, size
, 1, fp
) != 1) {
148 if (tmp
) g_free(tmp
);
155 *str
= g_strconcat(tmp
, buf
, NULL
);
159 tmp
= *str
= g_strdup(buf
);
168 g_warning("Cache data is corrupted\n");
174 #define READ_CACHE_DATA(data, fp) \
176 if (msgcache_read_cache_data_str(fp, &data) < 0) { \
177 procmsg_msginfo_free(msginfo); \
183 #define READ_CACHE_DATA_INT(n, fp) \
185 if (fread(&n, sizeof(n), 1, fp) != 1) { \
186 g_warning("Cache data is corrupted\n"); \
187 procmsg_msginfo_free(msginfo); \
193 MsgCache
*msgcache_read_cache(FolderItem
*item
, const gchar
*cache_file
)
198 MsgTmpFlags tmp_flags
= 0;
199 gchar file_buf
[BUFFSIZE
];
202 gboolean error
= FALSE
;
204 g_return_val_if_fail(cache_file
!= NULL
, NULL
);
205 g_return_val_if_fail(item
!= NULL
, NULL
);
207 if ((fp
= fopen(cache_file
, "rb")) == NULL
) {
208 debug_print("\tNo cache file\n");
211 setvbuf(fp
, file_buf
, _IOFBF
, sizeof(file_buf
));
213 debug_print("\tReading message cache from %s...\n", cache_file
);
215 /* compare cache version */
216 if (fread(&ver
, sizeof(ver
), 1, fp
) != 1 ||
217 CACHE_VERSION
!= ver
) {
218 debug_print("Cache version is different. Discarding it.\n");
223 if (item
->stype
== F_QUEUE
) {
224 tmp_flags
|= MSG_QUEUED
;
225 } else if (item
->stype
== F_DRAFT
) {
226 tmp_flags
|= MSG_DRAFT
;
229 cache
= msgcache_new();
231 g_hash_table_freeze(cache
->msgnum_table
);
233 while (fread(&num
, sizeof(num
), 1, fp
) == 1) {
234 msginfo
= procmsg_msginfo_new();
235 msginfo
->msgnum
= num
;
236 READ_CACHE_DATA_INT(msginfo
->size
, fp
);
237 READ_CACHE_DATA_INT(msginfo
->mtime
, fp
);
238 READ_CACHE_DATA_INT(msginfo
->date_t
, fp
);
239 READ_CACHE_DATA_INT(msginfo
->flags
.tmp_flags
, fp
);
241 READ_CACHE_DATA(msginfo
->fromname
, fp
);
243 READ_CACHE_DATA(msginfo
->date
, fp
);
244 READ_CACHE_DATA(msginfo
->from
, fp
);
245 READ_CACHE_DATA(msginfo
->to
, fp
);
246 READ_CACHE_DATA(msginfo
->cc
, fp
);
247 READ_CACHE_DATA(msginfo
->newsgroups
, fp
);
248 READ_CACHE_DATA(msginfo
->subject
, fp
);
249 READ_CACHE_DATA(msginfo
->msgid
, fp
);
250 READ_CACHE_DATA(msginfo
->inreplyto
, fp
);
251 READ_CACHE_DATA(msginfo
->references
, fp
);
252 READ_CACHE_DATA(msginfo
->xref
, fp
);
254 msginfo
->folder
= item
;
255 msginfo
->flags
.tmp_flags
|= tmp_flags
;
257 g_hash_table_insert(cache
->msgnum_table
, &msginfo
->msgnum
, msginfo
);
259 g_hash_table_insert(cache
->msgid_table
, msginfo
->msgid
, msginfo
);
260 cache
->memusage
+= procmsg_msginfo_memusage(msginfo
);
265 g_hash_table_thaw(cache
->msgnum_table
);
266 msgcache_destroy(cache
);
270 cache
->last_access
= time(NULL
);
271 g_hash_table_thaw(cache
->msgnum_table
);
273 debug_print("done. (%d items read)\n", g_hash_table_size(cache
->msgnum_table
));
274 debug_print("Cache size: %d messages, %d byte\n", g_hash_table_size(cache
->msgnum_table
), cache
->memusage
);
279 void msgcache_read_mark(MsgCache
*cache
, const gchar
*mark_file
)
283 MsgPermFlags perm_flags
;
287 if ((fp
= fopen(mark_file
, "rb")) == NULL
) {
288 debug_print("Mark file not found.\n");
290 } else if (fread(&ver
, sizeof(ver
), 1, fp
) != 1 || MARK_VERSION
!= ver
) {
291 debug_print("Mark version is different (%d != %d). "
292 "Discarding it.\n", ver
, MARK_VERSION
);
294 debug_print("\tReading message marks from %s...\n", mark_file
);
296 while (fread(&num
, sizeof(num
), 1, fp
) == 1) {
297 if (fread(&perm_flags
, sizeof(perm_flags
), 1, fp
) != 1) break;
299 msginfo
= g_hash_table_lookup(cache
->msgnum_table
, &num
);
301 msginfo
->flags
.perm_flags
= perm_flags
;
308 #define WRITE_CACHE_DATA_INT(n, fp) \
309 fwrite(&n, sizeof(n), 1, fp)
311 #define WRITE_CACHE_DATA(data, fp) \
315 if (data == NULL || (len = strlen(data)) == 0) { \
317 WRITE_CACHE_DATA_INT(len, fp); \
319 len = strlen(data); \
320 WRITE_CACHE_DATA_INT(len, fp); \
321 fwrite(data, len, 1, fp); \
325 void msgcache_write_cache(MsgInfo
*msginfo
, FILE *fp
)
327 MsgTmpFlags flags
= msginfo
->flags
.tmp_flags
& MSG_CACHED_FLAG_MASK
;
329 WRITE_CACHE_DATA_INT(msginfo
->msgnum
, fp
);
330 WRITE_CACHE_DATA_INT(msginfo
->size
, fp
);
331 WRITE_CACHE_DATA_INT(msginfo
->mtime
, fp
);
332 WRITE_CACHE_DATA_INT(msginfo
->date_t
, fp
);
333 WRITE_CACHE_DATA_INT(flags
, fp
);
335 WRITE_CACHE_DATA(msginfo
->fromname
, fp
);
337 WRITE_CACHE_DATA(msginfo
->date
, fp
);
338 WRITE_CACHE_DATA(msginfo
->from
, fp
);
339 WRITE_CACHE_DATA(msginfo
->to
, fp
);
340 WRITE_CACHE_DATA(msginfo
->cc
, fp
);
341 WRITE_CACHE_DATA(msginfo
->newsgroups
, fp
);
342 WRITE_CACHE_DATA(msginfo
->subject
, fp
);
343 WRITE_CACHE_DATA(msginfo
->msgid
, fp
);
344 WRITE_CACHE_DATA(msginfo
->inreplyto
, fp
);
345 WRITE_CACHE_DATA(msginfo
->references
, fp
);
346 WRITE_CACHE_DATA(msginfo
->xref
, fp
);
349 static void msgcache_write_flags(MsgInfo
*msginfo
, FILE *fp
)
351 MsgPermFlags flags
= msginfo
->flags
.perm_flags
;
353 WRITE_CACHE_DATA_INT(msginfo
->msgnum
, fp
);
354 WRITE_CACHE_DATA_INT(flags
, fp
);
363 static void msgcache_write_func(gpointer key
, gpointer value
, gpointer user_data
)
366 struct write_fps
*write_fps
;
368 msginfo
= (MsgInfo
*)value
;
369 write_fps
= user_data
;
371 msgcache_write_cache(msginfo
, write_fps
->cache_fp
);
372 msgcache_write_flags(msginfo
, write_fps
->mark_fp
);
375 gint
msgcache_write(const gchar
*cache_file
, const gchar
*mark_file
, MsgCache
*cache
)
378 struct write_fps write_fps
;
381 g_return_val_if_fail(cache_file
!= NULL
, -1);
382 g_return_val_if_fail(mark_file
!= NULL
, -1);
383 g_return_val_if_fail(cache
!= NULL
, -1);
385 debug_print("\tWriting message cache to %s and %s...\n", cache_file
, mark_file
);
387 if ((fp
= fopen(cache_file
, "wb")) == NULL
) {
388 FILE_OP_ERROR(cache_file
, "fopen");
391 if (change_file_mode_rw(fp
, cache_file
) < 0)
392 FILE_OP_ERROR(cache_file
, "chmod");
395 WRITE_CACHE_DATA_INT(ver
, fp
);
396 write_fps
.cache_fp
= fp
;
398 if ((fp
= fopen(mark_file
, "wb")) == NULL
) {
399 FILE_OP_ERROR(mark_file
, "fopen");
400 fclose(write_fps
.cache_fp
);
405 WRITE_CACHE_DATA_INT(ver
, fp
);
406 write_fps
.mark_fp
= fp
;
408 g_hash_table_foreach(cache
->msgnum_table
, msgcache_write_func
, (gpointer
)&write_fps
);
410 fclose(write_fps
.cache_fp
);
411 fclose(write_fps
.mark_fp
);
413 cache
->last_access
= time(NULL
);
415 debug_print("done.\n");
419 MsgInfo
*msgcache_get_msg(MsgCache
*cache
, guint num
)
423 g_return_val_if_fail(cache
!= NULL
, NULL
);
425 msginfo
= g_hash_table_lookup(cache
->msgnum_table
, &num
);
428 cache
->last_access
= time(NULL
);
430 return procmsg_msginfo_new_ref(msginfo
);
433 MsgInfo
*msgcache_get_msg_by_id(MsgCache
*cache
, const gchar
*msgid
)
437 g_return_val_if_fail(cache
!= NULL
, NULL
);
439 msginfo
= g_hash_table_lookup(cache
->msgid_table
, msgid
);
442 cache
->last_access
= time(NULL
);
444 return procmsg_msginfo_new_ref(msginfo
);
447 static void msgcache_get_msg_list_func(gpointer key
, gpointer value
, gpointer user_data
)
449 GSList
**listptr
= user_data
;
450 MsgInfo
*msginfo
= value
;
452 *listptr
= g_slist_prepend(*listptr
, procmsg_msginfo_new_ref(msginfo
));
455 GSList
*msgcache_get_msg_list(MsgCache
*cache
)
457 GSList
*msg_list
= NULL
;
459 g_return_val_if_fail(cache
!= NULL
, NULL
);
461 g_hash_table_foreach((GHashTable
*)cache
->msgnum_table
, msgcache_get_msg_list_func
, (gpointer
)&msg_list
);
462 cache
->last_access
= time(NULL
);
464 msg_list
= g_slist_reverse(msg_list
);
469 time_t msgcache_get_last_access_time(MsgCache
*cache
)
471 g_return_val_if_fail(cache
!= NULL
, 0);
473 return cache
->last_access
;
476 gint
msgcache_get_memory_usage(MsgCache
*cache
)
478 g_return_val_if_fail(cache
!= NULL
, 0);
480 return cache
->memusage
;