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