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