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