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