2005-06-15 [colin] 1.9.11cvs74
[claws.git] / src / folder.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2005 Hiroyuki Yamamoto
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 <stdio.h>
29 #include <string.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <unistd.h>
33 #include <stdlib.h>
34
35 #include "folder.h"
36 #include "session.h"
37 #include "inc.h"
38 #include "imap.h"
39 #include "news.h"
40 #include "mh.h"
41 #include "utils.h"
42 #include "xml.h"
43 #include "codeconv.h"
44 #include "prefs_gtk.h"
45 #include "account.h"
46 #include "filtering.h"
47 #include "procheader.h"
48 #include "hooks.h"
49 #include "log.h"
50 #include "folder_item_prefs.h"
51 #include "remotefolder.h"
52 #include "partial_download.h"
53 #include "statusbar.h"
54
55 /* Dependecies to be removed ?! */
56 #include "prefs_common.h"
57 #include "prefs_account.h"
58
59 static GList *folder_list = NULL;
60 static GSList *class_list = NULL;
61 static GSList *folder_unloaded_list = NULL;
62
63 void folder_init                (Folder         *folder,
64                                  const gchar    *name);
65
66 static gchar *folder_get_list_path      (void);
67 static GNode *folder_get_xml_node       (Folder         *folder);
68 static Folder *folder_get_from_xml      (GNode          *node);
69 static void folder_update_op_count_rec  (GNode          *node);
70
71
72 static void folder_get_persist_prefs_recursive
73                                         (GNode *node, GHashTable *pptable);
74 static gboolean persist_prefs_free      (gpointer key, gpointer val, gpointer data);
75 void folder_item_read_cache             (FolderItem *item);
76 void folder_item_free_cache             (FolderItem *item);
77 gint folder_item_scan_full              (FolderItem *item, gboolean filtering);
78 static void folder_item_update_with_msg (FolderItem *item, FolderItemUpdateFlags update_flags,
79                                          MsgInfo *msg);
80
81 void folder_system_init(void)
82 {
83         folder_register_class(mh_get_class());
84         folder_register_class(imap_get_class());
85         folder_register_class(news_get_class());
86 }
87
88 GSList *folder_get_class_list(void)
89 {
90         return class_list;
91 }
92
93 void folder_register_class(FolderClass *klass)
94 {
95         GSList *xmllist, *cur;
96
97         debug_print("registering folder class %s\n", klass->idstr);
98
99         class_list = g_slist_append(class_list, klass);
100
101         xmllist = g_slist_copy(folder_unloaded_list);
102         for (cur = xmllist; cur != NULL; cur = g_slist_next(cur)) {
103                 GNode *node = (GNode *) cur->data;
104                 XMLNode *xmlnode = (XMLNode *) node->data;
105                 GList *cur = xmlnode->tag->attr;
106
107                 for (; cur != NULL; cur = g_list_next(cur)) {
108                         XMLAttr *attr = (XMLAttr *) cur->data;
109
110                         if (!attr || !attr->name || !attr->value) continue;
111                         if (!strcmp(attr->name, "type") && !strcmp(attr->value, klass->idstr)) {
112                                 Folder *folder;
113
114                                 folder = folder_get_from_xml(node);
115                                 folder_add(folder);
116                                 folder_unloaded_list = g_slist_remove(folder_unloaded_list, node);
117
118                                 cur = NULL;
119                                 continue;
120                         }
121                 }
122         }
123         g_slist_free(xmllist);
124 }
125
126 void folder_unregister_class(FolderClass *klass)
127 {
128         GList *folderlist, *cur;
129
130         debug_print("unregistering folder class %s\n", klass->idstr);
131
132         class_list = g_slist_remove(class_list, klass);
133
134         folderlist = g_list_copy(folder_get_list());
135         for (cur = folderlist; cur != NULL; cur = g_list_next(cur)) {
136                 Folder *folder = (Folder *) cur->data;
137
138                 if (folder->klass == klass) {
139                         GNode *xmlnode = folder_get_xml_node(folder);
140                         folder_unloaded_list = g_slist_append(folder_unloaded_list, xmlnode);
141                         folder_destroy(folder);
142                 }
143         }
144         g_list_free(folderlist);
145 }
146
147 Folder *folder_new(FolderClass *klass, const gchar *name, const gchar *path)
148 {
149         Folder *folder = NULL;
150         FolderItem *item;
151
152         g_return_val_if_fail(klass != NULL, NULL);
153
154         name = name ? name : path;
155         folder = klass->new_folder(name, path);
156
157         /* Create root folder item */
158         item = folder_item_new(folder, name, NULL);
159         item->folder = folder;
160         folder->node = item->node = g_node_new(item);
161         folder->data = NULL;
162
163         return folder;
164 }
165
166 void folder_init(Folder *folder, const gchar *name)
167 {
168         g_return_if_fail(folder != NULL);
169
170         folder_set_name(folder, name);
171
172         /* Init folder data */
173         folder->account = NULL;
174         folder->sort = 0;
175         folder->inbox = NULL;
176         folder->outbox = NULL;
177         folder->draft = NULL;
178         folder->queue = NULL;
179         folder->trash = NULL;
180 }
181
182 void folder_destroy(Folder *folder)
183 {
184         g_return_if_fail(folder != NULL);
185         g_return_if_fail(folder->klass->destroy_folder != NULL);
186
187         folder_remove(folder);
188
189         folder_tree_destroy(folder);
190
191         folder->klass->destroy_folder(folder);
192
193         g_free(folder->name);
194         g_free(folder);
195 }
196
197 void folder_set_xml(Folder *folder, XMLTag *tag)
198 {
199         GList *cur;
200         FolderItem *rootitem = NULL;
201
202         if ((folder->node != NULL) && (folder->node->data != NULL))
203                 rootitem = (FolderItem *) folder->node->data;
204
205         for (cur = tag->attr; cur != NULL; cur = g_list_next(cur)) {
206                 XMLAttr *attr = (XMLAttr *) cur->data;
207
208                 if (!attr || !attr->name || !attr->value) continue;
209                 if (!strcmp(attr->name, "name")) {
210                         if (folder->name != NULL)
211                                 g_free(folder->name);
212                         folder->name = g_strdup(attr->value);
213                         if (rootitem != NULL) {
214                                 if (rootitem->name != NULL)
215                                         g_free(rootitem->name);
216                                 rootitem->name = g_strdup(attr->value);
217                         }
218                 } else if (!strcmp(attr->name, "account_id")) {
219                         PrefsAccount *account;
220
221                         account = account_find_from_id(atoi(attr->value));
222                         if (!account)
223                                 g_warning("account_id: %s not found\n", attr->value);
224                         else {
225                                 folder->account = account;
226                                 account->folder = folder;
227                         }
228                 } else if (!strcmp(attr->name, "collapsed")) {
229                         if (rootitem != NULL)
230                                 rootitem->collapsed = *attr->value == '1' ? TRUE : FALSE;
231                 } else if (!strcmp(attr->name, "sort")) {
232                         folder->sort = atoi(attr->value);
233                 }
234         }
235 }
236
237 XMLTag *folder_get_xml(Folder *folder)
238 {
239         XMLTag *tag;
240
241         tag = xml_tag_new("folder");
242
243         if (folder->name)
244                 xml_tag_add_attr(tag, xml_attr_new("name", folder->name));
245         if (folder->account)
246                 xml_tag_add_attr(tag, xml_attr_new_int("account_id", folder->account->account_id));
247         if (folder->node && folder->node->data) {
248                 FolderItem *rootitem = (FolderItem *) folder->node->data;
249
250                 xml_tag_add_attr(tag, xml_attr_new("collapsed", rootitem->collapsed ? "1" : "0"));
251         }
252         xml_tag_add_attr(tag, xml_attr_new_int("sort", folder->sort));
253
254         return tag;
255 }
256
257 FolderItem *folder_item_new(Folder *folder, const gchar *name, const gchar *path)
258 {
259         FolderItem *item = NULL;
260
261         if (folder->klass->item_new) {
262                 item = folder->klass->item_new(folder);
263         } else {
264                 item = g_new0(FolderItem, 1);
265         }
266
267         g_return_val_if_fail(item != NULL, NULL);
268
269         item->stype = F_NORMAL;
270         item->name = g_strdup(name);
271         item->path = g_strdup(path);
272         item->mtime = 0;
273         item->new_msgs = 0;
274         item->unread_msgs = 0;
275         item->unreadmarked_msgs = 0;
276         item->total_msgs = 0;
277         item->last_num = -1;
278         item->cache = NULL;
279         item->no_sub = FALSE;
280         item->no_select = FALSE;
281         item->collapsed = FALSE;
282         item->thread_collapsed = FALSE;
283         item->threaded  = TRUE;
284         item->ret_rcpt  = FALSE;
285         item->opened    = FALSE;
286         item->node = g_node_new(item);
287         item->folder = NULL;
288         item->account = NULL;
289         item->apply_sub = FALSE;
290         item->mark_queue = NULL;
291         item->data = NULL;
292
293         item->prefs = folder_item_prefs_new();
294
295         return item;
296 }
297
298 void folder_item_append(FolderItem *parent, FolderItem *item)
299 {
300         g_return_if_fail(parent != NULL);
301         g_return_if_fail(parent->folder != NULL);
302         g_return_if_fail(parent->node != NULL);
303         g_return_if_fail(item != NULL);
304
305         item->folder = parent->folder;
306         g_node_append(parent->node, item->node);
307 }
308
309 static gboolean folder_item_remove_func(GNode *node, gpointer data)
310 {
311         FolderItem *item = FOLDER_ITEM(node->data);
312         FolderUpdateData hookdata;
313
314         if (item->cache != NULL) {
315                 msgcache_destroy(item->cache);
316                 item->cache = NULL;
317         }
318
319         hookdata.folder = item->folder;
320         hookdata.update_flags = FOLDER_TREE_CHANGED | FOLDER_REMOVE_FOLDERITEM;
321         hookdata.item = item;
322         hooks_invoke(FOLDER_UPDATE_HOOKLIST, &hookdata);
323
324         folder_item_destroy(item);
325
326         return FALSE;
327 }
328
329 void folder_item_remove(FolderItem *item)
330 {
331         GNode *node;
332
333         g_return_if_fail(item != NULL);
334         g_return_if_fail(item->folder != NULL);
335         g_return_if_fail(item->node != NULL);
336
337         node = item->node;
338
339         if (item->folder->node == node)
340                 item->folder->node = NULL;
341
342         g_node_traverse(node, G_POST_ORDER, G_TRAVERSE_ALL, -1,
343                         folder_item_remove_func, NULL);
344         g_node_destroy(node);
345 }
346
347 void folder_item_remove_children(FolderItem *item)
348 {
349         GNode *node, *next;
350
351         g_return_if_fail(item != NULL);
352         g_return_if_fail(item->folder != NULL);
353         g_return_if_fail(item->node != NULL);
354
355         node = item->node->children;
356         while (node != NULL) {
357                 next = node->next;
358                 folder_item_remove(FOLDER_ITEM(node->data));
359                 node = next;
360         }
361 }
362
363 void folder_item_destroy(FolderItem *item)
364 {
365         Folder *folder;
366
367         g_return_if_fail(item != NULL);
368
369         folder = item->folder;
370         if (folder) {
371                 if (folder->inbox == item)
372                         folder->inbox = NULL;
373                 else if (folder->outbox == item)
374                         folder->outbox = NULL;
375                 else if (folder->draft == item)
376                         folder->draft = NULL;
377                 else if (folder->queue == item)
378                         folder->queue = NULL;
379                 else if (folder->trash == item)
380                         folder->trash = NULL;
381         }
382
383         if (item->cache)
384                 folder_item_free_cache(item);
385         if (item->prefs)
386                 folder_item_prefs_free(item->prefs);
387         g_free(item->name);
388         g_free(item->path);
389
390         if (item->folder != NULL) {
391                 if(item->folder->klass->item_destroy) {
392                         item->folder->klass->item_destroy(item->folder, item);
393                 } else {
394                         g_free(item);
395                 }
396         }
397 }
398
399 FolderItem *folder_item_parent(FolderItem *item)
400 {
401         g_return_val_if_fail(item != NULL, NULL);
402         g_return_val_if_fail(item->node != NULL, NULL);
403
404         if (item->node->parent == NULL)
405                 return NULL;
406         return (FolderItem *) item->node->parent->data;
407 }
408
409 void folder_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag)
410 {
411         GList *cur;
412
413         for (cur = tag->attr; cur != NULL; cur = g_list_next(cur)) {
414                 XMLAttr *attr = (XMLAttr *) cur->data;
415
416                 if (!attr || !attr->name || !attr->value) continue;
417                 if (!strcmp(attr->name, "type")) {
418                         if (!g_ascii_strcasecmp(attr->value, "normal"))
419                                 item->stype = F_NORMAL;
420                         else if (!g_ascii_strcasecmp(attr->value, "inbox"))
421                                 item->stype = F_INBOX;
422                         else if (!g_ascii_strcasecmp(attr->value, "outbox"))
423                                 item->stype = F_OUTBOX;
424                         else if (!g_ascii_strcasecmp(attr->value, "draft"))
425                                 item->stype = F_DRAFT;
426                         else if (!g_ascii_strcasecmp(attr->value, "queue"))
427                                 item->stype = F_QUEUE;
428                         else if (!g_ascii_strcasecmp(attr->value, "trash"))
429                                 item->stype = F_TRASH;
430                 } else if (!strcmp(attr->name, "name")) {
431                         if (item->name != NULL)
432                                 g_free(item->name);
433                         item->name = g_strdup(attr->value);
434                 } else if (!strcmp(attr->name, "path")) {
435                         if (item->path != NULL)
436                                 g_free(item->path);
437                         item->path = g_strdup(attr->value);
438                 } else if (!strcmp(attr->name, "mtime"))
439                         item->mtime = strtoul(attr->value, NULL, 10);
440                 else if (!strcmp(attr->name, "new"))
441                         item->new_msgs = atoi(attr->value);
442                 else if (!strcmp(attr->name, "unread"))
443                         item->unread_msgs = atoi(attr->value);
444                 else if (!strcmp(attr->name, "unreadmarked"))
445                         item->unreadmarked_msgs = atoi(attr->value);
446                 else if (!strcmp(attr->name, "total"))
447                         item->total_msgs = atoi(attr->value);
448                 else if (!strcmp(attr->name, "no_sub"))
449                         item->no_sub = *attr->value == '1' ? TRUE : FALSE;
450                 else if (!strcmp(attr->name, "no_select"))
451                         item->no_select = *attr->value == '1' ? TRUE : FALSE;
452                 else if (!strcmp(attr->name, "collapsed"))
453                         item->collapsed = *attr->value == '1' ? TRUE : FALSE;
454                 else if (!strcmp(attr->name, "thread_collapsed"))
455                         item->thread_collapsed =  *attr->value == '1' ? TRUE : FALSE;
456                 else if (!strcmp(attr->name, "threaded"))
457                         item->threaded =  *attr->value == '1' ? TRUE : FALSE;
458                 else if (!strcmp(attr->name, "hidereadmsgs"))
459                         item->hide_read_msgs =  *attr->value == '1' ? TRUE : FALSE;
460                 else if (!strcmp(attr->name, "reqretrcpt"))
461                         item->ret_rcpt =  *attr->value == '1' ? TRUE : FALSE;
462                 else if (!strcmp(attr->name, "sort_key")) {
463                         if (!strcmp(attr->value, "none"))
464                                 item->sort_key = SORT_BY_NONE;
465                         else if (!strcmp(attr->value, "number"))
466                                 item->sort_key = SORT_BY_NUMBER;
467                         else if (!strcmp(attr->value, "size"))
468                                 item->sort_key = SORT_BY_SIZE;
469                         else if (!strcmp(attr->value, "date"))
470                                 item->sort_key = SORT_BY_DATE;
471                         else if (!strcmp(attr->value, "from"))
472                                 item->sort_key = SORT_BY_FROM;
473                         else if (!strcmp(attr->value, "subject"))
474                                 item->sort_key = SORT_BY_SUBJECT;
475                         else if (!strcmp(attr->value, "score"))
476                                 item->sort_key = SORT_BY_SCORE;
477                         else if (!strcmp(attr->value, "label"))
478                                 item->sort_key = SORT_BY_LABEL;
479                         else if (!strcmp(attr->value, "mark"))
480                                 item->sort_key = SORT_BY_MARK;
481                         else if (!strcmp(attr->value, "unread"))
482                                 item->sort_key = SORT_BY_STATUS;
483                         else if (!strcmp(attr->value, "mime"))
484                                 item->sort_key = SORT_BY_MIME;
485                         else if (!strcmp(attr->value, "to"))
486                                 item->sort_key = SORT_BY_TO;
487                         else if (!strcmp(attr->value, "locked"))
488                                 item->sort_key = SORT_BY_LOCKED;
489                 } else if (!strcmp(attr->name, "sort_type")) {
490                         if (!strcmp(attr->value, "ascending"))
491                                 item->sort_type = SORT_ASCENDING;
492                         else
493                                 item->sort_type = SORT_DESCENDING;
494                 } else if (!strcmp(attr->name, "account_id")) {
495                         PrefsAccount *account;
496
497                         account = account_find_from_id(atoi(attr->value));
498                         if (!account)
499                                 g_warning("account_id: %s not found\n", attr->value);
500                         else
501                                 item->account = account;
502                 } else if (!strcmp(attr->name, "apply_sub"))
503                         item->apply_sub = *attr->value == '1' ? TRUE : FALSE;
504         }
505 }
506
507 XMLTag *folder_item_get_xml(Folder *folder, FolderItem *item)
508 {
509         static gchar *folder_item_stype_str[] = {"normal", "inbox", "outbox",
510                                                  "draft", "queue", "trash"};
511         static gchar *sort_key_str[] = {"none", "number", "size", "date",
512                                         "from", "subject", "score", "label",
513                                         "mark", "unread", "mime", "to", 
514                                         "locked"};
515         XMLTag *tag;
516         gchar *value;
517
518         tag = xml_tag_new("folderitem");
519
520         xml_tag_add_attr(tag, xml_attr_new("type", folder_item_stype_str[item->stype]));
521         if (item->name)
522                 xml_tag_add_attr(tag, xml_attr_new("name", item->name));
523         if (item->path)
524                 xml_tag_add_attr(tag, xml_attr_new("path", item->path));
525         if (item->no_sub)
526                 xml_tag_add_attr(tag, xml_attr_new("no_sub", "1"));
527         if (item->no_select)
528                 xml_tag_add_attr(tag, xml_attr_new("no_select", "1"));
529         xml_tag_add_attr(tag, xml_attr_new("collapsed", item->collapsed && item->node->children ? "1" : "0"));
530         xml_tag_add_attr(tag, xml_attr_new("thread_collapsed", item->thread_collapsed ? "1" : "0"));
531         xml_tag_add_attr(tag, xml_attr_new("threaded", item->threaded ? "1" : "0"));
532         xml_tag_add_attr(tag, xml_attr_new("hidereadmsgs", item->hide_read_msgs ? "1" : "0"));
533         if (item->ret_rcpt)
534                 xml_tag_add_attr(tag, xml_attr_new("reqretrcpt", "1"));
535
536         if (item->sort_key != SORT_BY_NONE) {
537                 xml_tag_add_attr(tag, xml_attr_new("sort_key", sort_key_str[item->sort_key]));
538                 xml_tag_add_attr(tag, xml_attr_new("sort_type", item->sort_type == SORT_ASCENDING ? "ascending" : "descending"));
539         }
540
541         value = g_strdup_printf("%ld", (unsigned long int) item->mtime);
542         xml_tag_add_attr(tag, xml_attr_new("mtime", value));
543         g_free(value);
544         xml_tag_add_attr(tag, xml_attr_new_int("new", item->new_msgs));
545         xml_tag_add_attr(tag, xml_attr_new_int("unread", item->unread_msgs));
546         xml_tag_add_attr(tag, xml_attr_new_int("unreadmarked", item->unreadmarked_msgs));
547         xml_tag_add_attr(tag, xml_attr_new_int("total", item->total_msgs));
548
549         if (item->account)
550                 xml_tag_add_attr(tag, xml_attr_new_int("account_id", item->account->account_id));
551         if (item->apply_sub)
552                 xml_tag_add_attr(tag, xml_attr_new("apply_sub", "1"));
553
554         return tag;
555 }
556
557 void folder_set_ui_func(Folder *folder, FolderUIFunc func, gpointer data)
558 {
559         g_return_if_fail(folder != NULL);
560
561         folder->ui_func = func;
562         folder->ui_func_data = data;
563 }
564
565 void folder_set_name(Folder *folder, const gchar *name)
566 {
567         g_return_if_fail(folder != NULL);
568
569         g_free(folder->name);
570         folder->name = name ? g_strdup(name) : NULL;
571         if (folder->node && folder->node->data) {
572                 FolderItem *item = (FolderItem *)folder->node->data;
573
574                 g_free(item->name);
575                 item->name = name ? g_strdup(name) : NULL;
576         }
577 }
578
579 void folder_set_sort(Folder *folder, guint sort)
580 {
581         g_return_if_fail(folder != NULL);
582
583         if (folder->sort != sort) {
584                 folder_remove(folder);
585                 folder->sort = sort;
586                 folder_add(folder);
587         }
588 }
589
590 gboolean folder_tree_destroy_func(GNode *node, gpointer data) {
591         FolderItem *item = (FolderItem *) node->data;
592
593         folder_item_destroy(item);
594         return FALSE;
595 }
596
597 void folder_tree_destroy(Folder *folder)
598 {
599         GNode *node;
600
601         g_return_if_fail(folder != NULL);
602
603         node = folder->node;
604         
605         prefs_filtering_clear_folder(folder);
606
607         if (node != NULL) {
608                 g_node_traverse(node, G_POST_ORDER, G_TRAVERSE_ALL, -1,
609                                 folder_tree_destroy_func, NULL);
610                 g_node_destroy(node);
611                 folder->node = NULL;
612         }
613 }
614
615 void folder_add(Folder *folder)
616 {
617         Folder *cur_folder;
618         GList *cur;
619         gint i;
620         FolderUpdateData hookdata;
621
622         g_return_if_fail(folder != NULL);
623
624         for (i = 0, cur = folder_list; cur != NULL; cur = cur->next, i++) {
625                 cur_folder = FOLDER(cur->data);
626                 if (cur_folder->sort < folder->sort)
627                         break;
628         }
629
630         folder_list = g_list_insert(folder_list, folder, i);
631
632         hookdata.folder = folder;
633         hookdata.update_flags = FOLDER_ADD_FOLDER;
634         hookdata.item = NULL;
635         hooks_invoke(FOLDER_UPDATE_HOOKLIST, &hookdata);
636 }
637
638 void folder_remove(Folder *folder)
639 {
640         FolderUpdateData hookdata;
641
642         g_return_if_fail(folder != NULL);
643
644         folder_list = g_list_remove(folder_list, folder);
645
646         hookdata.folder = folder;
647         hookdata.update_flags = FOLDER_REMOVE_FOLDER;
648         hookdata.item = NULL;
649         hooks_invoke(FOLDER_UPDATE_HOOKLIST, &hookdata);
650 }
651
652 GList *folder_get_list(void)
653 {
654         return folder_list;
655 }
656
657 gint folder_read_list(void)
658 {
659         GNode *node, *cur;
660         XMLNode *xmlnode;
661         gchar *path;
662
663         path = folder_get_list_path();
664         if (!is_file_exist(path)) return -1;
665         node = xml_parse_file(path);
666         if (!node) return -1;
667
668         xmlnode = node->data;
669         if (strcmp2(xmlnode->tag->tag, "folderlist") != 0) {
670                 g_warning("wrong folder list\n");
671                 xml_free_tree(node);
672                 return -1;
673         }
674
675         cur = node->children;
676         while (cur != NULL) {
677                 Folder *folder;
678
679                 folder = folder_get_from_xml(cur);
680                 if (folder != NULL)
681                         folder_add(folder);
682                 else
683                         folder_unloaded_list = g_slist_append(folder_unloaded_list,
684                                 (gpointer) xml_copy_tree(cur));
685                 cur = cur->next;
686         }
687
688         xml_free_tree(node);
689         if (folder_list)
690                 return 0;
691         else
692                 return -1;
693 }
694
695 void folder_write_list(void)
696 {
697         GList *list;
698         GSList *slist;
699         Folder *folder;
700         gchar *path;
701         PrefFile *pfile;
702         GNode *rootnode;
703         XMLNode *xmlnode;
704         XMLTag *tag;
705
706         path = folder_get_list_path();
707         if ((pfile = prefs_write_open(path)) == NULL) return;
708
709         xml_file_put_xml_decl(pfile->fp);
710         tag = xml_tag_new("folderlist");
711
712         xmlnode = xml_node_new(tag, NULL);
713
714         rootnode = g_node_new(xmlnode);
715
716         for (list = folder_list; list != NULL; list = list->next) {
717                 GNode *node;
718
719                 folder = list->data;
720                 node = folder_get_xml_node(folder);
721                 if (node != NULL)
722                         g_node_append(rootnode, node);
723         }
724
725         for (slist = folder_unloaded_list; slist != NULL; slist = g_slist_next(slist)) {
726                 GNode *node = (GNode *) slist->data;
727
728                 g_node_append(rootnode, (gpointer) xml_copy_tree(node));
729         }
730
731         xml_write_tree(rootnode, pfile->fp);
732
733         if (prefs_file_close(pfile) < 0)
734                 g_warning("failed to write folder list.\n");
735
736         xml_free_tree(rootnode);
737 }
738
739 gboolean folder_scan_tree_func(GNode *node, gpointer data)
740 {
741         GHashTable *pptable = (GHashTable *)data;
742         FolderItem *item = (FolderItem *)node->data;
743         
744         folder_item_restore_persist_prefs(item, pptable);
745         folder_item_scan_full(item, FALSE);
746
747         return FALSE;
748 }
749
750 void folder_scan_tree(Folder *folder)
751 {
752         GHashTable *pptable;
753         FolderUpdateData hookdata;
754         
755         if (!folder->klass->scan_tree)
756                 return;
757         
758         pptable = folder_persist_prefs_new(folder);
759
760         /*
761          * should be changed and tree update should be done without 
762          * destroying the tree first
763          */
764         folder_tree_destroy(folder);
765         folder->klass->scan_tree(folder);
766
767         hookdata.folder = folder;
768         hookdata.update_flags = FOLDER_TREE_CHANGED;
769         hookdata.item = NULL;
770         hooks_invoke(FOLDER_UPDATE_HOOKLIST, &hookdata);
771
772         g_node_traverse(folder->node, G_POST_ORDER, G_TRAVERSE_ALL, -1, folder_scan_tree_func, pptable);
773         folder_persist_prefs_free(pptable);
774
775         prefs_matcher_read_config();
776
777         folder_write_list();
778 }
779
780 FolderItem *folder_create_folder(FolderItem *parent, const gchar *name)
781 {
782         FolderItem *new_item;
783
784         new_item = parent->folder->klass->create_folder(parent->folder, parent, name);
785         if (new_item) {
786                 FolderUpdateData hookdata;
787
788                 new_item->cache = msgcache_new();
789
790                 hookdata.folder = new_item->folder;
791                 hookdata.update_flags = FOLDER_TREE_CHANGED | FOLDER_ADD_FOLDERITEM;
792                 hookdata.item = new_item;
793                 hooks_invoke(FOLDER_UPDATE_HOOKLIST, &hookdata);
794         }
795
796         return new_item;
797 }
798
799 gint folder_item_rename(FolderItem *item, gchar *newname)
800 {
801         gint retval;
802
803         g_return_val_if_fail(item != NULL, -1);
804         g_return_val_if_fail(newname != NULL, -1);
805
806         retval = item->folder->klass->rename_folder(item->folder, item, newname);
807
808         if (retval >= 0) {
809                 FolderItemUpdateData hookdata;
810
811                 hookdata.item = item;
812                 hookdata.update_flags = F_ITEM_UPDATE_NAME;
813                 hookdata.msg = NULL;
814                 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &hookdata);
815         }
816
817         return retval;
818 }
819
820 struct TotalMsgCount
821 {
822         guint new_msgs;
823         guint unread_msgs;
824         guint unreadmarked_msgs;
825         guint total_msgs;
826 };
827
828 struct FuncToAllFoldersData
829 {
830         FolderItemFunc  function;
831         gpointer        data;
832 };
833
834 static gboolean folder_func_to_all_folders_func(GNode *node, gpointer data)
835 {
836         FolderItem *item;
837         struct FuncToAllFoldersData *function_data = (struct FuncToAllFoldersData *) data;
838
839         g_return_val_if_fail(node->data != NULL, FALSE);
840
841         item = FOLDER_ITEM(node->data);
842         g_return_val_if_fail(item != NULL, FALSE);
843
844         function_data->function(item, function_data->data);
845
846         return FALSE;
847 }
848
849 void folder_func_to_all_folders(FolderItemFunc function, gpointer data)
850 {
851         GList *list;
852         Folder *folder;
853         struct FuncToAllFoldersData function_data;
854         
855         function_data.function = function;
856         function_data.data = data;
857
858         for (list = folder_list; list != NULL; list = list->next) {
859                 folder = FOLDER(list->data);
860                 if (folder->node)
861                         g_node_traverse(folder->node, G_PRE_ORDER,
862                                         G_TRAVERSE_ALL, -1,
863                                         folder_func_to_all_folders_func,
864                                         &function_data);
865         }
866 }
867
868 static void folder_count_total_msgs_func(FolderItem *item, gpointer data)
869 {
870         struct TotalMsgCount *count = (struct TotalMsgCount *)data;
871
872         count->new_msgs += item->new_msgs;
873         count->unread_msgs += item->unread_msgs;
874         count->unreadmarked_msgs += item->unreadmarked_msgs;
875         count->total_msgs += item->total_msgs;
876 }
877
878 struct TotalMsgStatus
879 {
880         guint new;
881         guint unread;
882         guint total;
883         GString *str;
884 };
885
886 static gboolean folder_get_status_full_all_func(GNode *node, gpointer data)
887 {
888         FolderItem *item;
889         struct TotalMsgStatus *status = (struct TotalMsgStatus *)data;
890         gchar *id;
891  
892         g_return_val_if_fail(node->data != NULL, FALSE);
893  
894         item = FOLDER_ITEM(node->data);
895
896         if (!item->path) return FALSE;
897
898         status->new += item->new_msgs;
899         status->unread += item->unread_msgs;
900         status->total += item->total_msgs;
901
902         if (status->str) {
903                 id = folder_item_get_identifier(item);
904                 g_string_append_printf(status->str, "%5d %5d %5d %s\n",
905                                   item->new_msgs, item->unread_msgs,
906                                   item->total_msgs, id);
907                 g_free(id);
908         }
909  
910         return FALSE;
911  }
912  
913 static void folder_get_status_full_all(GString *str, guint *new, guint *unread,
914                                        guint *total)
915 {
916         GList *list;
917         Folder *folder;
918         struct TotalMsgStatus status;
919  
920         status.new = status.unread = status.total = 0;
921         status.str = str;
922  
923         debug_print("Counting total number of messages...\n");
924  
925         for (list = folder_list; list != NULL; list = list->next) {
926                 folder = FOLDER(list->data);
927                 if (folder->node)
928                         g_node_traverse(folder->node, G_PRE_ORDER,
929                                         G_TRAVERSE_ALL, -1,
930                                         folder_get_status_full_all_func,
931                                         &status);
932         }
933  
934         *new = status.new;
935         *unread = status.unread;
936         *total = status.total;
937 }
938
939 gchar *folder_get_status(GPtrArray *folders, gboolean full)
940 {
941         guint new, unread, total;
942         GString *str;
943         gint i;
944         gchar *ret;
945
946         new = unread = total = 0;
947
948         str = g_string_new(NULL);
949
950         if (folders) {
951                 for (i = 0; i < folders->len; i++) {
952                         FolderItem *item;
953
954                         item = g_ptr_array_index(folders, i);
955                         new += item->new_msgs;
956                         unread += item->unread_msgs;
957                         total += item->total_msgs;
958
959                         if (full) {
960                                 gchar *id;
961
962                                 id = folder_item_get_identifier(item);
963                                 g_string_append_printf(str, "%5d %5d %5d %s\n",
964                                                   item->new_msgs, item->unread_msgs,
965                                                   item->total_msgs, id);
966                                 g_free(id);
967                         }
968                 }
969         } else {
970                 folder_get_status_full_all(full ? str : NULL,
971                                            &new, &unread, &total);
972         }
973
974         if (full)
975                 g_string_append_printf(str, "%5d %5d %5d\n", new, unread, total);
976         else
977                 g_string_append_printf(str, "%d %d %d\n", new, unread, total);
978
979         ret = str->str;
980         g_string_free(str, FALSE);
981  
982         return ret;
983 }
984
985 void folder_count_total_msgs(guint *new_msgs, guint *unread_msgs, guint *unreadmarked_msgs, guint *total_msgs)
986 {
987         struct TotalMsgCount count;
988
989         count.new_msgs = count.unread_msgs = count.unreadmarked_msgs = count.total_msgs = 0;
990
991         debug_print("Counting total number of messages...\n");
992
993         folder_func_to_all_folders(folder_count_total_msgs_func, &count);
994
995         *new_msgs = count.new_msgs;
996         *unread_msgs = count.unread_msgs;
997         *unreadmarked_msgs = count.unreadmarked_msgs;
998         *total_msgs = count.total_msgs;
999 }
1000
1001 Folder *folder_find_from_path(const gchar *path)
1002 {
1003         GList *list;
1004         Folder *folder;
1005
1006         for (list = folder_list; list != NULL; list = list->next) {
1007                 folder = list->data;
1008                 if ((FOLDER_TYPE(folder) == F_MH || 
1009                      FOLDER_TYPE(folder) == F_MBOX) &&
1010                     !path_cmp(LOCAL_FOLDER(folder)->rootpath, path))
1011                         return folder;
1012         }
1013
1014         return NULL;
1015 }
1016
1017 Folder *folder_find_from_name(const gchar *name, FolderClass *klass)
1018 {
1019         GList *list;
1020         Folder *folder;
1021
1022         for (list = folder_list; list != NULL; list = list->next) {
1023                 folder = list->data;
1024                 if (folder->klass == klass && 
1025                     strcmp2(name, folder->name) == 0)
1026                         return folder;
1027         }
1028
1029         return NULL;
1030 }
1031
1032 static gboolean folder_item_find_func(GNode *node, gpointer data)
1033 {
1034         FolderItem *item = node->data;
1035         gpointer *d = data;
1036         const gchar *path = d[0];
1037
1038         if (path_cmp(path, item->path) != 0)
1039                 return FALSE;
1040
1041         d[1] = item;
1042
1043         return TRUE;
1044 }
1045
1046 FolderItem *folder_find_item_from_path(const gchar *path)
1047 {
1048         Folder *folder;
1049         gpointer d[2];
1050
1051         folder = folder_get_default_folder();
1052         g_return_val_if_fail(folder != NULL, NULL);
1053
1054         d[0] = (gpointer)path;
1055         d[1] = NULL;
1056         g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1057                         folder_item_find_func, d);
1058         return d[1];
1059 }
1060
1061 FolderItem *folder_find_child_item_by_name(FolderItem *item, const gchar *name)
1062 {
1063         GNode *node;
1064         FolderItem *child;
1065
1066         for (node = item->node->children; node != NULL; node = node->next) {
1067                 gchar *base;
1068                 child = FOLDER_ITEM(node->data);
1069                 base = g_path_get_basename(child->path);
1070                 if (strcmp2(base, name) == 0) {
1071                         g_free(base);
1072                         return child;
1073                 }
1074                 g_free(base);
1075         }
1076
1077         return NULL;
1078 }
1079
1080 FolderClass *folder_get_class_from_string(const gchar *str)
1081 {
1082         GSList *classlist;
1083
1084         classlist = folder_get_class_list();
1085         for (; classlist != NULL; classlist = g_slist_next(classlist)) {
1086                 FolderClass *class = (FolderClass *) classlist->data;
1087                 if (g_ascii_strcasecmp(class->idstr, str) == 0)
1088                         return class;
1089         }
1090
1091         return NULL;
1092 }
1093
1094 gchar *folder_get_identifier(Folder *folder)
1095 {
1096         gchar *type_str;
1097
1098         g_return_val_if_fail(folder != NULL, NULL);
1099
1100         type_str = folder->klass->idstr;
1101         return g_strconcat("#", type_str, "/", folder->name, NULL);
1102 }
1103
1104 gchar *folder_item_get_identifier(FolderItem *item)
1105 {
1106         gchar *id = NULL;
1107         gchar *folder_id = NULL;
1108
1109         g_return_val_if_fail(item != NULL, NULL);
1110         g_return_val_if_fail(item->path != NULL, NULL);
1111
1112         folder_id = folder_get_identifier(item->folder);
1113         id = g_strconcat(folder_id, "/", item->path, NULL);
1114         g_free(folder_id);
1115
1116         return id;
1117 }
1118
1119 FolderItem *folder_find_item_from_identifier(const gchar *identifier)
1120 {
1121         Folder *folder;
1122         gpointer d[2];
1123         gchar *str;
1124         gchar *p;
1125         gchar *name;
1126         gchar *path;
1127         FolderClass *class;
1128
1129         g_return_val_if_fail(identifier != NULL, NULL);
1130
1131         if (*identifier != '#')
1132                 return folder_find_item_from_path(identifier);
1133
1134         Xstrdup_a(str, identifier, return NULL);
1135
1136         p = strchr(str, '/');
1137         if (!p)
1138                 return folder_find_item_from_path(identifier);
1139         *p = '\0';
1140         p++;
1141         class = folder_get_class_from_string(&str[1]);
1142         if (class == NULL)
1143                 return folder_find_item_from_path(identifier);
1144
1145         name = p;
1146         p = strchr(p, '/');
1147         if (!p)
1148                 return folder_find_item_from_path(identifier);
1149         *p = '\0';
1150         p++;
1151
1152         folder = folder_find_from_name(name, class);
1153         if (!folder)
1154                 return folder_find_item_from_path(identifier);
1155
1156         path = p;
1157
1158         d[0] = (gpointer)path;
1159         d[1] = NULL;
1160         g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1161                         folder_item_find_func, d);
1162         return d[1];
1163 }
1164
1165 /**
1166  * Get a displayable name for a FolderItem
1167  *
1168  * \param item FolderItem for that a name should be created
1169  * \return Displayable name for item, returned string has to
1170  *         be freed
1171  */
1172 gchar *folder_item_get_name(FolderItem *item)
1173 {
1174         gchar *name = NULL;
1175
1176         g_return_val_if_fail(item != NULL, g_strdup(""));
1177
1178         switch (item->stype) {
1179         case F_INBOX:
1180                 name = g_strdup(!strcmp2(item->name, INBOX_DIR) ? _("Inbox") :
1181                                 item->name);
1182                 break;
1183         case F_OUTBOX:
1184                 name = g_strdup(!strcmp2(item->name, OUTBOX_DIR) ? _("Sent") :
1185                                 item->name);
1186                 break;
1187         case F_QUEUE:
1188                 name = g_strdup(!strcmp2(item->name, QUEUE_DIR) ? _("Queue") :
1189                                 item->name);
1190                 break;
1191         case F_TRASH:
1192                 name = g_strdup(!strcmp2(item->name, TRASH_DIR) ? _("Trash") :
1193                                 item->name);
1194                 break;
1195         case F_DRAFT:
1196                 name = g_strdup(!strcmp2(item->name, DRAFT_DIR) ? _("Drafts") :
1197                                 item->name);
1198                 break;
1199         default:
1200                 break;
1201         }
1202
1203         if (name == NULL) {
1204                 /*
1205                  * should probably be done by a virtual function,
1206                  * the folder knows the ui string and how to abbrev
1207                 */
1208                 if (folder_item_parent(item) == NULL) {
1209                         name = g_strconcat(item->name, " (", item->folder->klass->uistr, ")", NULL);
1210                 } else {
1211                         if (FOLDER_CLASS(item->folder) == news_get_class() &&
1212                             item->path && !strcmp2(item->name, item->path))
1213                                 name = get_abbrev_newsgroup_name
1214                                         (item->path,
1215                                          prefs_common.ng_abbrev_len);
1216                         else
1217                                 name = g_strdup(item->name);
1218                 }
1219         }
1220
1221         if (name == NULL)
1222                 name = g_strdup("");
1223
1224         return name;
1225 }
1226
1227 Folder *folder_get_default_folder(void)
1228 {
1229         return folder_list ? FOLDER(folder_list->data) : NULL;
1230 }
1231
1232 FolderItem *folder_get_default_inbox(void)
1233 {
1234         GList *flist;
1235
1236         for (flist = folder_list; flist != NULL; flist = g_list_next(flist)) {
1237                 Folder * folder = FOLDER(flist->data);
1238
1239                 if (folder == NULL)
1240                         continue;
1241                 if (folder->inbox == NULL)
1242                         continue;
1243
1244                 return folder->inbox;
1245         }
1246
1247         return NULL;
1248 }
1249
1250 FolderItem *folder_get_default_outbox(void)
1251 {
1252         GList *flist;
1253
1254         for (flist = folder_list; flist != NULL; flist = g_list_next(flist)) {
1255                 Folder * folder = FOLDER(flist->data);
1256
1257                 if (folder == NULL)
1258                         continue;
1259                 if (folder->outbox == NULL)
1260                         continue;
1261
1262                 return folder->outbox;
1263         }
1264
1265         return NULL;
1266 }
1267
1268 FolderItem *folder_get_default_draft(void)
1269 {
1270         GList *flist;
1271
1272         for (flist = folder_list; flist != NULL; flist = g_list_next(flist)) {
1273                 Folder * folder = FOLDER(flist->data);
1274
1275                 if (folder == NULL)
1276                         continue;
1277                 if (folder->draft == NULL)
1278                         continue;
1279
1280                 return folder->draft;
1281         }
1282
1283         return NULL;
1284 }
1285
1286 FolderItem *folder_get_default_queue(void)
1287 {
1288         GList *flist;
1289
1290         for (flist = folder_list; flist != NULL; flist = g_list_next(flist)) {
1291                 Folder * folder = FOLDER(flist->data);
1292
1293                 if (folder == NULL)
1294                         continue;
1295                 if (folder->queue == NULL)
1296                         continue;
1297
1298                 return folder->queue;
1299         }
1300
1301         return NULL;
1302 }
1303
1304 FolderItem *folder_get_default_trash(void)
1305 {
1306         GList *flist;
1307
1308         for (flist = folder_list; flist != NULL; flist = g_list_next(flist)) {
1309                 Folder * folder = FOLDER(flist->data);
1310
1311                 if (folder == NULL)
1312                         continue;
1313                 if (folder->trash == NULL)
1314                         continue;
1315
1316                 return folder->trash;
1317         }
1318
1319         return NULL;
1320 }
1321
1322 #define CREATE_FOLDER_IF_NOT_EXIST(member, dir, type)           \
1323 {                                                               \
1324         if (!folder->member) {                                  \
1325                 item = folder_item_new(folder, dir, dir);       \
1326                 item->stype = type;                             \
1327                 folder_item_append(rootitem, item);             \
1328                 folder->member = item;                          \
1329         }                                                       \
1330 }
1331
1332 void folder_set_missing_folders(void)
1333 {
1334         Folder *folder;
1335         FolderItem *rootitem;
1336         FolderItem *item;
1337         GList *list;
1338
1339         for (list = folder_list; list != NULL; list = list->next) {
1340                 folder = list->data;
1341                 if (FOLDER_TYPE(folder) != F_MH) continue;
1342                 rootitem = FOLDER_ITEM(folder->node->data);
1343                 g_return_if_fail(rootitem != NULL);
1344
1345                 if (folder->inbox && folder->outbox && folder->draft &&
1346                     folder->queue && folder->trash)
1347                         continue;
1348
1349                 if (folder->klass->create_tree(folder) < 0) {
1350                         g_warning("%s: can't create the folder tree.\n",
1351                                   LOCAL_FOLDER(folder)->rootpath);
1352                         continue;
1353                 }
1354
1355                 CREATE_FOLDER_IF_NOT_EXIST(inbox,  INBOX_DIR,  F_INBOX);
1356                 CREATE_FOLDER_IF_NOT_EXIST(outbox, OUTBOX_DIR, F_OUTBOX);
1357                 CREATE_FOLDER_IF_NOT_EXIST(draft,  DRAFT_DIR,  F_DRAFT);
1358                 CREATE_FOLDER_IF_NOT_EXIST(queue,  QUEUE_DIR,  F_QUEUE);
1359                 CREATE_FOLDER_IF_NOT_EXIST(trash,  TRASH_DIR,  F_TRASH);
1360         }
1361 }
1362
1363 static gboolean folder_unref_account_func(GNode *node, gpointer data)
1364 {
1365         FolderItem *item = node->data;
1366         PrefsAccount *account = data;
1367
1368         if (item->account == account)
1369                 item->account = NULL;
1370
1371         return FALSE;
1372 }
1373
1374 void folder_unref_account_all(PrefsAccount *account)
1375 {
1376         Folder *folder;
1377         GList *list;
1378
1379         if (!account) return;
1380
1381         for (list = folder_list; list != NULL; list = list->next) {
1382                 folder = list->data;
1383                 if (folder->account == account)
1384                         folder->account = NULL;
1385                 g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1386                                 folder_unref_account_func, account);
1387         }
1388 }
1389
1390 #undef CREATE_FOLDER_IF_NOT_EXIST
1391
1392 gchar *folder_item_get_path(FolderItem *item)
1393 {
1394         Folder *folder;
1395
1396         g_return_val_if_fail(item != NULL, NULL);
1397         folder = item->folder;
1398         g_return_val_if_fail(folder != NULL, NULL);
1399
1400         return folder->klass->item_get_path(folder, item);
1401 }
1402
1403 void folder_item_set_default_flags(FolderItem *dest, MsgFlags *flags)
1404 {
1405         if (!(dest->stype == F_OUTBOX ||
1406               dest->stype == F_QUEUE  ||
1407               dest->stype == F_DRAFT  ||
1408               dest->stype == F_TRASH)) {
1409                 flags->perm_flags = MSG_NEW|MSG_UNREAD;
1410         } else {
1411                 flags->perm_flags = 0;
1412         }
1413         if (FOLDER_TYPE(dest->folder) == F_MH) {
1414                 if (dest->stype == F_QUEUE) {
1415                         MSG_SET_TMP_FLAGS(*flags, MSG_QUEUED);
1416                 } else if (dest->stype == F_DRAFT) {
1417                         MSG_SET_TMP_FLAGS(*flags, MSG_DRAFT);
1418                 }
1419         }
1420 }
1421
1422 static gint folder_sort_cache_list_by_msgnum(gconstpointer a, gconstpointer b)
1423 {
1424         MsgInfo *msginfo_a = (MsgInfo *) a;
1425         MsgInfo *msginfo_b = (MsgInfo *) b;
1426
1427         return (msginfo_a->msgnum - msginfo_b->msgnum);
1428 }
1429
1430 static gint folder_sort_folder_list(gconstpointer a, gconstpointer b)
1431 {
1432         guint gint_a = GPOINTER_TO_INT(a);
1433         guint gint_b = GPOINTER_TO_INT(b);
1434         
1435         return (gint_a - gint_b);
1436 }
1437
1438 gint folder_item_open(FolderItem *item)
1439 {
1440         gchar *buf;
1441         if((item->folder->klass->scan_required != NULL) &&
1442            (item->folder->klass->scan_required(item->folder, item))) {
1443                 folder_item_scan_full(item, TRUE);
1444         }
1445         folder_item_syncronize_flags(item);
1446         
1447         /* Processing */
1448         buf = g_strdup_printf(_("Processing (%s)...\n"), 
1449                               item->path ? item->path : item->name);
1450         debug_print("%s\n", buf);
1451         g_free(buf);
1452         
1453         folder_item_apply_processing(item);
1454
1455         item->opened = TRUE;
1456
1457         debug_print("done.\n");
1458
1459         return 0;
1460 }
1461
1462 gint folder_item_close(FolderItem *item)
1463 {
1464         GSList *mlist, *cur;
1465         Folder *folder;
1466         
1467         g_return_val_if_fail(item != NULL, -1);
1468
1469         if (item->new_msgs) {
1470                 folder_item_update_freeze();
1471                 mlist = folder_item_get_msg_list(item);
1472                 for (cur = mlist ; cur != NULL ; cur = cur->next) {
1473                         MsgInfo * msginfo;
1474
1475                         msginfo = (MsgInfo *) cur->data;
1476                         if (MSG_IS_NEW(msginfo->flags))
1477                                 procmsg_msginfo_unset_flags(msginfo, MSG_NEW, 0);
1478                         procmsg_msginfo_free(msginfo);
1479                 }
1480                 g_slist_free(mlist);
1481                 folder_item_update_thaw();
1482         }               
1483
1484         folder_item_write_cache(item);
1485         
1486         folder_item_update(item, F_ITEM_UPDATE_MSGCNT);
1487
1488         item->opened = FALSE;
1489         folder = item->folder;
1490
1491         if (folder->klass->close == NULL)
1492                 return 0;
1493
1494         return folder->klass->close(folder, item);
1495 }
1496
1497 static MsgInfoList *get_msginfos(FolderItem *item, MsgNumberList *numlist)
1498 {
1499         MsgInfoList *msglist = NULL;
1500         Folder *folder = item->folder;
1501
1502         if (folder->klass->get_msginfos != NULL)
1503                 msglist = folder->klass->get_msginfos(folder, item, numlist);
1504         else {
1505                 MsgNumberList *elem;
1506
1507                 for (elem = numlist; elem != NULL; elem = g_slist_next(elem)) {
1508                         MsgInfo *msginfo;
1509                         guint num;
1510
1511                         num = GPOINTER_TO_INT(elem->data);
1512                         msginfo = folder->klass->get_msginfo(folder, item, num);
1513                         if (msginfo != NULL)
1514                                 msglist = g_slist_prepend(msglist, msginfo);
1515                 }               
1516         }
1517
1518         return msglist;
1519 }
1520
1521 static MsgInfo *get_msginfo(FolderItem *item, guint num)
1522 {
1523         MsgNumberList numlist;
1524         MsgInfoList *msglist;
1525         MsgInfo *msginfo = NULL;
1526
1527         numlist.data = GINT_TO_POINTER(num);
1528         numlist.next = NULL;
1529         msglist = get_msginfos(item, &numlist);
1530         if (msglist != NULL)
1531                 msginfo = procmsg_msginfo_new_ref(msglist->data);
1532         procmsg_msg_list_free(msglist);
1533
1534         return msginfo;
1535 }
1536
1537 static gint syncronize_flags(FolderItem *item, MsgInfoList *msglist)
1538 {
1539         GRelation *relation;
1540         gint ret = 0;
1541         GSList *cur;
1542
1543         if(msglist == NULL)
1544                 return 0;
1545         if(item->folder->klass->get_flags == NULL)
1546                 return 0;
1547
1548         relation = g_relation_new(2);
1549         g_relation_index(relation, 0, g_direct_hash, g_direct_equal);
1550         if ((ret = item->folder->klass->get_flags(
1551             item->folder, item, msglist, relation)) == 0) {
1552                 GTuples *tuples;
1553                 MsgInfo *msginfo;
1554                 MsgPermFlags permflags = 0;
1555                 gboolean skip;
1556
1557                 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1558                         msginfo = (MsgInfo *) cur->data;
1559                 
1560                         tuples = g_relation_select(relation, msginfo, 0);
1561                         skip = tuples->len < 1;
1562                         if (!skip)
1563                                 permflags = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1564                         g_tuples_destroy(tuples);
1565                         if (skip)
1566                                 continue;
1567                         
1568                         if (msginfo->flags.perm_flags != permflags) {
1569                                 procmsg_msginfo_set_flags(msginfo,
1570                                         permflags & ~msginfo->flags.perm_flags, 0);
1571                                 procmsg_msginfo_unset_flags(msginfo,
1572                                         ~permflags & msginfo->flags.perm_flags, 0);
1573                         }
1574                 }
1575         }
1576         g_relation_destroy(relation);   
1577
1578         return ret;
1579 }
1580
1581 gint folder_item_scan_full(FolderItem *item, gboolean filtering)
1582 {
1583         Folder *folder;
1584         GSList *folder_list = NULL, *cache_list = NULL;
1585         GSList *folder_list_cur, *cache_list_cur, *new_list = NULL;
1586         GSList *exists_list = NULL, *elem;
1587         GSList *newmsg_list = NULL;
1588         guint newcnt = 0, unreadcnt = 0, totalcnt = 0, unreadmarkedcnt = 0;
1589         guint cache_max_num, folder_max_num, cache_cur_num, folder_cur_num;
1590         gboolean update_flags = 0, old_uids_valid = FALSE;
1591     
1592         g_return_val_if_fail(item != NULL, -1);
1593         if (item->path == NULL) return -1;
1594
1595         folder = item->folder;
1596
1597         g_return_val_if_fail(folder != NULL, -1);
1598         g_return_val_if_fail(folder->klass->get_num_list != NULL, -1);
1599
1600         debug_print("Scanning folder %s for cache changes.\n", item->path);
1601
1602         /* Get list of messages for folder and cache */
1603         if (folder->klass->get_num_list(item->folder, item, &folder_list, &old_uids_valid) < 0) {
1604                 debug_print("Error fetching list of message numbers\n");
1605                 return(-1);
1606         }
1607
1608         if (old_uids_valid) {
1609                 if (!item->cache)
1610                         folder_item_read_cache(item);
1611                 cache_list = msgcache_get_msg_list(item->cache);
1612         } else {
1613                 if (item->cache)
1614                         msgcache_destroy(item->cache);
1615                 item->cache = msgcache_new();
1616                 cache_list = NULL;
1617         }
1618
1619         /* Sort both lists */
1620         cache_list = g_slist_sort(cache_list, folder_sort_cache_list_by_msgnum);
1621         folder_list = g_slist_sort(folder_list, folder_sort_folder_list);
1622
1623         cache_list_cur = cache_list;
1624         folder_list_cur = folder_list;
1625
1626         if (cache_list_cur != NULL) {
1627                 GSList *cache_list_last;
1628         
1629                 cache_cur_num = ((MsgInfo *)cache_list_cur->data)->msgnum;
1630                 cache_list_last = g_slist_last(cache_list);
1631                 cache_max_num = ((MsgInfo *)cache_list_last->data)->msgnum;
1632         } else {
1633                 cache_cur_num = G_MAXINT;
1634                 cache_max_num = 0;
1635         }
1636
1637         if (folder_list_cur != NULL) {
1638                 GSList *folder_list_last;
1639         
1640                 folder_cur_num = GPOINTER_TO_INT(folder_list_cur->data);
1641                 folder_list_last = g_slist_last(folder_list);
1642                 folder_max_num = GPOINTER_TO_INT(folder_list_last->data);
1643         } else {
1644                 folder_cur_num = G_MAXINT;
1645                 folder_max_num = 0;
1646         }
1647
1648         while ((cache_cur_num != G_MAXINT) || (folder_cur_num != G_MAXINT)) {
1649                 /*
1650                  *  Message only exists in the folder
1651                  *  Remember message for fetching
1652                  */
1653                 if (folder_cur_num < cache_cur_num) {
1654                         gboolean add = FALSE;
1655
1656                         switch(FOLDER_TYPE(folder)) {
1657                                 case F_NEWS:
1658                                         if (folder_cur_num < cache_max_num)
1659                                                 break;
1660                                         
1661                                         if (folder->account->max_articles == 0) {
1662                                                 add = TRUE;
1663                                         }
1664
1665                                         if (folder_max_num <= folder->account->max_articles) {
1666                                                 add = TRUE;
1667                                         } else if (folder_cur_num > (folder_max_num - folder->account->max_articles)) {
1668                                                 add = TRUE;
1669                                         }
1670                                         break;
1671                                 default:
1672                                         add = TRUE;
1673                                         break;
1674                         }
1675                         
1676                         if (add) {
1677                                 new_list = g_slist_prepend(new_list, GINT_TO_POINTER(folder_cur_num));
1678                                 debug_print("Remembered message %d for fetching\n", folder_cur_num);
1679                         }
1680
1681                         /* Move to next folder number */
1682                         folder_list_cur = folder_list_cur->next;
1683
1684                         if (folder_list_cur != NULL)
1685                                 folder_cur_num = GPOINTER_TO_INT(folder_list_cur->data);
1686                         else
1687                                 folder_cur_num = G_MAXINT;
1688
1689                         continue;
1690                 }
1691
1692                 /*
1693                  *  Message only exists in the cache
1694                  *  Remove the message from the cache
1695                  */
1696                 if (cache_cur_num < folder_cur_num) {
1697                         msgcache_remove_msg(item->cache, cache_cur_num);
1698                         debug_print("Removed message %d from cache.\n", cache_cur_num);
1699
1700                         /* Move to next cache number */
1701                         cache_list_cur = cache_list_cur->next;
1702
1703                         if (cache_list_cur != NULL)
1704                                 cache_cur_num = ((MsgInfo *)cache_list_cur->data)->msgnum;
1705                         else
1706                                 cache_cur_num = G_MAXINT;
1707
1708                         update_flags |= F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT;
1709
1710                         continue;
1711                 }
1712
1713                 /*
1714                  *  Message number exists in folder and cache!
1715                  *  Check if the message has been modified
1716                  */
1717                 if (cache_cur_num == folder_cur_num) {
1718                         MsgInfo *msginfo;
1719
1720                         msginfo = msgcache_get_msg(item->cache, folder_cur_num);
1721                         if (folder->klass->is_msg_changed && folder->klass->is_msg_changed(folder, item, msginfo)) {
1722                                 msgcache_remove_msg(item->cache, msginfo->msgnum);
1723                                 new_list = g_slist_prepend(new_list, GINT_TO_POINTER(msginfo->msgnum));
1724                                 procmsg_msginfo_free(msginfo);
1725
1726                                 debug_print("Remembering message %d to update...\n", folder_cur_num);
1727                         } else
1728                                 exists_list = g_slist_prepend(exists_list, msginfo);
1729
1730                         /* Move to next folder and cache number */
1731                         cache_list_cur = cache_list_cur->next;
1732                         folder_list_cur = folder_list_cur->next;
1733
1734                         if (cache_list_cur != NULL)
1735                                 cache_cur_num = ((MsgInfo *)cache_list_cur->data)->msgnum;
1736                         else
1737                                 cache_cur_num = G_MAXINT;
1738
1739                         if (folder_list_cur != NULL)
1740                                 folder_cur_num = GPOINTER_TO_INT(folder_list_cur->data);
1741                         else
1742                                 folder_cur_num = G_MAXINT;
1743
1744                         continue;
1745                 }
1746         }
1747         
1748         for(cache_list_cur = cache_list; cache_list_cur != NULL; cache_list_cur = g_slist_next(cache_list_cur))
1749                 procmsg_msginfo_free((MsgInfo *) cache_list_cur->data);
1750
1751         g_slist_free(cache_list);
1752         g_slist_free(folder_list);
1753
1754         if (new_list != NULL) {
1755                 newmsg_list = get_msginfos(item, new_list);
1756                 g_slist_free(new_list);
1757         }
1758
1759         syncronize_flags(item, exists_list);
1760
1761         folder_item_update_freeze();
1762         if (newmsg_list != NULL) {
1763                 GSList *elem;
1764
1765                 for (elem = newmsg_list; elem != NULL; elem = g_slist_next(elem)) {
1766                         MsgInfo *msginfo = (MsgInfo *) elem->data;
1767
1768                         msgcache_add_msg(item->cache, msginfo);
1769                         if ((filtering == TRUE) &&
1770                             (item->stype == F_INBOX) &&
1771                             (item->folder->account != NULL) && 
1772                             (item->folder->account->filter_on_recv) &&
1773                             procmsg_msginfo_filter(msginfo))
1774                                 procmsg_msginfo_free(msginfo);
1775                         else
1776                                 exists_list = g_slist_prepend(exists_list, msginfo);
1777                 }
1778                 g_slist_free(newmsg_list);
1779
1780                 update_flags |= F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT;
1781         }
1782
1783         for (elem = exists_list; elem != NULL; elem = g_slist_next(elem)) {
1784                 MsgInfo *msginfo;
1785
1786                 msginfo = elem->data;
1787                 if (MSG_IS_IGNORE_THREAD(msginfo->flags) && (MSG_IS_NEW(msginfo->flags) || MSG_IS_UNREAD(msginfo->flags)))
1788                         procmsg_msginfo_unset_flags(msginfo, MSG_NEW | MSG_UNREAD, 0);
1789                 if (!MSG_IS_IGNORE_THREAD(msginfo->flags) && procmsg_msg_has_flagged_parent(msginfo, MSG_IGNORE_THREAD)) {
1790                         procmsg_msginfo_unset_flags(msginfo, MSG_NEW | MSG_UNREAD, 0);
1791                         procmsg_msginfo_set_flags(msginfo, MSG_IGNORE_THREAD, 0);
1792                 }
1793                 if ((item->stype == F_OUTBOX ||
1794                      item->stype == F_QUEUE  ||
1795                      item->stype == F_DRAFT  ||
1796                      item->stype == F_TRASH) &&
1797                     (MSG_IS_NEW(msginfo->flags) || MSG_IS_UNREAD(msginfo->flags)))
1798                         procmsg_msginfo_unset_flags(msginfo, MSG_NEW | MSG_UNREAD, 0);
1799                 if (MSG_IS_NEW(msginfo->flags))
1800                         newcnt++;
1801                 if (MSG_IS_UNREAD(msginfo->flags))
1802                         unreadcnt++;
1803                 if (MSG_IS_UNREAD(msginfo->flags) && procmsg_msg_has_marked_parent(msginfo))
1804                         unreadmarkedcnt++;
1805                 totalcnt++;
1806
1807                 procmsg_msginfo_free(msginfo);
1808         }
1809         g_slist_free(exists_list);
1810
1811         item->new_msgs = newcnt;
1812         item->unread_msgs = unreadcnt;
1813         item->total_msgs = totalcnt;
1814         item->unreadmarked_msgs = unreadmarkedcnt;
1815
1816         update_flags |= F_ITEM_UPDATE_MSGCNT;
1817
1818         folder_item_update(item, update_flags);
1819         folder_item_update_thaw();
1820
1821         return 0;
1822 }
1823
1824 gint folder_item_scan(FolderItem *item)
1825 {
1826         return folder_item_scan_full(item, TRUE);
1827 }
1828
1829 static gboolean folder_scan_all_items_func(GNode *node, gpointer data)
1830 {
1831         FolderItem *item = node->data;
1832
1833         folder_item_scan(item);
1834
1835         return FALSE;
1836 }
1837
1838 void folder_scan_all_items(Folder * folder)
1839 {
1840         g_node_traverse(folder->node, G_PRE_ORDER,
1841                         G_TRAVERSE_ALL, -1, folder_scan_all_items_func, NULL);
1842 }
1843
1844 static void folder_item_scan_foreach_func(gpointer key, gpointer val,
1845                                           gpointer data)
1846 {
1847         folder_item_scan(FOLDER_ITEM(key));
1848 }
1849
1850 void folder_item_scan_foreach(GHashTable *table)
1851 {
1852         g_hash_table_foreach(table, folder_item_scan_foreach_func, NULL);
1853 }
1854
1855 void folder_count_total_cache_memusage(FolderItem *item, gpointer data)
1856 {
1857         gint *memusage = (gint *)data;
1858
1859         if (item->cache == NULL)
1860                 return;
1861         
1862         *memusage += msgcache_get_memory_usage(item->cache);
1863 }
1864
1865 gint folder_item_syncronize_flags(FolderItem *item)
1866 {
1867         MsgInfoList *msglist = NULL;
1868         GSList *cur;
1869         gint ret = 0;
1870         
1871         g_return_val_if_fail(item != NULL, -1);
1872         g_return_val_if_fail(item->folder != NULL, -1);
1873         g_return_val_if_fail(item->folder->klass != NULL, -1);
1874         
1875         if (item->cache == NULL)
1876                 folder_item_read_cache(item);
1877         
1878         msglist = msgcache_get_msg_list(item->cache);
1879         
1880         ret = syncronize_flags(item, msglist);
1881
1882         for (cur = msglist; cur != NULL; cur = g_slist_next(cur))
1883                 procmsg_msginfo_free((MsgInfo *) cur->data);
1884         
1885         return ret;
1886 }
1887
1888 gint folder_cache_time_compare_func(gconstpointer a, gconstpointer b)
1889 {
1890         FolderItem *fa = (FolderItem *)a;
1891         FolderItem *fb = (FolderItem *)b;
1892         
1893         return (gint) (msgcache_get_last_access_time(fa->cache) - msgcache_get_last_access_time(fb->cache));
1894 }
1895
1896 void folder_find_expired_caches(FolderItem *item, gpointer data)
1897 {
1898         GSList **folder_item_list = (GSList **)data;
1899         gint difftime, expiretime;
1900         
1901         if (item->cache == NULL)
1902                 return;
1903
1904         if (item->opened > 0)
1905                 return;
1906
1907         difftime = (gint) (time(NULL) - msgcache_get_last_access_time(item->cache));
1908         expiretime = prefs_common.cache_min_keep_time * 60;
1909         debug_print("Cache unused time: %d (Expire time: %d)\n", difftime, expiretime);
1910         if (difftime > expiretime) {
1911                 *folder_item_list = g_slist_insert_sorted(*folder_item_list, item, folder_cache_time_compare_func);
1912         }
1913 }
1914
1915 void folder_item_free_cache(FolderItem *item)
1916 {
1917         g_return_if_fail(item != NULL);
1918         
1919         if (item->cache == NULL)
1920                 return;
1921         
1922         if (item->opened > 0)
1923                 return;
1924
1925         folder_item_write_cache(item);
1926         msgcache_destroy(item->cache);
1927         item->cache = NULL;
1928 }
1929
1930 void folder_clean_cache_memory(void)
1931 {
1932         gint memusage = 0;
1933
1934         folder_func_to_all_folders(folder_count_total_cache_memusage, &memusage);       
1935         debug_print("Total cache memory usage: %d\n", memusage);
1936         
1937         if (memusage > (prefs_common.cache_max_mem_usage * 1024)) {
1938                 GSList *folder_item_list = NULL, *listitem;
1939                 
1940                 debug_print("Trying to free cache memory\n");
1941
1942                 folder_func_to_all_folders(folder_find_expired_caches, &folder_item_list);      
1943                 listitem = folder_item_list;
1944                 while((listitem != NULL) && (memusage > (prefs_common.cache_max_mem_usage * 1024))) {
1945                         FolderItem *item = (FolderItem *)(listitem->data);
1946
1947                         debug_print("Freeing cache memory for %s\n", item->path ? item->path : item->name);
1948                         memusage -= msgcache_get_memory_usage(item->cache);
1949                         folder_item_free_cache(item);
1950                         listitem = listitem->next;
1951                 }
1952                 g_slist_free(folder_item_list);
1953         }
1954 }
1955
1956 void folder_item_read_cache(FolderItem *item)
1957 {
1958         gchar *cache_file, *mark_file;
1959         
1960         g_return_if_fail(item != NULL);
1961
1962         if (item->path != NULL) {
1963                 cache_file = folder_item_get_cache_file(item);
1964                 mark_file = folder_item_get_mark_file(item);
1965                 item->cache = msgcache_read_cache(item, cache_file);
1966                 if (!item->cache) {
1967                         MsgInfoList *list, *cur;
1968                         guint newcnt = 0, unreadcnt = 0, unreadmarkedcnt = 0;
1969                         MsgInfo *msginfo;
1970
1971                         item->cache = msgcache_new();
1972                         folder_item_scan_full(item, TRUE);
1973
1974                         msgcache_read_mark(item->cache, mark_file);
1975
1976                         list = msgcache_get_msg_list(item->cache);
1977                         for (cur = list; cur != NULL; cur = g_slist_next(cur)) {
1978                                 msginfo = cur->data;
1979
1980                                 if (MSG_IS_NEW(msginfo->flags))
1981                                         newcnt++;
1982                                 if (MSG_IS_UNREAD(msginfo->flags))
1983                                         unreadcnt++;
1984                                 if (MSG_IS_UNREAD(msginfo->flags) && procmsg_msg_has_marked_parent(msginfo))
1985                                         unreadmarkedcnt++;
1986                         }
1987                         item->new_msgs = newcnt;
1988                         item->unread_msgs = unreadcnt;
1989                         item->unreadmarked_msgs = unreadmarkedcnt;
1990                         procmsg_msg_list_free(list);
1991                 } else
1992                         msgcache_read_mark(item->cache, mark_file);
1993
1994                 g_free(cache_file);
1995                 g_free(mark_file);
1996         } else {
1997                 item->cache = msgcache_new();
1998         }
1999
2000         folder_clean_cache_memory();
2001 }
2002
2003 void folder_item_write_cache(FolderItem *item)
2004 {
2005         gchar *cache_file, *mark_file;
2006         FolderItemPrefs *prefs;
2007         gint filemode = 0;
2008         gchar *id;
2009         
2010         if (!item || !item->path || !item->cache)
2011                 return;
2012
2013         id = folder_item_get_identifier(item);
2014         debug_print("Save cache for folder %s\n", id);
2015         g_free(id);
2016
2017         cache_file = folder_item_get_cache_file(item);
2018         mark_file = folder_item_get_mark_file(item);
2019         if (msgcache_write(cache_file, mark_file, item->cache) < 0) {
2020                 prefs = item->prefs;
2021                 if (prefs && prefs->enable_folder_chmod && prefs->folder_chmod) {
2022                         /* for cache file */
2023                         filemode = prefs->folder_chmod;
2024                         if (filemode & S_IRGRP) filemode |= S_IWGRP;
2025                         if (filemode & S_IROTH) filemode |= S_IWOTH;
2026                         chmod(cache_file, filemode);
2027                 }
2028         }
2029
2030         g_free(cache_file);
2031         g_free(mark_file);
2032 }
2033
2034 MsgInfo *folder_item_get_msginfo(FolderItem *item, gint num)
2035 {
2036         Folder *folder;
2037         MsgInfo *msginfo = NULL;
2038         
2039         g_return_val_if_fail(item != NULL, NULL);
2040         
2041         folder = item->folder;
2042         if (!item->cache)
2043                 folder_item_read_cache(item);
2044         
2045         if ((msginfo = msgcache_get_msg(item->cache, num)) != NULL)
2046                 return msginfo;
2047         
2048         msginfo = get_msginfo(item, num);
2049         if (msginfo != NULL) {
2050                 msgcache_add_msg(item->cache, msginfo);
2051                 return msginfo;
2052         }
2053         
2054         return NULL;
2055 }
2056
2057 MsgInfo *folder_item_get_msginfo_by_msgid(FolderItem *item, const gchar *msgid)
2058 {
2059         Folder *folder;
2060         MsgInfo *msginfo;
2061         
2062         g_return_val_if_fail(item != NULL, NULL);
2063         g_return_val_if_fail(msgid != NULL, NULL);
2064         
2065         folder = item->folder;
2066         if (!item->cache)
2067                 folder_item_read_cache(item);
2068         
2069         if ((msginfo = msgcache_get_msg_by_id(item->cache, msgid)) != NULL)
2070                 return msginfo;
2071
2072         return NULL;
2073 }
2074
2075 GSList *folder_item_get_msg_list(FolderItem *item)
2076 {
2077         g_return_val_if_fail(item != NULL, NULL);
2078         
2079         if (item->cache == 0)
2080                 folder_item_read_cache(item);
2081
2082         g_return_val_if_fail(item->cache != NULL, NULL);
2083         
2084         return msgcache_get_msg_list(item->cache);
2085 }
2086
2087 void msginfo_set_mime_flags(GNode *node, gpointer data)
2088 {
2089         MsgInfo *msginfo = data;
2090         MimeInfo *mimeinfo = node->data;
2091         
2092         if (mimeinfo->disposition == DISPOSITIONTYPE_ATTACHMENT) {
2093                 procmsg_msginfo_set_flags(msginfo, 0, MSG_HAS_ATTACHMENT);
2094         } else if (mimeinfo->disposition == DISPOSITIONTYPE_UNKNOWN && 
2095                  mimeinfo->type != MIMETYPE_TEXT &&
2096                  mimeinfo->type != MIMETYPE_MULTIPART) {
2097                 if (!mimeinfo->subtype 
2098                 ||  strcmp(mimeinfo->subtype, "pgp-signature"))
2099                         procmsg_msginfo_set_flags(msginfo, 0, MSG_HAS_ATTACHMENT);
2100         }
2101
2102         /* don't descend below top level message for signed and encrypted info */
2103         if (mimeinfo->type == MIMETYPE_MESSAGE)
2104                 return;
2105
2106         if (privacy_mimeinfo_is_signed(mimeinfo)) {
2107                 procmsg_msginfo_set_flags(msginfo, 0, MSG_SIGNED);
2108         }
2109
2110         if (privacy_mimeinfo_is_encrypted(mimeinfo)) {
2111                 procmsg_msginfo_set_flags(msginfo, 0, MSG_ENCRYPTED);
2112         } else {
2113                 /* searching inside encrypted parts doesn't really make sense */
2114                 g_node_children_foreach(mimeinfo->node, G_TRAVERSE_ALL, msginfo_set_mime_flags, msginfo);
2115         }
2116 }
2117
2118 gchar *folder_item_fetch_msg(FolderItem *item, gint num)
2119 {
2120         Folder *folder;
2121         gchar *msgfile;
2122         MsgInfo *msginfo;
2123
2124         g_return_val_if_fail(item != NULL, NULL);
2125
2126         folder = item->folder;
2127
2128         g_return_val_if_fail(folder->klass->fetch_msg != NULL, NULL);
2129
2130         msgfile = folder->klass->fetch_msg(folder, item, num);
2131
2132         if (msgfile != NULL) {
2133                 msginfo = folder_item_get_msginfo(item, num);
2134                 if ((msginfo != NULL) && !MSG_IS_SCANNED(msginfo->flags)) {
2135                         MimeInfo *mimeinfo;
2136
2137                         if (msginfo->folder->stype != F_QUEUE && 
2138                             msginfo->folder->stype != F_DRAFT)
2139                                 mimeinfo = procmime_scan_file(msgfile);
2140                         else
2141                                 mimeinfo = procmime_scan_queue_file(msgfile);
2142                         /* check for attachments */
2143                         if (mimeinfo != NULL) { 
2144                                 g_node_children_foreach(mimeinfo->node, G_TRAVERSE_ALL, msginfo_set_mime_flags, msginfo);
2145                                 procmime_mimeinfo_free_all(mimeinfo);
2146
2147                                 procmsg_msginfo_set_flags(msginfo, 0, MSG_SCANNED);
2148                         }
2149                 }
2150         }
2151
2152         return msgfile;
2153 }
2154
2155 gchar *folder_item_fetch_msg_full(FolderItem *item, gint num, gboolean headers,
2156                                   gboolean body)
2157 {
2158         Folder *folder;
2159         gchar *msgfile;
2160         MsgInfo *msginfo;
2161
2162         g_return_val_if_fail(item != NULL, NULL);
2163
2164         folder = item->folder;
2165
2166         if (folder->klass->fetch_msg_full == NULL)
2167                 return folder_item_fetch_msg(item, num);
2168
2169         msgfile = folder->klass->fetch_msg_full(folder, item, num, 
2170                                                 headers, body);
2171
2172         if (msgfile != NULL) {
2173                 msginfo = folder_item_get_msginfo(item, num);
2174                 if ((msginfo != NULL) && !MSG_IS_SCANNED(msginfo->flags)) {
2175                         MimeInfo *mimeinfo;
2176
2177                         if (msginfo->folder->stype != F_QUEUE && 
2178                             msginfo->folder->stype != F_DRAFT)
2179                                 mimeinfo = procmime_scan_file(msgfile);
2180                         else
2181                                 mimeinfo = procmime_scan_queue_file(msgfile);
2182                         /* check for attachments */
2183                         if (mimeinfo != NULL) { 
2184                                 g_node_children_foreach(mimeinfo->node, G_TRAVERSE_ALL, msginfo_set_mime_flags, msginfo);
2185                                 procmime_mimeinfo_free_all(mimeinfo);
2186
2187                                 procmsg_msginfo_set_flags(msginfo, 0, MSG_SCANNED);
2188                         }
2189                 }
2190         }
2191
2192         return msgfile;
2193 }
2194
2195
2196 gint folder_item_fetch_all_msg(FolderItem *item)
2197 {
2198         Folder *folder;
2199         GSList *mlist;
2200         GSList *cur;
2201         gint num = 0;
2202         gint ret = 0;
2203         gint total = 0;
2204
2205         g_return_val_if_fail(item != NULL, -1);
2206
2207         debug_print("fetching all messages in %s ...\n", item->path);
2208         statusbar_print_all(_("Fetching all messages in %s ...\n"), item->path);
2209
2210         folder = item->folder;
2211
2212         if (folder->ui_func)
2213                 folder->ui_func(folder, item, folder->ui_func_data ?
2214                                 folder->ui_func_data : GINT_TO_POINTER(num));
2215
2216         mlist = folder_item_get_msg_list(item);
2217
2218         total = g_slist_length(mlist);
2219
2220         for (cur = mlist; cur != NULL; cur = cur->next) {
2221                 MsgInfo *msginfo = (MsgInfo *)cur->data;
2222                 gchar *msg;
2223
2224                 num++;
2225                 if (num % 10 == 0) {
2226                         gchar buf[32];
2227                         g_snprintf(buf, sizeof(buf), "%d / %d",
2228                                    num, total);
2229                         gtk_progress_bar_set_text
2230                                 (GTK_PROGRESS_BAR(mainwindow_get_mainwindow()->progressbar), buf);
2231                         gtk_progress_bar_set_fraction
2232                         (GTK_PROGRESS_BAR(mainwindow_get_mainwindow()->progressbar),
2233                          (gfloat)num / (gfloat)total);
2234                 }
2235
2236                 if (folder->ui_func)
2237                         folder->ui_func(folder, item,
2238                                         folder->ui_func_data ?
2239                                         folder->ui_func_data :
2240                                         GINT_TO_POINTER(num));
2241
2242                 msg = folder_item_fetch_msg(item, msginfo->msgnum);
2243                 if (!msg) {
2244                         g_warning("Can't fetch message %d. Aborting.\n",
2245                                   msginfo->msgnum);
2246                         ret = -1;
2247                         break;
2248                 }
2249                 g_free(msg);
2250         }
2251
2252         gtk_progress_bar_set_fraction
2253                 (GTK_PROGRESS_BAR(mainwindow_get_mainwindow()->progressbar), 0);
2254         gtk_progress_bar_set_text
2255                 (GTK_PROGRESS_BAR(mainwindow_get_mainwindow()->progressbar), "");
2256         statusbar_pop_all();
2257         procmsg_msg_list_free(mlist);
2258
2259         return ret;
2260 }
2261
2262 static gint folder_item_get_msg_num_by_file(FolderItem *dest, const gchar *file)
2263 {
2264         static HeaderEntry hentry[] = {{"Message-ID:",  NULL, TRUE},
2265                                        {NULL,           NULL, FALSE}};
2266         FILE *fp;
2267         MsgInfo *msginfo;
2268         gint msgnum = 0;
2269         gchar buf[BUFFSIZE];
2270
2271         if ((fp = fopen(file, "rb")) == NULL)
2272                 return 0;
2273
2274         if ((dest->stype == F_QUEUE) || (dest->stype == F_DRAFT))
2275                 while (fgets(buf, sizeof(buf), fp) != NULL)
2276                         if (buf[0] == '\r' || buf[0] == '\n') break;
2277
2278         procheader_get_header_fields(fp, hentry);
2279         if (hentry[0].body) {
2280                 extract_parenthesis(hentry[0].body, '<', '>');
2281                 remove_space(hentry[0].body);
2282                 if ((msginfo = msgcache_get_msg_by_id(dest->cache, hentry[0].body)) != NULL) {
2283                         msgnum = msginfo->msgnum;
2284                         procmsg_msginfo_free(msginfo);
2285
2286                         debug_print("found message as uid %d\n", msgnum);
2287                 }
2288         }
2289         
2290         g_free(hentry[0].body);
2291         hentry[0].body = NULL;
2292         fclose(fp);
2293
2294         return msgnum;
2295 }
2296
2297 static void copy_msginfo_flags(MsgInfo *source, MsgInfo *dest)
2298 {
2299         MsgPermFlags perm_flags = 0;
2300         MsgTmpFlags tmp_flags = 0;
2301
2302         /* create new flags */
2303         if (source != NULL) {
2304                 /* copy original flags */
2305                 perm_flags = source->flags.perm_flags;
2306                 tmp_flags = source->flags.tmp_flags;
2307         } else {
2308                 perm_flags = dest->flags.perm_flags;
2309                 tmp_flags = dest->flags.tmp_flags;
2310         }
2311
2312         /* remove new, unread and deleted in special folders */
2313         if (dest->folder->stype == F_OUTBOX ||
2314             dest->folder->stype == F_QUEUE  ||
2315             dest->folder->stype == F_DRAFT  ||
2316             dest->folder->stype == F_TRASH)
2317                 perm_flags &= ~(MSG_NEW | MSG_UNREAD | MSG_DELETED);
2318
2319         /* set ignore flag of ignored parent exists */
2320         if (procmsg_msg_has_flagged_parent(dest, MSG_IGNORE_THREAD))
2321                 perm_flags |= MSG_IGNORE_THREAD;
2322
2323         /* Unset tmp flags that should not be copied */
2324         tmp_flags &= ~(MSG_MOVE | MSG_COPY);
2325
2326         /* unset flags that are set but should not */
2327         procmsg_msginfo_unset_flags(dest,
2328                                     dest->flags.perm_flags & ~perm_flags,
2329                                     dest->flags.tmp_flags  & ~tmp_flags);
2330         /* set new flags */
2331         procmsg_msginfo_set_flags(dest,
2332                                   ~dest->flags.perm_flags & perm_flags,
2333                                   ~dest->flags.tmp_flags  & tmp_flags);
2334 }
2335
2336 static void add_msginfo_to_cache(FolderItem *item, MsgInfo *newmsginfo, MsgInfo *flagsource)
2337 {
2338         /* update folder stats */
2339         if (MSG_IS_NEW(newmsginfo->flags))
2340                 item->new_msgs++;
2341         if (MSG_IS_UNREAD(newmsginfo->flags))
2342                 item->unread_msgs++;
2343         if (MSG_IS_UNREAD(newmsginfo->flags) && procmsg_msg_has_marked_parent(newmsginfo))
2344                 item->unreadmarked_msgs++;
2345         item->total_msgs++;
2346
2347         folder_item_update_freeze();
2348         msgcache_add_msg(item->cache, newmsginfo);
2349         copy_msginfo_flags(flagsource, newmsginfo);
2350         folder_item_update_with_msg(item,  F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT | F_ITEM_UPDATE_ADDMSG, newmsginfo);
2351         folder_item_update_thaw();
2352 }
2353
2354 static void remove_msginfo_from_cache(FolderItem *item, MsgInfo *msginfo)
2355 {
2356         MsgInfoUpdate msginfo_update;
2357
2358         if (!item->cache)
2359                 folder_item_read_cache(item);
2360
2361         if (MSG_IS_NEW(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
2362                 msginfo->folder->new_msgs--;
2363         if (MSG_IS_UNREAD(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
2364                 msginfo->folder->unread_msgs--;
2365         if (MSG_IS_UNREAD(msginfo->flags) && procmsg_msg_has_marked_parent(msginfo))
2366                 msginfo->folder->unreadmarked_msgs--;
2367         msginfo->folder->total_msgs--;
2368
2369         msginfo_update.msginfo = msginfo;
2370         msginfo_update.flags = MSGINFO_UPDATE_DELETED;
2371         hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
2372
2373         msgcache_remove_msg(item->cache, msginfo->msgnum);
2374         folder_item_update_with_msg(msginfo->folder, F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT | F_ITEM_UPDATE_REMOVEMSG, msginfo);
2375 }
2376
2377 gint folder_item_add_msg(FolderItem *dest, const gchar *file,
2378                          MsgFlags *flags, gboolean remove_source)
2379 {
2380         GSList file_list;
2381         MsgFileInfo fileinfo;
2382
2383         g_return_val_if_fail(dest != NULL, -1);
2384         g_return_val_if_fail(file != NULL, -1);
2385  
2386         fileinfo.msginfo = NULL;
2387         fileinfo.file = (gchar *)file;
2388         fileinfo.flags = flags;
2389         file_list.data = &fileinfo;
2390         file_list.next = NULL;
2391
2392         return folder_item_add_msgs(dest, &file_list, remove_source);
2393 }
2394
2395 gint folder_item_add_msgs(FolderItem *dest, GSList *file_list,
2396                           gboolean remove_source)
2397 {
2398         Folder *folder;
2399         gint ret, num, lastnum = -1;
2400         GSList *file_cur;
2401         GRelation *relation;
2402         MsgFileInfo *fileinfo = NULL;
2403         gboolean folderscan = FALSE;
2404
2405         g_return_val_if_fail(dest != NULL, -1);
2406         g_return_val_if_fail(file_list != NULL, -1);
2407         g_return_val_if_fail(dest->folder != NULL, -1);
2408
2409         folder = dest->folder;
2410
2411         relation = g_relation_new(2);
2412         g_relation_index(relation, 0, g_direct_hash, g_direct_equal);
2413
2414         if (folder->klass->add_msgs != NULL) {
2415                 ret = folder->klass->add_msgs(folder, dest, file_list, relation);
2416                 if (ret < 0) {
2417                         g_relation_destroy(relation);
2418                         return ret;
2419                 }
2420         } else {
2421                 for (file_cur = file_list; file_cur != NULL; file_cur = g_slist_next(file_cur)) {
2422                         fileinfo = (MsgFileInfo *) file_cur->data;
2423
2424                         ret = folder->klass->add_msg(folder, dest, fileinfo->file, fileinfo->flags);
2425                         if (ret < 0) {
2426                                 g_relation_destroy(relation);
2427                                 return ret;
2428                         }
2429                         g_relation_insert(relation, fileinfo, GINT_TO_POINTER(ret));
2430                 }
2431         }
2432
2433         for (file_cur = file_list; file_cur != NULL; file_cur = g_slist_next(file_cur)) {
2434                 GTuples *tuples;
2435
2436                 fileinfo = (MsgFileInfo *) file_cur->data;
2437                 tuples = g_relation_select(relation, fileinfo, 0);
2438                 num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
2439                 g_tuples_destroy(tuples);
2440
2441                 if (num >= 0) {
2442                         MsgInfo *newmsginfo;
2443
2444                         if (num == 0) {
2445                                 if (!folderscan) {
2446                                         folder_item_scan_full(dest, FALSE);
2447                                         folderscan = TRUE;
2448                                 }
2449                                 num = folder_item_get_msg_num_by_file(dest, fileinfo->file);
2450                         }
2451
2452                         if (num > lastnum)
2453                                 lastnum = num;
2454
2455                         if (num >= 0 && remove_source) {
2456                                 if (unlink(fileinfo->file) < 0)
2457                                         FILE_OP_ERROR(fileinfo->file, "unlink");
2458                         }
2459
2460                         if (num == 0)
2461                                 continue;
2462
2463                         if (!folderscan && 
2464                             ((newmsginfo = get_msginfo(dest, num)) != NULL)) {
2465                                 add_msginfo_to_cache(dest, newmsginfo, NULL);
2466                                 procmsg_msginfo_free(newmsginfo);
2467                         } else if ((newmsginfo = msgcache_get_msg(dest->cache, num)) != NULL) {
2468                                 /* TODO: set default flags */
2469                                 procmsg_msginfo_free(newmsginfo);
2470                         }
2471                 }
2472         }
2473
2474         g_relation_destroy(relation);
2475
2476         return lastnum;
2477 }
2478
2479 /*
2480 gint folder_item_move_msg(FolderItem *dest, MsgInfo *msginfo)
2481 {
2482         Folder *folder;
2483         gint num;
2484
2485         g_return_val_if_fail(dest != NULL, -1);
2486         g_return_val_if_fail(msginfo != NULL, -1);
2487
2488         folder = dest->folder;
2489         if (dest->last_num < 0) folder->scan(folder, dest);
2490
2491         num = folder->move_msg(folder, dest, msginfo);
2492         if (num > 0) dest->last_num = num;
2493
2494         return num;
2495 }
2496 */
2497                 
2498 FolderItem *folder_item_move_recursive(FolderItem *src, FolderItem *dest) 
2499 {
2500         GSList *mlist;
2501         FolderItem *new_item;
2502         FolderItem *next_item;
2503         GNode *srcnode;
2504         gchar *old_id, *new_id;
2505
2506         mlist = folder_item_get_msg_list(src);
2507
2508         /* move messages */
2509         debug_print("Moving %s to %s\n", src->path, dest->path);
2510         new_item = folder_create_folder(dest, src->name);
2511         if (new_item == NULL) {
2512                 printf("Can't create folder\n");
2513                 return NULL;
2514         }
2515         
2516         if (new_item->folder == NULL)
2517                 new_item->folder = dest->folder;
2518
2519         /* move messages */
2520         log_message(_("Moving %s to %s...\n"), 
2521                         src->name, new_item->path);
2522         if (mlist != NULL)
2523                 folder_item_move_msgs(new_item, mlist);
2524         
2525         /*copy prefs*/
2526         folder_item_prefs_copy_prefs(src, new_item);
2527         new_item->collapsed = src->collapsed;
2528         new_item->thread_collapsed = src->thread_collapsed;
2529         new_item->threaded  = src->threaded;
2530         new_item->ret_rcpt  = src->ret_rcpt;
2531         new_item->hide_read_msgs = src->hide_read_msgs;
2532         new_item->sort_key  = src->sort_key;
2533         new_item->sort_type = src->sort_type;
2534
2535         prefs_matcher_write_config();
2536         
2537         /* recurse */
2538         srcnode = src->folder->node;    
2539         srcnode = g_node_find(srcnode, G_PRE_ORDER, G_TRAVERSE_ALL, src);
2540         srcnode = srcnode->children;
2541         while (srcnode != NULL) {
2542                 if (srcnode && srcnode->data) {
2543                         next_item = (FolderItem*) srcnode->data;
2544                         srcnode = srcnode->next;
2545                         if (folder_item_move_recursive(next_item, new_item) == NULL)
2546                                 return NULL;
2547                 }
2548         }
2549         old_id = folder_item_get_identifier(src);
2550         new_id = folder_item_get_identifier(new_item);
2551         debug_print("updating rules : %s => %s\n", old_id, new_id);
2552
2553         /* if src supports removing, otherwise only copy folder */
2554         if (src->folder->klass->remove_folder != NULL)  
2555                 src->folder->klass->remove_folder(src->folder, src);
2556         folder_write_list();
2557
2558         if (old_id != NULL && new_id != NULL)
2559                 prefs_filtering_rename_path(old_id, new_id);
2560         g_free(old_id);
2561         g_free(new_id);
2562
2563         return new_item;
2564 }
2565
2566 gint folder_item_move_to(FolderItem *src, FolderItem *dest, FolderItem **new_item)
2567 {
2568         FolderItem *tmp = folder_item_parent(dest);
2569         gchar * src_identifier, * dst_identifier;
2570         gchar * phys_srcpath, * phys_dstpath;
2571         
2572         while (tmp) {
2573                 if (tmp == src) {
2574                         return F_MOVE_FAILED_DEST_IS_CHILD;
2575                 }
2576                 tmp = folder_item_parent(tmp);;
2577         }
2578         
2579         tmp = folder_item_parent(src);
2580         
2581         src_identifier = folder_item_get_identifier(src);
2582         dst_identifier = folder_item_get_identifier(dest);
2583         
2584         if(dst_identifier == NULL && dest->folder && folder_item_parent(dest) == NULL) {
2585                 /* dest can be a root folder */
2586                 dst_identifier = folder_get_identifier(dest->folder);
2587         }
2588         if (src_identifier == NULL || dst_identifier == NULL) {
2589                 debug_print("Can't get identifiers\n");
2590                 return F_MOVE_FAILED;
2591         }
2592
2593         if (src->folder != dest->folder) {
2594                 return F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX;
2595         }
2596
2597         phys_srcpath = folder_item_get_path(src);
2598         phys_dstpath = g_strconcat(folder_item_get_path(dest),
2599                        G_DIR_SEPARATOR_S,
2600                        g_path_get_basename(phys_srcpath),
2601                        NULL);
2602
2603         if (folder_item_parent(src) == dest || src == dest) {
2604                 g_free(src_identifier);
2605                 g_free(dst_identifier);
2606                 g_free(phys_srcpath);
2607                 g_free(phys_dstpath);
2608                 return F_MOVE_FAILED_DEST_IS_PARENT;
2609         }
2610         debug_print("moving \"%s\" to \"%s\"\n", phys_srcpath, phys_dstpath);
2611         if ((tmp = folder_item_move_recursive(src, dest)) == NULL) {
2612                 return F_MOVE_FAILED;
2613         }
2614         
2615         g_free(src_identifier);
2616         g_free(dst_identifier);
2617         g_free(phys_srcpath);
2618         g_free(phys_dstpath);
2619
2620         *new_item = tmp;
2621
2622         return F_MOVE_OK;
2623 }
2624
2625 /**
2626  * Copy a list of message to a new folder and remove
2627  * source messages if wanted
2628  */
2629 static gint do_copy_msgs(FolderItem *dest, GSList *msglist, gboolean remove_source)
2630 {
2631         Folder *folder;
2632         GSList *l;
2633         gint num, lastnum = -1;
2634         gboolean folderscan = FALSE;
2635         GRelation *relation;
2636
2637         g_return_val_if_fail(dest != NULL, -1);
2638         g_return_val_if_fail(msglist != NULL, -1);
2639
2640         folder = dest->folder;
2641
2642         g_return_val_if_fail(folder->klass->copy_msg != NULL, -1);
2643
2644         relation = g_relation_new(2);
2645         g_relation_index(relation, 0, g_direct_hash, g_direct_equal);
2646         g_relation_index(relation, 1, g_direct_hash, g_direct_equal);
2647
2648         for (l = msglist ; l != NULL ; l = g_slist_next(l)) {
2649                 MsgInfo * msginfo = (MsgInfo *) l->data;
2650
2651                 if (msginfo->planned_download != 0) {
2652                         int old_planned = msginfo->planned_download;
2653                         partial_unmark(msginfo);
2654                         /* little hack to reenable after */
2655                         msginfo->planned_download = old_planned;
2656                 }
2657         }
2658
2659         /* 
2660          * Copy messages to destination folder and 
2661          * store new message numbers in newmsgnums
2662          */
2663         if (folder->klass->copy_msgs != NULL) {
2664                 if (folder->klass->copy_msgs(folder, dest, msglist, relation) < 0) {
2665                         g_relation_destroy(relation);
2666                         return -1;
2667                 }
2668         } else {
2669                 for (l = msglist ; l != NULL ; l = g_slist_next(l)) {
2670                         MsgInfo * msginfo = (MsgInfo *) l->data;
2671
2672                         num = folder->klass->copy_msg(folder, dest, msginfo);
2673                         g_relation_insert(relation, msginfo, GINT_TO_POINTER(num));
2674                 }
2675         }
2676
2677         /* Read cache for dest folder */
2678         if (!dest->cache) folder_item_read_cache(dest);
2679
2680         /* 
2681          * Fetch new MsgInfos for new messages in dest folder,
2682          * add them to the msgcache and update folder message counts
2683          */
2684         if (g_relation_count(relation, GINT_TO_POINTER(0), 1) > 0) {
2685                 folder_item_scan_full(dest, FALSE);
2686                 folderscan = TRUE;
2687         }
2688
2689         for (l = msglist; l != NULL; l = g_slist_next(l)) {
2690                 MsgInfo *msginfo = (MsgInfo *) l->data;
2691                 GTuples *tuples;
2692
2693                 tuples = g_relation_select(relation, msginfo, 0);
2694                 num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
2695                 g_tuples_destroy(tuples);
2696
2697                 if (num >= 0) {
2698                         MsgInfo *newmsginfo = NULL;
2699
2700                         if (folderscan) {
2701                                 if (msginfo->msgid != NULL) {
2702                                         newmsginfo = folder_item_get_msginfo_by_msgid(dest, msginfo->msgid);
2703                                         if (newmsginfo != NULL) {
2704                                                 copy_msginfo_flags(msginfo, newmsginfo);
2705                                                 num = newmsginfo->msgnum;
2706                                         }
2707                                 }
2708                         } else {
2709                                 newmsginfo = get_msginfo(dest, num);
2710                                 if (newmsginfo != NULL) {
2711                                         add_msginfo_to_cache(dest, newmsginfo, msginfo);
2712                                 }
2713                         }
2714
2715                         if (msginfo->planned_download 
2716                             == POP3_PARTIAL_DLOAD_DELE) {
2717                                 partial_mark_for_delete(newmsginfo);
2718                         }
2719                         if (msginfo->planned_download 
2720                             == POP3_PARTIAL_DLOAD_DLOAD) {
2721                                 partial_mark_for_download(newmsginfo);
2722                         }
2723                         procmsg_msginfo_free(newmsginfo);
2724
2725
2726                         if (num > lastnum)
2727                                 lastnum = num;
2728                 }
2729         }
2730
2731         if (remove_source) {
2732                 MsgInfo *msginfo = (MsgInfo *) msglist->data;
2733                 FolderItem *item = msginfo->folder;
2734                 /*
2735                  * Remove source messages from their folders if
2736                  * copying was successfull and update folder
2737                  * message counts
2738                  */
2739                 if (item->folder->klass->remove_msgs) {
2740                         item->folder->klass->remove_msgs(item->folder,
2741                                                                 msginfo->folder,
2742                                                                 msglist,
2743                                                                 relation);
2744                 }
2745                 for (l = msglist; l != NULL; l = g_slist_next(l)) {
2746                         msginfo = (MsgInfo *) l->data;
2747                         item = msginfo->folder;
2748                         GTuples *tuples;
2749
2750                         tuples = g_relation_select(relation, msginfo, 0);
2751                         num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
2752                         g_tuples_destroy(tuples);
2753
2754                         if ((num >= 0) && (item->folder->klass->remove_msg != NULL)) {
2755                                 if (!item->folder->klass->remove_msgs)
2756                                         item->folder->klass->remove_msg(item->folder,
2757                                                                 msginfo->folder,
2758                                                                 msginfo->msgnum);
2759                                 remove_msginfo_from_cache(item, msginfo);
2760                         }
2761                 }
2762         }
2763
2764         g_relation_destroy(relation);
2765         return lastnum;
2766 }
2767
2768 /**
2769  * Move a message to a new folder.
2770  *
2771  * \param dest Destination folder
2772  * \param msginfo The message
2773  */
2774 gint folder_item_move_msg(FolderItem *dest, MsgInfo *msginfo)
2775 {
2776         GSList list;
2777
2778         g_return_val_if_fail(dest != NULL, -1);
2779         g_return_val_if_fail(msginfo != NULL, -1);
2780
2781         list.data = msginfo;
2782         list.next = NULL;
2783
2784         return do_copy_msgs(dest, &list, TRUE);
2785 }
2786
2787 /**
2788  * Move a list of messages to a new folder.
2789  *
2790  * \param dest Destination folder
2791  * \param msglist List of messages
2792  */
2793 gint folder_item_move_msgs(FolderItem *dest, GSList *msglist)
2794 {
2795         gint result = -1;
2796         g_return_val_if_fail(dest != NULL, -1);
2797         g_return_val_if_fail(msglist != NULL, -1);
2798         inc_lock();
2799         result = do_copy_msgs(dest, msglist, TRUE);
2800         inc_unlock();
2801         return result;
2802 }
2803
2804 /**
2805  * Copy a message to a new folder.
2806  *
2807  * \param dest Destination folder
2808  * \param msginfo The message
2809  */
2810 gint folder_item_copy_msg(FolderItem *dest, MsgInfo *msginfo)
2811 {
2812         GSList list;
2813
2814         g_return_val_if_fail(dest != NULL, -1);
2815         g_return_val_if_fail(msginfo != NULL, -1);
2816     
2817         list.data = msginfo;
2818         list.next = NULL;
2819
2820         return do_copy_msgs(dest, &list, FALSE);
2821 }
2822
2823 /**
2824  * Copy a list of messages to a new folder.
2825  *
2826  * \param dest Destination folder
2827  * \param msglist List of messages
2828  */
2829 gint folder_item_copy_msgs(FolderItem *dest, GSList *msglist)
2830 {
2831         gint result;
2832         g_return_val_if_fail(dest != NULL, -1);
2833         g_return_val_if_fail(msglist != NULL, -1);
2834
2835         inc_lock();
2836         result = do_copy_msgs(dest, msglist, FALSE);
2837         inc_unlock();
2838         
2839         return result;
2840 }
2841
2842 gint folder_item_remove_msg(FolderItem *item, gint num)
2843 {
2844         Folder *folder;
2845         gint ret;
2846         MsgInfo *msginfo;
2847
2848         g_return_val_if_fail(item != NULL, -1);
2849         folder = item->folder;
2850         g_return_val_if_fail(folder->klass->remove_msg != NULL, -1);
2851
2852         if (!item->cache) folder_item_read_cache(item);
2853
2854         ret = folder->klass->remove_msg(folder, item, num);
2855
2856         msginfo = msgcache_get_msg(item->cache, num);
2857         if (msginfo != NULL) {
2858                 remove_msginfo_from_cache(item, msginfo);
2859                 procmsg_msginfo_free(msginfo);
2860         }
2861
2862         return ret;
2863 }
2864
2865 gint folder_item_remove_msgs(FolderItem *item, GSList *msglist)
2866 {
2867         Folder *folder;
2868         gint ret = 0;
2869
2870         g_return_val_if_fail(item != NULL, -1);
2871         folder = item->folder;
2872         g_return_val_if_fail(folder != NULL, -1);
2873         inc_lock();
2874         if (!item->cache) folder_item_read_cache(item);
2875
2876         folder_item_update_freeze();
2877         if (item->folder->klass->remove_msgs) {
2878                 ret = item->folder->klass->remove_msgs(item->folder,
2879                                                         item,
2880                                                         msglist,
2881                                                         NULL);
2882         }
2883         while (ret == 0 && msglist != NULL) {
2884                 MsgInfo *msginfo = (MsgInfo *)msglist->data;
2885                 if (MSG_IS_LOCKED(msginfo->flags)) {
2886                         msglist = msglist->next;
2887                         continue;
2888                 }
2889                 if (!item->folder->klass->remove_msgs)
2890                         ret = folder_item_remove_msg(item, msginfo->msgnum);
2891                 if (ret != 0) break;
2892                 msgcache_remove_msg(item->cache, msginfo->msgnum);
2893                 msglist = msglist->next;
2894         }
2895         folder_item_scan_full(item, FALSE);
2896         folder_item_update_thaw();
2897         inc_unlock();
2898         return ret;
2899 }
2900
2901 gint folder_item_remove_all_msg(FolderItem *item)
2902 {
2903         Folder *folder;
2904         gint result;
2905
2906         g_return_val_if_fail(item != NULL, -1);
2907
2908         folder = item->folder;
2909
2910         inc_lock();
2911         if (folder->klass->remove_all_msg != NULL) {
2912                 result = folder->klass->remove_all_msg(folder, item);
2913
2914                 if (result == 0) {
2915                         folder_item_free_cache(item);
2916                         item->cache = msgcache_new();
2917                 }
2918         } else {
2919                 MsgInfoList *msglist;
2920
2921                 msglist = folder_item_get_msg_list(item);
2922                 result = folder_item_remove_msgs(item, msglist);
2923                 procmsg_msg_list_free(msglist);
2924         }
2925
2926         if (result == 0) {
2927                 item->new_msgs = 0;
2928                 item->unread_msgs = 0;
2929                 item->unreadmarked_msgs = 0;
2930                 item->total_msgs = 0;
2931                 folder_item_update(item, F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT);
2932         }
2933
2934         inc_unlock();
2935         return result;
2936 }
2937
2938 void folder_item_change_msg_flags(FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
2939 {
2940         g_return_if_fail(item != NULL);
2941         g_return_if_fail(msginfo != NULL);
2942         
2943         if (item->folder->klass->change_flags != NULL) {
2944                 item->folder->klass->change_flags(item->folder, item, msginfo, newflags);
2945         } else {
2946                 msginfo->flags.perm_flags = newflags;
2947         }
2948 }
2949
2950 gboolean folder_item_is_msg_changed(FolderItem *item, MsgInfo *msginfo)
2951 {
2952         Folder *folder;
2953
2954         g_return_val_if_fail(item != NULL, FALSE);
2955
2956         folder = item->folder;
2957
2958         g_return_val_if_fail(folder->klass->is_msg_changed != NULL, -1);
2959
2960         return folder->klass->is_msg_changed(folder, item, msginfo);
2961 }
2962
2963 gchar *folder_item_get_cache_file(FolderItem *item)
2964 {
2965         gchar *path;
2966         gchar *file;
2967
2968         g_return_val_if_fail(item != NULL, NULL);
2969         g_return_val_if_fail(item->path != NULL, NULL);
2970
2971         path = folder_item_get_path(item);
2972         g_return_val_if_fail(path != NULL, NULL);
2973         if (!is_dir_exist(path))
2974                 make_dir_hier(path);
2975         file = g_strconcat(path, G_DIR_SEPARATOR_S, CACHE_FILE, NULL);
2976         g_free(path);
2977
2978         return file;
2979 }
2980
2981 gchar *folder_item_get_mark_file(FolderItem *item)
2982 {
2983         gchar *path;
2984         gchar *file;
2985
2986         g_return_val_if_fail(item != NULL, NULL);
2987         g_return_val_if_fail(item->path != NULL, NULL);
2988
2989         path = folder_item_get_path(item);
2990         g_return_val_if_fail(path != NULL, NULL);
2991         if (!is_dir_exist(path))
2992                 make_dir_hier(path);
2993         file = g_strconcat(path, G_DIR_SEPARATOR_S, MARK_FILE, NULL);
2994         g_free(path);
2995
2996         return file;
2997 }
2998
2999 static gpointer xml_to_folder_item(gpointer nodedata, gpointer data)
3000 {
3001         XMLNode *xmlnode = (XMLNode *) nodedata;
3002         Folder *folder = (Folder *) data;
3003         FolderItem *item;
3004
3005         g_return_val_if_fail(xmlnode != NULL, NULL);
3006         g_return_val_if_fail(folder != NULL, NULL);
3007
3008         if (strcmp2(xmlnode->tag->tag, "folderitem") != 0) {
3009                 g_warning("tag name != \"folderitem\"\n");
3010                 return NULL;
3011         }
3012
3013         item = folder_item_new(folder, "", "");
3014         if (folder->klass->item_set_xml != NULL)
3015                 folder->klass->item_set_xml(folder, item, xmlnode->tag);
3016         else
3017                 folder_item_set_xml(folder, item, xmlnode->tag);
3018
3019         item->folder = folder;
3020
3021         switch (item->stype) {
3022         case F_INBOX:  folder->inbox  = item; break;
3023         case F_OUTBOX: folder->outbox = item; break;
3024         case F_DRAFT:  folder->draft  = item; break;
3025         case F_QUEUE:  folder->queue  = item; break;
3026         case F_TRASH:  folder->trash  = item; break;
3027         default:       break;
3028         }
3029         folder_item_prefs_read_config(item);
3030
3031         return item;
3032 }
3033
3034 static gboolean folder_item_set_node(GNode *node, gpointer data)
3035 {
3036         FolderItem *item = (FolderItem *) node->data;
3037         item->node = node;
3038
3039         return FALSE;
3040 }
3041
3042 static Folder *folder_get_from_xml(GNode *node)
3043 {
3044         Folder *folder;
3045         XMLNode *xmlnode;
3046         GList *list;
3047         FolderClass *klass = NULL;
3048         GNode *cur;
3049
3050         g_return_val_if_fail(node->data != NULL, NULL);
3051
3052         xmlnode = node->data;
3053         if (strcmp2(xmlnode->tag->tag, "folder") != 0) {
3054                 g_warning("tag name != \"folder\"\n");
3055                 return NULL;
3056         }
3057         list = xmlnode->tag->attr;
3058         for (; list != NULL; list = list->next) {
3059                 XMLAttr *attr = list->data;
3060
3061                 if (!attr || !attr->name || !attr->value) continue;
3062                 if (!strcmp(attr->name, "type"))
3063                         klass = folder_get_class_from_string(attr->value);
3064         }
3065         if (klass == NULL)
3066                 return NULL;
3067
3068         folder = folder_new(klass, "", "");
3069         g_return_val_if_fail(folder != NULL, NULL);
3070
3071         if (klass->set_xml)
3072                 klass->set_xml(folder, xmlnode->tag);
3073         else
3074                 folder_set_xml(folder, xmlnode->tag);
3075
3076         cur = node->children;
3077         while (cur != NULL) {
3078                 GNode *itemnode;
3079
3080                 itemnode = g_node_map(cur, xml_to_folder_item, (gpointer) folder);
3081                 g_node_append(folder->node, itemnode);
3082                 cur = cur->next;
3083         }
3084         g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, folder_item_set_node, NULL);
3085         
3086         return folder;
3087 }
3088
3089 static gchar *folder_get_list_path(void)
3090 {
3091         static gchar *filename = NULL;
3092
3093         if (!filename)
3094                 filename =  g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
3095                                         FOLDER_LIST, NULL);
3096
3097         return filename;
3098 }
3099
3100 #define PUT_ESCAPE_STR(fp, attr, str)                   \
3101 {                                                       \
3102         fputs(" " attr "=\"", fp);                      \
3103         xml_file_put_escape_str(fp, str);               \
3104         fputs("\"", fp);                                \
3105 }
3106
3107 static gpointer folder_item_to_xml(gpointer nodedata, gpointer data)
3108 {
3109         FolderItem *item = (FolderItem *) nodedata;
3110         XMLTag *tag;
3111
3112         g_return_val_if_fail(item != NULL, NULL);
3113
3114         if (item->folder->klass->item_get_xml != NULL)
3115                 tag = item->folder->klass->item_get_xml(item->folder, item);
3116         else
3117                 tag = folder_item_get_xml(item->folder, item);
3118
3119         return xml_node_new(tag, NULL);;
3120 }
3121
3122 static GNode *folder_get_xml_node(Folder *folder)
3123 {
3124         GNode *node;
3125         XMLNode *xmlnode;
3126         XMLTag *tag;
3127
3128         g_return_val_if_fail(folder != NULL, NULL);
3129
3130         if (folder->klass->get_xml != NULL)
3131                 tag = folder->klass->get_xml(folder);
3132         else
3133                 tag = folder_get_xml(folder);
3134
3135         xml_tag_add_attr(tag, xml_attr_new("type", folder->klass->idstr));
3136
3137         xmlnode = xml_node_new(tag, NULL);
3138
3139         node = g_node_new(xmlnode);
3140         if (folder->node->children) {
3141                 GNode *cur;
3142
3143                 cur = folder->node->children;
3144                 while (cur) {
3145                         GNode *xmlnode;
3146
3147                         xmlnode = g_node_map(cur, folder_item_to_xml, (gpointer) folder);
3148                         g_node_append(node, xmlnode);
3149                         cur = cur->next;
3150                 }
3151         }
3152
3153         return node;
3154 }
3155
3156 static void folder_update_op_count_rec(GNode *node)
3157 {
3158         FolderItem *fitem = FOLDER_ITEM(node->data);
3159
3160         if (g_node_depth(node) > 0) {
3161                 if (fitem->op_count > 0) {
3162                         fitem->op_count = 0;
3163                         folder_item_update(fitem, F_ITEM_UPDATE_MSGCNT);
3164                 }
3165                 if (node->children) {
3166                         GNode *child;
3167
3168                         child = node->children;
3169                         while (child) {
3170                                 GNode *cur;
3171
3172                                 cur = child;
3173                                 child = cur->next;
3174                                 folder_update_op_count_rec(cur);
3175                         }
3176                 }
3177         }
3178 }
3179
3180 void folder_update_op_count(void) 
3181 {
3182         GList *cur;
3183         Folder *folder;
3184
3185         for (cur = folder_list; cur != NULL; cur = cur->next) {
3186                 folder = cur->data;
3187                 folder_update_op_count_rec(folder->node);
3188         }
3189 }
3190
3191 typedef struct _type_str {
3192         gchar * str;
3193         gint type;
3194 } type_str;
3195
3196
3197 /*
3198 static gchar * folder_item_get_tree_identifier(FolderItem * item)
3199 {
3200         if (item->parent != NULL) {
3201                 gchar * path;
3202                 gchar * id;
3203
3204                 path = folder_item_get_tree_identifier(item->parent);
3205                 if (path == NULL)
3206                         return NULL;
3207
3208                 id = g_strconcat(path, "/", item->name, NULL);
3209                 g_free(path);
3210
3211                 return id;
3212         }
3213         else {
3214                 return g_strconcat("/", item->name, NULL);
3215         }
3216 }
3217 */
3218
3219 /* CLAWS: temporary local folder for filtering */
3220 #define TEMP_FOLDER "TEMP_FOLDER"
3221 #define PROCESSING_FOLDER_ITEM "processing"     
3222
3223 static FolderItem *processing_folder_item;
3224
3225 static void folder_create_processing_folder(void)
3226 {
3227         Folder *processing_folder;
3228         gchar      *tmpname;
3229
3230         if ((processing_folder = folder_find_from_name(TEMP_FOLDER, mh_get_class())) == NULL) {
3231                 gchar *tmppath;
3232
3233                 tmppath =
3234                     g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
3235                                 "tempfolder", NULL);
3236                 processing_folder =
3237                     folder_new(mh_get_class(), TEMP_FOLDER, tmppath);
3238                 g_free(tmppath);
3239         }
3240         g_assert(processing_folder != NULL);
3241
3242         debug_print("tmpparentroot %s\n", LOCAL_FOLDER(processing_folder)->rootpath);
3243         if (LOCAL_FOLDER(processing_folder)->rootpath[0] == '/')
3244                 tmpname = g_strconcat(LOCAL_FOLDER(processing_folder)->rootpath,
3245                                       G_DIR_SEPARATOR_S, PROCESSING_FOLDER_ITEM,
3246                                       NULL);
3247         else
3248                 tmpname = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
3249                                       LOCAL_FOLDER(processing_folder)->rootpath,
3250                                       G_DIR_SEPARATOR_S, PROCESSING_FOLDER_ITEM,
3251                                       NULL);
3252
3253         if (!is_dir_exist(tmpname)) {
3254                 debug_print("*TMP* creating %s\n", tmpname);
3255                 processing_folder_item = processing_folder->klass->create_folder(processing_folder,
3256                                                                                  processing_folder->node->data,
3257                                                                                  PROCESSING_FOLDER_ITEM);
3258         } else {
3259                 debug_print("*TMP* already created\n");
3260                 processing_folder_item = folder_item_new(processing_folder, PROCESSING_FOLDER_ITEM, PROCESSING_FOLDER_ITEM);
3261                 g_assert(processing_folder_item);
3262                 folder_item_append(processing_folder->node->data, processing_folder_item);
3263         }
3264         g_assert(processing_folder_item != NULL);
3265         g_free(tmpname);
3266 }
3267
3268 FolderItem *folder_get_default_processing(void)
3269 {
3270         if (!processing_folder_item) {
3271                 folder_create_processing_folder();
3272         }
3273         return processing_folder_item;
3274 }
3275
3276 /* folder_persist_prefs_new() - return hash table with persistent
3277  * settings (and folder name as key). 
3278  * (note that in claws other options are in the folder_item_prefs_RC
3279  * file, so those don't need to be included in PersistPref yet) 
3280  */
3281 GHashTable *folder_persist_prefs_new(Folder *folder)
3282 {
3283         GHashTable *pptable;
3284
3285         g_return_val_if_fail(folder, NULL);
3286         pptable = g_hash_table_new(g_str_hash, g_str_equal);
3287         folder_get_persist_prefs_recursive(folder->node, pptable);
3288         return pptable;
3289 }
3290
3291 void folder_persist_prefs_free(GHashTable *pptable)
3292 {
3293         g_return_if_fail(pptable);
3294         g_hash_table_foreach_remove(pptable, persist_prefs_free, NULL);
3295         g_hash_table_destroy(pptable);
3296 }
3297
3298 const PersistPrefs *folder_get_persist_prefs(GHashTable *pptable, const char *name)
3299 {
3300         if (pptable == NULL || name == NULL) return NULL;
3301         return g_hash_table_lookup(pptable, name);
3302 }
3303
3304 void folder_item_restore_persist_prefs(FolderItem *item, GHashTable *pptable)
3305 {
3306         const PersistPrefs *pp;
3307         gchar *id = folder_item_get_identifier(item);
3308
3309         pp = folder_get_persist_prefs(pptable, id); 
3310         g_free(id);
3311
3312         if (!pp) return;
3313
3314         /* CLAWS: since not all folder properties have been migrated to 
3315          * folderlist.xml, we need to call the old stuff first before
3316          * setting things that apply both to Main and Claws. */
3317         folder_item_prefs_read_config(item); 
3318
3319         item->collapsed = pp->collapsed;
3320         item->thread_collapsed = pp->thread_collapsed;
3321         item->threaded  = pp->threaded;
3322         item->ret_rcpt  = pp->ret_rcpt;
3323         item->hide_read_msgs = pp->hide_read_msgs;
3324         item->sort_key  = pp->sort_key;
3325         item->sort_type = pp->sort_type;
3326 }
3327
3328 static void folder_get_persist_prefs_recursive(GNode *node, GHashTable *pptable)
3329 {
3330         FolderItem *item = FOLDER_ITEM(node->data);
3331         PersistPrefs *pp;
3332         GNode *child, *cur;
3333         gchar *id;
3334
3335         g_return_if_fail(node != NULL);
3336         g_return_if_fail(item != NULL);
3337
3338         /* NOTE: item->path == NULL means top level folder; not interesting
3339          * to store preferences of that one.  */
3340         if (item->path) {
3341                 id = folder_item_get_identifier(item);
3342                 pp = g_new0(PersistPrefs, 1);
3343                 g_return_if_fail(pp != NULL);
3344                 pp->collapsed = item->collapsed;
3345                 pp->thread_collapsed = item->thread_collapsed;
3346                 pp->threaded  = item->threaded;
3347                 pp->ret_rcpt  = item->ret_rcpt; 
3348                 pp->hide_read_msgs = item->hide_read_msgs;
3349                 pp->sort_key  = item->sort_key;
3350                 pp->sort_type = item->sort_type;
3351                 g_hash_table_insert(pptable, id, pp);
3352         }
3353
3354         if (node->children) {
3355                 child = node->children;
3356                 while (child) {
3357                         cur = child;
3358                         child = cur->next;
3359                         folder_get_persist_prefs_recursive(cur, pptable);
3360                 }
3361         }       
3362 }
3363
3364 static gboolean persist_prefs_free(gpointer key, gpointer val, gpointer data)
3365 {
3366         if (key) 
3367                 g_free(key);
3368         if (val) 
3369                 g_free(val);
3370         return TRUE;    
3371 }
3372
3373 void folder_item_apply_processing(FolderItem *item)
3374 {
3375         GSList *processing_list;
3376         GSList *mlist, *cur;
3377         guint total = 0, curmsg = 0;
3378         gchar buf[32];
3379
3380         g_return_if_fail(item != NULL);
3381         
3382         processing_list = item->prefs->processing;
3383
3384         folder_item_update_freeze();
3385
3386         mlist = folder_item_get_msg_list(item);
3387         total = g_slist_length(mlist);
3388         statusbar_print_all(_("Processing messages..."));
3389         for (cur = mlist ; cur != NULL ; cur = cur->next) {
3390                 MsgInfo * msginfo;
3391
3392                 msginfo = (MsgInfo *) cur->data;
3393                 
3394                 /* reset parameters that can be modified by processing */
3395                 msginfo->hidden = 0;
3396                 msginfo->score = 0;
3397                 curmsg ++;
3398                 if (curmsg % 10 == 0) {
3399                         g_snprintf(buf, sizeof(buf), "%d / %d",
3400                                    curmsg, total);
3401                         gtk_progress_bar_set_text
3402                                 (GTK_PROGRESS_BAR(mainwindow_get_mainwindow()->progressbar), buf);
3403                         gtk_progress_bar_set_fraction
3404                         (GTK_PROGRESS_BAR(mainwindow_get_mainwindow()->progressbar),
3405                          (gfloat)curmsg / (gfloat)total);
3406                 }
3407                 /* apply pre global rules */
3408                 filter_message_by_msginfo(pre_global_processing, msginfo);
3409                 
3410                 /* apply rules of the folder */
3411                 filter_message_by_msginfo(processing_list, msginfo);
3412
3413                 /* apply post global rules */
3414                 filter_message_by_msginfo(post_global_processing, msginfo);
3415                 
3416                 procmsg_msginfo_free(msginfo);
3417         }
3418         g_slist_free(mlist);
3419         
3420         gtk_progress_bar_set_text
3421                 (GTK_PROGRESS_BAR(mainwindow_get_mainwindow()->progressbar), "");
3422         gtk_progress_bar_set_fraction
3423                 (GTK_PROGRESS_BAR(mainwindow_get_mainwindow()->progressbar), 0);
3424         statusbar_pop_all();
3425
3426         folder_item_update_thaw();
3427 }
3428
3429 /*
3430  *  functions for handling FolderItem content changes
3431  */
3432 static gint folder_item_update_freeze_cnt = 0;
3433
3434 static void folder_item_update_with_msg(FolderItem *item, FolderItemUpdateFlags update_flags, MsgInfo *msg)
3435 {
3436         if (folder_item_update_freeze_cnt == 0 /* || (msg != NULL && item->opened) */) {
3437                 FolderItemUpdateData source;
3438         
3439                 source.item = item;
3440                 source.update_flags = update_flags;
3441                 source.msg = msg;
3442                 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &source);
3443         } else {
3444                 item->update_flags |= update_flags & ~(F_ITEM_UPDATE_ADDMSG | F_ITEM_UPDATE_REMOVEMSG);
3445         }
3446 }
3447
3448 /**
3449  * Notify the folder system about changes to a folder. If the
3450  * update system is not frozen the FOLDER_ITEM_UPDATE_HOOKLIST will
3451  * be invoked, otherwise the changes will be remebered until
3452  * the folder system is thawed.
3453  *
3454  * \param item The FolderItem that was changed
3455  * \param update_flags Type of changed that was made
3456  */
3457 void folder_item_update(FolderItem *item, FolderItemUpdateFlags update_flags)
3458 {
3459         folder_item_update_with_msg(item, update_flags, NULL);
3460 }
3461
3462 void folder_item_update_recursive(FolderItem *item, FolderItemUpdateFlags update_flags)
3463 {
3464         GNode *node = item->folder->node;       
3465
3466         node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
3467         node = node->children;
3468
3469         folder_item_update(item, update_flags);
3470         while (node != NULL) {
3471                 if (node && node->data) {
3472                         FolderItem *next_item = (FolderItem*) node->data;
3473
3474                         folder_item_update(next_item, update_flags);
3475                 }
3476                 node = node->next;
3477         }
3478 }
3479
3480 void folder_item_update_freeze(void)
3481 {
3482         folder_item_update_freeze_cnt++;
3483 }
3484
3485 static void folder_item_update_func(FolderItem *item, gpointer data)
3486 {
3487         FolderItemUpdateData source;
3488     
3489         if (item->update_flags) {
3490                 source.item = item;
3491                 source.update_flags = item->update_flags;
3492                 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &source);                             
3493                 item->update_flags = 0;
3494         }
3495 }
3496
3497 void folder_item_update_thaw(void)
3498 {
3499         if (folder_item_update_freeze_cnt > 0)
3500                 folder_item_update_freeze_cnt--;
3501         if (folder_item_update_freeze_cnt == 0) {
3502                 /* Update all folders */
3503                 folder_func_to_all_folders(folder_item_update_func, NULL);
3504         }
3505 }
3506
3507 void folder_item_set_batch (FolderItem *item, gboolean batch)
3508 {
3509         if (item->folder->klass->set_batch) {
3510                 item->folder->klass->set_batch(item->folder, item, batch);
3511         }
3512 }
3513
3514 #undef PUT_ESCAPE_STR