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