2011-11-21 [pawel] 3.7.10cvs97
[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, "hidereadthreads"))
576                         item->hide_read_threads =  *attr->value == '1' ? TRUE : FALSE;
577                 else if (!strcmp(attr->name, "reqretrcpt"))
578                         item->ret_rcpt =  *attr->value == '1' ? TRUE : FALSE;
579                 else if (!strcmp(attr->name, "sort_key")) {
580                         if (!strcmp(attr->value, "none"))
581                                 item->sort_key = SORT_BY_NONE;
582                         else if (!strcmp(attr->value, "number"))
583                                 item->sort_key = SORT_BY_NUMBER;
584                         else if (!strcmp(attr->value, "size"))
585                                 item->sort_key = SORT_BY_SIZE;
586                         else if (!strcmp(attr->value, "date"))
587                                 item->sort_key = SORT_BY_DATE;
588                         else if (!strcmp(attr->value, "from"))
589                                 item->sort_key = SORT_BY_FROM;
590                         else if (!strcmp(attr->value, "subject"))
591                                 item->sort_key = SORT_BY_SUBJECT;
592                         else if (!strcmp(attr->value, "score"))
593                                 item->sort_key = SORT_BY_SCORE;
594                         else if (!strcmp(attr->value, "label"))
595                                 item->sort_key = SORT_BY_LABEL;
596                         else if (!strcmp(attr->value, "mark"))
597                                 item->sort_key = SORT_BY_MARK;
598                         else if (!strcmp(attr->value, "unread"))
599                                 item->sort_key = SORT_BY_STATUS;
600                         else if (!strcmp(attr->value, "mime"))
601                                 item->sort_key = SORT_BY_MIME;
602                         else if (!strcmp(attr->value, "to"))
603                                 item->sort_key = SORT_BY_TO;
604                         else if (!strcmp(attr->value, "locked"))
605                                 item->sort_key = SORT_BY_LOCKED;
606                         else if (!strcmp(attr->value, "tags"))
607                                 item->sort_key = SORT_BY_TAGS;
608                         else if (!strcmp(attr->value, "thread_date"))
609                                 item->sort_key = SORT_BY_THREAD_DATE;
610                 } else if (!strcmp(attr->name, "sort_type")) {
611                         if (!strcmp(attr->value, "ascending"))
612                                 item->sort_type = SORT_ASCENDING;
613                         else
614                                 item->sort_type = SORT_DESCENDING;
615                 } else if (!strcmp(attr->name, "account_id")) {
616                         PrefsAccount *account;
617
618                         account = account_find_from_id(atoi(attr->value));
619                         if (!account)
620                                 g_warning("account_id: %s not found\n", attr->value);
621                         else
622                                 item->account = account;
623                 } else if (!strcmp(attr->name, "apply_sub")) {
624                         item->apply_sub = *attr->value == '1' ? TRUE : FALSE;
625                 } else if (!strcmp(attr->name, "last_seen")) {
626                         if (!claws_crashed())
627                                 item->last_seen = atoi(attr->value);
628                         else
629                                 item->last_seen = 0;
630                 }
631         }
632 }
633
634 XMLTag *folder_item_get_xml(Folder *folder, FolderItem *item)
635 {
636         static gchar *folder_item_stype_str[] = {"normal", "inbox", "outbox",
637                                                  "draft", "queue", "trash"};
638         static gchar *sort_key_str[] = {"none", "number", "size", "date",
639                                         "from", "subject", "score", "label",
640                                         "mark", "unread", "mime", "to", 
641                                         "locked", "tags", "thread_date" };
642         XMLTag *tag;
643         gchar *value;
644
645         tag = xml_tag_new("folderitem");
646
647         xml_tag_add_attr(tag, xml_attr_new("type", folder_item_stype_str[item->stype]));
648         if (item->name)
649                 xml_tag_add_attr(tag, xml_attr_new("name", item->name));
650         if (item->path)
651                 xml_tag_add_attr(tag, xml_attr_new("path", item->path));
652         if (item->no_sub)
653                 xml_tag_add_attr(tag, xml_attr_new("no_sub", "1"));
654         if (item->no_select)
655                 xml_tag_add_attr(tag, xml_attr_new("no_select", "1"));
656         xml_tag_add_attr(tag, xml_attr_new("collapsed", item->collapsed && item->node->children ? "1" : "0"));
657         xml_tag_add_attr(tag, xml_attr_new("thread_collapsed", item->thread_collapsed ? "1" : "0"));
658         xml_tag_add_attr(tag, xml_attr_new("threaded", item->threaded ? "1" : "0"));
659         xml_tag_add_attr(tag, xml_attr_new("hidereadmsgs", item->hide_read_msgs ? "1" : "0"));
660         xml_tag_add_attr(tag, xml_attr_new("hidedelmsgs", item->hide_del_msgs ? "1" : "0"));
661         xml_tag_add_attr(tag, xml_attr_new("hidereadthreads", item->hide_read_threads ? "1" : "0"));
662         if (item->ret_rcpt)
663                 xml_tag_add_attr(tag, xml_attr_new("reqretrcpt", "1"));
664
665         if (item->sort_key != SORT_BY_NONE) {
666                 xml_tag_add_attr(tag, xml_attr_new("sort_key", sort_key_str[item->sort_key]));
667                 xml_tag_add_attr(tag, xml_attr_new("sort_type", item->sort_type == SORT_ASCENDING ? "ascending" : "descending"));
668         }
669
670         value = g_strdup_printf("%ld", (unsigned long int) item->mtime);
671         xml_tag_add_attr(tag, xml_attr_new("mtime", value));
672         g_free(value);
673         xml_tag_add_attr(tag, xml_attr_new_int("new", item->new_msgs));
674         xml_tag_add_attr(tag, xml_attr_new_int("unread", item->unread_msgs));
675         xml_tag_add_attr(tag, xml_attr_new_int("unreadmarked", item->unreadmarked_msgs));
676         xml_tag_add_attr(tag, xml_attr_new_int("marked", item->marked_msgs));
677         xml_tag_add_attr(tag, xml_attr_new_int("total", item->total_msgs));
678         xml_tag_add_attr(tag, xml_attr_new_int("replied", item->replied_msgs));
679         xml_tag_add_attr(tag, xml_attr_new_int("forwarded", item->forwarded_msgs));
680         xml_tag_add_attr(tag, xml_attr_new_int("locked", item->locked_msgs));
681         xml_tag_add_attr(tag, xml_attr_new_int("ignore", item->ignored_msgs));
682         xml_tag_add_attr(tag, xml_attr_new_int("watched", item->watched_msgs));
683         xml_tag_add_attr(tag, xml_attr_new_int("order", item->order));
684
685         if (item->account)
686                 xml_tag_add_attr(tag, xml_attr_new_int("account_id", item->account->account_id));
687         if (item->apply_sub)
688                 xml_tag_add_attr(tag, xml_attr_new("apply_sub", "1"));
689
690         xml_tag_add_attr(tag, xml_attr_new_int("last_seen", item->last_seen));
691
692         return tag;
693 }
694
695 void folder_set_ui_func(Folder *folder, FolderUIFunc func, gpointer data)
696 {
697         cm_return_if_fail(folder != NULL);
698
699         folder->ui_func = func;
700         folder->ui_func_data = data;
701 }
702
703 void folder_set_name(Folder *folder, const gchar *name)
704 {
705         cm_return_if_fail(folder != NULL);
706
707         g_free(folder->name);
708         folder->name = name ? g_strdup(name) : NULL;
709         if (folder->node && folder->node->data) {
710                 FolderItem *item = (FolderItem *)folder->node->data;
711
712                 g_free(item->name);
713                 item->name = name ? g_strdup(name) : NULL;
714         }
715 }
716
717 void folder_set_sort(Folder *folder, guint sort)
718 {
719         cm_return_if_fail(folder != NULL);
720
721         if (folder->sort != sort) {
722                 folder_remove(folder);
723                 folder->sort = sort;
724                 folder_add(folder);
725         }
726 }
727
728 static gboolean folder_tree_destroy_func(GNode *node, gpointer data) {
729         FolderItem *item = (FolderItem *) node->data;
730
731         folder_item_destroy(item);
732         return FALSE;
733 }
734
735 void folder_tree_destroy(Folder *folder)
736 {
737         GNode *node;
738
739         cm_return_if_fail(folder != NULL);
740
741         node = folder->node;
742         
743         prefs_filtering_clear_folder(folder);
744
745         if (node != NULL) {
746                 g_node_traverse(node, G_POST_ORDER, G_TRAVERSE_ALL, -1,
747                                 folder_tree_destroy_func, NULL);
748                 g_node_destroy(node);
749                 folder->node = NULL;
750         }
751 }
752
753 void folder_add(Folder *folder)
754 {
755         Folder *cur_folder;
756         GList *cur;
757         gint i;
758         FolderUpdateData hookdata;
759
760         cm_return_if_fail(folder != NULL);
761
762         if ((FOLDER_TYPE(folder) == F_IMAP ||
763              FOLDER_TYPE(folder) == F_NEWS) &&
764             folder->account == NULL) {
765                 return;
766         }
767
768         for (i = 0, cur = folder_list; cur != NULL; cur = cur->next, i++) {
769                 cur_folder = FOLDER(cur->data);
770                 if (cur_folder->sort < folder->sort)
771                         break;
772         }
773
774         folder_list = g_list_insert(folder_list, folder, i);
775
776         hookdata.folder = folder;
777         hookdata.update_flags = FOLDER_ADD_FOLDER;
778         hookdata.item = NULL;
779         hooks_invoke(FOLDER_UPDATE_HOOKLIST, &hookdata);
780 }
781
782 void folder_remove(Folder *folder)
783 {
784         FolderUpdateData hookdata;
785
786         cm_return_if_fail(folder != NULL);
787
788         folder_list = g_list_remove(folder_list, folder);
789
790         hookdata.folder = folder;
791         hookdata.update_flags = FOLDER_REMOVE_FOLDER;
792         hookdata.item = NULL;
793         hooks_invoke(FOLDER_UPDATE_HOOKLIST, &hookdata);
794 }
795
796 GList *folder_get_list(void)
797 {
798         return folder_list;
799 }
800
801 gint folder_read_list(void)
802 {
803         GNode *node, *cur;
804         XMLNode *xmlnode;
805         gchar *path;
806
807         path = folder_get_list_path();
808         if (!is_file_exist(path)) return -1;
809         node = xml_parse_file(path);
810         if (!node) return -1;
811
812         xmlnode = node->data;
813         if (strcmp2(xmlnode->tag->tag, "folderlist") != 0) {
814                 g_warning("wrong folder list\n");
815                 xml_free_tree(node);
816                 return -1;
817         }
818
819         cur = node->children;
820         while (cur != NULL) {
821                 Folder *folder;
822
823                 folder = folder_get_from_xml(cur);
824                 if (folder != NULL)
825                         folder_add(folder);
826                 else
827                         folder_unloaded_list = g_slist_append(folder_unloaded_list,
828                                 (gpointer) xml_copy_tree(cur));
829                 cur = cur->next;
830         }
831
832         xml_free_tree(node);
833         if (folder_list || folder_unloaded_list)
834                 return 0;
835         else
836                 return -1;
837 }
838
839 void folder_write_list(void)
840 {
841         GList *list;
842         GSList *slist;
843         Folder *folder;
844         gchar *path;
845         PrefFile *pfile;
846         GNode *rootnode;
847         XMLNode *xmlnode;
848         XMLTag *tag;
849
850         path = folder_get_list_path();
851         if ((pfile = prefs_write_open(path)) == NULL) return;
852
853         if (xml_file_put_xml_decl(pfile->fp) < 0) {
854                 prefs_file_close_revert(pfile);
855                 g_warning("failed to start write folder list.\n");
856                 return;         
857         }
858         tag = xml_tag_new("folderlist");
859
860         xmlnode = xml_node_new(tag, NULL);
861
862         rootnode = g_node_new(xmlnode);
863
864         for (list = folder_list; list != NULL; list = list->next) {
865                 GNode *node;
866
867                 folder = list->data;
868                 node = folder_get_xml_node(folder);
869                 if (node != NULL)
870                         g_node_append(rootnode, node);
871         }
872
873         for (slist = folder_unloaded_list; slist != NULL; slist = g_slist_next(slist)) {
874                 GNode *node = (GNode *) slist->data;
875
876                 g_node_append(rootnode, (gpointer) xml_copy_tree(node));
877         }
878
879         if (xml_write_tree(rootnode, pfile->fp) < 0) {
880                 prefs_file_close_revert(pfile);
881                 g_warning("failed to write folder list.\n");
882         } else if (prefs_file_close(pfile) < 0) {
883                 g_warning("failed to write folder list.\n");
884         }
885         xml_free_tree(rootnode);
886 }
887
888 static gboolean folder_scan_tree_func(GNode *node, gpointer data)
889 {
890         GHashTable *pptable = (GHashTable *)data;
891         FolderItem *item = (FolderItem *)node->data;
892         
893         folder_item_restore_persist_prefs(item, pptable);
894         folder_item_scan_full(item, FALSE);
895
896         return FALSE;
897 }
898
899 void folder_scan_tree(Folder *folder, gboolean rebuild)
900 {
901         GHashTable *pptable;
902         FolderUpdateData hookdata;
903         Folder *old_folder = folder;
904
905         if (!folder->klass->scan_tree)
906                 return;
907         
908         pptable = folder_persist_prefs_new(folder);
909
910         if (rebuild)
911                 folder_remove(folder);
912
913         if (folder->klass->scan_tree(folder) < 0) {
914                 if (rebuild)
915                         folder_add(old_folder);
916                 return;
917         } else if (rebuild)
918                 folder_add(folder);
919
920         hookdata.folder = folder;
921         hookdata.update_flags = FOLDER_TREE_CHANGED;
922         hookdata.item = NULL;
923         hooks_invoke(FOLDER_UPDATE_HOOKLIST, &hookdata);
924
925         g_node_traverse(folder->node, G_POST_ORDER, G_TRAVERSE_ALL, -1, folder_scan_tree_func, pptable);
926         folder_persist_prefs_free(pptable);
927
928         prefs_matcher_read_config();
929
930         folder_write_list();
931 }
932
933 static gboolean folder_restore_prefs_func(GNode *node, gpointer data)
934 {
935         GHashTable *pptable = (GHashTable *)data;
936         FolderItem *item = (FolderItem *)node->data;
937         
938         folder_item_restore_persist_prefs(item, pptable);
939
940         return FALSE;
941 }
942
943 void folder_fast_scan_tree(Folder *folder)
944 {
945         GHashTable *pptable;
946         FolderUpdateData hookdata;
947
948         if (!folder->klass->scan_tree)
949                 return;
950         
951         pptable = folder_persist_prefs_new(folder);
952
953         if (folder->klass->scan_tree(folder) < 0) {
954                 return;
955         } 
956
957         hookdata.folder = folder;
958         hookdata.update_flags = FOLDER_TREE_CHANGED;
959         hookdata.item = NULL;
960         hooks_invoke(FOLDER_UPDATE_HOOKLIST, &hookdata);
961
962         g_node_traverse(folder->node, G_POST_ORDER, G_TRAVERSE_ALL, -1, folder_restore_prefs_func, pptable);
963         folder_persist_prefs_free(pptable);
964
965         prefs_matcher_read_config();
966
967         folder_write_list();
968 }
969
970 FolderItem *folder_create_folder(FolderItem *parent, const gchar *name)
971 {
972         FolderItem *new_item;
973         
974         cm_return_val_if_fail(parent != NULL, NULL);
975
976         new_item = parent->folder->klass->create_folder(parent->folder, parent, name);
977         if (new_item) {
978                 FolderUpdateData hookdata;
979
980                 new_item->cache = msgcache_new();
981                 new_item->cache_dirty = TRUE;
982                 new_item->mark_dirty = TRUE;
983                 new_item->tags_dirty = TRUE;
984
985                 hookdata.folder = new_item->folder;
986                 hookdata.update_flags = FOLDER_TREE_CHANGED | FOLDER_ADD_FOLDERITEM;
987                 hookdata.item = new_item;
988                 hooks_invoke(FOLDER_UPDATE_HOOKLIST, &hookdata);
989         }
990
991         return new_item;
992 }
993
994 gint folder_item_rename(FolderItem *item, gchar *newname)
995 {
996         gint retval;
997
998         cm_return_val_if_fail(item != NULL, -1);
999         cm_return_val_if_fail(newname != NULL, -1);
1000
1001         retval = item->folder->klass->rename_folder(item->folder, item, newname);
1002
1003         if (retval >= 0) {
1004                 FolderItemUpdateData hookdata;
1005                 FolderUpdateData hookdata2;
1006
1007                 hookdata.item = item;
1008                 hookdata.update_flags = F_ITEM_UPDATE_NAME;
1009                 hookdata.msg = NULL;
1010                 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &hookdata);
1011
1012                 hookdata2.folder = item->folder;
1013                 hookdata2.item = item;
1014                 hookdata2.update_flags = FOLDER_RENAME_FOLDERITEM;
1015                 hooks_invoke(FOLDER_UPDATE_HOOKLIST, &hookdata2);
1016         }
1017
1018         return retval;
1019 }
1020
1021 struct TotalMsgCount
1022 {
1023         guint new_msgs;
1024         guint unread_msgs;
1025         guint unreadmarked_msgs;
1026         guint marked_msgs;
1027         guint total_msgs;
1028         guint replied_msgs;
1029         guint forwarded_msgs;
1030         guint locked_msgs;
1031         guint ignored_msgs;
1032         guint watched_msgs;
1033 };
1034
1035 struct FuncToAllFoldersData
1036 {
1037         FolderItemFunc  function;
1038         gpointer        data;
1039 };
1040
1041 static gboolean folder_func_to_all_folders_func(GNode *node, gpointer data)
1042 {
1043         FolderItem *item;
1044         struct FuncToAllFoldersData *function_data = (struct FuncToAllFoldersData *) data;
1045
1046         cm_return_val_if_fail(node->data != NULL, FALSE);
1047
1048         item = FOLDER_ITEM(node->data);
1049         cm_return_val_if_fail(item != NULL, FALSE);
1050
1051         function_data->function(item, function_data->data);
1052
1053         return FALSE;
1054 }
1055
1056 void folder_func_to_all_folders(FolderItemFunc function, gpointer data)
1057 {
1058         GList *list;
1059         Folder *folder;
1060         struct FuncToAllFoldersData function_data;
1061         
1062         function_data.function = function;
1063         function_data.data = data;
1064
1065         for (list = folder_list; list != NULL; list = list->next) {
1066                 folder = FOLDER(list->data);
1067                 if (folder->node)
1068                         g_node_traverse(folder->node, G_PRE_ORDER,
1069                                         G_TRAVERSE_ALL, -1,
1070                                         folder_func_to_all_folders_func,
1071                                         &function_data);
1072         }
1073 }
1074
1075 static void folder_count_total_msgs_func(FolderItem *item, gpointer data)
1076 {
1077         struct TotalMsgCount *count = (struct TotalMsgCount *)data;
1078
1079         count->new_msgs += item->new_msgs;
1080         count->unread_msgs += item->unread_msgs;
1081         count->unreadmarked_msgs += item->unreadmarked_msgs;
1082         count->marked_msgs += item->marked_msgs;
1083         count->total_msgs += item->total_msgs;
1084         count->replied_msgs += item->replied_msgs;
1085         count->forwarded_msgs += item->forwarded_msgs;
1086         count->locked_msgs += item->locked_msgs;
1087         count->ignored_msgs += item->ignored_msgs;
1088         count->watched_msgs += item->watched_msgs;
1089 }
1090
1091 struct TotalMsgStatus
1092 {
1093         guint new;
1094         guint unread;
1095         guint total;
1096         GString *str;
1097 };
1098
1099 static gboolean folder_get_status_full_all_func(GNode *node, gpointer data)
1100 {
1101         FolderItem *item;
1102         struct TotalMsgStatus *status = (struct TotalMsgStatus *)data;
1103         gchar *id;
1104  
1105         cm_return_val_if_fail(node->data != NULL, FALSE);
1106  
1107         item = FOLDER_ITEM(node->data);
1108
1109         if (!item->path) return FALSE;
1110
1111         status->new += item->new_msgs;
1112         status->unread += item->unread_msgs;
1113         status->total += item->total_msgs;
1114
1115         if (status->str) {
1116                 id = folder_item_get_identifier(item);
1117                 g_string_append_printf(status->str, "%5d %5d %5d %s\n",
1118                                   item->new_msgs, item->unread_msgs,
1119                                   item->total_msgs, id);
1120                 g_free(id);
1121         }
1122  
1123         return FALSE;
1124  }
1125  
1126 static void folder_get_status_full_all(GString *str, guint *new, guint *unread,
1127                                        guint *total)
1128 {
1129         GList *list;
1130         Folder *folder;
1131         struct TotalMsgStatus status;
1132  
1133         status.new = status.unread = status.total = 0;
1134         status.str = str;
1135  
1136         debug_print("Counting total number of messages...\n");
1137  
1138         for (list = folder_list; list != NULL; list = list->next) {
1139                 folder = FOLDER(list->data);
1140                 if (folder->node)
1141                         g_node_traverse(folder->node, G_PRE_ORDER,
1142                                         G_TRAVERSE_ALL, -1,
1143                                         folder_get_status_full_all_func,
1144                                         &status);
1145         }
1146  
1147         *new = status.new;
1148         *unread = status.unread;
1149         *total = status.total;
1150 }
1151
1152 gchar *folder_get_status(GPtrArray *folders, gboolean full)
1153 {
1154         guint new, unread, total;
1155         GString *str;
1156         gint i;
1157         gchar *ret;
1158
1159         new = unread = total = 0;
1160
1161         str = g_string_new(NULL);
1162
1163         if (folders) {
1164                 for (i = 0; i < folders->len; i++) {
1165                         FolderItem *item;
1166
1167                         item = g_ptr_array_index(folders, i);
1168                         new += item->new_msgs;
1169                         unread += item->unread_msgs;
1170                         total += item->total_msgs;
1171
1172                         if (full) {
1173                                 gchar *id;
1174
1175                                 id = folder_item_get_identifier(item);
1176                                 g_string_append_printf(str, "%5d %5d %5d %s\n",
1177                                                   item->new_msgs, item->unread_msgs,
1178                                                   item->total_msgs, id);
1179                                 g_free(id);
1180                         }
1181                 }
1182         } else {
1183                 folder_get_status_full_all(full ? str : NULL,
1184                                            &new, &unread, &total);
1185         }
1186
1187         if (full)
1188                 g_string_append_printf(str, "%5d %5d %5d\n", new, unread, total);
1189         else
1190                 g_string_append_printf(str, "%d %d %d\n", new, unread, total);
1191
1192         ret = str->str;
1193         g_string_free(str, FALSE);
1194  
1195         return ret;
1196 }
1197
1198 void folder_count_total_msgs(guint *new_msgs, guint *unread_msgs, 
1199                              guint *unreadmarked_msgs, guint *marked_msgs,
1200                              guint *total_msgs, guint *replied_msgs,
1201                              guint *forwarded_msgs, guint *locked_msgs,
1202                              guint *ignored_msgs, guint *watched_msgs)
1203 {
1204         struct TotalMsgCount count;
1205
1206         count.new_msgs = count.unread_msgs = count.unreadmarked_msgs = 0;
1207         count.total_msgs = count.replied_msgs = count.forwarded_msgs = 0;
1208         count.locked_msgs = count.ignored_msgs = count.watched_msgs = 0;
1209         count.marked_msgs = 0;
1210
1211         debug_print("Counting total number of messages...\n");
1212
1213         folder_func_to_all_folders(folder_count_total_msgs_func, &count);
1214
1215         *new_msgs = count.new_msgs;
1216         *unread_msgs = count.unread_msgs;
1217         *unreadmarked_msgs = count.unreadmarked_msgs;
1218         *marked_msgs = count.marked_msgs;
1219         *total_msgs = count.total_msgs;
1220         *replied_msgs = count.replied_msgs;
1221         *forwarded_msgs = count.forwarded_msgs;
1222         *locked_msgs = count.locked_msgs;
1223         *ignored_msgs = count.ignored_msgs;
1224         *watched_msgs = count.watched_msgs;
1225 }
1226
1227 Folder *folder_find_from_path(const gchar *path)
1228 {
1229         GList *list;
1230         Folder *folder;
1231
1232         for (list = folder_list; list != NULL; list = list->next) {
1233                 folder = list->data;
1234                 if ((FOLDER_TYPE(folder) == F_MH || 
1235                      FOLDER_TYPE(folder) == F_MBOX) &&
1236                     !path_cmp(LOCAL_FOLDER(folder)->rootpath, path))
1237                         return folder;
1238         }
1239
1240         return NULL;
1241 }
1242
1243 Folder *folder_find_from_name(const gchar *name, FolderClass *klass)
1244 {
1245         GList *list;
1246         Folder *folder;
1247
1248         for (list = folder_list; list != NULL; list = list->next) {
1249                 folder = list->data;
1250                 if (folder->klass == klass && 
1251                     strcmp2(name, folder->name) == 0)
1252                         return folder;
1253         }
1254
1255         return NULL;
1256 }
1257
1258 static gboolean folder_item_find_func(GNode *node, gpointer data)
1259 {
1260         FolderItem *item = node->data;
1261         gpointer *d = data;
1262         const gchar *path = d[0];
1263
1264         if (path_cmp(path, item->path) != 0)
1265                 return FALSE;
1266
1267         d[1] = item;
1268
1269         return TRUE;
1270 }
1271
1272 FolderItem *folder_find_item_from_path(const gchar *path)
1273 {
1274         Folder *folder;
1275         gpointer d[2];
1276         GList *list = folder_get_list();
1277         
1278         folder = list ? list->data:NULL;
1279         
1280         cm_return_val_if_fail(folder != NULL, NULL);
1281
1282         d[0] = (gpointer)path;
1283         d[1] = NULL;
1284         while (d[1] == NULL && list) {
1285                 folder = FOLDER(list->data);
1286                 g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1287                         folder_item_find_func, d);
1288                 list = list->next;
1289         }
1290         return d[1];
1291 }
1292
1293 static gboolean folder_item_find_func_real_path(GNode *node, gpointer data)
1294 {
1295         FolderItem *item = node->data;
1296         gpointer *d = data;
1297         const gchar *path = d[0];
1298         gchar *tmp = folder_item_get_path(item);
1299         if (path_cmp(path, tmp) != 0) {
1300                 g_free(tmp);
1301                 return FALSE;
1302         }
1303         g_free(tmp);
1304         d[1] = item;
1305
1306         return TRUE;
1307 }
1308
1309 FolderItem *folder_find_item_from_real_path(const gchar *path)
1310 {
1311         Folder *folder;
1312         gpointer d[2];
1313         GList *list = folder_get_list();
1314         
1315         folder = list ? list->data:NULL;
1316         
1317         cm_return_val_if_fail(folder != NULL, NULL);
1318
1319         d[0] = (gpointer)path;
1320         d[1] = NULL;
1321         while (d[1] == NULL && list) {
1322                 folder = FOLDER(list->data);
1323                 g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1324                         folder_item_find_func_real_path, d);
1325                 list = list->next;
1326         }
1327         return d[1];
1328 }
1329
1330 FolderItem *folder_find_child_item_by_name(FolderItem *item, const gchar *name)
1331 {
1332         GNode *node;
1333         FolderItem *child;
1334
1335         for (node = item->node->children; node != NULL; node = node->next) {
1336                 child = FOLDER_ITEM(node->data);
1337                 if (strcmp2(child->name, name) == 0) {
1338                         return child;
1339                 }
1340         }
1341
1342         return NULL;
1343 }
1344
1345 FolderClass *folder_get_class_from_string(const gchar *str)
1346 {
1347         GSList *classlist;
1348
1349         classlist = folder_get_class_list();
1350         for (; classlist != NULL; classlist = g_slist_next(classlist)) {
1351                 FolderClass *class = (FolderClass *) classlist->data;
1352                 if (g_ascii_strcasecmp(class->idstr, str) == 0)
1353                         return class;
1354         }
1355
1356         return NULL;
1357 }
1358
1359 gchar *folder_get_identifier(Folder *folder)
1360 {
1361         gchar *type_str;
1362
1363         cm_return_val_if_fail(folder != NULL, NULL);
1364
1365         type_str = folder->klass->idstr;
1366         return g_strconcat("#", type_str, "/", folder->name, NULL);
1367 }
1368
1369 gchar *folder_item_get_identifier(FolderItem *item)
1370 {
1371         gchar *id = NULL;
1372         gchar *folder_id = NULL;
1373
1374         cm_return_val_if_fail(item != NULL, NULL);
1375
1376         if (item->path == NULL)
1377                 return NULL;
1378
1379         folder_id = folder_get_identifier(item->folder);
1380         id = g_strconcat(folder_id, "/", item->path, NULL);
1381         g_free(folder_id);
1382
1383         return id;
1384 }
1385
1386 Folder *folder_find_from_identifier(const gchar *identifier)
1387 {
1388         gchar *str;
1389         gchar *p;
1390         gchar *name;
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->hide_read_threads = src->hide_read_threads;
3285         new_item->sort_key  = src->sort_key;
3286         new_item->sort_type = src->sort_type;
3287
3288         prefs_matcher_write_config();
3289         
3290         /* recurse */
3291         srcnode = src->folder->node;    
3292         srcnode = g_node_find(srcnode, G_PRE_ORDER, G_TRAVERSE_ALL, src);
3293         srcnode = srcnode->children;
3294         while (srcnode != NULL) {
3295                 if (srcnode && srcnode->data) {
3296                         next_item = (FolderItem*) srcnode->data;
3297                         srcnode = srcnode->next;
3298                         if (folder_item_move_recursive(next_item, new_item, copy) == NULL) {
3299                                 return NULL;
3300                         }
3301                 }
3302         }
3303         old_id = folder_item_get_identifier(src);
3304         new_id = folder_item_get_identifier(new_item);
3305
3306         /* if src supports removing, otherwise only copy folder */
3307         if (src->folder->klass->remove_folder != NULL && !copy) 
3308                 src->folder->klass->remove_folder(src->folder, src);
3309         folder_write_list();
3310
3311         if (!copy) {
3312                 debug_print("updating rules : %s => %s\n", old_id, new_id);
3313                 if (old_id != NULL && new_id != NULL) {
3314                         prefs_filtering_rename_path(old_id, new_id);
3315                         account_rename_path(old_id, new_id);
3316                 }
3317         }
3318         g_free(old_id);
3319         g_free(new_id);
3320         
3321         return new_item;
3322 }
3323
3324 gint folder_item_move_to(FolderItem *src, FolderItem *dest, FolderItem **new_item, gboolean copy)
3325 {
3326         FolderItem *tmp = folder_item_parent(dest);
3327         gchar * src_identifier, * dst_identifier;
3328         gchar * phys_srcpath, * phys_dstpath, *tmppath;
3329
3330         while (tmp) {
3331                 if (tmp == src) {
3332                         return F_MOVE_FAILED_DEST_IS_CHILD;
3333                 }
3334                 tmp = folder_item_parent(tmp);
3335         }
3336         
3337         tmp = folder_item_parent(src);
3338         
3339         src_identifier = folder_item_get_identifier(src);
3340         dst_identifier = folder_item_get_identifier(dest);
3341         
3342         if(dst_identifier == NULL && dest->folder && folder_item_parent(dest) == NULL) {
3343                 /* dest can be a root folder */
3344                 dst_identifier = folder_get_identifier(dest->folder);
3345         }
3346         if (src_identifier == NULL || dst_identifier == NULL) {
3347                 debug_print("Can't get identifiers\n");
3348                 return F_MOVE_FAILED;
3349         }
3350
3351         if (src->folder != dest->folder && !copy) {
3352                 return F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX;
3353         }
3354
3355         phys_srcpath = folder_item_get_path(src);
3356         tmppath = folder_item_get_path(dest);
3357         phys_dstpath = g_strconcat(tmppath,
3358                        G_DIR_SEPARATOR_S,
3359                        g_path_get_basename(phys_srcpath),
3360                        NULL);
3361         g_free(tmppath);
3362
3363         if (folder_item_parent(src) == dest || src == dest) {
3364                 g_free(src_identifier);
3365                 g_free(dst_identifier);
3366                 g_free(phys_srcpath);
3367                 g_free(phys_dstpath);
3368                 return F_MOVE_FAILED_DEST_IS_PARENT;
3369         }
3370         debug_print("moving \"%s\" to \"%s\"\n", phys_srcpath, phys_dstpath);
3371         if ((tmp = folder_item_move_recursive(src, dest, copy)) == NULL) {
3372                 return F_MOVE_FAILED;
3373         }
3374         
3375         g_free(src_identifier);
3376         g_free(dst_identifier);
3377         g_free(phys_srcpath);
3378         g_free(phys_dstpath);
3379
3380         *new_item = tmp;
3381
3382         return F_MOVE_OK;
3383 }
3384
3385 struct find_data
3386 {
3387         gboolean found;
3388 };      
3389 static void find_num(gpointer key, gpointer value, gpointer data)
3390 {
3391         struct find_data *fdata = (struct find_data *)data;
3392         if (GPOINTER_TO_INT(value) == 0)
3393                 fdata->found = TRUE;
3394 }
3395
3396 static gboolean some_msgs_have_zero_num(GHashTable *hashtable)
3397 {
3398         struct find_data fdata;
3399         
3400         fdata.found = FALSE;
3401         g_hash_table_foreach(hashtable, find_num, &fdata);
3402         
3403         return fdata.found;
3404 }
3405
3406 /**
3407  * Copy a list of message to a new folder and remove
3408  * source messages if wanted
3409  */
3410 static gint do_copy_msgs(FolderItem *dest, GSList *msglist, gboolean remove_source)
3411 {
3412         Folder *folder;
3413         GSList *l;
3414         gint num, lastnum = -1;
3415         gboolean folderscan = FALSE;
3416         GHashTable *relation;
3417         GSList *not_moved = NULL;
3418         gint total = 0, curmsg = 0;
3419         MsgInfo *msginfo = NULL;
3420
3421         cm_return_val_if_fail(dest != NULL, -1);
3422         cm_return_val_if_fail(msglist != NULL, -1);
3423
3424         folder = dest->folder;
3425
3426         cm_return_val_if_fail(folder->klass->copy_msg != NULL, -1);
3427         if (dest->no_select)
3428                 return -1;
3429
3430         msginfo = (MsgInfo *)msglist->data;
3431         
3432         if (!msginfo)
3433                 return -1;
3434         
3435         if (!MSG_IS_QUEUED(msginfo->flags) && 
3436             MSG_IS_DRAFT(msginfo->flags) && 
3437             folder_has_parent_of_type(dest, F_QUEUE)) {
3438                 GSList *cur = msglist;
3439                 gboolean queue_err = FALSE;
3440                 for (; cur; cur = cur->next) {
3441                         Compose *compose = NULL;
3442                         FolderItem *queue = dest;
3443                         int val = 0;
3444                         
3445                         msginfo = (MsgInfo *)cur->data;
3446                         compose = compose_reedit(msginfo, TRUE);
3447                         if (compose == NULL) {
3448                                 queue_err = TRUE;
3449                                 continue;
3450                         }
3451                         val = compose_queue(compose, NULL, &queue, NULL,
3452                                         FALSE);
3453                         if (val < 0) {
3454                                 queue_err = TRUE;
3455                         } else if (remove_source) {
3456                                 folder_item_remove_msg(msginfo->folder, msginfo->msgnum);
3457                         }
3458                         if (val == 0)
3459                                 compose_close(compose);
3460                 }
3461                 return queue_err ? -1:0;
3462         }
3463
3464         relation = g_hash_table_new(g_direct_hash, g_direct_equal);
3465
3466         for (l = msglist ; l != NULL ; l = g_slist_next(l)) {
3467                 MsgInfo * msginfo = (MsgInfo *) l->data;
3468
3469                 if (msginfo->planned_download != 0) {
3470                         int old_planned = msginfo->planned_download;
3471                         partial_unmark(msginfo);
3472                         /* little hack to reenable after */
3473                         msginfo->planned_download = old_planned;
3474                 }
3475         }
3476
3477         /* 
3478          * Copy messages to destination folder and 
3479          * store new message numbers in newmsgnums
3480          */
3481         if (folder->klass->copy_msgs != NULL) {
3482                 if (folder->klass->copy_msgs(folder, dest, msglist, relation) < 0) {
3483                         g_hash_table_destroy(relation);
3484                         return -1;
3485                 }
3486         } else {
3487                 MsgInfo * msginfo;
3488                 l = msglist;
3489
3490                 /* immediately stop if src and dest folders are identical */
3491                 if (l != NULL) {
3492                         msginfo = (MsgInfo *) l->data;
3493                         if (msginfo != NULL && msginfo->folder == dest) {
3494                                 g_hash_table_destroy(relation);
3495                                 return -1;
3496                         }
3497                 }
3498
3499                 for (; l != NULL ; l = g_slist_next(l)) {
3500                         msginfo = (MsgInfo *) l->data;
3501
3502                         num = folder->klass->copy_msg(folder, dest, msginfo);
3503                         if (num > 0)
3504                                 g_hash_table_insert(relation, msginfo, GINT_TO_POINTER(num));
3505                         else
3506                                 not_moved = g_slist_prepend(not_moved, msginfo);
3507                 }
3508         }
3509
3510         if (remove_source) {
3511                 MsgInfo *msginfo = (MsgInfo *) msglist->data;
3512                 FolderItem *item = msginfo->folder;
3513                 /*
3514                  * Remove source messages from their folders if
3515                  * copying was successfull and update folder
3516                  * message counts
3517                  */
3518                 if (not_moved == NULL && item->folder->klass->remove_msgs) {
3519                         item->folder->klass->remove_msgs(item->folder,
3520                                                                 msginfo->folder,
3521                                                                 msglist,
3522                                                                 relation);
3523                 }
3524                 for (l = msglist; l != NULL; l = g_slist_next(l)) {
3525                         gpointer old_key, data;
3526                         msginfo = (MsgInfo *) l->data;
3527                         item = msginfo->folder;
3528
3529                         if (g_hash_table_lookup_extended(relation, msginfo, &old_key, &data))
3530                                 num = GPOINTER_TO_INT(data);
3531                         else
3532                                 num = 0;
3533
3534                         if (g_slist_find(not_moved, msginfo))
3535                                 continue;
3536
3537                         if ((num >= 0) && (item->folder->klass->remove_msg != NULL)) {
3538                                 if (!item->folder->klass->remove_msgs)
3539                                         item->folder->klass->remove_msg(item->folder,
3540                                                                 msginfo->folder,
3541                                                                 msginfo->msgnum);
3542                                 if (!item->folder->account || item->folder->account->imap_use_trash) {
3543                                         remove_msginfo_from_cache(item, msginfo);
3544                                 }
3545                         }
3546                 }
3547         }
3548
3549         /* Read cache for dest folder */
3550         if (!dest->cache) folder_item_read_cache(dest);
3551
3552         /* 
3553          * Fetch new MsgInfos for new messages in dest folder,
3554          * add them to the msgcache and update folder message counts
3555          */
3556         if (some_msgs_have_zero_num(relation)) {
3557                 folder_item_scan_full(dest, FALSE);
3558                 folderscan = TRUE;
3559         }
3560
3561         statusbar_print_all(_("Updating cache for %s..."), dest->path ? dest->path : "(null)");
3562         total = g_slist_length(msglist);
3563         
3564         if (FOLDER_TYPE(dest->folder) == F_IMAP && total > 1) {
3565                 folder_item_scan_full(dest, FALSE);
3566                 folderscan = TRUE;
3567         }
3568         folder_item_set_batch(dest, TRUE);
3569         for (l = msglist; l != NULL; l = g_slist_next(l)) {
3570                 MsgInfo *msginfo = (MsgInfo *) l->data;
3571                 gpointer data, old_key;
3572
3573                 if (!msginfo)
3574                         continue;
3575
3576                 if (g_hash_table_lookup_extended(relation, msginfo, &old_key, &data))
3577                         num = GPOINTER_TO_INT(data);
3578                 else
3579                         num = 0;
3580
3581                 statusbar_progress_all(curmsg++,total, 100);
3582                 if (curmsg % 100 == 0)
3583                         GTK_EVENTS_FLUSH();
3584
3585                 if (num >= 0) {
3586                         MsgInfo *newmsginfo = NULL;
3587
3588                         if (!folderscan && num > 0) {
3589                                 newmsginfo = get_msginfo(dest, num);
3590                                 if (newmsginfo != NULL) {
3591                                         add_msginfo_to_cache(dest, newmsginfo, msginfo);
3592                                 }
3593                         }
3594                         if (newmsginfo == NULL) {
3595                                 if (!folderscan) {
3596                                         folder_item_scan_full(dest, FALSE);
3597                                         folderscan = TRUE;
3598                                 }
3599                                 if (msginfo->msgid != NULL) {
3600                                         newmsginfo = folder_item_get_msginfo_by_msgid(dest, msginfo->msgid);
3601                                         if (newmsginfo != NULL) {
3602                                                 copy_msginfo_flags(msginfo, newmsginfo);
3603                                                 num = newmsginfo->msgnum;
3604                                         }
3605                                 }
3606                         }
3607
3608                         if (msginfo->planned_download 
3609                             == POP3_PARTIAL_DLOAD_DELE) {
3610                                 partial_mark_for_delete(newmsginfo);
3611                         }
3612                         if (msginfo->planned_download 
3613                             == POP3_PARTIAL_DLOAD_DLOAD) {
3614                                 partial_mark_for_download(newmsginfo);
3615                         }
3616                         if (!MSG_IS_POSTFILTERED (msginfo->flags)) {
3617                                 procmsg_msginfo_set_flags (   msginfo, MSG_POSTFILTERED, 0);
3618                                 if (newmsginfo) {
3619                                         procmsg_msginfo_set_flags (newmsginfo, MSG_POSTFILTERED, 0);
3620                                         hooks_invoke (MAIL_POSTFILTERING_HOOKLIST, newmsginfo);
3621                                 }
3622                         }
3623                         procmsg_msginfo_free(newmsginfo);
3624
3625
3626                         if (num > lastnum)
3627                                 lastnum = num;
3628                 }
3629         }
3630         folder_item_set_batch(dest, FALSE);
3631         statusbar_progress_all(0,0,0);
3632         statusbar_pop_all();
3633
3634         g_hash_table_destroy(relation);
3635         if (not_moved != NULL) {
3636                 g_slist_free(not_moved);
3637                 return -1;
3638         } else
3639                 return lastnum;
3640 }
3641
3642 /**
3643  * Move a message to a new folder.
3644  *
3645  * \param dest Destination folder
3646  * \param msginfo The message
3647  */
3648 gint folder_item_move_msg(FolderItem *dest, MsgInfo *msginfo)
3649 {
3650         GSList list;
3651
3652         cm_return_val_if_fail(dest != NULL, -1);
3653         cm_return_val_if_fail(msginfo != NULL, -1);
3654
3655         list.data = msginfo;
3656         list.next = NULL;
3657
3658         return do_copy_msgs(dest, &list, TRUE);
3659 }
3660
3661 /**
3662  * Move a list of messages to a new folder.
3663  *
3664  * \param dest Destination folder
3665  * \param msglist List of messages
3666  */
3667 gint folder_item_move_msgs(FolderItem *dest, GSList *msglist)
3668 {
3669         gint result = -1;
3670         cm_return_val_if_fail(dest != NULL, -1);
3671         cm_return_val_if_fail(msglist != NULL, -1);
3672         inc_lock();
3673         result = do_copy_msgs(dest, msglist, TRUE);
3674         inc_unlock();
3675         return result;
3676 }
3677
3678 /**
3679  * Copy a message to a new folder.
3680  *
3681  * \param dest Destination folder
3682  * \param msginfo The message
3683  */
3684 gint folder_item_copy_msg(FolderItem *dest, MsgInfo *msginfo)
3685 {
3686         GSList list;
3687
3688         cm_return_val_if_fail(dest != NULL, -1);
3689         cm_return_val_if_fail(msginfo != NULL, -1);
3690     
3691         list.data = msginfo;
3692         list.next = NULL;
3693
3694         return do_copy_msgs(dest, &list, FALSE);
3695 }
3696
3697 /**
3698  * Copy a list of messages to a new folder.
3699  *
3700  * \param dest Destination folder
3701  * \param msglist List of messages
3702  */
3703 gint folder_item_copy_msgs(FolderItem *dest, GSList *msglist)
3704 {
3705         gint result;
3706         cm_return_val_if_fail(dest != NULL, -1);
3707         cm_return_val_if_fail(msglist != NULL, -1);
3708
3709         inc_lock();
3710         result = do_copy_msgs(dest, msglist, FALSE);
3711         inc_unlock();
3712         
3713         return result;
3714 }
3715
3716 gint folder_item_remove_msg(FolderItem *item, gint num)
3717 {
3718         Folder *folder;
3719         gint ret;
3720         MsgInfo *msginfo;
3721
3722         cm_return_val_if_fail(item != NULL, -1);
3723         folder = item->folder;
3724         cm_return_val_if_fail(folder->klass->remove_msg != NULL, -1);
3725         if (item->no_select)
3726                 return -1;
3727
3728         if (!item->cache) folder_item_read_cache(item);
3729
3730         msginfo = msgcache_get_msg(item->cache, num);
3731         if (msginfo && MSG_IS_LOCKED(msginfo->flags)) {
3732                 procmsg_msginfo_free(msginfo);
3733                 return -1;
3734         }
3735         ret = folder->klass->remove_msg(folder, item, num);
3736
3737         if (!item->folder->account || item->folder->account->imap_use_trash) {
3738                 if (msginfo != NULL) {
3739                         remove_msginfo_from_cache(item, msginfo);
3740                         procmsg_msginfo_free(msginfo);
3741                 }
3742         }
3743
3744         return ret;
3745 }
3746
3747 gint folder_item_remove_msgs(FolderItem *item, GSList *msglist)
3748 {
3749         Folder *folder;
3750         gint ret = 0;
3751         GSList *real_list = NULL, *cur = NULL;
3752
3753         cm_return_val_if_fail(item != NULL, -1);
3754         folder = item->folder;
3755         cm_return_val_if_fail(folder != NULL, -1);
3756         if (item->no_select)
3757                 return -1;
3758         inc_lock();
3759         if (!item->cache) folder_item_read_cache(item);
3760
3761         folder_item_update_freeze();
3762         
3763         /* filter out locked mails */
3764         for (cur = msglist; cur; cur = cur->next) {
3765                 MsgInfo *info = (MsgInfo *)cur->data;
3766                 if (!MSG_IS_LOCKED(info->flags))
3767                         real_list = g_slist_prepend(real_list, info);
3768         }
3769
3770         real_list = g_slist_reverse(real_list);
3771
3772         if (item->folder->klass->remove_msgs) {
3773                 ret = item->folder->klass->remove_msgs(item->folder,
3774                                                         item,
3775                                                         real_list,
3776                                                         NULL);
3777         }
3778         while (ret == 0 && real_list != NULL) {
3779                 MsgInfo *msginfo = (MsgInfo *)real_list->data;
3780                 if (msginfo && MSG_IS_LOCKED(msginfo->flags)) {
3781                         real_list = real_list->next;
3782                         continue;
3783                 }
3784                 if (!item->folder->klass->remove_msgs)
3785                         ret = folder_item_remove_msg(item, msginfo->msgnum);
3786                 if (ret != 0) break;
3787                 msgcache_remove_msg(item->cache, msginfo->msgnum);
3788                 real_list = real_list->next;
3789         }
3790         g_slist_free(real_list);
3791         folder_item_scan_full(item, FALSE);
3792         folder_item_update_thaw();
3793         inc_unlock();
3794         return ret;
3795 }
3796
3797 gint folder_item_expunge(FolderItem *item)
3798 {
3799         Folder *folder = item->folder;
3800         gint result = 0;
3801         if (folder == NULL)
3802                 return -1;
3803         if (folder->klass->expunge) {
3804                 GSList *msglist = folder_item_get_msg_list(item);
3805                 GSList *cur;
3806                 result = folder->klass->expunge(folder, item);
3807                 if (result == 0) {
3808                         for (cur = msglist; cur; cur = cur->next) {
3809                                 MsgInfo *msginfo = (MsgInfo *)cur->data;
3810                                 if (MSG_IS_DELETED(msginfo->flags)) {
3811                                         remove_msginfo_from_cache(item, msginfo);
3812                                 }
3813                         }
3814                 }
3815                 procmsg_msg_list_free(msglist);
3816         }
3817         return result;
3818 }
3819
3820 gint folder_item_remove_all_msg(FolderItem *item)
3821 {
3822         Folder *folder;
3823         gint result;
3824
3825         cm_return_val_if_fail(item != NULL, -1);
3826         if (item->no_select)
3827                 return -1;
3828
3829         folder = item->folder;
3830
3831         inc_lock();
3832         if (folder->klass->remove_all_msg != NULL) {
3833                 result = folder->klass->remove_all_msg(folder, item);
3834
3835                 if (result == 0) {
3836                         folder_item_free_cache(item, TRUE);
3837                         item->cache = msgcache_new();
3838                         item->cache_dirty = TRUE;
3839                         item->mark_dirty = TRUE;
3840                         item->tags_dirty = TRUE;
3841                 }
3842         } else {
3843                 MsgInfoList *msglist;
3844
3845                 msglist = folder_item_get_msg_list(item);
3846                 result = folder_item_remove_msgs(item, msglist);
3847                 procmsg_msg_list_free(msglist);
3848         }
3849
3850         if (result == 0) {
3851                 item->new_msgs = 0;
3852                 item->unread_msgs = 0;
3853                 item->unreadmarked_msgs = 0;
3854                 item->marked_msgs = 0;
3855                 item->total_msgs = 0;
3856                 item->replied_msgs = 0;
3857                 item->forwarded_msgs = 0;
3858                 item->locked_msgs = 0;
3859                 item->ignored_msgs = 0;
3860                 item->watched_msgs = 0;
3861                 folder_item_update(item, F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT);
3862         }
3863
3864         inc_unlock();
3865         return result;
3866 }
3867
3868 void folder_item_change_msg_flags(FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3869 {
3870         cm_return_if_fail(item != NULL);
3871         cm_return_if_fail(msginfo != NULL);
3872         
3873         item->mark_dirty = TRUE;
3874
3875         if (item->no_select)
3876                 return;
3877         
3878         if (item->folder->klass->change_flags != NULL && item->scanning != ITEM_SCANNING_WITH_FLAGS) {
3879                 item->folder->klass->change_flags(item->folder, item, msginfo, newflags);
3880         } else {
3881                 msginfo->flags.perm_flags = newflags;
3882         }
3883 }
3884
3885 void folder_item_commit_tags(FolderItem *item, MsgInfo *msginfo, GSList *tags_set, GSList *tags_unset)
3886 {
3887         Folder *folder = NULL;
3888
3889         if (!msginfo)
3890                 return;
3891         if (!item)
3892                 return;
3893         if (!tags_set && !tags_unset)
3894                 return;
3895
3896         folder = item->folder;
3897         if (!folder)
3898                 return;
3899         
3900         item->tags_dirty = TRUE;
3901
3902         if (folder->klass->commit_tags == NULL)
3903                 return;
3904         
3905         folder->klass->commit_tags(item, msginfo, tags_set, tags_unset);
3906 }
3907
3908 gboolean folder_item_is_msg_changed(FolderItem *item, MsgInfo *msginfo)
3909 {
3910         Folder *folder;
3911
3912         cm_return_val_if_fail(item != NULL, FALSE);
3913         if (item->no_select)
3914                 return FALSE;
3915
3916         folder = item->folder;
3917
3918         cm_return_val_if_fail(folder->klass->is_msg_changed != NULL, -1);
3919
3920         return folder->klass->is_msg_changed(folder, item, msginfo);
3921 }
3922
3923 void folder_item_discard_cache(FolderItem *item)
3924 {
3925         gchar *dir;
3926         gchar *cache;
3927
3928         if (!item)
3929                 return;
3930
3931         if (item->cache) {
3932                 msgcache_destroy(item->cache);
3933                 item->cache = NULL;
3934         }
3935         dir = folder_item_get_path(item);
3936         if (is_dir_exist(dir))
3937                 remove_all_numbered_files(dir);
3938         g_free(dir);
3939         
3940         cache = folder_item_get_cache_file(item);
3941         if (is_file_exist(cache))
3942                 claws_unlink(cache);
3943         g_free(cache);
3944         
3945 }
3946
3947 static gchar *folder_item_get_cache_file(FolderItem *item)
3948 {
3949         gchar *path;
3950         gchar *file;
3951         gchar *old_file;
3952
3953         cm_return_val_if_fail(item != NULL, NULL);
3954         cm_return_val_if_fail(item->path != NULL, NULL);
3955
3956         path = folder_item_get_path(item);
3957         cm_return_val_if_fail(path != NULL, NULL);
3958         if (!is_dir_exist(path))
3959                 make_dir_hier(path);
3960         file = g_strconcat(path, G_DIR_SEPARATOR_S, CACHE_FILE, NULL);
3961         old_file = g_strconcat(path, G_DIR_SEPARATOR_S, OLD_CACHE_FILE, NULL);
3962
3963         if (!is_file_exist(file) && is_file_exist(old_file))
3964                 move_file(old_file, file, FALSE);
3965         g_free(old_file);
3966         g_free(path);
3967
3968         return file;
3969 }
3970
3971 static gchar *folder_item_get_mark_file(FolderItem *item)
3972 {
3973         gchar *path;
3974         gchar *file;
3975         gchar *old_file;
3976
3977         cm_return_val_if_fail(item != NULL, NULL);
3978         cm_return_val_if_fail(item->path != NULL, NULL);
3979
3980         path = folder_item_get_path(item);
3981         cm_return_val_if_fail(path != NULL, NULL);
3982         if (!is_dir_exist(path))
3983                 make_dir_hier(path);
3984         file = g_strconcat(path, G_DIR_SEPARATOR_S, MARK_FILE, NULL);
3985         old_file = g_strconcat(path, G_DIR_SEPARATOR_S, OLD_MARK_FILE, NULL);
3986
3987         if (!is_file_exist(file) && is_file_exist(old_file))
3988                 move_file(old_file, file, FALSE);
3989         g_free(old_file);
3990         g_free(path);
3991
3992         return file;
3993 }
3994
3995 static gchar *folder_item_get_tags_file(FolderItem *item)
3996 {
3997         gchar *path;
3998         gchar *identifier;
3999         gchar *file;
4000
4001         /* we save tags files in rc_dir, because tagsrc is there too,
4002          * and storing tags directly in the mailboxes would give strange
4003          * result when using another Claws mailbox from another install
4004          * with different tags. */
4005
4006         cm_return_val_if_fail(item != NULL, NULL);
4007
4008         identifier = folder_item_get_identifier(item);
4009         cm_return_val_if_fail(identifier != NULL, NULL);
4010
4011 #ifdef G_OS_WIN32
4012         while (strchr(identifier, '/'))
4013                 *strchr(identifier, '/') = '\\';
4014 #endif
4015
4016         path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
4017                            "tagsdb", G_DIR_SEPARATOR_S,
4018                            identifier, NULL);
4019         
4020         g_free(identifier);
4021                            
4022         if (!is_dir_exist(path))
4023                 make_dir_hier(path);
4024
4025         file = g_strconcat(path, G_DIR_SEPARATOR_S, TAGS_FILE, NULL);
4026         
4027         g_free(path);
4028
4029         return file;
4030 }
4031
4032 static gpointer xml_to_folder_item(gpointer nodedata, gpointer data)
4033 {
4034         XMLNode *xmlnode = (XMLNode *) nodedata;
4035         Folder *folder = (Folder *) data;
4036         FolderItem *item;
4037
4038         cm_return_val_if_fail(xmlnode != NULL, NULL);
4039         cm_return_val_if_fail(folder != NULL, NULL);
4040
4041         if (strcmp2(xmlnode->tag->tag, "folderitem") != 0) {
4042                 g_warning("tag name != \"folderitem\"\n");
4043                 return NULL;
4044         }
4045
4046         item = folder_item_new(folder, "", "");
4047         if (folder->klass->item_set_xml != NULL)
4048                 folder->klass->item_set_xml(folder, item, xmlnode->tag);
4049         else
4050                 folder_item_set_xml(folder, item, xmlnode->tag);
4051
4052         item->folder = folder;
4053
4054         switch (item->stype) {
4055         case F_INBOX:  folder->inbox  = item; break;
4056         case F_OUTBOX: folder->outbox = item; break;
4057         case F_DRAFT:  folder->draft  = item; break;
4058         case F_QUEUE:  folder->queue  = item; break;
4059         case F_TRASH:  folder->trash  = item; break;
4060         default:       break;
4061         }
4062         folder_item_prefs_read_config(item);
4063
4064         return item;
4065 }
4066
4067 static gboolean folder_item_set_node(GNode *node, gpointer data)
4068 {
4069         FolderItem *item = (FolderItem *) node->data;
4070         item->node = node;
4071
4072         return FALSE;
4073 }
4074
4075 static Folder *folder_get_from_xml(GNode *node)
4076 {
4077         Folder *folder;
4078         XMLNode *xmlnode;
4079         GList *list;
4080         FolderClass *klass = NULL;
4081         GNode *cur;
4082
4083         cm_return_val_if_fail(node->data != NULL, NULL);
4084
4085         xmlnode = node->data;
4086         if (strcmp2(xmlnode->tag->tag, "folder") != 0) {
4087                 g_warning("tag name != \"folder\"\n");
4088                 return NULL;
4089         }
4090         list = xmlnode->tag->attr;
4091         for (; list != NULL; list = list->next) {
4092                 XMLAttr *attr = list->data;
4093
4094                 if (!attr || !attr->name || !attr->value) continue;
4095                 if (!strcmp(attr->name, "type"))
4096                         klass = folder_get_class_from_string(attr->value);
4097         }
4098         if (klass == NULL)
4099                 return NULL;
4100
4101         folder = folder_new(klass, "", "");
4102         cm_return_val_if_fail(folder != NULL, NULL);
4103
4104         if (klass->set_xml)
4105                 klass->set_xml(folder, xmlnode->tag);
4106         else
4107                 folder_set_xml(folder, xmlnode->tag);
4108
4109         cur = node->children;
4110         while (cur != NULL) {
4111                 GNode *itemnode;
4112
4113                 itemnode = g_node_map(cur, xml_to_folder_item, (gpointer) folder);
4114                 g_node_append(folder->node, itemnode);
4115                 cur = cur->next;
4116         }
4117         g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, folder_item_set_node, NULL);
4118         
4119         return folder;
4120 }
4121
4122 static gchar *folder_get_list_path(void)
4123 {
4124         static gchar *filename = NULL;
4125
4126         if (!filename)
4127                 filename =  g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
4128                                         FOLDER_LIST, NULL);
4129
4130         return filename;
4131 }
4132
4133 static gpointer folder_item_to_xml(gpointer nodedata, gpointer data)
4134 {
4135         FolderItem *item = (FolderItem *) nodedata;
4136         XMLTag *tag;
4137
4138         cm_return_val_if_fail(item != NULL, NULL);
4139
4140         if (item->folder->klass->item_get_xml != NULL)
4141                 tag = item->folder->klass->item_get_xml(item->folder, item);
4142         else
4143                 tag = folder_item_get_xml(item->folder, item);
4144
4145         return xml_node_new(tag, NULL);
4146 }
4147
4148 static GNode *folder_get_xml_node(Folder *folder)
4149 {
4150         GNode *node;
4151         XMLNode *xmlnode;
4152         XMLTag *tag;
4153
4154         cm_return_val_if_fail(folder != NULL, NULL);
4155
4156         if (folder->klass->get_xml != NULL)
4157                 tag = folder->klass->get_xml(folder);
4158         else
4159                 tag = folder_get_xml(folder);
4160
4161         xml_tag_add_attr(tag, xml_attr_new("type", folder->klass->idstr));
4162
4163         xmlnode = xml_node_new(tag, NULL);
4164
4165         node = g_node_new(xmlnode);
4166         
4167         cm_return_val_if_fail (folder->node != NULL, NULL);
4168         
4169         if (folder->node->children) {
4170                 GNode *cur;
4171
4172                 cur = folder->node->children;
4173                 while (cur) {
4174                         GNode *xmlnode;
4175
4176                         xmlnode = g_node_map(cur, folder_item_to_xml, (gpointer) folder);
4177                         g_node_append(node, xmlnode);
4178                         cur = cur->next;
4179                 }
4180         }
4181
4182         return node;
4183 }
4184
4185 static void folder_update_op_count_rec(GNode *node)
4186 {
4187         FolderItem *fitem = FOLDER_ITEM(node->data);
4188
4189         if (g_node_depth(node) > 0) {
4190                 if (fitem->op_count > 0) {
4191                         fitem->op_count = 0;
4192                         folder_item_update(fitem, F_ITEM_UPDATE_MSGCNT);
4193                 }
4194                 if (node->children) {
4195                         GNode *child;
4196
4197                         child = node->children;
4198                         while (child) {
4199                                 GNode *cur;
4200
4201                                 cur = child;
4202                                 child = cur->next;
4203                                 folder_update_op_count_rec(cur);
4204                         }
4205                 }
4206         }
4207 }
4208
4209 void folder_update_op_count(void) 
4210 {
4211         GList *cur;
4212         Folder *folder;
4213
4214         for (cur = folder_list; cur != NULL; cur = cur->next) {
4215                 folder = cur->data;
4216                 folder_update_op_count_rec(folder->node);
4217         }
4218 }
4219
4220 typedef struct _type_str {
4221         gchar * str;
4222         gint type;
4223 } type_str;
4224
4225
4226 /*
4227 static gchar * folder_item_get_tree_identifier(FolderItem * item)
4228 {
4229         if (item->parent != NULL) {
4230                 gchar * path;
4231                 gchar * id;
4232
4233                 path = folder_item_get_tree_identifier(item->parent);
4234                 if (path == NULL)
4235                         return NULL;
4236
4237                 id = g_strconcat(path, "/", item->name, NULL);
4238                 g_free(path);
4239
4240                 return id;
4241         }
4242         else {
4243                 return g_strconcat("/", item->name, NULL);
4244         }
4245 }
4246 */
4247
4248 /* CLAWS: temporary local folder for filtering */
4249 #define TEMP_FOLDER "TEMP_FOLDER"
4250 #define PROCESSING_FOLDER_ITEM "processing"     
4251
4252 static FolderItem *processing_folder_item;
4253
4254 static void folder_create_processing_folder(void)
4255 {
4256         Folder *processing_folder;
4257         gchar      *tmpname;
4258
4259         if ((processing_folder = folder_find_from_name(TEMP_FOLDER, mh_get_class())) == NULL) {
4260                 gchar *tmppath;
4261
4262                 tmppath =
4263                     g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
4264                                 "tempfolder", NULL);
4265                 processing_folder =
4266                     folder_new(mh_get_class(), TEMP_FOLDER, tmppath);
4267                 g_free(tmppath);
4268         }
4269         g_assert(processing_folder != NULL);
4270
4271         debug_print("tmpparentroot %s\n", LOCAL_FOLDER(processing_folder)->rootpath);
4272         /* FIXME: [W32] The code below does not correctly merge
4273            relative filenames; there should be a function to handle
4274            this.  */
4275         if (!is_relative_filename(LOCAL_FOLDER(processing_folder)->rootpath))
4276                 tmpname = g_strconcat(LOCAL_FOLDER(processing_folder)->rootpath,
4277                                       G_DIR_SEPARATOR_S, PROCESSING_FOLDER_ITEM,
4278                                       NULL);
4279         else
4280                 tmpname = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
4281                                       LOCAL_FOLDER(processing_folder)->rootpath,
4282                                       G_DIR_SEPARATOR_S, PROCESSING_FOLDER_ITEM,
4283                                       NULL);
4284
4285         if (!is_dir_exist(tmpname)) {
4286                 debug_print("*TMP* creating %s\n", tmpname);
4287                 processing_folder_item = processing_folder->klass->create_folder(processing_folder,
4288                                                                                  processing_folder->node->data,
4289                                                                                  PROCESSING_FOLDER_ITEM);
4290         } else {
4291                 debug_print("*TMP* already created\n");
4292                 processing_folder_item = folder_item_new(processing_folder, PROCESSING_FOLDER_ITEM, PROCESSING_FOLDER_ITEM);
4293                 g_assert(processing_folder_item);
4294                 folder_item_append(processing_folder->node->data, processing_folder_item);
4295         }
4296         g_assert(processing_folder_item != NULL);
4297         g_free(tmpname);
4298 }
4299
4300 FolderItem *folder_get_default_processing(void)
4301 {
4302         if (!processing_folder_item) {
4303                 folder_create_processing_folder();
4304         }
4305         return processing_folder_item;
4306 }
4307
4308 /* folder_persist_prefs_new() - return hash table with persistent
4309  * settings (and folder name as key). 
4310  * (note that in claws other options are in the folder_item_prefs_RC
4311  * file, so those don't need to be included in PersistPref yet) 
4312  */
4313 static GHashTable *folder_persist_prefs_new(Folder *folder)
4314 {
4315         GHashTable *pptable;
4316
4317         cm_return_val_if_fail(folder, NULL);
4318         pptable = g_hash_table_new(g_str_hash, g_str_equal);
4319         folder_get_persist_prefs_recursive(folder->node, pptable);
4320         return pptable;
4321 }
4322
4323 static void folder_persist_prefs_free(GHashTable *pptable)
4324 {
4325         cm_return_if_fail(pptable);
4326         g_hash_table_foreach_remove(pptable, persist_prefs_free, NULL);
4327         g_hash_table_destroy(pptable);
4328 }
4329
4330 static const PersistPrefs *folder_get_persist_prefs(GHashTable *pptable, const char *name)
4331 {
4332         if (pptable == NULL || name == NULL) return NULL;
4333         return g_hash_table_lookup(pptable, name);
4334 }
4335
4336 static void folder_item_restore_persist_prefs(FolderItem *item, GHashTable *pptable)
4337 {
4338         const PersistPrefs *pp;
4339         gchar *id = folder_item_get_identifier(item);
4340
4341         pp = folder_get_persist_prefs(pptable, id); 
4342         g_free(id);
4343
4344         if (!pp) return;
4345
4346         /* CLAWS: since not all folder properties have been migrated to 
4347          * folderlist.xml, we need to call the old stuff first before
4348          * setting things that apply both to Main and Claws. */
4349         folder_item_prefs_read_config(item); 
4350
4351         item->collapsed = pp->collapsed;
4352         item->thread_collapsed = pp->thread_collapsed;
4353         item->threaded  = pp->threaded;
4354         item->ret_rcpt  = pp->ret_rcpt;
4355         item->hide_read_msgs = pp->hide_read_msgs;
4356         item->hide_del_msgs = pp->hide_del_msgs;
4357         item->hide_read_threads = pp->hide_read_threads;
4358         item->sort_key  = pp->sort_key;
4359         item->sort_type = pp->sort_type;
4360 }
4361
4362 static void folder_get_persist_prefs_recursive(GNode *node, GHashTable *pptable)
4363 {
4364         FolderItem *item = FOLDER_ITEM(node->data);
4365         PersistPrefs *pp;
4366         GNode *child, *cur;
4367         gchar *id;
4368
4369         cm_return_if_fail(node != NULL);
4370         cm_return_if_fail(item != NULL);
4371
4372         /* NOTE: item->path == NULL means top level folder; not interesting
4373          * to store preferences of that one.  */
4374         if (item->path) {
4375                 id = folder_item_get_identifier(item);
4376                 pp = g_new0(PersistPrefs, 1);
4377                 cm_return_if_fail(pp != NULL);
4378                 pp->collapsed = item->collapsed;
4379                 pp->thread_collapsed = item->thread_collapsed;
4380                 pp->threaded  = item->threaded;
4381                 pp->ret_rcpt  = item->ret_rcpt; 
4382                 pp->hide_read_msgs = item->hide_read_msgs;
4383                 pp->hide_del_msgs = item->hide_del_msgs;
4384                 pp->hide_read_threads = item->hide_read_threads;
4385                 pp->sort_key  = item->sort_key;
4386                 pp->sort_type = item->sort_type;
4387                 g_hash_table_insert(pptable, id, pp);
4388         }
4389
4390         if (node->children) {
4391                 child = node->children;
4392                 while (child) {
4393                         cur = child;
4394                         child = cur->next;
4395                         folder_get_persist_prefs_recursive(cur, pptable);
4396                 }
4397         }       
4398 }
4399
4400 static gboolean persist_prefs_free(gpointer key, gpointer val, gpointer data)
4401 {
4402         g_free(key);
4403         g_free(val);
4404         return TRUE;    
4405 }
4406
4407 void folder_item_apply_processing(FolderItem *item)
4408 {
4409         GSList *processing_list;
4410         GSList *mlist, *cur;
4411         guint total = 0, curmsg = 0;
4412         gint last_apply_per_account;
4413
4414         cm_return_if_fail(item != NULL);
4415
4416         if (item->no_select)
4417                return;
4418
4419         processing_list = item->prefs->processing;
4420
4421         if (!pre_global_processing && !processing_list
4422         &&  !post_global_processing)
4423                 return;
4424
4425         debug_print("processing %s\n", item->name);
4426         folder_item_update_freeze();
4427
4428         inc_lock();
4429
4430         mlist = folder_item_get_msg_list(item);
4431         total = g_slist_length(mlist);
4432         statusbar_print_all(_("Processing messages..."));
4433
4434         last_apply_per_account = prefs_common.apply_per_account_filtering_rules;
4435         prefs_common.apply_per_account_filtering_rules = FILTERING_ACCOUNT_RULES_SKIP;
4436
4437         folder_item_set_batch(item, TRUE);
4438         for (cur = mlist ; cur != NULL ; cur = cur->next) {
4439                 MsgInfo * msginfo;
4440
4441                 msginfo = (MsgInfo *) cur->data;
4442                 
4443                 /* reset parameters that can be modified by processing */
4444                 msginfo->hidden = 0;
4445                 msginfo->score = 0;
4446
4447                 statusbar_progress_all(curmsg++,total, 10);
4448
4449                 /* apply pre global rules */
4450                 filter_message_by_msginfo(pre_global_processing, msginfo, NULL,
4451                                 FILTERING_PRE_PROCESSING, NULL);
4452                 
4453                 /* apply rules of the folder */
4454                 filter_message_by_msginfo(processing_list, msginfo, NULL,
4455                                 FILTERING_FOLDER_PROCESSING, item->name);
4456
4457                 /* apply post global rules */
4458                 filter_message_by_msginfo(post_global_processing, msginfo, NULL,
4459                                 FILTERING_POST_PROCESSING, NULL);
4460                 if (curmsg % 1000 == 0)
4461                         GTK_EVENTS_FLUSH();
4462         }
4463         folder_item_set_batch(item, FALSE);
4464
4465         prefs_common.apply_per_account_filtering_rules = last_apply_per_account;
4466
4467         if (pre_global_processing || processing_list
4468             || post_global_processing)
4469                 filtering_move_and_copy_msgs(mlist);
4470         for (cur = mlist ; cur != NULL ; cur = cur->next) {
4471                 MsgInfo * msginfo = (MsgInfo *)cur->data;
4472                 procmsg_msginfo_free(msginfo);
4473         }
4474         g_slist_free(mlist);
4475         
4476         statusbar_progress_all(0,0,0);
4477         statusbar_pop_all();
4478
4479         inc_unlock();
4480
4481         folder_item_update_thaw();
4482 }
4483
4484 /*
4485  *  functions for handling FolderItem content changes
4486  */
4487 static gint folder_item_update_freeze_cnt = 0;
4488
4489 static void folder_item_update_with_msg(FolderItem *item, FolderItemUpdateFlags update_flags, MsgInfo *msg)
4490 {
4491         if (folder_item_update_freeze_cnt == 0 /* || (msg != NULL && item->opened) */) {
4492                 FolderItemUpdateData source;
4493         
4494                 source.item = item;
4495                 source.update_flags = update_flags;
4496                 source.msg = msg;
4497                 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &source);
4498         } else {
4499                 item->update_flags |= update_flags & ~(F_ITEM_UPDATE_ADDMSG | F_ITEM_UPDATE_REMOVEMSG);
4500         }
4501 }
4502
4503 /**
4504  * Notify the folder system about changes to a folder. If the
4505  * update system is not frozen the FOLDER_ITEM_UPDATE_HOOKLIST will
4506  * be invoked, otherwise the changes will be remebered until
4507  * the folder system is thawed.
4508  *
4509  * \param item The FolderItem that was changed
4510  * \param update_flags Type of changed that was made
4511  */
4512 void folder_item_update(FolderItem *item, FolderItemUpdateFlags update_flags)
4513 {
4514         folder_item_update_with_msg(item, update_flags, NULL);
4515 }
4516
4517 void folder_item_update_recursive(FolderItem *item, FolderItemUpdateFlags update_flags)
4518 {
4519         GNode *node = item->folder->node;       
4520
4521         node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
4522         node = node->children;
4523
4524         folder_item_update(item, update_flags);
4525         while (node != NULL) {
4526                 if (node && node->data) {
4527                         FolderItem *next_item = (FolderItem*) node->data;
4528
4529                         folder_item_update(next_item, update_flags);
4530                 }
4531                 node = node->next;
4532         }
4533 }
4534
4535 void folder_item_update_freeze(void)
4536 {
4537         folder_item_update_freeze_cnt++;
4538 }
4539
4540 static void folder_item_update_func(FolderItem *item, gpointer data)
4541 {
4542         FolderItemUpdateData source;
4543     
4544         if (item->update_flags) {
4545                 source.item = item;
4546                 source.update_flags = item->update_flags;
4547                 source.msg = NULL;
4548                 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &source);                             
4549                 item->update_flags = 0;
4550         }
4551 }
4552
4553 void folder_item_update_thaw(void)
4554 {
4555         if (folder_item_update_freeze_cnt > 0)
4556                 folder_item_update_freeze_cnt--;
4557         if (folder_item_update_freeze_cnt == 0) {
4558                 /* Update all folders */
4559                 folder_func_to_all_folders(folder_item_update_func, NULL);
4560         }
4561 }
4562
4563 void folder_item_synchronise(FolderItem *item)
4564 {
4565         if (!item)
4566                 return;
4567         if (item->prefs->offlinesync && item->folder->klass->synchronise) {
4568                 statuswindow_print_all(_("Synchronising %s for offline use...\n"), item->path ? item->path : "(null)");
4569                 item->folder->klass->synchronise(item, 
4570                         item->prefs->offlinesync_days);
4571                 if (item->prefs->offlinesync_days > 0 &&
4572                     item->prefs->remove_old_bodies)
4573                         folder_item_clean_local_files(item, item->prefs->offlinesync_days);
4574                 statuswindow_pop_all();
4575         }
4576 }
4577
4578 static void folder_item_synchronise_func(FolderItem *item, gpointer data)
4579 {
4580         Folder *folder = (Folder *)data;
4581         if (folder == NULL || item->folder == folder) {
4582                 folder_item_synchronise(item);
4583         }
4584 }
4585
4586 void folder_synchronise(Folder *folder)
4587 {
4588         folder_func_to_all_folders(folder_item_synchronise_func, folder);
4589 }
4590
4591 typedef struct _WantSyncData {
4592         Folder *folder;
4593         gboolean want_sync;
4594 } WantSyncData;
4595
4596 static void folder_item_want_synchronise_func(FolderItem *item, gpointer data)
4597 {
4598         WantSyncData *want_sync_data = (WantSyncData *)data;
4599         
4600         if (want_sync_data->folder == NULL || item->folder == want_sync_data->folder) {
4601                 if (item->prefs->offlinesync && item->folder->klass->synchronise)
4602                         want_sync_data->want_sync |= TRUE;
4603         }
4604 }
4605
4606 gboolean folder_want_synchronise(Folder *folder)
4607 {
4608         WantSyncData *want_sync_data = g_new0(WantSyncData, 1);
4609         gboolean result;
4610         want_sync_data->folder = folder;
4611         want_sync_data->want_sync = FALSE;
4612         
4613         folder_func_to_all_folders(folder_item_want_synchronise_func, want_sync_data);
4614         result = want_sync_data->want_sync;
4615         g_free(want_sync_data);
4616         if (result > 0)
4617                 debug_print("Folder %s wants sync\n", folder->name);
4618         return result;
4619 }
4620
4621 void folder_item_set_batch (FolderItem *item, gboolean batch)
4622 {
4623         if (!item || !item->folder)
4624                 return;
4625         if (item->folder->klass->set_batch) {
4626                 item->folder->klass->set_batch(item->folder, item, batch);
4627         }
4628 }
4629
4630 gboolean folder_has_parent_of_type(FolderItem *item, 
4631                                           SpecialFolderItemType type) 
4632 {
4633         FolderItem *cur = item;
4634
4635         if (!item)
4636                 return FALSE;
4637         /* if we already know it, make it short */
4638         if (item->parent_stype != -1) {
4639                 return (item->parent_stype == type);
4640         }
4641         
4642         /* if we don't, find the type from the first possible parent,
4643          * and set our parent type to be faster next time */
4644         while (cur) {
4645                 if (cur->stype == type || cur->parent_stype == type) {
4646                         item->parent_stype = type;
4647                         return TRUE;
4648                 }
4649                 cur = folder_item_parent(cur);
4650         }
4651         
4652         /* if we didn't match what was asked, we didn't return. If our
4653          * parent type is unknown, we may as well find it now to be faster
4654          * later. */
4655         if (item->parent_stype == -1) {
4656                 cur = item;
4657                 while (cur) {
4658                         /* here's an exception: Inbox subfolders are normal. */
4659                         if (item->parent_stype == -1 && cur->stype == F_INBOX 
4660                         && item != cur) {
4661                                 item->parent_stype = F_NORMAL;
4662                                 break;
4663                         }
4664                         /* ah, we know this parent's parent's type, we may as 
4665                          * well copy it instead of going up the full way */
4666                         if (cur->parent_stype != -1) {
4667                                 item->parent_stype = cur->parent_stype;
4668                                 break;
4669                         }
4670                         /* we found a parent that has a special type. That's 
4671                          * our parent type. */
4672                         if (cur->stype != F_NORMAL) {
4673                                 cur->parent_stype = cur->stype;
4674                                 item->parent_stype = cur->stype;
4675                                 break;
4676                         }
4677                         /* if we didn't find anything, go up once more */
4678                         cur = folder_item_parent(cur);
4679                 }
4680                 /* as we still didn't find anything, our parents must all be 
4681                  * normal. */
4682                 if (item->parent_stype == -1) {
4683                         item->parent_stype = F_NORMAL;
4684                 }
4685         }
4686         return FALSE;
4687 }
4688
4689 gboolean folder_subscribe (const gchar *uri)
4690 {
4691         GList *cur;
4692         for (cur = folder_get_list(); cur != NULL; cur = g_list_next(cur)) {
4693                 Folder *folder = (Folder *) cur->data;
4694
4695                 if (folder->klass->subscribe
4696                 &&  folder->klass->subscribe(folder, uri)) {
4697                         return TRUE;
4698                 }
4699         }
4700         return FALSE;
4701
4702 }
4703
4704 gboolean folder_get_sort_type           (Folder         *folder,
4705                                          FolderSortKey  *sort_key,
4706                                          FolderSortType *sort_type)
4707 {
4708         if (!folder || !sort_key || !sort_type)
4709                 return FALSE;
4710         if (folder->klass->get_sort_type == NULL)
4711                 return FALSE;
4712         folder->klass->get_sort_type(folder, sort_key, sort_type); 
4713         return TRUE;
4714 }