[evolution-kolab] Prototype automatic resource discovery.
- From: Matthew Barnes <mbarnes src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution-kolab] Prototype automatic resource discovery.
- Date: Tue, 21 Aug 2012 18:45:34 +0000 (UTC)
commit 90e54b8ea598167268be84dbcfe4a97510b61fed
Author: Matthew Barnes <mbarnes redhat com>
Date: Mon Aug 13 13:24:26 2012 -0400
Prototype automatic resource discovery.
On startup, the EKolabBackend queries the server for a folder list and
creates ESources to represent the event/task/note/contact type folders.
src/collection/e-kolab-backend.c | 487 ++++++++++++++++++++++++++++++++++++-
src/collection/e-kolab-backend.h | 30 +++
src/libekolab/kolab-mail-access.c | 24 ++-
3 files changed, 531 insertions(+), 10 deletions(-)
---
diff --git a/src/collection/e-kolab-backend.c b/src/collection/e-kolab-backend.c
index 34373c3..bd52aa2 100644
--- a/src/collection/e-kolab-backend.c
+++ b/src/collection/e-kolab-backend.c
@@ -19,12 +19,23 @@
#include <libekolab/e-source-kolab-folder.h>
#include <libekolab/camel-kolab-imapx-settings.h>
+#include <libekolabutil/kolab-util-camel.h>
+
#define E_KOLAB_BACKEND_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE \
((obj), E_TYPE_KOLAB_BACKEND, EKolabBackendPrivate))
+/* This forces the GType to be registered in a way that
+ * avoids a "statement with no effect" compiler warning. */
+#define REGISTER_TYPE(type) \
+ (g_type_class_unref (g_type_class_ref (type)))
+
+#define KOLAB_PATH_SEPARATOR '/'
+#define KOLAB_PATH_SEPARATOR_S "/"
+
struct _EKolabBackendPrivate {
- gint placeholder; /* remove when there's something else to add */
+ KolabMailAccess *koma;
+ GMutex koma_lock;
};
G_DEFINE_DYNAMIC_TYPE (
@@ -32,17 +43,77 @@ G_DEFINE_DYNAMIC_TYPE (
e_kolab_backend,
E_TYPE_COLLECTION_BACKEND)
+static CamelKolabIMAPXSettings *
+kolab_backend_get_settings (EKolabBackend *backend)
+{
+ ESource *source;
+ ESourceCamel *extension;
+ CamelSettings *settings;
+ const gchar *extension_name;
+ const gchar *protocol;
+
+ protocol = KOLAB_CAMEL_PROVIDER_PROTOCOL;
+ source = e_backend_get_source (E_BACKEND (backend));
+ extension_name = e_source_camel_get_extension_name (protocol);
+ extension = e_source_get_extension (source, extension_name);
+ settings = e_source_camel_get_settings (extension);
+
+ return CAMEL_KOLAB_IMAPX_SETTINGS (settings);
+}
+
+static void
+kolab_backend_sync_folders_done_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ EKolabBackend *backend;
+ GError *error = NULL;
+
+ backend = E_KOLAB_BACKEND (source_object);
+
+ e_kolab_backend_sync_folders_finish (backend, result, &error);
+
+ if (error != NULL) {
+ g_critical ("%s: %s", G_STRFUNC, error->message);
+ g_error_free (error);
+ }
+}
+
+static void
+kolab_backend_dispose (GObject *object)
+{
+ EKolabBackendPrivate *priv;
+
+ priv = E_KOLAB_BACKEND_GET_PRIVATE (object);
+
+ if (priv->koma != NULL) {
+ g_object_unref (priv->koma);
+ priv->koma = NULL;
+ }
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (e_kolab_backend_parent_class)->dispose (object);
+}
+
+static void
+kolab_backend_finalize (GObject *object)
+{
+ EKolabBackendPrivate *priv;
+
+ priv = E_KOLAB_BACKEND_GET_PRIVATE (object);
+
+ g_mutex_clear (&priv->koma_lock);
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (e_kolab_backend_parent_class)->finalize (object);
+}
+
static void
kolab_backend_populate (ECollectionBackend *backend)
{
- /* FIXME At this point the backend should query the Kolab server
- * for all available non-mail folders such as address books
- * and calendars, and create ESources for them.
- *
- * Upstream authors are still fleshing out the details of
- * how this should work. Query Matthew Barnes when you're
- * ready to implement this.
- */
+ e_kolab_backend_sync_folders (
+ E_KOLAB_BACKEND (backend), NULL,
+ kolab_backend_sync_folders_done_cb, NULL);
}
static gchar *
@@ -79,16 +150,24 @@ kolab_backend_child_removed (ECollectionBackend *backend,
static void
e_kolab_backend_class_init (EKolabBackendClass *class)
{
+ GObjectClass *object_class;
ECollectionBackendClass *backend_class;
g_type_class_add_private (class, sizeof (EKolabBackendPrivate));
+ object_class = G_OBJECT_CLASS (class);
+ object_class->dispose = kolab_backend_dispose;
+ object_class->finalize = kolab_backend_finalize;
+
backend_class = E_COLLECTION_BACKEND_CLASS (class);
backend_class->populate = kolab_backend_populate;
backend_class->dup_resource_id = kolab_backend_dup_resource_id;
backend_class->child_added = kolab_backend_child_added;
backend_class->child_removed = kolab_backend_child_removed;
+ /* Register relevant ESourceExtension types. */
+ REGISTER_TYPE (E_TYPE_SOURCE_KOLAB_FOLDER);
+
/* This generates an ESourceCamel subtype for CamelKolabIMAPXSettings. */
e_source_camel_generate_subtype ("kolab", CAMEL_TYPE_KOLAB_IMAPX_SETTINGS);
}
@@ -101,7 +180,18 @@ e_kolab_backend_class_finalize (EKolabBackendClass *class)
static void
e_kolab_backend_init (EKolabBackend *backend)
{
+ GError *error = NULL;
+
backend->priv = E_KOLAB_BACKEND_GET_PRIVATE (backend);
+
+ g_mutex_init (&backend->priv->koma_lock);
+
+ /* Initialize Camel and NSS. If we fail here, there's not
+ * much else to do but abort the whole service immediately. */
+ if (!kolab_util_camel_init (&error)) {
+ g_error ("%s: %s", G_STRFUNC, error->message);
+ g_assert_not_reached ();
+ }
}
void
@@ -113,3 +203,382 @@ e_kolab_backend_type_register (GTypeModule *type_module)
e_kolab_backend_register_type (type_module);
}
+ESource *
+e_kolab_backend_new_child (EKolabBackend *backend,
+ KolabFolderDescriptor *desc)
+{
+ ESource *source;
+ ESourceBackend *backend_extension;
+ ESourceResource *resource_extension;
+ const gchar *display_name;
+ const gchar *extension_name;
+
+ g_return_val_if_fail (E_IS_KOLAB_BACKEND (backend), NULL);
+ g_return_val_if_fail (desc != NULL, NULL);
+
+ /* Return NULL for folder types we don't support, and map
+ * the folder type to an equivalent ESourceExtension name
+ * while we're at it. */
+ switch (desc->type_id) {
+ case KOLAB_FOLDER_TYPE_EVENT:
+ case KOLAB_FOLDER_TYPE_EVENT_DEFAULT:
+ extension_name = E_SOURCE_EXTENSION_CALENDAR;
+ break;
+ case KOLAB_FOLDER_TYPE_TASK:
+ case KOLAB_FOLDER_TYPE_TASK_DEFAULT:
+ extension_name = E_SOURCE_EXTENSION_TASK_LIST;
+ break;
+ case KOLAB_FOLDER_TYPE_NOTE:
+ case KOLAB_FOLDER_TYPE_NOTE_DEFAULT:
+ extension_name = E_SOURCE_EXTENSION_MEMO_LIST;
+ break;
+ case KOLAB_FOLDER_TYPE_CONTACT:
+ case KOLAB_FOLDER_TYPE_CONTACT_DEFAULT:
+ extension_name = E_SOURCE_EXTENSION_ADDRESS_BOOK;
+ break;
+ default:
+ return NULL;
+ }
+
+ source = e_collection_backend_new_child (
+ E_COLLECTION_BACKEND (backend), desc->name);
+
+ /* Use the extension name set in the switch statement above. */
+ backend_extension = e_source_get_extension (source, extension_name);
+ e_source_backend_set_backend_name (backend_extension, "kolab");
+
+ /* XXX The resource identity is the folder path. This is not a
+ * stable identifier -- a folder rename will invalidate the
+ * cache on the next start -- but it's all we're given. */
+ extension_name = E_SOURCE_EXTENSION_KOLAB_FOLDER;
+ resource_extension = e_source_get_extension (source, extension_name);
+ e_source_resource_set_identity (resource_extension, desc->name);
+
+ /* The hierarchy is flattened for non-mail folders,
+ * so the last path segment is the display name. */
+ if (desc->name != NULL) {
+ gchar **segments;
+ guint n_segments;
+
+ segments = g_strsplit (desc->name, KOLAB_PATH_SEPARATOR_S, -1);
+ n_segments = g_strv_length (segments);
+
+ if (n_segments > 0) {
+ const gchar *display_name;
+ display_name = segments[n_segments - 1];
+ e_source_set_display_name (source, display_name);
+ }
+
+ g_strfreev (segments);
+ }
+
+ return source;
+}
+
+KolabMailAccess *
+e_kolab_backend_ref_mail_access_sync (EKolabBackend *backend,
+ GCancellable *cancellable,
+ GError **error)
+{
+ KolabMailAccess *koma = NULL;
+ CamelKolabIMAPXSettings *settings;
+ KolabSettingsHandler *settings_handler;
+ ESource *source;
+ gboolean success;
+
+ g_return_val_if_fail (E_IS_KOLAB_BACKEND (backend), NULL);
+
+ g_mutex_lock (&backend->priv->koma_lock);
+ if (backend->priv->koma != NULL)
+ koma = g_object_ref (backend->priv->koma);
+ g_mutex_unlock (&backend->priv->koma_lock);
+
+ /* Return a stashed KolabMailAccess if we have one. */
+ if (koma != NULL)
+ return koma;
+
+ /* FIXME Creating a KolabMailAccess instance is a somewhat
+ * involved process which is repeated in the calendar
+ * and address book backends. Should be centralized
+ * in libekolab and provided as a one-step function.
+ * The configure steps at least should be done during
+ * initialization using constructor properties. */
+
+ settings = kolab_backend_get_settings (backend);
+ source = e_backend_get_source (E_BACKEND (backend));
+
+ settings_handler = kolab_settings_handler_new (settings);
+
+ /* XXX Christian recommends KOLAB_FOLDER_CONTEXT_EMAIL here
+ * although the operations we need are supposed to work
+ * for any folder context. */
+ success = kolab_settings_handler_configure (
+ settings_handler, KOLAB_FOLDER_CONTEXT_EMAIL, error);
+ if (!success) {
+ g_object_unref (settings_handler);
+ return NULL;
+ }
+
+ if (!kolab_settings_handler_bringup (settings_handler, error)) {
+ g_object_unref (settings_handler);
+ return NULL;
+ }
+
+ /* This must be done after configure() and bringup(). */
+ success = kolab_settings_handler_set_char_field (
+ settings_handler,
+ KOLAB_SETTINGS_HANDLER_CHAR_FIELD_ESOURCE_UID,
+ e_source_dup_uid (source), error);
+ if (!success) {
+ g_object_unref (settings_handler);
+ return NULL;
+ }
+
+ koma = g_object_new (KOLAB_TYPE_MAIL_ACCESS, NULL);
+
+ if (!kolab_mail_access_configure (koma, settings_handler, error)) {
+ g_object_unref (settings_handler);
+ g_object_unref (koma);
+ return NULL;
+ }
+
+ if (!kolab_mail_access_bringup (koma, cancellable, error)) {
+ g_object_unref (settings_handler);
+ g_object_unref (koma);
+ return NULL;
+ }
+
+ success = e_backend_authenticate_sync (
+ E_BACKEND (backend),
+ E_SOURCE_AUTHENTICATOR (koma),
+ cancellable, error);
+ if (!success) {
+ g_object_unref (settings_handler);
+ g_object_unref (koma);
+ return NULL;
+ }
+
+ /* Another thread may have created a KolabMailAccess since
+ * our previous check above. If so, replace it with ours. */
+ g_mutex_lock (&backend->priv->koma_lock);
+ if (backend->priv->koma != NULL)
+ g_object_unref (backend->priv->koma);
+ backend->priv->koma = g_object_ref (koma);
+ g_mutex_unlock (&backend->priv->koma_lock);
+
+ g_object_unref (settings_handler);
+
+ return koma;
+}
+
+/* Helper for e_kolab_backend_ref_mail_access() */
+static void
+kolab_backend_ref_mail_access_thread (GSimpleAsyncResult *simple,
+ GObject *object,
+ GCancellable *cancellable)
+{
+ KolabMailAccess *koma;
+ GError *error = NULL;
+
+ koma = e_kolab_backend_ref_mail_access_sync (
+ E_KOLAB_BACKEND (object), cancellable, &error);
+
+ if (koma != NULL)
+ g_simple_async_result_set_op_res_gpointer (
+ simple, koma, (GDestroyNotify) g_object_unref);
+
+ if (error != NULL)
+ g_simple_async_result_take_error (simple, error);
+}
+
+void
+e_kolab_backend_ref_mail_access (EKolabBackend *backend,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+
+ g_return_if_fail (E_IS_KOLAB_BACKEND (backend));
+
+ simple = g_simple_async_result_new (
+ G_OBJECT (backend), callback, user_data,
+ e_kolab_backend_ref_mail_access);
+
+ g_simple_async_result_set_check_cancellable (simple, cancellable);
+
+ g_simple_async_result_run_in_thread (
+ simple, kolab_backend_ref_mail_access_thread,
+ G_PRIORITY_DEFAULT, cancellable);
+
+ g_object_unref (simple);
+}
+
+KolabMailAccess *
+e_kolab_backend_ref_mail_access_finish (EKolabBackend *backend,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+ KolabMailAccess *koma;
+
+ g_return_val_if_fail (
+ g_simple_async_result_is_valid (
+ result, G_OBJECT (backend),
+ e_kolab_backend_ref_mail_access), NULL);
+
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+
+ if (g_simple_async_result_propagate_error (simple, error))
+ return NULL;
+
+ koma = g_simple_async_result_get_op_res_gpointer (simple);
+ g_return_val_if_fail (KOLAB_IS_MAIL_ACCESS (koma), NULL);
+
+ return g_object_ref (koma);
+}
+
+gboolean
+e_kolab_backend_sync_folders_sync (EKolabBackend *backend,
+ GCancellable *cancellable,
+ GError **error)
+{
+ ECollectionBackend *col_backend;
+ ESourceRegistryServer *server;
+ KolabMailAccess *koma;
+ GList *list, *link;
+ GQueue trash = G_QUEUE_INIT;
+ GError *local_error = NULL;
+
+ g_return_val_if_fail (E_IS_KOLAB_BACKEND (backend), FALSE);
+
+ col_backend = E_COLLECTION_BACKEND (backend);
+
+ /* If we're offline, all we can do is add the last known set
+ * of folders from our cache of previously used ESources. */
+ if (!e_backend_get_online (E_BACKEND (backend))) {
+ server = e_collection_backend_ref_server (col_backend);
+ list = e_collection_backend_claim_all_resources (col_backend);
+
+ for (link = list; link != NULL; link = g_list_next (link))
+ e_source_registry_server_add_source (
+ server, E_SOURCE (link->data));
+
+ g_list_free_full (list, (GDestroyNotify) g_object_unref);
+ g_object_unref (server);
+
+ return TRUE;
+ }
+
+ koma = e_kolab_backend_ref_mail_access_sync (
+ backend, cancellable, error);
+
+ if (koma == NULL)
+ return FALSE;
+
+ list = kolab_mail_access_query_folder_info_online (
+ koma, cancellable, &local_error);
+
+ g_object_unref (koma);
+
+ if (local_error != NULL) {
+ g_propagate_error (error, local_error);
+ return FALSE;
+ }
+
+ /* Create or reuse ESources to represent Kolab folders. */
+
+ server = e_collection_backend_ref_server (col_backend);
+
+ for (link = list; link != NULL; link = g_list_next (link)) {
+ KolabFolderDescriptor *desc = link->data;
+ ESource *source;
+
+ source = e_kolab_backend_new_child (backend, desc);
+
+ if (source != NULL) {
+ e_source_registry_server_add_source (server, source);
+ g_object_unref (source);
+ }
+ }
+
+ g_object_unref (server);
+
+ g_list_free_full (
+ list, (GDestroyNotify) kolab_util_folder_descriptor_free);
+
+ /* Discard any previously cached folders no longer
+ * present in the folder list from the Kolab server.
+ *
+ * Note: This is just an attempt to cleanup cached
+ * data, so we don't really care about errors.
+ */
+
+ list = e_collection_backend_claim_all_resources (col_backend);
+
+ for (link = list; link != NULL; link = g_list_next (link)) {
+ ESource *source = E_SOURCE (link->data);
+ e_source_remove_sync (source, NULL, NULL);
+ }
+
+ g_list_free_full (list, (GDestroyNotify) g_object_unref);
+
+ return TRUE;
+}
+
+/* Helper for e_kolab_backend_sync_folders() */
+static void
+kolab_backend_sync_folders_thread (GSimpleAsyncResult *simple,
+ GObject *object,
+ GCancellable *cancellable)
+{
+ GError *error = NULL;
+
+ e_kolab_backend_sync_folders_sync (
+ E_KOLAB_BACKEND (object), cancellable, &error);
+
+ if (error != NULL)
+ g_simple_async_result_take_error (simple, error);
+}
+
+void
+e_kolab_backend_sync_folders (EKolabBackend *backend,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+
+ g_return_if_fail (E_IS_KOLAB_BACKEND (backend));
+
+ simple = g_simple_async_result_new (
+ G_OBJECT (backend), callback, user_data,
+ e_kolab_backend_sync_folders);
+
+ g_simple_async_result_set_check_cancellable (simple, cancellable);
+
+ g_simple_async_result_run_in_thread (
+ simple, kolab_backend_sync_folders_thread,
+ G_PRIORITY_DEFAULT, cancellable);
+
+ g_object_unref (simple);
+}
+
+gboolean
+e_kolab_backend_sync_folders_finish (EKolabBackend *backend,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+
+ g_return_val_if_fail (
+ g_simple_async_result_is_valid (
+ result, G_OBJECT (backend),
+ e_kolab_backend_sync_folders), FALSE);
+
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+
+ /* Assume success unless a GError was set. */
+ return !g_simple_async_result_propagate_error (simple, error);
+}
+
diff --git a/src/collection/e-kolab-backend.h b/src/collection/e-kolab-backend.h
index ab581e2..72b6e98 100644
--- a/src/collection/e-kolab-backend.h
+++ b/src/collection/e-kolab-backend.h
@@ -19,6 +19,8 @@
#include <libebackend/libebackend.h>
+#include "libekolab/kolab-mail-access.h"
+
/* Standard GObject macros */
#define E_TYPE_KOLAB_BACKEND \
(e_kolab_backend_get_type ())
@@ -55,6 +57,34 @@ struct _EKolabBackendClass {
GType e_kolab_backend_get_type (void) G_GNUC_CONST;
void e_kolab_backend_type_register (GTypeModule *type_module);
+ESource * e_kolab_backend_new_child (EKolabBackend *backend,
+ KolabFolderDescriptor *desc);
+KolabMailAccess *
+ e_kolab_backend_ref_mail_access_sync
+ (EKolabBackend *backend,
+ GCancellable *cancellable,
+ GError **error);
+void e_kolab_backend_ref_mail_access (EKolabBackend *backend,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+KolabMailAccess *
+ e_kolab_backend_ref_mail_access_finish
+ (EKolabBackend *backend,
+ GAsyncResult *result,
+ GError **error);
+gboolean e_kolab_backend_sync_folders_sync
+ (EKolabBackend *backend,
+ GCancellable *cancellable,
+ GError **error);
+void e_kolab_backend_sync_folders (EKolabBackend *backend,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean e_kolab_backend_sync_folders_finish
+ (EKolabBackend *backend,
+ GAsyncResult *result,
+ GError **error);
G_END_DECLS
diff --git a/src/libekolab/kolab-mail-access.c b/src/libekolab/kolab-mail-access.c
index 3b3d1ca..8e886b7 100644
--- a/src/libekolab/kolab-mail-access.c
+++ b/src/libekolab/kolab-mail-access.c
@@ -111,6 +111,10 @@ mail_access_try_password_sync (ESourceAuthenticator *authenticator,
GCancellable *cancellable,
GError **error)
{
+ KolabMailAccessPrivate *priv;
+ ESourceAuthenticationResult result;
+ gboolean success;
+
/* FIXME This needs to test the provided password and return:
*
* E_SOURCE_AUTHENTICATION_ACCEPTED if the password is valid.
@@ -136,7 +140,25 @@ mail_access_try_password_sync (ESourceAuthenticator *authenticator,
* This is broken behavior.
*/
- return E_SOURCE_AUTHENTICATION_ACCEPTED;
+ priv = KOLAB_MAIL_ACCESS_PRIVATE (authenticator);
+
+ if (priv->ksettings != NULL)
+ kolab_settings_handler_set_char_field (
+ priv->ksettings,
+ KOLAB_SETTINGS_HANDLER_CHAR_FIELD_KOLAB_USER_PASSWORD,
+ g_strdup (password->str), NULL);
+
+ success = kolab_mail_access_set_opmode (
+ KOLAB_MAIL_ACCESS (authenticator),
+ KOLAB_MAIL_ACCESS_OPMODE_ONLINE,
+ cancellable, error);
+
+ if (success)
+ result = E_SOURCE_AUTHENTICATION_ACCEPTED;
+ else
+ result = E_SOURCE_AUTHENTICATION_ERROR;
+
+ return result;
}
static void
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]