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