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