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 */
98 mbox_check_msgnum_validity,
100 /* Message functions */
114 FolderClass *mbox_get_class(void)
119 Folder *mbox_folder_new(const gchar *name, const gchar *path)
123 folder = (Folder *)g_new0(MBOXFolder, 1);
124 folder->klass = &mbox_class;
125 mbox_folder_init(folder, name, path);
130 void mbox_folder_destroy(Folder *folder)
132 folder_local_folder_destroy(LOCAL_FOLDER(folder));
135 static void mbox_folder_init(Folder *folder, const gchar *name, const gchar *path)
137 folder_local_folder_init(folder, name, path);
140 static void mbox_folder_create_parent(const gchar * path)
142 if (!is_file_exist(path)) {
145 new_path = g_dirname(path);
146 if (new_path[strlen(new_path) - 1] == G_DIR_SEPARATOR)
147 new_path[strlen(new_path) - 1] = '\0';
149 if (!is_dir_exist(new_path))
150 make_dir_hier(new_path);
157 gchar *mbox_folder_get_path(Folder *folder, FolderItem *item)
162 g_return_val_if_fail(item != NULL, NULL);
164 if (item->path && item->path[0] == G_DIR_SEPARATOR) {
165 mbox_folder_create_parent(item->path);
166 return g_strdup(item->path);
169 folder_path = g_strdup(LOCAL_FOLDER(item->folder)->rootpath);
170 g_return_val_if_fail(folder_path != NULL, NULL);
172 if (folder_path[0] == G_DIR_SEPARATOR) {
174 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
178 path = g_strdup(folder_path);
181 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
182 folder_path, G_DIR_SEPARATOR_S,
185 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
191 mbox_folder_create_parent(path);
197 /**********************************************************/
201 /**********************************************************/
204 static GSList * file_lock = NULL;
206 static gboolean mbox_file_lock_file(gchar * base)
208 gchar *lockfile, *locklink;
212 lockfile = g_strdup_printf("%s.%d", base, getpid());
213 if ((lockfp = fopen(lockfile, "wb")) == NULL) {
214 FILE_OP_ERROR(lockfile, "fopen");
215 g_warning("can't create lock file %s\n", lockfile);
216 g_warning("use 'flock' instead of 'file' if possible.\n");
221 fprintf(lockfp, "%d\n", getpid());
224 locklink = g_strconcat(base, ".lock", NULL);
225 while (link(lockfile, locklink) < 0) {
226 FILE_OP_ERROR(lockfile, "link");
228 g_warning("can't create %s\n", lockfile);
235 g_warning("mailbox is owned by another"
236 " process, waiting...\n");
247 static gboolean mbox_fcntl_lockwrite_file(FILE * fp)
251 lck.l_type = F_WRLCK;
256 if (fcntl(fileno(fp), F_SETLK, &lck) < 0)
262 static gboolean mbox_fcntl_lockread_file(FILE * fp)
266 lck.l_type = F_RDLCK;
271 if (fcntl(fileno(fp), F_SETLK, &lck) < 0)
277 static gboolean mbox_fcntl_unlock_file(FILE * fp)
281 lck.l_type = F_UNLCK;
286 if (fcntl(fileno(fp), F_SETLK, &lck) < 0)
292 static gboolean mbox_file_unlock_file(gchar * base)
296 lockfile = g_strdup_printf("%s.lock", base);
303 static gboolean mbox_lockread_file(FILE * fp, gchar * base)
307 result = mbox_fcntl_lockread_file(fp);
309 if ((result = mbox_file_lock_file(base)) == TRUE) {
310 file_lock = g_slist_append(file_lock, g_strdup(base));
311 debug_print("lockfile lock %s.\n", base);
314 g_warning("could not lock read file %s\n", base);
317 debug_print("fcntl lock %s.\n", base);
322 static gboolean mbox_lockwrite_file(FILE * fp, gchar * base)
326 result = mbox_fcntl_lockwrite_file(fp);
328 if ((result = mbox_file_lock_file(base)) == TRUE) {
329 file_lock = g_slist_append(file_lock, g_strdup(base));
330 debug_print("lockfile lock %s.\n", base);
333 g_warning("could not lock write file %s\n", base);
336 debug_print("fcntl lock %s.\n", base);
341 static gboolean mbox_unlock_file(FILE * fp, gchar * base)
343 gboolean result = FALSE;
345 gboolean unlocked = FALSE;
347 for(l = file_lock ; l != NULL ; l = g_slist_next(l)) {
348 gchar * data = l->data;
350 if (strcmp(data, base) == 0) {
351 file_lock = g_slist_remove(file_lock, data);
353 result = mbox_file_unlock_file(base);
355 debug_print("lockfile unlock - %s.\n", base);
361 result = mbox_fcntl_unlock_file(fp);
362 debug_print("fcntl unlock - %s.\n", base);
368 /**********************************************************/
372 /**********************************************************/
374 #define MAILFILE_ERROR_NO_ERROR 0x000
375 #define MAILFILE_ERROR_FILE_NOT_FOUND 0x001
376 #define MAILFILE_ERROR_MEMORY 0x002
377 #define MAILFILE_ERROR_MESSAGE_NOT_FOUND 0x003
379 static int mailfile_error = MAILFILE_ERROR_NO_ERROR;
381 #define STATE_BEGIN 0x000
382 #define STATE_TEXT_READ 0x001
383 #define STATE_FROM_READ 0x002
384 #define STATE_FIELD_READ 0x003
385 #define STATE_END 0x004
386 #define STATE_END_OF_FILE 0x005
387 #define STATE_MEM_ERROR 0x006
388 #define STATE_TEXT_BEGIN 0x007
390 #define STATE_MASK 0x0FF /* filter state from functions */
392 #define STATE_RESTORE_POS 0x100 /* go back while reading */
394 typedef struct _mailfile mailfile;
418 #define MSG_IS_INVALID(msg) \
419 ((msg).perm_flags == (msg).tmp_flags && (msg).tmp_flags == -1)
421 #define MSG_SET_INVALID(msg) \
422 ((msg).perm_flags = (msg).tmp_flags = -1)
424 static int startFrom(char * s)
426 return (strncmp(s, "From ", 5) == 0);
429 static int startSpace(char * s)
431 return ((*s == ' ') || (*s == '\t'));
434 static int startEmpty(char * s)
439 static void free_msg_list(GList * l)
441 GList * elt = g_list_first(l);
446 elt = g_list_next(elt);
453 static mailfile * mailfile_init_from_file(FILE * f, gchar * filename)
456 GList * msg_list = NULL;
463 struct _message * data = NULL;
469 while (state != STATE_END_OF_FILE) {
470 if ((state & STATE_RESTORE_POS) == 0) {
471 former_pos = lastpos;
474 r = fgets(s, 256, f);
477 ignore_next = (s[strlen(s) - 1] != '\n');
482 switch(state & 0x0F) {
485 state = STATE_END_OF_FILE;
486 else if (startFrom(s)) {
487 state = STATE_FROM_READ;
489 data = g_new0(struct _message, 1);
491 free_msg_list(msg_list);
496 data->msgnum = msgnum;
497 data->offset = lastpos;
498 data->header = lastpos;
501 data->messageid = NULL;
502 data->fromspace = NULL;
503 MSG_SET_INVALID(data->flags);
504 MSG_SET_INVALID(data->old_flags);
505 data->fetched = FALSE;
506 msg_list = g_list_append(msg_list,
514 case STATE_TEXT_READ:
517 else if (startFrom(s))
518 state = STATE_END | STATE_RESTORE_POS;
520 state = STATE_TEXT_READ;
523 case STATE_TEXT_BEGIN:
524 data->content = lastpos;
527 else if (startFrom(s)) {
528 state = STATE_END | STATE_RESTORE_POS;
531 state = STATE_TEXT_READ;
535 case STATE_FROM_READ:
536 data->content = lastpos;
539 else if (startSpace(s))
540 state = STATE_FROM_READ;
541 else if (startEmpty(s))
542 state = STATE_TEXT_READ;
544 state = STATE_FIELD_READ;
547 case STATE_FIELD_READ:
548 data->content = lastpos;
551 else if (startSpace(s))
552 state = STATE_FIELD_READ;
553 else if (startEmpty(s)) {
554 state = STATE_TEXT_BEGIN;
557 state = STATE_FIELD_READ;
561 if ((state & STATE_MASK) == STATE_END) {
562 state = STATE_BEGIN | (state & STATE_RESTORE_POS);
568 r = fgets(s, 256, f);
569 if (r == NULL || *r == '\0')
572 while (s[strlen(s) - 1] != '\n');
576 mf = (mailfile *) g_new0(struct _mailfile, 1);
578 free_msg_list(msg_list);
579 mailfile_error = MAILFILE_ERROR_MEMORY;
583 mf->msg_list = g_list_first(msg_list);
585 mf->filename = g_strdup(filename);
588 mailfile_error = MAILFILE_ERROR_NO_ERROR;
593 static mailfile * mailfile_init(char * filename)
599 f = fopen(filename, "rb");
602 mailfile_error = MAILFILE_ERROR_FILE_NOT_FOUND;
606 mbox_lockread_file(f, filename);
608 mf = mailfile_init_from_file(f, filename);
610 mbox_unlock_file(f, filename);
617 static void mailfile_done(mailfile * f)
619 free_msg_list(f->msg_list);
626 #define MAX_READ 4096
628 static char * readfile(char * filename, int offset, int max_offset)
637 handle = fopen(filename, "rb");
639 if (handle == NULL) {
640 mailfile_error = MAILFILE_ERROR_FILE_NOT_FOUND;
644 size = max_offset - offset;
646 message = (char *) malloc(size + 1);
647 if (message == NULL) {
649 mailfile_error = MAILFILE_ERROR_MEMORY;
653 fseek(handle, offset, SEEK_SET);
657 if ((size - pos) > MAX_READ)
662 bread = fread(message + pos, 1, max, handle);
678 static char * mailfile_readmsg(mailfile f, int index)
684 struct _message * msginfo;
686 nth = g_list_nth(f->msg_list, index);
689 mailfile_error = MAILFILE_ERROR_MESSAGE_NOT_FOUND;
693 msginfo = (struct _message *)nth->data;
695 offset = msginfo->offset;
696 max_offset = msginfo->end;
697 message = readfile(f->filename, offset, max_offset);
699 mailfile_error = MAILFILE_ERROR_NO_ERROR;
704 static char * mailfile_readheader(mailfile f, int index)
710 struct _message * msginfo;
712 nth = g_list_nth(f->msg_list, index);
715 mailfile_error = MAILFILE_ERROR_MESSAGE_NOT_FOUND;
719 msginfo = (struct _message *)nth->data;
721 offset = msginfo->offset;
722 max_offset = msginfo->content;
723 message = readfile(f->filename, offset, max_offset);
725 mailfile_error = MAILFILE_ERROR_NO_ERROR;
730 static int mailfile_count(mailfile * f)
732 return g_list_length(f->msg_list);
735 static int mailfile_find_deleted(mailfile f, char * filename)
739 handle = fopen(filename, "rb");
742 struct _message m = elt->data;
743 n = fread(&m.deleted, sizeof(int), 1, handle);
746 elt = g_list_next(elt);
755 /**********************************************************/
757 /* mbox cache operations */
759 /**********************************************************/
766 gboolean modification;
769 typedef struct _mboxcache mboxcache;
771 static GHashTable * mbox_cache_table = NULL;
773 static MsgInfo *mbox_parse_msg(FILE * fp, struct _message * msg,
777 MsgFlags flags = { 0, 0 };
779 MSG_SET_PERM_FLAGS(flags, MSG_NEW | MSG_UNREAD);
781 g_return_val_if_fail(fp != NULL, NULL);
784 if (item->stype == F_QUEUE) {
785 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
786 } else if (item->stype == F_DRAFT) {
787 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
791 msginfo = procheader_parse_stream(fp, flags, FALSE, FALSE);
793 if (!msginfo) return NULL;
796 msginfo->msgnum = msg->msgnum;
797 msginfo->folder = item;
803 static void mbox_cache_init()
805 mbox_cache_table = g_hash_table_new(g_str_hash, g_str_equal);
808 static void mbox_cache_free_mbox(mboxcache * cache)
810 g_hash_table_remove(mbox_cache_table, cache->filename);
813 mailfile_done(cache->mf);
815 g_ptr_array_free(cache->tab_mf, FALSE);
817 g_free(cache->filename);
821 static void mbox_cache_get_msginfo_from_file(FILE * fp, GList * msg_list)
826 for(l = msg_list ; l != NULL ; l = g_list_next(l)) {
827 struct _message * msg;
829 msg = (struct _message *) l->data;
831 fseek(fp, msg->header, SEEK_SET);
832 msginfo = mbox_parse_msg(fp, msg, NULL);
836 g_strdup(msginfo->msgid);
837 if (msginfo->fromspace)
839 g_strdup(msginfo->fromspace);
840 msg->flags = msginfo->flags;
841 msg->old_flags = msginfo->flags;
843 procmsg_msginfo_free(msginfo);
848 static void mbox_cache_get_msginfo(gchar * filename, GList * msg_list)
852 fp = fopen(filename, "rb");
856 mbox_cache_get_msginfo_from_file(fp, msg_list);
860 static mboxcache * mbox_cache_read_mbox(gchar * filename)
867 if (stat(filename, &s) < 0)
870 mf = mailfile_init(filename);
874 cache = g_new0(mboxcache, 1);
876 cache->mtime = s.st_mtime;
878 cache->filename = g_strdup(filename);
879 cache->modification = FALSE;
881 cache->tab_mf = g_ptr_array_new();
882 for(l = mf->msg_list ; l != NULL ; l = g_list_next(l))
883 g_ptr_array_add(cache->tab_mf, l->data);
885 mbox_cache_get_msginfo(filename, mf->msg_list);
887 debug_print("read mbox - %s\n", filename);
892 static mboxcache * mbox_cache_read_mbox_from_file(FILE * fp, gchar * filename)
899 if (stat(filename, &s) < 0)
902 mf = mailfile_init_from_file(fp, filename);
906 cache = g_new0(mboxcache, 1);
908 cache->mtime = s.st_mtime;
910 cache->filename = g_strdup(filename);
912 cache->tab_mf = g_ptr_array_new();
913 for(l = mf->msg_list ; l != NULL ; l = g_list_next(l))
914 g_ptr_array_add(cache->tab_mf, l->data);
916 mbox_cache_get_msginfo_from_file(fp, mf->msg_list);
918 debug_print("read mbox from file - %s\n", filename);
923 static void mbox_cache_insert_mbox(mboxcache * data)
925 if (mbox_cache_table == NULL)
928 g_hash_table_insert(mbox_cache_table, data->filename, data);
931 static mboxcache * mbox_cache_get_mbox(gchar * filename)
933 if (mbox_cache_table == NULL)
936 return g_hash_table_lookup(mbox_cache_table, filename);
940 static gint mbox_cache_get_count(gchar * filename)
944 cache = mbox_cache_get_mbox(filename);
947 if (cache->mf == NULL)
949 return cache->mf->count;
952 static GList * mbox_cache_get_msg_list(gchar * filename)
956 cache = mbox_cache_get_mbox(filename);
961 if (cache->mf == NULL)
964 return cache->mf->msg_list;
967 static void mbox_cache_synchronize_lists(GList * old_msg_list,
968 GList * new_msg_list)
973 for(l2 = old_msg_list ; l2 != NULL ; l2 = g_list_next(l2)) {
974 struct _message * msg2 = l2->data;
976 if ((msg2->messageid == NULL) ||
977 (msg2->fromspace == NULL))
980 for(l = new_msg_list ; l != NULL ; l = g_list_next(l)) {
981 struct _message * msg = l->data;
983 if ((msg->messageid == NULL) ||
984 (msg->fromspace == NULL))
987 if ((strcmp(msg->messageid, msg2->messageid) == 0) &&
988 (strcmp(msg->fromspace, msg2->fromspace) == 0)) {
989 if (msg2->flags.perm_flags != msg2->old_flags.perm_flags) {
990 msg->flags = msg2->flags;
998 static void mbox_cache_synchronize(gchar * filename, gboolean sync)
1000 mboxcache * new_cache;
1001 mboxcache * old_cache;
1002 gboolean scan_new = TRUE;
1005 old_cache = mbox_cache_get_mbox(filename);
1007 if (old_cache != NULL) {
1008 if (stat(filename, &s) < 0) {
1009 FILE_OP_ERROR(filename, "stat");
1010 } else if (old_cache->mtime == s.st_mtime) {
1011 debug_print("Folder is not modified.\n");
1018 if (strstr(filename, "trash") == 0)
1019 printf("old_cache: %p %s\n", old_cache, filename);
1021 printf("begin old\n");
1022 for(l = old_cache->mf->msg_list ; l != NULL ;
1023 l = g_list_next(l)) {
1024 struct _message * msg = l->data;
1025 printf("%p\n", msg);
1027 printf("end old\n");
1031 new_cache = mbox_cache_read_mbox(filename);
1034 if (strstr(filename, "trash") == 0)
1035 printf("new_cache: %p %s\n", new_cache, filename);
1037 printf("begin new\n");
1038 for(l = new_cache->mf->msg_list ; l != NULL ;
1039 l = g_list_next(l)) {
1040 struct _message * msg = l->data;
1041 printf("%p\n", msg);
1043 printf("end new\n");
1050 if (sync && new_cache && old_cache)
1051 mbox_cache_synchronize_lists(old_cache->mf->msg_list,
1052 new_cache->mf->msg_list);
1054 if (old_cache != NULL)
1055 mbox_cache_free_mbox(old_cache);
1058 mbox_cache_insert_mbox(new_cache);
1060 printf("insert %p %s\n", new_cache, new_cache->filename);
1061 printf("inserted %s %p\n", filename,
1062 mbox_cache_get_mbox(filename));
1068 static void mbox_cache_synchronize_from_file(FILE * fp, gchar * filename,
1071 mboxcache * new_cache;
1072 mboxcache * old_cache;
1073 gboolean scan_new = TRUE;
1076 old_cache = mbox_cache_get_mbox(filename);
1078 if (old_cache != NULL) {
1079 if (stat(filename, &s) < 0) {
1080 FILE_OP_ERROR(filename, "stat");
1081 } else if (old_cache->mtime == s.st_mtime) {
1082 debug_print("Folder is not modified.\n");
1093 if (strstr(filename, "trash") == 0)
1094 printf("old_cache: %p %s\n", old_cache, filename);
1097 printf("begin old\n");
1098 for(l = old_cache->mf->msg_list ; l != NULL ;
1099 l = g_list_next(l)) {
1100 struct _message * msg = l->data;
1101 printf("%p\n", msg);
1103 printf("end old\n");
1107 new_cache = mbox_cache_read_mbox_from_file(fp, filename);
1110 if (strstr(filename, "trash") == 0)
1111 printf("new_cache: %p %s\n", new_cache, filename);
1114 printf("begin new\n");
1115 for(l = new_cache->mf->msg_list ; l != NULL ;
1116 l = g_list_next(l)) {
1117 struct _message * msg = l->data;
1118 printf("%p\n", msg);
1120 printf("end new\n");
1127 if (sync && new_cache && old_cache)
1128 mbox_cache_synchronize_lists(old_cache->mf->msg_list,
1129 new_cache->mf->msg_list);
1131 if (old_cache != NULL)
1132 mbox_cache_free_mbox(old_cache);
1135 mbox_cache_insert_mbox(new_cache);
1137 printf("insert %p %s\n", new_cache, new_cache->filename);
1138 printf("inserted %s %p\n", filename,
1139 mbox_cache_get_mbox(filename));
1145 gboolean mbox_cache_msg_fetched(gchar * filename, gint num)
1147 struct _message * msg;
1150 cache = mbox_cache_get_mbox(filename);
1155 msg = (struct _message *) g_ptr_array_index(cache->tab_mf,
1160 return msg->fetched;
1163 void mbox_cache_msg_set_fetched(gchar * filename, gint num)
1165 struct _message * msg;
1168 cache = mbox_cache_get_mbox(filename);
1173 msg = (struct _message *) g_ptr_array_index(cache->tab_mf,
1178 msg->fetched = TRUE;
1181 struct _message * mbox_cache_get_msg(gchar * filename, gint num)
1185 cache = mbox_cache_get_mbox(filename);
1187 if (cache == NULL) {
1191 return (struct _message *) g_ptr_array_index(cache->tab_mf,
1196 /**********************************************************/
1198 /* mbox operations */
1200 /**********************************************************/
1202 static gboolean mbox_extract_msg(FolderItem * item, gint msgnum,
1203 gchar * dest_filename)
1205 struct _message * msg;
1212 /* GList * msg_list;*/
1213 gboolean already_fetched;
1216 mbox_path = mbox_folder_get_path(item->folder, item);
1218 if (mbox_path == NULL)
1221 src = fopen(mbox_path, "rb");
1227 mbox_lockread_file(src, mbox_path);
1229 mbox_cache_synchronize_from_file(src, mbox_path, TRUE);
1231 already_fetched = mbox_cache_msg_fetched(mbox_path, msgnum);
1233 if (already_fetched) {
1234 mbox_unlock_file(src, mbox_path);
1240 msg = mbox_cache_get_msg(mbox_path, msgnum);
1243 mbox_unlock_file(src, mbox_path);
1249 offset = msg->offset;
1250 max_offset = msg->end;
1252 size = max_offset - offset;
1254 fseek(src, offset, SEEK_SET);
1256 dest = fopen(dest_filename, "wb");
1258 mbox_unlock_file(src, mbox_path);
1264 if (change_file_mode_rw(dest, dest_filename) < 0) {
1265 FILE_OP_ERROR(dest_filename, "chmod");
1266 g_warning("can't change file mode\n");
1269 if (!mbox_write_data(src, dest, dest_filename, size)) {
1270 mbox_unlock_file(src, mbox_path);
1273 unlink(dest_filename);
1281 FILE_OP_ERROR(mbox_path, "fread");
1285 mbox_cache_msg_set_fetched(mbox_path, msgnum);
1287 if (fclose(dest) == -1) {
1288 FILE_OP_ERROR(dest_filename, "fclose");
1292 mbox_unlock_file(src, mbox_path);
1294 if (fclose(src) == -1) {
1295 FILE_OP_ERROR(mbox_path, "fclose");
1302 unlink(dest_filename);
1309 gchar *mbox_fetch_msg(Folder *folder, FolderItem *item, gint num)
1314 g_return_val_if_fail(item != NULL, NULL);
1316 path = folder_item_get_path(item);
1317 if (!is_dir_exist(path))
1318 make_dir_hier(path);
1320 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(num), NULL);
1324 if (!mbox_extract_msg(item, num, filename)) {
1332 gint mbox_add_msg(Folder *folder, FolderItem *dest, const gchar *file,
1342 gchar from_line[MSGBUFSIZE];
1344 if (dest->last_num < 0) {
1345 mbox_scan_folder(folder, dest);
1346 if (dest->last_num < 0) return -1;
1349 src_fp = fopen(file, "rb");
1350 if (src_fp == NULL) {
1354 mbox_path = mbox_folder_get_path(folder, dest);
1355 if (mbox_path == NULL)
1358 dest_fp = fopen(mbox_path, "ab");
1359 if (dest_fp == NULL) {
1365 if (change_file_mode_rw(dest_fp, mbox_path) < 0) {
1366 FILE_OP_ERROR(mbox_path, "chmod");
1367 g_warning("can't change file mode\n");
1370 old_size = ftell(dest_fp);
1372 mbox_lockwrite_file(dest_fp, mbox_path);
1374 if (fgets(from_line, sizeof(from_line), src_fp) == NULL) {
1375 mbox_unlock_file(dest_fp, mbox_path);
1376 g_warning("unvalid file - %s.\n", file);
1383 if (strncmp(from_line, "From ", 5) != 0) {
1386 if (stat(file, &s) < 0) {
1387 mbox_unlock_file(dest_fp, mbox_path);
1388 g_warning("invalid file - %s.\n", file);
1395 fprintf(dest_fp, "From - %s", ctime(&s.st_mtime));
1398 fputs(from_line, dest_fp);
1401 n_read = fread(buf, 1, sizeof(buf), src_fp);
1402 if ((n_read < (gint) sizeof(buf)) && ferror(src_fp))
1404 if (fwrite(buf, n_read, 1, dest_fp) < 1) {
1405 mbox_unlock_file(dest_fp, mbox_path);
1406 g_warning("writing to %s failed.\n", mbox_path);
1407 ftruncate(fileno(dest_fp), old_size);
1414 if (n_read < (gint) sizeof(buf))
1420 if (ferror(src_fp)) {
1421 FILE_OP_ERROR(mbox_path, "fread");
1424 mbox_unlock_file(dest_fp, mbox_path);
1426 if (fclose(src_fp) == -1) {
1427 FILE_OP_ERROR(file, "fclose");
1431 if (fclose(dest_fp) == -1) {
1432 FILE_OP_ERROR(mbox_path, "fclose");
1438 ftruncate(fileno(dest_fp), old_size);
1446 return dest->last_num;
1450 gint mbox_remove_msg(Folder *folder, FolderItem *item, gint num)
1452 struct _message * msg;
1455 mbox_path = mbox_folder_get_path(folder, item);
1456 if (mbox_path == NULL)
1459 mbox_cache_synchronize(mbox_path, TRUE);
1461 msg = mbox_cache_get_msg(mbox_path, num);
1466 MSG_SET_PERM_FLAGS(msg->flags, MSG_REALLY_DELETED);
1471 gint mbox_remove_all_msg(Folder *folder, FolderItem *item)
1476 mbox_path = mbox_folder_get_path(folder, item);
1477 if (mbox_path == NULL)
1480 fp = fopen(mbox_path, "wb");
1494 gint mbox_move_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1499 filename = mbox_fetch_msg(folder, msginfo->folder, msginfo->msgnum);
1500 if (filename == NULL)
1503 msgnum = mbox_add_msg(folder, dest, filename, TRUE);
1506 MSG_SET_FLAGS(msginfo->flags, MSG_REALLY_DELETED);
1507 mbox_change_flags(folder, msginfo->folder, msginfo);
1513 gint mbox_move_msgs_with_dest(Folder *folder, FolderItem *dest, GSList *msglist)
1516 gchar * mbox_path = NULL;
1518 for(l = msglist ; l != NULL ; l = g_slist_next(l)) {
1519 MsgInfo * msginfo = (MsgInfo *) l->data;
1521 if (msginfo->folder && mbox_path == NULL)
1522 mbox_path = mbox_folder_get_path(msginfo->folder);
1524 mbox_move_msg(folder, dest, msginfo);
1528 mbox_cache_synchronize(mbox_path);
1532 mbox_path = mbox_folder_get_path(dest);
1533 mbox_cache_synchronize(mbox_path);
1536 return dest->last_num;
1541 gint mbox_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1546 filename = mbox_fetch_msg(folder, msginfo->folder, msginfo->msgnum);
1547 if (filename == NULL)
1550 msgnum = mbox_add_msg(folder, dest, filename, FALSE);
1555 gint mbox_copy_msgs_with_dest(Folder *folder, FolderItem *dest, GSList *msglist)
1558 gchar * mbox_path = NULL;
1560 for(l = msglist ; l != NULL ; l = g_slist_next(l)) {
1561 MsgInfo * msginfo = (MsgInfo *) l->data;
1563 if (msginfo->folder && mbox_path == NULL)
1564 mbox_path = mbox_folder_get_path(msginfo->folder);
1566 mbox_copy_msg(folder, dest, msginfo);
1570 mbox_cache_synchronize(mbox_path);
1574 mbox_path = mbox_folder_get_path(dest);
1575 mbox_cache_synchronize(mbox_path);
1578 return dest->last_num;
1582 struct _copy_flags_info
1588 typedef struct _copy_flags_info CopyFlagsInfo;
1590 GSList * copy_flags_data = NULL;
1592 gint mbox_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1594 Folder * src_folder;
1597 CopyFlagsInfo * flags_info;
1599 src_folder = msginfo->folder->folder;
1602 mbox_path = mbox_folder_get_path(msginfo->folder);
1603 mbox_rewrite(mbox_path);
1607 filename = folder_item_fetch_msg(msginfo->folder,
1609 if (filename == NULL)
1612 num = mbox_add_msg(folder, dest, filename, FALSE);
1615 mbox_path = mbox_folder_get_path(dest);
1616 msg = mbox_cache_get_msg(mbox_path, num);
1618 msg->flags = msginfo->flags;
1625 flags_info = g_new0(CopyFlagsInfo, 1);
1626 flags_info->num = num;
1627 flags_info->flags = msginfo->flags;
1628 copy_flags_data = g_slist_append(copy_flags_data, flags_info);
1633 void mbox_scan_folder(Folder *folder, FolderItem *item)
1640 mbox_path = mbox_folder_get_path(folder, item);
1641 if (mbox_path == NULL)
1644 mbox_cache_synchronize(mbox_path, TRUE);
1646 cached = mbox_cache_get_mbox(mbox_path);
1648 if (cached == NULL) {
1650 item->unread_msgs = 0;
1651 item->total_msgs = 0;
1657 n_msg = mbox_cache_get_count(mbox_path);
1660 item->new_msgs = item->unread_msgs = item->total_msgs = 0;
1667 for(l = mbox_cache_get_msg_list(mbox_path) ; l != NULL ;
1668 l = g_list_next(l)) {
1669 struct _message * msg = (struct _message *) l->data;
1670 if (!MSG_IS_REALLY_DELETED(msg->flags))
1672 if (MSG_IS_NEW(msg->flags) && !MSG_IS_IGNORE_THREAD(msg->flags))
1674 if (MSG_IS_UNREAD(msg->flags) && !MSG_IS_IGNORE_THREAD(msg->flags))
1678 item->new_msgs = new;
1679 item->unread_msgs = unread;
1680 item->total_msgs = total;
1683 debug_print("Last number in dir %s = %d\n", mbox_path,
1685 item->last_num = n_msg;
1689 gchar * mbox_get_virtual_path(FolderItem * item)
1694 if (item->parent == NULL) {
1698 gchar * parent_path;
1699 gchar * result_path;
1701 parent_path = mbox_get_virtual_path(item->parent);
1702 if (parent_path == NULL)
1703 result_path = g_strdup(item->name);
1705 result_path = g_strconcat(parent_path,
1708 g_free(parent_path);
1714 static gboolean mbox_write_data(FILE * mbox_fp, FILE * new_fp,
1715 gchar * new_filename, gint size)
1723 while (pos < size) {
1724 if ((size - pos) > (gint) sizeof(buf))
1729 n_read = fread(buf, 1, max, mbox_fp);
1731 if (n_read < max && ferror(mbox_fp)) {
1734 if (fwrite(buf, n_read, 1, new_fp) < 1) {
1735 g_warning("writing to %s failed.\n", new_filename);
1748 static gboolean mbox_write_message(FILE * mbox_fp, FILE * new_fp,
1749 gchar * new_filename,
1750 struct _message * msg)
1753 GPtrArray * headers;
1756 fseek(mbox_fp, msg->header, SEEK_SET);
1758 headers = procheader_get_header_array_asis(mbox_fp);
1760 for (i = 0; i < (gint) headers->len; i++) {
1761 Header * h = g_ptr_array_index(headers, i);
1763 if (!procheader_headername_equal(h->name,
1765 !procheader_headername_equal(h->name,
1767 fwrite(h->name, strlen(h->name),
1769 if (h->name[strlen(h->name) - 1] != ' ')
1770 fwrite(" ", 1, 1, new_fp);
1771 fwrite(h->body, strlen(h->body),
1773 fwrite("\n", 1, 1, new_fp);
1775 procheader_header_free(h);
1776 g_ptr_array_remove_index(headers, i);
1780 g_ptr_array_free(headers, FALSE);
1782 if (!MSG_IS_INVALID(msg->flags)) {
1784 fwrite("Status: ", strlen("Status: "), 1, new_fp);
1785 if (!MSG_IS_UNREAD(msg->flags))
1786 fwrite("R", 1, 1, new_fp);
1787 fwrite("O", 1, 1, new_fp);
1788 fwrite("\n", 1, 1, new_fp);
1790 /* X-Status header */
1791 if (MSG_IS_REALLY_DELETED(msg->flags)
1792 || MSG_IS_MARKED(msg->flags)
1793 || MSG_IS_DELETED(msg->flags)
1794 || MSG_IS_REPLIED(msg->flags)
1795 || MSG_IS_FORWARDED(msg->flags)) {
1796 fwrite("X-Status: ", strlen("X-Status: "), 1, new_fp);
1797 if (MSG_IS_REALLY_DELETED(msg->flags))
1798 fwrite("D", 1, 1, new_fp); /* really deleted */
1800 if (MSG_IS_MARKED(msg->flags))
1801 fwrite("F", 1, 1, new_fp);
1802 if (MSG_IS_DELETED(msg->flags))
1803 fwrite("d", 1, 1, new_fp);
1804 if (MSG_IS_REPLIED(msg->flags))
1805 fwrite("r", 1, 1, new_fp);
1806 if (MSG_IS_FORWARDED(msg->flags))
1807 fwrite("f", 1, 1, new_fp);
1809 fwrite("\n", 1, 1, new_fp);
1813 fwrite("\n", 1, 1, new_fp);
1815 size = msg->end - msg->content;
1816 fseek(mbox_fp, msg->content, SEEK_SET);
1818 return mbox_write_data(mbox_fp, new_fp, new_filename, size);
1821 void mbox_change_flags(Folder * folder, FolderItem * item, MsgInfo * info, MsgPermFlags newflags)
1823 struct _message * msg;
1827 mbox_path = mbox_folder_get_path(folder, item);
1828 if (mbox_path == NULL)
1831 info->flags.perm_flags = newflags;
1833 msg = mbox_cache_get_msg(mbox_path, info->msgnum);
1835 cache = mbox_cache_get_mbox(mbox_path);
1839 if ((msg == NULL) || (cache == NULL))
1842 msg->flags = info->flags;
1844 cache->modification = TRUE;
1848 static gboolean mbox_purge_deleted(gchar * mbox)
1855 gboolean modification = FALSE;
1859 mbox_cache_synchronize(mbox, TRUE);
1861 msg_list = mbox_cache_get_msg_list(mbox);
1863 for(l = msg_list ; l != NULL ; l = g_list_next(l)) {
1864 struct _message * msg = (struct _message *) l->data;
1865 if (!MSG_IS_INVALID(msg->flags) && MSG_IS_REALLY_DELETED(msg->flags)) {
1866 modification = TRUE;
1871 if (!modification) {
1872 debug_print("no deleted messages - %s\n", mbox);
1876 debug_print("purge deleted messages - %s\n", mbox);
1878 mbox_fp = fopen(mbox, "rb+");
1879 mbox_lockwrite_file(mbox_fp, mbox);
1881 mbox_cache_synchronize_from_file(mbox_fp, mbox, TRUE);
1883 new = g_strconcat(mbox, ".", itos((int) mbox), NULL);
1884 new_fp = fopen(new, "wb");
1886 if (change_file_mode_rw(new_fp, new) < 0) {
1887 FILE_OP_ERROR(new, "chmod");
1888 g_warning("can't change file mode\n");
1891 mbox_lockwrite_file(new_fp, new);
1896 msg_list = mbox_cache_get_msg_list(mbox);
1897 for(l = msg_list ; l != NULL ; l = g_list_next(l)) {
1898 struct _message * msg = (struct _message *) l->data;
1899 if (MSG_IS_INVALID(msg->flags) || !MSG_IS_REALLY_DELETED(msg->flags)) {
1900 if (!mbox_write_message(mbox_fp, new_fp, new, msg)) {
1910 if (rename(new, mbox) == -1) {
1911 g_warning("can't rename %s to %s\n", new, mbox);
1912 mbox_unlock_file(new_fp, new);
1914 mbox_unlock_file(mbox_fp, mbox);
1920 if (change_file_mode_rw(new_fp, mbox) < 0) {
1921 FILE_OP_ERROR(new, "chmod");
1922 g_warning("can't change file mode\n");
1925 mbox_unlock_file(new_fp, new);
1929 mbox_unlock_file(mbox_fp, mbox);
1933 debug_print("%i messages written - %s\n", count, mbox);
1935 mbox_cache_synchronize(mbox, FALSE);
1940 #define MAKE_DIR_IF_NOT_EXIST(dir) \
1942 if (!is_dir_exist(dir)) { \
1943 if (is_file_exist(dir)) { \
1944 g_warning("File `%s' already exists.\n" \
1945 "Can't create folder.", dir); \
1948 if (mkdir(dir, S_IRWXU) < 0) { \
1949 FILE_OP_ERROR(dir, "mkdir"); \
1952 if (chmod(dir, S_IRWXU) < 0) \
1953 FILE_OP_ERROR(dir, "chmod"); \
1957 gint mbox_create_tree(Folder *folder)
1961 g_return_val_if_fail(folder != NULL, -1);
1963 CHDIR_RETURN_VAL_IF_FAIL(get_home_dir(), -1);
1964 rootpath = LOCAL_FOLDER(folder)->rootpath;
1965 MAKE_DIR_IF_NOT_EXIST(rootpath);
1966 CHDIR_RETURN_VAL_IF_FAIL(rootpath, -1);
1971 #undef MAKE_DIR_IF_NOT_EXIST
1973 static gchar * mbox_get_new_path(FolderItem * parent, gchar * name)
1977 if (strchr(name, '/') == NULL) {
1978 if (parent->path != NULL)
1979 path = g_strconcat(parent->path, ".sbd", G_DIR_SEPARATOR_S, name, NULL);
1981 path = g_strdup(name);
1984 path = g_strdup(name);
1989 static gchar * mbox_get_folderitem_name(gchar * name)
1993 foldername = g_strdup(g_basename(name));
1998 FolderItem *mbox_create_folder(Folder *folder, FolderItem *parent,
2002 FolderItem *new_item;
2005 g_return_val_if_fail(folder != NULL, NULL);
2006 g_return_val_if_fail(parent != NULL, NULL);
2007 g_return_val_if_fail(name != NULL, NULL);
2009 path = mbox_get_new_path(parent, (gchar *) name);
2011 foldername = mbox_get_folderitem_name((gchar *) name);
2013 new_item = folder_item_new(folder, foldername, path);
2014 folder_item_append(parent, new_item);
2016 if (!strcmp(name, "inbox")) {
2017 new_item->stype = F_INBOX;
2018 new_item->folder->inbox = new_item;
2019 } else if (!strcmp(name, "outbox")) {
2020 new_item->stype = F_OUTBOX;
2021 new_item->folder->outbox = new_item;
2022 } else if (!strcmp(name, "draft")) {
2023 new_item->stype = F_DRAFT;
2024 new_item->folder->draft = new_item;
2025 } else if (!strcmp(name, "queue")) {
2026 new_item->stype = F_QUEUE;
2027 new_item->folder->queue = new_item;
2028 } else if (!strcmp(name, "trash")) {
2029 new_item->stype = F_TRASH;
2030 new_item->folder->trash = new_item;
2039 gint mbox_rename_folder(Folder *folder, FolderItem *item, const gchar *name)
2044 g_return_val_if_fail(folder != NULL, -1);
2045 g_return_val_if_fail(item != NULL, -1);
2046 g_return_val_if_fail(item->path != NULL, -1);
2047 g_return_val_if_fail(name != NULL, -1);
2049 path = mbox_get_new_path(item->parent, (gchar *) name);
2050 foldername = mbox_get_folderitem_name((gchar *) name);
2052 if (rename(item->path, path) == -1) {
2055 g_warning("Cannot rename folder item");
2063 item->name = foldername;
2069 gint mbox_remove_folder(Folder *folder, FolderItem *item)
2071 g_return_val_if_fail(folder != NULL, -1);
2072 g_return_val_if_fail(item != NULL, -1);
2073 g_return_val_if_fail(item->path != NULL, -1);
2075 folder_item_remove(item);
2079 gint mbox_get_num_list(Folder *folder, FolderItem *item, GSList **mlist)
2086 mbox_path = mbox_folder_get_path(folder, item);
2088 if (mbox_path == NULL)
2091 mbox_purge_deleted(mbox_path);
2093 fp = fopen(mbox_path, "rb");
2100 mbox_lockread_file(fp, mbox_path);
2102 mbox_cache_synchronize_from_file(fp, mbox_path, TRUE);
2104 item->last_num = mbox_cache_get_count(mbox_path);
2106 for(l = mbox_cache_get_msg_list(mbox_path) ; l != NULL ;
2107 l = g_list_next(l)) {
2108 struct _message * msg;
2110 msg = (struct _message *) l->data;
2112 if (MSG_IS_INVALID(msg->flags) || !MSG_IS_REALLY_DELETED(msg->flags)) {
2113 *mlist = g_slist_append(*mlist, GINT_TO_POINTER(msg->msgnum));
2116 MSG_SET_PERM_FLAGS(msg->flags, MSG_REALLY_DELETED);
2120 mbox_unlock_file(fp, mbox_path);
2129 MsgInfo *mbox_get_msginfo(Folder *folder, FolderItem *item, gint num)
2132 struct _message *msg;
2136 g_return_val_if_fail(folder != NULL, NULL);
2137 g_return_val_if_fail(item != NULL, NULL);
2139 mbox_path = mbox_folder_get_path(folder, item);
2141 g_return_val_if_fail(mbox_path != NULL, NULL);
2143 src = fopen(mbox_path, "rb");
2148 mbox_lockread_file(src, mbox_path);
2149 mbox_cache_synchronize_from_file(src, mbox_path, TRUE);
2151 msg = mbox_cache_get_msg(mbox_path, num);
2153 mbox_unlock_file(src, mbox_path);
2159 fseek(src, msg->header, SEEK_SET);
2160 msginfo = mbox_parse_msg(src, msg, item);
2162 mbox_unlock_file(src, mbox_path);
2169 gboolean mbox_check_msgnum_validity(Folder *folder, FolderItem *item)
2171 mboxcache * old_cache;
2172 gboolean scan_new = TRUE;
2176 filename = mbox_folder_get_path(folder, item);
2178 old_cache = mbox_cache_get_mbox(filename);
2180 if (old_cache != NULL) {
2181 if (stat(filename, &s) < 0) {
2182 FILE_OP_ERROR(filename, "stat");
2183 } else if (old_cache->mtime == s.st_mtime) {
2184 debug_print("Folder is not modified.\n");