diff options
Diffstat (limited to 'calendar/common/authentication.c')
-rw-r--r-- | calendar/common/authentication.c | 273 |
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); +} |