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 =
84 mbox_check_msgnum_validity,
87 FolderClass *mbox_get_class()
92 Folder *mbox_folder_new(const gchar *name, const gchar *path)
96 folder = (Folder *)g_new0(MBOXFolder, 1);
97 folder->class = &mbox_class;
98 mbox_folder_init(folder, name, path);
103 void mbox_folder_destroy(Folder *folder)
105 folder_local_folder_destroy(LOCAL_FOLDER(folder));
108 static void mbox_folder_init(Folder *folder, const gchar *name, const gchar *path)
110 folder_local_folder_init(folder, name, path);
113 static void mbox_folder_create_parent(const gchar * path)
115 if (!is_file_exist(path)) {
118 new_path = g_dirname(path);
119 if (new_path[strlen(new_path) - 1] == G_DIR_SEPARATOR)
120 new_path[strlen(new_path) - 1] = '\0';
122 if (!is_dir_exist(new_path))
123 make_dir_hier(new_path);
130 static gchar *mbox_folder_get_path(FolderItem *item)
135 g_return_val_if_fail(item != NULL, NULL);
137 if (item->path && item->path[0] == G_DIR_SEPARATOR) {
138 mbox_folder_create_parent(item->path);
139 return g_strdup(item->path);
142 folder_path = g_strdup(LOCAL_FOLDER(item->folder)->rootpath);
143 g_return_val_if_fail(folder_path != NULL, NULL);
145 if (folder_path[0] == G_DIR_SEPARATOR) {
147 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
151 path = g_strdup(folder_path);
154 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
155 folder_path, G_DIR_SEPARATOR_S,
158 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
164 mbox_folder_create_parent(path);
170 /**********************************************************/
174 /**********************************************************/
177 static GSList * file_lock = NULL;
179 static gboolean mbox_file_lock_file(gchar * base)
181 gchar *lockfile, *locklink;
185 lockfile = g_strdup_printf("%s.%d", base, getpid());
186 if ((lockfp = fopen(lockfile, "wb")) == NULL) {
187 FILE_OP_ERROR(lockfile, "fopen");
188 g_warning("can't create lock file %s\n", lockfile);
189 g_warning("use 'flock' instead of 'file' if possible.\n");
194 fprintf(lockfp, "%d\n", getpid());
197 locklink = g_strconcat(base, ".lock", NULL);
198 while (link(lockfile, locklink) < 0) {
199 FILE_OP_ERROR(lockfile, "link");
201 g_warning("can't create %s\n", lockfile);
208 g_warning("mailbox is owned by another"
209 " process, waiting...\n");
220 static gboolean mbox_fcntl_lockwrite_file(FILE * fp)
224 lck.l_type = F_WRLCK;
229 if (fcntl(fileno(fp), F_SETLK, &lck) < 0)
235 static gboolean mbox_fcntl_lockread_file(FILE * fp)
239 lck.l_type = F_RDLCK;
244 if (fcntl(fileno(fp), F_SETLK, &lck) < 0)
250 static gboolean mbox_fcntl_unlock_file(FILE * fp)
254 lck.l_type = F_UNLCK;
259 if (fcntl(fileno(fp), F_SETLK, &lck) < 0)
265 static gboolean mbox_file_unlock_file(gchar * base)
269 lockfile = g_strdup_printf("%s.lock", base);
276 static gboolean mbox_lockread_file(FILE * fp, gchar * base)
280 result = mbox_fcntl_lockread_file(fp);
282 if ((result = mbox_file_lock_file(base)) == TRUE) {
283 file_lock = g_slist_append(file_lock, g_strdup(base));
284 debug_print("lockfile lock %s.\n", base);
287 g_warning("could not lock read file %s\n", base);
290 debug_print("fcntl lock %s.\n", base);
295 static gboolean mbox_lockwrite_file(FILE * fp, gchar * base)
299 result = mbox_fcntl_lockwrite_file(fp);
301 if ((result = mbox_file_lock_file(base)) == TRUE) {
302 file_lock = g_slist_append(file_lock, g_strdup(base));
303 debug_print("lockfile lock %s.\n", base);
306 g_warning("could not lock write file %s\n", base);
309 debug_print("fcntl lock %s.\n", base);
314 static gboolean mbox_unlock_file(FILE * fp, gchar * base)
316 gboolean result = FALSE;
318 gboolean unlocked = FALSE;
320 for(l = file_lock ; l != NULL ; l = g_slist_next(l)) {
321 gchar * data = l->data;
323 if (strcmp(data, base) == 0) {
324 file_lock = g_slist_remove(file_lock, data);
326 result = mbox_file_unlock_file(base);
328 debug_print("lockfile unlock - %s.\n", base);
334 result = mbox_fcntl_unlock_file(fp);
335 debug_print("fcntl unlock - %s.\n", base);
341 /**********************************************************/
345 /**********************************************************/
347 #define MAILFILE_ERROR_NO_ERROR 0x000
348 #define MAILFILE_ERROR_FILE_NOT_FOUND 0x001
349 #define MAILFILE_ERROR_MEMORY 0x002
350 #define MAILFILE_ERROR_MESSAGE_NOT_FOUND 0x003
352 static int mailfile_error = MAILFILE_ERROR_NO_ERROR;
354 #define STATE_BEGIN 0x000
355 #define STATE_TEXT_READ 0x001
356 #define STATE_FROM_READ 0x002
357 #define STATE_FIELD_READ 0x003
358 #define STATE_END 0x004
359 #define STATE_END_OF_FILE 0x005
360 #define STATE_MEM_ERROR 0x006
361 #define STATE_TEXT_BEGIN 0x007
363 #define STATE_MASK 0x0FF /* filter state from functions */
365 #define STATE_RESTORE_POS 0x100 /* go back while reading */
367 typedef struct _mailfile mailfile;
391 #define MSG_IS_INVALID(msg) \
392 ((msg).perm_flags == (msg).tmp_flags && (msg).tmp_flags == -1)
394 #define MSG_SET_INVALID(msg) \
395 ((msg).perm_flags = (msg).tmp_flags = -1)
397 static int startFrom(char * s)
399 return (strncmp(s, "From ", 5) == 0);
402 static int startSpace(char * s)
404 return ((*s == ' ') || (*s == '\t'));
407 static int startEmpty(char * s)
412 static void free_msg_list(GList * l)
414 GList * elt = g_list_first(l);
419 elt = g_list_next(elt);
426 static mailfile * mailfile_init_from_file(FILE * f, gchar * filename)
429 GList * msg_list = NULL;
436 struct _message * data = NULL;
442 while (state != STATE_END_OF_FILE) {
443 if ((state & STATE_RESTORE_POS) == 0) {
444 former_pos = lastpos;
447 r = fgets(s, 256, f);
450 ignore_next = (s[strlen(s) - 1] != '\n');
455 switch(state & 0x0F) {
458 state = STATE_END_OF_FILE;
459 else if (startFrom(s)) {
460 state = STATE_FROM_READ;
462 data = g_new0(struct _message, 1);
464 free_msg_list(msg_list);
469 data->msgnum = msgnum;
470 data->offset = lastpos;
471 data->header = lastpos;
474 data->messageid = NULL;
475 data->fromspace = NULL;
476 MSG_SET_INVALID(data->flags);
477 MSG_SET_INVALID(data->old_flags);
478 data->fetched = FALSE;
479 msg_list = g_list_append(msg_list,
487 case STATE_TEXT_READ:
490 else if (startFrom(s))
491 state = STATE_END | STATE_RESTORE_POS;
493 state = STATE_TEXT_READ;
496 case STATE_TEXT_BEGIN:
497 data->content = lastpos;
500 else if (startFrom(s)) {
501 state = STATE_END | STATE_RESTORE_POS;
504 state = STATE_TEXT_READ;
508 case STATE_FROM_READ:
509 data->content = lastpos;
512 else if (startSpace(s))
513 state = STATE_FROM_READ;
514 else if (startEmpty(s))
515 state = STATE_TEXT_READ;
517 state = STATE_FIELD_READ;
520 case STATE_FIELD_READ:
521 data->content = lastpos;
524 else if (startSpace(s))
525 state = STATE_FIELD_READ;
526 else if (startEmpty(s)) {
527 state = STATE_TEXT_BEGIN;
530 state = STATE_FIELD_READ;
534 if ((state & STATE_MASK) == STATE_END) {
535 state = STATE_BEGIN | (state & STATE_RESTORE_POS);
541 r = fgets(s, 256, f);
542 if (r == NULL || *r == '\0')
545 while (s[strlen(s) - 1] != '\n');
549 mf = (mailfile *) g_new0(struct _mailfile, 1);
551 free_msg_list(msg_list);
552 mailfile_error = MAILFILE_ERROR_MEMORY;
556 mf->msg_list = g_list_first(msg_list);
558 mf->filename = g_strdup(filename);
561 mailfile_error = MAILFILE_ERROR_NO_ERROR;
566 static mailfile * mailfile_init(char * filename)
572 f = fopen(filename, "rb");
575 mailfile_error = MAILFILE_ERROR_FILE_NOT_FOUND;
579 mbox_lockread_file(f, filename);
581 mf = mailfile_init_from_file(f, filename);
583 mbox_unlock_file(f, filename);
590 static void mailfile_done(mailfile * f)
592 free_msg_list(f->msg_list);
599 #define MAX_READ 4096
601 static char * readfile(char * filename, int offset, int max_offset)
610 handle = fopen(filename, "rb");
612 if (handle == NULL) {
613 mailfile_error = MAILFILE_ERROR_FILE_NOT_FOUND;
617 size = max_offset - offset;
619 message = (char *) malloc(size + 1);
620 if (message == NULL) {
622 mailfile_error = MAILFILE_ERROR_MEMORY;
626 fseek(handle, offset, SEEK_SET);
630 if ((size - pos) > MAX_READ)
635 bread = fread(message + pos, 1, max, handle);
651 static char * mailfile_readmsg(mailfile f, int index)
657 struct _message * msginfo;
659 nth = g_list_nth(f->msg_list, index);
662 mailfile_error = MAILFILE_ERROR_MESSAGE_NOT_FOUND;
666 msginfo = (struct _message *)nth->data;
668 offset = msginfo->offset;
669 max_offset = msginfo->end;
670 message = readfile(f->filename, offset, max_offset);
672 mailfile_error = MAILFILE_ERROR_NO_ERROR;
677 static char * mailfile_readheader(mailfile f, int index)
683 struct _message * msginfo;
685 nth = g_list_nth(f->msg_list, index);
688 mailfile_error = MAILFILE_ERROR_MESSAGE_NOT_FOUND;
692 msginfo = (struct _message *)nth->data;
694 offset = msginfo->offset;
695 max_offset = msginfo->content;
696 message = readfile(f->filename, offset, max_offset);
698 mailfile_error = MAILFILE_ERROR_NO_ERROR;
703 static int mailfile_count(mailfile * f)
705 return g_list_length(f->msg_list);
708 static int mailfile_find_deleted(mailfile f, char * filename)
712 handle = fopen(filename, "rb");
715 struct _message m = elt->data;
716 n = fread(&m.deleted, sizeof(int), 1, handle);
719 elt = g_list_next(elt);
728 /**********************************************************/
730 /* mbox cache operations */
732 /**********************************************************/
739 gboolean modification;
742 typedef struct _mboxcache mboxcache;
744 static GHashTable * mbox_cache_table = NULL;
746 static MsgInfo *mbox_parse_msg(FILE * fp, struct _message * msg,
750 MsgFlags flags = { 0, 0 };
752 MSG_SET_PERM_FLAGS(flags, MSG_NEW | MSG_UNREAD);
754 g_return_val_if_fail(fp != NULL, NULL);
757 if (item->stype == F_QUEUE) {
758 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
759 } else if (item->stype == F_DRAFT) {
760 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
764 msginfo = procheader_parse_stream(fp, flags, FALSE, FALSE);
766 if (!msginfo) return NULL;
769 msginfo->msgnum = msg->msgnum;
770 msginfo->folder = item;
776 static void mbox_cache_init()
778 mbox_cache_table = g_hash_table_new(g_str_hash, g_str_equal);
781 static void mbox_cache_free_mbox(mboxcache * cache)
783 g_hash_table_remove(mbox_cache_table, cache->filename);
786 mailfile_done(cache->mf);
788 g_ptr_array_free(cache->tab_mf, FALSE);
790 g_free(cache->filename);
794 static void mbox_cache_get_msginfo_from_file(FILE * fp, GList * msg_list)
799 for(l = msg_list ; l != NULL ; l = g_list_next(l)) {
800 struct _message * msg;
802 msg = (struct _message *) l->data;
804 fseek(fp, msg->header, SEEK_SET);
805 msginfo = mbox_parse_msg(fp, msg, NULL);
809 g_strdup(msginfo->msgid);
810 if (msginfo->fromspace)
812 g_strdup(msginfo->fromspace);
813 msg->flags = msginfo->flags;
814 msg->old_flags = msginfo->flags;
816 procmsg_msginfo_free(msginfo);
821 static void mbox_cache_get_msginfo(gchar * filename, GList * msg_list)
825 fp = fopen(filename, "rb");
829 mbox_cache_get_msginfo_from_file(fp, msg_list);
833 static mboxcache * mbox_cache_read_mbox(gchar * filename)
840 if (stat(filename, &s) < 0)
843 mf = mailfile_init(filename);
847 cache = g_new0(mboxcache, 1);
849 cache->mtime = s.st_mtime;
851 cache->filename = g_strdup(filename);
852 cache->modification = FALSE;
854 cache->tab_mf = g_ptr_array_new();
855 for(l = mf->msg_list ; l != NULL ; l = g_list_next(l))
856 g_ptr_array_add(cache->tab_mf, l->data);
858 mbox_cache_get_msginfo(filename, mf->msg_list);
860 debug_print("read mbox - %s\n", filename);
865 static mboxcache * mbox_cache_read_mbox_from_file(FILE * fp, gchar * filename)
872 if (stat(filename, &s) < 0)
875 mf = mailfile_init_from_file(fp, filename);
879 cache = g_new0(mboxcache, 1);
881 cache->mtime = s.st_mtime;
883 cache->filename = g_strdup(filename);
885 cache->tab_mf = g_ptr_array_new();
886 for(l = mf->msg_list ; l != NULL ; l = g_list_next(l))
887 g_ptr_array_add(cache->tab_mf, l->data);
889 mbox_cache_get_msginfo_from_file(fp, mf->msg_list);
891 debug_print("read mbox from file - %s\n", filename);
896 static void mbox_cache_insert_mbox(mboxcache * data)
898 if (mbox_cache_table == NULL)
901 g_hash_table_insert(mbox_cache_table, data->filename, data);
904 static mboxcache * mbox_cache_get_mbox(gchar * filename)
906 if (mbox_cache_table == NULL)
909 return g_hash_table_lookup(mbox_cache_table, filename);
913 static gint mbox_cache_get_count(gchar * filename)
917 cache = mbox_cache_get_mbox(filename);
920 if (cache->mf == NULL)
922 return cache->mf->count;
925 static GList * mbox_cache_get_msg_list(gchar * filename)
929 cache = mbox_cache_get_mbox(filename);
934 if (cache->mf == NULL)
937 return cache->mf->msg_list;
940 static void mbox_cache_synchronize_lists(GList * old_msg_list,
941 GList * new_msg_list)
946 for(l2 = old_msg_list ; l2 != NULL ; l2 = g_list_next(l2)) {
947 struct _message * msg2 = l2->data;
949 if ((msg2->messageid == NULL) ||
950 (msg2->fromspace == NULL))
953 for(l = new_msg_list ; l != NULL ; l = g_list_next(l)) {
954 struct _message * msg = l->data;
956 if ((msg->messageid == NULL) ||
957 (msg->fromspace == NULL))
960 if ((strcmp(msg->messageid, msg2->messageid) == 0) &&
961 (strcmp(msg->fromspace, msg2->fromspace) == 0)) {
962 if (msg2->flags.perm_flags != msg2->old_flags.perm_flags) {
963 msg->flags = msg2->flags;
971 static void mbox_cache_synchronize(gchar * filename, gboolean sync)
973 mboxcache * new_cache;
974 mboxcache * old_cache;
975 gboolean scan_new = TRUE;
978 old_cache = mbox_cache_get_mbox(filename);
980 if (old_cache != NULL) {
981 if (stat(filename, &s) < 0) {
982 FILE_OP_ERROR(filename, "stat");
983 } else if (old_cache->mtime == s.st_mtime) {
984 debug_print("Folder is not modified.\n");
991 if (strstr(filename, "trash") == 0)
992 printf("old_cache: %p %s\n", old_cache, filename);
994 printf("begin old\n");
995 for(l = old_cache->mf->msg_list ; l != NULL ;
996 l = g_list_next(l)) {
997 struct _message * msg = l->data;
1000 printf("end old\n");
1004 new_cache = mbox_cache_read_mbox(filename);
1007 if (strstr(filename, "trash") == 0)
1008 printf("new_cache: %p %s\n", new_cache, filename);
1010 printf("begin new\n");
1011 for(l = new_cache->mf->msg_list ; l != NULL ;
1012 l = g_list_next(l)) {
1013 struct _message * msg = l->data;
1014 printf("%p\n", msg);
1016 printf("end new\n");
1023 if (sync && new_cache && old_cache)
1024 mbox_cache_synchronize_lists(old_cache->mf->msg_list,
1025 new_cache->mf->msg_list);
1027 if (old_cache != NULL)
1028 mbox_cache_free_mbox(old_cache);
1031 mbox_cache_insert_mbox(new_cache);
1033 printf("insert %p %s\n", new_cache, new_cache->filename);
1034 printf("inserted %s %p\n", filename,
1035 mbox_cache_get_mbox(filename));
1041 static void mbox_cache_synchronize_from_file(FILE * fp, gchar * filename,
1044 mboxcache * new_cache;
1045 mboxcache * old_cache;
1046 gboolean scan_new = TRUE;
1049 old_cache = mbox_cache_get_mbox(filename);
1051 if (old_cache != NULL) {
1052 if (stat(filename, &s) < 0) {
1053 FILE_OP_ERROR(filename, "stat");
1054 } else if (old_cache->mtime == s.st_mtime) {
1055 debug_print("Folder is not modified.\n");
1066 if (strstr(filename, "trash") == 0)
1067 printf("old_cache: %p %s\n", old_cache, filename);
1070 printf("begin old\n");
1071 for(l = old_cache->mf->msg_list ; l != NULL ;
1072 l = g_list_next(l)) {
1073 struct _message * msg = l->data;
1074 printf("%p\n", msg);
1076 printf("end old\n");
1080 new_cache = mbox_cache_read_mbox_from_file(fp, filename);
1083 if (strstr(filename, "trash") == 0)
1084 printf("new_cache: %p %s\n", new_cache, filename);
1087 printf("begin new\n");
1088 for(l = new_cache->mf->msg_list ; l != NULL ;
1089 l = g_list_next(l)) {
1090 struct _message * msg = l->data;
1091 printf("%p\n", msg);
1093 printf("end new\n");
1100 if (sync && new_cache && old_cache)
1101 mbox_cache_synchronize_lists(old_cache->mf->msg_list,
1102 new_cache->mf->msg_list);
1104 if (old_cache != NULL)
1105 mbox_cache_free_mbox(old_cache);
1108 mbox_cache_insert_mbox(new_cache);
1110 printf("insert %p %s\n", new_cache, new_cache->filename);
1111 printf("inserted %s %p\n", filename,
1112 mbox_cache_get_mbox(filename));
1118 gboolean mbox_cache_msg_fetched(gchar * filename, gint num)
1120 struct _message * msg;
1123 cache = mbox_cache_get_mbox(filename);
1128 msg = (struct _message *) g_ptr_array_index(cache->tab_mf,
1133 return msg->fetched;
1136 void mbox_cache_msg_set_fetched(gchar * filename, gint num)
1138 struct _message * msg;
1141 cache = mbox_cache_get_mbox(filename);
1146 msg = (struct _message *) g_ptr_array_index(cache->tab_mf,
1151 msg->fetched = TRUE;
1154 struct _message * mbox_cache_get_msg(gchar * filename, gint num)
1158 cache = mbox_cache_get_mbox(filename);
1160 if (cache == NULL) {
1164 return (struct _message *) g_ptr_array_index(cache->tab_mf,
1169 /**********************************************************/
1171 /* mbox operations */
1173 /**********************************************************/
1176 GSList *mbox_get_msg_list(Folder *folder, FolderItem *item, gboolean use_cache)
1185 struct timeval tv_before, tv_after, tv_result;
1187 gettimeofday(&tv_before, NULL);
1192 mbox_path = mbox_folder_get_path(item);
1194 if (mbox_path == NULL)
1197 mbox_purge_deleted(mbox_path);
1199 fp = fopen(mbox_path, "rb");
1206 mbox_lockread_file(fp, mbox_path);
1208 mbox_cache_synchronize_from_file(fp, mbox_path, TRUE);
1210 item->last_num = mbox_cache_get_count(mbox_path);
1212 for(l = mbox_cache_get_msg_list(mbox_path) ; l != NULL ;
1213 l = g_list_next(l)) {
1214 struct _message * msg;
1216 msg = (struct _message *) l->data;
1218 if (MSG_IS_INVALID(msg->flags) || !MSG_IS_REALLY_DELETED(msg->flags)) {
1219 fseek(fp, msg->header, SEEK_SET);
1221 msginfo = mbox_parse_msg(fp, msg, item);
1223 if (!MSG_IS_INVALID(msg->flags))
1224 msginfo->flags = msg->flags;
1226 msg->old_flags = msginfo->flags;
1227 msg->flags = msginfo->flags;
1230 mlist = g_slist_append(mlist, msginfo);
1233 MSG_SET_PERM_FLAGS(msg->flags, MSG_REALLY_DELETED);
1237 mbox_unlock_file(fp, mbox_path);
1244 gettimeofday(&tv_after, NULL);
1246 timersub(&tv_after, &tv_before, &tv_result);
1247 g_print("mbox_get_msg_list: %s: elapsed time: %ld.%06ld sec\n",
1248 mbox_path, tv_result.tv_sec, tv_result.tv_usec);
1254 static gboolean mbox_extract_msg(FolderItem * item, gint msgnum,
1255 gchar * dest_filename)
1257 struct _message * msg;
1264 /* GList * msg_list;*/
1265 gboolean already_fetched;
1268 mbox_path = mbox_folder_get_path(item);
1270 if (mbox_path == NULL)
1273 src = fopen(mbox_path, "rb");
1279 mbox_lockread_file(src, mbox_path);
1281 mbox_cache_synchronize_from_file(src, mbox_path, TRUE);
1283 already_fetched = mbox_cache_msg_fetched(mbox_path, msgnum);
1285 if (already_fetched) {
1286 mbox_unlock_file(src, mbox_path);
1292 msg = mbox_cache_get_msg(mbox_path, msgnum);
1295 mbox_unlock_file(src, mbox_path);
1301 offset = msg->offset;
1302 max_offset = msg->end;
1304 size = max_offset - offset;
1306 fseek(src, offset, SEEK_SET);
1308 dest = fopen(dest_filename, "wb");
1310 mbox_unlock_file(src, mbox_path);
1316 if (change_file_mode_rw(dest, dest_filename) < 0) {
1317 FILE_OP_ERROR(dest_filename, "chmod");
1318 g_warning("can't change file mode\n");
1321 if (!mbox_write_data(src, dest, dest_filename, size)) {
1322 mbox_unlock_file(src, mbox_path);
1325 unlink(dest_filename);
1333 FILE_OP_ERROR(mbox_path, "fread");
1337 mbox_cache_msg_set_fetched(mbox_path, msgnum);
1339 if (fclose(dest) == -1) {
1340 FILE_OP_ERROR(dest_filename, "fclose");
1344 mbox_unlock_file(src, mbox_path);
1346 if (fclose(src) == -1) {
1347 FILE_OP_ERROR(mbox_path, "fclose");
1354 unlink(dest_filename);
1361 gchar *mbox_fetch_msg(Folder *folder, FolderItem *item, gint num)
1366 g_return_val_if_fail(item != NULL, NULL);
1368 path = folder_item_get_path(item);
1369 if (!is_dir_exist(path))
1370 make_dir_hier(path);
1372 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(num), NULL);
1376 if (!mbox_extract_msg(item, num, filename)) {
1384 gint mbox_add_msg(Folder *folder, FolderItem *dest, const gchar *file,
1385 gboolean remove_source)
1394 gchar from_line[MSGBUFSIZE];
1396 if (dest->last_num < 0) {
1397 mbox_scan_folder(folder, dest);
1398 if (dest->last_num < 0) return -1;
1401 src_fp = fopen(file, "rb");
1402 if (src_fp == NULL) {
1406 mbox_path = mbox_folder_get_path(dest);
1407 if (mbox_path == NULL)
1410 dest_fp = fopen(mbox_path, "ab");
1411 if (dest_fp == NULL) {
1417 if (change_file_mode_rw(dest_fp, mbox_path) < 0) {
1418 FILE_OP_ERROR(mbox_path, "chmod");
1419 g_warning("can't change file mode\n");
1422 old_size = ftell(dest_fp);
1424 mbox_lockwrite_file(dest_fp, mbox_path);
1426 if (fgets(from_line, sizeof(from_line), src_fp) == NULL) {
1427 mbox_unlock_file(dest_fp, mbox_path);
1428 g_warning("unvalid file - %s.\n", file);
1435 if (strncmp(from_line, "From ", 5) != 0) {
1438 if (stat(file, &s) < 0) {
1439 mbox_unlock_file(dest_fp, mbox_path);
1440 g_warning("invalid file - %s.\n", file);
1447 fprintf(dest_fp, "From - %s", ctime(&s.st_mtime));
1450 fputs(from_line, dest_fp);
1453 n_read = fread(buf, 1, sizeof(buf), src_fp);
1454 if ((n_read < (gint) sizeof(buf)) && ferror(src_fp))
1456 if (fwrite(buf, n_read, 1, dest_fp) < 1) {
1457 mbox_unlock_file(dest_fp, mbox_path);
1458 g_warning("writing to %s failed.\n", mbox_path);
1459 ftruncate(fileno(dest_fp), old_size);
1466 if (n_read < (gint) sizeof(buf))
1472 if (ferror(src_fp)) {
1473 FILE_OP_ERROR(mbox_path, "fread");
1476 mbox_unlock_file(dest_fp, mbox_path);
1478 if (fclose(src_fp) == -1) {
1479 FILE_OP_ERROR(file, "fclose");
1483 if (fclose(dest_fp) == -1) {
1484 FILE_OP_ERROR(mbox_path, "fclose");
1490 ftruncate(fileno(dest_fp), old_size);
1495 if (remove_source) {
1496 if (unlink(file) < 0)
1497 FILE_OP_ERROR(file, "unlink");
1503 return dest->last_num;
1507 gint mbox_remove_msg(Folder *folder, FolderItem *item, gint num)
1509 struct _message * msg;
1512 mbox_path = mbox_folder_get_path(item);
1513 if (mbox_path == NULL)
1516 mbox_cache_synchronize(mbox_path, TRUE);
1518 msg = mbox_cache_get_msg(mbox_path, num);
1523 MSG_SET_PERM_FLAGS(msg->flags, MSG_REALLY_DELETED);
1528 gint mbox_remove_all_msg(Folder *folder, FolderItem *item)
1533 mbox_path = mbox_folder_get_path(item);
1534 if (mbox_path == NULL)
1537 fp = fopen(mbox_path, "wb");
1551 gint mbox_move_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1556 filename = mbox_fetch_msg(folder, msginfo->folder, msginfo->msgnum);
1557 if (filename == NULL)
1560 msgnum = mbox_add_msg(folder, dest, filename, TRUE);
1563 MSG_SET_FLAGS(msginfo->flags, MSG_REALLY_DELETED);
1564 mbox_change_flags(folder, msginfo->folder, msginfo);
1570 gint mbox_move_msgs_with_dest(Folder *folder, FolderItem *dest, GSList *msglist)
1573 gchar * mbox_path = NULL;
1575 for(l = msglist ; l != NULL ; l = g_slist_next(l)) {
1576 MsgInfo * msginfo = (MsgInfo *) l->data;
1578 if (msginfo->folder && mbox_path == NULL)
1579 mbox_path = mbox_folder_get_path(msginfo->folder);
1581 mbox_move_msg(folder, dest, msginfo);
1585 mbox_cache_synchronize(mbox_path);
1589 mbox_path = mbox_folder_get_path(dest);
1590 mbox_cache_synchronize(mbox_path);
1593 return dest->last_num;
1598 gint mbox_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1603 filename = mbox_fetch_msg(folder, msginfo->folder, msginfo->msgnum);
1604 if (filename == NULL)
1607 msgnum = mbox_add_msg(folder, dest, filename, FALSE);
1612 gint mbox_copy_msgs_with_dest(Folder *folder, FolderItem *dest, GSList *msglist)
1615 gchar * mbox_path = NULL;
1617 for(l = msglist ; l != NULL ; l = g_slist_next(l)) {
1618 MsgInfo * msginfo = (MsgInfo *) l->data;
1620 if (msginfo->folder && mbox_path == NULL)
1621 mbox_path = mbox_folder_get_path(msginfo->folder);
1623 mbox_copy_msg(folder, dest, msginfo);
1627 mbox_cache_synchronize(mbox_path);
1631 mbox_path = mbox_folder_get_path(dest);
1632 mbox_cache_synchronize(mbox_path);
1635 return dest->last_num;
1639 struct _copy_flags_info
1645 typedef struct _copy_flags_info CopyFlagsInfo;
1647 GSList * copy_flags_data = NULL;
1649 gint mbox_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1651 Folder * src_folder;
1654 CopyFlagsInfo * flags_info;
1656 src_folder = msginfo->folder->folder;
1659 mbox_path = mbox_folder_get_path(msginfo->folder);
1660 mbox_rewrite(mbox_path);
1664 filename = folder_item_fetch_msg(msginfo->folder,
1666 if (filename == NULL)
1669 num = mbox_add_msg(folder, dest, filename, FALSE);
1672 mbox_path = mbox_folder_get_path(dest);
1673 msg = mbox_cache_get_msg(mbox_path, num);
1675 msg->flags = msginfo->flags;
1682 flags_info = g_new0(CopyFlagsInfo, 1);
1683 flags_info->num = num;
1684 flags_info->flags = msginfo->flags;
1685 copy_flags_data = g_slist_append(copy_flags_data, flags_info);
1690 void mbox_finished_copy(Folder *folder, FolderItem *dest)
1696 mbox_path = mbox_folder_get_path(dest);
1697 if (mbox_path == NULL)
1700 mbox_cache_synchronize(mbox_path, TRUE);
1702 for(l = copy_flags_data ; l != NULL ; l = g_slist_next(l)) {
1703 CopyFlagsInfo * flags_info = l->data;
1704 struct _message * msg;
1706 msg = mbox_cache_get_msg(mbox_path, flags_info->num);
1708 msg->flags = flags_info->flags;
1712 if (copy_flags_data != NULL) {
1713 cache = mbox_cache_get_mbox(mbox_path);
1714 cache->modification = TRUE;
1717 g_slist_free(copy_flags_data);
1718 copy_flags_data = NULL;
1720 mbox_rewrite(mbox_path);
1725 void mbox_scan_folder(Folder *folder, FolderItem *item)
1732 mbox_path = mbox_folder_get_path(item);
1733 if (mbox_path == NULL)
1736 mbox_cache_synchronize(mbox_path, TRUE);
1738 cached = mbox_cache_get_mbox(mbox_path);
1740 if (cached == NULL) {
1749 n_msg = mbox_cache_get_count(mbox_path);
1752 item->new = item->unread = item->total = 0;
1759 for(l = mbox_cache_get_msg_list(mbox_path) ; l != NULL ;
1760 l = g_list_next(l)) {
1761 struct _message * msg = (struct _message *) l->data;
1762 if (!MSG_IS_REALLY_DELETED(msg->flags))
1764 if (MSG_IS_NEW(msg->flags) && !MSG_IS_IGNORE_THREAD(msg->flags))
1766 if (MSG_IS_UNREAD(msg->flags) && !MSG_IS_IGNORE_THREAD(msg->flags))
1771 item->unread = unread;
1772 item->total = total;
1775 debug_print("Last number in dir %s = %d\n", mbox_path,
1777 item->last_num = n_msg;
1781 gchar * mbox_get_virtual_path(FolderItem * item)
1786 if (item->parent == NULL) {
1790 gchar * parent_path;
1791 gchar * result_path;
1793 parent_path = mbox_get_virtual_path(item->parent);
1794 if (parent_path == NULL)
1795 result_path = g_strdup(item->name);
1797 result_path = g_strconcat(parent_path,
1800 g_free(parent_path);
1806 static gboolean mbox_write_data(FILE * mbox_fp, FILE * new_fp,
1807 gchar * new_filename, gint size)
1815 while (pos < size) {
1816 if ((size - pos) > (gint) sizeof(buf))
1821 n_read = fread(buf, 1, max, mbox_fp);
1823 if (n_read < max && ferror(mbox_fp)) {
1826 if (fwrite(buf, n_read, 1, new_fp) < 1) {
1827 g_warning("writing to %s failed.\n", new_filename);
1840 static gboolean mbox_write_message(FILE * mbox_fp, FILE * new_fp,
1841 gchar * new_filename,
1842 struct _message * msg)
1845 GPtrArray * headers;
1848 fseek(mbox_fp, msg->header, SEEK_SET);
1850 headers = procheader_get_header_array_asis(mbox_fp);
1852 for (i = 0; i < (gint) headers->len; i++) {
1853 Header * h = g_ptr_array_index(headers, i);
1855 if (!procheader_headername_equal(h->name,
1857 !procheader_headername_equal(h->name,
1859 fwrite(h->name, strlen(h->name),
1861 if (h->name[strlen(h->name) - 1] != ' ')
1862 fwrite(" ", 1, 1, new_fp);
1863 fwrite(h->body, strlen(h->body),
1865 fwrite("\n", 1, 1, new_fp);
1867 procheader_header_free(h);
1868 g_ptr_array_remove_index(headers, i);
1872 g_ptr_array_free(headers, FALSE);
1874 if (!MSG_IS_INVALID(msg->flags)) {
1876 fwrite("Status: ", strlen("Status: "), 1, new_fp);
1877 if (!MSG_IS_UNREAD(msg->flags))
1878 fwrite("R", 1, 1, new_fp);
1879 fwrite("O", 1, 1, new_fp);
1880 fwrite("\n", 1, 1, new_fp);
1882 /* X-Status header */
1883 if (MSG_IS_REALLY_DELETED(msg->flags)
1884 || MSG_IS_MARKED(msg->flags)
1885 || MSG_IS_DELETED(msg->flags)
1886 || MSG_IS_REPLIED(msg->flags)
1887 || MSG_IS_FORWARDED(msg->flags)) {
1888 fwrite("X-Status: ", strlen("X-Status: "), 1, new_fp);
1889 if (MSG_IS_REALLY_DELETED(msg->flags))
1890 fwrite("D", 1, 1, new_fp); /* really deleted */
1892 if (MSG_IS_MARKED(msg->flags))
1893 fwrite("F", 1, 1, new_fp);
1894 if (MSG_IS_DELETED(msg->flags))
1895 fwrite("d", 1, 1, new_fp);
1896 if (MSG_IS_REPLIED(msg->flags))
1897 fwrite("r", 1, 1, new_fp);
1898 if (MSG_IS_FORWARDED(msg->flags))
1899 fwrite("f", 1, 1, new_fp);
1901 fwrite("\n", 1, 1, new_fp);
1905 fwrite("\n", 1, 1, new_fp);
1907 size = msg->end - msg->content;
1908 fseek(mbox_fp, msg->content, SEEK_SET);
1910 return mbox_write_data(mbox_fp, new_fp, new_filename, size);
1913 void mbox_update_mark(Folder * folder, FolderItem * item)
1917 mbox_path = mbox_folder_get_path(item);
1918 if (mbox_path == NULL)
1921 mbox_rewrite(mbox_path);
1925 void mbox_change_flags(Folder * folder, FolderItem * item, MsgInfo * info)
1927 struct _message * msg;
1931 mbox_path = mbox_folder_get_path(item);
1932 if (mbox_path == NULL)
1935 msg = mbox_cache_get_msg(mbox_path, info->msgnum);
1937 cache = mbox_cache_get_mbox(mbox_path);
1941 if ((msg == NULL) || (cache == NULL))
1944 msg->flags = info->flags;
1946 cache->modification = TRUE;
1951 static gboolean mbox_rewrite(gchar * mbox)
1962 msg_list = mbox_cache_get_msg_list(mbox);
1964 cache = mbox_cache_get_mbox(mbox);
1968 if (!cache->modification) {
1969 debug_print("no modification - %s\n", mbox);
1973 debug_print("save modification - %s\n", mbox);
1975 mbox_fp = fopen(mbox, "rb+");
1976 mbox_lockwrite_file(mbox_fp, mbox);
1978 mbox_cache_synchronize_from_file(mbox_fp, mbox, TRUE);
1980 new = g_strconcat(mbox, ".", itos((int) mbox), NULL);
1981 new_fp = fopen(new, "wb");
1983 if (change_file_mode_rw(new_fp, new) < 0) {
1984 FILE_OP_ERROR(new, "chmod");
1985 g_warning("can't change file mode\n");
1988 mbox_lockwrite_file(new_fp, new);
1993 msg_list = mbox_cache_get_msg_list(mbox);
1994 for(l = msg_list ; l != NULL ; l = g_list_next(l)) {
1995 struct _message * msg = (struct _message *) l->data;
1996 if (!mbox_write_message(mbox_fp, new_fp, new, msg)) {
2005 if (rename(new, mbox) == -1) {
2006 g_warning("can't rename %s to %s\n", new, mbox);
2007 mbox_unlock_file(new_fp, new);
2009 mbox_unlock_file(mbox_fp, mbox);
2015 if (change_file_mode_rw(new_fp, mbox) < 0) {
2016 FILE_OP_ERROR(new, "chmod");
2017 g_warning("can't change file mode\n");
2020 mbox_unlock_file(new_fp, new);
2024 mbox_unlock_file(mbox_fp, mbox);
2028 debug_print("%i messages written - %s\n", count, mbox);
2030 cache = mbox_cache_get_mbox(mbox);
2035 mbox_cache_synchronize(mbox, FALSE);
2042 static gboolean mbox_purge_deleted(gchar * mbox)
2049 gboolean modification = FALSE;
2053 mbox_cache_synchronize(mbox, TRUE);
2055 msg_list = mbox_cache_get_msg_list(mbox);
2057 for(l = msg_list ; l != NULL ; l = g_list_next(l)) {
2058 struct _message * msg = (struct _message *) l->data;
2059 if (!MSG_IS_INVALID(msg->flags) && MSG_IS_REALLY_DELETED(msg->flags)) {
2060 modification = TRUE;
2065 if (!modification) {
2066 debug_print("no deleted messages - %s\n", mbox);
2070 debug_print("purge deleted messages - %s\n", mbox);
2072 mbox_fp = fopen(mbox, "rb+");
2073 mbox_lockwrite_file(mbox_fp, mbox);
2075 mbox_cache_synchronize_from_file(mbox_fp, mbox, TRUE);
2077 new = g_strconcat(mbox, ".", itos((int) mbox), NULL);
2078 new_fp = fopen(new, "wb");
2080 if (change_file_mode_rw(new_fp, new) < 0) {
2081 FILE_OP_ERROR(new, "chmod");
2082 g_warning("can't change file mode\n");
2085 mbox_lockwrite_file(new_fp, new);
2090 msg_list = mbox_cache_get_msg_list(mbox);
2091 for(l = msg_list ; l != NULL ; l = g_list_next(l)) {
2092 struct _message * msg = (struct _message *) l->data;
2093 if (MSG_IS_INVALID(msg->flags) || !MSG_IS_REALLY_DELETED(msg->flags)) {
2094 if (!mbox_write_message(mbox_fp, new_fp, new, msg)) {
2104 if (rename(new, mbox) == -1) {
2105 g_warning("can't rename %s to %s\n", new, mbox);
2106 mbox_unlock_file(new_fp, new);
2108 mbox_unlock_file(mbox_fp, mbox);
2114 if (change_file_mode_rw(new_fp, mbox) < 0) {
2115 FILE_OP_ERROR(new, "chmod");
2116 g_warning("can't change file mode\n");
2119 mbox_unlock_file(new_fp, new);
2123 mbox_unlock_file(mbox_fp, mbox);
2127 debug_print("%i messages written - %s\n", count, mbox);
2129 mbox_cache_synchronize(mbox, FALSE);
2134 #define MAKE_DIR_IF_NOT_EXIST(dir) \
2136 if (!is_dir_exist(dir)) { \
2137 if (is_file_exist(dir)) { \
2138 g_warning("File `%s' already exists.\n" \
2139 "Can't create folder.", dir); \
2142 if (mkdir(dir, S_IRWXU) < 0) { \
2143 FILE_OP_ERROR(dir, "mkdir"); \
2146 if (chmod(dir, S_IRWXU) < 0) \
2147 FILE_OP_ERROR(dir, "chmod"); \
2151 gint mbox_create_tree(Folder *folder)
2155 g_return_val_if_fail(folder != NULL, -1);
2157 CHDIR_RETURN_VAL_IF_FAIL(get_home_dir(), -1);
2158 rootpath = LOCAL_FOLDER(folder)->rootpath;
2159 MAKE_DIR_IF_NOT_EXIST(rootpath);
2160 CHDIR_RETURN_VAL_IF_FAIL(rootpath, -1);
2165 #undef MAKE_DIR_IF_NOT_EXIST
2167 static gchar * mbox_get_new_path(FolderItem * parent, gchar * name)
2171 if (strchr(name, '/') == NULL) {
2172 if (parent->path != NULL)
2173 path = g_strconcat(parent->path, ".sbd", G_DIR_SEPARATOR_S, name, NULL);
2175 path = g_strdup(name);
2178 path = g_strdup(name);
2183 static gchar * mbox_get_folderitem_name(gchar * name)
2187 foldername = g_strdup(g_basename(name));
2192 FolderItem *mbox_create_folder(Folder *folder, FolderItem *parent,
2196 FolderItem *new_item;
2199 g_return_val_if_fail(folder != NULL, NULL);
2200 g_return_val_if_fail(parent != NULL, NULL);
2201 g_return_val_if_fail(name != NULL, NULL);
2203 path = mbox_get_new_path(parent, (gchar *) name);
2205 foldername = mbox_get_folderitem_name((gchar *) name);
2207 new_item = folder_item_new(folder, foldername, path);
2208 folder_item_append(parent, new_item);
2210 if (!strcmp(name, "inbox")) {
2211 new_item->stype = F_INBOX;
2212 new_item->folder->inbox = new_item;
2213 } else if (!strcmp(name, "outbox")) {
2214 new_item->stype = F_OUTBOX;
2215 new_item->folder->outbox = new_item;
2216 } else if (!strcmp(name, "draft")) {
2217 new_item->stype = F_DRAFT;
2218 new_item->folder->draft = new_item;
2219 } else if (!strcmp(name, "queue")) {
2220 new_item->stype = F_QUEUE;
2221 new_item->folder->queue = new_item;
2222 } else if (!strcmp(name, "trash")) {
2223 new_item->stype = F_TRASH;
2224 new_item->folder->trash = new_item;
2233 gint mbox_rename_folder(Folder *folder, FolderItem *item, const gchar *name)
2238 g_return_val_if_fail(folder != NULL, -1);
2239 g_return_val_if_fail(item != NULL, -1);
2240 g_return_val_if_fail(item->path != NULL, -1);
2241 g_return_val_if_fail(name != NULL, -1);
2243 path = mbox_get_new_path(item->parent, (gchar *) name);
2244 foldername = mbox_get_folderitem_name((gchar *) name);
2246 if (rename(item->path, path) == -1) {
2249 g_warning("Cannot rename folder item");
2257 item->name = foldername;
2263 gint mbox_remove_folder(Folder *folder, FolderItem *item)
2265 g_return_val_if_fail(folder != NULL, -1);
2266 g_return_val_if_fail(item != NULL, -1);
2267 g_return_val_if_fail(item->path != NULL, -1);
2269 folder_item_remove(item);
2273 gint mbox_get_num_list(Folder *folder, FolderItem *item, GSList **mlist)
2280 mbox_path = mbox_folder_get_path(item);
2282 if (mbox_path == NULL)
2285 mbox_purge_deleted(mbox_path);
2287 fp = fopen(mbox_path, "rb");
2294 mbox_lockread_file(fp, mbox_path);
2296 mbox_cache_synchronize_from_file(fp, mbox_path, TRUE);
2298 item->last_num = mbox_cache_get_count(mbox_path);
2300 for(l = mbox_cache_get_msg_list(mbox_path) ; l != NULL ;
2301 l = g_list_next(l)) {
2302 struct _message * msg;
2304 msg = (struct _message *) l->data;
2306 if (MSG_IS_INVALID(msg->flags) || !MSG_IS_REALLY_DELETED(msg->flags)) {
2307 *mlist = g_slist_append(*mlist, GINT_TO_POINTER(msg->msgnum));
2310 MSG_SET_PERM_FLAGS(msg->flags, MSG_REALLY_DELETED);
2314 mbox_unlock_file(fp, mbox_path);
2323 MsgInfo *mbox_get_msginfo(Folder *folder, FolderItem *item, gint num)
2326 struct _message *msg;
2330 g_return_val_if_fail(folder != NULL, NULL);
2331 g_return_val_if_fail(item != NULL, NULL);
2333 mbox_path = mbox_folder_get_path(item);
2335 g_return_val_if_fail(mbox_path != NULL, NULL);
2337 src = fopen(mbox_path, "rb");
2342 mbox_lockread_file(src, mbox_path);
2343 mbox_cache_synchronize_from_file(src, mbox_path, TRUE);
2345 msg = mbox_cache_get_msg(mbox_path, num);
2347 mbox_unlock_file(src, mbox_path);
2353 fseek(src, msg->header, SEEK_SET);
2354 msginfo = mbox_parse_msg(src, msg, item);
2356 mbox_unlock_file(src, mbox_path);
2363 gboolean mbox_check_msgnum_validity(Folder *folder, FolderItem *item)
2365 mboxcache * old_cache;
2366 gboolean scan_new = TRUE;
2370 filename = mbox_folder_get_path(item);
2372 old_cache = mbox_cache_get_mbox(filename);
2374 if (old_cache != NULL) {
2375 if (stat(filename, &s) < 0) {
2376 FILE_OP_ERROR(filename, "stat");
2377 } else if (old_cache->mtime == s.st_mtime) {
2378 debug_print("Folder is not modified.\n");