diff options
Diffstat (limited to 'panels/network/connection-editor/ce-page.c')
-rw-r--r-- | panels/network/connection-editor/ce-page.c | 417 |
1 files changed, 417 insertions, 0 deletions
diff --git a/panels/network/connection-editor/ce-page.c b/panels/network/connection-editor/ce-page.c new file mode 100644 index 0000000..fb5d399 --- /dev/null +++ b/panels/network/connection-editor/ce-page.c @@ -0,0 +1,417 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2012 Red Hat, Inc + * + * Licensed under the GNU General Public License Version 2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" + +#include <string.h> + +#include <net/if_arp.h> +#include <netinet/ether.h> + +#include <NetworkManager.h> + +#include <glib/gi18n.h> + +#include "ce-page.h" + + +G_DEFINE_INTERFACE (CEPage, ce_page, G_TYPE_OBJECT) + +enum { + CHANGED, + INITIALIZED, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +gboolean +ce_page_validate (CEPage *self, NMConnection *connection, GError **error) +{ + g_return_val_if_fail (CE_IS_PAGE (self), FALSE); + g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE); + + if (CE_PAGE_GET_IFACE (self)->validate) + return CE_PAGE_GET_IFACE (self)->validate (self, connection, error); + + return TRUE; +} + +const char * +ce_page_get_title (CEPage *self) +{ + g_return_val_if_fail (CE_IS_PAGE (self), NULL); + + return CE_PAGE_GET_IFACE (self)->get_title (self); +} + +void +ce_page_changed (CEPage *self) +{ + g_return_if_fail (CE_IS_PAGE (self)); + + g_signal_emit (self, signals[CHANGED], 0); +} + +static void +ce_page_default_init (CEPageInterface *iface) +{ + signals[CHANGED] = + g_signal_new ("changed", + G_TYPE_FROM_INTERFACE (iface), + G_SIGNAL_RUN_FIRST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + signals[INITIALIZED] = + g_signal_new ("initialized", + G_TYPE_FROM_INTERFACE (iface), + G_SIGNAL_RUN_FIRST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); +} + +static void +emit_initialized (CEPage *self, + GError *error) +{ + g_signal_emit (self, signals[INITIALIZED], 0, error); + g_clear_error (&error); +} + +void +ce_page_complete_init (CEPage *self, + NMConnection *connection, + const gchar *setting_name, + GVariant *secrets, + GError *error) +{ + g_autoptr(GError) update_error = NULL; + g_autoptr(GVariant) setting_dict = NULL; + gboolean ignore_error = FALSE; + + g_return_if_fail (self != NULL); + g_return_if_fail (CE_IS_PAGE (self)); + + if (error) { + ignore_error = g_error_matches (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_SETTING_NOT_FOUND) || + g_error_matches (error, NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_NO_SECRETS); + } + + /* Ignore missing settings errors */ + if (error && !ignore_error) { + emit_initialized (self, error); + return; + } else if (!setting_name || !secrets || g_variant_n_children (secrets) == 0) { + /* Success, no secrets */ + emit_initialized (self, NULL); + return; + } + + g_assert (setting_name); + g_assert (secrets); + + setting_dict = g_variant_lookup_value (secrets, setting_name, NM_VARIANT_TYPE_SETTING); + if (!setting_dict) { + /* Success, no secrets */ + emit_initialized (self, NULL); + return; + } + + /* Update the connection with the new secrets */ + if (!nm_connection_update_secrets (connection, + setting_name, + secrets, + &update_error)) + g_warning ("Couldn't update secrets: %s", update_error->message); + + emit_initialized (self, NULL); +} + +gchar ** +ce_page_get_mac_list (NMClient *client, + GType device_type, + const gchar *mac_property) +{ + const GPtrArray *devices; + GPtrArray *macs; + int i; + + macs = g_ptr_array_new (); + devices = nm_client_get_devices (client); + for (i = 0; devices && (i < devices->len); i++) { + NMDevice *dev = g_ptr_array_index (devices, i); + const char *iface; + g_autofree gchar *mac = NULL; + g_autofree gchar *item = NULL; + + if (!G_TYPE_CHECK_INSTANCE_TYPE (dev, device_type)) + continue; + + g_object_get (G_OBJECT (dev), mac_property, &mac, NULL); + iface = nm_device_get_iface (NM_DEVICE (dev)); + item = g_strdup_printf ("%s (%s)", mac, iface); + g_ptr_array_add (macs, g_steal_pointer (&item)); + } + + g_ptr_array_add (macs, NULL); + return (char **)g_ptr_array_free (macs, FALSE); +} + +void +ce_page_setup_mac_combo (GtkComboBoxText *combo, + const gchar *current_mac, + gchar **mac_list) +{ + gchar **m, *active_mac = NULL; + gint current_mac_len; + GtkWidget *entry; + + if (current_mac) + current_mac_len = strlen (current_mac); + else + current_mac_len = -1; + + for (m= mac_list; m && *m; m++) { + gtk_combo_box_text_append_text (combo, *m); + if (current_mac && + g_ascii_strncasecmp (*m, current_mac, current_mac_len) == 0 + && ((*m)[current_mac_len] == '\0' || (*m)[current_mac_len] == ' ')) + active_mac = *m; + } + + if (current_mac) { + if (!active_mac) { + gtk_combo_box_text_prepend_text (combo, current_mac); + } + + entry = gtk_bin_get_child (GTK_BIN (combo)); + if (entry) + gtk_entry_set_text (GTK_ENTRY (entry), active_mac ? active_mac : current_mac); + } +} + +gchar * +ce_page_trim_address (const gchar *addr) +{ + char *space; + + if (!addr || *addr == '\0') + return NULL; + + space = strchr (addr, ' '); + if (space != NULL) + return g_strndup (addr, space - addr); + return g_strdup (addr); +} + +void +ce_page_setup_cloned_mac_combo (GtkComboBoxText *combo, const char *current) +{ + GtkWidget *entry; + static const char *entries[][2] = { { "preserve", N_("Preserve") }, + { "permanent", N_("Permanent") }, + { "random", N_("Random") }, + { "stable", N_("Stable") } }; + int i, active = -1; + + gtk_widget_set_tooltip_text (GTK_WIDGET (combo), + _("The MAC address entered here will be used as hardware address for " + "the network device this connection is activated on. This feature is " + "known as MAC cloning or spoofing. Example: 00:11:22:33:44:55")); + + gtk_combo_box_text_remove_all (combo); + + for (i = 0; i < G_N_ELEMENTS (entries); i++) { + gtk_combo_box_text_append (combo, entries[i][0], _(entries[i][1])); + if (g_strcmp0 (current, entries[i][0]) == 0) + active = i; + } + + if (active != -1) { + gtk_combo_box_set_active (GTK_COMBO_BOX (combo), active); + } else if (current && current[0]) { + entry = gtk_bin_get_child (GTK_BIN (combo)); + g_assert (entry); + gtk_entry_set_text (GTK_ENTRY (entry), current); + } +} + +char * +ce_page_cloned_mac_get (GtkComboBoxText *combo) +{ + g_autofree gchar *active_text = NULL; + const char *id; + + id = gtk_combo_box_get_active_id (GTK_COMBO_BOX (combo)); + if (id) + return g_strdup (id); + + active_text = gtk_combo_box_text_get_active_text (combo); + + if (active_text[0] == '\0') + return NULL; + + return g_steal_pointer (&active_text); +} + +gboolean +ce_page_address_is_valid (const gchar *addr) +{ + guint8 invalid_addr[4][ETH_ALEN] = { + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x44, 0x44, 0x44, 0x44, 0x44, 0x44}, + {0x00, 0x30, 0xb4, 0x00, 0x00, 0x00}, /* prism54 dummy MAC */ + }; + guint8 addr_bin[ETH_ALEN]; + g_autofree gchar *trimmed_addr = NULL; + guint i; + + if (!addr || *addr == '\0') + return TRUE; + + trimmed_addr = ce_page_trim_address (addr); + + if (!nm_utils_hwaddr_valid (trimmed_addr, -1)) + return FALSE; + + if (!nm_utils_hwaddr_aton (trimmed_addr, addr_bin, ETH_ALEN)) + return FALSE; + + /* Check for multicast address */ + if ((((guint8 *) addr_bin)[0]) & 0x01) + return FALSE; + + for (i = 0; i < G_N_ELEMENTS (invalid_addr); i++) { + if (nm_utils_hwaddr_matches (addr_bin, ETH_ALEN, invalid_addr[i], ETH_ALEN)) + return FALSE; + } + + return TRUE; +} + +gboolean +ce_page_cloned_mac_combo_valid (GtkComboBoxText *combo) +{ + g_autofree gchar *active_text = NULL; + + if (gtk_combo_box_get_active (GTK_COMBO_BOX (combo)) != -1) + return TRUE; + + active_text = gtk_combo_box_text_get_active_text (combo); + + return active_text[0] == '\0' || ce_page_address_is_valid (active_text); +} + +const gchar * +ce_page_get_security_setting (CEPage *self) +{ + if (CE_PAGE_GET_IFACE (self)->get_security_setting) + return CE_PAGE_GET_IFACE (self)->get_security_setting (self); + + return NULL; +} + +gint +ce_get_property_default (NMSetting *setting, const gchar *property_name) +{ + GParamSpec *spec; + GValue value = { 0, }; + + spec = g_object_class_find_property (G_OBJECT_GET_CLASS (setting), property_name); + g_return_val_if_fail (spec != NULL, -1); + + g_value_init (&value, spec->value_type); + g_param_value_set_default (spec, &value); + + if (G_VALUE_HOLDS_CHAR (&value)) + return (int) g_value_get_schar (&value); + else if (G_VALUE_HOLDS_INT (&value)) + return g_value_get_int (&value); + else if (G_VALUE_HOLDS_INT64 (&value)) + return (int) g_value_get_int64 (&value); + else if (G_VALUE_HOLDS_LONG (&value)) + return (int) g_value_get_long (&value); + else if (G_VALUE_HOLDS_UINT (&value)) + return (int) g_value_get_uint (&value); + else if (G_VALUE_HOLDS_UINT64 (&value)) + return (int) g_value_get_uint64 (&value); + else if (G_VALUE_HOLDS_ULONG (&value)) + return (int) g_value_get_ulong (&value); + else if (G_VALUE_HOLDS_UCHAR (&value)) + return (int) g_value_get_uchar (&value); + g_return_val_if_fail (FALSE, 0); + return 0; +} + +gchar * +ce_page_get_next_available_name (const GPtrArray *connections, + NameFormat format, + const gchar *type_name) +{ + GSList *names = NULL, *l; + gchar *cname = NULL; + gint i = 0; + guint con_idx; + + for (con_idx = 0; con_idx < connections->len; con_idx++) { + NMConnection *connection = g_ptr_array_index (connections, con_idx); + const gchar *id; + + id = nm_connection_get_id (connection); + g_assert (id); + names = g_slist_append (names, (gpointer) id); + } + + /* Find the next available unique connection name */ + while (!cname && (i++ < 10000)) { + g_autofree gchar *temp = NULL; + gboolean found = FALSE; + + switch (format) { + case NAME_FORMAT_TYPE: + temp = g_strdup_printf ("%s %d", type_name, i); + break; + case NAME_FORMAT_PROFILE: + temp = g_strdup_printf (_("Profile %d"), i); + break; + default: + g_assert_not_reached (); + } + + for (l = names; l; l = l->next) { + if (!strcmp (l->data, temp)) { + found = TRUE; + break; + } + } + if (!found) + cname = g_steal_pointer (&temp); + } + g_slist_free (names); + + return cname; +} |