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