ac6828f590563de94ca1cd432d2b4d7bde1d8c3b
[claws.git] / src / folder.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2011 Hiroyuki Yamamoto and the Claws Mail team
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  * 
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #include "defs.h"
25
26 #include <glib.h>
27 #include <glib/gi18n.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <unistd.h>
33 #include <stdlib.h>
34 #ifdef WIN32
35 #include <w32lib.h>
36 #endif
37
38 #include "folder.h"
39 #include "session.h"
40 #include "inc.h"
41 #include "imap.h"
42 #include "news.h"
43 #include "mh.h"
44 #include "utils.h"
45 #include "xml.h"
46 #include "codeconv.h"
47 #include "prefs_gtk.h"
48 #include "account.h"
49 #include "filtering.h"
50 #include "procheader.h"
51 #include "hooks.h"
52 #include "log.h"
53 #include "folder_item_prefs.h"
54 #include "remotefolder.h"
55 #include "partial_download.h"
56 #include "statusbar.h"
57 #include "gtkutils.h"
58 #include "timing.h"
59 #include "compose.h"
60 #include "main.h"
61
62 /* Dependecies to be removed ?! */
63 #include "prefs_common.h"
64 #include "prefs_account.h"
65
66 /* Define possible missing constants for Windows. */
67 #ifdef G_OS_WIN32
68 # ifndef S_IRGRP
69 # define S_IRGRP 0
70 # define S_IWGRP 0
71 # endif
72 # ifndef S_IROTH
73 # define S_IROTH 0
74 # define S_IWOTH 0
75 # endif
76 #endif
77
78 static GList *folder_list = NULL;
79 static GSList *class_list = NULL;
80 static GSList *folder_unloaded_list = NULL;
81
82 void folder_init                (Folder         *folder,
83                                  const gchar    *name);
84
85 static gchar *folder_item_get_cache_file        (FolderItem     *item);
86 static gchar *folder_item_get_mark_file (FolderItem     *item);
87 static gchar *folder_item_get_tags_file (FolderItem     *item);
88 static gchar *folder_get_list_path      (void);
89 static GNode *folder_get_xml_node       (Folder         *folder);
90 static Folder *folder_get_from_xml      (GNode          *node);
91 static void folder_update_op_count_rec  (GNode          *node);
92
93
94 static void folder_get_persist_prefs_recursive
95                                         (GNode *node, GHashTable *pptable);
96 static gboolean persist_prefs_free      (gpointer key, gpointer val, gpointer data);
97 static void folder_item_read_cache              (FolderItem *item);
98 gint folder_item_scan_full              (FolderItem *item, gboolean filtering);
99 static void folder_item_update_with_msg (FolderItem *item, FolderItemUpdateFlags update_flags,
100                                          MsgInfo *msg);
101 static GHashTable *folder_persist_prefs_new     (Folder *folder);
102 static void folder_persist_prefs_free           (GHashTable *pptable);
103 static void folder_item_restore_persist_prefs   (FolderItem *item, GHashTable *pptable);
104
105 void folder_system_init(void)
106 {
107         folder_register_class(mh_get_class());
108         folder_register_class(imap_get_class());
109         folder_register_class(news_get_class());
110 }
111
112 static GSList *folder_get_class_list(void)
113 {
114         return class_list;
115 }
116
117 void folder_register_class(FolderClass *klass)
118 {
119         GSList *xmllist, *cur;
120
121         debug_print("registering folder class %s\n", klass->idstr);
122
123         class_list = g_slist_append(class_list, klass);
124
125         xmllist = g_slist_copy(folder_unloaded_list);
126         for (cur = xmllist; cur != NULL; cur = g_slist_next(cur)) {
127                 GNode *node = (GNode *) cur->data;
128                 XMLNode *xmlnode = (XMLNode *) node->data;
129                 GList *cur = xmlnode->tag->attr;
130
131                 for (; cur != NULL; cur = g_list_next(cur)) {
132                         XMLAttr *attr = (XMLAttr *) cur->data;
133
134                         if (!attr || !attr->name || !attr->value) continue;
135                         if (!strcmp(attr->name, "type") && !strcmp(attr->value, klass->idstr)) {
136                                 Folder *folder;
137
138                                 folder = folder_get_from_xml(node);
139                                 if (folder) {
140                                         folder_add(folder);
141                                         folder_unloaded_list = g_slist_remove(folder_unloaded_list, node);
142                                 }
143                                 cur = NULL;
144                                 continue;
145                         }
146                 }
147         }
148         g_slist_free(xmllist);
149 }
150
151 void folder_unregister_class(FolderClass *klass)
152 {
153         GList *folderlist, *cur;
154
155         debug_print("unregistering folder class %s\n", klass->idstr);
156
157         class_list = g_slist_remove(class_list, klass);
158
159         folderlist = g_list_copy(folder_get_list());
160         for (cur = folderlist; cur != NULL; cur = g_list_next(cur)) {
161                 Folder *folder = (Folder *) cur->data;
162
163                 if (folder->klass == klass) {
164                         GNode *xmlnode = folder_get_xml_node(folder);
165                         folder_unloaded_list = g_slist_append(folder_unloaded_list, xmlnode);
166                         folder_destroy(folder);
167                 }
168         }
169         g_list_free(folderlist);
170 }
171
172 Folder *folder_new(FolderClass *klass, const gchar *name, const gchar *path)
173 {
174         Folder *folder = NULL;
175         FolderItem *item;
176
177         cm_return_val_if_fail(klass != NULL, NULL);
178
179         name = name ? name : path;
180         folder = klass->new_folder(name, path);
181
182         /* Create root folder item */
183         item = folder_item_new(folder, name, NULL);
184         if (item == NULL) {
185                 return NULL;
186         }
187         item->folder = folder;
188         folder->node = item->node = g_node_new(item);
189         folder->data = NULL;
190
191         return folder;
192 }
193
194 void folder_init(Folder *folder, const gchar *name)
195 {
196         cm_return_if_fail(folder != NULL);
197
198         folder_set_name(folder, name);
199
200         /* Init folder data */
201         folder->account = NULL;
202         folder->sort = 0;
203         folder->inbox = NULL;
204         folder->outbox = NULL;
205         folder->draft = NULL;
206         folder->queue = NULL;
207         folder->trash = NULL;
208 }
209
210 static void reset_parent_type(FolderItem *item, gpointer data) {
211         item->parent_stype = -1;
212 }
213
214 void folder_item_change_type(FolderItem *item, SpecialFolderItemType newtype)
215 {
216         Folder *folder = NULL;
217         FolderUpdateData hookdata;
218
219         if (item == NULL)
220                 return;
221
222         folder = item->folder;
223         /* unset previous root of newtype */
224         switch(newtype) {
225         case F_INBOX:
226                 folder_item_change_type(folder->inbox, F_NORMAL);
227                 folder->inbox = item;
228                 break;
229         case F_OUTBOX:
230                 folder_item_change_type(folder->outbox, F_NORMAL);
231                 folder->outbox = item;
232                 break;
233         case F_QUEUE:
234                 folder_item_change_type(folder->queue, F_NORMAL);
235                 folder->queue = item;
236                 break;
237         case F_DRAFT:
238                 folder_item_change_type(folder->draft, F_NORMAL);
239                 folder->draft = item;
240                 break;
241         case F_TRASH:
242                 folder_item_change_type(folder->trash, F_NORMAL);
243                 folder->trash = item;
244                 break;
245         case F_NORMAL:
246         default:
247                 break;
248         }
249         /* set new type for current folder and sons */
250         item->stype = newtype;
251         folder_func_to_all_folders(reset_parent_type, NULL);
252         
253         hookdata.folder = folder;
254         hookdata.update_flags = FOLDER_TREE_CHANGED;
255         hookdata.item = NULL;
256         hooks_invoke(FOLDER_UPDATE_HOOKLIST, &hookdata);
257 }
258
259 void folder_destroy(Folder *folder)
260 {
261         cm_return_if_fail(folder != NULL);
262         cm_return_if_fail(folder->klass->destroy_folder != NULL);
263
264         folder_remove(folder);
265
266         folder_tree_destroy(folder);
267
268         folder->klass->destroy_folder(folder);
269
270         g_free(folder->name);
271         g_free(folder);
272 }
273
274 void folder_set_xml(Folder *folder, XMLTag *tag)
275 {
276         GList *cur;
277         FolderItem *rootitem = NULL;
278
279         if ((folder->node != NULL) && (folder->node->data != NULL))
280                 rootitem = (FolderItem *) folder->node->data;
281
282         for (cur = tag->attr; cur != NULL; cur = g_list_next(cur)) {
283                 XMLAttr *attr = (XMLAttr *) cur->data;
284
285                 if (!attr || !attr->name || !attr->value) continue;
286                 if (!strcmp(attr->name, "name")) {
287                         g_free(folder->name);
288                         folder->name = g_strdup(attr->value);
289                         if (rootitem != NULL) {
290                                 g_free(rootitem->name);
291                                 rootitem->name = g_strdup(attr->value);
292                         }
293                 } else if (!strcmp(attr->name, "account_id")) {
294                         PrefsAccount *account;
295
296                         account = account_find_from_id(atoi(attr->value));
297                         if (!account)
298                                 g_warning("account_id: %s not found\n", attr->value);
299                         else {
300                                 folder->account = account;
301                                 account->folder = folder;
302                         }
303                 } else if (!strcmp(attr->name, "collapsed")) {
304                         if (rootitem != NULL)
305                                 rootitem->collapsed = *attr->value == '1' ? TRUE : FALSE;
306                 } else if (!strcmp(attr->name, "sort")) {
307                         folder->sort = atoi(attr->value);
308                 }
309         }
310 }
311
312 XMLTag *folder_get_xml(Folder *folder)
313 {
314         XMLTag *tag;
315
316         tag = xml_tag_new("folder");
317
318         if (folder->name)
319                 xml_tag_add_attr(tag, xml_attr_new("name", folder->name));
320         if (folder->account)
321                 xml_tag_add_attr(tag, xml_attr_new_int("account_id", folder->account->account_id));
322         if (folder->node && folder->node->data) {
323                 FolderItem *rootitem = (FolderItem *) folder->node->data;
324
325                 xml_tag_add_attr(tag, xml_attr_new("collapsed", rootitem->collapsed ? "1" : "0"));
326         }
327         xml_tag_add_attr(tag, xml_attr_new_int("sort", folder->sort));
328
329         return tag;
330 }
331
332 FolderItem *folder_item_new(Folder *folder, const gchar *name, const gchar *path)
333 {
334         FolderItem *item = NULL;
335         
336         cm_return_val_if_fail(folder != NULL, NULL);
337         
338         if (folder->klass->item_new) {
339                 item = folder->klass->item_new(folder);
340         } else {
341                 item = g_new0(FolderItem, 1);
342         }
343
344         cm_return_val_if_fail(item != NULL, NULL);
345
346         item->stype = F_NORMAL;
347         item->name = g_strdup(name);
348         item->path = g_strdup(path);
349         item->mtime = 0;
350         item->new_msgs = 0;
351         item->unread_msgs = 0;
352         item->unreadmarked_msgs = 0;
353         item->marked_msgs = 0;
354         item->total_msgs = 0;
355         item->replied_msgs = 0;
356         item->forwarded_msgs = 0;
357         item->locked_msgs = 0;
358         item->ignored_msgs = 0;
359         item->watched_msgs = 0;
360         item->order = 0;
361         item->last_num = -1;
362         item->cache = NULL;
363         item->no_sub = FALSE;
364         item->no_select = FALSE;
365         item->collapsed = FALSE;
366         item->thread_collapsed = FALSE;
367         item->threaded  = TRUE;
368         item->ret_rcpt  = FALSE;
369         item->opened    = FALSE;
370         item->node = g_node_new(item);
371         item->folder = NULL;
372         item->account = NULL;
373         item->apply_sub = FALSE;
374         item->mark_queue = NULL;
375         item->data = NULL;
376         item->parent_stype = -1;
377
378         item->sort_key = SORT_BY_DATE;
379         item->sort_type = SORT_ASCENDING;
380
381         item->prefs = folder_item_prefs_new();
382
383         return item;
384 }
385
386 void folder_item_append(FolderItem *parent, FolderItem *item)
387 {
388         cm_return_if_fail(parent != NULL);
389         cm_return_if_fail(parent->folder != NULL);
390         cm_return_if_fail(parent->node != NULL);
391         cm_return_if_fail(item != NULL);
392
393         item->folder = parent->folder;
394         g_node_append(parent->node, item->node);
395 }
396
397 void folder_item_remove(FolderItem *item)
398 {
399         GNode *node, *start_node;
400         FolderUpdateData hookdata;
401         gchar *tags_file = NULL;
402
403         cm_return_if_fail(item != NULL);
404         cm_return_if_fail(item->folder != NULL);
405         cm_return_if_fail(item->folder->node != NULL);
406
407         start_node = node = item->node;
408         
409         node = item->folder->node;
410         
411         node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
412         node = node->children;
413
414         /* remove my children */
415         while (node != NULL) {
416                 if (node && node->data) {
417                         FolderItem *sub_item = (FolderItem*) node->data;
418                         node = node->next;
419                         folder_item_remove(sub_item);
420                 }
421         }
422         
423         /* remove myself */
424         if (item->cache != NULL) {
425                 msgcache_destroy(item->cache);
426                 item->cache = NULL;
427         }
428         tags_file = folder_item_get_tags_file(item);
429         if (tags_file)
430                 claws_unlink(tags_file);
431         g_free(tags_file);
432
433         hookdata.folder = item->folder;
434         hookdata.update_flags = FOLDER_TREE_CHANGED | FOLDER_REMOVE_FOLDERITEM;
435         hookdata.item = item;
436         hooks_invoke(FOLDER_UPDATE_HOOKLIST, &hookdata);
437
438         node = start_node;
439
440         if (item->folder->node == node)
441                 item->folder->node = NULL;
442
443         folder_item_destroy(item);
444
445         g_node_destroy(node);
446 }
447
448 void folder_item_remove_children(FolderItem *item)
449 {
450         GNode *node, *next;
451
452         cm_return_if_fail(item != NULL);
453         cm_return_if_fail(item->folder != NULL);
454         cm_return_if_fail(item->node != NULL);
455
456         node = item->node->children;
457         while (node != NULL) {
458                 next = node->next;
459                 folder_item_remove(FOLDER_ITEM(node->data));
460                 node = next;
461         }
462 }
463
464 void folder_item_destroy(FolderItem *item)
465 {
466         Folder *folder;
467
468         cm_return_if_fail(item != NULL);
469
470         folder = item->folder;
471         if (folder) {
472                 if (folder->inbox == item)
473                         folder->inbox = NULL;
474                 else if (folder->outbox == item)
475                         folder->outbox = NULL;
476                 else if (folder->draft == item)
477                         folder->draft = NULL;
478                 else if (folder->queue == item)
479                         folder->queue = NULL;
480                 else if (folder->trash == item)
481                         folder->trash = NULL;
482         }
483
484         if (item->cache)
485                 folder_item_free_cache(item, TRUE);
486         if (item->prefs)
487                 folder_item_prefs_free(item->prefs);
488         g_free(item->name);
489         g_free(item->path);
490
491         if (item->folder != NULL) {
492                 if(item->folder->klass->item_destroy) {
493                         item->folder->klass->item_destroy(item->folder, item);
494                 } else {
495                         g_free(item);
496                 }
497         }
498 }
499
500 FolderItem *folder_item_parent(FolderItem *item)
501 {
502         cm_return_val_if_fail(item != NULL, NULL);
503         cm_return_val_if_fail(item->node != NULL, NULL);
504
505         if (item->node->parent == NULL)
506                 return NULL;
507         return (FolderItem *) item->node->parent->data;
508 }
509
510 void folder_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag)
511 {
512         GList *cur;
513
514         for (cur = tag->attr; cur != NULL; cur = g_list_next(cur)) {
515                 XMLAttr *attr = (XMLAttr *) cur->data;
516
517                 if (!attr || !attr->name || !attr->value) continue;
518                 if (!strcmp(attr->name, "type")) {
519                         if (!g_ascii_strcasecmp(attr->value, "normal"))
520                                 item->stype = F_NORMAL;
521                         else if (!g_ascii_strcasecmp(attr->value, "inbox"))
522                                 item->stype = F_INBOX;
523                         else if (!g_ascii_strcasecmp(attr->value, "outbox"))
524                                 item->stype = F_OUTBOX;
525                         else if (!g_ascii_strcasecmp(attr->value, "draft"))
526                                 item->stype = F_DRAFT;
527                         else if (!g_ascii_strcasecmp(attr->value, "queue"))
528                                 item->stype = F_QUEUE;
529                         else if (!g_ascii_strcasecmp(attr->value, "trash"))
530                                 item->stype = F_TRASH;
531                 } else if (!strcmp(attr->name, "name")) {
532                         g_free(item->name);
533                         item->name = g_strdup(attr->value);
534                 } else if (!strcmp(attr->name, "path")) {
535                         g_free(item->path);
536                         item->path = g_strdup(attr->value);
537                 } else if (!strcmp(attr->name, "mtime"))
538                         item->mtime = strtoul(attr->value, NULL, 10);
539                 else if (!strcmp(attr->name, "new"))
540                         item->new_msgs = atoi(attr->value);
541                 else if (!strcmp(attr->name, "unread"))
542                         item->unread_msgs = atoi(attr->value);
543                 else if (!strcmp(attr->name, "unreadmarked"))
544                         item->unreadmarked_msgs = atoi(attr->value);
545                 else if (!strcmp(attr->name, "marked"))
546                         item->marked_msgs = atoi(attr->value);
547                 else if (!strcmp(attr->name, "replied"))
548                         item->replied_msgs = atoi(attr->value);
549                 else if (!strcmp(attr->name, "forwarded"))
550                         item->forwarded_msgs = atoi(attr->value);
551                 else if (!strcmp(attr->name, "locked"))
552                         item->locked_msgs = atoi(attr->value);
553                 else if (!strcmp(attr->name, "ignored"))
554                         item->ignored_msgs = atoi(attr->value);
555                 else if (!strcmp(attr->name, "watched"))
556                         item->watched_msgs = atoi(attr->value);
557                 else if (!strcmp(attr->name, "order"))
558                         item->order = atoi(attr->value);
559                 else if (!strcmp(attr->name, "total"))
560                         item->total_msgs = atoi(attr->value);
561                 else if (!strcmp(attr->name, "no_sub"))
562                         item->no_sub = *attr->value == '1' ? TRUE : FALSE;
563                 else if (!strcmp(attr->name, "no_select"))
564                         item->no_select = *attr->value == '1' ? TRUE : FALSE;
565                 else if (!strcmp(attr->name, "collapsed"))
566                         item->collapsed = *attr->value == '1' ? TRUE : FALSE;
567                 else if (!strcmp(attr->name, "thread_collapsed"))
568                         item->thread_collapsed =  *attr->value == '1' ? TRUE : FALSE;
569                 else if (!strcmp(attr->name, "threaded"))
570                         item->threaded =  *attr->value == '1' ? TRUE : FALSE;
571                 else if (!strcmp(attr->name, "hidereadmsgs"))
572                         item->hide_read_msgs =  *attr->value == '1' ? TRUE : FALSE;
573                 else if (!strcmp(attr->name, "hidedelmsgs"))
574                         item->hide_del_msgs =  *attr->value == '1' ? TRUE : FALSE;
575                 else if (!strcmp(attr->name, "reqretrcpt"))
576                         item->ret_rcpt =  *attr->value == '1' ? TRUE : FALSE;
577                 else if (!strcmp(attr->name, "sort_key")) {
578                         if (!strcmp(attr->value, "none"))
579                                 item->sort_key = SORT_BY_NONE;
580                         else if (!strcmp(attr->value, "number"))
581                                 item->sort_key = SORT_BY_NUMBER;
582                         else if (!strcmp(attr->value, "size"))
583                                 item->sort_key = SORT_BY_SIZE;
584                         else if (!strcmp(attr->value, "date"))
585                                 item->sort_key = SORT_BY_DATE;
586                         else if (!strcmp(attr->value, "from"))
587                                 item->sort_key = SORT_BY_FROM;
588                         else if (!strcmp(attr->value, "subject"))
589                                 item->sort_key = SORT_BY_SUBJECT;
590                         else if (!strcmp(attr->value, "score"))
591                                 item->sort_key = SORT_BY_SCORE;
592                         else if (!strcmp(attr->value, "label"))
593                                 item->sort_key = SORT_BY_LABEL;
594                         else if (!strcmp(attr->value, "mark"))
595                                 item->sort_key = SORT_BY_MARK;
596                         else if (!strcmp(attr->value, "unread"))
597                                 item->sort_key = SORT_BY_STATUS;
598                         else if (!strcmp(attr->value, "mime"))
599                                 item->sort_key = SORT_BY_MIME;
600                         else if (!strcmp(attr->value, "to"))
601                                 item->sort_key = SORT_BY_TO;
602                         else if (!strcmp(attr->value, "locked"))
603                                 item->sort_key = SORT_BY_LOCKED;
604                         else if (!strcmp(attr->value, "tags"))
605                                 item->sort_key = SORT_BY_TAGS;
606                         else if (!strcmp(attr->value, "thread_date"))
607                                 item->sort_key = SORT_BY_THREAD_DATE;
608                 } else if (!strcmp(attr->name, "sort_type")) {
609                         if (!strcmp(attr->value, "ascending"))
610                                 item->sort_type = SORT_ASCENDING;
611                         else
612                                 item->sort_type = SORT_DESCENDING;
613                 } else if (!strcmp(attr->name, "account_id")) {
614                         PrefsAccount *account;
615
616                         account = account_find_from_id(atoi(attr->value));
617                         if (!account)
618                                 g_warning("account_id: %s not found\n", attr->value);
619                         else
620                                 item->account = account;
621                 } else if (!strcmp(attr->name, "apply_sub")) {
622                         item->apply_sub = *attr->value == '1' ? TRUE : FALSE;
623                 } else if (!strcmp(attr->name, "last_seen")) {
624                         if (!claws_crashed())
625                                 item->last_seen = atoi(attr->value);
626                         else
627                                 item->last_seen = 0;
628                 }
629         }
630 }
631
632 XMLTag *folder_item_get_xml(Folder *folder, FolderItem *item)
633 {
634         static gchar *folder_item_stype_str[] = {"normal", "inbox", "outbox",
635                                                  "draft", "queue", "trash"};
636         static gchar *sort_key_str[] = {"none", "number", "size", "date",
637                                         "from", "subject", "score", "label",
638                                         "mark", "unread", "mime", "to", 
639                                         "locked", "tags", "thread_date" };
640         XMLTag *tag;
641         gchar *value;
642
643         tag = xml_tag_new("folderitem");
644
645         xml_tag_add_attr(tag, xml_attr_new("type", folder_item_stype_str[item->stype]));
646         if (item->name)
647                 xml_tag_add_attr(tag, xml_attr_new("name", item->name));
648         if (item->path)
649                 xml_tag_add_attr(tag, xml_attr_new("path", item->path));
650         if (item->no_sub)
651                 xml_tag_add_attr(tag, xml_attr_new("no_sub", "1"));
652         if (item->no_select)
653                 xml_tag_add_attr(tag, xml_attr_new("no_select", "1"));
654         xml_tag_add_attr(tag, xml_attr_new("collapsed", item->collapsed && item->node->children ? "1" : "0"));
655         xml_tag_add_attr(tag, xml_attr_new("thread_collapsed", item->thread_collapsed ? "1" : "0"));
656         xml_tag_add_attr(tag, xml_attr_new("threaded", item->threaded ? "1" : "0"));
657         xml_tag_add_attr(tag, xml_attr_new("hidereadmsgs", item->hide_read_msgs ? "1" : "0"));
658         xml_tag_add_attr(tag, xml_attr_new("hidedelmsgs", item->hide_del_msgs ? "1" : "0"));
659         if (item->ret_rcpt)
660                 xml_tag_add_attr(tag, xml_attr_new("reqretrcpt", "1"));
661
662         if (item->sort_key != SORT_BY_NONE) {
663                 xml_tag_add_attr(tag, xml_attr_new("sort_key", sort_key_str[item->sort_key]));
664                 xml_tag_add_attr(tag, xml_attr_new("sort_type", item->sort_type == SORT_ASCENDING ? "ascending" : "descending"));
665         }
666
667         value = g_strdup_printf("%ld", (unsigned long int) item->mtime);
668         xml_tag_add_attr(tag, xml_attr_new("mtime", value));
669         g_free(value);
670         xml_tag_add_attr(tag, xml_attr_new_int("new", item->new_msgs));
671         xml_tag_add_attr(tag, xml_attr_new_int("unread", item->unread_msgs));
672         xml_tag_add_attr(tag, xml_attr_new_int("unreadmarked", item->unreadmarked_msgs));
673         xml_tag_add_attr(tag, xml_attr_new_int("marked", item->marked_msgs));
674         xml_tag_add_attr(tag, xml_attr_new_int("total", item->total_msgs));
675         xml_tag_add_attr(tag, xml_attr_new_int("replied", item->replied_msgs));
676         xml_tag_add_attr(tag, xml_attr_new_int("forwarded", item->forwarded_msgs));
677         xml_tag_add_attr(tag, xml_attr_new_int("locked", item->locked_msgs));
678         xml_tag_add_attr(tag, xml_attr_new_int("ignore", item->ignored_msgs));
679         xml_tag_add_attr(tag, xml_attr_new_int("watched", item->watched_msgs));
680         xml_tag_add_attr(tag, xml_attr_new_int("order", item->order));
681
682         if (item->account)
683                 xml_tag_add_attr(tag, xml_attr_new_int("account_id", item->account->account_id));
684         if (item->apply_sub)
685                 xml_tag_add_attr(tag, xml_attr_new("apply_sub", "1"));
686
687         xml_tag_add_attr(tag, xml_attr_new_int("last_seen", item->last_seen));
688
689         return tag;
690 }
691
692 void folder_set_ui_func(Folder *folder, FolderUIFunc func, gpointer data)
693 {
694         cm_return_if_fail(folder != NULL);
695
696         folder->ui_func = func;
697         folder->ui_func_data = data;
698 }
699
700 void folder_set_name(Folder *folder, const gchar *name)
701 {
702         cm_return_if_fail(folder != NULL);
703
704         g_free(folder->name);
705         folder->name = name ? g_strdup(name) : NULL;
706         if (folder->node && folder->node->data) {
707                 FolderItem *item = (FolderItem *)folder->node->data;
708
709                 g_free(item->name);
710                 item->name = name ? g_strdup(name) : NULL;
711         }
712 }
713
714 void folder_set_sort(Folder *folder, guint sort)
715 {
716         cm_return_if_fail(folder != NULL);
717
718         if (folder->sort != sort) {
719                 folder_remove(folder);
720                 folder->sort = sort;
721                 folder_add(folder);
722         }
723 }
724
725 static gboolean folder_tree_destroy_func(GNode *node, gpointer data) {
726         FolderItem *item = (FolderItem *) node->data;
727
728         folder_item_destroy(item);
729         return FALSE;
730 }
731
732 void folder_tree_destroy(Folder *folder)
733 {
734         GNode *node;
735
736         cm_return_if_fail(folder != NULL);
737
738         node = folder->node;
739         
740         prefs_filtering_clear_folder(folder);
741
742         if (node != NULL) {
743                 g_node_traverse(node, G_POST_ORDER, G_TRAVERSE_ALL, -1,
744                                 folder_tree_destroy_func, NULL);
745                 g_node_destroy(node);
746                 folder->node = NULL;
747         }
748 }
749
750 void folder_add(Folder *folder)
751 {
752         Folder *cur_folder;
753         GList *cur;
754         gint i;
755         FolderUpdateData hookdata;
756
757         cm_return_if_fail(folder != NULL);
758
759         if ((FOLDER_TYPE(folder) == F_IMAP ||
760              FOLDER_TYPE(folder) == F_NEWS) &&
761             folder->account == NULL) {
762                 return;
763         }
764
765         for (i = 0, cur = folder_list; cur != NULL; cur = cur->next, i++) {
766                 cur_folder = FOLDER(cur->data);
767                 if (cur_folder->sort < folder->sort)
768                         break;
769         }
770
771         folder_list = g_list_insert(folder_list, folder, i);
772
773         hookdata.folder = folder;
774         hookdata.update_flags = FOLDER_ADD_FOLDER;
775         hookdata.item = NULL;
776         hooks_invoke(FOLDER_UPDATE_HOOKLIST, &hookdata);
777 }
778
779 void folder_remove(Folder *folder)
780 {
781         FolderUpdateData hookdata;
782
783         cm_return_if_fail(folder != NULL);
784
785         folder_list = g_list_remove(folder_list, folder);
786
787         hookdata.folder = folder;
788         hookdata.update_flags = FOLDER_REMOVE_FOLDER;
789         hookdata.item = NULL;
790         hooks_invoke(FOLDER_UPDATE_HOOKLIST, &hookdata);
791 }
792
793 GList *folder_get_list(void)
794 {
795         return folder_list;
796 }
797
798 gint folder_read_list(void)
799 {
800         GNode *node, *cur;
801         XMLNode *xmlnode;
802         gchar *path;
803
804         path = folder_get_list_path();
805         if (!is_file_exist(path)) return -1;
806         node = xml_parse_file(path);
807         if (!node) return -1;
808
809         xmlnode = node->data;
810         if (strcmp2(xmlnode->tag->tag, "folderlist") != 0) {
811                 g_warning("wrong folder list\n");
812                 xml_free_tree(node);
813                 return -1;
814         }
815
816         cur = node->children;
817         while (cur != NULL) {
818                 Folder *folder;
819
820                 folder = folder_get_from_xml(cur);
821                 if (folder != NULL)
822                         folder_add(folder);
823                 else
824                         folder_unloaded_list = g_slist_append(folder_unloaded_list,
825                                 (gpointer) xml_copy_tree(cur));
826                 cur = cur->next;
827         }
828
829         xml_free_tree(node);
830         if (folder_list || folder_unloaded_list)
831                 return 0;
832         else
833                 return -1;
834 }
835
836 void folder_write_list(void)
837 {
838         GList *list;
839         GSList *slist;
840         Folder *folder;
841         gchar *path;
842         PrefFile *pfile;
843         GNode *rootnode;
844         XMLNode *xmlnode;
845         XMLTag *tag;
846
847         path = folder_get_list_path();
848         if ((pfile = prefs_write_open(path)) == NULL) return;
849
850         if (xml_file_put_xml_decl(pfile->fp) < 0) {
851                 prefs_file_close_revert(pfile);
852                 g_warning("failed to start write folder list.\n");
853                 return;         
854         }
855         tag = xml_tag_new("folderlist");
856
857         xmlnode = xml_node_new(tag, NULL);
858
859         rootnode = g_node_new(xmlnode);
860
861         for (list = folder_list; list != NULL; list = list->next) {
862                 GNode *node;
863
864                 folder = list->data;
865                 node = folder_get_xml_node(folder);
866                 if (node != NULL)
867                         g_node_append(rootnode, node);
868         }
869
870         for (slist = folder_unloaded_list; slist != NULL; slist = g_slist_next(slist)) {
871                 GNode *node = (GNode *) slist->data;
872
873                 g_node_append(rootnode, (gpointer) xml_copy_tree(node));
874         }
875
876         if (xml_write_tree(rootnode, pfile->fp) < 0) {
877                 prefs_file_close_revert(pfile);
878                 g_warning("failed to write folder list.\n");
879         } else if (prefs_file_close(pfile) < 0) {
880                 g_warning("failed to write folder list.\n");
881         }
882         xml_free_tree(rootnode);
883 }
884
885 static gboolean folder_scan_tree_func(GNode *node, gpointer data)
886 {
887         GHashTable *pptable = (GHashTable *)data;
888         FolderItem *item = (FolderItem *)node->data;
889         
890         folder_item_restore_persist_prefs(item, pptable);
891         folder_item_scan_full(item, FALSE);
892
893         return FALSE;
894 }
895
896 void folder_scan_tree(Folder *folder, gboolean rebuild)
897 {
898         GHashTable *pptable;
899         FolderUpdateData hookdata;
900         Folder *old_folder = folder;
901
902         if (!folder->klass->scan_tree)
903                 return;
904         
905         pptable = folder_persist_prefs_new(folder);
906
907         if (rebuild)
908                 folder_remove(folder);
909
910         if (folder->klass->scan_tree(folder) < 0) {
911                 if (rebuild)
912                         folder_add(old_folder);
913                 return;
914         } else if (rebuild)
915                 folder_add(folder);
916
917         hookdata.folder = folder;
918         hookdata.update_flags = FOLDER_TREE_CHANGED;
919         hookdata.item = NULL;
920         hooks_invoke(FOLDER_UPDATE_HOOKLIST, &hookdata);
921
922         g_node_traverse(folder->node, G_POST_ORDER, G_TRAVERSE_ALL, -1, folder_scan_tree_func, pptable);
923         folder_persist_prefs_free(pptable);
924
925         prefs_matcher_read_config();
926
927         folder_write_list();
928 }
929
930 static gboolean folder_restore_prefs_func(GNode *node, gpointer data)
931 {
932         GHashTable *pptable = (GHashTable *)data;
933         FolderItem *item = (FolderItem *)node->data;
934         
935         folder_item_restore_persist_prefs(item, pptable);
936
937         return FALSE;
938 }
939
940 void folder_fast_scan_tree(Folder *folder)
941 {
942         GHashTable *pptable;
943         FolderUpdateData hookdata;
944
945         if (!folder->klass->scan_tree)
946                 return;
947         
948         pptable = folder_persist_prefs_new(folder);
949
950         if (folder->klass->scan_tree(folder) < 0) {
951                 return;
952         } 
953
954         hookdata.folder = folder;
955         hookdata.update_flags = FOLDER_TREE_CHANGED;
956         hookdata.item = NULL;
957         hooks_invoke(FOLDER_UPDATE_HOOKLIST, &hookdata);
958
959         g_node_traverse(folder->node, G_POST_ORDER, G_TRAVERSE_ALL, -1, folder_restore_prefs_func, pptable);
960         folder_persist_prefs_free(pptable);
961
962         prefs_matcher_read_config();
963
964         folder_write_list();
965 }
966
967 FolderItem *folder_create_folder(FolderItem *parent, const gchar *name)
968 {
969         FolderItem *new_item;
970         
971         cm_return_val_if_fail(parent != NULL, NULL);
972
973         new_item = parent->folder->klass->create_folder(parent->folder, parent, name);
974         if (new_item) {
975                 FolderUpdateData hookdata;
976
977                 new_item->cache = msgcache_new();
978                 new_item->cache_dirty = TRUE;
979                 new_item->mark_dirty = TRUE;
980                 new_item->tags_dirty = TRUE;
981
982                 hookdata.folder = new_item->folder;
983                 hookdata.update_flags = FOLDER_TREE_CHANGED | FOLDER_ADD_FOLDERITEM;
984                 hookdata.item = new_item;
985                 hooks_invoke(FOLDER_UPDATE_HOOKLIST, &hookdata);
986         }
987
988         return new_item;
989 }
990
991 gint folder_item_rename(FolderItem *item, gchar *newname)
992 {
993         gint retval;
994
995         cm_return_val_if_fail(item != NULL, -1);
996         cm_return_val_if_fail(newname != NULL, -1);
997
998         retval = item->folder->klass->rename_folder(item->folder, item, newname);
999
1000         if (retval >= 0) {
1001                 FolderItemUpdateData hookdata;
1002                 FolderUpdateData hookdata2;
1003
1004                 hookdata.item = item;
1005                 hookdata.update_flags = F_ITEM_UPDATE_NAME;
1006                 hookdata.msg = NULL;
1007                 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &hookdata);
1008
1009                 hookdata2.folder = item->folder;
1010                 hookdata2.item = item;
1011                 hookdata2.update_flags = FOLDER_RENAME_FOLDERITEM;
1012                 hooks_invoke(FOLDER_UPDATE_HOOKLIST, &hookdata2);
1013         }
1014
1015         return retval;
1016 }
1017
1018 struct TotalMsgCount
1019 {
1020         guint new_msgs;
1021         guint unread_msgs;
1022         guint unreadmarked_msgs;
1023         guint marked_msgs;
1024         guint total_msgs;
1025         guint replied_msgs;
1026         guint forwarded_msgs;
1027         guint locked_msgs;
1028         guint ignored_msgs;
1029         guint watched_msgs;
1030 };
1031
1032 struct FuncToAllFoldersData
1033 {
1034         FolderItemFunc  function;
1035         gpointer        data;
1036 };
1037
1038 static gboolean folder_func_to_all_folders_func(GNode *node, gpointer data)
1039 {
1040         FolderItem *item;
1041         struct FuncToAllFoldersData *function_data = (struct FuncToAllFoldersData *) data;
1042
1043         cm_return_val_if_fail(node->data != NULL, FALSE);
1044
1045         item = FOLDER_ITEM(node->data);
1046         cm_return_val_if_fail(item != NULL, FALSE);
1047
1048         function_data->function(item, function_data->data);
1049
1050         return FALSE;
1051 }
1052
1053 void folder_func_to_all_folders(FolderItemFunc function, gpointer data)
1054 {
1055         GList *list;
1056         Folder *folder;
1057         struct FuncToAllFoldersData function_data;
1058         
1059         function_data.function = function;
1060         function_data.data = data;
1061
1062         for (list = folder_list; list != NULL; list = list->next) {
1063                 folder = FOLDER(list->data);
1064                 if (folder->node)
1065                         g_node_traverse(folder->node, G_PRE_ORDER,
1066                                         G_TRAVERSE_ALL, -1,
1067                                         folder_func_to_all_folders_func,
1068                                         &function_data);
1069         }
1070 }
1071
1072 static void folder_count_total_msgs_func(FolderItem *item, gpointer data)
1073 {
1074         struct TotalMsgCount *count = (struct TotalMsgCount *)data;
1075
1076         count->new_msgs += item->new_msgs;
1077         count->unread_msgs += item->unread_msgs;
1078         count->unreadmarked_msgs += item->unreadmarked_msgs;
1079         count->marked_msgs += item->marked_msgs;
1080         count->total_msgs += item->total_msgs;
1081         count->replied_msgs += item->replied_msgs;
1082         count->forwarded_msgs += item->forwarded_msgs;
1083         count->locked_msgs += item->locked_msgs;
1084         count->ignored_msgs += item->ignored_msgs;
1085         count->watched_msgs += item->watched_msgs;
1086 }
1087
1088 struct TotalMsgStatus
1089 {
1090         guint new;
1091         guint unread;
1092         guint total;
1093         GString *str;
1094 };
1095
1096 static gboolean folder_get_status_full_all_func(GNode *node, gpointer data)
1097 {
1098         FolderItem *item;
1099         struct TotalMsgStatus *status = (struct TotalMsgStatus *)data;
1100         gchar *id;
1101  
1102         cm_return_val_if_fail(node->data != NULL, FALSE);
1103  
1104         item = FOLDER_ITEM(node->data);
1105
1106         if (!item->path) return FALSE;
1107
1108         status->new += item->new_msgs;
1109         status->unread += item->unread_msgs;
1110         status->total += item->total_msgs;
1111
1112         if (status->str) {
1113                 id = folder_item_get_identifier(item);
1114                 g_string_append_printf(status->str, "%5d %5d %5d %s\n",
1115                                   item->new_msgs, item->unread_msgs,
1116                                   item->total_msgs, id);
1117                 g_free(id);
1118         }
1119  
1120         return FALSE;
1121  }
1122  
1123 static void folder_get_status_full_all(GString *str, guint *new, guint *unread,
1124                                        guint *total)
1125 {
1126         GList *list;
1127         Folder *folder;
1128         struct TotalMsgStatus status;
1129  
1130         status.new = status.unread = status.total = 0;
1131         status.str = str;
1132  
1133         debug_print("Counting total number of messages...\n");
1134  
1135         for (list = folder_list; list != NULL; list = list->next) {
1136                 folder = FOLDER(list->data);
1137                 if (folder->node)
1138                         g_node_traverse(folder->node, G_PRE_ORDER,
1139                                         G_TRAVERSE_ALL, -1,
1140                                         folder_get_status_full_all_func,
1141                                         &status);
1142         }
1143  
1144         *new = status.new;
1145         *unread = status.unread;
1146         *total = status.total;
1147 }
1148
1149 gchar *folder_get_status(GPtrArray *folders, gboolean full)
1150 {
1151         guint new, unread, total;
1152         GString *str;
1153         gint i;
1154         gchar *ret;
1155
1156         new = unread = total = 0;
1157
1158         str = g_string_new(NULL);
1159
1160         if (folders) {
1161                 for (i = 0; i < folders->len; i++) {
1162                         FolderItem *item;
1163
1164                         item = g_ptr_array_index(folders, i);
1165                         new += item->new_msgs;
1166                         unread += item->unread_msgs;
1167                         total += item->total_msgs;
1168
1169                         if (full) {
1170                                 gchar *id;
1171
1172                                 id = folder_item_get_identifier(item);
1173                                 g_string_append_printf(str, "%5d %5d %5d %s\n",
1174                                                   item->new_msgs, item->unread_msgs,
1175                                                   item->total_msgs, id);
1176                                 g_free(id);
1177                         }
1178                 }
1179         } else {
1180                 folder_get_status_full_all(full ? str : NULL,
1181                                            &new, &unread, &total);
1182         }
1183
1184         if (full)
1185                 g_string_append_printf(str, "%5d %5d %5d\n", new, unread, total);
1186         else
1187                 g_string_append_printf(str, "%d %d %d\n", new, unread, total);
1188
1189         ret = str->str;
1190         g_string_free(str, FALSE);
1191  
1192         return ret;
1193 }
1194
1195 void folder_count_total_msgs(guint *new_msgs, guint *unread_msgs, 
1196                              guint *unreadmarked_msgs, guint *marked_msgs,
1197                              guint *total_msgs, guint *replied_msgs,
1198                              guint *forwarded_msgs, guint *locked_msgs,
1199                              guint *ignored_msgs, guint *watched_msgs)
1200 {
1201         struct TotalMsgCount count;
1202
1203         count.new_msgs = count.unread_msgs = count.unreadmarked_msgs = 0;
1204         count.total_msgs = count.replied_msgs = count.forwarded_msgs = 0;
1205         count.locked_msgs = count.ignored_msgs = count.watched_msgs = 0;
1206         count.marked_msgs = 0;
1207
1208         debug_print("Counting total number of messages...\n");
1209
1210         folder_func_to_all_folders(folder_count_total_msgs_func, &count);
1211
1212         *new_msgs = count.new_msgs;
1213         *unread_msgs = count.unread_msgs;
1214         *unreadmarked_msgs = count.unreadmarked_msgs;
1215         *marked_msgs = count.marked_msgs;
1216         *total_msgs = count.total_msgs;
1217         *replied_msgs = count.replied_msgs;
1218         *forwarded_msgs = count.forwarded_msgs;
1219         *locked_msgs = count.locked_msgs;
1220         *ignored_msgs = count.ignored_msgs;
1221         *watched_msgs = count.watched_msgs;
1222 }
1223
1224 Folder *folder_find_from_path(const gchar *path)
1225 {
1226         GList *list;
1227         Folder *folder;
1228
1229         for (list = folder_list; list != NULL; list = list->next) {
1230                 folder = list->data;
1231                 if ((FOLDER_TYPE(folder) == F_MH || 
1232                      FOLDER_TYPE(folder) == F_MBOX) &&
1233                     !path_cmp(LOCAL_FOLDER(folder)->rootpath, path))
1234                         return folder;
1235         }
1236
1237         return NULL;
1238 }
1239
1240 Folder *folder_find_from_name(const gchar *name, FolderClass *klass)
1241 {
1242         GList *list;
1243         Folder *folder;
1244
1245         for (list = folder_list; list != NULL; list = list->next) {
1246                 folder = list->data;
1247                 if (folder->klass == klass && 
1248                     strcmp2(name, folder->name) == 0)
1249                         return folder;
1250         }
1251
1252         return NULL;
1253 }
1254
1255 static gboolean folder_item_find_func(GNode *node, gpointer data)
1256 {
1257         FolderItem *item = node->data;
1258         gpointer *d = data;
1259         const gchar *path = d[0];
1260
1261         if (path_cmp(path, item->path) != 0)
1262                 return FALSE;
1263
1264         d[1] = item;
1265
1266         return TRUE;
1267 }
1268
1269 FolderItem *folder_find_item_from_path(const gchar *path)
1270 {
1271         Folder *folder;
1272         gpointer d[2];
1273         GList *list = folder_get_list();
1274         
1275         folder = list ? list->data:NULL;
1276         
1277         cm_return_val_if_fail(folder != NULL, NULL);
1278
1279         d[0] = (gpointer)path;
1280         d[1] = NULL;
1281         while (d[1] == NULL && list) {
1282                 folder = FOLDER(list->data);
1283                 g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1284                         folder_item_find_func, d);
1285                 list = list->next;
1286         }
1287         return d[1];
1288 }
1289
1290 static gboolean folder_item_find_func_real_path(GNode *node, gpointer data)
1291 {
1292         FolderItem *item = node->data;
1293         gpointer *d = data;
1294         const gchar *path = d[0];
1295         gchar *tmp = folder_item_get_path(item);
1296         if (path_cmp(path, tmp) != 0) {
1297                 g_free(tmp);
1298                 return FALSE;
1299         }
1300         g_free(tmp);
1301         d[1] = item;
1302
1303         return TRUE;
1304 }
1305
1306 FolderItem *folder_find_item_from_real_path(const gchar *path)
1307 {
1308         Folder *folder;
1309         gpointer d[2];
1310         GList *list = folder_get_list();
1311         
1312         folder = list ? list->data:NULL;
1313         
1314         cm_return_val_if_fail(folder != NULL, NULL);
1315
1316         d[0] = (gpointer)path;
1317         d[1] = NULL;
1318         while (d[1] == NULL && list) {
1319                 folder = FOLDER(list->data);
1320                 g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1321                         folder_item_find_func_real_path, d);
1322                 list = list->next;
1323         }
1324         return d[1];
1325 }
1326
1327 FolderItem *folder_find_child_item_by_name(FolderItem *item, const gchar *name)
1328 {
1329         GNode *node;
1330         FolderItem *child;
1331
1332         for (node = item->node->children; node != NULL; node = node->next) {
1333                 child = FOLDER_ITEM(node->data);
1334                 if (strcmp2(child->name, name) == 0) {
1335                         return child;
1336                 }
1337         }
1338
1339         return NULL;
1340 }
1341
1342 FolderClass *folder_get_class_from_string(const gchar *str)
1343 {
1344         GSList *classlist;
1345
1346         classlist = folder_get_class_list();
1347         for (; classlist != NULL; classlist = g_slist_next(classlist)) {
1348                 FolderClass *class = (FolderClass *) classlist->data;
1349                 if (g_ascii_strcasecmp(class->idstr, str) == 0)
1350                         return class;
1351         }
1352
1353         return NULL;
1354 }
1355
1356 gchar *folder_get_identifier(Folder *folder)
1357 {
1358         gchar *type_str;
1359
1360         cm_return_val_if_fail(folder != NULL, NULL);
1361
1362         type_str = folder->klass->idstr;
1363         return g_strconcat("#", type_str, "/", folder->name, NULL);
1364 }
1365
1366 gchar *folder_item_get_identifier(FolderItem *item)
1367 {
1368         gchar *id = NULL;
1369         gchar *folder_id = NULL;
1370
1371         cm_return_val_if_fail(item != NULL, NULL);
1372
1373         if (item->path == NULL)
1374                 return NULL;
1375
1376         folder_id = folder_get_identifier(item->folder);
1377         id = g_strconcat(folder_id, "/", item->path, NULL);
1378         g_free(folder_id);
1379
1380         return id;
1381 }
1382
1383 Folder *folder_find_from_identifier(const gchar *identifier)
1384 {
1385         Folder *folder;
1386         gpointer d[2];
1387         gchar *str;
1388         gchar *p;
1389         gchar *name;
1390         gchar *path;
1391         FolderClass *class;
1392
1393         cm_return_val_if_fail(identifier != NULL, NULL);
1394
1395         if (*identifier != '#')
1396                 return NULL;
1397
1398         Xstrdup_a(str, identifier, return NULL);
1399
1400         p = strchr(str, '/');
1401         if (!p)
1402                 return NULL;
1403         *p = '\0';
1404         p++;
1405         class = folder_get_class_from_string(&str[1]);
1406         if (class == NULL)
1407                 return NULL;
1408
1409         name = p;
1410         p = strchr(p, '/');
1411         if (p)
1412                 return NULL;
1413
1414         return folder_find_from_name(name, class);
1415 }
1416
1417 FolderItem *folder_find_item_from_identifier(const gchar *identifier)
1418 {
1419         Folder *folder;
1420         gpointer d[2];
1421         gchar *str;
1422         gchar *p;
1423         gchar *name;
1424         gchar *path;
1425         FolderClass *class;
1426
1427         cm_return_val_if_fail(identifier != NULL, NULL);
1428
1429         if (*identifier != '#')
1430                 return folder_find_item_from_path(identifier);
1431
1432         Xstrdup_a(str, identifier, return NULL);
1433
1434         p = strchr(str, '/');
1435         if (!p)
1436                 return folder_find_item_from_path(identifier);
1437         *p = '\0';
1438         p++;
1439         class = folder_get_class_from_string(&str[1]);
1440         if (class == NULL)
1441                 return folder_find_item_from_path(identifier);
1442
1443         name = p;
1444         p = strchr(p, '/');
1445         if (!p)
1446                 return folder_find_item_from_path(identifier);
1447         *p = '\0';
1448         p++;
1449
1450         folder = folder_find_from_name(name, class);
1451         if (!folder)
1452                 return folder_find_item_from_path(identifier);
1453
1454         path = p;
1455
1456         d[0] = (gpointer)path;
1457         d[1] = NULL;
1458         g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1459                         folder_item_find_func, d);
1460         return d[1];
1461 }
1462
1463 /** Returns the FolderItem from a given identifier
1464  *
1465  * The FolderItem is created if it doesn't already exist.
1466  * If creation failed, the function returns NULL.
1467  * 
1468  * Identifiers are of the form #type/Mailbox/FolderA/FolderB/FolderC
1469  */
1470 FolderItem *folder_get_item_from_identifier(const gchar *identifier)
1471 {
1472         FolderItem *item, *last_parent;
1473         Folder *folder;
1474         gchar *p1, *p2, *str;
1475         size_t len;
1476         FolderClass *class;
1477         gboolean created_something = FALSE;
1478
1479         item = folder_find_item_from_identifier(identifier);
1480         if(item)
1481                 return item;
1482
1483         /* trivial sanity check: need at least # and two slashes */
1484         len = strlen(identifier);
1485         if(len < 3)
1486                 return NULL;
1487
1488         /* make sure identifier ends with a slash */
1489         if(identifier[len-1] == G_DIR_SEPARATOR) {
1490                 Xstrdup_a(str, identifier, return NULL);
1491         }
1492         else {
1493                 Xstrndup_a(str, identifier, len+1, return NULL);
1494                 str[len] = G_DIR_SEPARATOR;
1495         }
1496
1497         /* find folder class */
1498         p1 = strchr(str, G_DIR_SEPARATOR);
1499         if(!p1)
1500                 return NULL;
1501         *p1 = '\0';
1502         class = folder_get_class_from_string(&str[1]);
1503         if(!class)
1504                 return NULL;
1505         *p1 = G_DIR_SEPARATOR;
1506         ++p1;
1507
1508         /* find folder from class and name */
1509         p2 = strchr(p1, G_DIR_SEPARATOR);
1510         if(!p2)
1511                 return NULL;
1512         *p2 = '\0';
1513         folder = folder_find_from_name(p1, class);
1514         if(!folder)
1515                 return NULL;
1516         *p2 = G_DIR_SEPARATOR;
1517         ++p2;
1518         p1 = p2;
1519
1520         /* Now, move forward and make sure all sections in the path exist */
1521         last_parent = folder->node->data;
1522         while((p1 = strchr(p1, G_DIR_SEPARATOR)) != NULL) {
1523                 *p1 = '\0';
1524                 item = folder_find_item_from_identifier(str);
1525                 if(!item) {
1526                         item = folder_create_folder(last_parent, p2);
1527                         if(!item)
1528                                 return NULL;
1529                         debug_print("Created folder '%s'\n", str);
1530                         created_something = TRUE;
1531                         if(prefs_common.inherit_folder_props && (last_parent != item->folder->node->data)) {
1532                                 folder_item_prefs_copy_prefs(last_parent, item);
1533                         }
1534                 }
1535                 last_parent = item;
1536                 *p1 = G_DIR_SEPARATOR;
1537                 ++p1;
1538                 p2 = p1;
1539         }
1540
1541         if(created_something)
1542                 folder_write_list();
1543
1544         return item;
1545 }
1546
1547
1548 /**
1549  * Get a displayable name for a FolderItem
1550  *
1551  * \param item FolderItem for that a name should be created
1552  * \return Displayable name for item, returned string has to
1553  *         be freed
1554  */
1555 gchar *folder_item_get_name(FolderItem *item)
1556 {
1557         gchar *name = NULL;
1558
1559         cm_return_val_if_fail(item != NULL, g_strdup(""));
1560
1561         switch (item->stype) {
1562         case F_INBOX:
1563                 name = g_strdup(!strcmp2(item->name, INBOX_DIR) ? _("Inbox") :
1564                                 item->name);
1565                 break;
1566         case F_OUTBOX:
1567                 name = g_strdup(!strcmp2(item->name, OUTBOX_DIR) ? _("Sent") :
1568                                 item->name);
1569                 break;
1570         case F_QUEUE:
1571                 name = g_strdup(!strcmp2(item->name, QUEUE_DIR) ? _("Queue") :
1572                                 item->name);
1573                 break;
1574         case F_TRASH:
1575                 name = g_strdup(!strcmp2(item->name, TRASH_DIR) ? _("Trash") :
1576                                 item->name);
1577                 break;
1578         case F_DRAFT:
1579                 name = g_strdup(!strcmp2(item->name, DRAFT_DIR) ? _("Drafts") :
1580                                 item->name);
1581                 break;
1582         default:
1583                 break;
1584         }
1585
1586         if (name == NULL) {
1587                 /*
1588                  * should probably be done by a virtual function,
1589                  * the folder knows the ui string and how to abbrev
1590                 */
1591                 if (folder_item_parent(item) == NULL) {
1592                         name = g_strconcat(item->name, " (", item->folder->klass->uistr, ")", NULL);
1593                 } else {
1594                         if (FOLDER_CLASS(item->folder) == news_get_class() &&
1595                             item->path && !strcmp2(item->name, item->path))
1596                                 name = get_abbrev_newsgroup_name
1597                                         (item->path,
1598                                          prefs_common.ng_abbrev_len);
1599                         else
1600                                 name = g_strdup(item->name);
1601                 }
1602         }
1603
1604         if (name == NULL)
1605                 name = g_strdup("");
1606
1607         return name;
1608 }
1609
1610 gboolean folder_have_mailbox (void)
1611 {
1612         GList *cur;
1613         for (cur = folder_list; cur != NULL; cur = g_list_next(cur)) {
1614                 Folder *folder = FOLDER(cur->data);
1615                 if (folder->inbox && folder->outbox)
1616                         return TRUE;
1617         }
1618         return FALSE;
1619 }
1620
1621 FolderItem *folder_get_default_inbox(void)
1622 {
1623         GList *flist;
1624
1625         for (flist = folder_list; flist != NULL; flist = g_list_next(flist)) {
1626                 Folder * folder = FOLDER(flist->data);
1627
1628                 if (folder == NULL)
1629                         continue;
1630                 if (folder->inbox == NULL)
1631                         continue;
1632                 if (folder->klass->type == F_UNKNOWN)
1633                         continue;
1634
1635                 return folder->inbox;
1636         }
1637
1638         return NULL;
1639 }
1640
1641 FolderItem *folder_get_default_inbox_for_class(FolderType type)
1642 {
1643         GList *flist;
1644
1645         for (flist = folder_list; flist != NULL; flist = g_list_next(flist)) {
1646                 Folder * folder = FOLDER(flist->data);
1647
1648                 if (folder == NULL)
1649                         continue;
1650                 if (folder->inbox == NULL)
1651                         continue;
1652                 if (folder->klass->type != type)
1653                         continue;
1654
1655                 return folder->inbox;
1656         }
1657
1658         return NULL;
1659 }
1660
1661 FolderItem *folder_get_default_outbox(void)
1662 {
1663         GList *flist;
1664
1665         for (flist = folder_list; flist != NULL; flist = g_list_next(flist)) {
1666                 Folder * folder = FOLDER(flist->data);
1667
1668                 if (folder == NULL)
1669                         continue;
1670                 if (folder->outbox == NULL)
1671                         continue;
1672                 if (folder->klass->type == F_UNKNOWN)
1673                         continue;
1674
1675                 return folder->outbox;
1676         }
1677
1678         return NULL;
1679 }
1680
1681 FolderItem *folder_get_default_outbox_for_class(FolderType type)
1682 {
1683         GList *flist;
1684
1685         for (flist = folder_list; flist != NULL; flist = g_list_next(flist)) {
1686                 Folder * folder = FOLDER(flist->data);
1687
1688                 if (folder == NULL)
1689                         continue;
1690                 if (folder->outbox == NULL)
1691                         continue;
1692                 if (folder->klass->type != type)
1693                         continue;
1694
1695                 return folder->outbox;
1696         }
1697
1698         return NULL;
1699 }
1700
1701 FolderItem *folder_get_default_draft(void)
1702 {
1703         GList *flist;
1704
1705         for (flist = folder_list; flist != NULL; flist = g_list_next(flist)) {
1706                 Folder * folder = FOLDER(flist->data);
1707
1708                 if (folder == NULL)
1709                         continue;
1710                 if (folder->draft == NULL)
1711                         continue;
1712                 if (folder->klass->type == F_UNKNOWN)
1713                         continue;
1714
1715                 return folder->draft;
1716         }
1717
1718         return NULL;
1719 }
1720
1721 FolderItem *folder_get_default_draft_for_class(FolderType type)
1722 {
1723         GList *flist;
1724
1725         for (flist = folder_list; flist != NULL; flist = g_list_next(flist)) {
1726                 Folder * folder = FOLDER(flist->data);
1727
1728                 if (folder == NULL)
1729                         continue;
1730                 if (folder->draft == NULL)
1731                         continue;
1732                 if (folder->klass->type != type)
1733                         continue;
1734
1735                 return folder->draft;
1736         }
1737
1738         return NULL;
1739 }
1740
1741 FolderItem *folder_get_default_queue(void)
1742 {
1743         GList *flist;
1744
1745         for (flist = folder_list; flist != NULL; flist = g_list_next(flist)) {
1746                 Folder * folder = FOLDER(flist->data);
1747
1748                 if (folder == NULL)
1749                         continue;
1750                 if (folder->queue == NULL)
1751                         continue;
1752                 if (folder->klass->type == F_UNKNOWN)
1753                         continue;
1754
1755                 return folder->queue;
1756         }
1757
1758         return NULL;
1759 }
1760
1761 FolderItem *folder_get_default_queue_for_class(FolderType type)
1762 {
1763         GList *flist;
1764
1765         for (flist = folder_list; flist != NULL; flist = g_list_next(flist)) {
1766                 Folder * folder = FOLDER(flist->data);
1767
1768                 if (folder == NULL)
1769                         continue;
1770                 if (folder->queue == NULL)
1771                         continue;
1772                 if (folder->klass->type != type)
1773                         continue;
1774
1775                 return folder->queue;
1776         }
1777
1778         return NULL;
1779 }
1780
1781 FolderItem *folder_get_default_trash(void)
1782 {
1783         GList *flist;
1784
1785         for (flist = folder_list; flist != NULL; flist = g_list_next(flist)) {
1786                 Folder * folder = FOLDER(flist->data);
1787
1788                 if (folder == NULL)
1789                         continue;
1790                 if (folder->trash == NULL)
1791                         continue;
1792                 if (folder->klass->type == F_UNKNOWN)
1793                         continue;
1794
1795                 return folder->trash;
1796         }
1797
1798         return NULL;
1799 }
1800
1801 FolderItem *folder_get_default_trash_for_class(FolderType type)
1802 {
1803         GList *flist;
1804
1805         for (flist = folder_list; flist != NULL; flist = g_list_next(flist)) {
1806                 Folder * folder = FOLDER(flist->data);
1807
1808                 if (folder == NULL)
1809                         continue;
1810                 if (folder->trash == NULL)
1811                         continue;
1812                 if (folder->klass->type != type)
1813                         continue;
1814
1815                 return folder->trash;
1816         }
1817
1818         return NULL;
1819 }
1820
1821 #define CREATE_FOLDER_IF_NOT_EXIST(member, dir, type)           \
1822 {                                                               \
1823         if (!folder->member) {                                  \
1824                 item = folder_item_new(folder, dir, dir);       \
1825                 item->stype = type;                             \
1826                 folder_item_append(rootitem, item);             \
1827                 folder->member = item;                          \
1828         }                                                       \
1829 }
1830
1831 void folder_set_missing_folders(void)
1832 {
1833         Folder *folder;
1834         FolderItem *rootitem;
1835         FolderItem *item;
1836         GList *list;
1837
1838         for (list = folder_list; list != NULL; list = list->next) {
1839                 folder = list->data;
1840                 if (FOLDER_TYPE(folder) != F_MH) continue;
1841                 rootitem = FOLDER_ITEM(folder->node->data);
1842                 cm_return_if_fail(rootitem != NULL);
1843
1844                 if (folder->inbox && folder->outbox && folder->draft &&
1845                     folder->queue && folder->trash)
1846                         continue;
1847
1848                 if (folder->klass->create_tree(folder) < 0) {
1849                         g_warning("%s: can't create the folder tree.\n",
1850                                   LOCAL_FOLDER(folder)->rootpath);
1851                         continue;
1852                 }
1853
1854                 CREATE_FOLDER_IF_NOT_EXIST(inbox,  INBOX_DIR,  F_INBOX);
1855                 CREATE_FOLDER_IF_NOT_EXIST(outbox, OUTBOX_DIR, F_OUTBOX);
1856                 CREATE_FOLDER_IF_NOT_EXIST(draft,  DRAFT_DIR,  F_DRAFT);
1857                 CREATE_FOLDER_IF_NOT_EXIST(queue,  QUEUE_DIR,  F_QUEUE);
1858                 CREATE_FOLDER_IF_NOT_EXIST(trash,  TRASH_DIR,  F_TRASH);
1859         }
1860 }
1861
1862 static gboolean folder_unref_account_func(GNode *node, gpointer data)
1863 {
1864         FolderItem *item = node->data;
1865         PrefsAccount *account = data;
1866
1867         if (item->account == account)
1868                 item->account = NULL;
1869
1870         return FALSE;
1871 }
1872
1873 void folder_unref_account_all(PrefsAccount *account)
1874 {
1875         Folder *folder;
1876         GList *list;
1877
1878         if (!account) return;
1879
1880         for (list = folder_list; list != NULL; list = list->next) {
1881                 folder = list->data;
1882                 if (folder->account == account)
1883                         folder->account = NULL;
1884                 g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1885                                 folder_unref_account_func, account);
1886         }
1887 }
1888
1889 #undef CREATE_FOLDER_IF_NOT_EXIST
1890
1891 gchar *folder_item_get_path(FolderItem *item)
1892 {
1893         Folder *folder;
1894
1895         cm_return_val_if_fail(item != NULL, NULL);
1896         folder = item->folder;
1897         cm_return_val_if_fail(folder != NULL, NULL);
1898
1899         return folder->klass->item_get_path(folder, item);
1900 }
1901
1902 static gint folder_sort_cache_list_by_msgnum(gconstpointer a, gconstpointer b)
1903 {
1904         MsgInfo *msginfo_a = (MsgInfo *) a;
1905         MsgInfo *msginfo_b = (MsgInfo *) b;
1906
1907         return (msginfo_a->msgnum - msginfo_b->msgnum);
1908 }
1909
1910 static gint folder_sort_folder_list(gconstpointer a, gconstpointer b)
1911 {
1912         guint gint_a = GPOINTER_TO_INT(a);
1913         guint gint_b = GPOINTER_TO_INT(b);
1914         
1915         return (gint_a - gint_b);
1916 }
1917
1918 static gint syncronize_flags(FolderItem *item, MsgInfoList *msglist)
1919 {
1920         GHashTable *relation;
1921         gint ret = 0;
1922         GSList *cur;
1923
1924         if(msglist == NULL)
1925                 return 0;
1926         if(item->folder->klass->get_flags == NULL)
1927                 return 0;
1928         if (item->no_select)
1929                 return 0;
1930
1931         relation = g_hash_table_new(g_direct_hash, g_direct_equal);
1932         if ((ret = item->folder->klass->get_flags(
1933             item->folder, item, msglist, relation)) == 0) {
1934                 gpointer data, old_key;
1935                 MsgInfo *msginfo;
1936                 MsgPermFlags permflags = 0;
1937
1938                 folder_item_update_freeze();
1939                 folder_item_set_batch(item, TRUE);
1940                 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1941                         msginfo = (MsgInfo *) cur->data;
1942                 
1943                         if (g_hash_table_lookup_extended(relation, msginfo, &old_key, &data)) {
1944                                 permflags = GPOINTER_TO_INT(data);
1945
1946                                 if (msginfo->flags.perm_flags != permflags) {
1947                                         procmsg_msginfo_change_flags(msginfo,
1948                                                 permflags & ~msginfo->flags.perm_flags, 0,
1949                                                 ~permflags & msginfo->flags.perm_flags, 0);
1950                                 }
1951                         }
1952                 }
1953                 folder_item_set_batch(item, FALSE);
1954                 folder_item_update_thaw();
1955         }
1956         g_hash_table_destroy(relation); 
1957
1958         return ret;
1959 }
1960
1961 static gint folder_item_syncronize_flags(FolderItem *item)
1962 {
1963         MsgInfoList *msglist = NULL;
1964         GSList *cur;
1965         gint ret = 0;
1966         
1967         cm_return_val_if_fail(item != NULL, -1);
1968         cm_return_val_if_fail(item->folder != NULL, -1);
1969         cm_return_val_if_fail(item->folder->klass != NULL, -1);
1970         if (item->no_select)
1971                 return -1;
1972
1973         item->scanning = ITEM_SCANNING_WITH_FLAGS;
1974
1975         if (item->cache == NULL)
1976                 folder_item_read_cache(item);
1977         
1978         msglist = msgcache_get_msg_list(item->cache);
1979         
1980         ret = syncronize_flags(item, msglist);
1981
1982         for (cur = msglist; cur != NULL; cur = g_slist_next(cur))
1983                 procmsg_msginfo_free((MsgInfo *) cur->data);
1984         
1985         g_slist_free(msglist);
1986
1987         item->scanning = ITEM_NOT_SCANNING;
1988
1989         return ret;
1990 }
1991
1992 static void folder_item_process_open (FolderItem *item,
1993                                  void (*before_proc_func)(gpointer data),
1994                                  void (*after_proc_func)(gpointer data),
1995                                  gpointer data)
1996 {
1997         gchar *buf;
1998         if (item == NULL)
1999                 return;
2000         if((item->folder->klass->scan_required != NULL) &&
2001            (item->folder->klass->scan_required(item->folder, item))) {
2002                 folder_item_scan_full(item, TRUE);
2003         } else {
2004                 folder_item_syncronize_flags(item);
2005         }
2006         
2007         /* Processing */
2008         if (item->prefs->enable_processing_when_opening) {
2009                 buf = g_strdup_printf(_("Processing (%s)...\n"), 
2010                               item->path ? item->path : item->name);
2011                 g_free(buf);
2012
2013                 if (before_proc_func)
2014                         before_proc_func(data);
2015
2016                 folder_item_apply_processing(item);
2017
2018                 if (after_proc_func)
2019                         after_proc_func(data);
2020         }
2021         item->processing_pending = FALSE;
2022         return; 
2023 }
2024
2025 gint folder_item_open(FolderItem *item)
2026 {
2027         START_TIMING(""); 
2028         if (item->no_select)
2029                 return -1;
2030
2031         if (item->scanning != ITEM_NOT_SCANNING) {
2032                 debug_print("%s is scanning... \n", item->path ? item->path : item->name);
2033                 return -2;
2034         }
2035
2036         item->processing_pending = TRUE;
2037         folder_item_process_open (item, NULL, NULL, NULL);
2038         
2039         item->opened = TRUE;
2040         END_TIMING();
2041         return 0;
2042 }
2043
2044 gint folder_item_close(FolderItem *item)
2045 {
2046         GSList *mlist, *cur;
2047         Folder *folder;
2048         
2049         cm_return_val_if_fail(item != NULL, -1);
2050
2051         if (item->no_select)
2052                 return -1;
2053
2054         if (item->new_msgs) {
2055                 folder_item_update_freeze();
2056                 mlist = folder_item_get_msg_list(item);
2057                 for (cur = mlist ; cur != NULL ; cur = cur->next) {
2058                         MsgInfo * msginfo;
2059
2060                         msginfo = (MsgInfo *) cur->data;
2061                         if (MSG_IS_NEW(msginfo->flags))
2062                                 procmsg_msginfo_unset_flags(msginfo, MSG_NEW, 0);
2063                         procmsg_msginfo_free(msginfo);
2064                 }
2065                 g_slist_free(mlist);
2066                 folder_item_update_thaw();
2067         }               
2068
2069         folder_item_write_cache(item);
2070         
2071         folder_item_update(item, F_ITEM_UPDATE_MSGCNT);
2072
2073         item->opened = FALSE;
2074         folder = item->folder;
2075
2076         if (folder->klass->close == NULL)
2077                 return 0;
2078
2079         return folder->klass->close(folder, item);
2080 }
2081
2082 static MsgInfoList *get_msginfos(FolderItem *item, MsgNumberList *numlist)
2083 {
2084         MsgInfoList *msglist = NULL;
2085         Folder *folder = item->folder;
2086         if (item->no_select)
2087                 return NULL;
2088         
2089         if (folder->klass->get_msginfos != NULL)
2090                 msglist = folder->klass->get_msginfos(folder, item, numlist);
2091         else {
2092                 MsgNumberList *elem;
2093
2094                 for (elem = numlist; elem != NULL; elem = g_slist_next(elem)) {
2095                         MsgInfo *msginfo;
2096                         guint num;
2097
2098                         num = GPOINTER_TO_INT(elem->data);
2099                         msginfo = folder->klass->get_msginfo(folder, item, num);
2100                         if (msginfo != NULL)
2101                                 msglist = g_slist_prepend(msglist, msginfo);
2102                 }               
2103         }
2104
2105         return msglist;
2106 }
2107
2108 static MsgInfo *get_msginfo(FolderItem *item, guint num)
2109 {
2110         MsgNumberList numlist;
2111         MsgInfoList *msglist;
2112         MsgInfo *msginfo = NULL;
2113
2114         numlist.data = GINT_TO_POINTER(num);
2115         numlist.next = NULL;
2116         msglist = get_msginfos(item, &numlist);
2117         if (msglist != NULL)
2118                 msginfo = procmsg_msginfo_new_ref(msglist->data);
2119         procmsg_msg_list_free(msglist);
2120
2121         return msginfo;
2122 }
2123
2124 gint folder_item_scan_full(FolderItem *item, gboolean filtering)
2125 {
2126         Folder *folder;
2127         GSList *folder_list = NULL, *cache_list = NULL;
2128         GSList *folder_list_cur, *cache_list_cur, *new_list = NULL;
2129         GSList *exists_list = NULL, *elem;
2130         GSList *newmsg_list = NULL;
2131         guint newcnt = 0, unreadcnt = 0, totalcnt = 0;
2132         guint markedcnt = 0, unreadmarkedcnt = 0;
2133         guint repliedcnt = 0, forwardedcnt = 0;
2134         guint lockedcnt = 0, ignoredcnt = 0, watchedcnt = 0;
2135
2136         guint cache_max_num, folder_max_num, cache_cur_num, folder_cur_num;
2137         gboolean update_flags = 0, old_uids_valid = FALSE;
2138         GHashTable *subject_table = NULL;
2139         
2140         cm_return_val_if_fail(item != NULL, -1);
2141         if (item->path == NULL) return -1;
2142
2143         folder = item->folder;
2144
2145         cm_return_val_if_fail(folder != NULL, -1);
2146         cm_return_val_if_fail(folder->klass->get_num_list != NULL, -1);
2147
2148         item->scanning = ITEM_SCANNING_WITH_FLAGS;
2149
2150         debug_print("Scanning folder %s for cache changes.\n", item->path ? item->path : "(null)");
2151         
2152         /* Get list of messages for folder and cache */
2153         if (folder->klass->get_num_list(item->folder, item, &folder_list, &old_uids_valid) < 0) {
2154                 debug_print("Error fetching list of message numbers\n");
2155                 item->scanning = ITEM_NOT_SCANNING;
2156                 return(-1);
2157         }
2158
2159         if(prefs_common.thread_by_subject) {
2160                 subject_table = g_hash_table_new(g_str_hash, g_str_equal);
2161         }
2162         
2163         if (old_uids_valid) {
2164                 if (!item->cache)
2165                         folder_item_read_cache(item);
2166                 cache_list = msgcache_get_msg_list(item->cache);
2167         } else {
2168                 if (item->cache)
2169                         msgcache_destroy(item->cache);
2170                 item->cache = msgcache_new();
2171                 item->cache_dirty = TRUE;
2172                 item->mark_dirty = TRUE;
2173                 item->tags_dirty = TRUE;
2174                 cache_list = NULL;
2175         }
2176
2177         /* Sort both lists */
2178         cache_list = g_slist_sort(cache_list, folder_sort_cache_list_by_msgnum);
2179         folder_list = g_slist_sort(folder_list, folder_sort_folder_list);
2180
2181         cache_list_cur = cache_list;
2182         folder_list_cur = folder_list;
2183
2184         if (cache_list_cur != NULL) {
2185                 GSList *cache_list_last;
2186         
2187                 cache_cur_num = ((MsgInfo *)cache_list_cur->data)->msgnum;
2188                 cache_list_last = g_slist_last(cache_list);
2189                 cache_max_num = ((MsgInfo *)cache_list_last->data)->msgnum;
2190         } else {
2191                 cache_cur_num = G_MAXINT;
2192                 cache_max_num = 0;
2193         }
2194
2195         if (folder_list_cur != NULL) {
2196                 GSList *folder_list_last;
2197         
2198                 folder_cur_num = GPOINTER_TO_INT(folder_list_cur->data);
2199                 folder_list_last = g_slist_last(folder_list);
2200                 folder_max_num = GPOINTER_TO_INT(folder_list_last->data);
2201         } else {
2202                 folder_cur_num = G_MAXINT;
2203                 folder_max_num = 0;
2204         }
2205
2206         while ((cache_cur_num != G_MAXINT) || (folder_cur_num != G_MAXINT)) {
2207                 /*
2208                  *  Message only exists in the folder
2209                  *  Remember message for fetching
2210                  */
2211                 if (folder_cur_num < cache_cur_num) {
2212                         gboolean add = FALSE;
2213
2214                         switch(FOLDER_TYPE(folder)) {
2215                                 case F_NEWS:
2216                                         if (folder_cur_num < cache_max_num)
2217                                                 break;
2218                                         
2219                                         if (folder->account->max_articles == 0) {
2220                                                 add = TRUE;
2221                                         }
2222
2223                                         if (folder_max_num <= folder->account->max_articles) {
2224                                                 add = TRUE;
2225                                         } else if (folder_cur_num > (folder_max_num - folder->account->max_articles)) {
2226                                                 add = TRUE;
2227                                         }
2228                                         break;
2229                                 default:
2230                                         add = TRUE;
2231                                         break;
2232                         }
2233                         
2234                         if (add) {
2235                                 new_list = g_slist_prepend(new_list, GINT_TO_POINTER(folder_cur_num));
2236                                 debug_print("Remembered message %d for fetching\n", folder_cur_num);
2237                         }
2238
2239                         /* Move to next folder number */
2240                         if (folder_list_cur)
2241                                 folder_list_cur = folder_list_cur->next;
2242
2243                         if (folder_list_cur != NULL)
2244                                 folder_cur_num = GPOINTER_TO_INT(folder_list_cur->data);
2245                         else
2246                                 folder_cur_num = G_MAXINT;
2247
2248                         continue;
2249                 }
2250
2251                 /*
2252                  *  Message only exists in the cache
2253                  *  Remove the message from the cache
2254                  */
2255                 if (cache_cur_num < folder_cur_num) {
2256                         msgcache_remove_msg(item->cache, cache_cur_num);
2257                         debug_print("Removed message %d from cache.\n", cache_cur_num);
2258
2259                         /* Move to next cache number */
2260                         if (cache_list_cur)
2261                                 cache_list_cur = cache_list_cur->next;
2262
2263                         if (cache_list_cur != NULL)
2264                                 cache_cur_num = ((MsgInfo *)cache_list_cur->data)->msgnum;
2265                         else
2266                                 cache_cur_num = G_MAXINT;
2267
2268                         update_flags |= F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT;
2269
2270                         continue;
2271                 }
2272
2273                 /*
2274                  *  Message number exists in folder and cache!
2275                  *  Check if the message has been modified
2276                  */
2277                 if (cache_cur_num == folder_cur_num) {
2278                         MsgInfo *msginfo;
2279
2280                         msginfo = msgcache_get_msg(item->cache, folder_cur_num);
2281                         if (msginfo && folder->klass->is_msg_changed && folder->klass->is_msg_changed(folder, item, msginfo)) {
2282                                 msgcache_remove_msg(item->cache, msginfo->msgnum);
2283                                 new_list = g_slist_prepend(new_list, GINT_TO_POINTER(msginfo->msgnum));
2284                                 procmsg_msginfo_free(msginfo);
2285
2286                                 debug_print("Remembering message %d to update...\n", folder_cur_num);
2287                         } else if (msginfo) {
2288                                 exists_list = g_slist_prepend(exists_list, msginfo);
2289
2290                                 if(prefs_common.thread_by_subject &&
2291                                         MSG_IS_IGNORE_THREAD(msginfo->flags) &&
2292                                         !subject_table_lookup(subject_table, msginfo->subject)) {
2293                                         subject_table_insert(subject_table, msginfo->subject, msginfo);
2294                                 }
2295                         }
2296                         
2297                         /* Move to next folder and cache number */
2298                         if (cache_list_cur)
2299                                 cache_list_cur = cache_list_cur->next;
2300                         
2301                         if (folder_list_cur)
2302                                 folder_list_cur = folder_list_cur->next;
2303
2304                         if (cache_list_cur != NULL)
2305                                 cache_cur_num = ((MsgInfo *)cache_list_cur->data)->msgnum;
2306                         else
2307                                 cache_cur_num = G_MAXINT;
2308
2309                         if (folder_list_cur != NULL)
2310                                 folder_cur_num = GPOINTER_TO_INT(folder_list_cur->data);
2311                         else
2312                                 folder_cur_num = G_MAXINT;
2313
2314                         continue;
2315                 }
2316         }
2317         
2318         for(cache_list_cur = cache_list; cache_list_cur != NULL; cache_list_cur = g_slist_next(cache_list_cur))
2319                 procmsg_msginfo_free((MsgInfo *) cache_list_cur->data);
2320
2321         g_slist_free(cache_list);
2322         g_slist_free(folder_list);
2323
2324         if (new_list != NULL) {
2325                 GSList *tmp_list = NULL;
2326                 newmsg_list = get_msginfos(item, new_list);
2327                 g_slist_free(new_list);
2328                 tmp_list = g_slist_concat(g_slist_copy(exists_list), g_slist_copy(newmsg_list));
2329                 syncronize_flags(item, tmp_list);
2330                 g_slist_free(tmp_list);
2331         } else {
2332                 syncronize_flags(item, exists_list);
2333         }
2334
2335         folder_item_update_freeze();
2336         
2337         item->scanning = ITEM_SCANNING;
2338
2339         if (newmsg_list != NULL) {
2340                 GSList *elem, *to_filter = NULL;
2341                 gboolean do_filter = (filtering == TRUE) &&
2342                                         (item->stype == F_INBOX) &&
2343                                         (item->folder->account != NULL) && 
2344                                         (item->folder->account->filter_on_recv);
2345                 
2346                 for (elem = newmsg_list; elem != NULL; elem = g_slist_next(elem)) {
2347                         MsgInfo *msginfo = (MsgInfo *) elem->data;
2348
2349                         msgcache_add_msg(item->cache, msginfo);
2350                         if (!do_filter) {
2351                                 exists_list = g_slist_prepend(exists_list, msginfo);
2352
2353                                 if(prefs_common.thread_by_subject &&
2354                                         MSG_IS_IGNORE_THREAD(msginfo->flags) &&
2355                                         !subject_table_lookup(subject_table, msginfo->subject)) {
2356                                         subject_table_insert(subject_table, msginfo->subject, msginfo);
2357                                 }                       
2358                         }
2359                 }
2360
2361                 if (do_filter) {
2362                         GSList *unfiltered;
2363                         
2364                         folder_item_set_batch(item, TRUE);
2365                         procmsg_msglist_filter(newmsg_list, item->folder->account, 
2366                                         &to_filter, &unfiltered, 
2367                                         TRUE);
2368                         folder_item_set_batch(item, FALSE);
2369                         
2370                         filtering_move_and_copy_msgs(newmsg_list);
2371                         if (to_filter != NULL) {
2372                                 for (elem = to_filter; elem; elem = g_slist_next(elem)) {
2373                                         MsgInfo *msginfo = (MsgInfo *)elem->data;
2374                                         procmsg_msginfo_free(msginfo);
2375                                 }
2376                                 g_slist_free(to_filter);
2377                         }
2378                         if (unfiltered != NULL) {
2379                                 for (elem = unfiltered; elem; elem = g_slist_next(elem)) {
2380                                         MsgInfo *msginfo = (MsgInfo *)elem->data;
2381                                         exists_list = g_slist_prepend(exists_list, msginfo);
2382
2383                                         if(prefs_common.thread_by_subject &&
2384                                                 MSG_IS_IGNORE_THREAD(msginfo->flags) &&
2385                                                 !subject_table_lookup(subject_table, msginfo->subject)) {
2386                                                 subject_table_insert(subject_table, msginfo->subject, msginfo);
2387                                         }
2388                                 }
2389                                 g_slist_free(unfiltered);
2390                         }
2391                         if (prefs_common.real_time_sync)
2392                                 folder_item_synchronise(item);
2393                 } else {
2394                         if (prefs_common.real_time_sync)
2395                                 folder_item_synchronise(item);
2396                 }
2397
2398                 g_slist_free(newmsg_list);
2399
2400                 update_flags |= F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT;
2401         }
2402
2403         folder_item_set_batch(item, TRUE);
2404         for (elem = exists_list; elem != NULL; elem = g_slist_next(elem)) {
2405                 MsgInfo *msginfo, *parent_msginfo;
2406
2407                 msginfo = elem->data;
2408                 if (MSG_IS_IGNORE_THREAD(msginfo->flags) && (MSG_IS_NEW(msginfo->flags) || MSG_IS_UNREAD(msginfo->flags)))
2409                         procmsg_msginfo_unset_flags(msginfo, MSG_NEW | MSG_UNREAD, 0);
2410                 if (!MSG_IS_IGNORE_THREAD(msginfo->flags) && procmsg_msg_has_flagged_parent(msginfo, MSG_IGNORE_THREAD)) {
2411                         procmsg_msginfo_change_flags(msginfo, MSG_IGNORE_THREAD, 0, MSG_NEW | MSG_UNREAD, 0);
2412                 }
2413                 if (!MSG_IS_WATCH_THREAD(msginfo->flags) && procmsg_msg_has_flagged_parent(msginfo, MSG_WATCH_THREAD)) {
2414                         procmsg_msginfo_set_flags(msginfo, MSG_WATCH_THREAD, 0);
2415                 }
2416                 if(prefs_common.thread_by_subject && !msginfo->inreplyto &&
2417                         !msginfo->references && !MSG_IS_IGNORE_THREAD(msginfo->flags) &&
2418                         (parent_msginfo = subject_table_lookup(subject_table, msginfo->subject)))
2419                 {
2420                         if(MSG_IS_IGNORE_THREAD(parent_msginfo->flags)) {
2421                                 procmsg_msginfo_change_flags(msginfo, MSG_IGNORE_THREAD, 0,
2422                                                 MSG_NEW | MSG_UNREAD, 0);
2423                         }
2424                 }
2425                 if ((folder_has_parent_of_type(item, F_OUTBOX) ||
2426                      folder_has_parent_of_type(item, F_QUEUE)  ||
2427                      folder_has_parent_of_type(item, F_DRAFT)  ||
2428                      folder_has_parent_of_type(item, F_TRASH)) &&
2429                     (MSG_IS_NEW(msginfo->flags) || MSG_IS_UNREAD(msginfo->flags)))
2430                         procmsg_msginfo_unset_flags(msginfo, MSG_NEW | MSG_UNREAD, 0);
2431                 if (MSG_IS_NEW(msginfo->flags))
2432                         newcnt++;
2433                 if (MSG_IS_UNREAD(msginfo->flags))
2434                         unreadcnt++;
2435                 if (MSG_IS_UNREAD(msginfo->flags) && procmsg_msg_has_marked_parent(msginfo))
2436                         unreadmarkedcnt++;
2437                 if (MSG_IS_MARKED(msginfo->flags))
2438                         markedcnt++;
2439                 if (MSG_IS_REPLIED(msginfo->flags))
2440                         repliedcnt++;
2441                 if (MSG_IS_FORWARDED(msginfo->flags))
2442                         forwardedcnt++;
2443                 if (MSG_IS_LOCKED(msginfo->flags))
2444                         lockedcnt++;
2445                 if (MSG_IS_IGNORE_THREAD(msginfo->flags))
2446                         ignoredcnt++;
2447                 if (MSG_IS_WATCH_THREAD(msginfo->flags))
2448                         watchedcnt++;
2449
2450                 totalcnt++;
2451
2452                 procmsg_msginfo_free(msginfo);
2453         }
2454         folder_item_set_batch(item, FALSE);
2455         g_slist_free(exists_list);
2456         
2457         if(prefs_common.thread_by_subject) {
2458                 g_hash_table_destroy(subject_table);
2459         }
2460         
2461         if (item->new_msgs != newcnt || item->unread_msgs != unreadcnt
2462         ||  item->total_msgs != totalcnt || item->marked_msgs != markedcnt
2463         ||  item->unreadmarked_msgs != unreadmarkedcnt
2464         ||  item->replied_msgs != repliedcnt || item->forwarded_msgs != forwardedcnt
2465         ||  item->locked_msgs != lockedcnt || item->ignored_msgs != ignoredcnt
2466         ||  item->watched_msgs != watchedcnt) {
2467                 update_flags |= F_ITEM_UPDATE_CONTENT;
2468         }
2469
2470         item->new_msgs = newcnt;
2471         item->unread_msgs = unreadcnt;
2472         item->total_msgs = totalcnt;
2473         item->unreadmarked_msgs = unreadmarkedcnt;
2474         item->marked_msgs = markedcnt;
2475         item->replied_msgs = repliedcnt;
2476         item->forwarded_msgs = forwardedcnt;
2477         item->locked_msgs = lockedcnt;
2478         item->ignored_msgs = ignoredcnt;
2479         item->watched_msgs = watchedcnt;
2480
2481         update_flags |= F_ITEM_UPDATE_MSGCNT;
2482
2483         folder_item_update(item, update_flags);
2484         folder_item_update_thaw();
2485         
2486         item->scanning = ITEM_NOT_SCANNING;
2487
2488         return 0;
2489 }
2490
2491 gint folder_item_scan(FolderItem *item)
2492 {
2493         return folder_item_scan_full(item, TRUE);
2494 }
2495
2496 static void folder_count_total_cache_memusage(FolderItem *item, gpointer data)
2497 {
2498         gint *memusage = (gint *)data;
2499
2500         if (item->cache == NULL)
2501                 return;
2502         
2503         *memusage += msgcache_get_memory_usage(item->cache);
2504 }
2505
2506 static gint folder_cache_time_compare_func(gconstpointer a, gconstpointer b)
2507 {
2508         FolderItem *fa = (FolderItem *)a;
2509         FolderItem *fb = (FolderItem *)b;
2510         
2511         return (gint) (msgcache_get_last_access_time(fa->cache) - msgcache_get_last_access_time(fb->cache));
2512 }
2513
2514 static void folder_find_expired_caches(FolderItem *item, gpointer data)
2515 {
2516         GSList **folder_item_list = (GSList **)data;
2517         gint difftime, expiretime;
2518         
2519         if (item->cache == NULL)
2520                 return;
2521
2522         if (item->opened > 0)
2523                 return;
2524
2525         difftime = (gint) (time(NULL) - msgcache_get_last_access_time(item->cache));
2526         expiretime = prefs_common.cache_min_keep_time * 60;
2527         debug_print("Cache unused time: %d (Expire time: %d)\n", difftime, expiretime);
2528
2529         if (difftime > expiretime && !item->opened && !item->processing_pending) {
2530                 *folder_item_list = g_slist_insert_sorted(*folder_item_list, item, folder_cache_time_compare_func);
2531         }
2532 }
2533
2534 gboolean folder_item_free_cache(FolderItem *item, gboolean force)
2535 {
2536         cm_return_val_if_fail(item != NULL, TRUE);
2537         
2538         if (item->cache == NULL)
2539                 return TRUE;
2540         
2541         if (item->opened > 0 && !force)
2542                 return FALSE;
2543
2544         folder_item_write_cache(item);
2545         msgcache_destroy(item->cache);
2546         item->cache = NULL;
2547         return TRUE;
2548 }
2549
2550 void folder_clean_cache_memory_force(void)
2551 {
2552         int old_cache_max_mem_usage = prefs_common.cache_max_mem_usage;
2553         int old_cache_min_keep_time = prefs_common.cache_min_keep_time;
2554
2555         prefs_common.cache_max_mem_usage = 0;
2556         prefs_common.cache_min_keep_time = 0;
2557
2558         folder_clean_cache_memory(NULL);
2559
2560         prefs_common.cache_max_mem_usage = old_cache_max_mem_usage;
2561         prefs_common.cache_min_keep_time = old_cache_min_keep_time;
2562 }
2563
2564 void folder_clean_cache_memory(FolderItem *protected_item)
2565 {
2566         gint memusage = 0;
2567
2568         folder_func_to_all_folders(folder_count_total_cache_memusage, &memusage);       
2569         debug_print("Total cache memory usage: %d\n", memusage);
2570         
2571         if (memusage > (prefs_common.cache_max_mem_usage * 1024)) {
2572                 GSList *folder_item_list = NULL, *listitem;
2573                 
2574                 debug_print("Trying to free cache memory\n");
2575
2576                 folder_func_to_all_folders(folder_find_expired_caches, &folder_item_list);      
2577                 listitem = folder_item_list;
2578                 while((listitem != NULL) && (memusage > (prefs_common.cache_max_mem_usage * 1024))) {
2579                         FolderItem *item = (FolderItem *)(listitem->data);
2580                         gint cache_size = 0;
2581                         if (item == protected_item) {
2582                                 listitem = listitem->next;
2583                                 continue;
2584                         }
2585                         debug_print("Freeing cache memory for %s\n", item->path ? item->path : item->name);
2586                         cache_size = msgcache_get_memory_usage(item->cache);
2587                         if (folder_item_free_cache(item, FALSE))
2588                                 memusage -= cache_size;
2589
2590                         listitem = listitem->next;
2591                 }
2592                 g_slist_free(folder_item_list);
2593         }
2594 }
2595
2596 static void folder_item_remove_cached_msg(FolderItem *item, MsgInfo *msginfo)
2597 {
2598         Folder *folder = item->folder;
2599
2600         cm_return_if_fail(folder != NULL);
2601
2602         if (folder->klass->remove_cached_msg == NULL)
2603                 return;
2604         
2605         folder->klass->remove_cached_msg(folder, item, msginfo);
2606 }
2607
2608 static void folder_item_clean_local_files(FolderItem *item, gint days)
2609 {
2610         cm_return_if_fail(item != NULL);
2611         cm_return_if_fail(item->folder != NULL);
2612
2613         if (FOLDER_TYPE(item->folder) == F_IMAP ||
2614             FOLDER_TYPE(item->folder) == F_NEWS) {
2615                 GSList *msglist = folder_item_get_msg_list(item);
2616                 GSList *cur;
2617                 time_t t = time(NULL);
2618                 for (cur = msglist; cur; cur = cur->next) {
2619                         MsgInfo *msginfo = (MsgInfo *)cur->data;
2620                         gint age = (t - msginfo->date_t) / (60*60*24);
2621                         if (age > days)
2622                                 folder_item_remove_cached_msg(item, msginfo);
2623                 }
2624                 procmsg_msg_list_free(msglist);
2625         }
2626 }
2627
2628 static void folder_item_read_cache(FolderItem *item)
2629 {
2630         gchar *cache_file, *mark_file, *tags_file;
2631         START_TIMING("");
2632         cm_return_if_fail(item != NULL);
2633
2634         if (item->path != NULL) {
2635                 cache_file = folder_item_get_cache_file(item);
2636                 mark_file = folder_item_get_mark_file(item);
2637                 tags_file = folder_item_get_tags_file(item);
2638                 item->cache = msgcache_read_cache(item, cache_file);
2639                 item->cache_dirty = FALSE;
2640                 item->mark_dirty = FALSE;
2641                 item->tags_dirty = FALSE;
2642                 if (!item->cache) {
2643                         MsgInfoList *list, *cur;
2644                         guint newcnt = 0, unreadcnt = 0;
2645                         guint markedcnt = 0, unreadmarkedcnt = 0;
2646                         guint repliedcnt = 0, forwardedcnt = 0;
2647                         guint lockedcnt = 0, ignoredcnt = 0;
2648                         guint watchedcnt = 0;
2649                         MsgInfo *msginfo;
2650
2651                         item->cache = msgcache_new();
2652                         item->cache_dirty = TRUE;
2653                         item->mark_dirty = TRUE;
2654                         item->tags_dirty = TRUE;
2655                         folder_item_scan_full(item, TRUE);
2656
2657                         msgcache_read_mark(item->cache, mark_file);
2658
2659                         list = msgcache_get_msg_list(item->cache);
2660                         for (cur = list; cur != NULL; cur = g_slist_next(cur)) {
2661                                 msginfo = cur->data;
2662
2663                                 if (MSG_IS_NEW(msginfo->flags))
2664                                         newcnt++;
2665                                 if (MSG_IS_UNREAD(msginfo->flags))
2666                                         unreadcnt++;
2667                                 if (MSG_IS_UNREAD(msginfo->flags) && procmsg_msg_has_marked_parent(msginfo))
2668                                         unreadmarkedcnt++;
2669                                 if (MSG_IS_MARKED(msginfo->flags))
2670                                         markedcnt++;
2671                                 if (MSG_IS_REPLIED(msginfo->flags))
2672                                         repliedcnt++;
2673                                 if (MSG_IS_FORWARDED(msginfo->flags))
2674                                         forwardedcnt++;
2675                                 if (MSG_IS_LOCKED(msginfo->flags))
2676                                         lockedcnt++;
2677                                 if (MSG_IS_IGNORE_THREAD(msginfo->flags))
2678                                         ignoredcnt++;
2679                                 if (MSG_IS_WATCH_THREAD(msginfo->flags))
2680                                         watchedcnt++;
2681                                 procmsg_msginfo_unset_flags(msginfo, MSG_FULLY_CACHED, 0);
2682                         }
2683                         item->new_msgs = newcnt;
2684                         item->unread_msgs = unreadcnt;
2685                         item->unreadmarked_msgs = unreadmarkedcnt;
2686                         item->marked_msgs = markedcnt;
2687                         item->replied_msgs = repliedcnt;
2688                         item->forwarded_msgs = forwardedcnt;
2689                         item->locked_msgs = lockedcnt;
2690                         item->ignored_msgs = ignoredcnt;
2691                         item->watched_msgs = watchedcnt;
2692                         procmsg_msg_list_free(list);
2693                 } else
2694                         msgcache_read_mark(item->cache, mark_file);
2695
2696                 msgcache_read_tags(item->cache, tags_file);
2697
2698                 g_free(cache_file);
2699                 g_free(mark_file);
2700                 g_free(tags_file);
2701         } else {
2702                 item->cache = msgcache_new();
2703                 item->cache_dirty = TRUE;
2704                 item->mark_dirty = TRUE;
2705                 item->tags_dirty = TRUE;
2706         }
2707
2708         END_TIMING();
2709         folder_clean_cache_memory(item);
2710 }
2711
2712 void folder_item_write_cache(FolderItem *item)
2713 {
2714         gchar *cache_file = NULL, *mark_file = NULL, *tags_file = NULL;
2715         FolderItemPrefs *prefs;
2716         gint filemode = 0;
2717         gchar *id;
2718         time_t last_mtime = (time_t)0;
2719         gboolean need_scan = FALSE;
2720         
2721         if (!item || !item->path || !item->cache)
2722                 return;
2723
2724         last_mtime = item->mtime;
2725         if (item->folder->klass->set_mtime) {
2726                 if (item->folder->klass->scan_required)
2727                         need_scan = item->folder->klass->scan_required(item->folder, item);
2728                 else
2729                         need_scan = TRUE;
2730         }
2731
2732         id = folder_item_get_identifier(item);
2733         debug_print("Save cache for folder %s\n", id);
2734         g_free(id);
2735
2736         if (item->cache_dirty)
2737                 cache_file = folder_item_get_cache_file(item);
2738         if (item->cache_dirty || item->mark_dirty)
2739                 mark_file = folder_item_get_mark_file(item);
2740         if (item->cache_dirty || item->tags_dirty)
2741                 tags_file = folder_item_get_tags_file(item);
2742         if (msgcache_write(cache_file, mark_file, tags_file, item->cache) < 0) {
2743                 prefs = item->prefs;
2744                 if (prefs && prefs->enable_folder_chmod && prefs->folder_chmod) {
2745                         /* for cache file */
2746                         filemode = prefs->folder_chmod;
2747                         if (filemode & S_IRGRP) filemode |= S_IWGRP;
2748                         if (filemode & S_IROTH) filemode |= S_IWOTH;
2749                         chmod(cache_file, filemode);
2750                 }
2751         } else {
2752                 item->cache_dirty = FALSE;
2753                 item->mark_dirty = FALSE;
2754                 item->tags_dirty = FALSE;
2755         }
2756
2757         if (!need_scan && item->folder->klass->set_mtime) {
2758                 if (item->mtime == last_mtime) {
2759                         item->folder->klass->set_mtime(item->folder, item);
2760                 }
2761         }
2762
2763         g_free(cache_file);
2764         g_free(mark_file);
2765         g_free(tags_file);
2766 }
2767
2768 MsgInfo *folder_item_get_msginfo(FolderItem *item, gint num)
2769 {
2770         Folder *folder;
2771         MsgInfo *msginfo = NULL;
2772         
2773         cm_return_val_if_fail(item != NULL, NULL);
2774         if (item->no_select)
2775                 return NULL;
2776         folder = item->folder;
2777         if (!item->cache)
2778                 folder_item_read_cache(item);
2779         
2780         if ((msginfo = msgcache_get_msg(item->cache, num)) != NULL)
2781                 return msginfo;
2782         
2783         msginfo = get_msginfo(item, num);
2784         if (msginfo != NULL) {
2785                 msgcache_add_msg(item->cache, msginfo);
2786                 return msginfo;
2787         }
2788         
2789         return NULL;
2790 }
2791
2792 MsgInfo *folder_item_get_msginfo_by_msgid(FolderItem *item, const gchar *msgid)
2793 {
2794         Folder *folder;
2795         MsgInfo *msginfo;
2796         
2797         cm_return_val_if_fail(item != NULL, NULL);
2798         cm_return_val_if_fail(msgid != NULL, NULL);
2799         if (item->no_select)
2800                 return FALSE;
2801         
2802         folder = item->folder;
2803         if (!item->cache)
2804                 folder_item_read_cache(item);
2805         
2806         if ((msginfo = msgcache_get_msg_by_id(item->cache, msgid)) != NULL)
2807                 return msginfo;
2808
2809         return NULL;
2810 }
2811
2812 GSList *folder_item_get_msg_list(FolderItem *item)
2813 {
2814         cm_return_val_if_fail(item != NULL, NULL);
2815         if (item->no_select)
2816                 return FALSE;
2817         
2818         if (item->cache == 0)
2819                 folder_item_read_cache(item);
2820
2821         cm_return_val_if_fail(item->cache != NULL, NULL);
2822         
2823         return msgcache_get_msg_list(item->cache);
2824 }
2825
2826 static void msginfo_set_mime_flags(GNode *node, gpointer data)
2827 {
2828         MsgInfo *msginfo = data;
2829         MimeInfo *mimeinfo = node->data;
2830         
2831         if (mimeinfo->disposition == DISPOSITIONTYPE_ATTACHMENT
2832          && (!mimeinfo->subtype || (strcmp(mimeinfo->subtype, "pgp-signature") &&
2833              strcmp(mimeinfo->subtype, "x-pkcs7-signature") &&
2834              strcmp(mimeinfo->subtype, "pkcs7-signature")))) {
2835                 procmsg_msginfo_set_flags(msginfo, 0, MSG_HAS_ATTACHMENT);
2836         } else if (mimeinfo->disposition == DISPOSITIONTYPE_UNKNOWN && 
2837                  mimeinfo->id == NULL &&
2838                  mimeinfo->type != MIMETYPE_TEXT &&
2839                  mimeinfo->type != MIMETYPE_MULTIPART) {
2840                 if (!mimeinfo->subtype 
2841                 || (strcmp(mimeinfo->subtype, "pgp-signature") && 
2842                     strcmp(mimeinfo->subtype, "x-pkcs7-signature") &&
2843                     strcmp(mimeinfo->subtype, "pkcs7-signature")))
2844                         procmsg_msginfo_set_flags(msginfo, 0, MSG_HAS_ATTACHMENT);
2845         } else if (mimeinfo->disposition == DISPOSITIONTYPE_INLINE &&
2846                  mimeinfo->id == NULL &&
2847                 (strcmp(mimeinfo->subtype, "pgp-signature") &&
2848                  strcmp(mimeinfo->subtype, "x-pkcs7-signature") &&
2849                  strcmp(mimeinfo->subtype, "pkcs7-signature")) && 
2850                 (procmime_mimeinfo_get_parameter(mimeinfo, "name") != NULL ||
2851                  procmime_mimeinfo_get_parameter(mimeinfo, "filename") != NULL)) {
2852                 procmsg_msginfo_set_flags(msginfo, 0, MSG_HAS_ATTACHMENT);
2853         } 
2854
2855         /* don't descend below top level message for signed and encrypted info */
2856         if (mimeinfo->type == MIMETYPE_MESSAGE)
2857                 return;
2858
2859         if (privacy_mimeinfo_is_signed(mimeinfo)) {
2860                 procmsg_msginfo_set_flags(msginfo, 0, MSG_SIGNED);
2861         }
2862
2863         if (privacy_mimeinfo_is_encrypted(mimeinfo)) {
2864                 procmsg_msginfo_set_flags(msginfo, 0, MSG_ENCRYPTED);
2865         } else {
2866                 /* searching inside encrypted parts doesn't really make sense */
2867                 g_node_children_foreach(mimeinfo->node, G_TRAVERSE_ALL, msginfo_set_mime_flags, msginfo);
2868         }
2869 }
2870
2871 gchar *folder_item_fetch_msg(FolderItem *item, gint num)
2872 {
2873         Folder *folder;
2874         gchar *msgfile;
2875         MsgInfo *msginfo;
2876
2877         cm_return_val_if_fail(item != NULL, NULL);
2878
2879         folder = item->folder;
2880
2881         cm_return_val_if_fail(folder->klass->fetch_msg != NULL, NULL);
2882         if (item->no_select)
2883                 return NULL;
2884
2885         msgfile = folder->klass->fetch_msg(folder, item, num);
2886
2887         if (msgfile != NULL) {
2888                 msginfo = folder_item_get_msginfo(item, num);
2889                 if ((msginfo != NULL) && !MSG_IS_SCANNED(msginfo->flags)) {
2890                         MimeInfo *mimeinfo;
2891
2892                         if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) && 
2893                             !folder_has_parent_of_type(msginfo->folder, F_DRAFT))
2894                                 mimeinfo = procmime_scan_file(msgfile);
2895                         else
2896                                 mimeinfo = procmime_scan_queue_file(msgfile);
2897                         /* check for attachments */
2898                         if (mimeinfo != NULL) { 
2899                                 g_node_children_foreach(mimeinfo->node, G_TRAVERSE_ALL, msginfo_set_mime_flags, msginfo);
2900                                 procmime_mimeinfo_free_all(mimeinfo);
2901
2902                                 procmsg_msginfo_set_flags(msginfo, 0, MSG_SCANNED);
2903                         }
2904                 }
2905                 procmsg_msginfo_free(msginfo);
2906         }
2907
2908         return msgfile;
2909 }
2910
2911 gchar *folder_item_fetch_msg_full(FolderItem *item, gint num, gboolean headers,
2912                                   gboolean body)
2913 {
2914         Folder *folder;
2915         gchar *msgfile;
2916         MsgInfo *msginfo;
2917
2918         cm_return_val_if_fail(item != NULL, NULL);
2919         if (item->no_select)
2920                 return NULL;
2921         
2922         folder = item->folder;
2923
2924         if (folder->klass->fetch_msg_full == NULL)
2925                 return folder_item_fetch_msg(item, num);
2926
2927         if (item->prefs->offlinesync && prefs_common.real_time_sync)
2928                 msgfile = folder->klass->fetch_msg_full(folder, item, num, 
2929                                                 TRUE, TRUE);
2930         else
2931                 msgfile = folder->klass->fetch_msg_full(folder, item, num, 
2932                                                 headers, body);
2933
2934         if (msgfile != NULL) {
2935                 msginfo = folder_item_get_msginfo(item, num);
2936                 if ((msginfo != NULL) && !MSG_IS_SCANNED(msginfo->flags)) {
2937                         MimeInfo *mimeinfo;
2938
2939                         if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
2940                             !folder_has_parent_of_type(msginfo->folder, F_DRAFT))
2941                                 mimeinfo = procmime_scan_file(msgfile);
2942                         else
2943                                 mimeinfo = procmime_scan_queue_file(msgfile);
2944                         /* check for attachments */
2945                         if (mimeinfo != NULL) { 
2946                                 g_node_children_foreach(mimeinfo->node, G_TRAVERSE_ALL, msginfo_set_mime_flags, msginfo);
2947                                 procmime_mimeinfo_free_all(mimeinfo);
2948
2949                                 procmsg_msginfo_set_flags(msginfo, 0, MSG_SCANNED);
2950                         }
2951                 }
2952                 procmsg_msginfo_free(msginfo);
2953         }
2954
2955         return msgfile;
2956 }
2957
2958
2959 static gint folder_item_get_msg_num_by_file(FolderItem *dest, const gchar *file)
2960 {
2961         static HeaderEntry hentry[] = {{"Message-ID:",  NULL, TRUE},
2962                                        {NULL,           NULL, FALSE}};
2963         FILE *fp;
2964         MsgInfo *msginfo;
2965         gint msgnum = 0;
2966         gchar buf[BUFFSIZE];
2967
2968         if ((fp = g_fopen(file, "rb")) == NULL)
2969                 return 0;
2970
2971         if ((folder_has_parent_of_type(dest, F_QUEUE)) || 
2972             (folder_has_parent_of_type(dest, F_DRAFT)))
2973                 while (fgets(buf, sizeof(buf), fp) != NULL) {
2974                         /* new way */
2975                         if ((!strncmp(buf, "X-Claws-End-Special-Headers: 1",
2976                                 strlen("X-Claws-End-Special-Headers:"))) ||
2977                             (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1",
2978                                 strlen("X-Sylpheed-End-Special-Headers:"))))
2979                                 break;
2980                         /* old way */
2981                         if (buf[0] == '\r' || buf[0] == '\n') break;
2982                         /* from other mailers */
2983                         if (!strncmp(buf, "Date: ", 6)
2984                         ||  !strncmp(buf, "To: ", 4)
2985                         ||  !strncmp(buf, "From: ", 6)
2986                         ||  !strncmp(buf, "Subject: ", 9)) {
2987                                 rewind(fp);
2988                                 break;
2989                         }
2990                 }
2991
2992         procheader_get_header_fields(fp, hentry);
2993         debug_print("looking for %s\n", hentry[0].body);
2994         if (hentry[0].body) {
2995                 extract_parenthesis(hentry[0].body, '<', '>');
2996                 remove_space(hentry[0].body);
2997                 if ((msginfo = msgcache_get_msg_by_id(dest->cache, hentry[0].body)) != NULL) {
2998                         msgnum = msginfo->msgnum;
2999                         procmsg_msginfo_free(msginfo);
3000
3001                         debug_print("found message as uid %d\n", msgnum);
3002                 }
3003         }
3004         
3005         g_free(hentry[0].body);
3006         hentry[0].body = NULL;
3007         fclose(fp);
3008
3009         return msgnum;
3010 }
3011
3012 static void copy_msginfo_flags(MsgInfo *source, MsgInfo *dest)
3013 {
3014         MsgPermFlags perm_flags = 0;
3015         MsgTmpFlags tmp_flags = 0;
3016
3017         /* create new flags */
3018         if (source != NULL) {
3019                 /* copy original flags */
3020                 perm_flags = source->flags.perm_flags;
3021                 tmp_flags = source->flags.tmp_flags;
3022         } else {
3023                 perm_flags = dest->flags.perm_flags;
3024                 tmp_flags = dest->flags.tmp_flags;
3025         }
3026
3027         /* remove new, unread and deleted in special folders */
3028         if (folder_has_parent_of_type(dest->folder, F_OUTBOX) || 
3029             folder_has_parent_of_type(dest->folder, F_QUEUE) || 
3030             folder_has_parent_of_type(dest->folder, F_DRAFT) || 
3031             folder_has_parent_of_type(dest->folder, F_TRASH))
3032                 perm_flags &= ~(MSG_NEW | MSG_UNREAD | MSG_DELETED);
3033
3034         /* set ignore flag of ignored parent exists */
3035         if (procmsg_msg_has_flagged_parent(dest, MSG_IGNORE_THREAD))
3036                 perm_flags |= MSG_IGNORE_THREAD;
3037
3038         /* unset FULLY_CACHED flags */
3039         perm_flags &= ~MSG_FULLY_CACHED;
3040
3041         if (procmsg_msg_has_flagged_parent(dest, MSG_WATCH_THREAD))
3042                 perm_flags |= MSG_WATCH_THREAD;
3043
3044         /* Unset tmp flags that should not be copied */
3045         tmp_flags &= ~(MSG_MOVE | MSG_COPY | MSG_MOVE_DONE);
3046
3047         /* unset flags that are set but should not */
3048         /* and set new flags */
3049         procmsg_msginfo_change_flags(dest,
3050                                   ~dest->flags.perm_flags & perm_flags,
3051                                   ~dest->flags.tmp_flags  & tmp_flags,
3052                                    dest->flags.perm_flags & ~perm_flags,
3053                                    dest->flags.tmp_flags  & ~tmp_flags);
3054         
3055         if (source && source->tags) {
3056                 g_slist_free(dest->tags);
3057                 dest->tags = g_slist_copy(source->tags);
3058                 folder_item_commit_tags(dest->folder, dest, dest->tags, NULL);
3059         }
3060 }
3061
3062 static void add_msginfo_to_cache(FolderItem *item, MsgInfo *newmsginfo, MsgInfo *flagsource)
3063 {
3064         /* update folder stats */
3065         if (MSG_IS_NEW(newmsginfo->flags))
3066                 item->new_msgs++;
3067         if (MSG_IS_UNREAD(newmsginfo->flags))
3068                 item->unread_msgs++;
3069         if (MSG_IS_UNREAD(newmsginfo->flags) && procmsg_msg_has_marked_parent(newmsginfo))
3070                 item->unreadmarked_msgs++;
3071         if (MSG_IS_MARKED(newmsginfo->flags))
3072                 item->marked_msgs++;
3073         if (MSG_IS_REPLIED(newmsginfo->flags))
3074                 item->replied_msgs++;
3075         if (MSG_IS_FORWARDED(newmsginfo->flags))
3076                 item->forwarded_msgs++;
3077         if (MSG_IS_LOCKED(newmsginfo->flags))
3078                 item->locked_msgs++;
3079         if (MSG_IS_IGNORE_THREAD(newmsginfo->flags))
3080                 item->ignored_msgs++;
3081         if (MSG_IS_WATCH_THREAD(newmsginfo->flags))
3082                 item->watched_msgs++;
3083         item->total_msgs++;
3084
3085         folder_item_update_freeze();
3086
3087         if (!item->cache)
3088                 folder_item_read_cache(item);
3089
3090         msgcache_add_msg(item->cache, newmsginfo);
3091         copy_msginfo_flags(flagsource, newmsginfo);
3092         folder_item_update_with_msg(item,  F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT | F_ITEM_UPDATE_ADDMSG, newmsginfo);
3093         folder_item_update_thaw();
3094 }
3095
3096 static void remove_msginfo_from_cache(FolderItem *item, MsgInfo *msginfo)
3097 {
3098         MsgInfoUpdate msginfo_update;
3099
3100         if (!item->cache)
3101                 folder_item_read_cache(item);
3102
3103         if (MSG_IS_NEW(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
3104                 msginfo->folder->new_msgs--;
3105         if (MSG_IS_UNREAD(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
3106                 msginfo->folder->unread_msgs--;
3107         if (MSG_IS_UNREAD(msginfo->flags) && procmsg_msg_has_marked_parent(msginfo))
3108                 msginfo->folder->unreadmarked_msgs--;
3109         if (MSG_IS_MARKED(msginfo->flags))
3110                 item->marked_msgs--;
3111         if (MSG_IS_REPLIED(msginfo->flags))
3112                 item->replied_msgs--;
3113         if (MSG_IS_FORWARDED(msginfo->flags))
3114                 item->forwarded_msgs--;
3115         if (MSG_IS_LOCKED(msginfo->flags))
3116                 item->locked_msgs--;
3117         if (MSG_IS_IGNORE_THREAD(msginfo->flags))
3118                 item->ignored_msgs--;
3119         if (MSG_IS_WATCH_THREAD(msginfo->flags))
3120                 item->watched_msgs--;
3121
3122         msginfo->folder->total_msgs--;
3123
3124         msginfo_update.msginfo = msginfo;
3125         msginfo_update.flags = MSGINFO_UPDATE_DELETED;
3126         hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
3127
3128         msgcache_remove_msg(item->cache, msginfo->msgnum);
3129         folder_item_update_with_msg(msginfo->folder, F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT | F_ITEM_UPDATE_REMOVEMSG, msginfo);
3130 }
3131
3132 gint folder_item_add_msg(FolderItem *dest, const gchar *file,
3133                          MsgFlags *flags, gboolean remove_source)
3134 {
3135         GSList file_list;
3136         MsgFileInfo fileinfo;
3137
3138         cm_return_val_if_fail(dest != NULL, -1);
3139         cm_return_val_if_fail(file != NULL, -1);
3140  
3141         fileinfo.msginfo = NULL;
3142         fileinfo.file = (gchar *)file;
3143         fileinfo.flags = flags;
3144         file_list.data = &fileinfo;
3145         file_list.next = NULL;
3146
3147         return folder_item_add_msgs(dest, &file_list, remove_source);
3148 }
3149
3150 gint folder_item_add_msgs(FolderItem *dest, GSList *file_list,
3151                           gboolean remove_source)
3152 {
3153         Folder *folder;
3154         gint ret, num, lastnum = -1;
3155         GSList *file_cur;
3156         GHashTable *relation;
3157         MsgFileInfo *fileinfo = NULL;
3158         gboolean folderscan = FALSE;
3159
3160         cm_return_val_if_fail(dest != NULL, -1);
3161         cm_return_val_if_fail(file_list != NULL, -1);
3162         cm_return_val_if_fail(dest->folder != NULL, -1);
3163         if (dest->no_select)
3164                 return -1;
3165
3166         folder = dest->folder;
3167
3168         relation = g_hash_table_new(g_direct_hash, g_direct_equal);
3169
3170         if (folder->klass->add_msgs != NULL) {
3171                 ret = folder->klass->add_msgs(folder, dest, file_list, relation);
3172                 if (ret < 0) {
3173                         g_hash_table_destroy(relation);
3174                         return ret;
3175                 }
3176         } else {
3177                 for (file_cur = file_list; file_cur != NULL; file_cur = g_slist_next(file_cur)) {
3178                         fileinfo = (MsgFileInfo *) file_cur->data;
3179
3180                         ret = folder->klass->add_msg(folder, dest, fileinfo->file, fileinfo->flags);
3181                         if (ret < 0) {
3182                                 g_hash_table_destroy(relation);
3183                                 return ret;
3184                         }
3185                         g_hash_table_insert(relation, fileinfo, GINT_TO_POINTER(ret));
3186                 }
3187         }
3188
3189         for (file_cur = file_list; file_cur != NULL; file_cur = g_slist_next(file_cur)) {
3190                 gpointer data, old_key;
3191
3192                 fileinfo = (MsgFileInfo *) file_cur->data;
3193                 if (g_hash_table_lookup_extended(relation, fileinfo, &old_key, &data))
3194                         num = GPOINTER_TO_INT(data);
3195                 else
3196                         num = -1;
3197
3198                 if (num >= 0) {
3199                         MsgInfo *newmsginfo;
3200
3201                         if (num == 0) {
3202                                 if (!folderscan) {
3203                                         folder_item_scan_full(dest, FALSE);
3204                                         folderscan = TRUE;
3205                                 }
3206                                 num = folder_item_get_msg_num_by_file(dest, fileinfo->file);
3207                                 debug_print("got num %d\n", num);
3208                         }
3209
3210                         if (num > lastnum)
3211                                 lastnum = num;
3212
3213                         if (num >= 0 && remove_source) {
3214                                 if (claws_unlink(fileinfo->file) < 0)
3215                                         FILE_OP_ERROR(fileinfo->file, "unlink");
3216                         }
3217
3218                         if (num == 0)
3219                                 continue;
3220
3221                         if (!folderscan && 
3222                             ((newmsginfo = get_msginfo(dest, num)) != NULL)) {
3223                                 add_msginfo_to_cache(dest, newmsginfo, NULL);
3224                                 procmsg_msginfo_free(newmsginfo);
3225                         } else if ((newmsginfo = msgcache_get_msg(dest->cache, num)) != NULL) {
3226                                 /* TODO: set default flags */
3227                                 procmsg_msginfo_free(newmsginfo);
3228                         }
3229                 }
3230         }
3231
3232         g_hash_table_destroy(relation);
3233
3234         return lastnum;
3235 }
3236
3237 static FolderItem *folder_item_move_recursive(FolderItem *src, FolderItem *dest, gboolean copy) 
3238 {
3239         GSList *mlist;
3240         FolderItem *new_item;
3241         FolderItem *next_item;
3242         GNode *srcnode;
3243         gchar *old_id, *new_id;
3244
3245         /* move messages */
3246         debug_print("%s %s to %s\n", copy?"Copying":"Moving", src->path, dest->path);
3247         new_item = folder_create_folder(dest, src->name);
3248         if (new_item == NULL) {
3249                 g_print("Can't create folder\n");
3250                 return NULL;
3251         }
3252         
3253         if (new_item->folder == NULL)
3254                 new_item->folder = dest->folder;
3255
3256         /* move messages */
3257         log_message(LOG_PROTOCOL, copy ?_("Copying %s to %s...\n"):_("Moving %s to %s...\n"), 
3258                         src->name, new_item->path);
3259
3260         mlist = folder_item_get_msg_list(src);
3261         
3262         if (mlist != NULL) {
3263                 if (copy)
3264                         folder_item_copy_msgs(new_item, mlist);
3265                 else
3266                         folder_item_move_msgs(new_item, mlist);
3267                 procmsg_msg_list_free(mlist);
3268         }
3269         
3270         /*copy prefs*/
3271         folder_item_prefs_copy_prefs(src, new_item);
3272         
3273         /* copy internal data */
3274         if (src->folder->klass == new_item->folder->klass &&
3275             src->folder->klass->copy_private_data != NULL)
3276                 src->folder->klass->copy_private_data(src->folder,
3277                                         src, new_item);
3278         new_item->collapsed = src->collapsed;
3279         new_item->thread_collapsed = src->thread_collapsed;
3280         new_item->threaded  = src->threaded;
3281         new_item->ret_rcpt  = src->ret_rcpt;
3282         new_item->hide_read_msgs = src->hide_read_msgs;
3283         new_item->hide_del_msgs = src->hide_del_msgs;
3284         new_item->sort_key  = src->sort_key;
3285         new_item->sort_type = src->sort_type;
3286
3287         prefs_matcher_write_config();
3288         
3289         /* recurse */
3290         srcnode = src->folder->node;    
3291         srcnode = g_node_find(srcnode, G_PRE_ORDER, G_TRAVERSE_ALL, src);
3292         srcnode = srcnode->children;
3293         while (srcnode != NULL) {
3294                 if (srcnode && srcnode->data) {
3295                         next_item = (FolderItem*) srcnode->data;
3296                         srcnode = srcnode->next;
3297                         if (folder_item_move_recursive(next_item, new_item, copy) == NULL) {
3298                                 return NULL;
3299                         }
3300                 }
3301         }
3302         old_id = folder_item_get_identifier(src);
3303         new_id = folder_item_get_identifier(new_item);
3304
3305         /* if src supports removing, otherwise only copy folder */
3306         if (src->folder->klass->remove_folder != NULL && !copy) 
3307                 src->folder->klass->remove_folder(src->folder, src);
3308         folder_write_list();
3309
3310         if (!copy) {
3311                 debug_print("updating rules : %s => %s\n", old_id, new_id);
3312                 if (old_id != NULL && new_id != NULL) {
3313                         prefs_filtering_rename_path(old_id, new_id);
3314                         account_rename_path(old_id, new_id);
3315                 }
3316         }
3317         g_free(old_id);
3318         g_free(new_id);
3319         
3320         return new_item;
3321 }
3322
3323 gint folder_item_move_to(FolderItem *src, FolderItem *dest, FolderItem **new_item, gboolean copy)
3324 {
3325         FolderItem *tmp = folder_item_parent(dest);
3326         gchar * src_identifier, * dst_identifier;
3327         gchar * phys_srcpath, * phys_dstpath, *tmppath;
3328
3329         while (tmp) {
3330                 if (tmp == src) {
3331                         return F_MOVE_FAILED_DEST_IS_CHILD;
3332                 }
3333                 tmp = folder_item_parent(tmp);
3334         }
3335         
3336         tmp = folder_item_parent(src);
3337         
3338         src_identifier = folder_item_get_identifier(src);
3339         dst_identifier = folder_item_get_identifier(dest);
3340         
3341         if(dst_identifier == NULL && dest->folder && folder_item_parent(dest) == NULL) {
3342                 /* dest can be a root folder */
3343                 dst_identifier = folder_get_identifier(dest->folder);
3344         }
3345         if (src_identifier == NULL || dst_identifier == NULL) {
3346                 debug_print("Can't get identifiers\n");
3347                 return F_MOVE_FAILED;
3348         }
3349
3350         if (src->folder != dest->folder && !copy) {
3351                 return F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX;
3352         }
3353
3354         phys_srcpath = folder_item_get_path(src);
3355         tmppath = folder_item_get_path(dest);
3356         phys_dstpath = g_strconcat(tmppath,
3357                        G_DIR_SEPARATOR_S,
3358                        g_path_get_basename(phys_srcpath),
3359                        NULL);
3360         g_free(tmppath);
3361
3362         if (folder_item_parent(src) == dest || src == dest) {
3363                 g_free(src_identifier);
3364                 g_free(dst_identifier);
3365                 g_free(phys_srcpath);
3366                 g_free(phys_dstpath);
3367                 return F_MOVE_FAILED_DEST_IS_PARENT;
3368         }
3369         debug_print("moving \"%s\" to \"%s\"\n", phys_srcpath, phys_dstpath);
3370         if ((tmp = folder_item_move_recursive(src, dest, copy)) == NULL) {
3371                 return F_MOVE_FAILED;
3372         }
3373         
3374         g_free(src_identifier);
3375         g_free(dst_identifier);
3376         g_free(phys_srcpath);
3377         g_free(phys_dstpath);
3378
3379         *new_item = tmp;
3380
3381         return F_MOVE_OK;
3382 }
3383
3384 struct find_data
3385 {
3386         gboolean found;
3387 };      
3388 static void find_num(gpointer key, gpointer value, gpointer data)
3389 {
3390         struct find_data *fdata = (struct find_data *)data;
3391         if (GPOINTER_TO_INT(value) == 0)
3392                 fdata->found = TRUE;
3393 }
3394
3395 static gboolean some_msgs_have_zero_num(GHashTable *hashtable)
3396 {
3397         struct find_data fdata;
3398         
3399         fdata.found = FALSE;
3400         g_hash_table_foreach(hashtable, find_num, &fdata);
3401         
3402         return fdata.found;
3403 }
3404
3405 /**
3406  * Copy a list of message to a new folder and remove
3407  * source messages if wanted
3408  */
3409 static gint do_copy_msgs(FolderItem *dest, GSList *msglist, gboolean remove_source)
3410 {
3411         Folder *folder;
3412         GSList *l;
3413         gint num, lastnum = -1;
3414         gboolean folderscan = FALSE;
3415         GHashTable *relation;
3416         GSList *not_moved = NULL;
3417         gint total = 0, curmsg = 0;
3418         MsgInfo *msginfo = NULL;
3419
3420         cm_return_val_if_fail(dest != NULL, -1);
3421         cm_return_val_if_fail(msglist != NULL, -1);
3422
3423         folder = dest->folder;
3424
3425         cm_return_val_if_fail(folder->klass->copy_msg != NULL, -1);
3426         if (dest->no_select)
3427                 return -1;
3428
3429         msginfo = (MsgInfo *)msglist->data;
3430         
3431         if (!msginfo)
3432                 return -1;
3433         
3434         if (!MSG_IS_QUEUED(msginfo->flags) && 
3435             MSG_IS_DRAFT(msginfo->flags) && 
3436             folder_has_parent_of_type(dest, F_QUEUE)) {
3437                 GSList *cur = msglist;
3438                 gboolean queue_err = FALSE;
3439                 for (; cur; cur = cur->next) {
3440                         Compose *compose = NULL;
3441                         FolderItem *queue = dest;
3442                         int val = 0;
3443                         
3444                         msginfo = (MsgInfo *)cur->data;
3445                         compose = compose_reedit(msginfo, TRUE);
3446                         if (compose == NULL) {
3447                                 queue_err = TRUE;
3448                                 continue;
3449                         }
3450                         val = compose_queue(compose, NULL, &queue, NULL,
3451                                         FALSE);
3452                         if (val < 0) {
3453                                 queue_err = TRUE;
3454                         } else if (remove_source) {
3455                                 folder_item_remove_msg(msginfo->folder, msginfo->msgnum);
3456                         }
3457                         if (val == 0)
3458                                 compose_close(compose);
3459                 }
3460                 return queue_err ? -1:0;
3461         }
3462
3463         relation = g_hash_table_new(g_direct_hash, g_direct_equal);
3464
3465         for (l = msglist ; l != NULL ; l = g_slist_next(l)) {
3466                 MsgInfo * msginfo = (MsgInfo *) l->data;
3467
3468                 if (msginfo->planned_download != 0) {
3469                         int old_planned = msginfo->planned_download;
3470                         partial_unmark(msginfo);
3471                         /* little hack to reenable after */
3472                         msginfo->planned_download = old_planned;
3473                 }
3474         }
3475
3476         /* 
3477          * Copy messages to destination folder and 
3478          * store new message numbers in newmsgnums
3479          */
3480         if (folder->klass->copy_msgs != NULL) {
3481                 if (folder->klass->copy_msgs(folder, dest, msglist, relation) < 0) {
3482                         g_hash_table_destroy(relation);
3483                         return -1;
3484                 }
3485         } else {
3486                 MsgInfo * msginfo;
3487                 l = msglist;
3488
3489                 /* immediately stop if src and dest folders are identical */
3490                 if (l != NULL) {
3491                         msginfo = (MsgInfo *) l->data;
3492                         if (msginfo != NULL && msginfo->folder == dest) {
3493                                 g_hash_table_destroy(relation);
3494                                 return -1;
3495                         }
3496                 }
3497
3498                 for (; l != NULL ; l = g_slist_next(l)) {
3499                         msginfo = (MsgInfo *) l->data;
3500
3501                         num = folder->klass->copy_msg(folder, dest, msginfo);
3502                         if (num > 0)
3503                                 g_hash_table_insert(relation, msginfo, GINT_TO_POINTER(num));
3504                         else
3505                                 not_moved = g_slist_prepend(not_moved, msginfo);
3506                 }
3507         }
3508
3509         if (remove_source) {
3510                 MsgInfo *msginfo = (MsgInfo *) msglist->data;
3511                 FolderItem *item = msginfo->folder;
3512                 /*
3513                  * Remove source messages from their folders if
3514                  * copying was successfull and update folder
3515                  * message counts
3516                  */
3517                 if (not_moved == NULL && item->folder->klass->remove_msgs) {
3518                         item->folder->klass->remove_msgs(item->folder,
3519                                                                 msginfo->folder,
3520                                                                 msglist,
3521                                                                 relation);
3522                 }
3523                 for (l = msglist; l != NULL; l = g_slist_next(l)) {
3524                         gpointer old_key, data;
3525                         msginfo = (MsgInfo *) l->data;
3526                         item = msginfo->folder;
3527
3528                         if (g_hash_table_lookup_extended(relation, msginfo, &old_key, &data))
3529                                 num = GPOINTER_TO_INT(data);
3530                         else
3531                                 num = 0;
3532
3533                         if (g_slist_find(not_moved, msginfo))
3534                                 continue;
3535
3536                         if ((num >= 0) && (item->folder->klass->remove_msg != NULL)) {
3537                                 if (!item->folder->klass->remove_msgs)
3538                                         item->folder->klass->remove_msg(item->folder,
3539                                                                 msginfo->folder,
3540                                                                 msginfo->msgnum);
3541                                 if (!item->folder->account || item->folder->account->imap_use_trash) {
3542                                         remove_msginfo_from_cache(item, msginfo);
3543                                 }
3544                         }
3545                 }
3546         }
3547
3548         /* Read cache for dest folder */
3549         if (!dest->cache) folder_item_read_cache(dest);
3550
3551         /* 
3552          * Fetch new MsgInfos for new messages in dest folder,
3553          * add them to the msgcache and update folder message counts
3554          */
3555         if (some_msgs_have_zero_num(relation)) {
3556                 folder_item_scan_full(dest, FALSE);
3557                 folderscan = TRUE;
3558         }
3559
3560         statusbar_print_all(_("Updating cache for %s..."), dest->path ? dest->path : "(null)");
3561         total = g_slist_length(msglist);
3562         
3563         if (FOLDER_TYPE(dest->folder) == F_IMAP && total > 1) {
3564                 folder_item_scan_full(dest, FALSE);
3565                 folderscan = TRUE;
3566         }
3567         folder_item_set_batch(dest, TRUE);
3568         for (l = msglist; l != NULL; l = g_slist_next(l)) {
3569                 MsgInfo *msginfo = (MsgInfo *) l->data;
3570                 gpointer data, old_key;
3571
3572                 if (!msginfo)
3573                         continue;
3574
3575                 if (g_hash_table_lookup_extended(relation, msginfo, &old_key, &data))
3576                         num = GPOINTER_TO_INT(data);
3577                 else
3578                         num = 0;
3579
3580                 statusbar_progress_all(curmsg++,total, 100);
3581                 if (curmsg % 100 == 0)
3582                         GTK_EVENTS_FLUSH();
3583
3584                 if (num >= 0) {
3585                         MsgInfo *newmsginfo = NULL;
3586
3587                         if (!folderscan && num > 0) {
3588                                 newmsginfo = get_msginfo(dest, num);
3589                                 if (newmsginfo != NULL) {
3590                                         add_msginfo_to_cache(dest, newmsginfo, msginfo);
3591                                 }
3592                         }
3593                         if (newmsginfo == NULL) {
3594                                 if (!folderscan) {
3595                                         folder_item_scan_full(dest, FALSE);
3596                                         folderscan = TRUE;
3597                                 }
3598                                 if (msginfo->msgid != NULL) {
3599                                         newmsginfo = folder_item_get_msginfo_by_msgid(dest, msginfo->msgid);
3600                                         if (newmsginfo != NULL) {
3601                                                 copy_msginfo_flags(msginfo, newmsginfo);
3602                                                 num = newmsginfo->msgnum;
3603                                         }
3604                                 }
3605                         }
3606
3607                         if (msginfo->planned_download 
3608                             == POP3_PARTIAL_DLOAD_DELE) {
3609                                 partial_mark_for_delete(newmsginfo);
3610                         }
3611                         if (msginfo->planned_download 
3612                             == POP3_PARTIAL_DLOAD_DLOAD) {
3613                                 partial_mark_for_download(newmsginfo);
3614                         }
3615                         if (!MSG_IS_POSTFILTERED (msginfo->flags)) {
3616                                 procmsg_msginfo_set_flags (   msginfo, MSG_POSTFILTERED, 0);
3617                                 if (newmsginfo) {
3618                                         procmsg_msginfo_set_flags (newmsginfo, MSG_POSTFILTERED, 0);
3619                                         hooks_invoke (MAIL_POSTFILTERING_HOOKLIST, newmsginfo);
3620                                 }
3621                         }
3622                         procmsg_msginfo_free(newmsginfo);
3623
3624
3625                         if (num > lastnum)
3626                                 lastnum = num;
3627                 }
3628         }
3629         folder_item_set_batch(dest, FALSE);
3630         statusbar_progress_all(0,0,0);
3631         statusbar_pop_all();
3632
3633         g_hash_table_destroy(relation);
3634         if (not_moved != NULL) {
3635                 g_slist_free(not_moved);
3636                 return -1;
3637         } else
3638                 return lastnum;
3639 }
3640
3641 /**
3642  * Move a message to a new folder.
3643  *
3644  * \param dest Destination folder
3645  * \param msginfo The message
3646  */
3647 gint folder_item_move_msg(FolderItem *dest, MsgInfo *msginfo)
3648 {
3649         GSList list;
3650
3651         cm_return_val_if_fail(dest != NULL, -1);
3652         cm_return_val_if_fail(msginfo != NULL, -1);
3653
3654         list.data = msginfo;
3655         list.next = NULL;
3656
3657         return do_copy_msgs(dest, &list, TRUE);
3658 }
3659
3660 /**
3661  * Move a list of messages to a new folder.
3662  *
3663  * \param dest Destination folder
3664  * \param msglist List of messages
3665  */
3666 gint folder_item_move_msgs(FolderItem *dest, GSList *msglist)
3667 {
3668         gint result = -1;
3669         cm_return_val_if_fail(dest != NULL, -1);
3670         cm_return_val_if_fail(msglist != NULL, -1);
3671         inc_lock();
3672         result = do_copy_msgs(dest, msglist, TRUE);
3673         inc_unlock();
3674         return result;
3675 }
3676
3677 /**
3678  * Copy a message to a new folder.
3679  *
3680  * \param dest Destination folder
3681  * \param msginfo The message
3682  */
3683 gint folder_item_copy_msg(FolderItem *dest, MsgInfo *msginfo)
3684 {
3685         GSList list;
3686
3687         cm_return_val_if_fail(dest != NULL, -1);
3688         cm_return_val_if_fail(msginfo != NULL, -1);
3689     
3690         list.data = msginfo;
3691         list.next = NULL;
3692
3693         return do_copy_msgs(dest, &list, FALSE);
3694 }
3695
3696 /**
3697  * Copy a list of messages to a new folder.
3698  *
3699  * \param dest Destination folder
3700  * \param msglist List of messages
3701  */
3702 gint folder_item_copy_msgs(FolderItem *dest, GSList *msglist)
3703 {
3704         gint result;
3705         cm_return_val_if_fail(dest != NULL, -1);
3706         cm_return_val_if_fail(msglist != NULL, -1);
3707
3708         inc_lock();
3709         result = do_copy_msgs(dest, msglist, FALSE);
3710         inc_unlock();
3711         
3712         return result;
3713 }
3714
3715 gint folder_item_remove_msg(FolderItem *item, gint num)
3716 {
3717         Folder *folder;
3718         gint ret;
3719         MsgInfo *msginfo;
3720
3721         cm_return_val_if_fail(item != NULL, -1);
3722         folder = item->folder;
3723         cm_return_val_if_fail(folder->klass->remove_msg != NULL, -1);
3724         if (item->no_select)
3725                 return -1;
3726
3727         if (!item->cache) folder_item_read_cache(item);
3728
3729         msginfo = msgcache_get_msg(item->cache, num);
3730         if (msginfo && MSG_IS_LOCKED(msginfo->flags)) {
3731                 procmsg_msginfo_free(msginfo);
3732                 return -1;
3733         }
3734         ret = folder->klass->remove_msg(folder, item, num);
3735
3736         if (!item->folder->account || item->folder->account->imap_use_trash) {
3737                 if (msginfo != NULL) {
3738                         remove_msginfo_from_cache(item, msginfo);
3739                         procmsg_msginfo_free(msginfo);
3740                 }
3741         }
3742
3743         return ret;
3744 }
3745
3746 gint folder_item_remove_msgs(FolderItem *item, GSList *msglist)
3747 {
3748         Folder *folder;
3749         gint ret = 0;
3750         GSList *real_list = NULL, *cur = NULL;
3751
3752         cm_return_val_if_fail(item != NULL, -1);
3753         folder = item->folder;
3754         cm_return_val_if_fail(folder != NULL, -1);
3755         if (item->no_select)
3756                 return -1;
3757         inc_lock();
3758         if (!item->cache) folder_item_read_cache(item);
3759
3760         folder_item_update_freeze();
3761         
3762         /* filter out locked mails */
3763         for (cur = msglist; cur; cur = cur->next) {
3764                 MsgInfo *info = (MsgInfo *)cur->data;
3765                 if (!MSG_IS_LOCKED(info->flags))
3766                         real_list = g_slist_prepend(real_list, info);
3767         }
3768
3769         real_list = g_slist_reverse(real_list);
3770
3771         if (item->folder->klass->remove_msgs) {
3772                 ret = item->folder->klass->remove_msgs(item->folder,
3773                                                         item,
3774                                                         real_list,
3775                                                         NULL);
3776         }
3777         while (ret == 0 && real_list != NULL) {
3778                 MsgInfo *msginfo = (MsgInfo *)real_list->data;
3779                 if (msginfo && MSG_IS_LOCKED(msginfo->flags)) {
3780                         real_list = real_list->next;
3781                         continue;
3782                 }
3783                 if (!item->folder->klass->remove_msgs)
3784                         ret = folder_item_remove_msg(item, msginfo->msgnum);
3785                 if (ret != 0) break;
3786                 msgcache_remove_msg(item->cache, msginfo->msgnum);
3787                 real_list = real_list->next;
3788         }
3789         g_slist_free(real_list);
3790         folder_item_scan_full(item, FALSE);
3791         folder_item_update_thaw();
3792         inc_unlock();
3793         return ret;
3794 }
3795
3796 gint folder_item_expunge(FolderItem *item)
3797 {
3798         Folder *folder = item->folder;
3799         gint result = 0;
3800         if (folder == NULL)
3801                 return -1;
3802         if (folder->klass->expunge) {
3803                 GSList *msglist = folder_item_get_msg_list(item);
3804                 GSList *cur;
3805                 result = folder->klass->expunge(folder, item);
3806                 if (result == 0) {
3807                         for (cur = msglist; cur; cur = cur->next) {
3808                                 MsgInfo *msginfo = (MsgInfo *)cur->data;
3809                                 if (MSG_IS_DELETED(msginfo->flags)) {
3810                                         remove_msginfo_from_cache(item, msginfo);
3811                                 }
3812                         }
3813                 }
3814                 procmsg_msg_list_free(msglist);
3815         }
3816         return result;
3817 }
3818
3819 gint folder_item_remove_all_msg(FolderItem *item)
3820 {
3821         Folder *folder;
3822         gint result;
3823
3824         cm_return_val_if_fail(item != NULL, -1);
3825         if (item->no_select)
3826                 return -1;
3827
3828         folder = item->folder;
3829
3830         inc_lock();
3831         if (folder->klass->remove_all_msg != NULL) {
3832                 result = folder->klass->remove_all_msg(folder, item);
3833
3834                 if (result == 0) {
3835                         folder_item_free_cache(item, TRUE);
3836                         item->cache = msgcache_new();
3837                         item->cache_dirty = TRUE;
3838                         item->mark_dirty = TRUE;
3839                         item->tags_dirty = TRUE;
3840                 }
3841         } else {
3842                 MsgInfoList *msglist;
3843
3844                 msglist = folder_item_get_msg_list(item);
3845                 result = folder_item_remove_msgs(item, msglist);
3846                 procmsg_msg_list_free(msglist);
3847         }
3848
3849         if (result == 0) {
3850                 item->new_msgs = 0;
3851                 item->unread_msgs = 0;
3852                 item->unreadmarked_msgs = 0;
3853                 item->marked_msgs = 0;
3854                 item->total_msgs = 0;
3855                 item->replied_msgs = 0;
3856                 item->forwarded_msgs = 0;
3857                 item->locked_msgs = 0;
3858                 item->ignored_msgs = 0;
3859                 item->watched_msgs = 0;
3860                 folder_item_update(item, F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT);
3861         }
3862
3863         inc_unlock();
3864         return result;
3865 }
3866
3867 void folder_item_change_msg_flags(FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3868 {
3869         cm_return_if_fail(item != NULL);
3870         cm_return_if_fail(msginfo != NULL);
3871         
3872         item->mark_dirty = TRUE;
3873
3874         if (item->no_select)
3875                 return;
3876         
3877         if (item->folder->klass->change_flags != NULL && item->scanning != ITEM_SCANNING_WITH_FLAGS) {
3878                 item->folder->klass->change_flags(item->folder, item, msginfo, newflags);
3879         } else {
3880                 msginfo->flags.perm_flags = newflags;
3881         }
3882 }
3883
3884 void folder_item_commit_tags(FolderItem *item, MsgInfo *msginfo, GSList *tags_set, GSList *tags_unset)
3885 {
3886         Folder *folder = NULL;
3887
3888         if (!msginfo)
3889                 return;
3890         if (!item)
3891                 return;
3892         if (!tags_set && !tags_unset)
3893                 return;
3894
3895         folder = item->folder;
3896         if (!folder)
3897                 return;
3898         
3899         item->tags_dirty = TRUE;
3900
3901         if (folder->klass->commit_tags == NULL)
3902                 return;
3903         
3904         folder->klass->commit_tags(item, msginfo, tags_set, tags_unset);
3905 }
3906
3907 gboolean folder_item_is_msg_changed(FolderItem *item, MsgInfo *msginfo)
3908 {
3909         Folder *folder;
3910
3911         cm_return_val_if_fail(item != NULL, FALSE);
3912         if (item->no_select)
3913                 return FALSE;
3914
3915         folder = item->folder;
3916
3917         cm_return_val_if_fail(folder->klass->is_msg_changed != NULL, -1);
3918
3919         return folder->klass->is_msg_changed(folder, item, msginfo);
3920 }
3921
3922 void folder_item_discard_cache(FolderItem *item)
3923 {
3924         gchar *dir;
3925         gchar *cache;
3926
3927         if (!item)
3928                 return;
3929
3930         if (item->cache) {
3931                 msgcache_destroy(item->cache);
3932                 item->cache = NULL;
3933         }
3934         dir = folder_item_get_path(item);
3935         if (is_dir_exist(dir))
3936                 remove_all_numbered_files(dir);
3937         g_free(dir);
3938         
3939         cache = folder_item_get_cache_file(item);
3940         if (is_file_exist(cache))
3941                 claws_unlink(cache);
3942         g_free(cache);
3943         
3944 }
3945
3946 static gchar *folder_item_get_cache_file(FolderItem *item)
3947 {
3948         gchar *path;
3949         gchar *file;
3950         gchar *old_file;
3951
3952         cm_return_val_if_fail(item != NULL, NULL);
3953         cm_return_val_if_fail(item->path != NULL, NULL);
3954
3955         path = folder_item_get_path(item);
3956         cm_return_val_if_fail(path != NULL, NULL);
3957         if (!is_dir_exist(path))
3958                 make_dir_hier(path);
3959         file = g_strconcat(path, G_DIR_SEPARATOR_S, CACHE_FILE, NULL);
3960         old_file = g_strconcat(path, G_DIR_SEPARATOR_S, OLD_CACHE_FILE, NULL);
3961
3962         if (!is_file_exist(file) && is_file_exist(old_file))
3963                 move_file(old_file, file, FALSE);
3964         g_free(old_file);
3965         g_free(path);
3966
3967         return file;
3968 }
3969
3970 static gchar *folder_item_get_mark_file(FolderItem *item)
3971 {
3972         gchar *path;
3973         gchar *file;
3974         gchar *old_file;
3975
3976         cm_return_val_if_fail(item != NULL, NULL);
3977         cm_return_val_if_fail(item->path != NULL, NULL);
3978
3979         path = folder_item_get_path(item);
3980         cm_return_val_if_fail(path != NULL, NULL);
3981         if (!is_dir_exist(path))
3982                 make_dir_hier(path);
3983         file = g_strconcat(path, G_DIR_SEPARATOR_S, MARK_FILE, NULL);
3984         old_file = g_strconcat(path, G_DIR_SEPARATOR_S, OLD_MARK_FILE, NULL);
3985
3986         if (!is_file_exist(file) && is_file_exist(old_file))
3987                 move_file(old_file, file, FALSE);
3988         g_free(old_file);
3989         g_free(path);
3990
3991         return file;
3992 }
3993
3994 static gchar *folder_item_get_tags_file(FolderItem *item)
3995 {
3996         gchar *path;
3997         gchar *identifier;
3998         gchar *file;
3999
4000         /* we save tags files in rc_dir, because tagsrc is there too,
4001          * and storing tags directly in the mailboxes would give strange
4002          * result when using another Claws mailbox from another install
4003          * with different tags. */
4004
4005         cm_return_val_if_fail(item != NULL, NULL);
4006
4007         identifier = folder_item_get_identifier(item);
4008         cm_return_val_if_fail(identifier != NULL, NULL);
4009
4010 #ifdef G_OS_WIN32
4011         while (strchr(identifier, '/'))
4012                 *strchr(identifier, '/') = '\\';
4013 #endif
4014
4015         path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
4016                            "tagsdb", G_DIR_SEPARATOR_S,
4017                            identifier, NULL);
4018         
4019         g_free(identifier);
4020                            
4021         if (!is_dir_exist(path))
4022                 make_dir_hier(path);
4023
4024         file = g_strconcat(path, G_DIR_SEPARATOR_S, TAGS_FILE, NULL);
4025         
4026         g_free(path);
4027
4028         return file;
4029 }
4030
4031 static gpointer xml_to_folder_item(gpointer nodedata, gpointer data)
4032 {
4033         XMLNode *xmlnode = (XMLNode *) nodedata;
4034         Folder *folder = (Folder *) data;
4035         FolderItem *item;
4036
4037         cm_return_val_if_fail(xmlnode != NULL, NULL);
4038         cm_return_val_if_fail(folder != NULL, NULL);
4039
4040         if (strcmp2(xmlnode->tag->tag, "folderitem") != 0) {
4041                 g_warning("tag name != \"folderitem\"\n");
4042                 return NULL;
4043         }
4044
4045         item = folder_item_new(folder, "", "");
4046         if (folder->klass->item_set_xml != NULL)
4047                 folder->klass->item_set_xml(folder, item, xmlnode->tag);
4048         else
4049                 folder_item_set_xml(folder, item, xmlnode->tag);
4050
4051         item->folder = folder;
4052
4053         switch (item->stype) {
4054         case F_INBOX:  folder->inbox  = item; break;
4055         case F_OUTBOX: folder->outbox = item; break;
4056         case F_DRAFT:  folder->draft  = item; break;
4057         case F_QUEUE:  folder->queue  = item; break;
4058         case F_TRASH:  folder->trash  = item; break;
4059         default:       break;
4060         }
4061         folder_item_prefs_read_config(item);
4062
4063         return item;
4064 }
4065
4066 static gboolean folder_item_set_node(GNode *node, gpointer data)
4067 {
4068         FolderItem *item = (FolderItem *) node->data;
4069         item->node = node;
4070
4071         return FALSE;
4072 }
4073
4074 static Folder *folder_get_from_xml(GNode *node)
4075 {
4076         Folder *folder;
4077         XMLNode *xmlnode;
4078         GList *list;
4079         FolderClass *klass = NULL;
4080         GNode *cur;
4081
4082         cm_return_val_if_fail(node->data != NULL, NULL);
4083
4084         xmlnode = node->data;
4085         if (strcmp2(xmlnode->tag->tag, "folder") != 0) {
4086                 g_warning("tag name != \"folder\"\n");
4087                 return NULL;
4088         }
4089         list = xmlnode->tag->attr;
4090         for (; list != NULL; list = list->next) {
4091                 XMLAttr *attr = list->data;
4092
4093                 if (!attr || !attr->name || !attr->value) continue;
4094                 if (!strcmp(attr->name, "type"))
4095                         klass = folder_get_class_from_string(attr->value);
4096         }
4097         if (klass == NULL)
4098                 return NULL;
4099
4100         folder = folder_new(klass, "", "");
4101         cm_return_val_if_fail(folder != NULL, NULL);
4102
4103         if (klass->set_xml)
4104                 klass->set_xml(folder, xmlnode->tag);
4105         else
4106                 folder_set_xml(folder, xmlnode->tag);
4107
4108         cur = node->children;
4109         while (cur != NULL) {
4110                 GNode *itemnode;
4111
4112                 itemnode = g_node_map(cur, xml_to_folder_item, (gpointer) folder);
4113                 g_node_append(folder->node, itemnode);
4114                 cur = cur->next;
4115         }
4116         g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, folder_item_set_node, NULL);
4117         
4118         return folder;
4119 }
4120
4121 static gchar *folder_get_list_path(void)
4122 {
4123         static gchar *filename = NULL;
4124
4125         if (!filename)
4126                 filename =  g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
4127                                         FOLDER_LIST, NULL);
4128
4129         return filename;
4130 }
4131
4132 static gpointer folder_item_to_xml(gpointer nodedata, gpointer data)
4133 {
4134         FolderItem *item = (FolderItem *) nodedata;
4135         XMLTag *tag;
4136
4137         cm_return_val_if_fail(item != NULL, NULL);
4138
4139         if (item->folder->klass->item_get_xml != NULL)
4140                 tag = item->folder->klass->item_get_xml(item->folder, item);
4141         else
4142                 tag = folder_item_get_xml(item->folder, item);
4143
4144         return xml_node_new(tag, NULL);
4145 }
4146
4147 static GNode *folder_get_xml_node(Folder *folder)
4148 {
4149         GNode *node;
4150         XMLNode *xmlnode;
4151         XMLTag *tag;
4152
4153         cm_return_val_if_fail(folder != NULL, NULL);
4154
4155         if (folder->klass->get_xml != NULL)
4156                 tag = folder->klass->get_xml(folder);
4157         else
4158                 tag = folder_get_xml(folder);
4159
4160         xml_tag_add_attr(tag, xml_attr_new("type", folder->klass->idstr));
4161
4162         xmlnode = xml_node_new(tag, NULL);
4163
4164         node = g_node_new(xmlnode);
4165         
4166         cm_return_val_if_fail (folder->node != NULL, NULL);
4167         
4168         if (folder->node->children) {
4169                 GNode *cur;
4170
4171                 cur = folder->node->children;
4172                 while (cur) {
4173                         GNode *xmlnode;
4174
4175                         xmlnode = g_node_map(cur, folder_item_to_xml, (gpointer) folder);
4176                         g_node_append(node, xmlnode);
4177                         cur = cur->next;
4178                 }
4179         }
4180
4181         return node;
4182 }
4183
4184 static void folder_update_op_count_rec(GNode *node)
4185 {
4186         FolderItem *fitem = FOLDER_ITEM(node->data);
4187
4188         if (g_node_depth(node) > 0) {
4189                 if (fitem->op_count > 0) {
4190                         fitem->op_count = 0;
4191                         folder_item_update(fitem, F_ITEM_UPDATE_MSGCNT);
4192                 }
4193                 if (node->children) {
4194                         GNode *child;
4195
4196                         child = node->children;
4197                         while (child) {
4198                                 GNode *cur;
4199
4200                                 cur = child;
4201                                 child = cur->next;
4202                                 folder_update_op_count_rec(cur);
4203                         }
4204                 }
4205         }
4206 }
4207
4208 void folder_update_op_count(void) 
4209 {
4210         GList *cur;
4211         Folder *folder;
4212
4213         for (cur = folder_list; cur != NULL; cur = cur->next) {
4214                 folder = cur->data;
4215                 folder_update_op_count_rec(folder->node);
4216         }
4217 }
4218
4219 typedef struct _type_str {
4220         gchar * str;
4221         gint type;
4222 } type_str;
4223
4224
4225 /*
4226 static gchar * folder_item_get_tree_identifier(FolderItem * item)
4227 {
4228         if (item->parent != NULL) {
4229                 gchar * path;
4230                 gchar * id;
4231
4232                 path = folder_item_get_tree_identifier(item->parent);
4233                 if (path == NULL)
4234                         return NULL;
4235
4236                 id = g_strconcat(path, "/", item->name, NULL);
4237                 g_free(path);
4238
4239                 return id;
4240         }
4241         else {
4242                 return g_strconcat("/", item->name, NULL);
4243         }
4244 }
4245 */
4246
4247 /* CLAWS: temporary local folder for filtering */
4248 #define TEMP_FOLDER "TEMP_FOLDER"
4249 #define PROCESSING_FOLDER_ITEM "processing"     
4250
4251 static FolderItem *processing_folder_item;
4252
4253 static void folder_create_processing_folder(void)
4254 {
4255         Folder *processing_folder;
4256         gchar      *tmpname;
4257
4258         if ((processing_folder = folder_find_from_name(TEMP_FOLDER, mh_get_class())) == NULL) {
4259                 gchar *tmppath;
4260
4261                 tmppath =
4262                     g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
4263                                 "tempfolder", NULL);
4264                 processing_folder =
4265                     folder_new(mh_get_class(), TEMP_FOLDER, tmppath);
4266                 g_free(tmppath);
4267         }
4268         g_assert(processing_folder != NULL);
4269
4270         debug_print("tmpparentroot %s\n", LOCAL_FOLDER(processing_folder)->rootpath);
4271         /* FIXME: [W32] The code below does not correctly merge
4272            relative filenames; there should be a function to handle
4273            this.  */
4274         if (!is_relative_filename(LOCAL_FOLDER(processing_folder)->rootpath))
4275                 tmpname = g_strconcat(LOCAL_FOLDER(processing_folder)->rootpath,
4276                                       G_DIR_SEPARATOR_S, PROCESSING_FOLDER_ITEM,
4277                                       NULL);
4278         else
4279                 tmpname = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
4280                                       LOCAL_FOLDER(processing_folder)->rootpath,
4281                                       G_DIR_SEPARATOR_S, PROCESSING_FOLDER_ITEM,
4282                                       NULL);
4283
4284         if (!is_dir_exist(tmpname)) {
4285                 debug_print("*TMP* creating %s\n", tmpname);
4286                 processing_folder_item = processing_folder->klass->create_folder(processing_folder,
4287                                                                                  processing_folder->node->data,
4288                                                                                  PROCESSING_FOLDER_ITEM);
4289         } else {
4290                 debug_print("*TMP* already created\n");
4291                 processing_folder_item = folder_item_new(processing_folder, PROCESSING_FOLDER_ITEM, PROCESSING_FOLDER_ITEM);
4292                 g_assert(processing_folder_item);
4293                 folder_item_append(processing_folder->node->data, processing_folder_item);
4294         }
4295         g_assert(processing_folder_item != NULL);
4296         g_free(tmpname);
4297 }
4298
4299 FolderItem *folder_get_default_processing(void)
4300 {
4301         if (!processing_folder_item) {
4302                 folder_create_processing_folder();
4303         }
4304         return processing_folder_item;
4305 }
4306
4307 /* folder_persist_prefs_new() - return hash table with persistent
4308  * settings (and folder name as key). 
4309  * (note that in claws other options are in the folder_item_prefs_RC
4310  * file, so those don't need to be included in PersistPref yet) 
4311  */
4312 static GHashTable *folder_persist_prefs_new(Folder *folder)
4313 {
4314         GHashTable *pptable;
4315
4316         cm_return_val_if_fail(folder, NULL);
4317         pptable = g_hash_table_new(g_str_hash, g_str_equal);
4318         folder_get_persist_prefs_recursive(folder->node, pptable);
4319         return pptable;
4320 }
4321
4322 static void folder_persist_prefs_free(GHashTable *pptable)
4323 {
4324         cm_return_if_fail(pptable);
4325         g_hash_table_foreach_remove(pptable, persist_prefs_free, NULL);
4326         g_hash_table_destroy(pptable);
4327 }
4328
4329 static const PersistPrefs *folder_get_persist_prefs(GHashTable *pptable, const char *name)
4330 {
4331         if (pptable == NULL || name == NULL) return NULL;
4332         return g_hash_table_lookup(pptable, name);
4333 }
4334
4335 static void folder_item_restore_persist_prefs(FolderItem *item, GHashTable *pptable)
4336 {
4337         const PersistPrefs *pp;
4338         gchar *id = folder_item_get_identifier(item);
4339
4340         pp = folder_get_persist_prefs(pptable, id); 
4341         g_free(id);
4342
4343         if (!pp) return;
4344
4345         /* CLAWS: since not all folder properties have been migrated to 
4346          * folderlist.xml, we need to call the old stuff first before
4347          * setting things that apply both to Main and Claws. */
4348         folder_item_prefs_read_config(item); 
4349
4350         item->collapsed = pp->collapsed;
4351         item->thread_collapsed = pp->thread_collapsed;
4352         item->threaded  = pp->threaded;
4353         item->ret_rcpt  = pp->ret_rcpt;
4354         item->hide_read_msgs = pp->hide_read_msgs;
4355         item->hide_del_msgs = pp->hide_del_msgs;
4356         item->sort_key  = pp->sort_key;
4357         item->sort_type = pp->sort_type;
4358 }
4359
4360 static void folder_get_persist_prefs_recursive(GNode *node, GHashTable *pptable)
4361 {
4362         FolderItem *item = FOLDER_ITEM(node->data);
4363         PersistPrefs *pp;
4364         GNode *child, *cur;
4365         gchar *id;
4366
4367         cm_return_if_fail(node != NULL);
4368         cm_return_if_fail(item != NULL);
4369
4370         /* NOTE: item->path == NULL means top level folder; not interesting
4371          * to store preferences of that one.  */
4372         if (item->path) {
4373                 id = folder_item_get_identifier(item);
4374                 pp = g_new0(PersistPrefs, 1);
4375                 cm_return_if_fail(pp != NULL);
4376                 pp->collapsed = item->collapsed;
4377                 pp->thread_collapsed = item->thread_collapsed;
4378                 pp->threaded  = item->threaded;
4379                 pp->ret_rcpt  = item->ret_rcpt; 
4380                 pp->hide_read_msgs = item->hide_read_msgs;
4381                 pp->hide_del_msgs = item->hide_del_msgs;
4382                 pp->sort_key  = item->sort_key;
4383                 pp->sort_type = item->sort_type;
4384                 g_hash_table_insert(pptable, id, pp);
4385         }
4386
4387         if (node->children) {
4388                 child = node->children;
4389                 while (child) {
4390                         cur = child;
4391                         child = cur->next;
4392                         folder_get_persist_prefs_recursive(cur, pptable);
4393                 }
4394         }       
4395 }
4396
4397 static gboolean persist_prefs_free(gpointer key, gpointer val, gpointer data)
4398 {
4399         g_free(key);
4400         g_free(val);
4401         return TRUE;    
4402 }
4403
4404 void folder_item_apply_processing(FolderItem *item)
4405 {
4406         GSList *processing_list;
4407         GSList *mlist, *cur;
4408         guint total = 0, curmsg = 0;
4409         gint last_apply_per_account;
4410
4411         cm_return_if_fail(item != NULL);
4412
4413         if (item->no_select)
4414                return;
4415
4416         processing_list = item->prefs->processing;
4417
4418         if (!pre_global_processing && !processing_list
4419         &&  !post_global_processing)
4420                 return;
4421
4422         debug_print("processing %s\n", item->name);
4423         folder_item_update_freeze();
4424
4425         inc_lock();
4426
4427         mlist = folder_item_get_msg_list(item);
4428         total = g_slist_length(mlist);
4429         statusbar_print_all(_("Processing messages..."));
4430
4431         last_apply_per_account = prefs_common.apply_per_account_filtering_rules;
4432         prefs_common.apply_per_account_filtering_rules = FILTERING_ACCOUNT_RULES_SKIP;
4433
4434         folder_item_set_batch(item, TRUE);
4435         for (cur = mlist ; cur != NULL ; cur = cur->next) {
4436                 MsgInfo * msginfo;
4437
4438                 msginfo = (MsgInfo *) cur->data;
4439                 
4440                 /* reset parameters that can be modified by processing */
4441                 msginfo->hidden = 0;
4442                 msginfo->score = 0;
4443
4444                 statusbar_progress_all(curmsg++,total, 10);
4445
4446                 /* apply pre global rules */
4447                 filter_message_by_msginfo(pre_global_processing, msginfo, NULL,
4448                                 FILTERING_PRE_PROCESSING, NULL);
4449                 
4450                 /* apply rules of the folder */
4451                 filter_message_by_msginfo(processing_list, msginfo, NULL,
4452                                 FILTERING_FOLDER_PROCESSING, item->name);
4453
4454                 /* apply post global rules */
4455                 filter_message_by_msginfo(post_global_processing, msginfo, NULL,
4456                                 FILTERING_POST_PROCESSING, NULL);
4457                 if (curmsg % 1000 == 0)
4458                         GTK_EVENTS_FLUSH();
4459         }
4460         folder_item_set_batch(item, FALSE);
4461
4462         prefs_common.apply_per_account_filtering_rules = last_apply_per_account;
4463
4464         if (pre_global_processing || processing_list
4465             || post_global_processing)
4466                 filtering_move_and_copy_msgs(mlist);
4467         for (cur = mlist ; cur != NULL ; cur = cur->next) {
4468                 MsgInfo * msginfo = (MsgInfo *)cur->data;
4469                 procmsg_msginfo_free(msginfo);
4470         }
4471         g_slist_free(mlist);
4472         
4473         statusbar_progress_all(0,0,0);
4474         statusbar_pop_all();
4475
4476         inc_unlock();
4477
4478         folder_item_update_thaw();
4479 }
4480
4481 /*
4482  *  functions for handling FolderItem content changes
4483  */
4484 static gint folder_item_update_freeze_cnt = 0;
4485
4486 static void folder_item_update_with_msg(FolderItem *item, FolderItemUpdateFlags update_flags, MsgInfo *msg)
4487 {
4488         if (folder_item_update_freeze_cnt == 0 /* || (msg != NULL && item->opened) */) {
4489                 FolderItemUpdateData source;
4490         
4491                 source.item = item;
4492                 source.update_flags = update_flags;
4493                 source.msg = msg;
4494                 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &source);
4495         } else {
4496                 item->update_flags |= update_flags & ~(F_ITEM_UPDATE_ADDMSG | F_ITEM_UPDATE_REMOVEMSG);
4497         }
4498 }
4499
4500 /**
4501  * Notify the folder system about changes to a folder. If the
4502  * update system is not frozen the FOLDER_ITEM_UPDATE_HOOKLIST will
4503  * be invoked, otherwise the changes will be remebered until
4504  * the folder system is thawed.
4505  *
4506  * \param item The FolderItem that was changed
4507  * \param update_flags Type of changed that was made
4508  */
4509 void folder_item_update(FolderItem *item, FolderItemUpdateFlags update_flags)
4510 {
4511         folder_item_update_with_msg(item, update_flags, NULL);
4512 }
4513
4514 void folder_item_update_recursive(FolderItem *item, FolderItemUpdateFlags update_flags)
4515 {
4516         GNode *node = item->folder->node;       
4517
4518         node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
4519         node = node->children;
4520
4521         folder_item_update(item, update_flags);
4522         while (node != NULL) {
4523                 if (node && node->data) {
4524                         FolderItem *next_item = (FolderItem*) node->data;
4525
4526                         folder_item_update(next_item, update_flags);
4527                 }
4528                 node = node->next;
4529         }
4530 }
4531
4532 void folder_item_update_freeze(void)
4533 {
4534         folder_item_update_freeze_cnt++;
4535 }
4536
4537 static void folder_item_update_func(FolderItem *item, gpointer data)
4538 {
4539         FolderItemUpdateData source;
4540     
4541         if (item->update_flags) {
4542                 source.item = item;
4543                 source.update_flags = item->update_flags;
4544                 source.msg = NULL;
4545                 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &source);                             
4546                 item->update_flags = 0;
4547         }
4548 }
4549
4550 void folder_item_update_thaw(void)
4551 {
4552         if (folder_item_update_freeze_cnt > 0)
4553                 folder_item_update_freeze_cnt--;
4554         if (folder_item_update_freeze_cnt == 0) {
4555                 /* Update all folders */
4556                 folder_func_to_all_folders(folder_item_update_func, NULL);
4557         }
4558 }
4559
4560 void folder_item_synchronise(FolderItem *item)
4561 {
4562         if (!item)
4563                 return;
4564         if (item->prefs->offlinesync && item->folder->klass->synchronise) {
4565                 statuswindow_print_all(_("Synchronising %s for offline use...\n"), item->path ? item->path : "(null)");
4566                 item->folder->klass->synchronise(item, 
4567                         item->prefs->offlinesync_days);
4568                 if (item->prefs->offlinesync_days > 0 &&
4569                     item->prefs->remove_old_bodies)
4570                         folder_item_clean_local_files(item, item->prefs->offlinesync_days);
4571                 statuswindow_pop_all();
4572         }
4573 }
4574
4575 static void folder_item_synchronise_func(FolderItem *item, gpointer data)
4576 {
4577         Folder *folder = (Folder *)data;
4578         if (folder == NULL || item->folder == folder) {
4579                 folder_item_synchronise(item);
4580         }
4581 }
4582
4583 void folder_synchronise(Folder *folder)
4584 {
4585         folder_func_to_all_folders(folder_item_synchronise_func, folder);
4586 }
4587
4588 typedef struct _WantSyncData {
4589         Folder *folder;
4590         gboolean want_sync;
4591 } WantSyncData;
4592
4593 static void folder_item_want_synchronise_func(FolderItem *item, gpointer data)
4594 {
4595         WantSyncData *want_sync_data = (WantSyncData *)data;
4596         
4597         if (want_sync_data->folder == NULL || item->folder == want_sync_data->folder) {
4598                 if (item->prefs->offlinesync && item->folder->klass->synchronise)
4599                         want_sync_data->want_sync |= TRUE;
4600         }
4601 }
4602
4603 gboolean folder_want_synchronise(Folder *folder)
4604 {
4605         WantSyncData *want_sync_data = g_new0(WantSyncData, 1);
4606         gboolean result;
4607         want_sync_data->folder = folder;
4608         want_sync_data->want_sync = FALSE;
4609         
4610         folder_func_to_all_folders(folder_item_want_synchronise_func, want_sync_data);
4611         result = want_sync_data->want_sync;
4612         g_free(want_sync_data);
4613         if (result > 0)
4614                 debug_print("Folder %s wants sync\n", folder->name);
4615         return result;
4616 }
4617
4618 void folder_item_set_batch (FolderItem *item, gboolean batch)
4619 {
4620         if (!item || !item->folder)
4621                 return;
4622         if (item->folder->klass->set_batch) {
4623                 item->folder->klass->set_batch(item->folder, item, batch);
4624         }
4625 }
4626
4627 gboolean folder_has_parent_of_type(FolderItem *item, 
4628                                           SpecialFolderItemType type) 
4629 {
4630         FolderItem *cur = item;
4631
4632         if (!item)
4633                 return FALSE;
4634         /* if we already know it, make it short */
4635         if (item->parent_stype != -1) {
4636                 return (item->parent_stype == type);
4637         }
4638         
4639         /* if we don't, find the type from the first possible parent,
4640          * and set our parent type to be faster next time */
4641         while (cur) {
4642                 if (cur->stype == type || cur->parent_stype == type) {
4643                         item->parent_stype = type;
4644                         return TRUE;
4645                 }
4646                 cur = folder_item_parent(cur);
4647         }
4648         
4649         /* if we didn't match what was asked, we didn't return. If our
4650          * parent type is unknown, we may as well find it now to be faster
4651          * later. */
4652         if (item->parent_stype == -1) {
4653                 cur = item;
4654                 while (cur) {
4655                         /* here's an exception: Inbox subfolders are normal. */
4656                         if (item->parent_stype == -1 && cur->stype == F_INBOX 
4657                         && item != cur) {
4658                                 item->parent_stype = F_NORMAL;
4659                                 break;
4660                         }
4661                         /* ah, we know this parent's parent's type, we may as 
4662                          * well copy it instead of going up the full way */
4663                         if (cur->parent_stype != -1) {
4664                                 item->parent_stype = cur->parent_stype;
4665                                 break;
4666                         }
4667                         /* we found a parent that has a special type. That's 
4668                          * our parent type. */
4669                         if (cur->stype != F_NORMAL) {
4670                                 cur->parent_stype = cur->stype;
4671                                 item->parent_stype = cur->stype;
4672                                 break;
4673                         }
4674                         /* if we didn't find anything, go up once more */
4675                         cur = folder_item_parent(cur);
4676                 }
4677                 /* as we still didn't find anything, our parents must all be 
4678                  * normal. */
4679                 if (item->parent_stype == -1) {
4680                         item->parent_stype = F_NORMAL;
4681                 }
4682         }
4683         return FALSE;
4684 }
4685
4686 gboolean folder_subscribe (const gchar *uri)
4687 {
4688         GList *cur;
4689         for (cur = folder_get_list(); cur != NULL; cur = g_list_next(cur)) {
4690                 Folder *folder = (Folder *) cur->data;
4691
4692                 if (folder->klass->subscribe
4693                 &&  folder->klass->subscribe(folder, uri)) {
4694                         return TRUE;
4695                 }
4696         }
4697         return FALSE;
4698
4699 }
4700
4701 gboolean folder_get_sort_type           (Folder         *folder,
4702                                          FolderSortKey  *sort_key,
4703                                          FolderSortType *sort_type)
4704 {
4705         if (!folder || !sort_key || !sort_type)
4706                 return FALSE;
4707         if (folder->klass->get_sort_type == NULL)
4708                 return FALSE;
4709         folder->klass->get_sort_type(folder, sort_key, sort_type); 
4710         return TRUE;
4711 }