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 |
|
|
|