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