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 Folder *mbox_folder_new(const gchar * name, const gchar * path);
39 static void mbox_folder_destroy(Folder * folder);
41 static gchar *mbox_fetch_msg(Folder * folder, FolderItem * item, gint num);
43 static void mbox_scan_folder(Folder * folder, FolderItem * item);
44 static gint mbox_add_msg(Folder * folder, FolderItem * dest,
45 const gchar * file, MsgFlags *flags);
47 static gint mbox_remove_all_msg(Folder * folder, FolderItem * item);
48 static gint mbox_remove_msg(Folder * folder, FolderItem * item, gint num);
49 static void mbox_change_flags(Folder * folder, FolderItem * item,
50 MsgInfo * info, MsgPermFlags newflags);
51 static gint mbox_copy_msg(Folder * folder, FolderItem * dest,
53 static gint mbox_create_tree(Folder * folder);
54 static FolderItem *mbox_create_folder(Folder * folder, FolderItem * parent,
56 static gint mbox_rename_folder(Folder * folder, FolderItem * item,
58 static gint mbox_remove_folder(Folder * folder, FolderItem * item);
60 static void mbox_folder_init (Folder *folder,
64 static gboolean mbox_write_data(FILE * mbox_fp, FILE * new_fp,
65 gchar * new_filename, gint size);
66 static gboolean mbox_purge_deleted(gchar * mbox);
67 static gchar * mbox_get_new_path(FolderItem * parent, gchar * name);
68 static gchar * mbox_get_folderitem_name(gchar * name);
70 static MsgInfo *mbox_get_msginfo(Folder *folder, FolderItem *item, gint num);
71 static gint mbox_get_num_list(Folder *folder, FolderItem *item, GSList **list);
72 static gboolean mbox_check_msgnum_validity(Folder *folder, FolderItem *item);
73 static gchar *mbox_folder_get_path(Folder *folder, FolderItem *item);
75 FolderClass mbox_class =
81 /* Folder functions */
87 /* FolderItem functions */
99 mbox_check_msgnum_validity,
101 /* Message functions */
115 FolderClass *mbox_get_class(void)
120 Folder *mbox_folder_new(const gchar *name, const gchar *path)
124 folder = (Folder *)g_new0(MBOXFolder, 1);
125 folder->klass = &mbox_class;
126 mbox_folder_init(folder, name, path);
131 void mbox_folder_destroy(Folder *folder)
133 folder_local_folder_destroy(LOCAL_FOLDER(folder));
136 static void mbox_folder_init(Folder *folder, const gchar *name, const gchar *path)
138 folder_local_folder_init(folder, name, path);
141 static void mbox_folder_create_parent(const gchar * path)
143 if (!is_file_exist(path)) {
146 new_path = g_dirname(path);
147 if (new_path[strlen(new_path) - 1] == G_DIR_SEPARATOR)
148 new_path[strlen(new_path) - 1] = '\0';
150 if (!is_dir_exist(new_path))
151 make_dir_hier(new_path);
158 gchar *mbox_folder_get_path(Folder *folder, FolderItem *item)
163 g_return_val_if_fail(item != NULL, NULL);
165 if (item->path && item->path[0] == G_DIR_SEPARATOR) {
166 mbox_folder_create_parent(item->path);
167 return g_strdup(item->path);
170 folder_path = g_strdup(LOCAL_FOLDER(item->folder)->rootpath);
171 g_return_val_if_fail(folder_path != NULL, NULL);
173 if (folder_path[0] == G_DIR_SEPARATOR) {
175 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
179 path = g_strdup(folder_path);
182 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
183 folder_path, G_DIR_SEPARATOR_S,
186 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
192 mbox_folder_create_parent(path);
198 /**********************************************************/
202 /**********************************************************/
205 static GSList * file_lock = NULL;
207 static gboolean mbox_file_lock_file(gchar * base)
209 gchar *lockfile, *locklink;
213 lockfile = g_strdup_printf("%s.%d", base, getpid());
214 if ((lockfp = fopen(lockfile, "wb")) == NULL) {
215 FILE_OP_ERROR(lockfile, "fopen");
216 g_warning("can't create lock file %s\n", lockfile);
217 g_warning("use 'flock' instead of 'file' if possible.\n");
222 fprintf(lockfp, "%d\n", getpid());
225 locklink = g_strconcat(base, ".lock", NULL);
226 while (link(lockfile, locklink) < 0) {
227 FILE_OP_ERROR(lockfile, "link");
229 g_warning("can't create %s\n", lockfile);
236 g_warning("mailbox is owned by another"
237 " process, waiting...\n");
248 static gboolean mbox_fcntl_lockwrite_file(FILE * fp)
252 lck.l_type = F_WRLCK;
257 if (fcntl(fileno(fp), F_SETLK, &lck) < 0)
263 static gboolean mbox_fcntl_lockread_file(FILE * fp)
267 lck.l_type = F_RDLCK;
272 if (fcntl(fileno(fp), F_SETLK, &lck) < 0)
278 static gboolean mbox_fcntl_unlock_file(FILE * fp)
282 lck.l_type = F_UNLCK;
287 if (fcntl(fileno(fp), F_SETLK, &lck) < 0)
293 static gboolean mbox_file_unlock_file(gchar * base)
297 lockfile = g_strdup_printf("%s.lock", base);
304 static gboolean mbox_lockread_file(FILE * fp, gchar * base)
308 result = mbox_fcntl_lockread_file(fp);
310 if ((result = mbox_file_lock_file(base)) == TRUE) {
311 file_lock = g_slist_append(file_lock, g_strdup(base));
312 debug_print("lockfile lock %s.\n", base);
315 g_warning("could not lock read file %s\n", base);
318 debug_print("fcntl lock %s.\n", base);
323 static gboolean mbox_lockwrite_file(FILE * fp, gchar * base)
327 result = mbox_fcntl_lockwrite_file(fp);
329 if ((result = mbox_file_lock_file(base)) == TRUE) {
330 file_lock = g_slist_append(file_lock, g_strdup(base));
331 debug_print("lockfile lock %s.\n", base);
334 g_warning("could not lock write file %s\n", base);
337 debug_print("fcntl lock %s.\n", base);
342 static gboolean mbox_unlock_file(FILE * fp, gchar * base)
344 gboolean result = FALSE;
346 gboolean unlocked = FALSE;
348 for(l = file_lock ; l != NULL ; l = g_slist_next(l)) {
349 gchar * data = l->data;
351 if (strcmp(data, base) == 0) {
352 file_lock = g_slist_remove(file_lock, data);
354 result = mbox_file_unlock_file(base);
356 debug_print("lockfile unlock - %s.\n", base);
362 result = mbox_fcntl_unlock_file(fp);
363 debug_print("fcntl unlock - %s.\n", base);
369 /**********************************************************/
373 /**********************************************************/
375 #define MAILFILE_ERROR_NO_ERROR 0x000
376 #define MAILFILE_ERROR_FILE_NOT_FOUND 0x001
377 #define MAILFILE_ERROR_MEMORY 0x002
378 #define MAILFILE_ERROR_MESSAGE_NOT_FOUND 0x003
380 static int mailfile_error = MAILFILE_ERROR_NO_ERROR;
382 #define STATE_BEGIN 0x000
383 #define STATE_TEXT_READ 0x001
384 #define STATE_FROM_READ 0x002
385 #define STATE_FIELD_READ 0x003
386 #define STATE_END 0x004
387 #define STATE_END_OF_FILE 0x005
388 #define STATE_MEM_ERROR 0x006
389 #define STATE_TEXT_BEGIN 0x007
391 #define STATE_MASK 0x0FF /* filter state from functions */
393 #define STATE_RESTORE_POS 0x100 /* go back while reading */
395 typedef struct _mailfile mailfile;
419 #define MSG_IS_INVALID(msg) \
420 ((msg).perm_flags == (msg).tmp_flags && (msg).tmp_flags == -1)
422 #define MSG_SET_INVALID(msg) \
423 ((msg).perm_flags = (msg).tmp_flags = -1)
425 static int startFrom(char * s)
427 return (strncmp(s, "From ", 5) == 0);
430 static int startSpace(char * s)
432 return ((*s == ' ') || (*s == '\t'));
435 static int startEmpty(char * s)
440 static void free_msg_list(GList * l)
442 GList * elt = g_list_first(l);
447 elt = g_list_next(elt);
454 static mailfile * mailfile_init_from_file(FILE * f, gchar * filename)
457 GList * msg_list = NULL;
464 struct _message * data = NULL;
470 while (state != STATE_END_OF_FILE) {
471 if ((state & STATE_RESTORE_POS) == 0) {
472 former_pos = lastpos;
475 r = fgets(s, 256, f);
478 ignore_next = (s[strlen(s) - 1] != '\n');
483 switch(state & 0x0F) {
486 state = STATE_END_OF_FILE;
487 else if (startFrom(s)) {
488 state = STATE_FROM_READ;
490 data = g_new0(struct _message, 1);
492 free_msg_list(msg_list);
497 data->msgnum = msgnum;
498 data->offset = lastpos;
499 data->header = lastpos;
502 data->messageid = NULL;
503 data->fromspace = NULL;
504 MSG_SET_INVALID(data->flags);
505 MSG_SET_INVALID(data->old_flags);
506 data->fetched = FALSE;
507 msg_list = g_list_append(msg_list,
515 case STATE_TEXT_READ:
518 else if (startFrom(s))
519 state = STATE_END | STATE_RESTORE_POS;
521 state = STATE_TEXT_READ;
524 case STATE_TEXT_BEGIN:
525 data->content = lastpos;
528 else if (startFrom(s)) {
529 state = STATE_END | STATE_RESTORE_POS;
532 state = STATE_TEXT_READ;
536 case STATE_FROM_READ:
537 data->content = lastpos;
540 else if (startSpace(s))
541 state = STATE_FROM_READ;
542 else if (startEmpty(s))
543 state = STATE_TEXT_READ;
545 state = STATE_FIELD_READ;
548 case STATE_FIELD_READ:
549 data->content = lastpos;
552 else if (startSpace(s))
553 state = STATE_FIELD_READ;
554 else if (startEmpty(s)) {
555 state = STATE_TEXT_BEGIN;
558 state = STATE_FIELD_READ;
562 if ((state & STATE_MASK) == STATE_END) {
563 state = STATE_BEGIN | (state & STATE_RESTORE_POS);
569 r = fgets(s, 256, f);
570 if (r == NULL || *r == '\0')
573 while (s[strlen(s) - 1] != '\n');
577 mf = (mailfile *) g_new0(struct _mailfile, 1);
579 free_msg_list(msg_list);
580 mailfile_error = MAILFILE_ERROR_MEMORY;
584 mf->msg_list = g_list_first(msg_list);
586 mf->filename = g_strdup(filename);
589 mailfile_error = MAILFILE_ERROR_NO_ERROR;
594 static mailfile * mailfile_init(char * filename)
600 f = fopen(filename, "rb");
603 mailfile_error = MAILFILE_ERROR_FILE_NOT_FOUND;
607 mbox_lockread_file(f, filename);
609 mf = mailfile_init_from_file(f, filename);
611 mbox_unlock_file(f, filename);
618 static void mailfile_done(mailfile * f)
620 free_msg_list(f->msg_list);
627 #define MAX_READ 4096
629 static char * readfile(char * filename, int offset, int max_offset)
638 handle = fopen(filename, "rb");
640 if (handle == NULL) {
641 mailfile_error = MAILFILE_ERROR_FILE_NOT_FOUND;
645 size = max_offset - offset;
647 message = (char *) malloc(size + 1);
648 if (message == NULL) {
650 mailfile_error = MAILFILE_ERROR_MEMORY;
654 fseek(handle, offset, SEEK_SET);
658 if ((size - pos) > MAX_READ)
663 bread = fread(message + pos, 1, max, handle);
679 static char * mailfile_readmsg(mailfile f, int index)
685 struct _message * msginfo;
687 nth = g_list_nth(f->msg_list, index);
690 mailfile_error = MAILFILE_ERROR_MESSAGE_NOT_FOUND;
694 msginfo = (struct _message *)nth->data;
696 offset = msginfo->offset;
697 max_offset = msginfo->end;
698 message = readfile(f->filename, offset, max_offset);
700 mailfile_error = MAILFILE_ERROR_NO_ERROR;
705 static char * mailfile_readheader(mailfile f, int index)
711 struct _message * msginfo;
713 nth = g_list_nth(f->msg_list, index);
716 mailfile_error = MAILFILE_ERROR_MESSAGE_NOT_FOUND;
720 msginfo = (struct _message *)nth->data;
722 offset = msginfo->offset;
723 max_offset = msginfo->content;
724 message = readfile(f->filename, offset, max_offset);
726 mailfile_error = MAILFILE_ERROR_NO_ERROR;
731 static int mailfile_count(mailfile * f)
733 return g_list_length(f->msg_list);
736 static int mailfile_find_deleted(mailfile f, char * filename)
740 handle = fopen(filename, "rb");
743 struct _message m = elt->data;
744 n = fread(&m.deleted, sizeof(int), 1, handle);
747 elt = g_list_next(elt);
756 /**********************************************************/
758 /* mbox cache operations */
760 /**********************************************************/
767 gboolean modification;
770 typedef struct _mboxcache mboxcache;
772 static GHashTable * mbox_cache_table = NULL;
774 static MsgInfo *mbox_parse_msg(FILE * fp, struct _message * msg,
778 MsgFlags flags = { 0, 0 };
780 MSG_SET_PERM_FLAGS(flags, MSG_NEW | MSG_UNREAD);
782 g_return_val_if_fail(fp != NULL, NULL);
785 if (item->stype == F_QUEUE) {
786 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
787 } else if (item->stype == F_DRAFT) {
788 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
792 msginfo = procheader_parse_stream(fp, flags, FALSE, FALSE);
794 if (!msginfo) return NULL;
797 msginfo->msgnum = msg->msgnum;
798 msginfo->folder = item;
804 static void mbox_cache_init()
806 mbox_cache_table = g_hash_table_new(g_str_hash, g_str_equal);
809 static void mbox_cache_free_mbox(mboxcache * cache)
811 g_hash_table_remove(mbox_cache_table, cache->filename);
814 mailfile_done(cache->mf);
816 g_ptr_array_free(cache->tab_mf, FALSE);
818 g_free(cache->filename);
822 static void mbox_cache_get_msginfo_from_file(FILE * fp, GList * msg_list)
827 for(l = msg_list ; l != NULL ; l = g_list_next(l)) {
828 struct _message * msg;
830 msg = (struct _message *) l->data;
832 fseek(fp, msg->header, SEEK_SET);
833 msginfo = mbox_parse_msg(fp, msg, NULL);
837 g_strdup(msginfo->msgid);
838 if (msginfo->fromspace)
840 g_strdup(msginfo->fromspace);
841 msg->flags = msginfo->flags;
842 msg->old_flags = msginfo->flags;
844 procmsg_msginfo_free(msginfo);
849 static void mbox_cache_get_msginfo(gchar * filename, GList * msg_list)
853 fp = fopen(filename, "rb");
857 mbox_cache_get_msginfo_from_file(fp, msg_list);
861 static mboxcache * mbox_cache_read_mbox(gchar * filename)
868 if (stat(filename, &s) < 0)
871 mf = mailfile_init(filename);
875 cache = g_new0(mboxcache, 1);
877 cache->mtime = s.st_mtime;
879 cache->filename = g_strdup(filename);
880 cache->modification = FALSE;
882 cache->tab_mf = g_ptr_array_new();
883 for(l = mf->msg_list ; l != NULL ; l = g_list_next(l))
884 g_ptr_array_add(cache->tab_mf, l->data);
886 mbox_cache_get_msginfo(filename, mf->msg_list);
888 debug_print("read mbox - %s\n", filename);
893 static mboxcache * mbox_cache_read_mbox_from_file(FILE * fp, gchar * filename)
900 if (stat(filename, &s) < 0)
903 mf = mailfile_init_from_file(fp, filename);
907 cache = g_new0(mboxcache, 1);
909 cache->mtime = s.st_mtime;
911 cache->filename = g_strdup(filename);
913 cache->tab_mf = g_ptr_array_new();
914 for(l = mf->msg_list ; l != NULL ; l = g_list_next(l))
915 g_ptr_array_add(cache->tab_mf, l->data);
917 mbox_cache_get_msginfo_from_file(fp, mf->msg_list);
919 debug_print("read mbox from file - %s\n", filename);
924 static void mbox_cache_insert_mbox(mboxcache * data)
926 if (mbox_cache_table == NULL)
929 g_hash_table_insert(mbox_cache_table, data->filename, data);
932 static mboxcache * mbox_cache_get_mbox(gchar * filename)
934 if (mbox_cache_table == NULL)
937 return g_hash_table_lookup(mbox_cache_table, filename);
941 static gint mbox_cache_get_count(gchar * filename)
945 cache = mbox_cache_get_mbox(filename);
948 if (cache->mf == NULL)
950 return cache->mf->count;
953 static GList * mbox_cache_get_msg_list(gchar * filename)
957 cache = mbox_cache_get_mbox(filename);
962 if (cache->mf == NULL)
965 return cache->mf->msg_list;
968 static void mbox_cache_synchronize_lists(GList * old_msg_list,
969 GList * new_msg_list)
974 for(l2 = old_msg_list ; l2 != NULL ; l2 = g_list_next(l2)) {
975 struct _message * msg2 = l2->data;
977 if ((msg2->messageid == NULL) ||
978 (msg2->fromspace == NULL))
981 for(l = new_msg_list ; l != NULL ; l = g_list_next(l)) {
982 struct _message * msg = l->data;
984 if ((msg->messageid == NULL) ||
985 (msg->fromspace == NULL))
988 if ((strcmp(msg->messageid, msg2->messageid) == 0) &&
989 (strcmp(msg->fromspace, msg2->fromspace) == 0)) {
990 if (msg2->flags.perm_flags != msg2->old_flags.perm_flags) {
991 msg->flags = msg2->flags;
999 static void mbox_cache_synchronize(gchar * filename, gboolean sync)
1001 mboxcache * new_cache;
1002 mboxcache * old_cache;
1003 gboolean scan_new = TRUE;
1006 old_cache = mbox_cache_get_mbox(filename);
1008 if (old_cache != NULL) {
1009 if (stat(filename, &s) < 0) {
1010 FILE_OP_ERROR(filename, "stat");
1011 } else if (old_cache->mtime == s.st_mtime) {
1012 debug_print("Folder is not modified.\n");
1019 if (strstr(filename, "trash") == 0)
1020 printf("old_cache: %p %s\n", old_cache, filename);
1022 printf("begin old\n");
1023 for(l = old_cache->mf->msg_list ; l != NULL ;
1024 l = g_list_next(l)) {
1025 struct _message * msg = l->data;
1026 printf("%p\n", msg);
1028 printf("end old\n");
1032 new_cache = mbox_cache_read_mbox(filename);
1035 if (strstr(filename, "trash") == 0)
1036 printf("new_cache: %p %s\n", new_cache, filename);
1038 printf("begin new\n");
1039 for(l = new_cache->mf->msg_list ; l != NULL ;
1040 l = g_list_next(l)) {
1041 struct _message * msg = l->data;
1042 printf("%p\n", msg);
1044 printf("end new\n");
1051 if (sync && new_cache && old_cache)
1052 mbox_cache_synchronize_lists(old_cache->mf->msg_list,
1053 new_cache->mf->msg_list);
1055 if (old_cache != NULL)
1056 mbox_cache_free_mbox(old_cache);
1059 mbox_cache_insert_mbox(new_cache);
1061 printf("insert %p %s\n", new_cache, new_cache->filename);
1062 printf("inserted %s %p\n", filename,
1063 mbox_cache_get_mbox(filename));
1069 static void mbox_cache_synchronize_from_file(FILE * fp, gchar * filename,
1072 mboxcache * new_cache;
1073 mboxcache * old_cache;
1074 gboolean scan_new = TRUE;
1077 old_cache = mbox_cache_get_mbox(filename);
1079 if (old_cache != NULL) {
1080 if (stat(filename, &s) < 0) {
1081 FILE_OP_ERROR(filename, "stat");
1082 } else if (old_cache->mtime == s.st_mtime) {
1083 debug_print("Folder is not modified.\n");
1094 if (strstr(filename, "trash") == 0)
1095 printf("old_cache: %p %s\n", old_cache, filename);
1098 printf("begin old\n");
1099 for(l = old_cache->mf->msg_list ; l != NULL ;
1100 l = g_list_next(l)) {
1101 struct _message * msg = l->data;
1102 printf("%p\n", msg);
1104 printf("end old\n");
1108 new_cache = mbox_cache_read_mbox_from_file(fp, filename);
1111 if (strstr(filename, "trash") == 0)
1112 printf("new_cache: %p %s\n", new_cache, filename);
1115 printf("begin new\n");
1116 for(l = new_cache->mf->msg_list ; l != NULL ;
1117 l = g_list_next(l)) {
1118 struct _message * msg = l->data;
1119 printf("%p\n", msg);
1121 printf("end new\n");
1128 if (sync && new_cache && old_cache)
1129 mbox_cache_synchronize_lists(old_cache->mf->msg_list,
1130 new_cache->mf->msg_list);
1132 if (old_cache != NULL)
1133 mbox_cache_free_mbox(old_cache);
1136 mbox_cache_insert_mbox(new_cache);
1138 printf("insert %p %s\n", new_cache, new_cache->filename);
1139 printf("inserted %s %p\n", filename,
1140 mbox_cache_get_mbox(filename));
1146 gboolean mbox_cache_msg_fetched(gchar * filename, gint num)
1148 struct _message * msg;
1151 cache = mbox_cache_get_mbox(filename);
1156 msg = (struct _message *) g_ptr_array_index(cache->tab_mf,
1161 return msg->fetched;
1164 void mbox_cache_msg_set_fetched(gchar * filename, gint num)
1166 struct _message * msg;
1169 cache = mbox_cache_get_mbox(filename);
1174 msg = (struct _message *) g_ptr_array_index(cache->tab_mf,
1179 msg->fetched = TRUE;
1182 struct _message * mbox_cache_get_msg(gchar * filename, gint num)
1186 cache = mbox_cache_get_mbox(filename);
1188 if (cache == NULL) {
1192 return (struct _message *) g_ptr_array_index(cache->tab_mf,
1197 /**********************************************************/
1199 /* mbox operations */
1201 /**********************************************************/
1203 static gboolean mbox_extract_msg(FolderItem * item, gint msgnum,
1204 gchar * dest_filename)
1206 struct _message * msg;
1213 /* GList * msg_list;*/
1214 gboolean already_fetched;
1217 mbox_path = mbox_folder_get_path(item->folder, item);
1219 if (mbox_path == NULL)
1222 src = fopen(mbox_path, "rb");
1228 mbox_lockread_file(src, mbox_path);
1230 mbox_cache_synchronize_from_file(src, mbox_path, TRUE);
1232 already_fetched = mbox_cache_msg_fetched(mbox_path, msgnum);
1234 if (already_fetched) {
1235 mbox_unlock_file(src, mbox_path);
1241 msg = mbox_cache_get_msg(mbox_path, msgnum);
1244 mbox_unlock_file(src, mbox_path);
1250 offset = msg->offset;
1251 max_offset = msg->end;
1253 size = max_offset - offset;
1255 fseek(src, offset, SEEK_SET);
1257 dest = fopen(dest_filename, "wb");
1259 mbox_unlock_file(src, mbox_path);
1265 if (change_file_mode_rw(dest, dest_filename) < 0) {
1266 FILE_OP_ERROR(dest_filename, "chmod");
1267 g_warning("can't change file mode\n");
1270 if (!mbox_write_data(src, dest, dest_filename, size)) {
1271 mbox_unlock_file(src, mbox_path);
1274 unlink(dest_filename);
1282 FILE_OP_ERROR(mbox_path, "fread");
1286 mbox_cache_msg_set_fetched(mbox_path, msgnum);
1288 if (fclose(dest) == -1) {
1289 FILE_OP_ERROR(dest_filename, "fclose");
1293 mbox_unlock_file(src, mbox_path);
1295 if (fclose(src) == -1) {
1296 FILE_OP_ERROR(mbox_path, "fclose");
1303 unlink(dest_filename);
1310 gchar *mbox_fetch_msg(Folder *folder, FolderItem *item, gint num)
1315 g_return_val_if_fail(item != NULL, NULL);
1317 path = folder_item_get_path(item);
1318 if (!is_dir_exist(path))
1319 make_dir_hier(path);
1321 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(num), NULL);
1325 if (!mbox_extract_msg(item, num, filename)) {
1333 gint mbox_add_msg(Folder *folder, FolderItem *dest, const gchar *file,
1343 gchar from_line[MSGBUFSIZE];
1345 if (dest->last_num < 0) {
1346 mbox_scan_folder(folder, dest);
1347 if (dest->last_num < 0) return -1;
1350 src_fp = fopen(file, "rb");
1351 if (src_fp == NULL) {
1355 mbox_path = mbox_folder_get_path(folder, dest);
1356 if (mbox_path == NULL)
1359 dest_fp = fopen(mbox_path, "ab");
1360 if (dest_fp == NULL) {
1366 if (change_file_mode_rw(dest_fp, mbox_path) < 0) {
1367 FILE_OP_ERROR(mbox_path, "chmod");
1368 g_warning("can't change file mode\n");
1371 old_size = ftell(dest_fp);
1373 mbox_lockwrite_file(dest_fp, mbox_path);
1375 if (fgets(from_line, sizeof(from_line), src_fp) == NULL) {
1376 mbox_unlock_file(dest_fp, mbox_path);
1377 g_warning("unvalid file - %s.\n", file);
1384 if (strncmp(from_line, "From ", 5) != 0) {
1387 if (stat(file, &s) < 0) {
1388 mbox_unlock_file(dest_fp, mbox_path);
1389 g_warning("invalid file - %s.\n", file);
1396 fprintf(dest_fp, "From - %s", ctime(&s.st_mtime));
1399 fputs(from_line, dest_fp);
1402 n_read = fread(buf, 1, sizeof(buf), src_fp);
1403 if ((n_read < (gint) sizeof(buf)) && ferror(src_fp))
1405 if (fwrite(buf, n_read, 1, dest_fp) < 1) {
1406 mbox_unlock_file(dest_fp, mbox_path);
1407 g_warning("writing to %s failed.\n", mbox_path);
1408 ftruncate(fileno(dest_fp), old_size);
1415 if (n_read < (gint) sizeof(buf))
1421 if (ferror(src_fp)) {
1422 FILE_OP_ERROR(mbox_path, "fread");
1425 mbox_unlock_file(dest_fp, mbox_path);
1427 if (fclose(src_fp) == -1) {
1428 FILE_OP_ERROR(file, "fclose");
1432 if (fclose(dest_fp) == -1) {
1433 FILE_OP_ERROR(mbox_path, "fclose");
1439 ftruncate(fileno(dest_fp), old_size);
1447 return dest->last_num;
1451 gint mbox_remove_msg(Folder *folder, FolderItem *item, gint num)
1453 struct _message * msg;
1456 mbox_path = mbox_folder_get_path(folder, item);
1457 if (mbox_path == NULL)
1460 mbox_cache_synchronize(mbox_path, TRUE);
1462 msg = mbox_cache_get_msg(mbox_path, num);
1467 MSG_SET_PERM_FLAGS(msg->flags, MSG_REALLY_DELETED);
1472 gint mbox_remove_all_msg(Folder *folder, FolderItem *item)
1477 mbox_path = mbox_folder_get_path(folder, item);
1478 if (mbox_path == NULL)
1481 fp = fopen(mbox_path, "wb");
1495 gint mbox_move_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1500 filename = mbox_fetch_msg(folder, msginfo->folder, msginfo->msgnum);
1501 if (filename == NULL)
1504 msgnum = mbox_add_msg(folder, dest, filename, TRUE);
1507 MSG_SET_FLAGS(msginfo->flags, MSG_REALLY_DELETED);
1508 mbox_change_flags(folder, msginfo->folder, msginfo);
1514 gint mbox_move_msgs_with_dest(Folder *folder, FolderItem *dest, GSList *msglist)
1517 gchar * mbox_path = NULL;
1519 for(l = msglist ; l != NULL ; l = g_slist_next(l)) {
1520 MsgInfo * msginfo = (MsgInfo *) l->data;
1522 if (msginfo->folder && mbox_path == NULL)
1523 mbox_path = mbox_folder_get_path(msginfo->folder);
1525 mbox_move_msg(folder, dest, msginfo);
1529 mbox_cache_synchronize(mbox_path);
1533 mbox_path = mbox_folder_get_path(dest);
1534 mbox_cache_synchronize(mbox_path);
1537 return dest->last_num;
1542 gint mbox_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1547 filename = mbox_fetch_msg(folder, msginfo->folder, msginfo->msgnum);
1548 if (filename == NULL)
1551 msgnum = mbox_add_msg(folder, dest, filename, FALSE);
1556 gint mbox_copy_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_copy_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;
1583 struct _copy_flags_info
1589 typedef struct _copy_flags_info CopyFlagsInfo;
1591 GSList * copy_flags_data = NULL;
1593 gint mbox_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1595 Folder * src_folder;
1598 CopyFlagsInfo * flags_info;
1600 src_folder = msginfo->folder->folder;
1603 mbox_path = mbox_folder_get_path(msginfo->folder);
1604 mbox_rewrite(mbox_path);
1608 filename = folder_item_fetch_msg(msginfo->folder,
1610 if (filename == NULL)
1613 num = mbox_add_msg(folder, dest, filename, FALSE);
1616 mbox_path = mbox_folder_get_path(dest);
1617 msg = mbox_cache_get_msg(mbox_path, num);
1619 msg->flags = msginfo->flags;
1626 flags_info = g_new0(CopyFlagsInfo, 1);
1627 flags_info->num = num;
1628 flags_info->flags = msginfo->flags;
1629 copy_flags_data = g_slist_append(copy_flags_data, flags_info);
1634 void mbox_scan_folder(Folder *folder, FolderItem *item)
1641 mbox_path = mbox_folder_get_path(folder, item);
1642 if (mbox_path == NULL)
1645 mbox_cache_synchronize(mbox_path, TRUE);
1647 cached = mbox_cache_get_mbox(mbox_path);
1649 if (cached == NULL) {
1651 item->unread_msgs = 0;
1652 item->total_msgs = 0;
1658 n_msg = mbox_cache_get_count(mbox_path);
1661 item->new_msgs = item->unread_msgs = item->total_msgs = 0;
1668 for(l = mbox_cache_get_msg_list(mbox_path) ; l != NULL ;
1669 l = g_list_next(l)) {
1670 struct _message * msg = (struct _message *) l->data;
1671 if (!MSG_IS_REALLY_DELETED(msg->flags))
1673 if (MSG_IS_NEW(msg->flags) && !MSG_IS_IGNORE_THREAD(msg->flags))
1675 if (MSG_IS_UNREAD(msg->flags) && !MSG_IS_IGNORE_THREAD(msg->flags))
1679 item->new_msgs = new;
1680 item->unread_msgs = unread;
1681 item->total_msgs = total;
1684 debug_print("Last number in dir %s = %d\n", mbox_path,
1686 item->last_num = n_msg;
1690 gchar * mbox_get_virtual_path(FolderItem * item)
1695 if (item->parent == NULL) {
1699 gchar * parent_path;
1700 gchar * result_path;
1702 parent_path = mbox_get_virtual_path(item->parent);
1703 if (parent_path == NULL)
1704 result_path = g_strdup(item->name);
1706 result_path = g_strconcat(parent_path,
1709 g_free(parent_path);
1715 static gboolean mbox_write_data(FILE * mbox_fp, FILE * new_fp,
1716 gchar * new_filename, gint size)
1724 while (pos < size) {
1725 if ((size - pos) > (gint) sizeof(buf))
1730 n_read = fread(buf, 1, max, mbox_fp);
1732 if (n_read < max && ferror(mbox_fp)) {
1735 if (fwrite(buf, n_read, 1, new_fp) < 1) {
1736 g_warning("writing to %s failed.\n", new_filename);
1749 static gboolean mbox_write_message(FILE * mbox_fp, FILE * new_fp,
1750 gchar * new_filename,
1751 struct _message * msg)
1754 GPtrArray * headers;
1757 fseek(mbox_fp, msg->header, SEEK_SET);
1759 headers = procheader_get_header_array_asis(mbox_fp);
1761 for (i = 0; i < (gint) headers->len; i++) {
1762 Header * h = g_ptr_array_index(headers, i);
1764 if (!procheader_headername_equal(h->name,
1766 !procheader_headername_equal(h->name,
1768 fwrite(h->name, strlen(h->name),
1770 if (h->name[strlen(h->name) - 1] != ' ')
1771 fwrite(" ", 1, 1, new_fp);
1772 fwrite(h->body, strlen(h->body),
1774 fwrite("\n", 1, 1, new_fp);
1776 procheader_header_free(h);
1777 g_ptr_array_remove_index(headers, i);
1781 g_ptr_array_free(headers, FALSE);
1783 if (!MSG_IS_INVALID(msg->flags)) {
1785 fwrite("Status: ", strlen("Status: "), 1, new_fp);
1786 if (!MSG_IS_UNREAD(msg->flags))
1787 fwrite("R", 1, 1, new_fp);
1788 fwrite("O", 1, 1, new_fp);
1789 fwrite("\n", 1, 1, new_fp);
1791 /* X-Status header */
1792 if (MSG_IS_REALLY_DELETED(msg->flags)
1793 || MSG_IS_MARKED(msg->flags)
1794 || MSG_IS_DELETED(msg->flags)
1795 || MSG_IS_REPLIED(msg->flags)
1796 || MSG_IS_FORWARDED(msg->flags)) {
1797 fwrite("X-Status: ", strlen("X-Status: "), 1, new_fp);
1798 if (MSG_IS_REALLY_DELETED(msg->flags))
1799 fwrite("D", 1, 1, new_fp); /* really deleted */
1801 if (MSG_IS_MARKED(msg->flags))
1802 fwrite("F", 1, 1, new_fp);
1803 if (MSG_IS_DELETED(msg->flags))
1804 fwrite("d", 1, 1, new_fp);
1805 if (MSG_IS_REPLIED(msg->flags))
1806 fwrite("r", 1, 1, new_fp);
1807 if (MSG_IS_FORWARDED(msg->flags))
1808 fwrite("f", 1, 1, new_fp);
1810 fwrite("\n", 1, 1, new_fp);
1814 fwrite("\n", 1, 1, new_fp);
1816 size = msg->end - msg->content;
1817 fseek(mbox_fp, msg->content, SEEK_SET);
1819 return mbox_write_data(mbox_fp, new_fp, new_filename, size);
1822 void mbox_change_flags(Folder * folder, FolderItem * item, MsgInfo * info, MsgPermFlags newflags)
1824 struct _message * msg;
1828 mbox_path = mbox_folder_get_path(folder, item);
1829 if (mbox_path == NULL)
1832 info->flags.perm_flags = newflags;
1834 msg = mbox_cache_get_msg(mbox_path, info->msgnum);
1836 cache = mbox_cache_get_mbox(mbox_path);
1840 if ((msg == NULL) || (cache == NULL))
1843 msg->flags = info->flags;
1845 cache->modification = TRUE;
1849 static gboolean mbox_purge_deleted(gchar * mbox)
1856 gboolean modification = FALSE;
1860 mbox_cache_synchronize(mbox, TRUE);
1862 msg_list = mbox_cache_get_msg_list(mbox);
1864 for(l = msg_list ; l != NULL ; l = g_list_next(l)) {
1865 struct _message * msg = (struct _message *) l->data;
1866 if (!MSG_IS_INVALID(msg->flags) && MSG_IS_REALLY_DELETED(msg->flags)) {
1867 modification = TRUE;
1872 if (!modification) {
1873 debug_print("no deleted messages - %s\n", mbox);
1877 debug_print("purge deleted messages - %s\n", mbox);
1879 mbox_fp = fopen(mbox, "rb+");
1880 mbox_lockwrite_file(mbox_fp, mbox);
1882 mbox_cache_synchronize_from_file(mbox_fp, mbox, TRUE);
1884 new = g_strconcat(mbox, ".", itos((int) mbox), NULL);
1885 new_fp = fopen(new, "wb");
1887 if (change_file_mode_rw(new_fp, new) < 0) {
1888 FILE_OP_ERROR(new, "chmod");
1889 g_warning("can't change file mode\n");
1892 mbox_lockwrite_file(new_fp, new);
1897 msg_list = mbox_cache_get_msg_list(mbox);
1898 for(l = msg_list ; l != NULL ; l = g_list_next(l)) {
1899 struct _message * msg = (struct _message *) l->data;
1900 if (MSG_IS_INVALID(msg->flags) || !MSG_IS_REALLY_DELETED(msg->flags)) {
1901 if (!mbox_write_message(mbox_fp, new_fp, new, msg)) {
1911 if (rename(new, mbox) == -1) {
1912 g_warning("can't rename %s to %s\n", new, mbox);
1913 mbox_unlock_file(new_fp, new);
1915 mbox_unlock_file(mbox_fp, mbox);
1921 if (change_file_mode_rw(new_fp, mbox) < 0) {
1922 FILE_OP_ERROR(new, "chmod");
1923 g_warning("can't change file mode\n");
1926 mbox_unlock_file(new_fp, new);
1930 mbox_unlock_file(mbox_fp, mbox);
1934 debug_print("%i messages written - %s\n", count, mbox);
1936 mbox_cache_synchronize(mbox, FALSE);
1941 #define MAKE_DIR_IF_NOT_EXIST(dir) \
1943 if (!is_dir_exist(dir)) { \
1944 if (is_file_exist(dir)) { \
1945 g_warning("File `%s' already exists.\n" \
1946 "Can't create folder.", dir); \
1949 if (mkdir(dir, S_IRWXU) < 0) { \
1950 FILE_OP_ERROR(dir, "mkdir"); \
1953 if (chmod(dir, S_IRWXU) < 0) \
1954 FILE_OP_ERROR(dir, "chmod"); \
1958 gint mbox_create_tree(Folder *folder)
1962 g_return_val_if_fail(folder != NULL, -1);
1964 CHDIR_RETURN_VAL_IF_FAIL(get_home_dir(), -1);
1965 rootpath = LOCAL_FOLDER(folder)->rootpath;
1966 MAKE_DIR_IF_NOT_EXIST(rootpath);
1967 CHDIR_RETURN_VAL_IF_FAIL(rootpath, -1);
1972 #undef MAKE_DIR_IF_NOT_EXIST
1974 static gchar * mbox_get_new_path(FolderItem * parent, gchar * name)
1978 if (strchr(name, '/') == NULL) {
1979 if (parent->path != NULL)
1980 path = g_strconcat(parent->path, ".sbd", G_DIR_SEPARATOR_S, name, NULL);
1982 path = g_strdup(name);
1985 path = g_strdup(name);
1990 static gchar * mbox_get_folderitem_name(gchar * name)
1994 foldername = g_strdup(g_basename(name));
1999 FolderItem *mbox_create_folder(Folder *folder, FolderItem *parent,
2003 FolderItem *new_item;
2006 g_return_val_if_fail(folder != NULL, NULL);
2007 g_return_val_if_fail(parent != NULL, NULL);
2008 g_return_val_if_fail(name != NULL, NULL);
2010 path = mbox_get_new_path(parent, (gchar *) name);
2012 foldername = mbox_get_folderitem_name((gchar *) name);
2014 new_item = folder_item_new(folder, foldername, path);
2015 folder_item_append(parent, new_item);
2017 if (!strcmp(name, "inbox")) {
2018 new_item->stype = F_INBOX;
2019 new_item->folder->inbox = new_item;
2020 } else if (!strcmp(name, "outbox")) {
2021 new_item->stype = F_OUTBOX;
2022 new_item->folder->outbox = new_item;
2023 } else if (!strcmp(name, "draft")) {
2024 new_item->stype = F_DRAFT;
2025 new_item->folder->draft = new_item;
2026 } else if (!strcmp(name, "queue")) {
2027 new_item->stype = F_QUEUE;
2028 new_item->folder->queue = new_item;
2029 } else if (!strcmp(name, "trash")) {
2030 new_item->stype = F_TRASH;
2031 new_item->folder->trash = new_item;
2040 gint mbox_rename_folder(Folder *folder, FolderItem *item, const gchar *name)
2045 g_return_val_if_fail(folder != NULL, -1);
2046 g_return_val_if_fail(item != NULL, -1);
2047 g_return_val_if_fail(item->path != NULL, -1);
2048 g_return_val_if_fail(name != NULL, -1);
2050 path = mbox_get_new_path(item->parent, (gchar *) name);
2051 foldername = mbox_get_folderitem_name((gchar *) name);
2053 if (rename(item->path, path) == -1) {
2056 g_warning("Cannot rename folder item");
2064 item->name = foldername;
2070 gint mbox_remove_folder(Folder *folder, FolderItem *item)
2072 g_return_val_if_fail(folder != NULL, -1);
2073 g_return_val_if_fail(item != NULL, -1);
2074 g_return_val_if_fail(item->path != NULL, -1);
2076 folder_item_remove(item);
2080 gint mbox_get_num_list(Folder *folder, FolderItem *item, GSList **mlist)
2087 mbox_path = mbox_folder_get_path(folder, item);
2089 if (mbox_path == NULL)
2092 mbox_purge_deleted(mbox_path);
2094 fp = fopen(mbox_path, "rb");
2101 mbox_lockread_file(fp, mbox_path);
2103 mbox_cache_synchronize_from_file(fp, mbox_path, TRUE);
2105 item->last_num = mbox_cache_get_count(mbox_path);
2107 for(l = mbox_cache_get_msg_list(mbox_path) ; l != NULL ;
2108 l = g_list_next(l)) {
2109 struct _message * msg;
2111 msg = (struct _message *) l->data;
2113 if (MSG_IS_INVALID(msg->flags) || !MSG_IS_REALLY_DELETED(msg->flags)) {
2114 *mlist = g_slist_append(*mlist, GINT_TO_POINTER(msg->msgnum));
2117 MSG_SET_PERM_FLAGS(msg->flags, MSG_REALLY_DELETED);
2121 mbox_unlock_file(fp, mbox_path);
2130 MsgInfo *mbox_get_msginfo(Folder *folder, FolderItem *item, gint num)
2133 struct _message *msg;
2137 g_return_val_if_fail(folder != NULL, NULL);
2138 g_return_val_if_fail(item != NULL, NULL);
2140 mbox_path = mbox_folder_get_path(folder, item);
2142 g_return_val_if_fail(mbox_path != NULL, NULL);
2144 src = fopen(mbox_path, "rb");
2149 mbox_lockread_file(src, mbox_path);
2150 mbox_cache_synchronize_from_file(src, mbox_path, TRUE);
2152 msg = mbox_cache_get_msg(mbox_path, num);
2154 mbox_unlock_file(src, mbox_path);
2160 fseek(src, msg->header, SEEK_SET);
2161 msginfo = mbox_parse_msg(src, msg, item);
2163 mbox_unlock_file(src, mbox_path);
2170 gboolean mbox_check_msgnum_validity(Folder *folder, FolderItem *item)
2172 mboxcache * old_cache;
2173 gboolean scan_new = TRUE;
2177 filename = mbox_folder_get_path(folder, item);
2179 old_cache = mbox_cache_get_mbox(filename);
2181 if (old_cache != NULL) {
2182 if (stat(filename, &s) < 0) {
2183 FILE_OP_ERROR(filename, "stat");
2184 } else if (old_cache->mtime == s.st_mtime) {
2185 debug_print("Folder is not modified.\n");