bugfix for filtering
[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;
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, "reqretrcpt"))
1168                         ret_rcpt =  *attr->value == '1' ? TRUE : FALSE;
1169         }
1170
1171         item = folder_item_new(name, path);
1172         item->stype = stype;
1173         item->account = account;
1174         item->mtime = mtime;
1175         item->new = new;
1176         item->unread = unread;
1177         item->total = total;
1178         item->no_sub = no_sub;
1179         item->no_select = no_select;
1180         item->collapsed = collapsed;
1181         item->threaded  = threaded;
1182         item->ret_rcpt  = ret_rcpt;
1183         item->parent = FOLDER_ITEM(node->parent->data);
1184         item->folder = folder;
1185         switch (stype) {
1186         case F_INBOX:  folder->inbox  = item; break;
1187         case F_OUTBOX: folder->outbox = item; break;
1188         case F_DRAFT:  folder->draft  = item; break;
1189         case F_QUEUE:  folder->queue  = item; break;
1190         case F_TRASH:  folder->trash  = item; break;
1191         default:
1192                 break;
1193         }
1194
1195         prefs_folder_item_read_config(item);
1196
1197         node->data = item;
1198         xml_free_node(xmlnode);
1199
1200         return FALSE;
1201 }
1202
1203 static gboolean folder_read_folder_func(GNode *node, gpointer data)
1204 {
1205         Folder *folder;
1206         XMLNode *xmlnode;
1207         GList *list;
1208         FolderType type = F_UNKNOWN;
1209         const gchar *name = NULL;
1210         const gchar *path = NULL;
1211         PrefsAccount *account = NULL;
1212         gboolean collapsed = FALSE, threaded = TRUE, ret_rcpt = FALSE;
1213
1214         if (g_node_depth(node) != 2) return FALSE;
1215         g_return_val_if_fail(node->data != NULL, FALSE);
1216
1217         xmlnode = node->data;
1218         if (strcmp2(xmlnode->tag->tag, "folder") != 0) {
1219                 g_warning("tag name != \"folder\"\n");
1220                 return TRUE;
1221         }
1222         g_node_unlink(node);
1223         list = xmlnode->tag->attr;
1224         for (; list != NULL; list = list->next) {
1225                 XMLAttr *attr = list->data;
1226
1227                 if (!attr || !attr->name || !attr->value) continue;
1228                 if (!strcmp(attr->name, "type")) {
1229                         if (!strcasecmp(attr->value, "mh"))
1230                                 type = F_MH;
1231                         else if (!strcasecmp(attr->value, "mbox"))
1232                                 type = F_MBOX;
1233                         else if (!strcasecmp(attr->value, "maildir"))
1234                                 type = F_MAILDIR;
1235                         else if (!strcasecmp(attr->value, "imap"))
1236                                 type = F_IMAP;
1237                         else if (!strcasecmp(attr->value, "news"))
1238                                 type = F_NEWS;
1239                 } else if (!strcmp(attr->name, "name"))
1240                         name = attr->value;
1241                 else if (!strcmp(attr->name, "path"))
1242                         path = attr->value;
1243                 else if (!strcmp(attr->name, "account_id")) {
1244                         account = account_find_from_id(atoi(attr->value));
1245                         if (!account) g_warning("account_id: %s not found\n",
1246                                                 attr->value);
1247                 } else if (!strcmp(attr->name, "collapsed"))
1248                         collapsed = *attr->value == '1' ? TRUE : FALSE;
1249                 else if (!strcmp(attr->name, "threaded"))
1250                         threaded = *attr->value == '1' ? TRUE : FALSE;
1251                 else if (!strcmp(attr->name, "reqretrcpt"))
1252                         ret_rcpt = *attr->value == '1' ? TRUE : FALSE;
1253         }
1254
1255         folder = folder_new(type, name, path);
1256         g_return_val_if_fail(folder != NULL, FALSE);
1257         folder->account = account;
1258         if (account && (type == F_IMAP || type == F_NEWS))
1259                 account->folder = REMOTE_FOLDER(folder);
1260         node->data = folder->node->data;
1261         g_node_destroy(folder->node);
1262         folder->node = node;
1263         folder_add(folder);
1264         FOLDER_ITEM(node->data)->collapsed = collapsed;
1265         FOLDER_ITEM(node->data)->threaded  = threaded;
1266         FOLDER_ITEM(node->data)->ret_rcpt  = ret_rcpt;
1267
1268         g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1269                         folder_build_tree, folder);
1270
1271         return FALSE;
1272 }
1273
1274 static gchar *folder_get_list_path(void)
1275 {
1276         static gchar *filename = NULL;
1277
1278         if (!filename)
1279                 filename =  g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1280                                         FOLDER_LIST, NULL);
1281
1282         return filename;
1283 }
1284
1285 static void folder_write_list_recursive(GNode *node, gpointer data)
1286 {
1287         FILE *fp = (FILE *)data;
1288         FolderItem *item = FOLDER_ITEM(node->data);
1289         gint i, depth;
1290         static gchar *folder_type_str[] = {"mh", "mbox", "maildir", "imap",
1291                                            "news", "unknown"};
1292         static gchar *folder_item_stype_str[] = {"normal", "inbox", "outbox",
1293                                                  "draft", "queue", "trash"};
1294
1295         g_return_if_fail(item != NULL);
1296
1297         depth = g_node_depth(node);
1298         for (i = 0; i < depth; i++)
1299                 fputs("    ", fp);
1300         if (depth == 1) {
1301                 Folder *folder = item->folder;
1302
1303                 fprintf(fp, "<folder type=\"%s\"", folder_type_str[folder->type]);
1304                 if (folder->name) {
1305                         fputs(" name=\"", fp);
1306                         xml_file_put_escape_str(fp, folder->name);
1307                         fputs("\"", fp);
1308                 }
1309                 if ((folder->type == F_MH) || (folder->type == F_MBOX)) {
1310                         fputs(" path=\"", fp);
1311                         xml_file_put_escape_str
1312                                 (fp, LOCAL_FOLDER(folder)->rootpath);
1313                         fputs("\"", fp);
1314                 }
1315                 if (folder->account)
1316                         fprintf(fp, " account_id=\"%d\"",
1317                                 folder->account->account_id);
1318                 if (item->collapsed && node->children)
1319                         fputs(" collapsed=\"1\"", fp);
1320                 if (item->ret_rcpt) 
1321                         fputs(" reqretrcpt=\"1\"", fp);
1322         } else {
1323                 fprintf(fp, "<folderitem type=\"%s\"",
1324                         folder_item_stype_str[item->stype]);
1325                 if (item->name) {
1326                         fputs(" name=\"", fp);
1327                         xml_file_put_escape_str(fp, item->name);
1328                         fputs("\"", fp);
1329                 }
1330                 if (item->path) {
1331                         fputs(" path=\"", fp);
1332                         xml_file_put_escape_str(fp, item->path);
1333                         fputs("\"", fp);
1334                 }
1335                 if (item->account)
1336                         fprintf(fp, " account_id=\"%d\"",
1337                                 item->account->account_id);
1338                 if (item->no_sub)
1339                         fputs(" no_sub=\"1\"", fp);
1340                 if (item->no_select)
1341                         fputs(" no_select=\"1\"", fp);
1342                 if (item->collapsed && node->children)
1343                         fputs(" collapsed=\"1\"", fp);
1344                 if (item->threaded)
1345                         fputs(" threaded=\"1\"", fp);
1346                 else
1347                         fputs(" threaded=\"0\"", fp);
1348                 if (item->ret_rcpt)
1349                         fputs(" reqretrcpt=\"1\"", fp);
1350                 fprintf(fp,
1351                         " mtime=\"%lu\" new=\"%d\" unread=\"%d\" total=\"%d\"",
1352                         item->mtime, item->new, item->unread, item->total);
1353         }
1354
1355         if (node->children) {
1356                 GNode *child;
1357                 fputs(">\n", fp);
1358
1359                 child = node->children;
1360                 while (child) {
1361                         GNode *cur;
1362
1363                         cur = child;
1364                         child = cur->next;
1365                         folder_write_list_recursive(cur, data);
1366                 }
1367
1368                 for (i = 0; i < depth; i++)
1369                         fputs("    ", fp);
1370                 fprintf(fp, "</%s>\n", depth == 1 ? "folder" : "folderitem");
1371         } else
1372                 fputs(" />\n", fp);
1373 }
1374
1375 static void folder_update_op_count_rec(GNode *node) {
1376         FolderItem *fitem = FOLDER_ITEM(node->data);
1377
1378         if (g_node_depth(node) > 0) {
1379                 if (fitem->op_count > 0) {
1380                         fitem->op_count = 0;
1381                         folderview_update_item(fitem, 0);
1382                 }
1383                 if (node->children) {
1384                         GNode *child;
1385
1386                         child = node->children;
1387                         while (child) {
1388                                 GNode *cur;
1389
1390                                 cur = child;
1391                                 child = cur->next;
1392                                 folder_update_op_count_rec(cur);
1393                         }
1394                 }
1395         }
1396 }
1397
1398 void folder_update_op_count() {
1399         GList *cur;
1400         Folder *folder;
1401
1402         for (cur = folder_list; cur != NULL; cur = cur->next) {
1403                 folder = cur->data;
1404                 folder_update_op_count_rec(folder->node);
1405         }
1406 }
1407
1408 typedef struct _type_str {
1409         gchar * str;
1410         gint type;
1411 } type_str;
1412
1413
1414 static type_str type_str_table[] = 
1415 {
1416         {"#mh", F_MH},
1417         {"#mbox", F_MBOX},
1418         {"#maildir", F_MAILDIR},
1419         {"#imap", F_IMAP},
1420         {"#news", F_NEWS}
1421 };
1422
1423
1424 static gchar * folder_get_type_string(gint type)
1425 {
1426         gint i;
1427
1428         for(i = 0 ; i < sizeof(type_str_table) / sizeof(type_str) ; i++) {
1429                 if (type_str_table[i].type == type)
1430                         return type_str_table[i].str;
1431         }
1432         return NULL;
1433 }
1434
1435 static gint folder_get_type_from_string(gchar * str)
1436 {
1437         gint i;
1438
1439         for(i = 0 ; i < sizeof(type_str_table) / sizeof(type_str) ; i++) {
1440                 if (g_strcasecmp(type_str_table[i].str, str))
1441                         return type_str_table[i].type;
1442         }
1443         return F_UNKNOWN;
1444 }
1445
1446 gchar * folder_get_identifier(Folder * folder)
1447 {
1448         gchar * type_str;
1449         type_str = folder_get_type_string(folder->type);
1450
1451         return g_strconcat(type_str, "/", folder->name, NULL);
1452 }
1453
1454 /*
1455 static gchar * folder_item_get_tree_identifier(FolderItem * item)
1456 {
1457         if (item->parent != NULL) {
1458                 gchar * path;
1459                 gchar * id;
1460
1461                 path = folder_item_get_tree_identifier(item->parent);
1462                 if (path == NULL)
1463                         return NULL;
1464
1465                 id = g_strconcat(path, "/", item->name, NULL);
1466                 g_free(path);
1467
1468                 return id;
1469         }
1470         else {
1471                 return g_strconcat("/", item->name, NULL);
1472         }
1473 }
1474 */
1475
1476 gchar * folder_item_get_identifier(FolderItem * item)
1477 {
1478         gchar * id;
1479         gchar * folder_str;
1480
1481         g_return_val_if_fail(item->path != NULL, NULL);
1482
1483         folder_str = folder_get_identifier(item->folder);
1484         id = g_strconcat(folder_str, "/", item->path, NULL);
1485         g_free(folder_str);
1486
1487         return id;
1488 }
1489
1490 Folder * folder_find_from_name(const gchar * name)
1491 {
1492         GList *list;
1493         Folder *folder;
1494
1495         for (list = g_list_first(folder_list); list != NULL;
1496              list = list->next) {
1497                 folder = list->data;
1498                 if (strcmp(name, folder->name) == 0)
1499                         return folder;
1500         }
1501         return NULL;
1502 }
1503
1504 FolderItem * folder_find_item_from_identifier(const gchar *identifier)
1505 {
1506         Folder *folder;
1507         gpointer d[2];
1508         gchar * str;
1509         gchar * p;
1510         gint type;
1511         gchar * name;
1512         gchar * path;
1513
1514         Xstrdup_a(str, identifier, return NULL);
1515
1516         /* extract box type */
1517
1518         p = strchr(str, '/');
1519
1520         if (p == NULL)
1521                 return NULL;
1522
1523         *p = '\0';
1524
1525         type = folder_get_type_from_string(str);
1526
1527         str = p + 1;
1528
1529         /* extract box name */
1530
1531         p = strchr(str, '/');
1532
1533         if (p == NULL)
1534                 return NULL;
1535
1536         *p = '\0';
1537
1538         name = str;
1539
1540         folder = folder_find_from_name(name);
1541         if (folder == NULL)
1542                 return;
1543
1544         path = p + 1;
1545
1546         d[0] = (gpointer)path;
1547         d[1] = NULL;
1548         g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1549                         folder_item_find_func, d);
1550         return d[1];
1551 }
1552
1553 /* CLAWS: temporary local folder for filtering */
1554
1555 static Folder *processing_folder;
1556 static FolderItem *processing_folder_item;
1557
1558 static void folder_create_processing_folder(void)
1559 {
1560 #define PROCESSING_FOLDER ".processing" 
1561         Folder     *tmpparent;
1562         FolderItem *tmpfolder;
1563         gchar      *tmpname;
1564         struct stat s;
1565
1566         tmpparent = folder_get_default_folder();
1567         g_assert(tmpparent);
1568         debug_print("tmpparentroot %s\n", LOCAL_FOLDER(tmpparent)->rootpath);
1569         if (LOCAL_FOLDER(tmpparent)->rootpath[0] == '/')
1570                 tmpname = g_strconcat(LOCAL_FOLDER(tmpparent)->rootpath,
1571                                       G_DIR_SEPARATOR_S, PROCESSING_FOLDER,
1572                                       NULL);
1573         else
1574                 tmpname = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1575                                       LOCAL_FOLDER(tmpparent)->rootpath,
1576                                       G_DIR_SEPARATOR_S, PROCESSING_FOLDER,
1577                                       NULL);
1578
1579         processing_folder = folder_new(F_MH, "PROCESSING", LOCAL_FOLDER(tmpparent)->rootpath);
1580         g_assert(processing_folder);
1581
1582         if (!is_dir_exist(tmpname)) {
1583                 debug_print("*TMP* creating %s\n", tmpname);
1584                 processing_folder_item = processing_folder->create_folder(processing_folder,
1585                                                                           processing_folder->node->data,
1586                                                                           PROCESSING_FOLDER);
1587                 g_assert(processing_folder_item);                                                                         
1588         }
1589         else {
1590                 debug_print("*TMP* already created\n");
1591                 processing_folder_item = folder_item_new(".processing", ".processing");
1592                 g_assert(processing_folder_item);
1593                 folder_item_append(processing_folder->node->data, processing_folder_item);
1594         }
1595         g_free(tmpname);
1596 }
1597
1598 FolderItem *folder_get_default_processing(void)
1599 {
1600         if (!processing_folder_item) {
1601                 folder_create_processing_folder();
1602         }
1603         return processing_folder_item;
1604 }
1605