GCC Code Coverage Report


Directory: ./
File: panels/network/connection-editor/ce-page-ip6.c
Date: 2024-05-03 09:46:52
Exec Total Coverage
Lines: 0 444 0.0%
Functions: 0 25 0.0%
Branches: 0 205 0.0%

Line Branch Exec Source
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
2 *
3 * Copyright (C) 2012 Red Hat, Inc
4 *
5 * Licensed under the GNU General Public License Version 2
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 */
21
22 #include "config.h"
23
24 #include <errno.h>
25 #include <stdlib.h>
26 #include <arpa/inet.h>
27 #include <glib/gi18n.h>
28 #include <NetworkManager.h>
29
30 #include "ce-ip-address-entry.h"
31 #include "ce-page.h"
32 #include "ce-page-ip6.h"
33 #include "ui-helpers.h"
34
35 static void ensure_empty_address_row (CEPageIP6 *self);
36 static void ensure_empty_routes_row (CEPageIP6 *self);
37
38
39 struct _CEPageIP6
40 {
41 AdwBin parent;
42
43 GtkBox *address_box;
44 GtkLabel *address_address_label;
45 GtkLabel *address_prefix_label;
46 GtkLabel *address_gateway_label;
47 GtkSizeGroup *address_sizegroup;
48 GtkLabel *auto_dns_label;
49 GtkSwitch *auto_dns_switch;
50 GtkLabel *auto_routes_label;
51 GtkSwitch *auto_routes_switch;
52 GtkBox *content_box;
53 GtkCheckButton *disabled_radio;
54 GtkBox *dns_box;
55 GtkEntry *dns_entry;
56 GtkGrid *main_box;
57 GtkCheckButton *never_default_check;
58 GtkBox *routes_box;
59 GtkBox *route_config_box;
60 GtkLabel *routes_address_label;
61 GtkLabel *routes_prefix_label;
62 GtkLabel *routes_gateway_label;
63 GtkLabel *routes_metric_label;
64 GtkSizeGroup *routes_address_sizegroup;
65 GtkSizeGroup *routes_prefix_sizegroup;
66 GtkSizeGroup *routes_gateway_sizegroup;
67 GtkSizeGroup *routes_metric_sizegroup;
68 GtkSizeGroup *routes_sizegroup;
69 GtkCheckButton *shared_radio;
70
71 NMSettingIPConfig *setting;
72
73 GtkWidget *address_list;
74 GtkWidget *routes_list;
75
76 GActionGroup *method_group;
77 };
78
79 static void ce_page_iface_init (CEPageInterface *);
80
81 G_DEFINE_TYPE_WITH_CODE (CEPageIP6, ce_page_ip6, ADW_TYPE_BIN,
82 G_IMPLEMENT_INTERFACE (ce_page_get_type (), ce_page_iface_init))
83
84 enum {
85 METHOD_COL_NAME,
86 METHOD_COL_METHOD
87 };
88
89 static void
90 sync_dns_entry_warning (CEPageIP6 *self)
91 {
92 g_autoptr(GVariant) method_variant = NULL;
93 const gchar *method;
94
95 method_variant = g_action_group_get_action_state (self->method_group, "ip6method");
96 method = g_variant_get_string (method_variant, NULL);
97
98 if (gtk_entry_get_text_length (self->dns_entry) &&
99 gtk_switch_get_active (self->auto_dns_switch) &&
100 (g_strcmp0 (method, "automatic") == 0 || g_strcmp0 (method, "dhcp") == 0)) {
101 gtk_entry_set_icon_from_icon_name (self->dns_entry, GTK_ENTRY_ICON_SECONDARY, "dialog-warning-symbolic");
102 gtk_entry_set_icon_tooltip_text (self->dns_entry, GTK_ENTRY_ICON_SECONDARY, _("Automatic DNS is enabled. Did you intend to disable Automatic DNS?"));
103 gtk_widget_add_css_class (GTK_WIDGET (self->dns_entry), "warning");
104 } else {
105 gtk_entry_set_icon_from_icon_name (self->dns_entry, GTK_ENTRY_ICON_SECONDARY, NULL);
106 gtk_entry_set_icon_tooltip_text (self->dns_entry, GTK_ENTRY_ICON_SECONDARY, NULL);
107 gtk_widget_remove_css_class (GTK_WIDGET (self->dns_entry), "warning");
108 }
109 }
110
111 static void
112 method_changed (CEPageIP6 *self)
113 {
114 gboolean addr_enabled;
115 gboolean dns_enabled;
116 gboolean routes_enabled;
117 gboolean auto_dns_enabled;
118 gboolean auto_routes_enabled;
119 g_autoptr(GVariant) method_variant = NULL;
120 const gchar *method;
121
122 method_variant = g_action_group_get_action_state (self->method_group, "ip6method");
123 method = g_variant_get_string (method_variant, NULL);
124
125 if (g_str_equal (method, "disabled") ||
126 g_str_equal (method, "shared")) {
127 addr_enabled = FALSE;
128 dns_enabled = FALSE;
129 routes_enabled = FALSE;
130 auto_dns_enabled = FALSE;
131 auto_routes_enabled = FALSE;
132 } else {
133 addr_enabled = g_str_equal (method, "manual");
134 dns_enabled = !g_str_equal (method, "local");
135 routes_enabled = !g_str_equal (method, "local");
136 auto_dns_enabled = g_str_equal (method, "automatic") || g_str_equal (method, "dhcp");
137 auto_routes_enabled = g_str_equal (method, "automatic");
138 }
139
140 gtk_widget_set_visible (GTK_WIDGET (self->address_box), addr_enabled);
141 gtk_widget_set_sensitive (GTK_WIDGET (self->dns_box), dns_enabled);
142 gtk_widget_set_sensitive (GTK_WIDGET (self->routes_box), routes_enabled);
143
144 gtk_widget_set_sensitive (GTK_WIDGET (self->auto_dns_label), auto_dns_enabled);
145 gtk_widget_set_sensitive (GTK_WIDGET (self->auto_dns_switch), auto_dns_enabled);
146 gtk_widget_set_sensitive (GTK_WIDGET (self->auto_routes_label), auto_routes_enabled);
147 gtk_widget_set_sensitive (GTK_WIDGET (self->auto_routes_switch), auto_routes_enabled);
148
149 sync_dns_entry_warning (self);
150
151 ce_page_changed (CE_PAGE (self));
152 }
153
154 static void
155 update_row_sensitivity (CEPageIP6 *self, GtkWidget *list)
156 {
157 GtkWidget *child;
158 gint rows = 0, i = 0;
159
160 for (child = gtk_widget_get_first_child (GTK_WIDGET (list));
161 child;
162 child = gtk_widget_get_next_sibling (child)) {
163 GtkWidget *button;
164
165 button = GTK_WIDGET (g_object_get_data (G_OBJECT (child), "delete-button"));
166 if (button != NULL)
167 rows++;
168 }
169 for (child = gtk_widget_get_first_child (GTK_WIDGET (list));
170 child;
171 child = gtk_widget_get_next_sibling (child)) {
172 GtkWidget *button;
173
174 button = GTK_WIDGET (g_object_get_data (G_OBJECT (child), "delete-button"));
175 if (button != NULL)
176 gtk_widget_set_sensitive (button, rows > 1 && ++i < rows);
177 }
178 }
179
180 static void
181 remove_row (CEPageIP6 *self, GtkButton *button)
182 {
183 GtkWidget *row;
184 GtkWidget *row_box;
185 GtkWidget *list;
186
187 row_box = gtk_widget_get_parent (GTK_WIDGET (button));
188 row = gtk_widget_get_parent (row_box);
189 list = gtk_widget_get_parent (row);
190
191 gtk_list_box_remove (GTK_LIST_BOX (list), row);
192
193 ce_page_changed (CE_PAGE (self));
194
195 update_row_sensitivity (self, list);
196 }
197
198 static gboolean
199 validate_row (GtkWidget *row)
200 {
201 GtkWidget *child;
202 GtkWidget *box;
203 gboolean valid;
204
205 valid = FALSE;
206 box = gtk_list_box_row_get_child (GTK_LIST_BOX_ROW (row));
207
208 for (child = gtk_widget_get_first_child (box);
209 child;
210 child = gtk_widget_get_next_sibling (child)) {
211 if (!GTK_IS_ENTRY (child))
212 continue;
213
214 valid = valid || gtk_entry_get_text_length (GTK_ENTRY (child)) > 0;
215 }
216
217 return valid;
218 }
219
220 static void
221 add_address_row (CEPageIP6 *self,
222 const gchar *address,
223 const gchar *network,
224 const gchar *gateway)
225 {
226 GtkWidget *row;
227 GtkWidget *row_box;
228 GtkWidget *widget;
229 GtkWidget *delete_button;
230
231 row = gtk_list_box_row_new ();
232 gtk_list_box_row_set_activatable (GTK_LIST_BOX_ROW (row), FALSE);
233
234 row_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
235 gtk_widget_add_css_class (row_box, "linked");
236
237 widget = GTK_WIDGET (ce_ip_address_entry_new (AF_INET6));
238 g_signal_connect_object (widget, "changed", G_CALLBACK (ce_page_changed), self, G_CONNECT_SWAPPED);
239 g_signal_connect_object (widget, "activate", G_CALLBACK (ensure_empty_address_row), self, G_CONNECT_SWAPPED);
240 g_object_set_data (G_OBJECT (row), "address", widget);
241 gtk_editable_set_text (GTK_EDITABLE (widget), address);
242 gtk_editable_set_width_chars (GTK_EDITABLE (widget), 16);
243 gtk_widget_set_hexpand (widget, TRUE);
244 gtk_accessible_update_relation (GTK_ACCESSIBLE (widget),
245 GTK_ACCESSIBLE_RELATION_LABELLED_BY, self->address_address_label, NULL,
246 -1);
247 gtk_box_append (GTK_BOX (row_box), widget);
248
249 widget = gtk_entry_new ();
250 g_signal_connect_object (widget, "changed", G_CALLBACK (ce_page_changed), self, G_CONNECT_SWAPPED);
251 g_signal_connect_object (widget, "activate", G_CALLBACK (ensure_empty_address_row), self, G_CONNECT_SWAPPED);
252 g_object_set_data (G_OBJECT (row), "prefix", widget);
253 gtk_editable_set_text (GTK_EDITABLE (widget), network);
254 gtk_editable_set_width_chars (GTK_EDITABLE (widget), 16);
255 gtk_widget_set_hexpand (widget, TRUE);
256 gtk_accessible_update_relation (GTK_ACCESSIBLE (widget),
257 GTK_ACCESSIBLE_RELATION_LABELLED_BY, self->address_prefix_label, NULL,
258 -1);
259 gtk_box_append (GTK_BOX (row_box), widget);
260
261 widget = GTK_WIDGET (ce_ip_address_entry_new (AF_INET6));
262 g_signal_connect_object (widget, "changed", G_CALLBACK (ce_page_changed), self, G_CONNECT_SWAPPED);
263 g_signal_connect_object (widget, "activate", G_CALLBACK (ensure_empty_address_row), self, G_CONNECT_SWAPPED);
264 g_object_set_data (G_OBJECT (row), "gateway", widget);
265 gtk_editable_set_text (GTK_EDITABLE (widget), gateway ? gateway : "");
266 gtk_editable_set_width_chars (GTK_EDITABLE (widget), 16);
267 gtk_widget_set_hexpand (widget, TRUE);
268 gtk_accessible_update_relation (GTK_ACCESSIBLE (widget),
269 GTK_ACCESSIBLE_RELATION_LABELLED_BY, self->address_gateway_label, NULL,
270 -1);
271 gtk_box_append (GTK_BOX (row_box), widget);
272
273 delete_button = gtk_button_new_from_icon_name ("edit-delete-symbolic");
274 gtk_widget_set_sensitive (delete_button, FALSE);
275 g_signal_connect_object (delete_button, "clicked", G_CALLBACK (remove_row), self, G_CONNECT_SWAPPED);
276 gtk_accessible_update_property (GTK_ACCESSIBLE (delete_button),
277 GTK_ACCESSIBLE_PROPERTY_LABEL, _("Delete Address"),
278 -1);
279 gtk_box_append (GTK_BOX (row_box), delete_button);
280 g_object_set_data (G_OBJECT (row), "delete-button", delete_button);
281
282 gtk_size_group_add_widget (self->address_sizegroup, delete_button);
283
284 gtk_list_box_row_set_child (GTK_LIST_BOX_ROW (row), row_box);
285 gtk_list_box_append (GTK_LIST_BOX (self->address_list), row);
286
287 update_row_sensitivity (self, self->address_list);
288 }
289
290 static void
291 ensure_empty_address_row (CEPageIP6 *self)
292 {
293 GtkWidget *child = gtk_widget_get_last_child (self->address_list);
294
295 /* Add the last, stub row if needed*/
296 if (!child || validate_row (child))
297 add_address_row (self, "", "", "");
298 }
299
300 static void
301 add_address_box (CEPageIP6 *self)
302 {
303 GtkWidget *list;
304 gint i;
305
306 self->address_list = list = gtk_list_box_new ();
307 gtk_list_box_set_selection_mode (GTK_LIST_BOX (list), GTK_SELECTION_NONE);
308 gtk_box_append (self->address_box, list);
309
310 for (i = 0; i < nm_setting_ip_config_get_num_addresses (self->setting); i++) {
311 NMIPAddress *addr;
312 g_autofree gchar *netmask = NULL;
313
314 addr = nm_setting_ip_config_get_address (self->setting, i);
315 netmask = g_strdup_printf ("%u", nm_ip_address_get_prefix (addr));
316 add_address_row (self, nm_ip_address_get_address (addr), netmask,
317 i == 0 ? nm_setting_ip_config_get_gateway (self->setting) : NULL);
318 }
319 if (nm_setting_ip_config_get_num_addresses (self->setting) == 0)
320 ensure_empty_address_row (self);
321 }
322
323 static void
324 add_dns_section (CEPageIP6 *self)
325 {
326 GString *string;
327 gint i;
328
329 gtk_switch_set_active (self->auto_dns_switch, !nm_setting_ip_config_get_ignore_auto_dns (self->setting));
330 g_signal_connect_object (self->auto_dns_switch, "notify::active", G_CALLBACK (ce_page_changed), self, G_CONNECT_SWAPPED);
331 g_signal_connect_object (self->auto_dns_switch, "notify::active", G_CALLBACK (sync_dns_entry_warning), self, G_CONNECT_SWAPPED);
332
333 string = g_string_new ("");
334
335 for (i = 0; i < nm_setting_ip_config_get_num_dns (self->setting); i++) {
336 const char *address;
337
338 address = nm_setting_ip_config_get_dns (self->setting, i);
339
340 if (i > 0)
341 g_string_append (string, ", ");
342
343 g_string_append (string, address);
344
345 }
346
347 gtk_editable_set_text (GTK_EDITABLE (self->dns_entry), string->str);
348
349 g_signal_connect_object (self->dns_entry, "notify::text", G_CALLBACK (ce_page_changed), self, G_CONNECT_SWAPPED);
350 g_signal_connect_object (self->dns_entry, "notify::text", G_CALLBACK (sync_dns_entry_warning), self, G_CONNECT_SWAPPED);
351
352 sync_dns_entry_warning (self);
353
354 g_string_free (string, TRUE);
355 }
356
357 static void
358 add_route_row (CEPageIP6 *self,
359 const gchar *address,
360 const gchar *prefix,
361 const gchar *gateway,
362 const gchar *metric)
363 {
364 GtkWidget *row;
365 GtkWidget *row_box;
366 GtkWidget *widget;
367 GtkWidget *delete_button;
368
369 row = gtk_list_box_row_new ();
370 gtk_list_box_row_set_activatable (GTK_LIST_BOX_ROW (row), FALSE);
371
372 row_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
373 gtk_widget_add_css_class (row_box, "linked");
374
375 widget = GTK_WIDGET (ce_ip_address_entry_new (AF_INET6));
376 g_signal_connect_object (widget, "changed", G_CALLBACK (ce_page_changed), self, G_CONNECT_SWAPPED);
377 g_signal_connect_object (widget, "activate", G_CALLBACK (ensure_empty_routes_row), self, G_CONNECT_SWAPPED);
378 g_object_set_data (G_OBJECT (row), "address", widget);
379 gtk_editable_set_text (GTK_EDITABLE (widget), address);
380 gtk_editable_set_width_chars (GTK_EDITABLE (widget), 16);
381 gtk_widget_set_hexpand (widget, TRUE);
382 gtk_accessible_update_relation (GTK_ACCESSIBLE (widget),
383 GTK_ACCESSIBLE_RELATION_LABELLED_BY, self->routes_address_label, NULL,
384 -1);
385 gtk_box_append (GTK_BOX (row_box), widget);
386
387 gtk_size_group_add_widget (self->routes_address_sizegroup, widget);
388
389 widget = gtk_entry_new ();
390 g_signal_connect_object (widget, "changed", G_CALLBACK (ce_page_changed), self, G_CONNECT_SWAPPED);
391 g_signal_connect_object (widget, "activate", G_CALLBACK (ensure_empty_routes_row), self, G_CONNECT_SWAPPED);
392 g_object_set_data (G_OBJECT (row), "prefix", widget);
393 gtk_editable_set_text (GTK_EDITABLE (widget), prefix ? prefix : "");
394 gtk_editable_set_width_chars (GTK_EDITABLE (widget), 16);
395 gtk_widget_set_hexpand (widget, TRUE);
396 gtk_accessible_update_relation (GTK_ACCESSIBLE (widget),
397 GTK_ACCESSIBLE_RELATION_LABELLED_BY, self->routes_prefix_label, NULL,
398 -1);
399 gtk_box_append (GTK_BOX (row_box), widget);
400
401 gtk_size_group_add_widget (self->routes_prefix_sizegroup, widget);
402
403 widget = GTK_WIDGET (ce_ip_address_entry_new (AF_INET6));
404 g_signal_connect_object (widget, "changed", G_CALLBACK (ce_page_changed), self, G_CONNECT_SWAPPED);
405 g_signal_connect_object (widget, "activate", G_CALLBACK (ensure_empty_routes_row), self, G_CONNECT_SWAPPED);
406 g_object_set_data (G_OBJECT (row), "gateway", widget);
407 gtk_editable_set_text (GTK_EDITABLE (widget), gateway);
408 gtk_editable_set_width_chars (GTK_EDITABLE (widget), 16);
409 gtk_widget_set_hexpand (widget, TRUE);
410 gtk_accessible_update_relation (GTK_ACCESSIBLE (widget),
411 GTK_ACCESSIBLE_RELATION_LABELLED_BY, self->routes_gateway_label, NULL,
412 -1);
413 gtk_box_append (GTK_BOX (row_box), widget);
414
415 gtk_size_group_add_widget (self->routes_gateway_sizegroup, widget);
416
417 widget = gtk_entry_new ();
418 g_signal_connect_object (widget, "changed", G_CALLBACK (ce_page_changed), self, G_CONNECT_SWAPPED);
419 g_signal_connect_object (widget, "activate", G_CALLBACK (ensure_empty_routes_row), self, G_CONNECT_SWAPPED);
420 g_object_set_data (G_OBJECT (row), "metric", widget);
421 gtk_editable_set_text (GTK_EDITABLE (widget), metric ? metric : "");
422 gtk_editable_set_width_chars (GTK_EDITABLE (widget), 5);
423 gtk_widget_set_hexpand (widget, TRUE);
424 gtk_accessible_update_relation (GTK_ACCESSIBLE (widget),
425 GTK_ACCESSIBLE_RELATION_LABELLED_BY, self->routes_prefix_label, NULL,
426 -1);
427 gtk_box_append (GTK_BOX (row_box), widget);
428
429 gtk_size_group_add_widget (self->routes_metric_sizegroup, widget);
430
431 delete_button = gtk_button_new_from_icon_name ("edit-delete-symbolic");
432 g_signal_connect_object (delete_button, "clicked", G_CALLBACK (remove_row), self, G_CONNECT_SWAPPED);
433 gtk_accessible_update_property (GTK_ACCESSIBLE (delete_button),
434 GTK_ACCESSIBLE_PROPERTY_LABEL, _("Delete Route"),
435 -1);
436 gtk_widget_set_halign (delete_button, GTK_ALIGN_CENTER);
437 gtk_widget_set_valign (delete_button, GTK_ALIGN_CENTER);
438 gtk_box_append (GTK_BOX (row_box), delete_button);
439 g_object_set_data (G_OBJECT (row), "delete-button", delete_button);
440
441 gtk_size_group_add_widget (self->routes_sizegroup, delete_button);
442
443 gtk_list_box_row_set_child (GTK_LIST_BOX_ROW (row), row_box);
444 gtk_list_box_append (GTK_LIST_BOX (self->routes_list), row);
445
446 update_row_sensitivity (self, self->routes_list);
447 }
448
449 static void
450 ensure_empty_routes_row (CEPageIP6 *self)
451 {
452 GtkWidget *child = gtk_widget_get_last_child (self->routes_list);
453
454 /* Add the last, stub row if needed*/
455 if (!child || validate_row (child))
456 add_route_row (self, "", NULL, "", NULL);
457 }
458
459 static void
460 add_empty_route_row (CEPageIP6 *self)
461 {
462 add_route_row (self, "", NULL, "", NULL);
463 }
464
465 static void
466 add_route_config_box (CEPageIP6 *self)
467 {
468 GtkWidget *list;
469 gint i;
470
471 self->routes_list = list = gtk_list_box_new ();
472 gtk_list_box_set_selection_mode (GTK_LIST_BOX (list), GTK_SELECTION_NONE);
473 gtk_box_append (self->route_config_box, list);
474 gtk_switch_set_active (self->auto_routes_switch, !nm_setting_ip_config_get_ignore_auto_routes (self->setting));
475 g_signal_connect_object (self->auto_routes_switch, "notify::active", G_CALLBACK (ce_page_changed), self, G_CONNECT_SWAPPED);
476
477 for (i = 0; i < nm_setting_ip_config_get_num_routes (self->setting); i++) {
478 NMIPRoute *route;
479 g_autofree gchar *prefix = NULL;
480 g_autofree gchar *metric = NULL;
481
482 route = nm_setting_ip_config_get_route (self->setting, i);
483 prefix = g_strdup_printf ("%u", nm_ip_route_get_prefix (route));
484 metric = g_strdup_printf ("%" G_GINT64_FORMAT, nm_ip_route_get_metric (route));
485 add_route_row (self, nm_ip_route_get_dest (route),
486 prefix,
487 nm_ip_route_get_next_hop (route),
488 metric);
489 }
490 if (nm_setting_ip_config_get_num_routes (self->setting) == 0)
491 add_empty_route_row (self);
492 }
493
494 static void
495 connect_ip6_page (CEPageIP6 *self)
496 {
497 const gchar *str_method;
498 gchar *method;
499
500 add_address_box (self);
501 add_dns_section (self);
502 add_route_config_box (self);
503
504 str_method = nm_setting_ip_config_get_method (self->setting);
505
506 method = "automatic";
507 if (g_strcmp0 (str_method, NM_SETTING_IP6_CONFIG_METHOD_DHCP) == 0) {
508 method = "dhcp";
509 } else if (g_strcmp0 (str_method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL) == 0) {
510 method = "local";
511 } else if (g_strcmp0 (str_method, NM_SETTING_IP6_CONFIG_METHOD_MANUAL) == 0) {
512 method = "manual";
513 } else if (g_strcmp0 (str_method, NM_SETTING_IP6_CONFIG_METHOD_SHARED) == 0) {
514 method = "shared";
515 } else if (g_strcmp0 (str_method, NM_SETTING_IP6_CONFIG_METHOD_DISABLED) == 0 ||
516 g_strcmp0 (str_method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) == 0) {
517 method = "disabled";
518 }
519
520 gtk_check_button_set_active (GTK_CHECK_BUTTON (self->never_default_check),
521 nm_setting_ip_config_get_never_default (self->setting));
522 g_signal_connect_object (self->never_default_check, "toggled", G_CALLBACK (ce_page_changed), self, G_CONNECT_SWAPPED);
523
524 g_action_group_change_action_state (self->method_group, "ip6method", g_variant_new_string (method));
525
526 method_changed (self);
527 }
528
529 static gboolean
530 ui_to_setting (CEPageIP6 *self)
531 {
532 GtkWidget *child;
533 g_autoptr(GVariant) method_variant = NULL;
534 const gchar *method;
535 gboolean ignore_auto_dns;
536 gboolean ignore_auto_routes;
537 gboolean never_default;
538 gboolean add_addresses = FALSE;
539 gboolean add_routes = FALSE;
540 gboolean ret = TRUE;
541 GStrv dns_addresses = NULL;
542 gchar *dns_text = NULL;
543 guint i;
544
545 method_variant = g_action_group_get_action_state (self->method_group, "ip6method");
546 method = g_variant_get_string (method_variant, NULL);
547 if (g_str_equal (method, "disabled"))
548 method = NM_SETTING_IP6_CONFIG_METHOD_DISABLED;
549 else if (g_str_equal (method, "manual"))
550 method = NM_SETTING_IP6_CONFIG_METHOD_MANUAL;
551 else if (g_str_equal (method, "local"))
552 method = NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL;
553 else if (g_str_equal (method, "dhcp"))
554 method = NM_SETTING_IP6_CONFIG_METHOD_DHCP;
555 else if (g_str_equal (method, "automatic"))
556 method = NM_SETTING_IP6_CONFIG_METHOD_AUTO;
557 else if (g_str_equal (method, "shared"))
558 method = NM_SETTING_IP6_CONFIG_METHOD_SHARED;
559 else
560 g_assert_not_reached ();
561
562 nm_setting_ip_config_clear_addresses (self->setting);
563 if (g_str_equal (method, NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) {
564 add_addresses = TRUE;
565 } else {
566 g_object_set (G_OBJECT (self->setting),
567 NM_SETTING_IP_CONFIG_GATEWAY, NULL,
568 NULL);
569 }
570
571 for (child = gtk_widget_get_first_child (self->address_list);
572 add_addresses && child;
573 child = gtk_widget_get_next_sibling (child)) {
574 GtkWidget *row = child;
575 CEIPAddressEntry *address_entry;
576 CEIPAddressEntry *gateway_entry;
577 const gchar *text_prefix;
578 guint32 prefix;
579 gchar *end;
580 NMIPAddress *addr;
581
582 address_entry = CE_IP_ADDRESS_ENTRY (g_object_get_data (G_OBJECT (row), "address"));
583 if (!address_entry)
584 continue;
585
586 text_prefix = gtk_editable_get_text (GTK_EDITABLE (g_object_get_data (G_OBJECT (row), "prefix")));
587 gateway_entry = CE_IP_ADDRESS_ENTRY (g_object_get_data (G_OBJECT (row), "gateway"));
588
589 if (ce_ip_address_entry_is_empty (address_entry) && !*text_prefix && ce_ip_address_entry_is_empty (gateway_entry)) {
590 /* ignore empty rows */
591 widget_unset_error (g_object_get_data (G_OBJECT (row), "prefix"));
592 continue;
593 }
594
595 if (!ce_ip_address_entry_is_valid (address_entry))
596 ret = FALSE;
597
598 prefix = strtoul (text_prefix, &end, 10);
599 if (!end || *end || prefix == 0 || prefix > 128) {
600 widget_set_error (g_object_get_data (G_OBJECT (row), "prefix"));
601 ret = FALSE;
602 } else {
603 widget_unset_error (g_object_get_data (G_OBJECT (row), "prefix"));
604 }
605
606 if (!ce_ip_address_entry_is_valid (gateway_entry))
607 ret = FALSE;
608
609 if (!ret)
610 continue;
611
612 addr = nm_ip_address_new (AF_INET6, gtk_editable_get_text (GTK_EDITABLE (address_entry)), prefix, NULL);
613 if (!ce_ip_address_entry_is_empty (gateway_entry))
614 g_object_set (G_OBJECT (self->setting),
615 NM_SETTING_IP_CONFIG_GATEWAY, gtk_editable_get_text (GTK_EDITABLE (gateway_entry)),
616 NULL);
617 nm_setting_ip_config_add_address (self->setting, addr);
618
619 if (!gtk_widget_get_next_sibling (row))
620 ensure_empty_address_row (self);
621 }
622
623 nm_setting_ip_config_clear_dns (self->setting);
624 dns_text = g_strstrip (g_strdup (gtk_editable_get_text (GTK_EDITABLE (self->dns_entry))));
625
626 if (g_str_equal (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) ||
627 g_str_equal (method, NM_SETTING_IP6_CONFIG_METHOD_DHCP) ||
628 g_str_equal (method, NM_SETTING_IP6_CONFIG_METHOD_MANUAL))
629 dns_addresses = g_strsplit_set (dns_text, ", ", -1);
630 else
631 dns_addresses = NULL;
632
633 for (i = 0; dns_addresses && dns_addresses[i]; i++) {
634 const gchar *text;
635 struct in6_addr tmp_addr;
636
637 text = dns_addresses[i];
638
639 if (!text || !*text)
640 continue;
641
642 if (inet_pton (AF_INET6, text, &tmp_addr) <= 0) {
643 g_clear_pointer (&dns_addresses, g_strfreev);
644 widget_set_error (GTK_WIDGET (self->dns_entry));
645 ret = FALSE;
646 break;
647 } else {
648 widget_unset_error (GTK_WIDGET (self->dns_entry));
649 nm_setting_ip_config_add_dns (self->setting, text);
650 }
651 }
652
653 if (dns_text[0] == '\0')
654 widget_unset_error (GTK_WIDGET (self->dns_entry));
655
656 nm_setting_ip_config_clear_routes (self->setting);
657 add_routes = g_str_equal (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) ||
658 g_str_equal (method, NM_SETTING_IP6_CONFIG_METHOD_DHCP) ||
659 g_str_equal (method, NM_SETTING_IP6_CONFIG_METHOD_MANUAL);
660
661 for (child = gtk_widget_get_first_child (self->routes_list);
662 add_routes && child;
663 child = gtk_widget_get_next_sibling (child)) {
664 GtkWidget *row = child;
665 CEIPAddressEntry *address_entry;
666 CEIPAddressEntry *gateway_entry;
667 const gchar *text_prefix;
668 const gchar *text_metric;
669 guint32 prefix;
670 gint64 metric;
671 gchar *end;
672 NMIPRoute *route;
673
674 address_entry = CE_IP_ADDRESS_ENTRY (g_object_get_data (G_OBJECT (row), "address"));
675 if (!address_entry)
676 continue;
677
678 text_prefix = gtk_editable_get_text (GTK_EDITABLE (g_object_get_data (G_OBJECT (row), "prefix")));
679 gateway_entry = CE_IP_ADDRESS_ENTRY (g_object_get_data (G_OBJECT (row), "gateway"));
680 text_metric = gtk_editable_get_text (GTK_EDITABLE (g_object_get_data (G_OBJECT (row), "metric")));
681
682 if (ce_ip_address_entry_is_empty (address_entry) && !*text_prefix && ce_ip_address_entry_is_empty (gateway_entry) && !*text_metric) {
683 /* ignore empty rows */
684 widget_unset_error (g_object_get_data (G_OBJECT (row), "prefix"));
685 widget_unset_error (g_object_get_data (G_OBJECT (row), "metric"));
686 continue;
687 }
688
689 if (!ce_ip_address_entry_is_valid (address_entry))
690 ret = FALSE;
691
692 prefix = strtoul (text_prefix, &end, 10);
693 if (!end || *end || prefix == 0 || prefix > 128) {
694 widget_set_error (g_object_get_data (G_OBJECT (row), "prefix"));
695 ret = FALSE;
696 } else {
697 widget_unset_error (g_object_get_data (G_OBJECT (row), "prefix"));
698 }
699
700 if (!ce_ip_address_entry_is_valid (gateway_entry))
701 ret = FALSE;
702
703 metric = -1;
704 if (*text_metric) {
705 errno = 0;
706 metric = g_ascii_strtoull (text_metric, NULL, 10);
707 if (errno) {
708 widget_set_error (g_object_get_data (G_OBJECT (row), "metric"));
709 ret = FALSE;
710 } else {
711 widget_unset_error (g_object_get_data (G_OBJECT (row), "metric"));
712 }
713 } else {
714 widget_unset_error (g_object_get_data (G_OBJECT (row), "metric"));
715 }
716
717 if (!ret)
718 continue;
719
720 route = nm_ip_route_new (AF_INET6,
721 gtk_editable_get_text (GTK_EDITABLE (address_entry)),
722 prefix,
723 gtk_editable_get_text (GTK_EDITABLE (gateway_entry)),
724 metric,
725 NULL);
726 nm_setting_ip_config_add_route (self->setting, route);
727 nm_ip_route_unref (route);
728
729 if (!gtk_widget_get_next_sibling (row))
730 ensure_empty_routes_row (self);
731 }
732
733 if (!ret)
734 goto out;
735
736 ignore_auto_dns = !gtk_switch_get_active (self->auto_dns_switch);
737 ignore_auto_routes = !gtk_switch_get_active (self->auto_routes_switch);
738 never_default = gtk_check_button_get_active (self->never_default_check);
739
740 g_object_set (self->setting,
741 NM_SETTING_IP_CONFIG_METHOD, method,
742 NM_SETTING_IP_CONFIG_IGNORE_AUTO_DNS, ignore_auto_dns,
743 NM_SETTING_IP_CONFIG_IGNORE_AUTO_ROUTES, ignore_auto_routes,
744 NM_SETTING_IP_CONFIG_NEVER_DEFAULT, never_default,
745 NULL);
746
747 out:
748 g_clear_pointer (&dns_addresses, g_strfreev);
749 g_clear_pointer (&dns_text, g_free);
750
751 return ret;
752 }
753
754 static void
755 on_ip6_method_activated_cb (GSimpleAction* action,
756 GVariant* parameter,
757 gpointer user_data)
758 {
759 CEPageIP6 *self = CE_PAGE_IP6 (user_data);
760 g_simple_action_set_state (action, parameter);
761
762 method_changed (self);
763 }
764
765 static const gchar *
766 ce_page_ip6_get_title (CEPage *page)
767 {
768 return _("IPv6");
769 }
770
771 static gboolean
772 ce_page_ip6_validate (CEPage *self,
773 NMConnection *connection,
774 GError **error)
775 {
776 if (!ui_to_setting (CE_PAGE_IP6 (self)))
777 return FALSE;
778
779 return nm_setting_verify (NM_SETTING (CE_PAGE_IP6 (self)->setting), NULL, error);
780 }
781
782 static void
783 ce_page_ip6_init (CEPageIP6 *self)
784 {
785 const GActionEntry ip6_entries[] = {
786 { "ip6method", on_ip6_method_activated_cb, "s", "'automatic'", NULL, { 0 } },
787 };
788 self->method_group = G_ACTION_GROUP (g_simple_action_group_new ());
789
790 g_action_map_add_action_entries (G_ACTION_MAP (self->method_group), ip6_entries, G_N_ELEMENTS (ip6_entries), self);
791 gtk_widget_insert_action_group (GTK_WIDGET (self), "ip6page", G_ACTION_GROUP (self->method_group));
792
793 gtk_widget_init_template (GTK_WIDGET (self));
794 }
795
796 static void
797 ce_page_ip6_class_init (CEPageIP6Class *klass)
798 {
799 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
800
801 gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/network/ip6-page.ui");
802
803 gtk_widget_class_bind_template_child (widget_class, CEPageIP6, address_box);
804 gtk_widget_class_bind_template_child (widget_class, CEPageIP6, address_address_label);
805 gtk_widget_class_bind_template_child (widget_class, CEPageIP6, address_prefix_label);
806 gtk_widget_class_bind_template_child (widget_class, CEPageIP6, address_gateway_label);
807 gtk_widget_class_bind_template_child (widget_class, CEPageIP6, address_sizegroup);
808 gtk_widget_class_bind_template_child (widget_class, CEPageIP6, auto_dns_label);
809 gtk_widget_class_bind_template_child (widget_class, CEPageIP6, auto_dns_switch);
810 gtk_widget_class_bind_template_child (widget_class, CEPageIP6, auto_routes_label);
811 gtk_widget_class_bind_template_child (widget_class, CEPageIP6, auto_routes_switch);
812 gtk_widget_class_bind_template_child (widget_class, CEPageIP6, content_box);
813 gtk_widget_class_bind_template_child (widget_class, CEPageIP6, dns_box);
814 gtk_widget_class_bind_template_child (widget_class, CEPageIP6, dns_entry);
815 gtk_widget_class_bind_template_child (widget_class, CEPageIP6, main_box);
816 gtk_widget_class_bind_template_child (widget_class, CEPageIP6, never_default_check);
817 gtk_widget_class_bind_template_child (widget_class, CEPageIP6, routes_box);
818 gtk_widget_class_bind_template_child (widget_class, CEPageIP6, route_config_box);
819 gtk_widget_class_bind_template_child (widget_class, CEPageIP6, routes_address_label);
820 gtk_widget_class_bind_template_child (widget_class, CEPageIP6, routes_address_sizegroup);
821 gtk_widget_class_bind_template_child (widget_class, CEPageIP6, routes_prefix_label);
822 gtk_widget_class_bind_template_child (widget_class, CEPageIP6, routes_prefix_sizegroup);
823 gtk_widget_class_bind_template_child (widget_class, CEPageIP6, routes_gateway_label);
824 gtk_widget_class_bind_template_child (widget_class, CEPageIP6, routes_gateway_sizegroup);
825 gtk_widget_class_bind_template_child (widget_class, CEPageIP6, routes_metric_label);
826 gtk_widget_class_bind_template_child (widget_class, CEPageIP6, routes_metric_sizegroup);
827 gtk_widget_class_bind_template_child (widget_class, CEPageIP6, routes_sizegroup);
828 }
829
830 static void
831 ce_page_iface_init (CEPageInterface *iface)
832 {
833 iface->get_title = ce_page_ip6_get_title;
834 iface->validate = ce_page_ip6_validate;
835 }
836
837 CEPageIP6 *
838 ce_page_ip6_new (NMConnection *connection,
839 NMClient *client)
840 {
841 CEPageIP6 *self;
842
843 self = CE_PAGE_IP6 (g_object_new (ce_page_ip6_get_type (), NULL));
844
845 self->setting = nm_connection_get_setting_ip6_config (connection);
846 if (!self->setting) {
847 self->setting = NM_SETTING_IP_CONFIG (nm_setting_ip6_config_new ());
848 nm_connection_add_setting (connection, NM_SETTING (self->setting));
849 }
850
851 connect_ip6_page (self);
852
853 return self;
854 }
855