GCC Code Coverage Report


Directory: ./
File: panels/privacy/bolt/cc-bolt-device-dialog.c
Date: 2024-05-04 07:58:27
Exec Total Coverage
Lines: 0 189 0.0%
Functions: 0 20 0.0%
Branches: 0 85 0.0%

Line Branch Exec Source
1 /* Copyright (C) 2018 Red Hat, Inc
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors: Christian J. Kellner <ckellner@redhat.com>
17 *
18 */
19
20 #include <config.h>
21
22 #include <glib/gi18n.h>
23 #include "cc-list-row.h"
24
25 #include "bolt-device.h"
26 #include "bolt-error.h"
27 #include "bolt-time.h"
28
29 #include "cc-bolt-device-dialog.h"
30 #include "cc-bolt-device-entry.h"
31
32 struct _CcBoltDeviceDialog
33 {
34 AdwWindow parent;
35
36 BoltClient *client;
37 BoltDevice *device;
38 GCancellable *cancel;
39
40 AdwToastOverlay *toast_overlay;
41
42 /* device details */
43 CcListRow *status_row;
44 CcListRow *uuid_row;
45 CcListRow *time_row;
46
47 /* parents */
48 AdwPreferencesGroup *parents_group;
49 GtkListBox *parents_devices;
50
51 /* actions */
52 GtkWidget *button_box;
53 GtkSpinner *spinner;
54 GtkButton *connect_button;
55 GtkButton *forget_button;
56 };
57
58 static void on_forget_button_clicked_cb (CcBoltDeviceDialog *dialog);
59 static void on_connect_button_clicked_cb (CcBoltDeviceDialog *dialog);
60
61 G_DEFINE_TYPE (CcBoltDeviceDialog, cc_bolt_device_dialog, ADW_TYPE_WINDOW);
62
63 #define RESOURCE_UI "/org/gnome/control-center/privacy/bolt/cc-bolt-device-dialog.ui"
64
65 static const char *
66 status_to_string_for_ui (BoltDevice *dev)
67 {
68 BoltStatus status;
69 BoltAuthFlags aflags;
70 gboolean nopcie;
71
72 status = bolt_device_get_status (dev);
73 aflags = bolt_device_get_authflags(dev);
74 nopcie = bolt_flag_isset (aflags, BOLT_AUTH_NOPCIE);
75
76 switch (status)
77 {
78 case BOLT_STATUS_DISCONNECTED:
79 return C_("Thunderbolt Device Status", "Disconnected");
80
81 case BOLT_STATUS_CONNECTING:
82 return C_("Thunderbolt Device Status", "Connecting");
83
84 case BOLT_STATUS_CONNECTED:
85 return C_("Thunderbolt Device Status", "Connected");
86
87 case BOLT_STATUS_AUTH_ERROR:
88 return C_("Thunderbolt Device Status", "Authorization Error");
89
90 case BOLT_STATUS_AUTHORIZING:
91 return C_("Thunderbolt Device Status", "Authorizing");
92
93 case BOLT_STATUS_AUTHORIZED:
94 case BOLT_STATUS_AUTHORIZED_NEWKEY:
95 case BOLT_STATUS_AUTHORIZED_SECURE:
96 case BOLT_STATUS_AUTHORIZED_DPONLY:
97 if (nopcie)
98 return C_("Thunderbolt Device Status", "Reduced Functionality");
99 else
100 return C_("Thunderbolt Device Status", "Connected & Authorized");
101
102 case BOLT_STATUS_UNKNOWN:
103 break; /* use default return value, i.e. Unknown */
104 }
105
106 return C_("Thunderbolt Device Status", "Unknown");
107 }
108
109 static void
110 dialog_update_from_device (CcBoltDeviceDialog *dialog)
111 {
112 g_autofree char *generated = NULL;
113 g_autofree char *timestr = NULL;
114 const char *label;
115 const char *uuid;
116 const char *status_brief;
117 BoltStatus status;
118 gboolean stored;
119 BoltDevice *dev;
120 guint timestamp;
121
122 if (gtk_widget_in_destruction (GTK_WIDGET (dialog)))
123 return;
124
125 dev = dialog->device;
126
127 uuid = bolt_device_get_uid (dev);
128 label = bolt_device_get_label (dev);
129
130 stored = bolt_device_is_stored (dev);
131 status = bolt_device_get_status (dev);
132
133 if (label == NULL)
134 {
135 const char *name = bolt_device_get_name (dev);
136 const char *vendor = bolt_device_get_vendor (dev);
137
138 generated = g_strdup_printf ("%s %s", name, vendor);
139 label = generated;
140 }
141
142 gtk_window_set_title (GTK_WINDOW (dialog), label);
143
144 status_brief = status_to_string_for_ui (dev);
145 cc_list_row_set_secondary_label (dialog->status_row, status_brief);
146 gtk_widget_set_visible (GTK_WIDGET (dialog->forget_button), stored);
147
148 /* while we are having an ongoing operation we are setting the buttons
149 * to be in-sensitive. In that case, if the button was visible
150 * before it will be hidden when the operation is finished by the
151 * dialog_operation_done() function */
152 if (gtk_widget_is_sensitive (GTK_WIDGET (dialog->connect_button)))
153 gtk_widget_set_visible (GTK_WIDGET (dialog->connect_button),
154 status == BOLT_STATUS_CONNECTED);
155
156 cc_list_row_set_secondary_label (dialog->uuid_row, uuid);
157
158 if (bolt_status_is_authorized (status))
159 {
160 /* Translators: The time point the device was authorized. */
161 adw_preferences_row_set_title (ADW_PREFERENCES_ROW (dialog->time_row), _("Authorized at"));
162 timestamp = bolt_device_get_authtime (dev);
163 }
164 else if (bolt_status_is_connected (status))
165 {
166 /* Translators: The time point the device was connected. */
167 adw_preferences_row_set_title (ADW_PREFERENCES_ROW (dialog->time_row), _("Connected at"));
168 timestamp = bolt_device_get_conntime (dev);
169 }
170 else
171 {
172 /* Translators: The time point the device was enrolled,
173 * i.e. authorized and stored in the device database. */
174 adw_preferences_row_set_title (ADW_PREFERENCES_ROW (dialog->time_row), _("Enrolled at"));
175 timestamp = bolt_device_get_storetime (dev);
176 }
177
178 timestr = bolt_epoch_format (timestamp, "%c");
179 cc_list_row_set_secondary_label (dialog->time_row, timestr);
180
181 }
182
183 static void
184 on_device_notify_cb (GObject *gobject,
185 GParamSpec *pspec,
186 gpointer user_data)
187 {
188 CcBoltDeviceDialog *dialog = CC_BOLT_DEVICE_DIALOG (user_data);
189
190 dialog_update_from_device (dialog);
191 }
192
193 static void
194 dialog_operation_start (CcBoltDeviceDialog *dialog)
195 {
196 gtk_widget_set_sensitive (GTK_WIDGET (dialog->connect_button), FALSE);
197 gtk_widget_set_sensitive (GTK_WIDGET (dialog->forget_button), FALSE);
198 gtk_spinner_start (dialog->spinner);
199 }
200
201 static void
202 dialog_operation_done (CcBoltDeviceDialog *dialog,
203 GtkWidget *sender,
204 GError *error)
205 {
206 GtkWidget *cb = GTK_WIDGET (dialog->connect_button);
207 GtkWidget *fb = GTK_WIDGET (dialog->forget_button);
208
209 /* don' do anything if we are being destroyed */
210 if (gtk_widget_in_destruction (GTK_WIDGET (dialog)))
211 return;
212
213 /* also don't do anything if the op was canceled */
214 if (error != NULL && bolt_err_cancelled (error))
215 return;
216
217 gtk_spinner_stop (dialog->spinner);
218
219 if (error != NULL)
220 {
221 AdwToast *toast = adw_toast_new (error->message);
222 adw_toast_overlay_add_toast (dialog->toast_overlay, toast);
223
224 /* set the *other* button to sensitive */
225 gtk_widget_set_sensitive (cb, cb != sender);
226 gtk_widget_set_sensitive (fb, fb != sender);
227 }
228 else
229 {
230 gtk_widget_set_visible (sender, FALSE);
231 gtk_widget_set_sensitive (cb, TRUE);
232 gtk_widget_set_sensitive (fb, TRUE);
233 }
234 }
235
236 static void
237 on_connect_all_done (GObject *source_object,
238 GAsyncResult *res,
239 gpointer user_data)
240 {
241 g_autoptr(GError) err = NULL;
242 CcBoltDeviceDialog *dialog = CC_BOLT_DEVICE_DIALOG (user_data);
243 gboolean ok;
244
245 ok = bolt_client_connect_all_finish (dialog->client, res, &err);
246
247 if (!ok)
248 g_prefix_error (&err, _("Failed to authorize device: "));
249
250 dialog_operation_done (dialog, GTK_WIDGET (dialog->connect_button), err);
251 }
252
253 static void
254 on_connect_button_clicked_cb (CcBoltDeviceDialog *dialog)
255 {
256 g_autoptr(GPtrArray) devices = NULL;
257 BoltDevice *device = dialog->device;
258 GtkWidget *child;
259
260 g_return_if_fail (device != NULL);
261
262 dialog_operation_start (dialog);
263
264 devices = g_ptr_array_new ();
265
266 /* Iter from the last child to the first one */
267 for (child = gtk_widget_get_last_child (GTK_WIDGET (dialog->parents_devices));
268 child;
269 child = gtk_widget_get_prev_sibling (child))
270 {
271 CcBoltDeviceEntry *entry;
272 BoltDevice *dev;
273 BoltStatus status;
274
275 entry = CC_BOLT_DEVICE_ENTRY (child);
276 dev = cc_bolt_device_entry_get_device (entry);
277 status = bolt_device_get_status (dev);
278
279 /* skip any devices down in the chain that are already authorized
280 * NB: it is not possible to have gaps of non-authorized devices
281 * in the chain, i.e. once we encounter a non-authorized device,
282 * all following device (down the chain, towards the target) will
283 * also be not authorized. */
284 if (!bolt_status_is_pending (status))
285 continue;
286
287 /* device is now either !stored || pending */
288 g_ptr_array_add (devices, dev);
289 }
290
291 /* finally the actual device of the dialog */
292 g_ptr_array_add (devices, device);
293
294 bolt_client_connect_all_async (dialog->client,
295 devices,
296 BOLT_POLICY_DEFAULT,
297 BOLT_AUTHCTRL_NONE,
298 dialog->cancel,
299 on_connect_all_done,
300 dialog);
301 }
302
303 static void
304 on_forget_device_done (GObject *source_object,
305 GAsyncResult *res,
306 gpointer user_data)
307 {
308 g_autoptr(GError) err = NULL;
309 CcBoltDeviceDialog *dialog = CC_BOLT_DEVICE_DIALOG (user_data);
310 gboolean ok;
311
312 ok = bolt_client_forget_device_finish (dialog->client, res, &err);
313
314 if (!ok)
315 g_prefix_error (&err, _("Failed to forget device: "));
316
317 dialog_operation_done (dialog, GTK_WIDGET (dialog->forget_button), err);
318 }
319
320 static void
321 on_forget_button_clicked_cb (CcBoltDeviceDialog *dialog)
322 {
323 const char *uid = NULL;
324
325 g_return_if_fail (dialog->device != NULL);
326
327 uid = bolt_device_get_uid (dialog->device);
328 dialog_operation_start (dialog);
329
330 bolt_client_forget_device_async (dialog->client,
331 uid,
332 dialog->cancel,
333 on_forget_device_done,
334 dialog);
335 }
336
337 static void
338 cc_bolt_device_dialog_finalize (GObject *object)
339 {
340 CcBoltDeviceDialog *dialog = CC_BOLT_DEVICE_DIALOG (object);
341
342 g_clear_object (&dialog->device);
343 g_cancellable_cancel (dialog->cancel);
344 g_clear_object (&dialog->cancel);
345 g_clear_object (&dialog->client);
346
347 G_OBJECT_CLASS (cc_bolt_device_dialog_parent_class)->finalize (object);
348 }
349
350 static void
351 cc_bolt_device_dialog_class_init (CcBoltDeviceDialogClass *klass)
352 {
353 GObjectClass *object_class = G_OBJECT_CLASS (klass);
354 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
355
356 object_class->finalize = cc_bolt_device_dialog_finalize;
357
358 gtk_widget_class_set_template_from_resource (widget_class, RESOURCE_UI);
359
360 gtk_widget_class_bind_template_child (widget_class, CcBoltDeviceDialog, toast_overlay);
361
362 gtk_widget_class_bind_template_child (widget_class, CcBoltDeviceDialog, status_row);
363
364 gtk_widget_class_bind_template_child (widget_class, CcBoltDeviceDialog, uuid_row);
365 gtk_widget_class_bind_template_child (widget_class, CcBoltDeviceDialog, time_row);
366
367 gtk_widget_class_bind_template_child (widget_class, CcBoltDeviceDialog, parents_devices);
368 gtk_widget_class_bind_template_child (widget_class, CcBoltDeviceDialog, parents_group);
369
370 gtk_widget_class_bind_template_child (widget_class, CcBoltDeviceDialog, button_box);
371 gtk_widget_class_bind_template_child (widget_class, CcBoltDeviceDialog, spinner);
372 gtk_widget_class_bind_template_child (widget_class, CcBoltDeviceDialog, connect_button);
373 gtk_widget_class_bind_template_child (widget_class, CcBoltDeviceDialog, forget_button);
374
375 gtk_widget_class_bind_template_callback (widget_class, on_connect_button_clicked_cb);
376 gtk_widget_class_bind_template_callback (widget_class, on_forget_button_clicked_cb);
377 }
378
379 static void
380 cc_bolt_device_dialog_init (CcBoltDeviceDialog *dialog)
381 {
382 gtk_widget_init_template (GTK_WIDGET (dialog));
383 }
384
385 /* public functions */
386 CcBoltDeviceDialog *
387 cc_bolt_device_dialog_new (void)
388 {
389 CcBoltDeviceDialog *dialog;
390
391 dialog = g_object_new (CC_TYPE_BOLT_DEVICE_DIALOG,
392 NULL);
393 return dialog;
394 }
395
396 void
397 cc_bolt_device_dialog_set_client (CcBoltDeviceDialog *dialog,
398 BoltClient *client)
399 {
400 g_clear_object (&dialog->client);
401 dialog->client = g_object_ref (client);
402 }
403
404 void
405 cc_bolt_device_dialog_set_device (CcBoltDeviceDialog *dialog,
406 BoltDevice *device,
407 GPtrArray *parents)
408 {
409 g_autofree char *msg = NULL;
410 guint i;
411
412 if (device == dialog->device)
413 return;
414
415 if (dialog->device)
416 {
417 GtkWidget *child;
418
419 g_cancellable_cancel (dialog->cancel);
420 g_clear_object (&dialog->cancel);
421 dialog->cancel = g_cancellable_new ();
422
423 g_signal_handlers_disconnect_by_func (dialog->device,
424 G_CALLBACK (on_device_notify_cb),
425 dialog);
426 g_clear_object (&dialog->device);
427
428 while ((child = gtk_widget_get_first_child (GTK_WIDGET (dialog->parents_devices))) != NULL)
429 gtk_list_box_remove (dialog->parents_devices, child);
430
431 gtk_widget_set_visible (GTK_WIDGET (dialog->parents_group), FALSE);
432 }
433
434 if (device == NULL)
435 return;
436
437 dialog->device = g_object_ref (device);
438 g_signal_connect_object (dialog->device,
439 "notify",
440 G_CALLBACK (on_device_notify_cb),
441 dialog,
442 0);
443
444 /* reset the sensitivity of the buttons, because
445 * dialog_update_from_device, because it can't know */
446 gtk_widget_set_sensitive (GTK_WIDGET (dialog->connect_button), TRUE);
447 gtk_widget_set_sensitive (GTK_WIDGET (dialog->forget_button), TRUE);
448
449 dialog_update_from_device (dialog);
450
451 /* no parents, we are done here */
452 if (!parents || parents->len == 0)
453 return;
454
455 msg = g_strdup_printf (ngettext ("Depends on %u other device",
456 "Depends on %u other devices",
457 parents->len), parents->len);
458
459 adw_preferences_group_set_title (dialog->parents_group, msg);
460 gtk_widget_set_visible (GTK_WIDGET (dialog->parents_group), TRUE);
461
462 for (i = 0; i < parents->len; i++)
463 {
464 CcBoltDeviceEntry *entry;
465 BoltDevice *parent;
466
467 parent = g_ptr_array_index (parents, i);
468
469 entry = cc_bolt_device_entry_new (parent, TRUE);
470 gtk_list_box_append (dialog->parents_devices, GTK_WIDGET (entry));
471 }
472 }
473
474 BoltDevice *
475 cc_bolt_device_dialog_peek_device (CcBoltDeviceDialog *dialog)
476 {
477 return dialog->device;
478 }
479
480 gboolean
481 cc_bolt_device_dialog_device_equal (CcBoltDeviceDialog *dialog,
482 BoltDevice *device)
483 {
484 return dialog->device != NULL && device == dialog->device;
485 }
486