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);
76 FolderClass mbox_class =
82 /* Folder functions */
88 /* FolderItem functions */
102 /* Message functions */
116 FolderClass *mbox_get_class(void)
121 Folder *mbox_folder_new(const gchar *name, const gchar *path)
125 folder = (Folder *)g_new0(MBOXFolder, 1);
126 folder->klass = &mbox_class;
127 mbox_folder_init(folder, name, path);
132 void mbox_folder_destroy(Folder *folder)
134 folder_local_folder_destroy(LOCAL_FOLDER(folder));
137 static void mbox_folder_init(Folder *folder, const gchar *name, const gchar *path)
139 folder_local_folder_init(folder, name, path);
142 static void mbox_folder_create_parent(const gchar * path)
144 if (!is_file_exist(path)) {
147 new_path = g_dirname(path);
148 if (new_path[strlen(new_path) - 1] == G_DIR_SEPARATOR)
149 new_path[strlen(new_path) - 1] = '\0';
151 if (!is_dir_exist(new_path))
152 make_dir_hier(new_path);
159 gchar *mbox_folder_get_path(Folder *folder, FolderItem *item)
164 g_return_val_if_fail(item != NULL, NULL);
166 if (item->path && item->path[0] == G_DIR_SEPARATOR) {
167 mbox_folder_create_parent(item->path);
168 return g_strdup(item->path);
171 folder_path = g_strdup(LOCAL_FOLDER(item->folder)->rootpath);
172 g_return_val_if_fail(folder_path != NULL, NULL);
174 if (folder_path[0] == G_DIR_SEPARATOR) {
176 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
180 path = g_strdup(folder_path);
183 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
184 folder_path, G_DIR_SEPARATOR_S,
187 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
193 mbox_folder_create_parent(path);
199 /**********************************************************/
203 /**********************************************************/
206 static GSList * file_lock = NULL;
208 static gboolean mbox_file_lock_file(gchar * base)
210 gchar *lockfile, *locklink;
214 lockfile = g_strdup_printf("%s.%d", base, getpid());
215 if ((lockfp = fopen(lockfile, "wb")) == NULL) {
216 FILE_OP_ERROR(lockfile, "fopen");
217 g_warning("can't create lock file %s\n", lockfile);
218 g_warning("use 'flock' instead of 'file' if possible.\n");
223 fprintf(lockfp, "%d\n", getpid());
226 locklink = g_strconcat(base, ".lock", NULL);
227 while (link(lockfile, locklink) < 0) {
228 FILE_OP_ERROR(lockfile, "link");
230 g_warning("can't create %s\n", lockfile);
237 g_warning("mailbox is owned by another"
238 " process, waiting...\n");
249 static gboolean mbox_fcntl_lockwrite_file(FILE * fp)
253 lck.l_type = F_WRLCK;
258 if (fcntl(fileno(fp), F_SETLK, &lck) < 0)
264 static gboolean mbox_fcntl_lockread_file(FILE * fp)
268 lck.l_type = F_RDLCK;
273 if (fcntl(fileno(fp), F_SETLK, &lck) < 0)
279 static gboolean mbox_fcntl_unlock_file(FILE * fp)
283 lck.l_type = F_UNLCK;
288 if (fcntl(fileno(fp), F_SETLK, &lck) < 0)
294 static gboolean mbox_file_unlock_file(gchar * base)
298 lockfile = g_strdup_printf("%s.lock", base);
305 static gboolean mbox_lockread_file(FILE * fp, gchar * base)
309 result = mbox_fcntl_lockread_file(fp);
311 if ((result = mbox_file_lock_file(base)) == TRUE) {
312 file_lock = g_slist_append(file_lock, g_strdup(base));
313 debug_print("lockfile lock %s.\n", base);
316 g_warning("could not lock read file %s\n", base);
319 debug_print("fcntl lock %s.\n", base);
324 static gboolean mbox_lockwrite_file(FILE * fp, gchar * base)
328 result = mbox_fcntl_lockwrite_file(fp);
330 if ((result = mbox_file_lock_file(base)) == TRUE) {
331 file_lock = g_slist_append(file_lock, g_strdup(base));
332 debug_print("lockfile lock %s.\n", base);
335 g_warning("could not lock write file %s\n", base);
338 debug_print("fcntl lock %s.\n", base);
343 static gboolean mbox_unlock_file(FILE * fp, gchar * base)
345 gboolean result = FALSE;
347 gboolean unlocked = FALSE;
349 for(l = file_lock ; l != NULL ; l = g_slist_next(l)) {
350 gchar * data = l->data;
352 if (strcmp(data, base) == 0) {
353 file_lock = g_slist_remove(file_lock, data);
355 result = mbox_file_unlock_file(base);
357 debug_print("lockfile unlock - %s.\n", base);
363 result = mbox_fcntl_unlock_file(fp);
364 debug_print("fcntl unlock - %s.\n", base);
370 /**********************************************************/
374 /**********************************************************/
376 #define MAILFILE_ERROR_NO_ERROR 0x000
377 #define MAILFILE_ERROR_FILE_NOT_FOUND 0x001
378 #define MAILFILE_ERROR_MEMORY 0x002
379 #define MAILFILE_ERROR_MESSAGE_NOT_FOUND 0x003
381 static int mailfile_error = MAILFILE_ERROR_NO_ERROR;
383 #define STATE_BEGIN 0x000
384 #define STATE_TEXT_READ 0x001
385 #define STATE_FROM_READ 0x002
386 #define STATE_FIELD_READ 0x003
387 #define STATE_END 0x004
388 #define STATE_END_OF_FILE 0x005
389 #define STATE_MEM_ERROR 0x006
390 #define STATE_TEXT_BEGIN 0x007
392 #define STATE_MASK 0x0FF /* filter state from functions */
394 #define STATE_RESTORE_POS 0x100 /* go back while reading */
396 typedef struct _mailfile mailfile;
420 #define MSG_IS_INVALID(msg) \
421 ((msg).perm_flags == (msg).tmp_flags && (msg).tmp_flags == -1)
423 #define MSG_SET_INVALID(msg) \
424 ((msg).perm_flags = (msg).tmp_flags = -1)
426 static int startFrom(char * s)
428 return (strncmp(s, "From ", 5) == 0);
431 static int startSpace(char * s)
433 return ((*s == ' ') || (*s == '\t'));
436 static int startEmpty(char * s)
441 static void free_msg_list(GList * l)
443 GList * elt = g_list_first(l);
448 elt = g_list_next(elt);
455 static mailfile * mailfile_init_from_file(FILE * f, gchar * filename)
458 GList * msg_list = NULL;
465 struct _message * data = NULL;
471 while (state != STATE_END_OF_FILE) {
472 if ((state & STATE_RESTORE_POS) == 0) {
473 former_pos = lastpos;
476 r = fgets(s, 256, f);
479 ignore_next = (s[strlen(s) - 1] != '\n');
484 switch(state & 0x0F) {
487 state = STATE_END_OF_FILE;
488 else if (startFrom(s)) {
489 state = STATE_FROM_READ;
491 data = g_new0(struct _message, 1);
493 free_msg_list(msg_list);
498 data->msgnum = msgnum;
499 data->offset = lastpos;
500 data->header = lastpos;
503 data->messageid = NULL;
504 data->fromspace = NULL;
505 MSG_SET_INVALID(data->flags);
506 MSG_SET_INVALID(data->old_flags);
507 data->fetched = FALSE;
508 msg_list = g_list_append(msg_list,
516 case STATE_TEXT_READ:
519 else if (startFrom(s))
520 state = STATE_END | STATE_RESTORE_POS;
522 state = STATE_TEXT_READ;
525 case STATE_TEXT_BEGIN:
526 data->content = lastpos;
529 else if (startFrom(s)) {
530 state = STATE_END | STATE_RESTORE_POS;
533 state = STATE_TEXT_READ;
537 case STATE_FROM_READ:
538 data->content = lastpos;
541 else if (startSpace(s))
542 state = STATE_FROM_READ;
543 else if (startEmpty(s))
544 state = STATE_TEXT_READ;
546 state = STATE_FIELD_READ;
549 case STATE_FIELD_READ:
550 data->content = lastpos;
553 else if (startSpace(s))
554 state = STATE_FIELD_READ;
555 else if (startEmpty(s)) {
556 state = STATE_TEXT_BEGIN;
559 state = STATE_FIELD_READ;
563 if ((state & STATE_MASK) == STATE_END) {
564 state = STATE_BEGIN | (state & STATE_RESTORE_POS);
570 r = fgets(s, 256, f);
571 if (r == NULL || *r == '\0')
574 while (s[strlen(s) - 1] != '\n');
578 mf = (mailfile *) g_new0(struct _mailfile, 1);
580 free_msg_list(msg_list);
581 mailfile_error = MAILFILE_ERROR_MEMORY;
585 mf->msg_list = g_list_first(msg_list);
587 mf->filename = g_strdup(filename);
590 mailfile_error = MAILFILE_ERROR_NO_ERROR;
595 static mailfile * mailfile_init(char * filename)
601 f = fopen(filename, "rb");
604 mailfile_error = MAILFILE_ERROR_FILE_NOT_FOUND;
608 mbox_lockread_file(f, filename);
610 mf = mailfile_init_from_file(f, filename);
612 mbox_unlock_file(f, filename);
619 static void mailfile_done(mailfile * f)
621 free_msg_list(f->msg_list);
628 #define MAX_READ 4096
630 static char * readfile(char * filename, int offset, int max_offset)
639 handle = fopen(filename, "rb");
641 if (handle == NULL) {
642 mailfile_error = MAILFILE_ERROR_FILE_NOT_FOUND;
646 size = max_offset - offset;
648 message = (char *) malloc(size + 1);
649 if (message == NULL) {
651 mailfile_error = MAILFILE_ERROR_MEMORY;
655 fseek(handle, offset, SEEK_SET);
659 if ((size - pos) > MAX_READ)
664 bread = fread(message + pos, 1, max, handle);
680 static char * mailfile_readmsg(mailfile f, int index)
686 struct _message * msginfo;
688 nth = g_list_nth(f->msg_list, index);
691 mailfile_error = MAILFILE_ERROR_MESSAGE_NOT_FOUND;
695 msginfo = (struct _message *)nth->data;
697 offset = msginfo->offset;
698 max_offset = msginfo->end;
699 message = readfile(f->filename, offset, max_offset);
701 mailfile_error = MAILFILE_ERROR_NO_ERROR;
706 static char * mailfile_readheader(mailfile f, int index)
712 struct _message * msginfo;
714 nth = g_list_nth(f->msg_list, index);
717 mailfile_error = MAILFILE_ERROR_MESSAGE_NOT_FOUND;
721 msginfo = (struct _message *)nth->data;
723 offset = msginfo->offset;
724 max_offset = msginfo->content;
725 message = readfile(f->filename, offset, max_offset);
727 mailfile_error = MAILFILE_ERROR_NO_ERROR;
732 static int mailfile_count(mailfile * f)
734 return g_list_length(f->msg_list);
737 static int mailfile_find_deleted(mailfile f, char * filename)
741 handle = fopen(filename, "rb");
744 struct _message m = elt->data;
745 n = fread(&m.deleted, sizeof(int), 1, handle);
748 elt = g_list_next(elt);
757 /**********************************************************/
759 /* mbox cache operations */
761 /**********************************************************/
768 gboolean modification;
771 typedef struct _mboxcache mboxcache;
773 static GHashTable * mbox_cache_table = NULL;
775 static MsgInfo *mbox_parse_msg(FILE * fp, struct _message * msg,
779 MsgFlags flags = { 0, 0 };
781 MSG_SET_PERM_FLAGS(flags, MSG_NEW | MSG_UNREAD);
783 g_return_val_if_fail(fp != NULL, NULL);
786 if (item->stype == F_QUEUE) {
787 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
788 } else if (item->stype == F_DRAFT) {
789 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
793 msginfo = procheader_parse_stream(fp, flags, FALSE, FALSE);
795 if (!msginfo) return NULL;
798 msginfo->msgnum = msg->msgnum;
799 msginfo->folder = item;
805 static void mbox_cache_init()
807 mbox_cache_table = g_hash_table_new(g_str_hash, g_str_equal);
810 static void mbox_cache_free_mbox(mboxcache * cache)
812 g_hash_table_remove(mbox_cache_table, cache->filename);
815 mailfile_done(cache->mf);
817 g_ptr_array_free(cache->tab_mf, FALSE);
819 g_free(cache->filename);
823 static void mbox_cache_get_msginfo_from_file(FILE * fp, GList * msg_list)
828 for(l = msg_list ; l != NULL ; l = g_list_next(l)) {
829 struct _message * msg;
831 msg = (struct _message *) l->data;
833 fseek(fp, msg->header, SEEK_SET);
834 msginfo = mbox_parse_msg(fp, msg, NULL);
838 g_strdup(msginfo->msgid);
839 if (msginfo->fromspace)
841 g_strdup(msginfo->fromspace);
842 msg->flags = msginfo->flags;
843 msg->old_flags = msginfo->flags;
845 procmsg_msginfo_free(msginfo);
850 static void mbox_cache_get_msginfo(gchar * filename, GList * msg_list)
854 fp = fopen(filename, "rb");
858 mbox_cache_get_msginfo_from_file(fp, msg_list);
862 static mboxcache * mbox_cache_read_mbox(gchar * filename)
869 if (stat(filename, &s) < 0)
872 mf = mailfile_init(filename);
876 cache = g_new0(mboxcache, 1);
878 cache->mtime = s.st_mtime;
880 cache->filename = g_strdup(filename);
881 cache->modification = FALSE;
883 cache->tab_mf = g_ptr_array_new();
884 for(l = mf->msg_list ; l != NULL ; l = g_list_next(l))
885 g_ptr_array_add(cache->tab_mf, l->data);
887 mbox_cache_get_msginfo(filename, mf->msg_list);
889 debug_print("read mbox - %s\n", filename);
894 static mboxcache * mbox_cache_read_mbox_from_file(FILE * fp, gchar * filename)
901 if (stat(filename, &s) < 0)
904 mf = mailfile_init_from_file(fp, filename);
908 cache = g_new0(mboxcache, 1);
910 cache->mtime = s.st_mtime;
912 cache->filename = g_strdup(filename);
914 cache->tab_mf = g_ptr_array_new();
915 for(l = mf->msg_list ; l != NULL ; l = g_list_next(l))
916 g_ptr_array_add(cache->tab_mf, l->data);
918 mbox_cache_get_msginfo_from_file(fp, mf->msg_list);
920 debug_print("read mbox from file - %s\n", filename);
925 static void mbox_cache_insert_mbox(mboxcache * data)
927 if (mbox_cache_table == NULL)
930 g_hash_table_insert(mbox_cache_table, data->filename, data);
933 static mboxcache * mbox_cache_get_mbox(gchar * filename)
935 if (mbox_cache_table == NULL)
938 return g_hash_table_lookup(mbox_cache_table, filename);
942 static gint mbox_cache_get_count(gchar * filename)
946 cache = mbox_cache_get_mbox(filename);
949 if (cache->mf == NULL)
951 return cache->mf->count;
954 static GList * mbox_cache_get_msg_list(gchar * filename)
958 cache = mbox_cache_get_mbox(filename);
963 if (cache->mf == NULL)
966 return cache->mf->msg_list;
969 static void mbox_cache_synchronize_lists(GList * old_msg_list,
970 GList * new_msg_list)
975 for(l2 = old_msg_list ; l2 != NULL ; l2 = g_list_next(l2)) {
976 struct _message * msg2 = l2->data;
978 if ((msg2->messageid == NULL) ||
979 (msg2->fromspace == NULL))
982 for(l = new_msg_list ; l != NULL ; l = g_list_next(l)) {
983 struct _message * msg = l->data;
985 if ((msg->messageid == NULL) ||
986 (msg->fromspace == NULL))
989 if ((strcmp(msg->messageid, msg2->messageid) == 0) &&
990 (strcmp(msg->fromspace, msg2->fromspace) == 0)) {
991 if (msg2->flags.perm_flags != msg2->old_flags.perm_flags) {
992 msg->flags = msg2->flags;
1000 static void mbox_cache_synchronize(gchar * filename, gboolean sync)
1002 mboxcache * new_cache;
1003 mboxcache * old_cache;
1004 gboolean scan_new = TRUE;
1007 old_cache = mbox_cache_get_mbox(filename);
1009 if (old_cache != NULL) {
1010 if (stat(filename, &s) < 0) {
1011 FILE_OP_ERROR(filename, "stat");
1012 } else if (old_cache->mtime == s.st_mtime) {
1013 debug_print("Folder is not modified.\n");
1020 if (strstr(filename, "trash") == 0)
1021 printf("old_cache: %p %s\n", old_cache, filename);
1023 printf("begin old\n");
1024 for(l = old_cache->mf->msg_list ; l != NULL ;
1025 l = g_list_next(l)) {
1026 struct _message * msg = l->data;
1027 printf("%p\n", msg);
1029 printf("end old\n");
1033 new_cache = mbox_cache_read_mbox(filename);
1036 if (strstr(filename, "trash") == 0)
1037 printf("new_cache: %p %s\n", new_cache, filename);
1039 printf("begin new\n");
1040 for(l = new_cache->mf->msg_list ; l != NULL ;
1041 l = g_list_next(l)) {
1042 struct _message * msg = l->data;
1043 printf("%p\n", msg);
1045 printf("end new\n");
1052 if (sync && new_cache && old_cache)
1053 mbox_cache_synchronize_lists(old_cache->mf->msg_list,
1054 new_cache->mf->msg_list);
1056 if (old_cache != NULL)
1057 mbox_cache_free_mbox(old_cache);
1060 mbox_cache_insert_mbox(new_cache);
1062 printf("insert %p %s\n", new_cache, new_cache->filename);
1063 printf("inserted %s %p\n", filename,
1064 mbox_cache_get_mbox(filename));
1070 static void mbox_cache_synchronize_from_file(FILE * fp, gchar * filename,
1073 mboxcache * new_cache;
1074 mboxcache * old_cache;
1075 gboolean scan_new = TRUE;
1078 old_cache = mbox_cache_get_mbox(filename);
1080 if (old_cache != NULL) {
1081 if (stat(filename, &s) < 0) {
1082 FILE_OP_ERROR(filename, "stat");
1083 } else if (old_cache->mtime == s.st_mtime) {
1084 debug_print("Folder is not modified.\n");
1095 if (strstr(filename, "trash") == 0)
1096 printf("old_cache: %p %s\n", old_cache, filename);
1099 printf("begin old\n");
1100 for(l = old_cache->mf->msg_list ; l != NULL ;
1101 l = g_list_next(l)) {
1102 struct _message * msg = l->data;
1103 printf("%p\n", msg);
1105 printf("end old\n");
1109 new_cache = mbox_cache_read_mbox_from_file(fp, filename);
1112 if (strstr(filename, "trash") == 0)
1113 printf("new_cache: %p %s\n", new_cache, filename);
1116 printf("begin new\n");
1117 for(l = new_cache->mf->msg_list ; l != NULL ;
1118 l = g_list_next(l)) {
1119 struct _message * msg = l->data;
1120 printf("%p\n", msg);
1122 printf("end new\n");
1129 if (sync && new_cache && old_cache)
1130 mbox_cache_synchronize_lists(old_cache->mf->msg_list,
1131 new_cache->mf->msg_list);
1133 if (old_cache != NULL)
1134 mbox_cache_free_mbox(old_cache);
1137 mbox_cache_insert_mbox(new_cache);
1139 printf("insert %p %s\n", new_cache, new_cache->filename);
1140 printf("inserted %s %p\n", filename,
1141 mbox_cache_get_mbox(filename));
1147 gboolean mbox_cache_msg_fetched(gchar * filename, gint num)
1149 struct _message * msg;
1152 cache = mbox_cache_get_mbox(filename);
1157 msg = (struct _message *) g_ptr_array_index(cache->tab_mf,
1162 return msg->fetched;
1165 void mbox_cache_msg_set_fetched(gchar * filename, gint num)
1167 struct _message * msg;
1170 cache = mbox_cache_get_mbox(filename);
1175 msg = (struct _message *) g_ptr_array_index(cache->tab_mf,
1180 msg->fetched = TRUE;
1183 struct _message * mbox_cache_get_msg(gchar * filename, gint num)
1187 cache = mbox_cache_get_mbox(filename);
1189 if (cache == NULL) {
1193 return (struct _message *) g_ptr_array_index(cache->tab_mf,
1198 /**********************************************************/
1200 /* mbox operations */
1202 /**********************************************************/
1204 static gboolean mbox_extract_msg(FolderItem * item, gint msgnum,
1205 gchar * dest_filename)
1207 struct _message * msg;
1214 /* GList * msg_list;*/
1215 gboolean already_fetched;
1218 mbox_path = mbox_folder_get_path(item->folder, item);
1220 if (mbox_path == NULL)
1223 src = fopen(mbox_path, "rb");
1229 mbox_lockread_file(src, mbox_path);
1231 mbox_cache_synchronize_from_file(src, mbox_path, TRUE);
1233 already_fetched = mbox_cache_msg_fetched(mbox_path, msgnum);
1235 if (already_fetched) {
1236 mbox_unlock_file(src, mbox_path);
1242 msg = mbox_cache_get_msg(mbox_path, msgnum);
1245 mbox_unlock_file(src, mbox_path);
1251 offset = msg->offset;
1252 max_offset = msg->end;
1254 size = max_offset - offset;
1256 fseek(src, offset, SEEK_SET);
1258 dest = fopen(dest_filename, "wb");
1260 mbox_unlock_file(src, mbox_path);
1266 if (change_file_mode_rw(dest, dest_filename) < 0) {
1267 FILE_OP_ERROR(dest_filename, "chmod");
1268 g_warning("can't change file mode\n");
1271 if (!mbox_write_data(src, dest, dest_filename, size)) {
1272 mbox_unlock_file(src, mbox_path);
1275 unlink(dest_filename);
1283 FILE_OP_ERROR(mbox_path, "fread");
1287 mbox_cache_msg_set_fetched(mbox_path, msgnum);
1289 if (fclose(dest) == -1) {
1290 FILE_OP_ERROR(dest_filename, "fclose");
1294 mbox_unlock_file(src, mbox_path);
1296 if (fclose(src) == -1) {
1297 FILE_OP_ERROR(mbox_path, "fclose");
1304 unlink(dest_filename);
1311 gchar *mbox_fetch_msg(Folder *folder, FolderItem *item, gint num)
1316 g_return_val_if_fail(item != NULL, NULL);
1318 path = folder_item_get_path(item);
1319 if (!is_dir_exist(path))
1320 make_dir_hier(path);
1322 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(num), NULL);
1326 if (!mbox_extract_msg(item, num, filename)) {
1334 gint mbox_add_msg(Folder *folder, FolderItem *dest, const gchar *file,
1344 gchar from_line[MSGBUFSIZE];
1346 if (dest->last_num < 0) {
1347 mbox_scan_folder(folder, dest);
1348 if (dest->last_num < 0) return -1;
1351 src_fp = fopen(file, "rb");
1352 if (src_fp == NULL) {
1356 mbox_path = mbox_folder_get_path(folder, dest);
1357 if (mbox_path == NULL)
1360 dest_fp = fopen(mbox_path, "ab");
1361 if (dest_fp == NULL) {
1367 if (change_file_mode_rw(dest_fp, mbox_path) < 0) {
1368 FILE_OP_ERROR(mbox_path, "chmod");
1369 g_warning("can't change file mode\n");
1372 old_size = ftell(dest_fp);
1374 mbox_lockwrite_file(dest_fp, mbox_path);
1376 if (fgets(from_line, sizeof(from_line), src_fp) == NULL) {
1377 mbox_unlock_file(dest_fp, mbox_path);
1378 g_warning("unvalid file - %s.\n", file);
1385 if (strncmp(from_line, "From ", 5) != 0) {
1388 if (stat(file, &s) < 0) {
1389 mbox_unlock_file(dest_fp, mbox_path);
1390 g_warning("invalid file - %s.\n", file);
1397 fprintf(dest_fp, "From - %s", ctime(&s.st_mtime));
1400 fputs(from_line, dest_fp);
1403 n_read = fread(buf, 1, sizeof(buf), src_fp);
1404 if ((n_read < (gint) sizeof(buf)) && ferror(src_fp))
1406 if (fwrite(buf, n_read, 1, dest_fp) < 1) {
1407 mbox_unlock_file(dest_fp, mbox_path);
1408 g_warning("writing to %s failed.\n", mbox_path);
1409 ftruncate(fileno(dest_fp), old_size);
1416 if (n_read < (gint) sizeof(buf))
1422 if (ferror(src_fp)) {
1423 FILE_OP_ERROR(mbox_path, "fread");
1426 mbox_unlock_file(dest_fp, mbox_path);
1428 if (fclose(src_fp) == -1) {
1429 FILE_OP_ERROR(file, "fclose");
1433 if (fclose(dest_fp) == -1) {
1434 FILE_OP_ERROR(mbox_path, "fclose");
1440 ftruncate(fileno(dest_fp), old_size);
1448 return dest->last_num;
1452 gint mbox_remove_msg(Folder *folder, FolderItem *item, gint num)
1454 struct _message * msg;
1457 mbox_path = mbox_folder_get_path(folder, item);
1458 if (mbox_path == NULL)
1461 mbox_cache_synchronize(mbox_path, TRUE);
1463 msg = mbox_cache_get_msg(mbox_path, num);
1468 MSG_SET_PERM_FLAGS(msg->flags, MSG_REALLY_DELETED);
1473 gint mbox_remove_all_msg(Folder *folder, FolderItem *item)
1478 mbox_path = mbox_folder_get_path(folder, item);
1479 if (mbox_path == NULL)
1482 fp = fopen(mbox_path, "wb");
1496 gint mbox_move_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1501 filename = mbox_fetch_msg(folder, msginfo->folder, msginfo->msgnum);
1502 if (filename == NULL)
1505 msgnum = mbox_add_msg(folder, dest, filename, TRUE);
1508 MSG_SET_FLAGS(msginfo->flags, MSG_REALLY_DELETED);
1509 mbox_change_flags(folder, msginfo->folder, msginfo);
1515 gint mbox_move_msgs_with_dest(Folder *folder, FolderItem *dest, GSList *msglist)
1518 gchar * mbox_path = NULL;
1520 for(l = msglist ; l != NULL ; l = g_slist_next(l)) {
1521 MsgInfo * msginfo = (MsgInfo *) l->data;
1523 if (msginfo->folder && mbox_path == NULL)
1524 mbox_path = mbox_folder_get_path(msginfo->folder);
1526 mbox_move_msg(folder, dest, msginfo);
1530 mbox_cache_synchronize(mbox_path);
1534 mbox_path = mbox_folder_get_path(dest);
1535 mbox_cache_synchronize(mbox_path);
1538 return dest->last_num;
1543 gint mbox_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1548 filename = mbox_fetch_msg(folder, msginfo->folder, msginfo->msgnum);
1549 if (filename == NULL)
1552 msgnum = mbox_add_msg(folder, dest, filename, FALSE);
1557 gint mbox_copy_msgs_with_dest(Folder *folder, FolderItem *dest, GSList *msglist)
1560 gchar * mbox_path = NULL;
1562 for(l = msglist ; l != NULL ; l = g_slist_next(l)) {
1563 MsgInfo * msginfo = (MsgInfo *) l->data;
1565 if (msginfo->folder && mbox_path == NULL)
1566 mbox_path = mbox_folder_get_path(msginfo->folder);
1568 mbox_copy_msg(folder, dest, msginfo);
1572 mbox_cache_synchronize(mbox_path);
1576 mbox_path = mbox_folder_get_path(dest);
1577 mbox_cache_synchronize(mbox_path);
1580 return dest->last_num;
1584 struct _copy_flags_info
1590 typedef struct _copy_flags_info CopyFlagsInfo;
1592 GSList * copy_flags_data = NULL;
1594 gint mbox_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1596 Folder * src_folder;
1599 CopyFlagsInfo * flags_info;
1601 src_folder = msginfo->folder->folder;
1604 mbox_path = mbox_folder_get_path(msginfo->folder);
1605 mbox_rewrite(mbox_path);
1609 filename = folder_item_fetch_msg(msginfo->folder,
1611 if (filename == NULL)
1614 num = mbox_add_msg(folder, dest, filename, FALSE);
1617 mbox_path = mbox_folder_get_path(dest);
1618 msg = mbox_cache_get_msg(mbox_path, num);
1620 msg->flags = msginfo->flags;
1627 flags_info = g_new0(CopyFlagsInfo, 1);
1628 flags_info->num = num;
1629 flags_info->flags = msginfo->flags;
1630 copy_flags_data = g_slist_append(copy_flags_data, flags_info);
1635 void mbox_scan_folder(Folder *folder, FolderItem *item)
1642 mbox_path = mbox_folder_get_path(folder, item);
1643 if (mbox_path == NULL)
1646 mbox_cache_synchronize(mbox_path, TRUE);
1648 cached = mbox_cache_get_mbox(mbox_path);
1650 if (cached == NULL) {
1652 item->unread_msgs = 0;
1653 item->total_msgs = 0;
1659 n_msg = mbox_cache_get_count(mbox_path);
1662 item->new_msgs = item->unread_msgs = item->total_msgs = 0;
1669 for(l = mbox_cache_get_msg_list(mbox_path) ; l != NULL ;
1670 l = g_list_next(l)) {
1671 struct _message * msg = (struct _message *) l->data;
1672 if (!MSG_IS_REALLY_DELETED(msg->flags))
1674 if (MSG_IS_NEW(msg->flags) && !MSG_IS_IGNORE_THREAD(msg->flags))
1676 if (MSG_IS_UNREAD(msg->flags) && !MSG_IS_IGNORE_THREAD(msg->flags))
1680 item->new_msgs = new;
1681 item->unread_msgs = unread;
1682 item->total_msgs = total;
1685 debug_print("Last number in dir %s = %d\n", mbox_path,
1687 item->last_num = n_msg;
1691 gchar * mbox_get_virtual_path(FolderItem * item)
1696 if (item->parent == NULL) {
1700 gchar * parent_path;
1701 gchar * result_path;
1703 parent_path = mbox_get_virtual_path(item->parent);
1704 if (parent_path == NULL)
1705 result_path = g_strdup(item->name);
1707 result_path = g_strconcat(parent_path,
1710 g_free(parent_path);
1716 static gboolean mbox_write_data(FILE * mbox_fp, FILE * new_fp,
1717 gchar * new_filename, gint size)
1725 while (pos < size) {
1726 if ((size - pos) > (gint) sizeof(buf))
1731 n_read = fread(buf, 1, max, mbox_fp);
1733 if (n_read < max && ferror(mbox_fp)) {
1736 if (fwrite(buf, n_read, 1, new_fp) < 1) {
1737 g_warning("writing to %s failed.\n", new_filename);
1750 static gboolean mbox_write_message(FILE * mbox_fp, FILE * new_fp,
1751 gchar * new_filename,
1752 struct _message * msg)
1755 GPtrArray * headers;
1758 fseek(mbox_fp, msg->header, SEEK_SET);
1760 headers = procheader_get_header_array_asis(mbox_fp);
1762 for (i = 0; i < (gint) headers->len; i++) {
1763 Header * h = g_ptr_array_index(headers, i);
1765 if (!procheader_headername_equal(h->name,
1767 !procheader_headername_equal(h->name,
1769 fwrite(h->name, strlen(h->name),
1771 if (h->name[strlen(h->name) - 1] != ' ')
1772 fwrite(" ", 1, 1, new_fp);
1773 fwrite(h->body, strlen(h->body),
1775 fwrite("\n", 1, 1, new_fp);
1777 procheader_header_free(h);
1778 g_ptr_array_remove_index(headers, i);
1782 g_ptr_array_free(headers, FALSE);
1784 if (!MSG_IS_INVALID(msg->flags)) {
1786 fwrite("Status: ", strlen("Status: "), 1, new_fp);
1787 if (!MSG_IS_UNREAD(msg->flags))
1788 fwrite("R", 1, 1, new_fp);
1789 fwrite("O", 1, 1, new_fp);
1790 fwrite("\n", 1, 1, new_fp);
1792 /* X-Status header */
1793 if (MSG_IS_REALLY_DELETED(msg->flags)
1794 || MSG_IS_MARKED(msg->flags)
1795 || MSG_IS_DELETED(msg->flags)
1796 || MSG_IS_REPLIED(msg->flags)
1797 || MSG_IS_FORWARDED(msg->flags)) {
1798 fwrite("X-Status: ", strlen("X-Status: "), 1, new_fp);
1799 if (MSG_IS_REALLY_DELETED(msg->flags))
1800 fwrite("D", 1, 1, new_fp); /* really deleted */
1802 if (MSG_IS_MARKED(msg->flags))
1803 fwrite("F", 1, 1, new_fp);
1804 if (MSG_IS_DELETED(msg->flags))
1805 fwrite("d", 1, 1, new_fp);
1806 if (MSG_IS_REPLIED(msg->flags))
1807 fwrite("r", 1, 1, new_fp);
1808 if (MSG_IS_FORWARDED(msg->flags))
1809 fwrite("f", 1, 1, new_fp);
1811 fwrite("\n", 1, 1, new_fp);
1815 fwrite("\n", 1, 1, new_fp);
1817 size = msg->end - msg->content;
1818 fseek(mbox_fp, msg->content, SEEK_SET);
1820 return mbox_write_data(mbox_fp, new_fp, new_filename, size);
1823 void mbox_change_flags(Folder * folder, FolderItem * item, MsgInfo * info, MsgPermFlags newflags)
1825 struct _message * msg;
1829 mbox_path = mbox_folder_get_path(folder, item);
1830 if (mbox_path == NULL)
1833 info->flags.perm_flags = newflags;
1835 msg = mbox_cache_get_msg(mbox_path, info->msgnum);
1837 cache = mbox_cache_get_mbox(mbox_path);
1841 if ((msg == NULL) || (cache == NULL))
1844 msg->flags = info->flags;
1846 cache->modification = TRUE;
1850 static gboolean mbox_purge_deleted(gchar * mbox)
1857 gboolean modification = FALSE;
1861 mbox_cache_synchronize(mbox, TRUE);
1863 msg_list = mbox_cache_get_msg_list(mbox);
1865 for(l = msg_list ; l != NULL ; l = g_list_next(l)) {
1866 struct _message * msg = (struct _message *) l->data;
1867 if (!MSG_IS_INVALID(msg->flags) && MSG_IS_REALLY_DELETED(msg->flags)) {
1868 modification = TRUE;
1873 if (!modification) {
1874 debug_print("no deleted messages - %s\n", mbox);
1878 debug_print("purge deleted messages - %s\n", mbox);
1880 mbox_fp = fopen(mbox, "rb+");
1881 mbox_lockwrite_file(mbox_fp, mbox);
1883 mbox_cache_synchronize_from_file(mbox_fp, mbox, TRUE);
1885 new = g_strconcat(mbox, ".", itos((int) mbox), NULL);
1886 new_fp = fopen(new, "wb");
1888 if (change_file_mode_rw(new_fp, new) < 0) {
1889 FILE_OP_ERROR(new, "chmod");
1890 g_warning("can't change file mode\n");
1893 mbox_lockwrite_file(new_fp, new);
1898 msg_list = mbox_cache_get_msg_list(mbox);
1899 for(l = msg_list ; l != NULL ; l = g_list_next(l)) {
1900 struct _message * msg = (struct _message *) l->data;
1901 if (MSG_IS_INVALID(msg->flags) || !MSG_IS_REALLY_DELETED(msg->flags)) {
1902 if (!mbox_write_message(mbox_fp, new_fp, new, msg)) {
1912 if (rename(new, mbox) == -1) {
1913 g_warning("can't rename %s to %s\n", new, mbox);
1914 mbox_unlock_file(new_fp, new);
1916 mbox_unlock_file(mbox_fp, mbox);
1922 if (change_file_mode_rw(new_fp, mbox) < 0) {
1923 FILE_OP_ERROR(new, "chmod");
1924 g_warning("can't change file mode\n");
1927 mbox_unlock_file(new_fp, new);
1931 mbox_unlock_file(mbox_fp, mbox);
1935 debug_print("%i messages written - %s\n", count, mbox);
1937 mbox_cache_synchronize(mbox, FALSE);
1942 #define MAKE_DIR_IF_NOT_EXIST(dir) \
1944 if (!is_dir_exist(dir)) { \
1945 if (is_file_exist(dir)) { \
1946 g_warning("File `%s' already exists.\n" \
1947 "Can't create folder.", dir); \
1950 if (mkdir(dir, S_IRWXU) < 0) { \
1951 FILE_OP_ERROR(dir, "mkdir"); \
1954 if (chmod(dir, S_IRWXU) < 0) \
1955 FILE_OP_ERROR(dir, "chmod"); \
1959 gint mbox_create_tree(Folder *folder)
1963 g_return_val_if_fail(folder != NULL, -1);
1965 CHDIR_RETURN_VAL_IF_FAIL(get_home_dir(), -1);
1966 rootpath = LOCAL_FOLDER(folder)->rootpath;
1967 MAKE_DIR_IF_NOT_EXIST(rootpath);
1968 CHDIR_RETURN_VAL_IF_FAIL(rootpath, -1);
1973 #undef MAKE_DIR_IF_NOT_EXIST
1975 static gchar * mbox_get_new_path(FolderItem * parent, gchar * name)
1979 if (strchr(name, '/') == NULL) {
1980 if (parent->path != NULL)
1981 path = g_strconcat(parent->path, ".sbd", G_DIR_SEPARATOR_S, name, NULL);
1983 path = g_strdup(name);
1986 path = g_strdup(name);
1991 static gchar * mbox_get_folderitem_name(gchar * name)
1995 foldername = g_strdup(g_basename(name));
2000 FolderItem *mbox_create_folder(Folder *folder, FolderItem *parent,
2004 FolderItem *new_item;
2007 g_return_val_if_fail(folder != NULL, NULL);
2008 g_return_val_if_fail(parent != NULL, NULL);
2009 g_return_val_if_fail(name != NULL, NULL);
2011 path = mbox_get_new_path(parent, (gchar *) name);
2013 foldername = mbox_get_folderitem_name((gchar *) name);
2015 new_item = folder_item_new(folder, foldername, path);
2016 folder_item_append(parent, new_item);
2018 if (!strcmp(name, "inbox")) {
2019 new_item->stype = F_INBOX;
2020 new_item->folder->inbox = new_item;
2021 } else if (!strcmp(name, "outbox")) {
2022 new_item->stype = F_OUTBOX;
2023 new_item->folder->outbox = new_item;
2024 } else if (!strcmp(name, "draft")) {
2025 new_item->stype = F_DRAFT;
2026 new_item->folder->draft = new_item;
2027 } else if (!strcmp(name, "queue")) {
2028 new_item->stype = F_QUEUE;
2029 new_item->folder->queue = new_item;
2030 } else if (!strcmp(name, "trash")) {
2031 new_item->stype = F_TRASH;
2032 new_item->folder->trash = new_item;
2041 gint mbox_rename_folder(Folder *folder, FolderItem *item, const gchar *name)
2046 g_return_val_if_fail(folder != NULL, -1);
2047 g_return_val_if_fail(item != NULL, -1);
2048 g_return_val_if_fail(item->path != NULL, -1);
2049 g_return_val_if_fail(name != NULL, -1);
2051 path = mbox_get_new_path(item->parent, (gchar *) name);
2052 foldername = mbox_get_folderitem_name((gchar *) name);
2054 if (rename(item->path, path) == -1) {
2057 g_warning("Cannot rename folder item");
2065 item->name = foldername;
2071 gint mbox_remove_folder(Folder *folder, FolderItem *item)
2073 g_return_val_if_fail(folder != NULL, -1);
2074 g_return_val_if_fail(item != NULL, -1);
2075 g_return_val_if_fail(item->path != NULL, -1);
2077 folder_item_remove(item);
2081 gint mbox_get_num_list(Folder *folder, FolderItem *item, GSList **mlist, gboolean *old_uids_valid)
2086 mboxcache * old_cache;
2090 mbox_path = mbox_folder_get_path(folder, item);
2091 if (mbox_path == NULL)
2094 old_cache = mbox_cache_get_mbox(mbox_path);
2095 *old_uids_valid = TRUE;
2096 if (old_cache != NULL) {
2097 if (stat(mbox_path, &s) < 0) {
2098 FILE_OP_ERROR(mbox_path, "stat");
2099 } else if (old_cache->mtime == s.st_mtime) {
2100 debug_print("Folder is not modified.\n");
2101 *old_uids_valid = FALSE;
2105 mbox_purge_deleted(mbox_path);
2107 fp = fopen(mbox_path, "rb");
2114 mbox_lockread_file(fp, mbox_path);
2116 mbox_cache_synchronize_from_file(fp, mbox_path, TRUE);
2118 item->last_num = mbox_cache_get_count(mbox_path);
2120 for(l = mbox_cache_get_msg_list(mbox_path) ; l != NULL ;
2121 l = g_list_next(l)) {
2122 struct _message * msg;
2124 msg = (struct _message *) l->data;
2126 if (MSG_IS_INVALID(msg->flags) || !MSG_IS_REALLY_DELETED(msg->flags)) {
2127 *mlist = g_slist_append(*mlist, GINT_TO_POINTER(msg->msgnum));
2130 MSG_SET_PERM_FLAGS(msg->flags, MSG_REALLY_DELETED);
2134 mbox_unlock_file(fp, mbox_path);
2143 MsgInfo *mbox_get_msginfo(Folder *folder, FolderItem *item, gint num)
2146 struct _message *msg;
2150 g_return_val_if_fail(folder != NULL, NULL);
2151 g_return_val_if_fail(item != NULL, NULL);
2153 mbox_path = mbox_folder_get_path(folder, item);
2155 g_return_val_if_fail(mbox_path != NULL, NULL);
2157 src = fopen(mbox_path, "rb");
2162 mbox_lockread_file(src, mbox_path);
2163 mbox_cache_synchronize_from_file(src, mbox_path, TRUE);
2165 msg = mbox_cache_get_msg(mbox_path, num);
2167 mbox_unlock_file(src, mbox_path);
2173 fseek(src, msg->header, SEEK_SET);
2174 msginfo = mbox_parse_msg(src, msg, item);
2176 mbox_unlock_file(src, mbox_path);
2183 gboolean mbox_scan_required(Folder *folder, FolderItem *item)
2185 mboxcache * old_cache;
2186 gboolean scan_new = TRUE;
2190 filename = mbox_folder_get_path(folder, item);
2191 old_cache = mbox_cache_get_mbox(filename);
2193 if (old_cache != NULL) {
2194 if (stat(filename, &s) < 0) {
2195 FILE_OP_ERROR(filename, "stat");
2196 } else if (old_cache->mtime == s.st_mtime) {
2197 debug_print("Folder is not modified.\n");