aboutsummaryrefslogtreecommitdiff
path: root/src/mh.c
diff options
context:
space:
mode:
authorhiro <hiro@ee746299-78ed-0310-b773-934348b2243d>2005-01-12 11:22:08 +0000
committerhiro <hiro@ee746299-78ed-0310-b773-934348b2243d>2005-01-12 11:22:08 +0000
commitb9ca7b1ef5cd1f96ae6e28ae78d12c1e3258c23f (patch)
tree1203adec5f70af1ddd49868528d8d3a5b9004329 /src/mh.c
Initial import of Sylpheed (GTK2 version).
git-svn-id: svn://sylpheed.sraoss.jp/sylpheed/trunk@1 ee746299-78ed-0310-b773-934348b2243d
Diffstat (limited to 'src/mh.c')
-rw-r--r--src/mh.c1282
1 files changed, 1282 insertions, 0 deletions
diff --git a/src/mh.c b/src/mh.c
new file mode 100644
index 00000000..7c7e5bd3
--- /dev/null
+++ b/src/mh.c
@@ -0,0 +1,1282 @@
+/*
+ * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 1999-2003 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 <dirent.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+#undef MEASURE_TIME
+
+#ifdef MEASURE_TIME
+# include <sys/time.h>
+#endif
+
+#include "intl.h"
+#include "folder.h"
+#include "mh.h"
+#include "procmsg.h"
+#include "procheader.h"
+#include "utils.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_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_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
+ struct timeval tv_before, tv_after, tv_result;
+
+ gettimeofday(&tv_before, NULL);
+#endif
+
+ g_return_val_if_fail(item != NULL, NULL);
+
+ 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);
+ } else if (use_cache) {
+ GSList *newlist;
+
+ mlist = procmsg_read_cache(item, TRUE);
+ msg_table = procmsg_msg_hash_table_create(mlist);
+
+ newlist = mh_get_uncached_msgs(msg_table, item);
+ if (msg_table)
+ g_hash_table_destroy(msg_table);
+
+ mlist = g_slist_concat(mlist, newlist);
+ } else
+ mlist = mh_get_uncached_msgs(NULL, item);
+
+ item->mtime = cur_mtime;
+
+ procmsg_set_flags(mlist, item);
+
+ mlist = procmsg_sort_msg_list(mlist, item->sort_key, item->sort_type);
+
+#ifdef MEASURE_TIME
+ gettimeofday(&tv_after, NULL);
+
+ timersub(&tv_after, &tv_before, &tv_result);
+ g_print("mh_get_msg_list: %s: elapsed time: %ld.%06ld sec\n",
+ item->path, tv_result.tv_sec, tv_result.tv_usec);
+#endif
+
+ 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;
+
+ if (link(fileinfo->file, destfile) < 0) {
+ 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;
+ }
+ }
+
+ 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 (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 (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)
+ 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 (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_home_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_rename_folder(Folder *folder, FolderItem *item,
+ const gchar *name)
+{
+ gchar *fs_name;
+ gchar *oldpath;
+ gchar *dirname;
+ gchar *newpath;
+ gchar *paths[2];
+
+ 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);
+ g_return_val_if_fail(name != NULL, -1);
+
+ oldpath = folder_item_get_path(item);
+ dirname = g_dirname(oldpath);
+ fs_name = g_filename_from_utf8(name, -1, NULL, NULL, NULL);
+ newpath = g_strconcat(dirname, G_DIR_SEPARATOR_S,
+ fs_name ? fs_name : name, NULL);
+ g_free(fs_name);
+ g_free(dirname);
+
+ if (rename(oldpath, newpath) < 0) {
+ FILE_OP_ERROR(oldpath, "rename");
+ g_free(oldpath);
+ g_free(newpath);
+ return -1;
+ }
+
+ g_free(oldpath);
+ g_free(newpath);
+
+ if (strchr(item->path, G_DIR_SEPARATOR) != NULL) {
+ dirname = g_dirname(item->path);
+ newpath = g_strconcat(dirname, G_DIR_SEPARATOR_S, name, NULL);
+ g_free(dirname);
+ } else
+ newpath = g_strdup(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_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 (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) {
+ /* 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;
+ DIR *dp;
+ struct dirent *d;
+ 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);
+ dp = opendir(fs_path);
+ if (!dp) {
+ FILE_OP_ERROR(fs_path, "opendir");
+ g_free(fs_path);
+ return;
+ }
+ 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);
+
+ while ((d = readdir(dp)) != NULL) {
+ if (d->d_name[0] == '.') continue;
+
+ utf8name = g_filename_to_utf8(d->d_name, -1, NULL, NULL, NULL);
+ if (!utf8name)
+ utf8name = g_strdup(d->d_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 (
+#ifdef HAVE_DIRENT_D_TYPE
+ d->d_type == DT_DIR ||
+ (d->d_type == DT_UNKNOWN &&
+#endif
+ stat(entry, &s) == 0 && S_ISDIR(s.st_mode)
+#ifdef 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
+
+ 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(d->d_name, INBOX_DIR)) {
+ new_item->stype = F_INBOX;
+ folder->inbox = new_item;
+ } else if (!folder->outbox &&
+ !strcmp(d->d_name, OUTBOX_DIR)) {
+ new_item->stype = F_OUTBOX;
+ folder->outbox = new_item;
+ } else if (!folder->draft &&
+ !strcmp(d->d_name, DRAFT_DIR)) {
+ new_item->stype = F_DRAFT;
+ folder->draft = new_item;
+ } else if (!folder->queue &&
+ !strcmp(d->d_name, QUEUE_DIR)) {
+ new_item->stype = F_QUEUE;
+ folder->queue = new_item;
+ } else if (!folder->trash &&
+ !strcmp(d->d_name, TRASH_DIR)) {
+ new_item->stype = F_TRASH;
+ folder->trash = new_item;
+ }
+ }
+
+ mh_scan_tree_recursive(new_item);
+ } else if (to_number(d->d_name) != -1) n_msg++;
+
+ g_free(entry);
+ g_free(utf8entry);
+ g_free(utf8name);
+ }
+
+ closedir(dp);
+
+ 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;
+}