GCC Code Coverage Report


Directory: ./
File: panels/network/connection-editor/ce-page-ip4.c
Date: 2024-05-04 07:58:27
Exec Total Coverage
Lines: 0 462 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-netmask-entry.h"
32 #include "ce-page.h"
33 #include "ce-page-ip4.h"
34 #include "ui-helpers.h"
35
36 static void ensure_empty_address_row (CEPageIP4 *self);
37 static void ensure_empty_routes_row (CEPageIP4 *self);
38
39 struct _CEPageIP4
40 {
41 AdwBin parent;
42
43 GtkLabel *address_address_label;
44 GtkBox *address_box;
45 GtkLabel *address_gateway_label;
46 GtkLabel *address_netmask_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 GtkBox *dns_box;
54 GtkEntry *dns_entry;
55 GtkGrid *main_box;
56 GtkCheckButton *never_default_check;
57 GtkBox *routes_box;
58 GtkBox *route_config_box;
59 GtkLabel *routes_address_label;
60 GtkLabel *routes_gateway_label;
61 GtkLabel *routes_netmask_label;
62 GtkLabel *routes_metric_label;
63 GtkSizeGroup *routes_address_sizegroup;
64 GtkSizeGroup *routes_gateway_sizegroup;
65 GtkSizeGroup *routes_netmask_sizegroup;
66 GtkSizeGroup *routes_metric_sizegroup;
67 GtkSizeGroup *routes_sizegroup;
68
69 NMSettingIPConfig *setting;
70
71 GtkWidget *address_list;
72 GtkWidget *routes_list;
73
74 GActionGroup *method_group;
75 };
76
77 static void ce_page_iface_init (CEPageInterface *);
78
79 G_DEFINE_TYPE_WITH_CODE (CEPageIP4, ce_page_ip4, ADW_TYPE_BIN,
80 G_IMPLEMENT_INTERFACE (ce_page_get_type (), ce_page_iface_init))
81
82 enum {
83 METHOD_COL_NAME,
84 METHOD_COL_METHOD
85 };
86
87 static void
88 sync_dns_entry_warning (CEPageIP4 *self)
89 {
90 g_autoptr(GVariant) method_variant = NULL;
91 const gchar *method;
92
93 method_variant = g_action_group_get_action_state (self->method_group, "ip4method");
94 method = g_variant_get_string (method_variant, NULL);
95
96 if (gtk_entry_get_text_length (self->dns_entry) &&
97 gtk_switch_get_active (self->auto_dns_switch) &&
98 g_strcmp0 (method, "automatic") == 0) {
99 gtk_entry_set_icon_from_icon_name (self->dns_entry, GTK_ENTRY_ICON_SECONDARY, "dialog-warning-symbolic");
100 gtk_entry_set_icon_tooltip_text (self->dns_entry, GTK_ENTRY_ICON_SECONDARY, _("Automatic DNS is enabled. Did you intend to disable Automatic DNS?"));
101 gtk_widget_add_css_class (GTK_WIDGET (self->dns_entry), "warning");
102 } else {
103 gtk_entry_set_icon_from_icon_name (self->dns_entry, GTK_ENTRY_ICON_SECONDARY, NULL);
104 gtk_entry_set_icon_tooltip_text (self->dns_entry, GTK_ENTRY_ICON_SECONDARY, NULL);
105 gtk_widget_remove_css_class (GTK_WIDGET (self->dns_entry), "warning");
106 }
107 }
108
109 static void
110 method_changed (CEPageIP4 *self)
111 {
112 gboolean addr_enabled;
113 gboolean dns_enabled;
114 gboolean routes_enabled;
115 gboolean auto_enabled;
116 g_autoptr(GVariant) method_variant = NULL;
117 const gchar *method;
118
119 method_variant = g_action_group_get_action_state (self->method_group, "ip4method");
120 method = g_variant_get_string (method_variant, NULL);
121
122 if (g_str_equal (method, "disabled") ||
123 g_str_equal (method, "shared")) {
124 addr_enabled = FALSE;
125 dns_enabled = FALSE;
126 routes_enabled = FALSE;
127 auto_enabled = FALSE;
128 } else {
129 addr_enabled = g_str_equal (method, "manual");
130 dns_enabled = !g_str_equal (method, "local");
131 routes_enabled = !g_str_equal (method, "local");
132 auto_enabled = g_str_equal (method, "automatic");
133 }
134
135 gtk_widget_set_visible (GTK_WIDGET (self->address_box), addr_enabled);
136 gtk_widget_set_sensitive (GTK_WIDGET (self->dns_box), dns_enabled);
137 gtk_widget_set_sensitive (GTK_WIDGET (self->routes_box), routes_enabled);
138
139 gtk_widget_set_sensitive (GTK_WIDGET (self->auto_dns_label), auto_enabled);
140 gtk_widget_set_sensitive (GTK_WIDGET (self->auto_dns_switch), auto_enabled);
141 gtk_widget_set_sensitive (GTK_WIDGET (self->auto_routes_label), auto_enabled);
142 gtk_widget_set_sensitive (GTK_WIDGET (self->auto_routes_switch), auto_enabled);
143
144 sync_dns_entry_warning (self);
145
146 ce_page_changed (CE_PAGE (self));
147 }
148
149 static void
150 update_row_sensitivity (CEPageIP4 *self, GtkWidget *list)
151 {
152 GtkWidget *child;
153 gint rows = 0, i = 0;
154
155 for (child = gtk_widget_get_first_child (GTK_WIDGET (list));
156 child;
157 child = gtk_widget_get_next_sibling (child)) {
158 GtkWidget *button;
159
160 button = GTK_WIDGET (g_object_get_data (G_OBJECT (child), "delete-button"));
161 if (button != NULL)
162 rows++;
163 }
164 for (child = gtk_widget_get_first_child (GTK_WIDGET (list));
165 child;
166 child = gtk_widget_get_next_sibling (child)) {
167 GtkWidget *button;
168
169 button = GTK_WIDGET (g_object_get_data (G_OBJECT (child), "delete-button"));
170 if (button != NULL)
171 gtk_widget_set_sensitive (button, rows > 1 && ++i < rows);
172 }
173 }
174
175 static void
176 update_row_gateway_sensitivity (CEPageIP4 *self)
177 {
178 GtkWidget *child;
179 gint rows = 0;
180
181 for (child = gtk_widget_get_first_child (self->address_list);
182 child;
183 child = gtk_widget_get_next_sibling (child)) {
184 GtkWidget *entry;
185
186 entry = GTK_WIDGET (g_object_get_data (G_OBJECT (child), "gateway"));
187
188 gtk_widget_set_sensitive (entry, (rows == 0));
189
190 rows++;
191 }
192 }
193
194 static void
195 remove_row (CEPageIP4 *self, GtkButton *button)
196 {
197 GtkWidget *list;
198 GtkWidget *row;
199 GtkWidget *row_box;
200
201 row_box = gtk_widget_get_parent (GTK_WIDGET (button));
202 row = gtk_widget_get_parent (row_box);
203 list = gtk_widget_get_parent (row);
204
205 gtk_list_box_remove (GTK_LIST_BOX (list), row);
206
207 ce_page_changed (CE_PAGE (self));
208
209 update_row_sensitivity (self, list);
210 if (list == self->address_list)
211 update_row_gateway_sensitivity (self);
212 }
213
214 static gboolean
215 validate_row (GtkWidget *row)
216 {
217 GtkWidget *child;
218 GtkWidget *box;
219 gboolean valid;
220
221 valid = FALSE;
222 box = gtk_list_box_row_get_child (GTK_LIST_BOX_ROW (row));
223
224 for (child = gtk_widget_get_first_child (box);
225 child;
226 child = gtk_widget_get_next_sibling (child)) {
227 if (!GTK_IS_ENTRY (child))
228 continue;
229
230 valid = valid || gtk_entry_get_text_length (GTK_ENTRY (child)) > 0;
231 }
232
233 return valid;
234 }
235
236 static void
237 add_address_row (CEPageIP4 *self,
238 const gchar *address,
239 const gchar *network,
240 const gchar *gateway)
241 {
242 GtkWidget *row;
243 GtkWidget *row_box;
244 GtkWidget *widget;
245 GtkWidget *delete_button;
246
247 row = gtk_list_box_row_new ();
248 gtk_list_box_row_set_activatable (GTK_LIST_BOX_ROW (row), FALSE);
249
250 row_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
251 gtk_widget_add_css_class (row_box, "linked");
252
253 widget = GTK_WIDGET (ce_ip_address_entry_new (AF_INET));
254 g_signal_connect_object (widget, "changed", G_CALLBACK (ce_page_changed), self, G_CONNECT_SWAPPED);
255 g_signal_connect_object (widget, "activate", G_CALLBACK (ensure_empty_address_row), self, G_CONNECT_SWAPPED);
256 g_object_set_data (G_OBJECT (row), "address", widget);
257 gtk_editable_set_text (GTK_EDITABLE (widget), address);
258 gtk_editable_set_width_chars (GTK_EDITABLE (widget), 16);
259 gtk_widget_set_hexpand (widget, TRUE);
260 gtk_accessible_update_relation (GTK_ACCESSIBLE (widget),
261 GTK_ACCESSIBLE_RELATION_LABELLED_BY, self->address_address_label, NULL,
262 -1);
263 gtk_box_append (GTK_BOX (row_box), widget);
264
265 widget = GTK_WIDGET (ce_netmask_entry_new ());
266 g_signal_connect_object (widget, "changed", G_CALLBACK (ce_page_changed), self, G_CONNECT_SWAPPED);
267 g_signal_connect_object (widget, "activate", G_CALLBACK (ensure_empty_address_row), self, G_CONNECT_SWAPPED);
268 g_object_set_data (G_OBJECT (row), "netmask", widget);
269 gtk_editable_set_text (GTK_EDITABLE (widget), network);
270 gtk_editable_set_width_chars (GTK_EDITABLE (widget), 16);
271 gtk_widget_set_hexpand (widget, TRUE);
272 gtk_accessible_update_relation (GTK_ACCESSIBLE (widget),
273 GTK_ACCESSIBLE_RELATION_LABELLED_BY, self->address_netmask_label, NULL,
274 -1);
275 gtk_box_append (GTK_BOX (row_box), widget);
276
277 widget = GTK_WIDGET (ce_ip_address_entry_new (AF_INET));
278 g_signal_connect_object (widget, "changed", G_CALLBACK (ce_page_changed), self, G_CONNECT_SWAPPED);
279 g_signal_connect_object (widget, "activate", G_CALLBACK (ensure_empty_address_row), self, G_CONNECT_SWAPPED);
280 g_object_set_data (G_OBJECT (row), "gateway", widget);
281 gtk_editable_set_text (GTK_EDITABLE (widget), gateway ? gateway : "");
282 gtk_editable_set_width_chars (GTK_EDITABLE (widget), 16);
283 gtk_widget_set_hexpand (widget, TRUE);
284 gtk_accessible_update_relation (GTK_ACCESSIBLE (widget),
285 GTK_ACCESSIBLE_RELATION_LABELLED_BY, self->address_gateway_label, NULL,
286 -1);
287 gtk_box_append (GTK_BOX (row_box), widget);
288
289 delete_button = gtk_button_new_from_icon_name ("edit-delete-symbolic");
290 gtk_widget_set_sensitive (delete_button, FALSE);
291 g_signal_connect_object (delete_button, "clicked", G_CALLBACK (remove_row), self, G_CONNECT_SWAPPED);
292 gtk_accessible_update_property (GTK_ACCESSIBLE (delete_button),
293 GTK_ACCESSIBLE_PROPERTY_LABEL, _("Delete Address"),
294 -1);
295 gtk_box_append (GTK_BOX (row_box), delete_button);
296 g_object_set_data (G_OBJECT (row), "delete-button", delete_button);
297
298 gtk_size_group_add_widget (self->address_sizegroup, delete_button);
299
300 gtk_list_box_row_set_child (GTK_LIST_BOX_ROW (row), row_box);
301 gtk_list_box_append (GTK_LIST_BOX (self->address_list), row);
302
303 update_row_gateway_sensitivity (self);
304 update_row_sensitivity (self, self->address_list);
305 }
306
307 static void
308 ensure_empty_address_row (CEPageIP4 *self)
309 {
310 GtkWidget *child = gtk_widget_get_last_child (self->address_list);
311
312 /* Add the last, stub row if needed*/
313 if (!child || validate_row (child))
314 add_address_row (self, "", "", "");
315 }
316
317 static void
318 add_address_box (CEPageIP4 *self)
319 {
320 GtkWidget *list;
321 gint i;
322
323 self->address_list = list = gtk_list_box_new ();
324 gtk_list_box_set_selection_mode (GTK_LIST_BOX (list), GTK_SELECTION_NONE);
325 gtk_box_append (self->address_box, list);
326
327 for (i = 0; i < nm_setting_ip_config_get_num_addresses (self->setting); i++) {
328 NMIPAddress *addr;
329 struct in_addr tmp_addr;
330 gchar network[INET_ADDRSTRLEN + 1];
331
332 addr = nm_setting_ip_config_get_address (self->setting, i);
333 if (!addr)
334 continue;
335
336 tmp_addr.s_addr = nm_utils_ip4_prefix_to_netmask (nm_ip_address_get_prefix (addr));
337 (void) inet_ntop (AF_INET, &tmp_addr, &network[0], sizeof (network));
338
339 add_address_row (self,
340 nm_ip_address_get_address (addr),
341 network,
342 i == 0 ? nm_setting_ip_config_get_gateway (self->setting) : "");
343 }
344 if (nm_setting_ip_config_get_num_addresses (self->setting) == 0)
345 ensure_empty_address_row (self);
346 }
347
348 static void
349 add_dns_section (CEPageIP4 *self)
350 {
351 GString *string;
352 gint i;
353
354 gtk_switch_set_active (self->auto_dns_switch, !nm_setting_ip_config_get_ignore_auto_dns (self->setting));
355 g_signal_connect_object (self->auto_dns_switch, "notify::active", G_CALLBACK (ce_page_changed), self, G_CONNECT_SWAPPED);
356 g_signal_connect_object (self->auto_dns_switch, "notify::active", G_CALLBACK (sync_dns_entry_warning), self, G_CONNECT_SWAPPED);
357
358 string = g_string_new ("");
359
360 for (i = 0; i < nm_setting_ip_config_get_num_dns (self->setting); i++) {
361 const char *address;
362
363 address = nm_setting_ip_config_get_dns (self->setting, i);
364
365 if (i > 0)
366 g_string_append (string, ", ");
367
368 g_string_append (string, address);
369 }
370
371 gtk_editable_set_text (GTK_EDITABLE (self->dns_entry), string->str);
372
373 g_signal_connect_object (self->dns_entry, "notify::text", G_CALLBACK (ce_page_changed), self, G_CONNECT_SWAPPED);
374 g_signal_connect_object (self->dns_entry, "notify::text", G_CALLBACK (sync_dns_entry_warning), self, G_CONNECT_SWAPPED);
375
376 sync_dns_entry_warning (self);
377
378 g_string_free (string, TRUE);
379 }
380
381 static void
382 add_route_row (CEPageIP4 *self,
383 const gchar *address,
384 const gchar *netmask,
385 const gchar *gateway,
386 gint metric)
387 {
388 GtkWidget *row;
389 GtkWidget *row_box;
390 GtkWidget *widget;
391 GtkWidget *delete_button;
392
393 row = gtk_list_box_row_new ();
394 gtk_list_box_row_set_activatable (GTK_LIST_BOX_ROW (row), FALSE);
395
396 row_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
397 gtk_widget_add_css_class (row_box, "linked");
398
399 widget = GTK_WIDGET (ce_ip_address_entry_new (AF_INET));
400 g_signal_connect_object (widget, "changed", G_CALLBACK (ce_page_changed), self, G_CONNECT_SWAPPED);
401 g_signal_connect_object (widget, "activate", G_CALLBACK (ensure_empty_routes_row), self, G_CONNECT_SWAPPED);
402 g_object_set_data (G_OBJECT (row), "address", widget);
403 gtk_editable_set_text (GTK_EDITABLE (widget), address);
404 gtk_editable_set_width_chars (GTK_EDITABLE (widget), 16);
405 gtk_widget_set_hexpand (widget, TRUE);
406 gtk_accessible_update_relation (GTK_ACCESSIBLE (widget),
407 GTK_ACCESSIBLE_RELATION_LABELLED_BY, self->routes_address_label, NULL,
408 -1);
409 gtk_box_append (GTK_BOX (row_box), widget);
410
411 gtk_size_group_add_widget (self->routes_address_sizegroup, widget);
412
413 widget = GTK_WIDGET (ce_netmask_entry_new ());
414 g_signal_connect_object (widget, "changed", G_CALLBACK (ce_page_changed), self, G_CONNECT_SWAPPED);
415 g_signal_connect_object (widget, "activate", G_CALLBACK (ensure_empty_routes_row), self, G_CONNECT_SWAPPED);
416 g_object_set_data (G_OBJECT (row), "netmask", widget);
417 gtk_editable_set_text (GTK_EDITABLE (widget), netmask);
418 gtk_editable_set_width_chars (GTK_EDITABLE (widget), 16);
419 gtk_widget_set_hexpand (widget, TRUE);
420 gtk_accessible_update_relation (GTK_ACCESSIBLE (widget),
421 GTK_ACCESSIBLE_RELATION_LABELLED_BY, self->routes_netmask_label, NULL,
422 -1);
423 gtk_box_append (GTK_BOX (row_box), widget);
424
425 gtk_size_group_add_widget (self->routes_netmask_sizegroup, widget);
426
427 widget = GTK_WIDGET (ce_ip_address_entry_new (AF_INET));
428 g_signal_connect_object (widget, "changed", G_CALLBACK (ce_page_changed), self, G_CONNECT_SWAPPED);
429 g_signal_connect_object (widget, "activate", G_CALLBACK (ensure_empty_routes_row), self, G_CONNECT_SWAPPED);
430 g_object_set_data (G_OBJECT (row), "gateway", widget);
431 gtk_editable_set_text (GTK_EDITABLE (widget), gateway ? gateway : "");
432 gtk_editable_set_width_chars (GTK_EDITABLE (widget), 16);
433 gtk_widget_set_hexpand (widget, TRUE);
434 gtk_accessible_update_relation (GTK_ACCESSIBLE (widget),
435 GTK_ACCESSIBLE_RELATION_LABELLED_BY, self->routes_gateway_label, NULL,
436 -1);
437 gtk_box_append (GTK_BOX (row_box), widget);
438
439 gtk_size_group_add_widget (self->routes_gateway_sizegroup, widget);
440
441 widget = gtk_entry_new ();
442 g_signal_connect_object (widget, "changed", G_CALLBACK (ce_page_changed), self, G_CONNECT_SWAPPED);
443 g_signal_connect_object (widget, "activate", G_CALLBACK (ensure_empty_routes_row), self, G_CONNECT_SWAPPED);
444 g_object_set_data (G_OBJECT (row), "metric", widget);
445 if (metric >= 0) {
446 g_autofree gchar *s = g_strdup_printf ("%d", metric);
447 gtk_editable_set_text (GTK_EDITABLE (widget), s);
448 }
449 gtk_editable_set_width_chars (GTK_EDITABLE (widget), 5);
450 gtk_widget_set_hexpand (widget, TRUE);
451 gtk_accessible_update_relation (GTK_ACCESSIBLE (widget),
452 GTK_ACCESSIBLE_RELATION_LABELLED_BY, self->routes_metric_label, NULL,
453 -1);
454 gtk_box_append (GTK_BOX (row_box), widget);
455
456 gtk_size_group_add_widget (self->routes_metric_sizegroup, widget);
457
458 delete_button = gtk_button_new_from_icon_name ("edit-delete-symbolic");
459 gtk_widget_set_sensitive (delete_button, FALSE);
460 g_signal_connect_object (delete_button, "clicked", G_CALLBACK (remove_row), self, G_CONNECT_SWAPPED);
461 gtk_accessible_update_property (GTK_ACCESSIBLE (delete_button),
462 GTK_ACCESSIBLE_PROPERTY_LABEL, _("Delete Route"),
463 -1);
464 gtk_widget_set_halign (delete_button, GTK_ALIGN_CENTER);
465 gtk_widget_set_valign (delete_button, GTK_ALIGN_CENTER);
466 gtk_box_append (GTK_BOX (row_box), delete_button);
467 g_object_set_data (G_OBJECT (row), "delete-button", delete_button);
468
469 gtk_size_group_add_widget (self->routes_sizegroup, delete_button);
470
471 gtk_list_box_row_set_child (GTK_LIST_BOX_ROW (row), row_box);
472 gtk_list_box_append (GTK_LIST_BOX (self->routes_list), row);
473
474 update_row_sensitivity (self, self->routes_list);
475 }
476
477 static void
478 ensure_empty_routes_row (CEPageIP4 *self)
479 {
480 GtkWidget *child = gtk_widget_get_last_child (self->routes_list);
481
482 /* Add the last, stub row if needed*/
483 if (!child || validate_row (child))
484 add_route_row (self, "", "", "", -1);
485 }
486
487 static void
488 add_route_config_box (CEPageIP4 *self)
489 {
490 GtkWidget *list;
491 gint i;
492
493 self->routes_list = list = gtk_list_box_new ();
494 gtk_list_box_set_selection_mode (GTK_LIST_BOX (list), GTK_SELECTION_NONE);
495 gtk_box_append (GTK_BOX (self->route_config_box), list);
496 gtk_switch_set_active (self->auto_routes_switch, !nm_setting_ip_config_get_ignore_auto_routes (self->setting));
497 g_signal_connect_object (self->auto_routes_switch, "notify::active", G_CALLBACK (ce_page_changed), self, G_CONNECT_SWAPPED);
498
499 for (i = 0; i < nm_setting_ip_config_get_num_routes (self->setting); i++) {
500 NMIPRoute *route;
501 struct in_addr tmp_addr;
502 gchar netmask[INET_ADDRSTRLEN + 1];
503
504 route = nm_setting_ip_config_get_route (self->setting, i);
505 if (!route)
506 continue;
507
508 tmp_addr.s_addr = nm_utils_ip4_prefix_to_netmask (nm_ip_route_get_prefix (route));
509 (void) inet_ntop (AF_INET, &tmp_addr, &netmask[0], sizeof (netmask));
510
511 add_route_row (self,
512 nm_ip_route_get_dest (route),
513 netmask,
514 nm_ip_route_get_next_hop (route),
515 nm_ip_route_get_metric (route));
516 }
517 if (nm_setting_ip_config_get_num_routes (self->setting) == 0)
518 ensure_empty_routes_row (self);
519 }
520
521 static void
522 connect_ip4_page (CEPageIP4 *self)
523 {
524 const gchar *str_method;
525 gchar *method;
526
527 add_address_box (self);
528 add_dns_section (self);
529 add_route_config_box (self);
530
531 str_method = nm_setting_ip_config_get_method (self->setting);
532
533 method = "automatic";
534 if (g_strcmp0 (str_method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL) == 0) {
535 method = "local";
536 } else if (g_strcmp0 (str_method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL) == 0) {
537 method = "manual";
538 } else if (g_strcmp0 (str_method, NM_SETTING_IP4_CONFIG_METHOD_SHARED) == 0) {
539 method = "shared";
540 } else if (g_strcmp0 (str_method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) == 0) {
541 method = "disabled";
542 }
543
544 gtk_check_button_set_active (GTK_CHECK_BUTTON (self->never_default_check),
545 nm_setting_ip_config_get_never_default (self->setting));
546 g_signal_connect_object (self->never_default_check, "toggled", G_CALLBACK (ce_page_changed), self, G_CONNECT_SWAPPED);
547
548 g_action_group_change_action_state (self->method_group, "ip4method", g_variant_new_string (method));
549
550 method_changed (self);
551 }
552
553 static gboolean
554 ui_to_setting (CEPageIP4 *self)
555 {
556 const gchar *method;
557 g_autoptr(GVariant) method_variant = NULL;
558 gboolean ignore_auto_dns;
559 gboolean ignore_auto_routes;
560 gboolean never_default;
561 GPtrArray *addresses = NULL;
562 GPtrArray *dns_servers = NULL;
563 GPtrArray *routes = NULL;
564 GtkWidget *child;
565 GStrv dns_addresses = NULL;
566 gboolean ret = TRUE;
567 const char *default_gateway = NULL;
568 gboolean add_addresses = FALSE;
569 gboolean add_routes = FALSE;
570 gchar *dns_text = NULL;
571 guint i;
572
573 method_variant = g_action_group_get_action_state (self->method_group, "ip4method");
574 method = g_variant_get_string (method_variant, NULL);
575 if (g_str_equal (method, "disabled"))
576 method = NM_SETTING_IP4_CONFIG_METHOD_DISABLED;
577 else if (g_str_equal (method, "automatic"))
578 method = NM_SETTING_IP4_CONFIG_METHOD_AUTO;
579 else if (g_str_equal (method, "local"))
580 method = NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL;
581 else if (g_str_equal (method, "manual"))
582 method = NM_SETTING_IP4_CONFIG_METHOD_MANUAL;
583 else if (g_str_equal (method, "shared"))
584 method = NM_SETTING_IP4_CONFIG_METHOD_SHARED;
585 else
586 g_assert_not_reached ();
587
588 addresses = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_ip_address_unref);
589 add_addresses = g_str_equal (method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL);
590
591 for (child = gtk_widget_get_first_child (self->address_list);
592 add_addresses && child;
593 child = gtk_widget_get_next_sibling (child)) {
594 GtkWidget *row = child;
595 CEIPAddressEntry *address_entry;
596 CENetmaskEntry *netmask_entry;
597 CEIPAddressEntry *gateway_entry;
598 NMIPAddress *addr;
599
600 address_entry = CE_IP_ADDRESS_ENTRY (g_object_get_data (G_OBJECT (row), "address"));
601 if (!address_entry)
602 continue;
603
604 netmask_entry = CE_NETMASK_ENTRY (g_object_get_data (G_OBJECT (row), "netmask"));
605 gateway_entry = CE_IP_ADDRESS_ENTRY (g_object_get_data (G_OBJECT (row), "gateway"));
606
607 if (ce_ip_address_entry_is_empty (address_entry) && ce_netmask_entry_is_empty (netmask_entry) && ce_ip_address_entry_is_empty (gateway_entry)) {
608 /* ignore empty rows */
609 continue;
610 }
611
612 if (!ce_ip_address_entry_is_valid (address_entry))
613 ret = FALSE;
614
615 if (!ce_netmask_entry_is_valid (netmask_entry))
616 ret = FALSE;
617
618 if (!ce_ip_address_entry_is_valid (gateway_entry)) {
619 ret = FALSE;
620 } else {
621 if (!ce_ip_address_entry_is_empty (gateway_entry)) {
622 g_assert (default_gateway == NULL);
623 default_gateway = gtk_editable_get_text (GTK_EDITABLE (gateway_entry));
624 }
625 }
626
627 if (!ret)
628 continue;
629
630 addr = nm_ip_address_new (AF_INET, gtk_editable_get_text (GTK_EDITABLE (address_entry)), ce_netmask_entry_get_prefix (netmask_entry), NULL);
631 if (addr)
632 g_ptr_array_add (addresses, addr);
633
634 if (!gtk_widget_get_next_sibling (row))
635 ensure_empty_address_row (self);
636 }
637
638 if (addresses->len == 0) {
639 g_ptr_array_free (addresses, TRUE);
640 addresses = NULL;
641 }
642
643 dns_servers = g_ptr_array_new_with_free_func (g_free);
644 dns_text = g_strstrip (g_strdup (gtk_editable_get_text (GTK_EDITABLE (self->dns_entry))));
645 if (g_str_equal (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) ||
646 g_str_equal (method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL))
647 dns_addresses = g_strsplit_set (dns_text, ", ", -1);
648 else
649 dns_addresses = NULL;
650
651 for (i = 0; dns_addresses && dns_addresses[i]; i++) {
652 const gchar *text;
653
654 text = dns_addresses[i];
655
656 if (!text || !*text)
657 continue;
658
659 if (!nm_utils_ipaddr_valid (AF_INET, text)) {
660 g_ptr_array_remove_range (dns_servers, 0, dns_servers->len);
661 widget_set_error (GTK_WIDGET (self->dns_entry));
662 ret = FALSE;
663 break;
664 } else {
665 widget_unset_error (GTK_WIDGET (self->dns_entry));
666 g_ptr_array_add (dns_servers, g_strdup (text));
667 }
668 }
669 g_clear_pointer (&dns_addresses, g_strfreev);
670
671 if (dns_text[0] == '\0')
672 widget_unset_error (GTK_WIDGET (self->dns_entry));
673
674 if (dns_servers->len == 0) {
675 g_ptr_array_free (dns_servers, TRUE);
676 dns_servers = NULL;
677 } else {
678 g_ptr_array_add (dns_servers, NULL);
679 }
680
681 routes = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_ip_route_unref);
682 add_routes = g_str_equal (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) ||
683 g_str_equal (method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL);
684
685 for (child = gtk_widget_get_first_child (self->routes_list);
686 add_routes && child;
687 child = gtk_widget_get_next_sibling (child)) {
688 GtkWidget *row = child;
689 CEIPAddressEntry *address_entry;
690 CENetmaskEntry *netmask_entry;
691 CEIPAddressEntry *gateway_entry;
692 const gchar *text_metric;
693 gint64 metric;
694 NMIPRoute *route;
695
696 address_entry = CE_IP_ADDRESS_ENTRY (g_object_get_data (G_OBJECT (row), "address"));
697 if (!address_entry)
698 continue;
699
700 netmask_entry = CE_NETMASK_ENTRY (g_object_get_data (G_OBJECT (row), "netmask"));
701 gateway_entry = CE_IP_ADDRESS_ENTRY (g_object_get_data (G_OBJECT (row), "gateway"));
702 text_metric = gtk_editable_get_text (GTK_EDITABLE (g_object_get_data (G_OBJECT (row), "metric")));
703
704 if (ce_ip_address_entry_is_empty (address_entry) && ce_netmask_entry_is_empty (netmask_entry) && ce_ip_address_entry_is_empty (gateway_entry) && !*text_metric) {
705 /* ignore empty rows */
706 continue;
707 }
708
709 if (!ce_ip_address_entry_is_valid (address_entry))
710 ret = FALSE;
711
712 if (!ce_netmask_entry_is_valid (netmask_entry))
713 ret = FALSE;
714
715 if (!ce_ip_address_entry_is_valid (gateway_entry))
716 ret = FALSE;
717
718 metric = -1;
719 if (*text_metric) {
720 errno = 0;
721 metric = g_ascii_strtoull (text_metric, NULL, 10);
722 if (errno || metric < 0 || metric > G_MAXUINT32) {
723 widget_set_error (GTK_WIDGET (g_object_get_data (G_OBJECT (row), "metric")));
724 ret = FALSE;
725 } else {
726 widget_unset_error (GTK_WIDGET (g_object_get_data (G_OBJECT (row), "metric")));
727 }
728 } else {
729 widget_unset_error (GTK_WIDGET (g_object_get_data (G_OBJECT (row), "metric")));
730 }
731
732 if (!ret)
733 continue;
734
735 route = nm_ip_route_new (AF_INET,
736 gtk_editable_get_text (GTK_EDITABLE (address_entry)),
737 ce_netmask_entry_get_prefix (netmask_entry),
738 gtk_editable_get_text (GTK_EDITABLE (gateway_entry)),
739 metric, NULL);
740 if (route)
741 g_ptr_array_add (routes, route);
742
743 if (!gtk_widget_get_next_sibling (row))
744 ensure_empty_routes_row (self);
745 }
746
747 if (routes->len == 0) {
748 g_ptr_array_free (routes, TRUE);
749 routes = NULL;
750 }
751
752 if (!ret)
753 goto out;
754
755 ignore_auto_dns = !gtk_switch_get_active (self->auto_dns_switch);
756 ignore_auto_routes = !gtk_switch_get_active (self->auto_routes_switch);
757 never_default = gtk_check_button_get_active (GTK_CHECK_BUTTON (self->never_default_check));
758
759 g_object_set (self->setting,
760 NM_SETTING_IP_CONFIG_METHOD, method,
761 NM_SETTING_IP_CONFIG_ADDRESSES, addresses,
762 NM_SETTING_IP_CONFIG_GATEWAY, default_gateway,
763 NM_SETTING_IP_CONFIG_DNS, dns_servers ? dns_servers->pdata : NULL,
764 NM_SETTING_IP_CONFIG_ROUTES, routes,
765 NM_SETTING_IP_CONFIG_IGNORE_AUTO_DNS, ignore_auto_dns,
766 NM_SETTING_IP_CONFIG_IGNORE_AUTO_ROUTES, ignore_auto_routes,
767 NM_SETTING_IP_CONFIG_NEVER_DEFAULT, never_default,
768 NULL);
769
770 out:
771 if (addresses)
772 g_ptr_array_free (addresses, TRUE);
773
774 if (dns_servers)
775 g_ptr_array_free (dns_servers, TRUE);
776
777 if (routes)
778 g_ptr_array_free (routes, TRUE);
779
780 g_clear_pointer (&dns_text, g_free);
781
782 return ret;
783 }
784
785 static void
786 on_ip4_method_activated_cb (GSimpleAction* action,
787 GVariant* parameter,
788 gpointer user_data)
789 {
790 CEPageIP4 *self = CE_PAGE_IP4 (user_data);
791 g_simple_action_set_state (action, parameter);
792
793 method_changed (self);
794 }
795
796 static const gchar *
797 ce_page_ip4_get_title (CEPage *page)
798 {
799 return _("IPv4");
800 }
801
802 static gboolean
803 ce_page_ip4_validate (CEPage *self,
804 NMConnection *connection,
805 GError **error)
806 {
807 if (!ui_to_setting (CE_PAGE_IP4 (self)))
808 return FALSE;
809
810 return nm_setting_verify (NM_SETTING (CE_PAGE_IP4 (self)->setting), NULL, error);
811 }
812
813 static void
814 ce_page_ip4_init (CEPageIP4 *self)
815 {
816 const GActionEntry ip4_entries[] = {
817 { "ip4method", on_ip4_method_activated_cb, "s", "'automatic'", NULL, { 0 } },
818 };
819 self->method_group = G_ACTION_GROUP (g_simple_action_group_new ());
820
821 g_action_map_add_action_entries (G_ACTION_MAP (self->method_group), ip4_entries, G_N_ELEMENTS (ip4_entries), self);
822 gtk_widget_insert_action_group (GTK_WIDGET (self), "ip4page", G_ACTION_GROUP (self->method_group));
823
824 gtk_widget_init_template (GTK_WIDGET (self));
825 }
826
827 static void
828 ce_page_ip4_class_init (CEPageIP4Class *klass)
829 {
830 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
831
832 gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/network/ip4-page.ui");
833
834 gtk_widget_class_bind_template_child (widget_class, CEPageIP4, address_box);
835 gtk_widget_class_bind_template_child (widget_class, CEPageIP4, address_sizegroup);
836 gtk_widget_class_bind_template_child (widget_class, CEPageIP4, auto_dns_label);
837 gtk_widget_class_bind_template_child (widget_class, CEPageIP4, auto_dns_switch);
838 gtk_widget_class_bind_template_child (widget_class, CEPageIP4, auto_routes_label);
839 gtk_widget_class_bind_template_child (widget_class, CEPageIP4, auto_routes_switch);
840 gtk_widget_class_bind_template_child (widget_class, CEPageIP4, content_box);
841 gtk_widget_class_bind_template_child (widget_class, CEPageIP4, dns_box);
842 gtk_widget_class_bind_template_child (widget_class, CEPageIP4, dns_entry);
843 gtk_widget_class_bind_template_child (widget_class, CEPageIP4, main_box);
844 gtk_widget_class_bind_template_child (widget_class, CEPageIP4, never_default_check);
845 gtk_widget_class_bind_template_child (widget_class, CEPageIP4, address_address_label);
846 gtk_widget_class_bind_template_child (widget_class, CEPageIP4, address_netmask_label);
847 gtk_widget_class_bind_template_child (widget_class, CEPageIP4, address_gateway_label);
848 gtk_widget_class_bind_template_child (widget_class, CEPageIP4, routes_box);
849 gtk_widget_class_bind_template_child (widget_class, CEPageIP4, route_config_box);
850 gtk_widget_class_bind_template_child (widget_class, CEPageIP4, routes_address_label);
851 gtk_widget_class_bind_template_child (widget_class, CEPageIP4, routes_netmask_label);
852 gtk_widget_class_bind_template_child (widget_class, CEPageIP4, routes_gateway_label);
853 gtk_widget_class_bind_template_child (widget_class, CEPageIP4, routes_metric_label);
854 gtk_widget_class_bind_template_child (widget_class, CEPageIP4, routes_address_sizegroup);
855 gtk_widget_class_bind_template_child (widget_class, CEPageIP4, routes_netmask_sizegroup);
856 gtk_widget_class_bind_template_child (widget_class, CEPageIP4, routes_gateway_sizegroup);
857 gtk_widget_class_bind_template_child (widget_class, CEPageIP4, routes_metric_sizegroup);
858 gtk_widget_class_bind_template_child (widget_class, CEPageIP4, routes_sizegroup);
859 }
860
861 static void
862 ce_page_iface_init (CEPageInterface *iface)
863 {
864 iface->get_title = ce_page_ip4_get_title;
865 iface->validate = ce_page_ip4_validate;
866 }
867
868 CEPageIP4 *
869 ce_page_ip4_new (NMConnection *connection,
870 NMClient *client)
871 {
872 CEPageIP4 *self;
873
874 self = CE_PAGE_IP4 (g_object_new (ce_page_ip4_get_type (), NULL));
875
876 self->setting = nm_connection_get_setting_ip4_config (connection);
877 if (!self->setting) {
878 self->setting = NM_SETTING_IP_CONFIG (nm_setting_ip4_config_new ());
879 nm_connection_add_setting (connection, NM_SETTING (self->setting));
880 }
881
882 connect_ip4_page (self);
883
884 return self;
885 }
886