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