summaryrefslogtreecommitdiff
path: root/libsylph/virtual.c
diff options
context:
space:
mode:
authorSimeon Simeonov2018-02-26 11:23:00 +0100
committerSimeon Simeonov2018-02-26 11:23:00 +0100
commit0b3cbf57875fd692e4ba0b336fefa4bee1ed00dc (patch)
treeaf916a30553c78ce9d4f2d8658656a175c5b6921 /libsylph/virtual.c
Initial commit for sylpheed 3.7.0
Diffstat (limited to 'libsylph/virtual.c')
-rw-r--r--libsylph/virtual.c603
1 files changed, 603 insertions, 0 deletions
diff --git a/libsylph/virtual.c b/libsylph/virtual.c
new file mode 100644
index 0000000..ead73ee
--- /dev/null
+++ b/libsylph/virtual.c
@@ -0,0 +1,603 @@
1/*
2 * LibSylph -- E-Mail client library
3 * Copyright (C) 1999-2010 Hiroyuki Yamamoto
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library 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 GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#ifdef HAVE_CONFIG_H
21# include "config.h"
22#endif
23
24#include "defs.h"
25
26#include <glib.h>
27#include <glib/gi18n.h>
28#include <dirent.h>
29#include <sys/stat.h>
30#include <time.h>
31#include <unistd.h>
32#include <string.h>
33#include <errno.h>
34
35#undef MEASURE_TIME
36
37#include "folder.h"
38#include "virtual.h"
39#include "mh.h"
40#include "procmsg.h"
41#include "procheader.h"
42#include "filter.h"
43#include "utils.h"
44
45typedef struct _VirtualSearchInfo VirtualSearchInfo;
46typedef struct _SearchCacheInfo SearchCacheInfo;
47
48struct _VirtualSearchInfo {
49 FilterRule *rule;
50 GSList *mlist;
51 GHashTable *search_cache_table;
52 FILE *fp;
53 gboolean requires_full_headers;
54 gboolean exclude_trash;
55};
56
57struct _SearchCacheInfo {
58 FolderItem *folder;
59 guint msgnum;
60 off_t size;
61 time_t mtime;
62 MsgFlags flags;
63};
64
65enum
66{
67 SCACHE_NOT_EXIST = 0,
68 SCACHE_MATCHED = 1,
69 SCACHE_NOT_MATCHED = 2
70};
71
72static void virtual_folder_init (Folder *folder,
73 const gchar *name,
74 const gchar *path);
75
76static GHashTable *virtual_read_search_cache
77 (FolderItem *item);
78static void virtual_write_search_cache (FILE *fp,
79 FolderItem *item,
80 MsgInfo *msginfo,
81 gint matched);
82
83static GSList *virtual_search_folder (VirtualSearchInfo *info,
84 FolderItem *item);
85static gboolean virtual_search_recursive_func
86 (GNode *node,
87 gpointer data);
88
89static Folder *virtual_folder_new (const gchar *name,
90 const gchar *path);
91static void virtual_folder_destroy (Folder *folder);
92
93static GSList *virtual_get_msg_list (Folder *folder,
94 FolderItem *item,
95 gboolean use_cache);
96static gchar *virtual_fetch_msg (Folder *folder,
97 FolderItem *item,
98 gint num);
99static MsgInfo *virtual_get_msginfo (Folder *folder,
100 FolderItem *item,
101 gint num);
102static gint virtual_close (Folder *folder,
103 FolderItem *item);
104
105static gint virtual_scan_folder (Folder *folder,
106 FolderItem *item);
107
108static gint virtual_rename_folder (Folder *folder,
109 FolderItem *item,
110 const gchar *name);
111static gint virtual_move_folder (Folder *folder,
112 FolderItem *item,
113 FolderItem *new_parent);
114static gint virtual_remove_folder (Folder *folder,
115 FolderItem *item);
116
117static FolderClass virtual_class =
118{
119 F_VIRTUAL,
120
121 virtual_folder_new,
122 virtual_folder_destroy,
123
124 NULL,
125 NULL,
126
127 virtual_get_msg_list,
128 NULL,
129 virtual_fetch_msg,
130 virtual_get_msginfo,
131 NULL,
132 NULL,
133 NULL,
134 NULL,
135 NULL,
136 NULL,
137 NULL,
138 NULL,
139 NULL,
140 NULL,
141 NULL,
142 NULL,
143 virtual_close,
144 virtual_scan_folder,
145
146 NULL,
147 virtual_rename_folder,
148 virtual_move_folder,
149 virtual_remove_folder,
150};
151
152
153FolderClass *virtual_get_class(void)
154{
155 return &virtual_class;
156}
157
158static Folder *virtual_folder_new(const gchar *name, const gchar *path)
159{
160 Folder *folder;
161
162 folder = (Folder *)g_new0(VirtualFolder, 1);
163 virtual_folder_init(folder, name, path);
164
165 return folder;
166}
167
168static void virtual_folder_destroy(Folder *folder)
169{
170 folder_local_folder_destroy(LOCAL_FOLDER(folder));
171}
172
173static void virtual_folder_init(Folder *folder, const gchar *name,
174 const gchar *path)
175{
176 folder->klass = virtual_get_class();
177 folder_local_folder_init(folder, name, path);
178}
179
180guint sinfo_hash(gconstpointer key)
181{
182 const SearchCacheInfo *sinfo = key;
183 guint h;
184
185 h = (guint)sinfo->folder;
186 h ^= sinfo->msgnum;
187 h ^= (guint)sinfo->size;
188 h ^= (guint)sinfo->mtime;
189 /* h ^= (guint)sinfo->flags.tmp_flags; */
190 h ^= (guint)sinfo->flags.perm_flags;
191
192 /* g_print("path: %s, n = %u, hash = %u\n",
193 sinfo->folder->path, sinfo->msgnum, h); */
194
195 return h;
196}
197
198gint sinfo_equal(gconstpointer v, gconstpointer v2)
199{
200 const SearchCacheInfo *s1 = v;
201 const SearchCacheInfo *s2 = v2;
202
203 return (s1->folder == s2->folder && s1->msgnum == s2->msgnum &&
204 s1->size == s2->size && s1->mtime == s2->mtime &&
205 /* s1->flags.tmp_flags == s2->flags.tmp_flags && */
206 s1->flags.perm_flags == s2->flags.perm_flags);
207}
208
209#define READ_CACHE_DATA_INT(n, fp) \
210{ \
211 guint32 idata; \
212 \
213 if (fread(&idata, sizeof(idata), 1, fp) != 1) { \
214 g_warning("Cache data is corrupted\n"); \
215 fclose(fp); \
216 return table; \
217 } else \
218 n = idata; \
219}
220
221static GHashTable *virtual_read_search_cache(FolderItem *item)
222{
223 GHashTable *table;
224 gchar *path, *file;
225 FILE *fp;
226 gchar *id;
227 gint count = 0;
228
229 g_return_val_if_fail(item != NULL, NULL);
230
231 path = folder_item_get_path(item);
232 file = g_strconcat(path, G_DIR_SEPARATOR_S, SEARCH_CACHE, NULL);
233 debug_print("reading search cache: %s\n", file);
234 fp = procmsg_open_data_file(file, SEARCH_CACHE_VERSION, DATA_READ,
235 NULL, 0);
236 g_free(file);
237 g_free(path);
238 if (!fp)
239 return NULL;
240
241 table = g_hash_table_new(sinfo_hash, sinfo_equal);
242
243 while (procmsg_read_cache_data_str(fp, &id) == 0) {
244 FolderItem *folder;
245 guint32 msgnum;
246 off_t size;
247 time_t mtime;
248 MsgFlags flags;
249 gint matched;
250 SearchCacheInfo *sinfo;
251
252 folder = folder_find_item_from_identifier(id);
253 g_free(id);
254
255 while (fread(&msgnum, sizeof(msgnum), 1, fp) == 1) {
256 if (msgnum == 0)
257 break;
258
259 READ_CACHE_DATA_INT(size, fp);
260 READ_CACHE_DATA_INT(mtime, fp);
261 READ_CACHE_DATA_INT(flags.tmp_flags, fp);
262 READ_CACHE_DATA_INT(flags.perm_flags, fp);
263 READ_CACHE_DATA_INT(matched, fp);
264
265 if (folder) {
266 sinfo = g_new(SearchCacheInfo, 1);
267 sinfo->folder = folder;
268 sinfo->msgnum = msgnum;
269 sinfo->size = size;
270 sinfo->mtime = mtime;
271 sinfo->flags = flags;
272 g_hash_table_insert(table, sinfo,
273 GINT_TO_POINTER(matched));
274 ++count;
275 }
276 }
277 }
278
279 debug_print("%d cache items read.\n", count);
280
281 fclose(fp);
282 return table;
283}
284
285static void virtual_write_search_cache(FILE *fp, FolderItem *item,
286 MsgInfo *msginfo, gint matched)
287{
288 if (!item && !msginfo) {
289 WRITE_CACHE_DATA_INT(0, fp);
290 return;
291 }
292
293 if (item) {
294 gchar *id;
295
296 id = folder_item_get_identifier(item);
297 if (id) {
298 WRITE_CACHE_DATA(id, fp);
299 g_free(id);
300 }
301 }
302
303 if (msginfo) {
304 WRITE_CACHE_DATA_INT(msginfo->msgnum, fp);
305 WRITE_CACHE_DATA_INT(msginfo->size, fp);
306 WRITE_CACHE_DATA_INT(msginfo->mtime, fp);
307 WRITE_CACHE_DATA_INT
308 ((msginfo->flags.tmp_flags & MSG_CACHED_FLAG_MASK), fp);
309 WRITE_CACHE_DATA_INT(msginfo->flags.perm_flags, fp);
310 WRITE_CACHE_DATA_INT(matched, fp);
311 }
312}
313
314static void search_cache_free_func(gpointer key, gpointer value, gpointer data)
315{
316 g_free(key);
317}
318
319static void virtual_search_cache_free(GHashTable *table)
320{
321 if (table) {
322 g_hash_table_foreach(table, search_cache_free_func, NULL);
323 g_hash_table_destroy(table);
324 }
325}
326
327static GSList *virtual_search_folder(VirtualSearchInfo *info, FolderItem *item)
328{
329 GSList *match_list = NULL;
330 GSList *mlist;
331 GSList *cur;
332 FilterInfo fltinfo;
333 gint count = 1, total, ncachehit = 0;
334 GTimeVal tv_prev, tv_cur;
335
336 g_return_val_if_fail(info != NULL, NULL);
337 g_return_val_if_fail(info->rule != NULL, NULL);
338 g_return_val_if_fail(item != NULL, NULL);
339 g_return_val_if_fail(item->path != NULL, NULL);
340
341 /* prevent circular reference */
342 if (item->stype == F_VIRTUAL)
343 return NULL;
344
345 g_get_current_time(&tv_prev);
346 status_print(_("Searching %s ..."), item->path);
347
348 mlist = folder_item_get_msg_list(item, TRUE);
349 total = g_slist_length(mlist);
350
351 memset(&fltinfo, 0, sizeof(FilterInfo));
352
353 debug_print("start query search: %s\n", item->path);
354
355 virtual_write_search_cache(info->fp, item, NULL, 0);
356
357 for (cur = mlist; cur != NULL; cur = cur->next) {
358 MsgInfo *msginfo = (MsgInfo *)cur->data;
359 GSList *hlist;
360
361 g_get_current_time(&tv_cur);
362 if (tv_cur.tv_sec > tv_prev.tv_sec ||
363 tv_cur.tv_usec - tv_prev.tv_usec >
364 PROGRESS_UPDATE_INTERVAL * 1000) {
365 status_print(_("Searching %s (%d / %d)..."),
366 item->path, count, total);
367 tv_prev = tv_cur;
368 }
369 ++count;
370
371 if (info->search_cache_table) {
372 gint matched;
373 SearchCacheInfo sinfo;
374
375 sinfo.folder = item;
376 sinfo.msgnum = msginfo->msgnum;
377 sinfo.size = msginfo->size;
378 sinfo.mtime = msginfo->mtime;
379 sinfo.flags = msginfo->flags;
380
381 matched = (gint)g_hash_table_lookup
382 (info->search_cache_table, &sinfo);
383 if (matched == SCACHE_MATCHED) {
384 match_list = g_slist_prepend
385 (match_list, msginfo);
386 cur->data = NULL;
387 virtual_write_search_cache(info->fp, NULL,
388 msginfo, matched);
389 ++ncachehit;
390 continue;
391 } else if (matched == SCACHE_NOT_MATCHED) {
392 virtual_write_search_cache(info->fp, NULL,
393 msginfo, matched);
394 ++ncachehit;
395 continue;
396 }
397 }
398
399 fltinfo.flags = msginfo->flags;
400 if (info->requires_full_headers) {
401 gchar *file;
402
403 file = procmsg_get_message_file(msginfo);
404 hlist = procheader_get_header_list_from_file(file);
405 g_free(file);
406 } else
407 hlist = procheader_get_header_list_from_msginfo
408 (msginfo);
409 if (!hlist)
410 continue;
411
412 if (filter_match_rule(info->rule, msginfo, hlist, &fltinfo)) {
413 match_list = g_slist_prepend(match_list, msginfo);
414 cur->data = NULL;
415 virtual_write_search_cache(info->fp, NULL, msginfo,
416 SCACHE_MATCHED);
417 } else {
418 virtual_write_search_cache(info->fp, NULL, msginfo,
419 SCACHE_NOT_MATCHED);
420 }
421
422 procheader_header_list_destroy(hlist);
423 }
424
425 debug_print("%d cache hits (%d total)\n", ncachehit, total);
426
427 virtual_write_search_cache(info->fp, NULL, NULL, 0);
428 procmsg_msg_list_free(mlist);
429
430 return g_slist_reverse(match_list);
431}
432
433static gboolean virtual_search_recursive_func(GNode *node, gpointer data)
434{
435 VirtualSearchInfo *info = (VirtualSearchInfo *)data;
436 FolderItem *item;
437 GSList *mlist;
438
439 g_return_val_if_fail(node->data != NULL, FALSE);
440
441 item = FOLDER_ITEM(node->data);
442
443 if (!item->path)
444 return FALSE;
445 if (info->exclude_trash && item->stype == F_TRASH)
446 return FALSE;
447
448 mlist = virtual_search_folder(info, item);
449 info->mlist = g_slist_concat(info->mlist, mlist);
450
451 return FALSE;
452}
453
454static GSList *virtual_get_msg_list(Folder *folder, FolderItem *item,
455 gboolean use_cache)
456{
457 GSList *mlist = NULL;
458 GSList *flist;
459 GSList *cur;
460 FilterRule *rule;
461 gchar *path;
462 gchar *rule_file;
463 gchar *cache_file;
464 FolderItem *target;
465 gint new = 0, unread = 0, total = 0;
466 VirtualSearchInfo info;
467
468 g_return_val_if_fail(item != NULL, NULL);
469 g_return_val_if_fail(item->stype == F_VIRTUAL, NULL);
470
471 path = folder_item_get_path(item);
472 rule_file = g_strconcat(path, G_DIR_SEPARATOR_S, "filter.xml", NULL);
473 flist = filter_read_file(rule_file);
474 g_free(rule_file);
475
476 g_free(path);
477
478 if (!flist) {
479 g_warning("filter rule not found\n");
480 return NULL;
481 }
482
483 rule = (FilterRule *)flist->data;
484 target = folder_find_item_from_identifier(rule->target_folder);
485
486 if (!target || target == item) {
487 g_warning("invalid target folder\n");
488 goto finish;
489 }
490
491 info.rule = rule;
492 info.mlist = NULL;
493 if (use_cache)
494 info.search_cache_table = virtual_read_search_cache(item);
495 else
496 info.search_cache_table = NULL;
497
498 path = folder_item_get_path(item);
499 cache_file = g_strconcat(path, G_DIR_SEPARATOR_S, SEARCH_CACHE, NULL);
500 info.fp = procmsg_open_data_file(cache_file, SEARCH_CACHE_VERSION,
501 DATA_WRITE, NULL, 0);
502 g_free(cache_file);
503 g_free(path);
504 if (!info.fp)
505 goto finish;
506
507 info.requires_full_headers =
508 filter_rule_requires_full_headers(rule);
509
510 if (rule->recursive) {
511 if (target->stype == F_TRASH)
512 info.exclude_trash = FALSE;
513 else
514 info.exclude_trash = TRUE;
515 } else
516 info.exclude_trash = FALSE;
517
518 if (rule->recursive) {
519 g_node_traverse(target->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
520 virtual_search_recursive_func, &info);
521 mlist = info.mlist;
522 } else
523 mlist = virtual_search_folder(&info, target);
524
525 fclose(info.fp);
526 virtual_search_cache_free(info.search_cache_table);
527
528 for (cur = mlist; cur != NULL; cur = cur->next) {
529 MsgInfo *msginfo = (MsgInfo *)cur->data;
530
531 if (MSG_IS_NEW(msginfo->flags))
532 ++new;
533 if (MSG_IS_UNREAD(msginfo->flags))
534 ++unread;
535 ++total;
536 }
537
538 item->new = new;
539 item->unread = unread;
540 item->total = total;
541 item->updated = TRUE;
542
543finish:
544 filter_rule_list_free(flist);
545 return mlist;
546}
547
548static gchar *virtual_fetch_msg(Folder *folder, FolderItem *item, gint num)
549{
550 return NULL;
551}
552
553static MsgInfo *virtual_get_msginfo(Folder *folder, FolderItem *item, gint num)
554{
555 return NULL;
556}
557
558static gint virtual_close(Folder *folder, FolderItem *item)
559{
560 return 0;
561}
562
563static gint virtual_scan_folder(Folder *folder, FolderItem *item)
564{
565 return 0;
566}
567
568static gint virtual_rename_folder(Folder *folder, FolderItem *item,
569 const gchar *name)
570{
571 g_return_val_if_fail(item != NULL, -1);
572 g_return_val_if_fail(item->stype == F_VIRTUAL, -1);
573
574 return mh_get_class()->rename_folder(folder, item, name);
575}
576
577static gint virtual_move_folder(Folder *folder, FolderItem *item,
578 FolderItem *new_parent)
579{
580 g_return_val_if_fail(item != NULL, -1);
581 g_return_val_if_fail(item->stype == F_VIRTUAL, -1);
582
583 return mh_get_class()->move_folder(folder, item, new_parent);
584}
585
586static gint virtual_remove_folder(Folder *folder, FolderItem *item)
587{
588 gchar *path;
589
590 g_return_val_if_fail(item != NULL, -1);
591 g_return_val_if_fail(item->stype == F_VIRTUAL, -1);
592
593 path = folder_item_get_path(item);
594 if (remove_dir_recursive(path) < 0) {
595 g_warning("can't remove directory '%s'\n", path);
596 g_free(path);
597 return -1;
598 }
599
600 g_free(path);
601 folder_item_remove(item);
602 return 0;
603}