sync with 0.8.1cvs21
[claws.git] / src / mbox_folder.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2001 Hiroyuki Yamamoto & The Sylpheed Claws 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 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 #include <unistd.h>
21 #include <fcntl.h>
22 #include <glib.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <unistd.h>
28
29 #include "mbox_folder.h"
30 #include "folder.h"
31 #include "procmsg.h"
32 #include "procheader.h"
33 #include "utils.h"
34 #include "intl.h"
35
36 #define MSGBUFSIZE      8192
37
38 static void     mbox_folder_init                (Folder         *folder,
39                                                  const gchar    *name,
40                                                  const gchar    *path);
41
42 static gboolean mbox_write_data(FILE * mbox_fp, FILE * new_fp,
43                                 gchar * new_filename, gint size);
44 static gboolean mbox_rewrite(gchar * mbox);
45 static gboolean mbox_purge_deleted(gchar * mbox);
46 static gchar * mbox_get_new_path(FolderItem * parent, gchar * name);
47 static gchar * mbox_get_folderitem_name(gchar * name);
48
49 MsgInfo *mbox_fetch_msginfo(Folder *folder, FolderItem *item, gint num);
50 GSList *mbox_get_num_list(Folder *folder, FolderItem *item);
51 gboolean mbox_check_msgnum_validity(Folder *folder, FolderItem *item);
52
53 Folder *mbox_folder_new(const gchar *name, const gchar *path)
54 {
55         Folder *folder;
56
57         folder = (Folder *)g_new0(MBOXFolder, 1);
58         mbox_folder_init(folder, name, path);
59
60         return folder;
61 }
62
63 void mbox_folder_destroy(MBOXFolder *folder)
64 {
65         folder_local_folder_destroy(LOCAL_FOLDER(folder));
66 }
67
68 static void mbox_folder_init(Folder *folder, const gchar *name, const gchar *path)
69 {
70         folder->type = F_MBOX;
71
72         folder_local_folder_init(folder, name, path);
73
74 /*
75         folder->get_msg_list        = mbox_get_msg_list;
76 */
77         folder->fetch_msg           = mbox_fetch_msg;
78         folder->fetch_msginfo       = mbox_fetch_msginfo;
79         folder->add_msg             = mbox_add_msg;
80         folder->copy_msg            = mbox_copy_msg;
81         folder->remove_msg          = mbox_remove_msg;
82         folder->remove_all_msg      = mbox_remove_all_msg;
83 /*
84         folder->scan                = mbox_scan_folder;
85 */
86         folder->get_num_list        = mbox_get_num_list;
87         folder->create_tree         = mbox_create_tree;
88         folder->create_folder       = mbox_create_folder;
89         folder->rename_folder       = mbox_rename_folder;
90         folder->remove_folder       = mbox_remove_folder;
91         folder->check_msgnum_validity
92                                     = mbox_check_msgnum_validity;
93 }
94
95 static gchar * mbox_folder_create_parent(const gchar * path)
96 {
97         if (!is_file_exist(path)) {
98                 gchar * new_path;
99
100                 new_path = g_dirname(path);
101                 if (new_path[strlen(new_path) - 1] == G_DIR_SEPARATOR)
102                         new_path[strlen(new_path) - 1] = '\0';
103
104                 if (!is_dir_exist(new_path))
105                         make_dir_hier(new_path);
106                 g_free(new_path);
107                 
108         }
109 }
110
111
112 static gchar *mbox_folder_get_path(FolderItem *item)
113 {
114         gchar *folder_path;
115         gchar *path;
116
117         g_return_val_if_fail(item != NULL, NULL);
118
119         if (item->path && item->path[0] == G_DIR_SEPARATOR) {
120                 mbox_folder_create_parent(item->path);
121                 return g_strdup(item->path);
122         }
123
124         folder_path = g_strdup(LOCAL_FOLDER(item->folder)->rootpath);
125         g_return_val_if_fail(folder_path != NULL, NULL);
126
127         if (folder_path[0] == G_DIR_SEPARATOR) {
128                 if (item->path) {
129                         path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
130                                            item->path, NULL);
131                 }
132                 else
133                         path = g_strdup(folder_path);
134         } else {
135                 if (item->path)
136                         path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
137                                            folder_path, G_DIR_SEPARATOR_S,
138                                            item->path, NULL);
139                 else
140                         path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
141                                            folder_path, NULL);
142         }
143
144         g_free(folder_path);
145         
146         mbox_folder_create_parent(path);
147
148         return path;
149 }
150
151
152 /**********************************************************/
153 /*                                                        */
154 /*                   file lock                            */
155 /*                                                        */
156 /**********************************************************/
157
158
159 static GSList * file_lock = NULL;
160
161 static gboolean mbox_file_lock_file(gchar * base)
162 {
163         gchar *lockfile, *locklink;
164         gint retry = 0;
165         FILE *lockfp;
166
167         lockfile = g_strdup_printf("%s.%d", base, getpid());
168         if ((lockfp = fopen(lockfile, "wb")) == NULL) {
169                 FILE_OP_ERROR(lockfile, "fopen");
170                 g_warning(_("can't create lock file %s\n"), lockfile);
171                 g_warning(_("use 'flock' instead of 'file' if possible.\n"));
172                 g_free(lockfile);
173                 return FALSE;
174         }
175         
176         fprintf(lockfp, "%d\n", getpid());
177         fclose(lockfp);
178         
179         locklink = g_strconcat(base, ".lock", NULL);
180         while (link(lockfile, locklink) < 0) {
181                 FILE_OP_ERROR(lockfile, "link");
182                 if (retry >= 5) {
183                         g_warning(_("can't create %s\n"), lockfile);
184                         unlink(lockfile);
185                         g_free(lockfile);
186                         return -1;
187                 }
188                 if (retry == 0)
189                         g_warning(_("mailbox is owned by another"
190                                     " process, waiting...\n"));
191                 retry++;
192                 sleep(5);
193         }
194         unlink(lockfile);
195         g_free(lockfile);
196
197         return TRUE;
198 }
199
200 static gboolean mbox_fcntl_lockwrite_file(FILE * fp)
201 {
202         struct flock lck;
203
204         lck.l_type = F_WRLCK;
205         lck.l_whence = 0;
206         lck.l_start = 0;
207         lck.l_len = 0;
208         
209         if (fcntl(fileno(fp), F_SETLK, &lck) < 0)
210                 return FALSE;
211         else
212                 return TRUE;
213 }
214
215 static gboolean mbox_fcntl_lockread_file(FILE * fp)
216 {
217         struct flock lck;
218
219         lck.l_type = F_RDLCK;
220         lck.l_whence = 0;
221         lck.l_start = 0;
222         lck.l_len = 0;
223         
224         if (fcntl(fileno(fp), F_SETLK, &lck) < 0)
225                 return FALSE;
226         else
227                 return TRUE;
228 }
229
230 static gboolean mbox_fcntl_unlock_file(FILE * fp)
231 {
232         struct flock lck;
233
234         lck.l_type = F_UNLCK;
235         lck.l_whence = 0;
236         lck.l_start = 0;
237         lck.l_len = 0;
238         
239         if (fcntl(fileno(fp), F_SETLK, &lck) < 0)
240                 return FALSE;
241         else
242                 return TRUE;
243 }
244
245 static gboolean mbox_file_unlock_file(gchar * base)
246 {
247         gchar *lockfile;
248
249         lockfile = g_strdup_printf("%s.lock", base);
250         unlink(lockfile);
251         g_free(lockfile);
252
253         return TRUE;
254 }
255
256 static gboolean mbox_lockread_file(FILE * fp, gchar * base)
257 {
258         gboolean result;
259
260         result = mbox_fcntl_lockread_file(fp);
261         if (!result) {
262                 if ((result = mbox_file_lock_file(base)) == TRUE) {
263                         file_lock = g_slist_append(file_lock, g_strdup(base));
264                         debug_print("lockfile lock %s.\n", base);
265                 }
266                 else
267                         g_warning(_("could not lock read file %s\n"), base);
268         }
269         else
270                 debug_print("fcntl lock %s.\n", base);
271
272         return result;
273 }
274
275 static gboolean mbox_lockwrite_file(FILE * fp, gchar * base)
276 {
277         gboolean result;
278
279         result = mbox_fcntl_lockwrite_file(fp);
280         if (!result) {
281                 if ((result = mbox_file_lock_file(base)) == TRUE) {
282                         file_lock = g_slist_append(file_lock, g_strdup(base));
283                         debug_print("lockfile lock %s.\n", base);
284                 }
285                 else
286                         g_warning(_("could not lock write file %s\n"), base);
287         }
288         else
289                 debug_print("fcntl lock %s.\n", base);
290
291         return result;
292 }
293
294 static gboolean mbox_unlock_file(FILE * fp, gchar * base)
295 {
296         gboolean result = FALSE;
297         GSList * l;
298         gboolean unlocked = FALSE;
299
300         for(l = file_lock ; l != NULL ; l = g_slist_next(l)) {
301                 gchar * data = l->data;
302
303                 if (strcmp(data, base) == 0) {
304                         file_lock = g_slist_remove(file_lock, data);
305                         g_free(data);
306                         result = mbox_file_unlock_file(base);
307                         unlocked = TRUE;
308                         debug_print("lockfile unlock - %s.\n", base);
309                         break;
310                 }
311         }
312         
313         if (!unlocked) {
314                 result = mbox_fcntl_unlock_file(fp);
315                 debug_print("fcntl unlock - %s.\n", base);
316         }
317
318         return result;
319 }
320
321 /**********************************************************/
322 /*                                                        */
323 /*                   mbox parsing                         */
324 /*                                                        */
325 /**********************************************************/
326
327 #define MAILFILE_ERROR_NO_ERROR          0x000
328 #define MAILFILE_ERROR_FILE_NOT_FOUND    0x001
329 #define MAILFILE_ERROR_MEMORY            0x002
330 #define MAILFILE_ERROR_MESSAGE_NOT_FOUND 0x003
331
332 static int mailfile_error = MAILFILE_ERROR_NO_ERROR;
333
334 #define STATE_BEGIN       0x000
335 #define STATE_TEXT_READ   0x001
336 #define STATE_FROM_READ   0x002
337 #define STATE_FIELD_READ  0x003
338 #define STATE_END         0x004
339 #define STATE_END_OF_FILE 0x005
340 #define STATE_MEM_ERROR   0x006
341 #define STATE_TEXT_BEGIN  0x007
342
343 #define STATE_MASK        0x0FF /* filter state from functions */
344
345 #define STATE_RESTORE_POS       0x100 /* go back while reading */
346
347 typedef struct _mailfile mailfile;
348
349 struct _mailfile
350 {
351         gint count;
352         gchar * filename;
353         GList * msg_list;
354 };
355
356 struct _message
357 {
358         int msgnum;
359         int offset;
360         int header;
361         int content;
362         int end;
363         int marked;
364         gchar * messageid;
365         gchar * fromspace;
366         MsgFlags flags;
367         MsgFlags old_flags;
368         gboolean fetched;
369 };
370
371 #define MSG_IS_INVALID(msg) \
372         ((msg).perm_flags == (msg).tmp_flags && (msg).tmp_flags == -1)
373
374 #define MSG_SET_INVALID(msg) \
375         ((msg).perm_flags = (msg).tmp_flags = -1)
376
377 static int startFrom(char * s)
378 {
379         return (strncmp(s, "From ", 5) == 0);
380 }
381
382 static int startSpace(char * s)
383 {
384         return ((*s == ' ') || (*s == '\t'));
385 }
386
387 static int startEmpty(char * s)
388 {
389         return (*s == '\n');
390 }
391
392 static void free_msg_list(GList * l)
393 {
394         GList * elt = g_list_first(l);
395
396         while (elt)
397                 {
398                         g_free(elt->data);
399                         elt = g_list_next(elt);
400                 }
401
402         g_list_free(l);
403 }
404
405
406 static mailfile * mailfile_init_from_file(FILE * f, gchar * filename)
407 {
408         int state;
409         GList * msg_list = NULL;
410         char * r = NULL;
411         char s[256];
412         int lastpos = 0;
413         int former_pos = 0;
414         int ignore_next = 0;
415         int msgnum = 0;
416         struct _message * data = NULL;
417         mailfile * mf;
418
419         state = STATE_BEGIN;
420
421
422         while (state != STATE_END_OF_FILE) {
423                 if ((state & STATE_RESTORE_POS) == 0) {
424                         former_pos = lastpos;
425                         lastpos = ftell(f);
426
427                         r = fgets(s, 256, f);
428
429                         if (r != NULL && *r)
430                                 ignore_next = (s[strlen(s) - 1] != '\n');
431                         else
432                                 ignore_next = 0;
433                 }
434
435                 switch(state & 0x0F) {
436                 case STATE_BEGIN:
437                         if (r == NULL)
438                                 state = STATE_END_OF_FILE;
439                         else if (startFrom(s)) {
440                                 state = STATE_FROM_READ;
441
442                                 data = g_new0(struct _message, 1);
443                                 if (data == NULL) {
444                                         free_msg_list(msg_list);
445                                         return NULL;
446                                 }
447                                 
448                                 msgnum ++;
449                                 data->msgnum = msgnum;
450                                 data->offset = lastpos;
451                                 data->header = lastpos;
452                                 data->end = 0;
453                                 data->content = 0;
454                                 data->messageid = NULL;
455                                 data->fromspace = NULL;
456                                 MSG_SET_INVALID(data->flags);
457                                 MSG_SET_INVALID(data->old_flags);
458                                 data->fetched = FALSE;
459                                 msg_list = g_list_append(msg_list,
460                                                          (gpointer) data);
461                         }
462                         else
463                                 state = STATE_BEGIN;
464
465                         break;
466
467                 case STATE_TEXT_READ:
468                         if (r == NULL)
469                                 state = STATE_END;
470                         else if (startFrom(s))
471                                 state = STATE_END | STATE_RESTORE_POS;
472                         else
473                                 state = STATE_TEXT_READ;
474                         break;
475
476                 case STATE_TEXT_BEGIN:
477                         data->content = lastpos;
478                         if (r == NULL)
479                                 state = STATE_END;
480                         else if (startFrom(s)) {
481                                 state = STATE_END | STATE_RESTORE_POS;
482                         }
483                         else {
484                                 state = STATE_TEXT_READ;
485                         }
486                         break;
487           
488                 case STATE_FROM_READ:
489                         data->content = lastpos;
490                         if (r == NULL)
491                                 state = STATE_END;
492                         else if (startSpace(s))
493                                 state = STATE_FROM_READ;
494                         else if (startEmpty(s))
495                                 state = STATE_TEXT_READ;
496                         else
497                                 state = STATE_FIELD_READ;
498                         break;
499           
500                 case STATE_FIELD_READ:
501                         data->content = lastpos;
502                         if (r == NULL)
503                                 state = STATE_END;
504                         else if (startSpace(s))
505                                 state = STATE_FIELD_READ;
506                         else if (startEmpty(s)) {
507                                 state = STATE_TEXT_BEGIN;
508                         }
509                         else
510                                 state = STATE_FIELD_READ;
511                         break;
512                 }
513       
514                 if ((state & STATE_MASK) == STATE_END) {
515                         state = STATE_BEGIN | (state & STATE_RESTORE_POS);
516                         data->end = lastpos;
517                 }
518
519                 if (ignore_next) {
520                         do {
521                                 r = fgets(s, 256, f);
522                                 if (r == NULL || *r == '\0')
523                                         break;
524                         }
525                         while (s[strlen(s) - 1] != '\n');
526                 }
527         }
528
529         mf = (mailfile *) g_new0(struct _mailfile, 1);
530         if (mf == NULL) {
531                 free_msg_list(msg_list);
532                 mailfile_error = MAILFILE_ERROR_MEMORY;
533                 return NULL;
534         }
535
536         mf->msg_list = g_list_first(msg_list);
537
538         mf->filename = g_strdup(filename);
539         mf->count = msgnum;
540
541         mailfile_error = MAILFILE_ERROR_NO_ERROR;
542
543         return mf;
544 }
545
546 static mailfile * mailfile_init(char * filename)
547 {
548
549         FILE * f;
550         mailfile * mf;
551   
552         f = fopen(filename, "rb");
553
554         if (f == NULL) {
555                 mailfile_error = MAILFILE_ERROR_FILE_NOT_FOUND;
556                 return NULL;
557         }
558
559         mbox_lockread_file(f, filename);
560
561         mf = mailfile_init_from_file(f, filename);
562
563         mbox_unlock_file(f, filename);
564
565         fclose(f);
566
567         return mf;
568 }
569
570 static void mailfile_done(mailfile * f)
571 {
572         free_msg_list(f->msg_list);
573         g_free(f->filename);
574
575         g_free(f);
576 }
577
578 /*
579 #define MAX_READ 4096
580
581 static char * readfile(char * filename, int offset, int max_offset)
582 {
583         char * message;
584         int size;
585         int pos;
586         int max;
587         int bread;
588         FILE * handle;
589
590         handle = fopen(filename, "rb");
591
592         if (handle == NULL) {
593                 mailfile_error = MAILFILE_ERROR_FILE_NOT_FOUND;
594                 return NULL;
595         }
596
597         size = max_offset - offset;
598
599         message = (char *) malloc(size + 1);
600         if (message == NULL) {
601                 fclose(handle);
602                 mailfile_error = MAILFILE_ERROR_MEMORY;
603                 return NULL;
604         }
605
606         fseek(handle, offset, SEEK_SET);
607
608         pos = 0;
609         while (pos < size) {
610                 if ((size - pos) > MAX_READ)
611                         max = MAX_READ;
612                 else
613                         max = (size - pos);
614
615                 bread = fread(message + pos, 1, max, handle);
616
617                 if (bread != -1)
618                         pos += bread;
619
620                 if (bread < max)
621                         break;
622         }
623
624         message[pos] = 0;
625
626         fclose(handle);
627
628         return message;
629 }
630
631 static char * mailfile_readmsg(mailfile f, int index)
632 {
633         GList * nth;
634         int max_offset;
635         int offset;
636         char * message;
637         struct _message * msginfo;
638
639         nth = g_list_nth(f->msg_list, index);
640
641         if (!nth) {
642                 mailfile_error = MAILFILE_ERROR_MESSAGE_NOT_FOUND;
643                 return NULL;
644         }
645
646         msginfo = (struct _message *)nth->data;
647
648         offset = msginfo->offset;
649         max_offset = msginfo->end;
650         message = readfile(f->filename, offset, max_offset);
651
652         mailfile_error = MAILFILE_ERROR_NO_ERROR;
653
654         return message;
655 }
656
657 static char * mailfile_readheader(mailfile f, int index)
658 {
659         GList * nth;
660         int max_offset;
661         int offset;
662         char * message;
663         struct _message * msginfo;
664
665         nth = g_list_nth(f->msg_list, index);
666
667         if (!nth) {
668                 mailfile_error = MAILFILE_ERROR_MESSAGE_NOT_FOUND;
669                 return NULL;
670         }
671
672         msginfo = (struct _message *)nth->data;
673
674         offset = msginfo->offset;
675         max_offset = msginfo->content;
676         message = readfile(f->filename, offset, max_offset);
677
678         mailfile_error = MAILFILE_ERROR_NO_ERROR;
679
680         return message;
681 }
682
683 static int mailfile_count(mailfile * f)
684 {
685         return g_list_length(f->msg_list);
686 }
687
688 static int mailfile_find_deleted(mailfile f, char * filename)
689 {
690         FILE * handle;
691
692         handle = fopen(filename, "rb");
693
694         while (elt) {
695                 struct _message m = elt->data;
696                 n = fread(&m.deleted, sizeof(int), 1, handle);
697                 if (!n)
698                         break;
699                 elt = g_list_next(elt);
700         }
701
702         fclose(handle);
703 }
704 */
705
706
707
708 /**********************************************************/
709 /*                                                        */
710 /*                   mbox cache operations                */
711 /*                                                        */
712 /**********************************************************/
713
714 struct _mboxcache {
715         gchar * filename;
716         mailfile * mf;
717         GPtrArray * tab_mf;
718         gint mtime;
719         gboolean modification;
720 };
721
722 typedef struct _mboxcache mboxcache;
723
724 static GHashTable * mbox_cache_table = NULL;
725
726 static MsgInfo *mbox_parse_msg(FILE * fp, struct _message * msg,
727                                FolderItem *item)
728 {
729         MsgInfo *msginfo;
730         MsgFlags flags = { 0, 0 };
731
732         MSG_SET_PERM_FLAGS(flags, MSG_NEW | MSG_UNREAD);
733
734         g_return_val_if_fail(fp != NULL, NULL);
735
736         if (item != NULL) {
737                 if (item->stype == F_QUEUE) {
738                         MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
739                 } else if (item->stype == F_DRAFT) {
740                         MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
741                 }
742         }
743
744         msginfo = procheader_parse_stream(fp, flags, FALSE, FALSE);
745
746         if (!msginfo) return NULL;
747
748         if (item != NULL) {
749                 msginfo->msgnum = msg->msgnum;
750                 msginfo->folder = item;
751         }
752
753         return msginfo;
754 }
755
756 static void mbox_cache_init()
757 {
758         mbox_cache_table = g_hash_table_new(g_str_hash, g_str_equal);
759 }
760
761 static void mbox_cache_done()
762 {
763         g_hash_table_destroy(mbox_cache_table);
764 }
765
766 static void mbox_cache_free_mbox(mboxcache * cache)
767 {
768         g_hash_table_remove(mbox_cache_table, cache->filename);
769
770         if (cache->mf)
771                 mailfile_done(cache->mf);
772         if (cache->tab_mf)
773                 g_ptr_array_free(cache->tab_mf, FALSE);
774         if (cache->filename)
775                 g_free(cache->filename);
776         g_free(cache);
777 }
778
779 static void mbox_cache_get_msginfo_from_file(FILE * fp, GList * msg_list)
780 {
781         GList * l;
782         MsgInfo * msginfo;
783
784         for(l = msg_list ; l != NULL ; l = g_list_next(l)) {
785                 struct _message * msg;
786
787                 msg = (struct _message *) l->data;
788
789                 fseek(fp, msg->header, SEEK_SET);
790                 msginfo = mbox_parse_msg(fp, msg, NULL);
791                 if (msginfo) {
792                         if (msginfo->msgid)
793                                 msg->messageid =
794                                         g_strdup(msginfo->msgid);
795                         if (msginfo->fromspace)
796                                 msg->fromspace =
797                                         g_strdup(msginfo->fromspace);
798                         msg->flags = msginfo->flags;
799                         msg->old_flags = msginfo->flags;
800
801                         procmsg_msginfo_free(msginfo);
802                 }
803         }
804 }
805
806 static void mbox_cache_get_msginfo(gchar * filename, GList * msg_list)
807 {
808         FILE * fp;
809
810         fp = fopen(filename, "rb");
811         if (fp == NULL)
812                 return;
813
814         mbox_cache_get_msginfo_from_file(fp, msg_list);
815         fclose(fp);
816 }
817
818 static mboxcache * mbox_cache_read_mbox(gchar * filename)
819 {
820         mboxcache * cache;
821         struct stat s;
822         mailfile * mf;
823         GList * l;
824
825         if (stat(filename, &s) < 0)
826                 return NULL;
827
828         mf = mailfile_init(filename);
829         if (mf == NULL)
830                 return NULL;
831
832         cache = g_new0(mboxcache, 1);
833
834         cache->mtime = s.st_mtime;
835         cache->mf = mf;
836         cache->filename = g_strdup(filename);
837         cache->modification = FALSE;
838
839         cache->tab_mf = g_ptr_array_new();
840         for(l = mf->msg_list ; l != NULL ; l = g_list_next(l))
841                 g_ptr_array_add(cache->tab_mf, l->data);
842
843         mbox_cache_get_msginfo(filename, mf->msg_list);
844
845         debug_print(_("read mbox - %s\n"), filename);
846
847         return cache;
848 }
849
850 static mboxcache * mbox_cache_read_mbox_from_file(FILE * fp, gchar * filename)
851 {
852         mboxcache * cache;
853         struct stat s;
854         mailfile * mf;
855         GList * l;
856
857         if (stat(filename, &s) < 0)
858                 return NULL;
859
860         mf = mailfile_init_from_file(fp, filename);
861         if (mf == NULL)
862                 return NULL;
863
864         cache = g_new0(mboxcache, 1);
865
866         cache->mtime = s.st_mtime;
867         cache->mf = mf;
868         cache->filename = g_strdup(filename);
869
870         cache->tab_mf = g_ptr_array_new();
871         for(l = mf->msg_list ; l != NULL ; l = g_list_next(l))
872                 g_ptr_array_add(cache->tab_mf, l->data);
873
874         mbox_cache_get_msginfo_from_file(fp, mf->msg_list);
875
876         debug_print(_("read mbox from file - %s\n"), filename);
877
878         return cache;
879 }
880
881 static void mbox_cache_insert_mbox(mboxcache * data)
882 {
883         if (mbox_cache_table == NULL)
884                 mbox_cache_init();
885
886         g_hash_table_insert(mbox_cache_table, data->filename, data);
887 }
888
889 static mboxcache * mbox_cache_get_mbox(gchar * filename)
890 {
891         if (mbox_cache_table == NULL)
892                 mbox_cache_init();
893
894         return g_hash_table_lookup(mbox_cache_table, filename);
895 }
896
897
898 static gint mbox_cache_get_count(gchar * filename)
899 {
900         mboxcache * cache;
901
902         cache = mbox_cache_get_mbox(filename);
903         if (cache == NULL)
904                 return -1;
905         if (cache->mf == NULL)
906                 return -1;
907         return cache->mf->count;
908 }
909
910 static gint mbox_cache_get_mtime(gchar * filename)
911 {
912         mboxcache * cache;
913
914         cache = mbox_cache_get_mbox(filename);
915         if (cache == NULL)
916                 return -1;
917         return cache->mtime;
918 }
919
920 static GList * mbox_cache_get_msg_list(gchar * filename)
921 {
922         mboxcache * cache;
923
924         cache = mbox_cache_get_mbox(filename);
925
926         if (cache == NULL)
927                 return NULL;
928
929         if (cache->mf == NULL)
930                 return NULL;
931
932         return cache->mf->msg_list;
933 }
934
935 static void mbox_cache_synchronize_lists(GList * old_msg_list,
936                                          GList * new_msg_list)
937 {
938         GList * l;
939         GList * l2;
940
941         for(l2 = old_msg_list ; l2 != NULL ; l2 = g_list_next(l2)) {
942                 struct _message * msg2 = l2->data;
943
944                 if ((msg2->messageid == NULL) ||
945                     (msg2->fromspace == NULL))
946                         continue;
947
948                 for(l = new_msg_list ; l != NULL ; l = g_list_next(l)) {
949                         struct _message * msg = l->data;
950                         
951                         if ((msg->messageid == NULL) ||
952                             (msg->fromspace == NULL))
953                                 continue;
954
955                         if ((strcmp(msg->messageid, msg2->messageid) == 0) &&
956                             (strcmp(msg->fromspace, msg2->fromspace) == 0)) {
957                                 if (msg2->flags.perm_flags != msg2->old_flags.perm_flags) {
958                                         msg->flags = msg2->flags;
959                                         break;
960                                 }
961                         }
962                 }
963         }
964 }
965
966 static void mbox_cache_synchronize(gchar * filename, gboolean sync)
967 {
968         mboxcache * new_cache;
969         mboxcache * old_cache;
970         gboolean scan_new = TRUE;
971         struct stat s;
972
973         old_cache = mbox_cache_get_mbox(filename);
974
975         if (old_cache != NULL) {
976                 if (stat(filename, &s) < 0) {
977                         FILE_OP_ERROR(filename, "stat");
978                 } else if (old_cache->mtime == s.st_mtime) {
979                         debug_print("Folder is not modified.\n");
980                         scan_new = FALSE;
981                 }
982         }
983
984         if (scan_new) {
985                 GList * l;
986
987                 /*              
988                 if (strstr(filename, "trash") == 0)
989                         printf("old_cache: %p %s\n", old_cache, filename);
990                         if (old_cache) {
991                                 printf("begin old\n");
992                                 for(l = old_cache->mf->msg_list ; l != NULL ;
993                                     l = g_list_next(l)) {
994                                         struct _message * msg = l->data;
995                                         printf("%p\n", msg);
996                                 }
997                                 printf("end old\n");
998                         }
999                 */              
1000
1001                 new_cache = mbox_cache_read_mbox(filename);
1002
1003                 /*
1004                 if (strstr(filename, "trash") == 0) 
1005                         printf("new_cache: %p %s\n", new_cache, filename);
1006                         if (new_cache) {
1007                                 printf("begin new\n");
1008                                 for(l = new_cache->mf->msg_list ; l != NULL ;
1009                                     l = g_list_next(l)) {
1010                                         struct _message * msg = l->data;
1011                                         printf("%p\n", msg);
1012                                 }
1013                                 printf("end new\n");
1014                         }
1015                 */
1016
1017                 if (!new_cache)
1018                         return;
1019
1020                 if (sync && new_cache && old_cache)
1021                         mbox_cache_synchronize_lists(old_cache->mf->msg_list,
1022                                                      new_cache->mf->msg_list);
1023
1024                 if (old_cache != NULL)
1025                         mbox_cache_free_mbox(old_cache);
1026
1027                 if (new_cache) {
1028                         mbox_cache_insert_mbox(new_cache);
1029                         /*
1030                         printf("insert %p %s\n", new_cache, new_cache->filename);
1031                         printf("inserted %s %p\n", filename,
1032                                mbox_cache_get_mbox(filename));
1033                         */
1034                 }
1035         }
1036 }
1037
1038 static void mbox_cache_synchronize_from_file(FILE * fp, gchar * filename,
1039                                              gboolean sync)
1040 {
1041         mboxcache * new_cache;
1042         mboxcache * old_cache;
1043         gboolean scan_new = TRUE;
1044         struct stat s;
1045
1046         old_cache = mbox_cache_get_mbox(filename);
1047
1048         if (old_cache != NULL) {
1049                 if (stat(filename, &s) < 0) {
1050                         FILE_OP_ERROR(filename, "stat");
1051                 } else if (old_cache->mtime == s.st_mtime) {
1052                         debug_print("Folder is not modified.\n");
1053                         scan_new = FALSE;
1054                 }
1055         }
1056
1057
1058         if (scan_new) {
1059
1060                 /*
1061                 GList * l;
1062
1063                 if (strstr(filename, "trash") == 0)
1064                         printf("old_cache: %p %s\n", old_cache, filename);
1065
1066                         if (old_cache) {
1067                                 printf("begin old\n");
1068                                 for(l = old_cache->mf->msg_list ; l != NULL ;
1069                                     l = g_list_next(l)) {
1070                                         struct _message * msg = l->data;
1071                                         printf("%p\n", msg);
1072                                 }
1073                                 printf("end old\n");
1074                         }
1075                 */
1076                 
1077                 new_cache = mbox_cache_read_mbox_from_file(fp, filename);
1078
1079                 /*
1080                 if (strstr(filename, "trash") == 0) 
1081                         printf("new_cache: %p %s\n", new_cache, filename);
1082
1083                         if (new_cache) {
1084                                 printf("begin new\n");
1085                                 for(l = new_cache->mf->msg_list ; l != NULL ;
1086                                     l = g_list_next(l)) {
1087                                         struct _message * msg = l->data;
1088                                         printf("%p\n", msg);
1089                                 }
1090                                 printf("end new\n");
1091                         }
1092                 */
1093
1094                 if (!new_cache)
1095                         return;
1096
1097                 if (sync && new_cache && old_cache)
1098                         mbox_cache_synchronize_lists(old_cache->mf->msg_list,
1099                                                      new_cache->mf->msg_list);
1100
1101                 if (old_cache != NULL)
1102                         mbox_cache_free_mbox(old_cache);
1103
1104                 if (new_cache) {
1105                         mbox_cache_insert_mbox(new_cache);
1106                         /*
1107                         printf("insert %p %s\n", new_cache, new_cache->filename);
1108                         printf("inserted %s %p\n", filename,
1109                                mbox_cache_get_mbox(filename));
1110                         */
1111                 }
1112         }
1113 }
1114
1115 gboolean mbox_cache_msg_fetched(gchar * filename, gint num)
1116 {
1117         struct _message * msg;
1118         mboxcache * cache;
1119
1120         cache = mbox_cache_get_mbox(filename);
1121
1122         if (cache == NULL)
1123                 return FALSE;
1124
1125         msg = (struct _message *) g_ptr_array_index(cache->tab_mf,
1126                                                     num - 1);
1127         if (msg == NULL)
1128                 return FALSE;
1129
1130         return msg->fetched;
1131 }
1132
1133 void mbox_cache_msg_set_fetched(gchar * filename, gint num)
1134 {
1135         struct _message * msg;
1136         mboxcache * cache;
1137
1138         cache = mbox_cache_get_mbox(filename);
1139
1140         if (cache == NULL)
1141                 return;
1142
1143         msg = (struct _message *) g_ptr_array_index(cache->tab_mf,
1144                                                     num - 1);
1145         if (msg == NULL)
1146                 return;
1147
1148         msg->fetched = TRUE;
1149 }
1150
1151 struct _message * mbox_cache_get_msg(gchar * filename, gint num)
1152 {
1153         mboxcache * cache;
1154
1155         cache = mbox_cache_get_mbox(filename);
1156
1157         if (cache == NULL) {
1158                 return NULL;
1159         }
1160
1161         return (struct _message *) g_ptr_array_index(cache->tab_mf,
1162                                                      num - 1);
1163 }
1164
1165
1166 /**********************************************************/
1167 /*                                                        */
1168 /*                   mbox operations                      */
1169 /*                                                        */
1170 /**********************************************************/
1171
1172
1173 GSList *mbox_get_msg_list(Folder *folder, FolderItem *item, gboolean use_cache)
1174 {
1175         GSList *mlist;
1176         MsgInfo * msginfo;
1177         GList * l;
1178         FILE * fp;
1179         gchar * mbox_path;
1180
1181 #ifdef MEASURE_TIME
1182         struct timeval tv_before, tv_after, tv_result;
1183
1184         gettimeofday(&tv_before, NULL);
1185 #endif
1186
1187         mlist = NULL;
1188
1189         mbox_path = mbox_folder_get_path(item);
1190
1191         if (mbox_path == NULL)
1192                 return NULL;
1193
1194         mbox_purge_deleted(mbox_path);
1195
1196         fp = fopen(mbox_path, "rb");
1197         
1198         if (fp == NULL) {
1199                 g_free(mbox_path);
1200                 return NULL;
1201         }
1202
1203         mbox_lockread_file(fp, mbox_path);
1204
1205         mbox_cache_synchronize_from_file(fp, mbox_path, TRUE);
1206
1207         item->last_num = mbox_cache_get_count(mbox_path);
1208
1209         for(l = mbox_cache_get_msg_list(mbox_path) ; l != NULL ;
1210             l = g_list_next(l)) {
1211                 struct _message * msg;
1212
1213                 msg = (struct _message *) l->data;
1214
1215                 if (MSG_IS_INVALID(msg->flags) || !MSG_IS_REALLY_DELETED(msg->flags)) {
1216                         fseek(fp, msg->header, SEEK_SET);
1217
1218                         msginfo = mbox_parse_msg(fp, msg, item);
1219
1220                         if (!MSG_IS_INVALID(msg->flags))
1221                                 msginfo->flags = msg->flags;
1222                         else {
1223                                 msg->old_flags = msginfo->flags;
1224                                 msg->flags = msginfo->flags;
1225                         }
1226
1227                         mlist = g_slist_append(mlist, msginfo);
1228                 }
1229                 else {
1230                         MSG_SET_PERM_FLAGS(msg->flags, MSG_REALLY_DELETED);
1231                 }
1232         }
1233
1234         mbox_unlock_file(fp, mbox_path);
1235
1236         g_free(mbox_path);
1237
1238         fclose(fp);
1239
1240 #ifdef MEASURE_TIME
1241         gettimeofday(&tv_after, NULL);
1242
1243         timersub(&tv_after, &tv_before, &tv_result);
1244         g_print("mbox_get_msg_list: %s: elapsed time: %ld.%06ld sec\n",
1245                 mbox_path, tv_result.tv_sec, tv_result.tv_usec);
1246 #endif
1247
1248         return mlist;
1249 }
1250
1251 static gboolean mbox_extract_msg(FolderItem * item, gint msgnum,
1252                                  gchar * dest_filename)
1253 {
1254         struct _message * msg;
1255         gint offset;
1256         gint max_offset;
1257         gint size;
1258         FILE * src;
1259         FILE * dest;
1260         gboolean err;
1261         /*      GList * msg_list;*/
1262         gboolean already_fetched;
1263         gchar * mbox_path;
1264
1265         mbox_path = mbox_folder_get_path(item);
1266
1267         if (mbox_path == NULL)
1268                 return FALSE;
1269
1270         src = fopen(mbox_path, "rb");
1271         if (src == NULL) {
1272                 g_free(mbox_path);
1273                 return FALSE;
1274         }
1275
1276         mbox_lockread_file(src, mbox_path);
1277
1278         mbox_cache_synchronize_from_file(src, mbox_path, TRUE);
1279
1280         already_fetched = mbox_cache_msg_fetched(mbox_path, msgnum);
1281
1282         if (already_fetched) {
1283                 mbox_unlock_file(src, mbox_path);
1284                 fclose(src);
1285                 g_free(mbox_path);
1286                 return TRUE;
1287         }
1288
1289         msg = mbox_cache_get_msg(mbox_path, msgnum);
1290
1291         if (msg == NULL) {
1292                 mbox_unlock_file(src, mbox_path);
1293                 fclose(src);
1294                 g_free(mbox_path);
1295                 return FALSE;
1296         }
1297
1298         offset = msg->offset;
1299         max_offset = msg->end;
1300         
1301         size = max_offset - offset;
1302
1303         fseek(src, offset, SEEK_SET);
1304
1305         dest = fopen(dest_filename, "wb");
1306         if (dest == NULL) {
1307                 mbox_unlock_file(src, mbox_path);
1308                 fclose(src);
1309                 g_free(mbox_path);
1310                 return FALSE;
1311         }
1312
1313         if (change_file_mode_rw(dest, dest_filename) < 0) {
1314                 FILE_OP_ERROR(dest_filename, "chmod");
1315                 g_warning(_("can't change file mode\n"));
1316         }
1317
1318         if (!mbox_write_data(src, dest, dest_filename, size)) {
1319                 mbox_unlock_file(src, mbox_path);
1320                 fclose(dest);
1321                 fclose(src);
1322                 unlink(dest_filename);
1323                 g_free(mbox_path);
1324                 return FALSE;
1325         }
1326
1327         err = FALSE;
1328
1329         if (ferror(src)) {
1330                 FILE_OP_ERROR(mbox_path, "fread");
1331                 err = TRUE;
1332         }
1333
1334         mbox_cache_msg_set_fetched(mbox_path, msgnum);
1335
1336         if (fclose(dest) == -1) {
1337                 FILE_OP_ERROR(dest_filename, "fclose");
1338                 err = TRUE;
1339         }
1340
1341         mbox_unlock_file(src, mbox_path);
1342
1343         if (fclose(src) == -1) {
1344                 FILE_OP_ERROR(mbox_path, "fclose");
1345                 err = TRUE;
1346         }
1347
1348         g_free(mbox_path);
1349
1350         if (err) {
1351                 unlink(dest_filename);
1352                 return FALSE;
1353         }
1354
1355         return TRUE;
1356 }
1357
1358 gchar *mbox_fetch_msg(Folder *folder, FolderItem *item, gint num)
1359 {
1360         gchar *path;
1361         gchar *filename;
1362         
1363         g_return_val_if_fail(item != NULL, NULL);
1364
1365         path = folder_item_get_path(item);
1366         if (!is_dir_exist(path))
1367                 make_dir_hier(path);
1368
1369         filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(num), NULL);
1370
1371         g_free(path);
1372
1373         if (!mbox_extract_msg(item, num, filename)) {
1374                 g_free(filename);
1375                 return NULL;
1376         }
1377
1378         return filename;
1379 }
1380
1381 gint mbox_add_msg(Folder *folder, FolderItem *dest, const gchar *file,
1382                   gboolean remove_source)
1383 {
1384         FILE * src_fp;
1385         FILE * dest_fp;
1386         gchar buf[BUFSIZ];
1387         gint old_size;
1388         gint n_read;
1389         gboolean err;
1390         gchar * mbox_path;
1391         gchar from_line[MSGBUFSIZE];
1392
1393         if (dest->last_num < 0) {
1394                 mbox_scan_folder(folder, dest);
1395                 if (dest->last_num < 0) return -1;
1396         }
1397
1398         src_fp = fopen(file, "rb");
1399         if (src_fp == NULL) {
1400                 return -1;
1401         }
1402
1403         mbox_path = mbox_folder_get_path(dest);
1404         if (mbox_path == NULL)
1405                 return -1;
1406
1407         dest_fp = fopen(mbox_path, "ab");
1408         if (dest_fp == NULL) {
1409                 fclose(src_fp);
1410                 g_free(mbox_path);
1411                 return -1;
1412         }
1413
1414         if (change_file_mode_rw(dest_fp, mbox_path) < 0) {
1415                 FILE_OP_ERROR(mbox_path, "chmod");
1416                 g_warning(_("can't change file mode\n"));
1417         }
1418
1419         old_size = ftell(dest_fp);
1420
1421         mbox_lockwrite_file(dest_fp, mbox_path);
1422
1423         if (fgets(from_line, sizeof(from_line), src_fp) == NULL) {
1424                 mbox_unlock_file(dest_fp, mbox_path);
1425                 g_warning(_("unvalid file - %s.\n"), file);
1426                 fclose(dest_fp);
1427                 fclose(src_fp);
1428                 g_free(mbox_path);
1429                 return -1;
1430         }
1431         
1432         if (strncmp(from_line, "From ", 5) != 0) {
1433                 struct stat s;
1434
1435                 if (stat(file, &s) < 0) {
1436                         mbox_unlock_file(dest_fp, mbox_path);
1437                         g_warning(_("invalid file - %s.\n"), file);
1438                         fclose(dest_fp);
1439                         fclose(src_fp);
1440                         g_free(mbox_path);
1441                         return -1;
1442                 }
1443
1444                 fprintf(dest_fp, "From - %s", ctime(&s.st_mtime));
1445         }
1446
1447         fputs(from_line, dest_fp);
1448
1449         while (1) {
1450                 n_read = fread(buf, 1, sizeof(buf), src_fp);
1451                 if ((n_read < (gint) sizeof(buf)) && ferror(src_fp))
1452                         break;
1453                 if (fwrite(buf, n_read, 1, dest_fp) < 1) {
1454                         mbox_unlock_file(dest_fp, mbox_path);
1455                         g_warning(_("writing to %s failed.\n"), mbox_path);
1456                         ftruncate(fileno(dest_fp), old_size);
1457                         fclose(dest_fp);
1458                         fclose(src_fp);
1459                         g_free(mbox_path);
1460                         return -1;
1461                 }
1462
1463                 if (n_read < (gint) sizeof(buf))
1464                         break;
1465         }
1466
1467         err = FALSE;
1468
1469         if (ferror(src_fp)) {
1470                 FILE_OP_ERROR(mbox_path, "fread");
1471         }
1472
1473         mbox_unlock_file(dest_fp, mbox_path);
1474
1475         if (fclose(src_fp) == -1) {
1476                 FILE_OP_ERROR(file, "fclose");
1477                 err = TRUE;
1478         }
1479
1480         if (fclose(dest_fp) == -1) {
1481                 FILE_OP_ERROR(mbox_path, "fclose");
1482                 g_free(mbox_path);
1483                 return -1;
1484         }
1485
1486         if (err) {
1487                 ftruncate(fileno(dest_fp), old_size);
1488                 g_free(mbox_path);
1489                 return -1;
1490         }
1491
1492         if (remove_source) {
1493                 if (unlink(file) < 0)
1494                         FILE_OP_ERROR(file, "unlink");
1495         }
1496
1497         g_free(mbox_path);
1498
1499         dest->last_num++;
1500         return dest->last_num;
1501
1502 }
1503
1504 gint mbox_remove_msg(Folder *folder, FolderItem *item, gint num)
1505 {
1506         struct _message * msg;
1507         gchar * mbox_path;
1508
1509         mbox_path = mbox_folder_get_path(item);
1510         if (mbox_path == NULL)
1511                 return -1;
1512
1513         mbox_cache_synchronize(mbox_path, TRUE);
1514
1515         msg = mbox_cache_get_msg(mbox_path, num);
1516
1517         g_free(mbox_path);
1518
1519         if (msg != NULL)
1520                 MSG_SET_PERM_FLAGS(msg->flags, MSG_REALLY_DELETED);
1521
1522         return 0;
1523 }
1524
1525 gint mbox_remove_all_msg(Folder *folder, FolderItem *item)
1526 {
1527         FILE * fp;
1528         gchar * mbox_path;
1529
1530         mbox_path = mbox_folder_get_path(item);
1531         if (mbox_path == NULL)
1532                 return -1;
1533
1534         fp = fopen(mbox_path, "wb");
1535         if (fp == NULL) {
1536                 g_free(mbox_path);
1537                 return -1;
1538         }
1539
1540         fclose(fp);
1541
1542         g_free(mbox_path);
1543
1544         return 0;
1545 }
1546
1547 /*
1548 gint mbox_move_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1549 {
1550         gchar * filename;
1551         gint msgnum;
1552
1553         filename = mbox_fetch_msg(folder, msginfo->folder, msginfo->msgnum);
1554         if (filename == NULL)
1555                 return -1;
1556
1557         msgnum = mbox_add_msg(folder, dest, filename, TRUE);
1558
1559         if (msgnum != -1) {
1560                 MSG_SET_FLAGS(msginfo->flags, MSG_REALLY_DELETED);
1561                 mbox_change_flags(folder, msginfo->folder, msginfo);
1562         }
1563
1564         return msgnum;
1565 }
1566
1567 gint mbox_move_msgs_with_dest(Folder *folder, FolderItem *dest, GSList *msglist)
1568 {
1569         GSList * l;
1570         gchar * mbox_path = NULL;
1571
1572         for(l = msglist ; l != NULL ; l = g_slist_next(l)) {
1573                 MsgInfo * msginfo = (MsgInfo *) l->data;
1574
1575                 if (msginfo->folder && mbox_path == NULL)
1576                         mbox_path = mbox_folder_get_path(msginfo->folder);
1577
1578                 mbox_move_msg(folder, dest, msginfo);
1579         }
1580
1581         if (mbox_path) {
1582                 mbox_cache_synchronize(mbox_path);
1583                 g_free(mbox_path);
1584         }
1585
1586         mbox_path = mbox_folder_get_path(dest);
1587         mbox_cache_synchronize(mbox_path);
1588         g_free(mbox_path);
1589
1590         return dest->last_num;
1591 }
1592 */
1593
1594 /*
1595 gint mbox_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1596 {
1597         gchar * filename;
1598         gint msgnum;
1599
1600         filename = mbox_fetch_msg(folder, msginfo->folder, msginfo->msgnum);
1601         if (filename == NULL)
1602                 return -1;
1603
1604         msgnum = mbox_add_msg(folder, dest, filename, FALSE);
1605
1606         return msgnum;
1607 }
1608
1609 gint mbox_copy_msgs_with_dest(Folder *folder, FolderItem *dest, GSList *msglist)
1610 {
1611         GSList * l;
1612         gchar * mbox_path = NULL;
1613
1614         for(l = msglist ; l != NULL ; l = g_slist_next(l)) {
1615                 MsgInfo * msginfo = (MsgInfo *) l->data;
1616
1617                 if (msginfo->folder && mbox_path == NULL)
1618                         mbox_path = mbox_folder_get_path(msginfo->folder);
1619
1620                 mbox_copy_msg(folder, dest, msginfo);
1621         }
1622
1623         if (mbox_path) {
1624                 mbox_cache_synchronize(mbox_path);
1625                 g_free(mbox_path);
1626         }
1627
1628         mbox_path = mbox_folder_get_path(dest);
1629         mbox_cache_synchronize(mbox_path);
1630         g_free(mbox_path);
1631
1632         return dest->last_num;
1633 }
1634 */
1635
1636 struct _copy_flags_info
1637 {
1638         gint num;
1639         MsgFlags flags;
1640 };
1641
1642 typedef struct _copy_flags_info CopyFlagsInfo;
1643
1644 GSList * copy_flags_data = NULL;
1645
1646 gint mbox_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1647 {
1648         Folder * src_folder;
1649         gchar * filename;
1650         gint num;
1651         gchar * destdir;
1652         gchar * mbox_path;
1653         struct _message * msg;
1654         CopyFlagsInfo * flags_info;
1655
1656         src_folder = msginfo->folder->folder;
1657         
1658         g_return_val_if_fail(src_folder->fetch_msg != NULL, -1);
1659         
1660         /*
1661         mbox_path = mbox_folder_get_path(msginfo->folder);
1662         mbox_rewrite(mbox_path);
1663         g_free(mbox_path);
1664         */
1665
1666         filename = src_folder->fetch_msg(src_folder,
1667                                          msginfo->folder,
1668                                          msginfo->msgnum);
1669         if (filename == NULL)
1670                 return -1;
1671
1672         num = folder->add_msg(folder, dest, filename, FALSE);
1673
1674         /*
1675         mbox_path = mbox_folder_get_path(dest);
1676         msg = mbox_cache_get_msg(mbox_path, num);
1677         if (msg != NULL)
1678                 msg->flags = msginfo->flags;
1679         g_free(mbox_path);
1680         */
1681
1682         if (num == -1)
1683                 return -1;
1684
1685         flags_info = g_new0(CopyFlagsInfo, 1);
1686         flags_info->num = num;
1687         flags_info->flags = msginfo->flags;
1688         copy_flags_data = g_slist_append(copy_flags_data, flags_info);
1689
1690         return num;
1691 }
1692
1693 void mbox_finished_copy(Folder *folder, FolderItem *dest)
1694 {
1695         gchar * mbox_path;
1696         GSList * l;
1697         mboxcache * cache;
1698         
1699         mbox_path = mbox_folder_get_path(dest);
1700         if (mbox_path == NULL)
1701                 return;
1702
1703         mbox_cache_synchronize(mbox_path, TRUE);
1704
1705         for(l = copy_flags_data ; l != NULL ; l = g_slist_next(l)) {
1706                 CopyFlagsInfo * flags_info = l->data;
1707                 struct _message * msg;
1708
1709                 msg = mbox_cache_get_msg(mbox_path, flags_info->num);
1710                 if (msg != NULL)
1711                         msg->flags = flags_info->flags;
1712                 g_free(flags_info);
1713         }
1714
1715         if (copy_flags_data != NULL) {
1716                 cache = mbox_cache_get_mbox(mbox_path);
1717                 cache->modification = TRUE;
1718         }
1719
1720         g_slist_free(copy_flags_data);
1721         copy_flags_data = NULL;
1722
1723         mbox_rewrite(mbox_path);
1724
1725         g_free(mbox_path);
1726 }
1727
1728 void mbox_scan_folder(Folder *folder, FolderItem *item)
1729 {
1730         gchar *mbox_path;
1731         gint n_msg;
1732         mboxcache * cached;
1733         GList * l;
1734
1735         mbox_path = mbox_folder_get_path(item);
1736         if (mbox_path == NULL)
1737                 return;
1738
1739         mbox_cache_synchronize(mbox_path, TRUE);
1740
1741         cached = mbox_cache_get_mbox(mbox_path);
1742
1743         if (cached == NULL) {
1744                 item->new = 0;
1745                 item->unread = 0;
1746                 item->total = 0;
1747                 item->last_num = 0;
1748                 g_free(mbox_path);
1749                 return;
1750         }
1751
1752         n_msg = mbox_cache_get_count(mbox_path);
1753
1754         if (n_msg == 0) {
1755                 item->new = item->unread = item->total = 0;
1756         }
1757         else {
1758                 gint new = 0;
1759                 gint unread = 0;
1760                 gint total = 0;
1761
1762                 for(l = mbox_cache_get_msg_list(mbox_path) ; l != NULL ;
1763                     l = g_list_next(l)) {
1764                         struct _message * msg = (struct _message *) l->data;
1765                         if (!MSG_IS_REALLY_DELETED(msg->flags))
1766                                 total ++;
1767                         if (MSG_IS_NEW(msg->flags) && !MSG_IS_IGNORE_THREAD(msg->flags))
1768                                 new ++;
1769                         if (MSG_IS_UNREAD(msg->flags) && !MSG_IS_IGNORE_THREAD(msg->flags))
1770                                 unread ++;
1771                 }
1772                 
1773                 item->new = new;
1774                 item->unread = unread;
1775                 item->total = total;
1776         }
1777
1778         debug_print(_("Last number in dir %s = %d\n"), mbox_path,
1779                     item->total);
1780         item->last_num = n_msg;
1781         g_free(mbox_path);
1782 }
1783
1784 gchar * mbox_get_virtual_path(FolderItem * item)
1785 {
1786         if (item == NULL)
1787                 return NULL;
1788
1789         if (item->parent == NULL) {
1790                 return NULL;
1791         }
1792         else {
1793                 gchar * parent_path;
1794                 gchar * result_path;
1795
1796                 parent_path = mbox_get_virtual_path(item->parent);
1797                 if (parent_path == NULL)
1798                         result_path = g_strdup(item->name);
1799                 else
1800                         result_path = g_strconcat(parent_path,
1801                                                   G_DIR_SEPARATOR_S,
1802                                                   item->name, NULL);
1803                 g_free(parent_path);
1804
1805                 return result_path;
1806         }
1807 }
1808
1809 static gboolean mbox_write_data(FILE * mbox_fp, FILE * new_fp,
1810                                 gchar * new_filename, gint size)
1811 {       
1812         gint n_read;
1813         gint pos;
1814         gchar buf[BUFSIZ];
1815         gint max;
1816
1817         pos = 0;
1818         while (pos < size) {
1819                 if ((size - pos) > (gint) sizeof(buf))
1820                         max = sizeof(buf);
1821                 else
1822                         max = (size - pos);
1823                 
1824                 n_read = fread(buf, 1, max, mbox_fp);
1825
1826                 if (n_read < max && ferror(mbox_fp)) {
1827                         return FALSE;
1828                 }
1829                 if (fwrite(buf, n_read, 1, new_fp) < 1) {
1830                         g_warning(_("writing to %s failed.\n"), new_filename);
1831                         return FALSE;
1832                 }
1833                 
1834                 if (n_read != -1)
1835                         pos += n_read;
1836                 
1837                 if (n_read < max)
1838                         break;
1839         }
1840         return TRUE;
1841 }
1842
1843 static gboolean mbox_write_message(FILE * mbox_fp, FILE * new_fp,
1844                                    gchar * new_filename,
1845                                    struct _message * msg)
1846 {
1847         gint size;
1848         GPtrArray * headers;
1849         gint i;
1850         
1851         fseek(mbox_fp, msg->header, SEEK_SET);
1852
1853         headers = procheader_get_header_array_asis(mbox_fp);
1854
1855         for (i = 0; i < (gint) headers->len; i++) {
1856                 Header * h = g_ptr_array_index(headers, i);
1857                 
1858                 if (!procheader_headername_equal(h->name, 
1859                                                  "Status") &&
1860                     !procheader_headername_equal(h->name, 
1861                                                  "X-Status")) {
1862                         fwrite(h->name, strlen(h->name),
1863                                1, new_fp);
1864                         if (h->name[strlen(h->name) - 1] != ' ')
1865                                 fwrite(" ", 1, 1, new_fp);
1866                         fwrite(h->body, strlen(h->body),
1867                                1, new_fp);
1868                         fwrite("\n", 1, 1, new_fp);
1869                 }
1870                 procheader_header_free(h);
1871                 g_ptr_array_remove_index(headers, i);
1872                 i--;
1873         }
1874
1875         g_ptr_array_free(headers, FALSE);
1876
1877         if (!MSG_IS_INVALID(msg->flags)) {
1878                 /* Status header */
1879                 fwrite("Status: ", strlen("Status: "), 1, new_fp);
1880                 if (!MSG_IS_UNREAD(msg->flags))
1881                         fwrite("R", 1, 1, new_fp);
1882                 fwrite("O", 1, 1, new_fp);
1883                 fwrite("\n", 1, 1, new_fp);
1884                 
1885                 /* X-Status header */
1886                 if (MSG_IS_REALLY_DELETED(msg->flags)
1887                 ||  MSG_IS_MARKED(msg->flags)
1888                 ||  MSG_IS_DELETED(msg->flags)
1889                 ||  MSG_IS_REPLIED(msg->flags)
1890                 ||  MSG_IS_FORWARDED(msg->flags)) {
1891                         fwrite("X-Status: ", strlen("X-Status: "), 1, new_fp);
1892                         if (MSG_IS_REALLY_DELETED(msg->flags))
1893                                 fwrite("D", 1, 1, new_fp); /* really deleted */
1894                         else {
1895                                 if (MSG_IS_MARKED(msg->flags))
1896                                         fwrite("F", 1, 1, new_fp);
1897                                 if (MSG_IS_DELETED(msg->flags))
1898                                         fwrite("d", 1, 1, new_fp);
1899                                 if (MSG_IS_REPLIED(msg->flags))
1900                                         fwrite("r", 1, 1, new_fp);
1901                                 if (MSG_IS_FORWARDED(msg->flags))
1902                                         fwrite("f", 1, 1, new_fp);
1903                         }
1904                         fwrite("\n", 1, 1, new_fp);
1905                 }
1906         }
1907
1908         fwrite("\n", 1, 1, new_fp);
1909
1910         size = msg->end - msg->content;
1911         fseek(mbox_fp, msg->content, SEEK_SET);
1912
1913         return mbox_write_data(mbox_fp, new_fp, new_filename, size);
1914 }
1915
1916 void mbox_update_mark(Folder * folder, FolderItem * item)
1917 {
1918         gchar * mbox_path;
1919
1920         mbox_path = mbox_folder_get_path(item);
1921         if (mbox_path == NULL)
1922                 return;
1923
1924         mbox_rewrite(mbox_path);
1925         g_free(mbox_path);
1926 }
1927
1928 void mbox_change_flags(Folder * folder, FolderItem * item, MsgInfo * info)
1929 {
1930         struct _message * msg;
1931         mboxcache * cache;
1932         gchar * mbox_path;
1933
1934         mbox_path = mbox_folder_get_path(item);
1935         if (mbox_path == NULL)
1936                 return;
1937
1938         msg = mbox_cache_get_msg(mbox_path, info->msgnum);
1939
1940         cache = mbox_cache_get_mbox(mbox_path);
1941
1942         g_free(mbox_path);
1943
1944         if ((msg == NULL) || (cache == NULL))
1945                 return;
1946
1947         msg->flags = info->flags;
1948
1949         cache->modification = TRUE;
1950 }
1951
1952
1953 static gboolean mbox_rewrite(gchar * mbox)
1954 {
1955         FILE * mbox_fp;
1956         FILE * new_fp;
1957         gchar * new;
1958         GList * l;
1959         gboolean result;
1960         GList * msg_list;
1961         gint count;
1962         mboxcache * cache;
1963
1964         msg_list = mbox_cache_get_msg_list(mbox);
1965
1966         cache = mbox_cache_get_mbox(mbox);
1967         if (cache == NULL)
1968                 return FALSE;
1969
1970         if (!cache->modification) {
1971                 debug_print(_("no modification - %s\n"), mbox);
1972                 return FALSE;
1973         }
1974
1975         debug_print(_("save modification - %s\n"), mbox);
1976
1977         mbox_fp = fopen(mbox, "rb+");
1978         mbox_lockwrite_file(mbox_fp, mbox);
1979
1980         mbox_cache_synchronize_from_file(mbox_fp, mbox, TRUE);
1981
1982         new = g_strconcat(mbox, ".", itos((int) mbox), NULL);
1983         new_fp = fopen(new, "wb");
1984
1985         if (change_file_mode_rw(new_fp, new) < 0) {
1986                 FILE_OP_ERROR(new, "chmod");
1987                 g_warning(_("can't change file mode\n"));
1988         }
1989
1990         mbox_lockwrite_file(new_fp, new);
1991
1992         result = TRUE;
1993
1994         count = 0;
1995         msg_list = mbox_cache_get_msg_list(mbox);
1996         for(l = msg_list ; l != NULL ; l = g_list_next(l)) {
1997                 struct _message * msg = (struct _message *) l->data;
1998                 if (!mbox_write_message(mbox_fp, new_fp, new, msg)) {
1999                         result = FALSE;
2000                         break;
2001                 }
2002                 count ++;
2003         }
2004
2005         unlink(mbox);
2006
2007         if (rename(new, mbox) == -1) {
2008                 g_warning(_("can't rename %s to %s\n"), new, mbox);
2009                 mbox_unlock_file(new_fp, new);
2010                 fclose(new_fp);
2011                 mbox_unlock_file(mbox_fp, mbox);
2012                 fclose(mbox_fp);
2013                 return -1;
2014         }
2015
2016         if (change_file_mode_rw(new_fp, mbox) < 0) {
2017                 FILE_OP_ERROR(new, "chmod");
2018                 g_warning(_("can't change file mode\n"));
2019         }
2020
2021         mbox_unlock_file(new_fp, new);
2022
2023         fclose(new_fp);
2024
2025         mbox_unlock_file(mbox_fp, mbox);
2026
2027         fclose(mbox_fp);
2028
2029         debug_print(_("%i messages written - %s\n"), count, mbox);
2030
2031         cache = mbox_cache_get_mbox(mbox);
2032
2033         if (cache != NULL)
2034                 cache->mtime = -1;
2035
2036         mbox_cache_synchronize(mbox, FALSE);
2037
2038         return result;
2039 }
2040
2041 static gboolean mbox_purge_deleted(gchar * mbox)
2042 {
2043         FILE * mbox_fp;
2044         FILE * new_fp;
2045         gchar * new;
2046         GList * l;
2047         gboolean result;
2048         gboolean modification = FALSE;
2049         GList * msg_list;
2050         gint count;
2051
2052         mbox_cache_synchronize(mbox, TRUE);
2053
2054         msg_list = mbox_cache_get_msg_list(mbox);
2055
2056         for(l = msg_list ; l != NULL ; l = g_list_next(l)) {
2057                 struct _message * msg = (struct _message *) l->data;
2058                 if (!MSG_IS_INVALID(msg->flags) && MSG_IS_REALLY_DELETED(msg->flags)) {
2059                         modification = TRUE;
2060                         break;
2061                 }
2062         }
2063
2064         if (!modification) {
2065                 debug_print(_("no deleted messages - %s\n"), mbox);
2066                 return FALSE;
2067         }
2068
2069         debug_print(_("purge deleted messages - %s\n"), mbox);
2070
2071         mbox_fp = fopen(mbox, "rb+");
2072         mbox_lockwrite_file(mbox_fp, mbox);
2073
2074         mbox_cache_synchronize_from_file(mbox_fp, mbox, TRUE);
2075
2076         new = g_strconcat(mbox, ".", itos((int) mbox), NULL);
2077         new_fp = fopen(new, "wb");
2078
2079         if (change_file_mode_rw(new_fp, new) < 0) {
2080                 FILE_OP_ERROR(new, "chmod");
2081                 g_warning(_("can't change file mode\n"));
2082         }
2083
2084         mbox_lockwrite_file(new_fp, new);
2085
2086         result = TRUE;
2087
2088         count = 0;
2089         msg_list = mbox_cache_get_msg_list(mbox);
2090         for(l = msg_list ; l != NULL ; l = g_list_next(l)) {
2091                 struct _message * msg = (struct _message *) l->data;
2092                 if (MSG_IS_INVALID(msg->flags) || !MSG_IS_REALLY_DELETED(msg->flags)) {
2093                         if (!mbox_write_message(mbox_fp, new_fp, new, msg)) {
2094                                 result = FALSE;
2095                                 break;
2096                         }
2097                         count ++;
2098                 }
2099         }
2100
2101         unlink(mbox);
2102
2103         if (rename(new, mbox) == -1) {
2104                 g_warning(_("can't rename %s to %s\n"), new, mbox);
2105                 mbox_unlock_file(new_fp, new);
2106                 fclose(new_fp);
2107                 mbox_unlock_file(mbox_fp, mbox);
2108                 fclose(mbox_fp);
2109                 return -1;
2110         }
2111
2112         if (change_file_mode_rw(new_fp, mbox) < 0) {
2113                 FILE_OP_ERROR(new, "chmod");
2114                 g_warning(_("can't change file mode\n"));
2115         }
2116
2117         mbox_unlock_file(new_fp, new);
2118
2119         fclose(new_fp);
2120
2121         mbox_unlock_file(mbox_fp, mbox);
2122
2123         fclose(mbox_fp);
2124
2125         debug_print(_("%i messages written - %s\n"), count, mbox);
2126
2127         mbox_cache_synchronize(mbox, FALSE);
2128
2129         return result;
2130 }
2131
2132 #define MAKE_DIR_IF_NOT_EXIST(dir) \
2133 { \
2134         if (!is_dir_exist(dir)) { \
2135                 if (is_file_exist(dir)) { \
2136                         g_warning(_("File `%s' already exists.\n" \
2137                                     "Can't create folder."), dir); \
2138                         return -1; \
2139                 } \
2140                 if (mkdir(dir, S_IRWXU) < 0) { \
2141                         FILE_OP_ERROR(dir, "mkdir"); \
2142                         return -1; \
2143                 } \
2144                 if (chmod(dir, S_IRWXU) < 0) \
2145                         FILE_OP_ERROR(dir, "chmod"); \
2146         } \
2147 }
2148
2149 gint mbox_create_tree(Folder *folder)
2150 {
2151         gchar *rootpath;
2152
2153         g_return_val_if_fail(folder != NULL, -1);
2154
2155         CHDIR_RETURN_VAL_IF_FAIL(get_home_dir(), -1);
2156         rootpath = LOCAL_FOLDER(folder)->rootpath;
2157         MAKE_DIR_IF_NOT_EXIST(rootpath);
2158         CHDIR_RETURN_VAL_IF_FAIL(rootpath, -1);
2159
2160         return 0;
2161 }
2162
2163 #undef MAKE_DIR_IF_NOT_EXIST
2164
2165 static gchar * mbox_get_new_path(FolderItem * parent, gchar * name)
2166 {
2167         gchar * path;
2168
2169         if (strchr(name, '/') == NULL) {
2170                 if (parent->path != NULL)
2171                         path = g_strconcat(parent->path, ".sbd", G_DIR_SEPARATOR_S, name, NULL);
2172                 else
2173                         path = g_strdup(name);
2174         }
2175         else
2176                 path = g_strdup(name);
2177
2178         return path;
2179 }
2180
2181 static gchar * mbox_get_folderitem_name(gchar * name)
2182 {
2183         gchar * foldername;
2184
2185         foldername = g_strdup(g_basename(name));
2186         
2187         return foldername;
2188 }
2189
2190 FolderItem *mbox_create_folder(Folder *folder, FolderItem *parent,
2191                                const gchar *name)
2192 {
2193         gchar * path;
2194         FolderItem *new_item;
2195         gchar * foldername;
2196
2197         g_return_val_if_fail(folder != NULL, NULL);
2198         g_return_val_if_fail(parent != NULL, NULL);
2199         g_return_val_if_fail(name != NULL, NULL);
2200
2201         path = mbox_get_new_path(parent, (gchar *) name);
2202
2203         foldername = mbox_get_folderitem_name((gchar *) name);
2204
2205         new_item = folder_item_new(folder, foldername, path);
2206         folder_item_append(parent, new_item);
2207
2208         if (!strcmp(name, "inbox")) {
2209                 new_item->stype = F_INBOX;
2210                 new_item->folder->inbox = new_item;
2211         } else if (!strcmp(name, "outbox")) {
2212                 new_item->stype = F_OUTBOX;
2213                 new_item->folder->outbox = new_item;
2214         } else if (!strcmp(name, "draft")) {
2215                 new_item->stype = F_DRAFT;
2216                 new_item->folder->draft = new_item;
2217         } else if (!strcmp(name, "queue")) {
2218                 new_item->stype = F_QUEUE;
2219                 new_item->folder->queue = new_item;
2220         } else if (!strcmp(name, "trash")) {
2221                 new_item->stype = F_TRASH;
2222                 new_item->folder->trash = new_item;
2223         }
2224         
2225         g_free(foldername);
2226         g_free(path);
2227         
2228         return new_item;
2229 }
2230
2231 gint mbox_rename_folder(Folder *folder, FolderItem *item, const gchar *name)
2232 {
2233         gchar * path;
2234         gchar * foldername;
2235
2236         g_return_val_if_fail(folder != NULL, -1);
2237         g_return_val_if_fail(item != NULL, -1);
2238         g_return_val_if_fail(item->path != NULL, -1);
2239         g_return_val_if_fail(name != NULL, -1);
2240
2241         path = mbox_get_new_path(item->parent, (gchar *) name);
2242         foldername = mbox_get_folderitem_name((gchar *) name);
2243
2244         if (rename(item->path, path) == -1) {
2245                 g_free(foldername);
2246                 g_free(path);
2247                 g_warning(_("Cannot rename folder item"));
2248
2249                 return -1;
2250         }
2251         else {
2252                 g_free(item->name);
2253                 g_free(item->path);
2254                 item->path = path;
2255                 item->name = foldername;
2256                 
2257                 return 0;
2258         }
2259 }
2260
2261 gint mbox_remove_folder(Folder *folder, FolderItem *item)
2262 {
2263         g_return_val_if_fail(folder != NULL, -1);
2264         g_return_val_if_fail(item != NULL, -1);
2265         g_return_val_if_fail(item->path != NULL, -1);
2266
2267         folder_item_remove(item);
2268         return 0;
2269 }
2270
2271 GSList *mbox_get_num_list(Folder *folder, FolderItem *item)
2272 {
2273         GSList *mlist;
2274         GList * l;
2275         FILE * fp;
2276         gchar * mbox_path;
2277
2278         mlist = NULL;
2279
2280         mbox_path = mbox_folder_get_path(item);
2281
2282         if (mbox_path == NULL)
2283                 return NULL;
2284
2285         mbox_purge_deleted(mbox_path);
2286
2287         fp = fopen(mbox_path, "rb");
2288         
2289         if (fp == NULL) {
2290                 g_free(mbox_path);
2291                 return NULL;
2292         }
2293
2294         mbox_lockread_file(fp, mbox_path);
2295
2296         mbox_cache_synchronize_from_file(fp, mbox_path, TRUE);
2297
2298         item->last_num = mbox_cache_get_count(mbox_path);
2299
2300         for(l = mbox_cache_get_msg_list(mbox_path) ; l != NULL ;
2301             l = g_list_next(l)) {
2302                 struct _message * msg;
2303
2304                 msg = (struct _message *) l->data;
2305
2306                 if (MSG_IS_INVALID(msg->flags) || !MSG_IS_REALLY_DELETED(msg->flags)) {
2307                         mlist = g_slist_append(mlist, GINT_TO_POINTER(msg->msgnum));
2308                 } else {
2309                         MSG_SET_PERM_FLAGS(msg->flags, MSG_REALLY_DELETED);
2310                 }
2311         }
2312
2313         mbox_unlock_file(fp, mbox_path);
2314
2315         g_free(mbox_path);
2316
2317         fclose(fp);
2318
2319         return mlist;
2320 }
2321
2322 MsgInfo *mbox_fetch_msginfo(Folder *folder, FolderItem *item, gint num)
2323 {
2324         gchar *mbox_path;
2325         struct _message *msg;
2326         FILE *src;
2327         MsgInfo *msginfo;
2328         
2329         g_return_val_if_fail(folder != NULL, NULL);
2330         g_return_val_if_fail(item != NULL, NULL);
2331
2332         mbox_path = mbox_folder_get_path(item);
2333
2334         g_return_val_if_fail(mbox_path != NULL, NULL);
2335         
2336         src = fopen(mbox_path, "rb");
2337         if (src == NULL) {
2338                 g_free(mbox_path);
2339                 return NULL;
2340         }
2341         mbox_lockread_file(src, mbox_path);
2342         mbox_cache_synchronize_from_file(src, mbox_path, TRUE);
2343
2344         msg = mbox_cache_get_msg(mbox_path, num);
2345         if (msg == NULL) {
2346                 mbox_unlock_file(src, mbox_path);
2347                 fclose(src);
2348                 g_free(mbox_path);
2349                 return NULL;
2350         }
2351         
2352         fseek(src, msg->header, SEEK_SET);
2353         msginfo = mbox_parse_msg(src, msg, item);
2354
2355         mbox_unlock_file(src, mbox_path);
2356         fclose(src);
2357         g_free(mbox_path);
2358
2359         return msginfo;
2360 }
2361
2362 gboolean mbox_check_msgnum_validity(Folder *folder, FolderItem *item)
2363 {
2364         mboxcache * old_cache;
2365         gboolean scan_new = TRUE;
2366         struct stat s;
2367         gchar *filename;
2368
2369         filename = mbox_folder_get_path(item);
2370         
2371         old_cache = mbox_cache_get_mbox(filename);
2372
2373         if (old_cache != NULL) {
2374                 if (stat(filename, &s) < 0) {
2375                         FILE_OP_ERROR(filename, "stat");
2376                 } else if (old_cache->mtime == s.st_mtime) {
2377                         debug_print("Folder is not modified.\n");
2378                         scan_new = FALSE;
2379                 }
2380         }
2381
2382         g_free(filename);
2383         
2384         return !scan_new;
2385 }
2386