2 * e-source-authenticator.c
4 * This library is free software you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation.
8 * This library is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this library; if not, see <http://www.gnu.org/licenses/>.
19 * SECTION: e-source-authenticator
20 * @include: libedataserver/libedataserver.h
21 * @short_description: Interface for authentication attempts
23 * An object implementing the #ESourceAuthenticator interface gets passed
24 * to e_source_registry_authenticate(). The job of an #ESourceAuthenticator
25 * is to test whether a remote server will accept a given password, and then
26 * indicate the result by returning an #ESourceAuthenticationResult value.
28 * Typically only #EBackend subclasses need to implement this interface,
29 * as client applications are not involved in authentication.
31 * Note this interface is designed around "stateful authentication", where
32 * one connects to a server, provides credentials for authentication once,
33 * and then issues commands in an authenticated state for the remainder of
36 * Backends requiring "stateless authentication" -- where credentials are
37 * included with each command -- will typically want to cache the password
38 * internally once it's verified as part of implementing this interface.
41 #include "e-source-authenticator.h"
44 #include <glib/gi18n-lib.h>
46 /* These are for building an authentication prompt. */
47 #include <libedataserver/e-source-address-book.h>
48 #include <libedataserver/e-source-authentication.h>
49 #include <libedataserver/e-source-calendar.h>
50 #include <libedataserver/e-source-collection.h>
51 #include <libedataserver/e-source-mail-account.h>
52 #include <libedataserver/e-source-mail-identity.h>
53 #include <libedataserver/e-source-mail-transport.h>
55 typedef struct _AsyncContext AsyncContext;
57 struct _AsyncContext {
59 ESourceAuthenticationResult result;
64 e_source_authenticator,
68 async_context_free (AsyncContext *async_context)
70 g_string_free (async_context->password, TRUE);
72 g_slice_free (AsyncContext, async_context);
76 source_authenticator_get_prompt_strings (ESourceAuthenticator *auth,
79 gchar **prompt_message,
80 gchar **prompt_description)
82 ESourceAuthentication *extension;
85 const gchar *extension_name;
100 } type = TYPE_UNKNOWN;
102 /* XXX This is kind of a hack but it should work for now. Build a
103 * suitable password prompt by checking for various extensions
104 * in the ESource. If no recognizable extensions are found, or
105 * if the result is ambiguous, just refer to the data source as
108 display_name = e_source_dup_display_name (source);
110 extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
111 extension = e_source_get_extension (source, extension_name);
112 host_name = e_source_authentication_dup_host (extension);
113 user_name = e_source_authentication_dup_user (extension);
115 extension_name = E_SOURCE_EXTENSION_ADDRESS_BOOK;
116 if (e_source_has_extension (source, extension_name)) {
117 type = TYPE_ADDRESS_BOOK;
120 extension_name = E_SOURCE_EXTENSION_CALENDAR;
121 if (e_source_has_extension (source, extension_name)) {
122 if (type == TYPE_UNKNOWN)
123 type = TYPE_CALENDAR;
125 type = TYPE_AMBIGUOUS;
128 extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
129 if (e_source_has_extension (source, extension_name)) {
130 if (type == TYPE_UNKNOWN)
131 type = TYPE_MAIL_ACCOUNT;
133 type = TYPE_AMBIGUOUS;
136 extension_name = E_SOURCE_EXTENSION_MAIL_TRANSPORT;
137 if (e_source_has_extension (source, extension_name)) {
138 if (type == TYPE_UNKNOWN)
139 type = TYPE_MAIL_TRANSPORT;
141 type = TYPE_AMBIGUOUS;
144 extension_name = E_SOURCE_EXTENSION_MEMO_LIST;
145 if (e_source_has_extension (source, extension_name)) {
146 if (type == TYPE_UNKNOWN)
147 type = TYPE_MEMO_LIST;
149 type = TYPE_AMBIGUOUS;
152 extension_name = E_SOURCE_EXTENSION_TASK_LIST;
153 if (e_source_has_extension (source, extension_name)) {
154 if (type == TYPE_UNKNOWN)
155 type = TYPE_TASK_LIST;
157 type = TYPE_AMBIGUOUS;
161 case TYPE_ADDRESS_BOOK:
162 message = _("Address book authentication request");
167 message = _("Calendar authentication request");
169 case TYPE_MAIL_ACCOUNT:
170 case TYPE_MAIL_TRANSPORT:
171 message = _("Mail authentication request");
173 default: /* generic account prompt */
174 message = _("Authentication request");
178 description = g_string_sized_new (256);
181 case TYPE_ADDRESS_BOOK:
182 g_string_append_printf (
184 _("Please enter the password for "
185 "address book \"%s\"."), display_name);
188 g_string_append_printf (
190 _("Please enter the password for "
191 "calendar \"%s\"."), display_name);
193 case TYPE_MAIL_ACCOUNT:
194 g_string_append_printf (
196 _("Please enter the password for "
197 "mail account \"%s\"."), display_name);
199 case TYPE_MAIL_TRANSPORT:
200 g_string_append_printf (
202 _("Please enter the password for "
203 "mail transport \"%s\"."), display_name);
206 g_string_append_printf (
208 _("Please enter the password for "
209 "memo list \"%s\"."), display_name);
212 g_string_append_printf (
214 _("Please enter the password for "
215 "task list \"%s\"."), display_name);
217 default: /* generic account prompt */
218 g_string_append_printf (
220 _("Please enter the password for "
221 "account \"%s\"."), display_name);
225 if (host_name != NULL && user_name != NULL)
226 g_string_append_printf (
227 description, "\n(user: %s, host: %s)",
228 user_name, host_name);
229 else if (host_name != NULL)
230 g_string_append_printf (
231 description, "\n(host: %s)", host_name);
232 else if (user_name != NULL)
233 g_string_append_printf (
234 description, "\n(user: %s)", user_name);
236 *prompt_title = g_strdup ("");
237 *prompt_message = g_strdup (message);
238 *prompt_description = g_string_free (description, FALSE);
240 g_free (display_name);
246 source_authenticator_get_without_password (ESourceAuthenticator *auth)
248 /* require password by default */
252 /* Helper for source_authenticator_try_password() */
254 source_authenticator_try_password_thread (GSimpleAsyncResult *simple,
256 GCancellable *cancellable)
258 AsyncContext *async_context;
259 GError *error = NULL;
261 async_context = g_simple_async_result_get_op_res_gpointer (simple);
263 async_context->result =
264 e_source_authenticator_try_password_sync (
265 E_SOURCE_AUTHENTICATOR (object),
266 async_context->password,
267 cancellable, &error);
270 g_simple_async_result_take_error (simple, error);
274 source_authenticator_try_password (ESourceAuthenticator *auth,
275 const GString *password,
276 GCancellable *cancellable,
277 GAsyncReadyCallback callback,
280 GSimpleAsyncResult *simple;
281 AsyncContext *async_context;
283 async_context = g_slice_new0 (AsyncContext);
284 async_context->password = g_string_new (password->str);
286 simple = g_simple_async_result_new (
287 G_OBJECT (auth), callback, user_data,
288 source_authenticator_try_password);
290 g_simple_async_result_set_check_cancellable (simple, cancellable);
292 g_simple_async_result_set_op_res_gpointer (
293 simple, async_context, (GDestroyNotify) async_context_free);
295 g_simple_async_result_run_in_thread (
296 simple, source_authenticator_try_password_thread,
297 G_PRIORITY_DEFAULT, cancellable);
299 g_object_unref (simple);
302 static ESourceAuthenticationResult
303 source_authenticator_try_password_finish (ESourceAuthenticator *auth,
304 GAsyncResult *result,
307 GSimpleAsyncResult *simple;
308 AsyncContext *async_context;
310 g_return_val_if_fail (
311 g_simple_async_result_is_valid (
312 result, G_OBJECT (auth),
313 source_authenticator_try_password),
314 E_SOURCE_AUTHENTICATION_REJECTED);
316 simple = G_SIMPLE_ASYNC_RESULT (result);
317 async_context = g_simple_async_result_get_op_res_gpointer (simple);
319 if (g_simple_async_result_propagate_error (simple, error))
320 return E_SOURCE_AUTHENTICATION_ERROR;
322 return async_context->result;
326 e_source_authenticator_default_init (ESourceAuthenticatorInterface *iface)
328 iface->get_prompt_strings = source_authenticator_get_prompt_strings;
329 iface->get_without_password = source_authenticator_get_without_password;
330 iface->try_password = source_authenticator_try_password;
331 iface->try_password_finish = source_authenticator_try_password_finish;
335 * e_source_authenticator_get_prompt_strings:
336 * @auth: an #ESourceAuthenticator
337 * @source: an #ESource
338 * @prompt_title: (out): the title of the prompt
339 * @prompt_message: (out): the prompt message for the user
340 * @prompt_description: (out): the detailed description of the prompt
342 * Generates authentication prompt strings for @source.
344 * For registry service clients, #ESourceRegistry calls this function as
345 * part of e_source_registry_authenticate_sync(). In the registry service
346 * itself, #EAuthenticationSession calls this function during initialization.
347 * This function should rarely need to be called explicitly outside of those
350 * The #ESourceAuthenticatorInterface defines a default behavior for this
351 * method which should suffice in most cases. But implementors can still
352 * override the method if needed for special circumstances.
354 * Free each of the returned prompt strings with g_free().
359 e_source_authenticator_get_prompt_strings (ESourceAuthenticator *auth,
361 gchar **prompt_title,
362 gchar **prompt_message,
363 gchar **prompt_description)
365 ESourceAuthenticatorInterface *iface;
367 g_return_if_fail (E_IS_SOURCE_AUTHENTICATOR (auth));
368 g_return_if_fail (E_IS_SOURCE (source));
369 g_return_if_fail (prompt_title != NULL);
370 g_return_if_fail (prompt_message != NULL);
371 g_return_if_fail (prompt_description != NULL);
373 iface = E_SOURCE_AUTHENTICATOR_GET_INTERFACE (auth);
374 g_return_if_fail (iface->get_prompt_strings);
376 iface->get_prompt_strings (
384 * e_source_authenticator_get_without_password:
385 * @auth: an #ESourceAuthenticator
387 * Returns whether the used authentication method can be used without
388 * a password prompt. If so, then user is not asked for the password,
389 * only if the authentication fails. The default implementation returns
390 * %FALSE, which means always asks for the password (or read it from
393 * Returns: whether to try to authenticate without asking for the password
398 e_source_authenticator_get_without_password (ESourceAuthenticator *auth)
400 ESourceAuthenticatorInterface *iface;
402 g_return_val_if_fail (E_IS_SOURCE_AUTHENTICATOR (auth), FALSE);
404 iface = E_SOURCE_AUTHENTICATOR_GET_INTERFACE (auth);
405 g_return_val_if_fail (iface->get_without_password, FALSE);
407 return iface->get_without_password (auth);
411 * e_source_authenticator_try_password_sync:
412 * @auth: an #ESourceAuthenticator
413 * @password: a user-provided password
414 * @cancellable: (allow-none): optional #GCancellable object, or %NULL
415 * @error: return location for a #GError, or %NULL
417 * Attempts to authenticate using @password.
419 * The password is passed in a #GString container so its content is not
420 * accidentally revealed in a stack trace.
422 * If an error occurs, the function sets @error and returns
423 * #E_SOURCE_AUTHENTICATION_ERROR.
425 * Returns: the authentication result
429 ESourceAuthenticationResult
430 e_source_authenticator_try_password_sync (ESourceAuthenticator *auth,
431 const GString *password,
432 GCancellable *cancellable,
435 ESourceAuthenticatorInterface *iface;
437 g_return_val_if_fail (
438 E_IS_SOURCE_AUTHENTICATOR (auth),
439 E_SOURCE_AUTHENTICATION_REJECTED);
440 g_return_val_if_fail (
442 E_SOURCE_AUTHENTICATION_REJECTED);
444 iface = E_SOURCE_AUTHENTICATOR_GET_INTERFACE (auth);
445 g_return_val_if_fail (
446 iface->try_password_sync != NULL,
447 E_SOURCE_AUTHENTICATION_REJECTED);
449 return iface->try_password_sync (
450 auth, password, cancellable, error);
454 * e_source_authenticator_try_password:
455 * @auth: an #ESourceAuthenticator
456 * @password: a user-provided password
457 * @cancellable: (allow-none): optional #GCancellable object, or %NULL
458 * @callback: (scope async): a #GAsyncReadyCallback to call when the request
460 * @user_data: (closure): data to pass to the callback function
462 * Asyncrhonously attempts to authenticate using @password.
464 * The password is passed in a #GString container so its content is not
465 * accidentally revealed in a stack trace.
467 * When the operation is finished, @callback will be called. You can then
468 * call e_source_authenticator_try_password_finish() to get the result of the
474 e_source_authenticator_try_password (ESourceAuthenticator *auth,
475 const GString *password,
476 GCancellable *cancellable,
477 GAsyncReadyCallback callback,
480 ESourceAuthenticatorInterface *iface;
482 g_return_if_fail (E_IS_SOURCE_AUTHENTICATOR (auth));
483 g_return_if_fail (password != NULL);
485 iface = E_SOURCE_AUTHENTICATOR_GET_INTERFACE (auth);
486 g_return_if_fail (iface->try_password != NULL);
488 iface->try_password (
489 auth, password, cancellable, callback, user_data);
493 * e_source_authenticator_try_password_finish:
494 * @auth: an #ESourceAuthenticator
495 * @result: a #GAsyncResult
496 * @error: return location for a #GError, or %NULL
498 * Finishes the operation started with e_source_authenticator_try_password().
500 * If an error occurred, the function sets @error and returns
501 * #E_SOURCE_AUTHENTICATION_ERROR.
503 * Returns: the authentication result
507 ESourceAuthenticationResult
508 e_source_authenticator_try_password_finish (ESourceAuthenticator *auth,
509 GAsyncResult *result,
512 ESourceAuthenticatorInterface *iface;
514 g_return_val_if_fail (
515 E_IS_SOURCE_AUTHENTICATOR (auth),
516 E_SOURCE_AUTHENTICATION_REJECTED);
517 g_return_val_if_fail (
518 G_IS_ASYNC_RESULT (result),
519 E_SOURCE_AUTHENTICATION_REJECTED);
521 iface = E_SOURCE_AUTHENTICATOR_GET_INTERFACE (auth);
522 g_return_val_if_fail (
523 iface->try_password_finish != NULL,
524 E_SOURCE_AUTHENTICATION_REJECTED);
526 return iface->try_password_finish (auth, result, error);