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