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