sync with sylpheed 0.5.0pre4
[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_create_parent(const gchar * path)
29 {
30         if (!is_file_exist(path)) {
31                 gchar * new_path;
32
33                 new_path = g_dirname(path);
34                 if (new_path[strlen(new_path) - 1] == G_DIR_SEPARATOR)
35                         new_path[strlen(new_path) - 1] = '\0';
36
37                 if (!is_dir_exist(new_path))
38                         make_dir_hier(new_path);
39                 g_free(new_path);
40                 
41         }
42 }
43
44
45 static gchar *mbox_folder_get_path(FolderItem *item)
46 {
47         gchar *folder_path;
48         gchar *path;
49
50         g_return_val_if_fail(item != NULL, NULL);
51
52         if (item->path && item->path[0] == G_DIR_SEPARATOR) {
53                 mbox_folder_create_parent(item->path);
54                 return g_strdup(item->path);
55         }
56
57         folder_path = g_strdup(LOCAL_FOLDER(item->folder)->rootpath);
58         g_return_val_if_fail(folder_path != NULL, NULL);
59
60         if (folder_path[0] == G_DIR_SEPARATOR) {
61                 if (item->path) {
62                         path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
63                                            item->path, NULL);
64                 }
65                 else
66                         path = g_strdup(folder_path);
67         } else {
68                 if (item->path)
69                         path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
70                                            folder_path, G_DIR_SEPARATOR_S,
71                                            item->path, NULL);
72                 else
73                         path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
74                                            folder_path, NULL);
75         }
76
77         g_free(folder_path);
78         
79         mbox_folder_create_parent(path);
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                 if ((msg2->messageid == NULL) ||
871                     (msg2->fromspace == NULL))
872                         continue;
873
874                 for(l = new_msg_list ; l != NULL ; l = g_list_next(l)) {
875                         struct _message * msg = l->data;
876                         
877                         if ((msg->messageid == NULL) ||
878                             (msg->fromspace == NULL))
879                                 continue;
880
881                         if ((strcmp(msg->messageid, msg2->messageid) == 0) &&
882                             (strcmp(msg->fromspace, msg2->fromspace) == 0)) {
883                                 if ((msg2->flags & MSG_PERMANENT_FLAG_MASK) !=
884                                     (msg2->old_flags &
885                                      MSG_PERMANENT_FLAG_MASK)) {
886                                         msg->flags = msg2->flags;
887                                         break;
888                                 }
889                         }
890                 }
891         }
892 }
893
894 static void mbox_cache_synchronize(gchar * filename, gboolean sync)
895 {
896         mboxcache * new_cache;
897         mboxcache * old_cache;
898         gboolean scan_new = TRUE;
899         struct stat s;
900
901         old_cache = mbox_cache_get_mbox(filename);
902
903         if (old_cache != NULL) {
904                 if (stat(filename, &s) < 0) {
905                         FILE_OP_ERROR(filename, "stat");
906                 } else if (old_cache->mtime == s.st_mtime) {
907                         debug_print("Folder is not modified.\n");
908                         scan_new = FALSE;
909                 }
910         }
911
912         if (scan_new) {
913                 GList * l;
914
915                 /*              
916                 if (strstr(filename, "trash") == 0)
917                         printf("old_cache: %p %s\n", old_cache, filename);
918                         if (old_cache) {
919                                 printf("begin old\n");
920                                 for(l = old_cache->mf->msg_list ; l != NULL ;
921                                     l = g_list_next(l)) {
922                                         struct _message * msg = l->data;
923                                         printf("%p\n", msg);
924                                 }
925                                 printf("end old\n");
926                         }
927                 */              
928
929                 new_cache = mbox_cache_read_mbox(filename);
930
931                 /*
932                 if (strstr(filename, "trash") == 0) 
933                         printf("new_cache: %p %s\n", new_cache, filename);
934                         if (new_cache) {
935                                 printf("begin new\n");
936                                 for(l = new_cache->mf->msg_list ; l != NULL ;
937                                     l = g_list_next(l)) {
938                                         struct _message * msg = l->data;
939                                         printf("%p\n", msg);
940                                 }
941                                 printf("end new\n");
942                         }
943                 */
944
945                 if (!new_cache)
946                         return;
947
948                 if (sync && new_cache && old_cache)
949                         mbox_cache_synchronize_lists(old_cache->mf->msg_list,
950                                                      new_cache->mf->msg_list);
951
952                 if (old_cache != NULL)
953                         mbox_cache_free_mbox(old_cache);
954
955                 if (new_cache) {
956                         mbox_cache_insert_mbox(new_cache);
957                         /*
958                         printf("insert %p %s\n", new_cache, new_cache->filename);
959                         printf("inserted %s %p\n", filename,
960                                mbox_cache_get_mbox(filename));
961                         */
962                 }
963         }
964 }
965
966 static void mbox_cache_synchronize_from_file(FILE * fp, gchar * filename,
967                                              gboolean sync)
968 {
969         mboxcache * new_cache;
970         mboxcache * old_cache;
971         gboolean scan_new = TRUE;
972         struct stat s;
973
974         old_cache = mbox_cache_get_mbox(filename);
975
976         if (old_cache != NULL) {
977                 if (stat(filename, &s) < 0) {
978                         FILE_OP_ERROR(filename, "stat");
979                 } else if (old_cache->mtime == s.st_mtime) {
980                         debug_print("Folder is not modified.\n");
981                         scan_new = FALSE;
982                 }
983         }
984
985
986         if (scan_new) {
987
988                 /*
989                 GList * l;
990
991                 if (strstr(filename, "trash") == 0)
992                         printf("old_cache: %p %s\n", old_cache, filename);
993
994                         if (old_cache) {
995                                 printf("begin old\n");
996                                 for(l = old_cache->mf->msg_list ; l != NULL ;
997                                     l = g_list_next(l)) {
998                                         struct _message * msg = l->data;
999                                         printf("%p\n", msg);
1000                                 }
1001                                 printf("end old\n");
1002                         }
1003                 */
1004                 
1005                 new_cache = mbox_cache_read_mbox_from_file(fp, filename);
1006
1007                 /*
1008                 if (strstr(filename, "trash") == 0) 
1009                         printf("new_cache: %p %s\n", new_cache, filename);
1010
1011                         if (new_cache) {
1012                                 printf("begin new\n");
1013                                 for(l = new_cache->mf->msg_list ; l != NULL ;
1014                                     l = g_list_next(l)) {
1015                                         struct _message * msg = l->data;
1016                                         printf("%p\n", msg);
1017                                 }
1018                                 printf("end new\n");
1019                         }
1020                 */
1021
1022                 if (!new_cache)
1023                         return;
1024
1025                 if (sync && new_cache && old_cache)
1026                         mbox_cache_synchronize_lists(old_cache->mf->msg_list,
1027                                                      new_cache->mf->msg_list);
1028
1029                 if (old_cache != NULL)
1030                         mbox_cache_free_mbox(old_cache);
1031
1032                 if (new_cache) {
1033                         mbox_cache_insert_mbox(new_cache);
1034                         /*
1035                         printf("insert %p %s\n", new_cache, new_cache->filename);
1036                         printf("inserted %s %p\n", filename,
1037                                mbox_cache_get_mbox(filename));
1038                         */
1039                 }
1040         }
1041 }
1042
1043 gboolean mbox_cache_msg_fetched(gchar * filename, gint num)
1044 {
1045         struct _message * msg;
1046         mboxcache * cache;
1047
1048         cache = mbox_cache_get_mbox(filename);
1049
1050         if (cache == NULL)
1051                 return FALSE;
1052
1053         msg = (struct _message *) g_ptr_array_index(cache->tab_mf,
1054                                                     num - 1);
1055         if (msg == NULL)
1056                 return FALSE;
1057
1058         return msg->fetched;
1059 }
1060
1061 void mbox_cache_msg_set_fetched(gchar * filename, gint num)
1062 {
1063         struct _message * msg;
1064         mboxcache * cache;
1065
1066         cache = mbox_cache_get_mbox(filename);
1067
1068         if (cache == NULL)
1069                 return;
1070
1071         msg = (struct _message *) g_ptr_array_index(cache->tab_mf,
1072                                                     num - 1);
1073         if (msg == NULL)
1074                 return;
1075
1076         msg->fetched = TRUE;
1077 }
1078
1079 struct _message * mbox_cache_get_msg(gchar * filename, gint num)
1080 {
1081         mboxcache * cache;
1082
1083         cache = mbox_cache_get_mbox(filename);
1084
1085         if (cache == NULL) {
1086                 return NULL;
1087         }
1088
1089         return (struct _message *) g_ptr_array_index(cache->tab_mf,
1090                                                      num - 1);
1091 }
1092
1093
1094 /**********************************************************/
1095 /*                                                        */
1096 /*                   mbox operations                      */
1097 /*                                                        */
1098 /**********************************************************/
1099
1100
1101 GSList *mbox_get_msg_list(Folder *folder, FolderItem *item, gboolean use_cache)
1102 {
1103         GSList *mlist;
1104         MsgInfo * msginfo;
1105         GList * l;
1106         FILE * fp;
1107         gchar * mbox_path;
1108
1109 #ifdef MEASURE_TIME
1110         struct timeval tv_before, tv_after, tv_result;
1111
1112         gettimeofday(&tv_before, NULL);
1113 #endif
1114
1115         mlist = NULL;
1116
1117         mbox_path = mbox_folder_get_path(item);
1118
1119         if (mbox_path == NULL)
1120                 return NULL;
1121
1122         mbox_purge_deleted(mbox_path);
1123
1124         fp = fopen(mbox_path, "r");
1125         
1126         if (fp == NULL) {
1127                 g_free(mbox_path);
1128                 return NULL;
1129         }
1130
1131         mbox_lockread_file(fp, mbox_path);
1132
1133         mbox_cache_synchronize_from_file(fp, mbox_path, TRUE);
1134
1135         item->last_num = mbox_cache_get_count(mbox_path);
1136
1137         for(l = mbox_cache_get_msg_list(mbox_path) ; l != NULL ;
1138             l = g_list_next(l)) {
1139                 struct _message * msg;
1140
1141                 msg = (struct _message *) l->data;
1142
1143                 if (msg->flags == -1 || !MSG_IS_REALLY_DELETED(msg->flags)) {
1144                         fseek(fp, msg->header, SEEK_SET);
1145
1146                         msginfo = mbox_parse_msg(fp, msg, item);
1147
1148                         if (msg->flags != -1)
1149                                 msginfo->flags = msg->flags;
1150                         else {
1151                                 msg->old_flags = msginfo->flags;
1152                                 msg->flags = msginfo->flags;
1153                         }
1154
1155                         mlist = g_slist_append(mlist, msginfo);
1156                 }
1157                 else {
1158                         msg->flags = MSG_REALLY_DELETED;
1159                 }
1160         }
1161
1162         mbox_unlock_file(fp, mbox_path);
1163
1164         g_free(mbox_path);
1165
1166         fclose(fp);
1167
1168 #ifdef MEASURE_TIME
1169         gettimeofday(&tv_after, NULL);
1170
1171         timersub(&tv_after, &tv_before, &tv_result);
1172         g_print("mbox_get_msg_list: %s: elapsed time: %ld.%06ld sec\n",
1173                 mbox_path, tv_result.tv_sec, tv_result.tv_usec);
1174 #endif
1175
1176         return mlist;
1177 }
1178
1179 static gboolean mbox_extract_msg(FolderItem * item, gint msgnum,
1180                                  gchar * dest_filename)
1181 {
1182         struct _message * msg;
1183         gint offset;
1184         gint max_offset;
1185         gint size;
1186         FILE * src;
1187         FILE * dest;
1188         gboolean err;
1189         /*      GList * msg_list;*/
1190         gboolean already_fetched;
1191         gchar * mbox_path;
1192
1193         mbox_path = mbox_folder_get_path(item);
1194
1195         if (mbox_path == NULL)
1196                 return FALSE;
1197
1198         src = fopen(mbox_path, "r");
1199         if (src == NULL) {
1200                 g_free(mbox_path);
1201                 return FALSE;
1202         }
1203
1204         mbox_lockread_file(src, mbox_path);
1205
1206         mbox_cache_synchronize_from_file(src, mbox_path, TRUE);
1207
1208         already_fetched = mbox_cache_msg_fetched(mbox_path, msgnum);
1209
1210         if (already_fetched) {
1211                 mbox_unlock_file(src, mbox_path);
1212                 fclose(src);
1213                 g_free(mbox_path);
1214                 return TRUE;
1215         }
1216
1217         msg = mbox_cache_get_msg(mbox_path, msgnum);
1218
1219         if (msg == NULL) {
1220                 mbox_unlock_file(src, mbox_path);
1221                 fclose(src);
1222                 g_free(mbox_path);
1223                 return FALSE;
1224         }
1225
1226         offset = msg->offset;
1227         max_offset = msg->end;
1228         
1229         size = max_offset - offset;
1230
1231         fseek(src, offset, SEEK_SET);
1232
1233         dest = fopen(dest_filename, "w");
1234         if (dest == NULL) {
1235                 mbox_unlock_file(src, mbox_path);
1236                 fclose(src);
1237                 g_free(mbox_path);
1238                 return FALSE;
1239         }
1240
1241         if (change_file_mode_rw(dest, dest_filename) < 0) {
1242                 FILE_OP_ERROR(dest_filename, "chmod");
1243                 g_warning(_("can't change file mode\n"));
1244         }
1245
1246         if (!mbox_write_data(src, dest, dest_filename, size)) {
1247                 mbox_unlock_file(src, mbox_path);
1248                 fclose(dest);
1249                 fclose(src);
1250                 unlink(dest_filename);
1251                 g_free(mbox_path);
1252                 return FALSE;
1253         }
1254
1255         err = FALSE;
1256
1257         if (ferror(src)) {
1258                 FILE_OP_ERROR(mbox_path, "fread");
1259                 err = TRUE;
1260         }
1261
1262         mbox_cache_msg_set_fetched(mbox_path, msgnum);
1263
1264         if (fclose(dest) == -1) {
1265                 FILE_OP_ERROR(dest_filename, "fclose");
1266                 err = TRUE;
1267         }
1268
1269         mbox_unlock_file(src, mbox_path);
1270
1271         if (fclose(src) == -1) {
1272                 FILE_OP_ERROR(mbox_path, "fclose");
1273                 err = TRUE;
1274         }
1275
1276         g_free(mbox_path);
1277
1278         if (err) {
1279                 unlink(dest_filename);
1280                 return FALSE;
1281         }
1282
1283         return TRUE;
1284 }
1285
1286 gchar *mbox_fetch_msg(Folder *folder, FolderItem *item, gint num)
1287 {
1288         gchar *path;
1289         gchar *filename;
1290         
1291         g_return_val_if_fail(item != NULL, NULL);
1292         g_return_val_if_fail(num > 0 && num <= item->last_num, NULL);
1293
1294         path = folder_item_get_path(item);
1295         if (!is_dir_exist(path))
1296                 make_dir_hier(path);
1297
1298         filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(num), NULL);
1299
1300         g_free(path);
1301
1302         if (!mbox_extract_msg(item, num, filename)) {
1303                 g_free(filename);
1304                 return NULL;
1305         }
1306
1307         return filename;
1308 }
1309
1310 gint mbox_add_msg(Folder *folder, FolderItem *dest, const gchar *file,
1311                   gboolean remove_source)
1312 {
1313         FILE * src_fp;
1314         FILE * dest_fp;
1315         gchar buf[BUFSIZ];
1316         gint old_size;
1317         gint n_read;
1318         gboolean err;
1319         gchar * mbox_path;
1320         gchar from_line[MSGBUFSIZE];
1321
1322         if (dest->last_num < 0) {
1323                 mbox_scan_folder(folder, dest);
1324                 if (dest->last_num < 0) return -1;
1325         }
1326
1327         src_fp = fopen(file, "r");
1328         if (src_fp == NULL) {
1329                 return -1;
1330         }
1331
1332         mbox_path = mbox_folder_get_path(dest);
1333         if (mbox_path == NULL)
1334                 return -1;
1335
1336         dest_fp = fopen(mbox_path, "a");
1337         if (dest_fp == NULL) {
1338                 fclose(src_fp);
1339                 g_free(mbox_path);
1340                 return -1;
1341         }
1342
1343         if (change_file_mode_rw(dest_fp, mbox_path) < 0) {
1344                 FILE_OP_ERROR(mbox_path, "chmod");
1345                 g_warning(_("can't change file mode\n"));
1346         }
1347
1348         old_size = ftell(dest_fp);
1349
1350         mbox_lockwrite_file(dest_fp, mbox_path);
1351
1352         if (fgets(from_line, sizeof(from_line), src_fp) == NULL) {
1353                 mbox_unlock_file(dest_fp, mbox_path);
1354                 g_warning(_("unvalid file - %s.\n"), file);
1355                 fclose(dest_fp);
1356                 fclose(src_fp);
1357                 g_free(mbox_path);
1358                 return -1;
1359         }
1360         
1361         if (strncmp(from_line, "From ", 5) != 0) {
1362                 struct stat s;
1363
1364                 if (stat(file, &s) < 0) {
1365                         mbox_unlock_file(dest_fp, mbox_path);
1366                         g_warning(_("unvalid file - %s.\n"), file);
1367                         fclose(dest_fp);
1368                         fclose(src_fp);
1369                         g_free(mbox_path);
1370                         return -1;
1371                 }
1372
1373                 fprintf(dest_fp, "From - %s", ctime(&s.st_mtime));
1374         }
1375
1376         fputs(from_line, dest_fp);
1377
1378         while (1) {
1379                 n_read = fread(buf, 1, sizeof(buf), src_fp);
1380                 if ((n_read < (gint) sizeof(buf)) && ferror(src_fp))
1381                         break;
1382                 if (fwrite(buf, n_read, 1, dest_fp) < 1) {
1383                         mbox_unlock_file(dest_fp, mbox_path);
1384                         g_warning(_("writing to %s failed.\n"), mbox_path);
1385                         ftruncate(fileno(dest_fp), old_size);
1386                         fclose(dest_fp);
1387                         fclose(src_fp);
1388                         g_free(mbox_path);
1389                         return -1;
1390                 }
1391
1392                 if (n_read < (gint) sizeof(buf))
1393                         break;
1394         }
1395
1396         err = FALSE;
1397
1398         if (ferror(src_fp)) {
1399                 FILE_OP_ERROR(mbox_path, "fread");
1400         }
1401
1402         mbox_unlock_file(dest_fp, mbox_path);
1403
1404         if (fclose(src_fp) == -1) {
1405                 FILE_OP_ERROR(file, "fclose");
1406                 err = TRUE;
1407         }
1408
1409         if (fclose(dest_fp) == -1) {
1410                 FILE_OP_ERROR(mbox_path, "fclose");
1411                 g_free(mbox_path);
1412                 return -1;
1413         }
1414
1415         if (err) {
1416                 ftruncate(fileno(dest_fp), old_size);
1417                 g_free(mbox_path);
1418                 return -1;
1419         }
1420
1421         if (remove_source) {
1422                 if (unlink(file) < 0)
1423                         FILE_OP_ERROR(file, "unlink");
1424         }
1425
1426         g_free(mbox_path);
1427
1428         dest->last_num++;
1429         return dest->last_num;
1430
1431 }
1432
1433 gint mbox_remove_msg(Folder *folder, FolderItem *item, gint num)
1434 {
1435         struct _message * msg;
1436         gchar * mbox_path;
1437
1438         mbox_path = mbox_folder_get_path(item);
1439         if (mbox_path == NULL)
1440                 return -1;
1441
1442         mbox_cache_synchronize(mbox_path, TRUE);
1443
1444         msg = mbox_cache_get_msg(mbox_path, num);
1445
1446         g_free(mbox_path);
1447         
1448         if (msg != NULL)
1449                 MSG_SET_FLAGS(msg->flags, MSG_REALLY_DELETED);
1450
1451         return 0;
1452 }
1453
1454 gint mbox_remove_all_msg(Folder *folder, FolderItem *item)
1455 {
1456         FILE * fp;
1457         gchar * mbox_path;
1458
1459         mbox_path = mbox_folder_get_path(item);
1460         if (mbox_path == NULL)
1461                 return -1;
1462
1463         fp = fopen(mbox_path, "w");
1464         if (fp == NULL) {
1465                 g_free(mbox_path);
1466                 return -1;
1467         }
1468
1469         fclose(fp);
1470
1471         g_free(mbox_path);
1472
1473         return 0;
1474 }
1475
1476 /*
1477 gint mbox_move_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1478 {
1479         gchar * filename;
1480         gint msgnum;
1481
1482         filename = mbox_fetch_msg(folder, msginfo->folder, msginfo->msgnum);
1483         if (filename == NULL)
1484                 return -1;
1485
1486         msgnum = mbox_add_msg(folder, dest, filename, TRUE);
1487
1488         if (msgnum != -1) {
1489                 MSG_SET_FLAGS(msginfo->flags, MSG_REALLY_DELETED);
1490                 mbox_change_flags(folder, msginfo->folder, msginfo);
1491         }
1492
1493         return msgnum;
1494 }
1495
1496 gint mbox_move_msgs_with_dest(Folder *folder, FolderItem *dest, GSList *msglist)
1497 {
1498         GSList * l;
1499         gchar * mbox_path = NULL;
1500
1501         for(l = msglist ; l != NULL ; l = g_slist_next(l)) {
1502                 MsgInfo * msginfo = (MsgInfo *) l->data;
1503
1504                 if (msginfo->folder && mbox_path == NULL)
1505                         mbox_path = mbox_folder_get_path(msginfo->folder);
1506
1507                 mbox_move_msg(folder, dest, msginfo);
1508         }
1509
1510         if (mbox_path) {
1511                 mbox_cache_synchronize(mbox_path);
1512                 g_free(mbox_path);
1513         }
1514
1515         mbox_path = mbox_folder_get_path(dest);
1516         mbox_cache_synchronize(mbox_path);
1517         g_free(mbox_path);
1518
1519         return dest->last_num;
1520 }
1521 */
1522
1523 /*
1524 gint mbox_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1525 {
1526         gchar * filename;
1527         gint msgnum;
1528
1529         filename = mbox_fetch_msg(folder, msginfo->folder, msginfo->msgnum);
1530         if (filename == NULL)
1531                 return -1;
1532
1533         msgnum = mbox_add_msg(folder, dest, filename, FALSE);
1534
1535         return msgnum;
1536 }
1537
1538 gint mbox_copy_msgs_with_dest(Folder *folder, FolderItem *dest, GSList *msglist)
1539 {
1540         GSList * l;
1541         gchar * mbox_path = NULL;
1542
1543         for(l = msglist ; l != NULL ; l = g_slist_next(l)) {
1544                 MsgInfo * msginfo = (MsgInfo *) l->data;
1545
1546                 if (msginfo->folder && mbox_path == NULL)
1547                         mbox_path = mbox_folder_get_path(msginfo->folder);
1548
1549                 mbox_copy_msg(folder, dest, msginfo);
1550         }
1551
1552         if (mbox_path) {
1553                 mbox_cache_synchronize(mbox_path);
1554                 g_free(mbox_path);
1555         }
1556
1557         mbox_path = mbox_folder_get_path(dest);
1558         mbox_cache_synchronize(mbox_path);
1559         g_free(mbox_path);
1560
1561         return dest->last_num;
1562 }
1563 */
1564
1565 struct _copy_flags_info
1566 {
1567         gint num;
1568         MsgFlags flags;
1569 };
1570
1571 typedef struct _copy_flags_info CopyFlagsInfo;
1572
1573 GSList * copy_flags_data = NULL;
1574
1575 gint mbox_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1576 {
1577         Folder * src_folder;
1578         gchar * filename;
1579         gint num;
1580         gchar * destdir;
1581         gchar * mbox_path;
1582         struct _message * msg;
1583         CopyFlagsInfo * flags_info;
1584
1585         src_folder = msginfo->folder->folder;
1586         
1587         g_return_val_if_fail(src_folder->fetch_msg != NULL, -1);
1588         
1589         /*
1590         mbox_path = mbox_folder_get_path(msginfo->folder);
1591         mbox_rewrite(mbox_path);
1592         g_free(mbox_path);
1593         */
1594
1595         filename = src_folder->fetch_msg(src_folder,
1596                                          msginfo->folder,
1597                                          msginfo->msgnum);
1598         if (filename == NULL)
1599                 return -1;
1600
1601         num = folder->add_msg(folder, dest, filename, FALSE);
1602
1603         /*
1604         mbox_path = mbox_folder_get_path(dest);
1605         msg = mbox_cache_get_msg(mbox_path, num);
1606         if (msg != NULL)
1607                 msg->flags = msginfo->flags;
1608         g_free(mbox_path);
1609         */
1610
1611         if (num == -1)
1612                 return -1;
1613
1614         flags_info = g_new0(CopyFlagsInfo, 1);
1615         flags_info->num = num;
1616         flags_info->flags = msginfo->flags;
1617         copy_flags_data = g_slist_append(copy_flags_data, flags_info);
1618
1619         return num;
1620 }
1621
1622 void mbox_finished_copy(Folder *folder, FolderItem *dest)
1623 {
1624         gchar * mbox_path;
1625         GSList * l;
1626         mboxcache * cache;
1627         
1628         mbox_path = mbox_folder_get_path(dest);
1629         if (mbox_path == NULL)
1630                 return;
1631
1632         mbox_cache_synchronize(mbox_path, TRUE);
1633
1634         for(l = copy_flags_data ; l != NULL ; l = g_slist_next(l)) {
1635                 CopyFlagsInfo * flags_info = l->data;
1636                 struct _message * msg;
1637
1638                 msg = mbox_cache_get_msg(mbox_path, flags_info->num);
1639                 if (msg != NULL)
1640                         msg->flags = flags_info->flags;
1641                 g_free(flags_info);
1642         }
1643
1644         if (copy_flags_data != NULL) {
1645                 cache = mbox_cache_get_mbox(mbox_path);
1646                 cache->modification = TRUE;
1647         }
1648
1649         g_slist_free(copy_flags_data);
1650         copy_flags_data = NULL;
1651
1652         mbox_rewrite(mbox_path);
1653
1654         g_free(mbox_path);
1655 }
1656
1657 void mbox_scan_folder(Folder *folder, FolderItem *item)
1658 {
1659         gchar *mbox_path;
1660         gint n_msg;
1661         mboxcache * cached;
1662         GList * l;
1663
1664         mbox_path = mbox_folder_get_path(item);
1665         if (mbox_path == NULL)
1666                 return;
1667
1668         mbox_cache_synchronize(mbox_path, TRUE);
1669
1670         cached = mbox_cache_get_mbox(mbox_path);
1671
1672         if (cached == NULL) {
1673                 item->new = 0;
1674                 item->unread = 0;
1675                 item->total = 0;
1676                 item->last_num = 0;
1677                 g_free(mbox_path);
1678                 return;
1679         }
1680
1681         n_msg = mbox_cache_get_count(mbox_path);
1682
1683         if (n_msg == 0) {
1684                 item->new = item->unread = item->total = 0;
1685         }
1686         else {
1687                 gint new = 0;
1688                 gint unread = 0;
1689                 gint total = 0;
1690
1691                 for(l = mbox_cache_get_msg_list(mbox_path) ; l != NULL ;
1692                     l = g_list_next(l)) {
1693                         struct _message * msg = (struct _message *) l->data;
1694                         if (!MSG_IS_REALLY_DELETED(msg->flags))
1695                                 total ++;
1696                         if (MSG_IS_NEW(msg->flags))
1697                                 new ++;
1698                         if (MSG_IS_UNREAD(msg->flags))
1699                                 unread ++;
1700                 }
1701                 
1702                 item->new = new;
1703                 item->unread = unread;
1704                 item->total = total;
1705         }
1706
1707         debug_print(_("Last number in dir %s = %d\n"), mbox_path,
1708                     item->total);
1709         item->last_num = n_msg;
1710         g_free(mbox_path);
1711 }
1712
1713 gchar * mbox_get_virtual_path(FolderItem * item)
1714 {
1715         if (item == NULL)
1716                 return NULL;
1717
1718         if (item->parent == NULL) {
1719                 return NULL;
1720         }
1721         else {
1722                 gchar * parent_path;
1723                 gchar * result_path;
1724
1725                 parent_path = mbox_get_virtual_path(item->parent);
1726                 if (parent_path == NULL)
1727                         result_path = g_strdup(item->name);
1728                 else
1729                         result_path = g_strconcat(parent_path,
1730                                                   G_DIR_SEPARATOR_S,
1731                                                   item->name, NULL);
1732                 g_free(parent_path);
1733
1734                 return result_path;
1735         }
1736 }
1737
1738 static gboolean mbox_write_data(FILE * mbox_fp, FILE * new_fp,
1739                                 gchar * new_filename, gint size)
1740 {       
1741         gint n_read;
1742         gint pos;
1743         gchar buf[BUFSIZ];
1744         gint max;
1745
1746         pos = 0;
1747         while (pos < size) {
1748                 if ((size - pos) > (gint) sizeof(buf))
1749                         max = sizeof(buf);
1750                 else
1751                         max = (size - pos);
1752                 
1753                 n_read = fread(buf, 1, max, mbox_fp);
1754
1755                 if (n_read < max && ferror(mbox_fp)) {
1756                         return FALSE;
1757                 }
1758                 if (fwrite(buf, n_read, 1, new_fp) < 1) {
1759                         g_warning(_("writing to %s failed.\n"), new_filename);
1760                         return FALSE;
1761                 }
1762                 
1763                 if (n_read != -1)
1764                         pos += n_read;
1765                 
1766                 if (n_read < max)
1767                         break;
1768         }
1769         return TRUE;
1770 }
1771
1772 static gboolean mbox_write_message(FILE * mbox_fp, FILE * new_fp,
1773                                    gchar * new_filename,
1774                                    struct _message * msg)
1775 {
1776         gint size;
1777         GPtrArray * headers;
1778         gint i;
1779         
1780         fseek(mbox_fp, msg->header, SEEK_SET);
1781
1782         headers = procheader_get_header_array_asis(mbox_fp);
1783
1784         for (i = 0; i < (gint) headers->len; i++) {
1785                 Header * h = g_ptr_array_index(headers, i);
1786                 
1787                 if (!procheader_headername_equal(h->name, 
1788                                                  "Status") &&
1789                     !procheader_headername_equal(h->name, 
1790                                                  "X-Status")) {
1791                         fwrite(h->name, strlen(h->name),
1792                                1, new_fp);
1793                         if (h->name[strlen(h->name) - 1] != ' ')
1794                                 fwrite(" ", 1, 1, new_fp);
1795                         fwrite(h->body, strlen(h->body),
1796                                1, new_fp);
1797                         fwrite("\n", 1, 1, new_fp);
1798                 }
1799                 procheader_header_free(h);
1800                 g_ptr_array_remove_index(headers, i);
1801                 i--;
1802         }
1803
1804         g_ptr_array_free(headers, FALSE);
1805
1806         if (msg->flags != -1) {
1807                 /* Status header */
1808                 fwrite("Status: ", strlen("Status: "), 1, new_fp);
1809                 if (!MSG_IS_UNREAD(msg->flags))
1810                         fwrite("R", 1, 1, new_fp);
1811                 fwrite("O", 1, 1, new_fp);
1812                 fwrite("\n", 1, 1, new_fp);
1813                 
1814                 /* X-Status header */
1815                 if (msg->flags & 
1816                     (MSG_REALLY_DELETED | MSG_MARKED | MSG_DELETED
1817                      | MSG_REPLIED | MSG_FORWARDED)) {
1818                         fwrite("X-Status: ", strlen("X-Status: "), 1, new_fp);
1819                         if (MSG_IS_REALLY_DELETED(msg->flags))
1820                                 fwrite("D", 1, 1, new_fp); /* really deleted */
1821                         else {
1822                                 if (MSG_IS_MARKED(msg->flags))
1823                                         fwrite("F", 1, 1, new_fp);
1824                                 if (MSG_IS_DELETED(msg->flags))
1825                                         fwrite("d", 1, 1, new_fp);
1826                                 if (MSG_IS_REPLIED(msg->flags))
1827                                         fwrite("r", 1, 1, new_fp);
1828                                 if (MSG_IS_FORWARDED(msg->flags))
1829                                         fwrite("f", 1, 1, new_fp);
1830                         }
1831                         fwrite("\n", 1, 1, new_fp);
1832                 }
1833         }
1834
1835         fwrite("\n", 1, 1, new_fp);
1836
1837         size = msg->end - msg->content;
1838         fseek(mbox_fp, msg->content, SEEK_SET);
1839
1840         return mbox_write_data(mbox_fp, new_fp, new_filename, size);
1841 }
1842
1843 void mbox_update_mark(Folder * folder, FolderItem * item)
1844 {
1845         gchar * mbox_path;
1846
1847         mbox_path = mbox_folder_get_path(item);
1848         if (mbox_path == NULL)
1849                 return;
1850
1851         mbox_rewrite(mbox_path);
1852         g_free(mbox_path);
1853 }
1854
1855 void mbox_change_flags(Folder * folder, FolderItem * item, MsgInfo * info)
1856 {
1857         struct _message * msg;
1858         mboxcache * cache;
1859         gchar * mbox_path;
1860
1861         mbox_path = mbox_folder_get_path(item);
1862         if (mbox_path == NULL)
1863                 return;
1864
1865         msg = mbox_cache_get_msg(mbox_path, info->msgnum);
1866
1867         cache = mbox_cache_get_mbox(mbox_path);
1868
1869         g_free(mbox_path);
1870
1871         if ((msg == NULL) || (cache == NULL))
1872                 return;
1873
1874         msg->flags = info->flags;
1875
1876         cache->modification = TRUE;
1877 }
1878
1879
1880 static gboolean mbox_rewrite(gchar * mbox)
1881 {
1882         FILE * mbox_fp;
1883         FILE * new_fp;
1884         gchar * new;
1885         GList * l;
1886         gboolean result;
1887         GList * msg_list;
1888         gint count;
1889         mboxcache * cache;
1890
1891         msg_list = mbox_cache_get_msg_list(mbox);
1892
1893         cache = mbox_cache_get_mbox(mbox);
1894         if (cache == NULL)
1895                 return FALSE;
1896
1897         if (!cache->modification) {
1898                 debug_print(_("no modification - %s\n"), mbox);
1899                 return FALSE;
1900         }
1901
1902         debug_print(_("save modification - %s\n"), mbox);
1903
1904         mbox_fp = fopen(mbox, "r+");
1905         mbox_lockwrite_file(mbox_fp, mbox);
1906
1907         mbox_cache_synchronize_from_file(mbox_fp, mbox, TRUE);
1908
1909         new = g_strconcat(mbox, ".", itos((int) mbox), NULL);
1910         new_fp = fopen(new, "w");
1911
1912         if (change_file_mode_rw(new_fp, new) < 0) {
1913                 FILE_OP_ERROR(new, "chmod");
1914                 g_warning(_("can't change file mode\n"));
1915         }
1916
1917         mbox_lockwrite_file(new_fp, new);
1918
1919         result = TRUE;
1920
1921         count = 0;
1922         msg_list = mbox_cache_get_msg_list(mbox);
1923         for(l = msg_list ; l != NULL ; l = g_list_next(l)) {
1924                 struct _message * msg = (struct _message *) l->data;
1925                 if (!mbox_write_message(mbox_fp, new_fp, new, msg)) {
1926                         result = FALSE;
1927                         break;
1928                 }
1929                 count ++;
1930         }
1931
1932         unlink(mbox);
1933
1934         if (rename(new, mbox) == -1) {
1935                 g_warning(_("can't rename %s to %s\n"), new, mbox);
1936                 mbox_unlock_file(new_fp, new);
1937                 fclose(new_fp);
1938                 mbox_unlock_file(mbox_fp, mbox);
1939                 fclose(mbox_fp);
1940                 return -1;
1941         }
1942
1943         if (change_file_mode_rw(new_fp, mbox) < 0) {
1944                 FILE_OP_ERROR(new, "chmod");
1945                 g_warning(_("can't change file mode\n"));
1946         }
1947
1948         mbox_unlock_file(new_fp, new);
1949
1950         fclose(new_fp);
1951
1952         mbox_unlock_file(mbox_fp, mbox);
1953
1954         fclose(mbox_fp);
1955
1956         debug_print(_("%i messages written - %s\n"), count, mbox);
1957
1958         cache = mbox_cache_get_mbox(mbox);
1959
1960         if (cache != NULL)
1961                 cache->mtime = -1;
1962
1963         mbox_cache_synchronize(mbox, FALSE);
1964
1965         return result;
1966 }
1967
1968 static gboolean mbox_purge_deleted(gchar * mbox)
1969 {
1970         FILE * mbox_fp;
1971         FILE * new_fp;
1972         gchar * new;
1973         GList * l;
1974         gboolean result;
1975         gboolean modification = FALSE;
1976         GList * msg_list;
1977         gint count;
1978
1979         mbox_cache_synchronize(mbox, TRUE);
1980
1981         msg_list = mbox_cache_get_msg_list(mbox);
1982
1983         for(l = msg_list ; l != NULL ; l = g_list_next(l)) {
1984                 struct _message * msg = (struct _message *) l->data;
1985                 if (msg->flags != -1 && MSG_IS_REALLY_DELETED(msg->flags)) {
1986                         modification = TRUE;
1987                         break;
1988                 }
1989         }
1990
1991         if (!modification) {
1992                 debug_print(_("no deleted messages - %s\n"), mbox);
1993                 return FALSE;
1994         }
1995
1996         debug_print(_("purge deleted messages - %s\n"), mbox);
1997
1998         mbox_fp = fopen(mbox, "r+");
1999         mbox_lockwrite_file(mbox_fp, mbox);
2000
2001         mbox_cache_synchronize_from_file(mbox_fp, mbox, TRUE);
2002
2003         new = g_strconcat(mbox, ".", itos((int) mbox), NULL);
2004         new_fp = fopen(new, "w");
2005
2006         if (change_file_mode_rw(new_fp, new) < 0) {
2007                 FILE_OP_ERROR(new, "chmod");
2008                 g_warning(_("can't change file mode\n"));
2009         }
2010
2011         mbox_lockwrite_file(new_fp, new);
2012
2013         result = TRUE;
2014
2015         count = 0;
2016         msg_list = mbox_cache_get_msg_list(mbox);
2017         for(l = msg_list ; l != NULL ; l = g_list_next(l)) {
2018                 struct _message * msg = (struct _message *) l->data;
2019                 if (msg->flags == -1 || !MSG_IS_REALLY_DELETED(msg->flags)) {
2020                         if (!mbox_write_message(mbox_fp, new_fp, new, msg)) {
2021                                 result = FALSE;
2022                                 break;
2023                         }
2024                         count ++;
2025                 }
2026         }
2027
2028         unlink(mbox);
2029
2030         if (rename(new, mbox) == -1) {
2031                 g_warning(_("can't rename %s to %s\n"), new, mbox);
2032                 mbox_unlock_file(new_fp, new);
2033                 fclose(new_fp);
2034                 mbox_unlock_file(mbox_fp, mbox);
2035                 fclose(mbox_fp);
2036                 return -1;
2037         }
2038
2039         if (change_file_mode_rw(new_fp, mbox) < 0) {
2040                 FILE_OP_ERROR(new, "chmod");
2041                 g_warning(_("can't change file mode\n"));
2042         }
2043
2044         mbox_unlock_file(new_fp, new);
2045
2046         fclose(new_fp);
2047
2048         mbox_unlock_file(mbox_fp, mbox);
2049
2050         fclose(mbox_fp);
2051
2052         debug_print(_("%i messages written - %s\n"), count, mbox);
2053
2054         mbox_cache_synchronize(mbox, FALSE);
2055
2056         return result;
2057 }
2058
2059 #define MAKE_DIR_IF_NOT_EXIST(dir) \
2060 { \
2061         if (!is_dir_exist(dir)) { \
2062                 if (is_file_exist(dir)) { \
2063                         g_warning(_("File `%s' already exists.\n" \
2064                                     "Can't create folder."), dir); \
2065                         return -1; \
2066                 } \
2067                 if (mkdir(dir, S_IRWXU) < 0) { \
2068                         FILE_OP_ERROR(dir, "mkdir"); \
2069                         return -1; \
2070                 } \
2071                 if (chmod(dir, S_IRWXU) < 0) \
2072                         FILE_OP_ERROR(dir, "chmod"); \
2073         } \
2074 }
2075
2076 gint mbox_create_tree(Folder *folder)
2077 {
2078         gchar *rootpath;
2079
2080         g_return_val_if_fail(folder != NULL, -1);
2081
2082         CHDIR_RETURN_VAL_IF_FAIL(get_home_dir(), -1);
2083         rootpath = LOCAL_FOLDER(folder)->rootpath;
2084         MAKE_DIR_IF_NOT_EXIST(rootpath);
2085         CHDIR_RETURN_VAL_IF_FAIL(rootpath, -1);
2086
2087         return 0;
2088 }
2089
2090 #undef MAKE_DIR_IF_NOT_EXIST
2091
2092 static gchar * mbox_get_new_path(FolderItem * parent, gchar * name)
2093 {
2094         gchar * path;
2095
2096         if (strchr(name, '/') == NULL) {
2097                 if (parent->path != NULL)
2098                         path = g_strconcat(parent->path, ".sbd", G_DIR_SEPARATOR_S, name, NULL);
2099                 else
2100                         path = g_strdup(name);
2101         }
2102         else
2103                 path = g_strdup(name);
2104
2105         return path;
2106 }
2107
2108 static gchar * mbox_get_folderitem_name(gchar * name)
2109 {
2110         gchar * foldername;
2111
2112         foldername = g_strdup(g_basename(name));
2113         
2114         return foldername;
2115 }
2116
2117 FolderItem *mbox_create_folder(Folder *folder, FolderItem *parent,
2118                                const gchar *name)
2119 {
2120         gchar * path;
2121         FolderItem *new_item;
2122         gchar * foldername;
2123
2124         g_return_val_if_fail(folder != NULL, NULL);
2125         g_return_val_if_fail(parent != NULL, NULL);
2126         g_return_val_if_fail(name != NULL, NULL);
2127
2128         path = mbox_get_new_path(parent, (gchar *) name);
2129
2130         foldername = mbox_get_folderitem_name((gchar *) name);
2131
2132         new_item = folder_item_new(foldername, path);
2133         folder_item_append(parent, new_item);
2134
2135         if (!strcmp(name, "inbox")) {
2136                 new_item->stype = F_INBOX;
2137                 new_item->folder->inbox = new_item;
2138         } else if (!strcmp(name, "outbox")) {
2139                 new_item->stype = F_OUTBOX;
2140                 new_item->folder->outbox = new_item;
2141         } else if (!strcmp(name, "draft")) {
2142                 new_item->stype = F_DRAFT;
2143                 new_item->folder->draft = new_item;
2144         } else if (!strcmp(name, "queue")) {
2145                 new_item->stype = F_QUEUE;
2146                 new_item->folder->queue = new_item;
2147         } else if (!strcmp(name, "trash")) {
2148                 new_item->stype = F_TRASH;
2149                 new_item->folder->trash = new_item;
2150         }
2151         
2152         g_free(foldername);
2153         g_free(path);
2154         
2155         return new_item;
2156 }
2157
2158 gint mbox_rename_folder(Folder *folder, FolderItem *item, const gchar *name)
2159 {
2160         gchar * path;
2161         gchar * foldername;
2162
2163         g_return_val_if_fail(folder != NULL, -1);
2164         g_return_val_if_fail(item != NULL, -1);
2165         g_return_val_if_fail(item->path != NULL, -1);
2166         g_return_val_if_fail(name != NULL, -1);
2167
2168         path = mbox_get_new_path(item->parent, (gchar *) name);
2169         foldername = mbox_get_folderitem_name((gchar *) name);
2170
2171         if (rename(item->path, path) == -1) {
2172                 g_free(foldername);
2173                 g_free(path);
2174                 g_warning(_("Cannot rename folder item"));
2175
2176                 return -1;
2177         }
2178         else {
2179                 g_free(item->name);
2180                 g_free(item->path);
2181                 item->path = path;
2182                 item->name = foldername;
2183                 
2184                 return 0;
2185         }
2186 }
2187
2188 gint mbox_remove_folder(Folder *folder, FolderItem *item)
2189 {
2190         g_return_val_if_fail(folder != NULL, -1);
2191         g_return_val_if_fail(item != NULL, -1);
2192         g_return_val_if_fail(item->path != NULL, -1);
2193
2194         folder_item_remove(item);
2195         return 0;
2196 }
2197