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