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,
72 GSList **list, gboolean *old_uids_valid);
73 static gboolean mbox_scan_required(Folder *folder, FolderItem *item);
74 static gchar *mbox_folder_get_path(Folder *folder, FolderItem *item);
75 static gchar *mbox_item_get_path(Folder *folder, FolderItem *item);
77 FolderClass mbox_class =
83 /* Folder functions */
89 /* FolderItem functions */
103 /* Message functions */
117 FolderClass *mbox_get_class(void)
122 Folder *mbox_folder_new(const gchar *name, const gchar *path)
126 folder = (Folder *)g_new0(MBOXFolder, 1);
127 folder->klass = &mbox_class;
128 mbox_folder_init(folder, name, path);
133 void mbox_folder_destroy(Folder *folder)
135 folder_local_folder_destroy(LOCAL_FOLDER(folder));
138 static void mbox_folder_init(Folder *folder, const gchar *name, const gchar *path)
140 folder_local_folder_init(folder, name, path);
143 static void mbox_folder_create_parent(const gchar * path)
145 if (!is_file_exist(path)) {
148 new_path = g_dirname(path);
149 if (new_path[strlen(new_path) - 1] == G_DIR_SEPARATOR)
150 new_path[strlen(new_path) - 1] = '\0';
152 if (!is_dir_exist(new_path))
153 make_dir_hier(new_path);
160 gchar *mbox_folder_get_path(Folder *folder, FolderItem *item)
165 g_return_val_if_fail(item != NULL, NULL);
167 if (item->path && item->path[0] == G_DIR_SEPARATOR) {
168 mbox_folder_create_parent(item->path);
169 return g_strdup(item->path);
172 folder_path = g_strdup(LOCAL_FOLDER(item->folder)->rootpath);
173 g_return_val_if_fail(folder_path != NULL, NULL);
175 if (folder_path[0] == G_DIR_SEPARATOR) {
177 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
181 path = g_strdup(folder_path);
184 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
185 folder_path, G_DIR_SEPARATOR_S,
188 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
194 mbox_folder_create_parent(path);
200 /**********************************************************/
204 /**********************************************************/
207 static GSList * file_lock = NULL;
209 static gboolean mbox_file_lock_file(gchar * base)
211 gchar *lockfile, *locklink;
215 lockfile = g_strdup_printf("%s.%d", base, getpid());
216 if ((lockfp = fopen(lockfile, "wb")) == NULL) {
217 FILE_OP_ERROR(lockfile, "fopen");
218 g_warning("can't create lock file %s\n", lockfile);
219 g_warning("use 'flock' instead of 'file' if possible.\n");
224 fprintf(lockfp, "%d\n", getpid());
227 locklink = g_strconcat(base, ".lock", NULL);
228 while (link(lockfile, locklink) < 0) {
229 FILE_OP_ERROR(lockfile, "link");
231 g_warning("can't create %s\n", lockfile);
238 g_warning("mailbox is owned by another"
239 " process, waiting...\n");
250 static gboolean mbox_fcntl_lockwrite_file(FILE * fp)
254 lck.l_type = F_WRLCK;
259 if (fcntl(fileno(fp), F_SETLK, &lck) < 0)
265 static gboolean mbox_fcntl_lockread_file(FILE * fp)
269 lck.l_type = F_RDLCK;
274 if (fcntl(fileno(fp), F_SETLK, &lck) < 0)
280 static gboolean mbox_fcntl_unlock_file(FILE * fp)
284 lck.l_type = F_UNLCK;
289 if (fcntl(fileno(fp), F_SETLK, &lck) < 0)
295 static gboolean mbox_file_unlock_file(gchar * base)
299 lockfile = g_strdup_printf("%s.lock", base);
306 static gboolean mbox_lockread_file(FILE * fp, gchar * base)
310 result = mbox_fcntl_lockread_file(fp);
312 if ((result = mbox_file_lock_file(base)) == TRUE) {
313 file_lock = g_slist_append(file_lock, g_strdup(base));
314 debug_print("lockfile lock %s.\n", base);
317 g_warning("could not lock read file %s\n", base);
320 debug_print("fcntl lock %s.\n", base);
325 static gboolean mbox_lockwrite_file(FILE * fp, gchar * base)
329 result = mbox_fcntl_lockwrite_file(fp);
331 if ((result = mbox_file_lock_file(base)) == TRUE) {
332 file_lock = g_slist_append(file_lock, g_strdup(base));
333 debug_print("lockfile lock %s.\n", base);
336 g_warning("could not lock write file %s\n", base);
339 debug_print("fcntl lock %s.\n", base);
344 static gboolean mbox_unlock_file(FILE * fp, gchar * base)
346 gboolean result = FALSE;
348 gboolean unlocked = FALSE;
350 for(l = file_lock ; l != NULL ; l = g_slist_next(l)) {
351 gchar * data = l->data;
353 if (strcmp(data, base) == 0) {
354 file_lock = g_slist_remove(file_lock, data);
356 result = mbox_file_unlock_file(base);
358 debug_print("lockfile unlock - %s.\n", base);
364 result = mbox_fcntl_unlock_file(fp);
365 debug_print("fcntl unlock - %s.\n", base);
371 /**********************************************************/
375 /**********************************************************/
377 #define MAILFILE_ERROR_NO_ERROR 0x000
378 #define MAILFILE_ERROR_FILE_NOT_FOUND 0x001
379 #define MAILFILE_ERROR_MEMORY 0x002
380 #define MAILFILE_ERROR_MESSAGE_NOT_FOUND 0x003
382 static int mailfile_error = MAILFILE_ERROR_NO_ERROR;
384 #define STATE_BEGIN 0x000
385 #define STATE_TEXT_READ 0x001
386 #define STATE_FROM_READ 0x002
387 #define STATE_FIELD_READ 0x003
388 #define STATE_END 0x004
389 #define STATE_END_OF_FILE 0x005
390 #define STATE_MEM_ERROR 0x006
391 #define STATE_TEXT_BEGIN 0x007
393 #define STATE_MASK 0x0FF /* filter state from functions */
395 #define STATE_RESTORE_POS 0x100 /* go back while reading */
397 typedef struct _mailfile mailfile;
421 #define MSG_IS_INVALID(msg) \
422 ((msg).perm_flags == (msg).tmp_flags && (msg).tmp_flags == -1)
424 #define MSG_SET_INVALID(msg) \
425 ((msg).perm_flags = (msg).tmp_flags = -1)
427 static int startFrom(char * s)
429 return (strncmp(s, "From ", 5) == 0);
432 static int startSpace(char * s)
434 return ((*s == ' ') || (*s == '\t'));
437 static int startEmpty(char * s)
442 static void free_msg_list(GList * l)
444 GList * elt = g_list_first(l);
449 elt = g_list_next(elt);
456 static mailfile * mailfile_init_from_file(FILE * f, gchar * filename)
459 GList * msg_list = NULL;
466 struct _message * data = NULL;
472 while (state != STATE_END_OF_FILE) {
473 if ((state & STATE_RESTORE_POS) == 0) {
474 former_pos = lastpos;
477 r = fgets(s, 256, f);
480 ignore_next = (s[strlen(s) - 1] != '\n');
485 switch(state & 0x0F) {
488 state = STATE_END_OF_FILE;
489 else if (startFrom(s)) {
490 state = STATE_FROM_READ;
492 data = g_new0(struct _message, 1);
494 free_msg_list(msg_list);
499 data->msgnum = msgnum;
500 data->offset = lastpos;
501 data->header = lastpos;
504 data->messageid = NULL;
505 data->fromspace = NULL;
506 MSG_SET_INVALID(data->flags);
507 MSG_SET_INVALID(data->old_flags);
508 data->fetched = FALSE;
509 msg_list = g_list_append(msg_list,
517 case STATE_TEXT_READ:
520 else if (startFrom(s))
521 state = STATE_END | STATE_RESTORE_POS;
523 state = STATE_TEXT_READ;
526 case STATE_TEXT_BEGIN:
527 data->content = lastpos;
530 else if (startFrom(s)) {
531 state = STATE_END | STATE_RESTORE_POS;
534 state = STATE_TEXT_READ;
538 case STATE_FROM_READ:
539 data->content = lastpos;
542 else if (startSpace(s))
543 state = STATE_FROM_READ;
544 else if (startEmpty(s))
545 state = STATE_TEXT_READ;
547 state = STATE_FIELD_READ;
550 case STATE_FIELD_READ:
551 data->content = lastpos;
554 else if (startSpace(s))
555 state = STATE_FIELD_READ;
556 else if (startEmpty(s)) {
557 state = STATE_TEXT_BEGIN;
560 state = STATE_FIELD_READ;
564 if ((state & STATE_MASK) == STATE_END) {
565 state = STATE_BEGIN | (state & STATE_RESTORE_POS);
571 r = fgets(s, 256, f);
572 if (r == NULL || *r == '\0')
575 while (s[strlen(s) - 1] != '\n');
579 mf = (mailfile *) g_new0(struct _mailfile, 1);
581 free_msg_list(msg_list);
582 mailfile_error = MAILFILE_ERROR_MEMORY;
586 mf->msg_list = g_list_first(msg_list);
588 mf->filename = g_strdup(filename);
591 mailfile_error = MAILFILE_ERROR_NO_ERROR;
596 static mailfile * mailfile_init(char * filename)
602 f = fopen(filename, "rb");
605 mailfile_error = MAILFILE_ERROR_FILE_NOT_FOUND;
609 mbox_lockread_file(f, filename);
611 mf = mailfile_init_from_file(f, filename);
613 mbox_unlock_file(f, filename);
620 static void mailfile_done(mailfile * f)
622 free_msg_list(f->msg_list);
629 #define MAX_READ 4096
631 static char * readfile(char * filename, int offset, int max_offset)
640 handle = fopen(filename, "rb");
642 if (handle == NULL) {
643 mailfile_error = MAILFILE_ERROR_FILE_NOT_FOUND;
647 size = max_offset - offset;
649 message = (char *) malloc(size + 1);
650 if (message == NULL) {
652 mailfile_error = MAILFILE_ERROR_MEMORY;
656 fseek(handle, offset, SEEK_SET);
660 if ((size - pos) > MAX_READ)
665 bread = fread(message + pos, 1, max, handle);
681 static char * mailfile_readmsg(mailfile f, int index)
687 struct _message * msginfo;
689 nth = g_list_nth(f->msg_list, index);
692 mailfile_error = MAILFILE_ERROR_MESSAGE_NOT_FOUND;
696 msginfo = (struct _message *)nth->data;
698 offset = msginfo->offset;
699 max_offset = msginfo->end;
700 message = readfile(f->filename, offset, max_offset);
702 mailfile_error = MAILFILE_ERROR_NO_ERROR;
707 static char * mailfile_readheader(mailfile f, int index)
713 struct _message * msginfo;
715 nth = g_list_nth(f->msg_list, index);
718 mailfile_error = MAILFILE_ERROR_MESSAGE_NOT_FOUND;
722 msginfo = (struct _message *)nth->data;
724 offset = msginfo->offset;
725 max_offset = msginfo->content;
726 message = readfile(f->filename, offset, max_offset);
728 mailfile_error = MAILFILE_ERROR_NO_ERROR;
733 static int mailfile_count(mailfile * f)
735 return g_list_length(f->msg_list);
738 static int mailfile_find_deleted(mailfile f, char * filename)
742 handle = fopen(filename, "rb");
745 struct _message m = elt->data;
746 n = fread(&m.deleted, sizeof(int), 1, handle);
749 elt = g_list_next(elt);
758 /**********************************************************/
760 /* mbox cache operations */
762 /**********************************************************/
769 gboolean modification;
772 typedef struct _mboxcache mboxcache;
774 static GHashTable * mbox_cache_table = NULL;
776 static MsgInfo *mbox_parse_msg(FILE * fp, struct _message * msg,
780 MsgFlags flags = { 0, 0 };
782 MSG_SET_PERM_FLAGS(flags, MSG_NEW | MSG_UNREAD);
784 g_return_val_if_fail(fp != NULL, NULL);
787 if (item->stype == F_QUEUE) {
788 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
789 } else if (item->stype == F_DRAFT) {
790 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
794 msginfo = procheader_parse_stream(fp, flags, FALSE, FALSE);
796 if (!msginfo) return NULL;
799 msginfo->msgnum = msg->msgnum;
800 msginfo->folder = item;
806 static void mbox_cache_init()
808 mbox_cache_table = g_hash_table_new(g_str_hash, g_str_equal);
811 static void mbox_cache_free_mbox(mboxcache * cache)
813 g_hash_table_remove(mbox_cache_table, cache->filename);
816 mailfile_done(cache->mf);
818 g_ptr_array_free(cache->tab_mf, FALSE);
820 g_free(cache->filename);
824 static void mbox_cache_get_msginfo_from_file(FILE * fp, GList * msg_list)
829 for(l = msg_list ; l != NULL ; l = g_list_next(l)) {
830 struct _message * msg;
832 msg = (struct _message *) l->data;
834 fseek(fp, msg->header, SEEK_SET);
835 msginfo = mbox_parse_msg(fp, msg, NULL);
839 g_strdup(msginfo->msgid);
840 if (msginfo->fromspace)
842 g_strdup(msginfo->fromspace);
843 msg->flags = msginfo->flags;
844 msg->old_flags = msginfo->flags;
846 procmsg_msginfo_free(msginfo);
851 static void mbox_cache_get_msginfo(gchar * filename, GList * msg_list)
855 fp = fopen(filename, "rb");
859 mbox_cache_get_msginfo_from_file(fp, msg_list);
863 static mboxcache * mbox_cache_read_mbox(gchar * filename)
870 if (stat(filename, &s) < 0)
873 mf = mailfile_init(filename);
877 cache = g_new0(mboxcache, 1);
879 cache->mtime = s.st_mtime;
881 cache->filename = g_strdup(filename);
882 cache->modification = FALSE;
884 cache->tab_mf = g_ptr_array_new();
885 for(l = mf->msg_list ; l != NULL ; l = g_list_next(l))
886 g_ptr_array_add(cache->tab_mf, l->data);
888 mbox_cache_get_msginfo(filename, mf->msg_list);
890 debug_print("read mbox - %s\n", filename);
895 static mboxcache * mbox_cache_read_mbox_from_file(FILE * fp, gchar * filename)
902 if (stat(filename, &s) < 0)
905 mf = mailfile_init_from_file(fp, filename);
909 cache = g_new0(mboxcache, 1);
911 cache->mtime = s.st_mtime;
913 cache->filename = g_strdup(filename);
915 cache->tab_mf = g_ptr_array_new();
916 for(l = mf->msg_list ; l != NULL ; l = g_list_next(l))
917 g_ptr_array_add(cache->tab_mf, l->data);
919 mbox_cache_get_msginfo_from_file(fp, mf->msg_list);
921 debug_print("read mbox from file - %s\n", filename);
926 static void mbox_cache_insert_mbox(mboxcache * data)
928 if (mbox_cache_table == NULL)
931 g_hash_table_insert(mbox_cache_table, data->filename, data);
934 static mboxcache * mbox_cache_get_mbox(gchar * filename)
936 if (mbox_cache_table == NULL)
939 return g_hash_table_lookup(mbox_cache_table, filename);
943 static gint mbox_cache_get_count(gchar * filename)
947 cache = mbox_cache_get_mbox(filename);
950 if (cache->mf == NULL)
952 return cache->mf->count;
955 static GList * mbox_cache_get_msg_list(gchar * filename)
959 cache = mbox_cache_get_mbox(filename);
964 if (cache->mf == NULL)
967 return cache->mf->msg_list;
970 static void mbox_cache_synchronize_lists(GList * old_msg_list,
971 GList * new_msg_list)
976 for(l2 = old_msg_list ; l2 != NULL ; l2 = g_list_next(l2)) {
977 struct _message * msg2 = l2->data;
979 if ((msg2->messageid == NULL) ||
980 (msg2->fromspace == NULL))
983 for(l = new_msg_list ; l != NULL ; l = g_list_next(l)) {
984 struct _message * msg = l->data;
986 if ((msg->messageid == NULL) ||
987 (msg->fromspace == NULL))
990 if ((strcmp(msg->messageid, msg2->messageid) == 0) &&
991 (strcmp(msg->fromspace, msg2->fromspace) == 0)) {
992 if (msg2->flags.perm_flags != msg2->old_flags.perm_flags) {
993 msg->flags = msg2->flags;
1001 static void mbox_cache_synchronize(gchar * filename, gboolean sync)
1003 mboxcache * new_cache;
1004 mboxcache * old_cache;
1005 gboolean scan_new = TRUE;
1008 old_cache = mbox_cache_get_mbox(filename);
1010 if (old_cache != NULL) {
1011 if (stat(filename, &s) < 0) {
1012 FILE_OP_ERROR(filename, "stat");
1013 } else if (old_cache->mtime == s.st_mtime) {
1014 debug_print("Folder is not modified.\n");
1021 if (strstr(filename, "trash") == 0)
1022 printf("old_cache: %p %s\n", old_cache, filename);
1024 printf("begin old\n");
1025 for(l = old_cache->mf->msg_list ; l != NULL ;
1026 l = g_list_next(l)) {
1027 struct _message * msg = l->data;
1028 printf("%p\n", msg);
1030 printf("end old\n");
1034 new_cache = mbox_cache_read_mbox(filename);
1037 if (strstr(filename, "trash") == 0)
1038 printf("new_cache: %p %s\n", new_cache, filename);
1040 printf("begin new\n");
1041 for(l = new_cache->mf->msg_list ; l != NULL ;
1042 l = g_list_next(l)) {
1043 struct _message * msg = l->data;
1044 printf("%p\n", msg);
1046 printf("end new\n");
1053 if (sync && new_cache && old_cache)
1054 mbox_cache_synchronize_lists(old_cache->mf->msg_list,
1055 new_cache->mf->msg_list);
1057 if (old_cache != NULL)
1058 mbox_cache_free_mbox(old_cache);
1061 mbox_cache_insert_mbox(new_cache);
1063 printf("insert %p %s\n", new_cache, new_cache->filename);
1064 printf("inserted %s %p\n", filename,
1065 mbox_cache_get_mbox(filename));
1071 static void mbox_cache_synchronize_from_file(FILE * fp, gchar * filename,
1074 mboxcache * new_cache;
1075 mboxcache * old_cache;
1076 gboolean scan_new = TRUE;
1079 old_cache = mbox_cache_get_mbox(filename);
1081 if (old_cache != NULL) {
1082 if (stat(filename, &s) < 0) {
1083 FILE_OP_ERROR(filename, "stat");
1084 } else if (old_cache->mtime == s.st_mtime) {
1085 debug_print("Folder is not modified.\n");
1096 if (strstr(filename, "trash") == 0)
1097 printf("old_cache: %p %s\n", old_cache, filename);
1100 printf("begin old\n");
1101 for(l = old_cache->mf->msg_list ; l != NULL ;
1102 l = g_list_next(l)) {
1103 struct _message * msg = l->data;
1104 printf("%p\n", msg);
1106 printf("end old\n");
1110 new_cache = mbox_cache_read_mbox_from_file(fp, filename);
1113 if (strstr(filename, "trash") == 0)
1114 printf("new_cache: %p %s\n", new_cache, filename);
1117 printf("begin new\n");
1118 for(l = new_cache->mf->msg_list ; l != NULL ;
1119 l = g_list_next(l)) {
1120 struct _message * msg = l->data;
1121 printf("%p\n", msg);
1123 printf("end new\n");
1130 if (sync && new_cache && old_cache)
1131 mbox_cache_synchronize_lists(old_cache->mf->msg_list,
1132 new_cache->mf->msg_list);
1134 if (old_cache != NULL)
1135 mbox_cache_free_mbox(old_cache);
1138 mbox_cache_insert_mbox(new_cache);
1140 printf("insert %p %s\n", new_cache, new_cache->filename);
1141 printf("inserted %s %p\n", filename,
1142 mbox_cache_get_mbox(filename));
1148 gboolean mbox_cache_msg_fetched(gchar * filename, gint num)
1150 struct _message * msg;
1153 cache = mbox_cache_get_mbox(filename);
1158 msg = (struct _message *) g_ptr_array_index(cache->tab_mf,
1163 return msg->fetched;
1166 void mbox_cache_msg_set_fetched(gchar * filename, gint num)
1168 struct _message * msg;
1171 cache = mbox_cache_get_mbox(filename);
1176 msg = (struct _message *) g_ptr_array_index(cache->tab_mf,
1181 msg->fetched = TRUE;
1184 struct _message * mbox_cache_get_msg(gchar * filename, gint num)
1188 cache = mbox_cache_get_mbox(filename);
1190 if (cache == NULL) {
1194 return (struct _message *) g_ptr_array_index(cache->tab_mf,
1199 /**********************************************************/
1201 /* mbox operations */
1203 /**********************************************************/
1205 static gboolean mbox_extract_msg(FolderItem * item, gint msgnum,
1206 gchar * dest_filename)
1208 struct _message * msg;
1215 /* GList * msg_list;*/
1216 gboolean already_fetched;
1219 mbox_path = mbox_folder_get_path(item->folder, item);
1221 if (mbox_path == NULL)
1224 src = fopen(mbox_path, "rb");
1230 mbox_lockread_file(src, mbox_path);
1232 mbox_cache_synchronize_from_file(src, mbox_path, TRUE);
1234 already_fetched = mbox_cache_msg_fetched(mbox_path, msgnum);
1236 if (already_fetched) {
1237 mbox_unlock_file(src, mbox_path);
1243 msg = mbox_cache_get_msg(mbox_path, msgnum);
1246 mbox_unlock_file(src, mbox_path);
1252 offset = msg->offset;
1253 max_offset = msg->end;
1255 size = max_offset - offset;
1257 fseek(src, offset, SEEK_SET);
1259 dest = fopen(dest_filename, "wb");
1261 mbox_unlock_file(src, mbox_path);
1267 if (change_file_mode_rw(dest, dest_filename) < 0) {
1268 FILE_OP_ERROR(dest_filename, "chmod");
1269 g_warning("can't change file mode\n");
1272 if (!mbox_write_data(src, dest, dest_filename, size)) {
1273 mbox_unlock_file(src, mbox_path);
1276 unlink(dest_filename);
1284 FILE_OP_ERROR(mbox_path, "fread");
1288 mbox_cache_msg_set_fetched(mbox_path, msgnum);
1290 if (fclose(dest) == -1) {
1291 FILE_OP_ERROR(dest_filename, "fclose");
1295 mbox_unlock_file(src, mbox_path);
1297 if (fclose(src) == -1) {
1298 FILE_OP_ERROR(mbox_path, "fclose");
1305 unlink(dest_filename);
1312 gchar *mbox_fetch_msg(Folder *folder, FolderItem *item, gint num)
1317 g_return_val_if_fail(item != NULL, NULL);
1319 path = folder_item_get_path(item);
1320 if (!is_dir_exist(path))
1321 make_dir_hier(path);
1323 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(num), NULL);
1327 if (!mbox_extract_msg(item, num, filename)) {
1335 gint mbox_add_msg(Folder *folder, FolderItem *dest, const gchar *file,
1345 gchar from_line[MSGBUFSIZE];
1347 if (dest->last_num < 0) {
1348 mbox_scan_folder(folder, dest);
1349 if (dest->last_num < 0) return -1;
1352 src_fp = fopen(file, "rb");
1353 if (src_fp == NULL) {
1357 mbox_path = mbox_folder_get_path(folder, dest);
1358 if (mbox_path == NULL)
1361 dest_fp = fopen(mbox_path, "ab");
1362 if (dest_fp == NULL) {
1368 if (change_file_mode_rw(dest_fp, mbox_path) < 0) {
1369 FILE_OP_ERROR(mbox_path, "chmod");
1370 g_warning("can't change file mode\n");
1373 old_size = ftell(dest_fp);
1375 mbox_lockwrite_file(dest_fp, mbox_path);
1377 if (fgets(from_line, sizeof(from_line), src_fp) == NULL) {
1378 mbox_unlock_file(dest_fp, mbox_path);
1379 g_warning("unvalid file - %s.\n", file);
1386 if (strncmp(from_line, "From ", 5) != 0) {
1389 if (stat(file, &s) < 0) {
1390 mbox_unlock_file(dest_fp, mbox_path);
1391 g_warning("invalid file - %s.\n", file);
1398 fprintf(dest_fp, "From - %s", ctime(&s.st_mtime));
1401 fputs(from_line, dest_fp);
1404 n_read = fread(buf, 1, sizeof(buf), src_fp);
1405 if ((n_read < (gint) sizeof(buf)) && ferror(src_fp))
1407 if (fwrite(buf, n_read, 1, dest_fp) < 1) {
1408 mbox_unlock_file(dest_fp, mbox_path);
1409 g_warning("writing to %s failed.\n", mbox_path);
1410 ftruncate(fileno(dest_fp), old_size);
1417 if (n_read < (gint) sizeof(buf))
1423 if (ferror(src_fp)) {
1424 FILE_OP_ERROR(mbox_path, "fread");
1427 mbox_unlock_file(dest_fp, mbox_path);
1429 if (fclose(src_fp) == -1) {
1430 FILE_OP_ERROR(file, "fclose");
1434 if (fclose(dest_fp) == -1) {
1435 FILE_OP_ERROR(mbox_path, "fclose");
1441 ftruncate(fileno(dest_fp), old_size);
1449 return dest->last_num;
1453 gint mbox_remove_msg(Folder *folder, FolderItem *item, gint num)
1455 struct _message * msg;
1458 mbox_path = mbox_folder_get_path(folder, item);
1459 if (mbox_path == NULL)
1462 mbox_cache_synchronize(mbox_path, TRUE);
1464 msg = mbox_cache_get_msg(mbox_path, num);
1469 MSG_SET_PERM_FLAGS(msg->flags, MSG_REALLY_DELETED);
1474 gint mbox_remove_all_msg(Folder *folder, FolderItem *item)
1479 mbox_path = mbox_folder_get_path(folder, item);
1480 if (mbox_path == NULL)
1483 fp = fopen(mbox_path, "wb");
1497 gint mbox_move_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1502 filename = mbox_fetch_msg(folder, msginfo->folder, msginfo->msgnum);
1503 if (filename == NULL)
1506 msgnum = mbox_add_msg(folder, dest, filename, TRUE);
1509 MSG_SET_FLAGS(msginfo->flags, MSG_REALLY_DELETED);
1510 mbox_change_flags(folder, msginfo->folder, msginfo);
1516 gint mbox_move_msgs_with_dest(Folder *folder, FolderItem *dest, GSList *msglist)
1519 gchar * mbox_path = NULL;
1521 for(l = msglist ; l != NULL ; l = g_slist_next(l)) {
1522 MsgInfo * msginfo = (MsgInfo *) l->data;
1524 if (msginfo->folder && mbox_path == NULL)
1525 mbox_path = mbox_folder_get_path(msginfo->folder);
1527 mbox_move_msg(folder, dest, msginfo);
1531 mbox_cache_synchronize(mbox_path);
1535 mbox_path = mbox_folder_get_path(dest);
1536 mbox_cache_synchronize(mbox_path);
1539 return dest->last_num;
1544 gint mbox_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1549 filename = mbox_fetch_msg(folder, msginfo->folder, msginfo->msgnum);
1550 if (filename == NULL)
1553 msgnum = mbox_add_msg(folder, dest, filename, FALSE);
1558 gint mbox_copy_msgs_with_dest(Folder *folder, FolderItem *dest, GSList *msglist)
1561 gchar * mbox_path = NULL;
1563 for(l = msglist ; l != NULL ; l = g_slist_next(l)) {
1564 MsgInfo * msginfo = (MsgInfo *) l->data;
1566 if (msginfo->folder && mbox_path == NULL)
1567 mbox_path = mbox_folder_get_path(msginfo->folder);
1569 mbox_copy_msg(folder, dest, msginfo);
1573 mbox_cache_synchronize(mbox_path);
1577 mbox_path = mbox_folder_get_path(dest);
1578 mbox_cache_synchronize(mbox_path);
1581 return dest->last_num;
1585 struct _copy_flags_info
1591 typedef struct _copy_flags_info CopyFlagsInfo;
1593 GSList * copy_flags_data = NULL;
1595 gint mbox_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1597 Folder * src_folder;
1600 CopyFlagsInfo * flags_info;
1602 src_folder = msginfo->folder->folder;
1605 mbox_path = mbox_folder_get_path(msginfo->folder);
1606 mbox_rewrite(mbox_path);
1610 filename = folder_item_fetch_msg(msginfo->folder,
1612 if (filename == NULL)
1615 num = mbox_add_msg(folder, dest, filename, FALSE);
1618 mbox_path = mbox_folder_get_path(dest);
1619 msg = mbox_cache_get_msg(mbox_path, num);
1621 msg->flags = msginfo->flags;
1628 flags_info = g_new0(CopyFlagsInfo, 1);
1629 flags_info->num = num;
1630 flags_info->flags = msginfo->flags;
1631 copy_flags_data = g_slist_append(copy_flags_data, flags_info);
1636 void mbox_scan_folder(Folder *folder, FolderItem *item)
1643 mbox_path = mbox_folder_get_path(folder, item);
1644 if (mbox_path == NULL)
1647 mbox_cache_synchronize(mbox_path, TRUE);
1649 cached = mbox_cache_get_mbox(mbox_path);
1651 if (cached == NULL) {
1653 item->unread_msgs = 0;
1654 item->total_msgs = 0;
1660 n_msg = mbox_cache_get_count(mbox_path);
1663 item->new_msgs = item->unread_msgs = item->total_msgs = 0;
1670 for(l = mbox_cache_get_msg_list(mbox_path) ; l != NULL ;
1671 l = g_list_next(l)) {
1672 struct _message * msg = (struct _message *) l->data;
1673 if (!MSG_IS_REALLY_DELETED(msg->flags))
1675 if (MSG_IS_NEW(msg->flags) && !MSG_IS_IGNORE_THREAD(msg->flags))
1677 if (MSG_IS_UNREAD(msg->flags) && !MSG_IS_IGNORE_THREAD(msg->flags))
1681 item->new_msgs = new;
1682 item->unread_msgs = unread;
1683 item->total_msgs = total;
1686 debug_print("Last number in dir %s = %d\n", mbox_path,
1688 item->last_num = n_msg;
1692 gchar * mbox_get_virtual_path(FolderItem * item)
1697 if (item->parent == NULL) {
1701 gchar * parent_path;
1702 gchar * result_path;
1704 parent_path = mbox_get_virtual_path(item->parent);
1705 if (parent_path == NULL)
1706 result_path = g_strdup(item->name);
1708 result_path = g_strconcat(parent_path,
1711 g_free(parent_path);
1717 static gboolean mbox_write_data(FILE * mbox_fp, FILE * new_fp,
1718 gchar * new_filename, gint size)
1726 while (pos < size) {
1727 if ((size - pos) > (gint) sizeof(buf))
1732 n_read = fread(buf, 1, max, mbox_fp);
1734 if (n_read < max && ferror(mbox_fp)) {
1737 if (fwrite(buf, n_read, 1, new_fp) < 1) {
1738 g_warning("writing to %s failed.\n", new_filename);
1751 static gboolean mbox_write_message(FILE * mbox_fp, FILE * new_fp,
1752 gchar * new_filename,
1753 struct _message * msg)
1756 GPtrArray * headers;
1759 fseek(mbox_fp, msg->header, SEEK_SET);
1761 headers = procheader_get_header_array_asis(mbox_fp);
1763 for (i = 0; i < (gint) headers->len; i++) {
1764 Header * h = g_ptr_array_index(headers, i);
1766 if (!procheader_headername_equal(h->name,
1768 !procheader_headername_equal(h->name,
1770 fwrite(h->name, strlen(h->name),
1772 if (h->name[strlen(h->name) - 1] != ' ')
1773 fwrite(" ", 1, 1, new_fp);
1774 fwrite(h->body, strlen(h->body),
1776 fwrite("\n", 1, 1, new_fp);
1778 procheader_header_free(h);
1779 g_ptr_array_remove_index(headers, i);
1783 g_ptr_array_free(headers, FALSE);
1785 if (!MSG_IS_INVALID(msg->flags)) {
1787 fwrite("Status: ", strlen("Status: "), 1, new_fp);
1788 if (!MSG_IS_UNREAD(msg->flags))
1789 fwrite("R", 1, 1, new_fp);
1790 fwrite("O", 1, 1, new_fp);
1791 fwrite("\n", 1, 1, new_fp);
1793 /* X-Status header */
1794 if (MSG_IS_REALLY_DELETED(msg->flags)
1795 || MSG_IS_MARKED(msg->flags)
1796 || MSG_IS_DELETED(msg->flags)
1797 || MSG_IS_REPLIED(msg->flags)
1798 || MSG_IS_FORWARDED(msg->flags)) {
1799 fwrite("X-Status: ", strlen("X-Status: "), 1, new_fp);
1800 if (MSG_IS_REALLY_DELETED(msg->flags))
1801 fwrite("D", 1, 1, new_fp); /* really deleted */
1803 if (MSG_IS_MARKED(msg->flags))
1804 fwrite("F", 1, 1, new_fp);
1805 if (MSG_IS_DELETED(msg->flags))
1806 fwrite("d", 1, 1, new_fp);
1807 if (MSG_IS_REPLIED(msg->flags))
1808 fwrite("r", 1, 1, new_fp);
1809 if (MSG_IS_FORWARDED(msg->flags))
1810 fwrite("f", 1, 1, new_fp);
1812 fwrite("\n", 1, 1, new_fp);
1816 fwrite("\n", 1, 1, new_fp);
1818 size = msg->end - msg->content;
1819 fseek(mbox_fp, msg->content, SEEK_SET);
1821 return mbox_write_data(mbox_fp, new_fp, new_filename, size);
1824 void mbox_change_flags(Folder * folder, FolderItem * item, MsgInfo * info, MsgPermFlags newflags)
1826 struct _message * msg;
1830 mbox_path = mbox_folder_get_path(folder, item);
1831 if (mbox_path == NULL)
1834 info->flags.perm_flags = newflags;
1836 msg = mbox_cache_get_msg(mbox_path, info->msgnum);
1838 cache = mbox_cache_get_mbox(mbox_path);
1842 if ((msg == NULL) || (cache == NULL))
1845 msg->flags = info->flags;
1847 cache->modification = TRUE;
1851 static gboolean mbox_purge_deleted(gchar * mbox)
1858 gboolean modification = FALSE;
1862 mbox_cache_synchronize(mbox, TRUE);
1864 msg_list = mbox_cache_get_msg_list(mbox);
1866 for(l = msg_list ; l != NULL ; l = g_list_next(l)) {
1867 struct _message * msg = (struct _message *) l->data;
1868 if (!MSG_IS_INVALID(msg->flags) && MSG_IS_REALLY_DELETED(msg->flags)) {
1869 modification = TRUE;
1874 if (!modification) {
1875 debug_print("no deleted messages - %s\n", mbox);
1879 debug_print("purge deleted messages - %s\n", mbox);
1881 mbox_fp = fopen(mbox, "rb+");
1882 mbox_lockwrite_file(mbox_fp, mbox);
1884 mbox_cache_synchronize_from_file(mbox_fp, mbox, TRUE);
1886 new = g_strconcat(mbox, ".", itos((int) mbox), NULL);
1887 new_fp = fopen(new, "wb");
1889 if (change_file_mode_rw(new_fp, new) < 0) {
1890 FILE_OP_ERROR(new, "chmod");
1891 g_warning("can't change file mode\n");
1894 mbox_lockwrite_file(new_fp, new);
1899 msg_list = mbox_cache_get_msg_list(mbox);
1900 for(l = msg_list ; l != NULL ; l = g_list_next(l)) {
1901 struct _message * msg = (struct _message *) l->data;
1902 if (MSG_IS_INVALID(msg->flags) || !MSG_IS_REALLY_DELETED(msg->flags)) {
1903 if (!mbox_write_message(mbox_fp, new_fp, new, msg)) {
1913 if (rename(new, mbox) == -1) {
1914 g_warning("can't rename %s to %s\n", new, mbox);
1915 mbox_unlock_file(new_fp, new);
1917 mbox_unlock_file(mbox_fp, mbox);
1923 if (change_file_mode_rw(new_fp, mbox) < 0) {
1924 FILE_OP_ERROR(new, "chmod");
1925 g_warning("can't change file mode\n");
1928 mbox_unlock_file(new_fp, new);
1932 mbox_unlock_file(mbox_fp, mbox);
1936 debug_print("%i messages written - %s\n", count, mbox);
1938 mbox_cache_synchronize(mbox, FALSE);
1943 #define MAKE_DIR_IF_NOT_EXIST(dir) \
1945 if (!is_dir_exist(dir)) { \
1946 if (is_file_exist(dir)) { \
1947 g_warning("File `%s' already exists.\n" \
1948 "Can't create folder.", dir); \
1951 if (mkdir(dir, S_IRWXU) < 0) { \
1952 FILE_OP_ERROR(dir, "mkdir"); \
1955 if (chmod(dir, S_IRWXU) < 0) \
1956 FILE_OP_ERROR(dir, "chmod"); \
1960 gint mbox_create_tree(Folder *folder)
1964 g_return_val_if_fail(folder != NULL, -1);
1966 CHDIR_RETURN_VAL_IF_FAIL(get_home_dir(), -1);
1967 rootpath = LOCAL_FOLDER(folder)->rootpath;
1968 MAKE_DIR_IF_NOT_EXIST(rootpath);
1969 CHDIR_RETURN_VAL_IF_FAIL(rootpath, -1);
1974 #undef MAKE_DIR_IF_NOT_EXIST
1976 static gchar * mbox_get_new_path(FolderItem * parent, gchar * name)
1980 if (strchr(name, '/') == NULL) {
1981 if (parent->path != NULL)
1982 path = g_strconcat(parent->path, ".sbd", G_DIR_SEPARATOR_S, name, NULL);
1984 path = g_strdup(name);
1987 path = g_strdup(name);
1992 static gchar * mbox_get_folderitem_name(gchar * name)
1996 foldername = g_strdup(g_basename(name));
2001 FolderItem *mbox_create_folder(Folder *folder, FolderItem *parent,
2005 FolderItem *new_item;
2008 g_return_val_if_fail(folder != NULL, NULL);
2009 g_return_val_if_fail(parent != NULL, NULL);
2010 g_return_val_if_fail(name != NULL, NULL);
2012 path = mbox_get_new_path(parent, (gchar *) name);
2014 foldername = mbox_get_folderitem_name((gchar *) name);
2016 new_item = folder_item_new(folder, foldername, path);
2017 folder_item_append(parent, new_item);
2019 if (!strcmp(name, "inbox")) {
2020 new_item->stype = F_INBOX;
2021 new_item->folder->inbox = new_item;
2022 } else if (!strcmp(name, "outbox")) {
2023 new_item->stype = F_OUTBOX;
2024 new_item->folder->outbox = new_item;
2025 } else if (!strcmp(name, "draft")) {
2026 new_item->stype = F_DRAFT;
2027 new_item->folder->draft = new_item;
2028 } else if (!strcmp(name, "queue")) {
2029 new_item->stype = F_QUEUE;
2030 new_item->folder->queue = new_item;
2031 } else if (!strcmp(name, "trash")) {
2032 new_item->stype = F_TRASH;
2033 new_item->folder->trash = new_item;
2042 gint mbox_rename_folder(Folder *folder, FolderItem *item, const gchar *name)
2047 g_return_val_if_fail(folder != NULL, -1);
2048 g_return_val_if_fail(item != NULL, -1);
2049 g_return_val_if_fail(item->path != NULL, -1);
2050 g_return_val_if_fail(name != NULL, -1);
2052 path = mbox_get_new_path(item->parent, (gchar *) name);
2053 foldername = mbox_get_folderitem_name((gchar *) name);
2055 if (rename(item->path, path) == -1) {
2058 g_warning("Cannot rename folder item");
2066 item->name = foldername;
2072 gint mbox_remove_folder(Folder *folder, FolderItem *item)
2074 g_return_val_if_fail(folder != NULL, -1);
2075 g_return_val_if_fail(item != NULL, -1);
2076 g_return_val_if_fail(item->path != NULL, -1);
2078 folder_item_remove(item);
2082 gint mbox_get_num_list(Folder *folder, FolderItem *item, GSList **mlist, gboolean *old_uids_valid)
2087 mboxcache * old_cache;
2091 mbox_path = mbox_folder_get_path(folder, item);
2092 if (mbox_path == NULL)
2095 old_cache = mbox_cache_get_mbox(mbox_path);
2096 *old_uids_valid = TRUE;
2097 if (old_cache != NULL) {
2098 if (stat(mbox_path, &s) < 0) {
2099 FILE_OP_ERROR(mbox_path, "stat");
2100 } else if (old_cache->mtime == s.st_mtime) {
2101 debug_print("Folder is not modified.\n");
2102 *old_uids_valid = FALSE;
2106 mbox_purge_deleted(mbox_path);
2108 fp = fopen(mbox_path, "rb");
2115 mbox_lockread_file(fp, mbox_path);
2117 mbox_cache_synchronize_from_file(fp, mbox_path, TRUE);
2119 item->last_num = mbox_cache_get_count(mbox_path);
2121 for(l = mbox_cache_get_msg_list(mbox_path) ; l != NULL ;
2122 l = g_list_next(l)) {
2123 struct _message * msg;
2125 msg = (struct _message *) l->data;
2127 if (MSG_IS_INVALID(msg->flags) || !MSG_IS_REALLY_DELETED(msg->flags)) {
2128 *mlist = g_slist_append(*mlist, GINT_TO_POINTER(msg->msgnum));
2131 MSG_SET_PERM_FLAGS(msg->flags, MSG_REALLY_DELETED);
2135 mbox_unlock_file(fp, mbox_path);
2144 MsgInfo *mbox_get_msginfo(Folder *folder, FolderItem *item, gint num)
2147 struct _message *msg;
2151 g_return_val_if_fail(folder != NULL, NULL);
2152 g_return_val_if_fail(item != NULL, NULL);
2154 mbox_path = mbox_folder_get_path(folder, item);
2156 g_return_val_if_fail(mbox_path != NULL, NULL);
2158 src = fopen(mbox_path, "rb");
2163 mbox_lockread_file(src, mbox_path);
2164 mbox_cache_synchronize_from_file(src, mbox_path, TRUE);
2166 msg = mbox_cache_get_msg(mbox_path, num);
2168 mbox_unlock_file(src, mbox_path);
2174 fseek(src, msg->header, SEEK_SET);
2175 msginfo = mbox_parse_msg(src, msg, item);
2177 mbox_unlock_file(src, mbox_path);
2184 gboolean mbox_scan_required(Folder *folder, FolderItem *item)
2186 mboxcache * old_cache;
2187 gboolean scan_new = TRUE;
2191 filename = mbox_folder_get_path(folder, item);
2192 old_cache = mbox_cache_get_mbox(filename);
2194 if (old_cache != NULL) {
2195 if (stat(filename, &s) < 0) {
2196 FILE_OP_ERROR(filename, "stat");
2197 } else if (old_cache->mtime == s.st_mtime) {
2198 debug_print("Folder is not modified.\n");
2208 static gchar *mbox_item_get_path(Folder *folder, FolderItem *item)
2210 gchar *itempath, *path;
2212 itempath = mbox_get_virtual_path(item);
2213 if (itempath == NULL)
2215 path = g_strconcat(get_mbox_cache_dir(),
2216 G_DIR_SEPARATOR_S, itempath, NULL);