[evolution-mapi] Bug #638247 - Deadlock when enables an account



commit dad54aaecf7080cec4ec57c055239ac86a646ebb
Author: Milan Crha <mcrha redhat com>
Date:   Thu Jan 13 13:42:00 2011 +0100

    Bug #638247 - Deadlock when enables an account

 .../exchange-mapi-account-listener.c               |  248 +++++++++++++++-----
 src/libexchangemapi/em-operation-queue.c           |   66 ++++++
 src/libexchangemapi/em-operation-queue.h           |    7 +
 src/libexchangemapi/exchange-mapi-connection.c     |   29 ++-
 src/libexchangemapi/exchange-mapi-folder.c         |   32 +++
 src/libexchangemapi/exchange-mapi-folder.h         |    2 +
 6 files changed, 324 insertions(+), 60 deletions(-)
---
diff --git a/src/account-setup-eplugin/exchange-mapi-account-listener.c b/src/account-setup-eplugin/exchange-mapi-account-listener.c
index e0fbe18..ba33d10 100644
--- a/src/account-setup-eplugin/exchange-mapi-account-listener.c
+++ b/src/account-setup-eplugin/exchange-mapi-account-listener.c
@@ -40,6 +40,7 @@
 #include <exchange-mapi-folder.h>
 #include <exchange-mapi-connection.h>
 #include <exchange-mapi-utils.h>
+#include <em-operation-queue.h>
 
 #define d(x)
 
@@ -63,8 +64,36 @@ struct _ExchangeMAPIAccountInfo {
 	gboolean enabled;
 };
 
+static ExchangeMAPIAccountInfo *
+copy_mapi_account_info (const ExchangeMAPIAccountInfo *src)
+{
+	ExchangeMAPIAccountInfo *res;
+
+	g_return_val_if_fail (src != NULL, NULL);
+
+	res = g_new0 (ExchangeMAPIAccountInfo, 1);
+	res->uid = g_strdup (src->uid);
+	res->name = g_strdup (src->name);
+	res->source_url = g_strdup (src->source_url);
+	res->enabled = src->enabled;
+
+	return res;
+}
+
+static void
+free_mapi_account_info (ExchangeMAPIAccountInfo *info)
+{
+	g_return_if_fail (info != NULL);
+
+	g_free (info->uid);
+	g_free (info->name);
+	g_free (info->source_url);
+	g_free (info);
+}
+
 /* list of ExchangeMAPIAccountInfo structures */
-static	GList *mapi_accounts = NULL;
+static GList *mapi_accounts = NULL;
+static gpointer async_ops = NULL; /* EMOperationQueue * */
 
 static GObjectClass *parent_class = NULL;
 
@@ -102,6 +131,9 @@ finalize (GObject *object)
 	g_list_free (mapi_accounts);
 
 	G_OBJECT_CLASS (parent_class)->finalize (object);
+
+	if (async_ops)
+		g_object_unref (async_ops);
 }
 
 static void
