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