2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2001 Hiroyuki Yamamoto & The Sylpheed Claws Team
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.
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.
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.
25 #include <sys/types.h>
29 #include "mbox_folder.h"
32 #include "procheader.h"
36 #define MSGBUFSIZE 8192
38 static void mbox_folder_init (Folder *folder,
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);
49 MsgInfo *mbox_get_msginfo(Folder *folder, FolderItem *item, gint num);
50 gint mbox_get_num_list(Folder *folder, FolderItem *item, GSList **list);
51 gboolean mbox_check_msgnum_validity(Folder *folder, FolderItem *item);
53 FolderClass mbox_class =
59 /* Folder functions */
65 /* FolderItem functions */
75 mbox_check_msgnum_validity,
77 /* Message functions */
93 FolderClass *mbox_get_class()
98 Folder *mbox_folder_new(const gchar *name, const gchar *path)
102 folder = (Folder *)g_new0(MBOXFolder, 1);
103 folder->class = &mbox_class;
104 mbox_folder_init(folder, name, path);
109 void mbox_folder_destroy(Folder *folder)
111 folder_local_folder_destroy(LOCAL_FOLDER(folder));
114 static void mbox_folder_init(Folder *folder, const gchar *name, const gchar *path)
116 folder_local_folder_init(folder, name, path);
119 static void mbox_folder_create_parent(const gchar * path)
121 if (!is_file_exist(path)) {
124 new_path = g_dirname(path);
125 if (new_path[strlen(new_path) - 1] == G_DIR_SEPARATOR)
126 new_path[strlen(new_path) - 1] = '\0';
128 if (!is_dir_exist(new_path))
129 make_dir_hier(new_path);
136 static gchar *mbox_folder_get_path(FolderItem *item)
141 g_return_val_if_fail(item != NULL, NULL);
143 if (item->path && item->path[0] == G_DIR_SEPARATOR) {
144 mbox_folder_create_parent(item->path);
145 return g_strdup(item->path);
148 folder_path = g_strdup(LOCAL_FOLDER(item->folder)->rootpath);
149 g_return_val_if_fail(folder_path != NULL, NULL);
151 if (folder_path[0] == G_DIR_SEPARATOR) {
153 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
157 path = g_strdup(folder_path);
160 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
161 folder_path, G_DIR_SEPARATOR_S,
164 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
170 mbox_folder_create_parent(path);
176 /**********************************************************/
180 /**********************************************************/
183 static GSList * file_lock = NULL;
185 static gboolean mbox_file_lock_file(gchar * base)
187 gchar *lockfile, *locklink;
191 lockfile = g_strdup_printf("%s.%d", base, getpid());
192 if ((lockfp = fopen(lockfile, "wb")) == NULL) {
193 FILE_OP_ERROR(lockfile, "fopen");
194 g_warning("can't create lock file %s\n", lockfile);
195 g_warning("use 'flock' instead of 'file' if possible.\n");
200 fprintf(lockfp, "%d\n", getpid());
203 locklink = g_strconcat(base, ".lock", NULL);
204 while (link(lockfile, locklink) < 0) {
205 FILE_OP_ERROR(lockfile, "link");
207 g_warning("can't create %s\n", lockfile);
214 g_warning("mailbox is owned by another"
215 " process, waiting...\n");
226 static gboolean mbox_fcntl_lockwrite_file(FILE * fp)
230 lck.l_type = F_WRLCK;
235 if (fcntl(fileno(fp), F_SETLK, &lck) < 0)
241 static gboolean mbox_fcntl_lockread_file(FILE * fp)
245 lck.l_type = F_RDLCK;
250 if (fcntl(fileno(fp), F_SETLK, &lck) < 0)
256 static gboolean mbox_fcntl_unlock_file(FILE * fp)
260 lck.l_type = F_UNLCK;
265 if (fcntl(fileno(fp), F_SETLK, &lck) < 0)
271 static gboolean mbox_file_unlock_file(gchar * base)
275 lockfile = g_strdup_printf("%s.lock", base);
282 static gboolean mbox_lockread_file(FILE * fp, gchar * base)
286 result = mbox_fcntl_lockread_file(fp);
288 if ((result = mbox_file_lock_file(base)) == TRUE) {
289 file_lock = g_slist_append(file_lock, g_strdup(base));
290 debug_print("lockfile lock %s.\n", base);
293 g_warning("could not lock read file %s\n", base);
296 debug_print("fcntl lock %s.\n", base);
301 static gboolean mbox_lockwrite_file(FILE * fp, gchar * base)
305 result = mbox_fcntl_lockwrite_file(fp);
307 if ((result = mbox_file_lock_file(base)) == TRUE) {
308 file_lock = g_slist_append(file_lock, g_strdup(base));
309 debug_print("lockfile lock %s.\n", base);
312 g_warning("could not lock write file %s\n", base);
315 debug_print("fcntl lock %s.\n", base);
320 static gboolean mbox_unlock_file(FILE * fp, gchar * base)
322 gboolean result = FALSE;
324 gboolean unlocked = FALSE;
326 for(l = file_lock ; l != NULL ; l = g_slist_next(l)) {
327 gchar * data = l->data;
329 if (strcmp(data, base) == 0) {
330 file_lock = g_slist_remove(file_lock, data);
332 result = mbox_file_unlock_file(base);
334 debug_print("lockfile unlock - %s.\n", base);
340 result = mbox_fcntl_unlock_file(fp);
341 debug_print("fcntl unlock - %s.\n", base);
347 /**********************************************************/
351 /**********************************************************/
353 #define MAILFILE_ERROR_NO_ERROR 0x000
354 #define MAILFILE_ERROR_FILE_NOT_FOUND 0x001
355 #define MAILFILE_ERROR_MEMORY 0x002
356 #define MAILFILE_ERROR_MESSAGE_NOT_FOUND 0x003
358 static int mailfile_error = MAILFILE_ERROR_NO_ERROR;
360 #define STATE_BEGIN 0x000
361 #define STATE_TEXT_READ 0x001
362 #define STATE_FROM_READ 0x002
363 #define STATE_FIELD_READ 0x003
364 #define STATE_END 0x004
365 #define STATE_END_OF_FILE 0x005
366 #define STATE_MEM_ERROR 0x006
367 #define STATE_TEXT_BEGIN 0x007
369 #define STATE_MASK 0x0FF /* filter state from functions */
371 #define STATE_RESTORE_POS 0x100 /* go back while reading */
373 typedef struct _mailfile mailfile;
397 #define MSG_IS_INVALID(msg) \
398 ((msg).perm_flags == (msg).tmp_flags && (msg).tmp_flags == -1)
400 #define MSG_SET_INVALID(msg) \
401 ((msg).perm_flags = (msg).tmp_flags = -1)
403 static int startFrom(char * s)
405 return (strncmp(s, "From ", 5) == 0);
408 static int startSpace(char * s)
410 return ((*s == ' ') || (*s == '\t'));
413 static int startEmpty(char * s)
418 static void free_msg_list(GList * l)
420 GList * elt = g_list_first(l);
425 elt = g_list_next(elt);
432 static mailfile * mailfile_init_from_file(FILE * f, gchar * filename)
435 GList * msg_list = NULL;
442 struct _message * data = NULL;
448 while (state != STATE_END_OF_FILE) {
449 if ((state & STATE_RESTORE_POS) == 0) {
450 former_pos = lastpos;
453 r = fgets(s, 256, f);
456 ignore_next = (s[strlen(s) - 1] != '\n');
461 switch(state & 0x0F) {
464 state = STATE_END_OF_FILE;
465 else if (startFrom(s)) {
466 state = STATE_FROM_READ;
468 data = g_new0(struct _message, 1);
470 free_msg_list(msg_list);
475 data->msgnum = msgnum;
476 data->offset = lastpos;
477 data->header = lastpos;
480 data->messageid = NULL;
481 data->fromspace = NULL;
482 MSG_SET_INVALID(data->flags);
483 MSG_SET_INVALID(data->old_flags);
484 data->fetched = FALSE;
485 msg_list = g_list_append(msg_list,
493 case STATE_TEXT_READ:
496 else if (startFrom(s))
497 state = STATE_END | STATE_RESTORE_POS;
499 state = STATE_TEXT_READ;
502 case STATE_TEXT_BEGIN:
503 data->content = lastpos;
506 else if (startFrom(s)) {
507 state = STATE_END | STATE_RESTORE_POS;
510 state = STATE_TEXT_READ;
514 case STATE_FROM_READ:
515 data->content = lastpos;
518 else if (startSpace(s))
519 state = STATE_FROM_READ;
520 else if (startEmpty(s))
521 state = STATE_TEXT_READ;
523 state = STATE_FIELD_READ;
526 case STATE_FIELD_READ:
527 data->content = lastpos;
530 else if (startSpace(s))
531 state = STATE_FIELD_READ;
532 else if (startEmpty(s)) {
533 state = STATE_TEXT_BEGIN;
536 state = STATE_FIELD_READ;
540 if ((state & STATE_MASK) == STATE_END) {
541 state = STATE_BEGIN | (state & STATE_RESTORE_POS);
547 r = fgets(s, 256, f);
548 if (r == NULL || *r == '\0')
551 while (s[strlen(s) - 1] != '\n');
555 mf = (mailfile *) g_new0(struct _mailfile, 1);
557 free_msg_list(msg_list);
558 mailfile_error = MAILFILE_ERROR_MEMORY;
562 mf->msg_list = g_list_first(msg_list);
564 mf->filename = g_strdup(filename);
567 mailfile_error = MAILFILE_ERROR_NO_ERROR;
572 static mailfile * mailfile_init(char * filename)
578 f = fopen(filename, "rb");
581 mailfile_error = MAILFILE_ERROR_FILE_NOT_FOUND;
585 mbox_lockread_file(f, filename);
587 mf = mailfile_init_from_file(f, filename);
589 mbox_unlock_file(f, filename);
596 static void mailfile_done(mailfile * f)
598 free_msg_list(f->msg_list);
605 #define MAX_READ 4096
607 static char * readfile(char * filename, int offset, int max_offset)
616 handle = fopen(filename, "rb");
618 if (handle == NULL) {
619 mailfile_error = MAILFILE_ERROR_FILE_NOT_FOUND;
623 size = max_offset - offset;
625 message = (char *) malloc(size + 1);
626 if (message == NULL) {
628 mailfile_error = MAILFILE_ERROR_MEMORY;
632 fseek(handle, offset, SEEK_SET);
636 if ((size - pos) > MAX_READ)
641 bread = fread(message + pos, 1, max, handle);
657 static char * mailfile_readmsg(mailfile f, int index)
663 struct _message * msginfo;
665 nth = g_list_nth(f->msg_list, index);
668 mailfile_error = MAILFILE_ERROR_MESSAGE_NOT_FOUND;
672 msginfo = (struct _message *)nth->data;
674 offset = msginfo->offset;
675 max_offset = msginfo->end;
676 message = readfile(f->filename, offset, max_offset);
678 mailfile_error = MAILFILE_ERROR_NO_ERROR;
683 static char * mailfile_readheader(mailfile f, int index)
689 struct _message * msginfo;
691 nth = g_list_nth(f->msg_list, index);
694 mailfile_error = MAILFILE_ERROR_MESSAGE_NOT_FOUND;
698 msginfo = (struct _message *)nth->data;
700 offset = msginfo->offset;
701 max_offset = msginfo->content;
702 message = readfile(f->filename, offset, max_offset);
704 mailfile_error = MAILFILE_ERROR_NO_ERROR;
709 static int mailfile_count(mailfile * f)
711 return g_list_length(f->msg_list);
714 static int mailfile_find_deleted(mailfile f, char * filename)
718 handle = fopen(filename, "rb");
721 struct _message m = elt->data;
722 n = fread(&m.deleted, sizeof(int), 1, handle);
725 elt = g_list_next(elt);
734 /**********************************************************/
736 /* mbox cache operations */
738 /**********************************************************/
745 gboolean modification;
748 typedef struct _mboxcache mboxcache;
750 static GHashTable * mbox_cache_table = NULL;
752 static MsgInfo *mbox_parse_msg(FILE * fp, struct _message * msg,
756 MsgFlags flags = { 0, 0 };
758 MSG_SET_PERM_FLAGS(flags, MSG_NEW | MSG_UNREAD);
760 g_return_val_if_fail(fp != NULL, NULL);
763 if (item->stype == F_QUEUE) {
764 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
765 } else if (item->stype == F_DRAFT) {
766 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
770 msginfo = procheader_parse_stream(fp, flags, FALSE, FALSE);
772 if (!msginfo) return NULL;
775 msginfo->msgnum = msg->msgnum;
776 msginfo->folder = item;
782 static void mbox_cache_init()
784 mbox_cache_table = g_hash_table_new(g_str_hash, g_str_equal);
787 static void mbox_cache_free_mbox(mboxcache * cache)
789 g_hash_table_remove(mbox_cache_table, cache->filename);
792 mailfile_done(cache->mf);
794 g_ptr_array_free(cache->tab_mf, FALSE);
796 g_free(cache->filename);
800 static void mbox_cache_get_msginfo_from_file(FILE * fp, GList * msg_list)
805 for(l = msg_list ; l != NULL ; l = g_list_next(l)) {
806 struct _message * msg;
808 msg = (struct _message *) l->data;
810 fseek(fp, msg->header, SEEK_SET);
811 msginfo = mbox_parse_msg(fp, msg, NULL);
815 g_strdup(msginfo->msgid);
816 if (msginfo->fromspace)
818 g_strdup(msginfo->fromspace);
819 msg->flags = msginfo->flags;
820 msg->old_flags = msginfo->flags;
822 procmsg_msginfo_free(msginfo);
827 static void mbox_cache_get_msginfo(gchar * filename, GList * msg_list)
831 fp = fopen(filename, "rb");
835 mbox_cache_get_msginfo_from_file(fp, msg_list);
839 static mboxcache * mbox_cache_read_mbox(gchar * filename)
846 if (stat(filename, &s) < 0)
849 mf = mailfile_init(filename);
853 cache = g_new0(mboxcache, 1);
855 cache->mtime = s.st_mtime;
857 cache->filename = g_strdup(filename);
858 cache->modification = FALSE;
860 cache->tab_mf = g_ptr_array_new();
861 for(l = mf->msg_list ; l != NULL ; l = g_list_next(l))
862 g_ptr_array_add(cache->tab_mf, l->data);
864 mbox_cache_get_msginfo(filename, mf->msg_list);
866 debug_print("read mbox - %s\n", filename);
871 static mboxcache * mbox_cache_read_mbox_from_file(FILE * fp, gchar * filename)
878 if (stat(filename, &s) < 0)
881 mf = mailfile_init_from_file(fp, filename);
885 cache = g_new0(mboxcache, 1);
887 cache->mtime = s.st_mtime;
889 cache->filename = g_strdup(filename);
891 cache->tab_mf = g_ptr_array_new();
892 for(l = mf->msg_list ; l != NULL ; l = g_list_next(l))
893 g_ptr_array_add(cache->tab_mf, l->data);
895 mbox_cache_get_msginfo_from_file(fp, mf->msg_list);
897 debug_print("read mbox from file - %s\n", filename);
902 static void mbox_cache_insert_mbox(mboxcache * data)
904 if (mbox_cache_table == NULL)
907 g_hash_table_insert(mbox_cache_table, data->filename, data);
910 static mboxcache * mbox_cache_get_mbox(gchar * filename)
912 if (mbox_cache_table == NULL)
915 return g_hash_table_lookup(mbox_cache_table, filename);
919 static gint mbox_cache_get_count(gchar * filename)
923 cache = mbox_cache_get_mbox(filename);
926 if (cache->mf == NULL)
928 return cache->mf->count;
931 static GList * mbox_cache_get_msg_list(gchar * filename)
935 cache = mbox_cache_get_mbox(filename);
940 if (cache->mf == NULL)
943 return cache->mf->msg_list;
946 static void mbox_cache_synchronize_lists(GList * old_msg_list,
947 GList * new_msg_list)
952 for(l2 = old_msg_list ; l2 != NULL ; l2 = g_list_next(l2)) {
953 struct _message * msg2 = l2->data;
955 if ((msg2->messageid == NULL) ||
956 (msg2->fromspace == NULL))
959 for(l = new_msg_list ; l != NULL ; l = g_list_next(l)) {
960 struct _message * msg = l->data;
962 if ((msg->messageid == NULL) ||
963 (msg->fromspace == NULL))
966 if ((strcmp(msg->messageid, msg2->messageid) == 0) &&
967 (strcmp(msg->fromspace, msg2->fromspace) == 0)) {
968 if (msg2->flags.perm_flags != msg2->old_flags.perm_flags) {
969 msg->flags = msg2->flags;
977 static void mbox_cache_synchronize(gchar * filename, gboolean sync)
979 mboxcache * new_cache;
980 mboxcache * old_cache;
981 gboolean scan_new = TRUE;
984 old_cache = mbox_cache_get_mbox(filename);
986 if (old_cache != NULL) {
987 if (stat(filename, &s) < 0) {
988 FILE_OP_ERROR(filename, "stat");
989 } else if (old_cache->mtime == s.st_mtime) {
990 debug_print("Folder is not modified.\n");
997 if (strstr(filename, "trash") == 0)
998 printf("old_cache: %p %s\n", old_cache, filename);
1000 printf("begin old\n");
1001 for(l = old_cache->mf->msg_list ; l != NULL ;
1002 l = g_list_next(l)) {
1003 struct _message * msg = l->data;
1004 printf("%p\n", msg);
1006 printf("end old\n");
1010 new_cache = mbox_cache_read_mbox(filename);
1013 if (strstr(filename, "trash") == 0)
1014 printf("new_cache: %p %s\n", new_cache, filename);
1016 printf("begin new\n");
1017 for(l = new_cache->mf->msg_list ; l != NULL ;
1018 l = g_list_next(l)) {
1019 struct _message * msg = l->data;
1020 printf("%p\n", msg);
1022 printf("end new\n");
1029 if (sync && new_cache && old_cache)
1030 mbox_cache_synchronize_lists(old_cache->mf->msg_list,
1031 new_cache->mf->msg_list);
1033 if (old_cache != NULL)
1034 mbox_cache_free_mbox(old_cache);
1037 mbox_cache_insert_mbox(new_cache);
1039 printf("insert %p %s\n", new_cache, new_cache->filename);
1040 printf("inserted %s %p\n", filename,
1041 mbox_cache_get_mbox(filename));
1047 static void mbox_cache_synchronize_from_file(FILE * fp, gchar * filename,
1050 mboxcache * new_cache;
1051 mboxcache * old_cache;
1052 gboolean scan_new = TRUE;
1055 old_cache = mbox_cache_get_mbox(filename);
1057 if (old_cache != NULL) {
1058 if (stat(filename, &s) < 0) {
1059 FILE_OP_ERROR(filename, "stat");
1060 } else if (old_cache->mtime == s.st_mtime) {
1061 debug_print("Folder is not modified.\n");
1072 if (strstr(filename, "trash") == 0)
1073 printf("old_cache: %p %s\n", old_cache, filename);
1076 printf("begin old\n");
1077 for(l = old_cache->mf->msg_list ; l != NULL ;
1078 l = g_list_next(l)) {
1079 struct _message * msg = l->data;
1080 printf("%p\n", msg);
1082 printf("end old\n");
1086 new_cache = mbox_cache_read_mbox_from_file(fp, filename);
1089 if (strstr(filename, "trash") == 0)
1090 printf("new_cache: %p %s\n", new_cache, filename);
1093 printf("begin new\n");
1094 for(l = new_cache->mf->msg_list ; l != NULL ;
1095 l = g_list_next(l)) {
1096 struct _message * msg = l->data;
1097 printf("%p\n", msg);
1099 printf("end new\n");
1106 if (sync && new_cache && old_cache)
1107 mbox_cache_synchronize_lists(old_cache->mf->msg_list,
1108 new_cache->mf->msg_list);
1110 if (old_cache != NULL)
1111 mbox_cache_free_mbox(old_cache);
1114 mbox_cache_insert_mbox(new_cache);
1116 printf("insert %p %s\n", new_cache, new_cache->filename);
1117 printf("inserted %s %p\n", filename,
1118 mbox_cache_get_mbox(filename));
1124 gboolean mbox_cache_msg_fetched(gchar * filename, gint num)
1126 struct _message * msg;
1129 cache = mbox_cache_get_mbox(filename);
1134 msg = (struct _message *) g_ptr_array_index(cache->tab_mf,
1139 return msg->fetched;
1142 void mbox_cache_msg_set_fetched(gchar * filename, gint num)
1144 struct _message * msg;
1147 cache = mbox_cache_get_mbox(filename);
1152 msg = (struct _message *) g_ptr_array_index(cache->tab_mf,
1157 msg->fetched = TRUE;
1160 struct _message * mbox_cache_get_msg(gchar * filename, gint num)
1164 cache = mbox_cache_get_mbox(filename);
1166 if (cache == NULL) {
1170 return (struct _message *) g_ptr_array_index(cache->tab_mf,
1175 /**********************************************************/
1177 /* mbox operations */
1179 /**********************************************************/
1182 GSList *mbox_get_msg_list(Folder *folder, FolderItem *item, gboolean use_cache)
1191 struct timeval tv_before, tv_after, tv_result;
1193 gettimeofday(&tv_before, NULL);
1198 mbox_path = mbox_folder_get_path(item);
1200 if (mbox_path == NULL)
1203 mbox_purge_deleted(mbox_path);
1205 fp = fopen(mbox_path, "rb");
1212 mbox_lockread_file(fp, mbox_path);
1214 mbox_cache_synchronize_from_file(fp, mbox_path, TRUE);
1216 item->last_num = mbox_cache_get_count(mbox_path);
1218 for(l = mbox_cache_get_msg_list(mbox_path) ; l != NULL ;
1219 l = g_list_next(l)) {
1220 struct _message * msg;
1222 msg = (struct _message *) l->data;
1224 if (MSG_IS_INVALID(msg->flags) || !MSG_IS_REALLY_DELETED(msg->flags)) {
1225 fseek(fp, msg->header, SEEK_SET);
1227 msginfo = mbox_parse_msg(fp, msg, item);
1229 if (!MSG_IS_INVALID(msg->flags))
1230 msginfo->flags = msg->flags;
1232 msg->old_flags = msginfo->flags;
1233 msg->flags = msginfo->flags;
1236 mlist = g_slist_append(mlist, msginfo);
1239 MSG_SET_PERM_FLAGS(msg->flags, MSG_REALLY_DELETED);
1243 mbox_unlock_file(fp, mbox_path);
1250 gettimeofday(&tv_after, NULL);
1252 timersub(&tv_after, &tv_before, &tv_result);
1253 g_print("mbox_get_msg_list: %s: elapsed time: %ld.%06ld sec\n",
1254 mbox_path, tv_result.tv_sec, tv_result.tv_usec);
1260 static gboolean mbox_extract_msg(FolderItem * item, gint msgnum,
1261 gchar * dest_filename)
1263 struct _message * msg;
1270 /* GList * msg_list;*/
1271 gboolean already_fetched;
1274 mbox_path = mbox_folder_get_path(item);
1276 if (mbox_path == NULL)
1279 src = fopen(mbox_path, "rb");
1285 mbox_lockread_file(src, mbox_path);
1287 mbox_cache_synchronize_from_file(src, mbox_path, TRUE);
1289 already_fetched = mbox_cache_msg_fetched(mbox_path, msgnum);
1291 if (already_fetched) {
1292 mbox_unlock_file(src, mbox_path);
1298 msg = mbox_cache_get_msg(mbox_path, msgnum);
1301 mbox_unlock_file(src, mbox_path);
1307 offset = msg->offset;
1308 max_offset = msg->end;
1310 size = max_offset - offset;
1312 fseek(src, offset, SEEK_SET);
1314 dest = fopen(dest_filename, "wb");
1316 mbox_unlock_file(src, mbox_path);
1322 if (change_file_mode_rw(dest, dest_filename) < 0) {
1323 FILE_OP_ERROR(dest_filename, "chmod");
1324 g_warning("can't change file mode\n");
1327 if (!mbox_write_data(src, dest, dest_filename, size)) {
1328 mbox_unlock_file(src, mbox_path);
1331 unlink(dest_filename);
1339 FILE_OP_ERROR(mbox_path, "fread");
1343 mbox_cache_msg_set_fetched(mbox_path, msgnum);
1345 if (fclose(dest) == -1) {
1346 FILE_OP_ERROR(dest_filename, "fclose");
1350 mbox_unlock_file(src, mbox_path);
1352 if (fclose(src) == -1) {
1353 FILE_OP_ERROR(mbox_path, "fclose");
1360 unlink(dest_filename);
1367 gchar *mbox_fetch_msg(Folder *folder, FolderItem *item, gint num)
1372 g_return_val_if_fail(item != NULL, NULL);
1374 path = folder_item_get_path(item);
1375 if (!is_dir_exist(path))
1376 make_dir_hier(path);
1378 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(num), NULL);
1382 if (!mbox_extract_msg(item, num, filename)) {
1390 gint mbox_add_msg(Folder *folder, FolderItem *dest, const gchar *file,
1391 gboolean remove_source)
1400 gchar from_line[MSGBUFSIZE];
1402 if (dest->last_num < 0) {
1403 mbox_scan_folder(folder, dest);
1404 if (dest->last_num < 0) return -1;
1407 src_fp = fopen(file, "rb");
1408 if (src_fp == NULL) {
1412 mbox_path = mbox_folder_get_path(dest);
1413 if (mbox_path == NULL)
1416 dest_fp = fopen(mbox_path, "ab");
1417 if (dest_fp == NULL) {
1423 if (change_file_mode_rw(dest_fp, mbox_path) < 0) {
1424 FILE_OP_ERROR(mbox_path, "chmod");
1425 g_warning("can't change file mode\n");
1428 old_size = ftell(dest_fp);
1430 mbox_lockwrite_file(dest_fp, mbox_path);
1432 if (fgets(from_line, sizeof(from_line), src_fp) == NULL) {
1433 mbox_unlock_file(dest_fp, mbox_path);
1434 g_warning("unvalid file - %s.\n", file);
1441 if (strncmp(from_line, "From ", 5) != 0) {
1444 if (stat(file, &s) < 0) {
1445 mbox_unlock_file(dest_fp, mbox_path);
1446 g_warning("invalid file - %s.\n", file);
1453 fprintf(dest_fp, "From - %s", ctime(&s.st_mtime));
1456 fputs(from_line, dest_fp);
1459 n_read = fread(buf, 1, sizeof(buf), src_fp);
1460 if ((n_read < (gint) sizeof(buf)) && ferror(src_fp))
1462 if (fwrite(buf, n_read, 1, dest_fp) < 1) {
1463 mbox_unlock_file(dest_fp, mbox_path);
1464 g_warning("writing to %s failed.\n", mbox_path);
1465 ftruncate(fileno(dest_fp), old_size);
1472 if (n_read < (gint) sizeof(buf))
1478 if (ferror(src_fp)) {
1479 FILE_OP_ERROR(mbox_path, "fread");
1482 mbox_unlock_file(dest_fp, mbox_path);
1484 if (fclose(src_fp) == -1) {
1485 FILE_OP_ERROR(file, "fclose");
1489 if (fclose(dest_fp) == -1) {
1490 FILE_OP_ERROR(mbox_path, "fclose");
1496 ftruncate(fileno(dest_fp), old_size);
1501 if (remove_source) {
1502 if (unlink(file) < 0)
1503 FILE_OP_ERROR(file, "unlink");
1509 return dest->last_num;
1513 gint mbox_remove_msg(Folder *folder, FolderItem *item, gint num)
1515 struct _message * msg;
1518 mbox_path = mbox_folder_get_path(item);
1519 if (mbox_path == NULL)
1522 mbox_cache_synchronize(mbox_path, TRUE);
1524 msg = mbox_cache_get_msg(mbox_path, num);
1529 MSG_SET_PERM_FLAGS(msg->flags, MSG_REALLY_DELETED);
1534 gint mbox_remove_all_msg(Folder *folder, FolderItem *item)
1539 mbox_path = mbox_folder_get_path(item);
1540 if (mbox_path == NULL)
1543 fp = fopen(mbox_path, "wb");
1557 gint mbox_move_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1562 filename = mbox_fetch_msg(folder, msginfo->folder, msginfo->msgnum);
1563 if (filename == NULL)
1566 msgnum = mbox_add_msg(folder, dest, filename, TRUE);
1569 MSG_SET_FLAGS(msginfo->flags, MSG_REALLY_DELETED);
1570 mbox_change_flags(folder, msginfo->folder, msginfo);
1576 gint mbox_move_msgs_with_dest(Folder *folder, FolderItem *dest, GSList *msglist)
1579 gchar * mbox_path = NULL;
1581 for(l = msglist ; l != NULL ; l = g_slist_next(l)) {
1582 MsgInfo * msginfo = (MsgInfo *) l->data;
1584 if (msginfo->folder && mbox_path == NULL)
1585 mbox_path = mbox_folder_get_path(msginfo->folder);
1587 mbox_move_msg(folder, dest, msginfo);
1591 mbox_cache_synchronize(mbox_path);
1595 mbox_path = mbox_folder_get_path(dest);
1596 mbox_cache_synchronize(mbox_path);
1599 return dest->last_num;
1604 gint mbox_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1609 filename = mbox_fetch_msg(folder, msginfo->folder, msginfo->msgnum);
1610 if (filename == NULL)
1613 msgnum = mbox_add_msg(folder, dest, filename, FALSE);
1618 gint mbox_copy_msgs_with_dest(Folder *folder, FolderItem *dest, GSList *msglist)
1621 gchar * mbox_path = NULL;
1623 for(l = msglist ; l != NULL ; l = g_slist_next(l)) {
1624 MsgInfo * msginfo = (MsgInfo *) l->data;
1626 if (msginfo->folder && mbox_path == NULL)
1627 mbox_path = mbox_folder_get_path(msginfo->folder);
1629 mbox_copy_msg(folder, dest, msginfo);
1633 mbox_cache_synchronize(mbox_path);
1637 mbox_path = mbox_folder_get_path(dest);
1638 mbox_cache_synchronize(mbox_path);
1641 return dest->last_num;
1645 struct _copy_flags_info
1651 typedef struct _copy_flags_info CopyFlagsInfo;
1653 GSList * copy_flags_data = NULL;
1655 gint mbox_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1657 Folder * src_folder;
1660 CopyFlagsInfo * flags_info;
1662 src_folder = msginfo->folder->folder;
1665 mbox_path = mbox_folder_get_path(msginfo->folder);
1666 mbox_rewrite(mbox_path);
1670 filename = folder_item_fetch_msg(msginfo->folder,
1672 if (filename == NULL)
1675 num = mbox_add_msg(folder, dest, filename, FALSE);
1678 mbox_path = mbox_folder_get_path(dest);
1679 msg = mbox_cache_get_msg(mbox_path, num);
1681 msg->flags = msginfo->flags;
1688 flags_info = g_new0(CopyFlagsInfo, 1);
1689 flags_info->num = num;
1690 flags_info->flags = msginfo->flags;
1691 copy_flags_data = g_slist_append(copy_flags_data, flags_info);
1696 void mbox_finished_copy(Folder *folder, FolderItem *dest)
1702 mbox_path = mbox_folder_get_path(dest);
1703 if (mbox_path == NULL)
1706 mbox_cache_synchronize(mbox_path, TRUE);
1708 for(l = copy_flags_data ; l != NULL ; l = g_slist_next(l)) {
1709 CopyFlagsInfo * flags_info = l->data;
1710 struct _message * msg;
1712 msg = mbox_cache_get_msg(mbox_path, flags_info->num);
1714 msg->flags = flags_info->flags;
1718 if (copy_flags_data != NULL) {
1719 cache = mbox_cache_get_mbox(mbox_path);
1720 cache->modification = TRUE;
1723 g_slist_free(copy_flags_data);
1724 copy_flags_data = NULL;
1726 mbox_rewrite(mbox_path);
1731 void mbox_scan_folder(Folder *folder, FolderItem *item)
1738 mbox_path = mbox_folder_get_path(item);
1739 if (mbox_path == NULL)
1742 mbox_cache_synchronize(mbox_path, TRUE);
1744 cached = mbox_cache_get_mbox(mbox_path);
1746 if (cached == NULL) {
1755 n_msg = mbox_cache_get_count(mbox_path);
1758 item->new = item->unread = item->total = 0;
1765 for(l = mbox_cache_get_msg_list(mbox_path) ; l != NULL ;
1766 l = g_list_next(l)) {
1767 struct _message * msg = (struct _message *) l->data;
1768 if (!MSG_IS_REALLY_DELETED(msg->flags))
1770 if (MSG_IS_NEW(msg->flags) && !MSG_IS_IGNORE_THREAD(msg->flags))
1772 if (MSG_IS_UNREAD(msg->flags) && !MSG_IS_IGNORE_THREAD(msg->flags))
1777 item->unread = unread;
1778 item->total = total;
1781 debug_print("Last number in dir %s = %d\n", mbox_path,
1783 item->last_num = n_msg;
1787 gchar * mbox_get_virtual_path(FolderItem * item)
1792 if (item->parent == NULL) {
1796 gchar * parent_path;
1797 gchar * result_path;
1799 parent_path = mbox_get_virtual_path(item->parent);
1800 if (parent_path == NULL)
1801 result_path = g_strdup(item->name);
1803 result_path = g_strconcat(parent_path,
1806 g_free(parent_path);
1812 static gboolean mbox_write_data(FILE * mbox_fp, FILE * new_fp,
1813 gchar * new_filename, gint size)
1821 while (pos < size) {
1822 if ((size - pos) > (gint) sizeof(buf))
1827 n_read = fread(buf, 1, max, mbox_fp);
1829 if (n_read < max && ferror(mbox_fp)) {
1832 if (fwrite(buf, n_read, 1, new_fp) < 1) {
1833 g_warning("writing to %s failed.\n", new_filename);
1846 static gboolean mbox_write_message(FILE * mbox_fp, FILE * new_fp,
1847 gchar * new_filename,
1848 struct _message * msg)
1851 GPtrArray * headers;
1854 fseek(mbox_fp, msg->header, SEEK_SET);
1856 headers = procheader_get_header_array_asis(mbox_fp);
1858 for (i = 0; i < (gint) headers->len; i++) {
1859 Header * h = g_ptr_array_index(headers, i);
1861 if (!procheader_headername_equal(h->name,
1863 !procheader_headername_equal(h->name,
1865 fwrite(h->name, strlen(h->name),
1867 if (h->name[strlen(h->name) - 1] != ' ')
1868 fwrite(" ", 1, 1, new_fp);
1869 fwrite(h->body, strlen(h->body),
1871 fwrite("\n", 1, 1, new_fp);
1873 procheader_header_free(h);
1874 g_ptr_array_remove_index(headers, i);
1878 g_ptr_array_free(headers, FALSE);
1880 if (!MSG_IS_INVALID(msg->flags)) {
1882 fwrite("Status: ", strlen("Status: "), 1, new_fp);
1883 if (!MSG_IS_UNREAD(msg->flags))
1884 fwrite("R", 1, 1, new_fp);
1885 fwrite("O", 1, 1, new_fp);
1886 fwrite("\n", 1, 1, new_fp);
1888 /* X-Status header */
1889 if (MSG_IS_REALLY_DELETED(msg->flags)
1890 || MSG_IS_MARKED(msg->flags)
1891 || MSG_IS_DELETED(msg->flags)
1892 || MSG_IS_REPLIED(msg->flags)
1893 || MSG_IS_FORWARDED(msg->flags)) {
1894 fwrite("X-Status: ", strlen("X-Status: "), 1, new_fp);
1895 if (MSG_IS_REALLY_DELETED(msg->flags))
1896 fwrite("D", 1, 1, new_fp); /* really deleted */
1898 if (MSG_IS_MARKED(msg->flags))
1899 fwrite("F", 1, 1, new_fp);
1900 if (MSG_IS_DELETED(msg->flags))
1901 fwrite("d", 1, 1, new_fp);
1902 if (MSG_IS_REPLIED(msg->flags))
1903 fwrite("r", 1, 1, new_fp);
1904 if (MSG_IS_FORWARDED(msg->flags))
1905 fwrite("f", 1, 1, new_fp);
1907 fwrite("\n", 1, 1, new_fp);
1911 fwrite("\n", 1, 1, new_fp);
1913 size = msg->end - msg->content;
1914 fseek(mbox_fp, msg->content, SEEK_SET);
1916 return mbox_write_data(mbox_fp, new_fp, new_filename, size);
1919 void mbox_update_mark(Folder * folder, FolderItem * item)
1923 mbox_path = mbox_folder_get_path(item);
1924 if (mbox_path == NULL)
1927 mbox_rewrite(mbox_path);
1931 void mbox_change_flags(Folder * folder, FolderItem * item, MsgInfo * info)
1933 struct _message * msg;
1937 mbox_path = mbox_folder_get_path(item);
1938 if (mbox_path == NULL)
1941 msg = mbox_cache_get_msg(mbox_path, info->msgnum);
1943 cache = mbox_cache_get_mbox(mbox_path);
1947 if ((msg == NULL) || (cache == NULL))
1950 msg->flags = info->flags;
1952 cache->modification = TRUE;
1957 static gboolean mbox_rewrite(gchar * mbox)
1968 msg_list = mbox_cache_get_msg_list(mbox);
1970 cache = mbox_cache_get_mbox(mbox);
1974 if (!cache->modification) {
1975 debug_print("no modification - %s\n", mbox);
1979 debug_print("save modification - %s\n", mbox);
1981 mbox_fp = fopen(mbox, "rb+");
1982 mbox_lockwrite_file(mbox_fp, mbox);
1984 mbox_cache_synchronize_from_file(mbox_fp, mbox, TRUE);
1986 new = g_strconcat(mbox, ".", itos((int) mbox), NULL);
1987 new_fp = fopen(new, "wb");
1989 if (change_file_mode_rw(new_fp, new) < 0) {
1990 FILE_OP_ERROR(new, "chmod");
1991 g_warning("can't change file mode\n");
1994 mbox_lockwrite_file(new_fp, new);
1999 msg_list = mbox_cache_get_msg_list(mbox);
2000 for(l = msg_list ; l != NULL ; l = g_list_next(l)) {
2001 struct _message * msg = (struct _message *) l->data;
2002 if (!mbox_write_message(mbox_fp, new_fp, new, msg)) {
2011 if (rename(new, mbox) == -1) {
2012 g_warning("can't rename %s to %s\n", new, mbox);
2013 mbox_unlock_file(new_fp, new);
2015 mbox_unlock_file(mbox_fp, mbox);
2021 if (change_file_mode_rw(new_fp, mbox) < 0) {
2022 FILE_OP_ERROR(new, "chmod");
2023 g_warning("can't change file mode\n");
2026 mbox_unlock_file(new_fp, new);
2030 mbox_unlock_file(mbox_fp, mbox);
2034 debug_print("%i messages written - %s\n", count, mbox);
2036 cache = mbox_cache_get_mbox(mbox);
2041 mbox_cache_synchronize(mbox, FALSE);
2048 static gboolean mbox_purge_deleted(gchar * mbox)
2055 gboolean modification = FALSE;
2059 mbox_cache_synchronize(mbox, TRUE);
2061 msg_list = mbox_cache_get_msg_list(mbox);
2063 for(l = msg_list ; l != NULL ; l = g_list_next(l)) {
2064 struct _message * msg = (struct _message *) l->data;
2065 if (!MSG_IS_INVALID(msg->flags) && MSG_IS_REALLY_DELETED(msg->flags)) {
2066 modification = TRUE;
2071 if (!modification) {
2072 debug_print("no deleted messages - %s\n", mbox);
2076 debug_print("purge deleted messages - %s\n", mbox);
2078 mbox_fp = fopen(mbox, "rb+");
2079 mbox_lockwrite_file(mbox_fp, mbox);
2081 mbox_cache_synchronize_from_file(mbox_fp, mbox, TRUE);
2083 new = g_strconcat(mbox, ".", itos((int) mbox), NULL);
2084 new_fp = fopen(new, "wb");
2086 if (change_file_mode_rw(new_fp, new) < 0) {
2087 FILE_OP_ERROR(new, "chmod");
2088 g_warning("can't change file mode\n");
2091 mbox_lockwrite_file(new_fp, new);
2096 msg_list = mbox_cache_get_msg_list(mbox);
2097 for(l = msg_list ; l != NULL ; l = g_list_next(l)) {
2098 struct _message * msg = (struct _message *) l->data;
2099 if (MSG_IS_INVALID(msg->flags) || !MSG_IS_REALLY_DELETED(msg->flags)) {
2100 if (!mbox_write_message(mbox_fp, new_fp, new, msg)) {
2110 if (rename(new, mbox) == -1) {
2111 g_warning("can't rename %s to %s\n", new, mbox);
2112 mbox_unlock_file(new_fp, new);
2114 mbox_unlock_file(mbox_fp, mbox);
2120 if (change_file_mode_rw(new_fp, mbox) < 0) {
2121 FILE_OP_ERROR(new, "chmod");
2122 g_warning("can't change file mode\n");
2125 mbox_unlock_file(new_fp, new);
2129 mbox_unlock_file(mbox_fp, mbox);
2133 debug_print("%i messages written - %s\n", count, mbox);
2135 mbox_cache_synchronize(mbox, FALSE);
2140 #define MAKE_DIR_IF_NOT_EXIST(dir) \
2142 if (!is_dir_exist(dir)) { \
2143 if (is_file_exist(dir)) { \
2144 g_warning("File `%s' already exists.\n" \
2145 "Can't create folder.", dir); \
2148 if (mkdir(dir, S_IRWXU) < 0) { \
2149 FILE_OP_ERROR(dir, "mkdir"); \
2152 if (chmod(dir, S_IRWXU) < 0) \
2153 FILE_OP_ERROR(dir, "chmod"); \
2157 gint mbox_create_tree(Folder *folder)
2161 g_return_val_if_fail(folder != NULL, -1);
2163 CHDIR_RETURN_VAL_IF_FAIL(get_home_dir(), -1);
2164 rootpath = LOCAL_FOLDER(folder)->rootpath;
2165 MAKE_DIR_IF_NOT_EXIST(rootpath);
2166 CHDIR_RETURN_VAL_IF_FAIL(rootpath, -1);
2171 #undef MAKE_DIR_IF_NOT_EXIST
2173 static gchar * mbox_get_new_path(FolderItem * parent, gchar * name)
2177 if (strchr(name, '/') == NULL) {
2178 if (parent->path != NULL)
2179 path = g_strconcat(parent->path, ".sbd", G_DIR_SEPARATOR_S, name, NULL);
2181 path = g_strdup(name);
2184 path = g_strdup(name);
2189 static gchar * mbox_get_folderitem_name(gchar * name)
2193 foldername = g_strdup(g_basename(name));
2198 FolderItem *mbox_create_folder(Folder *folder, FolderItem *parent,
2202 FolderItem *new_item;
2205 g_return_val_if_fail(folder != NULL, NULL);
2206 g_return_val_if_fail(parent != NULL, NULL);
2207 g_return_val_if_fail(name != NULL, NULL);
2209 path = mbox_get_new_path(parent, (gchar *) name);
2211 foldername = mbox_get_folderitem_name((gchar *) name);
2213 new_item = folder_item_new(folder, foldername, path);
2214 folder_item_append(parent, new_item);
2216 if (!strcmp(name, "inbox")) {
2217 new_item->stype = F_INBOX;
2218 new_item->folder->inbox = new_item;
2219 } else if (!strcmp(name, "outbox")) {
2220 new_item->stype = F_OUTBOX;
2221 new_item->folder->outbox = new_item;
2222 } else if (!strcmp(name, "draft")) {
2223 new_item->stype = F_DRAFT;
2224 new_item->folder->draft = new_item;
2225 } else if (!strcmp(name, "queue")) {
2226 new_item->stype = F_QUEUE;
2227 new_item->folder->queue = new_item;
2228 } else if (!strcmp(name, "trash")) {
2229 new_item->stype = F_TRASH;
2230 new_item->folder->trash = new_item;
2239 gint mbox_rename_folder(Folder *folder, FolderItem *item, const gchar *name)
2244 g_return_val_if_fail(folder != NULL, -1);
2245 g_return_val_if_fail(item != NULL, -1);
2246 g_return_val_if_fail(item->path != NULL, -1);
2247 g_return_val_if_fail(name != NULL, -1);
2249 path = mbox_get_new_path(item->parent, (gchar *) name);
2250 foldername = mbox_get_folderitem_name((gchar *) name);
2252 if (rename(item->path, path) == -1) {
2255 g_warning("Cannot rename folder item");
2263 item->name = foldername;
2269 gint mbox_remove_folder(Folder *folder, FolderItem *item)
2271 g_return_val_if_fail(folder != NULL, -1);
2272 g_return_val_if_fail(item != NULL, -1);
2273 g_return_val_if_fail(item->path != NULL, -1);
2275 folder_item_remove(item);
2279 gint mbox_get_num_list(Folder *folder, FolderItem *item, GSList **mlist)
2286 mbox_path = mbox_folder_get_path(item);
2288 if (mbox_path == NULL)
2291 mbox_purge_deleted(mbox_path);
2293 fp = fopen(mbox_path, "rb");
2300 mbox_lockread_file(fp, mbox_path);
2302 mbox_cache_synchronize_from_file(fp, mbox_path, TRUE);
2304 item->last_num = mbox_cache_get_count(mbox_path);
2306 for(l = mbox_cache_get_msg_list(mbox_path) ; l != NULL ;
2307 l = g_list_next(l)) {
2308 struct _message * msg;
2310 msg = (struct _message *) l->data;
2312 if (MSG_IS_INVALID(msg->flags) || !MSG_IS_REALLY_DELETED(msg->flags)) {
2313 *mlist = g_slist_append(*mlist, GINT_TO_POINTER(msg->msgnum));
2316 MSG_SET_PERM_FLAGS(msg->flags, MSG_REALLY_DELETED);
2320 mbox_unlock_file(fp, mbox_path);
2329 MsgInfo *mbox_get_msginfo(Folder *folder, FolderItem *item, gint num)
2332 struct _message *msg;
2336 g_return_val_if_fail(folder != NULL, NULL);
2337 g_return_val_if_fail(item != NULL, NULL);
2339 mbox_path = mbox_folder_get_path(item);
2341 g_return_val_if_fail(mbox_path != NULL, NULL);
2343 src = fopen(mbox_path, "rb");
2348 mbox_lockread_file(src, mbox_path);
2349 mbox_cache_synchronize_from_file(src, mbox_path, TRUE);
2351 msg = mbox_cache_get_msg(mbox_path, num);
2353 mbox_unlock_file(src, mbox_path);
2359 fseek(src, msg->header, SEEK_SET);
2360 msginfo = mbox_parse_msg(src, msg, item);
2362 mbox_unlock_file(src, mbox_path);
2369 gboolean mbox_check_msgnum_validity(Folder *folder, FolderItem *item)
2371 mboxcache * old_cache;
2372 gboolean scan_new = TRUE;
2376 filename = mbox_folder_get_path(item);
2378 old_cache = mbox_cache_get_mbox(filename);
2380 if (old_cache != NULL) {
2381 if (stat(filename, &s) < 0) {
2382 FILE_OP_ERROR(filename, "stat");
2383 } else if (old_cache->mtime == s.st_mtime) {
2384 debug_print("Folder is not modified.\n");