GCC Code Coverage Report


Directory: ./
File: panels/online-accounts/cc-online-accounts-panel.c
Date: 2024-05-04 07:58:27
Exec Total Coverage
Lines: 0 236 0.0%
Functions: 0 29 0.0%
Branches: 0 111 0.0%

Line Branch Exec Source
1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 /*
3 * Copyright (C) 2011 - 2017 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 * Author: David Zeuthen <davidz@redhat.com>
19 */
20
21 #include "config.h"
22
23 #include <gio/gio.h>
24 #include <string.h>
25 #include <glib/gi18n-lib.h>
26
27 #define GOA_API_IS_SUBJECT_TO_CHANGE
28 #include <goa/goa.h>
29
30 #include "cc-online-accounts-panel.h"
31 #include "cc-online-account-provider-row.h"
32 #include "cc-online-account-row.h"
33 #include "cc-online-accounts-resources.h"
34
35 #define GOA_API_IS_SUBJECT_TO_CHANGE
36 #define GOA_BACKEND_API_IS_SUBJECT_TO_CHANGE
37 #include <goabackend/goabackend.h>
38
39 struct _CcOnlineAccountsPanel
40 {
41 CcPanel parent_instance;
42
43 GtkFrame *accounts_frame;
44 GtkListBox *accounts_listbox;
45 AdwBanner *offline_banner;
46 GtkListBox *providers_listbox;
47
48 GoaClient *client;
49 GVariant *parameters;
50 GListStore *providers;
51 };
52
53 CC_PANEL_REGISTER (CcOnlineAccountsPanel, cc_online_accounts_panel);
54
55 enum {
56 PROP_0,
57 PROP_PARAMETERS
58 };
59
60 /* Rows methods */
61
62 typedef void (*RowForAccountCallback) (CcOnlineAccountsPanel *self, GtkWidget *row, GList *other_rows);
63
64 static void
65 remove_row_for_account_cb (CcOnlineAccountsPanel *self,
66 GtkWidget *row,
67 GList *other_rows)
68 {
69 gtk_list_box_remove (self->accounts_listbox, row);
70 gtk_widget_set_visible (GTK_WIDGET (self->accounts_frame), other_rows != NULL);
71 }
72
73 static void
74 modify_row_for_account (CcOnlineAccountsPanel *self,
75 GoaObject *object,
76 RowForAccountCallback callback)
77 {
78 GtkWidget *child;
79 GList *children = NULL;
80 GList *l;
81
82 for (child = gtk_widget_get_first_child (GTK_WIDGET (self->accounts_listbox));
83 child;
84 child = gtk_widget_get_next_sibling (child))
85 {
86 children = g_list_prepend (children, child);
87 }
88
89 children = g_list_reverse (children);
90
91 for (l = children; l != NULL; l = l->next)
92 {
93 GoaObject *row_object;
94
95 row_object = cc_online_account_row_get_object (CC_ONLINE_ACCOUNT_ROW (l->data));
96 if (row_object == object)
97 {
98 GtkWidget *row = GTK_WIDGET (l->data);
99
100 children = g_list_remove_link (children, l);
101 callback (self, row, children);
102 g_list_free (l);
103 break;
104 }
105 }
106
107 g_list_free (children);
108 }
109
110 static void
111 show_account_cb (GoaProvider *provider,
112 GAsyncResult *result,
113 CcOnlineAccountsPanel *self)
114 {
115 g_autoptr (GError) error = NULL;
116
117 if (!goa_provider_show_account_finish (provider, result, &error))
118 {
119 if (!g_error_matches (error, GOA_ERROR, GOA_ERROR_DIALOG_DISMISSED) && !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
120 g_warning ("Error showing account: %s", error->message);
121 }
122 }
123
124 static void
125 show_account (CcOnlineAccountsPanel *self,
126 GoaObject *object)
127 {
128 g_autoptr (GoaProvider) provider = NULL;
129 GtkRoot *root;
130 GoaAccount *account;
131 const char *provider_type;
132
133 /* Find the provider with a matching type */
134 account = goa_object_peek_account (object);
135 provider_type = goa_account_get_provider_type (account);
136 provider = goa_provider_get_for_provider_type (provider_type);
137 if (provider == NULL)
138 {
139 g_warning ("Error showing account: Unsupported provider");
140 return;
141 }
142
143 root = gtk_widget_get_root (GTK_WIDGET (self));
144 goa_provider_show_account (provider,
145 self->client,
146 object,
147 GTK_WINDOW (root),
148 cc_panel_get_cancellable (CC_PANEL (self)),
149 (GAsyncReadyCallback) show_account_cb,
150 self);
151 }
152
153 static void
154 create_account_cb (GoaProvider *provider,
155 GAsyncResult *result,
156 CcOnlineAccountsPanel *self)
157 {
158 g_autoptr (GoaObject) object = NULL;
159 g_autoptr (GError) error = NULL;
160
161 object = goa_provider_add_account_finish (provider, result, &error);
162 if (error != NULL)
163 {
164 if (!g_error_matches (error, GOA_ERROR, GOA_ERROR_DIALOG_DISMISSED) && !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
165 g_warning ("Error creating account: %s", error->message);
166
167 return;
168 }
169
170 show_account (self, object);
171 }
172
173 static void
174 create_account (CcOnlineAccountsPanel *self,
175 GoaProvider *provider)
176 {
177 GtkRoot *parent;
178
179 g_return_if_fail (GOA_IS_PROVIDER (provider));
180
181 parent = gtk_widget_get_root (GTK_WIDGET (self));
182 goa_provider_add_account (provider,
183 self->client,
184 GTK_WINDOW (parent),
185 cc_panel_get_cancellable (CC_PANEL (self)),
186 (GAsyncReadyCallback) create_account_cb,
187 self);
188 }
189
190 static void
191 select_account_by_id (CcOnlineAccountsPanel *self,
192 const gchar *account_id)
193 {
194 GtkWidget *child;
195
196 for (child = gtk_widget_get_first_child (GTK_WIDGET (self->accounts_listbox));
197 child;
198 child = gtk_widget_get_next_sibling (child))
199 {
200 GoaAccount *account;
201 GoaObject *row_object;
202
203 row_object = cc_online_account_row_get_object (CC_ONLINE_ACCOUNT_ROW (child));
204 account = goa_object_peek_account (row_object);
205
206 if (g_strcmp0 (goa_account_get_id (account), account_id) == 0)
207 {
208 show_account (self, row_object);
209 break;
210 }
211 }
212 }
213
214 static void
215 command_add (CcOnlineAccountsPanel *self,
216 GVariant *parameters)
217 {
218 const gchar *provider_name = NULL;
219 g_autoptr (GVariant) v = NULL;
220
221 g_assert (self != NULL);
222 g_assert (parameters != NULL);
223
224 switch (g_variant_n_children (parameters))
225 {
226 case 2:
227 g_variant_get_child (parameters, 1, "v", &v);
228 if (g_variant_is_of_type (v, G_VARIANT_TYPE_STRING))
229 provider_name = g_variant_get_string (v, NULL);
230 else
231 g_warning ("Wrong type for the second argument (provider name) GVariant, expected 's' but got '%s'",
232 (gchar *)g_variant_get_type (v));
233 break;
234
235 default:
236 g_warning ("Unexpected parameters found, ignore request");
237 return;
238 }
239
240 if (provider_name != NULL)
241 {
242 g_autoptr (GoaProvider) provider = NULL;
243 unsigned int n_items = 0;
244
245 n_items = g_list_model_get_n_items (G_LIST_MODEL (self->providers));
246 for (unsigned int i = 0; i < n_items; i++)
247 {
248 const char *provider_type = NULL;
249
250 provider = g_list_model_get_item (G_LIST_MODEL (self->providers), i);
251 provider_type = goa_provider_get_provider_type (provider);
252
253 if (g_strcmp0 (provider_type, provider_name) == 0)
254 break;
255
256 g_clear_object (&provider);
257 }
258
259 if (provider == NULL)
260 {
261 g_warning ("Unable to get a provider for type '%s'", provider_name);
262 return;
263 }
264
265 create_account (self, provider);
266 }
267 }
268
269 static void
270 load_custom_css (void)
271 {
272 g_autoptr (GtkCssProvider) provider = NULL;
273
274 provider = gtk_css_provider_new ();
275 gtk_css_provider_load_from_resource (provider, "/org/gnome/control-center/online-accounts/online-accounts.css");
276 gtk_style_context_add_provider_for_display (gdk_display_get_default (),
277 GTK_STYLE_PROVIDER (provider),
278 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
279 }
280
281 /* Callbacks */
282
283 static int
284 goa_provider_priority (const char *provider_type)
285 {
286 static const char *goa_priority[] = {
287 "owncloud", /* Nextcloud */
288 "google", /* Google */
289 "windows_live", /* Microsoft Personal */
290 "ms_graph", /* Microsoft 365 */
291 "exchange", /* Microsoft Exchange */
292 "fedora", /* Fedora */
293 "imap_smtp", /* Email (IMAP and SMTP) */
294 "webdav", /* Calendars, Contacts, Files (WebDAV) */
295 "kerberos", /* Enterprise Login (Kerberos) */
296 };
297
298 for (size_t i = 0; i < G_N_ELEMENTS (goa_priority); i++)
299 {
300 if (g_str_equal (goa_priority[i], provider_type))
301 return i;
302 }
303
304 /* New or unknown providers are sorted last */
305 return G_N_ELEMENTS (goa_priority) + 1;
306 }
307
308 static GtkWidget *
309 provider_create_row (gpointer item,
310 gpointer user_data)
311 {
312 return (GtkWidget *) cc_online_account_provider_row_new (GOA_PROVIDER (item));
313 }
314
315 static int
316 sort_accounts_func (GtkListBoxRow *a,
317 GtkListBoxRow *b,
318 gpointer user_data)
319 {
320 GoaAccount *a_account, *b_account;
321 GoaObject *a_object, *b_object;
322 const char *a_name, *b_name;
323
324 a_object = cc_online_account_row_get_object (CC_ONLINE_ACCOUNT_ROW (a));
325 a_account = goa_object_peek_account (a_object);
326 a_name = goa_account_get_provider_type (a_account);
327
328 b_object = cc_online_account_row_get_object (CC_ONLINE_ACCOUNT_ROW (b));
329 b_account = goa_object_peek_account (b_object);
330 b_name = goa_account_get_provider_type (b_account);
331
332 return goa_provider_priority (a_name) - goa_provider_priority (b_name);
333 }
334
335 static int
336 sort_providers_func (GoaProvider *a,
337 GoaProvider *b,
338 gpointer user_data)
339 {
340 const char *a_name = goa_provider_get_provider_type (a);
341 const char *b_name = goa_provider_get_provider_type (b);
342
343 return goa_provider_priority (a_name) - goa_provider_priority (b_name);
344 }
345
346 static void
347 add_account (CcOnlineAccountsPanel *self,
348 GoaObject *object)
349 {
350 CcOnlineAccountRow *row;
351
352 row = cc_online_account_row_new (object);
353 gtk_list_box_append (self->accounts_listbox, GTK_WIDGET (row));
354 gtk_widget_set_visible (GTK_WIDGET (self->accounts_frame), TRUE);
355 }
356
357 static void
358 add_provider (CcOnlineAccountsPanel *self,
359 GoaProvider *provider)
360 {
361 g_list_store_insert_sorted (self->providers,
362 provider,
363 (GCompareDataFunc) sort_providers_func,
364 self);
365 }
366
367 static void
368 on_account_added_cb (CcOnlineAccountsPanel *self,
369 GoaObject *object)
370 {
371 add_account (self, object);
372 }
373
374 static void
375 on_account_removed_cb (CcOnlineAccountsPanel *self,
376 GoaObject *object)
377 {
378 modify_row_for_account (self, object, remove_row_for_account_cb);
379 }
380
381 static void
382 on_accounts_listbox_row_activated (CcOnlineAccountsPanel *self,
383 GtkListBoxRow *activated_row)
384 {
385 GoaObject *object = cc_online_account_row_get_object (CC_ONLINE_ACCOUNT_ROW (activated_row));
386
387 show_account (self, object);
388 }
389
390 static void
391 on_provider_row_activated_cb (CcOnlineAccountsPanel *self,
392 GtkListBoxRow *activated_row)
393 {
394 GoaProvider *provider = cc_online_account_provider_row_get_provider (CC_ONLINE_ACCOUNT_PROVIDER_ROW (activated_row));
395
396 create_account (self, provider);
397 }
398
399 static void
400 goa_provider_get_all_cb (GObject *object,
401 GAsyncResult *res,
402 gpointer user_data)
403 {
404 g_autoptr (CcOnlineAccountsPanel) self = CC_ONLINE_ACCOUNTS_PANEL (user_data);
405 g_autolist (GoaProvider) providers = NULL;
406 g_autolist (GoaAccount) accounts = NULL;
407 g_autoptr (GError) error = NULL;
408
409 /* goa_provider_get_all() doesn't have a cancellable argument, so check if
410 * the panel cancellable was triggered.
411 */
412 if (g_cancellable_is_cancelled (cc_panel_get_cancellable (CC_PANEL (self))))
413 return;
414
415 if (!goa_provider_get_all_finish (&providers, res, &error))
416 {
417 g_warning ("Error listing providers: %s", error->message);
418 return;
419 }
420
421 for (const GList *iter = providers; iter != NULL; iter = iter->next)
422 add_provider (self, GOA_PROVIDER (iter->data));
423
424 /* Load existing accounts */
425 accounts = goa_client_get_accounts (self->client);
426
427 for (const GList *iter = accounts; iter != NULL; iter = iter->next)
428 add_account (self, GOA_OBJECT (iter->data));
429
430 g_signal_connect_swapped (self->client,
431 "account-added",
432 G_CALLBACK (on_account_added_cb),
433 self);
434
435 g_signal_connect_swapped (self->client,
436 "account-removed",
437 G_CALLBACK (on_account_removed_cb),
438 self);
439
440 /* With the client ready, check if we have a pending command */
441 gtk_widget_set_sensitive (GTK_WIDGET (self), TRUE);
442
443 if (self->parameters != NULL)
444 {
445 g_autoptr (GVariant) parameters = NULL;
446
447 parameters = g_steal_pointer (&self->parameters);
448 g_object_set (self, "parameters", parameters, NULL);
449 }
450 }
451
452 static void
453 goa_client_new_cb (GObject *object,
454 GAsyncResult *res,
455 gpointer user_data)
456 {
457 g_autoptr (CcOnlineAccountsPanel) self = CC_ONLINE_ACCOUNTS_PANEL (user_data);
458 g_autoptr (GError) error = NULL;
459
460 self->client = goa_client_new_finish (res, &error);
461 if (self->client == NULL)
462 {
463 g_warning ("Error connect to service: %s", error->message);
464 gtk_widget_set_sensitive (GTK_WIDGET (self), FALSE);
465 return;
466 }
467
468 goa_provider_get_all (goa_provider_get_all_cb, g_object_ref (self));
469 }
470
471 /* CcPanel overrides */
472
473 static const char *
474 cc_online_accounts_panel_get_help_uri (CcPanel *panel)
475 {
476 return "help:gnome-help/accounts";
477 }
478
479 /* GObject overrides */
480
481 static void
482 cc_online_accounts_panel_set_property (GObject *object,
483 guint property_id,
484 const GValue *value,
485 GParamSpec *pspec)
486 {
487 CcOnlineAccountsPanel *self = CC_ONLINE_ACCOUNTS_PANEL (object);
488
489 switch (property_id)
490 {
491 case PROP_PARAMETERS:
492 {
493 GVariant *parameters;
494 g_autoptr (GVariant) v = NULL;
495 const gchar *first_arg = NULL;
496
497 parameters = g_value_get_variant (value);
498 if (parameters == NULL)
499 return;
500
501 if (g_variant_n_children (parameters) > 0)
502 {
503 g_variant_get_child (parameters, 0, "v", &v);
504 if (g_variant_is_of_type (v, G_VARIANT_TYPE_STRING))
505 first_arg = g_variant_get_string (v, NULL);
506 else
507 g_warning ("Wrong type for the second argument GVariant, expected 's' but got '%s'",
508 (gchar *)g_variant_get_type (v));
509 }
510
511 /* Waiting for the client to load */
512 if (self->client == NULL)
513 {
514 g_clear_pointer (&self->parameters, g_variant_unref);
515 self->parameters = g_value_dup_variant (value);
516 }
517 else if (g_strcmp0 (first_arg, "add") == 0)
518 {
519 command_add (CC_ONLINE_ACCOUNTS_PANEL (object), parameters);
520 }
521 else if (first_arg != NULL)
522 {
523 select_account_by_id (CC_ONLINE_ACCOUNTS_PANEL (object), first_arg);
524 }
525
526 return;
527 }
528 }
529
530 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
531 }
532
533 static void
534 cc_online_accounts_panel_finalize (GObject *object)
535 {
536 CcOnlineAccountsPanel *self = CC_ONLINE_ACCOUNTS_PANEL (object);
537
538 g_clear_object (&self->client);
539 g_clear_pointer (&self->parameters, g_variant_unref);
540 g_clear_object (&self->providers);
541
542 G_OBJECT_CLASS (cc_online_accounts_panel_parent_class)->finalize (object);
543 }
544
545 static void
546 cc_online_accounts_panel_class_init (CcOnlineAccountsPanelClass *klass)
547 {
548 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
549 GObjectClass *object_class = G_OBJECT_CLASS (klass);
550 CcPanelClass *panel_class = CC_PANEL_CLASS (klass);
551
552 panel_class->get_help_uri = cc_online_accounts_panel_get_help_uri;
553
554 object_class->set_property = cc_online_accounts_panel_set_property;
555 object_class->finalize = cc_online_accounts_panel_finalize;
556
557 g_object_class_override_property (object_class, PROP_PARAMETERS, "parameters");
558
559 gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/online-accounts/cc-online-accounts-panel.ui");
560
561 gtk_widget_class_bind_template_child (widget_class, CcOnlineAccountsPanel, accounts_frame);
562 gtk_widget_class_bind_template_child (widget_class, CcOnlineAccountsPanel, accounts_listbox);
563 gtk_widget_class_bind_template_child (widget_class, CcOnlineAccountsPanel, offline_banner);
564 gtk_widget_class_bind_template_child (widget_class, CcOnlineAccountsPanel, providers_listbox);
565
566 gtk_widget_class_bind_template_callback (widget_class, on_accounts_listbox_row_activated);
567 gtk_widget_class_bind_template_callback (widget_class, on_provider_row_activated_cb);
568 }
569
570 static void
571 cc_online_accounts_panel_init (CcOnlineAccountsPanel *self)
572 {
573 GNetworkMonitor *monitor;
574
575 g_resources_register (cc_online_accounts_get_resource ());
576
577 gtk_widget_init_template (GTK_WIDGET (self));
578
579 gtk_list_box_set_sort_func (self->accounts_listbox,
580 sort_accounts_func,
581 self,
582 NULL);
583
584 self->providers = g_list_store_new (GOA_TYPE_PROVIDER);
585 gtk_list_box_bind_model (self->providers_listbox,
586 G_LIST_MODEL (self->providers),
587 provider_create_row,
588 self,
589 NULL);
590
591 monitor = g_network_monitor_get_default();
592 g_object_bind_property (monitor,
593 "network-available",
594 self->offline_banner,
595 "revealed",
596 G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN);
597
598 g_object_bind_property (monitor,
599 "network-available",
600 self->providers_listbox,
601 "sensitive",
602 G_BINDING_SYNC_CREATE);
603
604 load_custom_css ();
605
606 /* Disable the panel while we wait for the client */
607 gtk_widget_set_sensitive (GTK_WIDGET (self), FALSE);
608 goa_client_new (cc_panel_get_cancellable (CC_PANEL (self)),
609 goa_client_new_cb,
610 g_object_ref (self));
611 }
612