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