1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 /*
3 * Copyright © 2011 – 2019 Red Hat, Inc.
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General
16 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include "config.h"
20
21 #include <glib/gi18n-lib.h>
22
23 #include "goaprovider.h"
24 #include "goaprovider-priv.h"
25 #include "goaexchangeprovider.h"
26 #include "goagoogleprovider.h"
27 #include "goafacebookprovider.h"
28 #include "goaimapsmtpprovider.h"
29 #include "goaowncloudprovider.h"
30 #include "goaflickrprovider.h"
31 #include "goafoursquareprovider.h"
32 #include "goawindowsliveprovider.h"
33 #include "goamediaserverprovider.h"
34 #include "goalastfmprovider.h"
35
36 #ifdef GOA_FEDORA_ENABLED
37 #include "goafedoraprovider.h"
38 #endif
39
40 #ifdef GOA_KERBEROS_ENABLED
41 #include "goakerberosprovider.h"
42 #endif
43
44 #include "goautils.h"
45
46 /**
47 * SECTION:goaprovider
48 * @title: GoaProvider
49 * @short_description: Abstract base class for providers
50 *
51 * #GoaProvider is the base type for all providers.
52 */
53
54 enum {
55 PROP_0,
56 PROP_PRESEED_DATA,
57 NUM_PROPERTIES
58 };
59
60 static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
61
62 static gboolean goa_provider_ensure_credentials_sync_real (GoaProvider *self,
63 GoaObject *object,
64 gint *out_expires_in,
65 GCancellable *cancellable,
66 GError **error);
67
68 static gboolean goa_provider_build_object_real (GoaProvider *self,
69 GoaObjectSkeleton *object,
70 GKeyFile *key_file,
71 const gchar *group,
72 GDBusConnection *connection,
73 gboolean just_added,
74 GError **error);
75
76 static guint goa_provider_get_credentials_generation_real (GoaProvider *self);
77
78 static GIcon *goa_provider_get_provider_icon_real (GoaProvider *self,
79 GoaObject *object);
80
81 static void goa_provider_remove_account_real (GoaProvider *self,
82 GoaObject *object,
83 GCancellable *cancellable,
84 GAsyncReadyCallback callback,
85 gpointer user_data);
86
87 static gboolean goa_provider_remove_account_finish_real (GoaProvider *self,
88 GAsyncResult *res,
89 GError **error);
90
91 static void goa_provider_show_account_real (GoaProvider *provider,
92 GoaClient *client,
93 GoaObject *object,
94 GtkBox *vbox,
95 GtkGrid *dummy1,
96 GtkGrid *dummy2);
97
98 G_DEFINE_ABSTRACT_TYPE (GoaProvider, goa_provider, G_TYPE_OBJECT);
99
100 static struct {
101 GoaProviderFeatures feature;
102 const gchar *property;
103 const gchar *blurb;
104 } provider_features_info[] = {
105 /* The order in which the features are listed is
106 * important because it affects the order in which they are
107 * displayed in the show_account() UI
108 */
109 {
110 .feature = GOA_PROVIDER_FEATURE_MAIL,
111 .property = "mail-disabled",
112 .blurb = N_("_Mail"),
113 },
114 {
115 .feature = GOA_PROVIDER_FEATURE_CALENDAR,
116 .property = "calendar-disabled",
117 .blurb = N_("Cale_ndar"),
118 },
119 {
120 .feature = GOA_PROVIDER_FEATURE_CONTACTS,
121 .property = "contacts-disabled",
122 .blurb = N_("_Contacts"),
123 },
124 {
125 .feature = GOA_PROVIDER_FEATURE_CHAT,
126 .property = "chat-disabled",
127 .blurb = N_("C_hat"),
128 },
129 {
130 .feature = GOA_PROVIDER_FEATURE_DOCUMENTS,
131 .property = "documents-disabled",
132 .blurb = N_("_Documents"),
133 },
134 {
135 .feature = GOA_PROVIDER_FEATURE_MUSIC,
136 .property = "music-disabled",
137 .blurb = N_("M_usic"),
138 },
139 {
140 .feature = GOA_PROVIDER_FEATURE_PHOTOS,
141 .property = "photos-disabled",
142 .blurb = N_("_Photos"),
143 },
144 {
145 .feature = GOA_PROVIDER_FEATURE_FILES,
146 .property = "files-disabled",
147 .blurb = N_("_Files"),
148 },
149 {
150 .feature = GOA_PROVIDER_FEATURE_TICKETING,
151 .property = "ticketing-disabled",
152 .blurb = N_("Network _Resources"),
153 },
154 {
155 .feature = GOA_PROVIDER_FEATURE_READ_LATER,
156 .property = "read-later-disabled",
157 .blurb = N_("_Read Later"),
158 },
159 {
160 .feature = GOA_PROVIDER_FEATURE_PRINTERS,
161 .property = "printers-disabled",
162 .blurb = N_("Prin_ters"),
163 },
164 {
165 .feature = GOA_PROVIDER_FEATURE_MAPS,
166 .property = "maps-disabled",
167 .blurb = N_("_Maps"),
168 },
169 {
170 .feature = GOA_PROVIDER_FEATURE_TODO,
171 .property = "todo-disabled",
172 .blurb = N_("T_o Do"),
173 },
174 {
175 .feature = GOA_PROVIDER_FEATURE_INVALID,
176 .property = NULL,
177 .blurb = NULL,
178 }
179 };
180
181 static void
goa_provider_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)182 goa_provider_get_property (GObject *object,
183 guint property_id,
184 GValue *value,
185 GParamSpec *pspec)
186 {
187 switch (property_id) {
188 case PROP_PRESEED_DATA:
189 g_value_set_variant (value, NULL);
190 break;
191 default:
192 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
193 break;
194 }
195 }
196
197 static void
goa_provider_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)198 goa_provider_set_property (GObject *object,
199 guint property_id,
200 const GValue *value,
201 GParamSpec *pspec)
202 {
203 switch (property_id) {
204 case PROP_PRESEED_DATA:
205 break;
206 default:
207 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
208 break;
209 }
210 }
211
212 static void
goa_provider_init(GoaProvider * self)213 goa_provider_init (GoaProvider *self)
214 {
215 }
216
217 static void
goa_provider_class_init(GoaProviderClass * klass)218 goa_provider_class_init (GoaProviderClass *klass)
219 {
220 GObjectClass *object_class = G_OBJECT_CLASS (klass);
221
222 object_class->set_property = goa_provider_set_property;
223 object_class->get_property = goa_provider_get_property;
224
225 klass->build_object = goa_provider_build_object_real;
226 klass->ensure_credentials_sync = goa_provider_ensure_credentials_sync_real;
227 klass->get_credentials_generation = goa_provider_get_credentials_generation_real;
228 klass->get_provider_icon = goa_provider_get_provider_icon_real;
229 klass->remove_account = goa_provider_remove_account_real;
230 klass->remove_account_finish = goa_provider_remove_account_finish_real;
231 klass->show_account = goa_provider_show_account_real;
232
233 /**
234 * GoaProvider:preseed-data
235 *
236 * An #GVariant of type a{sv} storing any information already collected that
237 * can be useful when creating a new account. For instance, this can be useful
238 * to reuse the HTTP cookies from an existing browser session to skip the
239 * prompt for username and password in the OAuth2-based providers by passing
240 * a #GVariant with the following contents:
241 *
242 * <informalexample>
243 * <programlisting>
244 * {
245 * "cookies": [
246 * {
247 * "domain": "example.com",
248 * "name": "LSID",
249 * "value": "asdfasdfasdf"
250 * },
251 * {
252 * "domain": "accounts.example.com",
253 * "name": "SSID",
254 * "value": "asdfasdfasdf"
255 * }
256 * ]
257 * }
258 * </programlisting>
259 * </informalexample>
260 *
261 * Unknown or unsupported keys will be ignored by providers.
262 *
263 * Deprecated: 3.28: This property does nothing.
264 */
265 properties[PROP_PRESEED_DATA] =
266 g_param_spec_variant ("preseed-data",
267 "Collected data to pre-seed account creation",
268 "A a{sv} #GVariant containing a provider-type specific set of data that"
269 "can be useful during account creation (eg. http cookies from an existing"
270 "browser session or the entrypoint url for self-hosted services).",
271 G_VARIANT_TYPE_VARDICT,
272 NULL,
273 G_PARAM_DEPRECATED | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
274
275 g_object_class_install_properties (object_class, NUM_PROPERTIES, properties);
276 }
277
278 /**
279 * goa_provider_get_provider_type:
280 * @self: A #GoaProvider.
281 *
282 * Gets the type of @self.
283 *
284 * This is a pure virtual method - a subclass must provide an
285 * implementation.
286 *
287 * Returns: (transfer none): A string owned by @self, do not free.
288 */
289 const gchar *
goa_provider_get_provider_type(GoaProvider * self)290 goa_provider_get_provider_type (GoaProvider *self)
291 {
292 g_return_val_if_fail (GOA_IS_PROVIDER (self), NULL);
293 return GOA_PROVIDER_GET_CLASS (self)->get_provider_type (self);
294 }
295
296 /**
297 * goa_provider_get_provider_name:
298 * @self: A #GoaProvider.
299 * @object: (allow-none): A #GoaObject for an account.
300 *
301 * Gets a name for @self and @object that is suitable for display
302 * in an user interface. The returned value may depend on @object (if
303 * it's not %NULL) - for example, hosted accounts might return a
304 * different name.
305 *
306 * This is a pure virtual method - a subclass must provide an
307 * implementation.
308 *
309 * Returns: (transfer full): A string that should be freed with g_free().
310 */
311 gchar *
goa_provider_get_provider_name(GoaProvider * self,GoaObject * object)312 goa_provider_get_provider_name (GoaProvider *self,
313 GoaObject *object)
314 {
315 g_return_val_if_fail (GOA_IS_PROVIDER (self), NULL);
316 return GOA_PROVIDER_GET_CLASS (self)->get_provider_name (self, object);
317 }
318
319 /**
320 * goa_provider_get_provider_icon:
321 * @self: A #GoaProvider.
322 * @object: A #GoaObject for an account.
323 *
324 * Gets an icon for @self and @object that is suitable for display
325 * in an user interface. The returned value may depend on @object -
326 * for example, hosted accounts might return a different icon.
327 *
328 * This is a virtual method with a default implementation that returns
329 * a #GThemedIcon with fallbacks constructed from the name
330 * <literal>goa-account-TYPE</literal> where <literal>TYPE</literal>
331 * is the return value of goa_provider_get_provider_type().
332 *
333 * Returns: (transfer full): An icon that should be freed with g_object_unref().
334 */
335 GIcon *
goa_provider_get_provider_icon(GoaProvider * self,GoaObject * object)336 goa_provider_get_provider_icon (GoaProvider *self,
337 GoaObject *object)
338 {
339 g_return_val_if_fail (GOA_IS_PROVIDER (self), NULL);
340 return GOA_PROVIDER_GET_CLASS (self)->get_provider_icon (self, object);
341 }
342
343 static GIcon *
goa_provider_get_provider_icon_real(GoaProvider * self,GoaObject * object)344 goa_provider_get_provider_icon_real (GoaProvider *self,
345 GoaObject *object)
346 {
347 GIcon *ret;
348 gchar *s;
349 s = g_strdup_printf ("goa-account-%s", goa_provider_get_provider_type (self));
350 ret = g_themed_icon_new_with_default_fallbacks (s);
351 g_free (s);
352 return ret;
353 }
354
355 /**
356 * goa_provider_get_provider_group:
357 * @self: A #GoaProvider.
358 *
359 * Gets the group to which @self belongs that is suitable for
360 * organizing the providers while displaying them in an user
361 * interface.
362 *
363 * This is a pure virtual method - a subclass must provide an
364 * implementation.
365 *
366 * Returns: A #GoaProviderGroup.
367 *
368 * Since: 3.8
369 *
370 * Deprecated: 3.10: Use goa_provider_get_provider_features() instead.
371 */
372 GoaProviderGroup
goa_provider_get_provider_group(GoaProvider * self)373 goa_provider_get_provider_group (GoaProvider *self)
374 {
375 g_return_val_if_fail (GOA_IS_PROVIDER (self), GOA_PROVIDER_GROUP_INVALID);
376 return GOA_PROVIDER_GET_CLASS (self)->get_provider_group (self);
377 }
378
379 /**
380 * goa_provider_get_provider_features:
381 * @self: A #GoaProvider.
382 *
383 * Get the features bitmask (eg. %GOA_PROVIDER_FEATURE_CHAT|%GOA_PROVIDER_FEATURE_CONTACTS)
384 * supported by the provider.
385 *
386 * Returns: The #GoaProviderFeatures bitmask with the provided features.
387 *
388 * Since: 3.10
389 */
390 GoaProviderFeatures
goa_provider_get_provider_features(GoaProvider * self)391 goa_provider_get_provider_features (GoaProvider *self)
392 {
393 g_return_val_if_fail (GOA_IS_PROVIDER (self), GOA_PROVIDER_FEATURE_INVALID);
394 g_return_val_if_fail (GOA_PROVIDER_GET_CLASS (self)->get_provider_features != NULL, GOA_PROVIDER_FEATURE_INVALID);
395 return GOA_PROVIDER_GET_CLASS (self)->get_provider_features (self);
396 }
397
398 /* ---------------------------------------------------------------------------------------------------- */
399
400 /**
401 * goa_provider_add_account:
402 * @self: A #GoaProvider.
403 * @client: A #GoaClient.
404 * @dialog: A #GtkDialog.
405 * @vbox: A vertically oriented #GtkBox to put content in.
406 * @error: Return location for error or %NULL.
407 *
408 * This method brings up the user interface necessary to create a new
409 * account on @client of the type for @self, interacts with the
410 * user to get all information needed and creates the account.
411 *
412 * The passed in @dialog widget is guaranteed to be visible with @vbox
413 * being empty and the only visible widget in @dialog's content
414 * area. The dialog has exactly one action widget, a cancel button
415 * with response id GTK_RESPONSE_CANCEL. Implementations are free to
416 * add additional action widgets, as needed.
417 *
418 * If an account was successfully created, a #GoaObject for the
419 * created account is returned. If @dialog is dismissed, %NULL is
420 * returned and @error is set to %GOA_ERROR_DIALOG_DISMISSED. If an
421 * account couldn't be created then @error is set. In some cases,
422 * for example, when the credentials could not be stored in the
423 * keyring, a #GoaObject can be returned even if @error is set.
424 *
425 * The caller will always show an error dialog if @error is set unless
426 * the error is %GOA_ERROR_DIALOG_DISMISSED.
427 *
428 * Implementations should run the <link
429 * linkend="g_main_context_default">default main loop</link> while
430 * interacting with the user and may do so using e.g. gtk_dialog_run()
431 * on @dialog.
432 *
433 * This is a pure virtual method - a subclass must provide an
434 * implementation.
435 *
436 * Returns: The #GoaObject for the created account (must be relased
437 * with g_object_unref()) or %NULL if @error is set.
438 */
439 GoaObject *
goa_provider_add_account(GoaProvider * self,GoaClient * client,GtkDialog * dialog,GtkBox * vbox,GError ** error)440 goa_provider_add_account (GoaProvider *self,
441 GoaClient *client,
442 GtkDialog *dialog,
443 GtkBox *vbox,
444 GError **error)
445 {
446 GoaObject *ret;
447
448 g_return_val_if_fail (GOA_IS_PROVIDER (self), NULL);
449 g_return_val_if_fail (GOA_IS_CLIENT (client), NULL);
450 g_return_val_if_fail (GTK_IS_DIALOG (dialog), NULL);
451 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
452
453 ret = GOA_PROVIDER_GET_CLASS (self)->add_account (self, client, dialog, vbox, error);
454
455 g_warn_if_fail ((ret == NULL && (error == NULL || *error != NULL)) || GOA_IS_OBJECT (ret));
456
457 return ret;
458 }
459
460 /* ---------------------------------------------------------------------------------------------------- */
461
462 /**
463 * goa_provider_refresh_account:
464 * @self: A #GoaProvider.
465 * @client: A #GoaClient.
466 * @object: A #GoaObject with a #GoaAccount interface.
467 * @parent: (allow-none): Transient parent of dialogs or %NULL.
468 * @error: Return location for error or %NULL.
469 *
470 * This method brings up the user interface necessary for refreshing
471 * the credentials for the account specified by @object. This
472 * typically involves having the user log in to the account again.
473 *
474 * Implementations should use @parent (unless %NULL) as the transient
475 * parent of any created windows/dialogs.
476 *
477 * Implementations should run the <link
478 * linkend="g_main_context_default">default main loop</link> while
479 * interacting with the user.
480 *
481 * This is a pure virtual method - a subclass must provide an
482 * implementation.
483 *
484 * Returns: %TRUE if the account has been refreshed, %FALSE if @error
485 * is set.
486 */
487 gboolean
goa_provider_refresh_account(GoaProvider * self,GoaClient * client,GoaObject * object,GtkWindow * parent,GError ** error)488 goa_provider_refresh_account (GoaProvider *self,
489 GoaClient *client,
490 GoaObject *object,
491 GtkWindow *parent,
492 GError **error)
493 {
494 g_return_val_if_fail (GOA_IS_PROVIDER (self), FALSE);
495 g_return_val_if_fail (GOA_IS_CLIENT (client), FALSE);
496 g_return_val_if_fail (GOA_IS_OBJECT (object) && goa_object_peek_account (object) != NULL, FALSE);
497 g_return_val_if_fail (parent == NULL || GTK_IS_WINDOW (parent), FALSE);
498 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
499
500 return GOA_PROVIDER_GET_CLASS (self)->refresh_account (self, client, object, parent, error);
501 }
502
503 /* ---------------------------------------------------------------------------------------------------- */
504
505 /**
506 * goa_provider_show_account:
507 * @self: A #GoaProvider.
508 * @client: A #GoaClient.
509 * @object: A #GoaObject with a #GoaAccount interface.
510 * @vbox: A vertically oriented #GtkBox to put content in.
511 * @grid: A #GtkGrid to put content in.
512 * @dummy: Unused.
513 *
514 * Method used to add widgets in the control panel for the account
515 * represented by @object.
516 *
517 * This is a virtual method where the default implementation adds
518 * one GtkSwitch per service supported by the provider (as reported
519 * by goa_provider_get_provider_features()).
520 */
521 void
goa_provider_show_account(GoaProvider * self,GoaClient * client,GoaObject * object,GtkBox * vbox,GtkGrid * dummy1,GtkGrid * dummy2)522 goa_provider_show_account (GoaProvider *self,
523 GoaClient *client,
524 GoaObject *object,
525 GtkBox *vbox,
526 GtkGrid *dummy1,
527 GtkGrid *dummy2)
528 {
529 g_return_if_fail (GOA_IS_PROVIDER (self));
530 g_return_if_fail (GOA_IS_CLIENT (client));
531 g_return_if_fail (GOA_IS_OBJECT (object) && goa_object_peek_account (object) != NULL);
532 g_return_if_fail (GTK_IS_BOX (vbox));
533
534 GOA_PROVIDER_GET_CLASS (self)->show_account (self, client, object, vbox, dummy1, dummy2);
535 }
536
537 static void
goa_provider_show_account_real(GoaProvider * provider,GoaClient * client,GoaObject * object,GtkBox * vbox,GtkGrid * dummy1,GtkGrid * dummy2)538 goa_provider_show_account_real (GoaProvider *provider,
539 GoaClient *client,
540 GoaObject *object,
541 GtkBox *vbox,
542 GtkGrid *dummy1,
543 GtkGrid *dummy2)
544 {
545 GoaProviderFeatures features;
546 GtkWidget *grid;
547 gint row;
548 guint i;
549 const char *label;
550
551 row = 0;
552
553 goa_utils_account_add_attention_needed (client, object, provider, vbox);
554
555 grid = gtk_grid_new ();
556 gtk_widget_set_halign (grid, GTK_ALIGN_CENTER);
557 gtk_widget_set_hexpand (grid, TRUE);
558 gtk_widget_set_margin_end (grid, 72);
559 gtk_widget_set_margin_start (grid, 72);
560 gtk_widget_set_margin_top (grid, 24);
561 gtk_grid_set_column_spacing (GTK_GRID (grid), 12);
562 gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
563 gtk_container_add (GTK_CONTAINER (vbox), grid);
564
565 goa_utils_account_add_header (object, GTK_GRID (grid), row++);
566
567 features = goa_provider_get_provider_features (provider);
568 /* Translators: This is a label for a series of
569 * options switches. For example: “Use for Mail”. */
570 label = _("Use for");
571
572 for (i = 0; provider_features_info[i].property != NULL; i++)
573 {
574 if ((features & provider_features_info[i].feature) != 0)
575 {
576 goa_util_add_row_switch_from_keyfile_with_blurb (GTK_GRID (grid), row++, object,
577 label,
578 provider_features_info[i].property,
579 _(provider_features_info[i].blurb));
580 label = NULL;
581 }
582 }
583 }
584
585 /* ---------------------------------------------------------------------------------------------------- */
586
587 /**
588 * goa_provider_build_object:
589 * @self: A #GoaProvider.
590 * @object: The #GoaObjectSkeleton that is being built.
591 * @key_file: The #GKeyFile with configuation data.
592 * @group: The group in @key_file to get data from.
593 * @connection: The #GDBusConnection used by the daemon to connect to the message bus.
594 * @just_added: Whether the account was newly created or being updated.
595 * @error: Return location for error or %NULL.
596 *
597 * This method is called when construction account #GoaObject<!-- -->
598 * from configuration data - it basically provides a way to add
599 * provider-specific information.
600 *
601 * The passed in @object will have a #GoaAccount interface
602 * set. Implementations should validate and use data from @key_file to
603 * add more interfaces to @object.
604 *
605 * Note that this may be called on already exported objects - for
606 * example on configuration files reload.
607 *
608 * This is a pure virtual method - a subclass must provide an
609 * implementation.
610 *
611 * Returns: %TRUE if data was valid, %FALSE if @error is set.
612 */
613 gboolean
goa_provider_build_object(GoaProvider * self,GoaObjectSkeleton * object,GKeyFile * key_file,const gchar * group,GDBusConnection * connection,gboolean just_added,GError ** error)614 goa_provider_build_object (GoaProvider *self,
615 GoaObjectSkeleton *object,
616 GKeyFile *key_file,
617 const gchar *group,
618 GDBusConnection *connection,
619 gboolean just_added,
620 GError **error)
621 {
622 g_return_val_if_fail (GOA_IS_PROVIDER (self), FALSE);
623 g_return_val_if_fail (GOA_IS_OBJECT_SKELETON (object) && goa_object_peek_account (GOA_OBJECT (object)) != NULL, FALSE);
624 g_return_val_if_fail (key_file != NULL, FALSE);
625 g_return_val_if_fail (group != NULL, FALSE);
626 g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE);
627 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
628 return GOA_PROVIDER_GET_CLASS (self)->build_object (self,
629 object,
630 key_file,
631 group,
632 connection,
633 just_added,
634 error);
635 }
636
637 /* ---------------------------------------------------------------------------------------------------- */
638
639 typedef struct
640 {
641 GoaObject *object;
642 gint expires_in;
643 } EnsureCredentialsData;
644
645 static EnsureCredentialsData *
ensure_credentials_data_new(GoaObject * object)646 ensure_credentials_data_new (GoaObject *object)
647 {
648 EnsureCredentialsData *data;
649 data = g_new0 (EnsureCredentialsData, 1);
650 data->object = g_object_ref (object);
651 return data;
652 }
653
654 static void
ensure_credentials_data_free(EnsureCredentialsData * data)655 ensure_credentials_data_free (EnsureCredentialsData *data)
656 {
657 g_object_unref (data->object);
658 g_free (data);
659 }
660
661 static void
ensure_credentials_in_thread_func(GTask * task,gpointer object,gpointer task_data,GCancellable * cancellable)662 ensure_credentials_in_thread_func (GTask *task,
663 gpointer object,
664 gpointer task_data,
665 GCancellable *cancellable)
666 {
667 EnsureCredentialsData *data;
668 GError *error;
669
670 data = task_data;
671
672 error = NULL;
673 if (!goa_provider_ensure_credentials_sync (GOA_PROVIDER (object),
674 data->object,
675 &data->expires_in,
676 cancellable,
677 &error))
678 g_task_return_error (task, error);
679 else
680 g_task_return_boolean (task, TRUE);
681 }
682
683
684 /**
685 * goa_provider_ensure_credentials:
686 * @self: A #GoaProvider.
687 * @object: A #GoaObject with a #GoaAccount interface.
688 * @cancellable: (allow-none): A #GCancellable or %NULL.
689 * @callback: The function to call when the request is satisfied.
690 * @user_data: Pointer to pass to @callback.
691 *
692 * Ensures that credentials for @object are still valid.
693 *
694 * When the result is ready, @callback will be called in the the <link
695 * linkend="g-main-context-push-thread-default">thread-default main
696 * loop</link> this function was called from. You can then call
697 * goa_provider_ensure_credentials_finish() to get the result
698 * of the operation.
699 *
700 * This is a virtual method where the default implementation simply
701 * throws the %GOA_ERROR_NOT_SUPPORTED error. A subclass may provide
702 * another implementation.
703 */
704 void
goa_provider_ensure_credentials(GoaProvider * self,GoaObject * object,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)705 goa_provider_ensure_credentials (GoaProvider *self,
706 GoaObject *object,
707 GCancellable *cancellable,
708 GAsyncReadyCallback callback,
709 gpointer user_data)
710 {
711 GTask *task;
712
713 g_return_if_fail (GOA_IS_PROVIDER (self));
714 g_return_if_fail (GOA_IS_OBJECT (object));
715 g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
716
717 task = g_task_new (self, cancellable, callback, user_data);
718 g_task_set_task_data (task, ensure_credentials_data_new (object), (GDestroyNotify) ensure_credentials_data_free);
719 g_task_set_source_tag (task, goa_provider_ensure_credentials);
720 g_task_run_in_thread (task, ensure_credentials_in_thread_func);
721
722 g_object_unref (task);
723 }
724
725 /**
726 * goa_provider_ensure_credentials_finish:
727 * @self: A #GoaProvider.
728 * @out_expires_in: (out): Return location for how long the expired credentials are good for (0 if unknown) or %NULL.
729 * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback passed to goa_provider_ensure_credentials().
730 * @error: Return location for error or %NULL.
731 *
732 * Finishes an operation started with goa_provider_ensure_credentials().
733 *
734 * Returns: %TRUE if the credentials for the passed #GoaObject are valid, %FALSE if @error is set.
735 */
736 gboolean
goa_provider_ensure_credentials_finish(GoaProvider * self,gint * out_expires_in,GAsyncResult * res,GError ** error)737 goa_provider_ensure_credentials_finish (GoaProvider *self,
738 gint *out_expires_in,
739 GAsyncResult *res,
740 GError **error)
741 {
742 GTask *task;
743 gboolean had_error;
744
745 gboolean ret;
746 EnsureCredentialsData *data;
747
748 ret = FALSE;
749
750 g_return_val_if_fail (GOA_IS_PROVIDER (self), FALSE);
751 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
752
753 g_return_val_if_fail (g_task_is_valid (res, self), FALSE);
754 task = G_TASK (res);
755
756 g_return_val_if_fail (g_task_get_source_tag (task) == goa_provider_ensure_credentials, FALSE);
757
758 /* Workaround for bgo#764163 */
759 had_error = g_task_had_error (task);
760 ret = g_task_propagate_boolean (task, error);
761 if (had_error)
762 goto out;
763
764 data = g_task_get_task_data (task);
765 if (out_expires_in != NULL)
766 *out_expires_in = data->expires_in;
767
768 out:
769 return ret;
770 }
771
772 /* ---------------------------------------------------------------------------------------------------- */
773
774 /**
775 * goa_provider_ensure_credentials_sync:
776 * @self: A #GoaProvider.
777 * @object: A #GoaObject with a #GoaAccount interface.
778 * @out_expires_in: (out): Return location for how long the expired credentials are good for (0 if unknown) or %NULL.
779 * @cancellable: (allow-none): A #GCancellable or %NULL.
780 * @error: Return location for error or %NULL.
781 *
782 * Like goa_provider_ensure_credentials() but blocks the
783 * calling thread until an answer is received.
784 *
785 * Returns: %TRUE if the credentials for the passed #GoaObject are valid, %FALSE if @error is set.
786 */
787 gboolean
goa_provider_ensure_credentials_sync(GoaProvider * self,GoaObject * object,gint * out_expires_in,GCancellable * cancellable,GError ** error)788 goa_provider_ensure_credentials_sync (GoaProvider *self,
789 GoaObject *object,
790 gint *out_expires_in,
791 GCancellable *cancellable,
792 GError **error)
793 {
794 GoaAccount *account = NULL;
795 GoaProviderFeatures features;
796 gboolean disabled = TRUE;
797 gboolean ret = FALSE;
798 guint i;
799
800 g_return_val_if_fail (GOA_IS_PROVIDER (self), FALSE);
801 g_return_val_if_fail (GOA_IS_OBJECT (object), FALSE);
802 g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
803 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
804
805 account = goa_object_get_account (object);
806 g_return_val_if_fail (GOA_IS_ACCOUNT (account), FALSE);
807
808 features = goa_provider_get_provider_features (self);
809
810 for (i = 0; provider_features_info[i].property != NULL; i++)
811 {
812 if ((features & provider_features_info[i].feature) != 0)
813 {
814 gboolean feature_disabled;
815
816 g_object_get (account, provider_features_info[i].property, &feature_disabled, NULL);
817 disabled = disabled && feature_disabled;
818 if (!disabled)
819 break;
820 }
821 }
822
823 if (disabled)
824 {
825 g_set_error_literal (error, GOA_ERROR, GOA_ERROR_NOT_SUPPORTED, _("Account is disabled"));
826 goto out;
827 }
828
829 ret = GOA_PROVIDER_GET_CLASS (self)->ensure_credentials_sync (self, object, out_expires_in, cancellable, error);
830
831 out:
832 if (!ret && error != NULL && *error == NULL)
833 {
834 const gchar *provider_type;
835
836 provider_type = goa_provider_get_provider_type (self);
837 g_critical ("GoaProvider (%s) failed to set error correctly", provider_type);
838 g_set_error_literal (error, GOA_ERROR, GOA_ERROR_NOT_AUTHORIZED, _("Unknown error"));
839 }
840
841 g_clear_object (&account);
842 return ret;
843 }
844
845 /* ---------------------------------------------------------------------------------------------------- */
846
847 static gboolean
goa_provider_ensure_credentials_sync_real(GoaProvider * self,GoaObject * object,gint * out_expires_in,GCancellable * cancellable,GError ** error)848 goa_provider_ensure_credentials_sync_real (GoaProvider *self,
849 GoaObject *object,
850 gint *out_expires_in,
851 GCancellable *cancellable,
852 GError **error)
853 {
854 g_set_error (error,
855 GOA_ERROR,
856 GOA_ERROR_NOT_SUPPORTED,
857 _("ensure_credentials_sync is not implemented on type %s"),
858 g_type_name (G_TYPE_FROM_INSTANCE (self)));
859 return FALSE;
860 }
861
862 static gboolean
goa_provider_build_object_real(GoaProvider * self,GoaObjectSkeleton * object,GKeyFile * key_file,const gchar * group,GDBusConnection * connection,gboolean just_added,GError ** error)863 goa_provider_build_object_real (GoaProvider *self,
864 GoaObjectSkeleton *object,
865 GKeyFile *key_file,
866 const gchar *group,
867 GDBusConnection *connection,
868 gboolean just_added,
869 GError **error)
870 {
871 /* does nothing */
872 return TRUE;
873 }
874
875 /* ---------------------------------------------------------------------------------------------------- */
876
877 /**
878 * goa_provider_get_credentials_generation:
879 * @self: A #GoaProvider.
880 *
881 * Gets the generation of credentials being used for the provider.
882 *
883 * Implementations should bump this number when changes are introduced
884 * that may render existing credentials unusable.
885 *
886 * For example, if an additional scope is requested (e.g. access to
887 * contacts data) while obtaining credentials, then this number needs
888 * to be bumped since existing credentials are not good for the added
889 * scope.
890 *
891 * This is a virtual method where the default implementation returns
892 * 0.
893 *
894 * Returns: The current generation of credentials.
895 */
896 guint
goa_provider_get_credentials_generation(GoaProvider * self)897 goa_provider_get_credentials_generation (GoaProvider *self)
898 {
899 g_return_val_if_fail (GOA_IS_PROVIDER (self), 0);
900 return GOA_PROVIDER_GET_CLASS (self)->get_credentials_generation (self);
901 }
902
903 static guint
goa_provider_get_credentials_generation_real(GoaProvider * self)904 goa_provider_get_credentials_generation_real (GoaProvider *self)
905 {
906 return 0;
907 }
908
909 /* ---------------------------------------------------------------------------------------------------- */
910
911 void
goa_provider_ensure_extension_points_registered(void)912 goa_provider_ensure_extension_points_registered (void)
913 {
914 static gsize once_init_value = 0;
915
916 if (g_once_init_enter (&once_init_value))
917 {
918 GIOExtensionPoint *extension_point;
919
920 extension_point = g_io_extension_point_register (GOA_PROVIDER_EXTENSION_POINT_NAME);
921 g_io_extension_point_set_required_type (extension_point, GOA_TYPE_PROVIDER);
922
923 g_once_init_leave (&once_init_value, 1);
924 }
925 }
926
927 /* ---------------------------------------------------------------------------------------------------- */
928
929 static struct
930 {
931 const gchar *name;
932 GType (*get_type) (void);
933 } ordered_builtins_map[] = {
934 /* The order in which the providers' types are created is
935 * important because it affects the order in which they are
936 * returned by goa_provider_get_all.
937 */
938 #ifdef GOA_GOOGLE_ENABLED
939 { GOA_GOOGLE_NAME, goa_google_provider_get_type },
940 #endif
941 #ifdef GOA_OWNCLOUD_ENABLED
942 { GOA_OWNCLOUD_NAME, goa_owncloud_provider_get_type },
943 #endif
944 #ifdef GOA_FACEBOOK_ENABLED
945 { GOA_FACEBOOK_NAME, goa_facebook_provider_get_type },
946 #endif
947 #ifdef GOA_WINDOWS_LIVE_ENABLED
948 { GOA_WINDOWS_LIVE_NAME, goa_windows_live_provider_get_type },
949 #endif
950 #ifdef GOA_FLICKR_ENABLED
951 { GOA_FLICKR_NAME, goa_flickr_provider_get_type },
952 #endif
953 #ifdef GOA_FOURSQUARE_ENABLED
954 { GOA_FOURSQUARE_NAME, goa_foursquare_provider_get_type },
955 #endif
956 #ifdef GOA_EXCHANGE_ENABLED
957 { GOA_EXCHANGE_NAME, goa_exchange_provider_get_type },
958 #endif
959 #ifdef GOA_LASTFM_ENABLED
960 { GOA_LASTFM_NAME, goa_lastfm_provider_get_type },
961 #endif
962 #ifdef GOA_FEDORA_ENABLED
963 { GOA_FEDORA_NAME, goa_fedora_provider_get_type },
964 #endif
965 #ifdef GOA_IMAP_SMTP_ENABLED
966 { GOA_IMAP_SMTP_NAME, goa_imap_smtp_provider_get_type },
967 #endif
968 #ifdef GOA_KERBEROS_ENABLED
969 { GOA_KERBEROS_NAME, goa_kerberos_provider_get_type },
970 #endif
971 #ifdef GOA_MEDIA_SERVER_ENABLED
972 { GOA_MEDIA_SERVER_NAME, goa_media_server_provider_get_type },
973 #endif
974 { NULL, NULL }
975 };
976
977 void
goa_provider_ensure_builtins_loaded(void)978 goa_provider_ensure_builtins_loaded (void)
979 {
980 static gsize once_init_value = 0;
981
982 goa_provider_ensure_extension_points_registered ();
983
984 if (g_once_init_enter (&once_init_value))
985 {
986 GSettings *settings;
987 gchar **whitelisted_providers;
988 guint i;
989 guint j;
990 gboolean all = FALSE;
991
992 settings = g_settings_new (GOA_SETTINGS_SCHEMA);
993 whitelisted_providers = g_settings_get_strv (settings, GOA_SETTINGS_WHITELISTED_PROVIDERS);
994
995 /* Enable everything if there is 'all'. */
996 for (i = 0; whitelisted_providers[i] != NULL; i++)
997 {
998 if (g_strcmp0 (whitelisted_providers[i], "all") == 0)
999 {
1000 g_debug ("Loading all providers: ");
1001 for (j = 0; ordered_builtins_map[j].name != NULL; j++)
1002 {
1003 g_debug (" - %s", ordered_builtins_map[j].name);
1004 g_type_ensure ((*ordered_builtins_map[j].get_type) ());
1005 }
1006
1007 all = TRUE;
1008 break;
1009 }
1010 }
1011
1012 if (all)
1013 goto cleanup;
1014
1015 /* Otherwise try them one by one. */
1016 g_debug ("Loading whitelisted providers: ");
1017 for (i = 0; ordered_builtins_map[i].name != NULL; i++)
1018 {
1019 for (j = 0; whitelisted_providers[j] != NULL; j++)
1020 {
1021 if (g_strcmp0 (whitelisted_providers[j], ordered_builtins_map[i].name) == 0)
1022 {
1023 g_debug (" - %s", ordered_builtins_map[j].name);
1024 g_type_ensure ((*ordered_builtins_map[i].get_type) ());
1025 break;
1026 }
1027 }
1028 }
1029
1030 cleanup:
1031 g_strfreev (whitelisted_providers);
1032 g_object_unref (settings);
1033 g_once_init_leave (&once_init_value, 1);
1034 }
1035 }
1036
1037 /* ---------------------------------------------------------------------------------------------------- */
1038
1039 /**
1040 * goa_provider_get_for_provider_type:
1041 * @provider_type: A provider type.
1042 *
1043 * Looks up the %GOA_PROVIDER_EXTENSION_POINT_NAME extension
1044 * point and returns a newly created #GoaProvider for
1045 * @provider_type, if any.
1046 *
1047 * Returns: (transfer full): A #GoaProvider (that must be freed
1048 * with g_object_unref()) or %NULL if not found.
1049 */
1050 GoaProvider *
goa_provider_get_for_provider_type(const gchar * provider_type)1051 goa_provider_get_for_provider_type (const gchar *provider_type)
1052 {
1053 GIOExtension *extension;
1054 GIOExtensionPoint *extension_point;
1055 GoaProvider *ret;
1056
1057 g_return_val_if_fail (provider_type != NULL, NULL);
1058
1059 goa_provider_ensure_builtins_loaded ();
1060
1061 ret = NULL;
1062
1063 extension_point = g_io_extension_point_lookup (GOA_PROVIDER_EXTENSION_POINT_NAME);
1064 extension = g_io_extension_point_get_extension_by_name (extension_point, provider_type);
1065 if (extension != NULL)
1066 ret = GOA_PROVIDER (g_object_new (g_io_extension_get_type (extension), NULL));
1067 return ret;
1068 }
1069
1070 /* ---------------------------------------------------------------------------------------------------- */
1071
1072 static void
free_list_and_unref(gpointer data)1073 free_list_and_unref (gpointer data)
1074 {
1075 g_list_free_full (data, g_object_unref);
1076 }
1077
1078 /**
1079 * goa_provider_get_all:
1080 * @callback: The function to call when the request is satisfied.
1081 * @user_data: Pointer to pass to @callback.
1082 *
1083 * Creates a list of all the available #GoaProvider instances.
1084 *
1085 * When the result is ready, @callback will be called in the the <link
1086 * linkend="g-main-context-push-thread-default">thread-default main
1087 * loop</link> this function was called from. You can then call
1088 * goa_provider_get_all_finish() to get the result of the operation.
1089 *
1090 * See goa_provider_get_for_provider_type() for details on how the providers
1091 * are found.
1092 */
1093 void
goa_provider_get_all(GAsyncReadyCallback callback,gpointer user_data)1094 goa_provider_get_all (GAsyncReadyCallback callback,
1095 gpointer user_data)
1096 {
1097 GList *extensions;
1098 GList *providers = NULL;
1099 GList *l;
1100 GIOExtensionPoint *extension_point;
1101 GTask *task = NULL;
1102
1103 goa_provider_ensure_builtins_loaded ();
1104
1105 task = g_task_new (NULL, NULL, callback, user_data);
1106 g_task_set_source_tag (task, goa_provider_get_all);
1107
1108 extension_point = g_io_extension_point_lookup (GOA_PROVIDER_EXTENSION_POINT_NAME);
1109 extensions = g_io_extension_point_get_extensions (extension_point);
1110 /* TODO: what if there are two extensions with the same name? */
1111 for (l = extensions; l != NULL; l = l->next)
1112 {
1113 GIOExtension *extension = l->data;
1114 /* The extensions are loaded in the reverse order we used in
1115 * goa_provider_ensure_builtins_loaded, so we need to push
1116 * extension if front of the already loaded ones. */
1117 providers = g_list_prepend (providers, g_object_new (g_io_extension_get_type (extension), NULL));
1118 }
1119
1120 g_task_return_pointer (task, g_steal_pointer (&providers), free_list_and_unref);
1121
1122 g_list_free_full (providers, g_object_unref);
1123 }
1124
1125 /**
1126 * goa_provider_get_all_finish:
1127 * @out_providers: (out) (transfer full) (element-type GoaProvider):
1128 * Return location for a list of #GoaProvider instances.
1129 * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback passed to goa_provider_get_all().
1130 * @error: Return location for error or %NULL.
1131 *
1132 * Finishes an operation started with goa_provider_get_all().
1133 *
1134 * Returns: %TRUE if the list was successfully retrieved, %FALSE if @error is set.
1135 */
1136 gboolean
goa_provider_get_all_finish(GList ** out_providers,GAsyncResult * result,GError ** error)1137 goa_provider_get_all_finish (GList **out_providers,
1138 GAsyncResult *result,
1139 GError **error)
1140 {
1141 GTask *task;
1142 GList *providers;
1143 gboolean had_error;
1144
1145 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1146
1147 g_return_val_if_fail (g_task_is_valid (result, NULL), FALSE);
1148 task = G_TASK (result);
1149
1150 g_return_val_if_fail (g_task_get_source_tag (task) == goa_provider_get_all, FALSE);
1151
1152 /* Workaround for bgo#764163 */
1153 had_error = g_task_had_error (task);
1154 providers = g_task_propagate_pointer (task, error);
1155 if (had_error)
1156 return FALSE;
1157
1158 if (out_providers != NULL)
1159 {
1160 *out_providers = providers;
1161 providers = NULL;
1162 }
1163
1164 g_list_free_full (providers, g_object_unref);
1165 return TRUE;
1166 }
1167
1168 /* ---------------------------------------------------------------------------------------------------- */
1169
1170 void
goa_provider_remove_account(GoaProvider * self,GoaObject * object,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1171 goa_provider_remove_account (GoaProvider *self,
1172 GoaObject *object,
1173 GCancellable *cancellable,
1174 GAsyncReadyCallback callback,
1175 gpointer user_data)
1176 {
1177 g_return_if_fail (GOA_IS_PROVIDER (self));
1178 g_return_if_fail (GOA_IS_OBJECT (object));
1179 g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
1180
1181 GOA_PROVIDER_GET_CLASS (self)->remove_account (self, object, cancellable, callback, user_data);
1182 }
1183
1184 gboolean
goa_provider_remove_account_finish(GoaProvider * self,GAsyncResult * res,GError ** error)1185 goa_provider_remove_account_finish (GoaProvider *self,
1186 GAsyncResult *res,
1187 GError **error)
1188 {
1189 g_return_val_if_fail (GOA_IS_PROVIDER (self), FALSE);
1190 g_return_val_if_fail (G_IS_ASYNC_RESULT (res), FALSE);
1191 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1192
1193 return GOA_PROVIDER_GET_CLASS (self)->remove_account_finish (self, res, error);
1194 }
1195
1196 /* ---------------------------------------------------------------------------------------------------- */
1197
1198 static void
goa_provider_remove_account_real(GoaProvider * self,GoaObject * object,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1199 goa_provider_remove_account_real (GoaProvider *self,
1200 GoaObject *object,
1201 GCancellable *cancellable,
1202 GAsyncReadyCallback callback,
1203 gpointer user_data)
1204 {
1205 GTask *task;
1206
1207 task = g_task_new (self, cancellable, callback, user_data);
1208 g_task_set_source_tag (task, goa_provider_remove_account_real);
1209 g_task_return_boolean (task, TRUE);
1210 g_object_unref (task);
1211 }
1212
1213 static gboolean
goa_provider_remove_account_finish_real(GoaProvider * self,GAsyncResult * res,GError ** error)1214 goa_provider_remove_account_finish_real (GoaProvider *self,
1215 GAsyncResult *res,
1216 GError **error)
1217 {
1218 GTask *task;
1219
1220 g_return_val_if_fail (g_task_is_valid (res, self), FALSE);
1221 task = G_TASK (res);
1222
1223 g_return_val_if_fail (g_task_get_source_tag (task) == goa_provider_remove_account_real, FALSE);
1224
1225 return g_task_propagate_boolean (task, error);
1226 }
1227
1228 /* ---------------------------------------------------------------------------------------------------- */
1229
1230 /**
1231 * goa_provider_set_preseed_data:
1232 * @self: The #GoaProvider
1233 * @preseed_data: A #GVariant of type a{sv}
1234 *
1235 * Sets the #GoaProvider:preseed-data property to feed any information already
1236 * collected that can be useful when creating a new account.
1237 *
1238 * If the @preseed_data #GVariant is floating, it is consumed to allow
1239 * 'inline' use of the g_variant_new() family of functions.
1240 *
1241 * Deprecated: 3.28: This function does nothing.
1242 */
1243 void
goa_provider_set_preseed_data(GoaProvider * self,GVariant * preseed_data)1244 goa_provider_set_preseed_data (GoaProvider *self,
1245 GVariant *preseed_data)
1246 {
1247 }
1248
1249 /**
1250 * goa_provider_get_preseed_data:
1251 * @self: The #GoaProvider
1252 *
1253 * Gets the #GVariant set through the #GoaProvider:preseed-data property.
1254 *
1255 * Returns: (transfer none): A #GVariant that is known to be valid until
1256 * the property is overridden or the provider freed.
1257 *
1258 * Deprecated: 3.28: This function does nothing.
1259 */
1260 GVariant *
goa_provider_get_preseed_data(GoaProvider * self)1261 goa_provider_get_preseed_data (GoaProvider *self)
1262 {
1263 return NULL;
1264 }
1265
1266 /* ---------------------------------------------------------------------------------------------------- */
1267
1268 GtkWidget *
goa_util_add_row_widget(GtkGrid * grid,gint row,const gchar * label_text,GtkWidget * widget)1269 goa_util_add_row_widget (GtkGrid *grid,
1270 gint row,
1271 const gchar *label_text,
1272 GtkWidget *widget)
1273 {
1274 GtkWidget *label;
1275
1276 g_return_val_if_fail (GTK_IS_GRID (grid), NULL);
1277 g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
1278
1279 if (label_text != NULL)
1280 {
1281 GtkStyleContext *context;
1282
1283 label = gtk_label_new (label_text);
1284 context = gtk_widget_get_style_context (label);
1285 gtk_style_context_add_class (context, GTK_STYLE_CLASS_DIM_LABEL);
1286 gtk_widget_set_halign (label, GTK_ALIGN_END);
1287 gtk_widget_set_hexpand (label, TRUE);
1288 gtk_grid_attach (grid, label, 0, row, 1, 1);
1289 }
1290
1291 gtk_grid_attach (grid, widget, 1, row, 3, 1);
1292 return widget;
1293 }
1294
1295 /* ---------------------------------------------------------------------------------------------------- */
1296
1297 gchar *
goa_util_lookup_keyfile_string(GoaObject * object,const gchar * key)1298 goa_util_lookup_keyfile_string (GoaObject *object,
1299 const gchar *key)
1300 {
1301 GoaAccount *account;
1302 GError *error;
1303 GKeyFile *key_file;
1304 gchar *path;
1305 gchar *group;
1306 gchar *ret;
1307
1308 ret = NULL;
1309
1310 account = goa_object_peek_account (object);
1311 path = g_strdup_printf ("%s/goa-1.0/accounts.conf", g_get_user_config_dir ());
1312 group = g_strdup_printf ("Account %s", goa_account_get_id (account));
1313
1314 key_file = g_key_file_new ();
1315 error = NULL;
1316 if (!g_key_file_load_from_file (key_file,
1317 path,
1318 G_KEY_FILE_NONE,
1319 &error))
1320 {
1321 g_warning ("Error loading keyfile %s: %s (%s, %d)",
1322 path,
1323 error->message, g_quark_to_string (error->domain), error->code);
1324 g_error_free (error);
1325 goto out;
1326 }
1327 ret = g_key_file_get_string (key_file,
1328 group,
1329 key,
1330 &error);
1331 if (ret == NULL)
1332 {
1333 /* this is not fatal (think upgrade-path) */
1334 g_debug ("Error getting value for key %s in group `%s' from keyfile %s: %s (%s, %d)",
1335 key,
1336 group,
1337 path,
1338 error->message, g_quark_to_string (error->domain), error->code);
1339 g_error_free (error);
1340 goto out;
1341 }
1342
1343 out:
1344 g_key_file_unref (key_file);
1345 g_free (group);
1346 g_free (path);
1347 return ret;
1348 }
1349
1350 gboolean
goa_util_lookup_keyfile_boolean(GoaObject * object,const gchar * key)1351 goa_util_lookup_keyfile_boolean (GoaObject *object,
1352 const gchar *key)
1353 {
1354 GoaAccount *account;
1355 GError *error;
1356 GKeyFile *key_file;
1357 gchar *path;
1358 gchar *group;
1359 gboolean ret;
1360
1361 ret = FALSE;
1362
1363 account = goa_object_peek_account (object);
1364 path = g_strdup_printf ("%s/goa-1.0/accounts.conf", g_get_user_config_dir ());
1365 group = g_strdup_printf ("Account %s", goa_account_get_id (account));
1366
1367 key_file = g_key_file_new ();
1368 error = NULL;
1369 if (!g_key_file_load_from_file (key_file,
1370 path,
1371 G_KEY_FILE_NONE,
1372 &error))
1373 {
1374 g_warning ("Error loading keyfile %s: %s (%s, %d)",
1375 path,
1376 error->message, g_quark_to_string (error->domain), error->code);
1377 g_error_free (error);
1378 goto out;
1379 }
1380 ret = g_key_file_get_boolean (key_file,
1381 group,
1382 key,
1383 &error);
1384 if (error != NULL)
1385 {
1386 /* this is not fatal (think upgrade-path) */
1387 g_debug ("Error getting boolean value for key %s in group `%s' from keyfile %s: %s (%s, %d)",
1388 key,
1389 group,
1390 path,
1391 error->message, g_quark_to_string (error->domain), error->code);
1392 g_error_free (error);
1393 goto out;
1394 }
1395
1396 out:
1397 g_key_file_unref (key_file);
1398 g_free (group);
1399 g_free (path);
1400 return ret;
1401 }
1402
1403 /* ---------------------------------------------------------------------------------------------------- */
1404
1405 void
goa_util_account_notify_property_cb(GObject * object,GParamSpec * pspec,gpointer user_data)1406 goa_util_account_notify_property_cb (GObject *object, GParamSpec *pspec, gpointer user_data)
1407 {
1408 GoaAccount *account;
1409 gboolean value;
1410 const gchar *key;
1411 const gchar *name;
1412
1413 g_return_if_fail (GOA_IS_ACCOUNT (object));
1414
1415 account = GOA_ACCOUNT (object);
1416 key = user_data;
1417 name = g_param_spec_get_name (pspec);
1418
1419 g_object_get (account, name, &value, NULL);
1420 goa_utils_keyfile_set_boolean (account, key, !value);
1421 }
1422
1423 /* ---------------------------------------------------------------------------------------------------- */
1424
1425 GtkWidget *
goa_util_add_row_switch_from_keyfile_with_blurb(GtkGrid * grid,gint row,GoaObject * object,const gchar * label_text,const gchar * property,const gchar * blurb)1426 goa_util_add_row_switch_from_keyfile_with_blurb (GtkGrid *grid,
1427 gint row,
1428 GoaObject *object,
1429 const gchar *label_text,
1430 const gchar *property,
1431 const gchar *blurb)
1432 {
1433 GoaAccount *account;
1434 GtkWidget *hbox;
1435 GtkWidget *switch_;
1436 gboolean value;
1437
1438 account = goa_object_peek_account (object);
1439 g_object_get (account, property, &value, NULL);
1440 switch_ = gtk_switch_new ();
1441
1442 if (goa_account_get_attention_needed (account))
1443 {
1444 gtk_widget_set_sensitive (switch_, FALSE);
1445 gtk_switch_set_active (GTK_SWITCH (switch_), FALSE);
1446 }
1447 else
1448 {
1449 gtk_switch_set_active (GTK_SWITCH (switch_), !value);
1450 g_object_bind_property (switch_, "active",
1451 account, property,
1452 G_BINDING_BIDIRECTIONAL | G_BINDING_INVERT_BOOLEAN);
1453 }
1454
1455 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
1456 gtk_box_set_homogeneous (GTK_BOX (hbox), FALSE);
1457
1458 if (blurb != NULL)
1459 {
1460 GtkWidget *label;
1461
1462 label = gtk_label_new_with_mnemonic (blurb);
1463 gtk_label_set_mnemonic_widget (GTK_LABEL (label), switch_);
1464 gtk_label_set_width_chars (GTK_LABEL (label), 18);
1465 gtk_label_set_xalign (GTK_LABEL (label), 0.0);
1466 gtk_container_add (GTK_CONTAINER (hbox), label);
1467 }
1468
1469 gtk_container_add (GTK_CONTAINER (hbox), switch_);
1470 goa_util_add_row_widget (grid, row, label_text, hbox);
1471 return switch_;
1472 }
1473