@@ -467,7 +499,6 @@ remove_cal_esource (EAccount *existing_account_info, ExchangeMAPIFolderType fold
 	ESourceList *list;
 	const gchar *conf_key = NULL, *source_selection_key = NULL;
 	GSList *groups;
-	gboolean found_group;
 	GConfClient* client;
 	GSList *ids;
 	GSList *node_tobe_deleted;
@@ -489,13 +520,10 @@ remove_cal_esource (EAccount *existing_account_info, ExchangeMAPIFolderType fold
 
 	client = gconf_client_get_default();
 	list = e_source_list_new_for_gconf (client, conf_key);
-	groups = e_source_list_peek_groups (list);
 
 	base_uri = g_strdup_printf ("mapi://%s %s/", url->user, url->host);
 
-	found_group = FALSE;
-
-	for (; groups != NULL && !found_group; groups = g_slist_next (groups)) {
+	for (groups = e_source_list_peek_groups (list); groups != NULL; groups = g_slist_next (groups)) {
 		ESourceGroup *group = E_SOURCE_GROUP (groups->data);
 
 		if (strcmp (e_source_group_peek_name (group), existing_account_info->name) == 0 &&
@@ -519,7 +547,6 @@ remove_cal_esource (EAccount *existing_account_info, ExchangeMAPIFolderType fold
 			}
 			e_source_list_remove_group (list, group);
 			e_source_list_sync (list, NULL);
-			found_group = TRUE;
 			break;
 		}
 	}
@@ -530,8 +557,9 @@ remove_cal_esource (EAccount *existing_account_info, ExchangeMAPIFolderType fold
 }
 
 /* add sources for calendar and tasks if the account added is exchange account
-   adds the new account info to mapi_accounts list */
-
+   adds the new account info to mapi_accounts list;
+   it is always called in the main thread
+*/
 static void
 add_calendar_sources (EAccount *account, GSList *folders, mapi_id_t trash_fid)
 {
@@ -543,28 +571,48 @@ add_calendar_sources (EAccount *account, GSList *folders, mapi_id_t trash_fid)
 		add_cal_esource (account, folders, MAPI_FOLDER_TYPE_APPOINTMENT, url, trash_fid);
 		add_cal_esource (account, folders, MAPI_FOLDER_TYPE_TASK, url, trash_fid);
 		add_cal_esource (account, folders, MAPI_FOLDER_TYPE_MEMO, url, trash_fid);
-	}
 
-	camel_url_free (url);
+		camel_url_free (url);
+	}
 }
 
 /* removes calendar and tasks sources if the account removed is exchange account
-   removes the the account info from mapi_account list */
-
+   removes the the account info from mapi_account list;
+   it is always called in the main thread
+*/
 static void
-remove_calendar_sources (EAccount *account)
+remove_calendar_sources_async (gpointer worker_data, gboolean cancelled, gpointer user_data)
 {
+	EAccount *account = worker_data;
 	CamelURL *url;
 
+	g_return_if_fail (account != NULL);
+
 	url = camel_url_new (account->source->url, NULL);
 
 	if (url) {
 		remove_cal_esource (account, MAPI_FOLDER_TYPE_APPOINTMENT, url);
 		remove_cal_esource (account, MAPI_FOLDER_TYPE_TASK, url);
 		remove_cal_esource (account, MAPI_FOLDER_TYPE_MEMO, url);
+
+		camel_url_free (url);
 	}
 
-	camel_url_free (url);
+	g_object_unref (account);
+}
+
+static void
+remove_calendar_sources (EAccount *account)
+{
+	g_return_if_fail (account != NULL);
+
+	g_object_ref (account);
+
+	if (g_main_context_is_owner (g_main_context_default ())) {
+		remove_calendar_sources_async (account, FALSE, NULL);
+	} else {
+		em_async_queue_push (async_ops, account, NULL, NULL, remove_calendar_sources_async);
+	}
 }
 
 static gboolean
@@ -738,37 +786,37 @@ add_addressbook_sources (EAccount *account, GSList *folders, mapi_id_t trash_fid
 	return TRUE;
 }
 
+/* this is always called in the main thread */
 static void
-remove_addressbook_sources (ExchangeMAPIAccountInfo *existing_account_info)
+remove_addressbook_sources_async (gpointer worker_data, gboolean cancelled, gpointer user_data)
 {
+	ExchangeMAPIAccountInfo *existing_account_info = worker_data;
 	ESourceList *list;
 	ESourceGroup *group;
 	GSList *groups;
-	gboolean found_group;
 	CamelURL *url;
 	gchar *base_uri;
 	GConfClient *client;
 
+	g_return_if_fail (existing_account_info != NULL);
+
 	url = camel_url_new (existing_account_info->source_url, NULL);
 	if (url == NULL) {
+		free_mapi_account_info (existing_account_info);
 		return;
 	}
 
 	base_uri = g_strdup_printf ("mapi://%s %s/", url->user, url->host);
 	client = gconf_client_get_default ();
 	list = e_source_list_new_for_gconf (client, "/apps/evolution/addressbook/sources" );
-	groups = e_source_list_peek_groups (list);
-
-	found_group = FALSE;
-
-	for (; groups != NULL && !found_group; groups = g_slist_next (groups)) {
 
+	for (groups = e_source_list_peek_groups (list); groups != NULL; groups = g_slist_next (groups)) {
 		group = E_SOURCE_GROUP (groups->data);
-		if ( strcmp ( e_source_group_peek_base_uri (group), base_uri) == 0 && strcmp (e_source_group_peek_name (group), existing_account_info->name) == 0) {
 
+		if (strcmp (e_source_group_peek_base_uri (group), base_uri) == 0 && strcmp (e_source_group_peek_name (group), existing_account_info->name) == 0) {
 			e_source_list_remove_group (list, group);
 			e_source_list_sync (list, NULL);
-			found_group = TRUE;
+			break;
 		}
 	}
 
@@ -776,45 +824,104 @@ remove_addressbook_sources (ExchangeMAPIAccountInfo *existing_account_info)
 	g_object_unref (client);
 	g_free (base_uri);
 	camel_url_free (url);
+	free_mapi_account_info (existing_account_info);
 }
 
-static gboolean
-update_sources_idle_cb (gpointer data)
+static void
+remove_addressbook_sources (ExchangeMAPIAccountInfo *existing_account_info)
+{
+	g_return_if_fail (existing_account_info != NULL);
+
+	if (g_main_context_is_owner (g_main_context_default ())) {
+		remove_addressbook_sources_async (copy_mapi_account_info (existing_account_info), FALSE, NULL);
+	} else {
+		em_async_queue_push (async_ops, copy_mapi_account_info (existing_account_info), NULL, NULL, remove_addressbook_sources_async);
+	}
+}
+
+struct add_sources_data
+{
+	EAccount *account;
+	GSList *folders;
+	mapi_id_t trash_fid;
+};
+
+static void
+add_sources_async (gpointer worker_data, gboolean cancelled, gpointer user_data)
+{
+	struct add_sources_data *data = worker_data;
+
+	g_return_if_fail (data != NULL);
+
+	add_addressbook_sources (data->account, data->folders, data->trash_fid);
+	add_calendar_sources (data->account, data->folders, data->trash_fid);
+
+	g_object_unref (data->account);
+	exchange_mapi_folder_free_list (data->folders);
+	g_free (data);
+}
+
+static void
+add_sources (EAccount *account, GSList *folders, mapi_id_t trash_fid)
+{
+	struct add_sources_data *data;
+
+	g_return_if_fail (account != NULL);
+	g_return_if_fail (folders != NULL);
+
+	data = g_new0 (struct add_sources_data, 1);
+	data->account = g_object_ref (account);
+	data->folders = exchange_mapi_folder_copy_list (folders);
+	data->trash_fid = trash_fid;
+
+	if (g_main_context_is_owner (g_main_context_default ())) {
+		add_sources_async (data, FALSE, NULL);
+	} else {
+		em_async_queue_push (async_ops, data, NULL, NULL, add_sources_async);
+	}
+}
+
+static void
+update_sources_idle_cb (gpointer data, gboolean cancelled, gpointer user_data)
 {
 	ExchangeMapiConnection *conn = data;
 	EAccount *account;
 	GSList *folders_list;
 
-	g_return_val_if_fail (conn != NULL, FALSE);
+	g_return_if_fail (conn != NULL);
 
 	account = g_object_get_data (G_OBJECT (conn), "EAccount");
 	if (!account) {
 		g_object_unref (conn);
-		g_return_val_if_fail (account != NULL, FALSE);
-		return FALSE;
+		g_return_if_fail (account != NULL);
+		return;
 	}
 
 	g_object_set_data (G_OBJECT (conn), "EAccount", NULL);
 
-	folders_list = exchange_mapi_connection_peek_folders_list (conn);
+	if (!cancelled) {
+		folders_list = exchange_mapi_connection_peek_folders_list (conn);
 
-	if (account->enabled && lookup_account_info (account->uid)) {
-		mapi_id_t trash_fid = exchange_mapi_connection_get_default_folder_id (conn, olFolderDeletedItems, NULL);
+		if (account->enabled && lookup_account_info (account->uid)) {
+			mapi_id_t *trash_fid = user_data;
 
-		add_addressbook_sources (account, folders_list, trash_fid);
-		add_calendar_sources (account, folders_list, trash_fid);
+			add_sources (account, folders_list, trash_fid ? *trash_fid : 0);
+		}
 	}
 
 	g_object_unref (conn);
 	g_object_unref (account);
-
-	return FALSE;
+	g_free (user_data);
 }
 
 static void
-update_sources_fn (gpointer data, gpointer user_data)
+update_sources_cb (gpointer data, gboolean cancelled, gpointer user_data)
 {
 	ExchangeMapiConnection *conn = data;
+	mapi_id_t *trash_id = user_data;
+
+	if (cancelled)
+		return;
 
 	g_return_if_fail (conn != NULL);
 
@@ -822,28 +929,20 @@ update_sources_fn (gpointer data, gpointer user_data)
 	   thus next call will be quick as much as possible */
 	exchange_mapi_connection_peek_folders_list (conn);
 
-	/* run a job in a main thread */
-	g_idle_add (update_sources_idle_cb, conn);
+	if (trash_id)
+		*trash_id = exchange_mapi_connection_get_default_folder_id (conn, olFolderDeletedItems, NULL);
 }
 
 static void
 run_update_sources_thread (ExchangeMapiConnection *conn, EAccount *account)
 {
-	static GThreadPool *thread_pool = NULL;
-
 	g_return_if_fail (conn != NULL);
 	g_return_if_fail (account != NULL);
-
-	/* this should be called only on the main thread, thus no locking needed */
-	if (!thread_pool)
-		thread_pool = g_thread_pool_new (update_sources_fn, NULL, 1, FALSE, NULL);
+	g_return_if_fail (async_ops != NULL);
 
 	g_object_set_data (G_OBJECT (conn), "EAccount", g_object_ref (account));
 
-	if (!thread_pool)
-		update_sources_fn (conn, NULL);
-	else
-		g_thread_pool_push (thread_pool, conn, NULL);
+	em_async_queue_push (async_ops, conn, g_new0 (mapi_id_t, 1), update_sources_cb, update_sources_idle_cb);
 }
 
 struct create_sources_data
@@ -881,10 +980,12 @@ check_for_account_conn_cb (gpointer data)
 }
 
 static void
-update_account_sources (EAccount *account, gboolean can_create_profile)
+update_account_sources_async (gpointer worker_data, gboolean cancelled, gpointer user_data)
 {
 	CamelURL *url;
 	ExchangeMapiConnection *conn;
+	EAccount *account = worker_data;
+	gboolean can_create_profile = GPOINTER_TO_INT (user_data) ? TRUE : FALSE;
 
 	url = camel_url_new (account->source->url, NULL);
 	g_return_if_fail (url != NULL);
@@ -914,6 +1015,21 @@ update_account_sources (EAccount *account, gboolean can_create_profile)
 	}
 
 	camel_url_free (url);
+	g_object_unref (account);
+}
+
+static void
+update_account_sources (EAccount *account, gboolean can_create_profile)
+{
+	g_return_if_fail (account != NULL);
+
+	if (g_main_context_is_owner (g_main_context_default ())) {
+		/* called from main thread, but we want this to be called
+		   in its own thread, thus create it */
+		em_async_queue_push (async_ops, g_object_ref (account), GINT_TO_POINTER (can_create_profile ? 1 : 0), update_account_sources_async, NULL);
+	} else {
+		update_account_sources_async (g_object_ref (account), FALSE, GINT_TO_POINTER (can_create_profile ? 1 : 0));
+	}
 }
 
 static void
@@ -979,10 +1095,7 @@ mapi_account_removed (EAccountList *account_listener, EAccount *account)
 	}
 
 	/* Free up the structure */
-	g_free (info->uid);
-	g_free (info->name);
-	g_free (info->source_url);
-	g_free (info);
+	free_mapi_account_info (info);
 }
 
 static gboolean
