1 /*
2 * e-goa-password-based.c
3 *
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.
7 *
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 Lesser General Public License
11 * for more details.
12 *
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/>.
15 *
16 */
17
18 #include "evolution-data-server-config.h"
19
20 /* XXX Yeah, yeah... */
21 #define GOA_API_IS_SUBJECT_TO_CHANGE
22
23 #include <goa/goa.h>
24 #include <glib/gi18n-lib.h>
25
26 #include "e-goa-password-based.h"
27
28 struct _EGoaPasswordBasedPrivate {
29 GoaClient *goa_client;
30 GMutex lock;
31 };
32
33 G_DEFINE_DYNAMIC_TYPE_EXTENDED (EGoaPasswordBased,
34 e_goa_password_based,
35 E_TYPE_SOURCE_CREDENTIALS_PROVIDER_IMPL,
36 0,
37 G_ADD_PRIVATE_DYNAMIC (EGoaPasswordBased))
38
39 static GoaClient *
e_goa_password_based_ref_goa_client_sync(EGoaPasswordBased * goa_password_based,GCancellable * cancellable,GError ** error)40 e_goa_password_based_ref_goa_client_sync (EGoaPasswordBased *goa_password_based,
41 GCancellable *cancellable,
42 GError **error)
43 {
44 GoaClient *goa_client;
45
46 g_return_val_if_fail (E_IS_GOA_PASSWORD_BASED (goa_password_based), NULL);
47
48 g_mutex_lock (&goa_password_based->priv->lock);
49
50 if (goa_password_based->priv->goa_client) {
51 GDBusObjectManager *object_manager;
52 gchar *owner_name = NULL;
53
54 object_manager = goa_client_get_object_manager (goa_password_based->priv->goa_client);
55 if (object_manager)
56 owner_name = g_dbus_object_manager_client_get_name_owner (G_DBUS_OBJECT_MANAGER_CLIENT (object_manager));
57
58 if (!owner_name)
59 g_clear_object (&goa_password_based->priv->goa_client);
60
61 g_free (owner_name);
62 }
63
64 if (!goa_password_based->priv->goa_client)
65 goa_password_based->priv->goa_client = goa_client_new_sync (cancellable, error);
66
67 if (goa_password_based->priv->goa_client)
68 goa_client = g_object_ref (goa_password_based->priv->goa_client);
69 else
70 goa_client = NULL;
71
72 g_mutex_unlock (&goa_password_based->priv->lock);
73
74 return goa_client;
75 }
76
77 static ESource *
e_goa_password_based_ref_credentials_source(ESourceCredentialsProvider * provider,ESource * source)78 e_goa_password_based_ref_credentials_source (ESourceCredentialsProvider *provider,
79 ESource *source)
80 {
81 ESource *adept, *cred_source = NULL;
82
83 g_return_val_if_fail (E_IS_SOURCE_CREDENTIALS_PROVIDER (provider), NULL);
84 g_return_val_if_fail (E_IS_SOURCE (source), NULL);
85
86 adept = g_object_ref (source);
87
88 while (adept && !e_source_has_extension (adept, E_SOURCE_EXTENSION_GOA)) {
89 ESource *parent;
90
91 if (!e_source_get_parent (adept)) {
92 break;
93 }
94
95 parent = e_source_credentials_provider_ref_source (provider, e_source_get_parent (adept));
96
97 g_clear_object (&adept);
98 adept = parent;
99 }
100
101 if (adept && e_source_has_extension (adept, E_SOURCE_EXTENSION_GOA)) {
102 cred_source = g_object_ref (adept);
103 }
104
105 g_clear_object (&adept);
106
107 if (!cred_source)
108 cred_source = e_source_credentials_provider_ref_credentials_source (provider, source);
109
110 return cred_source;
111 }
112
113 static GoaObject *
e_goa_password_based_ref_account(ESourceCredentialsProvider * provider,ESource * source,GoaClient * goa_client)114 e_goa_password_based_ref_account (ESourceCredentialsProvider *provider,
115 ESource *source,
116 GoaClient *goa_client)
117 {
118 ESource *cred_source = NULL;
119 GoaObject *match = NULL;
120 GList *list, *link;
121 gchar *account_id = NULL;
122 ESourceGoa *extension = NULL;
123
124 if (e_source_has_extension (source, E_SOURCE_EXTENSION_GOA)) {
125 extension = e_source_get_extension (source, E_SOURCE_EXTENSION_GOA);
126 } else {
127 cred_source = e_goa_password_based_ref_credentials_source (provider, source);
128 if (cred_source && e_source_has_extension (cred_source, E_SOURCE_EXTENSION_GOA))
129 extension = e_source_get_extension (cred_source, E_SOURCE_EXTENSION_GOA);
130 }
131
132 if (!extension) {
133 g_clear_object (&cred_source);
134 return NULL;
135 }
136
137 account_id = e_source_goa_dup_account_id (extension);
138
139 g_clear_object (&cred_source);
140
141 if (account_id == NULL)
142 return NULL;
143
144 /* FIXME Use goa_client_lookup_by_id() once we require GOA 3.6. */
145 list = goa_client_get_accounts (goa_client);
146
147 for (link = list; link != NULL; link = g_list_next (link)) {
148 GoaObject *goa_object;
149 GoaAccount *goa_account;
150 const gchar *candidate_id;
151
152 goa_object = GOA_OBJECT (link->data);
153 goa_account = goa_object_get_account (goa_object);
154 candidate_id = goa_account_get_id (goa_account);
155
156 if (g_strcmp0 (account_id, candidate_id) == 0)
157 match = g_object_ref (goa_object);
158
159 g_object_unref (goa_account);
160
161 if (match != NULL)
162 break;
163 }
164
165 g_list_free_full (list, (GDestroyNotify) g_object_unref);
166 g_free (account_id);
167
168 return match;
169 }
170
171 static gboolean
e_goa_password_based_can_process(ESourceCredentialsProviderImpl * provider_impl,ESource * source)172 e_goa_password_based_can_process (ESourceCredentialsProviderImpl *provider_impl,
173 ESource *source)
174 {
175 gboolean can_process;
176
177 g_return_val_if_fail (E_IS_GOA_PASSWORD_BASED (provider_impl), FALSE);
178 g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
179
180 can_process = e_source_has_extension (source, E_SOURCE_EXTENSION_GOA);
181 if (!can_process) {
182 ESource *cred_source;
183
184 cred_source = e_goa_password_based_ref_credentials_source (
185 e_source_credentials_provider_impl_get_provider (provider_impl),
186 source);
187
188 if (cred_source) {
189 can_process = e_source_has_extension (cred_source, E_SOURCE_EXTENSION_GOA);
190 g_clear_object (&cred_source);
191 }
192 }
193
194 return can_process;
195 }
196
197 static gboolean
e_goa_password_based_can_store(ESourceCredentialsProviderImpl * provider_impl)198 e_goa_password_based_can_store (ESourceCredentialsProviderImpl *provider_impl)
199 {
200 g_return_val_if_fail (E_IS_GOA_PASSWORD_BASED (provider_impl), FALSE);
201
202 return FALSE;
203 }
204
205 static gboolean
e_goa_password_based_can_prompt(ESourceCredentialsProviderImpl * provider_impl)206 e_goa_password_based_can_prompt (ESourceCredentialsProviderImpl *provider_impl)
207 {
208 g_return_val_if_fail (E_IS_GOA_PASSWORD_BASED (provider_impl), FALSE);
209
210 return FALSE;
211 }
212
213 static gboolean
e_goa_password_based_lookup_sync(ESourceCredentialsProviderImpl * provider_impl,ESource * source,GCancellable * cancellable,ENamedParameters ** out_credentials,GError ** error)214 e_goa_password_based_lookup_sync (ESourceCredentialsProviderImpl *provider_impl,
215 ESource *source,
216 GCancellable *cancellable,
217 ENamedParameters **out_credentials,
218 GError **error)
219 {
220 GoaClient *goa_client = NULL;
221 GoaObject *goa_object = NULL;
222 GoaAccount *goa_account = NULL;
223 GoaPasswordBased *goa_password_based = NULL;
224 gchar *password = NULL;
225 gboolean use_imap_password;
226 gboolean use_smtp_password;
227 gboolean success = FALSE;
228 GError *local_error = NULL;
229
230 g_return_val_if_fail (E_IS_GOA_PASSWORD_BASED (provider_impl), FALSE);
231 g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
232 g_return_val_if_fail (out_credentials, FALSE);
233
234 goa_client = e_goa_password_based_ref_goa_client_sync (E_GOA_PASSWORD_BASED (provider_impl), cancellable, error);
235 if (goa_client == NULL) {
236 if (error && *error)
237 g_dbus_error_strip_remote_error (*error);
238 goto exit;
239 }
240
241 goa_object = e_goa_password_based_ref_account (
242 e_source_credentials_provider_impl_get_provider (provider_impl),
243 source, goa_client);
244
245 if (goa_object == NULL) {
246 g_set_error (
247 error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
248 _("Cannot find a corresponding account in "
249 "the org.gnome.OnlineAccounts service from "
250 "which to obtain a password for “%s”"),
251 e_source_get_display_name (source));
252 goto exit;
253 }
254
255 goa_account = goa_object_get_account (goa_object);
256 goa_password_based = goa_object_get_password_based (goa_object);
257
258 if (!goa_password_based) {
259 /* Can be OAuth/2 based, thus return empty credentials. */
260 *out_credentials = e_named_parameters_new ();
261 success = TRUE;
262 goto exit;
263 }
264
265 success = goa_account_call_ensure_credentials_sync (goa_account, NULL, cancellable, &local_error);
266 if (!success) {
267 if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT)) {
268 g_clear_error (&local_error);
269 } else if (local_error) {
270 g_dbus_error_strip_remote_error (local_error);
271 g_propagate_error (error, local_error);
272
273 goto exit;
274 }
275 }
276
277 use_imap_password = e_source_has_extension (source, E_SOURCE_EXTENSION_MAIL_ACCOUNT);
278 use_smtp_password = e_source_has_extension (source, E_SOURCE_EXTENSION_MAIL_TRANSPORT);
279
280 /* Use a suitable password ID for the ESource. */
281 if (use_imap_password) {
282 goa_password_based_call_get_password_sync (
283 goa_password_based, "imap-password",
284 &password, cancellable, error);
285 } else if (use_smtp_password) {
286 goa_password_based_call_get_password_sync (
287 goa_password_based, "smtp-password",
288 &password, cancellable, error);
289 } else {
290 /* Generic fallback - password ID is not used. */
291 goa_password_based_call_get_password_sync (
292 goa_password_based, "",
293 &password, cancellable, error);
294 }
295
296 if (password == NULL) {
297 success = FALSE;
298 if (error && *error)
299 g_dbus_error_strip_remote_error (*error);
300 goto exit;
301 }
302
303 *out_credentials = e_named_parameters_new ();
304 e_named_parameters_set (*out_credentials, E_SOURCE_CREDENTIAL_PASSWORD, password);
305
306 exit:
307 g_clear_object (&goa_client);
308 g_clear_object (&goa_object);
309 g_clear_object (&goa_account);
310 g_clear_object (&goa_password_based);
311
312 e_util_safe_free_string (password);
313
314 if (!success)
315 g_prefix_error (error, "%s", _("Failed to get password from GOA: "));
316
317 return success;
318 }
319
320 static void
e_goa_password_based_dispose(GObject * object)321 e_goa_password_based_dispose (GObject *object)
322 {
323 EGoaPasswordBased *goa_password_based = E_GOA_PASSWORD_BASED (object);
324
325 g_mutex_lock (&goa_password_based->priv->lock);
326
327 g_clear_object (&goa_password_based->priv->goa_client);
328
329 g_mutex_unlock (&goa_password_based->priv->lock);
330
331 /* Chain up to parent's method. */
332 G_OBJECT_CLASS (e_goa_password_based_parent_class)->dispose (object);
333 }
334
335 static void
e_goa_password_based_finalize(GObject * object)336 e_goa_password_based_finalize (GObject *object)
337 {
338 EGoaPasswordBased *goa_password_based = E_GOA_PASSWORD_BASED (object);
339
340 g_clear_object (&goa_password_based->priv->goa_client);
341 g_mutex_clear (&goa_password_based->priv->lock);
342
343 /* Chain up to parent's method. */
344 G_OBJECT_CLASS (e_goa_password_based_parent_class)->finalize (object);
345 }
346
347 static void
e_goa_password_based_class_init(EGoaPasswordBasedClass * class)348 e_goa_password_based_class_init (EGoaPasswordBasedClass *class)
349 {
350 ESourceCredentialsProviderImplClass *provider_impl_class;
351 GObjectClass *object_class;
352
353 provider_impl_class = E_SOURCE_CREDENTIALS_PROVIDER_IMPL_CLASS (class);
354 provider_impl_class->can_process = e_goa_password_based_can_process;
355 provider_impl_class->can_store = e_goa_password_based_can_store;
356 provider_impl_class->can_prompt = e_goa_password_based_can_prompt;
357 provider_impl_class->lookup_sync = e_goa_password_based_lookup_sync;
358
359 object_class = G_OBJECT_CLASS (class);
360 object_class->dispose = e_goa_password_based_dispose;
361 object_class->finalize = e_goa_password_based_finalize;
362 }
363
364 static void
e_goa_password_based_class_finalize(EGoaPasswordBasedClass * class)365 e_goa_password_based_class_finalize (EGoaPasswordBasedClass *class)
366 {
367 }
368
369 static void
e_goa_password_based_init(EGoaPasswordBased * session)370 e_goa_password_based_init (EGoaPasswordBased *session)
371 {
372 session->priv = e_goa_password_based_get_instance_private (session);
373
374 g_mutex_init (&session->priv->lock);
375 }
376
377 void
e_goa_password_based_type_register(GTypeModule * type_module)378 e_goa_password_based_type_register (GTypeModule *type_module)
379 {
380 /* XXX G_DEFINE_DYNAMIC_TYPE_EXTENDED declares a static type registration
381 * function, so we have to wrap it with a public function in
382 * order to register types from a separate compilation unit. */
383 e_goa_password_based_register_type (type_module);
384 }
385
386