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