2 * e-source-authenticator.c
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) version 3.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with the program; if not, see <http://www.gnu.org/licenses/>
20 * SECTION: e-source-authenticator
21 * @include: libedataserver/e-source-authenticator.h
22 * @short_description: Interface for authentication attempts
24 * An object implementing the #ESourceAuthenticator interface gets passed
25 * to e_source_registry_authenticate(). The job of an #ESourceAuthenticator
26 * is to test whether a remote server will accept a given password, and then
27 * indicate the result by returning an #ESourceAuthenticationResult value.
29 * Typically only #EBackend subclasses need to implement this interface,
30 * as client applications are not involved in authentication.
32 * Note this interface is designed around "stateful authentication", where
33 * one connects to a server, provides credentials for authentication once,
34 * and then issues commands in an authenticated state for the remainder of
37 * Backends requiring "stateless authentication" -- where credentials are
38 * included with each command -- will typically want to cache the password
39 * internally once it's verified as part of implementing this interface.
42 #include "e-source-authenticator.h"
45 #include <glib/gi18n-lib.h>
47 /* These are for building an authentication prompt. */
48 #include <libedataserver/e-source-address-book.h>
49 #include <libedataserver/e-source-authentication.h>
50 #include <libedataserver/e-source-calendar.h>
51 #include <libedataserver/e-source-collection.h>
52 #include <libedataserver/e-source-mail-account.h>
53 #include <libedataserver/e-source-mail-identity.h>
54 #include <libedataserver/e-source-mail-transport.h>
56 typedef struct _AsyncContext AsyncContext;
58 struct _AsyncContext {
60 ESourceAuthenticationResult result;
65 e_source_authenticator,
69 async_context_free (AsyncContext *async_context)
71 g_string_free (async_context->password, TRUE);
73 g_slice_free (AsyncContext, async_context);
77 source_authenticator_get_prompt_strings (ESourceAuthenticator *auth,
80 gchar **prompt_message,
81 gchar **prompt_description)
83 ESourceAuthentication *extension;
86 const gchar *extension_name;
101 } type = TYPE_UNKNOWN;
103 /* XXX This is kind of a hack but it should work for now. Build a
104 * suitable password prompt by checking for various extensions
105 * in the ESource. If no recognizable extensions are found, or
106 * if the result is ambiguous, just refer to the data source as
109 display_name = e_source_dup_display_name (source);
111 extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
112 extension = e_source_get_extension (source, extension_name);
113 host_name = e_source_authentication_dup_host (extension);
114 user_name = e_source_authentication_dup_user (extension);
116 extension_name = E_SOURCE_EXTENSION_ADDRESS_BOOK;
117 if (e_source_has_extension (source, extension_name)) {
118 if (type == TYPE_UNKNOWN)
119 type = TYPE_ADDRESS_BOOK;
121 type = TYPE_AMBIGUOUS;
124 extension_name = E_SOURCE_EXTENSION_CALENDAR;
125 if (e_source_has_extension (source, extension_name)) {
126 if (type == TYPE_UNKNOWN)
127 type = TYPE_CALENDAR;
129 type = TYPE_AMBIGUOUS;
132 extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
133 if (e_source_has_extension (source, extension_name)) {
134 if (type == TYPE_UNKNOWN)
135 type = TYPE_MAIL_ACCOUNT;
137 type = TYPE_AMBIGUOUS;
140 extension_name = E_SOURCE_EXTENSION_MAIL_TRANSPORT;
141 if (e_source_has_extension (source, extension_name)) {
142 if (type == TYPE_UNKNOWN)
143 type = TYPE_MAIL_TRANSPORT;
145 type = TYPE_AMBIGUOUS;
148 extension_name = E_SOURCE_EXTENSION_MEMO_LIST;
149 if (e_source_has_extension (source, extension_name)) {
150 if (type == TYPE_UNKNOWN)
151 type = TYPE_MEMO_LIST;
153 type = TYPE_AMBIGUOUS;
156 extension_name = E_SOURCE_EXTENSION_TASK_LIST;
157 if (e_source_has_extension (source, extension_name)) {
158 if (type == TYPE_UNKNOWN)
159 type = TYPE_TASK_LIST;
161 type = TYPE_AMBIGUOUS;
165 case TYPE_ADDRESS_BOOK:
166 message = _("Address book authentication request");
171 message = _("Calendar authentication request");
173 case TYPE_MAIL_ACCOUNT:
174 case TYPE_MAIL_TRANSPORT:
175 message = _("Mail authentication request");
177 default: /* generic account prompt */
178 message = _("Authentication request");
182 description = g_string_sized_new (256);
185 case TYPE_ADDRESS_BOOK:
186 g_string_append_printf (
188 _("Please enter the password for "
189 "address book \"%s\"."), display_name);
192 g_string_append_printf (
194 _("Please enter the password for "
195 "calendar \"%s\"."), display_name);
197 case TYPE_MAIL_ACCOUNT:
198 g_string_append_printf (
200 _("Please enter the password for "
201 "mail account \"%s\"."), display_name);
203 case TYPE_MAIL_TRANSPORT:
204 g_string_append_printf (
206 _("Please enter the password for "
207 "mail transport \"%s\"."), display_name);
210 g_string_append_printf (
212 _("Please enter the password for "
213 "memo list \"%s\"."), display_name);
216 g_string_append_printf (
218 _("Please enter the password for "
219 "task list \"%s\"."), display_name);
221 default: /* generic account prompt */
222 g_string_append_printf (
224 _("Please enter the password for "
225 "account \"%s\"."), display_name);
229 if (host_name != NULL && user_name != NULL)
230 g_string_append_printf (
231 description, "\n(user: %s, host: %s)",
232 user_name, host_name);
233 else if (host_name != NULL)
234 g_string_append_printf (
235 description, "\n(host: %s)", host_name);
236 else if (user_name != NULL)
237 g_string_append_printf (
238 description, "\n(user: %s)", user_name);
240 *prompt_title = g_strdup ("");
241 *prompt_message = g_strdup (message);
242 *prompt_description = g_string_free (description, FALSE);
244 g_free (display_name);
249 /* Helper for source_authenticator_try_password() */
251 source_authenticator_try_password_thread (GSimpleAsyncResult *simple,
253 GCancellable *cancellable)
255 AsyncContext *async_context;
256 GError *error = NULL;
258 async_context = g_simple_async_result_get_op_res_gpointer (simple);
260 async_context->result =
261 e_source_authenticator_try_password_sync (
262 E_SOURCE_AUTHENTICATOR (object),
263 async_context->password,
264 cancellable, &error);
267 g_simple_async_result_take_error (simple, error);
271 source_authenticator_try_password (ESourceAuthenticator *auth,
272 const GString *password,
273 GCancellable *cancellable,
274 GAsyncReadyCallback callback,
277 GSimpleAsyncResult *simple;
278 AsyncContext *async_context;
280 async_context = g_slice_new0 (AsyncContext);
281 async_context->password = g_string_new (password->str);
283 simple = g_simple_async_result_new (
284 G_OBJECT (auth), callback, user_data,
285 source_authenticator_try_password);
287 g_simple_async_result_set_check_cancellable (simple, cancellable);
289 g_simple_async_result_set_op_res_gpointer (
290 simple, async_context, (GDestroyNotify) async_context_free);
292 g_simple_async_result_run_in_thread (
293 simple, source_authenticator_try_password_thread,
294 G_PRIORITY_DEFAULT, cancellable);
296 g_object_unref (simple);
299 static ESourceAuthenticationResult
300 source_authenticator_try_password_finish (ESourceAuthenticator *auth,
301 GAsyncResult *result,
304 GSimpleAsyncResult *simple;
305 AsyncContext *async_context;
307 g_return_val_if_fail (
308 g_simple_async_result_is_valid (
309 result, G_OBJECT (auth),
310 source_authenticator_try_password),
311 E_SOURCE_AUTHENTICATION_REJECTED);
313 simple = G_SIMPLE_ASYNC_RESULT (result);
314 async_context = g_simple_async_result_get_op_res_gpointer (simple);
316 if (g_simple_async_result_propagate_error (simple, error))
317 return E_SOURCE_AUTHENTICATION_ERROR;
319 return async_context->result;
323 e_source_authenticator_default_init (ESourceAuthenticatorInterface *interface)
325 interface->get_prompt_strings = source_authenticator_get_prompt_strings;
326 interface->try_password = source_authenticator_try_password;
327 interface->try_password_finish = source_authenticator_try_password_finish;
331 * e_source_authenticator_get_prompt_strings:
332 * @auth: an #ESourceAuthenticator
333 * @source: an #ESource
334 * @prompt_title: (out): the title of the prompt
335 * @prompt_message: (out): the prompt message for the user
336 * @prompt_description: (out): the detailed description of the prompt
338 * Generates authentication prompt strings for @source.
340 * For registry service clients, #ESourceRegistry calls this function as
341 * part of e_source_registry_authenticate_sync(). In the registry service
342 * itself, #EAuthenticationSession calls this function during initialization.
343 * This function should rarely need to be called explicitly outside of those
346 * The #ESourceAuthenticatorInterface defines a default behavior for this
347 * method which should suffice in most cases. But implementors can still
348 * override the method if needed for special circumstances.
350 * Free each of the returned prompt strings with g_free().
355 e_source_authenticator_get_prompt_strings (ESourceAuthenticator *auth,
357 gchar **prompt_title,
358 gchar **prompt_message,
359 gchar **prompt_description)
361 ESourceAuthenticatorInterface *interface;
363 g_return_if_fail (E_IS_SOURCE_AUTHENTICATOR (auth));
364 g_return_if_fail (E_IS_SOURCE (source));
365 g_return_if_fail (prompt_title != NULL);
366 g_return_if_fail (prompt_message != NULL);
367 g_return_if_fail (prompt_description != NULL);
369 interface = E_SOURCE_AUTHENTICATOR_GET_INTERFACE (auth);
370 g_return_if_fail (interface->get_prompt_strings);
372 interface->get_prompt_strings (
380 * e_source_authenticator_try_password_sync:
381 * @auth: an #ESourceAuthenticator
382 * @password: a user-provided password
383 * @cancellable: (allow-none): optional #GCancellable object, or %NULL
384 * @error: return location for a #GError, or %NULL
386 * Attempts to authenticate using @password.
388 * The password is passed in a #GString container so its content is not
389 * accidentally revealed in a stack trace.
391 * If an error occurs, the function sets @error and returns
392 * #E_SOURCE_AUTHENTICATION_ERROR.
394 * Returns: the authentication result
398 ESourceAuthenticationResult
399 e_source_authenticator_try_password_sync (ESourceAuthenticator *auth,
400 const GString *password,
401 GCancellable *cancellable,
404 ESourceAuthenticatorInterface *interface;
406 g_return_val_if_fail (
407 E_IS_SOURCE_AUTHENTICATOR (auth),
408 E_SOURCE_AUTHENTICATION_REJECTED);
409 g_return_val_if_fail (
411 E_SOURCE_AUTHENTICATION_REJECTED);
413 interface = E_SOURCE_AUTHENTICATOR_GET_INTERFACE (auth);
414 g_return_val_if_fail (
415 interface->try_password_sync != NULL,
416 E_SOURCE_AUTHENTICATION_REJECTED);
418 return interface->try_password_sync (
419 auth, password, cancellable, error);
423 * e_source_authenticator_try_password:
424 * @auth: an #ESourceAuthenticator
425 * @password: a user-provided password
426 * @cancellable: (allow-none): optional #GCancellable object, or %NULL
427 * @callback: (scope async): a #GAsyncReadyCallback to call when the request
429 * @user_data: (closure): data to pass to the callback function
431 * Asyncrhonously attempts to authenticate using @password.
433 * The password is passed in a #GString container so its content is not
434 * accidentally revealed in a stack trace.
436 * When the operation is finished, @callback will be called. You can then
437 * call e_source_authenticator_try_password_finish() to get the result of the
443 e_source_authenticator_try_password (ESourceAuthenticator *auth,
444 const GString *password,
445 GCancellable *cancellable,
446 GAsyncReadyCallback callback,
449 ESourceAuthenticatorInterface *interface;
451 g_return_if_fail (E_IS_SOURCE_AUTHENTICATOR (auth));
452 g_return_if_fail (password != NULL);
454 interface = E_SOURCE_AUTHENTICATOR_GET_INTERFACE (auth);
455 g_return_if_fail (interface->try_password != NULL);
457 interface->try_password (
458 auth, password, cancellable, callback, user_data);
462 * e_source_authenticator_try_password_finish:
463 * @auth: an #ESourceAuthenticator
464 * @result: a #GAsyncResult
465 * @error: return location for a #GError, or %NULL
467 * Finishes the operation started with e_source_authenticator_try_password().
469 * If an error occurred, the function sets @error and returns
470 * #E_SOURCE_AUTHENTICATION_ERROR.
472 * Returns: the authentication result
476 ESourceAuthenticationResult
477 e_source_authenticator_try_password_finish (ESourceAuthenticator *auth,
478 GAsyncResult *result,
481 ESourceAuthenticatorInterface *interface;
483 g_return_val_if_fail (
484 E_IS_SOURCE_AUTHENTICATOR (auth),
485 E_SOURCE_AUTHENTICATION_REJECTED);
486 g_return_val_if_fail (
487 G_IS_ASYNC_RESULT (result),
488 E_SOURCE_AUTHENTICATION_REJECTED);
490 interface = E_SOURCE_AUTHENTICATOR_GET_INTERFACE (auth);
491 g_return_val_if_fail (
492 interface->try_password_finish != NULL,
493 E_SOURCE_AUTHENTICATION_REJECTED);
495 return interface->try_password_finish (auth, result, error);