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