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