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