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, gboolean remove_source);
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);
74 FolderClass mbox_class =
80 /* Folder functions */
86 /* FolderItem functions */
96 mbox_check_msgnum_validity,
98 /* Message functions */
110 FolderClass *mbox_get_class(void)
115 Folder *mbox_folder_new(const gchar *name, const gchar *path)
119 folder = (Folder *)g_new0(MBOXFolder, 1);
120 folder->klass = &mbox_class;
121 mbox_folder_init(folder, name, path);
126 void mbox_folder_destroy(Folder *folder)
128 folder_local_folder_destroy(LOCAL_FOLDER(folder));
131 static void mbox_folder_init(Folder *folder, const gchar *name, const gchar *path)
133 folder_local_folder_init(folder, name, path);
136 static void mbox_folder_create_parent(const gchar * path)
138 if (!is_file_exist(path)) {
141 new_path = g_dirname(path);
142 if (new_path[strlen(new_path) - 1] == G_DIR_SEPARATOR)
143 new_path[strlen(new_path) - 1] = '\0';
145 if (!is_dir_exist(new_path))
146 make_dir_hier(new_path);
153 static gchar *mbox_folder_get_path(FolderItem *item)
158 g_return_val_if_fail(item != NULL, NULL);
160 if (item->path && item->path[0] == G_DIR_SEPARATOR) {
161 mbox_folder_create_parent(item->path);
162 return g_strdup(item->path);
165 folder_path = g_strdup(LOCAL_FOLDER(item->folder)->rootpath);
166 g_return_val_if_fail(folder_path != NULL, NULL);
168 if (folder_path[0] == G_DIR_SEPARATOR) {
170 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
174 path = g_strdup(folder_path);
177 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
178 folder_path, G_DIR_SEPARATOR_S,
181 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
187 mbox_folder_create_parent(path);
193 /**********************************************************/
197 /**********************************************************/
200 static GSList * file_lock = NULL;
202 static gboolean mbox_file_lock_file(gchar * base)
204 gchar *lockfile, *locklink;
208 lockfile = g_strdup_printf("%s.%d", base, getpid());
209 if ((lockfp = fopen(lockfile, "wb")) == NULL) {
210 FILE_OP_ERROR(lockfile, "fopen");
211 g_warning("can't create lock file %s\n", lockfile);
212 g_warning("use 'flock' instead of 'file' if possible.\n");
217 fprintf(lockfp, "%d\n", getpid());
220 locklink = g_strconcat(base, ".lock", NULL);
221 while (link(lockfile, locklink) < 0) {
222 FILE_OP_ERROR(lockfile, "link");
224 g_warning("can't create %s\n", lockfile);
231 g_warning("mailbox is owned by another"
232 " process, waiting...\n");
243 static gboolean mbox_fcntl_lockwrite_file(FILE * fp)
247 lck.l_type = F_WRLCK;
252 if (fcntl(fileno(fp), F_SETLK, &lck) < 0)
258 static gboolean mbox_fcntl_lockread_file(FILE * fp)
262 lck.l_type = F_RDLCK;
267 if (fcntl(fileno(fp), F_SETLK, &lck) < 0)
273 static gboolean mbox_fcntl_unlock_file(FILE * fp)
277 lck.l_type = F_UNLCK;
282 if (fcntl(fileno(fp), F_SETLK, &lck) < 0)
288 static gboolean mbox_file_unlock_file(gchar * base)
292 lockfile = g_strdup_printf("%s.lock", base);
299 static gboolean mbox_lockread_file(FILE * fp, gchar * base)
303 result = mbox_fcntl_lockread_file(fp);
305 if ((result = mbox_file_lock_file(base)) == TRUE) {
306 file_lock = g_slist_append(file_lock, g_strdup(base));
307 debug_print("lockfile lock %s.\n", base);
310 g_warning("could not lock read file %s\n", base);
313 debug_print("fcntl lock %s.\n", base);
318 static gboolean mbox_lockwrite_file(FILE * fp, gchar * base)
322 result = mbox_fcntl_lockwrite_file(fp);
324 if ((result = mbox_file_lock_file(base)) == TRUE) {
325 file_lock = g_slist_append(file_lock, g_strdup(base));
326 debug_print("lockfile lock %s.\n", base);
329 g_warning("could not lock write file %s\n", base);
332 debug_print("fcntl lock %s.\n", base);
337 static gboolean mbox_unlock_file(FILE * fp, gchar * base)
339 gboolean result = FALSE;
341 gboolean unlocked = FALSE;
343 for(l = file_lock ; l != NULL ; l = g_slist_next(l)) {
344 gchar * data = l->data;
346 if (strcmp(data, base) == 0) {
347 file_lock = g_slist_remove(file_lock, data);
349 result = mbox_file_unlock_file(base);
351 debug_print("lockfile unlock - %s.\n", base);
357 result = mbox_fcntl_unlock_file(fp);
358 debug_print("fcntl unlock - %s.\n", base);
364 /**********************************************************/
368 /**********************************************************/
370 #define MAILFILE_ERROR_NO_ERROR 0x000
371 #define MAILFILE_ERROR_FILE_NOT_FOUND 0x001
372 #define MAILFILE_ERROR_MEMORY 0x002
373 #define MAILFILE_ERROR_MESSAGE_NOT_FOUND 0x003
375 static int mailfile_error = MAILFILE_ERROR_NO_ERROR;
377 #define STATE_BEGIN 0x000
378 #define STATE_TEXT_READ 0x001
379 #define STATE_FROM_READ 0x002
380 #define STATE_FIELD_READ 0x003
381 #define STATE_END 0x004
382 #define STATE_END_OF_FILE 0x005
383 #define STATE_MEM_ERROR 0x006
384 #define STATE_TEXT_BEGIN 0x007
386 #define STATE_MASK 0x0FF /* filter state from functions */
388 #define STATE_RESTORE_POS 0x100 /* go back while reading */
390 typedef struct _mailfile mailfile;
414 #define MSG_IS_INVALID(msg) \
415 ((msg).perm_flags == (msg).tmp_flags && (msg).tmp_flags == -1)
417 #define MSG_SET_INVALID(msg) \
418 ((msg).perm_flags = (msg).tmp_flags = -1)
420 static int startFrom(char * s)
422 return (strncmp(s, "From ", 5) == 0);
425 static int startSpace(char * s)
427 return ((*s == ' ') || (*s == '\t'));
430 static int startEmpty(char * s)
435 static void free_msg_list(GList * l)
437 GList * elt = g_list_first(l);
442 elt = g_list_next(elt);
449 static mailfile * mailfile_init_from_file(FILE * f, gchar * filename)
452 GList * msg_list = NULL;
459 struct _message * data = NULL;
465 while (state != STATE_END_OF_FILE) {
466 if ((state & STATE_RESTORE_POS) == 0) {
467 former_pos = lastpos;
470 r = fgets(s, 256, f);
473 ignore_next = (s[strlen(s) - 1] != '\n');
478 switch(state & 0x0F) {
481 state = STATE_END_OF_FILE;
482 else if (startFrom(s)) {
483 state = STATE_FROM_READ;
485 data = g_new0(struct _message, 1);
487 free_msg_list(msg_list);
492 data->msgnum = msgnum;
493 data->offset = lastpos;
494 data->header = lastpos;
497 data->messageid = NULL;
498 data->fromspace = NULL;
499 MSG_SET_INVALID(data->flags);
500 MSG_SET_INVALID(data->old_flags);
501 data->fetched = FALSE;
502 msg_list = g_list_append(msg_list,
510 case STATE_TEXT_READ:
513 else if (startFrom(s))
514 state = STATE_END | STATE_RESTORE_POS;
516 state = STATE_TEXT_READ;
519 case STATE_TEXT_BEGIN:
520 data->content = lastpos;
523 else if (startFrom(s)) {
524 state = STATE_END | STATE_RESTORE_POS;
527 state = STATE_TEXT_READ;
531 case STATE_FROM_READ:
532 data->content = lastpos;
535 else if (startSpace(s))
536 state = STATE_FROM_READ;
537 else if (startEmpty(s))
538 state = STATE_TEXT_READ;
540 state = STATE_FIELD_READ;
543 case STATE_FIELD_READ:
544 data->content = lastpos;
547 else if (startSpace(s))
548 state = STATE_FIELD_READ;
549 else if (startEmpty(s)) {
550 state = STATE_TEXT_BEGIN;
553 state = STATE_FIELD_READ;
557 if ((state & STATE_MASK) == STATE_END) {
558 state = STATE_BEGIN | (state & STATE_RESTORE_POS);
564 r = fgets(s, 256, f);
565 if (r == NULL || *r == '\0')
568 while (s[strlen(s) - 1] != '\n');
572 mf = (mailfile *) g_new0(struct _mailfile, 1);
574 free_msg_list(msg_list);
575 mailfile_error = MAILFILE_ERROR_MEMORY;
579 mf->msg_list = g_list_first(msg_list);
581 mf->filename = g_strdup(filename);
584 mailfile_error = MAILFILE_ERROR_NO_ERROR;
589 static mailfile * mailfile_init(char * filename)
595 f = fopen(filename, "rb");
598 mailfile_error = MAILFILE_ERROR_FILE_NOT_FOUND;
602 mbox_lockread_file(f, filename);
604 mf = mailfile_init_from_file(f, filename);
606 mbox_unlock_file(f, filename);
613 static void mailfile_done(mailfile * f)
615 free_msg_list(f->msg_list);
622 #define MAX_READ 4096
624 static char * readfile(char * filename, int offset, int max_offset)
633 handle = fopen(filename, "rb");
635 if (handle == NULL) {
636 mailfile_error = MAILFILE_ERROR_FILE_NOT_FOUND;
640 size = max_offset - offset;
642 message = (char *) malloc(size + 1);
643 if (message == NULL) {
645 mailfile_error = MAILFILE_ERROR_MEMORY;
649 fseek(handle, offset, SEEK_SET);
653 if ((size - pos) > MAX_READ)
658 bread = fread(message + pos, 1, max, handle);
674 static char * mailfile_readmsg(mailfile f, int index)
680 struct _message * msginfo;
682 nth = g_list_nth(f->msg_list, index);
685 mailfile_error = MAILFILE_ERROR_MESSAGE_NOT_FOUND;
689 msginfo = (struct _message *)nth->data;
691 offset = msginfo->offset;
692 max_offset = msginfo->end;
693 message = readfile(f->filename, offset, max_offset);
695 mailfile_error = MAILFILE_ERROR_NO_ERROR;
700 static char * mailfile_readheader(mailfile f, int index)
706 struct _message * msginfo;
708 nth = g_list_nth(f->msg_list, index);
711 mailfile_error = MAILFILE_ERROR_MESSAGE_NOT_FOUND;
715 msginfo = (struct _message *)nth->data;
717 offset = msginfo->offset;
718 max_offset = msginfo->content;
719 message = readfile(f->filename, offset, max_offset);
721 mailfile_error = MAILFILE_ERROR_NO_ERROR;
726 static int mailfile_count(mailfile * f)
728 return g_list_length(f->msg_list);
731 static int mailfile_find_deleted(mailfile f, char * filename)
735 handle = fopen(filename, "rb");
738 struct _message m = elt->data;
739 n = fread(&m.deleted, sizeof(int), 1, handle);
742 elt = g_list_next(elt);
751 /**********************************************************/
753 /* mbox cache operations */
755 /**********************************************************/
762 gboolean modification;
765 typedef struct _mboxcache mboxcache;
767 static GHashTable * mbox_cache_table = NULL;
769 static MsgInfo *mbox_parse_msg(FILE * fp, struct _message * msg,
773 MsgFlags flags = { 0, 0 };
775 MSG_SET_PERM_FLAGS(flags, MSG_NEW | MSG_UNREAD);
777 g_return_val_if_fail(fp != NULL, NULL);
780 if (item->stype == F_QUEUE) {
781 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
782 } else if (item->stype == F_DRAFT) {
783 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
787 msginfo = procheader_parse_stream(fp, flags, FALSE, FALSE);
789 if (!msginfo) return NULL;
792 msginfo->msgnum = msg->msgnum;
793 msginfo->folder = item;
799 static void mbox_cache_init()
801 mbox_cache_table = g_hash_table_new(g_str_hash, g_str_equal);
804 static void mbox_cache_free_mbox(mboxcache * cache)
806 g_hash_table_remove(mbox_cache_table, cache->filename);
809 mailfile_done(cache->mf);
811 g_ptr_array_free(cache->tab_mf, FALSE);
813 g_free(cache->filename);
817 static void mbox_cache_get_msginfo_from_file(FILE * fp, GList * msg_list)
822 for(l = msg_list ; l != NULL ; l = g_list_next(l)) {
823 struct _message * msg;
825 msg = (struct _message *) l->data;
827 fseek(fp, msg->header, SEEK_SET);
828 msginfo = mbox_parse_msg(fp, msg, NULL);
832 g_strdup(msginfo->msgid);
833 if (msginfo->fromspace)
835 g_strdup(msginfo->fromspace);
836 msg->flags = msginfo->flags;
837 msg->old_flags = msginfo->flags;
839 procmsg_msginfo_free(msginfo);
844 static void mbox_cache_get_msginfo(gchar * filename, GList * msg_list)
848 fp = fopen(filename, "rb");
852 mbox_cache_get_msginfo_from_file(fp, msg_list);
856 static mboxcache * mbox_cache_read_mbox(gchar * filename)
863 if (stat(filename, &s) < 0)
866 mf = mailfile_init(filename);
870 cache = g_new0(mboxcache, 1);
872 cache->mtime = s.st_mtime;
874 cache->filename = g_strdup(filename);
875 cache->modification = FALSE;
877 cache->tab_mf = g_ptr_array_new();
878 for(l = mf->msg_list ; l != NULL ; l = g_list_next(l))
879 g_ptr_array_add(cache->tab_mf, l->data);
881 mbox_cache_get_msginfo(filename, mf->msg_list);
883 debug_print("read mbox - %s\n", filename);
888 static mboxcache * mbox_cache_read_mbox_from_file(FILE * fp, gchar * filename)
895 if (stat(filename, &s) < 0)
898 mf = mailfile_init_from_file(fp, filename);
902 cache = g_new0(mboxcache, 1);
904 cache->mtime = s.st_mtime;
906 cache->filename = g_strdup(filename);
908 cache->tab_mf = g_ptr_array_new();
909 for(l = mf->msg_list ; l != NULL ; l = g_list_next(l))
910 g_ptr_array_add(cache->tab_mf, l->data);
912 mbox_cache_get_msginfo_from_file(fp, mf->msg_list);
914 debug_print("read mbox from file - %s\n", filename);
919 static void mbox_cache_insert_mbox(mboxcache * data)
921 if (mbox_cache_table == NULL)
924 g_hash_table_insert(mbox_cache_table, data->filename, data);
927 static mboxcache * mbox_cache_get_mbox(gchar * filename)
929 if (mbox_cache_table == NULL)
932 return g_hash_table_lookup(mbox_cache_table, filename);
936 static gint mbox_cache_get_count(gchar * filename)
940 cache = mbox_cache_get_mbox(filename);
943 if (cache->mf == NULL)
945 return cache->mf->count;
948 static GList * mbox_cache_get_msg_list(gchar * filename)
952 cache = mbox_cache_get_mbox(filename);
957 if (cache->mf == NULL)
960 return cache->mf->msg_list;
963 static void mbox_cache_synchronize_lists(GList * old_msg_list,
964 GList * new_msg_list)
969 for(l2 = old_msg_list ; l2 != NULL ; l2 = g_list_next(l2)) {
970 struct _message * msg2 = l2->data;
972 if ((msg2->messageid == NULL) ||
973 (msg2->fromspace == NULL))
976 for(l = new_msg_list ; l != NULL ; l = g_list_next(l)) {
977 struct _message * msg = l->data;
979 if ((msg->messageid == NULL) ||
980 (msg->fromspace == NULL))
983 if ((strcmp(msg->messageid, msg2->messageid) == 0) &&
984 (strcmp(msg->fromspace, msg2->fromspace) == 0)) {
985 if (msg2->flags.perm_flags != msg2->old_flags.perm_flags) {
986 msg->flags = msg2->flags;
994 static void mbox_cache_synchronize(gchar * filename, gboolean sync)
996 mboxcache * new_cache;
997 mboxcache * old_cache;
998 gboolean scan_new = TRUE;
1001 old_cache = mbox_cache_get_mbox(filename);
1003 if (old_cache != NULL) {
1004 if (stat(filename, &s) < 0) {
1005 FILE_OP_ERROR(filename, "stat");
1006 } else if (old_cache->mtime == s.st_mtime) {
1007 debug_print("Folder is not modified.\n");
1014 if (strstr(filename, "trash") == 0)
1015 printf("old_cache: %p %s\n", old_cache, filename);
1017 printf("begin old\n");
1018 for(l = old_cache->mf->msg_list ; l != NULL ;
1019 l = g_list_next(l)) {
1020 struct _message * msg = l->data;
1021 printf("%p\n", msg);
1023 printf("end old\n");
1027 new_cache = mbox_cache_read_mbox(filename);
1030 if (strstr(filename, "trash") == 0)
1031 printf("new_cache: %p %s\n", new_cache, filename);
1033 printf("begin new\n");
1034 for(l = new_cache->mf->msg_list ; l != NULL ;
1035 l = g_list_next(l)) {
1036 struct _message * msg = l->data;
1037 printf("%p\n", msg);
1039 printf("end new\n");
1046 if (sync && new_cache && old_cache)
1047 mbox_cache_synchronize_lists(old_cache->mf->msg_list,
1048 new_cache->mf->msg_list);
1050 if (old_cache != NULL)
1051 mbox_cache_free_mbox(old_cache);
1054 mbox_cache_insert_mbox(new_cache);
1056 printf("insert %p %s\n", new_cache, new_cache->filename);
1057 printf("inserted %s %p\n", filename,
1058 mbox_cache_get_mbox(filename));
1064 static void mbox_cache_synchronize_from_file(FILE * fp, gchar * filename,
1067 mboxcache * new_cache;
1068 mboxcache * old_cache;
1069 gboolean scan_new = TRUE;
1072 old_cache = mbox_cache_get_mbox(filename);
1074 if (old_cache != NULL) {
1075 if (stat(filename, &s) < 0) {
1076 FILE_OP_ERROR(filename, "stat");
1077 } else if (old_cache->mtime == s.st_mtime) {
1078 debug_print("Folder is not modified.\n");
1089 if (strstr(filename, "trash") == 0)
1090 printf("old_cache: %p %s\n", old_cache, filename);
1093 printf("begin old\n");
1094 for(l = old_cache->mf->msg_list ; l != NULL ;
1095 l = g_list_next(l)) {
1096 struct _message * msg = l->data;
1097 printf("%p\n", msg);
1099 printf("end old\n");
1103 new_cache = mbox_cache_read_mbox_from_file(fp, filename);
1106 if (strstr(filename, "trash") == 0)
1107 printf("new_cache: %p %s\n", new_cache, filename);
1110 printf("begin new\n");
1111 for(l = new_cache->mf->msg_list ; l != NULL ;
1112 l = g_list_next(l)) {
1113 struct _message * msg = l->data;
1114 printf("%p\n", msg);
1116 printf("end new\n");
1123 if (sync && new_cache && old_cache)
1124 mbox_cache_synchronize_lists(old_cache->mf->msg_list,
1125 new_cache->mf->msg_list);
1127 if (old_cache != NULL)
1128 mbox_cache_free_mbox(old_cache);
1131 mbox_cache_insert_mbox(new_cache);
1133 printf("insert %p %s\n", new_cache, new_cache->filename);
1134 printf("inserted %s %p\n", filename,
1135 mbox_cache_get_mbox(filename));
1141 gboolean mbox_cache_msg_fetched(gchar * filename, gint num)
1143 struct _message * msg;
1146 cache = mbox_cache_get_mbox(filename);
1151 msg = (struct _message *) g_ptr_array_index(cache->tab_mf,
1156 return msg->fetched;
1159 void mbox_cache_msg_set_fetched(gchar * filename, gint num)
1161 struct _message * msg;
1164 cache = mbox_cache_get_mbox(filename);
1169 msg = (struct _message *) g_ptr_array_index(cache->tab_mf,
1174 msg->fetched = TRUE;
1177 struct _message * mbox_cache_get_msg(gchar * filename, gint num)
1181 cache = mbox_cache_get_mbox(filename);
1183 if (cache == NULL) {
1187 return (struct _message *) g_ptr_array_index(cache->tab_mf,
1192 /**********************************************************/
1194 /* mbox operations */
1196 /**********************************************************/
1198 static gboolean mbox_extract_msg(FolderItem * item, gint msgnum,
1199 gchar * dest_filename)
1201 struct _message * msg;
1208 /* GList * msg_list;*/
1209 gboolean already_fetched;
1212 mbox_path = mbox_folder_get_path(item);
1214 if (mbox_path == NULL)
1217 src = fopen(mbox_path, "rb");
1223 mbox_lockread_file(src, mbox_path);
1225 mbox_cache_synchronize_from_file(src, mbox_path, TRUE);
1227 already_fetched = mbox_cache_msg_fetched(mbox_path, msgnum);
1229 if (already_fetched) {
1230 mbox_unlock_file(src, mbox_path);
1236 msg = mbox_cache_get_msg(mbox_path, msgnum);
1239 mbox_unlock_file(src, mbox_path);
1245 offset = msg->offset;
1246 max_offset = msg->end;
1248 size = max_offset - offset;
1250 fseek(src, offset, SEEK_SET);
1252 dest = fopen(dest_filename, "wb");
1254 mbox_unlock_file(src, mbox_path);
1260 if (change_file_mode_rw(dest, dest_filename) < 0) {
1261 FILE_OP_ERROR(dest_filename, "chmod");
1262 g_warning("can't change file mode\n");
1265 if (!mbox_write_data(src, dest, dest_filename, size)) {
1266 mbox_unlock_file(src, mbox_path);
1269 unlink(dest_filename);
1277 FILE_OP_ERROR(mbox_path, "fread");
1281 mbox_cache_msg_set_fetched(mbox_path, msgnum);
1283 if (fclose(dest) == -1) {
1284 FILE_OP_ERROR(dest_filename, "fclose");
1288 mbox_unlock_file(src, mbox_path);
1290 if (fclose(src) == -1) {
1291 FILE_OP_ERROR(mbox_path, "fclose");
1298 unlink(dest_filename);
1305 gchar *mbox_fetch_msg(Folder *folder, FolderItem *item, gint num)
1310 g_return_val_if_fail(item != NULL, NULL);
1312 path = folder_item_get_path(item);
1313 if (!is_dir_exist(path))
1314 make_dir_hier(path);
1316 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(num), NULL);
1320 if (!mbox_extract_msg(item, num, filename)) {
1328 gint mbox_add_msg(Folder *folder, FolderItem *dest, const gchar *file,
1329 gboolean remove_source)
1338 gchar from_line[MSGBUFSIZE];
1340 if (dest->last_num < 0) {
1341 mbox_scan_folder(folder, dest);
1342 if (dest->last_num < 0) return -1;
1345 src_fp = fopen(file, "rb");
1346 if (src_fp == NULL) {
1350 mbox_path = mbox_folder_get_path(dest);
1351 if (mbox_path == NULL)
1354 dest_fp = fopen(mbox_path, "ab");
1355 if (dest_fp == NULL) {
1361 if (change_file_mode_rw(dest_fp, mbox_path) < 0) {
1362 FILE_OP_ERROR(mbox_path, "chmod");
1363 g_warning("can't change file mode\n");
1366 old_size = ftell(dest_fp);
1368 mbox_lockwrite_file(dest_fp, mbox_path);
1370 if (fgets(from_line, sizeof(from_line), src_fp) == NULL) {
1371 mbox_unlock_file(dest_fp, mbox_path);
1372 g_warning("unvalid file - %s.\n", file);
1379 if (strncmp(from_line, "From ", 5) != 0) {
1382 if (stat(file, &s) < 0) {
1383 mbox_unlock_file(dest_fp, mbox_path);
1384 g_warning("invalid file - %s.\n", file);
1391 fprintf(dest_fp, "From - %s", ctime(&s.st_mtime));
1394 fputs(from_line, dest_fp);
1397 n_read = fread(buf, 1, sizeof(buf), src_fp);
1398 if ((n_read < (gint) sizeof(buf)) && ferror(src_fp))
1400 if (fwrite(buf, n_read, 1, dest_fp) < 1) {
1401 mbox_unlock_file(dest_fp, mbox_path);
1402 g_warning("writing to %s failed.\n", mbox_path);
1403 ftruncate(fileno(dest_fp), old_size);
1410 if (n_read < (gint) sizeof(buf))
1416 if (ferror(src_fp)) {
1417 FILE_OP_ERROR(mbox_path, "fread");
1420 mbox_unlock_file(dest_fp, mbox_path);
1422 if (fclose(src_fp) == -1) {
1423 FILE_OP_ERROR(file, "fclose");
1427 if (fclose(dest_fp) == -1) {
1428 FILE_OP_ERROR(mbox_path, "fclose");
1434 ftruncate(fileno(dest_fp), old_size);
1439 if (remove_source) {
1440 if (unlink(file) < 0)
1441 FILE_OP_ERROR(file, "unlink");
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(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(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(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(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(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(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(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");