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