5aa390371203ed760344d8fce375f4de1badb0ab
[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 (make_dir(dir) < 0) \
797                         return -1; \
798         } \
799 }
800
801 gint mh_create_tree(Folder *folder)
802 {
803         gchar *rootpath;
804
805         g_return_val_if_fail(folder != NULL, -1);
806
807         CHDIR_RETURN_VAL_IF_FAIL(get_home_dir(), -1);
808         rootpath = LOCAL_FOLDER(folder)->rootpath;
809         MAKE_DIR_IF_NOT_EXIST(rootpath);
810         CHDIR_RETURN_VAL_IF_FAIL(rootpath, -1);
811         MAKE_DIR_IF_NOT_EXIST(INBOX_DIR);
812         MAKE_DIR_IF_NOT_EXIST(OUTBOX_DIR);
813         MAKE_DIR_IF_NOT_EXIST(QUEUE_DIR);
814         MAKE_DIR_IF_NOT_EXIST(DRAFT_DIR);
815         MAKE_DIR_IF_NOT_EXIST(TRASH_DIR);
816
817         return 0;
818 }
819
820 #undef MAKE_DIR_IF_NOT_EXIST
821
822 FolderItem *mh_create_folder(Folder *folder, FolderItem *parent,
823                              const gchar *name)
824 {
825         gchar *path;
826         gchar *fullpath;
827         FolderItem *new_item;
828
829         g_return_val_if_fail(folder != NULL, NULL);
830         g_return_val_if_fail(parent != NULL, NULL);
831         g_return_val_if_fail(name != NULL, NULL);
832
833         path = folder_item_get_path(parent);
834         if (!is_dir_exist(path))
835                 make_dir_hier(path);
836
837         fullpath = g_strconcat(path, G_DIR_SEPARATOR_S, name, NULL);
838         g_free(path);
839
840         if (make_dir(fullpath) < 0) {
841                 g_free(fullpath);
842                 return NULL;
843         }
844
845         g_free(fullpath);
846
847         if (parent->path)
848                 path = g_strconcat(parent->path, G_DIR_SEPARATOR_S, name,
849                                    NULL);
850         else
851                 path = g_strdup(name);
852         new_item = folder_item_new(name, path);
853         folder_item_append(parent, new_item);
854         g_free(path);
855
856         return new_item;
857 }
858
859 gint mh_rename_folder(Folder *folder, FolderItem *item, const gchar *name)
860 {
861         gchar *oldpath;
862         gchar *dirname;
863         gchar *newpath;
864         GNode *node;
865         gchar *paths[2];
866
867         g_return_val_if_fail(folder != NULL, -1);
868         g_return_val_if_fail(item != NULL, -1);
869         g_return_val_if_fail(item->path != NULL, -1);
870         g_return_val_if_fail(name != NULL, -1);
871
872         oldpath = folder_item_get_path(item);
873         if (!is_dir_exist(oldpath))
874                 make_dir_hier(oldpath);
875
876         dirname = g_dirname(oldpath);
877         newpath = g_strconcat(dirname, G_DIR_SEPARATOR_S, name, NULL);
878         g_free(dirname);
879
880         if (rename(oldpath, newpath) < 0) {
881                 FILE_OP_ERROR(oldpath, "rename");
882                 g_free(oldpath);
883                 g_free(newpath);
884                 return -1;
885         }
886
887         g_free(oldpath);
888         g_free(newpath);
889
890         if (strchr(item->path, G_DIR_SEPARATOR) != NULL) {
891                 dirname = g_dirname(item->path);
892                 newpath = g_strconcat(dirname, G_DIR_SEPARATOR_S, name, NULL);
893                 g_free(dirname);
894         } else
895                 newpath = g_strdup(name);
896
897         g_free(item->name);
898         item->name = g_strdup(name);
899
900         node = g_node_find(item->folder->node, G_PRE_ORDER, G_TRAVERSE_ALL,
901                            item);
902         paths[0] = g_strdup(item->path);
903         paths[1] = newpath;
904         g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
905                         mh_rename_folder_func, paths);
906
907         g_free(paths[0]);
908         g_free(paths[1]);
909         return 0;
910 }
911
912 gint mh_remove_folder(Folder *folder, FolderItem *item)
913 {
914         gchar *path;
915
916         g_return_val_if_fail(folder != NULL, -1);
917         g_return_val_if_fail(item != NULL, -1);
918         g_return_val_if_fail(item->path != NULL, -1);
919
920         path = folder_item_get_path(item);
921         if (remove_dir_recursive(path) < 0) {
922                 g_warning("can't remove directory `%s'\n", path);
923                 g_free(path);
924                 return -1;
925         }
926
927         g_free(path);
928         folder_item_remove(item);
929         return 0;
930 }
931
932
933 static GSList *mh_get_uncached_msgs(GHashTable *msg_table, FolderItem *item)
934 {
935         gchar *path;
936         DIR *dp;
937         struct dirent *d;
938         struct stat s;
939         GSList *newlist = NULL;
940         GSList *last = NULL;
941         MsgInfo *msginfo;
942         gint n_newmsg = 0;
943         gint num;
944
945         g_return_val_if_fail(item != NULL, NULL);
946
947         path = folder_item_get_path(item);
948         g_return_val_if_fail(path != NULL, NULL);
949         if (change_dir(path) < 0) {
950                 g_free(path);
951                 return NULL;
952         }
953         g_free(path);
954
955         if ((dp = opendir(".")) == NULL) {
956                 FILE_OP_ERROR(item->path, "opendir");
957                 return NULL;
958         }
959
960         debug_print(_("\tSearching uncached messages... "));
961
962         if (msg_table) {
963                 while ((d = readdir(dp)) != NULL) {
964                         if ((num = to_number(d->d_name)) < 0) continue;
965                         if (stat(d->d_name, &s) < 0) {
966                                 FILE_OP_ERROR(d->d_name, "stat");
967                                 continue;
968                         }
969                         if (!S_ISREG(s.st_mode)) continue;
970
971                         msginfo = g_hash_table_lookup
972                                 (msg_table, GUINT_TO_POINTER(num));
973
974                         if (!msginfo) {
975                                 /* not found in the cache (uncached message) */
976                                 msginfo = mh_parse_msg(d->d_name, item);
977                                 if (!msginfo) continue;
978
979                                 if (!newlist)
980                                         last = newlist =
981                                                 g_slist_append(NULL, msginfo);
982                                 else {
983                                         last = g_slist_append(last, msginfo);
984                                         last = last->next;
985                                 }
986                                 n_newmsg++;
987                         }
988                 }
989         } else {
990                 /* discard all previous cache */
991                 while ((d = readdir(dp)) != NULL) {
992                         if (to_number(d->d_name) < 0) continue;
993                         if (stat(d->d_name, &s) < 0) {
994                                 FILE_OP_ERROR(d->d_name, "stat");
995                                 continue;
996                         }
997                         if (!S_ISREG(s.st_mode)) continue;
998
999                         msginfo = mh_parse_msg(d->d_name, item);
1000                         if (!msginfo) continue;
1001
1002                         if (!newlist)
1003                                 last = newlist = g_slist_append(NULL, msginfo);
1004                         else {
1005                                 last = g_slist_append(last, msginfo);
1006                                 last = last->next;
1007                         }
1008                         n_newmsg++;
1009                 }
1010         }
1011
1012         closedir(dp);
1013
1014         if (n_newmsg)
1015                 debug_print(_("%d uncached message(s) found.\n"), n_newmsg);
1016         else
1017                 debug_print(_("done.\n"));
1018
1019         /* sort new messages in numerical order */
1020         if (newlist) {
1021                 debug_print(_("\tSorting uncached messages in numerical order... "));
1022                 newlist = g_slist_sort
1023                         (newlist, (GCompareFunc)procmsg_cmp_msgnum_for_sort);
1024                 debug_print(_("done.\n"));
1025         }
1026
1027         return newlist;
1028 }
1029
1030 static MsgInfo *mh_parse_msg(const gchar *file, FolderItem *item)
1031 {
1032         struct stat s;
1033         MsgInfo *msginfo;
1034         MsgFlags flags;
1035
1036         flags.perm_flags = MSG_NEW|MSG_UNREAD;
1037         flags.tmp_flags = 0;
1038
1039         g_return_val_if_fail(item != NULL, NULL);
1040         g_return_val_if_fail(file != NULL, NULL);
1041
1042         if (item->stype == F_QUEUE) {
1043                 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
1044         } else if (item->stype == F_DRAFT) {
1045                 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
1046         }
1047
1048         msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
1049         if (!msginfo) return NULL;
1050
1051         msginfo->msgnum = atoi(file);
1052         msginfo->folder = item;
1053
1054         if (stat(file, &s) < 0) {
1055                 FILE_OP_ERROR(file, "stat");
1056                 msginfo->size = 0;
1057                 msginfo->mtime = 0;
1058         } else {
1059                 msginfo->size = s.st_size;
1060                 msginfo->mtime = s.st_mtime;
1061         }
1062
1063         return msginfo;
1064 }
1065
1066 static gboolean mh_is_maildir_one(const gchar *path, const gchar *dir)
1067 {
1068         gchar *entry;
1069         gboolean result;
1070
1071         entry = g_strconcat(path, G_DIR_SEPARATOR_S, dir, NULL);
1072         result = is_dir_exist(entry);
1073         g_free(entry);
1074
1075         return result;
1076 }
1077
1078 /*
1079  * check whether PATH is a Maildir style mailbox.
1080  * This is the case if the 3 subdir: new, cur, tmp are existing.
1081  * This functon assumes that entry is an directory
1082  */
1083 static gboolean mh_is_maildir(const gchar *path)
1084 {
1085         return mh_is_maildir_one(path, "new") &&
1086                mh_is_maildir_one(path, "cur") &&
1087                mh_is_maildir_one(path, "tmp");
1088 }
1089
1090 static void mh_scan_tree_recursive(FolderItem *item, GHashTable *pptable)
1091 {
1092         DIR *dp;
1093         struct dirent *d;
1094         struct stat s;
1095         gchar *entry;
1096         gint n_msg = 0;
1097
1098         g_return_if_fail(item != NULL);
1099         g_return_if_fail(item->folder != NULL);
1100
1101         dp = opendir(item->path ? item->path : ".");
1102         if (!dp) {
1103                 FILE_OP_ERROR(item->path ? item->path : ".", "opendir");
1104                 return;
1105         }
1106
1107         debug_print("scanning %s ...\n",
1108                     item->path ? item->path
1109                     : LOCAL_FOLDER(item->folder)->rootpath);
1110         if (item->folder->ui_func)
1111                 item->folder->ui_func(item->folder, item,
1112                                       item->folder->ui_func_data);
1113
1114         while ((d = readdir(dp)) != NULL) {
1115                 if (d->d_name[0] == '.') continue;
1116
1117                 if (item->path)
1118                         entry = g_strconcat(item->path, G_DIR_SEPARATOR_S,
1119                                             d->d_name, NULL);
1120                 else
1121                         entry = g_strdup(d->d_name);
1122
1123                 if (stat(entry, &s) < 0) {
1124                         FILE_OP_ERROR(entry, "stat");
1125                         g_free(entry);
1126                         continue;
1127                 }
1128
1129                 if (S_ISDIR(s.st_mode)) {
1130                         FolderItem *new_item;
1131
1132                         if (mh_is_maildir(entry)) {
1133                                 g_free(entry);
1134                                 continue;
1135                         }
1136
1137                         new_item = folder_item_new(d->d_name, entry);
1138                         folder_item_append(item, new_item);
1139                         if (!item->path) {
1140                                 if (!strcmp(d->d_name, INBOX_DIR)) {
1141                                         new_item->stype = F_INBOX;
1142                                         item->folder->inbox = new_item;
1143                                 } else if (!strcmp(d->d_name, OUTBOX_DIR)) {
1144                                         new_item->stype = F_OUTBOX;
1145                                         item->folder->outbox = new_item;
1146                                 } else if (!strcmp(d->d_name, DRAFT_DIR)) {
1147                                         new_item->stype = F_DRAFT;
1148                                         item->folder->draft = new_item;
1149                                 } else if (!strcmp(d->d_name, QUEUE_DIR)) {
1150                                         new_item->stype = F_QUEUE;
1151                                         item->folder->queue = new_item;
1152                                 } else if (!strcmp(d->d_name, TRASH_DIR)) {
1153                                         new_item->stype = F_TRASH;
1154                                         item->folder->trash = new_item;
1155                                 }
1156                         }
1157                         folder_item_restore_persist_prefs(new_item, pptable);
1158                         mh_scan_tree_recursive(new_item, pptable);
1159                 } else if (to_number(d->d_name) != -1) n_msg++;
1160
1161                 g_free(entry);
1162         }
1163
1164         closedir(dp);
1165
1166         if (item->path) {
1167                 gint new, unread, total, min, max;
1168
1169                 procmsg_get_mark_sum(item->path, &new, &unread, &total,
1170                                      &min, &max, 0);
1171                 if (n_msg > total) {
1172                         new += n_msg - total;
1173                         unread += n_msg - total;
1174                 }
1175                 item->new = new;
1176                 item->unread = unread;
1177                 item->total = n_msg;
1178         }
1179 }
1180
1181 static gboolean mh_rename_folder_func(GNode *node, gpointer data)
1182 {
1183         FolderItem *item = node->data;
1184         gchar **paths = data;
1185         const gchar *oldpath = paths[0];
1186         const gchar *newpath = paths[1];
1187         gchar *base;
1188         gchar *new_itempath;
1189         gint oldpathlen;
1190
1191         oldpathlen = strlen(oldpath);
1192         if (strncmp(oldpath, item->path, oldpathlen) != 0) {
1193                 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
1194                 return TRUE;
1195         }
1196
1197         base = item->path + oldpathlen;
1198         while (*base == G_DIR_SEPARATOR) base++;
1199         if (*base == '\0')
1200                 new_itempath = g_strdup(newpath);
1201         else
1202                 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
1203                                            NULL);
1204         g_free(item->path);
1205         item->path = new_itempath;
1206
1207         return FALSE;
1208 }