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