enable ability to hide read messages
[claws.git] / src / folder.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2001 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
30 #include "intl.h"
31 #include "folder.h"
32 #include "folderview.h"
33 #include "session.h"
34 #include "imap.h"
35 #include "news.h"
36 #include "mh.h"
37 #include "mbox_folder.h"
38 #include "utils.h"
39 #include "xml.h"
40 #include "codeconv.h"
41 #include "prefs.h"
42 #include "account.h"
43 #include "prefs_account.h"
44 #include "mbox_folder.h"
45
46 #include <sys/stat.h>
47 #include <sys/types.h>
48
49 static GList *folder_list = NULL;
50
51 static void folder_init         (Folder         *folder,
52                                  FolderType      type,
53                                  const gchar    *name);
54
55 static void local_folder_destroy        (LocalFolder    *lfolder);
56 static void remote_folder_destroy       (RemoteFolder   *rfolder);
57 static void mh_folder_destroy           (MHFolder       *folder);
58 static void mbox_folder_destroy         (MboxFolder     *folder);
59 static void imap_folder_destroy         (IMAPFolder     *folder);
60 static void news_folder_destroy         (NewsFolder     *folder);
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 Folder *folder_new(FolderType type, const gchar *name, const gchar *path)
71 {
72         Folder *folder = NULL;
73
74         name = name ? name : path;
75         switch (type) {
76         case F_MBOX:
77                 folder = mbox_folder_new(name, path);
78                 break;
79         case F_MH:
80                 folder = mh_folder_new(name, path);
81                 break;
82         case F_IMAP:
83                 folder = imap_folder_new(name, path);
84                 break;
85         case F_NEWS:
86                 folder = news_folder_new(name, path);
87                 break;
88         default:
89                 return NULL;
90         }
91
92         return folder;
93 }
94
95 Folder *mh_folder_new(const gchar *name, const gchar *path)
96 {
97         Folder *folder;
98
99         folder = (Folder *)g_new0(MHFolder, 1);
100         folder_init(folder, F_MH, name);
101         LOCAL_FOLDER(folder)->rootpath = g_strdup(path);
102
103         return folder;
104 }
105
106 Folder *mbox_folder_new(const gchar *name, const gchar *path)
107 {
108         /* implementing */
109         Folder *folder;
110
111         folder = (Folder *)g_new0(MboxFolder, 1);
112         folder_init(folder, F_MBOX, name);
113         LOCAL_FOLDER(folder)->rootpath = g_strdup(path);
114
115         return folder;
116 }
117
118 Folder *maildir_folder_new(const gchar *name, const gchar *path)
119 {
120         /* not yet implemented */
121         return NULL;
122 }
123
124 Folder *imap_folder_new(const gchar *name, const gchar *path)
125 {
126         Folder *folder;
127
128         folder = (Folder *)g_new0(IMAPFolder, 1);
129         folder_init(folder, F_IMAP, name);
130
131         return folder;
132 }
133
134 Folder *news_folder_new(const gchar *name, const gchar *path)
135 {
136         Folder *folder;
137
138         folder = (Folder *)g_new0(NewsFolder, 1);
139         folder_init(folder, F_NEWS, name);
140
141         return folder;
142 }
143
144 FolderItem *folder_item_new(const gchar *name, const gchar *path)
145 {
146         FolderItem *item;
147
148         item = g_new0(FolderItem, 1);
149
150         item->stype = F_NORMAL;
151         item->name = g_strdup(name);
152         item->path = g_strdup(path);
153         item->account = NULL;
154         item->mtime = 0;
155         item->new = 0;
156         item->unread = 0;
157         item->total = 0;
158         item->last_num = -1;
159         item->no_sub = FALSE;
160         item->no_select = FALSE;
161         item->collapsed = FALSE;
162         item->threaded  = TRUE;
163         item->ret_rcpt  = FALSE;
164         item->parent = NULL;
165         item->folder = NULL;
166         item->data = NULL;
167
168         item->prefs = prefs_folder_item_new();
169
170         return item;
171 }
172
173 void folder_item_append(FolderItem *parent, FolderItem *item)
174 {
175         GNode *node;
176
177         g_return_if_fail(parent != NULL);
178         g_return_if_fail(parent->folder != NULL);
179         g_return_if_fail(item != NULL);
180
181         node = parent->folder->node;
182         node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, parent);
183         g_return_if_fail(node != NULL);
184
185         item->parent = parent;
186         item->folder = parent->folder;
187         g_node_append_data(node, item);
188 }
189
190 void folder_item_remove(FolderItem *item)
191 {
192         GNode *node;
193
194         g_return_if_fail(item != NULL);
195         g_return_if_fail(item->folder != NULL);
196
197         node = item->folder->node;
198         node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
199         g_return_if_fail(node != NULL);
200
201         /* TODO: free all FolderItem's first */
202         if (item->folder->node == node)
203                 item->folder->node = NULL;
204         g_node_destroy(node);
205 }
206
207 void folder_item_destroy(FolderItem *item)
208 {
209         g_return_if_fail(item != NULL);
210
211         g_free(item->name);
212         g_free(item->path);
213         g_free(item);
214 }
215
216 void folder_set_ui_func(Folder *folder, FolderUIFunc func, gpointer data)
217 {
218         g_return_if_fail(folder != NULL);
219
220         folder->ui_func = func;
221         folder->ui_func_data = data;
222 }
223
224 void folder_set_name(Folder *folder, const gchar *name)
225 {
226         g_return_if_fail(folder != NULL);
227
228         g_free(folder->name);
229         folder->name = name ? g_strdup(name) : NULL;
230         if (folder->node && folder->node->data) {
231                 FolderItem *item = (FolderItem *)folder->node->data;
232
233                 g_free(item->name);
234                 item->name = name ? g_strdup(name) : NULL;
235         }
236 }
237
238 void folder_destroy(Folder *folder)
239 {
240         g_return_if_fail(folder != NULL);
241
242         folder_list = g_list_remove(folder_list, folder);
243
244         switch (folder->type) {
245         case F_MH:
246                 mh_folder_destroy(MH_FOLDER(folder));
247                 break;
248         case F_MBOX:
249                 mbox_folder_destroy(MBOX_FOLDER(folder));
250                 break;
251         case F_IMAP:
252                 imap_folder_destroy(IMAP_FOLDER(folder));
253                 break;
254         case F_NEWS:
255                 news_folder_destroy(NEWS_FOLDER(folder));
256                 break;
257         default:
258                 break;
259         }
260
261         folder_tree_destroy(folder);
262         g_free(folder->name);
263         g_free(folder);
264 }
265
266 void folder_tree_destroy(Folder *folder)
267 {
268         /* TODO: destroy all FolderItem before */
269         g_node_destroy(folder->node);
270
271         folder->inbox = NULL;
272         folder->outbox = NULL;
273         folder->draft = NULL;
274         folder->queue = NULL;
275         folder->trash = NULL;
276         folder->node = NULL;
277 }
278
279 void folder_add(Folder *folder)
280 {
281         Folder *cur_folder;
282         GList *cur;
283         gint i;
284
285         g_return_if_fail(folder != NULL);
286
287         for (i = 0, cur = folder_list; cur != NULL; cur = cur->next, i++) {
288                 cur_folder = FOLDER(cur->data);
289                 if (folder->type == F_MH) {
290                         if (cur_folder->type != F_MH) break;
291                 } else if (folder->type == F_MBOX) {
292                         if (cur_folder->type != F_MH &&
293                             cur_folder->type != F_MBOX) break;
294                 } else if (folder->type == F_IMAP) {
295                         if (cur_folder->type != F_MH &&
296                             cur_folder->type != F_MBOX &&
297                             cur_folder->type != F_IMAP) break;
298                 } else if (folder->type == F_NEWS) {
299                         if (cur_folder->type != F_MH &&
300                             cur_folder->type != F_MBOX &&
301                             cur_folder->type != F_IMAP &&
302                             cur_folder->type != F_NEWS) break;
303                 }
304         }
305
306         folder_list = g_list_insert(folder_list, folder, i);
307 }
308
309 GList *folder_get_list(void)
310 {
311         return folder_list;
312 }
313
314 gint folder_read_list(void)
315 {
316         GNode *node;
317         XMLNode *xmlnode;
318         gchar *path;
319
320         path = folder_get_list_path();
321         if (!is_file_exist(path)) return -1;
322         node = xml_parse_file(path);
323         if (!node) return -1;
324
325         xmlnode = node->data;
326         if (strcmp2(xmlnode->tag->tag, "folderlist") != 0) {
327                 g_warning("wrong folder list\n");
328                 xml_free_tree(node);
329                 return -1;
330         }
331
332         g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, 2,
333                         folder_read_folder_func, NULL);
334
335         xml_free_tree(node);
336         if (folder_list)
337                 return 0;
338         else
339                 return -1;
340 }
341
342 void folder_write_list(void)
343 {
344         GList *list;
345         Folder *folder;
346         gchar *path;
347         PrefFile *pfile;
348
349         path = folder_get_list_path();
350         if ((pfile = prefs_write_open(path)) == NULL) return;
351
352         fprintf(pfile->fp, "<?xml version=\"1.0\" encoding=\"%s\"?>\n",
353                 conv_get_current_charset_str());
354         fputs("\n<folderlist>\n", pfile->fp);
355
356         for (list = folder_list; list != NULL; list = list->next) {
357                 folder = list->data;
358                 folder_write_list_recursive(folder->node, pfile->fp);
359         }
360
361         fputs("</folderlist>\n", pfile->fp);
362
363         if (prefs_write_close(pfile) < 0)
364                 g_warning("failed to write folder list.\n");
365 }
366
367 struct TotalMsgCount
368 {
369         guint new;
370         guint unread;
371         guint total;
372 };
373
374 static gboolean folder_count_total_msgs_func(GNode *node, gpointer data)
375 {
376         FolderItem *item;
377         struct TotalMsgCount *count = (struct TotalMsgCount *)data;
378
379         g_return_val_if_fail(node->data != NULL, FALSE);
380
381         item = FOLDER_ITEM(node->data);
382         count->new += item->new;
383         count->unread += item->unread;
384         count->total += item->total;
385
386         return FALSE;
387 }
388
389 void folder_count_total_msgs(guint *new, guint *unread, guint *total)
390 {
391         GList *list;
392         Folder *folder;
393         struct TotalMsgCount count;
394
395         count.new = count.unread = count.total = 0;
396
397         debug_print(_("Counting total number of messages...\n"));
398
399         for (list = folder_list; list != NULL; list = list->next) {
400                 folder = FOLDER(list->data);
401                 if (folder->node)
402                         g_node_traverse(folder->node, G_PRE_ORDER,
403                                         G_TRAVERSE_ALL, -1,
404                                         folder_count_total_msgs_func,
405                                         &count);
406         }
407
408         *new = count.new;
409         *unread = count.unread;
410         *total = count.total;
411
412         return;
413 }
414
415 Folder *folder_find_from_path(const gchar *path)
416 {
417         GList *list;
418         Folder *folder;
419
420         for (list = folder_list; list != NULL; list = list->next) {
421                 folder = list->data;
422                 if ((folder->type == F_MH || folder->type == F_MBOX) &&
423                     !path_cmp(LOCAL_FOLDER(folder)->rootpath, path))
424                         return folder;
425         }
426
427         return NULL;
428 }
429
430 static gboolean folder_item_find_func(GNode *node, gpointer data)
431 {
432         FolderItem *item = node->data;
433         gpointer *d = data;
434         const gchar *path = d[0];
435
436         if (path_cmp(path, item->path) != 0)
437                 return FALSE;
438
439         d[1] = item;
440
441         return TRUE;
442 }
443
444 FolderItem *folder_find_item_from_path(const gchar *path)
445 {
446         Folder *folder;
447         gpointer d[2];
448
449         folder = folder_get_default_folder();
450         g_return_val_if_fail(folder != NULL, NULL);
451
452         d[0] = (gpointer)path;
453         d[1] = NULL;
454         g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
455                         folder_item_find_func, d);
456         return d[1];
457 }
458
459 Folder *folder_get_default_folder(void)
460 {
461         return folder_list ? FOLDER(folder_list->data) : NULL;
462 }
463
464 FolderItem *folder_get_default_inbox(void)
465 {
466         Folder *folder;
467
468         if (!folder_list) return NULL;
469         folder = FOLDER(folder_list->data);
470         g_return_val_if_fail(folder != NULL, NULL);
471         return folder->inbox;
472 }
473
474 FolderItem *folder_get_default_outbox(void)
475 {
476         Folder *folder;
477
478         if (!folder_list) return NULL;
479         folder = FOLDER(folder_list->data);
480         g_return_val_if_fail(folder != NULL, NULL);
481         return folder->outbox;
482 }
483
484 FolderItem *folder_get_default_draft(void)
485 {
486         Folder *folder;
487
488         if (!folder_list) return NULL;
489         folder = FOLDER(folder_list->data);
490         g_return_val_if_fail(folder != NULL, NULL);
491         return folder->draft;
492 }
493
494 FolderItem *folder_get_default_queue(void)
495 {
496         Folder *folder;
497
498         if (!folder_list) return NULL;
499         folder = FOLDER(folder_list->data);
500         g_return_val_if_fail(folder != NULL, NULL);
501         return folder->queue;
502 }
503
504 FolderItem *folder_get_default_trash(void)
505 {
506         Folder *folder;
507
508         if (!folder_list) return NULL;
509         folder = FOLDER(folder_list->data);
510         g_return_val_if_fail(folder != NULL, NULL);
511         return folder->trash;
512 }
513
514 gchar *folder_item_get_path(FolderItem *item)
515 {
516         gchar *folder_path;
517         gchar *path;
518
519         g_return_val_if_fail(item != NULL, NULL);
520
521         if (FOLDER_TYPE(item->folder) == F_MH)
522                 folder_path = g_strdup(LOCAL_FOLDER(item->folder)->rootpath);
523         else if (FOLDER_TYPE(item->folder) == F_MBOX) {
524                 path = mbox_get_virtual_path(item);
525                 if (path == NULL)
526                         return NULL;
527                 folder_path = g_strconcat(get_mbox_cache_dir(),
528                                           G_DIR_SEPARATOR_S, path, NULL);
529                 g_free(path);
530
531                 return folder_path;
532         }
533         else if (FOLDER_TYPE(item->folder) == F_IMAP) {
534                 g_return_val_if_fail(item->folder->account != NULL, NULL);
535                 folder_path = g_strconcat(get_imap_cache_dir(),
536                                           G_DIR_SEPARATOR_S,
537                                           item->folder->account->recv_server,
538                                           G_DIR_SEPARATOR_S,
539                                           item->folder->account->userid,
540                                           NULL);
541         } else if (FOLDER_TYPE(item->folder) == F_NEWS) {
542                 g_return_val_if_fail(item->folder->account != NULL, NULL);
543                 folder_path = g_strconcat(get_news_cache_dir(),
544                                           G_DIR_SEPARATOR_S,
545                                           item->folder->account->nntp_server,
546                                           NULL);
547         } else
548                 return NULL;
549
550         g_return_val_if_fail(folder_path != NULL, NULL);
551
552         if (folder_path[0] == G_DIR_SEPARATOR) {
553                 if (item->path)
554                         path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
555                                            item->path, NULL);
556                 else
557                         path = g_strdup(folder_path);
558         } else {
559                 if (item->path)
560                         path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
561                                            folder_path, G_DIR_SEPARATOR_S,
562                                            item->path, NULL);
563                 else
564                         path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
565                                            folder_path, NULL);
566         }
567
568         g_free(folder_path);
569         return path;
570 }
571
572 void folder_item_scan(FolderItem *item)
573 {
574         Folder *folder;
575
576         g_return_if_fail(item != NULL);
577
578         folder = item->folder;
579
580         g_return_if_fail(folder->scan != NULL);
581
582         folder->scan(folder, item);
583 }
584
585 static void folder_item_scan_foreach_func(gpointer key, gpointer val,
586                                           gpointer data)
587 {
588         folder_item_scan(FOLDER_ITEM(key));
589 }
590
591 void folder_item_scan_foreach(GHashTable *table)
592 {
593         g_hash_table_foreach(table, folder_item_scan_foreach_func, NULL);
594 }
595
596 gchar *folder_item_fetch_msg(FolderItem *item, gint num)
597 {
598         Folder *folder;
599
600         g_return_val_if_fail(item != NULL, NULL);
601
602         folder = item->folder;
603
604         g_return_val_if_fail(folder->scan != NULL, NULL);
605         g_return_val_if_fail(folder->fetch_msg != NULL, NULL);
606
607         if (item->last_num < 0) folder->scan(folder, item);
608
609         return folder->fetch_msg(folder, item, num);
610 }
611
612 gint folder_item_add_msg(FolderItem *dest, const gchar *file,
613                          gboolean remove_source)
614 {
615         Folder *folder;
616         gint num;
617
618         g_return_val_if_fail(dest != NULL, -1);
619         g_return_val_if_fail(file != NULL, -1);
620
621         folder = dest->folder;
622
623         g_return_val_if_fail(folder->scan != NULL, -1);
624         g_return_val_if_fail(folder->add_msg != NULL, -1);
625
626         if (dest->last_num < 0) folder->scan(folder, dest);
627
628         num = folder->add_msg(folder, dest, file, remove_source);
629         if (num > 0) dest->last_num = num;
630
631         return num;
632 }
633
634 /*
635 gint folder_item_move_msg(FolderItem *dest, MsgInfo *msginfo)
636 {
637         Folder *folder;
638         gint num;
639
640         g_return_val_if_fail(dest != NULL, -1);
641         g_return_val_if_fail(msginfo != NULL, -1);
642
643         folder = dest->folder;
644         if (dest->last_num < 0) folder->scan(folder, dest);
645
646         num = folder->move_msg(folder, dest, msginfo);
647         if (num > 0) dest->last_num = num;
648
649         return num;
650 }
651 */
652
653 gint folder_item_move_msg(FolderItem *dest, MsgInfo *msginfo)
654 {
655         Folder *folder;
656         gint num;
657         gchar * filename;
658         Folder * src_folder;
659
660         g_return_val_if_fail(dest != NULL, -1);
661         g_return_val_if_fail(msginfo != NULL, -1);
662
663         folder = dest->folder;
664
665         g_return_val_if_fail(folder->scan != NULL, -1);
666         g_return_val_if_fail(folder->remove_msg != NULL, -1);
667         g_return_val_if_fail(folder->copy_msg != NULL, -1);
668
669         if (dest->last_num < 0) folder->scan(folder, dest);
670
671         src_folder = msginfo->folder->folder;
672
673         num = folder->copy_msg(folder, dest, msginfo);
674
675         if (num != -1) {
676                 src_folder->remove_msg(src_folder,
677                                        msginfo->folder,
678                                        msginfo->msgnum);
679         }                                      
680         
681         if (folder->finished_copy)
682                 folder->finished_copy(folder, dest);
683
684         src_folder = msginfo->folder->folder;
685
686         if (msginfo->folder && src_folder->scan)
687                 src_folder->scan(src_folder, msginfo->folder);
688         folder->scan(folder, dest);
689
690         return num;
691 }
692
693 /*
694 gint folder_item_move_msgs_with_dest(FolderItem *dest, GSList *msglist)
695 {
696         Folder *folder;
697         gint num;
698
699         g_return_val_if_fail(dest != NULL, -1);
700         g_return_val_if_fail(msglist != NULL, -1);
701
702         folder = dest->folder;
703         if (dest->last_num < 0) folder->scan(folder, dest);
704
705         num = folder->move_msgs_with_dest(folder, dest, msglist);
706         if (num > 0) dest->last_num = num;
707         else dest->op_count = 0;
708
709         return num;
710 }
711 */
712
713 gint folder_item_move_msgs_with_dest(FolderItem *dest, GSList *msglist)
714 {
715         Folder *folder;
716         FolderItem * item;
717         GSList * l;
718         gchar * filename;
719
720         g_return_val_if_fail(dest != NULL, -1);
721         g_return_val_if_fail(msglist != NULL, -1);
722
723         folder = dest->folder;
724
725         g_return_val_if_fail(folder->scan != NULL, -1);
726         g_return_val_if_fail(folder->copy_msg != NULL, -1);
727         g_return_val_if_fail(folder->remove_msg != NULL, -1);
728
729         if (dest->last_num < 0) folder->scan(folder, dest);
730
731         item = NULL;
732         for(l = msglist ; l != NULL ; l = g_slist_next(l)) {
733                 MsgInfo * msginfo = (MsgInfo *) l->data;
734
735                 if (!item && msginfo->folder != NULL)
736                         item = msginfo->folder;
737
738                 if (folder->copy_msg(folder, dest, msginfo) != -1)
739                         item->folder->remove_msg(item->folder,
740                                                  msginfo->folder,
741                                                  msginfo->msgnum);
742         }
743
744         if (folder->finished_copy)
745                 folder->finished_copy(folder, dest);
746
747         if (item && item->folder->scan)
748                 item->folder->scan(item->folder, item);
749         folder->scan(folder, dest);
750
751         return dest->last_num;
752 }
753
754 /*
755 gint folder_item_copy_msg(FolderItem *dest, MsgInfo *msginfo)
756 {
757         Folder *folder;
758         gint num;
759
760         g_return_val_if_fail(dest != NULL, -1);
761         g_return_val_if_fail(msginfo != NULL, -1);
762
763         folder = dest->folder;
764         if (dest->last_num < 0) folder->scan(folder, dest);
765
766         num = folder->copy_msg(folder, dest, msginfo);
767         if (num > 0) dest->last_num = num;
768
769         return num;
770 }
771 */
772
773 gint folder_item_copy_msg(FolderItem *dest, MsgInfo *msginfo)
774 {
775         Folder *folder;
776         gint num;
777         gchar * filename;
778         Folder * src_folder;
779
780         g_return_val_if_fail(dest != NULL, -1);
781         g_return_val_if_fail(msginfo != NULL, -1);
782
783         folder = dest->folder;
784
785         g_return_val_if_fail(folder->scan != NULL, -1);
786         g_return_val_if_fail(folder->copy_msg != NULL, -1);
787
788         if (dest->last_num < 0) folder->scan(folder, dest);
789         
790         num = folder->copy_msg(folder, dest, msginfo);
791
792         if (folder->finished_copy)
793                 folder->finished_copy(folder, dest);
794
795         folder->scan(folder, dest);
796
797         return num;
798 }
799
800 /*
801 gint folder_item_copy_msgs_with_dest(FolderItem *dest, GSList *msglist)
802 {
803         Folder *folder;
804         gint num;
805
806         g_return_val_if_fail(dest != NULL, -1);
807         g_return_val_if_fail(msglist != NULL, -1);
808
809         folder = dest->folder;
810         if (dest->last_num < 0) folder->scan(folder, dest);
811
812         num = folder->copy_msgs_with_dest(folder, dest, msglist);
813         if (num > 0) dest->last_num = num;
814         else dest->op_count = 0;
815
816         return num;
817 }
818 */
819
820 gint folder_item_copy_msgs_with_dest(FolderItem *dest, GSList *msglist)
821 {
822         Folder *folder;
823         gint num;
824         GSList * l;
825         gchar * filename;
826
827         g_return_val_if_fail(dest != NULL, -1);
828         g_return_val_if_fail(msglist != NULL, -1);
829
830         folder = dest->folder;
831  
832         g_return_val_if_fail(folder->scan != NULL, -1);
833         g_return_val_if_fail(folder->copy_msg != NULL, -1);
834
835         if (dest->last_num < 0) folder->scan(folder, dest);
836
837         for(l = msglist ; l != NULL ; l = g_slist_next(l)) {
838                 MsgInfo * msginfo = (MsgInfo *) l->data;
839
840                 folder->copy_msg(folder, dest, msginfo);
841         }
842
843         if (folder->finished_copy)
844                 folder->finished_copy(folder, dest);
845
846         folder->scan(folder, dest);
847
848         return dest->last_num;
849 }
850
851 gint folder_item_remove_msg(FolderItem *item, gint num)
852 {
853         Folder *folder;
854         gint ret;
855
856         g_return_val_if_fail(item != NULL, -1);
857
858         folder = item->folder;
859         if (item->last_num < 0) folder->scan(folder, item);
860
861         ret = folder->remove_msg(folder, item, num);
862         if (ret == 0 && num == item->last_num)
863                 folder->scan(folder, item);
864
865         return ret;
866 }
867
868 gint folder_item_remove_msgs(FolderItem *item, GSList *msglist)
869 {
870         gint ret = 0;
871
872         g_return_val_if_fail(item != NULL, -1);
873
874         while (msglist != NULL) {
875                 MsgInfo *msginfo = (MsgInfo *)msglist->data;
876
877                 ret = folder_item_remove_msg(item, msginfo->msgnum);
878                 if (ret != 0) break;
879                 msglist = msglist->next;
880         }
881
882         return ret;
883 }
884
885 gint folder_item_remove_all_msg(FolderItem *item)
886 {
887         Folder *folder;
888         gint result;
889
890         g_return_val_if_fail(item != NULL, -1);
891
892         folder = item->folder;
893
894         g_return_val_if_fail(folder->scan != NULL, -1);
895         g_return_val_if_fail(folder->remove_all_msg != NULL, -1);
896
897         if (item->last_num < 0) folder->scan(folder, item);
898
899         result = folder->remove_all_msg(folder, item);
900
901         if (result == 0){
902                 if (folder->finished_remove)
903                         folder->finished_remove(folder, item);
904         }
905
906         return result;
907 }
908
909 gboolean folder_item_is_msg_changed(FolderItem *item, MsgInfo *msginfo)
910 {
911         Folder *folder;
912
913         g_return_val_if_fail(item != NULL, FALSE);
914
915         folder = item->folder;
916
917         g_return_val_if_fail(folder->is_msg_changed != NULL, -1);
918
919         return folder->is_msg_changed(folder, item, msginfo);
920 }
921
922 gchar *folder_item_get_cache_file(FolderItem *item)
923 {
924         gchar *path;
925         gchar *file;
926
927         g_return_val_if_fail(item != NULL, NULL);
928         g_return_val_if_fail(item->path != NULL, NULL);
929
930         path = folder_item_get_path(item);
931         g_return_val_if_fail(path != NULL, NULL);
932         if (!is_dir_exist(path))
933                 make_dir_hier(path);
934         file = g_strconcat(path, G_DIR_SEPARATOR_S, CACHE_FILE, NULL);
935         g_free(path);
936
937         return file;
938 }
939
940 gchar *folder_item_get_mark_file(FolderItem *item)
941 {
942         gchar *path;
943         gchar *file;
944
945         g_return_val_if_fail(item != NULL, NULL);
946         g_return_val_if_fail(item->path != NULL, NULL);
947
948         path = folder_item_get_path(item);
949         g_return_val_if_fail(path != NULL, NULL);
950         if (!is_dir_exist(path))
951                 make_dir_hier(path);
952         file = g_strconcat(path, G_DIR_SEPARATOR_S, MARK_FILE, NULL);
953         g_free(path);
954
955         return file;
956 }
957
958
959 static void folder_init(Folder *folder, FolderType type, const gchar *name)
960 {
961         FolderItem *item;
962
963         g_return_if_fail(folder != NULL);
964
965         folder->type = type;
966         folder_set_name(folder, name);
967         folder->account = NULL;
968         folder->inbox = NULL;
969         folder->outbox = NULL;
970         folder->draft = NULL;
971         folder->queue = NULL;
972         folder->trash = NULL;
973         folder->ui_func = NULL;
974         folder->ui_func_data = NULL;
975         item = folder_item_new(name, NULL);
976         item->folder = folder;
977         folder->node = g_node_new(item);
978         folder->data = NULL;
979
980         switch (type) {
981         case F_MH:
982                 folder->get_msg_list        = mh_get_msg_list;
983                 folder->fetch_msg           = mh_fetch_msg;
984                 folder->add_msg             = mh_add_msg;
985                 /*
986                 folder->move_msg            = mh_move_msg;
987                 folder->move_msgs_with_dest = mh_move_msgs_with_dest;
988                 folder->copy_msg            = mh_copy_msg;
989                 folder->copy_msgs_with_dest = mh_copy_msgs_with_dest;
990                 */
991                 folder->copy_msg            = mh_copy_msg;
992                 folder->remove_msg          = mh_remove_msg;
993                 folder->remove_all_msg      = mh_remove_all_msg;
994                 folder->is_msg_changed      = mh_is_msg_changed;
995                 folder->scan                = mh_scan_folder;
996                 folder->scan_tree           = mh_scan_tree;
997                 folder->create_tree         = mh_create_tree;
998                 folder->create_folder       = mh_create_folder;
999                 folder->rename_folder       = mh_rename_folder;
1000                 folder->remove_folder       = mh_remove_folder;
1001                 break;
1002         case F_IMAP:
1003                 folder->get_msg_list        = imap_get_msg_list;
1004                 folder->fetch_msg           = imap_fetch_msg;
1005                 folder->add_msg             = imap_add_msg;
1006                 folder->move_msg            = imap_move_msg;
1007                 folder->move_msgs_with_dest = imap_move_msgs_with_dest;
1008                 folder->copy_msg            = imap_copy_msg;
1009                 folder->copy_msgs_with_dest = imap_copy_msgs_with_dest;
1010                 folder->remove_msg          = imap_remove_msg;
1011                 folder->remove_all_msg      = imap_remove_all_msg;
1012                 folder->scan                = imap_scan_folder;
1013                 folder->scan_tree           = imap_scan_tree;
1014                 folder->create_tree         = imap_create_tree;
1015                 folder->create_folder       = imap_create_folder;
1016                 folder->remove_folder       = imap_remove_folder;
1017                 break;
1018         case F_NEWS:
1019                 folder->get_msg_list        = news_get_article_list;
1020                 folder->fetch_msg           = news_fetch_msg;
1021                 folder->scan                = news_scan_group;
1022                 break;
1023         case F_MBOX:
1024                 folder->get_msg_list        = mbox_get_msg_list;
1025                 folder->fetch_msg           = mbox_fetch_msg;
1026                 folder->scan                = mbox_scan_folder;
1027                 folder->add_msg             = mbox_add_msg;
1028                 folder->remove_all_msg      = mbox_remove_all_msg;
1029                 folder->remove_msg          = mbox_remove_msg;
1030                 /*
1031                 folder->move_msg            = mbox_move_msg;
1032                 folder->move_msgs_with_dest = mbox_move_msgs_with_dest;
1033                 folder->copy_msg            = mbox_copy_msg;
1034                 folder->copy_msgs_with_dest = mbox_copy_msgs_with_dest;
1035                 */
1036                 folder->copy_msg            = mbox_copy_msg;
1037
1038                 folder->create_tree         = mbox_create_tree;
1039                 folder->create_folder       = mbox_create_folder;
1040                 folder->rename_folder       = mbox_rename_folder;
1041                 folder->remove_folder       = mbox_remove_folder;
1042
1043                 folder->update_mark         = mbox_update_mark;
1044                 folder->change_flags        = mbox_change_flags;
1045                 folder->finished_copy       = mbox_finished_copy;
1046
1047                 break;
1048         default:
1049                 break;
1050         }
1051
1052         switch (type) {
1053         case F_MH:
1054         case F_MBOX:
1055         case F_MAILDIR:
1056                 LOCAL_FOLDER(folder)->rootpath = NULL;
1057                 break;
1058         case F_IMAP:
1059         case F_NEWS:
1060                 REMOTE_FOLDER(folder)->session = NULL;
1061                 break;
1062         default:
1063                 break;
1064         }
1065 }
1066
1067 static void local_folder_destroy(LocalFolder *lfolder)
1068 {
1069         g_return_if_fail(lfolder != NULL);
1070
1071         g_free(lfolder->rootpath);
1072 }
1073
1074 static void remote_folder_destroy(RemoteFolder *rfolder)
1075 {
1076         g_return_if_fail(rfolder != NULL);
1077
1078         if (rfolder->session)
1079                 session_destroy(rfolder->session);
1080 }
1081
1082 static void mh_folder_destroy(MHFolder *folder)
1083 {
1084         local_folder_destroy(LOCAL_FOLDER(folder));
1085 }
1086
1087 static void mbox_folder_destroy(MboxFolder *folder)
1088 {
1089         local_folder_destroy(LOCAL_FOLDER(folder));
1090 }
1091
1092 static void imap_folder_destroy(IMAPFolder *folder)
1093 {
1094         remote_folder_destroy(REMOTE_FOLDER(folder));
1095 }
1096
1097 static void news_folder_destroy(NewsFolder *folder)
1098 {
1099         remote_folder_destroy(REMOTE_FOLDER(folder));
1100 }
1101
1102 static gboolean folder_build_tree(GNode *node, gpointer data)
1103 {
1104         Folder *folder = FOLDER(data);
1105         FolderItem *item;
1106         XMLNode *xmlnode;
1107         GList *list;
1108         SpecialFolderItemType stype = F_NORMAL;
1109         const gchar *name = NULL;
1110         const gchar *path = NULL;
1111         PrefsAccount *account = NULL;
1112         gboolean no_sub = FALSE, no_select = FALSE, collapsed = FALSE, 
1113                  threaded = TRUE, ret_rcpt = FALSE, hidereadmsgs = FALSE;
1114         gint mtime = 0, new = 0, unread = 0, total = 0;
1115
1116         g_return_val_if_fail(node->data != NULL, FALSE);
1117         if (!node->parent) return FALSE;
1118
1119         xmlnode = node->data;
1120         if (strcmp2(xmlnode->tag->tag, "folderitem") != 0) {
1121                 g_warning("tag name != \"folderitem\"\n");
1122                 return FALSE;
1123         }
1124
1125         list = xmlnode->tag->attr;
1126         for (; list != NULL; list = list->next) {
1127                 XMLAttr *attr = list->data;
1128
1129                 if (!attr || !attr->name || !attr->value) continue;
1130                 if (!strcmp(attr->name, "type")) {
1131                         if (!strcasecmp(attr->value, "normal"))
1132                                 stype = F_NORMAL;
1133                         else if (!strcasecmp(attr->value, "inbox"))
1134                                 stype = F_INBOX;
1135                         else if (!strcasecmp(attr->value, "outbox"))
1136                                 stype = F_OUTBOX;
1137                         else if (!strcasecmp(attr->value, "draft"))
1138                                 stype = F_DRAFT;
1139                         else if (!strcasecmp(attr->value, "queue"))
1140                                 stype = F_QUEUE;
1141                         else if (!strcasecmp(attr->value, "trash"))
1142                                 stype = F_TRASH;
1143                 } else if (!strcmp(attr->name, "name"))
1144                         name = attr->value;
1145                 else if (!strcmp(attr->name, "path"))
1146                         path = attr->value;
1147                 else if (!strcmp(attr->name, "account_id")) {
1148                         account = account_find_from_id(atoi(attr->value));
1149                         if (!account) g_warning("account_id: %s not found\n",
1150                                                 attr->value);
1151                 } else if (!strcmp(attr->name, "mtime"))
1152                         mtime = atoi(attr->value);
1153                 else if (!strcmp(attr->name, "new"))
1154                         new = atoi(attr->value);
1155                 else if (!strcmp(attr->name, "unread"))
1156                         unread = atoi(attr->value);
1157                 else if (!strcmp(attr->name, "total"))
1158                         total = atoi(attr->value);
1159                 else if (!strcmp(attr->name, "no_sub"))
1160                         no_sub = *attr->value == '1' ? TRUE : FALSE;
1161                 else if (!strcmp(attr->name, "no_select"))
1162                         no_select = *attr->value == '1' ? TRUE : FALSE;
1163                 else if (!strcmp(attr->name, "collapsed"))
1164                         collapsed = *attr->value == '1' ? TRUE : FALSE;
1165                 else if (!strcmp(attr->name, "threaded"))
1166                         threaded =  *attr->value == '1' ? TRUE : FALSE;
1167                 else if (!strcmp(attr->name, "hidereadmsgs"))
1168                         hidereadmsgs =  *attr->value == '1' ? TRUE : FALSE;
1169                 else if (!strcmp(attr->name, "reqretrcpt"))
1170                         ret_rcpt =  *attr->value == '1' ? TRUE : FALSE;
1171         }
1172
1173         item = folder_item_new(name, path);
1174         item->stype = stype;
1175         item->account = account;
1176         item->mtime = mtime;
1177         item->new = new;
1178         item->unread = unread;
1179         item->total = total;
1180         item->no_sub = no_sub;
1181         item->no_select = no_select;
1182         item->collapsed = collapsed;
1183         item->threaded  = threaded;
1184         item->hide_read_msgs  = hidereadmsgs;
1185         item->ret_rcpt  = ret_rcpt;
1186         item->parent = FOLDER_ITEM(node->parent->data);
1187         item->folder = folder;
1188         switch (stype) {
1189         case F_INBOX:  folder->inbox  = item; break;
1190         case F_OUTBOX: folder->outbox = item; break;
1191         case F_DRAFT:  folder->draft  = item; break;
1192         case F_QUEUE:  folder->queue  = item; break;
1193         case F_TRASH:  folder->trash  = item; break;
1194         default:
1195                 break;
1196         }
1197
1198         prefs_folder_item_read_config(item);
1199
1200         node->data = item;
1201         xml_free_node(xmlnode);
1202
1203         return FALSE;
1204 }
1205
1206 static gboolean folder_read_folder_func(GNode *node, gpointer data)
1207 {
1208         Folder *folder;
1209         XMLNode *xmlnode;
1210         GList *list;
1211         FolderType type = F_UNKNOWN;
1212         const gchar *name = NULL;
1213         const gchar *path = NULL;
1214         PrefsAccount *account = NULL;
1215         gboolean collapsed = FALSE, threaded = TRUE, ret_rcpt = FALSE;
1216
1217         if (g_node_depth(node) != 2) return FALSE;
1218         g_return_val_if_fail(node->data != NULL, FALSE);
1219
1220         xmlnode = node->data;
1221         if (strcmp2(xmlnode->tag->tag, "folder") != 0) {
1222                 g_warning("tag name != \"folder\"\n");
1223                 return TRUE;
1224         }
1225         g_node_unlink(node);
1226         list = xmlnode->tag->attr;
1227         for (; list != NULL; list = list->next) {
1228                 XMLAttr *attr = list->data;
1229
1230                 if (!attr || !attr->name || !attr->value) continue;
1231                 if (!strcmp(attr->name, "type")) {
1232                         if (!strcasecmp(attr->value, "mh"))
1233                                 type = F_MH;
1234                         else if (!strcasecmp(attr->value, "mbox"))
1235                                 type = F_MBOX;
1236                         else if (!strcasecmp(attr->value, "maildir"))
1237                                 type = F_MAILDIR;
1238                         else if (!strcasecmp(attr->value, "imap"))
1239                                 type = F_IMAP;
1240                         else if (!strcasecmp(attr->value, "news"))
1241                                 type = F_NEWS;
1242                 } else if (!strcmp(attr->name, "name"))
1243                         name = attr->value;
1244                 else if (!strcmp(attr->name, "path"))
1245                         path = attr->value;
1246                 else if (!strcmp(attr->name, "account_id")) {
1247                         account = account_find_from_id(atoi(attr->value));
1248                         if (!account) g_warning("account_id: %s not found\n",
1249                                                 attr->value);
1250                 } else if (!strcmp(attr->name, "collapsed"))
1251                         collapsed = *attr->value == '1' ? TRUE : FALSE;
1252                 else if (!strcmp(attr->name, "threaded"))
1253                         threaded = *attr->value == '1' ? TRUE : FALSE;
1254                 else if (!strcmp(attr->name, "reqretrcpt"))
1255                         ret_rcpt = *attr->value == '1' ? TRUE : FALSE;
1256         }
1257
1258         folder = folder_new(type, name, path);
1259         g_return_val_if_fail(folder != NULL, FALSE);
1260         folder->account = account;
1261         if (account && (type == F_IMAP || type == F_NEWS))
1262                 account->folder = REMOTE_FOLDER(folder);
1263         node->data = folder->node->data;
1264         g_node_destroy(folder->node);
1265         folder->node = node;
1266         folder_add(folder);
1267         FOLDER_ITEM(node->data)->collapsed = collapsed;
1268         FOLDER_ITEM(node->data)->threaded  = threaded;
1269         FOLDER_ITEM(node->data)->ret_rcpt  = ret_rcpt;
1270
1271         g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1272                         folder_build_tree, folder);
1273
1274         return FALSE;
1275 }
1276
1277 static gchar *folder_get_list_path(void)
1278 {
1279         static gchar *filename = NULL;
1280
1281         if (!filename)
1282                 filename =  g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1283                                         FOLDER_LIST, NULL);
1284
1285         return filename;
1286 }
1287
1288 static void folder_write_list_recursive(GNode *node, gpointer data)
1289 {
1290         FILE *fp = (FILE *)data;
1291         FolderItem *item = FOLDER_ITEM(node->data);
1292         gint i, depth;
1293         static gchar *folder_type_str[] = {"mh", "mbox", "maildir", "imap",
1294                                            "news", "unknown"};
1295         static gchar *folder_item_stype_str[] = {"normal", "inbox", "outbox",
1296                                                  "draft", "queue", "trash"};
1297
1298         g_return_if_fail(item != NULL);
1299
1300         depth = g_node_depth(node);
1301         for (i = 0; i < depth; i++)
1302                 fputs("    ", fp);
1303         if (depth == 1) {
1304                 Folder *folder = item->folder;
1305
1306                 fprintf(fp, "<folder type=\"%s\"", folder_type_str[folder->type]);
1307                 if (folder->name) {
1308                         fputs(" name=\"", fp);
1309                         xml_file_put_escape_str(fp, folder->name);
1310                         fputs("\"", fp);
1311                 }
1312                 if ((folder->type == F_MH) || (folder->type == F_MBOX)) {
1313                         fputs(" path=\"", fp);
1314                         xml_file_put_escape_str
1315                                 (fp, LOCAL_FOLDER(folder)->rootpath);
1316                         fputs("\"", fp);
1317                 }
1318                 if (folder->account)
1319                         fprintf(fp, " account_id=\"%d\"",
1320                                 folder->account->account_id);
1321                 if (item->collapsed && node->children)
1322                         fputs(" collapsed=\"1\"", fp);
1323                 if (item->ret_rcpt) 
1324                         fputs(" reqretrcpt=\"1\"", fp);
1325         } else {
1326                 fprintf(fp, "<folderitem type=\"%s\"",
1327                         folder_item_stype_str[item->stype]);
1328                 if (item->name) {
1329                         fputs(" name=\"", fp);
1330                         xml_file_put_escape_str(fp, item->name);
1331                         fputs("\"", fp);
1332                 }
1333                 if (item->path) {
1334                         fputs(" path=\"", fp);
1335                         xml_file_put_escape_str(fp, item->path);
1336                         fputs("\"", fp);
1337                 }
1338                 if (item->account)
1339                         fprintf(fp, " account_id=\"%d\"",
1340                                 item->account->account_id);
1341                 if (item->no_sub)
1342                         fputs(" no_sub=\"1\"", fp);
1343                 if (item->no_select)
1344                         fputs(" no_select=\"1\"", fp);
1345                 if (item->collapsed && node->children)
1346                         fputs(" collapsed=\"1\"", fp);
1347                 if (item->threaded)
1348                         fputs(" threaded=\"1\"", fp);
1349                 else
1350                         fputs(" threaded=\"0\"", fp);
1351                 if (item->hide_read_msgs)
1352                         fputs(" hidereadmsgs=\"1\"", fp);
1353                 else
1354                         fputs(" hidereadmsgs=\"0\"", fp);
1355                 if (item->ret_rcpt)
1356                         fputs(" reqretrcpt=\"1\"", fp);
1357                 fprintf(fp,
1358                         " mtime=\"%lu\" new=\"%d\" unread=\"%d\" total=\"%d\"",
1359                         item->mtime, item->new, item->unread, item->total);
1360         }
1361
1362         if (node->children) {
1363                 GNode *child;
1364                 fputs(">\n", fp);
1365
1366                 child = node->children;
1367                 while (child) {
1368                         GNode *cur;
1369
1370                         cur = child;
1371                         child = cur->next;
1372                         folder_write_list_recursive(cur, data);
1373                 }
1374
1375                 for (i = 0; i < depth; i++)
1376                         fputs("    ", fp);
1377                 fprintf(fp, "</%s>\n", depth == 1 ? "folder" : "folderitem");
1378         } else
1379                 fputs(" />\n", fp);
1380 }
1381
1382 static void folder_update_op_count_rec(GNode *node) {
1383         FolderItem *fitem = FOLDER_ITEM(node->data);
1384
1385         if (g_node_depth(node) > 0) {
1386                 if (fitem->op_count > 0) {
1387                         fitem->op_count = 0;
1388                         folderview_update_item(fitem, 0);
1389                 }
1390                 if (node->children) {
1391                         GNode *child;
1392
1393                         child = node->children;
1394                         while (child) {
1395                                 GNode *cur;
1396
1397                                 cur = child;
1398                                 child = cur->next;
1399                                 folder_update_op_count_rec(cur);
1400                         }
1401                 }
1402         }
1403 }
1404
1405 void folder_update_op_count() {
1406         GList *cur;
1407         Folder *folder;
1408
1409         for (cur = folder_list; cur != NULL; cur = cur->next) {
1410                 folder = cur->data;
1411                 folder_update_op_count_rec(folder->node);
1412         }
1413 }
1414
1415 typedef struct _type_str {
1416         gchar * str;
1417         gint type;
1418 } type_str;
1419
1420
1421 static type_str type_str_table[] = 
1422 {
1423         {"#mh", F_MH},
1424         {"#mbox", F_MBOX},
1425         {"#maildir", F_MAILDIR},
1426         {"#imap", F_IMAP},
1427         {"#news", F_NEWS}
1428 };
1429
1430
1431 static gchar * folder_get_type_string(gint type)
1432 {
1433         gint i;
1434
1435         for(i = 0 ; i < sizeof(type_str_table) / sizeof(type_str) ; i++) {
1436                 if (type_str_table[i].type == type)
1437                         return type_str_table[i].str;
1438         }
1439         return NULL;
1440 }
1441
1442 static gint folder_get_type_from_string(gchar * str)
1443 {
1444         gint i;
1445
1446         for(i = 0 ; i < sizeof(type_str_table) / sizeof(type_str) ; i++) {
1447                 if (g_strcasecmp(type_str_table[i].str, str))
1448                         return type_str_table[i].type;
1449         }
1450         return F_UNKNOWN;
1451 }
1452
1453 gchar * folder_get_identifier(Folder * folder)
1454 {
1455         gchar * type_str;
1456         type_str = folder_get_type_string(folder->type);
1457
1458         return g_strconcat(type_str, "/", folder->name, NULL);
1459 }
1460
1461 /*
1462 static gchar * folder_item_get_tree_identifier(FolderItem * item)
1463 {
1464         if (item->parent != NULL) {
1465                 gchar * path;
1466                 gchar * id;
1467
1468                 path = folder_item_get_tree_identifier(item->parent);
1469                 if (path == NULL)
1470                         return NULL;
1471
1472                 id = g_strconcat(path, "/", item->name, NULL);
1473                 g_free(path);
1474
1475                 return id;
1476         }
1477         else {
1478                 return g_strconcat("/", item->name, NULL);
1479         }
1480 }
1481 */
1482
1483 gchar * folder_item_get_identifier(FolderItem * item)
1484 {
1485         gchar * id;
1486         gchar * folder_str;
1487
1488         g_return_val_if_fail(item->path != NULL, NULL);
1489
1490         folder_str = folder_get_identifier(item->folder);
1491         id = g_strconcat(folder_str, "/", item->path, NULL);
1492         g_free(folder_str);
1493
1494         return id;
1495 }
1496
1497 Folder * folder_find_from_name(const gchar * name)
1498 {
1499         GList *list;
1500         Folder *folder;
1501
1502         for (list = g_list_first(folder_list); list != NULL;
1503              list = list->next) {
1504                 folder = list->data;
1505                 if (strcmp(name, folder->name) == 0)
1506                         return folder;
1507         }
1508         return NULL;
1509 }
1510
1511 FolderItem * folder_find_item_from_identifier(const gchar *identifier)
1512 {
1513         Folder *folder;
1514         gpointer d[2];
1515         gchar * str;
1516         gchar * p;
1517         gint type;
1518         gchar * name;
1519         gchar * path;
1520
1521         Xstrdup_a(str, identifier, return NULL);
1522
1523         /* extract box type */
1524
1525         p = strchr(str, '/');
1526
1527         if (p == NULL)
1528                 return NULL;
1529
1530         *p = '\0';
1531
1532         type = folder_get_type_from_string(str);
1533
1534         str = p + 1;
1535
1536         /* extract box name */
1537
1538         p = strchr(str, '/');
1539
1540         if (p == NULL)
1541                 return NULL;
1542
1543         *p = '\0';
1544
1545         name = str;
1546
1547         folder = folder_find_from_name(name);
1548         if (folder == NULL)
1549                 return;
1550
1551         path = p + 1;
1552
1553         d[0] = (gpointer)path;
1554         d[1] = NULL;
1555         g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1556                         folder_item_find_func, d);
1557         return d[1];
1558 }
1559
1560 /* CLAWS: temporary local folder for filtering */
1561
1562 static Folder *processing_folder;
1563 static FolderItem *processing_folder_item;
1564
1565 static void folder_create_processing_folder(void)
1566 {
1567 #define PROCESSING_FOLDER ".processing" 
1568         Folder     *tmpparent;
1569         FolderItem *tmpfolder;
1570         gchar      *tmpname;
1571         struct stat s;
1572
1573         tmpparent = folder_get_default_folder();
1574         g_assert(tmpparent);
1575         debug_print("tmpparentroot %s\n", LOCAL_FOLDER(tmpparent)->rootpath);
1576         if (LOCAL_FOLDER(tmpparent)->rootpath[0] == '/')
1577                 tmpname = g_strconcat(LOCAL_FOLDER(tmpparent)->rootpath,
1578                                       G_DIR_SEPARATOR_S, PROCESSING_FOLDER,
1579                                       NULL);
1580         else
1581                 tmpname = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1582                                       LOCAL_FOLDER(tmpparent)->rootpath,
1583                                       G_DIR_SEPARATOR_S, PROCESSING_FOLDER,
1584                                       NULL);
1585
1586         processing_folder = folder_new(F_MH, "PROCESSING", LOCAL_FOLDER(tmpparent)->rootpath);
1587         g_assert(processing_folder);
1588
1589         if (!is_dir_exist(tmpname)) {
1590                 debug_print("*TMP* creating %s\n", tmpname);
1591                 processing_folder_item = processing_folder->create_folder(processing_folder,
1592                                                                           processing_folder->node->data,
1593                                                                           PROCESSING_FOLDER);
1594                 g_assert(processing_folder_item);                                                                         
1595         }
1596         else {
1597                 debug_print("*TMP* already created\n");
1598                 processing_folder_item = folder_item_new(".processing", ".processing");
1599                 g_assert(processing_folder_item);
1600                 folder_item_append(processing_folder->node->data, processing_folder_item);
1601         }
1602         g_free(tmpname);
1603 }
1604
1605 FolderItem *folder_get_default_processing(void)
1606 {
1607         if (!processing_folder_item) {
1608                 folder_create_processing_folder();
1609         }
1610         return processing_folder_item;
1611 }
1612