44ba92284ba7e4ba44c9171dc4b990826d6b7269
[claws.git] / src / mbox_folder.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2001 Hiroyuki Yamamoto & The Sylpheed Claws Team
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 #include <unistd.h>
21 #include <fcntl.h>
22 #include <glib.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <unistd.h>
28
29 #include "mbox_folder.h"
30 #include "folder.h"
31 #include "procmsg.h"
32 #include "procheader.h"
33 #include "utils.h"
34 #include "intl.h"
35
36 #define MSGBUFSIZE      8192
37
38 static Folder *mbox_folder_new(const gchar * name, const gchar * path);
39 static void mbox_folder_destroy(Folder * folder);
40
41 static gchar *mbox_fetch_msg(Folder * folder, FolderItem * item, gint num);
42
43 static void mbox_scan_folder(Folder * folder, FolderItem * item);
44 static gint mbox_add_msg(Folder * folder, FolderItem * dest,
45                          const gchar * file, gboolean remove_source);
46
47 static gint mbox_remove_all_msg(Folder * folder, FolderItem * item);
48 static gint mbox_remove_msg(Folder * folder, FolderItem * item, gint num);
49 static void mbox_change_flags(Folder * folder, FolderItem * item,
50                               MsgInfo * info, MsgPermFlags newflags);
51 static gint mbox_copy_msg(Folder * folder, FolderItem * dest,
52                           MsgInfo * msginfo);
53 static gint mbox_create_tree(Folder * folder);
54 static FolderItem *mbox_create_folder(Folder * folder, FolderItem * parent,
55                                       const gchar * name);
56 static gint mbox_rename_folder(Folder * folder, FolderItem * item,
57                                const gchar * name);
58 static gint mbox_remove_folder(Folder * folder, FolderItem * item);
59
60 static void     mbox_folder_init                (Folder         *folder,
61                                                  const gchar    *name,
62                                                  const gchar    *path);
63
64 static gboolean mbox_write_data(FILE * mbox_fp, FILE * new_fp,
65                                 gchar * new_filename, gint size);
66 static gboolean mbox_purge_deleted(gchar * mbox);
67 static gchar * mbox_get_new_path(FolderItem * parent, gchar * name);
68 static gchar * mbox_get_folderitem_name(gchar * name);
69
70 static MsgInfo *mbox_get_msginfo(Folder *folder, FolderItem *item, gint num);
71 static gint mbox_get_num_list(Folder *folder, FolderItem *item, GSList **list);
72 static gboolean mbox_check_msgnum_validity(Folder *folder, FolderItem *item);
73 static gchar *mbox_folder_get_path(Folder *folder, FolderItem *item);
74
75 FolderClass mbox_class =
76 {
77         F_MBOX,
78         "mbox",
79         "MBOX",
80
81         /* Folder functions */
82         mbox_folder_new,
83         mbox_folder_destroy,
84         NULL,
85         mbox_create_tree,
86
87         /* FolderItem functions */
88         NULL,
89         NULL,
90         mbox_folder_get_path,
91         mbox_create_folder,
92         mbox_rename_folder,
93         mbox_remove_folder,
94         mbox_get_num_list,
95         NULL,
96         NULL,
97         NULL,
98         mbox_check_msgnum_validity,
99
100         /* Message functions */
101         mbox_get_msginfo,
102         NULL,
103         mbox_fetch_msg,
104         mbox_add_msg,
105         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                   gboolean remove_source)
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         if (remove_source) {
1444                 if (unlink(file) < 0)
1445                         FILE_OP_ERROR(file, "unlink");
1446         }
1447
1448         g_free(mbox_path);
1449
1450         dest->last_num++;
1451         return dest->last_num;
1452
1453 }
1454
1455 gint mbox_remove_msg(Folder *folder, FolderItem *item, gint num)
1456 {
1457         struct _message * msg;
1458         gchar * mbox_path;
1459
1460         mbox_path = mbox_folder_get_path(folder, item);
1461         if (mbox_path == NULL)
1462                 return -1;
1463
1464         mbox_cache_synchronize(mbox_path, TRUE);
1465
1466         msg = mbox_cache_get_msg(mbox_path, num);
1467
1468         g_free(mbox_path);
1469
1470         if (msg != NULL)
1471                 MSG_SET_PERM_FLAGS(msg->flags, MSG_REALLY_DELETED);
1472
1473         return 0;
1474 }
1475
1476 gint mbox_remove_all_msg(Folder *folder, FolderItem *item)
1477 {
1478         FILE * fp;
1479         gchar * mbox_path;
1480
1481         mbox_path = mbox_folder_get_path(folder, item);
1482         if (mbox_path == NULL)
1483                 return -1;
1484
1485         fp = fopen(mbox_path, "wb");
1486         if (fp == NULL) {
1487                 g_free(mbox_path);
1488                 return -1;
1489         }
1490
1491         fclose(fp);
1492
1493         g_free(mbox_path);
1494
1495         return 0;
1496 }
1497
1498 /*
1499 gint mbox_move_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1500 {
1501         gchar * filename;
1502         gint msgnum;
1503
1504         filename = mbox_fetch_msg(folder, msginfo->folder, msginfo->msgnum);
1505         if (filename == NULL)
1506                 return -1;
1507
1508         msgnum = mbox_add_msg(folder, dest, filename, TRUE);
1509
1510         if (msgnum != -1) {
1511                 MSG_SET_FLAGS(msginfo->flags, MSG_REALLY_DELETED);
1512                 mbox_change_flags(folder, msginfo->folder, msginfo);
1513         }
1514
1515         return msgnum;
1516 }
1517
1518 gint mbox_move_msgs_with_dest(Folder *folder, FolderItem *dest, GSList *msglist)
1519 {
1520         GSList * l;
1521         gchar * mbox_path = NULL;
1522
1523         for(l = msglist ; l != NULL ; l = g_slist_next(l)) {
1524                 MsgInfo * msginfo = (MsgInfo *) l->data;
1525
1526                 if (msginfo->folder && mbox_path == NULL)
1527                         mbox_path = mbox_folder_get_path(msginfo->folder);
1528
1529                 mbox_move_msg(folder, dest, msginfo);
1530         }
1531
1532         if (mbox_path) {
1533                 mbox_cache_synchronize(mbox_path);
1534                 g_free(mbox_path);
1535         }
1536
1537         mbox_path = mbox_folder_get_path(dest);
1538         mbox_cache_synchronize(mbox_path);
1539         g_free(mbox_path);
1540
1541         return dest->last_num;
1542 }
1543 */
1544
1545 /*
1546 gint mbox_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1547 {
1548         gchar * filename;
1549         gint msgnum;
1550
1551         filename = mbox_fetch_msg(folder, msginfo->folder, msginfo->msgnum);
1552         if (filename == NULL)
1553                 return -1;
1554
1555         msgnum = mbox_add_msg(folder, dest, filename, FALSE);
1556
1557         return msgnum;
1558 }
1559
1560 gint mbox_copy_msgs_with_dest(Folder *folder, FolderItem *dest, GSList *msglist)
1561 {
1562         GSList * l;
1563         gchar * mbox_path = NULL;
1564
1565         for(l = msglist ; l != NULL ; l = g_slist_next(l)) {
1566                 MsgInfo * msginfo = (MsgInfo *) l->data;
1567
1568                 if (msginfo->folder && mbox_path == NULL)
1569                         mbox_path = mbox_folder_get_path(msginfo->folder);
1570
1571                 mbox_copy_msg(folder, dest, msginfo);
1572         }
1573
1574         if (mbox_path) {
1575                 mbox_cache_synchronize(mbox_path);
1576                 g_free(mbox_path);
1577         }
1578
1579         mbox_path = mbox_folder_get_path(dest);
1580         mbox_cache_synchronize(mbox_path);
1581         g_free(mbox_path);
1582
1583         return dest->last_num;
1584 }
1585 */
1586
1587 struct _copy_flags_info
1588 {
1589         gint num;
1590         MsgFlags flags;
1591 };
1592
1593 typedef struct _copy_flags_info CopyFlagsInfo;
1594
1595 GSList * copy_flags_data = NULL;
1596
1597 gint mbox_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1598 {
1599         Folder * src_folder;
1600         gchar * filename;
1601         gint num;
1602         CopyFlagsInfo * flags_info;
1603
1604         src_folder = msginfo->folder->folder;
1605         
1606         /*
1607         mbox_path = mbox_folder_get_path(msginfo->folder);
1608         mbox_rewrite(mbox_path);
1609         g_free(mbox_path);
1610         */
1611
1612         filename = folder_item_fetch_msg(msginfo->folder,
1613                                          msginfo->msgnum);
1614         if (filename == NULL)
1615                 return -1;
1616
1617         num = mbox_add_msg(folder, dest, filename, FALSE);
1618
1619         /*
1620         mbox_path = mbox_folder_get_path(dest);
1621         msg = mbox_cache_get_msg(mbox_path, num);
1622         if (msg != NULL)
1623                 msg->flags = msginfo->flags;
1624         g_free(mbox_path);
1625         */
1626
1627         if (num == -1)
1628                 return -1;
1629
1630         flags_info = g_new0(CopyFlagsInfo, 1);
1631         flags_info->num = num;
1632         flags_info->flags = msginfo->flags;
1633         copy_flags_data = g_slist_append(copy_flags_data, flags_info);
1634
1635         return num;
1636 }
1637
1638 void mbox_scan_folder(Folder *folder, FolderItem *item)
1639 {
1640         gchar *mbox_path;
1641         gint n_msg;
1642         mboxcache * cached;
1643         GList * l;
1644
1645         mbox_path = mbox_folder_get_path(folder, item);
1646         if (mbox_path == NULL)
1647                 return;
1648
1649         mbox_cache_synchronize(mbox_path, TRUE);
1650
1651         cached = mbox_cache_get_mbox(mbox_path);
1652
1653         if (cached == NULL) {
1654                 item->new_msgs = 0;
1655                 item->unread_msgs = 0;
1656                 item->total_msgs = 0;
1657                 item->last_num = 0;
1658                 g_free(mbox_path);
1659                 return;
1660         }
1661
1662         n_msg = mbox_cache_get_count(mbox_path);
1663
1664         if (n_msg == 0) {
1665                 item->new_msgs = item->unread_msgs = item->total_msgs = 0;
1666         }
1667         else {
1668                 gint new = 0;
1669                 gint unread = 0;
1670                 gint total = 0;
1671
1672                 for(l = mbox_cache_get_msg_list(mbox_path) ; l != NULL ;
1673                     l = g_list_next(l)) {
1674                         struct _message * msg = (struct _message *) l->data;
1675                         if (!MSG_IS_REALLY_DELETED(msg->flags))
1676                                 total ++;
1677                         if (MSG_IS_NEW(msg->flags) && !MSG_IS_IGNORE_THREAD(msg->flags))
1678                                 new ++;
1679                         if (MSG_IS_UNREAD(msg->flags) && !MSG_IS_IGNORE_THREAD(msg->flags))
1680                                 unread ++;
1681                 }
1682                 
1683                 item->new_msgs = new;
1684                 item->unread_msgs = unread;
1685                 item->total_msgs = total;
1686         }
1687
1688         debug_print("Last number in dir %s = %d\n", mbox_path,
1689                     item->total_msgs);
1690         item->last_num = n_msg;
1691         g_free(mbox_path);
1692 }
1693
1694 gchar * mbox_get_virtual_path(FolderItem * item)
1695 {
1696         if (item == NULL)
1697                 return NULL;
1698
1699         if (item->parent == NULL) {
1700                 return NULL;
1701         }
1702         else {
1703                 gchar * parent_path;
1704                 gchar * result_path;
1705
1706                 parent_path = mbox_get_virtual_path(item->parent);
1707                 if (parent_path == NULL)
1708                         result_path = g_strdup(item->name);
1709                 else
1710                         result_path = g_strconcat(parent_path,
1711                                                   G_DIR_SEPARATOR_S,
1712                                                   item->name, NULL);
1713                 g_free(parent_path);
1714
1715                 return result_path;
1716         }
1717 }
1718
1719 static gboolean mbox_write_data(FILE * mbox_fp, FILE * new_fp,
1720                                 gchar * new_filename, gint size)
1721 {       
1722         gint n_read;
1723         gint pos;
1724         gchar buf[BUFSIZ];
1725         gint max;
1726
1727         pos = 0;
1728         while (pos < size) {
1729                 if ((size - pos) > (gint) sizeof(buf))
1730                         max = sizeof(buf);
1731                 else
1732                         max = (size - pos);
1733                 
1734                 n_read = fread(buf, 1, max, mbox_fp);
1735
1736                 if (n_read < max && ferror(mbox_fp)) {
1737                         return FALSE;
1738                 }
1739                 if (fwrite(buf, n_read, 1, new_fp) < 1) {
1740                         g_warning("writing to %s failed.\n", new_filename);
1741                         return FALSE;
1742                 }
1743                 
1744                 if (n_read != -1)
1745                         pos += n_read;
1746                 
1747                 if (n_read < max)
1748                         break;
1749         }
1750         return TRUE;
1751 }
1752
1753 static gboolean mbox_write_message(FILE * mbox_fp, FILE * new_fp,
1754                                    gchar * new_filename,
1755                                    struct _message * msg)
1756 {
1757         gint size;
1758         GPtrArray * headers;
1759         gint i;
1760         
1761         fseek(mbox_fp, msg->header, SEEK_SET);
1762
1763         headers = procheader_get_header_array_asis(mbox_fp);
1764
1765         for (i = 0; i < (gint) headers->len; i++) {
1766                 Header * h = g_ptr_array_index(headers, i);
1767                 
1768                 if (!procheader_headername_equal(h->name, 
1769                                                  "Status") &&
1770                     !procheader_headername_equal(h->name, 
1771                                                  "X-Status")) {
1772                         fwrite(h->name, strlen(h->name),
1773                                1, new_fp);
1774                         if (h->name[strlen(h->name) - 1] != ' ')
1775                                 fwrite(" ", 1, 1, new_fp);
1776                         fwrite(h->body, strlen(h->body),
1777                                1, new_fp);
1778                         fwrite("\n", 1, 1, new_fp);
1779                 }
1780                 procheader_header_free(h);
1781                 g_ptr_array_remove_index(headers, i);
1782                 i--;
1783         }
1784
1785         g_ptr_array_free(headers, FALSE);
1786
1787         if (!MSG_IS_INVALID(msg->flags)) {
1788                 /* Status header */
1789                 fwrite("Status: ", strlen("Status: "), 1, new_fp);
1790                 if (!MSG_IS_UNREAD(msg->flags))
1791                         fwrite("R", 1, 1, new_fp);
1792                 fwrite("O", 1, 1, new_fp);
1793                 fwrite("\n", 1, 1, new_fp);
1794                 
1795                 /* X-Status header */
1796                 if (MSG_IS_REALLY_DELETED(msg->flags)
1797                 ||  MSG_IS_MARKED(msg->flags)
1798                 ||  MSG_IS_DELETED(msg->flags)
1799                 ||  MSG_IS_REPLIED(msg->flags)
1800                 ||  MSG_IS_FORWARDED(msg->flags)) {
1801                         fwrite("X-Status: ", strlen("X-Status: "), 1, new_fp);
1802                         if (MSG_IS_REALLY_DELETED(msg->flags))
1803                                 fwrite("D", 1, 1, new_fp); /* really deleted */
1804                         else {
1805                                 if (MSG_IS_MARKED(msg->flags))
1806                                         fwrite("F", 1, 1, new_fp);
1807                                 if (MSG_IS_DELETED(msg->flags))
1808                                         fwrite("d", 1, 1, new_fp);
1809                                 if (MSG_IS_REPLIED(msg->flags))
1810                                         fwrite("r", 1, 1, new_fp);
1811                                 if (MSG_IS_FORWARDED(msg->flags))
1812                                         fwrite("f", 1, 1, new_fp);
1813                         }
1814                         fwrite("\n", 1, 1, new_fp);
1815                 }
1816         }
1817
1818         fwrite("\n", 1, 1, new_fp);
1819
1820         size = msg->end - msg->content;
1821         fseek(mbox_fp, msg->content, SEEK_SET);
1822
1823         return mbox_write_data(mbox_fp, new_fp, new_filename, size);
1824 }
1825
1826 void mbox_change_flags(Folder * folder, FolderItem * item, MsgInfo * info, MsgPermFlags newflags)
1827 {
1828         struct _message * msg;
1829         mboxcache * cache;
1830         gchar * mbox_path;
1831
1832         mbox_path = mbox_folder_get_path(folder, item);
1833         if (mbox_path == NULL)
1834                 return;
1835
1836         info->flags.perm_flags = newflags;
1837
1838         msg = mbox_cache_get_msg(mbox_path, info->msgnum);
1839
1840         cache = mbox_cache_get_mbox(mbox_path);
1841
1842         g_free(mbox_path);
1843
1844         if ((msg == NULL) || (cache == NULL))
1845                 return;
1846
1847         msg->flags = info->flags;
1848
1849         cache->modification = TRUE;             
1850 }
1851
1852
1853 static gboolean mbox_purge_deleted(gchar * mbox)
1854 {
1855         FILE * mbox_fp;
1856         FILE * new_fp;
1857         gchar * new;
1858         GList * l;
1859         gboolean result;
1860         gboolean modification = FALSE;
1861         GList * msg_list;
1862         gint count;
1863
1864         mbox_cache_synchronize(mbox, TRUE);
1865
1866         msg_list = mbox_cache_get_msg_list(mbox);
1867
1868         for(l = msg_list ; l != NULL ; l = g_list_next(l)) {
1869                 struct _message * msg = (struct _message *) l->data;
1870                 if (!MSG_IS_INVALID(msg->flags) && MSG_IS_REALLY_DELETED(msg->flags)) {
1871                         modification = TRUE;
1872                         break;
1873                 }
1874         }
1875
1876         if (!modification) {
1877                 debug_print("no deleted messages - %s\n", mbox);
1878                 return FALSE;
1879         }
1880
1881         debug_print("purge deleted messages - %s\n", mbox);
1882
1883         mbox_fp = fopen(mbox, "rb+");
1884         mbox_lockwrite_file(mbox_fp, mbox);
1885
1886         mbox_cache_synchronize_from_file(mbox_fp, mbox, TRUE);
1887
1888         new = g_strconcat(mbox, ".", itos((int) mbox), NULL);
1889         new_fp = fopen(new, "wb");
1890
1891         if (change_file_mode_rw(new_fp, new) < 0) {
1892                 FILE_OP_ERROR(new, "chmod");
1893                 g_warning("can't change file mode\n");
1894         }
1895
1896         mbox_lockwrite_file(new_fp, new);
1897
1898         result = TRUE;
1899
1900         count = 0;
1901         msg_list = mbox_cache_get_msg_list(mbox);
1902         for(l = msg_list ; l != NULL ; l = g_list_next(l)) {
1903                 struct _message * msg = (struct _message *) l->data;
1904                 if (MSG_IS_INVALID(msg->flags) || !MSG_IS_REALLY_DELETED(msg->flags)) {
1905                         if (!mbox_write_message(mbox_fp, new_fp, new, msg)) {
1906                                 result = FALSE;
1907                                 break;
1908                         }
1909                         count ++;
1910                 }
1911         }
1912
1913         unlink(mbox);
1914
1915         if (rename(new, mbox) == -1) {
1916                 g_warning("can't rename %s to %s\n", new, mbox);
1917                 mbox_unlock_file(new_fp, new);
1918                 fclose(new_fp);
1919                 mbox_unlock_file(mbox_fp, mbox);
1920                 fclose(mbox_fp);
1921                 g_free(new);
1922                 return -1;
1923         }
1924
1925         if (change_file_mode_rw(new_fp, mbox) < 0) {
1926                 FILE_OP_ERROR(new, "chmod");
1927                 g_warning("can't change file mode\n");
1928         }
1929
1930         mbox_unlock_file(new_fp, new);
1931
1932         fclose(new_fp);
1933
1934         mbox_unlock_file(mbox_fp, mbox);
1935
1936         fclose(mbox_fp);
1937
1938         debug_print("%i messages written - %s\n", count, mbox);
1939
1940         mbox_cache_synchronize(mbox, FALSE);
1941         g_free(new);
1942         return result;
1943 }
1944
1945 #define MAKE_DIR_IF_NOT_EXIST(dir) \
1946 { \
1947         if (!is_dir_exist(dir)) { \
1948                 if (is_file_exist(dir)) { \
1949                         g_warning("File `%s' already exists.\n" \
1950                                     "Can't create folder.", dir); \
1951                         return -1; \
1952                 } \
1953                 if (mkdir(dir, S_IRWXU) < 0) { \
1954                         FILE_OP_ERROR(dir, "mkdir"); \
1955                         return -1; \
1956                 } \
1957                 if (chmod(dir, S_IRWXU) < 0) \
1958                         FILE_OP_ERROR(dir, "chmod"); \
1959         } \
1960 }
1961
1962 gint mbox_create_tree(Folder *folder)
1963 {
1964         gchar *rootpath;
1965
1966         g_return_val_if_fail(folder != NULL, -1);
1967
1968         CHDIR_RETURN_VAL_IF_FAIL(get_home_dir(), -1);
1969         rootpath = LOCAL_FOLDER(folder)->rootpath;
1970         MAKE_DIR_IF_NOT_EXIST(rootpath);
1971         CHDIR_RETURN_VAL_IF_FAIL(rootpath, -1);
1972
1973         return 0;
1974 }
1975
1976 #undef MAKE_DIR_IF_NOT_EXIST
1977
1978 static gchar * mbox_get_new_path(FolderItem * parent, gchar * name)
1979 {
1980         gchar * path;
1981
1982         if (strchr(name, '/') == NULL) {
1983                 if (parent->path != NULL)
1984                         path = g_strconcat(parent->path, ".sbd", G_DIR_SEPARATOR_S, name, NULL);
1985                 else
1986                         path = g_strdup(name);
1987         }
1988         else
1989                 path = g_strdup(name);
1990
1991         return path;
1992 }
1993
1994 static gchar * mbox_get_folderitem_name(gchar * name)
1995 {
1996         gchar * foldername;
1997
1998         foldername = g_strdup(g_basename(name));
1999         
2000         return foldername;
2001 }
2002
2003 FolderItem *mbox_create_folder(Folder *folder, FolderItem *parent,
2004                                const gchar *name)
2005 {
2006         gchar * path;
2007         FolderItem *new_item;
2008         gchar * foldername;
2009
2010         g_return_val_if_fail(folder != NULL, NULL);
2011         g_return_val_if_fail(parent != NULL, NULL);
2012         g_return_val_if_fail(name != NULL, NULL);
2013
2014         path = mbox_get_new_path(parent, (gchar *) name);
2015
2016         foldername = mbox_get_folderitem_name((gchar *) name);
2017
2018         new_item = folder_item_new(folder, foldername, path);
2019         folder_item_append(parent, new_item);
2020
2021         if (!strcmp(name, "inbox")) {
2022                 new_item->stype = F_INBOX;
2023                 new_item->folder->inbox = new_item;
2024         } else if (!strcmp(name, "outbox")) {
2025                 new_item->stype = F_OUTBOX;
2026                 new_item->folder->outbox = new_item;
2027         } else if (!strcmp(name, "draft")) {
2028                 new_item->stype = F_DRAFT;
2029                 new_item->folder->draft = new_item;
2030         } else if (!strcmp(name, "queue")) {
2031                 new_item->stype = F_QUEUE;
2032                 new_item->folder->queue = new_item;
2033         } else if (!strcmp(name, "trash")) {
2034                 new_item->stype = F_TRASH;
2035                 new_item->folder->trash = new_item;
2036         }
2037         
2038         g_free(foldername);
2039         g_free(path);
2040         
2041         return new_item;
2042 }
2043
2044 gint mbox_rename_folder(Folder *folder, FolderItem *item, const gchar *name)
2045 {
2046         gchar * path;
2047         gchar * foldername;
2048
2049         g_return_val_if_fail(folder != NULL, -1);
2050         g_return_val_if_fail(item != NULL, -1);
2051         g_return_val_if_fail(item->path != NULL, -1);
2052         g_return_val_if_fail(name != NULL, -1);
2053
2054         path = mbox_get_new_path(item->parent, (gchar *) name);
2055         foldername = mbox_get_folderitem_name((gchar *) name);
2056
2057         if (rename(item->path, path) == -1) {
2058                 g_free(foldername);
2059                 g_free(path);
2060                 g_warning("Cannot rename folder item");
2061
2062                 return -1;
2063         }
2064         else {
2065                 g_free(item->name);
2066                 g_free(item->path);
2067                 item->path = path;
2068                 item->name = foldername;
2069                 
2070                 return 0;
2071         }
2072 }
2073
2074 gint mbox_remove_folder(Folder *folder, FolderItem *item)
2075 {
2076         g_return_val_if_fail(folder != NULL, -1);
2077         g_return_val_if_fail(item != NULL, -1);
2078         g_return_val_if_fail(item->path != NULL, -1);
2079
2080         folder_item_remove(item);
2081         return 0;
2082 }
2083
2084 gint mbox_get_num_list(Folder *folder, FolderItem *item, GSList **mlist)
2085 {
2086         GList * l;
2087         FILE * fp;
2088         gchar * mbox_path;
2089         gint nummsgs = 0;
2090
2091         mbox_path = mbox_folder_get_path(folder, item);
2092
2093         if (mbox_path == NULL)
2094                 return -1;
2095
2096         mbox_purge_deleted(mbox_path);
2097
2098         fp = fopen(mbox_path, "rb");
2099         
2100         if (fp == NULL) {
2101                 g_free(mbox_path);
2102                 return -1;
2103         }
2104
2105         mbox_lockread_file(fp, mbox_path);
2106
2107         mbox_cache_synchronize_from_file(fp, mbox_path, TRUE);
2108
2109         item->last_num = mbox_cache_get_count(mbox_path);
2110
2111         for(l = mbox_cache_get_msg_list(mbox_path) ; l != NULL ;
2112             l = g_list_next(l)) {
2113                 struct _message * msg;
2114
2115                 msg = (struct _message *) l->data;
2116
2117                 if (MSG_IS_INVALID(msg->flags) || !MSG_IS_REALLY_DELETED(msg->flags)) {
2118                         *mlist = g_slist_append(*mlist, GINT_TO_POINTER(msg->msgnum));
2119                         nummsgs++;
2120                 } else {
2121                         MSG_SET_PERM_FLAGS(msg->flags, MSG_REALLY_DELETED);
2122                 }
2123         }
2124
2125         mbox_unlock_file(fp, mbox_path);
2126
2127         g_free(mbox_path);
2128
2129         fclose(fp);
2130
2131         return nummsgs;
2132 }
2133
2134 MsgInfo *mbox_get_msginfo(Folder *folder, FolderItem *item, gint num)
2135 {
2136         gchar *mbox_path;
2137         struct _message *msg;
2138         FILE *src;
2139         MsgInfo *msginfo;
2140         
2141         g_return_val_if_fail(folder != NULL, NULL);
2142         g_return_val_if_fail(item != NULL, NULL);
2143
2144         mbox_path = mbox_folder_get_path(folder, item);
2145
2146         g_return_val_if_fail(mbox_path != NULL, NULL);
2147         
2148         src = fopen(mbox_path, "rb");
2149         if (src == NULL) {
2150                 g_free(mbox_path);
2151                 return NULL;
2152         }
2153         mbox_lockread_file(src, mbox_path);
2154         mbox_cache_synchronize_from_file(src, mbox_path, TRUE);
2155
2156         msg = mbox_cache_get_msg(mbox_path, num);
2157         if (msg == NULL) {
2158                 mbox_unlock_file(src, mbox_path);
2159                 fclose(src);
2160                 g_free(mbox_path);
2161                 return NULL;
2162         }
2163         
2164         fseek(src, msg->header, SEEK_SET);
2165         msginfo = mbox_parse_msg(src, msg, item);
2166
2167         mbox_unlock_file(src, mbox_path);
2168         fclose(src);
2169         g_free(mbox_path);
2170
2171         return msginfo;
2172 }
2173
2174 gboolean mbox_check_msgnum_validity(Folder *folder, FolderItem *item)
2175 {
2176         mboxcache * old_cache;
2177         gboolean scan_new = TRUE;
2178         struct stat s;
2179         gchar *filename;
2180
2181         filename = mbox_folder_get_path(folder, item);
2182         
2183         old_cache = mbox_cache_get_mbox(filename);
2184
2185         if (old_cache != NULL) {
2186                 if (stat(filename, &s) < 0) {
2187                         FILE_OP_ERROR(filename, "stat");
2188                 } else if (old_cache->mtime == s.st_mtime) {
2189                         debug_print("Folder is not modified.\n");
2190                         scan_new = FALSE;
2191                 }
2192         }
2193
2194         g_free(filename);
2195         
2196         return !scan_new;
2197 }
2198