@@ -1066,12 +1179,19 @@ mapi_camel_url_equal (CamelURL *a, CamelURL *b)
 	return retval;
 }
 
+static void mapi_account_changed (EAccountList *account_listener, EAccount *account);
+
 static void
-mapi_account_changed (EAccountList *account_listener, EAccount *account)
+mapi_account_changed_async (gpointer worker_data, gboolean cancelled, gpointer user_data)
 {
 	CamelURL *new_url = NULL, *old_url = NULL;
 	gboolean isa_mapi_account = FALSE;
 	ExchangeMAPIAccountInfo *existing_account_info = NULL;
+	EAccountList *account_listener = worker_data;
+	EAccount *account = user_data;
+
+	g_return_if_fail (account_listener != NULL);
+	g_return_if_fail (account != NULL);
 
 	isa_mapi_account = is_mapi_account (account);
 
@@ -1146,6 +1266,17 @@ mapi_account_changed (EAccountList *account_listener, EAccount *account)
 		camel_url_free (old_url);
 
 	camel_url_free (new_url);
+
+	g_object_unref (account_listener);
+	g_object_unref (account);
+}
+
+static void
+mapi_account_changed (EAccountList *account_listener, EAccount *account)
+{
+	g_return_if_fail (async_ops != NULL);
+
+	em_async_queue_push (async_ops, g_object_ref (account_listener), g_object_ref (account), mapi_account_changed_async, NULL);
 }
 
 static void
