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 void mbox_folder_init (Folder *folder,
42 static gboolean mbox_write_data(FILE * mbox_fp, FILE * new_fp,
43 gchar * new_filename, gint size);
44 static gboolean mbox_rewrite(gchar * mbox);
45 static gboolean mbox_purge_deleted(gchar * mbox);
46 static gchar * mbox_get_new_path(FolderItem * parent, gchar * name);
47 static gchar * mbox_get_folderitem_name(gchar * name);
49 MsgInfo *mbox_get_msginfo(Folder *folder, FolderItem *item, gint num);
50 gint mbox_get_num_list(Folder *folder, FolderItem *item, GSList **list);
51 gboolean mbox_check_msgnum_validity(Folder *folder, FolderItem *item);
53 FolderClass mbox_class =
58 /* Folder functions */
64 /* FolderItem functions */
74 mbox_check_msgnum_validity,
76 /* Message functions */
92 FolderClass *mbox_get_class()
97 Folder *mbox_folder_new(const gchar *name, const gchar *path)
101 folder = (Folder *)g_new0(MBOXFolder, 1);
102 folder->class = &mbox_class;
103 mbox_folder_init(folder, name, path);
108 void mbox_folder_destroy(Folder *folder)
110 folder_local_folder_destroy(LOCAL_FOLDER(folder));
113 static void mbox_folder_init(Folder *folder, const gchar *name, const gchar *path)
115 folder_local_folder_init(folder, name, path);
118 static void mbox_folder_create_parent(const gchar * path)
120 if (!is_file_exist(path)) {
123 new_path = g_dirname(path);
124 if (new_path[strlen(new_path) - 1] == G_DIR_SEPARATOR)
125 new_path[strlen(new_path) - 1] = '\0';
127 if (!is_dir_exist(new_path))
128 make_dir_hier(new_path);
135 static gchar *mbox_folder_get_path(FolderItem *item)
140 g_return_val_if_fail(item != NULL, NULL);
142 if (item->path && item->path[0] == G_DIR_SEPARATOR) {
143 mbox_folder_create_parent(item->path);
144 return g_strdup(item->path);
147 folder_path = g_strdup(LOCAL_FOLDER(item->folder)->rootpath);
148 g_return_val_if_fail(folder_path != NULL, NULL);
150 if (folder_path[0] == G_DIR_SEPARATOR) {
152 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
156 path = g_strdup(folder_path);
159 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
160 folder_path, G_DIR_SEPARATOR_S,
163 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
169 mbox_folder_create_parent(path);
175 /**********************************************************/
179 /**********************************************************/
182 static GSList * file_lock = NULL;
184 static gboolean mbox_file_lock_file(gchar * base)
186 gchar *lockfile, *locklink;
190 lockfile = g_strdup_printf("%s.%d", base, getpid());
191 if ((lockfp = fopen(lockfile, "wb")) == NULL) {
192 FILE_OP_ERROR(lockfile, "fopen");
193 g_warning("can't create lock file %s\n", lockfile);
194 g_warning("use 'flock' instead of 'file' if possible.\n");
199 fprintf(lockfp, "%d\n", getpid());
202 locklink = g_strconcat(base, ".lock", NULL);
203 while (link(lockfile, locklink) < 0) {
204 FILE_OP_ERROR(lockfile, "link");
206 g_warning("can't create %s\n", lockfile);
213 g_warning("mailbox is owned by another"
214 " process, waiting...\n");
225 static gboolean mbox_fcntl_lockwrite_file(FILE * fp)
229 lck.l_type = F_WRLCK;
234 if (fcntl(fileno(fp), F_SETLK, &lck) < 0)
240 static gboolean mbox_fcntl_lockread_file(FILE * fp)
244 lck.l_type = F_RDLCK;
249 if (fcntl(fileno(fp), F_SETLK, &lck) < 0)
255 static gboolean mbox_fcntl_unlock_file(FILE * fp)
259 lck.l_type = F_UNLCK;
264 if (fcntl(fileno(fp), F_SETLK, &lck) < 0)
270 static gboolean mbox_file_unlock_file(gchar * base)
274 lockfile = g_strdup_printf("%s.lock", base);
281 static gboolean mbox_lockread_file(FILE * fp, gchar * base)
285 result = mbox_fcntl_lockread_file(fp);
287 if ((result = mbox_file_lock_file(base)) == TRUE) {
288 file_lock = g_slist_append(file_lock, g_strdup(base));
289 debug_print("lockfile lock %s.\n", base);
292 g_warning("could not lock read file %s\n", base);
295 debug_print("fcntl lock %s.\n", base);
300 static gboolean mbox_lockwrite_file(FILE * fp, gchar * base)
304 result = mbox_fcntl_lockwrite_file(fp);
306 if ((result = mbox_file_lock_file(base)) == TRUE) {
307 file_lock = g_slist_append(file_lock, g_strdup(base));
308 debug_print("lockfile lock %s.\n", base);
311 g_warning("could not lock write file %s\n", base);
314 debug_print("fcntl lock %s.\n", base);
319 static gboolean mbox_unlock_file(FILE * fp, gchar * base)
321 gboolean result = FALSE;
323 gboolean unlocked = FALSE;
325 for(l = file_lock ; l != NULL ; l = g_slist_next(l)) {
326 gchar * data = l->data;
328 if (strcmp(data, base) == 0) {
329 file_lock = g_slist_remove(file_lock, data);
331 result = mbox_file_unlock_file(base);
333 debug_print("lockfile unlock - %s.\n", base);
339 result = mbox_fcntl_unlock_file(fp);
340 debug_print("fcntl unlock - %s.\n", base);
346 /**********************************************************/
350 /**********************************************************/
352 #define MAILFILE_ERROR_NO_ERROR 0x000
353 #define MAILFILE_ERROR_FILE_NOT_FOUND 0x001
354 #define MAILFILE_ERROR_MEMORY 0x002
355 #define MAILFILE_ERROR_MESSAGE_NOT_FOUND 0x003
357 static int mailfile_error = MAILFILE_ERROR_NO_ERROR;
359 #define STATE_BEGIN 0x000
360 #define STATE_TEXT_READ 0x001
361 #define STATE_FROM_READ 0x002
362 #define STATE_FIELD_READ 0x003
363 #define STATE_END 0x004
364 #define STATE_END_OF_FILE 0x005
365 #define STATE_MEM_ERROR 0x006
366 #define STATE_TEXT_BEGIN 0x007
368 #define STATE_MASK 0x0FF /* filter state from functions */
370 #define STATE_RESTORE_POS 0x100 /* go back while reading */
372 typedef struct _mailfile mailfile;
396 #define MSG_IS_INVALID(msg) \
397 ((msg).perm_flags == (msg).tmp_flags && (msg).tmp_flags == -1)
399 #define MSG_SET_INVALID(msg) \
400 ((msg).perm_flags = (msg).tmp_flags = -1)
402 static int startFrom(char * s)
404 return (strncmp(s, "From ", 5) == 0);
407 static int startSpace(char * s)
409 return ((*s == ' ') || (*s == '\t'));
412 static int startEmpty(char * s)
417 static void free_msg_list(GList * l)
419 GList * elt = g_list_first(l);
424 elt = g_list_next(elt);
431 static mailfile * mailfile_init_from_file(FILE * f, gchar * filename)
434 GList * msg_list = NULL;
441 struct _message * data = NULL;
447 while (state != STATE_END_OF_FILE) {
448 if ((state & STATE_RESTORE_POS) == 0) {
449 former_pos = lastpos;
452 r = fgets(s, 256, f);
455 ignore_next = (s[strlen(s) - 1] != '\n');
460 switch(state & 0x0F) {
463 state = STATE_END_OF_FILE;
464 else if (startFrom(s)) {
465 state = STATE_FROM_READ;
467 data = g_new0(struct _message, 1);
469 free_msg_list(msg_list);
474 data->msgnum = msgnum;
475 data->offset = lastpos;
476 data->header = lastpos;
479 data->messageid = NULL;
480 data->fromspace = NULL;
481 MSG_SET_INVALID(data->flags);
482 MSG_SET_INVALID(data->old_flags);
483 data->fetched = FALSE;
484 msg_list = g_list_append(msg_list,
492 case STATE_TEXT_READ:
495 else if (startFrom(s))
496 state = STATE_END | STATE_RESTORE_POS;
498 state = STATE_TEXT_READ;
501 case STATE_TEXT_BEGIN:
502 data->content = lastpos;
505 else if (startFrom(s)) {
506 state = STATE_END | STATE_RESTORE_POS;
509 state = STATE_TEXT_READ;
513 case STATE_FROM_READ:
514 data->content = lastpos;
517 else if (startSpace(s))
518 state = STATE_FROM_READ;
519 else if (startEmpty(s))
520 state = STATE_TEXT_READ;
522 state = STATE_FIELD_READ;
525 case STATE_FIELD_READ:
526 data->content = lastpos;
529 else if (startSpace(s))
530 state = STATE_FIELD_READ;
531 else if (startEmpty(s)) {
532 state = STATE_TEXT_BEGIN;
535 state = STATE_FIELD_READ;
539 if ((state & STATE_MASK) == STATE_END) {
540 state = STATE_BEGIN | (state & STATE_RESTORE_POS);
546 r = fgets(s, 256, f);
547 if (r == NULL || *r == '\0')
550 while (s[strlen(s) - 1] != '\n');
554 mf = (mailfile *) g_new0(struct _mailfile, 1);
556 free_msg_list(msg_list);
557 mailfile_error = MAILFILE_ERROR_MEMORY;
561 mf->msg_list = g_list_first(msg_list);
563 mf->filename = g_strdup(filename);
566 mailfile_error = MAILFILE_ERROR_NO_ERROR;
571 static mailfile * mailfile_init(char * filename)
577 f = fopen(filename, "rb");
580 mailfile_error = MAILFILE_ERROR_FILE_NOT_FOUND;
584 mbox_lockread_file(f, filename);
586 mf = mailfile_init_from_file(f, filename);
588 mbox_unlock_file(f, filename);
595 static void mailfile_done(mailfile * f)
597 free_msg_list(f->msg_list);
604 #define MAX_READ 4096
606 static char * readfile(char * filename, int offset, int max_offset)
615 handle = fopen(filename, "rb");
617 if (handle == NULL) {
618 mailfile_error = MAILFILE_ERROR_FILE_NOT_FOUND;
622 size = max_offset - offset;
624 message = (char *) malloc(size + 1);
625 if (message == NULL) {
627 mailfile_error = MAILFILE_ERROR_MEMORY;
631 fseek(handle, offset, SEEK_SET);
635 if ((size - pos) > MAX_READ)
640 bread = fread(message + pos, 1, max, handle);
656 static char * mailfile_readmsg(mailfile f, int index)
662 struct _message * msginfo;
664 nth = g_list_nth(f->msg_list, index);
667 mailfile_error = MAILFILE_ERROR_MESSAGE_NOT_FOUND;
671 msginfo = (struct _message *)nth->data;
673 offset = msginfo->offset;
674 max_offset = msginfo->end;
675 message = readfile(f->filename, offset, max_offset);
677 mailfile_error = MAILFILE_ERROR_NO_ERROR;
682 static char * mailfile_readheader(mailfile f, int index)
688 struct _message * msginfo;
690 nth = g_list_nth(f->msg_list, index);
693 mailfile_error = MAILFILE_ERROR_MESSAGE_NOT_FOUND;
697 msginfo = (struct _message *)nth->data;
699 offset = msginfo->offset;
700 max_offset = msginfo->content;
701 message = readfile(f->filename, offset, max_offset);
703 mailfile_error = MAILFILE_ERROR_NO_ERROR;
708 static int mailfile_count(mailfile * f)
710 return g_list_length(f->msg_list);
713 static int mailfile_find_deleted(mailfile f, char * filename)
717 handle = fopen(filename, "rb");
720 struct _message m = elt->data;
721 n = fread(&m.deleted, sizeof(int), 1, handle);
724 elt = g_list_next(elt);
733 /**********************************************************/
735 /* mbox cache operations */
737 /**********************************************************/
744 gboolean modification;
747 typedef struct _mboxcache mboxcache;
749 static GHashTable * mbox_cache_table = NULL;
751 static MsgInfo *mbox_parse_msg(FILE * fp, struct _message * msg,
755 MsgFlags flags = { 0, 0 };
757 MSG_SET_PERM_FLAGS(flags, MSG_NEW | MSG_UNREAD);
759 g_return_val_if_fail(fp != NULL, NULL);
762 if (item->stype == F_QUEUE) {
763 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
764 } else if (item->stype == F_DRAFT) {
765 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
769 msginfo = procheader_parse_stream(fp, flags, FALSE, FALSE);
771 if (!msginfo) return NULL;
774 msginfo->msgnum = msg->msgnum;
775 msginfo->folder = item;
781 static void mbox_cache_init()
783 mbox_cache_table = g_hash_table_new(g_str_hash, g_str_equal);
786 static void mbox_cache_free_mbox(mboxcache * cache)
788 g_hash_table_remove(mbox_cache_table, cache->filename);
791 mailfile_done(cache->mf);
793 g_ptr_array_free(cache->tab_mf, FALSE);
795 g_free(cache->filename);
799 static void mbox_cache_get_msginfo_from_file(FILE * fp, GList * msg_list)
804 for(l = msg_list ; l != NULL ; l = g_list_next(l)) {
805 struct _message * msg;
807 msg = (struct _message *) l->data;
809 fseek(fp, msg->header, SEEK_SET);
810 msginfo = mbox_parse_msg(fp, msg, NULL);
814 g_strdup(msginfo->msgid);
815 if (msginfo->fromspace)
817 g_strdup(msginfo->fromspace);
818 msg->flags = msginfo->flags;
819 msg->old_flags = msginfo->flags;
821 procmsg_msginfo_free(msginfo);
826 static void mbox_cache_get_msginfo(gchar * filename, GList * msg_list)
830 fp = fopen(filename, "rb");
834 mbox_cache_get_msginfo_from_file(fp, msg_list);
838 static mboxcache * mbox_cache_read_mbox(gchar * filename)
845 if (stat(filename, &s) < 0)
848 mf = mailfile_init(filename);
852 cache = g_new0(mboxcache, 1);
854 cache->mtime = s.st_mtime;
856 cache->filename = g_strdup(filename);
857 cache->modification = FALSE;
859 cache->tab_mf = g_ptr_array_new();
860 for(l = mf->msg_list ; l != NULL ; l = g_list_next(l))
861 g_ptr_array_add(cache->tab_mf, l->data);
863 mbox_cache_get_msginfo(filename, mf->msg_list);
865 debug_print("read mbox - %s\n", filename);
870 static mboxcache * mbox_cache_read_mbox_from_file(FILE * fp, gchar * filename)
877 if (stat(filename, &s) < 0)
880 mf = mailfile_init_from_file(fp, filename);
884 cache = g_new0(mboxcache, 1);
886 cache->mtime = s.st_mtime;
888 cache->filename = g_strdup(filename);
890 cache->tab_mf = g_ptr_array_new();
891 for(l = mf->msg_list ; l != NULL ; l = g_list_next(l))
892 g_ptr_array_add(cache->tab_mf, l->data);
894 mbox_cache_get_msginfo_from_file(fp, mf->msg_list);
896 debug_print("read mbox from file - %s\n", filename);
901 static void mbox_cache_insert_mbox(mboxcache * data)
903 if (mbox_cache_table == NULL)
906 g_hash_table_insert(mbox_cache_table, data->filename, data);
909 static mboxcache * mbox_cache_get_mbox(gchar * filename)
911 if (mbox_cache_table == NULL)
914 return g_hash_table_lookup(mbox_cache_table, filename);
918 static gint mbox_cache_get_count(gchar * filename)
922 cache = mbox_cache_get_mbox(filename);
925 if (cache->mf == NULL)
927 return cache->mf->count;
930 static GList * mbox_cache_get_msg_list(gchar * filename)
934 cache = mbox_cache_get_mbox(filename);
939 if (cache->mf == NULL)
942 return cache->mf->msg_list;
945 static void mbox_cache_synchronize_lists(GList * old_msg_list,
946 GList * new_msg_list)
951 for(l2 = old_msg_list ; l2 != NULL ; l2 = g_list_next(l2)) {
952 struct _message * msg2 = l2->data;
954 if ((msg2->messageid == NULL) ||
955 (msg2->fromspace == NULL))
958 for(l = new_msg_list ; l != NULL ; l = g_list_next(l)) {
959 struct _message * msg = l->data;
961 if ((msg->messageid == NULL) ||
962 (msg->fromspace == NULL))
965 if ((strcmp(msg->messageid, msg2->messageid) == 0) &&
966 (strcmp(msg->fromspace, msg2->fromspace) == 0)) {
967 if (msg2->flags.perm_flags != msg2->old_flags.perm_flags) {
968 msg->flags = msg2->flags;
976 static void mbox_cache_synchronize(gchar * filename, gboolean sync)
978 mboxcache * new_cache;
979 mboxcache * old_cache;
980 gboolean scan_new = TRUE;
983 old_cache = mbox_cache_get_mbox(filename);
985 if (old_cache != NULL) {
986 if (stat(filename, &s) < 0) {
987 FILE_OP_ERROR(filename, "stat");
988 } else if (old_cache->mtime == s.st_mtime) {
989 debug_print("Folder is not modified.\n");
996 if (strstr(filename, "trash") == 0)
997 printf("old_cache: %p %s\n", old_cache, filename);
999 printf("begin old\n");
1000 for(l = old_cache->mf->msg_list ; l != NULL ;
1001 l = g_list_next(l)) {
1002 struct _message * msg = l->data;
1003 printf("%p\n", msg);
1005 printf("end old\n");
1009 new_cache = mbox_cache_read_mbox(filename);
1012 if (strstr(filename, "trash") == 0)
1013 printf("new_cache: %p %s\n", new_cache, filename);
1015 printf("begin new\n");
1016 for(l = new_cache->mf->msg_list ; l != NULL ;
1017 l = g_list_next(l)) {
1018 struct _message * msg = l->data;
1019 printf("%p\n", msg);
1021 printf("end new\n");
1028 if (sync && new_cache && old_cache)
1029 mbox_cache_synchronize_lists(old_cache->mf->msg_list,
1030 new_cache->mf->msg_list);
1032 if (old_cache != NULL)
1033 mbox_cache_free_mbox(old_cache);
1036 mbox_cache_insert_mbox(new_cache);
1038 printf("insert %p %s\n", new_cache, new_cache->filename);
1039 printf("inserted %s %p\n", filename,
1040 mbox_cache_get_mbox(filename));
1046 static void mbox_cache_synchronize_from_file(FILE * fp, gchar * filename,
1049 mboxcache * new_cache;
1050 mboxcache * old_cache;
1051 gboolean scan_new = TRUE;
1054 old_cache = mbox_cache_get_mbox(filename);
1056 if (old_cache != NULL) {
1057 if (stat(filename, &s) < 0) {
1058 FILE_OP_ERROR(filename, "stat");
1059 } else if (old_cache->mtime == s.st_mtime) {
1060 debug_print("Folder is not modified.\n");
1071 if (strstr(filename, "trash") == 0)
1072 printf("old_cache: %p %s\n", old_cache, filename);
1075 printf("begin old\n");
1076 for(l = old_cache->mf->msg_list ; l != NULL ;
1077 l = g_list_next(l)) {
1078 struct _message * msg = l->data;
1079 printf("%p\n", msg);
1081 printf("end old\n");
1085 new_cache = mbox_cache_read_mbox_from_file(fp, filename);
1088 if (strstr(filename, "trash") == 0)
1089 printf("new_cache: %p %s\n", new_cache, filename);
1092 printf("begin new\n");
1093 for(l = new_cache->mf->msg_list ; l != NULL ;
1094 l = g_list_next(l)) {
1095 struct _message * msg = l->data;
1096 printf("%p\n", msg);
1098 printf("end new\n");
1105 if (sync && new_cache && old_cache)
1106 mbox_cache_synchronize_lists(old_cache->mf->msg_list,
1107 new_cache->mf->msg_list);
1109 if (old_cache != NULL)
1110 mbox_cache_free_mbox(old_cache);
1113 mbox_cache_insert_mbox(new_cache);
1115 printf("insert %p %s\n", new_cache, new_cache->filename);
1116 printf("inserted %s %p\n", filename,
1117 mbox_cache_get_mbox(filename));
1123 gboolean mbox_cache_msg_fetched(gchar * filename, gint num)
1125 struct _message * msg;
1128 cache = mbox_cache_get_mbox(filename);
1133 msg = (struct _message *) g_ptr_array_index(cache->tab_mf,
1138 return msg->fetched;
1141 void mbox_cache_msg_set_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 msg->fetched = TRUE;
1159 struct _message * mbox_cache_get_msg(gchar * filename, gint num)
1163 cache = mbox_cache_get_mbox(filename);
1165 if (cache == NULL) {
1169 return (struct _message *) g_ptr_array_index(cache->tab_mf,
1174 /**********************************************************/
1176 /* mbox operations */
1178 /**********************************************************/
1181 GSList *mbox_get_msg_list(Folder *folder, FolderItem *item, gboolean use_cache)
1190 struct timeval tv_before, tv_after, tv_result;
1192 gettimeofday(&tv_before, NULL);
1197 mbox_path = mbox_folder_get_path(item);
1199 if (mbox_path == NULL)
1202 mbox_purge_deleted(mbox_path);
1204 fp = fopen(mbox_path, "rb");
1211 mbox_lockread_file(fp, mbox_path);
1213 mbox_cache_synchronize_from_file(fp, mbox_path, TRUE);
1215 item->last_num = mbox_cache_get_count(mbox_path);
1217 for(l = mbox_cache_get_msg_list(mbox_path) ; l != NULL ;
1218 l = g_list_next(l)) {
1219 struct _message * msg;
1221 msg = (struct _message *) l->data;
1223 if (MSG_IS_INVALID(msg->flags) || !MSG_IS_REALLY_DELETED(msg->flags)) {
1224 fseek(fp, msg->header, SEEK_SET);
1226 msginfo = mbox_parse_msg(fp, msg, item);
1228 if (!MSG_IS_INVALID(msg->flags))
1229 msginfo->flags = msg->flags;
1231 msg->old_flags = msginfo->flags;
1232 msg->flags = msginfo->flags;
1235 mlist = g_slist_append(mlist, msginfo);
1238 MSG_SET_PERM_FLAGS(msg->flags, MSG_REALLY_DELETED);
1242 mbox_unlock_file(fp, mbox_path);
1249 gettimeofday(&tv_after, NULL);
1251 timersub(&tv_after, &tv_before, &tv_result);
1252 g_print("mbox_get_msg_list: %s: elapsed time: %ld.%06ld sec\n",
1253 mbox_path, tv_result.tv_sec, tv_result.tv_usec);
1259 static gboolean mbox_extract_msg(FolderItem * item, gint msgnum,
1260 gchar * dest_filename)
1262 struct _message * msg;
1269 /* GList * msg_list;*/
1270 gboolean already_fetched;
1273 mbox_path = mbox_folder_get_path(item);
1275 if (mbox_path == NULL)
1278 src = fopen(mbox_path, "rb");
1284 mbox_lockread_file(src, mbox_path);
1286 mbox_cache_synchronize_from_file(src, mbox_path, TRUE);
1288 already_fetched = mbox_cache_msg_fetched(mbox_path, msgnum);
1290 if (already_fetched) {
1291 mbox_unlock_file(src, mbox_path);
1297 msg = mbox_cache_get_msg(mbox_path, msgnum);
1300 mbox_unlock_file(src, mbox_path);
1306 offset = msg->offset;
1307 max_offset = msg->end;
1309 size = max_offset - offset;
1311 fseek(src, offset, SEEK_SET);
1313 dest = fopen(dest_filename, "wb");
1315 mbox_unlock_file(src, mbox_path);
1321 if (change_file_mode_rw(dest, dest_filename) < 0) {
1322 FILE_OP_ERROR(dest_filename, "chmod");
1323 g_warning("can't change file mode\n");
1326 if (!mbox_write_data(src, dest, dest_filename, size)) {
1327 mbox_unlock_file(src, mbox_path);
1330 unlink(dest_filename);
1338 FILE_OP_ERROR(mbox_path, "fread");
1342 mbox_cache_msg_set_fetched(mbox_path, msgnum);
1344 if (fclose(dest) == -1) {
1345 FILE_OP_ERROR(dest_filename, "fclose");
1349 mbox_unlock_file(src, mbox_path);
1351 if (fclose(src) == -1) {
1352 FILE_OP_ERROR(mbox_path, "fclose");
1359 unlink(dest_filename);
1366 gchar *mbox_fetch_msg(Folder *folder, FolderItem *item, gint num)
1371 g_return_val_if_fail(item != NULL, NULL);
1373 path = folder_item_get_path(item);
1374 if (!is_dir_exist(path))
1375 make_dir_hier(path);
1377 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(num), NULL);
1381 if (!mbox_extract_msg(item, num, filename)) {
1389 gint mbox_add_msg(Folder *folder, FolderItem *dest, const gchar *file,
1390 gboolean remove_source)
1399 gchar from_line[MSGBUFSIZE];
1401 if (dest->last_num < 0) {
1402 mbox_scan_folder(folder, dest);
1403 if (dest->last_num < 0) return -1;
1406 src_fp = fopen(file, "rb");
1407 if (src_fp == NULL) {
1411 mbox_path = mbox_folder_get_path(dest);
1412 if (mbox_path == NULL)
1415 dest_fp = fopen(mbox_path, "ab");
1416 if (dest_fp == NULL) {
1422 if (change_file_mode_rw(dest_fp, mbox_path) < 0) {
1423 FILE_OP_ERROR(mbox_path, "chmod");
1424 g_warning("can't change file mode\n");
1427 old_size = ftell(dest_fp);
1429 mbox_lockwrite_file(dest_fp, mbox_path);
1431 if (fgets(from_line, sizeof(from_line), src_fp) == NULL) {
1432 mbox_unlock_file(dest_fp, mbox_path);
1433 g_warning("unvalid file - %s.\n", file);
1440 if (strncmp(from_line, "From ", 5) != 0) {
1443 if (stat(file, &s) < 0) {
1444 mbox_unlock_file(dest_fp, mbox_path);
1445 g_warning("invalid file - %s.\n", file);
1452 fprintf(dest_fp, "From - %s", ctime(&s.st_mtime));
1455 fputs(from_line, dest_fp);
1458 n_read = fread(buf, 1, sizeof(buf), src_fp);
1459 if ((n_read < (gint) sizeof(buf)) && ferror(src_fp))
1461 if (fwrite(buf, n_read, 1, dest_fp) < 1) {
1462 mbox_unlock_file(dest_fp, mbox_path);
1463 g_warning("writing to %s failed.\n", mbox_path);
1464 ftruncate(fileno(dest_fp), old_size);
1471 if (n_read < (gint) sizeof(buf))
1477 if (ferror(src_fp)) {
1478 FILE_OP_ERROR(mbox_path, "fread");
1481 mbox_unlock_file(dest_fp, mbox_path);
1483 if (fclose(src_fp) == -1) {
1484 FILE_OP_ERROR(file, "fclose");
1488 if (fclose(dest_fp) == -1) {
1489 FILE_OP_ERROR(mbox_path, "fclose");
1495 ftruncate(fileno(dest_fp), old_size);
1500 if (remove_source) {
1501 if (unlink(file) < 0)
1502 FILE_OP_ERROR(file, "unlink");
1508 return dest->last_num;
1512 gint mbox_remove_msg(Folder *folder, FolderItem *item, gint num)
1514 struct _message * msg;
1517 mbox_path = mbox_folder_get_path(item);
1518 if (mbox_path == NULL)
1521 mbox_cache_synchronize(mbox_path, TRUE);
1523 msg = mbox_cache_get_msg(mbox_path, num);
1528 MSG_SET_PERM_FLAGS(msg->flags, MSG_REALLY_DELETED);
1533 gint mbox_remove_all_msg(Folder *folder, FolderItem *item)
1538 mbox_path = mbox_folder_get_path(item);
1539 if (mbox_path == NULL)
1542 fp = fopen(mbox_path, "wb");
1556 gint mbox_move_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1561 filename = mbox_fetch_msg(folder, msginfo->folder, msginfo->msgnum);
1562 if (filename == NULL)
1565 msgnum = mbox_add_msg(folder, dest, filename, TRUE);
1568 MSG_SET_FLAGS(msginfo->flags, MSG_REALLY_DELETED);
1569 mbox_change_flags(folder, msginfo->folder, msginfo);
1575 gint mbox_move_msgs_with_dest(Folder *folder, FolderItem *dest, GSList *msglist)
1578 gchar * mbox_path = NULL;
1580 for(l = msglist ; l != NULL ; l = g_slist_next(l)) {
1581 MsgInfo * msginfo = (MsgInfo *) l->data;
1583 if (msginfo->folder && mbox_path == NULL)
1584 mbox_path = mbox_folder_get_path(msginfo->folder);
1586 mbox_move_msg(folder, dest, msginfo);
1590 mbox_cache_synchronize(mbox_path);
1594 mbox_path = mbox_folder_get_path(dest);
1595 mbox_cache_synchronize(mbox_path);
1598 return dest->last_num;
1603 gint mbox_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1608 filename = mbox_fetch_msg(folder, msginfo->folder, msginfo->msgnum);
1609 if (filename == NULL)
1612 msgnum = mbox_add_msg(folder, dest, filename, FALSE);
1617 gint mbox_copy_msgs_with_dest(Folder *folder, FolderItem *dest, GSList *msglist)
1620 gchar * mbox_path = NULL;
1622 for(l = msglist ; l != NULL ; l = g_slist_next(l)) {
1623 MsgInfo * msginfo = (MsgInfo *) l->data;
1625 if (msginfo->folder && mbox_path == NULL)
1626 mbox_path = mbox_folder_get_path(msginfo->folder);
1628 mbox_copy_msg(folder, dest, msginfo);
1632 mbox_cache_synchronize(mbox_path);
1636 mbox_path = mbox_folder_get_path(dest);
1637 mbox_cache_synchronize(mbox_path);
1640 return dest->last_num;
1644 struct _copy_flags_info
1650 typedef struct _copy_flags_info CopyFlagsInfo;
1652 GSList * copy_flags_data = NULL;
1654 gint mbox_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1656 Folder * src_folder;
1659 CopyFlagsInfo * flags_info;
1661 src_folder = msginfo->folder->folder;
1664 mbox_path = mbox_folder_get_path(msginfo->folder);
1665 mbox_rewrite(mbox_path);
1669 filename = folder_item_fetch_msg(msginfo->folder,
1671 if (filename == NULL)
1674 num = mbox_add_msg(folder, dest, filename, FALSE);
1677 mbox_path = mbox_folder_get_path(dest);
1678 msg = mbox_cache_get_msg(mbox_path, num);
1680 msg->flags = msginfo->flags;
1687 flags_info = g_new0(CopyFlagsInfo, 1);
1688 flags_info->num = num;
1689 flags_info->flags = msginfo->flags;
1690 copy_flags_data = g_slist_append(copy_flags_data, flags_info);
1695 void mbox_finished_copy(Folder *folder, FolderItem *dest)
1701 mbox_path = mbox_folder_get_path(dest);
1702 if (mbox_path == NULL)
1705 mbox_cache_synchronize(mbox_path, TRUE);
1707 for(l = copy_flags_data ; l != NULL ; l = g_slist_next(l)) {
1708 CopyFlagsInfo * flags_info = l->data;
1709 struct _message * msg;
1711 msg = mbox_cache_get_msg(mbox_path, flags_info->num);
1713 msg->flags = flags_info->flags;
1717 if (copy_flags_data != NULL) {
1718 cache = mbox_cache_get_mbox(mbox_path);
1719 cache->modification = TRUE;
1722 g_slist_free(copy_flags_data);
1723 copy_flags_data = NULL;
1725 mbox_rewrite(mbox_path);
1730 void mbox_scan_folder(Folder *folder, FolderItem *item)
1737 mbox_path = mbox_folder_get_path(item);
1738 if (mbox_path == NULL)
1741 mbox_cache_synchronize(mbox_path, TRUE);
1743 cached = mbox_cache_get_mbox(mbox_path);
1745 if (cached == NULL) {
1754 n_msg = mbox_cache_get_count(mbox_path);
1757 item->new = item->unread = item->total = 0;
1764 for(l = mbox_cache_get_msg_list(mbox_path) ; l != NULL ;
1765 l = g_list_next(l)) {
1766 struct _message * msg = (struct _message *) l->data;
1767 if (!MSG_IS_REALLY_DELETED(msg->flags))
1769 if (MSG_IS_NEW(msg->flags) && !MSG_IS_IGNORE_THREAD(msg->flags))
1771 if (MSG_IS_UNREAD(msg->flags) && !MSG_IS_IGNORE_THREAD(msg->flags))
1776 item->unread = unread;
1777 item->total = total;
1780 debug_print("Last number in dir %s = %d\n", mbox_path,
1782 item->last_num = n_msg;
1786 gchar * mbox_get_virtual_path(FolderItem * item)
1791 if (item->parent == NULL) {
1795 gchar * parent_path;
1796 gchar * result_path;
1798 parent_path = mbox_get_virtual_path(item->parent);
1799 if (parent_path == NULL)
1800 result_path = g_strdup(item->name);
1802 result_path = g_strconcat(parent_path,
1805 g_free(parent_path);
1811 static gboolean mbox_write_data(FILE * mbox_fp, FILE * new_fp,
1812 gchar * new_filename, gint size)
1820 while (pos < size) {
1821 if ((size - pos) > (gint) sizeof(buf))
1826 n_read = fread(buf, 1, max, mbox_fp);
1828 if (n_read < max && ferror(mbox_fp)) {
1831 if (fwrite(buf, n_read, 1, new_fp) < 1) {
1832 g_warning("writing to %s failed.\n", new_filename);
1845 static gboolean mbox_write_message(FILE * mbox_fp, FILE * new_fp,
1846 gchar * new_filename,
1847 struct _message * msg)
1850 GPtrArray * headers;
1853 fseek(mbox_fp, msg->header, SEEK_SET);
1855 headers = procheader_get_header_array_asis(mbox_fp);
1857 for (i = 0; i < (gint) headers->len; i++) {
1858 Header * h = g_ptr_array_index(headers, i);
1860 if (!procheader_headername_equal(h->name,
1862 !procheader_headername_equal(h->name,
1864 fwrite(h->name, strlen(h->name),
1866 if (h->name[strlen(h->name) - 1] != ' ')
1867 fwrite(" ", 1, 1, new_fp);
1868 fwrite(h->body, strlen(h->body),
1870 fwrite("\n", 1, 1, new_fp);
1872 procheader_header_free(h);
1873 g_ptr_array_remove_index(headers, i);
1877 g_ptr_array_free(headers, FALSE);
1879 if (!MSG_IS_INVALID(msg->flags)) {
1881 fwrite("Status: ", strlen("Status: "), 1, new_fp);
1882 if (!MSG_IS_UNREAD(msg->flags))
1883 fwrite("R", 1, 1, new_fp);
1884 fwrite("O", 1, 1, new_fp);
1885 fwrite("\n", 1, 1, new_fp);
1887 /* X-Status header */
1888 if (MSG_IS_REALLY_DELETED(msg->flags)
1889 || MSG_IS_MARKED(msg->flags)
1890 || MSG_IS_DELETED(msg->flags)
1891 || MSG_IS_REPLIED(msg->flags)
1892 || MSG_IS_FORWARDED(msg->flags)) {
1893 fwrite("X-Status: ", strlen("X-Status: "), 1, new_fp);
1894 if (MSG_IS_REALLY_DELETED(msg->flags))
1895 fwrite("D", 1, 1, new_fp); /* really deleted */
1897 if (MSG_IS_MARKED(msg->flags))
1898 fwrite("F", 1, 1, new_fp);
1899 if (MSG_IS_DELETED(msg->flags))
1900 fwrite("d", 1, 1, new_fp);
1901 if (MSG_IS_REPLIED(msg->flags))
1902 fwrite("r", 1, 1, new_fp);
1903 if (MSG_IS_FORWARDED(msg->flags))
1904 fwrite("f", 1, 1, new_fp);
1906 fwrite("\n", 1, 1, new_fp);
1910 fwrite("\n", 1, 1, new_fp);
1912 size = msg->end - msg->content;
1913 fseek(mbox_fp, msg->content, SEEK_SET);
1915 return mbox_write_data(mbox_fp, new_fp, new_filename, size);
1918 void mbox_update_mark(Folder * folder, FolderItem * item)
1922 mbox_path = mbox_folder_get_path(item);
1923 if (mbox_path == NULL)
1926 mbox_rewrite(mbox_path);
1930 void mbox_change_flags(Folder * folder, FolderItem * item, MsgInfo * info)
1932 struct _message * msg;
1936 mbox_path = mbox_folder_get_path(item);
1937 if (mbox_path == NULL)
1940 msg = mbox_cache_get_msg(mbox_path, info->msgnum);
1942 cache = mbox_cache_get_mbox(mbox_path);
1946 if ((msg == NULL) || (cache == NULL))
1949 msg->flags = info->flags;
1951 cache->modification = TRUE;
1956 static gboolean mbox_rewrite(gchar * mbox)
1967 msg_list = mbox_cache_get_msg_list(mbox);
1969 cache = mbox_cache_get_mbox(mbox);
1973 if (!cache->modification) {
1974 debug_print("no modification - %s\n", mbox);
1978 debug_print("save modification - %s\n", mbox);
1980 mbox_fp = fopen(mbox, "rb+");
1981 mbox_lockwrite_file(mbox_fp, mbox);
1983 mbox_cache_synchronize_from_file(mbox_fp, mbox, TRUE);
1985 new = g_strconcat(mbox, ".", itos((int) mbox), NULL);
1986 new_fp = fopen(new, "wb");
1988 if (change_file_mode_rw(new_fp, new) < 0) {
1989 FILE_OP_ERROR(new, "chmod");
1990 g_warning("can't change file mode\n");
1993 mbox_lockwrite_file(new_fp, new);
1998 msg_list = mbox_cache_get_msg_list(mbox);
1999 for(l = msg_list ; l != NULL ; l = g_list_next(l)) {
2000 struct _message * msg = (struct _message *) l->data;
2001 if (!mbox_write_message(mbox_fp, new_fp, new, msg)) {
2010 if (rename(new, mbox) == -1) {
2011 g_warning("can't rename %s to %s\n", new, mbox);
2012 mbox_unlock_file(new_fp, new);
2014 mbox_unlock_file(mbox_fp, mbox);
2020 if (change_file_mode_rw(new_fp, mbox) < 0) {
2021 FILE_OP_ERROR(new, "chmod");
2022 g_warning("can't change file mode\n");
2025 mbox_unlock_file(new_fp, new);
2029 mbox_unlock_file(mbox_fp, mbox);
2033 debug_print("%i messages written - %s\n", count, mbox);
2035 cache = mbox_cache_get_mbox(mbox);
2040 mbox_cache_synchronize(mbox, FALSE);
2047 static gboolean mbox_purge_deleted(gchar * mbox)
2054 gboolean modification = FALSE;
2058 mbox_cache_synchronize(mbox, TRUE);
2060 msg_list = mbox_cache_get_msg_list(mbox);
2062 for(l = msg_list ; l != NULL ; l = g_list_next(l)) {
2063 struct _message * msg = (struct _message *) l->data;
2064 if (!MSG_IS_INVALID(msg->flags) && MSG_IS_REALLY_DELETED(msg->flags)) {
2065 modification = TRUE;
2070 if (!modification) {
2071 debug_print("no deleted messages - %s\n", mbox);
2075 debug_print("purge deleted messages - %s\n", mbox);
2077 mbox_fp = fopen(mbox, "rb+");
2078 mbox_lockwrite_file(mbox_fp, mbox);
2080 mbox_cache_synchronize_from_file(mbox_fp, mbox, TRUE);
2082 new = g_strconcat(mbox, ".", itos((int) mbox), NULL);
2083 new_fp = fopen(new, "wb");
2085 if (change_file_mode_rw(new_fp, new) < 0) {
2086 FILE_OP_ERROR(new, "chmod");
2087 g_warning("can't change file mode\n");
2090 mbox_lockwrite_file(new_fp, new);
2095 msg_list = mbox_cache_get_msg_list(mbox);
2096 for(l = msg_list ; l != NULL ; l = g_list_next(l)) {
2097 struct _message * msg = (struct _message *) l->data;
2098 if (MSG_IS_INVALID(msg->flags) || !MSG_IS_REALLY_DELETED(msg->flags)) {
2099 if (!mbox_write_message(mbox_fp, new_fp, new, msg)) {
2109 if (rename(new, mbox) == -1) {
2110 g_warning("can't rename %s to %s\n", new, mbox);
2111 mbox_unlock_file(new_fp, new);
2113 mbox_unlock_file(mbox_fp, mbox);
2119 if (change_file_mode_rw(new_fp, mbox) < 0) {
2120 FILE_OP_ERROR(new, "chmod");
2121 g_warning("can't change file mode\n");
2124 mbox_unlock_file(new_fp, new);
2128 mbox_unlock_file(mbox_fp, mbox);
2132 debug_print("%i messages written - %s\n", count, mbox);
2134 mbox_cache_synchronize(mbox, FALSE);
2139 #define MAKE_DIR_IF_NOT_EXIST(dir) \
2141 if (!is_dir_exist(dir)) { \
2142 if (is_file_exist(dir)) { \
2143 g_warning("File `%s' already exists.\n" \
2144 "Can't create folder.", dir); \
2147 if (mkdir(dir, S_IRWXU) < 0) { \
2148 FILE_OP_ERROR(dir, "mkdir"); \
2151 if (chmod(dir, S_IRWXU) < 0) \
2152 FILE_OP_ERROR(dir, "chmod"); \
2156 gint mbox_create_tree(Folder *folder)
2160 g_return_val_if_fail(folder != NULL, -1);
2162 CHDIR_RETURN_VAL_IF_FAIL(get_home_dir(), -1);
2163 rootpath = LOCAL_FOLDER(folder)->rootpath;
2164 MAKE_DIR_IF_NOT_EXIST(rootpath);
2165 CHDIR_RETURN_VAL_IF_FAIL(rootpath, -1);
2170 #undef MAKE_DIR_IF_NOT_EXIST
2172 static gchar * mbox_get_new_path(FolderItem * parent, gchar * name)
2176 if (strchr(name, '/') == NULL) {
2177 if (parent->path != NULL)
2178 path = g_strconcat(parent->path, ".sbd", G_DIR_SEPARATOR_S, name, NULL);
2180 path = g_strdup(name);
2183 path = g_strdup(name);
2188 static gchar * mbox_get_folderitem_name(gchar * name)
2192 foldername = g_strdup(g_basename(name));
2197 FolderItem *mbox_create_folder(Folder *folder, FolderItem *parent,
2201 FolderItem *new_item;
2204 g_return_val_if_fail(folder != NULL, NULL);
2205 g_return_val_if_fail(parent != NULL, NULL);
2206 g_return_val_if_fail(name != NULL, NULL);
2208 path = mbox_get_new_path(parent, (gchar *) name);
2210 foldername = mbox_get_folderitem_name((gchar *) name);
2212 new_item = folder_item_new(folder, foldername, path);
2213 folder_item_append(parent, new_item);
2215 if (!strcmp(name, "inbox")) {
2216 new_item->stype = F_INBOX;
2217 new_item->folder->inbox = new_item;
2218 } else if (!strcmp(name, "outbox")) {
2219 new_item->stype = F_OUTBOX;
2220 new_item->folder->outbox = new_item;
2221 } else if (!strcmp(name, "draft")) {
2222 new_item->stype = F_DRAFT;
2223 new_item->folder->draft = new_item;
2224 } else if (!strcmp(name, "queue")) {
2225 new_item->stype = F_QUEUE;
2226 new_item->folder->queue = new_item;
2227 } else if (!strcmp(name, "trash")) {
2228 new_item->stype = F_TRASH;
2229 new_item->folder->trash = new_item;
2238 gint mbox_rename_folder(Folder *folder, FolderItem *item, const gchar *name)
2243 g_return_val_if_fail(folder != NULL, -1);
2244 g_return_val_if_fail(item != NULL, -1);
2245 g_return_val_if_fail(item->path != NULL, -1);
2246 g_return_val_if_fail(name != NULL, -1);
2248 path = mbox_get_new_path(item->parent, (gchar *) name);
2249 foldername = mbox_get_folderitem_name((gchar *) name);
2251 if (rename(item->path, path) == -1) {
2254 g_warning("Cannot rename folder item");
2262 item->name = foldername;
2268 gint mbox_remove_folder(Folder *folder, FolderItem *item)
2270 g_return_val_if_fail(folder != NULL, -1);
2271 g_return_val_if_fail(item != NULL, -1);
2272 g_return_val_if_fail(item->path != NULL, -1);
2274 folder_item_remove(item);
2278 gint mbox_get_num_list(Folder *folder, FolderItem *item, GSList **mlist)
2285 mbox_path = mbox_folder_get_path(item);
2287 if (mbox_path == NULL)
2290 mbox_purge_deleted(mbox_path);
2292 fp = fopen(mbox_path, "rb");
2299 mbox_lockread_file(fp, mbox_path);
2301 mbox_cache_synchronize_from_file(fp, mbox_path, TRUE);
2303 item->last_num = mbox_cache_get_count(mbox_path);
2305 for(l = mbox_cache_get_msg_list(mbox_path) ; l != NULL ;
2306 l = g_list_next(l)) {
2307 struct _message * msg;
2309 msg = (struct _message *) l->data;
2311 if (MSG_IS_INVALID(msg->flags) || !MSG_IS_REALLY_DELETED(msg->flags)) {
2312 *mlist = g_slist_append(*mlist, GINT_TO_POINTER(msg->msgnum));
2315 MSG_SET_PERM_FLAGS(msg->flags, MSG_REALLY_DELETED);
2319 mbox_unlock_file(fp, mbox_path);
2328 MsgInfo *mbox_get_msginfo(Folder *folder, FolderItem *item, gint num)
2331 struct _message *msg;
2335 g_return_val_if_fail(folder != NULL, NULL);
2336 g_return_val_if_fail(item != NULL, NULL);
2338 mbox_path = mbox_folder_get_path(item);
2340 g_return_val_if_fail(mbox_path != NULL, NULL);
2342 src = fopen(mbox_path, "rb");
2347 mbox_lockread_file(src, mbox_path);
2348 mbox_cache_synchronize_from_file(src, mbox_path, TRUE);
2350 msg = mbox_cache_get_msg(mbox_path, num);
2352 mbox_unlock_file(src, mbox_path);
2358 fseek(src, msg->header, SEEK_SET);
2359 msginfo = mbox_parse_msg(src, msg, item);
2361 mbox_unlock_file(src, mbox_path);
2368 gboolean mbox_check_msgnum_validity(Folder *folder, FolderItem *item)
2370 mboxcache * old_cache;
2371 gboolean scan_new = TRUE;
2375 filename = mbox_folder_get_path(item);
2377 old_cache = mbox_cache_get_mbox(filename);
2379 if (old_cache != NULL) {
2380 if (stat(filename, &s) < 0) {
2381 FILE_OP_ERROR(filename, "stat");
2382 } else if (old_cache->mtime == s.st_mtime) {
2383 debug_print("Folder is not modified.\n");