revert last commit
[claws.git] / src / mh.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2003 Hiroyuki Yamamoto
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #include "defs.h"
25
26 #include <glib.h>
27 #include <dirent.h>
28 #include <sys/stat.h>
29 #include <unistd.h>
30 #include <string.h>
31 #include <errno.h>
32
33 #undef MEASURE_TIME
34
35 #ifdef MEASURE_TIME
36 #  include <sys/time.h>
37 #endif
38
39 #include "intl.h"
40 #include "folder.h"
41 #include "mh.h"
42 #include "procmsg.h"
43 #include "procheader.h"
44 #include "utils.h"
45
46 static void     mh_folder_init          (Folder         *folder,
47                                          const gchar    *name,
48                                          const gchar    *path);
49
50 static Folder   *mh_folder_new          (const gchar    *name,
51                                          const gchar    *path);
52 static void     mh_folder_destroy       (Folder         *folder);
53 static gchar   *mh_fetch_msg            (Folder         *folder,
54                                          FolderItem     *item,
55                                          gint            num);
56 static MsgInfo *mh_get_msginfo          (Folder         *folder,
57                                          FolderItem     *item,
58                                          gint            num);
59 static gint     mh_add_msg              (Folder         *folder,
60                                          FolderItem     *dest,
61                                          const gchar    *file,
62                                          MsgFlags       *flags);
63 static gint     mh_add_msgs             (Folder         *folder,
64                                          FolderItem     *dest,
65                                          GSList         *file_list,
66                                          GRelation      *relation);
67 static gint     mh_copy_msg             (Folder         *folder,
68                                          FolderItem     *dest,
69                                          MsgInfo        *msginfo);
70 static gint     mh_remove_msg           (Folder         *folder,
71                                          FolderItem     *item,
72                                          gint            num);
73 static gint     mh_remove_all_msg       (Folder         *folder,
74                                          FolderItem     *item);
75 static gboolean mh_is_msg_changed       (Folder         *folder,
76                                          FolderItem     *item,
77                                          MsgInfo        *msginfo);
78
79 static gint     mh_get_num_list         (Folder         *folder,
80                                          FolderItem     *item, 
81                                          GSList         **list, 
82                                          gboolean       *old_uids_valid);
83 static gint     mh_scan_tree            (Folder         *folder);
84
85 static gint    mh_create_tree           (Folder         *folder);
86 static FolderItem *mh_create_folder     (Folder         *folder,
87                                          FolderItem     *parent,
88                                          const gchar    *name);
89 static gint    mh_rename_folder         (Folder         *folder,
90                                          FolderItem     *item,
91                                          const gchar    *name);
92 static gint    mh_remove_folder         (Folder         *folder,
93                                          FolderItem     *item);
94
95 static gchar   *mh_get_new_msg_filename         (FolderItem     *dest);
96
97 static MsgInfo *mh_parse_msg                    (const gchar    *file,
98                                                  FolderItem     *item);
99 static void     mh_remove_missing_folder_items  (Folder         *folder);
100 static void     mh_scan_tree_recursive          (FolderItem     *item);
101
102 static gboolean mh_rename_folder_func           (GNode          *node,
103                                                  gpointer        data);
104 static gchar   *mh_item_get_path                (Folder *folder, 
105                                                  FolderItem *item);
106
107 static FolderClass mh_class =
108 {
109         F_MH,
110         "mh",
111         "MH",
112
113         /* Folder functions */
114         mh_folder_new,
115         mh_folder_destroy,
116         mh_scan_tree,
117         mh_create_tree,
118
119         /* FolderItem functions */
120         NULL,
121         NULL,
122         mh_item_get_path,
123         mh_create_folder,
124         mh_rename_folder,
125         mh_remove_folder,
126         NULL,
127         mh_get_num_list,
128         NULL,
129         NULL,
130         NULL,
131         NULL,
132         
133         /* Message functions */
134         mh_get_msginfo,
135         NULL,
136         mh_fetch_msg,
137         mh_add_msg,
138         mh_add_msgs,
139         mh_copy_msg,
140         NULL,
141         mh_remove_msg,
142         mh_remove_all_msg,
143         mh_is_msg_changed,
144         NULL,
145 };
146
147 FolderClass *mh_get_class(void)
148 {
149         return &mh_class;
150 }
151
152 static Folder *mh_folder_new(const gchar *name, const gchar *path)
153 {
154         Folder *folder;
155
156         folder = (Folder *)g_new0(MHFolder, 1);
157         folder->klass = &mh_class;
158         mh_folder_init(folder, name, path);
159
160         return folder;
161 }
162
163 static void mh_folder_destroy(Folder *folder)
164 {
165         folder_local_folder_destroy(LOCAL_FOLDER(folder));
166 }
167
168 static void mh_folder_init(Folder *folder, const gchar *name, const gchar *path)
169 {
170         folder_local_folder_init(folder, name, path);
171
172 }
173
174 void mh_get_last_num(Folder *folder, FolderItem *item)
175 {
176         gchar *path;
177         DIR *dp;
178         struct dirent *d;
179         struct stat s;
180         gint max = 0;
181         gint num;
182
183         g_return_if_fail(item != NULL);
184
185         debug_print("mh_get_last_num(): Scanning %s ...\n", item->path);
186
187         path = folder_item_get_path(item);
188         g_return_if_fail(path != NULL);
189         if (change_dir(path) < 0) {
190                 g_free(path);
191                 return;
192         }
193         g_free(path);
194
195         if ((dp = opendir(".")) == NULL) {
196                 FILE_OP_ERROR(item->path, "opendir");
197                 return;
198         }
199
200         while ((d = readdir(dp)) != NULL) {
201                 if ((num = to_number(d->d_name)) >= 0 &&
202                     stat(d->d_name, &s) == 0 &&
203                     S_ISREG(s.st_mode)) {
204                         if (max < num)
205                                 max = num;
206                 }
207         }
208         closedir(dp);
209
210         debug_print("Last number in dir %s = %d\n", item->path, max);
211         item->last_num = max;
212 }
213
214 gint mh_get_num_list(Folder *folder, FolderItem *item, GSList **list, gboolean *old_uids_valid)
215 {
216
217         gchar *path;
218         DIR *dp;
219         struct dirent *d;
220         struct stat s;
221         gint num, nummsgs = 0;
222
223         g_return_val_if_fail(item != NULL, -1);
224
225         debug_print("mh_get_last_num(): Scanning %s ...\n", item->path);
226
227         *old_uids_valid = TRUE;
228
229         path = folder_item_get_path(item);
230         g_return_val_if_fail(path != NULL, -1);
231         if (change_dir(path) < 0) {
232                 g_free(path);
233                 return -1;
234         }
235         g_free(path);
236
237         if ((dp = opendir(".")) == NULL) {
238                 FILE_OP_ERROR(item->path, "opendir");
239                 return -1;
240         }
241
242         while ((d = readdir(dp)) != NULL) {
243                 if ((num = to_number(d->d_name)) >= 0 &&
244                     stat(d->d_name, &s) == 0 &&
245                     S_ISREG(s.st_mode)) {
246                         *list = g_slist_prepend(*list, GINT_TO_POINTER(num));
247                     nummsgs++;
248                 }
249         }
250         closedir(dp);
251
252         return nummsgs;
253 }
254
255 static gchar *mh_fetch_msg(Folder *folder, FolderItem *item, gint num)
256 {
257         gchar *path;
258         gchar *file;
259
260         g_return_val_if_fail(item != NULL, NULL);
261         g_return_val_if_fail(num > 0, NULL);
262
263         path = folder_item_get_path(item);
264         file = g_strconcat(path, G_DIR_SEPARATOR_S, itos(num), NULL);
265         g_free(path);
266         if (!is_file_exist(file)) {
267                 g_free(file);
268                 return NULL;
269         }
270
271         return file;
272 }
273
274 static MsgInfo *mh_get_msginfo(Folder *folder, FolderItem *item, gint num)
275 {
276         MsgInfo *msginfo;
277         gchar *file;
278
279         g_return_val_if_fail(item != NULL, NULL);
280         g_return_val_if_fail(num > 0, NULL);
281
282         file = mh_fetch_msg(folder, item, num);
283         if (!file) return NULL;
284
285         msginfo = mh_parse_msg(file, item);
286         if (msginfo)
287                 msginfo->msgnum = num;
288
289         g_free(file);
290
291         return msginfo;
292 }
293
294 static gchar *mh_get_new_msg_filename(FolderItem *dest)
295 {
296         gchar *destfile;
297         gchar *destpath;
298
299         destpath = folder_item_get_path(dest);
300         g_return_val_if_fail(destpath != NULL, NULL);
301
302         if (!is_dir_exist(destpath))
303                 make_dir_hier(destpath);
304
305         for (;;) {
306                 destfile = g_strdup_printf("%s%c%d", destpath, G_DIR_SEPARATOR,
307                                            dest->last_num + 1);
308                 if (is_file_entry_exist(destfile)) {
309                         dest->last_num++;
310                         g_free(destfile);
311                 } else
312                         break;
313         }
314
315         g_free(destpath);
316
317         return destfile;
318 }
319
320 static gint mh_add_msg(Folder *folder, FolderItem *dest, const gchar *file, MsgFlags *flags)
321 {
322         gint ret;
323         GSList file_list;
324         MsgFileInfo fileinfo;
325
326         g_return_val_if_fail(file != NULL, -1);
327
328         fileinfo.msginfo = NULL;
329         fileinfo.file = (gchar *)file;
330         fileinfo.flags = flags;
331         file_list.data = &fileinfo;
332         file_list.next = NULL;
333
334         ret = mh_add_msgs(folder, dest, &file_list, NULL);
335         return ret;
336
337  
338 static gint mh_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list, 
339                  GRelation *relation)
340
341         gchar *destfile;
342         GSList *cur;
343         MsgFileInfo *fileinfo;
344
345         g_return_val_if_fail(dest != NULL, -1);
346         g_return_val_if_fail(file_list != NULL, -1);
347
348         if (dest->last_num < 0) {
349                 mh_get_last_num(folder, dest);
350                 if (dest->last_num < 0) return -1;
351         }
352
353         for (cur = file_list; cur != NULL; cur = cur->next) {
354                 fileinfo = (MsgFileInfo *)cur->data;
355
356                 destfile = mh_get_new_msg_filename(dest);
357                 if (destfile == NULL) return -1;
358
359                 if (link(fileinfo->file, destfile) < 0) {
360                         if (copy_file(fileinfo->file, destfile, TRUE) < 0) {
361                                 g_warning(_("can't copy message %s to %s\n"),
362                                           fileinfo->file, destfile);
363                                 g_free(destfile);
364                                 return -1;
365                         }
366                 }
367                 if (relation != NULL)
368                         g_relation_insert(relation, fileinfo, GINT_TO_POINTER(dest->last_num + 1));
369                 g_free(destfile);
370                 dest->last_num++;
371         }
372
373         return dest->last_num;
374 }
375
376 static gint mh_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
377 {
378         gchar *srcfile;
379         gchar *destfile;
380         gint filemode = 0;
381         FolderItemPrefs *prefs;
382
383         g_return_val_if_fail(dest != NULL, -1);
384         g_return_val_if_fail(msginfo != NULL, -1);
385
386         if (msginfo->folder == dest) {
387                 g_warning("the src folder is identical to the dest.\n");
388                 return -1;
389         }
390
391         if (dest->last_num < 0) {
392                 mh_get_last_num(folder, dest);
393                 if (dest->last_num < 0) return -1;
394         }
395
396         prefs = dest->prefs;
397
398         srcfile = procmsg_get_message_file(msginfo);
399         destfile = mh_get_new_msg_filename(dest);
400         if (!destfile) {
401                 g_free(srcfile);
402                 return -1;
403         }
404         
405         debug_print("Copying message %s%c%d to %s ...\n",
406                     msginfo->folder->path, G_DIR_SEPARATOR,
407                     msginfo->msgnum, dest->path);
408         
409
410         if ((MSG_IS_QUEUED(msginfo->flags) || MSG_IS_DRAFT(msginfo->flags))
411         &&  dest->stype != F_QUEUE && dest->stype != F_DRAFT) {
412                 if (procmsg_remove_special_headers(srcfile, destfile) !=0) {
413                         g_free(srcfile);
414                         g_free(destfile);
415                         return -1;
416                 }
417         } else if (copy_file(srcfile, destfile, TRUE) < 0) {
418                 FILE_OP_ERROR(srcfile, "copy");
419                 g_free(srcfile);
420                 g_free(destfile);
421                 return -1;
422         }
423
424
425         if (prefs && prefs->enable_folder_chmod && prefs->folder_chmod) {
426                 if (chmod(destfile, prefs->folder_chmod) < 0)
427                         FILE_OP_ERROR(destfile, "chmod");
428
429                 /* for mark file */
430                 filemode = prefs->folder_chmod;
431                 if (filemode & S_IRGRP) filemode |= S_IWGRP;
432                 if (filemode & S_IROTH) filemode |= S_IWOTH;
433         }
434
435         g_free(srcfile);
436         g_free(destfile);
437         dest->last_num++;
438
439         return dest->last_num;
440 }
441
442 static gint mh_remove_msg(Folder *folder, FolderItem *item, gint num)
443 {
444         gchar *file;
445
446         g_return_val_if_fail(item != NULL, -1);
447
448         file = mh_fetch_msg(folder, item, num);
449         g_return_val_if_fail(file != NULL, -1);
450
451         if (unlink(file) < 0) {
452                 FILE_OP_ERROR(file, "unlink");
453                 g_free(file);
454                 return -1;
455         }
456
457         g_free(file);
458         return 0;
459 }
460
461 static gint mh_remove_all_msg(Folder *folder, FolderItem *item)
462 {
463         gchar *path;
464         gint val;
465
466         g_return_val_if_fail(item != NULL, -1);
467
468         path = folder_item_get_path(item);
469         g_return_val_if_fail(path != NULL, -1);
470         val = remove_all_numbered_files(path);
471         g_free(path);
472
473         return val;
474 }
475
476 static gboolean mh_is_msg_changed(Folder *folder, FolderItem *item,
477                                   MsgInfo *msginfo)
478 {
479         struct stat s;
480
481         if (stat(itos(msginfo->msgnum), &s) < 0 ||
482             msginfo->size  != s.st_size ||
483             msginfo->mtime != s.st_mtime)
484                 return TRUE;
485
486         return FALSE;
487 }
488
489 static gint mh_scan_tree(Folder *folder)
490 {
491         FolderItem *item;
492         gchar *rootpath;
493
494         g_return_val_if_fail(folder != NULL, -1);
495
496         if (!folder->node) {
497                 item = folder_item_new(folder, folder->name, NULL);
498                 item->folder = folder;
499                 folder->node = item->node = g_node_new(item);
500         } else
501                 item = FOLDER_ITEM(folder->node->data);
502
503         rootpath = folder_item_get_path(item);
504         if (change_dir(rootpath) < 0) {
505                 g_free(rootpath);
506                 return -1;
507         }
508         g_free(rootpath);
509
510         mh_create_tree(folder);
511         mh_remove_missing_folder_items(folder);
512         mh_scan_tree_recursive(item);
513
514         return 0;
515 }
516
517 #define MAKE_DIR_IF_NOT_EXIST(dir) \
518 { \
519         if (!is_dir_exist(dir)) { \
520                 if (is_file_exist(dir)) { \
521                         g_warning("File `%s' already exists.\n" \
522                                     "Can't create folder.", dir); \
523                         return -1; \
524                 } \
525                 if (make_dir(dir) < 0) \
526                         return -1; \
527         } \
528 }
529
530 static gint mh_create_tree(Folder *folder)
531 {
532         gchar *rootpath;
533
534         g_return_val_if_fail(folder != NULL, -1);
535
536         CHDIR_RETURN_VAL_IF_FAIL(get_home_dir(), -1);
537         rootpath = LOCAL_FOLDER(folder)->rootpath;
538         MAKE_DIR_IF_NOT_EXIST(rootpath);
539         CHDIR_RETURN_VAL_IF_FAIL(rootpath, -1);
540         MAKE_DIR_IF_NOT_EXIST(INBOX_DIR);
541         MAKE_DIR_IF_NOT_EXIST(OUTBOX_DIR);
542         MAKE_DIR_IF_NOT_EXIST(QUEUE_DIR);
543         MAKE_DIR_IF_NOT_EXIST(DRAFT_DIR);
544         MAKE_DIR_IF_NOT_EXIST(TRASH_DIR);
545
546         return 0;
547 }
548
549 #undef MAKE_DIR_IF_NOT_EXIST
550
551 static gchar *mh_item_get_path(Folder *folder, FolderItem *item)
552 {
553         gchar *folder_path, *path;
554
555         g_return_val_if_fail(folder != NULL, NULL);
556         g_return_val_if_fail(item != NULL, NULL);
557
558         folder_path = g_strdup(LOCAL_FOLDER(folder)->rootpath);
559         g_return_val_if_fail(folder_path != NULL, NULL);
560
561         if (folder_path[0] == G_DIR_SEPARATOR) {
562                 if (item->path)
563                         path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
564                                            item->path, NULL);
565                 else
566                         path = g_strdup(folder_path);
567         } else {
568                 if (item->path)
569                         path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
570                                            folder_path, G_DIR_SEPARATOR_S,
571                                            item->path, NULL);
572                 else
573                         path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
574                                            folder_path, NULL);
575         }
576         g_free(folder_path);
577
578         return path;
579 }
580
581 static FolderItem *mh_create_folder(Folder *folder, FolderItem *parent,
582                                     const gchar *name)
583 {
584         gchar *path;
585         gchar *fullpath;
586         FolderItem *new_item;
587         gchar *mh_sequences_filename;
588         FILE *mh_sequences_file;
589
590         g_return_val_if_fail(folder != NULL, NULL);
591         g_return_val_if_fail(parent != NULL, NULL);
592         g_return_val_if_fail(name != NULL, NULL);
593
594         path = folder_item_get_path(parent);
595         if (!is_dir_exist(path)) 
596                 if (make_dir_hier(path) != 0)
597                         return NULL;
598                 
599         fullpath = g_strconcat(path, G_DIR_SEPARATOR_S, name, NULL);
600         g_free(path);
601
602         if (make_dir(fullpath) < 0) {
603                 g_free(fullpath);
604                 return NULL;
605         }
606
607         g_free(fullpath);
608
609         if (parent->path)
610                 path = g_strconcat(parent->path, G_DIR_SEPARATOR_S, name,
611                                    NULL);
612         else
613                 path = g_strdup(name);
614         new_item = folder_item_new(folder, name, path);
615         folder_item_append(parent, new_item);
616
617         g_free(path);
618
619         path = folder_item_get_path(new_item);
620         mh_sequences_filename = g_strconcat(path, G_DIR_SEPARATOR_S,
621                                             ".mh_sequences", NULL);
622         if ((mh_sequences_file = fopen(mh_sequences_filename, "a+b")) != NULL) {
623                 fclose(mh_sequences_file);
624         }
625         g_free(mh_sequences_filename);
626         g_free(path);
627
628         return new_item;
629 }
630
631 static gint mh_rename_folder(Folder *folder, FolderItem *item,
632                              const gchar *name)
633 {
634         gchar *oldpath;
635         gchar *dirname;
636         gchar *newpath;
637         gchar *paths[2];
638
639         g_return_val_if_fail(folder != NULL, -1);
640         g_return_val_if_fail(item != NULL, -1);
641         g_return_val_if_fail(item->path != NULL, -1);
642         g_return_val_if_fail(name != NULL, -1);
643
644         oldpath = folder_item_get_path(item);
645         if (!is_dir_exist(oldpath))
646                 make_dir_hier(oldpath);
647
648         dirname = g_dirname(oldpath);
649         newpath = g_strconcat(dirname, G_DIR_SEPARATOR_S, name, NULL);
650         g_free(dirname);
651
652         if (rename(oldpath, newpath) < 0) {
653                 FILE_OP_ERROR(oldpath, "rename");
654                 g_free(oldpath);
655                 g_free(newpath);
656                 return -1;
657         }
658
659         g_free(oldpath);
660         g_free(newpath);
661
662         if (strchr(item->path, G_DIR_SEPARATOR) != NULL) {
663                 dirname = g_dirname(item->path);
664                 newpath = g_strconcat(dirname, G_DIR_SEPARATOR_S, name, NULL);
665                 g_free(dirname);
666         } else
667                 newpath = g_strdup(name);
668
669         g_free(item->name);
670         item->name = g_strdup(name);
671
672         paths[0] = g_strdup(item->path);
673         paths[1] = newpath;
674         g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
675                         mh_rename_folder_func, paths);
676
677         g_free(paths[0]);
678         g_free(paths[1]);
679         return 0;
680 }
681
682 static gint mh_remove_folder(Folder *folder, FolderItem *item)
683 {
684         gchar *path;
685
686         g_return_val_if_fail(folder != NULL, -1);
687         g_return_val_if_fail(item != NULL, -1);
688         g_return_val_if_fail(item->path != NULL, -1);
689
690         path = folder_item_get_path(item);
691         if (remove_dir_recursive(path) < 0) {
692                 g_warning("can't remove directory `%s'\n", path);
693                 g_free(path);
694                 return -1;
695         }
696
697         g_free(path);
698         folder_item_remove(item);
699         return 0;
700 }
701
702 static MsgInfo *mh_parse_msg(const gchar *file, FolderItem *item)
703 {
704         struct stat s;
705         MsgInfo *msginfo;
706         MsgFlags flags;
707
708         flags.perm_flags = MSG_NEW|MSG_UNREAD;
709         flags.tmp_flags = 0;
710
711         g_return_val_if_fail(item != NULL, NULL);
712         g_return_val_if_fail(file != NULL, NULL);
713
714         if (item->stype == F_QUEUE) {
715                 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
716         } else if (item->stype == F_DRAFT) {
717                 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
718         }
719
720         msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
721         if (!msginfo) return NULL;
722
723         msginfo->msgnum = atoi(file);
724         msginfo->folder = item;
725
726         if (stat(file, &s) < 0) {
727                 FILE_OP_ERROR(file, "stat");
728                 msginfo->size = 0;
729                 msginfo->mtime = 0;
730         } else {
731                 msginfo->size = s.st_size;
732                 msginfo->mtime = s.st_mtime;
733         }
734
735         return msginfo;
736 }
737
738 #if 0
739 static gboolean mh_is_maildir_one(const gchar *path, const gchar *dir)
740 {
741         gchar *entry;
742         gboolean result;
743
744         entry = g_strconcat(path, G_DIR_SEPARATOR_S, dir, NULL);
745         result = is_dir_exist(entry);
746         g_free(entry);
747
748         return result;
749 }
750
751 /*
752  * check whether PATH is a Maildir style mailbox.
753  * This is the case if the 3 subdir: new, cur, tmp are existing.
754  * This functon assumes that entry is an directory
755  */
756 static gboolean mh_is_maildir(const gchar *path)
757 {
758         return mh_is_maildir_one(path, "new") &&
759                mh_is_maildir_one(path, "cur") &&
760                mh_is_maildir_one(path, "tmp");
761 }
762 #endif
763
764 static gboolean mh_remove_missing_folder_items_func(GNode *node, gpointer data)
765 {
766         FolderItem *item;
767         gchar *path;
768
769         g_return_val_if_fail(node->data != NULL, FALSE);
770
771         if (G_NODE_IS_ROOT(node))
772                 return FALSE;
773
774         item = FOLDER_ITEM(node->data);
775
776         path = folder_item_get_path(item);
777         if (!is_dir_exist(path)) {
778                 debug_print("folder '%s' not found. removing...\n", path);
779                 folder_item_remove(item);
780         }
781         g_free(path);
782
783         return FALSE;
784 }
785
786 static void mh_remove_missing_folder_items(Folder *folder)
787 {
788         g_return_if_fail(folder != NULL);
789
790         debug_print("searching missing folders...\n");
791
792         g_node_traverse(folder->node, G_POST_ORDER, G_TRAVERSE_ALL, -1,
793                         mh_remove_missing_folder_items_func, folder);
794 }
795
796 static void mh_scan_tree_recursive(FolderItem *item)
797 {
798         Folder *folder;
799         DIR *dp;
800         struct dirent *d;
801         struct stat s;
802         gchar *entry;
803         gint n_msg = 0;
804
805         g_return_if_fail(item != NULL);
806         g_return_if_fail(item->folder != NULL);
807
808         folder = item->folder;
809
810         dp = opendir(item->path ? item->path : ".");
811         if (!dp) {
812                 FILE_OP_ERROR(item->path ? item->path : ".", "opendir");
813                 return;
814         }
815
816         debug_print("scanning %s ...\n",
817                     item->path ? item->path
818                     : LOCAL_FOLDER(item->folder)->rootpath);
819         if (folder->ui_func)
820                 folder->ui_func(folder, item, folder->ui_func_data);
821
822         while ((d = readdir(dp)) != NULL) {
823                 if (d->d_name[0] == '.') continue;
824
825                 if (item->path)
826                         entry = g_strconcat(item->path, G_DIR_SEPARATOR_S,
827                                             d->d_name, NULL);
828                 else
829                         entry = g_strdup(d->d_name);
830
831                 if (stat(entry, &s) < 0) {
832                         FILE_OP_ERROR(entry, "stat");
833                         g_free(entry);
834                         continue;
835                 }
836
837                 if (S_ISDIR(s.st_mode)) {
838                         FolderItem *new_item = NULL;
839                         GNode *node;
840
841 #if 0
842                         if (mh_is_maildir(entry)) {
843                                 g_free(entry);
844                                 continue;
845                         }
846 #endif
847
848                         node = item->node;
849                         for (node = node->children; node != NULL; node = node->next) {
850                                 FolderItem *cur_item = FOLDER_ITEM(node->data);
851                                 if (!strcmp2(cur_item->path, entry)) {
852                                         new_item = cur_item;
853                                         break;
854                                 }
855                         }
856                         if (!new_item) {
857                                 debug_print("new folder '%s' found.\n", entry);
858                                 new_item = folder_item_new(folder, d->d_name, entry);
859                                 folder_item_append(item, new_item);
860                         }
861
862                         if (!item->path) {
863                                 if (!folder->inbox &&
864                                     !strcmp(d->d_name, INBOX_DIR)) {
865                                         new_item->stype = F_INBOX;
866                                         folder->inbox = new_item;
867                                 } else if (!folder->outbox &&
868                                            !strcmp(d->d_name, OUTBOX_DIR)) {
869                                         new_item->stype = F_OUTBOX;
870                                         folder->outbox = new_item;
871                                 } else if (!folder->draft &&
872                                            !strcmp(d->d_name, DRAFT_DIR)) {
873                                         new_item->stype = F_DRAFT;
874                                         folder->draft = new_item;
875                                 } else if (!folder->queue &&
876                                            !strcmp(d->d_name, QUEUE_DIR)) {
877                                         new_item->stype = F_QUEUE;
878                                         folder->queue = new_item;
879                                 } else if (!folder->trash &&
880                                            !strcmp(d->d_name, TRASH_DIR)) {
881                                         new_item->stype = F_TRASH;
882                                         folder->trash = new_item;
883                                 }
884                         }
885
886                         mh_scan_tree_recursive(new_item);
887                 } else if (to_number(d->d_name) != -1) n_msg++;
888
889                 g_free(entry);
890         }
891
892         closedir(dp);
893
894 /*
895         if (item->path) {
896                 gint new, unread, total, min, max;
897
898                 procmsg_get_mark_sum(item->path, &new, &unread, &total,
899                                      &min, &max, 0);
900                 if (n_msg > total) {
901                         new += n_msg - total;
902                         unread += n_msg - total;
903                 }
904                 item->new = new;
905                 item->unread = unread;
906                 item->total = n_msg;
907         }
908 */
909 }
910
911 static gboolean mh_rename_folder_func(GNode *node, gpointer data)
912 {
913         FolderItem *item = node->data;
914         gchar **paths = data;
915         const gchar *oldpath = paths[0];
916         const gchar *newpath = paths[1];
917         gchar *base;
918         gchar *new_itempath;
919         gint oldpathlen;
920
921         oldpathlen = strlen(oldpath);
922         if (strncmp(oldpath, item->path, oldpathlen) != 0) {
923                 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
924                 return TRUE;
925         }
926
927         base = item->path + oldpathlen;
928         while (*base == G_DIR_SEPARATOR) base++;
929         if (*base == '\0')
930                 new_itempath = g_strdup(newpath);
931         else
932                 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
933                                            NULL);
934         g_free(item->path);
935         item->path = new_itempath;
936
937         return FALSE;
938 }