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