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