@@ -1188,6 +1319,13 @@ exchange_mapi_account_listener_new (void)
 {
 	ExchangeMAPIAccountListener *config_listener;
 
+	if (!async_ops) {
+		async_ops = em_async_queue_new ();
+		g_object_add_weak_pointer (G_OBJECT (async_ops), &async_ops);
+	} else {
+		g_object_ref (async_ops);
+	}
+
 	config_listener = g_object_new (EXCHANGE_MAPI_ACCOUNT_LISTENER_TYPE, NULL);
 	config_listener->priv->gconf_client = gconf_client_get_default();
 
diff --git a/src/libexchangemapi/em-operation-queue.c b/src/libexchangemapi/em-operation-queue.c
index b69b642..28a74e3 100644
--- a/src/libexchangemapi/em-operation-queue.c
+++ b/src/libexchangemapi/em-operation-queue.c
@@ -273,3 +273,69 @@ em_operation_queue_length (EMOperationQueue *queue)
 
 	return len;
 }
+
+struct async_queue_data
+{
+	gpointer worker_data;
+	gpointer user_data;
+	EMOperationQueueFunc worker_cb;
+	EMOperationQueueFunc done_cb;
+
+	gboolean cancelled;
+};
+
+static gboolean
+async_queue_idle_cb (gpointer user_data)
+{
+	struct async_queue_data *data = user_data;
+
+	g_return_val_if_fail (data != NULL, FALSE);
+	g_return_val_if_fail (data->done_cb != NULL, FALSE);
+
+	if (data->done_cb)
+		data->done_cb (data->worker_data, data->cancelled, data->user_data);
+
+	g_free (data);
+
+	return FALSE;
+}
+
+static void
+async_queue_worker_cb (gpointer worker_data, gboolean cancelled, gpointer user_data)
+{
+	struct async_queue_data *data = worker_data;
+
+	g_return_if_fail (data != NULL);
+
+	data->cancelled = cancelled;
+
+	if (data->worker_cb)
+		data->worker_cb (data->worker_data, data->cancelled, data->user_data);
+
+	if (data->done_cb)
+		g_idle_add (async_queue_idle_cb, data);
+	else
+		g_free (data);
+}
+
+EMOperationQueue *
+em_async_queue_new (void)
+{
+	return em_operation_queue_new (async_queue_worker_cb, NULL);
+}
+
+void
+em_async_queue_push (EMOperationQueue *queue, gpointer worker_data, gpointer user_data, EMOperationQueueFunc worker_cb, EMOperationQueueFunc done_cb)
+{
+	struct async_queue_data *data;
+
+	g_return_if_fail (queue != NULL);
+
+	data = g_new0 (struct async_queue_data, 1);
+	data->worker_data = worker_data;
+	data->user_data = user_data;
+	data->worker_cb = worker_cb;
+	data->done_cb = done_cb;
+
+	em_operation_queue_push (queue, data);
+}
diff --git a/src/libexchangemapi/em-operation-queue.h b/src/libexchangemapi/em-operation-queue.h
index 2781509..09b3307 100644
--- a/src/libexchangemapi/em-operation-queue.h
+++ b/src/libexchangemapi/em-operation-queue.h
@@ -62,4 +62,11 @@ gboolean		em_operation_queue_cancel	(EMOperationQueue *queue, gpointer worker_da
 gboolean		em_operation_queue_cancel_all	(EMOperationQueue *queue);
 gint			em_operation_queue_length	(EMOperationQueue *queue);
 
+EMOperationQueue *	em_async_queue_new		(void);
+void			em_async_queue_push		(EMOperationQueue *queue,
+							 gpointer worker_data,
+							 gpointer user_data,
+							 EMOperationQueueFunc worker_cb, /* run in a new thread */
+							 EMOperationQueueFunc done_cb);  /* run in a main thread */
+
 #endif /* EM_OPERATION_QUEUE */
diff --git a/src/libexchangemapi/exchange-mapi-connection.c b/src/libexchangemapi/exchange-mapi-connection.c
index a8dfc55..b7cd686 100644
--- a/src/libexchangemapi/exchange-mapi-connection.c
+++ b/src/libexchangemapi/exchange-mapi-connection.c
@@ -160,6 +160,7 @@ struct _ExchangeMapiConnectionPrivate {
 	mapi_object_t public_store;
 
 	GSList *folders;		/* list of ExchangeMapiFolder pointers */
+	GStaticRecMutex folders_lock;	/* lock for 'folders' variable */
 
 	GHashTable *named_ids;		/* cache of named ids; key is a folder ID, value is a hash table
 					   of named_id to prop_id in that respective folder */
@@ -174,8 +175,11 @@ disconnect (ExchangeMapiConnectionPrivate *priv)
 	if (!priv->session)
 		return;
 
+	g_static_rec_mutex_lock (&priv->folders_lock);
 	if (priv->folders)
 		exchange_mapi_folder_free_list (priv->folders);
+	priv->folders = NULL;
+	g_static_rec_mutex_unlock (&priv->folders_lock);
 
 	if (priv->has_public_store)
 		mapi_object_release (&priv->public_store);
@@ -187,7 +191,6 @@ disconnect (ExchangeMapiConnectionPrivate *priv)
 
 	priv->session = NULL;
 	priv->has_public_store = FALSE;
-	priv->folders = NULL;
 }
 
 /* should have session_lock locked already, when calling this function */
@@ -236,6 +239,7 @@ exchange_mapi_connection_finalize (GObject *object)
 
 		UNLOCK ();
 		g_static_rec_mutex_free (&priv->session_lock);
+		g_static_rec_mutex_free (&priv->folders_lock);
 	}
 
 	if (G_OBJECT_CLASS (exchange_mapi_connection_parent_class)->finalize)
