aboutsummaryrefslogtreecommitdiffstats
path: root/calendar/common/authentication.c
diff options
context:
space:
mode:
Diffstat (limited to 'calendar/common/authentication.c')
-rw-r--r--calendar/common/authentication.c273
1 files changed, 271 insertions, 2 deletions
diff --git a/calendar/common/authentication.c b/calendar/common/authentication.c
index 675754c5f0..b36e10a244 100644
--- a/calendar/common/authentication.c
+++ b/calendar/common/authentication.c
@@ -32,8 +32,6 @@
#include "authentication.h"
#include <libedataserver/e-url.h>
-static GHashTable *source_lists_hash = NULL;
-
static gchar *
auth_func_cb (ECal *ecal,
const gchar *prompt,
@@ -124,3 +122,274 @@ e_auth_new_cal_from_source (ESource *source, ECalSourceType type)
return cal;
}
+
+typedef struct {
+ ECal *cal;
+ GtkWindow *parent;
+ GCancellable *cancellable;
+ ECalSourceType source_type;
+ icaltimezone *default_zone;
+
+ /* Authentication Details */
+ gchar *auth_uri;
+ gchar *auth_component;
+} LoadContext;
+
+static void
+load_cal_source_context_free (LoadContext *context)
+{
+ if (context->cal != NULL)
+ g_object_unref (context->cal);
+
+ if (context->parent != NULL)
+ g_object_unref (context->parent);
+
+ if (context->cancellable != NULL)
+ g_object_unref (context->cancellable);
+
+ g_free (context->auth_uri);
+ g_free (context->auth_component);
+
+ g_slice_free (LoadContext, context);
+}
+
+static void
+load_cal_source_get_auth_details (ESource *source,
+ LoadContext *context)
+{
+ const gchar *property;
+
+ /* ECal figures out most of the details before invoking the
+ * authentication callback, but we still need a component name
+ * for e_passwords_ask_password(). */
+
+ /* auth_component */
+
+ property = e_source_get_property (source, "auth-domain");
+
+ if (property == NULL)
+ property = "Calendar";
+
+ context->auth_component = g_strdup (property);
+}
+
+static gchar *
+load_cal_source_authenticate (ECal *cal,
+ const gchar *prompt,
+ const gchar *uri,
+ LoadContext *context)
+{
+ const gchar *title;
+ gboolean remember; /* not used */
+ gchar *password;
+
+ /* Remember the URI so we don't have to reconstruct it if
+ * authentication fails and we have to forget the password. */
+ g_free (context->auth_uri);
+ context->auth_uri = g_strdup (uri);
+
+ /* XXX Dialog windows should not have titles. */
+ title = "";
+
+ password = e_passwords_get_password (context->auth_component, uri);
+
+ if (password == NULL)
+ password = e_passwords_ask_password (
+ title, context->auth_component, uri,
+ prompt, E_PASSWORDS_REMEMBER_FOREVER |
+ E_PASSWORDS_SECRET | E_PASSWORDS_ONLINE,
+ &remember, context->parent);
+
+ return password;
+}
+
+static void
+load_cal_source_thread (GSimpleAsyncResult *simple,
+ ESource *source,
+ GCancellable *cancellable)
+{
+ ECal *cal;
+ LoadContext *context;
+ GError *error = NULL;
+
+ context = g_simple_async_result_get_op_res_gpointer (simple);
+
+ /* XXX This doesn't take a GError, it just dumps
+ * error messages to the terminal... so broken. */
+ cal = e_cal_new (source, context->source_type);
+ g_return_if_fail (cal != NULL);
+
+ if (g_cancellable_set_error_if_cancelled (cancellable, &error)) {
+ g_simple_async_result_set_from_error (simple, error);
+ g_object_unref (cal);
+ g_error_free (error);
+ return;
+ }
+
+ if (!e_cal_set_default_timezone (cal, context->default_zone, &error)) {
+ g_simple_async_result_set_from_error (simple, error);
+ g_object_unref (cal);
+ g_error_free (error);
+ return;
+ }
+
+ /* XXX Why doesn't EBook have something like this? */
+ e_cal_set_auth_func (
+ cal, (ECalAuthFunc)
+ load_cal_source_authenticate, context);
+
+try_again:
+ if (!e_cal_open (cal, FALSE, &error))
+ goto fail;
+
+ /* We should be authenticated now if we're going to be,
+ * so remove the authentication callback so it doesn't
+ * get triggered after we free the load context. */
+ e_cal_set_auth_func (cal, NULL, NULL);
+
+ if (g_cancellable_set_error_if_cancelled (cancellable, &error)) {
+ g_simple_async_result_set_from_error (simple, error);
+ g_object_unref (cal);
+ g_error_free (error);
+ return;
+ }
+
+ context->cal = cal;
+
+ return;
+
+fail:
+ g_return_if_fail (error != NULL);
+
+ /* If authentication failed, forget the password and reprompt. */
+ if (g_error_matches (
+ error, E_CALENDAR_ERROR,
+ E_CALENDAR_STATUS_AUTHENTICATION_FAILED)) {
+ e_passwords_forget_password (
+ context->auth_component, context->auth_uri);
+ g_clear_error (&error);
+ goto try_again;
+
+ /* XXX Might this cause a busy loop? */
+ } else if (g_error_matches (
+ error, E_CALENDAR_ERROR, E_CALENDAR_STATUS_BUSY)) {
+ g_clear_error (&error);
+ goto try_again;
+
+ } else {
+ g_simple_async_result_set_from_error (simple, error);
+ g_object_unref (cal);
+ g_error_free (error);
+ }
+}
+
+/**
+ * e_load_cal_source_async:
+ * @source: an #ESource
+ * @source_type: the type of #ECal to load
+ * @default_zone: default time zone, or %NULL to use UTC
+ * @parent: parent window for password dialogs, or %NULL
+ * @cancellable: optional #GCancellable object, %NULL to ignore
+ * @callback: a #GAsyncReadyCallback to call when the request is satisfied
+ * @user_data: the data to pass to @callback
+ *
+ * Creates a new #ECal specified by @source and opens it, prompting the
+ * user for authentication if necessary.
+ *
+ * When the operation is finished, @callback will be called. You can
+ * then call e_load_cal_source_finish() to obtain the resulting #ECal.
+ **/
+void
+e_load_cal_source_async (ESource *source,
+ ECalSourceType source_type,
+ icaltimezone *default_zone,
+ GtkWindow *parent,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+ LoadContext *context;
+
+ g_return_if_fail (E_IS_SOURCE (source));
+
+ /* Source must have a group so we can obtain its URI. */
+ g_return_if_fail (e_source_peek_group (source) != NULL);
+
+ if (parent != NULL) {
+ g_return_if_fail (GTK_IS_WINDOW (parent));
+ g_object_ref (parent);
+ }
+
+ if (cancellable != NULL) {
+ g_return_if_fail (G_IS_CANCELLABLE (cancellable));
+ g_object_ref (cancellable);
+ }
+
+ if (default_zone == NULL)
+ default_zone = icaltimezone_get_utc_timezone ();
+
+ context = g_slice_new0 (LoadContext);
+ context->parent = parent;
+ context->cancellable = cancellable;
+ context->source_type = source_type;
+ context->default_zone = default_zone;
+
+ /* Extract authentication details from the ESource before
+ * spawning the thread, since ESource is not thread-safe. */
+ load_cal_source_get_auth_details (source, context);
+
+ simple = g_simple_async_result_new (
+ G_OBJECT (source), callback,
+ user_data, e_load_cal_source_async);
+
+ g_simple_async_result_set_op_res_gpointer (
+ simple, context, (GDestroyNotify)
+ load_cal_source_context_free);
+
+ g_simple_async_result_run_in_thread (
+ simple, (GSimpleAsyncThreadFunc) load_cal_source_thread,
+ G_PRIORITY_DEFAULT, context->cancellable);
+
+ g_object_unref (simple);
+}
+
+/**
+ * e_load_cal_source_finish:
+ * @source: an #ESource
+ * @result: a #GAsyncResult
+ * @error: return location for a #GError, or %NULL
+ *
+ * Finishes an asynchronous #ECal open operation started with
+ * e_load_cal_source_async(). If an error occurred, or the user
+ * declined to authenticate, the function will return %NULL and
+ * set @error.
+ *
+ * Returns: a ready-to-use #ECal, or %NULL on error
+ **/
+ECal *
+e_load_cal_source_finish (ESource *source,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+ LoadContext *context;
+
+ g_return_val_if_fail (E_IS_SOURCE (source), NULL);
+ g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
+
+ g_return_val_if_fail (
+ g_simple_async_result_is_valid (
+ result, G_OBJECT (source),
+ e_load_cal_source_async), NULL);
+
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+
+ if (g_simple_async_result_propagate_error (simple, error))
+ return NULL;
+
+ context = g_simple_async_result_get_op_res_gpointer (simple);
+ g_return_val_if_fail (context != NULL, NULL);
+
+ return g_object_ref (context->cal);
+}