sync with 0.7.5
[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,
48                                                  const gchar    *path);
49
50 static gchar   *mh_get_new_msg_filename         (FolderItem     *dest);
51
52 static GSList  *mh_get_uncached_msgs            (GHashTable     *msg_table,
53                                                  FolderItem     *item);
54 static MsgInfo *mh_parse_msg                    (const gchar    *file,
55                                                  FolderItem     *item);
56 static void     mh_scan_tree_recursive          (FolderItem     *item, 
57                                                  GHashTable *pptable);
58
59 static gboolean mh_rename_folder_func           (GNode          *node,
60                                                  gpointer        data);
61
62
63 Folder *mh_folder_new(const gchar *name, const gchar *path)
64 {
65         Folder *folder;
66
67         folder = (Folder *)g_new0(MHFolder, 1);
68         mh_folder_init(folder, name, path);
69
70         return folder;
71 }
72
73 void mh_folder_destroy(MHFolder *folder)
74 {
75         folder_local_folder_destroy(LOCAL_FOLDER(folder));
76 }
77
78 static void mh_folder_init(Folder *folder, const gchar *name, const gchar *path)
79 {
80         folder_local_folder_init(folder, name, path);
81
82         folder->type = F_MH;
83
84         folder->get_msg_list        = mh_get_msg_list;
85         folder->fetch_msg           = mh_fetch_msg;
86         folder->add_msg             = mh_add_msg;
87         folder->move_msg            = mh_move_msg;
88         folder->move_msgs_with_dest = mh_move_msgs_with_dest;
89         folder->copy_msg            = mh_copy_msg;
90         folder->copy_msgs_with_dest = mh_copy_msgs_with_dest;
91         folder->remove_msg          = mh_remove_msg;
92         folder->remove_all_msg      = mh_remove_all_msg;
93         folder->is_msg_changed      = mh_is_msg_changed;
94         folder->scan                = mh_scan_folder;
95         folder->scan_tree           = mh_scan_tree;
96         folder->create_tree         = mh_create_tree;
97         folder->create_folder       = mh_create_folder;
98         folder->rename_folder       = mh_rename_folder;
99         folder->remove_folder       = mh_remove_folder;
100 }
101
102 GSList *mh_get_msg_list(Folder *folder, FolderItem *item, gboolean use_cache)
103 {
104         GSList *mlist;
105         GHashTable *msg_table;
106         gchar *path;
107         struct stat s;
108         gboolean scan_new = TRUE;
109 #ifdef MEASURE_TIME
110         struct timeval tv_before, tv_after, tv_result;
111
112         gettimeofday(&tv_before, NULL);
113 #endif
114
115         g_return_val_if_fail(item != NULL, NULL);
116
117         path = folder_item_get_path(item);
118         if (stat(path, &s) < 0) {
119                 FILE_OP_ERROR(path, "stat");
120         } else {
121                 time_t mtime;
122
123                 mtime = MAX(s.st_mtime, s.st_ctime);
124                 if (item->mtime == mtime) {
125                         debug_print("Folder is not modified.\n");
126                         scan_new = FALSE;
127                 } else
128                         item->mtime = mtime;
129         }
130         g_free(path);
131
132         if (use_cache && !scan_new) {
133                 mlist = procmsg_read_cache(item, FALSE);
134                 if (!mlist)
135                         mlist = mh_get_uncached_msgs(NULL, item);
136         } else if (use_cache) {
137                 GSList *newlist;
138
139                 mlist = procmsg_read_cache(item, TRUE);
140                 msg_table = procmsg_msg_hash_table_create(mlist);
141
142                 newlist = mh_get_uncached_msgs(msg_table, item);
143                 if (msg_table)
144                         g_hash_table_destroy(msg_table);
145
146                 mlist = g_slist_concat(mlist, newlist);
147         } else
148                 mlist = mh_get_uncached_msgs(NULL, item);
149
150         procmsg_set_flags(mlist, item);
151
152 #ifdef MEASURE_TIME
153         gettimeofday(&tv_after, NULL);
154
155         timersub(&tv_after, &tv_before, &tv_result);
156         g_print("mh_get_msg_list: %s: elapsed time: %ld.%06ld sec\n",
157                 item->path, tv_result.tv_sec, tv_result.tv_usec);
158 #endif
159
160         return mlist;
161 }
162
163 gchar *mh_fetch_msg(Folder *folder, FolderItem *item, gint num)
164 {
165         gchar *path;
166         gchar *file;
167
168         g_return_val_if_fail(item != NULL, NULL);
169         g_return_val_if_fail(num > 0 && num <= item->last_num, NULL);
170
171         path = folder_item_get_path(item);
172         file = g_strconcat(path, G_DIR_SEPARATOR_S, itos(num), NULL);
173         g_free(path);
174         if (!is_file_exist(file)) {
175                 g_free(file);
176                 return NULL;
177         }
178
179         return file;
180 }
181
182 static gchar *mh_get_new_msg_filename(FolderItem *dest)
183 {
184         gchar *destfile;
185         gchar *destpath;
186
187         destpath = folder_item_get_path(dest);
188         g_return_val_if_fail(destpath != NULL, NULL);
189
190         if (!is_dir_exist(destpath))
191                 make_dir_hier(destpath);
192
193         for (;;) {
194                 destfile = g_strdup_printf("%s%c%d", destpath, G_DIR_SEPARATOR,
195                                            dest->last_num + 1);
196                 if (is_file_entry_exist(destfile)) {
197                         dest->last_num++;
198                         g_free(destfile);
199                 } else
200                         break;
201         }
202
203         g_free(destpath);
204
205         return destfile;
206 }
207
208 #define SET_DEST_MSG_FLAGS(fp, dest, msginfo) \
209 { \
210         MsgInfo newmsginfo; \
211  \
212         newmsginfo.msgnum = dest->last_num; \
213         newmsginfo.flags = msginfo->flags; \
214         if (dest->stype == F_OUTBOX || \
215             dest->stype == F_QUEUE  || \
216             dest->stype == F_DRAFT  || \
217             dest->stype == F_TRASH) \
218                 MSG_UNSET_PERM_FLAGS(newmsginfo.flags, \
219                                      MSG_NEW|MSG_UNREAD|MSG_DELETED); \
220  \
221         procmsg_write_flags(&newmsginfo, fp); \
222 }
223
224 gint mh_add_msg(Folder *folder, FolderItem *dest, const gchar *file,
225                 gboolean remove_source)
226 {
227         gchar *destfile;
228
229         g_return_val_if_fail(dest != NULL, -1);
230         g_return_val_if_fail(file != NULL, -1);
231
232         if (dest->last_num < 0) {
233                 mh_scan_folder(folder, dest);
234                 if (dest->last_num < 0) return -1;
235         }
236
237         destfile = mh_get_new_msg_filename(dest);
238         g_return_val_if_fail(destfile != NULL, -1);
239
240         if (link(file, destfile) < 0) {
241                 if (copy_file(file, destfile) < 0) {
242                         g_warning(_("can't copy message %s to %s\n"),
243                                   file, destfile);
244                         g_free(destfile);
245                         return -1;
246                 }
247         }
248
249         if (remove_source) {
250                 if (unlink(file) < 0)
251                         FILE_OP_ERROR(file, "unlink");
252         }
253
254         g_free(destfile);
255         dest->last_num++;
256         return dest->last_num;
257 }
258
259 static gint mh_do_move(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
260 {
261         gchar *destdir;
262         gchar *srcfile;
263         gchar *destfile;
264         FILE *fp;
265         gint filemode = 0;
266         PrefsFolderItem *prefs;
267
268         g_return_val_if_fail(dest != NULL, -1);
269         g_return_val_if_fail(msginfo != NULL, -1);
270
271         if (msginfo->folder == dest) {
272                 g_warning(_("the src folder is identical to the dest.\n"));
273                 return -1;
274         }
275
276         if (dest->last_num < 0) {
277                 mh_scan_folder(folder, dest);
278                 if (dest->last_num < 0) return -1;
279         }
280
281         prefs = dest->prefs;
282
283         destfile = mh_get_new_msg_filename(dest);
284         g_return_val_if_fail(destfile != NULL, -1);
285
286         debug_print(_("Moving message %s%c%d to %s ...\n"),
287                     msginfo->folder->path, G_DIR_SEPARATOR,
288                     msginfo->msgnum, dest->path);
289         srcfile = procmsg_get_message_file(msginfo);
290
291         destfile = mh_get_new_msg_filename(dest);
292         if(!destfile) return -1;
293
294         srcfile = procmsg_get_message_file(msginfo);
295
296         if (move_file(srcfile, destfile) < 0) {
297                 g_free(srcfile);
298                 g_free(destfile);
299                 return -1;
300         }
301
302         if (prefs && prefs->enable_folder_chmod && prefs->folder_chmod) {
303                 if (chmod(destfile, prefs->folder_chmod) < 0)
304                         FILE_OP_ERROR(destfile, "chmod");
305
306                 /* for mark file */
307                 filemode = prefs->folder_chmod;
308                 if (filemode & S_IRGRP) filemode |= S_IWGRP;
309                 if (filemode & S_IROTH) filemode |= S_IWOTH;
310         }
311
312         g_free(srcfile);
313         g_free(destfile);
314         dest->last_num++;
315
316         destdir = folder_item_get_path(dest);
317         if ((fp = procmsg_open_mark_file(destdir, TRUE)) == NULL)
318                 g_warning(_("Can't open mark file.\n"));
319         else {
320                 SET_DEST_MSG_FLAGS(fp, dest, msginfo);
321                 if (filemode) {
322 #if HAVE_FCHMOD
323                         fchmod(fileno(fp), filemode);
324 #else
325                         gchar *markfile;
326
327                         markfile = folder_item_get_mark_file(dest);
328                         if (markfile) {
329                                 chmod(markfile, filemode);
330                                 g_free(markfile);
331                         }
332 #endif
333                 }
334
335                 fclose(fp);
336         }
337         g_free(destdir);
338
339         return dest->last_num;
340 }
341
342 gint mh_move_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
343 {
344         gchar *srcfile;
345         gint ret = 0;
346  
347         g_return_val_if_fail(folder != NULL, -1);
348         g_return_val_if_fail(dest != NULL, -1);
349         g_return_val_if_fail(msginfo != NULL, -1);
350         g_return_val_if_fail(msginfo->folder != NULL, -1);
351  
352         if (folder == msginfo->folder->folder)
353                 return mh_do_move(folder, dest, msginfo);
354  
355         srcfile = procmsg_get_message_file(msginfo);
356         if (!srcfile) return -1;
357  
358         ret = mh_add_msg(folder, dest, srcfile, FALSE);
359         g_free(srcfile);
360  
361         if (ret != -1) {
362                 gchar *destdir;
363                 FILE *fp;
364  
365                 destdir = folder_item_get_path(dest);
366                 if ((fp = procmsg_open_mark_file(destdir, TRUE)) == NULL)
367                         g_warning(_("Can't open mark file.\n"));
368                 else {
369                         SET_DEST_MSG_FLAGS(fp, dest, msginfo);
370                         fclose(fp);
371                 }
372                 g_free(destdir);
373  
374                 ret = folder_item_remove_msg(msginfo->folder, msginfo->msgnum);
375         }
376  
377         return ret;
378 }
379
380 static gint mh_do_move_msgs_with_dest(Folder *folder, FolderItem *dest,
381                                       GSList *msglist)
382 {
383         gchar *destdir;
384         gchar *srcfile;
385         gchar *destfile;
386         FILE *fp;
387         GSList *cur;
388         MsgInfo *msginfo;
389         PrefsFolderItem *prefs;
390
391         g_return_val_if_fail(dest != NULL, -1);
392         g_return_val_if_fail(msglist != NULL, -1);
393
394         if (dest->last_num < 0) {
395                 mh_scan_folder(folder, dest);
396                 if (dest->last_num < 0) return -1;
397         }
398
399         prefs = dest->prefs;
400
401         destdir = folder_item_get_path(dest);
402         if ((fp = procmsg_open_mark_file(destdir, TRUE)) == NULL)
403                 g_warning(_("Can't open mark file.\n"));
404
405         for (cur = msglist; cur != NULL; cur = cur->next) {
406                 msginfo = (MsgInfo *)cur->data;
407
408                 if (msginfo->folder == dest) {
409                         g_warning(_("the src folder is identical to the dest.\n"));
410                         continue;
411                 }
412                 debug_print(_("Moving message %s%c%d to %s ...\n"),
413                             msginfo->folder->path, G_DIR_SEPARATOR,
414                             msginfo->msgnum, dest->path);
415
416                 destfile = mh_get_new_msg_filename(dest);
417                 if (!destfile) break;
418                 srcfile = procmsg_get_message_file(msginfo);
419                 destfile = mh_get_new_msg_filename(dest);
420                 if(!destfile) return -1;
421
422                 if (move_file(srcfile, destfile) < 0) {
423                         g_free(srcfile);
424                         g_free(destfile);
425                         break;
426                 }
427
428                 g_free(srcfile);
429                 g_free(destfile);
430                 dest->last_num++;
431
432                 if (fp) {
433                         SET_DEST_MSG_FLAGS(fp, dest, msginfo);
434                 }
435         }
436
437         g_free(destdir);
438         if (fp) fclose(fp);
439
440         return dest->last_num;
441 }
442
443 gint mh_move_msgs_with_dest(Folder *folder, FolderItem *dest, GSList *msglist)
444 {
445         MsgInfo *msginfo;
446         GSList *cur;
447         gint ret = 0;
448
449         msginfo = (MsgInfo *)msglist->data;
450         if (folder == msginfo->folder->folder)
451                 return mh_do_move_msgs_with_dest(folder, dest, msglist);
452
453         for (cur = msglist; cur != NULL; cur = cur->next) {
454                 msginfo = (MsgInfo *)cur->data;
455                 ret = mh_move_msg(folder, dest, msginfo);
456                 if (ret == -1) break;
457         }
458
459         return ret;
460 }
461
462 gint mh_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
463 {
464         gchar *destdir;
465         gchar *srcfile;
466         gchar *destfile;
467         FILE *fp;
468         gint filemode = 0;
469         PrefsFolderItem *prefs;
470
471         g_return_val_if_fail(dest != NULL, -1);
472         g_return_val_if_fail(msginfo != NULL, -1);
473
474         if (msginfo->folder == dest) {
475                 g_warning(_("the src folder is identical to the dest.\n"));
476                 return -1;
477         }
478
479         if (dest->last_num < 0) {
480                 mh_scan_folder(folder, dest);
481                 if (dest->last_num < 0) return -1;
482         }
483
484         prefs = dest->prefs;
485
486         destfile = mh_get_new_msg_filename(dest);
487         g_return_val_if_fail(destfile != NULL, -1);
488
489         debug_print(_("Copying message %s%c%d to %s ...\n"),
490                     msginfo->folder->path, G_DIR_SEPARATOR,
491                     msginfo->msgnum, dest->path);
492
493         srcfile = procmsg_get_message_file(msginfo);
494
495         if (copy_file(srcfile, destfile) < 0) {
496                 FILE_OP_ERROR(srcfile, "copy");
497                 g_free(srcfile);
498                 g_free(destfile);
499                 return -1;
500         }
501
502         if (prefs && prefs->enable_folder_chmod && prefs->folder_chmod) {
503                 if (chmod(destfile, prefs->folder_chmod) < 0)
504                         FILE_OP_ERROR(destfile, "chmod");
505
506                 /* for mark file */
507                 filemode = prefs->folder_chmod;
508                 if (filemode & S_IRGRP) filemode |= S_IWGRP;
509                 if (filemode & S_IROTH) filemode |= S_IWOTH;
510         }
511
512         g_free(srcfile);
513         g_free(destfile);
514         dest->last_num++;
515
516         destdir = folder_item_get_path(dest);
517         if ((fp = procmsg_open_mark_file(destdir, TRUE)) == NULL)
518                 g_warning(_("Can't open mark file.\n"));
519         else {
520                 SET_DEST_MSG_FLAGS(fp, dest, msginfo);
521
522                 if (filemode) {
523 #if HAVE_FCHMOD
524                         fchmod(fileno(fp), filemode);
525 #else
526                         gchar *markfile;
527
528                         markfile = folder_item_get_mark_file(dest);
529                         if (markfile) {
530                                 chmod(markfile, filemode);
531                                 g_free(markfile);
532                         }
533 #endif
534                 }
535
536                 fclose(fp);
537         }
538         g_free(destdir);
539
540         return dest->last_num;
541 }
542
543 /*
544 gint mh_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
545 {
546         Folder * src_folder;
547         gchar * filename;
548         gint num;
549         gchar * destdir;
550         FILE * fp;
551
552         src_folder = msginfo->folder->folder;
553         
554         g_return_val_if_fail(src_folder->fetch_msg != NULL, -1);
555         
556         filename = src_folder->fetch_msg(src_folder,
557                                          msginfo->folder,
558                                          msginfo->msgnum);
559         if (filename == NULL)
560                 return -1;
561
562         num = folder->add_msg(folder, dest, filename, FALSE);
563
564         destdir = folder_item_get_path(dest);
565
566         if (fp) {
567                 MsgInfo newmsginfo;
568
569                 newmsginfo.msgnum = dest->last_num;
570                 newmsginfo.flags = msginfo->flags;
571                 if (dest->stype == F_OUTBOX ||
572                     dest->stype == F_QUEUE  ||
573                     dest->stype == F_DRAFT  ||
574                     dest->stype == F_TRASH)
575                         MSG_UNSET_FLAGS(newmsginfo.flags,
576                                         MSG_NEW|MSG_UNREAD|MSG_DELETED);
577
578                 procmsg_write_flags(&newmsginfo, fp);
579                 fclose(fp);
580         }
581         
582         return num;
583 }
584 */
585
586 gint mh_copy_msgs_with_dest(Folder *folder, FolderItem *dest, GSList *msglist)
587 {
588         gchar *destdir;
589         gchar *srcfile;
590         gchar *destfile;
591         FILE *fp;
592         GSList *cur;
593         MsgInfo *msginfo;
594
595         g_return_val_if_fail(dest != NULL, -1);
596         g_return_val_if_fail(msglist != NULL, -1);
597
598         if (dest->last_num < 0) {
599                 mh_scan_folder(folder, dest);
600                 if (dest->last_num < 0) return -1;
601         }
602
603         destdir = folder_item_get_path(dest);
604         if (!is_dir_exist(destdir))
605                 make_dir_hier(destdir);
606
607         if ((fp = procmsg_open_mark_file(destdir, TRUE)) == NULL)
608                 g_warning(_("Can't open mark file.\n"));
609
610         for (cur = msglist; cur != NULL; cur = cur->next) {
611                 msginfo = (MsgInfo *)cur->data;
612
613                 if (msginfo->folder == dest) {
614                         g_warning(_("the src folder is identical to the dest.\n"));
615                         continue;
616                 }
617                 debug_print(_("Copying message %s%c%d to %s ...\n"),
618                             msginfo->folder->path, G_DIR_SEPARATOR,
619                             msginfo->msgnum, dest->path);
620
621                 destfile = mh_get_new_msg_filename(dest);
622                 if (!destfile) break;
623                 srcfile = procmsg_get_message_file(msginfo);
624
625                 if (copy_file(srcfile, destfile) < 0) {
626                         FILE_OP_ERROR(srcfile, "copy");
627                         g_free(srcfile);
628                         g_free(destfile);
629                         break;
630                 }
631
632                 g_free(srcfile);
633                 g_free(destfile);
634                 dest->last_num++;
635
636                 if (fp) {
637                         SET_DEST_MSG_FLAGS(fp, dest, msginfo);
638                 }
639         }
640
641         g_free(destdir);
642         if (fp) fclose(fp);
643
644         return dest->last_num;
645 }
646
647 gint mh_remove_msg(Folder *folder, FolderItem *item, gint num)
648 {
649         gchar *file;
650
651         g_return_val_if_fail(item != NULL, -1);
652
653         file = mh_fetch_msg(folder, item, num);
654         g_return_val_if_fail(file != NULL, -1);
655
656         if (unlink(file) < 0) {
657                 FILE_OP_ERROR(file, "unlink");
658                 g_free(file);
659                 return -1;
660         }
661
662         g_free(file);
663         return 0;
664 }
665
666 gint mh_remove_all_msg(Folder *folder, FolderItem *item)
667 {
668         gchar *path;
669         gint val;
670
671         g_return_val_if_fail(item != NULL, -1);
672
673         path = folder_item_get_path(item);
674         g_return_val_if_fail(path != NULL, -1);
675         val = remove_all_numbered_files(path);
676         g_free(path);
677
678         return val;
679 }
680
681 gboolean mh_is_msg_changed(Folder *folder, FolderItem *item, MsgInfo *msginfo)
682 {
683         struct stat s;
684
685         if (stat(itos(msginfo->msgnum), &s) < 0 ||
686             msginfo->size  != s.st_size ||
687             msginfo->mtime != s.st_mtime)
688                 return TRUE;
689
690         return FALSE;
691 }
692
693 gint mh_scan_folder(Folder *folder, FolderItem *item)
694 {
695         gchar *path;
696         DIR *dp;
697         struct dirent *d;
698         struct stat s;
699         gint max = 0;
700         gint num;
701         gint n_msg = 0;
702
703         g_return_val_if_fail(item != NULL, -1);
704
705         debug_print("mh_scan_folder(): Scanning %s ...\n", item->path);
706
707         path = folder_item_get_path(item);
708         g_return_val_if_fail(path != NULL, -1);
709         if (change_dir(path) < 0) {
710                 g_free(path);
711                 return -1;
712         }
713         g_free(path);
714
715         if ((dp = opendir(".")) == NULL) {
716                 FILE_OP_ERROR(item->path, "opendir");
717                 return -1;
718         }
719
720         if (folder->ui_func)
721                 folder->ui_func(folder, item, folder->ui_func_data);
722
723         while ((d = readdir(dp)) != NULL) {
724                 if ((num = to_number(d->d_name)) >= 0 &&
725                     stat(d->d_name, &s) == 0 &&
726                     S_ISREG(s.st_mode)) {
727                         n_msg++;
728                         if (max < num)
729                                 max = num;
730                 }
731         }
732
733         closedir(dp);
734
735         if (n_msg == 0)
736                 item->new = item->unread = item->total = 0;
737         else {
738                 gint new, unread, total, min, max;
739
740                 procmsg_get_mark_sum(".", &new, &unread, &total, &min, &max, 0);
741                 if (n_msg > total) {
742                         new += n_msg - total;
743                         unread += n_msg - total;
744                 }
745                 item->new = new;
746                 item->unread = unread;
747                 item->total = n_msg;
748         }
749
750         debug_print(_("Last number in dir %s = %d\n"), item->path, max);
751         item->last_num = max;
752
753         return 0;
754 }
755
756 void mh_scan_tree(Folder *folder)
757 {
758         FolderItem *item;
759         gchar *rootpath;
760         GHashTable *pptable;
761
762         g_return_if_fail(folder != NULL);
763
764         pptable = folder_persist_prefs_new(folder);
765
766         prefs_scoring_clear();
767         prefs_filtering_clear();
768         folder_tree_destroy(folder);
769         item = folder_item_new(folder->name, NULL);
770         item->folder = folder;
771         folder->node = g_node_new(item);
772
773         rootpath = folder_item_get_path(item);
774         if (change_dir(rootpath) < 0) {
775                 g_free(rootpath);
776                 return;
777         }
778         g_free(rootpath);
779
780         mh_create_tree(folder);
781         mh_scan_tree_recursive(item, pptable);
782         
783         folder_persist_prefs_free(pptable);
784
785         prefs_matcher_read_config();
786 }
787
788 #define MAKE_DIR_IF_NOT_EXIST(dir) \
789 { \
790         if (!is_dir_exist(dir)) { \
791                 if (is_file_exist(dir)) { \
792                         g_warning(_("File `%s' already exists.\n" \
793                                     "Can't create folder."), dir); \
794                         return -1; \
795                 } \
796                 if (mkdir(dir, S_IRWXU) < 0) { \
797                         FILE_OP_ERROR(dir, "mkdir"); \
798                         return -1; \
799                 } \
800                 if (chmod(dir, S_IRWXU) < 0) \
801                         FILE_OP_ERROR(dir, "chmod"); \
802         } \
803 }
804
805 gint mh_create_tree(Folder *folder)
806 {
807         gchar *rootpath;
808
809         g_return_val_if_fail(folder != NULL, -1);
810
811         CHDIR_RETURN_VAL_IF_FAIL(get_home_dir(), -1);
812         rootpath = LOCAL_FOLDER(folder)->rootpath;
813         MAKE_DIR_IF_NOT_EXIST(rootpath);
814         CHDIR_RETURN_VAL_IF_FAIL(rootpath, -1);
815         MAKE_DIR_IF_NOT_EXIST(INBOX_DIR);
816         MAKE_DIR_IF_NOT_EXIST(OUTBOX_DIR);
817         MAKE_DIR_IF_NOT_EXIST(QUEUE_DIR);
818         MAKE_DIR_IF_NOT_EXIST(DRAFT_DIR);
819         MAKE_DIR_IF_NOT_EXIST(TRASH_DIR);
820
821         return 0;
822 }
823
824 #undef MAKE_DIR_IF_NOT_EXIST
825
826 FolderItem *mh_create_folder(Folder *folder, FolderItem *parent,
827                              const gchar *name)
828 {
829         gchar *path;
830         gchar *fullpath;
831         FolderItem *new_item;
832
833         g_return_val_if_fail(folder != NULL, NULL);
834         g_return_val_if_fail(parent != NULL, NULL);
835         g_return_val_if_fail(name != NULL, NULL);
836
837         path = folder_item_get_path(parent);
838         if (!is_dir_exist(path))
839                 make_dir_hier(path);
840
841         fullpath = g_strconcat(path, G_DIR_SEPARATOR_S, name, NULL);
842         g_free(path);
843
844         if (mkdir(fullpath, S_IRWXU) < 0) {
845                 FILE_OP_ERROR(fullpath, "mkdir");
846                 g_free(fullpath);
847                 return NULL;
848         }
849         if (chmod(fullpath, S_IRWXU) < 0)
850                 FILE_OP_ERROR(fullpath, "chmod");
851
852         g_free(fullpath);
853
854         if (parent->path)
855                 path = g_strconcat(parent->path, G_DIR_SEPARATOR_S, name,
856                                    NULL);
857         else
858                 path = g_strdup(name);
859         new_item = folder_item_new(name, path);
860         folder_item_append(parent, new_item);
861         g_free(path);
862
863         return new_item;
864 }
865
866 gint mh_rename_folder(Folder *folder, FolderItem *item, const gchar *name)
867 {
868         gchar *oldpath;
869         gchar *dirname;
870         gchar *newpath;
871         GNode *node;
872         gchar *paths[2];
873
874         g_return_val_if_fail(folder != NULL, -1);
875         g_return_val_if_fail(item != NULL, -1);
876         g_return_val_if_fail(item->path != NULL, -1);
877         g_return_val_if_fail(name != NULL, -1);
878
879         oldpath = folder_item_get_path(item);
880         if (!is_dir_exist(oldpath))
881                 make_dir_hier(oldpath);
882
883         dirname = g_dirname(oldpath);
884         newpath = g_strconcat(dirname, G_DIR_SEPARATOR_S, name, NULL);
885         g_free(dirname);
886
887         if (rename(oldpath, newpath) < 0) {
888                 FILE_OP_ERROR(oldpath, "rename");
889                 g_free(oldpath);
890                 g_free(newpath);
891                 return -1;
892         }
893
894         g_free(oldpath);
895         g_free(newpath);
896
897         if (strchr(item->path, G_DIR_SEPARATOR) != NULL) {
898                 dirname = g_dirname(item->path);
899                 newpath = g_strconcat(dirname, G_DIR_SEPARATOR_S, name, NULL);
900                 g_free(dirname);
901         } else
902                 newpath = g_strdup(name);
903
904         g_free(item->name);
905         item->name = g_strdup(name);
906
907         node = g_node_find(item->folder->node, G_PRE_ORDER, G_TRAVERSE_ALL,
908                            item);
909         paths[0] = g_strdup(item->path);
910         paths[1] = newpath;
911         g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
912                         mh_rename_folder_func, paths);
913
914         g_free(paths[0]);
915         g_free(paths[1]);
916         return 0;
917 }
918
919 gint mh_remove_folder(Folder *folder, FolderItem *item)
920 {
921         gchar *path;
922
923         g_return_val_if_fail(folder != NULL, -1);
924         g_return_val_if_fail(item != NULL, -1);
925         g_return_val_if_fail(item->path != NULL, -1);
926
927         path = folder_item_get_path(item);
928         if (remove_dir_recursive(path) < 0) {
929                 g_warning("can't remove directory `%s'\n", path);
930                 g_free(path);
931                 return -1;
932         }
933
934         g_free(path);
935         folder_item_remove(item);
936         return 0;
937 }
938
939
940 static GSList *mh_get_uncached_msgs(GHashTable *msg_table, FolderItem *item)
941 {
942         gchar *path;
943         DIR *dp;
944         struct dirent *d;
945         struct stat s;
946         GSList *newlist = NULL;
947         GSList *last = NULL;
948         MsgInfo *msginfo;
949         gint n_newmsg = 0;
950         gint num;
951
952         g_return_val_if_fail(item != NULL, NULL);
953
954         path = folder_item_get_path(item);
955         g_return_val_if_fail(path != NULL, NULL);
956         if (change_dir(path) < 0) {
957                 g_free(path);
958                 return NULL;
959         }
960         g_free(path);
961
962         if ((dp = opendir(".")) == NULL) {
963                 FILE_OP_ERROR(item->path, "opendir");
964                 return NULL;
965         }
966
967         debug_print(_("\tSearching uncached messages... "));
968
969         if (msg_table) {
970                 while ((d = readdir(dp)) != NULL) {
971                         if ((num = to_number(d->d_name)) < 0) continue;
972                         if (stat(d->d_name, &s) < 0) {
973                                 FILE_OP_ERROR(d->d_name, "stat");
974                                 continue;
975                         }
976                         if (!S_ISREG(s.st_mode)) continue;
977
978                         msginfo = g_hash_table_lookup
979                                 (msg_table, GUINT_TO_POINTER(num));
980
981                         if (!msginfo) {
982                                 /* not found in the cache (uncached message) */
983                                 msginfo = mh_parse_msg(d->d_name, item);
984                                 if (!msginfo) continue;
985
986                                 if (!newlist)
987                                         last = newlist =
988                                                 g_slist_append(NULL, msginfo);
989                                 else {
990                                         last = g_slist_append(last, msginfo);
991                                         last = last->next;
992                                 }
993                                 n_newmsg++;
994                         }
995                 }
996         } else {
997                 /* discard all previous cache */
998                 while ((d = readdir(dp)) != NULL) {
999                         if (to_number(d->d_name) < 0) continue;
1000                         if (stat(d->d_name, &s) < 0) {
1001                                 FILE_OP_ERROR(d->d_name, "stat");
1002                                 continue;
1003                         }
1004                         if (!S_ISREG(s.st_mode)) continue;
1005
1006                         msginfo = mh_parse_msg(d->d_name, item);
1007                         if (!msginfo) continue;
1008
1009                         if (!newlist)
1010                                 last = newlist = g_slist_append(NULL, msginfo);
1011                         else {
1012                                 last = g_slist_append(last, msginfo);
1013                                 last = last->next;
1014                         }
1015                         n_newmsg++;
1016                 }
1017         }
1018
1019         closedir(dp);
1020
1021         if (n_newmsg)
1022                 debug_print(_("%d uncached message(s) found.\n"), n_newmsg);
1023         else
1024                 debug_print(_("done.\n"));
1025
1026         /* sort new messages in numerical order */
1027         if (newlist) {
1028                 debug_print(_("\tSorting uncached messages in numerical order... "));
1029                 newlist = g_slist_sort
1030                         (newlist, (GCompareFunc)procmsg_cmp_msgnum_for_sort);
1031                 debug_print(_("done.\n"));
1032         }
1033
1034         return newlist;
1035 }
1036
1037 static MsgInfo *mh_parse_msg(const gchar *file, FolderItem *item)
1038 {
1039         struct stat s;
1040         MsgInfo *msginfo;
1041         MsgFlags flags;
1042
1043         flags.perm_flags = MSG_NEW|MSG_UNREAD;
1044         flags.tmp_flags = 0;
1045
1046         g_return_val_if_fail(item != NULL, NULL);
1047         g_return_val_if_fail(file != NULL, NULL);
1048
1049         if (item->stype == F_QUEUE) {
1050                 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
1051         } else if (item->stype == F_DRAFT) {
1052                 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
1053         }
1054
1055         msginfo = procheader_parse(file, flags, FALSE, FALSE);
1056         if (!msginfo) return NULL;
1057
1058         msginfo->msgnum = atoi(file);
1059         msginfo->folder = item;
1060
1061         if (stat(file, &s) < 0) {
1062                 FILE_OP_ERROR(file, "stat");
1063                 msginfo->size = 0;
1064                 msginfo->mtime = 0;
1065         } else {
1066                 msginfo->size = s.st_size;
1067                 msginfo->mtime = s.st_mtime;
1068         }
1069
1070         return msginfo;
1071 }
1072
1073 static gboolean mh_is_maildir_one(const gchar *path, const gchar *dir)
1074 {
1075         gchar *entry;
1076         gboolean result;
1077
1078         entry = g_strconcat(path, G_DIR_SEPARATOR_S, dir, NULL);
1079         result = is_dir_exist(entry);
1080         g_free(entry);
1081
1082         return result;
1083 }
1084
1085 /*
1086  * check whether PATH is a Maildir style mailbox.
1087  * This is the case if the 3 subdir: new, cur, tmp are existing.
1088  * This functon assumes that entry is an directory
1089  */
1090 static gboolean mh_is_maildir(const gchar *path)
1091 {
1092         return mh_is_maildir_one(path, "new") &&
1093                mh_is_maildir_one(path, "cur") &&
1094                mh_is_maildir_one(path, "tmp");
1095 }
1096
1097 static void mh_scan_tree_recursive(FolderItem *item, GHashTable *pptable)
1098 {
1099         DIR *dp;
1100         struct dirent *d;
1101         struct stat s;
1102         gchar *entry;
1103         gint n_msg = 0;
1104
1105         g_return_if_fail(item != NULL);
1106         g_return_if_fail(item->folder != NULL);
1107
1108         dp = opendir(item->path ? item->path : ".");
1109         if (!dp) {
1110                 FILE_OP_ERROR(item->path ? item->path : ".", "opendir");
1111                 return;
1112         }
1113
1114         debug_print("scanning %s ...\n",
1115                     item->path ? item->path
1116                     : LOCAL_FOLDER(item->folder)->rootpath);
1117         if (item->folder->ui_func)
1118                 item->folder->ui_func(item->folder, item,
1119                                       item->folder->ui_func_data);
1120
1121         while ((d = readdir(dp)) != NULL) {
1122                 if (d->d_name[0] == '.') continue;
1123
1124                 if (item->path)
1125                         entry = g_strconcat(item->path, G_DIR_SEPARATOR_S,
1126                                             d->d_name, NULL);
1127                 else
1128                         entry = g_strdup(d->d_name);
1129
1130                 if (stat(entry, &s) < 0) {
1131                         FILE_OP_ERROR(entry, "stat");
1132                         g_free(entry);
1133                         continue;
1134                 }
1135
1136                 if (S_ISDIR(s.st_mode)) {
1137                         FolderItem *new_item;
1138
1139                         if (mh_is_maildir(entry)) {
1140                                 g_free(entry);
1141                                 continue;
1142                         }
1143
1144                         new_item = folder_item_new(d->d_name, entry);
1145                         folder_item_append(item, new_item);
1146                         if (!item->path) {
1147                                 if (!strcmp(d->d_name, INBOX_DIR)) {
1148                                         new_item->stype = F_INBOX;
1149                                         item->folder->inbox = new_item;
1150                                 } else if (!strcmp(d->d_name, OUTBOX_DIR)) {
1151                                         new_item->stype = F_OUTBOX;
1152                                         item->folder->outbox = new_item;
1153                                 } else if (!strcmp(d->d_name, DRAFT_DIR)) {
1154                                         new_item->stype = F_DRAFT;
1155                                         item->folder->draft = new_item;
1156                                 } else if (!strcmp(d->d_name, QUEUE_DIR)) {
1157                                         new_item->stype = F_QUEUE;
1158                                         item->folder->queue = new_item;
1159                                 } else if (!strcmp(d->d_name, TRASH_DIR)) {
1160                                         new_item->stype = F_TRASH;
1161                                         item->folder->trash = new_item;
1162                                 }
1163                         }
1164                         folder_item_restore_persist_prefs(new_item, pptable);
1165                         mh_scan_tree_recursive(new_item, pptable);
1166                 } else if (to_number(d->d_name) != -1) n_msg++;
1167
1168                 g_free(entry);
1169         }
1170
1171         closedir(dp);
1172
1173         if (item->path) {
1174                 gint new, unread, total, min, max;
1175
1176                 procmsg_get_mark_sum(item->path, &new, &unread, &total,
1177                                      &min, &max, 0);
1178                 if (n_msg > total) {
1179                         new += n_msg - total;
1180                         unread += n_msg - total;
1181                 }
1182                 item->new = new;
1183                 item->unread = unread;
1184                 item->total = n_msg;
1185         }
1186 }
1187
1188 static gboolean mh_rename_folder_func(GNode *node, gpointer data)
1189 {
1190         FolderItem *item = node->data;
1191         gchar **paths = data;
1192         const gchar *oldpath = paths[0];
1193         const gchar *newpath = paths[1];
1194         gchar *base;
1195         gchar *new_itempath;
1196         gint oldpathlen;
1197
1198         oldpathlen = strlen(oldpath);
1199         if (strncmp(oldpath, item->path, oldpathlen) != 0) {
1200                 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
1201                 return TRUE;
1202         }
1203
1204         base = item->path + oldpathlen;
1205         while (*base == G_DIR_SEPARATOR) base++;
1206         if (*base == '\0')
1207                 new_itempath = g_strdup(newpath);
1208         else
1209                 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
1210                                            NULL);
1211         g_free(item->path);
1212         item->path = new_itempath;
1213
1214         return FALSE;
1215 }