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