GCC Code Coverage Report


Directory: ./
File: panels/network/connection-editor/ce-page.c
Date: 2024-05-04 07:58:27
Exec Total Coverage
Lines: 0 192 0.0%
Functions: 0 17 0.0%
Branches: 0 207 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 <string.h>
25
26 #include <net/if_arp.h>
27 #include <netinet/ether.h>
28
29 #include <NetworkManager.h>
30
31 #include <glib/gi18n.h>
32
33 #include "ce-page.h"
34
35
36 G_DEFINE_INTERFACE (CEPage, ce_page, G_TYPE_OBJECT)
37
38 enum {
39 CHANGED,
40 INITIALIZED,
41 LAST_SIGNAL
42 };
43
44 static guint signals[LAST_SIGNAL] = { 0 };
45
46 gboolean
47 ce_page_validate (CEPage *self, NMConnection *connection, GError **error)
48 {
49 g_return_val_if_fail (CE_IS_PAGE (self), FALSE);
50 g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE);
51
52 if (CE_PAGE_GET_IFACE (self)->validate)
53 return CE_PAGE_GET_IFACE (self)->validate (self, connection, error);
54
55 return TRUE;
56 }
57
58 const char *
59 ce_page_get_title (CEPage *self)
60 {
61 g_return_val_if_fail (CE_IS_PAGE (self), NULL);
62
63 return CE_PAGE_GET_IFACE (self)->get_title (self);
64 }
65
66 void
67 ce_page_changed (CEPage *self)
68 {
69 g_return_if_fail (CE_IS_PAGE (self));
70
71 g_signal_emit (self, signals[CHANGED], 0);
72 }
73
74 static void
75 ce_page_default_init (CEPageInterface *iface)
76 {
77 signals[CHANGED] =
78 g_signal_new ("changed",
79 G_TYPE_FROM_INTERFACE (iface),
80 G_SIGNAL_RUN_FIRST,
81 0,
82 NULL, NULL,
83 g_cclosure_marshal_VOID__VOID,
84 G_TYPE_NONE, 0);
85
86 signals[INITIALIZED] =
87 g_signal_new ("initialized",
88 G_TYPE_FROM_INTERFACE (iface),
89 G_SIGNAL_RUN_FIRST,
90 0,
91 NULL, NULL,
92 g_cclosure_marshal_VOID__POINTER,
93 G_TYPE_NONE, 1, G_TYPE_POINTER);
94 }
95
96 static void
97 emit_initialized (CEPage *self,
98 GError *error)
99 {
100 g_signal_emit (self, signals[INITIALIZED], 0, error);
101 g_clear_error (&error);
102 }
103
104 void
105 ce_page_complete_init (CEPage *self,
106 NMConnection *connection,
107 const gchar *setting_name,
108 GVariant *secrets,
109 GError *error)
110 {
111 g_autoptr(GError) update_error = NULL;
112 g_autoptr(GVariant) setting_dict = NULL;
113 gboolean ignore_error = FALSE;
114
115 g_return_if_fail (self != NULL);
116 g_return_if_fail (CE_IS_PAGE (self));
117
118 if (error) {
119 ignore_error = g_error_matches (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_SETTING_NOT_FOUND) ||
120 g_error_matches (error, NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_NO_SECRETS);
121 }
122
123 /* Ignore missing settings errors */
124 if (error && !ignore_error) {
125 emit_initialized (self, error);
126 return;
127 } else if (!setting_name || !secrets || g_variant_n_children (secrets) == 0) {
128 /* Success, no secrets */
129 emit_initialized (self, NULL);
130 return;
131 }
132
133 g_assert (setting_name);
134 g_assert (secrets);
135
136 setting_dict = g_variant_lookup_value (secrets, setting_name, NM_VARIANT_TYPE_SETTING);
137 if (!setting_dict) {
138 /* Success, no secrets */
139 emit_initialized (self, NULL);
140 return;
141 }
142
143 /* Update the connection with the new secrets */
144 if (!nm_connection_update_secrets (connection,
145 setting_name,
146 secrets,
147 &update_error))
148 g_warning ("Couldn't update secrets: %s", update_error->message);
149
150 emit_initialized (self, NULL);
151 }
152
153 gchar **
154 ce_page_get_mac_list (NMClient *client,
155 GType device_type,
156 const gchar *mac_property)
157 {
158 const GPtrArray *devices;
159 GPtrArray *macs;
160 int i;
161
162 macs = g_ptr_array_new ();
163 devices = nm_client_get_devices (client);
164 for (i = 0; devices && (i < devices->len); i++) {
165 NMDevice *dev = g_ptr_array_index (devices, i);
166 const char *iface;
167 g_autofree gchar *mac = NULL;
168 g_autofree gchar *item = NULL;
169
170 if (!G_TYPE_CHECK_INSTANCE_TYPE (dev, device_type))
171 continue;
172
173 g_object_get (G_OBJECT (dev), mac_property, &mac, NULL);
174 iface = nm_device_get_iface (NM_DEVICE (dev));
175 item = g_strdup_printf ("%s (%s)", mac, iface);
176 g_ptr_array_add (macs, g_steal_pointer (&item));
177 }
178
179 g_ptr_array_add (macs, NULL);
180 return (char **)g_ptr_array_free (macs, FALSE);
181 }
182
183 void
184 ce_page_setup_mac_combo (GtkComboBoxText *combo,
185 const gchar *current_mac,
186 gchar **mac_list)
187 {
188 gchar **m, *active_mac = NULL;
189 gint current_mac_len;
190 GtkWidget *entry;
191
192 if (current_mac)
193 current_mac_len = strlen (current_mac);
194 else
195 current_mac_len = -1;
196
197 for (m= mac_list; m && *m; m++) {
198 gtk_combo_box_text_append_text (combo, *m);
199 if (current_mac &&
200 g_ascii_strncasecmp (*m, current_mac, current_mac_len) == 0
201 && ((*m)[current_mac_len] == '\0' || (*m)[current_mac_len] == ' '))
202 active_mac = *m;
203 }
204
205 if (current_mac) {
206 if (!active_mac) {
207 gtk_combo_box_text_prepend_text (combo, current_mac);
208 }
209
210 entry = gtk_combo_box_get_child (GTK_COMBO_BOX (combo));
211 if (entry)
212 gtk_editable_set_text (GTK_EDITABLE (entry), active_mac ? active_mac : current_mac);
213 }
214 }
215
216 gchar *
217 ce_page_trim_address (const gchar *addr)
218 {
219 char *space;
220
221 if (!addr || *addr == '\0')
222 return NULL;
223
224 space = strchr (addr, ' ');
225 if (space != NULL)
226 return g_strndup (addr, space - addr);
227 return g_strdup (addr);
228 }
229
230 void
231 ce_page_setup_cloned_mac_combo (GtkComboBoxText *combo, const char *current, gboolean is_wifi)
232 {
233 GtkWidget *entry;
234 static const char *entries_wired[][2] = { { "preserve", N_("Preserve") },
235 { "permanent", N_("Permanent") },
236 { "random", N_("Random") },
237 { "stable", N_("Stable") } };
238 static const char *entries_wifi[][2] = { { "preserve", N_("Preserve") },
239 { "permanent", N_("Permanent") },
240 { "random", N_("Random") },
241 { "stable", N_("Stable") },
242 #if NM_CHECK_VERSION(1, 46, 0)
243 { "stable-ssid", N_("Stable per SSID") }
244 #endif
245 };
246 int i, active = -1;
247 const char *(*entries)[2] = is_wifi ? entries_wifi : entries_wired;
248 gsize n_entries = is_wifi ? G_N_ELEMENTS (entries_wifi) : G_N_ELEMENTS (entries_wired);
249
250 gtk_widget_set_tooltip_text (GTK_WIDGET (combo),
251 _("The MAC address entered here will be used as hardware address for "
252 "the network device this connection is activated on. This feature is "
253 "known as MAC cloning or spoofing. Example: 00:11:22:33:44:55"));
254
255 gtk_combo_box_text_remove_all (combo);
256
257 for (i = 0; i < n_entries; i++) {
258 gtk_combo_box_text_append (combo, entries[i][0], _(entries[i][1]));
259 if (g_strcmp0 (current, entries[i][0]) == 0)
260 active = i;
261 }
262
263 if (active != -1) {
264 gtk_combo_box_set_active (GTK_COMBO_BOX (combo), active);
265 } else if (current && current[0]) {
266 entry = gtk_combo_box_get_child (GTK_COMBO_BOX (combo));
267 g_assert (entry);
268 gtk_editable_set_text (GTK_EDITABLE (entry), current);
269 }
270 }
271
272 char *
273 ce_page_cloned_mac_get (GtkComboBoxText *combo)
274 {
275 g_autofree gchar *active_text = NULL;
276 const char *id;
277
278 id = gtk_combo_box_get_active_id (GTK_COMBO_BOX (combo));
279 if (id)
280 return g_strdup (id);
281
282 active_text = gtk_combo_box_text_get_active_text (combo);
283
284 if (active_text[0] == '\0')
285 return NULL;
286
287 return g_steal_pointer (&active_text);
288 }
289
290 gboolean
291 ce_page_address_is_valid (const gchar *addr)
292 {
293 guint8 invalid_addr[4][ETH_ALEN] = {
294 {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
295 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
296 {0x44, 0x44, 0x44, 0x44, 0x44, 0x44},
297 {0x00, 0x30, 0xb4, 0x00, 0x00, 0x00}, /* prism54 dummy MAC */
298 };
299 guint8 addr_bin[ETH_ALEN];
300 g_autofree gchar *trimmed_addr = NULL;
301 guint i;
302
303 if (!addr || *addr == '\0')
304 return TRUE;
305
306 trimmed_addr = ce_page_trim_address (addr);
307
308 if (!nm_utils_hwaddr_valid (trimmed_addr, -1))
309 return FALSE;
310
311 if (!nm_utils_hwaddr_aton (trimmed_addr, addr_bin, ETH_ALEN))
312 return FALSE;
313
314 /* Check for multicast address */
315 if ((((guint8 *) addr_bin)[0]) & 0x01)
316 return FALSE;
317
318 for (i = 0; i < G_N_ELEMENTS (invalid_addr); i++) {
319 if (nm_utils_hwaddr_matches (addr_bin, ETH_ALEN, invalid_addr[i], ETH_ALEN))
320 return FALSE;
321 }
322
323 return TRUE;
324 }
325
326 gboolean
327 ce_page_cloned_mac_combo_valid (GtkComboBoxText *combo)
328 {
329 g_autofree gchar *active_text = NULL;
330
331 if (gtk_combo_box_get_active (GTK_COMBO_BOX (combo)) != -1)
332 return TRUE;
333
334 active_text = gtk_combo_box_text_get_active_text (combo);
335
336 return active_text[0] == '\0' || ce_page_address_is_valid (active_text);
337 }
338
339 const gchar *
340 ce_page_get_security_setting (CEPage *self)
341 {
342 if (CE_PAGE_GET_IFACE (self)->get_security_setting)
343 return CE_PAGE_GET_IFACE (self)->get_security_setting (self);
344
345 return NULL;
346 }
347
348 gint
349 ce_get_property_default (NMSetting *setting, const gchar *property_name)
350 {
351 GParamSpec *spec;
352 GValue value = { 0, };
353
354 spec = g_object_class_find_property (G_OBJECT_GET_CLASS (setting), property_name);
355 g_return_val_if_fail (spec != NULL, -1);
356
357 g_value_init (&value, spec->value_type);
358 g_param_value_set_default (spec, &value);
359
360 if (G_VALUE_HOLDS_CHAR (&value))
361 return (int) g_value_get_schar (&value);
362 else if (G_VALUE_HOLDS_INT (&value))
363 return g_value_get_int (&value);
364 else if (G_VALUE_HOLDS_INT64 (&value))
365 return (int) g_value_get_int64 (&value);
366 else if (G_VALUE_HOLDS_LONG (&value))
367 return (int) g_value_get_long (&value);
368 else if (G_VALUE_HOLDS_UINT (&value))
369 return (int) g_value_get_uint (&value);
370 else if (G_VALUE_HOLDS_UINT64 (&value))
371 return (int) g_value_get_uint64 (&value);
372 else if (G_VALUE_HOLDS_ULONG (&value))
373 return (int) g_value_get_ulong (&value);
374 else if (G_VALUE_HOLDS_UCHAR (&value))
375 return (int) g_value_get_uchar (&value);
376 g_return_val_if_fail (FALSE, 0);
377 return 0;
378 }
379
380 gchar *
381 ce_page_get_next_available_name (const GPtrArray *connections,
382 NameFormat format,
383 const gchar *type_name)
384 {
385 GSList *names = NULL, *l;
386 gchar *cname = NULL;
387 gint i = 0;
388 guint con_idx;
389
390 for (con_idx = 0; con_idx < connections->len; con_idx++) {
391 NMConnection *connection = g_ptr_array_index (connections, con_idx);
392 const gchar *id;
393
394 id = nm_connection_get_id (connection);
395 g_assert (id);
396 names = g_slist_append (names, (gpointer) id);
397 }
398
399 /* Find the next available unique connection name */
400 while (!cname && (i++ < 10000)) {
401 g_autofree gchar *temp = NULL;
402 gboolean found = FALSE;
403
404 switch (format) {
405 case NAME_FORMAT_TYPE:
406 temp = g_strdup_printf ("%s %d", type_name, i);
407 break;
408 case NAME_FORMAT_PROFILE:
409 temp = g_strdup_printf (_("Profile %d"), i);
410 break;
411 default:
412 g_assert_not_reached ();
413 }
414
415 for (l = names; l; l = l->next) {
416 if (!strcmp (l->data, temp)) {
417 found = TRUE;
418 break;
419 }
420 }
421 if (!found)
422 cname = g_steal_pointer (&temp);
423 }
424 g_slist_free (names);
425
426 return cname;
427 }
428