@@ -261,6 +265,9 @@ exchange_mapi_connection_init (ExchangeMapiConnection *conn)
 	priv = EXCHANGE_MAPI_CONNECTION_GET_PRIVATE (conn);
 	g_return_if_fail (priv != NULL);
 
+	g_static_rec_mutex_init (&priv->session_lock);
+	g_static_rec_mutex_init (&priv->folders_lock);
+
 	priv->session = NULL;
 	priv->profile = NULL;
 	priv->has_public_store = FALSE;
@@ -2041,7 +2048,9 @@ exchange_mapi_connection_create_folder (ExchangeMapiConnection *conn, uint32_t o
 
 	fid = mapi_object_get_id (&obj_folder);
 	g_debug("Folder %s created with id %016" G_GINT64_MODIFIER "X ", name, fid);
-	
+
+	g_static_rec_mutex_lock (&priv->folders_lock);
+
 	/* we should also update folder list locally */
 	if (fid != 0 && priv->folders != NULL) {
 		ExchangeMAPIFolder *folder = NULL;
@@ -2050,6 +2059,8 @@ exchange_mapi_connection_create_folder (ExchangeMapiConnection *conn, uint32_t o
 			priv->folders = g_slist_append (priv->folders, folder);
 	}
 
+	g_static_rec_mutex_unlock (&priv->folders_lock);
+
 cleanup:
 	mapi_object_release(&obj_folder);
 	mapi_object_release(&obj_top);
@@ -2175,7 +2186,10 @@ cleanup:
 	mapi_object_release(&obj_folder);
 	mapi_object_release(&obj_top);
 
+	g_static_rec_mutex_lock (&priv->folders_lock);
 	priv->folders = g_slist_remove (priv->folders, folder);
+	g_static_rec_mutex_unlock (&priv->folders_lock);
+
 	exchange_mapi_folder_free (folder);
 
 	UNLOCK ();
@@ -3349,10 +3363,15 @@ exchange_mapi_connection_peek_folders_list (ExchangeMapiConnection *conn)
 	CHECK_CORRECT_CONN_AND_GET_PRIV (conn, FALSE);
 	e_return_val_mapi_error_if_fail (priv->session != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 
-	LOCK ();
-	if (!priv->folders)
+	g_static_rec_mutex_lock (&priv->folders_lock);
+
+	if (!priv->folders) {
+		LOCK ();
 		exchange_mapi_connection_get_folders_list (conn, &priv->folders, perror);
-	UNLOCK ();
+		UNLOCK ();
+	}
+
+	g_static_rec_mutex_unlock (&priv->folders_lock);
 
 	return priv->folders;
 }
diff --git a/src/libexchangemapi/exchange-mapi-folder.c b/src/libexchangemapi/exchange-mapi-folder.c
index f4d3954..794bbf2 100644
--- a/src/libexchangemapi/exchange-mapi-folder.c
+++ b/src/libexchangemapi/exchange-mapi-folder.c
@@ -73,6 +73,25 @@ exchange_mapi_folder_new (const gchar *folder_name, const gchar *container_class
 	return folder;
 }
 
+ExchangeMAPIFolder *
+exchange_mapi_folder_copy (ExchangeMAPIFolder *src)
+{
+	ExchangeMAPIFolder *res;
+
+	g_return_val_if_fail (src != NULL, NULL);
+
+	res = g_new0 (ExchangeMAPIFolder, 1);
+	*res = *src;
+	
+	res->owner_name = g_strdup (src->owner_name);
+	res->owner_email = g_strdup (src->owner_email);
+	res->user_name = g_strdup (src->user_name);
+	res->user_email = g_strdup (src->user_email);
+	res->folder_name = g_strdup (src->folder_name);
+
+	return res;
+}
+
 void
 exchange_mapi_folder_free (ExchangeMAPIFolder *folder)
 {
@@ -130,6 +149,19 @@ exchange_mapi_folder_get_total_count (ExchangeMAPIFolder *folder)
 	return folder->total;
 }
 
+GSList *
+exchange_mapi_folder_copy_list (GSList *folder_list)
+{
+	GSList *res, *ii;
+	
+	res = g_slist_copy (folder_list);
+	for (ii = res; ii; ii = ii->next) {
+		ii->data = exchange_mapi_folder_copy (ii->data);
+	}
+
+	return res;
+}
+
 void
 exchange_mapi_folder_free_list (GSList *folder_list)
 {
diff --git a/src/libexchangemapi/exchange-mapi-folder.h b/src/libexchangemapi/exchange-mapi-folder.h
index a8ee366..6c4a769 100644
--- a/src/libexchangemapi/exchange-mapi-folder.h
+++ b/src/libexchangemapi/exchange-mapi-folder.h
@@ -77,6 +77,7 @@ exchange_mapi_folder_new (const gchar *folder_name, const gchar *container_class
 			  ExchangeMAPIFolderCategory catgory,
 			  mapi_id_t folder_id, mapi_id_t parent_folder_id,
 			  uint32_t child_count, uint32_t unread_count, uint32_t total);
+ExchangeMAPIFolder *exchange_mapi_folder_copy (ExchangeMAPIFolder *src);
 void exchange_mapi_folder_free (ExchangeMAPIFolder *folder);
 ExchangeMAPIFolderType exchange_mapi_container_class (gchar *type);
 
@@ -88,6 +89,7 @@ guint32 exchange_mapi_folder_get_unread_count (ExchangeMAPIFolder *folder);
 guint32 exchange_mapi_folder_get_total_count (ExchangeMAPIFolder *folder);
 gboolean exchange_mapi_folder_is_root (ExchangeMAPIFolder *folder);
 
+GSList *exchange_mapi_folder_copy_list (GSList *folder_list);
 void exchange_mapi_folder_free_list (GSList *folder_list);
 
 #endif



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]