/* -*- 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 #include #include #include #include #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_combo_box_get_child (GTK_COMBO_BOX (combo)); if (entry) gtk_editable_set_text (GTK_EDITABLE (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_combo_box_get_child (GTK_COMBO_BOX (combo)); g_assert (entry); gtk_editable_set_text (GTK_EDITABLE (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; }