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