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