2007-07-27 [colin] 2.10.0cvs72
[claws.git] / src / mh.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2007 Hiroyuki Yamamoto and the Claws Mail team
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 3 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, see <http://www.gnu.org/licenses/>.
17  * 
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 <glib/gi18n.h>
28 #include <dirent.h>
29 #include <sys/stat.h>
30 #include <unistd.h>
31 #include <string.h>
32 #include <errno.h>
33 #include <time.h>
34
35 #include "folder.h"
36 #include "mh.h"
37 #include "procmsg.h"
38 #include "procheader.h"
39 #include "utils.h"
40 #include "codeconv.h"
41 #include "statusbar.h"
42 #include "gtkutils.h"
43 #include "timing.h"
44
45 /* Define possible missing constants for Windows. */
46 #ifdef G_OS_WIN32
47 # ifndef S_IRGRP
48 # define S_IRGRP 0
49 # define S_IWGRP 0
50 # endif
51 # ifndef S_IROTH
52 # define S_IROTH 0
53 # define S_IWOTH 0
54 # endif
55 #endif
56
57
58 static void     mh_folder_init          (Folder         *folder,
59                                          const gchar    *name,
60                                          const gchar    *path);
61
62 static Folder   *mh_folder_new          (const gchar    *name,
63                                          const gchar    *path);
64 static void     mh_folder_destroy       (Folder         *folder);
65 static gchar   *mh_fetch_msg            (Folder         *folder,
66                                          FolderItem     *item,
67                                          gint            num);
68 static MsgInfo *mh_get_msginfo          (Folder         *folder,
69                                          FolderItem     *item,
70                                          gint            num);
71 static gint     mh_add_msg              (Folder         *folder,
72                                          FolderItem     *dest,
73                                          const gchar    *file,
74                                          MsgFlags       *flags);
75 static gint     mh_add_msgs             (Folder         *folder,
76                                          FolderItem     *dest,
77                                          GSList         *file_list,
78                                          GRelation      *relation);
79 static gint     mh_copy_msg             (Folder         *folder,
80                                          FolderItem     *dest,
81                                          MsgInfo        *msginfo);
82 static gint     mh_copy_msgs            (Folder         *folder, 
83                                          FolderItem     *dest, 
84                                          MsgInfoList    *msglist, 
85                                          GRelation      *relation);
86 static gint     mh_remove_msg           (Folder         *folder,
87                                          FolderItem     *item,
88                                          gint            num);
89 static gint     mh_remove_msgs          (Folder         *folder, 
90                                          FolderItem     *item, 
91                                          MsgInfoList    *msglist, 
92                                          GRelation      *relation);
93 static gint     mh_remove_all_msg       (Folder         *folder,
94                                          FolderItem     *item);
95 static gboolean mh_is_msg_changed       (Folder         *folder,
96                                          FolderItem     *item,
97                                          MsgInfo        *msginfo);
98
99 static gint     mh_get_num_list         (Folder         *folder,
100                                          FolderItem     *item, 
101                                          GSList         **list, 
102                                          gboolean       *old_uids_valid);
103 static gint     mh_scan_tree            (Folder         *folder);
104
105 static gint    mh_create_tree           (Folder         *folder);
106 static FolderItem *mh_create_folder     (Folder         *folder,
107                                          FolderItem     *parent,
108                                          const gchar    *name);
109 static gint    mh_rename_folder         (Folder         *folder,
110                                          FolderItem     *item,
111                                          const gchar    *name);
112 static gint    mh_remove_folder         (Folder         *folder,
113                                          FolderItem     *item);
114
115 static gchar   *mh_get_new_msg_filename         (FolderItem     *dest);
116
117 static MsgInfo *mh_parse_msg                    (const gchar    *file,
118                                                  FolderItem     *item);
119 static void     mh_remove_missing_folder_items  (Folder         *folder);
120 static gchar    *mh_filename_from_utf8          (const gchar    *path);
121 static gchar    *mh_filename_to_utf8            (const gchar    *path);
122 static void     mh_scan_tree_recursive          (FolderItem     *item);
123
124 static gboolean mh_rename_folder_func           (GNode          *node,
125                                                  gpointer        data);
126 static gchar   *mh_item_get_path                (Folder *folder, 
127                                                  FolderItem *item);
128
129 static gboolean mh_scan_required        (Folder         *folder,
130                                          FolderItem     *item);
131 static int mh_item_close                (Folder         *folder,
132                                          FolderItem     *item);
133 #if 0
134 static gint mh_get_flags                (Folder *folder, FolderItem *item,
135                                          MsgInfoList *msginfo_list, GRelation *msgflags);
136 #endif
137 static void mh_write_sequences          (FolderItem     *item, gboolean remove_unseen);
138
139 static FolderClass mh_class;
140
141 FolderClass *mh_get_class(void)
142 {
143         if (mh_class.idstr == NULL) {
144                 mh_class.type = F_MH;
145                 mh_class.idstr = "mh";
146                 mh_class.uistr = "MH";
147                 
148                 /* Folder functions */
149                 mh_class.new_folder = mh_folder_new;
150                 mh_class.destroy_folder = mh_folder_destroy;
151                 mh_class.set_xml = folder_local_set_xml;
152                 mh_class.get_xml = folder_local_get_xml;
153                 mh_class.scan_tree = mh_scan_tree;
154                 mh_class.create_tree = mh_create_tree;
155
156                 /* FolderItem functions */
157                 mh_class.item_get_path = mh_item_get_path;
158                 mh_class.create_folder = mh_create_folder;
159                 mh_class.rename_folder = mh_rename_folder;
160                 mh_class.remove_folder = mh_remove_folder;
161                 mh_class.get_num_list = mh_get_num_list;
162                 mh_class.scan_required = mh_scan_required;
163                 mh_class.close = mh_item_close;
164                 mh_class.get_flags = NULL; /*mh_get_flags */;
165
166                 /* Message functions */
167                 mh_class.get_msginfo = mh_get_msginfo;
168                 mh_class.fetch_msg = mh_fetch_msg;
169                 mh_class.add_msg = mh_add_msg;
170                 mh_class.add_msgs = mh_add_msgs;
171                 mh_class.copy_msg = mh_copy_msg;
172                 mh_class.copy_msgs = mh_copy_msgs;
173                 mh_class.remove_msg = mh_remove_msg;
174                 mh_class.remove_msgs = mh_remove_msgs;
175                 mh_class.remove_all_msg = mh_remove_all_msg;
176                 mh_class.is_msg_changed = mh_is_msg_changed;
177         }
178
179         return &mh_class;
180 }
181
182 static Folder *mh_folder_new(const gchar *name, const gchar *path)
183 {
184         Folder *folder;
185
186         folder = (Folder *)g_new0(MHFolder, 1);
187         folder->klass = &mh_class;
188         mh_folder_init(folder, name, path);
189
190         return folder;
191 }
192
193 static void mh_folder_destroy(Folder *folder)
194 {
195         folder_local_folder_destroy(LOCAL_FOLDER(folder));
196 }
197
198 static void mh_folder_init(Folder *folder, const gchar *name, const gchar *path)
199 {
200         folder_local_folder_init(folder, name, path);
201
202 }
203
204 gboolean mh_scan_required(Folder *folder, FolderItem *item)
205 {
206         gchar *path;
207         struct stat s;
208
209         path = folder_item_get_path(item);
210         g_return_val_if_fail(path != NULL, FALSE);
211
212         if (stat(path, &s) < 0) {
213                 FILE_OP_ERROR(path, "stat");
214                 g_free(path);
215                 return FALSE;
216         }
217
218         if ((s.st_mtime > item->mtime) &&
219                 (s.st_mtime - 3600 != item->mtime)) {
220                 debug_print("MH scan required, folder updated: %s (%ld > %ld)\n",
221                             path,
222                             (long int) s.st_mtime,
223                             (long int) item->mtime);
224                 g_free(path);
225                 return TRUE;
226         }
227
228         debug_print("MH scan not required: %s (%ld <= %ld)\n",
229                     path,
230                     (long int) s.st_mtime,
231                     (long int) item->mtime);
232         g_free(path);
233         return FALSE;
234 }
235
236 static void mh_get_last_num(Folder *folder, FolderItem *item)
237 {
238         gchar *path;
239         DIR *dp;
240         struct dirent *d;
241         gint max = 0;
242         gint num;
243
244         g_return_if_fail(item != NULL);
245
246         debug_print("mh_get_last_num(): Scanning %s ...\n", item->path);
247
248         path = folder_item_get_path(item);
249         g_return_if_fail(path != NULL);
250         if (change_dir(path) < 0) {
251                 g_free(path);
252                 return;
253         }
254         g_free(path);
255
256         if ((dp = opendir(".")) == NULL) {
257                 FILE_OP_ERROR(item->path, "opendir");
258                 return;
259         }
260
261         while ((d = readdir(dp)) != NULL) {
262                 if ((num = to_number(d->d_name)) > 0 &&
263                     dirent_is_regular_file(d)) {
264                         if (max < num)
265                                 max = num;
266                 }
267         }
268         closedir(dp);
269
270         debug_print("Last number in dir %s = %d\n", item->path, max);
271         item->last_num = max;
272 }
273
274 gint mh_get_num_list(Folder *folder, FolderItem *item, GSList **list, gboolean *old_uids_valid)
275 {
276
277         gchar *path;
278         DIR *dp;
279         struct dirent *d;
280         gint num, nummsgs = 0;
281
282         g_return_val_if_fail(item != NULL, -1);
283
284         debug_print("mh_get_num_list(): Scanning %s ...\n", item->path);
285
286         *old_uids_valid = TRUE;
287
288         path = folder_item_get_path(item);
289         g_return_val_if_fail(path != NULL, -1);
290         if (change_dir(path) < 0) {
291                 g_free(path);
292                 return -1;
293         }
294         g_free(path);
295
296         if ((dp = opendir(".")) == NULL) {
297                 FILE_OP_ERROR(item->path, "opendir");
298                 return -1;
299         }
300
301         while ((d = readdir(dp)) != NULL) {
302                 if ((num = to_number(d->d_name)) > 0) {
303                         *list = g_slist_prepend(*list, GINT_TO_POINTER(num));
304                         nummsgs++;
305                 }
306         }
307         closedir(dp);
308
309         mh_set_mtime(item);
310         return nummsgs;
311 }
312
313 static gchar *mh_fetch_msg(Folder *folder, FolderItem *item, gint num)
314 {
315         gchar *path;
316         gchar *file;
317
318         g_return_val_if_fail(item != NULL, NULL);
319         g_return_val_if_fail(num > 0, NULL);
320
321         path = folder_item_get_path(item);
322         file = g_strconcat(path, G_DIR_SEPARATOR_S, itos(num), NULL);
323
324         if (!is_file_exist(file)) {
325                 g_free(file);
326                 g_free(path);
327                 return NULL;
328         }
329         g_free(path);
330         return file;
331 }
332
333 static MsgInfo *mh_get_msginfo(Folder *folder, FolderItem *item, gint num)
334 {
335         MsgInfo *msginfo;
336         gchar *file;
337
338         g_return_val_if_fail(item != NULL, NULL);
339         if (num <= 0)
340                 return NULL;
341
342         file = mh_fetch_msg(folder, item, num);
343         if (!file) return NULL;
344
345         msginfo = mh_parse_msg(file, item);
346         if (msginfo)
347                 msginfo->msgnum = num;
348
349         g_free(file);
350
351         return msginfo;
352 }
353
354 static gchar *mh_get_new_msg_filename(FolderItem *dest)
355 {
356         gchar *destfile;
357         gchar *destpath;
358
359         destpath = folder_item_get_path(dest);
360         g_return_val_if_fail(destpath != NULL, NULL);
361
362         if (!is_dir_exist(destpath))
363                 make_dir_hier(destpath);
364
365         for (;;) {
366                 destfile = g_strdup_printf("%s%c%d", destpath, G_DIR_SEPARATOR,
367                                            dest->last_num + 1);
368                 if (is_file_entry_exist(destfile)) {
369                         dest->last_num++;
370                         g_free(destfile);
371                 } else
372                         break;
373         }
374
375         g_free(destpath);
376
377         return destfile;
378 }
379
380 static gint mh_add_msg(Folder *folder, FolderItem *dest, const gchar *file, MsgFlags *flags)
381 {
382         gint ret;
383         GSList file_list;
384         MsgFileInfo fileinfo;
385
386         g_return_val_if_fail(file != NULL, -1);
387
388         fileinfo.msginfo = NULL;
389         fileinfo.file = (gchar *)file;
390         fileinfo.flags = flags;
391         file_list.data = &fileinfo;
392         file_list.next = NULL;
393
394         ret = mh_add_msgs(folder, dest, &file_list, NULL);
395         return ret;
396
397  
398 static gint mh_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list, 
399                  GRelation *relation)
400
401         gchar *destfile;
402         GSList *cur;
403         MsgFileInfo *fileinfo;
404
405         g_return_val_if_fail(dest != NULL, -1);
406         g_return_val_if_fail(file_list != NULL, -1);
407
408         if (dest->last_num < 0) {
409                 mh_get_last_num(folder, dest);
410                 if (dest->last_num < 0) return -1;
411         }
412
413         for (cur = file_list; cur != NULL; cur = cur->next) {
414                 fileinfo = (MsgFileInfo *)cur->data;
415
416                 destfile = mh_get_new_msg_filename(dest);
417                 if (destfile == NULL) return -1;
418
419 #ifdef G_OS_UNIX
420                 if (link(fileinfo->file, destfile) < 0) {
421 #endif
422                         if (copy_file(fileinfo->file, destfile, TRUE) < 0) {
423                                 g_warning(_("can't copy message %s to %s\n"),
424                                           fileinfo->file, destfile);
425                                 g_free(destfile);
426                                 return -1;
427                         }
428 #ifdef G_OS_UNIX
429                 }
430 #endif
431
432                 if (relation != NULL)
433                         g_relation_insert(relation, fileinfo, GINT_TO_POINTER(dest->last_num + 1));
434                 g_free(destfile);
435                 dest->last_num++;
436         }
437         mh_write_sequences(dest, TRUE);
438         return dest->last_num;
439 }
440
441 static gint mh_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
442 {
443         GSList msglist;
444
445         g_return_val_if_fail(msginfo != NULL, -1);
446
447         msglist.data = msginfo;
448         msglist.next = NULL;
449
450         return mh_copy_msgs(folder, dest, &msglist, NULL);      
451 }
452
453 static gint mh_copy_msgs(Folder *folder, FolderItem *dest, MsgInfoList *msglist, 
454                          GRelation *relation)
455 {
456         gboolean dest_need_scan = FALSE;
457         gboolean src_need_scan = FALSE;
458         FolderItem *src = NULL;
459         gchar *srcfile;
460         gchar *destfile;
461         gint filemode = 0;
462         FolderItemPrefs *prefs;
463         MsgInfo *msginfo = NULL;
464         MsgInfoList *cur = NULL;
465         gint curnum = 0, total = 0;
466         gchar *srcpath = NULL;
467         gboolean full_fetch = FALSE;
468         time_t last_dest_mtime = (time_t)0;
469         time_t last_src_mtime = (time_t)0;
470
471         g_return_val_if_fail(dest != NULL, -1);
472         g_return_val_if_fail(msglist != NULL, -1);
473         
474         msginfo = (MsgInfo *)msglist->data;
475
476         g_return_val_if_fail(msginfo != NULL, -1);
477
478         if (msginfo->folder == dest) {
479                 g_warning("the src folder is identical to the dest.\n");
480                 return -1;
481         }
482
483         if (msginfo->folder->folder != dest->folder)
484                 full_fetch = TRUE;
485         
486         if (FOLDER_TYPE(msginfo->folder->folder) == F_MH) {
487                 src = msginfo->folder;
488         }
489
490         if (dest->last_num < 0) {
491                 mh_get_last_num(folder, dest);
492                 if (dest->last_num < 0) return -1;
493         }
494
495         prefs = dest->prefs;
496
497         srcpath = folder_item_get_path(msginfo->folder);
498
499         dest_need_scan = mh_scan_required(dest->folder, dest);
500         last_dest_mtime = dest->mtime;
501
502         if (src) {
503                 src_need_scan = mh_scan_required(src->folder, src);
504                 last_src_mtime = src->mtime;
505         }
506
507         total = g_slist_length(msglist);
508         if (total > 100) {
509                 if (MSG_IS_MOVE(msginfo->flags))
510                         statusbar_print_all(_("Moving messages..."));
511                 else
512                         statusbar_print_all(_("Copying messages..."));
513         }
514         for (cur = msglist; cur; cur = cur->next) {
515                 msginfo = (MsgInfo *)cur->data;
516                 if (!msginfo) {
517                         goto err_reset_status;
518                 }
519                 if (!full_fetch) {
520                         srcfile = g_strconcat(srcpath, 
521                                 G_DIR_SEPARATOR_S, 
522                                 itos(msginfo->msgnum), NULL);
523                 } else {
524                         srcfile = procmsg_get_message_file(msginfo);
525                 }
526                 if (!srcfile) {
527                         goto err_reset_status;
528                 }
529                 destfile = mh_get_new_msg_filename(dest);
530                 if (!destfile) {
531                         g_free(srcfile);
532                         goto err_reset_status;
533                 }
534
535                 if (total > 100) {
536                         statusbar_progress_all(curnum, total, 100);
537                         if (curnum % 100 == 0)
538                                 GTK_EVENTS_FLUSH();
539                         curnum++;
540                 }
541
542                 debug_print("Copying message %s%c%d to %s ...\n",
543                             msginfo->folder->path, G_DIR_SEPARATOR,
544                             msginfo->msgnum, dest->path);
545
546
547                 if (MSG_IS_MOVE(msginfo->flags)) {
548                         msginfo->flags.tmp_flags &= ~MSG_MOVE_DONE;
549                         if (move_file(srcfile, destfile, TRUE) < 0) {
550                                 FILE_OP_ERROR(srcfile, "move");
551                                 if (copy_file(srcfile, destfile, TRUE) < 0) {
552                                         FILE_OP_ERROR(srcfile, "copy");
553                                         g_free(srcfile);
554                                         g_free(destfile);
555                                         goto err_reset_status;
556                                 }
557                         } else {
558                                 /* say unlinking's not necessary */
559                                 msginfo->flags.tmp_flags |= MSG_MOVE_DONE;
560                         }
561                 } else if (copy_file(srcfile, destfile, TRUE) < 0) {
562                         FILE_OP_ERROR(srcfile, "copy");
563                         g_free(srcfile);
564                         g_free(destfile);
565                         goto err_reset_status;
566                 } 
567                 if (prefs && prefs->enable_folder_chmod && prefs->folder_chmod) {
568                         if (chmod(destfile, prefs->folder_chmod) < 0)
569                                 FILE_OP_ERROR(destfile, "chmod");
570
571                         /* for mark file */
572                         filemode = prefs->folder_chmod;
573                         if (filemode & S_IRGRP) filemode |= S_IWGRP;
574                         if (filemode & S_IROTH) filemode |= S_IWOTH;
575                 }
576                 if (relation)
577                         g_relation_insert(relation, msginfo, GINT_TO_POINTER(dest->last_num+1));
578                 g_free(srcfile);
579                 g_free(destfile);
580                 dest->last_num++;
581         }
582
583         g_free(srcpath);
584         mh_write_sequences(dest, TRUE);
585
586         if (dest->mtime == last_dest_mtime && !dest_need_scan) {
587                 mh_set_mtime(dest);
588         }
589
590         if (src && src->mtime == last_src_mtime && !src_need_scan) {
591                 mh_set_mtime(src);
592         }
593
594         if (total > 100) {
595                 statusbar_progress_all(0,0,0);
596                 statusbar_pop_all();
597         }
598         return dest->last_num;
599 err_reset_status:
600         g_free(srcpath);
601         mh_write_sequences(dest, TRUE);
602         if (total > 100) {
603                 statusbar_progress_all(0,0,0);
604                 statusbar_pop_all();
605         }
606         return -1;
607
608 }
609
610 static gint mh_remove_msg(Folder *folder, FolderItem *item, gint num)
611 {
612         gboolean need_scan = FALSE;
613         time_t last_mtime = (time_t)0;
614         gchar *file;
615
616         g_return_val_if_fail(item != NULL, -1);
617
618         file = mh_fetch_msg(folder, item, num);
619         g_return_val_if_fail(file != NULL, -1);
620
621         need_scan = mh_scan_required(folder, item);
622         last_mtime = item->mtime;
623
624         if (g_unlink(file) < 0) {
625                 FILE_OP_ERROR(file, "unlink");
626                 g_free(file);
627                 return -1;
628         }
629
630         if (item->mtime == last_mtime && !need_scan) {
631                 mh_set_mtime(item);
632         }
633         g_free(file);
634         return 0;
635 }
636
637 static gint mh_remove_msgs(Folder *folder, FolderItem *item, 
638                     MsgInfoList *msglist, GRelation *relation)
639 {
640         gboolean need_scan = FALSE;
641         gchar *path, *file;
642         time_t last_mtime = (time_t)0;
643         MsgInfoList *cur;
644         gint total = 0, curnum = 0;
645
646         g_return_val_if_fail(item != NULL, -1);
647
648         path = folder_item_get_path(item);
649         
650         need_scan = mh_scan_required(folder, item);
651         last_mtime = item->mtime;
652
653         total = g_slist_length(msglist);
654         if (total > 100) {
655                 statusbar_print_all(_("Deleting messages..."));
656         }
657
658         for (cur = msglist; cur; cur = cur->next) {
659                 MsgInfo *msginfo = (MsgInfo *)cur->data;
660                 if (msginfo == NULL)
661                         continue;
662                 if (MSG_IS_MOVE(msginfo->flags) && MSG_IS_MOVE_DONE(msginfo->flags)) {
663                         msginfo->flags.tmp_flags &= ~MSG_MOVE_DONE;
664                         continue;
665                 }
666                 if (total > 100) {
667                         statusbar_progress_all(curnum, total, 100);
668                         if (curnum % 100 == 0)
669                                 GTK_EVENTS_FLUSH();
670                         curnum++;
671                 }
672
673                 file = g_strconcat(path, G_DIR_SEPARATOR_S, itos(msginfo->msgnum), NULL);
674                 if (file == NULL)
675                         continue;
676                 
677                 if (g_unlink(file) < 0) {
678                         g_free(file);
679                         continue;
680                 }
681                 
682                 g_free(file);
683         }
684
685         if (total > 100) {
686                 statusbar_progress_all(0,0,0);
687                 statusbar_pop_all();
688         }
689         if (item->mtime == last_mtime && !need_scan) {
690                 mh_set_mtime(item);
691         }
692
693         g_free(path);
694         return 0;
695 }
696
697 static gint mh_remove_all_msg(Folder *folder, FolderItem *item)
698 {
699         gchar *path;
700         gint val;
701
702         g_return_val_if_fail(item != NULL, -1);
703
704         path = folder_item_get_path(item);
705         g_return_val_if_fail(path != NULL, -1);
706         val = remove_all_numbered_files(path);
707         g_free(path);
708
709         mh_write_sequences(item, TRUE);
710
711         return val;
712 }
713
714 static gboolean mh_is_msg_changed(Folder *folder, FolderItem *item,
715                                   MsgInfo *msginfo)
716 {
717         struct stat s;
718
719         if (g_stat(itos(msginfo->msgnum), &s) < 0 ||
720             msginfo->size  != s.st_size || (
721                 (msginfo->mtime - s.st_mtime != 0) &&
722                 (msginfo->mtime - s.st_mtime != 3600) &&
723                 (msginfo->mtime - s.st_mtime != -3600)))
724                 return TRUE;
725
726         return FALSE;
727 }
728
729 static gint mh_scan_tree(Folder *folder)
730 {
731         FolderItem *item;
732         gchar *rootpath;
733
734         g_return_val_if_fail(folder != NULL, -1);
735
736         if (!folder->node) {
737                 item = folder_item_new(folder, folder->name, NULL);
738                 item->folder = folder;
739                 folder->node = item->node = g_node_new(item);
740         } else
741                 item = FOLDER_ITEM(folder->node->data);
742
743         rootpath = folder_item_get_path(item);
744         if (change_dir(rootpath) < 0) {
745                 g_free(rootpath);
746                 return -1;
747         }
748         g_free(rootpath);
749
750         mh_create_tree(folder);
751         mh_remove_missing_folder_items(folder);
752         mh_scan_tree_recursive(item);
753
754         return 0;
755 }
756
757 #define MAKE_DIR_IF_NOT_EXIST(dir) \
758 { \
759         if (!is_dir_exist(dir)) { \
760                 if (is_file_exist(dir)) { \
761                         g_warning("File `%s' already exists.\n" \
762                                     "Can't create folder.", dir); \
763                         return -1; \
764                 } \
765                 if (make_dir_hier(dir) < 0) \
766                         return -1; \
767         } \
768 }
769
770 static gint mh_create_tree(Folder *folder)
771 {
772         gchar *rootpath;
773
774         g_return_val_if_fail(folder != NULL, -1);
775
776         CHDIR_RETURN_VAL_IF_FAIL(get_mail_base_dir(), -1);
777         rootpath = LOCAL_FOLDER(folder)->rootpath;
778         MAKE_DIR_IF_NOT_EXIST(rootpath);
779         CHDIR_RETURN_VAL_IF_FAIL(rootpath, -1);
780         MAKE_DIR_IF_NOT_EXIST(INBOX_DIR);
781         MAKE_DIR_IF_NOT_EXIST(OUTBOX_DIR);
782         MAKE_DIR_IF_NOT_EXIST(QUEUE_DIR);
783         MAKE_DIR_IF_NOT_EXIST(DRAFT_DIR);
784         MAKE_DIR_IF_NOT_EXIST(TRASH_DIR);
785
786         return 0;
787 }
788
789 #undef MAKE_DIR_IF_NOT_EXIST
790
791 static gchar *mh_item_get_path(Folder *folder, FolderItem *item)
792 {
793         gchar *folder_path, *path;
794         gchar *real_path;
795         g_return_val_if_fail(folder != NULL, NULL);
796         g_return_val_if_fail(item != NULL, NULL);
797
798         folder_path = g_strdup(LOCAL_FOLDER(folder)->rootpath);
799         g_return_val_if_fail(folder_path != NULL, NULL);
800
801         /* FIXME: [W32] The code below does not correctly merge
802            relative filenames; there should be a function to handle
803            this.  */
804         if ( !is_relative_filename (folder_path) ) {
805                 if (item->path)
806                         path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
807                                            item->path, NULL);
808                 else
809                         path = g_strdup(folder_path);
810         } else {
811                 if (item->path)
812                         path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
813                                            folder_path, G_DIR_SEPARATOR_S,
814                                            item->path, NULL);
815                 else
816                         path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
817                                            folder_path, NULL);
818         }
819         g_free(folder_path);
820         real_path = mh_filename_from_utf8(path);
821         if (!is_dir_exist(real_path) && is_dir_exist(path)) {
822                 /* mmh, older version did put utf8 filenames instead of
823                  * the correct encoding */
824                 rename(path, real_path);
825                 folder_item_scan(item);
826         }
827
828         g_free(path);
829         return real_path;
830 }
831
832 static FolderItem *mh_create_folder(Folder *folder, FolderItem *parent,
833                                     const gchar *name)
834 {
835         gchar *path, *real_name;
836         gchar *fullpath;
837         FolderItem *new_item;
838         gchar *mh_sequences_filename;
839         FILE *mh_sequences_file;
840
841         g_return_val_if_fail(folder != NULL, NULL);
842         g_return_val_if_fail(parent != NULL, NULL);
843         g_return_val_if_fail(name != NULL, NULL);
844
845         path = folder_item_get_path(parent);
846         if (!is_dir_exist(path)) 
847                 if (make_dir_hier(path) != 0)
848                         return NULL;
849                 
850         real_name = mh_filename_from_utf8(name);
851         fullpath = g_strconcat(path, G_DIR_SEPARATOR_S, real_name, NULL);
852         g_free(real_name);
853         g_free(path);
854
855         if (make_dir(fullpath) < 0) {
856                 g_free(fullpath);
857                 return NULL;
858         }
859
860         g_free(fullpath);
861
862         if (parent->path)
863                 path = g_strconcat(parent->path, G_DIR_SEPARATOR_S, name,
864                                    NULL);
865         else
866                 path = g_strdup(name);
867         new_item = folder_item_new(folder, name, path);
868         folder_item_append(parent, new_item);
869
870         g_free(path);
871
872         path = folder_item_get_path(new_item);
873         mh_sequences_filename = g_strconcat(path, G_DIR_SEPARATOR_S,
874                                             ".mh_sequences", NULL);
875         if ((mh_sequences_file = g_fopen(mh_sequences_filename, "a+b")) != NULL) {
876                 fclose(mh_sequences_file);
877         }
878         g_free(mh_sequences_filename);
879         g_free(path);
880
881         return new_item;
882 }
883
884 static gint mh_rename_folder(Folder *folder, FolderItem *item,
885                              const gchar *name)
886 {
887         gchar *real_name;
888         gchar *oldpath;
889         gchar *dirname;
890         gchar *newpath, *utf8newpath;
891         gchar *paths[2];
892
893         g_return_val_if_fail(folder != NULL, -1);
894         g_return_val_if_fail(item != NULL, -1);
895         g_return_val_if_fail(item->path != NULL, -1);
896         g_return_val_if_fail(name != NULL, -1);
897
898         oldpath = folder_item_get_path(item);
899         if (!is_dir_exist(oldpath))
900                 make_dir_hier(oldpath);
901
902         dirname = g_path_get_dirname(oldpath);
903         real_name = mh_filename_from_utf8(name);
904         newpath = g_strconcat(dirname, G_DIR_SEPARATOR_S, real_name, NULL);
905         g_free(real_name);
906
907         if (g_rename(oldpath, newpath) < 0) {
908                 FILE_OP_ERROR(oldpath, "rename");
909                 g_free(oldpath);
910                 g_free(newpath);
911                 return -1;
912         }
913
914         g_free(oldpath);
915         g_free(newpath);
916
917         if (strchr(item->path, G_DIR_SEPARATOR) != NULL) {
918                 dirname = g_path_get_dirname(item->path);
919                 utf8newpath = g_strconcat(dirname, G_DIR_SEPARATOR_S,
920                                           name, NULL);
921                 g_free(dirname);
922         } else
923                 utf8newpath = g_strdup(name);
924
925         g_free(item->name);
926         item->name = g_strdup(name);
927
928         paths[0] = g_strdup(item->path);
929         paths[1] = utf8newpath;
930         g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
931                         mh_rename_folder_func, paths);
932
933         g_free(paths[0]);
934         g_free(paths[1]);
935         return 0;
936 }
937
938 static gint mh_remove_folder(Folder *folder, FolderItem *item)
939 {
940         gchar *path;
941
942         g_return_val_if_fail(folder != NULL, -1);
943         g_return_val_if_fail(item != NULL, -1);
944         g_return_val_if_fail(item->path != NULL, -1);
945
946         path = folder_item_get_path(item);
947         if (remove_dir_recursive(path) < 0) {
948                 g_warning("can't remove directory `%s'\n", path);
949                 g_free(path);
950                 return -1;
951         }
952
953         g_free(path);
954         folder_item_remove(item);
955         return 0;
956 }
957
958 static MsgInfo *mh_parse_msg(const gchar *file, FolderItem *item)
959 {
960         MsgInfo *msginfo;
961         MsgFlags flags;
962
963         g_return_val_if_fail(item != NULL, NULL);
964         g_return_val_if_fail(file != NULL, NULL);
965
966         flags.perm_flags = MSG_NEW|MSG_UNREAD;
967         flags.tmp_flags = 0;
968
969         if (folder_has_parent_of_type(item, F_QUEUE)) {
970                 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
971         } else if (folder_has_parent_of_type(item, F_DRAFT)) {
972                 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
973         }
974
975         msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
976         if (!msginfo) return NULL;
977
978         msginfo->msgnum = atoi(file);
979         msginfo->folder = item;
980
981         return msginfo;
982 }
983
984 static gboolean mh_remove_missing_folder_items_func(GNode *node, gpointer data)
985 {
986         FolderItem *item;
987         gchar *path;
988
989         g_return_val_if_fail(node->data != NULL, FALSE);
990
991         if (G_NODE_IS_ROOT(node))
992                 return FALSE;
993
994         item = FOLDER_ITEM(node->data);
995
996         path = folder_item_get_path(item);
997         if (!is_dir_exist(path)) {
998                 debug_print("folder '%s' not found. removing...\n", path);
999                 folder_item_remove(item);
1000         }
1001         g_free(path);
1002
1003         return FALSE;
1004 }
1005
1006 static void mh_remove_missing_folder_items(Folder *folder)
1007 {
1008         g_return_if_fail(folder != NULL);
1009
1010         debug_print("searching missing folders...\n");
1011
1012         g_node_traverse(folder->node, G_POST_ORDER, G_TRAVERSE_ALL, -1,
1013                         mh_remove_missing_folder_items_func, folder);
1014 }
1015
1016 static void mh_scan_tree_recursive(FolderItem *item)
1017 {
1018         Folder *folder;
1019 #ifdef G_OS_WIN32
1020         GDir *dir;
1021 #else
1022         DIR *dp;
1023         struct dirent *d;
1024 #endif
1025         const gchar *dir_name;
1026         struct stat s;
1027         gchar *real_path, *entry, *utf8entry, *utf8name;
1028         gint n_msg = 0;
1029
1030         g_return_if_fail(item != NULL);
1031         g_return_if_fail(item->folder != NULL);
1032
1033         folder = item->folder;
1034
1035         real_path = item->path ? mh_filename_from_utf8(item->path) : g_strdup(".");
1036 #ifdef G_OS_WIN32
1037         dir = g_dir_open(real_path, 0, NULL);
1038         if (!dir) {
1039                 g_warning("failed to open directory: %s\n", real_path);
1040                 g_free(real_path);
1041                 return;
1042         }
1043 #else
1044         dp = opendir(real_path);
1045         if (!dp) {
1046                 FILE_OP_ERROR(real_path, "opendir");
1047                 return;
1048         }
1049 #endif
1050         g_free(real_path);
1051
1052         debug_print("scanning %s ...\n",
1053                     item->path ? item->path
1054                     : LOCAL_FOLDER(item->folder)->rootpath);
1055         if (folder->ui_func)
1056                 folder->ui_func(folder, item, folder->ui_func_data);
1057
1058 #ifdef G_OS_WIN32
1059         while ((dir_name = g_dir_read_name(dir)) != NULL) {
1060 #else
1061         while ((d = readdir(dp)) != NULL) {
1062                 dir_name = d->d_name;
1063 #endif
1064                 if (dir_name[0] == '.') continue;
1065
1066                 utf8name = mh_filename_to_utf8(dir_name);
1067                 if (item->path)
1068                         utf8entry = g_strconcat(item->path, G_DIR_SEPARATOR_S,
1069                                                 utf8name, NULL);
1070                 else
1071                         utf8entry = g_strdup(utf8name);
1072                 entry = mh_filename_from_utf8(utf8entry);
1073
1074                 if (
1075 #if !defined(G_OS_WIN32) && !defined(MAEMO) && defined(HAVE_DIRENT_D_TYPE)
1076                         d->d_type == DT_DIR ||
1077                         (d->d_type == DT_UNKNOWN &&
1078 #endif
1079                         g_stat(entry, &s) == 0 && S_ISDIR(s.st_mode)
1080 #if !defined(G_OS_WIN32) && !defined(MAEMO) && defined(HAVE_DIRENT_D_TYPE)
1081                         )
1082 #endif
1083                    ) {
1084                         FolderItem *new_item = NULL;
1085                         GNode *node;
1086
1087                         node = item->node;
1088                         for (node = node->children; node != NULL; node = node->next) {
1089                                 FolderItem *cur_item = FOLDER_ITEM(node->data);
1090                                 gchar *curpath = mh_filename_from_utf8(cur_item->path);
1091                                 if (!strcmp2(curpath, entry)) {
1092                                         new_item = cur_item;
1093                                         g_free(curpath);
1094                                         break;
1095                                 }
1096                                 g_free(curpath);
1097                         }
1098                         if (!new_item) {
1099                                 debug_print("new folder '%s' found.\n", entry);
1100                                 new_item = folder_item_new(folder, utf8name, utf8entry);
1101                                 folder_item_append(item, new_item);
1102                         }
1103
1104                         if (!item->path) {
1105                                 if (!folder->inbox &&
1106                                     !strcmp(dir_name, INBOX_DIR)) {
1107                                         new_item->stype = F_INBOX;
1108                                         folder->inbox = new_item;
1109                                 } else if (!folder->outbox &&
1110                                            !strcmp(dir_name, OUTBOX_DIR)) {
1111                                         new_item->stype = F_OUTBOX;
1112                                         folder->outbox = new_item;
1113                                 } else if (!folder->draft &&
1114                                            !strcmp(dir_name, DRAFT_DIR)) {
1115                                         new_item->stype = F_DRAFT;
1116                                         folder->draft = new_item;
1117                                 } else if (!folder->queue &&
1118                                            !strcmp(dir_name, QUEUE_DIR)) {
1119                                         new_item->stype = F_QUEUE;
1120                                         folder->queue = new_item;
1121                                 } else if (!folder->trash &&
1122                                            !strcmp(dir_name, TRASH_DIR)) {
1123                                         new_item->stype = F_TRASH;
1124                                         folder->trash = new_item;
1125                                 }
1126                         }
1127
1128                         mh_scan_tree_recursive(new_item);
1129                 } else if (to_number(dir_name) > 0) n_msg++;
1130
1131                 g_free(entry);
1132                 g_free(utf8entry);
1133                 g_free(utf8name);
1134         }
1135
1136 #ifdef G_OS_WIN32
1137         g_dir_close(dir);
1138 #else
1139         closedir(dp);
1140 #endif
1141
1142         mh_set_mtime(item);
1143 }
1144
1145 static gboolean mh_rename_folder_func(GNode *node, gpointer data)
1146 {
1147         FolderItem *item = node->data;
1148         gchar **paths = data;
1149         const gchar *oldpath = paths[0];
1150         const gchar *newpath = paths[1];
1151         gchar *base;
1152         gchar *new_itempath;
1153         gint oldpathlen;
1154
1155         oldpathlen = strlen(oldpath);
1156         if (strncmp(oldpath, item->path, oldpathlen) != 0) {
1157                 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
1158                 return TRUE;
1159         }
1160
1161         base = item->path + oldpathlen;
1162         while (*base == G_DIR_SEPARATOR) base++;
1163         if (*base == '\0')
1164                 new_itempath = g_strdup(newpath);
1165         else
1166                 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
1167                                            NULL);
1168         g_free(item->path);
1169         item->path = new_itempath;
1170
1171         return FALSE;
1172 }
1173
1174 static gchar *mh_filename_from_utf8(const gchar *path)
1175 {
1176         gchar *real_path = g_filename_from_utf8(path, -1, NULL, NULL, NULL);
1177
1178         if (!real_path) {
1179                 g_warning("mh_filename_from_utf8: failed to convert character set\n");
1180                 real_path = g_strdup(path);
1181         }
1182
1183         return real_path;
1184 }
1185
1186 static gchar *mh_filename_to_utf8(const gchar *path)
1187 {
1188         gchar *utf8path = g_filename_to_utf8(path, -1, NULL, NULL, NULL);
1189         if (!utf8path) {
1190                 g_warning("mh_filename_to_utf8: failed to convert character set\n");
1191                 utf8path = g_strdup(path);
1192         }
1193
1194         return utf8path;
1195 }
1196
1197 static gint sort_cache_list_by_msgnum(gconstpointer a, gconstpointer b)
1198 {
1199         MsgInfo *msginfo_a = (MsgInfo *) a;
1200         MsgInfo *msginfo_b = (MsgInfo *) b;
1201
1202         return (msginfo_a->msgnum - msginfo_b->msgnum);
1203 }
1204
1205 static gchar *get_unseen_seq_name(void)
1206 {
1207         static gchar *seq_name = NULL;
1208         if (!seq_name) {
1209                 gchar buf[BUFFSIZE];
1210                 gchar *tmp;
1211                 gchar *profile_path = g_strconcat(
1212                         get_home_dir(), G_DIR_SEPARATOR_S,
1213                         ".mh_profile", NULL);
1214                 FILE *fp = g_fopen(profile_path, "r");
1215                 if (fp) {
1216                         while (fgets(buf, sizeof(buf), fp) != NULL) {
1217                                 if (!strncmp(buf, "Unseen-Sequence:", strlen("Unseen-Sequence:"))) {
1218                                         gchar *seq_tmp = buf+strlen("Unseen-Sequence:");
1219                                         while (*seq_tmp == ' ')
1220                                                 seq_tmp++;
1221                                         seq_name = g_strdup(seq_tmp);
1222                                         seq_name = strretchomp(seq_name);
1223                                         break;
1224                                 }
1225                         }
1226                         fclose(fp);
1227                 }
1228                 if (!seq_name)
1229                         seq_name = g_strdup("unseen");
1230                 tmp = g_strdup_printf("%s:", seq_name);
1231                 g_free(seq_name);
1232                 seq_name = tmp;
1233         }
1234         return seq_name;        
1235 }
1236
1237 #if 0
1238 static gint mh_get_flags(Folder *folder, FolderItem *item,
1239                            MsgInfoList *msginfo_list, GRelation *msgflags)
1240 {
1241         gchar *mh_sequences_filename;
1242         FILE *mh_sequences_file;
1243         gchar buf[BUFFSIZE];
1244         gchar *unseen_list = NULL;
1245         gchar *path;
1246         MsgInfoList *mcur = NULL;
1247 /*
1248         GTimer *timer = g_timer_new();
1249         g_timer_start(timer);
1250 */
1251         if (!item)
1252                 return 0;
1253
1254         /* don't update from .mh_sequences if the item's opened: mails may have
1255          * been marked read/unread and it's not yet written in the file. */     
1256         if (item->opened)
1257                 return 0;
1258
1259         path = folder_item_get_path(item);
1260
1261         mh_sequences_filename = g_strconcat(path, G_DIR_SEPARATOR_S,
1262                                             ".mh_sequences", NULL);
1263         g_free(path);
1264         if ((mh_sequences_file = g_fopen(mh_sequences_filename, "r+b")) != NULL) {
1265                 while (fgets(buf, sizeof(buf), mh_sequences_file) != NULL) {
1266                         if (!strncmp(buf, get_unseen_seq_name(), strlen(get_unseen_seq_name()))) {
1267                                 unseen_list = g_strdup(buf+strlen(get_unseen_seq_name()));
1268                                 break;
1269                         }
1270                 }
1271                 fclose(mh_sequences_file);
1272         }
1273         
1274         g_free(mh_sequences_filename);
1275         
1276         if (unseen_list) {
1277                 gchar *cur = NULL;
1278                 gchar *token = NULL, *next = NULL, *boundary = NULL;
1279                 gint num = 0;
1280                 GHashTable *unseen_table = g_hash_table_new(g_direct_hash, g_direct_equal);
1281
1282                 cur = unseen_list = strretchomp(unseen_list);
1283                 debug_print("found unseen list in .mh_sequences: %s\n", unseen_list);
1284 next_token:
1285                 while (*cur && *cur == ' ')
1286                         cur++;
1287                 
1288                 if ((next = strchr(cur, ' ')) != NULL) {
1289                         token = cur;
1290                         cur = next+1;
1291                         *next = '\0';
1292                 } else {
1293                         token = cur;
1294                         cur = NULL;
1295                 }
1296                 
1297                 if ((boundary = strchr(token, '-')) != NULL) {
1298                         gchar *start, *end;
1299                         int i;
1300                         start = token;
1301                         end = boundary+1;
1302                         *boundary='\0';
1303                         for (i = atoi(start); i <= atoi(end); i++) {
1304                                 g_hash_table_insert(unseen_table, GINT_TO_POINTER(i), GINT_TO_POINTER(1));
1305                         }
1306                 } else if ((num = atoi(token)) > 0) {
1307                         g_hash_table_insert(unseen_table, GINT_TO_POINTER(num), GINT_TO_POINTER(1));
1308                 }
1309                 
1310                 if (cur)
1311                         goto next_token;
1312                 for (mcur = msginfo_list; mcur; mcur = mcur->next) {
1313                         MsgInfo *msginfo = (MsgInfo *)mcur->data;
1314                         MsgPermFlags flags = msginfo->flags.perm_flags;
1315                         if (g_hash_table_lookup(unseen_table, GINT_TO_POINTER(msginfo->msgnum))) {
1316                                 flags |= MSG_UNREAD;
1317                         } else if (!(flags & MSG_NEW)) { /* don't mark new msgs as read */
1318                                 flags &= ~(MSG_UNREAD);
1319                         }
1320                         if (flags != msginfo->flags.perm_flags)
1321                                 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
1322                 }
1323                 g_hash_table_destroy(unseen_table);
1324                 g_free(unseen_list);
1325         }
1326 /*
1327         g_timer_stop(timer);
1328         printf("mh_get_flags: %f secs\n", g_timer_elapsed(timer, NULL));
1329         g_timer_destroy(timer);
1330 */
1331         return 0;
1332 }
1333 #endif
1334
1335 static void mh_write_sequences(FolderItem *item, gboolean remove_unseen)
1336 {
1337         gchar *mh_sequences_old, *mh_sequences_new;
1338         FILE *mh_sequences_old_fp, *mh_sequences_new_fp;
1339         gchar buf[BUFFSIZE];
1340         gchar *path = NULL;
1341         START_TIMING("");
1342
1343         if (!item)
1344                 return;
1345         
1346         path = folder_item_get_path(item);
1347
1348         mh_sequences_old = g_strconcat(path, G_DIR_SEPARATOR_S,
1349                                             ".mh_sequences", NULL);
1350         mh_sequences_new = g_strconcat(path, G_DIR_SEPARATOR_S,
1351                                             ".mh_sequences.new", NULL);
1352         if ((mh_sequences_new_fp = g_fopen(mh_sequences_new, "w+b")) != NULL) {
1353                 GSList *msglist = folder_item_get_msg_list(item);
1354                 GSList *cur;
1355                 MsgInfo *info = NULL;
1356                 gint start = -1, end = -1;
1357                 gchar *sequence = g_strdup("");
1358                 gint seq_len = 0;
1359                 msglist = g_slist_sort(msglist, sort_cache_list_by_msgnum);
1360                 cur = msglist;
1361                 
1362                 /* write the unseen sequence if we don't have to scrap it */
1363                 if (!remove_unseen) do {
1364                         info = (MsgInfo *)(cur ? cur->data:NULL);
1365                         if (info && (MSG_IS_UNREAD(info->flags) || MSG_IS_NEW(info->flags))) {
1366                                 if (start < 0)
1367                                         start = end = info->msgnum;
1368                                 else
1369                                         end = info->msgnum;
1370                         } else {
1371                                 if (start > 0 && end > 0) {
1372                                         gchar tmp[32];
1373                                         gint tmp_len = 0;
1374                                         if (start != end)
1375                                                 snprintf(tmp, 31, " %d-%d", start, end);
1376                                         else
1377                                                 snprintf(tmp, 31, " %d", start);
1378                                         
1379                                         tmp_len = strlen(tmp);
1380                                         sequence = g_realloc(sequence, seq_len+tmp_len+1);
1381                                         strcpy(sequence+seq_len, tmp);
1382                                         seq_len += tmp_len;
1383
1384                                         start = end = -1;
1385                                 }
1386                         }
1387                         cur = cur ? cur->next:NULL;
1388                 } while (cur || (start > 0 && end > 0));
1389                 if (sequence && *sequence) {
1390                         fprintf(mh_sequences_new_fp, "%s%s\n", 
1391                                         get_unseen_seq_name(), sequence);
1392                         debug_print("wrote unseen sequence: '%s%s'\n", 
1393                                         get_unseen_seq_name(), sequence);
1394                 }
1395                 /* rewrite the rest of the file */
1396                 if ((mh_sequences_old_fp = g_fopen(mh_sequences_old, "r+b")) != NULL) {
1397                         while (fgets(buf, sizeof(buf), mh_sequences_old_fp) != NULL) {
1398                                 if (strncmp(buf, get_unseen_seq_name(), strlen(get_unseen_seq_name())))
1399                                         fprintf(mh_sequences_new_fp, "%s", buf);
1400                         }
1401                         fclose(mh_sequences_old_fp);
1402                 }
1403                 
1404                 fflush(mh_sequences_new_fp);
1405 #if 0
1406                 fsync(fileno(mh_sequences_new_fp));
1407 #endif
1408                 fclose(mh_sequences_new_fp);
1409
1410                 g_rename(mh_sequences_new, mh_sequences_old);
1411                 g_free(sequence);
1412                 procmsg_msg_list_free(msglist);
1413         }
1414         g_free(mh_sequences_old);
1415         g_free(mh_sequences_new);
1416         g_free(path);
1417
1418         END_TIMING();
1419 }
1420
1421 static int mh_item_close(Folder *folder, FolderItem *item)
1422 {
1423         time_t last_mtime = (time_t)0;
1424         gboolean need_scan = mh_scan_required(item->folder, item);
1425         last_mtime = item->mtime;
1426
1427         mh_write_sequences(item, FALSE);
1428
1429         if (item->mtime == last_mtime && !need_scan) {
1430                 mh_set_mtime(item);
1431         }
1432
1433         return 0;
1434 }
1435
1436 void mh_set_mtime(FolderItem *item)
1437 {
1438         struct stat s;
1439         gchar *path = folder_item_get_path(item);
1440
1441         g_return_if_fail(path != NULL);
1442
1443         if (stat(path, &s) < 0) {
1444                 FILE_OP_ERROR(path, "stat");
1445                 g_free(path);
1446                 return;
1447         }
1448
1449         item->mtime = s.st_mtime;
1450         debug_print("MH: forced mtime of %s to %ld\n", item->name, item->mtime);
1451         g_free(path);
1452 }