GCC Code Coverage Report


Directory: ./
File: panels/applications/cc-snap-row.c
Date: 2024-05-04 07:58:27
Exec Total Coverage
Lines: 0 265 0.0%
Functions: 0 19 0.0%
Branches: 0 199 0.0%

Line Branch Exec Source
1 /* cc-snap-row.c
2 *
3 * Copyright 2019 Canonical Ltd.
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program 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
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: GPL-3.0-or-later
19 */
20
21 #include <config.h>
22 #include <glib/gi18n.h>
23
24 #include "cc-snap-row.h"
25 #include "cc-applications-resources.h"
26 #include "cc-snapd-client.h"
27
28 #define CHANGE_POLL_TIME 100
29
30 struct _CcSnapRow
31 {
32 AdwActionRow parent;
33
34 GtkSwitch *slot_toggle;
35 GtkComboBox *slots_combo;
36 GtkListStore *slots_combo_model;
37
38 GCancellable *cancellable;
39
40 CcSnapdClient *client;
41 JsonObject *plug;
42 JsonObject *connected_slot;
43 JsonArray *slots;
44 JsonObject *target_slot;
45
46 gchar *change_id;
47 guint change_timeout;
48 };
49
50 G_DEFINE_TYPE (CcSnapRow, cc_snap_row, ADW_TYPE_ACTION_ROW)
51
52 static void
53 update_state (CcSnapRow *self)
54 {
55 gboolean have_single_option;
56 GtkTreeIter iter;
57
58 have_single_option = json_array_get_length (self->slots) == 1;
59 gtk_widget_set_visible (GTK_WIDGET (self->slot_toggle), have_single_option);
60 gtk_widget_set_visible (GTK_WIDGET (self->slots_combo), !have_single_option);
61
62 gtk_switch_set_active (self->slot_toggle, self->connected_slot != NULL);
63
64 if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (self->slots_combo_model), &iter))
65 {
66 do
67 {
68 JsonObject *slot;
69
70 gtk_tree_model_get (GTK_TREE_MODEL (self->slots_combo_model), &iter, 0, &slot, -1);
71 if (slot == self->connected_slot)
72 gtk_combo_box_set_active_iter (self->slots_combo, &iter);
73 } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (self->slots_combo_model), &iter));
74 }
75 }
76
77 static void
78 disable_controls (CcSnapRow *self)
79 {
80 gtk_widget_set_sensitive (GTK_WIDGET (self->slot_toggle), FALSE);
81 gtk_widget_set_sensitive (GTK_WIDGET (self->slots_combo), FALSE);
82 }
83
84 static void
85 enable_controls (CcSnapRow *self)
86 {
87 gtk_widget_set_sensitive (GTK_WIDGET (self->slot_toggle), TRUE);
88 gtk_widget_set_sensitive (GTK_WIDGET (self->slots_combo), TRUE);
89 }
90
91 static void
92 change_complete (CcSnapRow *self)
93 {
94 g_clear_object (&self->client);
95 g_clear_pointer (&self->target_slot, json_object_unref);
96 g_clear_pointer (&self->change_id, g_free);
97 g_clear_handle_id (&self->change_timeout, g_source_remove);
98
99 update_state (self);
100 enable_controls (self);
101 }
102
103 static gboolean
104 poll_change_cb (gpointer user_data)
105 {
106 CcSnapRow *self = user_data;
107 g_autoptr(JsonObject) change = NULL;
108 g_autoptr(GError) error = NULL;
109
110 change = cc_snapd_client_get_change_sync (self->client, self->change_id, self->cancellable, &error);
111 if (change == NULL)
112 {
113 g_warning ("Failed to monitor change %s: %s", self->change_id, error->message);
114 change_complete (self);
115 return G_SOURCE_REMOVE;
116 }
117
118 if (json_object_get_boolean_member (change, "ready"))
119 {
120 const gchar *status = json_object_get_string_member (change, "status");
121
122 if (g_strcmp0 (status, "Done") == 0)
123 {
124 g_clear_pointer (&self->connected_slot, json_object_unref);
125 self->connected_slot = self->target_slot ? json_object_ref (self->target_slot) : NULL;
126 }
127 else
128 {
129 g_warning ("Change completed with status %s", status);
130 }
131
132 change_complete (self);
133 return G_SOURCE_REMOVE;
134 }
135
136 return G_SOURCE_CONTINUE;
137 }
138
139 static void
140 monitor_change (CcSnapRow *self, const gchar *change_id)
141 {
142 g_free (self->change_id);
143 self->change_id = g_strdup (change_id);
144 g_clear_handle_id (&self->change_timeout, g_source_remove);
145 self->change_timeout = g_timeout_add (CHANGE_POLL_TIME, poll_change_cb, self);
146 }
147
148 static CcSnapdClient *
149 get_client(CcSnapRow *self)
150 {
151 if (self->client == NULL)
152 self->client = cc_snapd_client_new ();
153 return self->client;
154 }
155
156 static void
157 connect_plug (CcSnapRow *self, JsonObject *slot)
158 {
159 g_autofree gchar *change_id = NULL;
160 g_autoptr(GError) error = NULL;
161
162 /* already connected */
163 if (self->connected_slot != NULL &&
164 g_strcmp0 (json_object_get_string_member (self->connected_slot, "snap"),
165 json_object_get_string_member (slot, "snap")) == 0 &&
166 g_strcmp0 (json_object_get_string_member (self->connected_slot, "slot"),
167 json_object_get_string_member (slot, "slot")) == 0)
168 return;
169
170 disable_controls (self);
171
172 change_id = cc_snapd_client_connect_interface_sync (get_client (self),
173 json_object_get_string_member (self->plug, "snap"),
174 json_object_get_string_member (self->plug, "plug"),
175 json_object_get_string_member (slot, "snap"),
176 json_object_get_string_member (slot, "slot"),
177 self->cancellable,
178 &error);
179 if (change_id == NULL)
180 {
181 g_warning ("Failed to connect plug: %s", error->message);
182 change_complete (self);
183 return;
184 }
185
186 g_clear_pointer (&self->target_slot, json_object_unref);
187 self->target_slot = json_object_ref (slot);
188 monitor_change (self, change_id);
189 }
190
191 static void
192 disconnect_plug (CcSnapRow *self)
193 {
194 g_autofree gchar *change_id = NULL;
195 g_autoptr(GError) error = NULL;
196
197 /* already disconnected */
198 if (self->connected_slot == NULL)
199 return;
200
201 disable_controls (self);
202
203 change_id = cc_snapd_client_disconnect_interface_sync (get_client (self),
204 json_object_get_string_member (self->plug, "snap"),
205 json_object_get_string_member (self->plug, "plug"),
206 "", "",
207 self->cancellable, &error);
208 if (change_id == NULL)
209 {
210 g_warning ("Failed to disconnect plug: %s", error->message);
211 change_complete (self);
212 return;
213 }
214
215 g_clear_pointer (&self->target_slot, json_object_unref);
216 monitor_change (self, change_id);
217 }
218
219 static void
220 switch_changed_cb (CcSnapRow *self)
221 {
222 if (gtk_switch_get_active (self->slot_toggle))
223 {
224 if (json_array_get_length (self->slots) == 1)
225 connect_plug (self, json_array_get_object_element (self->slots, 0));
226 }
227 else
228 {
229 disconnect_plug (self);
230 }
231 }
232
233 static void
234 combo_changed_cb (CcSnapRow *self)
235 {
236 GtkTreeIter iter;
237 JsonObject *slot = NULL;
238
239 if (!gtk_combo_box_get_active_iter (self->slots_combo, &iter))
240 return;
241
242 gtk_tree_model_get (GTK_TREE_MODEL (self->slots_combo_model), &iter, 0, &slot, -1);
243 if (slot != NULL)
244 connect_plug (self, slot);
245 else
246 disconnect_plug (self);
247 }
248
249 static const gchar *
250 make_interface_label (const gchar *interface_name)
251 {
252 if (strcmp (interface_name, "account-control") == 0)
253 return _("Add user accounts and change passwords");
254 else if (strcmp (interface_name, "alsa") == 0)
255 return _("Play and record sound");
256 else if (strcmp (interface_name, "audio-playback") == 0)
257 return _("Play audio");
258 else if (strcmp (interface_name, "audio-record") == 0)
259 return _("Record audio");
260 else if (strcmp (interface_name, "avahi-observe") == 0)
261 return _("Detect network devices using mDNS/DNS-SD (Bonjour/zeroconf)");
262 else if (strcmp (interface_name, "bluetooth-control") == 0)
263 return _("Access bluetooth hardware directly");
264 else if (strcmp (interface_name, "bluez") == 0)
265 return _("Use bluetooth devices");
266 else if (strcmp (interface_name, "camera") == 0)
267 return _("Use your camera");
268 else if (strcmp (interface_name, "cups-control") == 0)
269 return _("Print documents");
270 else if (strcmp (interface_name, "joystick") == 0)
271 return _("Use any connected joystick");
272 else if (strcmp (interface_name, "docker") == 0)
273 return _("Allow connecting to the Docker service");
274 else if (strcmp (interface_name, "firewall-control") == 0)
275 return _("Configure network firewall");
276 else if (strcmp (interface_name, "fuse-support") == 0)
277 return _("Setup and use privileged FUSE filesystems");
278 else if (strcmp (interface_name, "fwupd") == 0)
279 return _("Update firmware on this device");
280 else if (strcmp (interface_name, "hardware-observe") == 0)
281 return _("Access hardware information");
282 else if (strcmp (interface_name, "hardware-random-control") == 0)
283 return _("Provide entropy to hardware random number generator");
284 else if (strcmp (interface_name, "hardware-random-observe") == 0)
285 return _("Use hardware-generated random numbers");
286 else if (strcmp (interface_name, "home") == 0)
287 return _("Access files in your home folder");
288 else if (strcmp (interface_name, "libvirt") == 0)
289 return _("Access libvirt service");
290 else if (strcmp (interface_name, "locale-control") == 0)
291 return _("Change system language and region settings");
292 else if (strcmp (interface_name, "location-control") == 0)
293 return _("Change location settings and providers");
294 else if (strcmp (interface_name, "location-observe") == 0)
295 return _("Access your location");
296 else if (strcmp (interface_name, "log-observe") == 0)
297 return _("Read system and application logs");
298 else if (strcmp (interface_name, "lxd") == 0)
299 return _("Access LXD service");
300 else if (strcmp (interface_name, "modem-manager") == 0)
301 return _("Use and configure modems");
302 else if (strcmp (interface_name, "mount-observe") == 0)
303 return _("Read system mount information and disk quotas");
304 else if (strcmp (interface_name, "mpris") == 0)
305 return _("Control music and video players");
306 else if (strcmp (interface_name, "network-control") == 0)
307 return _("Change low-level network settings");
308 else if (strcmp (interface_name, "network-manager") == 0)
309 return _("Access the NetworkManager service to read and change network settings");
310 else if (strcmp (interface_name, "network-observe") == 0)
311 return _("Read access to network settings");
312 else if (strcmp (interface_name, "network-setup-control") == 0)
313 return _("Change network settings");
314 else if (strcmp (interface_name, "network-setup-observe") == 0)
315 return _("Read network settings");
316 else if (strcmp (interface_name, "ofono") == 0)
317 return _("Access the ofono service to read and change network settings for mobile telephony");
318 else if (strcmp (interface_name, "openvswitch") == 0)
319 return _("Control Open vSwitch hardware");
320 else if (strcmp (interface_name, "optical-drive") == 0)
321 return _("Read from CD/DVD");
322 else if (strcmp (interface_name, "password-manager-service") == 0)
323 return _("Read, add, change, or remove saved passwords");
324 else if (strcmp (interface_name, "ppp") == 0)
325 return _("Access pppd and ppp devices for configuring Point-to-Point Protocol connections");
326 else if (strcmp (interface_name, "process-control") == 0)
327 return _("Pause or end any process on the system");
328 else if (strcmp (interface_name, "pulseaudio") == 0)
329 return _("Play and record sound");
330 else if (strcmp (interface_name, "raw-usb") == 0)
331 return _("Access USB hardware directly");
332 else if (strcmp (interface_name, "removable-media") == 0)
333 return _("Read/write files on removable storage devices");
334 else if (strcmp (interface_name, "screen-inhibit-control") == 0)
335 return _("Prevent screen sleep/lock");
336 else if (strcmp (interface_name, "serial-port") == 0)
337 return _("Access serial port hardware");
338 else if (strcmp (interface_name, "shutdown") == 0)
339 return _("Restart or power off the device");
340 else if (strcmp (interface_name, "snapd-control") == 0)
341 return _("Install, remove and configure software");
342 else if (strcmp (interface_name, "storage-framework-service") == 0)
343 return _("Access Storage Framework service");
344 else if (strcmp (interface_name, "system-observe") == 0)
345 return _("Read process and system information");
346 else if (strcmp (interface_name, "system-trace") == 0)
347 return _("Monitor and control any running program");
348 else if (strcmp (interface_name, "time-control") == 0)
349 return _("Change the date and time");
350 else if (strcmp (interface_name, "timeserver-control") == 0)
351 return _("Change time server settings");
352 else if (strcmp (interface_name, "timezone-control") == 0)
353 return _("Change the time zone");
354 else if (strcmp (interface_name, "udisks2") == 0)
355 return _("Access the UDisks2 service for configuring disks and removable media");
356 else if (strcmp (interface_name, "upower-observe") == 0)
357 return _("Access energy usage data");
358 else if (strcmp (interface_name, "u2f-devices") == 0)
359 return _("Read/write access to U2F devices exposed");
360 else
361 return interface_name;
362 }
363
364 static void
365 cc_snap_row_finalize (GObject *object)
366 {
367 CcSnapRow *self = CC_SNAP_ROW (object);
368
369 g_clear_object (&self->cancellable);
370 g_clear_object (&self->client);
371 g_clear_pointer (&self->plug, json_object_unref);
372 g_clear_pointer (&self->slots, json_array_unref);
373 g_clear_pointer (&self->target_slot, json_object_unref);
374 g_clear_pointer (&self->change_id, g_free);
375 g_clear_handle_id (&self->change_timeout, g_source_remove);
376 self->change_timeout = 0;
377
378 G_OBJECT_CLASS (cc_snap_row_parent_class)->finalize (object);
379 }
380
381 static void
382 cc_snap_row_class_init (CcSnapRowClass *klass)
383 {
384 GObjectClass *object_class = G_OBJECT_CLASS (klass);
385 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
386
387 object_class->finalize = cc_snap_row_finalize;
388
389 gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/applications/cc-snap-row.ui");
390
391 gtk_widget_class_bind_template_child (widget_class, CcSnapRow, slot_toggle);
392 gtk_widget_class_bind_template_child (widget_class, CcSnapRow, slots_combo);
393 gtk_widget_class_bind_template_child (widget_class, CcSnapRow, slots_combo_model);
394
395 gtk_widget_class_bind_template_callback (widget_class, combo_changed_cb);
396 gtk_widget_class_bind_template_callback (widget_class, switch_changed_cb);
397 }
398
399 static void
400 cc_snap_row_init (CcSnapRow *self)
401 {
402 gtk_widget_init_template (GTK_WIDGET (self));
403 }
404
405 CcSnapRow *
406 cc_snap_row_new (GCancellable *cancellable, JsonObject *plug, JsonArray *slots)
407 {
408 CcSnapRow *self;
409 const gchar *label = NULL;
410 GtkTreeIter iter;
411
412 self = CC_SNAP_ROW (g_object_new (CC_TYPE_SNAP_ROW, NULL));
413
414 self->cancellable = g_object_ref (cancellable);
415 self->plug = json_object_ref (plug);
416 self->slots = json_array_ref (slots);
417
418 if (json_object_has_member (plug, "connections"))
419 {
420 JsonArray *connected_slots = json_object_get_array_member (plug, "connections");
421 JsonObject *connected_slot_ref = json_array_get_object_element (connected_slots, 0);
422
423 for (guint i = 0; i < json_array_get_length (slots); i++)
424 {
425 JsonObject *slot = json_array_get_object_element (slots, i);
426
427 if (g_strcmp0 (json_object_get_string_member (slot, "snap"),
428 json_object_get_string_member (connected_slot_ref, "snap")) == 0 &&
429 g_strcmp0 (json_object_get_string_member (slot, "slot"),
430 json_object_get_string_member (connected_slot_ref, "slot")) == 0)
431 self->connected_slot = slot;
432 }
433 }
434
435 label = make_interface_label (json_object_get_string_member (plug, "interface"));
436 adw_preferences_row_set_title (ADW_PREFERENCES_ROW (self), label);
437
438 /* Add option into combo box */
439 gtk_list_store_append (self->slots_combo_model, &iter);
440 gtk_list_store_set (self->slots_combo_model, &iter, 1, "--", -1);
441 for (guint i = 0; i < json_array_get_length (slots); i++)
442 {
443 JsonObject *slot = json_array_get_object_element (slots, i);
444 g_autofree gchar *label = NULL;
445
446 label = g_strdup_printf ("%s:%s", json_object_get_string_member (slot, "snap"),
447 json_object_get_string_member (slot, "slot"));
448 gtk_list_store_append (self->slots_combo_model, &iter);
449 gtk_list_store_set (self->slots_combo_model, &iter, 0, slot, 1, label, -1);
450 }
451
452 update_state (self);
453
454 return self;
455 }
456