diff options
author | hiro <hiro@ee746299-78ed-0310-b773-934348b2243d> | 2005-09-05 10:00:53 +0000 |
---|---|---|
committer | hiro <hiro@ee746299-78ed-0310-b773-934348b2243d> | 2005-09-05 10:00:53 +0000 |
commit | 3bf24b9336184fe9e28f7e09b9c5200a5f82b7d2 (patch) | |
tree | 51ccac6f26dcdf9fcfac1a7879477bfde2759b80 /libsylph/mh.c | |
parent | 11776e5a524745b01ac145439ac2892a29bd0826 (diff) |
moved more modules to libsylph.
git-svn-id: svn://sylpheed.sraoss.jp/sylpheed/trunk@548 ee746299-78ed-0310-b773-934348b2243d
Diffstat (limited to 'libsylph/mh.c')
-rw-r--r-- | libsylph/mh.c | 1431 |
1 files changed, 1431 insertions, 0 deletions
diff --git a/libsylph/mh.c b/libsylph/mh.c new file mode 100644 index 00000000..40554867 --- /dev/null +++ b/libsylph/mh.c @@ -0,0 +1,1431 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2005 Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "defs.h" + +#include <glib.h> +#include <glib/gi18n.h> +#include <dirent.h> +#include <sys/stat.h> +#include <time.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> + +#undef MEASURE_TIME + +#include "folder.h" +#include "mh.h" +#include "procmsg.h" +#include "procheader.h" +#include "utils.h" +#include "prefs_common.h" + +static void mh_folder_init (Folder *folder, + const gchar *name, + const gchar *path); + +static Folder *mh_folder_new (const gchar *name, + const gchar *path); +static void mh_folder_destroy (Folder *folder); + +static GSList *mh_get_msg_list (Folder *folder, + FolderItem *item, + gboolean use_cache); +static gchar *mh_fetch_msg (Folder *folder, + FolderItem *item, + gint num); +static MsgInfo *mh_get_msginfo (Folder *folder, + FolderItem *item, + gint num); +static gint mh_add_msg (Folder *folder, + FolderItem *dest, + const gchar *file, + MsgFlags *flags, + gboolean remove_source); +static gint mh_add_msgs (Folder *folder, + FolderItem *dest, + GSList *file_list, + gboolean remove_source, + gint *first); +static gint mh_move_msg (Folder *folder, + FolderItem *dest, + MsgInfo *msginfo); +static gint mh_move_msgs (Folder *folder, + FolderItem *dest, + GSList *msglist); +static gint mh_copy_msg (Folder *folder, + FolderItem *dest, + MsgInfo *msginfo); +static gint mh_copy_msgs (Folder *folder, + FolderItem *dest, + GSList *msglist); +static gint mh_remove_msg (Folder *folder, + FolderItem *item, + MsgInfo *msginfo); +static gint mh_remove_all_msg (Folder *folder, + FolderItem *item); +static gboolean mh_is_msg_changed (Folder *folder, + FolderItem *item, + MsgInfo *msginfo); +static gint mh_close (Folder *folder, + FolderItem *item); + +static gint mh_scan_folder_full (Folder *folder, + FolderItem *item, + gboolean count_sum); +static gint mh_scan_folder (Folder *folder, + FolderItem *item); +static gint mh_scan_tree (Folder *folder); + +static gint mh_create_tree (Folder *folder); +static FolderItem *mh_create_folder (Folder *folder, + FolderItem *parent, + const gchar *name); +static gint mh_rename_folder (Folder *folder, + FolderItem *item, + const gchar *name); +static gint mh_move_folder (Folder *folder, + FolderItem *item, + FolderItem *new_parent); +static gint mh_remove_folder (Folder *folder, + FolderItem *item); + +static gchar *mh_get_new_msg_filename (FolderItem *dest); + +static gint mh_do_move_msgs (Folder *folder, + FolderItem *dest, + GSList *msglist); + +static time_t mh_get_mtime (FolderItem *item); +static GSList *mh_get_uncached_msgs (GHashTable *msg_table, + FolderItem *item); +static MsgInfo *mh_parse_msg (const gchar *file, + FolderItem *item); +static void mh_remove_missing_folder_items (Folder *folder); +static void mh_scan_tree_recursive (FolderItem *item); + +static gboolean mh_rename_folder_func (GNode *node, + gpointer data); + +static FolderClass mh_class = +{ + F_MH, + + mh_folder_new, + mh_folder_destroy, + + mh_scan_tree, + mh_create_tree, + + mh_get_msg_list, + mh_fetch_msg, + mh_get_msginfo, + mh_add_msg, + mh_add_msgs, + mh_move_msg, + mh_move_msgs, + mh_copy_msg, + mh_copy_msgs, + mh_remove_msg, + NULL, + mh_remove_all_msg, + mh_is_msg_changed, + mh_close, + mh_scan_folder, + + mh_create_folder, + mh_rename_folder, + mh_move_folder, + mh_remove_folder, +}; + + +FolderClass *mh_get_class(void) +{ + return &mh_class; +} + +static Folder *mh_folder_new(const gchar *name, const gchar *path) +{ + Folder *folder; + + folder = (Folder *)g_new0(MHFolder, 1); + mh_folder_init(folder, name, path); + + return folder; +} + +static void mh_folder_destroy(Folder *folder) +{ + folder_local_folder_destroy(LOCAL_FOLDER(folder)); +} + +static void mh_folder_init(Folder *folder, const gchar *name, const gchar *path) +{ + folder->klass = mh_get_class(); + folder_local_folder_init(folder, name, path); +} + +static GSList *mh_get_msg_list(Folder *folder, FolderItem *item, + gboolean use_cache) +{ + GSList *mlist; + GHashTable *msg_table; + time_t cur_mtime; +#ifdef MEASURE_TIME + GTimer *timer; +#endif + + g_return_val_if_fail(item != NULL, NULL); + +#ifdef MEASURE_TIME + timer = g_timer_new(); +#endif + + cur_mtime = mh_get_mtime(item); + + if (use_cache && item->mtime == cur_mtime) { + debug_print("Folder is not modified.\n"); + mlist = procmsg_read_cache(item, FALSE); + if (!mlist) { + mlist = mh_get_uncached_msgs(NULL, item); + if (mlist) + item->cache_dirty = TRUE; + } + } else if (use_cache) { + GSList *newlist, *cur, *next; + gboolean strict_cache_check = prefs_common.strict_cache_check; + + if (item->stype == F_QUEUE || item->stype == F_DRAFT) + strict_cache_check = TRUE; + + mlist = procmsg_read_cache(item, strict_cache_check); + msg_table = procmsg_msg_hash_table_create(mlist); + newlist = mh_get_uncached_msgs(msg_table, item); + if (newlist) + item->cache_dirty = TRUE; + if (msg_table) + g_hash_table_destroy(msg_table); + + if (!strict_cache_check) { + /* remove nonexistent messages */ + for (cur = mlist; cur != NULL; cur = next) { + MsgInfo *msginfo = (MsgInfo *)cur->data; + next = cur->next; + if (!MSG_IS_CACHED(msginfo->flags)) { + debug_print("removing nonexistent message %d from cache\n", msginfo->msgnum); + mlist = g_slist_remove(mlist, msginfo); + procmsg_msginfo_free(msginfo); + item->cache_dirty = TRUE; + item->mark_dirty = TRUE; + } + } + } + + mlist = g_slist_concat(mlist, newlist); + } else { + mlist = mh_get_uncached_msgs(NULL, item); + item->cache_dirty = TRUE; + } + + item->mtime = cur_mtime; + + procmsg_set_flags(mlist, item); + + mlist = procmsg_sort_msg_list(mlist, item->sort_key, item->sort_type); + +#ifdef MEASURE_TIME + g_timer_stop(timer); + g_print("%s: %s: elapsed time: %f sec\n", + G_STRFUNC, item->path, g_timer_elapsed(timer, NULL)); + g_timer_destroy(timer); +#endif + debug_print("cache_dirty: %d, mark_dirty: %d\n", + item->cache_dirty, item->mark_dirty); + + return mlist; +} + +static gchar *mh_fetch_msg(Folder *folder, FolderItem *item, gint num) +{ + gchar *path; + gchar *file; + + g_return_val_if_fail(item != NULL, NULL); + g_return_val_if_fail(num > 0, NULL); + + if (item->last_num < 0 || num > item->last_num) { + mh_scan_folder(folder, item); + if (item->last_num < 0) return NULL; + } + + g_return_val_if_fail(num <= item->last_num, NULL); + + path = folder_item_get_path(item); + file = g_strconcat(path, G_DIR_SEPARATOR_S, itos(num), NULL); + g_free(path); + if (!is_file_exist(file)) { + g_free(file); + return NULL; + } + + return file; +} + +static MsgInfo *mh_get_msginfo(Folder *folder, FolderItem *item, gint num) +{ + MsgInfo *msginfo; + gchar *file; + + g_return_val_if_fail(item != NULL, NULL); + g_return_val_if_fail(num > 0, NULL); + + file = mh_fetch_msg(folder, item, num); + if (!file) return NULL; + + msginfo = mh_parse_msg(file, item); + if (msginfo) + msginfo->msgnum = num; + + g_free(file); + + return msginfo; +} + +static gchar *mh_get_new_msg_filename(FolderItem *dest) +{ + gchar *destfile; + gchar *destpath; + + destpath = folder_item_get_path(dest); + g_return_val_if_fail(destpath != NULL, NULL); + + if (!is_dir_exist(destpath)) + make_dir_hier(destpath); + + for (;;) { + destfile = g_strdup_printf("%s%c%d", destpath, G_DIR_SEPARATOR, + dest->last_num + 1); + if (is_file_entry_exist(destfile)) { + dest->last_num++; + g_free(destfile); + } else + break; + } + + g_free(destpath); + + return destfile; +} + +#define SET_DEST_MSG_FLAGS(fp, dest, n, fl) \ +{ \ + MsgInfo newmsginfo; \ + \ + newmsginfo.msgnum = n; \ + newmsginfo.flags = fl; \ + if (dest->stype == F_OUTBOX || \ + dest->stype == F_QUEUE || \ + dest->stype == F_DRAFT || \ + dest->stype == F_TRASH) \ + MSG_UNSET_PERM_FLAGS(newmsginfo.flags, \ + MSG_NEW|MSG_UNREAD|MSG_DELETED); \ + \ + if (fp) \ + procmsg_write_flags(&newmsginfo, fp); \ + else if (dest->opened) \ + procmsg_add_flags(dest, n, newmsginfo.flags); \ +} + +static gint mh_add_msg(Folder *folder, FolderItem *dest, const gchar *file, + MsgFlags *flags, gboolean remove_source) +{ + GSList file_list; + MsgFileInfo fileinfo; + + g_return_val_if_fail(file != NULL, -1); + + fileinfo.file = (gchar *)file; + fileinfo.flags = flags; + file_list.data = &fileinfo; + file_list.next = NULL; + + return mh_add_msgs(folder, dest, &file_list, remove_source, NULL); +} + +static gint mh_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list, + gboolean remove_source, gint *first) +{ + gchar *destfile; + GSList *cur; + MsgFileInfo *fileinfo; + gint first_ = 0; + FILE *fp; + + g_return_val_if_fail(dest != NULL, -1); + g_return_val_if_fail(file_list != NULL, -1); + + if (dest->last_num < 0) { + mh_scan_folder(folder, dest); + if (dest->last_num < 0) return -1; + } + + if ((((MsgFileInfo *)file_list->data)->flags == NULL && + file_list->next == NULL) || dest->opened) + fp = NULL; + else if ((fp = procmsg_open_mark_file(dest, DATA_APPEND)) == NULL) + g_warning("Can't open mark file.\n"); + + for (cur = file_list; cur != NULL; cur = cur->next) { + fileinfo = (MsgFileInfo *)cur->data; + + destfile = mh_get_new_msg_filename(dest); + if (destfile == NULL) return -1; + if (first_ == 0 || first_ > dest->last_num + 1) + first_ = dest->last_num + 1; + +#ifdef G_OS_UNIX + if (link(fileinfo->file, destfile) < 0) { +#endif + if (copy_file(fileinfo->file, destfile, TRUE) < 0) { + g_warning(_("can't copy message %s to %s\n"), + fileinfo->file, destfile); + g_free(destfile); + return -1; + } +#ifdef G_OS_UNIX + } +#endif + + g_free(destfile); + dest->last_num++; + dest->total++; + dest->updated = TRUE; + + if (fileinfo->flags) { + if (MSG_IS_RECEIVED(*fileinfo->flags)) { + if (dest->unmarked_num == 0) + dest->new = 0; + dest->unmarked_num++; + procmsg_add_mark_queue(dest, dest->last_num, + *fileinfo->flags); + } else { + SET_DEST_MSG_FLAGS(fp, dest, dest->last_num, + *fileinfo->flags); + } + if (MSG_IS_NEW(*fileinfo->flags)) + dest->new++; + if (MSG_IS_UNREAD(*fileinfo->flags)) + dest->unread++; + } else { + if (dest->unmarked_num == 0) + dest->new = 0; + dest->unmarked_num++; + dest->new++; + dest->unread++; + } + } + + if (fp) fclose(fp); + + if (first) + *first = first_; + + if (remove_source) { + for (cur = file_list; cur != NULL; cur = cur->next) { + fileinfo = (MsgFileInfo *)cur->data; + if (g_unlink(fileinfo->file) < 0) + FILE_OP_ERROR(fileinfo->file, "unlink"); + } + } + + return dest->last_num; +} + +static gint mh_do_move_msgs(Folder *folder, FolderItem *dest, GSList *msglist) +{ + FolderItem *src; + gchar *srcfile; + gchar *destfile; + FILE *fp; + GSList *cur; + MsgInfo *msginfo; + + g_return_val_if_fail(dest != NULL, -1); + g_return_val_if_fail(msglist != NULL, -1); + + if (dest->last_num < 0) { + mh_scan_folder(folder, dest); + if (dest->last_num < 0) return -1; + } + + if (dest->opened) + fp = NULL; + else if ((fp = procmsg_open_mark_file(dest, DATA_APPEND)) == NULL) + g_warning(_("Can't open mark file.\n")); + + for (cur = msglist; cur != NULL; cur = cur->next) { + msginfo = (MsgInfo *)cur->data; + src = msginfo->folder; + + if (src == dest) { + g_warning(_("the src folder is identical to the dest.\n")); + continue; + } + debug_print("Moving message %s%c%d to %s ...\n", + src->path, G_DIR_SEPARATOR, msginfo->msgnum, + dest->path); + + destfile = mh_get_new_msg_filename(dest); + if (!destfile) break; + srcfile = procmsg_get_message_file(msginfo); + + if (move_file(srcfile, destfile, FALSE) < 0) { + g_free(srcfile); + g_free(destfile); + break; + } + + g_free(srcfile); + g_free(destfile); + src->total--; + src->updated = TRUE; + dest->last_num++; + dest->total++; + dest->updated = TRUE; + + if (fp) { + SET_DEST_MSG_FLAGS(fp, dest, dest->last_num, + msginfo->flags); + } + + if (MSG_IS_NEW(msginfo->flags)) { + src->new--; + dest->new++; + } + if (MSG_IS_UNREAD(msginfo->flags)) { + src->unread--; + dest->unread++; + } + + MSG_SET_TMP_FLAGS(msginfo->flags, MSG_INVALID); + } + + if (fp) fclose(fp); + + return dest->last_num; +} + +static gint mh_move_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo) +{ + GSList msglist; + + g_return_val_if_fail(msginfo != NULL, -1); + + msglist.data = msginfo; + msglist.next = NULL; + + return mh_move_msgs(folder, dest, &msglist); +} + +static gint mh_move_msgs(Folder *folder, FolderItem *dest, GSList *msglist) +{ + MsgInfo *msginfo; + GSList *file_list; + gint ret = 0; + gint first; + + msginfo = (MsgInfo *)msglist->data; + if (folder == msginfo->folder->folder) + return mh_do_move_msgs(folder, dest, msglist); + + file_list = procmsg_get_message_file_list(msglist); + g_return_val_if_fail(file_list != NULL, -1); + + ret = mh_add_msgs(folder, dest, file_list, FALSE, &first); + + procmsg_message_file_list_free(file_list); + + if (ret != -1) + ret = folder_item_remove_msgs(msginfo->folder, msglist); + + return ret; +} + +static gint mh_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo) +{ + GSList msglist; + + g_return_val_if_fail(msginfo != NULL, -1); + + msglist.data = msginfo; + msglist.next = NULL; + + return mh_copy_msgs(folder, dest, &msglist); +} + +static gint mh_copy_msgs(Folder *folder, FolderItem *dest, GSList *msglist) +{ + gchar *srcfile; + gchar *destfile; + FILE *fp; + GSList *cur; + MsgInfo *msginfo; + + g_return_val_if_fail(dest != NULL, -1); + g_return_val_if_fail(msglist != NULL, -1); + + if (dest->last_num < 0) { + mh_scan_folder(folder, dest); + if (dest->last_num < 0) return -1; + } + + if (dest->opened) + fp = NULL; + else if ((fp = procmsg_open_mark_file(dest, DATA_APPEND)) == NULL) + g_warning(_("Can't open mark file.\n")); + + for (cur = msglist; cur != NULL; cur = cur->next) { + msginfo = (MsgInfo *)cur->data; + + if (msginfo->folder == dest) { + g_warning(_("the src folder is identical to the dest.\n")); + continue; + } + debug_print(_("Copying message %s%c%d to %s ...\n"), + msginfo->folder->path, G_DIR_SEPARATOR, + msginfo->msgnum, dest->path); + + destfile = mh_get_new_msg_filename(dest); + if (!destfile) break; + srcfile = procmsg_get_message_file(msginfo); + + if (copy_file(srcfile, destfile, TRUE) < 0) { + FILE_OP_ERROR(srcfile, "copy"); + g_free(srcfile); + g_free(destfile); + break; + } + + g_free(srcfile); + g_free(destfile); + dest->last_num++; + dest->total++; + dest->updated = TRUE; + + if (fp) { + SET_DEST_MSG_FLAGS(fp, dest, dest->last_num, + msginfo->flags); + } + + if (MSG_IS_NEW(msginfo->flags)) + dest->new++; + if (MSG_IS_UNREAD(msginfo->flags)) + dest->unread++; + } + + if (fp) fclose(fp); + + return dest->last_num; +} + +static gint mh_remove_msg(Folder *folder, FolderItem *item, MsgInfo *msginfo) +{ + gchar *file; + + g_return_val_if_fail(item != NULL, -1); + + file = mh_fetch_msg(folder, item, msginfo->msgnum); + g_return_val_if_fail(file != NULL, -1); + + if (g_unlink(file) < 0) { + FILE_OP_ERROR(file, "unlink"); + g_free(file); + return -1; + } + g_free(file); + + item->total--; + item->updated = TRUE; + if (MSG_IS_NEW(msginfo->flags)) + item->new--; + if (MSG_IS_UNREAD(msginfo->flags)) + item->unread--; + MSG_SET_TMP_FLAGS(msginfo->flags, MSG_INVALID); + + if (msginfo->msgnum == item->last_num) + mh_scan_folder_full(folder, item, FALSE); + + return 0; +} + +static gint mh_remove_all_msg(Folder *folder, FolderItem *item) +{ + gchar *path; + gint val; + + g_return_val_if_fail(item != NULL, -1); + + path = folder_item_get_path(item); + g_return_val_if_fail(path != NULL, -1); + val = remove_all_numbered_files(path); + g_free(path); + if (val == 0) { + item->new = item->unread = item->total = 0; + item->last_num = 0; + item->updated = TRUE; + } + + return val; +} + +static gboolean mh_is_msg_changed(Folder *folder, FolderItem *item, + MsgInfo *msginfo) +{ + struct stat s; + + if (g_stat(itos(msginfo->msgnum), &s) < 0 || + msginfo->size != s.st_size || + msginfo->mtime != s.st_mtime) + return TRUE; + + return FALSE; +} + +static gint mh_close(Folder *folder, FolderItem *item) +{ + return 0; +} + +static gint mh_scan_folder_full(Folder *folder, FolderItem *item, + gboolean count_sum) +{ + gchar *path; + DIR *dp; + struct dirent *d; + gint max = 0; + gint num; + gint n_msg = 0; + + g_return_val_if_fail(item != NULL, -1); + + debug_print("mh_scan_folder(): Scanning %s ...\n", item->path); + + path = folder_item_get_path(item); + g_return_val_if_fail(path != NULL, -1); + if (change_dir(path) < 0) { + g_free(path); + return -1; + } + g_free(path); + + if ((dp = opendir(".")) == NULL) { + FILE_OP_ERROR(item->path, "opendir"); + return -1; + } + + if (folder->ui_func) + folder->ui_func(folder, item, folder->ui_func_data); + + while ((d = readdir(dp)) != NULL) { + if ((num = to_number(d->d_name)) > 0 && + dirent_is_regular_file(d)) { + n_msg++; + if (max < num) + max = num; + } + } + + closedir(dp); + + if (n_msg == 0) + item->new = item->unread = item->total = 0; + else if (count_sum) { + gint new, unread, total, min, max_; + + procmsg_get_mark_sum + (item, &new, &unread, &total, &min, &max_, 0); + + if (n_msg > total) { + item->unmarked_num = new = n_msg - total; + unread += n_msg - total; + } else + item->unmarked_num = 0; + + item->new = new; + item->unread = unread; + item->total = n_msg; + } + + item->updated = TRUE; + + debug_print(_("Last number in dir %s = %d\n"), item->path, max); + item->last_num = max; + + return 0; +} + +static gint mh_scan_folder(Folder *folder, FolderItem *item) +{ + return mh_scan_folder_full(folder, item, TRUE); +} + +static gint mh_scan_tree(Folder *folder) +{ + FolderItem *item; + gchar *rootpath; + + g_return_val_if_fail(folder != NULL, -1); + + if (!folder->node) { + item = folder_item_new(folder->name, NULL); + item->folder = folder; + folder->node = item->node = g_node_new(item); + } else + item = FOLDER_ITEM(folder->node->data); + + rootpath = folder_item_get_path(item); + if (change_dir(rootpath) < 0) { + g_free(rootpath); + return -1; + } + g_free(rootpath); + + mh_create_tree(folder); + mh_remove_missing_folder_items(folder); + mh_scan_tree_recursive(item); + + return 0; +} + +#define MAKE_DIR_IF_NOT_EXIST(dir) \ +{ \ + if (!is_dir_exist(dir)) { \ + if (is_file_exist(dir)) { \ + g_warning(_("File `%s' already exists.\n" \ + "Can't create folder."), dir); \ + return -1; \ + } \ + if (make_dir(dir) < 0) \ + return -1; \ + } \ +} + +static gint mh_create_tree(Folder *folder) +{ + gchar *rootpath; + + g_return_val_if_fail(folder != NULL, -1); + + CHDIR_RETURN_VAL_IF_FAIL(get_mail_base_dir(), -1); + rootpath = LOCAL_FOLDER(folder)->rootpath; + MAKE_DIR_IF_NOT_EXIST(rootpath); + CHDIR_RETURN_VAL_IF_FAIL(rootpath, -1); + MAKE_DIR_IF_NOT_EXIST(INBOX_DIR); + MAKE_DIR_IF_NOT_EXIST(OUTBOX_DIR); + MAKE_DIR_IF_NOT_EXIST(QUEUE_DIR); + MAKE_DIR_IF_NOT_EXIST(DRAFT_DIR); + MAKE_DIR_IF_NOT_EXIST(TRASH_DIR); + + return 0; +} + +#undef MAKE_DIR_IF_NOT_EXIST + +static FolderItem *mh_create_folder(Folder *folder, FolderItem *parent, + const gchar *name) +{ + gchar *path; + gchar *fs_name; + gchar *fullpath; + FolderItem *new_item; + + g_return_val_if_fail(folder != NULL, NULL); + g_return_val_if_fail(parent != NULL, NULL); + g_return_val_if_fail(name != NULL, NULL); + + path = folder_item_get_path(parent); + fs_name = g_filename_from_utf8(name, -1, NULL, NULL, NULL); + fullpath = g_strconcat(path, G_DIR_SEPARATOR_S, + fs_name ? fs_name : name, NULL); + g_free(fs_name); + g_free(path); + + if (make_dir(fullpath) < 0) { + g_free(fullpath); + return NULL; + } + + g_free(fullpath); + + if (parent->path) + path = g_strconcat(parent->path, G_DIR_SEPARATOR_S, name, + NULL); + else + path = g_strdup(name); + new_item = folder_item_new(name, path); + folder_item_append(parent, new_item); + g_free(path); + + return new_item; +} + +static gint mh_move_folder_real(Folder *folder, FolderItem *item, + FolderItem *new_parent, const gchar *name) +{ + gchar *oldpath; + gchar *newpath; + gchar *dirname; + gchar *new_dir; + gchar *name_; + gchar *utf8_name; + gchar *paths[2]; + + g_return_val_if_fail(folder != NULL, -1); + g_return_val_if_fail(item != NULL, -1); + g_return_val_if_fail(folder == item->folder, -1); + g_return_val_if_fail(item->path != NULL, -1); + g_return_val_if_fail(new_parent != NULL || name != NULL, -1); + if (new_parent) { + g_return_val_if_fail(item != new_parent, -1); + g_return_val_if_fail(item->parent != new_parent, -1); + g_return_val_if_fail(item->folder == new_parent->folder, -1); + if (g_node_is_ancestor(item->node, new_parent->node)) { + g_warning("folder to be moved is ancestor of new parent\n"); + return -1; + } + } + + oldpath = folder_item_get_path(item); + if (new_parent) { + if (name) { + name_ = g_filename_from_utf8(name, -1, NULL, NULL, + NULL); + if (!name_) + name_ = g_strdup(name); + utf8_name = g_strdup(name); + } else { + name_ = g_path_get_basename(oldpath); + utf8_name = g_filename_to_utf8(name_, -1, NULL, NULL, + NULL); + if (!utf8_name) + utf8_name = g_strdup(name_); + } + new_dir = folder_item_get_path(new_parent); + newpath = g_strconcat(new_dir, G_DIR_SEPARATOR_S, name_, NULL); + g_free(new_dir); + } else { + name_ = g_filename_from_utf8(name, -1, NULL, NULL, NULL); + utf8_name = g_strdup(name); + dirname = g_dirname(oldpath); + newpath = g_strconcat(dirname, G_DIR_SEPARATOR_S, + name_ ? name_ : name, NULL); + g_free(dirname); + } + g_free(name_); + + if (is_file_entry_exist(newpath)) { + g_warning("%s already exists\n", newpath); + g_free(oldpath); + g_free(newpath); + g_free(utf8_name); + return -1; + } + + debug_print("mh_move_folder: rename(%s, %s)\n", oldpath, newpath); + + if (g_rename(oldpath, newpath) < 0) { + FILE_OP_ERROR(oldpath, "rename"); + g_free(oldpath); + g_free(newpath); + g_free(utf8_name); + return -1; + } + + g_free(oldpath); + g_free(newpath); + + if (new_parent) { + g_node_unlink(item->node); + g_node_append(new_parent->node, item->node); + item->parent = new_parent; + if (new_parent->path != NULL) { + newpath = g_strconcat(new_parent->path, + G_DIR_SEPARATOR_S, utf8_name, + NULL); + g_free(utf8_name); + } else + newpath = utf8_name; + } else { + if (strchr(item->path, G_DIR_SEPARATOR) != NULL) { + dirname = g_dirname(item->path); + newpath = g_strconcat(dirname, G_DIR_SEPARATOR_S, + utf8_name, NULL); + g_free(dirname); + g_free(utf8_name); + } else + newpath = utf8_name; + } + + if (name) { + g_free(item->name); + item->name = g_strdup(name); + } + + paths[0] = g_strdup(item->path); + paths[1] = newpath; + g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1, + mh_rename_folder_func, paths); + + g_free(paths[0]); + g_free(paths[1]); + + return 0; +} + +static gint mh_move_folder(Folder *folder, FolderItem *item, + FolderItem *new_parent) +{ + return mh_move_folder_real(folder, item, new_parent, NULL); +} + +static gint mh_rename_folder(Folder *folder, FolderItem *item, + const gchar *name) +{ + return mh_move_folder_real(folder, item, NULL, name); +} + +static gint mh_remove_folder(Folder *folder, FolderItem *item) +{ + gchar *path; + + g_return_val_if_fail(folder != NULL, -1); + g_return_val_if_fail(item != NULL, -1); + g_return_val_if_fail(item->path != NULL, -1); + + path = folder_item_get_path(item); + if (remove_dir_recursive(path) < 0) { + g_warning("can't remove directory `%s'\n", path); + g_free(path); + return -1; + } + + g_free(path); + folder_item_remove(item); + return 0; +} + + +static time_t mh_get_mtime(FolderItem *item) +{ + gchar *path; + struct stat s; + + path = folder_item_get_path(item); + if (g_stat(path, &s) < 0) { + FILE_OP_ERROR(path, "stat"); + return -1; + } else { + return MAX(s.st_mtime, s.st_ctime); + } +} + +static GSList *mh_get_uncached_msgs(GHashTable *msg_table, FolderItem *item) +{ + gchar *path; + DIR *dp; + struct dirent *d; + GSList *newlist = NULL; + GSList *last = NULL; + MsgInfo *msginfo; + gint n_newmsg = 0; + gint num; + + g_return_val_if_fail(item != NULL, NULL); + + path = folder_item_get_path(item); + g_return_val_if_fail(path != NULL, NULL); + if (change_dir(path) < 0) { + g_free(path); + return NULL; + } + g_free(path); + + if ((dp = opendir(".")) == NULL) { + FILE_OP_ERROR(item->path, "opendir"); + return NULL; + } + + debug_print("Searching uncached messages...\n"); + + if (msg_table) { + while ((d = readdir(dp)) != NULL) { + if ((num = to_number(d->d_name)) <= 0) continue; + + msginfo = g_hash_table_lookup + (msg_table, GUINT_TO_POINTER(num)); + + if (msginfo) { + MSG_SET_TMP_FLAGS(msginfo->flags, MSG_CACHED); + } else { + /* not found in the cache (uncached message) */ + msginfo = mh_parse_msg(d->d_name, item); + if (!msginfo) continue; + + if (!newlist) + last = newlist = + g_slist_append(NULL, msginfo); + else { + last = g_slist_append(last, msginfo); + last = last->next; + } + n_newmsg++; + } + } + } else { + /* discard all previous cache */ + while ((d = readdir(dp)) != NULL) { + if (to_number(d->d_name) <= 0) continue; + + msginfo = mh_parse_msg(d->d_name, item); + if (!msginfo) continue; + + if (!newlist) + last = newlist = g_slist_append(NULL, msginfo); + else { + last = g_slist_append(last, msginfo); + last = last->next; + } + n_newmsg++; + } + } + + closedir(dp); + + if (n_newmsg) + debug_print("%d uncached message(s) found.\n", n_newmsg); + else + debug_print("done.\n"); + + /* sort new messages in numerical order */ + if (newlist && item->sort_key == SORT_BY_NONE) { + debug_print("Sorting uncached messages in numerical order...\n"); + newlist = g_slist_sort + (newlist, (GCompareFunc)procmsg_cmp_msgnum_for_sort); + debug_print("done.\n"); + } + + return newlist; +} + +static MsgInfo *mh_parse_msg(const gchar *file, FolderItem *item) +{ + MsgInfo *msginfo; + MsgFlags flags; + + g_return_val_if_fail(item != NULL, NULL); + g_return_val_if_fail(file != NULL, NULL); + + flags.perm_flags = MSG_NEW|MSG_UNREAD; + flags.tmp_flags = 0; + + if (item->stype == F_QUEUE) { + MSG_SET_TMP_FLAGS(flags, MSG_QUEUED); + } else if (item->stype == F_DRAFT) { + MSG_SET_TMP_FLAGS(flags, MSG_DRAFT); + } + + msginfo = procheader_parse_file(file, flags, FALSE); + if (!msginfo) return NULL; + + msginfo->msgnum = atoi(file); + msginfo->folder = item; + + return msginfo; +} + +#if 0 +static gboolean mh_is_maildir_one(const gchar *path, const gchar *dir) +{ + gchar *entry; + gboolean result; + + entry = g_strconcat(path, G_DIR_SEPARATOR_S, dir, NULL); + result = is_dir_exist(entry); + g_free(entry); + + return result; +} + +/* + * check whether PATH is a Maildir style mailbox. + * This is the case if the 3 subdir: new, cur, tmp are existing. + * This functon assumes that entry is an directory + */ +static gboolean mh_is_maildir(const gchar *path) +{ + return mh_is_maildir_one(path, "new") && + mh_is_maildir_one(path, "cur") && + mh_is_maildir_one(path, "tmp"); +} +#endif + +static gboolean mh_remove_missing_folder_items_func(GNode *node, gpointer data) +{ + FolderItem *item; + gchar *path; + + g_return_val_if_fail(node->data != NULL, FALSE); + + if (G_NODE_IS_ROOT(node)) + return FALSE; + + item = FOLDER_ITEM(node->data); + + path = folder_item_get_path(item); + if (!is_dir_exist(path)) { + debug_print("folder '%s' not found. removing...\n", path); + folder_item_remove(item); + } + g_free(path); + + return FALSE; +} + +static void mh_remove_missing_folder_items(Folder *folder) +{ + g_return_if_fail(folder != NULL); + + debug_print("searching missing folders...\n"); + + g_node_traverse(folder->node, G_POST_ORDER, G_TRAVERSE_ALL, -1, + mh_remove_missing_folder_items_func, folder); +} + +static void mh_scan_tree_recursive(FolderItem *item) +{ + Folder *folder; +#ifdef G_OS_WIN32 + GDir *dir; +#else + DIR *dp; + struct dirent *d; +#endif + const gchar *dir_name; + struct stat s; + gchar *fs_path; + gchar *entry; + gchar *utf8entry; + gchar *utf8name; + gint n_msg = 0; + + g_return_if_fail(item != NULL); + g_return_if_fail(item->folder != NULL); + + folder = item->folder; + + fs_path = item->path ? + g_filename_from_utf8(item->path, -1, NULL, NULL, NULL) + : g_strdup("."); + if (!fs_path) + fs_path = g_strdup(item->path); +#ifdef G_OS_WIN32 + dir = g_dir_open(fs_path, 0, NULL); + if (!dir) { + g_warning("failed to open directory: %s\n", fs_path); + g_free(fs_path); + return; + } +#else + dp = opendir(fs_path); + if (!dp) { + FILE_OP_ERROR(fs_path, "opendir"); + g_free(fs_path); + return; + } +#endif + g_free(fs_path); + + debug_print("scanning %s ...\n", + item->path ? item->path + : LOCAL_FOLDER(item->folder)->rootpath); + if (folder->ui_func) + folder->ui_func(folder, item, folder->ui_func_data); + +#ifdef G_OS_WIN32 + while ((dir_name = g_dir_read_name(dir)) != NULL) { +#else + while ((d = readdir(dp)) != NULL) { + dir_name = d->d_name; +#endif + if (dir_name[0] == '.') continue; + + utf8name = g_filename_to_utf8(dir_name, -1, NULL, NULL, NULL); + if (!utf8name) + utf8name = g_strdup(dir_name); + + if (item->path) + utf8entry = g_strconcat(item->path, G_DIR_SEPARATOR_S, + utf8name, NULL); + else + utf8entry = g_strdup(utf8name); + entry = g_filename_from_utf8(utf8entry, -1, NULL, NULL, NULL); + if (!entry) + entry = g_strdup(utf8entry); + + if ( +#if !defined(G_OS_WIN32) && defined(HAVE_DIRENT_D_TYPE) + d->d_type == DT_DIR || + (d->d_type == DT_UNKNOWN && +#endif + g_stat(entry, &s) == 0 && S_ISDIR(s.st_mode) +#if !defined(G_OS_WIN32) && defined(HAVE_DIRENT_D_TYPE) + ) +#endif + ) { + FolderItem *new_item = NULL; + GNode *node; + +#if 0 + if (mh_is_maildir(entry)) { + g_free(entry); + g_free(utf8entry); + g_free(utf8name); + continue; + } +#endif + +#ifndef G_OS_WIN32 + if (g_utf8_validate(utf8name, -1, NULL) == FALSE) { + g_warning(_("Directory name\n" + "'%s' is not a valid UTF-8 string.\n" + "Maybe the locale encoding is used for filename.\n" + "If that is the case, you must set the following environmental variable\n" + "(see README for detail):\n" + "\n" + "\tG_FILENAME_ENCODING=@locale\n"), + utf8name); + g_free(entry); + g_free(utf8entry); + g_free(utf8name); + continue; + } +#endif /* G_OS_WIN32 */ + + node = item->node; + for (node = node->children; node != NULL; node = node->next) { + FolderItem *cur_item = FOLDER_ITEM(node->data); + if (!strcmp2(cur_item->path, utf8entry)) { + new_item = cur_item; + break; + } + } + if (!new_item) { + debug_print("new folder '%s' found.\n", entry); + new_item = folder_item_new(utf8name, utf8entry); + folder_item_append(item, new_item); + } + + if (!item->path) { + if (!folder->inbox && + !strcmp(dir_name, INBOX_DIR)) { + new_item->stype = F_INBOX; + folder->inbox = new_item; + } else if (!folder->outbox && + !strcmp(dir_name, OUTBOX_DIR)) { + new_item->stype = F_OUTBOX; + folder->outbox = new_item; + } else if (!folder->draft && + !strcmp(dir_name, DRAFT_DIR)) { + new_item->stype = F_DRAFT; + folder->draft = new_item; + } else if (!folder->queue && + !strcmp(dir_name, QUEUE_DIR)) { + new_item->stype = F_QUEUE; + folder->queue = new_item; + } else if (!folder->trash && + !strcmp(dir_name, TRASH_DIR)) { + new_item->stype = F_TRASH; + folder->trash = new_item; + } + } + + mh_scan_tree_recursive(new_item); + } else if (to_number(dir_name) > 0) n_msg++; + + g_free(entry); + g_free(utf8entry); + g_free(utf8name); + } + +#ifdef G_OS_WIN32 + g_dir_close(dir); +#else + closedir(dp); +#endif + + if (item->path) { + gint new, unread, total, min, max; + + procmsg_get_mark_sum + (item, &new, &unread, &total, &min, &max, 0); + if (n_msg > total) { + new += n_msg - total; + unread += n_msg - total; + } + item->new = new; + item->unread = unread; + item->total = n_msg; + item->updated = TRUE; + } +} + +static gboolean mh_rename_folder_func(GNode *node, gpointer data) +{ + FolderItem *item = node->data; + gchar **paths = data; + const gchar *oldpath = paths[0]; + const gchar *newpath = paths[1]; + gchar *base; + gchar *new_itempath; + gint oldpathlen; + + oldpathlen = strlen(oldpath); + if (strncmp(oldpath, item->path, oldpathlen) != 0) { + g_warning("path doesn't match: %s, %s\n", oldpath, item->path); + return TRUE; + } + + base = item->path + oldpathlen; + while (*base == G_DIR_SEPARATOR) base++; + if (*base == '\0') + new_itempath = g_strdup(newpath); + else + new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base, + NULL); + g_free(item->path); + item->path = new_itempath; + + return FALSE; +} |