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