summaryrefslogtreecommitdiffabout
path: root/libetpan/src/engine/mailengine.c
authorzautrix <zautrix>2005-03-18 20:17:03 (UTC)
committer zautrix <zautrix>2005-03-18 20:17:03 (UTC)
commit9e549686b23b6dffdcbd09c9b10dc2cb795fbcdf (patch) (side-by-side diff)
tree2528e6cc740225ca0f47d5ac8ff70f7d3bb10621 /libetpan/src/engine/mailengine.c
parent9319998f20f03dcc217fbb39656755dc65226276 (diff)
downloadkdepimpi-9e549686b23b6dffdcbd09c9b10dc2cb795fbcdf.zip
kdepimpi-9e549686b23b6dffdcbd09c9b10dc2cb795fbcdf.tar.gz
kdepimpi-9e549686b23b6dffdcbd09c9b10dc2cb795fbcdf.tar.bz2
Initial revision
Diffstat (limited to 'libetpan/src/engine/mailengine.c') (more/less context) (ignore whitespace changes)
-rw-r--r--libetpan/src/engine/mailengine.c1636
1 files changed, 1636 insertions, 0 deletions
diff --git a/libetpan/src/engine/mailengine.c b/libetpan/src/engine/mailengine.c
new file mode 100644
index 0000000..be4df38
--- a/dev/null
+++ b/libetpan/src/engine/mailengine.c
@@ -0,0 +1,1636 @@
+/*
+ * libEtPan! -- a mail library
+ *
+ * Copyright (C) 2001, 2005 - DINH Viet Hoa
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the libEtPan! project nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * $Id$
+ */
+
+#include "mailengine.h"
+
+#ifndef CONFIG_H
+#define CONFIG_H
+#include "config.h"
+#endif
+
+#include "mailfolder.h"
+#include "maildriver.h"
+#include "imapdriver_cached.h"
+#include "mailstorage.h"
+#include "imapdriver_cached_message.h"
+#include <stdlib.h>
+#include "mailprivacy.h"
+#ifdef LIBETPAN_REENTRANT
+#include <pthread.h>
+#endif
+#include <string.h>
+
+/* ************************************************************* */
+/* Message finder */
+
+#if 0
+struct message_folder_finder {
+#ifdef LIBETPAN_REENTRANT
+ pthread_mutex_t lock;
+#endif
+
+ /* msg => folder */
+ chash * message_hash;
+};
+
+static int message_folder_finder_init(struct message_folder_finder * finder)
+{
+ int r;
+
+#ifdef LIBETPAN_REENTRANT
+ r = pthread_mutex_init(&finder->lock, NULL);
+ if (r != 0)
+ return MAIL_ERROR_MEMORY;
+#endif
+
+ finder->message_hash = chash_new(CHASH_DEFAULTSIZE, CHASH_COPYKEY);
+ if (finder->message_hash == NULL)
+ return MAIL_ERROR_MEMORY;
+
+ return MAIL_NO_ERROR;
+}
+
+static void message_folder_finder_done(struct message_folder_finder * finder)
+{
+ chash_free(finder->message_hash);
+#ifdef LIBETPAN_REENTRANT
+ pthread_mutex_destroy(&finder->lock);
+#endif
+}
+
+static struct mailfolder *
+message_folder_finder_lookup(struct message_folder_finder * finder,
+ mailmessage * msg)
+{
+ int r;
+ chashdatum key;
+ chashdatum data;
+ struct mailfolder * folder;
+
+ key.data = &msg;
+ key.len = sizeof(msg);
+#ifdef LIBETPAN_REENTRANT
+ pthread_mutex_lock(&finder->lock);
+#endif
+ r = chash_get(finder->message_hash, &key, &data);
+#ifdef LIBETPAN_REENTRANT
+ pthread_mutex_unlock(&finder->lock);
+#endif
+ if (r < 0)
+ return NULL;
+
+ folder = data.data;
+
+ return folder;
+}
+
+static inline int
+message_folder_finder_store_no_lock(struct message_folder_finder * finder,
+ mailmessage * msg, struct mailfolder * folder)
+{
+ int r;
+ chashdatum key;
+ chashdatum data;
+
+ key.data = &msg;
+ key.len = sizeof(msg);
+ data.data = folder;
+ data.len = 0;
+ r = chash_set(finder->message_hash, &key, &data, NULL);
+ if (r < 0)
+ return MAIL_ERROR_MEMORY;
+
+ return MAIL_NO_ERROR;
+}
+
+static int
+message_folder_finder_store(struct message_folder_finder * finder,
+ mailmessage * msg, struct mailfolder * folder)
+{
+ int r;
+
+#ifdef LIBETPAN_REENTRANT
+ pthread_mutex_lock(&finder->lock);
+#endif
+ r = message_folder_finder_store_no_lock(finder, msg, folder);
+#ifdef LIBETPAN_REENTRANT
+ pthread_mutex_unlock(&finder->lock);
+#endif
+
+ return r;
+}
+
+static inline void
+message_folder_finder_delete_no_lock(struct message_folder_finder * finder,
+ mailmessage * msg)
+{
+ chashdatum key;
+
+ key.data = &msg;
+ key.len = sizeof(msg);
+ chash_delete(finder->message_hash, &key, NULL);
+}
+
+static void
+message_folder_finder_delete(struct message_folder_finder * finder,
+ mailmessage * msg)
+{
+#ifdef LIBETPAN_REENTRANT
+ pthread_mutex_lock(&finder->lock);
+#endif
+ message_folder_finder_delete_no_lock(finder, msg);
+#ifdef LIBETPAN_REENTRANT
+ pthread_mutex_unlock(&finder->lock);
+#endif
+}
+#endif
+
+/* ************************************************************* */
+/* Message ref info */
+
+struct message_ref_elt {
+ mailmessage * msg;
+ int ref_count;
+ int mime_ref_count;
+ struct mailfolder * folder;
+ int lost;
+};
+
+static struct message_ref_elt *
+message_ref_elt_new(struct mailfolder * folder, mailmessage * msg)
+{
+ struct message_ref_elt * ref;
+
+ ref = malloc(sizeof(* ref));
+ if (ref == NULL)
+ return NULL;
+
+ ref->msg = msg;
+ ref->ref_count = 0;
+ ref->mime_ref_count = 0;
+ ref->folder = folder;
+ ref->lost = 0;
+
+ return ref;
+}
+
+static void message_ref_elt_free(struct message_ref_elt * ref_elt)
+{
+ free(ref_elt);
+}
+
+static inline int message_ref(struct message_ref_elt * ref_elt)
+{
+ ref_elt->ref_count ++;
+
+ return ref_elt->ref_count;
+}
+
+static inline int message_unref(struct message_ref_elt * ref_elt)
+{
+ ref_elt->ref_count --;
+
+ return ref_elt->ref_count;
+}
+
+
+static inline int message_mime_ref(struct mailprivacy * privacy,
+ struct message_ref_elt * ref_elt)
+{
+ int r;
+
+ if (ref_elt->mime_ref_count == 0) {
+ struct mailmime * mime;
+
+ r = mailprivacy_msg_get_bodystructure(privacy, ref_elt->msg, &mime);
+ if (r != MAIL_NO_ERROR)
+ return -r;
+ }
+
+ message_ref(ref_elt);
+
+ ref_elt->mime_ref_count ++;
+
+ return ref_elt->mime_ref_count;
+}
+
+static inline int message_mime_unref(struct mailprivacy * privacy,
+ struct message_ref_elt * ref_elt)
+{
+ message_unref(ref_elt);
+
+ ref_elt->mime_ref_count --;
+
+ if (ref_elt->mime_ref_count == 0)
+ mailprivacy_msg_flush(privacy, ref_elt->msg);
+
+ return ref_elt->mime_ref_count;
+}
+
+
+/* ************************************************************* */
+/* Folder ref info */
+
+struct folder_ref_info {
+ struct mailfolder * folder;
+#if 0
+ struct message_folder_finder * msg_folder_finder;
+#endif
+
+ /* msg => msg_ref_info */
+ chash * msg_hash;
+
+ /* uid => msg */
+ chash * uid_hash;
+
+ int lost_session;
+};
+
+static struct folder_ref_info *
+folder_ref_info_new(struct mailfolder * folder
+ /*, struct message_folder_finder * msg_folder_finder */)
+{
+ struct folder_ref_info * ref_info;
+
+ ref_info = malloc(sizeof(* ref_info));
+ if (ref_info == NULL)
+ goto err;
+
+ ref_info->folder = folder;
+#if 0
+ ref_info->msg_folder_finder = msg_folder_finder;
+#endif
+
+ ref_info->msg_hash = chash_new(CHASH_DEFAULTSIZE, CHASH_COPYKEY);
+ if (ref_info->msg_hash == NULL)
+ goto free;
+
+ ref_info->uid_hash = chash_new(CHASH_DEFAULTSIZE, CHASH_COPYNONE);
+ if (ref_info->uid_hash == NULL)
+ goto free_msg_hash;
+
+ ref_info->lost_session = 1;
+
+ return ref_info;
+
+ free_msg_hash:
+ chash_free(ref_info->msg_hash);
+ free:
+ free(ref_info);
+ err:
+ return NULL;
+}
+
+static void folder_ref_info_free(struct folder_ref_info * ref_info)
+{
+ chash_free(ref_info->uid_hash);
+ chash_free(ref_info->msg_hash);
+ free(ref_info);
+}
+
+static struct message_ref_elt *
+folder_info_get_msg_ref(struct folder_ref_info * ref_info, mailmessage * msg)
+{
+ chashdatum key;
+ chashdatum data;
+ struct message_ref_elt * ref_elt;
+ int r;
+
+ key.data = &msg;
+ key.len = sizeof(msg);
+ r = chash_get(ref_info->msg_hash, &key, &data);
+ if (r < 0)
+ return NULL;
+
+ ref_elt = data.data;
+
+ return ref_elt;
+}
+
+static mailmessage *
+folder_info_get_msg_by_uid(struct folder_ref_info * ref_info,
+ char * uid)
+{
+ chashdatum key;
+ chashdatum data;
+ mailmessage * msg;
+ int r;
+
+ key.data = uid;
+ key.len = strlen(uid);
+ r = chash_get(ref_info->uid_hash, &key, &data);
+ if (r < 0)
+ return NULL;
+
+ msg = data.data;
+
+ return msg;
+}
+
+static int folder_message_ref(struct folder_ref_info * ref_info,
+ mailmessage * msg)
+{
+ struct message_ref_elt * msg_ref;
+
+ msg_ref = folder_info_get_msg_ref(ref_info, msg);
+ return message_ref(msg_ref);
+}
+
+static void folder_message_remove(struct folder_ref_info * ref_info,
+ mailmessage * msg);
+
+#ifdef DEBUG_ENGINE
+#include "etpan-app.h"
+
+void * engine_app = NULL;
+#endif
+
+static int folder_message_unref(struct folder_ref_info * ref_info,
+ mailmessage * msg)
+{
+ struct message_ref_elt * msg_ref;
+ int count;
+
+ msg_ref = folder_info_get_msg_ref(ref_info, msg);
+
+ if (msg_ref->ref_count == 0) {
+#ifdef ETPAN_APP_DEBUG
+ ETPAN_APP_DEBUG((engine_app, "** BUG detected negative ref count !"));
+#endif
+ }
+
+ count = message_unref(msg_ref);
+ if (count == 0) {
+ folder_message_remove(ref_info, msg);
+ mailmessage_free(msg);
+ }
+
+ return count;
+}
+
+static int folder_message_mime_ref(struct mailprivacy * privacy,
+ struct folder_ref_info * ref_info,
+ mailmessage * msg)
+{
+ struct message_ref_elt * msg_ref;
+
+ msg_ref = folder_info_get_msg_ref(ref_info, msg);
+
+ return message_mime_ref(privacy, msg_ref);
+}
+
+static int folder_message_mime_unref(struct mailprivacy * privacy,
+ struct folder_ref_info * ref_info,
+ mailmessage * msg)
+{
+ struct message_ref_elt * msg_ref;
+
+ msg_ref = folder_info_get_msg_ref(ref_info, msg);
+ return message_mime_unref(privacy, msg_ref);
+}
+
+static int folder_message_add(struct folder_ref_info * ref_info,
+ mailmessage * msg)
+{
+ chashdatum key;
+ chashdatum data;
+ struct message_ref_elt * msg_ref;
+ int r;
+
+ /*
+ r = message_folder_finder_store(ref_info->msg_folder_finder,
+ msg, ref_info->folder);
+ if (r != MAIL_NO_ERROR)
+ goto err;
+ */
+
+ msg_ref = message_ref_elt_new(ref_info->folder, msg);
+ if (msg_ref == NULL)
+ goto msg_folder_remove;
+
+ key.data = &msg;
+ key.len = sizeof(msg);
+ data.data = msg_ref;
+ data.len = 0;
+
+ r = chash_set(ref_info->msg_hash, &key, &data, NULL);
+ if (r < 0)
+ goto free_msg_ref;
+
+ if (msg->msg_uid != NULL) {
+ key.data = msg->msg_uid;
+ key.len = strlen(msg->msg_uid);
+ data.data = msg;
+ data.len = 0;
+
+ r = chash_set(ref_info->uid_hash, &key, &data, NULL);
+ if (r < 0)
+ goto remove_msg_ref;
+ }
+
+ return MAIL_NO_ERROR;
+
+ remove_msg_ref:
+ key.data = &msg;
+ key.len = sizeof(msg);
+ chash_delete(ref_info->msg_hash, &key, NULL);
+ free_msg_ref:
+ message_ref_elt_free(msg_ref);
+ msg_folder_remove:
+ /*
+ message_folder_finder_delete(ref_info->msg_folder_finder, msg);
+ */
+ err:
+ return MAIL_ERROR_MEMORY;
+}
+
+
+static void folder_message_remove(struct folder_ref_info * ref_info,
+ mailmessage * msg)
+{
+ chashdatum key;
+ struct message_ref_elt * msg_ref;
+
+ if (msg->msg_uid != NULL) {
+ key.data = msg->msg_uid;
+ key.len = strlen(msg->msg_uid);
+
+ chash_delete(ref_info->uid_hash, &key, NULL);
+ }
+
+ msg_ref = folder_info_get_msg_ref(ref_info, msg);
+ message_ref_elt_free(msg_ref);
+
+ key.data = &msg;
+ key.len = sizeof(msg);
+
+ chash_delete(ref_info->msg_hash, &key, NULL);
+
+ /*
+ message_folder_finder_delete(ref_info->msg_folder_finder, msg);
+ */
+}
+
+
+static int folder_update_msg_list(struct folder_ref_info * ref_info,
+ struct mailmessage_list ** p_new_msg_list,
+ struct mailmessage_list ** p_lost_msg_list)
+{
+ int r;
+ int res;
+ struct mailmessage_list * new_env_list;
+ unsigned int i;
+ carray * lost_msg_tab;
+ struct mailmessage_list * lost_msg_list;
+ unsigned int free_start_index;
+ chashiter * iter;
+ unsigned int lost_count;
+
+ r = mailfolder_get_messages_list(ref_info->folder, &new_env_list);
+ if (r != MAIL_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ for(iter = chash_begin(ref_info->msg_hash) ; iter != NULL ;
+ iter = chash_next(ref_info->msg_hash, iter)) {
+ struct message_ref_elt * msg_ref;
+ chashdatum data;
+
+ chash_value(iter, &data);
+ msg_ref = data.data;
+ msg_ref->lost = 1;
+ }
+
+ lost_count = chash_count(ref_info->msg_hash);
+
+ for(i = 0 ; i < carray_count(new_env_list->msg_tab) ; i ++) {
+ mailmessage * msg;
+ mailmessage * old_msg;
+
+ msg = carray_get(new_env_list->msg_tab, i);
+
+ if (msg->msg_uid == NULL)
+ continue;
+
+ old_msg = folder_info_get_msg_by_uid(ref_info, msg->msg_uid);
+ if (old_msg != NULL) {
+ struct message_ref_elt * msg_ref;
+
+ /* replace old message */
+ old_msg->msg_index = msg->msg_index;
+ carray_set(new_env_list->msg_tab, i, old_msg);
+ mailmessage_free(msg);
+
+ msg_ref = folder_info_get_msg_ref(ref_info, old_msg);
+ msg_ref->lost = 0;
+ lost_count --;
+ }
+ else {
+ /* set new message */
+ r = folder_message_add(ref_info, msg);
+ if (r != MAIL_NO_ERROR) {
+ free_start_index = i;
+ res = r;
+ goto free_remaining;
+ }
+ }
+ }
+
+ /* build the table of lost messages */
+ lost_msg_tab = carray_new(lost_count);
+ if (lost_msg_tab == NULL) {
+ res = MAIL_ERROR_MEMORY;
+ goto free_env_list;
+ }
+
+ carray_set_size(lost_msg_tab, lost_count);
+
+ i = 0;
+ for(iter = chash_begin(ref_info->msg_hash) ; iter != NULL ;
+ iter = chash_next(ref_info->msg_hash, iter)) {
+ struct message_ref_elt * msg_ref;
+ chashdatum key;
+ chashdatum value;
+ mailmessage * msg;
+
+ chash_key(iter, &key);
+ memcpy(&msg, key.data, sizeof(msg));
+
+ chash_value(iter, &value);
+ msg_ref = value.data;
+ if (msg_ref->lost) {
+ carray_set(lost_msg_tab, i, msg);
+ i ++;
+ }
+ }
+
+ lost_msg_list = mailmessage_list_new(lost_msg_tab);
+ if (lost_msg_list == NULL) {
+ res = MAIL_ERROR_MEMORY;
+ goto free_lost_msg_tab;
+ }
+
+ /* reference messages */
+ for(i = 0 ; i < carray_count(new_env_list->msg_tab) ; i ++) {
+ mailmessage * msg;
+
+ msg = carray_get(new_env_list->msg_tab, i);
+ folder_message_ref(ref_info, msg);
+ }
+
+ * p_new_msg_list = new_env_list;
+ * p_lost_msg_list = lost_msg_list;
+
+ return MAIL_NO_ERROR;
+
+ free_lost_msg_tab:
+ carray_free(lost_msg_tab);
+ free_env_list:
+ for(i = 0 ; i < carray_count(new_env_list->msg_tab) ; i ++) {
+ mailmessage * msg;
+ struct message_ref_elt * msg_ref;
+
+ msg = carray_get(new_env_list->msg_tab, i);
+ msg_ref = folder_info_get_msg_ref(ref_info, msg);
+ if (msg_ref != NULL) {
+ if (msg_ref->ref_count == 0)
+ folder_message_remove(ref_info, msg);
+ }
+ }
+ carray_set_size(new_env_list->msg_tab, 0);
+ mailmessage_list_free(new_env_list);
+ goto err;
+ free_remaining:
+ for(i = 0 ; i < carray_count(new_env_list->msg_tab) ; i ++) {
+ mailmessage * msg;
+ struct message_ref_elt * msg_ref;
+
+ msg = carray_get(new_env_list->msg_tab, i);
+ msg_ref = folder_info_get_msg_ref(ref_info, msg);
+ if (msg_ref != NULL) {
+ if (msg_ref->ref_count == 0)
+ folder_message_remove(ref_info, msg);
+ }
+ }
+ for(i = free_start_index ; i < carray_count(new_env_list->msg_tab) ; i ++) {
+ mailmessage * msg;
+
+ msg = carray_get(new_env_list->msg_tab, i);
+ mailmessage_free(msg);
+ }
+ carray_set_size(new_env_list->msg_tab, 0);
+ mailmessage_list_free(new_env_list);
+ err:
+ return res;
+}
+
+/*
+ folder_fetch_env_list()
+*/
+
+static int folder_fetch_env_list(struct folder_ref_info * ref_info,
+ struct mailmessage_list * msg_list)
+{
+ return mailfolder_get_envelopes_list(ref_info->folder, msg_list);
+}
+
+static void folder_free_msg_list(struct folder_ref_info * ref_info,
+ struct mailmessage_list * env_list)
+{
+ unsigned int i;
+
+ for(i = 0 ; i < carray_count(env_list->msg_tab) ; i ++) {
+ mailmessage * msg;
+ int count;
+
+ msg = carray_get(env_list->msg_tab, i);
+
+ count = folder_message_unref(ref_info, msg);
+ }
+ carray_set_size(env_list->msg_tab, 0);
+ mailmessage_list_free(env_list);
+}
+
+
+/* ************************************************************* */
+/* Storage ref info */
+
+struct storage_ref_info {
+ struct mailstorage * storage;
+#if 0
+ struct message_folder_finder * msg_folder_finder;
+#endif
+
+#if 0
+ /* msg => folder */
+ chash * msg_ref;
+#endif
+
+ /* folder => folder_ref_info */
+ chash * folder_ref_info;
+};
+
+static struct storage_ref_info *
+storage_ref_info_new(struct mailstorage * storage
+ /*, struct message_folder_finder * msg_folder_finder */)
+{
+ struct storage_ref_info * ref_info;
+
+ ref_info = malloc(sizeof(* ref_info));
+ if (ref_info == NULL)
+ goto err;
+
+ ref_info->storage = storage;
+#if 0
+ ref_info->msg_folder_finder = msg_folder_finder;
+#endif
+
+ ref_info->folder_ref_info = chash_new(CHASH_DEFAULTSIZE, CHASH_COPYKEY);
+ if (ref_info->folder_ref_info == NULL)
+ goto free;
+
+ return ref_info;
+
+ free:
+ free(ref_info);
+ err:
+ return NULL;
+}
+
+static void storage_ref_info_free(struct storage_ref_info * ref_info)
+{
+#if 0
+ chash_free(ref_info->msg_ref);
+#endif
+ chash_free(ref_info->folder_ref_info);
+ free(ref_info);
+}
+
+
+static struct folder_ref_info *
+storage_get_folder_ref(struct storage_ref_info * ref_info,
+ struct mailfolder * folder)
+{
+ struct folder_ref_info * folder_ref;
+ chashdatum key;
+ chashdatum value;
+ int r;
+
+ key.data = &folder;
+ key.len = sizeof(folder);
+ r = chash_get(ref_info->folder_ref_info, &key, &value);
+ if (r < 0)
+ return NULL;
+
+ folder_ref = value.data;
+
+ return folder_ref;
+}
+
+static struct folder_ref_info *
+storage_folder_add_ref(struct storage_ref_info * ref_info,
+ struct mailfolder * folder)
+{
+ struct folder_ref_info * folder_ref;
+ chashdatum key;
+ chashdatum value;
+ int r;
+
+ folder_ref = folder_ref_info_new(folder /*, ref_info->msg_folder_finder */);
+ if (folder_ref == NULL)
+ goto err;
+
+ key.data = &folder;
+ key.len = sizeof(folder);
+ value.data = folder_ref;
+ value.len = 0;
+ r = chash_set(ref_info->folder_ref_info, &key, &value, NULL);
+ if (r < 0)
+ goto free;
+
+ return folder_ref;
+
+ free:
+ folder_ref_info_free(folder_ref);
+ err:
+ return NULL;
+}
+
+
+static void storage_folder_remove_ref(struct storage_ref_info * ref_info,
+ struct mailfolder * folder)
+{
+ struct folder_ref_info * folder_ref;
+ chashdatum key;
+ chashdatum value;
+ int r;
+
+ key.data = &folder;
+ key.len = sizeof(folder);
+ r = chash_get(ref_info->folder_ref_info, &key, &value);
+ if (r < 0)
+ return;
+
+ folder_ref = value.data;
+
+ if (folder_ref == NULL)
+ return;
+
+ folder_ref_info_free(folder_ref);
+
+ chash_delete(ref_info->folder_ref_info, &key, &value);
+}
+
+static int storage_folder_get_msg_list(struct storage_ref_info * ref_info,
+ struct mailfolder * folder,
+ struct mailmessage_list ** p_new_msg_list,
+ struct mailmessage_list ** p_lost_msg_list)
+{
+ struct folder_ref_info * folder_ref_info;
+
+ folder_ref_info = storage_get_folder_ref(ref_info, folder);
+ if (folder_ref_info == NULL)
+ return MAIL_ERROR_INVAL;
+
+ return folder_update_msg_list(folder_ref_info,
+ p_new_msg_list, p_lost_msg_list);
+}
+
+static int storage_folder_fetch_env_list(struct storage_ref_info * ref_info,
+ struct mailfolder * folder,
+ struct mailmessage_list * msg_list)
+{
+ struct folder_ref_info * folder_ref_info;
+
+ folder_ref_info = storage_get_folder_ref(ref_info, folder);
+ if (folder_ref_info == NULL)
+ return MAIL_ERROR_INVAL;
+
+ return folder_fetch_env_list(folder_ref_info, msg_list);
+}
+
+static void
+storage_folder_free_msg_list(struct storage_ref_info * ref_info,
+ struct mailfolder * folder,
+ struct mailmessage_list * env_list)
+{
+ struct folder_ref_info * folder_ref_info;
+
+ folder_ref_info = storage_get_folder_ref(ref_info, folder);
+
+ folder_free_msg_list(folder_ref_info, env_list);
+}
+
+
+/* connection and disconnection */
+
+static void
+folder_restore_session(struct folder_ref_info * ref_info)
+{
+ chashiter * iter;
+ mailsession * session;
+
+ session = ref_info->folder->fld_session;
+
+ for(iter = chash_begin(ref_info->msg_hash) ; iter != NULL ;
+ iter = chash_next(ref_info->msg_hash, iter)) {
+ chashdatum key;
+ mailmessage * msg;
+
+ chash_key(iter, &key);
+ memcpy(&msg, key.data, sizeof(msg));
+ msg->msg_session = session;
+
+ if (msg->msg_driver == imap_cached_message_driver) {
+ struct imap_cached_session_state_data * imap_cached_data;
+ mailmessage * ancestor_msg;
+
+ imap_cached_data = ref_info->folder->fld_session->sess_data;
+ ancestor_msg = msg->msg_data;
+ ancestor_msg->msg_session = imap_cached_data->imap_ancestor;
+ }
+ }
+}
+
+static void
+storage_restore_message_session(struct storage_ref_info * ref_info)
+{
+ chashiter * iter;
+
+ for(iter = chash_begin(ref_info->folder_ref_info) ; iter != NULL ;
+ iter = chash_next(ref_info->folder_ref_info, iter)) {
+ chashdatum data;
+ struct folder_ref_info * folder_ref_info;
+
+ chash_value(iter, &data);
+ folder_ref_info = data.data;
+ if (folder_ref_info->lost_session) {
+ if (folder_ref_info->folder->fld_session != NULL) {
+ /* restore folder session */
+ folder_restore_session(folder_ref_info);
+
+ folder_ref_info->lost_session = 0;
+ }
+ }
+ }
+}
+
+
+static int do_storage_connect(struct storage_ref_info * ref_info)
+{
+ int r;
+
+ r = mailstorage_connect(ref_info->storage);
+ if (r != MAIL_NO_ERROR)
+ return r;
+
+ return MAIL_NO_ERROR;
+}
+
+static void do_storage_disconnect(struct storage_ref_info * ref_info)
+{
+ clistiter * cur;
+
+ /* storage is disconnected, session is lost */
+ for(cur = clist_begin(ref_info->storage->sto_shared_folders) ; cur != NULL ;
+ cur = clist_next(cur)) {
+ struct folder_ref_info * folder_ref_info;
+ struct mailfolder * folder;
+
+ folder = clist_content(cur);
+ /* folder is disconnected (in storage), session is lost */
+
+ folder_ref_info = storage_get_folder_ref(ref_info, folder);
+ folder_ref_info->lost_session = 1;
+ }
+
+ /* storage is disconnected */
+ mailstorage_disconnect(ref_info->storage);
+}
+
+
+
+static int folder_connect(struct storage_ref_info * ref_info,
+ struct mailfolder * folder)
+{
+ int r;
+ struct folder_ref_info * folder_ref_info;
+
+ r = do_storage_connect(ref_info);
+ if (r != MAIL_NO_ERROR)
+ return r;
+
+ r = mailfolder_connect(folder);
+ if (r != MAIL_NO_ERROR)
+ return r;
+
+ folder_ref_info = storage_get_folder_ref(ref_info, folder);
+
+ return MAIL_NO_ERROR;
+}
+
+
+static void folder_disconnect(struct storage_ref_info * ref_info,
+ struct mailfolder * folder)
+{
+ struct folder_ref_info * folder_ref_info;
+
+ folder_ref_info = storage_get_folder_ref(ref_info, folder);
+
+ /* folder is disconnected, session is lost */
+ folder_ref_info->lost_session = 1;
+ mailfolder_disconnect(folder);
+
+ if (folder->fld_shared_session)
+ do_storage_disconnect(ref_info);
+}
+
+
+static int storage_folder_connect(struct storage_ref_info * ref_info,
+ struct mailfolder * folder)
+{
+ int r;
+ int res;
+ struct folder_ref_info * folder_ref_info;
+
+ folder_ref_info = storage_get_folder_ref(ref_info, folder);
+ if (folder_ref_info == NULL) {
+ folder_ref_info = storage_folder_add_ref(ref_info, folder);
+ if (folder_ref_info == NULL)
+ return MAIL_ERROR_MEMORY;
+ }
+
+ /* connect folder */
+
+ r = folder_connect(ref_info, folder);
+ if (r == MAIL_ERROR_STREAM) {
+ /* properly handles disconnection */
+
+ /* reconnect */
+ folder_disconnect(ref_info, folder);
+ r = folder_connect(ref_info, folder);
+ }
+
+ if (r != MAIL_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ /* test folder connection */
+ r = mailfolder_noop(folder);
+ if (r == MAIL_ERROR_STREAM) {
+ /* reconnect */
+ folder_disconnect(ref_info, folder);
+ r = folder_connect(ref_info, folder);
+ }
+
+ if ((r != MAIL_ERROR_NOT_IMPLEMENTED) && (r != MAIL_NO_ERROR)) {
+ res = r;
+ goto disconnect;
+ }
+
+ storage_restore_message_session(ref_info);
+
+ return MAIL_NO_ERROR;
+
+ disconnect:
+ folder_disconnect(ref_info, folder);
+ err:
+ return res;
+}
+
+static void storage_folder_disconnect(struct storage_ref_info * ref_info,
+ struct mailfolder * folder)
+{
+ mailfolder_disconnect(folder);
+ storage_folder_remove_ref(ref_info, folder);
+}
+
+static int storage_connect(struct storage_ref_info * ref_info)
+{
+ int r;
+ int res;
+
+ /* connect storage */
+
+ /* properly handles disconnection */
+ r = do_storage_connect(ref_info);
+ if (r == MAIL_ERROR_STREAM) {
+ /* reconnect storage */
+ do_storage_disconnect(ref_info);
+ r = do_storage_connect(ref_info);
+ }
+
+ if (r != MAIL_NO_ERROR) {
+ res = r;
+ goto disconnect;
+ }
+
+ /* test storage connection */
+
+ r = mailsession_noop(ref_info->storage->sto_session);
+ if ((r != MAIL_ERROR_NOT_IMPLEMENTED) && (r != MAIL_NO_ERROR)) {
+ /* properly handles disconnection */
+
+ /* reconnect storage */
+ do_storage_disconnect(ref_info);
+ r = do_storage_connect(ref_info);
+ }
+
+ if (r != MAIL_NO_ERROR) {
+ res = r;
+ goto disconnect;
+ }
+
+ storage_restore_message_session(ref_info);
+
+ return MAIL_NO_ERROR;
+
+ disconnect:
+ do_storage_disconnect(ref_info);
+ return res;
+}
+
+
+static void storage_disconnect(struct storage_ref_info * ref_info)
+{
+ chashiter * iter;
+
+ /* disconnect folders */
+ while ((iter = chash_begin(ref_info->folder_ref_info)) != NULL) {
+ chashdatum key;
+ struct mailfolder * folder;
+
+ chash_key(iter, &key);
+ memcpy(&folder, key.data, sizeof(folder));
+
+ storage_folder_disconnect(ref_info, folder);
+ }
+
+ /* disconnect storage */
+ do_storage_disconnect(ref_info);
+}
+
+
+/* ************************************************************* */
+/* interface for mailengine */
+
+struct mailengine {
+ struct mailprivacy * privacy;
+
+#ifdef LIBETPAN_REENTRANT
+ pthread_mutex_t storage_hash_lock;
+#endif
+ /* storage => storage_ref_info */
+ chash * storage_hash;
+
+#if 0
+ struct message_folder_finder msg_folder_finder;
+#endif
+};
+
+static struct storage_ref_info *
+get_storage_ref_info(struct mailengine * engine,
+ struct mailstorage * storage)
+{
+ chashdatum key;
+ chashdatum data;
+ int r;
+ struct storage_ref_info * ref_info;
+
+ key.data = &storage;
+ key.len = sizeof(storage);
+#ifdef LIBETPAN_REENTRANT
+ pthread_mutex_lock(&engine->storage_hash_lock);
+#endif
+ r = chash_get(engine->storage_hash, &key, &data);
+#ifdef LIBETPAN_REENTRANT
+ pthread_mutex_unlock(&engine->storage_hash_lock);
+#endif
+ if (r < 0)
+ return NULL;
+
+ ref_info = data.data;
+
+ return ref_info;
+}
+
+static struct storage_ref_info *
+add_storage_ref_info(struct mailengine * engine,
+ struct mailstorage * storage)
+{
+ chashdatum key;
+ chashdatum data;
+ int r;
+ struct storage_ref_info * ref_info;
+
+ ref_info = storage_ref_info_new(storage
+ /* , &engine->msg_folder_finder */);
+ if (ref_info == NULL)
+ goto err;
+
+ key.data = &storage;
+ key.len = sizeof(storage);
+ data.data = ref_info;
+ data.len = 0;
+
+#ifdef LIBETPAN_REENTRANT
+ pthread_mutex_lock(&engine->storage_hash_lock);
+#endif
+ r = chash_set(engine->storage_hash, &key, &data, NULL);
+#ifdef LIBETPAN_REENTRANT
+ pthread_mutex_unlock(&engine->storage_hash_lock);
+#endif
+ if (r < 0)
+ goto free;
+
+ ref_info = data.data;
+
+ return ref_info;
+
+ free:
+ storage_ref_info_free(ref_info);
+ err:
+ return NULL;
+}
+
+static void
+remove_storage_ref_info(struct mailengine * engine,
+ struct mailstorage * storage)
+{
+ chashdatum key;
+ chashdatum data;
+ struct storage_ref_info * ref_info;
+
+ key.data = &storage;
+ key.len = sizeof(storage);
+
+#ifdef LIBETPAN_REENTRANT
+ pthread_mutex_lock(&engine->storage_hash_lock);
+#endif
+
+ chash_get(engine->storage_hash, &key, &data);
+ ref_info = data.data;
+
+ if (ref_info != NULL) {
+ storage_ref_info_free(ref_info);
+
+ chash_delete(engine->storage_hash, &key, NULL);
+ }
+
+#ifdef LIBETPAN_REENTRANT
+ pthread_mutex_unlock(&engine->storage_hash_lock);
+#endif
+}
+
+struct mailengine *
+libetpan_engine_new(struct mailprivacy * privacy)
+{
+ struct mailengine * engine;
+ int r;
+
+ engine = malloc(sizeof(* engine));
+ if (engine == NULL)
+ goto err;
+
+ engine->privacy = privacy;
+
+#if 0
+ r = message_folder_finder_init(&engine->msg_folder_finder);
+ if (r != MAIL_NO_ERROR)
+ goto free;
+#endif
+
+#ifdef LIBETPAN_REENTRANT
+ r = pthread_mutex_init(&engine->storage_hash_lock, NULL);
+ if (r != 0)
+ goto free;
+#endif
+
+ engine->storage_hash = chash_new(CHASH_DEFAULTSIZE, CHASH_COPYKEY);
+ if (engine->storage_hash == NULL)
+ goto destroy_mutex;
+
+ return engine;
+
+ destroy_mutex:
+#ifdef LIBETPAN_REENTRANT
+ pthread_mutex_destroy(&engine->storage_hash_lock);
+#endif
+#if 0
+ finder_done:
+ message_folder_finder_done(&engine->msg_folder_finder);
+#endif
+ free:
+ free(engine);
+ err:
+ return NULL;
+}
+
+void libetpan_engine_free(struct mailengine * engine)
+{
+ chash_free(engine->storage_hash);
+#ifdef LIBETPAN_REENTRANT
+ pthread_mutex_destroy(&engine->storage_hash_lock);
+#endif
+#if 0
+ message_folder_finder_done(&engine->msg_folder_finder);
+#endif
+ free(engine);
+}
+
+#if 0
+static struct folder_ref_info *
+message_get_folder_ref(struct mailengine * engine,
+ mailmessage * msg)
+{
+ struct mailfolder * folder;
+ struct mailstorage * storage;
+ struct storage_ref_info * storage_ref_info;
+ struct folder_ref_info * folder_ref_info;
+
+ folder = message_folder_finder_lookup(&engine->msg_folder_finder, msg);
+ if (folder == NULL)
+ storage = NULL;
+ else
+ storage = folder->fld_storage;
+
+ storage_ref_info = get_storage_ref_info(engine, storage);
+
+ folder_ref_info = storage_get_folder_ref(storage_ref_info, folder);
+
+ return folder_ref_info;
+}
+#endif
+
+static struct folder_ref_info *
+message_get_folder_ref(struct mailengine * engine,
+ mailmessage * msg)
+{
+ struct mailfolder * folder;
+ struct mailstorage * storage;
+ struct storage_ref_info * storage_ref_info;
+ struct folder_ref_info * folder_ref_info;
+
+ folder = msg->msg_folder;
+ if (folder == NULL)
+ storage = NULL;
+ else
+ storage = folder->fld_storage;
+
+ storage_ref_info = get_storage_ref_info(engine, storage);
+
+ folder_ref_info = storage_get_folder_ref(storage_ref_info, folder);
+
+ return folder_ref_info;
+}
+
+int libetpan_message_ref(struct mailengine * engine,
+ mailmessage * msg)
+{
+ struct folder_ref_info * ref_info;
+
+ ref_info = message_get_folder_ref(engine, msg);
+
+ return folder_message_ref(ref_info, msg);
+}
+
+int libetpan_message_unref(struct mailengine * engine,
+ mailmessage * msg)
+{
+ struct folder_ref_info * ref_info;
+
+ ref_info = message_get_folder_ref(engine, msg);
+
+ return folder_message_unref(ref_info, msg);
+}
+
+
+int libetpan_message_mime_ref(struct mailengine * engine,
+ mailmessage * msg)
+{
+ struct folder_ref_info * ref_info;
+
+ ref_info = message_get_folder_ref(engine, msg);
+
+ return folder_message_mime_ref(engine->privacy, ref_info, msg);
+}
+
+int libetpan_message_mime_unref(struct mailengine * engine,
+ mailmessage * msg)
+{
+ struct folder_ref_info * ref_info;
+
+ ref_info = message_get_folder_ref(engine, msg);
+
+ return folder_message_mime_unref(engine->privacy, ref_info, msg);
+}
+
+int libetpan_folder_get_msg_list(struct mailengine * engine,
+ struct mailfolder * folder,
+ struct mailmessage_list ** p_new_msg_list,
+ struct mailmessage_list ** p_lost_msg_list)
+{
+ struct storage_ref_info * ref_info;
+
+ ref_info = get_storage_ref_info(engine, folder->fld_storage);
+
+ return storage_folder_get_msg_list(ref_info, folder,
+ p_new_msg_list, p_lost_msg_list);
+}
+
+int libetpan_folder_fetch_env_list(struct mailengine * engine,
+ struct mailfolder * folder,
+ struct mailmessage_list * msg_list)
+{
+ struct storage_ref_info * ref_info;
+
+ ref_info = get_storage_ref_info(engine, folder->fld_storage);
+
+ return storage_folder_fetch_env_list(ref_info, folder, msg_list);
+}
+
+void libetpan_folder_free_msg_list(struct mailengine * engine,
+ struct mailfolder * folder,
+ struct mailmessage_list * env_list)
+{
+ struct storage_ref_info * ref_info;
+
+ ref_info = get_storage_ref_info(engine, folder->fld_storage);
+
+ storage_folder_free_msg_list(ref_info, folder, env_list);
+}
+
+
+int libetpan_storage_add(struct mailengine * engine,
+ struct mailstorage * storage)
+{
+ struct storage_ref_info * storage_ref_info;
+ struct folder_ref_info * folder_ref_info;
+
+ storage_ref_info = add_storage_ref_info(engine, storage);
+ if (storage_ref_info == NULL)
+ goto err;
+
+ if (storage == NULL) {
+ folder_ref_info = storage_folder_add_ref(storage_ref_info, NULL);
+ if (folder_ref_info == NULL)
+ goto remove_storage_ref_info;
+ }
+
+ return MAIL_NO_ERROR;
+
+ remove_storage_ref_info:
+ remove_storage_ref_info(engine, storage);
+ err:
+ return MAIL_ERROR_MEMORY;
+}
+
+void libetpan_storage_remove(struct mailengine * engine,
+ struct mailstorage * storage)
+{
+ struct storage_ref_info * storage_ref_info;
+
+ storage_ref_info = get_storage_ref_info(engine, storage);
+ if (storage == NULL) {
+ storage_folder_remove_ref(storage_ref_info, NULL);
+ }
+
+ remove_storage_ref_info(engine, storage);
+}
+
+int libetpan_storage_connect(struct mailengine * engine,
+ struct mailstorage * storage)
+{
+ struct storage_ref_info * ref_info;
+
+ ref_info = get_storage_ref_info(engine, storage);
+
+ return storage_connect(ref_info);
+}
+
+
+void libetpan_storage_disconnect(struct mailengine * engine,
+ struct mailstorage * storage)
+{
+ struct storage_ref_info * ref_info;
+
+ ref_info = get_storage_ref_info(engine, storage);
+
+ storage_disconnect(ref_info);
+}
+
+int libetpan_storage_used(struct mailengine * engine,
+ struct mailstorage * storage)
+{
+ struct storage_ref_info * ref_info;
+
+ ref_info = get_storage_ref_info(engine, storage);
+
+ return (chash_count(ref_info->folder_ref_info) != 0);
+}
+
+
+int libetpan_folder_connect(struct mailengine * engine,
+ struct mailfolder * folder)
+{
+ struct storage_ref_info * ref_info;
+
+ ref_info = get_storage_ref_info(engine, folder->fld_storage);
+
+ return storage_folder_connect(ref_info, folder);
+}
+
+
+void libetpan_folder_disconnect(struct mailengine * engine,
+ struct mailfolder * folder)
+{
+ struct storage_ref_info * ref_info;
+
+ ref_info = get_storage_ref_info(engine, folder->fld_storage);
+
+ storage_folder_disconnect(ref_info, folder);
+}
+
+
+struct mailfolder *
+libetpan_message_get_folder(struct mailengine * engine,
+ mailmessage * msg)
+{
+#if 0
+ return message_folder_finder_lookup(&engine->msg_folder_finder, msg);
+#endif
+ return msg->msg_folder;
+}
+
+
+struct mailstorage *
+libetpan_message_get_storage(struct mailengine * engine,
+ mailmessage * msg)
+{
+ struct mailfolder * folder;
+
+ folder = libetpan_message_get_folder(engine, msg);
+
+ if (folder == NULL)
+ return NULL;
+ else
+ return folder->fld_storage;
+}
+
+
+int libetpan_message_register(struct mailengine * engine,
+ struct mailfolder * folder,
+ mailmessage * msg)
+{
+ struct storage_ref_info * storage_ref_info;
+ int r;
+ int res;
+ struct folder_ref_info * folder_ref_info;
+ struct mailstorage * storage;
+
+#if 0
+ r = message_folder_finder_store(&engine->msg_folder_finder, msg, folder);
+ if (r != MAIL_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+#endif
+
+ if (folder != NULL)
+ storage = folder->fld_storage;
+ else
+ storage = NULL;
+
+ storage_ref_info = get_storage_ref_info(engine, storage);
+
+ folder_ref_info = storage_get_folder_ref(storage_ref_info, folder);
+
+ r = folder_message_add(folder_ref_info, msg);
+ if (r != MAIL_NO_ERROR) {
+ res = r;
+ goto delete;
+ }
+
+ return MAIL_NO_ERROR;
+
+ delete:
+#if 0
+ message_folder_finder_delete(&engine->msg_folder_finder, msg);
+#endif
+ err:
+ return res;
+}
+
+struct mailprivacy *
+libetpan_engine_get_privacy(struct mailengine * engine)
+{
+ return engine->privacy;
+}
+
+
+static void folder_debug(struct folder_ref_info * folder_ref_info, FILE * f)
+{
+ fprintf(f, "folder debug -- begin\n");
+ if (folder_ref_info->folder == NULL) {
+ fprintf(f, "NULL folder\n");
+ }
+ else {
+ if (folder_ref_info->folder->fld_virtual_name != NULL)
+ fprintf(f, "folder %s\n", folder_ref_info->folder->fld_virtual_name);
+ else
+ fprintf(f, "folder [no name]\n");
+ }
+
+ fprintf(f, "message count: %i\n", chash_count(folder_ref_info->msg_hash));
+ fprintf(f, "UID count: %i\n", chash_count(folder_ref_info->uid_hash));
+ fprintf(f, "folder debug -- end\n");
+}
+
+static void storage_debug(struct storage_ref_info * storage_ref_info, FILE * f)
+{
+ chashiter * iter;
+
+ fprintf(f, "storage debug -- begin\n");
+ if (storage_ref_info->storage == NULL) {
+ fprintf(f, "NULL storage\n");
+ }
+ else {
+ if (storage_ref_info->storage->sto_id != NULL)
+ fprintf(f, "storage %s\n", storage_ref_info->storage->sto_id);
+ else
+ fprintf(f, "storage [no name]\n");
+ }
+ fprintf(f, "folder count: %i\n",
+ chash_count(storage_ref_info->folder_ref_info));
+
+ for(iter = chash_begin(storage_ref_info->folder_ref_info) ; iter != NULL ;
+ iter = chash_next(storage_ref_info->folder_ref_info, iter)) {
+ chashdatum data;
+ struct folder_ref_info * folder_ref_info;
+
+ chash_value(iter, &data);
+ folder_ref_info = data.data;
+
+ folder_debug(folder_ref_info, f);
+ }
+ fprintf(f, "storage debug -- end\n");
+}
+
+void libetpan_engine_debug(struct mailengine * engine, FILE * f)
+{
+ chashiter * iter;
+
+ fprintf(f, "mail engine debug -- begin\n");
+
+ for(iter = chash_begin(engine->storage_hash) ; iter != NULL ;
+ iter = chash_next(engine->storage_hash, iter)) {
+ chashdatum data;
+ struct storage_ref_info * storage_ref_info;
+
+ chash_value(iter, &data);
+ storage_ref_info = data.data;
+
+ storage_debug(storage_ref_info, f);
+ }
+
+#if 0
+ fprintf(f, "global message references: %i\n",
+ chash_count(engine->msg_folder_finder.message_hash));
+#endif
+ fprintf(f, "mail engine debug -- end\n");
+}
+