aboutsummaryrefslogtreecommitdiffstats
path: root/e-util/e-client-utils.c
diff options
context:
space:
mode:
Diffstat (limited to 'e-util/e-client-utils.c')
-rw-r--r--e-util/e-client-utils.c445
1 files changed, 445 insertions, 0 deletions
diff --git a/e-util/e-client-utils.c b/e-util/e-client-utils.c
new file mode 100644
index 0000000000..ed0688b637
--- /dev/null
+++ b/e-util/e-client-utils.c
@@ -0,0 +1,445 @@
+/*
+ * e-client-utils.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Copyright (C) 2011 Red Hat, Inc. (www.redhat.com)
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib/gi18n-lib.h>
+#include <gtk/gtk.h>
+#include <libsoup/soup.h>
+
+#include <libebook/libebook.h>
+#include <libecal/libecal.h>
+
+#include "e-client-utils.h"
+
+/**
+ * e_client_utils_new:
+ *
+ * Proxy function for e_book_client_utils_new() and e_cal_client_utils_new().
+ *
+ * Since: 3.2
+ **/
+EClient *
+e_client_utils_new (ESource *source,
+ EClientSourceType source_type,
+ GError **error)
+{
+ EClient *res = NULL;
+
+ g_return_val_if_fail (source != NULL, NULL);
+ g_return_val_if_fail (E_IS_SOURCE (source), NULL);
+
+ switch (source_type) {
+ case E_CLIENT_SOURCE_TYPE_CONTACTS:
+ res = E_CLIENT (e_book_client_new (source, error));
+ break;
+ case E_CLIENT_SOURCE_TYPE_EVENTS:
+ res = E_CLIENT (e_cal_client_new (source, E_CAL_CLIENT_SOURCE_TYPE_EVENTS, error));
+ break;
+ case E_CLIENT_SOURCE_TYPE_MEMOS:
+ res = E_CLIENT (e_cal_client_new (source, E_CAL_CLIENT_SOURCE_TYPE_MEMOS, error));
+ break;
+ case E_CLIENT_SOURCE_TYPE_TASKS:
+ res = E_CLIENT (e_cal_client_new (source, E_CAL_CLIENT_SOURCE_TYPE_TASKS, error));
+ break;
+ default:
+ g_return_val_if_reached (NULL);
+ break;
+ }
+
+ return res;
+}
+
+typedef struct _EClientUtilsAsyncOpData {
+ GAsyncReadyCallback callback;
+ gpointer user_data;
+ GCancellable *cancellable;
+ ESource *source;
+ EClient *client;
+ gboolean open_finished;
+ GError *opened_cb_error;
+ guint retry_open_id;
+ gboolean only_if_exists;
+ guint pending_properties_count;
+} EClientUtilsAsyncOpData;
+
+static void
+free_client_utils_async_op_data (EClientUtilsAsyncOpData *async_data)
+{
+ g_return_if_fail (async_data != NULL);
+ g_return_if_fail (async_data->cancellable != NULL);
+ g_return_if_fail (async_data->client != NULL);
+
+ if (async_data->retry_open_id)
+ g_source_remove (async_data->retry_open_id);
+
+ g_signal_handlers_disconnect_matched (async_data->cancellable, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, async_data);
+ g_signal_handlers_disconnect_matched (async_data->client, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, async_data);
+
+ if (async_data->opened_cb_error)
+ g_error_free (async_data->opened_cb_error);
+ g_object_unref (async_data->cancellable);
+ g_object_unref (async_data->client);
+ g_object_unref (async_data->source);
+ g_free (async_data);
+}
+
+static gboolean
+complete_async_op_in_idle_cb (gpointer user_data)
+{
+ GSimpleAsyncResult *simple = user_data;
+ gint run_main_depth;
+
+ g_return_val_if_fail (simple != NULL, FALSE);
+
+ run_main_depth = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (simple), "run-main-depth"));
+ if (run_main_depth < 1)
+ run_main_depth = 1;
+
+ /* do not receive in higher level than was initially run */
+ if (g_main_depth () > run_main_depth) {
+ return TRUE;
+ }
+
+ g_simple_async_result_complete (simple);
+ g_object_unref (simple);
+
+ return FALSE;
+}
+
+#define return_async_error_if_fail(expr, callback, user_data, src, source_tag) G_STMT_START { \
+ if (G_LIKELY ((expr))) { } else { \
+ GError *error; \
+ \
+ error = g_error_new (E_CLIENT_ERROR, E_CLIENT_ERROR_INVALID_ARG, \
+ "%s: assertion '%s' failed", G_STRFUNC, #expr); \
+ \
+ return_async_error (error, callback, user_data, src, source_tag); \
+ g_error_free (error); \
+ return; \
+ } \
+ } G_STMT_END
+
+static void
+return_async_error (const GError *error,
+ GAsyncReadyCallback callback,
+ gpointer user_data,
+ ESource *source,
+ gpointer source_tag)
+{
+ GSimpleAsyncResult *simple;
+
+ g_return_if_fail (error != NULL);
+ g_return_if_fail (source_tag != NULL);
+
+ simple = g_simple_async_result_new (G_OBJECT (source), callback, user_data, source_tag);
+ g_simple_async_result_set_from_error (simple, error);
+
+ g_object_set_data (G_OBJECT (simple), "run-main-depth", GINT_TO_POINTER (g_main_depth ()));
+ g_idle_add (complete_async_op_in_idle_cb, simple);
+}
+
+static void
+client_utils_get_backend_property_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ EClient *client = E_CLIENT (source_object);
+ EClientUtilsAsyncOpData *async_data = user_data;
+ GSimpleAsyncResult *simple;
+
+ g_return_if_fail (async_data != NULL);
+ g_return_if_fail (async_data->client != NULL);
+ g_return_if_fail (async_data->client == client);
+
+ if (result) {
+ gchar *prop_value = NULL;
+
+ if (e_client_get_backend_property_finish (client, result, &prop_value, NULL))
+ g_free (prop_value);
+
+ async_data->pending_properties_count--;
+ if (async_data->pending_properties_count)
+ return;
+ }
+
+ simple = g_simple_async_result_new (G_OBJECT (async_data->source), async_data->callback, async_data->user_data, e_client_utils_open_new);
+ g_simple_async_result_set_op_res_gpointer (simple, g_object_ref (async_data->client), g_object_unref);
+
+ g_object_set_data (G_OBJECT (simple), "run-main-depth", GINT_TO_POINTER (g_main_depth ()));
+ g_idle_add (complete_async_op_in_idle_cb, simple);
+
+ free_client_utils_async_op_data (async_data);
+}
+
+static void
+client_utils_capabilities_retrieved_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ EClient *client = E_CLIENT (source_object);
+ EClientUtilsAsyncOpData *async_data = user_data;
+ gchar *capabilities = NULL;
+ gboolean caps_res;
+
+ g_return_if_fail (async_data != NULL);
+ g_return_if_fail (async_data->client != NULL);
+ g_return_if_fail (async_data->client == client);
+
+ caps_res = e_client_retrieve_capabilities_finish (client, result, &capabilities, NULL);
+ g_free (capabilities);
+
+ if (caps_res) {
+ async_data->pending_properties_count = 1;
+
+ /* precache backend properties */
+ if (E_IS_CAL_CLIENT (client)) {
+ async_data->pending_properties_count += 3;
+
+ e_client_get_backend_property (async_data->client, CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS, async_data->cancellable, client_utils_get_backend_property_cb, async_data);
+ e_client_get_backend_property (async_data->client, CAL_BACKEND_PROPERTY_ALARM_EMAIL_ADDRESS, async_data->cancellable, client_utils_get_backend_property_cb, async_data);
+ e_client_get_backend_property (async_data->client, CAL_BACKEND_PROPERTY_DEFAULT_OBJECT, async_data->cancellable, client_utils_get_backend_property_cb, async_data);
+ } else if (E_IS_BOOK_CLIENT (client)) {
+ async_data->pending_properties_count += 2;
+
+ e_client_get_backend_property (async_data->client, BOOK_BACKEND_PROPERTY_REQUIRED_FIELDS, async_data->cancellable, client_utils_get_backend_property_cb, async_data);
+ e_client_get_backend_property (async_data->client, BOOK_BACKEND_PROPERTY_SUPPORTED_FIELDS, async_data->cancellable, client_utils_get_backend_property_cb, async_data);
+ } else {
+ g_warn_if_reached ();
+ client_utils_get_backend_property_cb (source_object, NULL, async_data);
+ return;
+ }
+
+ e_client_get_backend_property (async_data->client, CLIENT_BACKEND_PROPERTY_CACHE_DIR, async_data->cancellable, client_utils_get_backend_property_cb, async_data);
+ } else {
+ client_utils_get_backend_property_cb (source_object, NULL, async_data);
+ }
+}
+
+static void
+client_utils_open_new_done (EClientUtilsAsyncOpData *async_data)
+{
+ g_return_if_fail (async_data != NULL);
+ g_return_if_fail (async_data->client != NULL);
+
+ /* retrieve capabilities just to have them cached on #EClient for later use */
+ e_client_retrieve_capabilities (async_data->client, async_data->cancellable, client_utils_capabilities_retrieved_cb, async_data);
+}
+
+static gboolean client_utils_retry_open_timeout_cb (gpointer user_data);
+static void client_utils_opened_cb (EClient *client, const GError *error, EClientUtilsAsyncOpData *async_data);
+
+static void
+finish_or_retry_open (EClientUtilsAsyncOpData *async_data,
+ const GError *error)
+{
+ g_return_if_fail (async_data != NULL);
+
+ if (error && g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_BUSY)) {
+ /* postpone for 1/2 of a second, backend is busy now */
+ async_data->open_finished = FALSE;
+ async_data->retry_open_id = g_timeout_add (500, client_utils_retry_open_timeout_cb, async_data);
+ } else if (error) {
+ return_async_error (error, async_data->callback, async_data->user_data, async_data->source, e_client_utils_open_new);
+ free_client_utils_async_op_data (async_data);
+ } else {
+ client_utils_open_new_done (async_data);
+ }
+}
+
+static void
+client_utils_opened_cb (EClient *client,
+ const GError *error,
+ EClientUtilsAsyncOpData *async_data)
+{
+ g_return_if_fail (client != NULL);
+ g_return_if_fail (async_data != NULL);
+ g_return_if_fail (client == async_data->client);
+
+ g_signal_handlers_disconnect_by_func (client, G_CALLBACK (client_utils_opened_cb), async_data);
+
+ if (!async_data->open_finished) {
+ /* there can happen that the "opened" signal is received
+ * before the e_client_open () is finished, thus keep detailed
+ * error for later use, if any */
+ if (error)
+ async_data->opened_cb_error = g_error_copy (error);
+ } else {
+ finish_or_retry_open (async_data, error);
+ }
+}
+
+static void
+client_utils_open_new_async_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ EClientUtilsAsyncOpData *async_data = user_data;
+ GError *error = NULL;
+
+ g_return_if_fail (source_object != NULL);
+ g_return_if_fail (result != NULL);
+ g_return_if_fail (async_data != NULL);
+ g_return_if_fail (async_data->callback != NULL);
+ g_return_if_fail (async_data->client == E_CLIENT (source_object));
+
+ async_data->open_finished = TRUE;
+
+ if (!e_client_open_finish (E_CLIENT (source_object), result, &error)
+ || g_cancellable_set_error_if_cancelled (async_data->cancellable, &error)) {
+ finish_or_retry_open (async_data, error);
+ g_error_free (error);
+ return;
+ }
+
+ if (async_data->opened_cb_error) {
+ finish_or_retry_open (async_data, async_data->opened_cb_error);
+ return;
+ }
+
+ if (e_client_is_opened (async_data->client)) {
+ client_utils_open_new_done (async_data);
+ return;
+ }
+
+ /* wait for 'opened' signal, which is received in client_utils_opened_cb */
+}
+
+static gboolean
+client_utils_retry_open_timeout_cb (gpointer user_data)
+{
+ EClientUtilsAsyncOpData *async_data = user_data;
+
+ g_return_val_if_fail (async_data != NULL, FALSE);
+
+ g_signal_handlers_disconnect_matched (async_data->cancellable, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, async_data);
+
+ /* reconnect to the signal */
+ g_signal_connect (async_data->client, "opened", G_CALLBACK (client_utils_opened_cb), async_data);
+
+ e_client_open (async_data->client, async_data->only_if_exists, async_data->cancellable, client_utils_open_new_async_cb, async_data);
+
+ async_data->retry_open_id = 0;
+
+ return FALSE;
+}
+
+/**
+ * e_client_utils_open_new:
+ * @source: an #ESource to be opened
+ * @source_type: an #EClientSourceType of the @source
+ * @only_if_exists: if %TRUE, fail if this client doesn't already exist, otherwise create it first
+ * @cancellable: a #GCancellable; can be %NULL
+ * @callback: callback to call when a result is ready
+ * @user_data: user data for the @callback
+ *
+ * Begins asynchronous opening of a new #EClient corresponding
+ * to the @source of type @source_type. The resulting #EClient
+ * is fully opened and authenticated client, ready to be used.
+ * The opened client has also fetched capabilities.
+ * This call is finished by e_client_utils_open_new_finish()
+ * from the @callback.
+ *
+ * Since: 3.2
+ **/
+void
+e_client_utils_open_new (ESource *source,
+ EClientSourceType source_type,
+ gboolean only_if_exists,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ EClient *client;
+ GError *error = NULL;
+ EClientUtilsAsyncOpData *async_data;
+
+ g_return_if_fail (callback != NULL);
+ return_async_error_if_fail (source != NULL, callback, user_data, source, e_client_utils_open_new);
+ return_async_error_if_fail (E_IS_SOURCE (source), callback, user_data, source, e_client_utils_open_new);
+
+ client = e_client_utils_new (source, source_type, &error);
+ if (!client) {
+ return_async_error (error, callback, user_data, source, e_client_utils_open_new);
+ g_error_free (error);
+ return;
+ }
+
+ async_data = g_new0 (EClientUtilsAsyncOpData, 1);
+ async_data->callback = callback;
+ async_data->user_data = user_data;
+ async_data->source = g_object_ref (source);
+ async_data->client = client;
+ async_data->open_finished = FALSE;
+ async_data->only_if_exists = only_if_exists;
+ async_data->retry_open_id = 0;
+
+ if (cancellable)
+ async_data->cancellable = g_object_ref (cancellable);
+ else
+ async_data->cancellable = g_cancellable_new ();
+
+ /* wait till backend notifies about its opened state */
+ g_signal_connect (client, "opened", G_CALLBACK (client_utils_opened_cb), async_data);
+
+ e_client_open (async_data->client, async_data->only_if_exists, async_data->cancellable, client_utils_open_new_async_cb, async_data);
+}
+
+/**
+ * e_client_utils_open_new_finish:
+ * @source: an #ESource on which the e_client_utils_open_new() was invoked
+ * @result: a #GAsyncResult
+ * @client: (out): Return value for an #EClient
+ * @error: (out): a #GError to set an error, if any
+ *
+ * Finishes previous call of e_client_utils_open_new() and
+ * sets @client to a fully opened and authenticated #EClient.
+ * This @client, if not NULL, should be freed with g_object_unref().
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ *
+ * Since: 3.2
+ **/
+gboolean
+e_client_utils_open_new_finish (ESource *source,
+ GAsyncResult *result,
+ EClient **client,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+
+ g_return_val_if_fail (source != NULL, FALSE);
+ g_return_val_if_fail (result != NULL, FALSE);
+ g_return_val_if_fail (client != NULL, FALSE);
+ g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (source), e_client_utils_open_new), FALSE);
+
+ *client = NULL;
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+
+ if (g_simple_async_result_propagate_error (simple, error))
+ return FALSE;
+
+ *client = g_object_ref (g_simple_async_result_get_op_res_gpointer (simple));
+
+ return *client != NULL;
+}