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