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