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 Folder *mbox_folder_new(const gchar *name, const gchar *path)
53 folder = (Folder *)g_new0(MBOXFolder, 1);
54 mbox_folder_init(folder, name, path);
59 void mbox_folder_destroy(MBOXFolder *folder)
61 folder_local_folder_destroy(LOCAL_FOLDER(folder));
64 static void mbox_folder_init(Folder *folder, const gchar *name, const gchar *path)
66 folder_local_folder_init(folder, name, path);
68 folder->type = F_MBOX;
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;
83 static gchar * mbox_folder_create_parent(const gchar * path)
85 if (!is_file_exist(path)) {
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';
92 if (!is_dir_exist(new_path))
93 make_dir_hier(new_path);
100 static gchar *mbox_folder_get_path(FolderItem *item)
105 g_return_val_if_fail(item != NULL, NULL);
107 if (item->path && item->path[0] == G_DIR_SEPARATOR) {
108 mbox_folder_create_parent(item->path);
109 return g_strdup(item->path);
112 folder_path = g_strdup(LOCAL_FOLDER(item->folder)->rootpath);
113 g_return_val_if_fail(folder_path != NULL, NULL);
115 if (folder_path[0] == G_DIR_SEPARATOR) {
117 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
121 path = g_strdup(folder_path);
124 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
125 folder_path, G_DIR_SEPARATOR_S,
128 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
134 mbox_folder_create_parent(path);
140 /**********************************************************/
144 /**********************************************************/
147 static GSList * file_lock = NULL;
149 static gboolean mbox_file_lock_file(gchar * base)
151 gchar *lockfile, *locklink;
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"));
164 fprintf(lockfp, "%d\n", getpid());
167 locklink = g_strconcat(base, ".lock", NULL);
168 while (link(lockfile, locklink) < 0) {
169 FILE_OP_ERROR(lockfile, "link");
171 g_warning(_("can't create %s\n"), lockfile);
177 g_warning(_("mailbox is owned by another"
178 " process, waiting...\n"));
188 static gboolean mbox_fcntl_lockwrite_file(FILE * fp)
192 lck.l_type = F_WRLCK;
197 if (fcntl(fileno(fp), F_SETLK, &lck) < 0)
203 static gboolean mbox_fcntl_lockread_file(FILE * fp)
207 lck.l_type = F_RDLCK;
212 if (fcntl(fileno(fp), F_SETLK, &lck) < 0)
218 static gboolean mbox_fcntl_unlock_file(FILE * fp)
222 lck.l_type = F_UNLCK;
227 if (fcntl(fileno(fp), F_SETLK, &lck) < 0)
233 static gboolean mbox_file_unlock_file(gchar * base)
237 lockfile = g_strdup_printf("%s.lock", base);
244 static gboolean mbox_lockread_file(FILE * fp, gchar * base)
248 result = mbox_fcntl_lockread_file(fp);
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);
255 g_warning(_("could not lock read file %s\n"), base);
258 debug_print("fcntl lock %s.\n", base);
263 static gboolean mbox_lockwrite_file(FILE * fp, gchar * base)
267 result = mbox_fcntl_lockwrite_file(fp);
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);
274 g_warning(_("could not lock write file %s\n"), base);
277 debug_print("fcntl lock %s.\n", base);
282 static gboolean mbox_unlock_file(FILE * fp, gchar * base)
284 gboolean result = FALSE;
286 gboolean unlocked = FALSE;
288 for(l = file_lock ; l != NULL ; l = g_slist_next(l)) {
289 gchar * data = l->data;
291 if (strcmp(data, base) == 0) {
292 file_lock = g_slist_remove(file_lock, data);
294 result = mbox_file_unlock_file(base);
296 debug_print("lockfile unlock - %s.\n", base);
302 result = mbox_fcntl_unlock_file(fp);
303 debug_print("fcntl unlock - %s.\n", base);
309 /**********************************************************/
313 /**********************************************************/
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
320 static int mailfile_error = MAILFILE_ERROR_NO_ERROR;
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
331 #define STATE_MASK 0x0FF /* filter state from functions */
333 #define STATE_RESTORE_POS 0x100 /* go back while reading */
335 typedef struct _mailfile mailfile;
359 #define MSG_IS_INVALID(msg) \
360 ((msg).perm_flags == (msg).tmp_flags && (msg).tmp_flags == -1)
362 #define MSG_SET_INVALID(msg) \
363 ((msg).perm_flags = (msg).tmp_flags = -1)
365 static int startFrom(char * s)
367 return (strncmp(s, "From ", 5) == 0);
370 static int startSpace(char * s)
372 return ((*s == ' ') || (*s == '\t'));
375 static int startEmpty(char * s)
380 static void free_msg_list(GList * l)
382 GList * elt = g_list_first(l);
387 elt = g_list_next(elt);
394 static mailfile * mailfile_init_from_file(FILE * f, gchar * filename)
397 GList * msg_list = NULL;
404 struct _message * data = NULL;
410 while (state != STATE_END_OF_FILE) {
411 if ((state & STATE_RESTORE_POS) == 0) {
412 former_pos = lastpos;
415 r = fgets(s, 256, f);
418 ignore_next = (s[strlen(s) - 1] != '\n');
423 switch(state & 0x0F) {
426 state = STATE_END_OF_FILE;
427 else if (startFrom(s)) {
428 state = STATE_FROM_READ;
430 data = g_new0(struct _message, 1);
432 free_msg_list(msg_list);
437 data->msgnum = msgnum;
438 data->offset = lastpos;
439 data->header = lastpos;
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,
455 case STATE_TEXT_READ:
458 else if (startFrom(s))
459 state = STATE_END | STATE_RESTORE_POS;
461 state = STATE_TEXT_READ;
464 case STATE_TEXT_BEGIN:
465 data->content = lastpos;
468 else if (startFrom(s)) {
469 state = STATE_END | STATE_RESTORE_POS;
472 state = STATE_TEXT_READ;
476 case STATE_FROM_READ:
477 data->content = lastpos;
480 else if (startSpace(s))
481 state = STATE_FROM_READ;
482 else if (startEmpty(s))
483 state = STATE_TEXT_READ;
485 state = STATE_FIELD_READ;
488 case STATE_FIELD_READ:
489 data->content = lastpos;
492 else if (startSpace(s))
493 state = STATE_FIELD_READ;
494 else if (startEmpty(s)) {
495 state = STATE_TEXT_BEGIN;
498 state = STATE_FIELD_READ;
502 if ((state & STATE_MASK) == STATE_END) {
503 state = STATE_BEGIN | (state & STATE_RESTORE_POS);
509 r = fgets(s, 256, f);
510 if (r == NULL || *r == '\0')
513 while (s[strlen(s) - 1] != '\n');
517 mf = (mailfile *) g_new0(struct _mailfile, 1);
519 free_msg_list(msg_list);
520 mailfile_error = MAILFILE_ERROR_MEMORY;
524 mf->msg_list = g_list_first(msg_list);
526 mf->filename = g_strdup(filename);
529 mailfile_error = MAILFILE_ERROR_NO_ERROR;
534 static mailfile * mailfile_init(char * filename)
540 f = fopen(filename, "rb");
543 mailfile_error = MAILFILE_ERROR_FILE_NOT_FOUND;
547 mbox_lockread_file(f, filename);
549 mf = mailfile_init_from_file(f, filename);
551 mbox_unlock_file(f, filename);
558 static void mailfile_done(mailfile * f)
560 free_msg_list(f->msg_list);
567 #define MAX_READ 4096
569 static char * readfile(char * filename, int offset, int max_offset)
578 handle = fopen(filename, "rb");
580 if (handle == NULL) {
581 mailfile_error = MAILFILE_ERROR_FILE_NOT_FOUND;
585 size = max_offset - offset;
587 message = (char *) malloc(size + 1);
588 if (message == NULL) {
590 mailfile_error = MAILFILE_ERROR_MEMORY;
594 fseek(handle, offset, SEEK_SET);
598 if ((size - pos) > MAX_READ)
603 bread = fread(message + pos, 1, max, handle);
619 static char * mailfile_readmsg(mailfile f, int index)
625 struct _message * msginfo;
627 nth = g_list_nth(f->msg_list, index);
630 mailfile_error = MAILFILE_ERROR_MESSAGE_NOT_FOUND;
634 msginfo = (struct _message *)nth->data;
636 offset = msginfo->offset;
637 max_offset = msginfo->end;
638 message = readfile(f->filename, offset, max_offset);
640 mailfile_error = MAILFILE_ERROR_NO_ERROR;
645 static char * mailfile_readheader(mailfile f, int index)
651 struct _message * msginfo;
653 nth = g_list_nth(f->msg_list, index);
656 mailfile_error = MAILFILE_ERROR_MESSAGE_NOT_FOUND;
660 msginfo = (struct _message *)nth->data;
662 offset = msginfo->offset;
663 max_offset = msginfo->content;
664 message = readfile(f->filename, offset, max_offset);
666 mailfile_error = MAILFILE_ERROR_NO_ERROR;
671 static int mailfile_count(mailfile * f)
673 return g_list_length(f->msg_list);
676 static int mailfile_find_deleted(mailfile f, char * filename)
680 handle = fopen(filename, "rb");
683 struct _message m = elt->data;
684 n = fread(&m.deleted, sizeof(int), 1, handle);
687 elt = g_list_next(elt);
696 /**********************************************************/
698 /* mbox cache operations */
700 /**********************************************************/
707 gboolean modification;
710 typedef struct _mboxcache mboxcache;
712 static GHashTable * mbox_cache_table = NULL;
714 static MsgInfo *mbox_parse_msg(FILE * fp, struct _message * msg,
718 MsgFlags flags = { 0, 0 };
720 MSG_SET_PERM_FLAGS(flags, MSG_NEW | MSG_UNREAD);
722 g_return_val_if_fail(fp != NULL, 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);
732 msginfo = procheader_file_parse(fp, flags, FALSE, FALSE);
734 if (!msginfo) return NULL;
737 msginfo->msgnum = msg->msgnum;
738 msginfo->folder = item;
744 static void mbox_cache_init()
746 mbox_cache_table = g_hash_table_new(g_str_hash, g_str_equal);
749 static void mbox_cache_done()
751 g_hash_table_destroy(mbox_cache_table);
754 static void mbox_cache_free_mbox(mboxcache * cache)
756 g_hash_table_remove(mbox_cache_table, cache->filename);
759 mailfile_done(cache->mf);
761 g_ptr_array_free(cache->tab_mf, FALSE);
763 g_free(cache->filename);
767 static void mbox_cache_get_msginfo_from_file(FILE * fp, GList * msg_list)
772 for(l = msg_list ; l != NULL ; l = g_list_next(l)) {
773 struct _message * msg;
775 msg = (struct _message *) l->data;
777 fseek(fp, msg->header, SEEK_SET);
778 msginfo = mbox_parse_msg(fp, msg, NULL);
782 g_strdup(msginfo->msgid);
783 if (msginfo->fromspace)
785 g_strdup(msginfo->fromspace);
786 msg->flags = msginfo->flags;
787 msg->old_flags = msginfo->flags;
789 procmsg_msginfo_free(msginfo);
794 static void mbox_cache_get_msginfo(gchar * filename, GList * msg_list)
798 fp = fopen(filename, "rb");
802 mbox_cache_get_msginfo_from_file(fp, msg_list);
806 static mboxcache * mbox_cache_read_mbox(gchar * filename)
813 if (stat(filename, &s) < 0)
816 mf = mailfile_init(filename);
820 cache = g_new0(mboxcache, 1);
822 cache->mtime = s.st_mtime;
824 cache->filename = g_strdup(filename);
825 cache->modification = FALSE;
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);
831 mbox_cache_get_msginfo(filename, mf->msg_list);
833 debug_print(_("read mbox - %s\n"), filename);
838 static mboxcache * mbox_cache_read_mbox_from_file(FILE * fp, gchar * filename)
845 if (stat(filename, &s) < 0)
848 mf = mailfile_init_from_file(fp, filename);
852 cache = g_new0(mboxcache, 1);
854 cache->mtime = s.st_mtime;
856 cache->filename = g_strdup(filename);
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);
862 mbox_cache_get_msginfo_from_file(fp, mf->msg_list);
864 debug_print(_("read mbox from file - %s\n"), filename);
869 static void mbox_cache_insert_mbox(mboxcache * data)
871 if (mbox_cache_table == NULL)
874 g_hash_table_insert(mbox_cache_table, data->filename, data);
877 static mboxcache * mbox_cache_get_mbox(gchar * filename)
879 if (mbox_cache_table == NULL)
882 return g_hash_table_lookup(mbox_cache_table, filename);
886 static gint mbox_cache_get_count(gchar * filename)
890 cache = mbox_cache_get_mbox(filename);
893 if (cache->mf == NULL)
895 return cache->mf->count;
898 static gint mbox_cache_get_mtime(gchar * filename)
902 cache = mbox_cache_get_mbox(filename);
908 static GList * mbox_cache_get_msg_list(gchar * filename)
912 cache = mbox_cache_get_mbox(filename);
917 if (cache->mf == NULL)
920 return cache->mf->msg_list;
923 static void mbox_cache_synchronize_lists(GList * old_msg_list,
924 GList * new_msg_list)
929 for(l2 = old_msg_list ; l2 != NULL ; l2 = g_list_next(l2)) {
930 struct _message * msg2 = l2->data;
932 if ((msg2->messageid == NULL) ||
933 (msg2->fromspace == NULL))
936 for(l = new_msg_list ; l != NULL ; l = g_list_next(l)) {
937 struct _message * msg = l->data;
939 if ((msg->messageid == NULL) ||
940 (msg->fromspace == NULL))
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;
954 static void mbox_cache_synchronize(gchar * filename, gboolean sync)
956 mboxcache * new_cache;
957 mboxcache * old_cache;
958 gboolean scan_new = TRUE;
961 old_cache = mbox_cache_get_mbox(filename);
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");
976 if (strstr(filename, "trash") == 0)
977 printf("old_cache: %p %s\n", old_cache, filename);
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;
989 new_cache = mbox_cache_read_mbox(filename);
992 if (strstr(filename, "trash") == 0)
993 printf("new_cache: %p %s\n", new_cache, filename);
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;
1001 printf("end new\n");
1008 if (sync && new_cache && old_cache)
1009 mbox_cache_synchronize_lists(old_cache->mf->msg_list,
1010 new_cache->mf->msg_list);
1012 if (old_cache != NULL)
1013 mbox_cache_free_mbox(old_cache);
1016 mbox_cache_insert_mbox(new_cache);
1018 printf("insert %p %s\n", new_cache, new_cache->filename);
1019 printf("inserted %s %p\n", filename,
1020 mbox_cache_get_mbox(filename));
1026 static void mbox_cache_synchronize_from_file(FILE * fp, gchar * filename,
1029 mboxcache * new_cache;
1030 mboxcache * old_cache;
1031 gboolean scan_new = TRUE;
1034 old_cache = mbox_cache_get_mbox(filename);
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");
1051 if (strstr(filename, "trash") == 0)
1052 printf("old_cache: %p %s\n", old_cache, filename);
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);
1061 printf("end old\n");
1065 new_cache = mbox_cache_read_mbox_from_file(fp, filename);
1068 if (strstr(filename, "trash") == 0)
1069 printf("new_cache: %p %s\n", new_cache, filename);
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);
1078 printf("end new\n");
1085 if (sync && new_cache && old_cache)
1086 mbox_cache_synchronize_lists(old_cache->mf->msg_list,
1087 new_cache->mf->msg_list);
1089 if (old_cache != NULL)
1090 mbox_cache_free_mbox(old_cache);
1093 mbox_cache_insert_mbox(new_cache);
1095 printf("insert %p %s\n", new_cache, new_cache->filename);
1096 printf("inserted %s %p\n", filename,
1097 mbox_cache_get_mbox(filename));
1103 gboolean mbox_cache_msg_fetched(gchar * filename, gint num)
1105 struct _message * msg;
1108 cache = mbox_cache_get_mbox(filename);
1113 msg = (struct _message *) g_ptr_array_index(cache->tab_mf,
1118 return msg->fetched;
1121 void mbox_cache_msg_set_fetched(gchar * filename, gint num)
1123 struct _message * msg;
1126 cache = mbox_cache_get_mbox(filename);
1131 msg = (struct _message *) g_ptr_array_index(cache->tab_mf,
1136 msg->fetched = TRUE;
1139 struct _message * mbox_cache_get_msg(gchar * filename, gint num)
1143 cache = mbox_cache_get_mbox(filename);
1145 if (cache == NULL) {
1149 return (struct _message *) g_ptr_array_index(cache->tab_mf,
1154 /**********************************************************/
1156 /* mbox operations */
1158 /**********************************************************/
1161 GSList *mbox_get_msg_list(Folder *folder, FolderItem *item, gboolean use_cache)
1170 struct timeval tv_before, tv_after, tv_result;
1172 gettimeofday(&tv_before, NULL);
1177 mbox_path = mbox_folder_get_path(item);
1179 if (mbox_path == NULL)
1182 mbox_purge_deleted(mbox_path);
1184 fp = fopen(mbox_path, "rb");
1191 mbox_lockread_file(fp, mbox_path);
1193 mbox_cache_synchronize_from_file(fp, mbox_path, TRUE);
1195 item->last_num = mbox_cache_get_count(mbox_path);
1197 for(l = mbox_cache_get_msg_list(mbox_path) ; l != NULL ;
1198 l = g_list_next(l)) {
1199 struct _message * msg;
1201 msg = (struct _message *) l->data;
1203 if (MSG_IS_INVALID(msg->flags) || !MSG_IS_REALLY_DELETED(msg->flags)) {
1204 fseek(fp, msg->header, SEEK_SET);
1206 msginfo = mbox_parse_msg(fp, msg, item);
1208 if (!MSG_IS_INVALID(msg->flags))
1209 msginfo->flags = msg->flags;
1211 msg->old_flags = msginfo->flags;
1212 msg->flags = msginfo->flags;
1215 mlist = g_slist_append(mlist, msginfo);
1218 MSG_SET_PERM_FLAGS(msg->flags, MSG_REALLY_DELETED);
1222 mbox_unlock_file(fp, mbox_path);
1229 gettimeofday(&tv_after, NULL);
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);
1239 static gboolean mbox_extract_msg(FolderItem * item, gint msgnum,
1240 gchar * dest_filename)
1242 struct _message * msg;
1249 /* GList * msg_list;*/
1250 gboolean already_fetched;
1253 mbox_path = mbox_folder_get_path(item);
1255 if (mbox_path == NULL)
1258 src = fopen(mbox_path, "rb");
1264 mbox_lockread_file(src, mbox_path);
1266 mbox_cache_synchronize_from_file(src, mbox_path, TRUE);
1268 already_fetched = mbox_cache_msg_fetched(mbox_path, msgnum);
1270 if (already_fetched) {
1271 mbox_unlock_file(src, mbox_path);
1277 msg = mbox_cache_get_msg(mbox_path, msgnum);
1280 mbox_unlock_file(src, mbox_path);
1286 offset = msg->offset;
1287 max_offset = msg->end;
1289 size = max_offset - offset;
1291 fseek(src, offset, SEEK_SET);
1293 dest = fopen(dest_filename, "wb");
1295 mbox_unlock_file(src, mbox_path);
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"));
1306 if (!mbox_write_data(src, dest, dest_filename, size)) {
1307 mbox_unlock_file(src, mbox_path);
1310 unlink(dest_filename);
1318 FILE_OP_ERROR(mbox_path, "fread");
1322 mbox_cache_msg_set_fetched(mbox_path, msgnum);
1324 if (fclose(dest) == -1) {
1325 FILE_OP_ERROR(dest_filename, "fclose");
1329 mbox_unlock_file(src, mbox_path);
1331 if (fclose(src) == -1) {
1332 FILE_OP_ERROR(mbox_path, "fclose");
1339 unlink(dest_filename);
1346 gchar *mbox_fetch_msg(Folder *folder, FolderItem *item, gint num)
1351 g_return_val_if_fail(item != NULL, NULL);
1352 g_return_val_if_fail(num > 0 && num <= item->last_num, NULL);
1354 path = folder_item_get_path(item);
1355 if (!is_dir_exist(path))
1356 make_dir_hier(path);
1358 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(num), NULL);
1362 if (!mbox_extract_msg(item, num, filename)) {
1370 gint mbox_add_msg(Folder *folder, FolderItem *dest, const gchar *file,
1371 gboolean remove_source)
1380 gchar from_line[MSGBUFSIZE];
1382 if (dest->last_num < 0) {
1383 mbox_scan_folder(folder, dest);
1384 if (dest->last_num < 0) return -1;
1387 src_fp = fopen(file, "rb");
1388 if (src_fp == NULL) {
1392 mbox_path = mbox_folder_get_path(dest);
1393 if (mbox_path == NULL)
1396 dest_fp = fopen(mbox_path, "ab");
1397 if (dest_fp == NULL) {
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"));
1408 old_size = ftell(dest_fp);
1410 mbox_lockwrite_file(dest_fp, mbox_path);
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);
1421 if (strncmp(from_line, "From ", 5) != 0) {
1424 if (stat(file, &s) < 0) {
1425 mbox_unlock_file(dest_fp, mbox_path);
1426 g_warning(_("unvalid file - %s.\n"), file);
1433 fprintf(dest_fp, "From - %s", ctime(&s.st_mtime));
1436 fputs(from_line, dest_fp);
1439 n_read = fread(buf, 1, sizeof(buf), src_fp);
1440 if ((n_read < (gint) sizeof(buf)) && ferror(src_fp))
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);
1452 if (n_read < (gint) sizeof(buf))
1458 if (ferror(src_fp)) {
1459 FILE_OP_ERROR(mbox_path, "fread");
1462 mbox_unlock_file(dest_fp, mbox_path);
1464 if (fclose(src_fp) == -1) {
1465 FILE_OP_ERROR(file, "fclose");
1469 if (fclose(dest_fp) == -1) {
1470 FILE_OP_ERROR(mbox_path, "fclose");
1476 ftruncate(fileno(dest_fp), old_size);
1481 if (remove_source) {
1482 if (unlink(file) < 0)
1483 FILE_OP_ERROR(file, "unlink");
1489 return dest->last_num;
1493 gint mbox_remove_msg(Folder *folder, FolderItem *item, gint num)
1495 struct _message * msg;
1498 mbox_path = mbox_folder_get_path(item);
1499 if (mbox_path == NULL)
1502 mbox_cache_synchronize(mbox_path, TRUE);
1504 msg = mbox_cache_get_msg(mbox_path, num);
1509 MSG_SET_PERM_FLAGS(msg->flags, MSG_REALLY_DELETED);
1514 gint mbox_remove_all_msg(Folder *folder, FolderItem *item)
1519 mbox_path = mbox_folder_get_path(item);
1520 if (mbox_path == NULL)
1523 fp = fopen(mbox_path, "wb");
1537 gint mbox_move_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1542 filename = mbox_fetch_msg(folder, msginfo->folder, msginfo->msgnum);
1543 if (filename == NULL)
1546 msgnum = mbox_add_msg(folder, dest, filename, TRUE);
1549 MSG_SET_FLAGS(msginfo->flags, MSG_REALLY_DELETED);
1550 mbox_change_flags(folder, msginfo->folder, msginfo);
1556 gint mbox_move_msgs_with_dest(Folder *folder, FolderItem *dest, GSList *msglist)
1559 gchar * mbox_path = NULL;
1561 for(l = msglist ; l != NULL ; l = g_slist_next(l)) {
1562 MsgInfo * msginfo = (MsgInfo *) l->data;
1564 if (msginfo->folder && mbox_path == NULL)
1565 mbox_path = mbox_folder_get_path(msginfo->folder);
1567 mbox_move_msg(folder, dest, msginfo);
1571 mbox_cache_synchronize(mbox_path);
1575 mbox_path = mbox_folder_get_path(dest);
1576 mbox_cache_synchronize(mbox_path);
1579 return dest->last_num;
1584 gint mbox_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1589 filename = mbox_fetch_msg(folder, msginfo->folder, msginfo->msgnum);
1590 if (filename == NULL)
1593 msgnum = mbox_add_msg(folder, dest, filename, FALSE);
1598 gint mbox_copy_msgs_with_dest(Folder *folder, FolderItem *dest, GSList *msglist)
1601 gchar * mbox_path = NULL;
1603 for(l = msglist ; l != NULL ; l = g_slist_next(l)) {
1604 MsgInfo * msginfo = (MsgInfo *) l->data;
1606 if (msginfo->folder && mbox_path == NULL)
1607 mbox_path = mbox_folder_get_path(msginfo->folder);
1609 mbox_copy_msg(folder, dest, msginfo);
1613 mbox_cache_synchronize(mbox_path);
1617 mbox_path = mbox_folder_get_path(dest);
1618 mbox_cache_synchronize(mbox_path);
1621 return dest->last_num;
1625 struct _copy_flags_info
1631 typedef struct _copy_flags_info CopyFlagsInfo;
1633 GSList * copy_flags_data = NULL;
1635 gint mbox_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1637 Folder * src_folder;
1642 struct _message * msg;
1643 CopyFlagsInfo * flags_info;
1645 src_folder = msginfo->folder->folder;
1647 g_return_val_if_fail(src_folder->fetch_msg != NULL, -1);
1650 mbox_path = mbox_folder_get_path(msginfo->folder);
1651 mbox_rewrite(mbox_path);
1655 filename = src_folder->fetch_msg(src_folder,
1658 if (filename == NULL)
1661 num = folder->add_msg(folder, dest, filename, FALSE);
1664 mbox_path = mbox_folder_get_path(dest);
1665 msg = mbox_cache_get_msg(mbox_path, num);
1667 msg->flags = msginfo->flags;
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);
1682 void mbox_finished_copy(Folder *folder, FolderItem *dest)
1688 mbox_path = mbox_folder_get_path(dest);
1689 if (mbox_path == NULL)
1692 mbox_cache_synchronize(mbox_path, TRUE);
1694 for(l = copy_flags_data ; l != NULL ; l = g_slist_next(l)) {
1695 CopyFlagsInfo * flags_info = l->data;
1696 struct _message * msg;
1698 msg = mbox_cache_get_msg(mbox_path, flags_info->num);
1700 msg->flags = flags_info->flags;
1704 if (copy_flags_data != NULL) {
1705 cache = mbox_cache_get_mbox(mbox_path);
1706 cache->modification = TRUE;
1709 g_slist_free(copy_flags_data);
1710 copy_flags_data = NULL;
1712 mbox_rewrite(mbox_path);
1717 void mbox_scan_folder(Folder *folder, FolderItem *item)
1724 mbox_path = mbox_folder_get_path(item);
1725 if (mbox_path == NULL)
1728 mbox_cache_synchronize(mbox_path, TRUE);
1730 cached = mbox_cache_get_mbox(mbox_path);
1732 if (cached == NULL) {
1741 n_msg = mbox_cache_get_count(mbox_path);
1744 item->new = item->unread = item->total = 0;
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))
1756 if (MSG_IS_NEW(msg->flags) && !MSG_IS_IGNORE_THREAD(msg->flags))
1758 if (MSG_IS_UNREAD(msg->flags) && !MSG_IS_IGNORE_THREAD(msg->flags))
1763 item->unread = unread;
1764 item->total = total;
1767 debug_print(_("Last number in dir %s = %d\n"), mbox_path,
1769 item->last_num = n_msg;
1773 gchar * mbox_get_virtual_path(FolderItem * item)
1778 if (item->parent == NULL) {
1782 gchar * parent_path;
1783 gchar * result_path;
1785 parent_path = mbox_get_virtual_path(item->parent);
1786 if (parent_path == NULL)
1787 result_path = g_strdup(item->name);
1789 result_path = g_strconcat(parent_path,
1792 g_free(parent_path);
1798 static gboolean mbox_write_data(FILE * mbox_fp, FILE * new_fp,
1799 gchar * new_filename, gint size)
1807 while (pos < size) {
1808 if ((size - pos) > (gint) sizeof(buf))
1813 n_read = fread(buf, 1, max, mbox_fp);
1815 if (n_read < max && ferror(mbox_fp)) {
1818 if (fwrite(buf, n_read, 1, new_fp) < 1) {
1819 g_warning(_("writing to %s failed.\n"), new_filename);
1832 static gboolean mbox_write_message(FILE * mbox_fp, FILE * new_fp,
1833 gchar * new_filename,
1834 struct _message * msg)
1837 GPtrArray * headers;
1840 fseek(mbox_fp, msg->header, SEEK_SET);
1842 headers = procheader_get_header_array_asis(mbox_fp);
1844 for (i = 0; i < (gint) headers->len; i++) {
1845 Header * h = g_ptr_array_index(headers, i);
1847 if (!procheader_headername_equal(h->name,
1849 !procheader_headername_equal(h->name,
1851 fwrite(h->name, strlen(h->name),
1853 if (h->name[strlen(h->name) - 1] != ' ')
1854 fwrite(" ", 1, 1, new_fp);
1855 fwrite(h->body, strlen(h->body),
1857 fwrite("\n", 1, 1, new_fp);
1859 procheader_header_free(h);
1860 g_ptr_array_remove_index(headers, i);
1864 g_ptr_array_free(headers, FALSE);
1866 if (!MSG_IS_INVALID(msg->flags)) {
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);
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 */
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);
1893 fwrite("\n", 1, 1, new_fp);
1897 fwrite("\n", 1, 1, new_fp);
1899 size = msg->end - msg->content;
1900 fseek(mbox_fp, msg->content, SEEK_SET);
1902 return mbox_write_data(mbox_fp, new_fp, new_filename, size);
1905 void mbox_update_mark(Folder * folder, FolderItem * item)
1909 mbox_path = mbox_folder_get_path(item);
1910 if (mbox_path == NULL)
1913 mbox_rewrite(mbox_path);
1917 void mbox_change_flags(Folder * folder, FolderItem * item, MsgInfo * info)
1919 struct _message * msg;
1923 mbox_path = mbox_folder_get_path(item);
1924 if (mbox_path == NULL)
1927 msg = mbox_cache_get_msg(mbox_path, info->msgnum);
1929 cache = mbox_cache_get_mbox(mbox_path);
1933 if ((msg == NULL) || (cache == NULL))
1936 msg->flags = info->flags;
1938 cache->modification = TRUE;
1942 static gboolean mbox_rewrite(gchar * mbox)
1953 msg_list = mbox_cache_get_msg_list(mbox);
1955 cache = mbox_cache_get_mbox(mbox);
1959 if (!cache->modification) {
1960 debug_print(_("no modification - %s\n"), mbox);
1964 debug_print(_("save modification - %s\n"), mbox);
1966 mbox_fp = fopen(mbox, "rb+");
1967 mbox_lockwrite_file(mbox_fp, mbox);
1969 mbox_cache_synchronize_from_file(mbox_fp, mbox, TRUE);
1971 new = g_strconcat(mbox, ".", itos((int) mbox), NULL);
1972 new_fp = fopen(new, "wb");
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"));
1979 mbox_lockwrite_file(new_fp, new);
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)) {
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);
2000 mbox_unlock_file(mbox_fp, mbox);
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"));
2010 mbox_unlock_file(new_fp, new);
2014 mbox_unlock_file(mbox_fp, mbox);
2018 debug_print(_("%i messages written - %s\n"), count, mbox);
2020 cache = mbox_cache_get_mbox(mbox);
2025 mbox_cache_synchronize(mbox, FALSE);
2030 static gboolean mbox_purge_deleted(gchar * mbox)
2037 gboolean modification = FALSE;
2041 mbox_cache_synchronize(mbox, TRUE);
2043 msg_list = mbox_cache_get_msg_list(mbox);
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;
2053 if (!modification) {
2054 debug_print(_("no deleted messages - %s\n"), mbox);
2058 debug_print(_("purge deleted messages - %s\n"), mbox);
2060 mbox_fp = fopen(mbox, "rb+");
2061 mbox_lockwrite_file(mbox_fp, mbox);
2063 mbox_cache_synchronize_from_file(mbox_fp, mbox, TRUE);
2065 new = g_strconcat(mbox, ".", itos((int) mbox), NULL);
2066 new_fp = fopen(new, "wb");
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"));
2073 mbox_lockwrite_file(new_fp, new);
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)) {
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);
2096 mbox_unlock_file(mbox_fp, mbox);
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"));
2106 mbox_unlock_file(new_fp, new);
2110 mbox_unlock_file(mbox_fp, mbox);
2114 debug_print(_("%i messages written - %s\n"), count, mbox);
2116 mbox_cache_synchronize(mbox, FALSE);
2121 #define MAKE_DIR_IF_NOT_EXIST(dir) \
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); \
2129 if (mkdir(dir, S_IRWXU) < 0) { \
2130 FILE_OP_ERROR(dir, "mkdir"); \
2133 if (chmod(dir, S_IRWXU) < 0) \
2134 FILE_OP_ERROR(dir, "chmod"); \
2138 gint mbox_create_tree(Folder *folder)
2142 g_return_val_if_fail(folder != NULL, -1);
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);
2152 #undef MAKE_DIR_IF_NOT_EXIST
2154 static gchar * mbox_get_new_path(FolderItem * parent, gchar * name)
2158 if (strchr(name, '/') == NULL) {
2159 if (parent->path != NULL)
2160 path = g_strconcat(parent->path, ".sbd", G_DIR_SEPARATOR_S, name, NULL);
2162 path = g_strdup(name);
2165 path = g_strdup(name);
2170 static gchar * mbox_get_folderitem_name(gchar * name)
2174 foldername = g_strdup(g_basename(name));
2179 FolderItem *mbox_create_folder(Folder *folder, FolderItem *parent,
2183 FolderItem *new_item;
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);
2190 path = mbox_get_new_path(parent, (gchar *) name);
2192 foldername = mbox_get_folderitem_name((gchar *) name);
2194 new_item = folder_item_new(foldername, path);
2195 folder_item_append(parent, new_item);
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;
2220 gint mbox_rename_folder(Folder *folder, FolderItem *item, const gchar *name)
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);
2230 path = mbox_get_new_path(item->parent, (gchar *) name);
2231 foldername = mbox_get_folderitem_name((gchar *) name);
2233 if (rename(item->path, path) == -1) {
2236 g_warning(_("Cannot rename folder item"));
2244 item->name = foldername;
2250 gint mbox_remove_folder(Folder *folder, FolderItem *item)
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);
2256 folder_item_remove(item);