GCC Code Coverage Report


Directory: ./
File: panels/system/users/cc-users-page.c
Date: 2024-05-04 07:58:27
Exec Total Coverage
Lines: 0 149 0.0%
Functions: 0 18 0.0%
Branches: 0 53 0.0%

Line Branch Exec Source
1 /*
2 * cc-users-page.c
3 *
4 * Copyright 2023 Red Hat Inc
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 *
19 * SPDX-License-Identifier: GPL-3.0-or-later
20 *
21 * Author(s):
22 * Felipe Borges <felipeborges@gnome.org>
23 */
24
25 #undef G_LOG_DOMAIN
26 #define G_LOG_DOMAIN "cc-users-page"
27
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include "cc-add-user-dialog.h"
33 #include "cc-enterprise-login-dialog.h"
34 #include "cc-users-page.h"
35 #include "cc-list-row.h"
36 #include "cc-user-page.h"
37 #include "user-utils.h"
38
39 #include <act/act.h>
40 #include <config.h>
41 #include <errno.h>
42 #include <locale.h>
43 #include <glib/gi18n.h>
44 #include <gio/gio.h>
45 #include <gtk/gtk.h>
46 #include <polkit/polkit.h>
47
48 #define USER_ACCOUNTS_PERMISSION "org.gnome.controlcenter.user-accounts.administration"
49
50 struct _CcUsersPage {
51 AdwNavigationPage parent_instance;
52
53 GtkButton *add_user_button;
54 GtkButton *add_enterprise_user_button;
55 CcUserPage *current_user_page;
56 AdwNavigationView *navigation;
57 GtkWidget *other_users_group;
58 GtkListBox *user_list;
59
60 GListStore *model;
61 GPermission *permission;
62 ActUserManager *user_manager;
63 };
64
65 G_DEFINE_TYPE (CcUsersPage, cc_users_page, ADW_TYPE_NAVIGATION_PAGE)
66
67 static void
68 add_enterprise_user (CcUsersPage *self)
69 {
70 CcEnterpriseLoginDialog *dialog = cc_enterprise_login_dialog_new ();
71
72 adw_dialog_present (ADW_DIALOG (dialog), GTK_WIDGET (self));
73 }
74
75 static void
76 add_user (CcUsersPage *self)
77 {
78 CcAddUserDialog *dialog = cc_add_user_dialog_new (self->permission);
79
80 adw_dialog_present (ADW_DIALOG (dialog), GTK_WIDGET (self));
81 }
82
83 static void
84 on_user_row_activated (CcUsersPage *self,
85 AdwActionRow *row)
86 {
87 CcUserPage *user_page;
88 ActUser *user;
89 uid_t uid;
90
91 uid = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (row), "uid"));
92 user = act_user_manager_get_user_by_id (act_user_manager_get_default (), uid);
93
94 user_page = cc_user_page_new ();
95 cc_user_page_set_user (user_page, user, self->permission);
96 adw_navigation_view_push (self->navigation, ADW_NAVIGATION_PAGE (user_page));
97 }
98
99 static void
100 on_other_users_model_changed (CcUsersPage *self)
101 {
102 gtk_widget_set_visible (self->other_users_group,
103 g_list_model_get_n_items (G_LIST_MODEL (self->model)) > 0);
104 }
105
106 static GtkWidget *
107 create_user_row (gpointer item, gpointer user_data)
108 {
109 ActUser *user;
110 GtkWidget *row, *user_image;
111
112 row = g_object_new (CC_TYPE_LIST_ROW, "show-arrow", TRUE, NULL);
113 gtk_list_box_row_set_activatable (GTK_LIST_BOX_ROW (row), TRUE);
114
115 user = item;
116 g_object_set_data (G_OBJECT (row), "uid", GINT_TO_POINTER (act_user_get_uid (user)));
117 adw_preferences_row_set_title (ADW_PREFERENCES_ROW (row),
118 get_real_or_user_name (user));
119 user_image = adw_avatar_new (32, NULL, TRUE);
120 setup_avatar_for_user (ADW_AVATAR (user_image), user);
121 adw_action_row_add_prefix (ADW_ACTION_ROW (row), user_image);
122
123 return row;
124 }
125
126 static gint
127 sort_users (gconstpointer a, gconstpointer b, gpointer user_data)
128 {
129 ActUser *ua, *ub;
130 g_autofree gchar *name1 = NULL;
131 g_autofree gchar *name2 = NULL;
132
133 ua = ACT_USER ((gpointer*)a);
134 ub = ACT_USER ((gpointer*)b);
135
136 /* Make sure the current user is shown first */
137 if (act_user_get_uid (ua) == getuid ()) {
138 return -G_MAXINT32;
139 } else if (act_user_get_uid (ub) == getuid ()) {
140 return G_MAXINT32;
141 }
142
143 name1 = g_utf8_collate_key (get_real_or_user_name (ua), -1);
144 name2 = g_utf8_collate_key (get_real_or_user_name (ub), -1);
145
146 return strcmp (name1, name2);
147 }
148
149 static void
150 on_user_changed (CcUsersPage *self,
151 ActUser *user)
152 {
153 CcUserPage *page;
154
155 /* Refresh the users list */
156 g_list_store_sort (self->model, sort_users, self);
157
158 /* If the user has a page open, refresh that page */
159 page = CC_USER_PAGE (adw_navigation_view_find_page (self->navigation, act_user_get_user_name (user)));
160 if (page != NULL)
161 cc_user_page_set_user (page, user, self->permission);
162 }
163
164 static void
165 on_user_added (CcUsersPage *self,
166 ActUser *user)
167 {
168 CcUserPage *page;
169 g_list_store_insert_sorted (self->model, user, sort_users, self);
170
171 page = CC_USER_PAGE (adw_navigation_view_get_visible_page (self->navigation));
172 if (page != self->current_user_page)
173 return;
174
175 /* We're on the current user's page and a new user was just created. It's very likely the user
176 * was just created by our own add-user dialog. So let's display the new user */
177
178 page = cc_user_page_new ();
179 cc_user_page_set_user (page, user, self->permission);
180 adw_navigation_view_push (self->navigation, ADW_NAVIGATION_PAGE (page));
181 }
182
183 static void
184 on_user_removed (CcUsersPage *self,
185 ActUser *user)
186 {
187 AdwNavigationPage *page;
188 guint position;
189
190 if (g_list_store_find (self->model, user, &position))
191 g_list_store_remove (self->model, position);
192
193 page = adw_navigation_view_find_page (self->navigation, act_user_get_user_name (user));
194 if (page != NULL)
195 adw_navigation_view_pop_to_page (self->navigation, ADW_NAVIGATION_PAGE (self->current_user_page));
196 }
197
198 static void
199 users_loaded (CcUsersPage *self)
200 {
201 g_autoptr(GSList) user_list = NULL;
202 GSList *l;
203 guint n_users;
204
205 user_list = act_user_manager_list_users (self->user_manager);
206 for (l = user_list; l; l = l->next) {
207 ActUser *user = ACT_USER (l->data);
208
209 if (act_user_is_system_account (user)) {
210 continue;
211 }
212
213 /* Increase the user count for all accounts except for "system" accounts, such
214 * as "root" or "nobody". */
215 n_users++;
216 if (act_user_get_uid (user) == getuid ()) {
217 cc_user_page_set_user (self->current_user_page, user, self->permission);
218
219 continue;
220 }
221
222 g_list_store_insert_sorted (self->model,
223 user,
224 sort_users,
225 self);
226 }
227 }
228
229 static void
230 check_realmd_list_names_cb (GObject *object,
231 GAsyncResult *result,
232 gpointer user_data)
233 {
234 g_autoptr(CcUsersPage) self = CC_USERS_PAGE (user_data);
235 g_autoptr(GVariant) ret = NULL;
236 g_autoptr(GVariant) names = NULL;
237 GVariantIter iter;
238 gchar *name;
239 g_autoptr(GError) error = NULL;
240
241 ret = g_dbus_connection_call_finish (G_DBUS_CONNECTION (object), result, &error);
242 if (ret == NULL) {
243 g_warning ("Unable to query dbus: %s", error->message);
244 return;
245 }
246
247 names = g_variant_get_child_value (ret, 0);
248 g_variant_iter_init (&iter, names);
249 while (g_variant_iter_loop (&iter, "&s", &name)) {
250 if (g_str_equal (name, "org.freedesktop.realmd"))
251 gtk_widget_set_visible (GTK_WIDGET (self->add_enterprise_user_button), TRUE);
252 }
253 }
254
255 static void
256 check_realmd (CcUsersPage *self)
257 {
258 g_autoptr(GDBusConnection) connection = NULL;
259 g_autoptr(GError) error = NULL;
260
261 connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
262 if (connection == NULL) {
263 g_warning ("Unable to connect to dbus: %s", error->message);
264 return;
265 }
266
267 g_dbus_connection_call (connection,
268 "org.freedesktop.DBus",
269 "/org/freedesktop/DBus",
270 "org.freedesktop.DBus",
271 "ListActivatableNames",
272 NULL,
273 G_VARIANT_TYPE ("(as)"),
274 G_DBUS_CALL_FLAGS_NONE,
275 -1, NULL,
276 check_realmd_list_names_cb,
277 g_object_ref (self));
278 }
279
280 static void
281 cc_users_page_dispose (GObject *object)
282 {
283 CcUsersPage *self = CC_USERS_PAGE (object);
284
285 g_clear_object (&self->model);
286 g_clear_object (&self->permission);
287
288 G_OBJECT_CLASS (cc_users_page_parent_class)->dispose (object);
289 }
290
291 static void
292 cc_users_page_init (CcUsersPage *self)
293 {
294 g_autoptr(GError) error = NULL;
295 g_autoptr(GtkCssProvider) provider = NULL;
296 gboolean is_loaded = FALSE;
297
298 gtk_widget_init_template (GTK_WIDGET (self));
299
300 provider = gtk_css_provider_new ();
301 gtk_css_provider_load_from_resource (provider,
302 "/org/gnome/control-center/system/users/users.css");
303 gtk_style_context_add_provider_for_display (gdk_display_get_default (),
304 GTK_STYLE_PROVIDER (provider),
305 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
306
307 self->model = g_list_store_new (ACT_TYPE_USER);
308 gtk_list_box_bind_model (self->user_list,
309 G_LIST_MODEL (self->model),
310 (GtkListBoxCreateWidgetFunc)create_user_row,
311 self,
312 NULL);
313 g_signal_connect_object (self->model,
314 "items-changed",
315 G_CALLBACK (on_other_users_model_changed),
316 self,
317 G_CONNECT_SWAPPED);
318 self->permission = (GPermission *)polkit_permission_new_sync (USER_ACCOUNTS_PERMISSION, NULL, NULL, &error);
319 if (self->permission == NULL) {
320 g_warning ("Cannot create '%s' permission: %s", USER_ACCOUNTS_PERMISSION, error->message);
321 }
322
323 g_object_bind_property (self->permission, "allowed", self->add_user_button, "sensitive", G_BINDING_SYNC_CREATE);
324
325 self->user_manager = act_user_manager_get_default ();
326 g_signal_connect_object (self->user_manager,
327 "notify::is-loaded",
328 G_CALLBACK (users_loaded),
329 self,
330 G_CONNECT_SWAPPED);
331 g_signal_connect_object (self->user_manager,
332 "user-added",
333 G_CALLBACK (on_user_added),
334 self,
335 G_CONNECT_SWAPPED);
336 g_signal_connect_object (self->user_manager,
337 "user-removed",
338 G_CALLBACK (on_user_removed),
339 self,
340 G_CONNECT_SWAPPED);
341 g_signal_connect_object (self->user_manager,
342 "user-changed",
343 G_CALLBACK (on_user_changed),
344 self,
345 G_CONNECT_SWAPPED);
346
347 g_object_get (self->user_manager, "is-loaded", &is_loaded, NULL);
348 if (is_loaded) {
349 users_loaded (self);
350 }
351
352 check_realmd (self);
353 }
354
355 static void
356 cc_users_page_class_init (CcUsersPageClass * klass)
357 {
358 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
359 GObjectClass *object_class = G_OBJECT_CLASS (klass);
360
361 object_class->dispose = cc_users_page_dispose;
362
363 g_type_ensure (CC_TYPE_LIST_ROW);
364 g_type_ensure (CC_TYPE_USER_PAGE);
365
366 gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/system/users/cc-users-page.ui");
367
368 gtk_widget_class_bind_template_child (widget_class, CcUsersPage, add_enterprise_user_button);
369 gtk_widget_class_bind_template_child (widget_class, CcUsersPage, add_user_button);
370 gtk_widget_class_bind_template_child (widget_class, CcUsersPage, current_user_page);
371 gtk_widget_class_bind_template_child (widget_class, CcUsersPage, navigation);
372 gtk_widget_class_bind_template_child (widget_class, CcUsersPage, other_users_group);
373 gtk_widget_class_bind_template_child (widget_class, CcUsersPage, user_list);
374
375 gtk_widget_class_bind_template_callback (widget_class, add_user);
376 gtk_widget_class_bind_template_callback (widget_class, add_enterprise_user);
377 gtk_widget_class_bind_template_callback (widget_class, on_user_row_activated);
378 }
379