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