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