diff options
Diffstat (limited to '')
33 files changed, 7763 insertions, 0 deletions
diff --git a/panels/network/connection-editor/8021x-security-page.ui b/panels/network/connection-editor/8021x-security-page.ui new file mode 100644 index 0000000..cde5d85 --- /dev/null +++ b/panels/network/connection-editor/8021x-security-page.ui @@ -0,0 +1,62 @@ +<?xml version="1.0" encoding="UTF-8"?> +<interface> + <!-- interface-requires gtk+ 3.0 --> + <template class="CEPage8021xSecurity" parent="GtkGrid"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="margin_start">50</property> + <property name="margin_end">50</property> + <property name="margin_top">12</property> + <property name="margin_bottom">12</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="row_spacing">10</property> + <property name="column_spacing">6</property> + <child> + <object class="GtkLabel" id="security_label"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="xalign">1</property> + <property name="label" translatable="yes">802.1x _Security</property> + <property name="use_underline">True</property> + <property name="mnemonic_widget">enable_8021x_switch</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">0</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkSwitch" id="enable_8021x_switch"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="halign">start</property> + <property name="hexpand">True</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">0</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkBox" id="box"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <child> + <placeholder/> + </child> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">1</property> + <property name="width">2</property> + <property name="height">1</property> + </packing> + </child> + </template> +</interface> diff --git a/panels/network/connection-editor/ce-page-8021x-security.c b/panels/network/connection-editor/ce-page-8021x-security.c new file mode 100644 index 0000000..1e4f146 --- /dev/null +++ b/panels/network/connection-editor/ce-page-8021x-security.c @@ -0,0 +1,209 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager Connection editor -- Connection editor for NetworkManager + * + * Dan Williams <dcbw@redhat.com> + * + * 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. + * + * (C) Copyright 2008 - 2012 Red Hat, Inc. + */ + +#include "config.h" + +#include <glib/gi18n.h> +#include <NetworkManager.h> +#include <string.h> + +#include "ws-wpa-eap.h" +#include "wireless-security.h" +#include "ce-page.h" +#include "ce-page-ethernet.h" +#include "ce-page-8021x-security.h" + +struct _CEPage8021xSecurity { + GtkGrid parent; + + GtkBox *box; + GtkSwitch *enable_8021x_switch; + GtkLabel *security_label; + + NMConnection *connection; + WirelessSecurityWPAEAP *security; + GtkSizeGroup *group; + gboolean initial_have_8021x; +}; + +static void ce_page_iface_init (CEPageInterface *); + +G_DEFINE_TYPE_WITH_CODE (CEPage8021xSecurity, ce_page_8021x_security, GTK_TYPE_GRID, + G_IMPLEMENT_INTERFACE (ce_page_get_type (), ce_page_iface_init)) + +static void +enable_toggled (CEPage8021xSecurity *self) +{ + gtk_widget_set_sensitive (GTK_WIDGET (self->security), gtk_switch_get_active (self->enable_8021x_switch)); + ce_page_changed (CE_PAGE (self)); +} + +static void +security_item_changed_cb (CEPage8021xSecurity *self) +{ + ce_page_changed (CE_PAGE (self)); +} + +static void +finish_setup (CEPage8021xSecurity *self, gpointer unused, GError *error, gpointer user_data) +{ + GtkWidget *parent; + + if (error) + return; + + self->group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); + + self->security = ws_wpa_eap_new (self->connection); + if (!self->security) { + g_warning ("Could not load 802.1x user interface."); + return; + } + + g_signal_connect_object (WIRELESS_SECURITY (self->security), "changed", G_CALLBACK (security_item_changed_cb), self, G_CONNECT_SWAPPED); + parent = gtk_widget_get_parent (GTK_WIDGET (self->security)); + if (parent) + gtk_container_remove (GTK_CONTAINER (parent), GTK_WIDGET (self->security)); + + gtk_switch_set_active (self->enable_8021x_switch, self->initial_have_8021x); + g_signal_connect_object (self->enable_8021x_switch, "notify::active", G_CALLBACK (enable_toggled), self, G_CONNECT_SWAPPED); + gtk_widget_set_sensitive (GTK_WIDGET (self->security), self->initial_have_8021x); + + gtk_size_group_add_widget (self->group, GTK_WIDGET (self->security_label)); + wireless_security_add_to_size_group (WIRELESS_SECURITY (self->security), self->group); + + gtk_container_add (GTK_CONTAINER (self->box), GTK_WIDGET (self->security)); + +} + +static const gchar * +ce_page_8021x_security_get_security_setting (CEPage *page) +{ + CEPage8021xSecurity *self = CE_PAGE_8021X_SECURITY (page); + + if (self->initial_have_8021x) + return NM_SETTING_802_1X_SETTING_NAME; + + return NULL; +} + +static const gchar * +ce_page_8021x_security_get_title (CEPage *page) +{ + return _("Security"); +} + +static gboolean +ce_page_8021x_security_validate (CEPage *cepage, NMConnection *connection, GError **error) +{ + CEPage8021xSecurity *self = CE_PAGE_8021X_SECURITY (cepage); + gboolean valid = TRUE; + + if (gtk_switch_get_active (self->enable_8021x_switch)) { + NMSetting *s_8021x; + + /* FIXME: get failed property and error out of wireless security objects */ + valid = wireless_security_validate (WIRELESS_SECURITY (self->security), error); + if (valid) { + g_autoptr(NMConnection) tmp_connection = NULL; + NMSetting *s_con; + + /* Here's a nice hack to work around the fact that ws_802_1x_fill_connection needs wireless setting. */ + tmp_connection = nm_simple_connection_new (); + nm_connection_add_setting (tmp_connection, nm_setting_wireless_new ()); + + /* temp connection needs a 'connection' setting too, since most of + * the EAP methods need the UUID for CA cert ignore stuff. + */ + s_con = nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION); + nm_connection_add_setting (tmp_connection, nm_setting_duplicate (s_con)); + + ws_wpa_eap_fill_connection (self->security, tmp_connection); + + s_8021x = nm_connection_get_setting (tmp_connection, NM_TYPE_SETTING_802_1X); + nm_connection_add_setting (connection, NM_SETTING (g_object_ref (s_8021x))); + } + } else { + nm_connection_remove_setting (connection, NM_TYPE_SETTING_802_1X); + valid = TRUE; + } + + return valid; +} + +static void +ce_page_8021x_security_init (CEPage8021xSecurity *self) +{ + gtk_widget_init_template (GTK_WIDGET (self)); +} + +static void +ce_page_8021x_security_dispose (GObject *object) +{ + CEPage8021xSecurity *self = CE_PAGE_8021X_SECURITY (object); + + g_clear_object (&self->connection); + g_clear_object (&self->security); + g_clear_object (&self->group); + + G_OBJECT_CLASS (ce_page_8021x_security_parent_class)->dispose (object); +} + +static void +ce_page_8021x_security_class_init (CEPage8021xSecurityClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->dispose = ce_page_8021x_security_dispose; + + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/network/8021x-security-page.ui"); + + gtk_widget_class_bind_template_child (widget_class, CEPage8021xSecurity, box); + gtk_widget_class_bind_template_child (widget_class, CEPage8021xSecurity, enable_8021x_switch); + gtk_widget_class_bind_template_child (widget_class, CEPage8021xSecurity, security_label); +} + +static void +ce_page_iface_init (CEPageInterface *iface) +{ + iface->get_security_setting = ce_page_8021x_security_get_security_setting; + iface->get_title = ce_page_8021x_security_get_title; + iface->validate = ce_page_8021x_security_validate; +} + +CEPage8021xSecurity * +ce_page_8021x_security_new (NMConnection *connection) +{ + CEPage8021xSecurity *self; + + self = CE_PAGE_8021X_SECURITY (g_object_new (ce_page_8021x_security_get_type (), NULL)); + + self->connection = g_object_ref (connection); + + if (nm_connection_get_setting_802_1x (connection)) + self->initial_have_8021x = TRUE; + + g_signal_connect (self, "initialized", G_CALLBACK (finish_setup), NULL); + + return self; +} diff --git a/panels/network/connection-editor/ce-page-8021x-security.h b/panels/network/connection-editor/ce-page-8021x-security.h new file mode 100644 index 0000000..e783587 --- /dev/null +++ b/panels/network/connection-editor/ce-page-8021x-security.h @@ -0,0 +1,34 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager Connection editor -- Connection editor for NetworkManager + * + * Dan Williams <dcbw@redhat.com> + * + * 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. + * + * (C) Copyright 2008 - 2012 Red Hat, Inc. + */ + +#pragma once + +#include <gtk/gtk.h> +#include <NetworkManager.h> + +G_BEGIN_DECLS + +G_DECLARE_FINAL_TYPE (CEPage8021xSecurity, ce_page_8021x_security, CE, PAGE_8021X_SECURITY, GtkGrid) + +CEPage8021xSecurity *ce_page_8021x_security_new (NMConnection *connection); + +G_END_DECLS diff --git a/panels/network/connection-editor/ce-page-details.c b/panels/network/connection-editor/ce-page-details.c new file mode 100644 index 0000000..cd5d36c --- /dev/null +++ b/panels/network/connection-editor/ce-page-details.c @@ -0,0 +1,525 @@ +/* -*- 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 <glib/gi18n.h> + +#include <NetworkManager.h> + +#include "ce-page.h" +#include "ce-page-details.h" + +struct _CEPageDetails +{ + GtkGrid parent; + + GtkCheckButton *all_user_check; + GtkCheckButton *auto_connect_check; + GtkLabel *dns_heading_label; + GtkLabel *dns_label; + GtkButton *forget_button; + GtkLabel *freq_heading_label; + GtkLabel *freq_label; + GtkLabel *ipv4_heading_label; + GtkLabel *ipv4_label; + GtkLabel *ipv6_heading_label; + GtkLabel *ipv6_label; + GtkLabel *last_used_heading_label; + GtkLabel *last_used_label; + GtkLabel *mac_heading_label; + GtkLabel *mac_label; + GtkCheckButton *restrict_data_check; + GtkLabel *route_heading_label; + GtkLabel *route_label; + GtkLabel *security_heading_label; + GtkLabel *security_label; + GtkLabel *speed_heading_label; + GtkLabel *speed_label; + GtkLabel *strength_heading_label; + GtkLabel *strength_label; + + NMConnection *connection; + NMDevice *device; + NMAccessPoint *ap; + NetConnectionEditor *editor; +}; + +static void ce_page_iface_init (CEPageInterface *); + +G_DEFINE_TYPE_WITH_CODE (CEPageDetails, ce_page_details, GTK_TYPE_GRID, + G_IMPLEMENT_INTERFACE (ce_page_get_type (), ce_page_iface_init)) + +static void +forget_cb (CEPageDetails *self) +{ + net_connection_editor_forget (self->editor); +} + +static gchar * +get_ap_security_string (NMAccessPoint *ap) +{ + NM80211ApSecurityFlags wpa_flags, rsn_flags; + NM80211ApFlags flags; + GString *str; + + flags = nm_access_point_get_flags (ap); + wpa_flags = nm_access_point_get_wpa_flags (ap); + rsn_flags = nm_access_point_get_rsn_flags (ap); + + str = g_string_new (""); + if ((flags & NM_802_11_AP_FLAGS_PRIVACY) && + (wpa_flags == NM_802_11_AP_SEC_NONE) && + (rsn_flags == NM_802_11_AP_SEC_NONE)) { + /* TRANSLATORS: this WEP WiFi security */ + g_string_append_printf (str, "%s, ", _("WEP")); + } + if (wpa_flags != NM_802_11_AP_SEC_NONE) { + /* TRANSLATORS: this WPA WiFi security */ + g_string_append_printf (str, "%s, ", _("WPA")); + } + if (rsn_flags != NM_802_11_AP_SEC_NONE) { +#if NM_CHECK_VERSION(1,20,6) + if (rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_SAE) { + /* TRANSLATORS: this WPA3 WiFi security */ + g_string_append_printf (str, "%s, ", _("WPA3")); + } +#if NM_CHECK_VERSION(1,24,0) + else if (rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_OWE) { + /* TRANSLATORS: this Enhanced Open WiFi security */ + g_string_append_printf (str, "%s, ", _("Enhanced Open")); + } +#endif + else +#endif + { + /* TRANSLATORS: this WPA WiFi security */ + g_string_append_printf (str, "%s, ", _("WPA2")); + } + } + if ((wpa_flags & NM_802_11_AP_SEC_KEY_MGMT_802_1X) || + (rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_802_1X)) { + /* TRANSLATORS: this Enterprise WiFi security */ + g_string_append_printf (str, "%s, ", _("Enterprise")); + } + if (str->len > 0) + g_string_set_size (str, str->len - 2); + else { + g_string_append (str, C_("Wifi security", "None")); + } + return g_string_free (str, FALSE); +} + +static void +update_last_used (CEPageDetails *self, NMConnection *connection) +{ + g_autofree gchar *last_used = NULL; + g_autoptr(GDateTime) now = NULL; + g_autoptr(GDateTime) then = NULL; + gint days; + GTimeSpan diff; + guint64 timestamp; + NMSettingConnection *s_con; + + s_con = nm_connection_get_setting_connection (connection); + if (s_con == NULL) + goto out; + timestamp = nm_setting_connection_get_timestamp (s_con); + if (timestamp == 0) { + last_used = g_strdup (_("Never")); + goto out; + } + + /* calculate the amount of time that has elapsed */ + now = g_date_time_new_now_utc (); + then = g_date_time_new_from_unix_utc (timestamp); + + diff = g_date_time_difference (now, then); + days = diff / G_TIME_SPAN_DAY; + if (days == 0) + last_used = g_strdup (_("Today")); + else if (days == 1) + last_used = g_strdup (_("Yesterday")); + else + last_used = g_strdup_printf (ngettext ("%i day ago", "%i days ago", days), days); +out: + gtk_label_set_label (self->last_used_label, last_used); + gtk_widget_set_visible (GTK_WIDGET (self->last_used_heading_label), last_used != NULL); + gtk_widget_set_visible (GTK_WIDGET (self->last_used_label), last_used != NULL); +} + +static void +all_user_changed (CEPageDetails *self) +{ + gboolean all_users; + NMSettingConnection *sc; + + sc = nm_connection_get_setting_connection (self->connection); + all_users = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->all_user_check)); + + g_object_set (sc, "permissions", NULL, NULL); + if (!all_users) + nm_setting_connection_add_permission (sc, "user", g_get_user_name (), NULL); +} + +static void +restrict_data_changed (CEPageDetails *self) +{ + NMSettingConnection *s_con; + NMMetered metered; + + s_con = nm_connection_get_setting_connection (self->connection); + + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->restrict_data_check))) + metered = NM_METERED_YES; + else + metered = NM_METERED_NO; + + g_object_set (s_con, "metered", metered, NULL); +} + +static void +update_restrict_data (CEPageDetails *self) +{ + NMSettingConnection *s_con; + NMMetered metered; + const gchar *type; + + s_con = nm_connection_get_setting_connection (self->connection); + + if (s_con == NULL) + return; + + /* Disable for VPN; NetworkManager does not implement that yet (see + * bug https://bugzilla.gnome.org/show_bug.cgi?id=792618) */ + type = nm_setting_connection_get_connection_type (s_con); + if (g_str_equal (type, NM_SETTING_VPN_SETTING_NAME)) + return; + + metered = nm_setting_connection_get_metered (s_con); + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->restrict_data_check), + metered == NM_METERED_YES || metered == NM_METERED_GUESS_YES); + gtk_widget_show (GTK_WIDGET (self->restrict_data_check)); + + g_signal_connect_object (self->restrict_data_check, "notify::active", G_CALLBACK (restrict_data_changed), self, G_CONNECT_SWAPPED); + g_signal_connect_object (self->restrict_data_check, "notify::active", G_CALLBACK (ce_page_changed), self, G_CONNECT_SWAPPED); +} + +static void +connect_details_page (CEPageDetails *self) +{ + NMSettingConnection *sc; + guint speed; + NMDeviceWifiCapabilities wifi_caps; + guint frequency; + guint strength; + NMDeviceState state; + NMAccessPoint *active_ap; + g_autofree gchar *speed_label = NULL; + const gchar *type; + const gchar *hw_address = NULL; + g_autofree gchar *security_string = NULL; + g_autofree gchar *freq_string = NULL; + const gchar *strength_label; + gboolean device_is_active; + NMIPConfig *ipv4_config = NULL, *ipv6_config = NULL; + gboolean have_ipv4_address = FALSE, have_ipv6_address = FALSE; + + sc = nm_connection_get_setting_connection (self->connection); + type = nm_setting_connection_get_connection_type (sc); + + if (NM_IS_DEVICE_WIFI (self->device)) { + active_ap = nm_device_wifi_get_active_access_point (NM_DEVICE_WIFI (self->device)); + frequency = nm_access_point_get_frequency (active_ap); + } else { + active_ap = NULL; + frequency = 0; + } + + state = self->device ? nm_device_get_state (self->device) : NM_DEVICE_STATE_DISCONNECTED; + + device_is_active = FALSE; + speed = 0; + wifi_caps = 0; + if (active_ap && self->ap == active_ap && state != NM_DEVICE_STATE_UNAVAILABLE) { + device_is_active = TRUE; + if (NM_IS_DEVICE_WIFI (self->device)) { + speed = nm_device_wifi_get_bitrate (NM_DEVICE_WIFI (self->device)) / 1000; + wifi_caps = nm_device_wifi_get_capabilities (NM_DEVICE_WIFI (self->device)); + } + } else if (self->device) { + NMActiveConnection *ac; + const gchar *p1, *p2; + + ac = nm_device_get_active_connection (self->device); + p1 = ac ? nm_active_connection_get_uuid (ac) : NULL; + p2 = nm_connection_get_uuid (self->connection); + if (g_strcmp0 (p1, p2) == 0) { + device_is_active = TRUE; + if (NM_IS_DEVICE_WIFI (self->device)) { + speed = nm_device_wifi_get_bitrate (NM_DEVICE_WIFI (self->device)) / 1000; + wifi_caps = nm_device_wifi_get_capabilities (NM_DEVICE_WIFI (self->device)); + } else if (NM_IS_DEVICE_ETHERNET (self->device)) + speed = nm_device_ethernet_get_speed (NM_DEVICE_ETHERNET (self->device)); + } + } + + if (speed > 0 && frequency > 0) + speed_label = g_strdup_printf (_("%d Mb/s (%1.1f GHz)"), speed, (float) (frequency) / 1000.0); + else if (speed > 0) + speed_label = g_strdup_printf (_("%d Mb/s"), speed); + gtk_label_set_label (self->speed_label, speed_label); + gtk_widget_set_visible (GTK_WIDGET (self->speed_heading_label), speed_label != NULL); + gtk_widget_set_visible (GTK_WIDGET (self->speed_label), speed_label != NULL); + + if (NM_IS_DEVICE_WIFI (self->device)) + hw_address = nm_device_wifi_get_hw_address (NM_DEVICE_WIFI (self->device)); + else if (NM_IS_DEVICE_ETHERNET (self->device)) + hw_address = nm_device_ethernet_get_hw_address (NM_DEVICE_ETHERNET (self->device)); + + gtk_label_set_label (self->mac_label, hw_address); + gtk_widget_set_visible (GTK_WIDGET (self->mac_heading_label), hw_address != NULL); + gtk_widget_set_visible (GTK_WIDGET (self->mac_label), hw_address != NULL); + + if (wifi_caps & NM_WIFI_DEVICE_CAP_FREQ_VALID) { + if (wifi_caps & NM_WIFI_DEVICE_CAP_FREQ_2GHZ && + wifi_caps & NM_WIFI_DEVICE_CAP_FREQ_5GHZ) + freq_string = g_strdup (_("2.4 GHz / 5 GHz")); + else if (wifi_caps & NM_WIFI_DEVICE_CAP_FREQ_2GHZ) + freq_string = g_strdup (_("2.4 GHz")); + else if (wifi_caps & NM_WIFI_DEVICE_CAP_FREQ_5GHZ) + freq_string = g_strdup (_("5 GHz")); + } + + gtk_label_set_label (self->freq_label, freq_string); + gtk_widget_set_visible (GTK_WIDGET (self->freq_heading_label), freq_string != NULL); + gtk_widget_set_visible (GTK_WIDGET (self->freq_label), freq_string != NULL); + + if (device_is_active && active_ap) + security_string = get_ap_security_string (active_ap); + gtk_label_set_label (self->security_label, security_string); + gtk_widget_set_visible (GTK_WIDGET (self->security_heading_label), security_string != NULL); + gtk_widget_set_visible (GTK_WIDGET (self->security_label), security_string != NULL); + + strength = 0; + if (self->ap != NULL) + strength = nm_access_point_get_strength (self->ap); + + if (strength <= 0) + strength_label = NULL; + else if (strength < 20) + strength_label = C_("Signal strength", "None"); + else if (strength < 40) + strength_label = C_("Signal strength", "Weak"); + else if (strength < 50) + strength_label = C_("Signal strength", "Ok"); + else if (strength < 80) + strength_label = C_("Signal strength", "Good"); + else + strength_label = C_("Signal strength", "Excellent"); + gtk_label_set_label (self->strength_label, strength_label); + gtk_widget_set_visible (GTK_WIDGET (self->strength_heading_label), strength_label != NULL); + gtk_widget_set_visible (GTK_WIDGET (self->strength_label), strength_label != NULL); + + if (device_is_active && self->device != NULL) + ipv4_config = nm_device_get_ip4_config (self->device); + if (ipv4_config != NULL) { + GPtrArray *addresses; + const gchar *ipv4_text = NULL; + g_autofree gchar *dns_text = NULL; + const gchar *route_text; + + addresses = nm_ip_config_get_addresses (ipv4_config); + if (addresses->len > 0) + ipv4_text = nm_ip_address_get_address (g_ptr_array_index (addresses, 0)); + gtk_label_set_label (self->ipv4_label, ipv4_text); + gtk_widget_set_visible (GTK_WIDGET (self->ipv4_heading_label), ipv4_text != NULL); + gtk_widget_set_visible (GTK_WIDGET (self->ipv4_label), ipv4_text != NULL); + have_ipv4_address = ipv4_text != NULL; + + dns_text = g_strjoinv (" ", (char **) nm_ip_config_get_nameservers (ipv4_config)); + gtk_label_set_label (self->dns_label, dns_text); + gtk_widget_set_visible (GTK_WIDGET (self->dns_heading_label), dns_text != NULL); + gtk_widget_set_visible (GTK_WIDGET (self->dns_label), dns_text != NULL); + + route_text = nm_ip_config_get_gateway (ipv4_config); + gtk_label_set_label (self->route_label, route_text); + gtk_widget_set_visible (GTK_WIDGET (self->route_heading_label), route_text != NULL); + gtk_widget_set_visible (GTK_WIDGET (self->route_label), route_text != NULL); + } else { + gtk_widget_hide (GTK_WIDGET (self->ipv4_heading_label)); + gtk_widget_hide (GTK_WIDGET (self->ipv4_label)); + gtk_widget_hide (GTK_WIDGET (self->dns_heading_label)); + gtk_widget_hide (GTK_WIDGET (self->dns_label)); + gtk_widget_hide (GTK_WIDGET (self->route_heading_label)); + gtk_widget_hide (GTK_WIDGET (self->route_label)); + } + + if (device_is_active && self->device != NULL) + ipv6_config = nm_device_get_ip6_config (self->device); + if (ipv6_config != NULL) { + GPtrArray *addresses; + const gchar *ipv6_text = NULL; + + addresses = nm_ip_config_get_addresses (ipv6_config); + if (addresses->len > 0) + ipv6_text = nm_ip_address_get_address (g_ptr_array_index (addresses, 0)); + gtk_label_set_label (self->ipv6_label, ipv6_text); + gtk_widget_set_visible (GTK_WIDGET (self->ipv6_heading_label), ipv6_text != NULL); + gtk_widget_set_visible (GTK_WIDGET (self->ipv6_label), ipv6_text != NULL); + have_ipv6_address = ipv6_text != NULL; + } else { + gtk_widget_hide (GTK_WIDGET (self->ipv6_heading_label)); + gtk_widget_hide (GTK_WIDGET (self->ipv6_label)); + } + + if (have_ipv4_address && have_ipv6_address) { + gtk_label_set_label (self->ipv4_heading_label, _("IPv4 Address")); + gtk_label_set_label (self->ipv6_heading_label, _("IPv6 Address")); + } + else { + gtk_label_set_label (self->ipv4_heading_label, _("IP Address")); + gtk_label_set_label (self->ipv6_heading_label, _("IP Address")); + } + + if (!device_is_active && self->connection) + update_last_used (self, self->connection); + else { + gtk_widget_set_visible (GTK_WIDGET (self->last_used_heading_label), FALSE); + gtk_widget_set_visible (GTK_WIDGET (self->last_used_label), FALSE); + } + + /* Auto connect check */ + if (g_str_equal (type, NM_SETTING_VPN_SETTING_NAME)) { + gtk_widget_hide (GTK_WIDGET (self->auto_connect_check)); + } else { + g_object_bind_property (sc, "autoconnect", + self->auto_connect_check, "active", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + g_signal_connect_object (self->auto_connect_check, "toggled", G_CALLBACK (ce_page_changed), self, G_CONNECT_SWAPPED); + } + + /* All users check */ + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->all_user_check), + nm_setting_connection_get_num_permissions (sc) == 0); + g_signal_connect_object (self->all_user_check, "toggled", G_CALLBACK (all_user_changed), self, G_CONNECT_SWAPPED); + g_signal_connect_object (self->all_user_check, "toggled", G_CALLBACK (ce_page_changed), self, G_CONNECT_SWAPPED); + + /* Restrict Data check */ + update_restrict_data (self); + + /* Forget button */ + g_signal_connect_object (self->forget_button, "clicked", G_CALLBACK (forget_cb), self, G_CONNECT_SWAPPED); + + if (g_str_equal (type, NM_SETTING_WIRELESS_SETTING_NAME)) + gtk_button_set_label (self->forget_button, _("Forget Connection")); + else if (g_str_equal (type, NM_SETTING_WIRED_SETTING_NAME)) + gtk_button_set_label (self->forget_button, _("Remove Connection Profile")); + else if (g_str_equal (type, NM_SETTING_VPN_SETTING_NAME)) + gtk_button_set_label (self->forget_button, _("Remove VPN")); + else + gtk_widget_hide (GTK_WIDGET (self->forget_button)); +} + +static void +ce_page_details_dispose (GObject *object) +{ + CEPageDetails *self = CE_PAGE_DETAILS (object); + + g_clear_object (&self->connection); + + G_OBJECT_CLASS (ce_page_details_parent_class)->dispose (object); +} + +static const gchar * +ce_page_details_get_title (CEPage *page) +{ + return _("Details"); +} + +static void +ce_page_details_init (CEPageDetails *self) +{ + gtk_widget_init_template (GTK_WIDGET (self)); +} + +static void +ce_page_details_class_init (CEPageDetailsClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->dispose = ce_page_details_dispose; + + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/network/details-page.ui"); + + gtk_widget_class_bind_template_child (widget_class, CEPageDetails, all_user_check); + gtk_widget_class_bind_template_child (widget_class, CEPageDetails, auto_connect_check); + gtk_widget_class_bind_template_child (widget_class, CEPageDetails, dns_heading_label); + gtk_widget_class_bind_template_child (widget_class, CEPageDetails, dns_label); + gtk_widget_class_bind_template_child (widget_class, CEPageDetails, forget_button); + gtk_widget_class_bind_template_child (widget_class, CEPageDetails, freq_heading_label); + gtk_widget_class_bind_template_child (widget_class, CEPageDetails, freq_label); + gtk_widget_class_bind_template_child (widget_class, CEPageDetails, ipv4_heading_label); + gtk_widget_class_bind_template_child (widget_class, CEPageDetails, ipv4_label); + gtk_widget_class_bind_template_child (widget_class, CEPageDetails, ipv6_heading_label); + gtk_widget_class_bind_template_child (widget_class, CEPageDetails, ipv6_label); + gtk_widget_class_bind_template_child (widget_class, CEPageDetails, last_used_heading_label); + gtk_widget_class_bind_template_child (widget_class, CEPageDetails, last_used_label); + gtk_widget_class_bind_template_child (widget_class, CEPageDetails, mac_heading_label); + gtk_widget_class_bind_template_child (widget_class, CEPageDetails, mac_label); + gtk_widget_class_bind_template_child (widget_class, CEPageDetails, restrict_data_check); + gtk_widget_class_bind_template_child (widget_class, CEPageDetails, route_heading_label); + gtk_widget_class_bind_template_child (widget_class, CEPageDetails, route_label); + gtk_widget_class_bind_template_child (widget_class, CEPageDetails, security_heading_label); + gtk_widget_class_bind_template_child (widget_class, CEPageDetails, security_label); + gtk_widget_class_bind_template_child (widget_class, CEPageDetails, speed_heading_label); + gtk_widget_class_bind_template_child (widget_class, CEPageDetails, speed_label); + gtk_widget_class_bind_template_child (widget_class, CEPageDetails, strength_heading_label); + gtk_widget_class_bind_template_child (widget_class, CEPageDetails, strength_label); +} + +static void +ce_page_iface_init (CEPageInterface *iface) +{ + iface->get_title = ce_page_details_get_title; +} + +CEPageDetails * +ce_page_details_new (NMConnection *connection, + NMDevice *device, + NMAccessPoint *ap, + NetConnectionEditor *editor) +{ + CEPageDetails *self; + + self = CE_PAGE_DETAILS (g_object_new (ce_page_details_get_type (), NULL)); + + self->connection = g_object_ref (connection); + self->editor = editor; + self->device = device; + self->ap = ap; + + connect_details_page (self); + + return self; +} diff --git a/panels/network/connection-editor/ce-page-details.h b/panels/network/connection-editor/ce-page-details.h new file mode 100644 index 0000000..4fd660c --- /dev/null +++ b/panels/network/connection-editor/ce-page-details.h @@ -0,0 +1,38 @@ +/* -*- 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. + */ + +#pragma once + +#include <gtk/gtk.h> +#include <NetworkManager.h> + +#include "net-connection-editor.h" + +G_BEGIN_DECLS + +G_DECLARE_FINAL_TYPE (CEPageDetails, ce_page_details, CE, PAGE_DETAILS, GtkGrid) + +CEPageDetails *ce_page_details_new (NMConnection *connection, + NMDevice *device, + NMAccessPoint *ap, + NetConnectionEditor *editor); + +G_END_DECLS diff --git a/panels/network/connection-editor/ce-page-ethernet.c b/panels/network/connection-editor/ce-page-ethernet.c new file mode 100644 index 0000000..98fe460 --- /dev/null +++ b/panels/network/connection-editor/ce-page-ethernet.c @@ -0,0 +1,224 @@ +/* -*- 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 <glib/gi18n.h> +#include <net/if_arp.h> +#include <NetworkManager.h> + +#include "ce-page.h" +#include "ce-page-ethernet.h" +#include "ui-helpers.h" + +struct _CEPageEthernet +{ + GtkGrid parent; + + GtkComboBoxText *cloned_mac_combo; + GtkComboBoxText *mac_combo; + GtkSpinButton *mtu_spin; + GtkWidget *mtu_label; + GtkEntry *name_entry; + + NMClient *client; + NMSettingConnection *setting_connection; + NMSettingWired *setting_wired; +}; + +static void ce_page_iface_init (CEPageInterface *); + +G_DEFINE_TYPE_WITH_CODE (CEPageEthernet, ce_page_ethernet, GTK_TYPE_GRID, + G_IMPLEMENT_INTERFACE (ce_page_get_type (), ce_page_iface_init)) + +static void +mtu_changed (CEPageEthernet *self) +{ + if (gtk_spin_button_get_value_as_int (self->mtu_spin) == 0) + gtk_widget_hide (self->mtu_label); + else + gtk_widget_show (self->mtu_label); +} + +static void +mtu_output_cb (CEPageEthernet *self) +{ + gint defvalue; + gint val; + g_autofree gchar *buf = NULL; + + val = gtk_spin_button_get_value_as_int (self->mtu_spin); + defvalue = ce_get_property_default (NM_SETTING (self->setting_wired), NM_SETTING_WIRED_MTU); + if (val == defvalue) + buf = g_strdup (_("automatic")); + else + buf = g_strdup_printf ("%d", val); + + if (strcmp (buf, gtk_entry_get_text (GTK_ENTRY (self->mtu_spin)))) + gtk_entry_set_text (GTK_ENTRY (self->mtu_spin), buf); +} + +static void +connect_ethernet_page (CEPageEthernet *self) +{ + NMSettingWired *setting = self->setting_wired; + char **mac_list; + const char *s_mac_str; + const gchar *name; + const gchar *cloned_mac; + + name = nm_setting_connection_get_id (self->setting_connection); + gtk_entry_set_text (self->name_entry, name); + + /* Device MAC address */ + mac_list = ce_page_get_mac_list (self->client, NM_TYPE_DEVICE_ETHERNET, + NM_DEVICE_ETHERNET_PERMANENT_HW_ADDRESS); + s_mac_str = nm_setting_wired_get_mac_address (setting); + ce_page_setup_mac_combo (self->mac_combo, s_mac_str, mac_list); + g_strfreev (mac_list); + g_signal_connect_object (self->mac_combo, "changed", G_CALLBACK (ce_page_changed), self, G_CONNECT_SWAPPED); + + /* Cloned MAC address */ + cloned_mac = nm_setting_wired_get_cloned_mac_address (setting); + ce_page_setup_cloned_mac_combo (self->cloned_mac_combo, cloned_mac); + g_signal_connect_object (self->cloned_mac_combo, "changed", G_CALLBACK (ce_page_changed), self, G_CONNECT_SWAPPED); + + /* MTU */ + g_signal_connect_object (self->mtu_spin, "output", G_CALLBACK (mtu_output_cb), self, G_CONNECT_SWAPPED); + gtk_spin_button_set_value (self->mtu_spin, (gdouble) nm_setting_wired_get_mtu (setting)); + g_signal_connect_object (self->mtu_spin, "value-changed", G_CALLBACK (mtu_changed), self, G_CONNECT_SWAPPED); + mtu_changed (self); + + g_signal_connect_object (self->name_entry, "changed", G_CALLBACK (ce_page_changed), self, G_CONNECT_SWAPPED); + g_signal_connect_object (self->mtu_spin, "value-changed", G_CALLBACK (ce_page_changed), self, G_CONNECT_SWAPPED); +} + +static void +ui_to_setting (CEPageEthernet *self) +{ + g_autofree gchar *device_mac = NULL; + g_autofree gchar *cloned_mac = NULL; + const gchar *text; + GtkWidget *entry; + + entry = gtk_bin_get_child (GTK_BIN (self->mac_combo)); + if (entry) { + text = gtk_entry_get_text (GTK_ENTRY (entry)); + device_mac = ce_page_trim_address (text); + } + + cloned_mac = ce_page_cloned_mac_get (self->cloned_mac_combo); + + g_object_set (self->setting_wired, + NM_SETTING_WIRED_MAC_ADDRESS, device_mac, + NM_SETTING_WIRED_CLONED_MAC_ADDRESS, cloned_mac, + NM_SETTING_WIRED_MTU, (guint32) gtk_spin_button_get_value_as_int (self->mtu_spin), + NULL); + + g_object_set (self->setting_connection, + NM_SETTING_CONNECTION_ID, gtk_entry_get_text (self->name_entry), + NULL); +} + +static const gchar * +ce_page_ethernet_get_title (CEPage *page) +{ + return _("Identity"); +} + +static gboolean +ce_page_ethernet_validate (CEPage *page, + NMConnection *connection, + GError **error) +{ + CEPageEthernet *self = CE_PAGE_ETHERNET (page); + GtkWidget *entry; + gboolean ret = TRUE; + + entry = gtk_bin_get_child (GTK_BIN (self->mac_combo)); + if (entry) { + if (!ce_page_address_is_valid (gtk_entry_get_text (GTK_ENTRY (entry)))) { + widget_set_error (entry); + ret = FALSE; + } else { + widget_unset_error (entry); + } + } + + if (!ce_page_cloned_mac_combo_valid (self->cloned_mac_combo)) { + widget_set_error (gtk_bin_get_child (GTK_BIN (self->cloned_mac_combo))); + ret = FALSE; + } else { + widget_unset_error (gtk_bin_get_child (GTK_BIN (self->cloned_mac_combo))); + } + + if (!ret) + return ret; + + ui_to_setting (self); + + return nm_setting_verify (NM_SETTING (self->setting_connection), NULL, error) && + nm_setting_verify (NM_SETTING (self->setting_wired), NULL, error); +} + +static void +ce_page_ethernet_init (CEPageEthernet *self) +{ + gtk_widget_init_template (GTK_WIDGET (self)); +} + +static void +ce_page_ethernet_class_init (CEPageEthernetClass *klass) +{ + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/network/ethernet-page.ui"); + + gtk_widget_class_bind_template_child (widget_class, CEPageEthernet, cloned_mac_combo); + gtk_widget_class_bind_template_child (widget_class, CEPageEthernet, mac_combo); + gtk_widget_class_bind_template_child (widget_class, CEPageEthernet, mtu_spin); + gtk_widget_class_bind_template_child (widget_class, CEPageEthernet, mtu_label); + gtk_widget_class_bind_template_child (widget_class, CEPageEthernet, name_entry); +} + +static void +ce_page_iface_init (CEPageInterface *iface) +{ + iface->get_title = ce_page_ethernet_get_title; + iface->validate = ce_page_ethernet_validate; +} + +CEPageEthernet * +ce_page_ethernet_new (NMConnection *connection, + NMClient *client) +{ + CEPageEthernet *self; + + self = CE_PAGE_ETHERNET (g_object_new (ce_page_ethernet_get_type (), NULL)); + + self->client = client; + self->setting_connection = nm_connection_get_setting_connection (connection); + self->setting_wired = nm_connection_get_setting_wired (connection); + + connect_ethernet_page (self); + + return self; +} diff --git a/panels/network/connection-editor/ce-page-ethernet.h b/panels/network/connection-editor/ce-page-ethernet.h new file mode 100644 index 0000000..5fce19f --- /dev/null +++ b/panels/network/connection-editor/ce-page-ethernet.h @@ -0,0 +1,34 @@ +/* -*- 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. + */ + +#pragma once + +#include <gtk/gtk.h> +#include <NetworkManager.h> + +G_BEGIN_DECLS + +G_DECLARE_FINAL_TYPE (CEPageEthernet, ce_page_ethernet, CE, PAGE_ETHERNET, GtkGrid) + +CEPageEthernet *ce_page_ethernet_new (NMConnection *connection, + NMClient *client); + +G_END_DECLS diff --git a/panels/network/connection-editor/ce-page-ip4.c b/panels/network/connection-editor/ce-page-ip4.c new file mode 100644 index 0000000..2caf8a8 --- /dev/null +++ b/panels/network/connection-editor/ce-page-ip4.c @@ -0,0 +1,895 @@ +/* -*- 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 <errno.h> +#include <stdlib.h> +#include <arpa/inet.h> +#include <glib/gi18n.h> +#include <NetworkManager.h> + +#include "list-box-helper.h" +#include "ce-page.h" +#include "ce-page-ip4.h" +#include "ui-helpers.h" + +static void ensure_empty_address_row (CEPageIP4 *self); +static void ensure_empty_routes_row (CEPageIP4 *self); + +struct _CEPageIP4 +{ + GtkScrolledWindow parent; + + GtkBox *address_box; + GtkSizeGroup *address_sizegroup; + GtkSwitch *auto_dns_switch; + GtkSwitch *auto_routes_switch; + GtkRadioButton *automatic_radio; + GtkBox *content_box; + GtkRadioButton *disabled_radio; + GtkEntry *dns_entry; + GtkRadioButton *local_radio; + GtkRadioButton *manual_radio; + GtkCheckButton *never_default_check; + GtkBox *routes_box; + GtkSizeGroup *routes_metric_sizegroup; + GtkSizeGroup *routes_sizegroup; + GtkRadioButton *shared_radio; + + NMSettingIPConfig *setting; + + GtkWidget *address_list; + GtkWidget *routes_list; +}; + +static void ce_page_iface_init (CEPageInterface *); + +G_DEFINE_TYPE_WITH_CODE (CEPageIP4, ce_page_ip4, GTK_TYPE_SCROLLED_WINDOW, + G_IMPLEMENT_INTERFACE (ce_page_get_type (), ce_page_iface_init)) + +enum { + METHOD_COL_NAME, + METHOD_COL_METHOD +}; + +enum { + IP4_METHOD_AUTO, + IP4_METHOD_MANUAL, + IP4_METHOD_LINK_LOCAL, + IP4_METHOD_SHARED, + IP4_METHOD_DISABLED +}; + +static void +method_changed (CEPageIP4 *self) +{ + gboolean addr_enabled; + gboolean dns_enabled; + gboolean routes_enabled; + + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->disabled_radio)) || + gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->shared_radio))) { + addr_enabled = FALSE; + dns_enabled = FALSE; + routes_enabled = FALSE; + } else { + addr_enabled = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->manual_radio)); + dns_enabled = !gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->local_radio)); + routes_enabled = !gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->local_radio)); + } + + gtk_widget_set_visible (GTK_WIDGET (self->address_box), addr_enabled); + gtk_widget_set_sensitive (GTK_WIDGET (self->dns_entry), dns_enabled); + gtk_widget_set_sensitive (GTK_WIDGET (self->routes_list), routes_enabled); + gtk_widget_set_sensitive (GTK_WIDGET (self->never_default_check), routes_enabled); + + ce_page_changed (CE_PAGE (self)); +} + +static void +update_row_sensitivity (CEPageIP4 *self, GtkWidget *list) +{ + GList *children, *l; + gint rows = 0, i = 0; + + children = gtk_container_get_children (GTK_CONTAINER (list)); + for (l = children; l; l = l->next) { + GtkWidget *row = l->data; + GtkWidget *button; + + button = GTK_WIDGET (g_object_get_data (G_OBJECT (row), "delete-button")); + if (button != NULL) + rows++; + } + for (l = children; l; l = l->next) { + GtkWidget *row = l->data; + GtkWidget *button; + + button = GTK_WIDGET (g_object_get_data (G_OBJECT (row), "delete-button")); + if (button != NULL) + gtk_widget_set_sensitive (button, rows > 1 && ++i < rows); + } + g_list_free (children); +} + +static void +update_row_gateway_sensitivity (CEPageIP4 *self) +{ + GList *children, *l; + gint rows = 0; + + children = gtk_container_get_children (GTK_CONTAINER (self->address_list)); + for (l = children; l; l = l->next) { + GtkWidget *row = l->data; + GtkWidget *entry; + + entry = GTK_WIDGET (g_object_get_data (G_OBJECT (row), "gateway")); + + gtk_widget_set_sensitive (entry, (rows == 0)); + + rows++; + } + g_list_free (children); +} + +static void +remove_row (CEPageIP4 *self, GtkButton *button) +{ + GtkWidget *list; + GtkWidget *row; + GtkWidget *row_box; + + row_box = gtk_widget_get_parent (GTK_WIDGET (button)); + row = gtk_widget_get_parent (row_box); + list = gtk_widget_get_parent (row); + + gtk_container_remove (GTK_CONTAINER (list), row); + + ce_page_changed (CE_PAGE (self)); + + update_row_sensitivity (self, list); + if (list == self->address_list) + update_row_gateway_sensitivity (self); +} + +static gboolean +validate_row (GtkWidget *row) +{ + GtkWidget *box; + GList *children, *l; + gboolean valid; + + valid = FALSE; + box = gtk_bin_get_child (GTK_BIN (row)); + children = gtk_container_get_children (GTK_CONTAINER (box)); + + for (l = children; l != NULL; l = l->next) { + if (!GTK_IS_ENTRY (l->data)) + continue; + + valid = valid || gtk_entry_get_text_length (l->data) > 0; + } + + g_list_free (children); + + return valid; +} + +static void +add_address_row (CEPageIP4 *self, + const gchar *address, + const gchar *network, + const gchar *gateway) +{ + GtkWidget *row; + GtkWidget *row_box; + GtkWidget *widget; + GtkWidget *delete_button; + GtkWidget *image; + + row = gtk_list_box_row_new (); + + row_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); + gtk_style_context_add_class (gtk_widget_get_style_context (row_box), "linked"); + + widget = gtk_entry_new (); + g_signal_connect_object (widget, "changed", G_CALLBACK (ce_page_changed), self, G_CONNECT_SWAPPED); + g_signal_connect_object (widget, "activate", G_CALLBACK (ensure_empty_address_row), self, G_CONNECT_SWAPPED); + g_object_set_data (G_OBJECT (row), "address", widget); + gtk_entry_set_text (GTK_ENTRY (widget), address); + gtk_entry_set_width_chars (GTK_ENTRY (widget), 16); + gtk_widget_set_hexpand (widget, TRUE); + gtk_container_add (GTK_CONTAINER (row_box), widget); + + widget = gtk_entry_new (); + g_signal_connect_object (widget, "changed", G_CALLBACK (ce_page_changed), self, G_CONNECT_SWAPPED); + g_signal_connect_object (widget, "activate", G_CALLBACK (ensure_empty_address_row), self, G_CONNECT_SWAPPED); + g_object_set_data (G_OBJECT (row), "network", widget); + gtk_entry_set_text (GTK_ENTRY (widget), network); + gtk_entry_set_width_chars (GTK_ENTRY (widget), 16); + gtk_widget_set_hexpand (widget, TRUE); + gtk_container_add (GTK_CONTAINER (row_box), widget); + + widget = gtk_entry_new (); + g_signal_connect_object (widget, "changed", G_CALLBACK (ce_page_changed), self, G_CONNECT_SWAPPED); + g_signal_connect_object (widget, "activate", G_CALLBACK (ensure_empty_address_row), self, G_CONNECT_SWAPPED); + g_object_set_data (G_OBJECT (row), "gateway", widget); + gtk_entry_set_text (GTK_ENTRY (widget), gateway ? gateway : ""); + gtk_entry_set_width_chars (GTK_ENTRY (widget), 16); + gtk_widget_set_hexpand (widget, TRUE); + gtk_container_add (GTK_CONTAINER (row_box), widget); + + delete_button = gtk_button_new (); + gtk_widget_set_sensitive (delete_button, FALSE); + gtk_style_context_add_class (gtk_widget_get_style_context (delete_button), "image-button"); + g_signal_connect_object (delete_button, "clicked", G_CALLBACK (remove_row), self, G_CONNECT_SWAPPED); + image = gtk_image_new_from_icon_name ("edit-delete-symbolic", GTK_ICON_SIZE_MENU); + atk_object_set_name (gtk_widget_get_accessible (delete_button), _("Delete Address")); + gtk_button_set_image (GTK_BUTTON (delete_button), image); + gtk_container_add (GTK_CONTAINER (row_box), delete_button); + g_object_set_data (G_OBJECT (row), "delete-button", delete_button); + + gtk_size_group_add_widget (self->address_sizegroup, delete_button); + + gtk_container_add (GTK_CONTAINER (row), row_box); + gtk_widget_show_all (row); + gtk_container_add (GTK_CONTAINER (self->address_list), row); + + update_row_gateway_sensitivity (self); + update_row_sensitivity (self, self->address_list); +} + +static void +ensure_empty_address_row (CEPageIP4 *self) +{ + GList *children, *l; + + children = gtk_container_get_children (GTK_CONTAINER (self->address_list)); + l = children; + + while (l && l->next) + l = l->next; + + /* Add the last, stub row if needed*/ + if (!l || validate_row (l->data)) + add_address_row (self, "", "", ""); + + g_list_free (children); +} + +static void +add_address_box (CEPageIP4 *self) +{ + GtkWidget *list; + gint i; + + self->address_list = list = gtk_list_box_new (); + gtk_list_box_set_selection_mode (GTK_LIST_BOX (list), GTK_SELECTION_NONE); + gtk_list_box_set_header_func (GTK_LIST_BOX (list), cc_list_box_update_header_func, NULL, NULL); + gtk_container_add (GTK_CONTAINER (self->address_box), list); + + for (i = 0; i < nm_setting_ip_config_get_num_addresses (self->setting); i++) { + NMIPAddress *addr; + struct in_addr tmp_addr; + gchar network[INET_ADDRSTRLEN + 1]; + + addr = nm_setting_ip_config_get_address (self->setting, i); + if (!addr) + continue; + + tmp_addr.s_addr = nm_utils_ip4_prefix_to_netmask (nm_ip_address_get_prefix (addr)); + (void) inet_ntop (AF_INET, &tmp_addr, &network[0], sizeof (network)); + + add_address_row (self, + nm_ip_address_get_address (addr), + network, + i == 0 ? nm_setting_ip_config_get_gateway (self->setting) : ""); + } + if (nm_setting_ip_config_get_num_addresses (self->setting) == 0) + ensure_empty_address_row (self); + + gtk_widget_show_all (GTK_WIDGET (self->address_box)); +} + +static void +add_dns_section (CEPageIP4 *self) +{ + GString *string; + gint i; + + gtk_switch_set_active (self->auto_dns_switch, !nm_setting_ip_config_get_ignore_auto_dns (self->setting)); + g_signal_connect_object (self->auto_dns_switch, "notify::active", G_CALLBACK (ce_page_changed), self, G_CONNECT_SWAPPED); + + string = g_string_new (""); + + for (i = 0; i < nm_setting_ip_config_get_num_dns (self->setting); i++) { + const char *address; + + address = nm_setting_ip_config_get_dns (self->setting, i); + + if (i > 0) + g_string_append (string, ", "); + + g_string_append (string, address); + } + + gtk_entry_set_text (self->dns_entry, string->str); + + g_signal_connect_object (self->dns_entry, "notify::text", G_CALLBACK (ce_page_changed), self, G_CONNECT_SWAPPED); + + g_string_free (string, TRUE); +} + +static void +add_route_row (CEPageIP4 *self, + const gchar *address, + const gchar *netmask, + const gchar *gateway, + gint metric) +{ + GtkWidget *row; + GtkWidget *row_box; + GtkWidget *widget; + GtkWidget *delete_button; + GtkWidget *image; + + row = gtk_list_box_row_new (); + + row_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); + gtk_style_context_add_class (gtk_widget_get_style_context (row_box), "linked"); + + widget = gtk_entry_new (); + g_signal_connect_object (widget, "changed", G_CALLBACK (ce_page_changed), self, G_CONNECT_SWAPPED); + g_signal_connect_object (widget, "activate", G_CALLBACK (ensure_empty_routes_row), self, G_CONNECT_SWAPPED); + g_object_set_data (G_OBJECT (row), "address", widget); + gtk_entry_set_text (GTK_ENTRY (widget), address); + gtk_entry_set_width_chars (GTK_ENTRY (widget), 16); + gtk_widget_set_hexpand (widget, TRUE); + gtk_container_add (GTK_CONTAINER (row_box), widget); + + widget = gtk_entry_new (); + g_signal_connect_object (widget, "changed", G_CALLBACK (ce_page_changed), self, G_CONNECT_SWAPPED); + g_signal_connect_object (widget, "activate", G_CALLBACK (ensure_empty_routes_row), self, G_CONNECT_SWAPPED); + g_object_set_data (G_OBJECT (row), "netmask", widget); + gtk_entry_set_text (GTK_ENTRY (widget), netmask); + gtk_entry_set_width_chars (GTK_ENTRY (widget), 16); + gtk_widget_set_hexpand (widget, TRUE); + gtk_container_add (GTK_CONTAINER (row_box), widget); + + widget = gtk_entry_new (); + g_signal_connect_object (widget, "changed", G_CALLBACK (ce_page_changed), self, G_CONNECT_SWAPPED); + g_signal_connect_object (widget, "activate", G_CALLBACK (ensure_empty_routes_row), self, G_CONNECT_SWAPPED); + g_object_set_data (G_OBJECT (row), "gateway", widget); + gtk_entry_set_text (GTK_ENTRY (widget), gateway ? gateway : ""); + gtk_entry_set_width_chars (GTK_ENTRY (widget), 16); + gtk_widget_set_hexpand (widget, TRUE); + gtk_container_add (GTK_CONTAINER (row_box), widget); + + widget = gtk_entry_new (); + g_signal_connect_object (widget, "changed", G_CALLBACK (ce_page_changed), self, G_CONNECT_SWAPPED); + g_signal_connect_object (widget, "activate", G_CALLBACK (ensure_empty_routes_row), self, G_CONNECT_SWAPPED); + g_object_set_data (G_OBJECT (row), "metric", widget); + if (metric >= 0) { + g_autofree gchar *s = g_strdup_printf ("%d", metric); + gtk_entry_set_text (GTK_ENTRY (widget), s); + } + gtk_entry_set_width_chars (GTK_ENTRY (widget), 5); + gtk_widget_set_hexpand (widget, TRUE); + gtk_container_add (GTK_CONTAINER (row_box), widget); + + gtk_size_group_add_widget (self->routes_metric_sizegroup, widget); + + delete_button = gtk_button_new (); + gtk_style_context_add_class (gtk_widget_get_style_context (delete_button), "image-button"); + g_signal_connect_object (delete_button, "clicked", G_CALLBACK (remove_row), self, G_CONNECT_SWAPPED); + image = gtk_image_new_from_icon_name ("edit-delete-symbolic", GTK_ICON_SIZE_MENU); + atk_object_set_name (gtk_widget_get_accessible (delete_button), _("Delete Route")); + gtk_button_set_image (GTK_BUTTON (delete_button), image); + gtk_widget_set_halign (delete_button, GTK_ALIGN_CENTER); + gtk_widget_set_valign (delete_button, GTK_ALIGN_CENTER); + gtk_container_add (GTK_CONTAINER (row_box), delete_button); + g_object_set_data (G_OBJECT (row), "delete-button", delete_button); + + gtk_size_group_add_widget (self->routes_sizegroup, delete_button); + + gtk_container_add (GTK_CONTAINER (row), row_box); + gtk_widget_show_all (row); + gtk_container_add (GTK_CONTAINER (self->routes_list), row); + + update_row_sensitivity (self, self->routes_list); +} + +static void +ensure_empty_routes_row (CEPageIP4 *self) +{ + GList *children, *l; + + children = gtk_container_get_children (GTK_CONTAINER (self->routes_list)); + l = children; + + while (l && l->next) + l = l->next; + + /* Add the last, stub row if needed*/ + if (!l || validate_row (l->data)) + add_route_row (self, "", "", "", -1); + + g_list_free (children); +} + +static void +add_routes_box (CEPageIP4 *self) +{ + GtkWidget *list; + gint i; + + self->routes_list = list = gtk_list_box_new (); + gtk_list_box_set_selection_mode (GTK_LIST_BOX (list), GTK_SELECTION_NONE); + gtk_list_box_set_header_func (GTK_LIST_BOX (list), cc_list_box_update_header_func, NULL, NULL); + gtk_container_add (GTK_CONTAINER (self->routes_box), list); + gtk_switch_set_active (self->auto_routes_switch, !nm_setting_ip_config_get_ignore_auto_routes (self->setting)); + g_signal_connect_object (self->auto_routes_switch, "notify::active", G_CALLBACK (ce_page_changed), self, G_CONNECT_SWAPPED); + + for (i = 0; i < nm_setting_ip_config_get_num_routes (self->setting); i++) { + NMIPRoute *route; + struct in_addr tmp_addr; + gchar netmask[INET_ADDRSTRLEN + 1]; + + route = nm_setting_ip_config_get_route (self->setting, i); + if (!route) + continue; + + tmp_addr.s_addr = nm_utils_ip4_prefix_to_netmask (nm_ip_route_get_prefix (route)); + (void) inet_ntop (AF_INET, &tmp_addr, &netmask[0], sizeof (netmask)); + + add_route_row (self, + nm_ip_route_get_dest (route), + netmask, + nm_ip_route_get_next_hop (route), + nm_ip_route_get_metric (route)); + } + if (nm_setting_ip_config_get_num_routes (self->setting) == 0) + ensure_empty_routes_row (self); + + gtk_widget_show_all (GTK_WIDGET (self->routes_box)); +} + +static void +connect_ip4_page (CEPageIP4 *self) +{ + const gchar *str_method; + guint method; + + add_address_box (self); + add_dns_section (self); + add_routes_box (self); + + str_method = nm_setting_ip_config_get_method (self->setting); + g_signal_connect_object (self->disabled_radio, "notify::active", G_CALLBACK (ce_page_changed), self, G_CONNECT_SWAPPED); + g_object_bind_property (self->disabled_radio, "active", + self->content_box, "sensitive", + G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN); + + g_signal_connect_object (self->shared_radio, "notify::active", G_CALLBACK (ce_page_changed), self, G_CONNECT_SWAPPED); + g_object_bind_property (self->shared_radio, "active", + self->content_box, "sensitive", + G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN); + + method = IP4_METHOD_AUTO; + if (g_strcmp0 (str_method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL) == 0) { + method = IP4_METHOD_LINK_LOCAL; + } else if (g_strcmp0 (str_method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL) == 0) { + method = IP4_METHOD_MANUAL; + } else if (g_strcmp0 (str_method, NM_SETTING_IP4_CONFIG_METHOD_SHARED) == 0) { + method = IP4_METHOD_SHARED; + } else if (g_strcmp0 (str_method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) == 0) { + method = IP4_METHOD_DISABLED; + } + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->never_default_check), + nm_setting_ip_config_get_never_default (self->setting)); + g_signal_connect_object (self->never_default_check, "toggled", G_CALLBACK (ce_page_changed), self, G_CONNECT_SWAPPED); + + g_signal_connect_object (self->automatic_radio, "toggled", G_CALLBACK (method_changed), self, G_CONNECT_SWAPPED); + g_signal_connect_object (self->local_radio, "toggled", G_CALLBACK (method_changed), self, G_CONNECT_SWAPPED); + g_signal_connect_object (self->manual_radio, "toggled", G_CALLBACK (method_changed), self, G_CONNECT_SWAPPED); + g_signal_connect_object (self->disabled_radio, "toggled", G_CALLBACK (method_changed), self, G_CONNECT_SWAPPED); + + switch (method) { + case IP4_METHOD_AUTO: + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->automatic_radio), TRUE); + break; + case IP4_METHOD_LINK_LOCAL: + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->local_radio), TRUE); + break; + case IP4_METHOD_MANUAL: + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->manual_radio), TRUE); + break; + case IP4_METHOD_SHARED: + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->shared_radio), TRUE); + break; + case IP4_METHOD_DISABLED: + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->disabled_radio), TRUE); + break; + default: + break; + } + + method_changed (self); +} + +static gboolean +parse_netmask (const char *str, guint32 *prefix) +{ + struct in_addr tmp_addr; + glong tmp_prefix; + + errno = 0; + + /* Is it a prefix? */ + if (!strchr (str, '.')) { + tmp_prefix = strtol (str, NULL, 10); + if (!errno && tmp_prefix >= 0 && tmp_prefix <= 32) { + *prefix = tmp_prefix; + return TRUE; + } + } + + /* Is it a netmask? */ + if (inet_pton (AF_INET, str, &tmp_addr) > 0) { + *prefix = nm_utils_ip4_netmask_to_prefix (tmp_addr.s_addr); + return TRUE; + } + + return FALSE; +} + +static gboolean +ui_to_setting (CEPageIP4 *self) +{ + const gchar *method; + gboolean ignore_auto_dns; + gboolean ignore_auto_routes; + gboolean never_default; + GPtrArray *addresses = NULL; + GPtrArray *dns_servers = NULL; + GPtrArray *routes = NULL; + GStrv dns_addresses = NULL; + GList *children, *l; + gboolean ret = TRUE; + const char *default_gateway = NULL; + gchar *dns_text = NULL; + guint i; + + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->disabled_radio))) + method = NM_SETTING_IP4_CONFIG_METHOD_DISABLED; + else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->automatic_radio))) + method = NM_SETTING_IP4_CONFIG_METHOD_AUTO; + else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->local_radio))) + method = NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL; + else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->manual_radio))) + method = NM_SETTING_IP4_CONFIG_METHOD_MANUAL; + else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->shared_radio))) + method = NM_SETTING_IP4_CONFIG_METHOD_SHARED; + + addresses = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_ip_address_unref); + if (g_str_equal (method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) + children = gtk_container_get_children (GTK_CONTAINER (self->address_list)); + else + children = NULL; + + for (l = children; l; l = l->next) { + GtkWidget *row = l->data; + GtkEntry *entry; + GtkEntry *gateway_entry; + const gchar *text_address; + const gchar *text_netmask; + const gchar *text_gateway = ""; + NMIPAddress *addr; + guint32 prefix; + + entry = GTK_ENTRY (g_object_get_data (G_OBJECT (row), "address")); + if (!entry) + continue; + + text_address = gtk_entry_get_text (entry); + text_netmask = gtk_entry_get_text (GTK_ENTRY (g_object_get_data (G_OBJECT (row), "network"))); + gateway_entry = g_object_get_data (G_OBJECT (row), "gateway"); + if (gtk_widget_is_visible (GTK_WIDGET (gateway_entry))) + text_gateway = gtk_entry_get_text (gateway_entry); + + if (!*text_address && !*text_netmask && !*text_gateway) { + /* ignore empty rows */ + widget_unset_error (GTK_WIDGET (entry)); + widget_unset_error (g_object_get_data (G_OBJECT (row), "network")); + widget_unset_error (GTK_WIDGET (gateway_entry)); + continue; + } + + if (!nm_utils_ipaddr_valid (AF_INET, text_address)) { + widget_set_error (GTK_WIDGET (entry)); + ret = FALSE; + } else { + widget_unset_error (GTK_WIDGET (entry)); + } + + if (!parse_netmask (text_netmask, &prefix)) { + widget_set_error (g_object_get_data (G_OBJECT (row), "network")); + ret = FALSE; + } else { + widget_unset_error (g_object_get_data (G_OBJECT (row), "network")); + } + + if (gtk_widget_is_visible (GTK_WIDGET (gateway_entry)) && + *text_gateway && + !nm_utils_ipaddr_valid (AF_INET, text_gateway)) { + widget_set_error (g_object_get_data (G_OBJECT (row), "gateway")); + ret = FALSE; + } else { + widget_unset_error (GTK_WIDGET (gateway_entry)); + if (gtk_widget_is_visible (GTK_WIDGET (gateway_entry)) && *text_gateway) { + g_assert (default_gateway == NULL); + default_gateway = text_gateway; + } + } + + if (!ret) + continue; + + addr = nm_ip_address_new (AF_INET, text_address, prefix, NULL); + if (addr) + g_ptr_array_add (addresses, addr); + + if (!l || !l->next) + ensure_empty_address_row (self); + } + g_list_free (children); + + if (addresses->len == 0) { + g_ptr_array_free (addresses, TRUE); + addresses = NULL; + } + + dns_servers = g_ptr_array_new_with_free_func (g_free); + dns_text = g_strstrip (g_strdup (gtk_entry_get_text (GTK_ENTRY (self->dns_entry)))); + if (g_str_equal (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) || + g_str_equal (method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) + dns_addresses = g_strsplit_set (dns_text, ", ", -1); + else + dns_addresses = NULL; + + for (i = 0; dns_addresses && dns_addresses[i]; i++) { + const gchar *text; + + text = dns_addresses[i]; + + if (!text || !*text) + continue; + + if (!nm_utils_ipaddr_valid (AF_INET, text)) { + g_ptr_array_remove_range (dns_servers, 0, dns_servers->len); + widget_set_error (GTK_WIDGET (self->dns_entry)); + ret = FALSE; + break; + } else { + widget_unset_error (GTK_WIDGET (self->dns_entry)); + g_ptr_array_add (dns_servers, g_strdup (text)); + } + } + g_clear_pointer (&dns_addresses, g_strfreev); + + if (dns_servers->len == 0) { + g_ptr_array_free (dns_servers, TRUE); + dns_servers = NULL; + } else { + g_ptr_array_add (dns_servers, NULL); + } + + routes = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_ip_route_unref); + if (g_str_equal (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) || + g_str_equal (method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) + children = gtk_container_get_children (GTK_CONTAINER (self->routes_list)); + else + children = NULL; + + for (l = children; l; l = l->next) { + GtkWidget *row = l->data; + GtkEntry *entry; + const gchar *text_address; + const gchar *text_netmask; + const gchar *text_gateway; + const gchar *text_metric; + gint64 metric; + guint32 netmask; + NMIPRoute *route; + + entry = GTK_ENTRY (g_object_get_data (G_OBJECT (row), "address")); + if (!entry) + continue; + + text_address = gtk_entry_get_text (entry); + text_netmask = gtk_entry_get_text (GTK_ENTRY (g_object_get_data (G_OBJECT (row), "netmask"))); + text_gateway = gtk_entry_get_text (GTK_ENTRY (g_object_get_data (G_OBJECT (row), "gateway"))); + text_metric = gtk_entry_get_text (GTK_ENTRY (g_object_get_data (G_OBJECT (row), "metric"))); + + if (!*text_address && !*text_netmask && !*text_gateway && !*text_metric) { + /* ignore empty rows */ + continue; + } + + if (text_address && !nm_utils_ipaddr_valid (AF_INET, text_address)) { + widget_set_error (GTK_WIDGET (entry)); + ret = FALSE; + } else { + widget_unset_error (GTK_WIDGET (entry)); + } + + if (!parse_netmask (text_netmask, &netmask)) { + widget_set_error (GTK_WIDGET (g_object_get_data (G_OBJECT (row), "netmask"))); + ret = FALSE; + } else { + widget_unset_error (GTK_WIDGET (g_object_get_data (G_OBJECT (row), "netmask"))); + } + + if (text_gateway && !nm_utils_ipaddr_valid (AF_INET, text_gateway)) { + widget_set_error (GTK_WIDGET (g_object_get_data (G_OBJECT (row), "gateway"))); + ret = FALSE; + } else { + widget_unset_error (GTK_WIDGET (g_object_get_data (G_OBJECT (row), "gateway"))); + } + + metric = -1; + if (*text_metric) { + errno = 0; + metric = g_ascii_strtoull (text_metric, NULL, 10); + if (errno || metric < 0 || metric > G_MAXUINT32) { + widget_set_error (GTK_WIDGET (g_object_get_data (G_OBJECT (row), "metric"))); + ret = FALSE; + } else { + widget_unset_error (GTK_WIDGET (g_object_get_data (G_OBJECT (row), "metric"))); + } + } else { + widget_unset_error (GTK_WIDGET (g_object_get_data (G_OBJECT (row), "metric"))); + } + + if (!ret) + continue; + + route = nm_ip_route_new (AF_INET, text_address, netmask, text_gateway, metric, NULL); + if (route) + g_ptr_array_add (routes, route); + + if (!l || !l->next) + ensure_empty_routes_row (self); + } + g_list_free (children); + + if (routes->len == 0) { + g_ptr_array_free (routes, TRUE); + routes = NULL; + } + + if (!ret) + goto out; + + ignore_auto_dns = !gtk_switch_get_active (self->auto_dns_switch); + ignore_auto_routes = !gtk_switch_get_active (self->auto_routes_switch); + never_default = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->never_default_check)); + + g_object_set (self->setting, + NM_SETTING_IP_CONFIG_METHOD, method, + NM_SETTING_IP_CONFIG_ADDRESSES, addresses, + NM_SETTING_IP_CONFIG_GATEWAY, default_gateway, + NM_SETTING_IP_CONFIG_DNS, dns_servers ? dns_servers->pdata : NULL, + NM_SETTING_IP_CONFIG_ROUTES, routes, + NM_SETTING_IP_CONFIG_IGNORE_AUTO_DNS, ignore_auto_dns, + NM_SETTING_IP_CONFIG_IGNORE_AUTO_ROUTES, ignore_auto_routes, + NM_SETTING_IP_CONFIG_NEVER_DEFAULT, never_default, + NULL); + +out: + if (addresses) + g_ptr_array_free (addresses, TRUE); + + if (dns_servers) + g_ptr_array_free (dns_servers, TRUE); + + if (routes) + g_ptr_array_free (routes, TRUE); + + g_clear_pointer (&dns_text, g_free); + + return ret; +} + +static const gchar * +ce_page_ip4_get_title (CEPage *page) +{ + return _("IPv4"); +} + +static gboolean +ce_page_ip4_validate (CEPage *self, + NMConnection *connection, + GError **error) +{ + if (!ui_to_setting (CE_PAGE_IP4 (self))) + return FALSE; + + return nm_setting_verify (NM_SETTING (CE_PAGE_IP4 (self)->setting), NULL, error); +} + +static void +ce_page_ip4_init (CEPageIP4 *self) +{ + gtk_widget_init_template (GTK_WIDGET (self)); +} + +static void +ce_page_ip4_class_init (CEPageIP4Class *klass) +{ + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/network/ip4-page.ui"); + + gtk_widget_class_bind_template_child (widget_class, CEPageIP4, address_box); + gtk_widget_class_bind_template_child (widget_class, CEPageIP4, address_sizegroup); + gtk_widget_class_bind_template_child (widget_class, CEPageIP4, auto_dns_switch); + gtk_widget_class_bind_template_child (widget_class, CEPageIP4, auto_routes_switch); + gtk_widget_class_bind_template_child (widget_class, CEPageIP4, automatic_radio); + gtk_widget_class_bind_template_child (widget_class, CEPageIP4, content_box); + gtk_widget_class_bind_template_child (widget_class, CEPageIP4, disabled_radio); + gtk_widget_class_bind_template_child (widget_class, CEPageIP4, dns_entry); + gtk_widget_class_bind_template_child (widget_class, CEPageIP4, local_radio); + gtk_widget_class_bind_template_child (widget_class, CEPageIP4, manual_radio); + gtk_widget_class_bind_template_child (widget_class, CEPageIP4, never_default_check); + gtk_widget_class_bind_template_child (widget_class, CEPageIP4, routes_box); + gtk_widget_class_bind_template_child (widget_class, CEPageIP4, routes_metric_sizegroup); + gtk_widget_class_bind_template_child (widget_class, CEPageIP4, routes_sizegroup); + gtk_widget_class_bind_template_child (widget_class, CEPageIP4, shared_radio); +} + +static void +ce_page_iface_init (CEPageInterface *iface) +{ + iface->get_title = ce_page_ip4_get_title; + iface->validate = ce_page_ip4_validate; +} + +CEPageIP4 * +ce_page_ip4_new (NMConnection *connection, + NMClient *client) +{ + CEPageIP4 *self; + + self = CE_PAGE_IP4 (g_object_new (ce_page_ip4_get_type (), NULL)); + + self->setting = nm_connection_get_setting_ip4_config (connection); + if (!self->setting) { + self->setting = NM_SETTING_IP_CONFIG (nm_setting_ip4_config_new ()); + nm_connection_add_setting (connection, NM_SETTING (self->setting)); + } + + connect_ip4_page (self); + + return self; +} diff --git a/panels/network/connection-editor/ce-page-ip4.h b/panels/network/connection-editor/ce-page-ip4.h new file mode 100644 index 0000000..b76905d --- /dev/null +++ b/panels/network/connection-editor/ce-page-ip4.h @@ -0,0 +1,34 @@ +/* -*- 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. + */ + +#pragma once + +#include <gtk/gtk.h> +#include <NetworkManager.h> + +G_BEGIN_DECLS + +G_DECLARE_FINAL_TYPE (CEPageIP4, ce_page_ip4, CE, PAGE_IP4, GtkScrolledWindow) + +CEPageIP4 *ce_page_ip4_new (NMConnection *connection, + NMClient *client); + +G_END_DECLS diff --git a/panels/network/connection-editor/ce-page-ip6.c b/panels/network/connection-editor/ce-page-ip6.c new file mode 100644 index 0000000..6b71429 --- /dev/null +++ b/panels/network/connection-editor/ce-page-ip6.c @@ -0,0 +1,829 @@ +/* -*- 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 <errno.h> +#include <stdlib.h> +#include <arpa/inet.h> +#include <glib/gi18n.h> +#include <NetworkManager.h> + +#include "list-box-helper.h" +#include "ce-page.h" +#include "ce-page-ip6.h" +#include "ui-helpers.h" + +static void ensure_empty_address_row (CEPageIP6 *self); +static void ensure_empty_routes_row (CEPageIP6 *self); + + +struct _CEPageIP6 +{ + GtkScrolledWindow parent; + + GtkBox *address_box; + GtkSizeGroup *address_sizegroup; + GtkSwitch *auto_dns_switch; + GtkSwitch *auto_routes_switch; + GtkRadioButton *automatic_radio; + GtkBox *content_box; + GtkRadioButton *dhcp_radio; + GtkRadioButton *disabled_radio; + GtkEntry *dns_entry; + GtkRadioButton *local_radio; + GtkRadioButton *manual_radio; + GtkCheckButton *never_default_check; + GtkBox *routes_box; + GtkSizeGroup *routes_metric_sizegroup; + GtkSizeGroup *routes_sizegroup; + GtkRadioButton *shared_radio; + + NMSettingIPConfig *setting; + + GtkWidget *address_list; + GtkWidget *routes_list; +}; + +static void ce_page_iface_init (CEPageInterface *); + +G_DEFINE_TYPE_WITH_CODE (CEPageIP6, ce_page_ip6, GTK_TYPE_SCROLLED_WINDOW, + G_IMPLEMENT_INTERFACE (ce_page_get_type (), ce_page_iface_init)) + +enum { + METHOD_COL_NAME, + METHOD_COL_METHOD +}; + +enum { + IP6_METHOD_AUTO, + IP6_METHOD_DHCP, + IP6_METHOD_MANUAL, + IP6_METHOD_LINK_LOCAL, + IP6_METHOD_SHARED, + IP6_METHOD_DISABLED +}; + +static void +method_changed (CEPageIP6 *self) +{ + gboolean addr_enabled; + gboolean dns_enabled; + gboolean routes_enabled; + + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->disabled_radio)) || + gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->shared_radio))) { + addr_enabled = FALSE; + dns_enabled = FALSE; + routes_enabled = FALSE; + } else { + addr_enabled = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->manual_radio)); + dns_enabled = !gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->local_radio)); + routes_enabled = !gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->local_radio)); + } + + gtk_widget_set_visible (GTK_WIDGET (self->address_box), addr_enabled); + gtk_widget_set_sensitive (GTK_WIDGET (self->dns_entry), dns_enabled); + gtk_widget_set_sensitive (GTK_WIDGET (self->routes_list), routes_enabled); + gtk_widget_set_sensitive (GTK_WIDGET (self->never_default_check), routes_enabled); + + ce_page_changed (CE_PAGE (self)); +} + +static void +update_row_sensitivity (CEPageIP6 *self, GtkWidget *list) +{ + GList *children, *l; + gint rows = 0, i = 0; + + children = gtk_container_get_children (GTK_CONTAINER (list)); + for (l = children; l; l = l->next) { + GtkWidget *row = l->data; + GtkWidget *button; + + button = GTK_WIDGET (g_object_get_data (G_OBJECT (row), "delete-button")); + if (button != NULL) + rows++; + } + for (l = children; l; l = l->next) { + GtkWidget *row = l->data; + GtkWidget *button; + + button = GTK_WIDGET (g_object_get_data (G_OBJECT (row), "delete-button")); + if (button != NULL) + gtk_widget_set_sensitive (button, rows > 1 && ++i < rows); + } + g_list_free (children); +} + +static void +remove_row (CEPageIP6 *self, GtkButton *button) +{ + GtkWidget *row; + GtkWidget *row_box; + GtkWidget *list; + + row_box = gtk_widget_get_parent (GTK_WIDGET (button)); + row = gtk_widget_get_parent (row_box); + list = gtk_widget_get_parent (row); + + gtk_container_remove (GTK_CONTAINER (list), row); + + ce_page_changed (CE_PAGE (self)); + + update_row_sensitivity (self, list); +} + +static gboolean +validate_row (GtkWidget *row) +{ + GtkWidget *box; + GList *children, *l; + gboolean valid; + + valid = FALSE; + box = gtk_bin_get_child (GTK_BIN (row)); + children = gtk_container_get_children (GTK_CONTAINER (box)); + + for (l = children; l != NULL; l = l->next) { + if (!GTK_IS_ENTRY (l->data)) + continue; + + valid = valid || gtk_entry_get_text_length (l->data) > 0; + } + + g_list_free (children); + + return valid; +} + +static void +add_address_row (CEPageIP6 *self, + const gchar *address, + const gchar *network, + const gchar *gateway) +{ + GtkWidget *row; + GtkWidget *row_box; + GtkWidget *widget; + GtkWidget *delete_button; + GtkWidget *image; + + row = gtk_list_box_row_new (); + + row_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); + gtk_style_context_add_class (gtk_widget_get_style_context (row_box), "linked"); + + widget = gtk_entry_new (); + g_signal_connect_object (widget, "changed", G_CALLBACK (ce_page_changed), self, G_CONNECT_SWAPPED); + g_signal_connect_object (widget, "activate", G_CALLBACK (ensure_empty_address_row), self, G_CONNECT_SWAPPED); + g_object_set_data (G_OBJECT (row), "address", widget); + gtk_entry_set_text (GTK_ENTRY (widget), address); + gtk_entry_set_width_chars (GTK_ENTRY (widget), 16); + gtk_widget_set_hexpand (widget, TRUE); + gtk_container_add (GTK_CONTAINER (row_box), widget); + + widget = gtk_entry_new (); + g_signal_connect_object (widget, "changed", G_CALLBACK (ce_page_changed), self, G_CONNECT_SWAPPED); + g_signal_connect_object (widget, "activate", G_CALLBACK (ensure_empty_address_row), self, G_CONNECT_SWAPPED); + g_object_set_data (G_OBJECT (row), "prefix", widget); + gtk_entry_set_text (GTK_ENTRY (widget), network); + gtk_entry_set_width_chars (GTK_ENTRY (widget), 16); + gtk_widget_set_hexpand (widget, TRUE); + gtk_container_add (GTK_CONTAINER (row_box), widget); + + widget = gtk_entry_new (); + g_signal_connect_object (widget, "changed", G_CALLBACK (ce_page_changed), self, G_CONNECT_SWAPPED); + g_signal_connect_object (widget, "activate", G_CALLBACK (ensure_empty_address_row), self, G_CONNECT_SWAPPED); + g_object_set_data (G_OBJECT (row), "gateway", widget); + gtk_entry_set_text (GTK_ENTRY (widget), gateway ? gateway : ""); + gtk_entry_set_width_chars (GTK_ENTRY (widget), 16); + gtk_widget_set_hexpand (widget, TRUE); + gtk_container_add (GTK_CONTAINER (row_box), widget); + + delete_button = gtk_button_new (); + gtk_widget_set_sensitive (delete_button, FALSE); + gtk_style_context_add_class (gtk_widget_get_style_context (delete_button), "image-button"); + g_signal_connect_object (delete_button, "clicked", G_CALLBACK (remove_row), self, G_CONNECT_SWAPPED); + image = gtk_image_new_from_icon_name ("edit-delete-symbolic", GTK_ICON_SIZE_MENU); + atk_object_set_name (gtk_widget_get_accessible (delete_button), _("Delete Address")); + gtk_button_set_image (GTK_BUTTON (delete_button), image); + gtk_container_add (GTK_CONTAINER (row_box), delete_button); + g_object_set_data (G_OBJECT (row), "delete-button", delete_button); + + gtk_size_group_add_widget (self->address_sizegroup, delete_button); + + gtk_container_add (GTK_CONTAINER (row), row_box); + gtk_widget_show_all (row); + gtk_container_add (GTK_CONTAINER (self->address_list), row); + + update_row_sensitivity (self, self->address_list); +} + +static void +ensure_empty_address_row (CEPageIP6 *self) +{ + GList *children, *l; + + children = gtk_container_get_children (GTK_CONTAINER (self->address_list)); + l = children; + + while (l && l->next) + l = l->next; + + /* Add the last, stub row if needed*/ + if (!l || validate_row (l->data)) + add_address_row (self, "", "", ""); + + g_list_free (children); +} + +static void +add_address_box (CEPageIP6 *self) +{ + GtkWidget *list; + gint i; + + self->address_list = list = gtk_list_box_new (); + gtk_list_box_set_selection_mode (GTK_LIST_BOX (list), GTK_SELECTION_NONE); + gtk_list_box_set_header_func (GTK_LIST_BOX (list), cc_list_box_update_header_func, NULL, NULL); + gtk_container_add (GTK_CONTAINER (self->address_box), list); + + for (i = 0; i < nm_setting_ip_config_get_num_addresses (self->setting); i++) { + NMIPAddress *addr; + g_autofree gchar *netmask = NULL; + + addr = nm_setting_ip_config_get_address (self->setting, i); + netmask = g_strdup_printf ("%u", nm_ip_address_get_prefix (addr)); + add_address_row (self, nm_ip_address_get_address (addr), netmask, + i == 0 ? nm_setting_ip_config_get_gateway (self->setting) : NULL); + } + if (nm_setting_ip_config_get_num_addresses (self->setting) == 0) + ensure_empty_address_row (self); + + gtk_widget_show_all (GTK_WIDGET (self->address_box)); +} + +static void +add_dns_section (CEPageIP6 *self) +{ + GString *string; + gint i; + + gtk_switch_set_active (self->auto_dns_switch, !nm_setting_ip_config_get_ignore_auto_dns (self->setting)); + g_signal_connect_object (self->auto_dns_switch, "notify::active", G_CALLBACK (ce_page_changed), self, G_CONNECT_SWAPPED); + + string = g_string_new (""); + + for (i = 0; i < nm_setting_ip_config_get_num_dns (self->setting); i++) { + const char *address; + + address = nm_setting_ip_config_get_dns (self->setting, i); + + if (i > 0) + g_string_append (string, ", "); + + g_string_append (string, address); + + } + + gtk_entry_set_text (self->dns_entry, string->str); + + g_signal_connect_object (self->dns_entry, "notify::text", G_CALLBACK (ce_page_changed), self, G_CONNECT_SWAPPED); + + g_string_free (string, TRUE); +} + +static void +add_route_row (CEPageIP6 *self, + const gchar *address, + const gchar *prefix, + const gchar *gateway, + const gchar *metric) +{ + GtkWidget *row; + GtkWidget *row_box; + GtkWidget *widget; + GtkWidget *delete_button; + GtkWidget *image; + + row = gtk_list_box_row_new (); + + row_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); + gtk_style_context_add_class (gtk_widget_get_style_context (row_box), "linked"); + + widget = gtk_entry_new (); + g_signal_connect_object (widget, "changed", G_CALLBACK (ce_page_changed), self, G_CONNECT_SWAPPED); + g_signal_connect_object (widget, "activate", G_CALLBACK (ensure_empty_routes_row), self, G_CONNECT_SWAPPED); + g_object_set_data (G_OBJECT (row), "address", widget); + gtk_entry_set_text (GTK_ENTRY (widget), address); + gtk_entry_set_width_chars (GTK_ENTRY (widget), 16); + gtk_widget_set_hexpand (widget, TRUE); + gtk_container_add (GTK_CONTAINER (row_box), widget); + + widget = gtk_entry_new (); + g_signal_connect_object (widget, "changed", G_CALLBACK (ce_page_changed), self, G_CONNECT_SWAPPED); + g_signal_connect_object (widget, "activate", G_CALLBACK (ensure_empty_routes_row), self, G_CONNECT_SWAPPED); + g_object_set_data (G_OBJECT (row), "prefix", widget); + gtk_entry_set_text (GTK_ENTRY (widget), prefix ? prefix : ""); + gtk_entry_set_width_chars (GTK_ENTRY (widget), 16); + gtk_widget_set_hexpand (widget, TRUE); + gtk_container_add (GTK_CONTAINER (row_box), widget); + + widget = gtk_entry_new (); + g_signal_connect_object (widget, "changed", G_CALLBACK (ce_page_changed), self, G_CONNECT_SWAPPED); + g_signal_connect_object (widget, "activate", G_CALLBACK (ensure_empty_routes_row), self, G_CONNECT_SWAPPED); + g_object_set_data (G_OBJECT (row), "gateway", widget); + gtk_entry_set_text (GTK_ENTRY (widget), gateway); + gtk_entry_set_width_chars (GTK_ENTRY (widget), 16); + gtk_widget_set_hexpand (widget, TRUE); + gtk_container_add (GTK_CONTAINER (row_box), widget); + + widget = gtk_entry_new (); + g_signal_connect_object (widget, "changed", G_CALLBACK (ce_page_changed), self, G_CONNECT_SWAPPED); + g_signal_connect_object (widget, "activate", G_CALLBACK (ensure_empty_routes_row), self, G_CONNECT_SWAPPED); + g_object_set_data (G_OBJECT (row), "metric", widget); + gtk_entry_set_text (GTK_ENTRY (widget), metric ? metric : ""); + gtk_entry_set_width_chars (GTK_ENTRY (widget), 5); + gtk_widget_set_hexpand (widget, TRUE); + gtk_container_add (GTK_CONTAINER (row_box), widget); + + gtk_size_group_add_widget (self->routes_metric_sizegroup, widget); + + delete_button = gtk_button_new (); + gtk_style_context_add_class (gtk_widget_get_style_context (delete_button), "image-button"); + g_signal_connect_object (delete_button, "clicked", G_CALLBACK (remove_row), self, G_CONNECT_SWAPPED); + image = gtk_image_new_from_icon_name ("edit-delete-symbolic", GTK_ICON_SIZE_MENU); + atk_object_set_name (gtk_widget_get_accessible (delete_button), _("Delete Route")); + gtk_button_set_image (GTK_BUTTON (delete_button), image); + gtk_widget_set_halign (delete_button, GTK_ALIGN_CENTER); + gtk_widget_set_valign (delete_button, GTK_ALIGN_CENTER); + gtk_container_add (GTK_CONTAINER (row_box), delete_button); + g_object_set_data (G_OBJECT (row), "delete-button", delete_button); + + gtk_size_group_add_widget (self->routes_sizegroup, delete_button); + + gtk_container_add (GTK_CONTAINER (row), row_box); + gtk_widget_show_all (row); + gtk_container_add (GTK_CONTAINER (self->routes_list), row); + + update_row_sensitivity (self, self->routes_list); +} + +static void +ensure_empty_routes_row (CEPageIP6 *self) +{ + GList *children, *l; + + children = gtk_container_get_children (GTK_CONTAINER (self->routes_list)); + l = children; + + while (l && l->next) + l = l->next; + + /* Add the last, stub row if needed*/ + if (!l || validate_row (l->data)) + add_route_row (self, "", NULL, "", NULL); + + g_list_free (children); +} + +static void +add_empty_route_row (CEPageIP6 *self) +{ + add_route_row (self, "", NULL, "", NULL); +} + +static void +add_routes_box (CEPageIP6 *self) +{ + GtkWidget *list; + gint i; + + self->routes_list = list = gtk_list_box_new (); + gtk_list_box_set_selection_mode (GTK_LIST_BOX (list), GTK_SELECTION_NONE); + gtk_list_box_set_header_func (GTK_LIST_BOX (list), cc_list_box_update_header_func, NULL, NULL); + gtk_container_add (GTK_CONTAINER (self->routes_box), list); + gtk_switch_set_active (self->auto_routes_switch, !nm_setting_ip_config_get_ignore_auto_routes (self->setting)); + g_signal_connect_object (self->auto_routes_switch, "notify::active", G_CALLBACK (ce_page_changed), self, G_CONNECT_SWAPPED); + + for (i = 0; i < nm_setting_ip_config_get_num_routes (self->setting); i++) { + NMIPRoute *route; + g_autofree gchar *prefix = NULL; + g_autofree gchar *metric = NULL; + + route = nm_setting_ip_config_get_route (self->setting, i); + prefix = g_strdup_printf ("%u", nm_ip_route_get_prefix (route)); + metric = g_strdup_printf ("%" G_GINT64_FORMAT, nm_ip_route_get_metric (route)); + add_route_row (self, nm_ip_route_get_dest (route), + prefix, + nm_ip_route_get_next_hop (route), + metric); + } + if (nm_setting_ip_config_get_num_routes (self->setting) == 0) + add_empty_route_row (self); + + gtk_widget_show_all (GTK_WIDGET (self->routes_box)); +} + +static void +connect_ip6_page (CEPageIP6 *self) +{ + const gchar *str_method; + guint method; + + add_address_box (self); + add_dns_section (self); + add_routes_box (self); + + str_method = nm_setting_ip_config_get_method (self->setting); + g_signal_connect_object (self->disabled_radio, "notify::active", G_CALLBACK (ce_page_changed), self, G_CONNECT_SWAPPED); + g_object_bind_property (self->disabled_radio, "active", + self->content_box, "sensitive", + G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN); + + g_signal_connect_object (self->shared_radio, "notify::active", G_CALLBACK (ce_page_changed), self, G_CONNECT_SWAPPED); + g_object_bind_property (self->shared_radio, "active", + self->content_box, "sensitive", + G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN); + + method = IP6_METHOD_AUTO; + if (g_strcmp0 (str_method, NM_SETTING_IP6_CONFIG_METHOD_DHCP) == 0) { + method = IP6_METHOD_DHCP; + } else if (g_strcmp0 (str_method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL) == 0) { + method = IP6_METHOD_LINK_LOCAL; + } else if (g_strcmp0 (str_method, NM_SETTING_IP6_CONFIG_METHOD_MANUAL) == 0) { + method = IP6_METHOD_MANUAL; + } else if (g_strcmp0 (str_method, NM_SETTING_IP6_CONFIG_METHOD_SHARED) == 0) { + method = IP6_METHOD_SHARED; + } else if (g_strcmp0 (str_method, NM_SETTING_IP6_CONFIG_METHOD_DISABLED) == 0 || + g_strcmp0 (str_method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) == 0) { + method = IP6_METHOD_DISABLED; + } + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->never_default_check), + nm_setting_ip_config_get_never_default (self->setting)); + g_signal_connect_object (self->never_default_check, "toggled", G_CALLBACK (ce_page_changed), self, G_CONNECT_SWAPPED); + + g_signal_connect_object (self->automatic_radio, "toggled", G_CALLBACK (method_changed), self, G_CONNECT_SWAPPED); + g_signal_connect_object (self->dhcp_radio, "toggled", G_CALLBACK (method_changed), self, G_CONNECT_SWAPPED); + g_signal_connect_object (self->local_radio, "toggled", G_CALLBACK (method_changed), self, G_CONNECT_SWAPPED); + g_signal_connect_object (self->manual_radio, "toggled", G_CALLBACK (method_changed), self, G_CONNECT_SWAPPED); + g_signal_connect_object (self->disabled_radio, "toggled", G_CALLBACK (method_changed), self, G_CONNECT_SWAPPED); + + switch (method) { + case IP6_METHOD_AUTO: + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->automatic_radio), TRUE); + break; + case IP6_METHOD_DHCP: + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->dhcp_radio), TRUE); + break; + case IP6_METHOD_LINK_LOCAL: + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->local_radio), TRUE); + break; + case IP6_METHOD_MANUAL: + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->manual_radio), TRUE); + break; + case IP6_METHOD_SHARED: + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->shared_radio), TRUE); + break; + case IP6_METHOD_DISABLED: + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->disabled_radio), TRUE); + break; + default: + break; + } + + method_changed (self); +} + +static gboolean +ui_to_setting (CEPageIP6 *self) +{ + const gchar *method; + gboolean ignore_auto_dns; + gboolean ignore_auto_routes; + gboolean never_default; + GList *children, *l; + gboolean ret = TRUE; + GStrv dns_addresses = NULL; + gchar *dns_text = NULL; + guint i; + + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->disabled_radio))) + method = NM_SETTING_IP6_CONFIG_METHOD_DISABLED; + else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->manual_radio))) + method = NM_SETTING_IP6_CONFIG_METHOD_MANUAL; + else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->local_radio))) + method = NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL; + else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->dhcp_radio))) + method = NM_SETTING_IP6_CONFIG_METHOD_DHCP; + else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->automatic_radio))) + method = NM_SETTING_IP6_CONFIG_METHOD_AUTO; + else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->shared_radio))) + method = NM_SETTING_IP6_CONFIG_METHOD_SHARED; + + nm_setting_ip_config_clear_addresses (self->setting); + if (g_str_equal (method, NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) { + children = gtk_container_get_children (GTK_CONTAINER (self->address_list)); + } else { + g_object_set (G_OBJECT (self->setting), + NM_SETTING_IP_CONFIG_GATEWAY, NULL, + NULL); + children = NULL; + } + + for (l = children; l; l = l->next) { + GtkWidget *row = l->data; + GtkEntry *entry; + const gchar *text_address; + const gchar *text_prefix; + const gchar *text_gateway; + guint32 prefix; + gchar *end; + NMIPAddress *addr; + gboolean have_gateway = FALSE; + + entry = GTK_ENTRY (g_object_get_data (G_OBJECT (row), "address")); + if (!entry) + continue; + + text_address = gtk_entry_get_text (entry); + text_prefix = gtk_entry_get_text (GTK_ENTRY (g_object_get_data (G_OBJECT (row), "prefix"))); + text_gateway = gtk_entry_get_text (GTK_ENTRY (g_object_get_data (G_OBJECT (row), "gateway"))); + + if (!*text_address && !*text_prefix && !*text_gateway) { + /* ignore empty rows */ + widget_unset_error (GTK_WIDGET (entry)); + widget_unset_error (g_object_get_data (G_OBJECT (row), "prefix")); + widget_unset_error (g_object_get_data (G_OBJECT (row), "gateway")); + continue; + } + + if (!*text_address || !nm_utils_ipaddr_valid (AF_INET6, text_address)) { + widget_set_error (GTK_WIDGET (entry)); + ret = FALSE; + } else { + widget_unset_error (GTK_WIDGET (entry)); + } + + prefix = strtoul (text_prefix, &end, 10); + if (!end || *end || prefix == 0 || prefix > 128) { + widget_set_error (g_object_get_data (G_OBJECT (row), "prefix")); + ret = FALSE; + } else { + widget_unset_error (g_object_get_data (G_OBJECT (row), "prefix")); + } + + if (*text_gateway && !nm_utils_ipaddr_valid (AF_INET6, text_gateway)) { + widget_set_error (g_object_get_data (G_OBJECT (row), "gateway")); + ret = FALSE; + } else { + widget_unset_error (g_object_get_data (G_OBJECT (row), "gateway")); + if (*text_gateway) + have_gateway = TRUE; + } + + if (!ret) + continue; + + addr = nm_ip_address_new (AF_INET6, text_address, prefix, NULL); + if (have_gateway) + g_object_set (G_OBJECT (self->setting), + NM_SETTING_IP_CONFIG_GATEWAY, text_gateway, + NULL); + nm_setting_ip_config_add_address (self->setting, addr); + + if (!l || !l->next) + ensure_empty_address_row (self); + } + g_list_free (children); + + nm_setting_ip_config_clear_dns (self->setting); + dns_text = g_strstrip (g_strdup (gtk_entry_get_text (GTK_ENTRY (self->dns_entry)))); + + if (g_str_equal (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) || + g_str_equal (method, NM_SETTING_IP6_CONFIG_METHOD_DHCP) || + g_str_equal (method, NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) + dns_addresses = g_strsplit_set (dns_text, ", ", -1); + else + dns_addresses = NULL; + + for (i = 0; dns_addresses && dns_addresses[i]; i++) { + const gchar *text; + struct in6_addr tmp_addr; + + text = dns_addresses[i]; + + if (!text || !*text) + continue; + + if (inet_pton (AF_INET6, text, &tmp_addr) <= 0) { + g_clear_pointer (&dns_addresses, g_strfreev); + widget_set_error (GTK_WIDGET (self->dns_entry)); + ret = FALSE; + break; + } else { + widget_unset_error (GTK_WIDGET (self->dns_entry)); + nm_setting_ip_config_add_dns (self->setting, text); + } + } + + nm_setting_ip_config_clear_routes (self->setting); + if (g_str_equal (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) || + g_str_equal (method, NM_SETTING_IP6_CONFIG_METHOD_DHCP) || + g_str_equal (method, NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) + children = gtk_container_get_children (GTK_CONTAINER (self->routes_list)); + else + children = NULL; + + for (l = children; l; l = l->next) { + GtkWidget *row = l->data; + GtkEntry *entry; + const gchar *text_address; + const gchar *text_prefix; + const gchar *text_gateway; + const gchar *text_metric; + guint32 prefix; + gint64 metric; + gchar *end; + NMIPRoute *route; + + entry = GTK_ENTRY (g_object_get_data (G_OBJECT (row), "address")); + if (!entry) + continue; + + text_address = gtk_entry_get_text (entry); + text_prefix = gtk_entry_get_text (GTK_ENTRY (g_object_get_data (G_OBJECT (row), "prefix"))); + text_gateway = gtk_entry_get_text (GTK_ENTRY (g_object_get_data (G_OBJECT (row), "gateway"))); + text_metric = gtk_entry_get_text (GTK_ENTRY (g_object_get_data (G_OBJECT (row), "metric"))); + + if (!*text_address && !*text_prefix && !*text_gateway && !*text_metric) { + /* ignore empty rows */ + widget_unset_error (GTK_WIDGET (entry)); + widget_unset_error (g_object_get_data (G_OBJECT (row), "prefix")); + widget_unset_error (g_object_get_data (G_OBJECT (row), "gateway")); + widget_unset_error (g_object_get_data (G_OBJECT (row), "metric")); + continue; + } + + if (!nm_utils_ipaddr_valid (AF_INET6, text_address)) { + widget_set_error (GTK_WIDGET (entry)); + ret = FALSE; + } else { + widget_unset_error (GTK_WIDGET (entry)); + } + + prefix = strtoul (text_prefix, &end, 10); + if (!end || *end || prefix == 0 || prefix > 128) { + widget_set_error (g_object_get_data (G_OBJECT (row), "prefix")); + ret = FALSE; + } else { + widget_unset_error (g_object_get_data (G_OBJECT (row), "prefix")); + } + + if (!nm_utils_ipaddr_valid (AF_INET6, text_gateway)) { + widget_set_error (g_object_get_data (G_OBJECT (row), "gateway")); + ret = FALSE; + } else { + widget_unset_error (g_object_get_data (G_OBJECT (row), "gateway")); + } + + metric = -1; + if (*text_metric) { + errno = 0; + metric = g_ascii_strtoull (text_metric, NULL, 10); + if (errno) { + widget_set_error (g_object_get_data (G_OBJECT (row), "metric")); + ret = FALSE; + } else { + widget_unset_error (g_object_get_data (G_OBJECT (row), "metric")); + } + } else { + widget_unset_error (g_object_get_data (G_OBJECT (row), "metric")); + } + + if (!ret) + continue; + + route = nm_ip_route_new (AF_INET6, text_address, prefix, text_gateway, metric, NULL); + nm_setting_ip_config_add_route (self->setting, route); + nm_ip_route_unref (route); + + if (!l || !l->next) + ensure_empty_routes_row (self); + } + g_list_free (children); + + if (!ret) + goto out; + + ignore_auto_dns = !gtk_switch_get_active (self->auto_dns_switch); + ignore_auto_routes = !gtk_switch_get_active (self->auto_routes_switch); + never_default = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->never_default_check)); + + g_object_set (self->setting, + NM_SETTING_IP_CONFIG_METHOD, method, + NM_SETTING_IP_CONFIG_IGNORE_AUTO_DNS, ignore_auto_dns, + NM_SETTING_IP_CONFIG_IGNORE_AUTO_ROUTES, ignore_auto_routes, + NM_SETTING_IP_CONFIG_NEVER_DEFAULT, never_default, + NULL); + +out: + g_clear_pointer (&dns_addresses, g_strfreev); + g_clear_pointer (&dns_text, g_free); + + return ret; +} + +static const gchar * +ce_page_ip6_get_title (CEPage *page) +{ + return _("IPv6"); +} + +static gboolean +ce_page_ip6_validate (CEPage *self, + NMConnection *connection, + GError **error) +{ + if (!ui_to_setting (CE_PAGE_IP6 (self))) + return FALSE; + + return nm_setting_verify (NM_SETTING (CE_PAGE_IP6 (self)->setting), NULL, error); +} + +static void +ce_page_ip6_init (CEPageIP6 *self) +{ + gtk_widget_init_template (GTK_WIDGET (self)); +} + +static void +ce_page_ip6_class_init (CEPageIP6Class *klass) +{ + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/network/ip6-page.ui"); + + gtk_widget_class_bind_template_child (widget_class, CEPageIP6, address_box); + gtk_widget_class_bind_template_child (widget_class, CEPageIP6, address_sizegroup); + gtk_widget_class_bind_template_child (widget_class, CEPageIP6, auto_dns_switch); + gtk_widget_class_bind_template_child (widget_class, CEPageIP6, auto_routes_switch); + gtk_widget_class_bind_template_child (widget_class, CEPageIP6, automatic_radio); + gtk_widget_class_bind_template_child (widget_class, CEPageIP6, content_box); + gtk_widget_class_bind_template_child (widget_class, CEPageIP6, dhcp_radio); + gtk_widget_class_bind_template_child (widget_class, CEPageIP6, disabled_radio); + gtk_widget_class_bind_template_child (widget_class, CEPageIP6, dns_entry); + gtk_widget_class_bind_template_child (widget_class, CEPageIP6, local_radio); + gtk_widget_class_bind_template_child (widget_class, CEPageIP6, manual_radio); + gtk_widget_class_bind_template_child (widget_class, CEPageIP6, never_default_check); + gtk_widget_class_bind_template_child (widget_class, CEPageIP6, routes_box); + gtk_widget_class_bind_template_child (widget_class, CEPageIP6, routes_metric_sizegroup); + gtk_widget_class_bind_template_child (widget_class, CEPageIP6, routes_sizegroup); + gtk_widget_class_bind_template_child (widget_class, CEPageIP6, shared_radio); +} + +static void +ce_page_iface_init (CEPageInterface *iface) +{ + iface->get_title = ce_page_ip6_get_title; + iface->validate = ce_page_ip6_validate; +} + +CEPageIP6 * +ce_page_ip6_new (NMConnection *connection, + NMClient *client) +{ + CEPageIP6 *self; + + self = CE_PAGE_IP6 (g_object_new (ce_page_ip6_get_type (), NULL)); + + self->setting = nm_connection_get_setting_ip6_config (connection); + if (!self->setting) { + self->setting = NM_SETTING_IP_CONFIG (nm_setting_ip6_config_new ()); + nm_connection_add_setting (connection, NM_SETTING (self->setting)); + } + + connect_ip6_page (self); + + return self; +} diff --git a/panels/network/connection-editor/ce-page-ip6.h b/panels/network/connection-editor/ce-page-ip6.h new file mode 100644 index 0000000..718a62c --- /dev/null +++ b/panels/network/connection-editor/ce-page-ip6.h @@ -0,0 +1,34 @@ +/* -*- 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. + */ + +#pragma once + +#include <gtk/gtk.h> +#include <NetworkManager.h> + +G_BEGIN_DECLS + +G_DECLARE_FINAL_TYPE (CEPageIP6, ce_page_ip6, CE, PAGE_IP6, GtkScrolledWindow) + +CEPageIP6 *ce_page_ip6_new (NMConnection *connection, + NMClient *client); + +G_END_DECLS diff --git a/panels/network/connection-editor/ce-page-security.c b/panels/network/connection-editor/ce-page-security.c new file mode 100644 index 0000000..7d7f305 --- /dev/null +++ b/panels/network/connection-editor/ce-page-security.c @@ -0,0 +1,542 @@ +/* -*- 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 <glib/gi18n.h> + +#include <NetworkManager.h> + +#include "ce-page.h" +#include "ce-page-security.h" +#include "wireless-security.h" +#include "ws-dynamic-wep.h" +#include "ws-leap.h" +#include "ws-wep-key.h" +#include "ws-wpa-eap.h" +#include "ws-wpa-psk.h" + +struct _CEPageSecurity +{ + GtkGrid parent; + + GtkBox *box; + GtkComboBox *security_combo; + GtkLabel *security_label; + + NMConnection *connection; + const gchar *security_setting; + GtkSizeGroup *group; + gboolean adhoc; +}; + +static void ce_page_iface_init (CEPageInterface *); + +G_DEFINE_TYPE_WITH_CODE (CEPageSecurity, ce_page_security, GTK_TYPE_GRID, + G_IMPLEMENT_INTERFACE (ce_page_get_type (), ce_page_iface_init)) + +enum { + S_NAME_COLUMN, + S_SEC_COLUMN, + S_ADHOC_VALID_COLUMN +}; + +static gboolean +find_proto (NMSettingWirelessSecurity *sec, const char *item) +{ + guint32 i; + + for (i = 0; i < nm_setting_wireless_security_get_num_protos (sec); i++) { + if (!strcmp (item, nm_setting_wireless_security_get_proto (sec, i))) + return TRUE; + } + return FALSE; +} + +static NMUtilsSecurityType +get_default_type_for_security (NMSettingWirelessSecurity *sec) +{ + const char *key_mgmt, *auth_alg; + + g_return_val_if_fail (sec != NULL, NMU_SEC_NONE); + + key_mgmt = nm_setting_wireless_security_get_key_mgmt (sec); + auth_alg = nm_setting_wireless_security_get_auth_alg (sec); + + /* No IEEE 802.1x */ + if (!strcmp (key_mgmt, "none")) + return NMU_SEC_STATIC_WEP; + + if (!strcmp (key_mgmt, "ieee8021x")) { + if (auth_alg && !strcmp (auth_alg, "leap")) + return NMU_SEC_LEAP; + return NMU_SEC_DYNAMIC_WEP; + } + +#if NM_CHECK_VERSION(1,24,0) + if (!strcmp (key_mgmt, "owe")) { + return NMU_SEC_OWE; + } +#endif + +#if NM_CHECK_VERSION(1,20,6) + if (!strcmp (key_mgmt, "sae")) { + return NMU_SEC_SAE; + } +#endif + + if ( !strcmp (key_mgmt, "wpa-none") + || !strcmp (key_mgmt, "wpa-psk")) { + if (find_proto (sec, "rsn")) + return NMU_SEC_WPA2_PSK; + else if (find_proto (sec, "wpa")) + return NMU_SEC_WPA_PSK; + else + return NMU_SEC_WPA_PSK; + } + + if (!strcmp (key_mgmt, "wpa-eap")) { + if (find_proto (sec, "rsn")) + return NMU_SEC_WPA2_ENTERPRISE; + else if (find_proto (sec, "wpa")) + return NMU_SEC_WPA_ENTERPRISE; + else + return NMU_SEC_WPA_ENTERPRISE; + } + + return NMU_SEC_INVALID; +} + +static WirelessSecurity * +security_combo_get_active (CEPageSecurity *self) +{ + GtkTreeIter iter; + GtkTreeModel *model; + WirelessSecurity *sec = NULL; + + model = gtk_combo_box_get_model (self->security_combo); + if (!gtk_combo_box_get_active_iter (self->security_combo, &iter)) + return NULL; + gtk_tree_model_get (model, &iter, S_SEC_COLUMN, &sec, -1); + + return sec; +} + +static void +wsec_size_group_clear (GtkSizeGroup *group) +{ + GSList *children; + GSList *iter; + + g_return_if_fail (group != NULL); + + children = gtk_size_group_get_widgets (group); + for (iter = children; iter; iter = g_slist_next (iter)) + gtk_size_group_remove_widget (group, GTK_WIDGET (iter->data)); +} + +static void +security_combo_changed (CEPageSecurity *self) +{ + GList *l, *children; + g_autoptr(WirelessSecurity) sec = NULL; + + wsec_size_group_clear (self->group); + + children = gtk_container_get_children (GTK_CONTAINER (self->box)); + for (l = children; l; l = l->next) { + gtk_container_remove (GTK_CONTAINER (self->box), GTK_WIDGET (l->data)); + } + + sec = security_combo_get_active (self); + if (sec) { + GtkWidget *parent; + + parent = gtk_widget_get_parent (GTK_WIDGET (sec)); + if (parent) + gtk_container_remove (GTK_CONTAINER (parent), GTK_WIDGET (sec)); + + gtk_size_group_add_widget (self->group, GTK_WIDGET (self->security_label)); + wireless_security_add_to_size_group (sec, self->group); + + gtk_container_add (GTK_CONTAINER (self->box), g_object_ref (GTK_WIDGET (sec))); + } + + ce_page_changed (CE_PAGE (self)); +} + +static void +security_item_changed_cb (CEPageSecurity *self) +{ + ce_page_changed (CE_PAGE (self)); +} + +static void +add_security_item (CEPageSecurity *self, + WirelessSecurity *sec, + GtkListStore *model, + GtkTreeIter *iter, + const char *text, + gboolean adhoc_valid) +{ + g_signal_connect_object (sec, "changed", G_CALLBACK (security_item_changed_cb), self, G_CONNECT_SWAPPED); + gtk_list_store_append (model, iter); + gtk_list_store_set (model, iter, + S_NAME_COLUMN, text, + S_SEC_COLUMN, sec, + S_ADHOC_VALID_COLUMN, adhoc_valid, + -1); + g_object_unref (sec); +} + +static void +set_sensitive (GtkCellLayout *cell_layout, + GtkCellRenderer *cell, + GtkTreeModel *tree_model, + GtkTreeIter *iter, + gpointer data) +{ + gboolean *adhoc = data; + gboolean sensitive = TRUE, adhoc_valid = TRUE; + + gtk_tree_model_get (tree_model, iter, S_ADHOC_VALID_COLUMN, &adhoc_valid, -1); + if (*adhoc && !adhoc_valid) + sensitive = FALSE; + + g_object_set (cell, "sensitive", sensitive, NULL); +} + +static void +finish_setup (CEPageSecurity *self) +{ + NMSettingWireless *sw; + NMSettingWirelessSecurity *sws; + gboolean is_adhoc = FALSE; + g_autoptr(GtkListStore) sec_model = NULL; + GtkTreeIter iter; + const gchar *mode; + guint32 dev_caps = 0; + NMUtilsSecurityType default_type = NMU_SEC_NONE; + int active = -1; + int item = 0; + GtkCellRenderer *renderer; + + sw = nm_connection_get_setting_wireless (self->connection); + g_assert (sw); + + self->group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); + + dev_caps = NM_WIFI_DEVICE_CAP_CIPHER_WEP40 + | NM_WIFI_DEVICE_CAP_CIPHER_WEP104 + | NM_WIFI_DEVICE_CAP_CIPHER_TKIP + | NM_WIFI_DEVICE_CAP_CIPHER_CCMP + | NM_WIFI_DEVICE_CAP_WPA + | NM_WIFI_DEVICE_CAP_RSN; + + mode = nm_setting_wireless_get_mode (sw); + if (mode && !strcmp (mode, "adhoc")) + is_adhoc = TRUE; + self->adhoc = is_adhoc; + + sws = nm_connection_get_setting_wireless_security (self->connection); + if (sws) + default_type = get_default_type_for_security (sws); + + sec_model = gtk_list_store_new (3, G_TYPE_STRING, wireless_security_get_type (), G_TYPE_BOOLEAN); + + if (nm_utils_security_valid (NMU_SEC_NONE, dev_caps, FALSE, is_adhoc, 0, 0, 0)) { + gtk_list_store_insert_with_values (sec_model, &iter, -1, + S_NAME_COLUMN, C_("Wi-Fi/Ethernet security", "None"), + S_ADHOC_VALID_COLUMN, TRUE, + -1); + if (default_type == NMU_SEC_NONE) + active = item; + item++; + } + +#if NM_CHECK_VERSION(1,24,0) + if (nm_utils_security_valid (NMU_SEC_OWE, dev_caps, FALSE, is_adhoc, 0, 0, 0)) { + gtk_list_store_insert_with_values (sec_model, &iter, -1, + S_NAME_COLUMN, _("Enhanced Open"), + S_ADHOC_VALID_COLUMN, FALSE, + -1); + if (active < 0 && default_type == NMU_SEC_OWE) + active = item; + item++; + } +#endif + + if (nm_utils_security_valid (NMU_SEC_STATIC_WEP, dev_caps, FALSE, is_adhoc, 0, 0, 0)) { + WirelessSecurityWEPKey *ws_wep; + NMWepKeyType wep_type = NM_WEP_KEY_TYPE_KEY; + + if (default_type == NMU_SEC_STATIC_WEP) { + sws = nm_connection_get_setting_wireless_security (self->connection); + if (sws) + wep_type = nm_setting_wireless_security_get_wep_key_type (sws); + if (wep_type == NM_WEP_KEY_TYPE_UNKNOWN) + wep_type = NM_WEP_KEY_TYPE_KEY; + } + + ws_wep = ws_wep_key_new (self->connection, NM_WEP_KEY_TYPE_KEY); + if (ws_wep) { + add_security_item (self, WIRELESS_SECURITY (ws_wep), sec_model, + &iter, _("WEP 40/128-bit Key (Hex or ASCII)"), + TRUE); + if ((active < 0) && (default_type == NMU_SEC_STATIC_WEP) && (wep_type == NM_WEP_KEY_TYPE_KEY)) + active = item; + item++; + } + + ws_wep = ws_wep_key_new (self->connection, NM_WEP_KEY_TYPE_PASSPHRASE); + if (ws_wep) { + add_security_item (self, WIRELESS_SECURITY (ws_wep), sec_model, + &iter, _("WEP 128-bit Passphrase"), TRUE); + if ((active < 0) && (default_type == NMU_SEC_STATIC_WEP) && (wep_type == NM_WEP_KEY_TYPE_PASSPHRASE)) + active = item; + item++; + } + } + + if (nm_utils_security_valid (NMU_SEC_LEAP, dev_caps, FALSE, is_adhoc, 0, 0, 0)) { + WirelessSecurityLEAP *ws_leap; + + ws_leap = ws_leap_new (self->connection); + if (ws_leap) { + add_security_item (self, WIRELESS_SECURITY (ws_leap), sec_model, + &iter, _("LEAP"), FALSE); + if ((active < 0) && (default_type == NMU_SEC_LEAP)) + active = item; + item++; + } + } + + if (nm_utils_security_valid (NMU_SEC_DYNAMIC_WEP, dev_caps, FALSE, is_adhoc, 0, 0, 0)) { + WirelessSecurityDynamicWEP *ws_dynamic_wep; + + ws_dynamic_wep = ws_dynamic_wep_new (self->connection); + if (ws_dynamic_wep) { + add_security_item (self, WIRELESS_SECURITY (ws_dynamic_wep), sec_model, + &iter, _("Dynamic WEP (802.1x)"), FALSE); + if ((active < 0) && (default_type == NMU_SEC_DYNAMIC_WEP)) + active = item; + item++; + } + } + + if (nm_utils_security_valid (NMU_SEC_WPA_PSK, dev_caps, FALSE, is_adhoc, 0, 0, 0) || + nm_utils_security_valid (NMU_SEC_WPA2_PSK, dev_caps, FALSE, is_adhoc, 0, 0, 0)) { + WirelessSecurityWPAPSK *ws_wpa_psk; + + ws_wpa_psk = ws_wpa_psk_new (self->connection); + if (ws_wpa_psk) { + add_security_item (self, WIRELESS_SECURITY (ws_wpa_psk), sec_model, + &iter, _("WPA & WPA2 Personal"), FALSE); + if ((active < 0) && ((default_type == NMU_SEC_WPA_PSK) || (default_type == NMU_SEC_WPA2_PSK))) + active = item; + item++; + } + } + + if (nm_utils_security_valid (NMU_SEC_WPA_ENTERPRISE, dev_caps, FALSE, is_adhoc, 0, 0, 0) || + nm_utils_security_valid (NMU_SEC_WPA2_ENTERPRISE, dev_caps, FALSE, is_adhoc, 0, 0, 0)) { + WirelessSecurityWPAEAP *ws_wpa_eap; + + ws_wpa_eap = ws_wpa_eap_new (self->connection); + if (ws_wpa_eap) { + add_security_item (self, WIRELESS_SECURITY (ws_wpa_eap), sec_model, + &iter, _("WPA & WPA2 Enterprise"), FALSE); + if ((active < 0) && ((default_type == NMU_SEC_WPA_ENTERPRISE) || (default_type == NMU_SEC_WPA2_ENTERPRISE))) + active = item; + item++; + } + } + +#if NM_CHECK_VERSION(1,20,6) + if (nm_utils_security_valid (NMU_SEC_SAE, dev_caps, FALSE, is_adhoc, 0, 0, 0)) { + WirelessSecurityWPAPSK *ws_wpa_psk; + + ws_wpa_psk = ws_wpa_psk_new (self->connection); + if (ws_wpa_psk) { + add_security_item (self, WIRELESS_SECURITY (ws_wpa_psk), sec_model, + &iter, _("WPA3 Personal"), FALSE); + if ((active < 0) && ((default_type == NMU_SEC_SAE))) + active = item; + item++; + } + } +#endif + + gtk_combo_box_set_model (self->security_combo, GTK_TREE_MODEL (sec_model)); + gtk_cell_layout_clear (GTK_CELL_LAYOUT (self->security_combo)); + + renderer = gtk_cell_renderer_text_new (); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (self->security_combo), renderer, TRUE); + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (self->security_combo), renderer, "text", S_NAME_COLUMN, NULL); + gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (self->security_combo), renderer, set_sensitive, &self->adhoc, NULL); + + gtk_combo_box_set_active (self->security_combo, active < 0 ? 0 : (guint32) active); + + security_combo_changed (self); + g_signal_connect_object (self->security_combo, "changed", + G_CALLBACK (security_combo_changed), self, G_CONNECT_SWAPPED); +} + +static void +ce_page_security_dispose (GObject *object) +{ + CEPageSecurity *self = CE_PAGE_SECURITY (object); + + g_clear_object (&self->connection); + g_clear_object (&self->group); + + G_OBJECT_CLASS (ce_page_security_parent_class)->dispose (object); +} + +static const gchar * +ce_page_security_get_security_setting (CEPage *page) +{ + return CE_PAGE_SECURITY (page)->security_setting; +} + +static const gchar * +ce_page_security_get_title (CEPage *page) +{ + return _("Security"); +} + +static gboolean +ce_page_security_validate (CEPage *page, + NMConnection *connection, + GError **error) +{ + CEPageSecurity *self = CE_PAGE_SECURITY (page); + NMSettingWireless *sw; + g_autoptr(WirelessSecurity) sec = NULL; + gboolean valid = FALSE; + const char *mode; + + sw = nm_connection_get_setting_wireless (connection); + + mode = nm_setting_wireless_get_mode (sw); + if (g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_ADHOC) == 0) + CE_PAGE_SECURITY (self)->adhoc = TRUE; + else + CE_PAGE_SECURITY (self)->adhoc = FALSE; + + sec = security_combo_get_active (CE_PAGE_SECURITY (self)); + if (sec) { + GBytes *ssid = nm_setting_wireless_get_ssid (sw); + + if (ssid) { + /* FIXME: get failed property and error out of wifi security objects */ + valid = wireless_security_validate (sec, error); + if (valid) + wireless_security_fill_connection (sec, connection); + } else { + g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_MISSING_SETTING, "Missing SSID"); + valid = FALSE; + } + + if (self->adhoc) { + if (!wireless_security_adhoc_compatible (sec)) { + if (valid) + g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_SETTING, "Security not compatible with Ad-Hoc mode"); + valid = FALSE; + } + } + } else { + /* No security, unencrypted */ + nm_connection_remove_setting (connection, NM_TYPE_SETTING_WIRELESS_SECURITY); + nm_connection_remove_setting (connection, NM_TYPE_SETTING_802_1X); + valid = TRUE; + } + + return valid; +} + +static void +ce_page_security_init (CEPageSecurity *self) +{ + gtk_widget_init_template (GTK_WIDGET (self)); +} + +static void +ce_page_security_class_init (CEPageSecurityClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->dispose = ce_page_security_dispose; + + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/network/security-page.ui"); + + gtk_widget_class_bind_template_child (widget_class, CEPageSecurity, box); + gtk_widget_class_bind_template_child (widget_class, CEPageSecurity, security_label); + gtk_widget_class_bind_template_child (widget_class, CEPageSecurity, security_combo); +} + +static void +ce_page_iface_init (CEPageInterface *iface) +{ + iface->get_security_setting = ce_page_security_get_security_setting; + iface->get_title = ce_page_security_get_title; + iface->validate = ce_page_security_validate; +} + +CEPageSecurity * +ce_page_security_new (NMConnection *connection) +{ + CEPageSecurity *self; + NMUtilsSecurityType default_type = NMU_SEC_NONE; + NMSettingWirelessSecurity *sws; + + self = CE_PAGE_SECURITY (g_object_new (ce_page_security_get_type (), NULL)); + + self->connection = g_object_ref (connection); + + sws = nm_connection_get_setting_wireless_security (connection); + if (sws) + default_type = get_default_type_for_security (sws); + + if (default_type == NMU_SEC_STATIC_WEP || + default_type == NMU_SEC_LEAP || + default_type == NMU_SEC_WPA_PSK || +#if NM_CHECK_VERSION(1,20,6) + default_type == NMU_SEC_SAE || +#endif +#if NM_CHECK_VERSION(1,24,0) + default_type == NMU_SEC_OWE || +#endif + default_type == NMU_SEC_WPA2_PSK) { + self->security_setting = NM_SETTING_WIRELESS_SECURITY_SETTING_NAME; + } + + if (default_type == NMU_SEC_DYNAMIC_WEP || + default_type == NMU_SEC_WPA_ENTERPRISE || + default_type == NMU_SEC_WPA2_ENTERPRISE) { + self->security_setting = NM_SETTING_802_1X_SETTING_NAME; + } + + g_signal_connect (self, "initialized", G_CALLBACK (finish_setup), NULL); + + return self; +} diff --git a/panels/network/connection-editor/ce-page-security.h b/panels/network/connection-editor/ce-page-security.h new file mode 100644 index 0000000..e7c84e5 --- /dev/null +++ b/panels/network/connection-editor/ce-page-security.h @@ -0,0 +1,33 @@ +/* -*- 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. + */ + +#pragma once + +#include <gtk/gtk.h> +#include <NetworkManager.h> + +G_BEGIN_DECLS + +G_DECLARE_FINAL_TYPE (CEPageSecurity, ce_page_security, CE, PAGE_SECURITY, GtkGrid) + +CEPageSecurity *ce_page_security_new (NMConnection *connection); + +G_END_DECLS diff --git a/panels/network/connection-editor/ce-page-vpn.c b/panels/network/connection-editor/ce-page-vpn.c new file mode 100644 index 0000000..9ad0a26 --- /dev/null +++ b/panels/network/connection-editor/ce-page-vpn.c @@ -0,0 +1,229 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2013 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 <glib/gi18n.h> + +#include <NetworkManager.h> + +#include "ce-page.h" +#include "ce-page-vpn.h" +#include "vpn-helpers.h" + +struct _CEPageVpn +{ + GtkBox parent; + + GtkLabel *failure_label; + GtkEntry *name_entry; + + NMConnection *connection; + NMSettingConnection *setting_connection; + NMSettingVpn *setting_vpn; + + NMVpnEditorPlugin *plugin; + NMVpnEditor *editor; +}; + +static void ce_page_iface_init (CEPageInterface *); + +G_DEFINE_TYPE_WITH_CODE (CEPageVpn, ce_page_vpn, GTK_TYPE_BOX, + G_IMPLEMENT_INTERFACE (ce_page_get_type (), ce_page_iface_init)) + +/* Hack to make the plugin-provided editor widget fit in better with + * the control center by changing + * + * Foo: [__________] + * Bar baz: [__________] + * + * to + * + * Foo [__________] + * Bar baz [__________] + */ +static void +vpn_gnome3ify_editor (GtkWidget *widget) +{ + if (GTK_IS_CONTAINER (widget)) { + GList *children, *iter; + + children = gtk_container_get_children (GTK_CONTAINER (widget)); + for (iter = children; iter; iter = iter->next) + vpn_gnome3ify_editor (iter->data); + g_list_free (children); + } else if (GTK_IS_LABEL (widget)) { + const char *text; + gfloat xalign; + g_autofree gchar *newtext = NULL; + int len; + + xalign = gtk_label_get_xalign (GTK_LABEL (widget)); + if (xalign != 0.0) + return; + text = gtk_label_get_text (GTK_LABEL (widget)); + len = strlen (text); + if (len < 2 || text[len - 1] != ':') + return; + + newtext = g_strndup (text, len - 1); + gtk_label_set_text (GTK_LABEL (widget), newtext); + gtk_label_set_xalign (GTK_LABEL (widget), 1.0); + } +} + +static void +load_vpn_plugin (CEPageVpn *self) +{ + GtkWidget *ui_widget; + + self->editor = nm_vpn_editor_plugin_get_editor (self->plugin, + self->connection, + NULL); + ui_widget = NULL; + if (self->editor) + ui_widget = GTK_WIDGET (nm_vpn_editor_get_widget (self->editor)); + + if (!ui_widget) { + g_clear_object (&self->editor); + self->plugin = NULL; + return; + } + vpn_gnome3ify_editor (ui_widget); + + gtk_widget_destroy (GTK_WIDGET (self->failure_label)); + + gtk_box_pack_start (GTK_BOX (self), ui_widget, TRUE, TRUE, 0); + gtk_widget_show_all (ui_widget); + + g_signal_connect_object (self->editor, "changed", G_CALLBACK (ce_page_changed), self, G_CONNECT_SWAPPED); +} + +static void +connect_vpn_page (CEPageVpn *self) +{ + const gchar *name; + + name = nm_setting_connection_get_id (self->setting_connection); + gtk_entry_set_text (self->name_entry, name); + g_signal_connect_object (self->name_entry, "changed", G_CALLBACK (ce_page_changed), self, G_CONNECT_SWAPPED); +} + +static void +ce_page_vpn_dispose (GObject *object) +{ + CEPageVpn *self = CE_PAGE_VPN (object); + + g_clear_object (&self->connection); + g_clear_object (&self->editor); + + G_OBJECT_CLASS (ce_page_vpn_parent_class)->dispose (object); +} + +static const gchar * +ce_page_vpn_get_security_setting (CEPage *page) +{ + return NM_SETTING_VPN_SETTING_NAME; +} + +static const gchar * +ce_page_vpn_get_title (CEPage *page) +{ + return _("Identity"); +} + +static gboolean +ce_page_vpn_validate (CEPage *page, + NMConnection *connection, + GError **error) +{ + CEPageVpn *self = CE_PAGE_VPN (page); + + g_object_set (self->setting_connection, + NM_SETTING_CONNECTION_ID, gtk_entry_get_text (self->name_entry), + NULL); + + if (!nm_setting_verify (NM_SETTING (self->setting_connection), NULL, error)) + return FALSE; + + if (!self->editor) + return TRUE; + + return nm_vpn_editor_update_connection (self->editor, connection, error); +} + +static void +ce_page_vpn_init (CEPageVpn *self) +{ + gtk_widget_init_template (GTK_WIDGET (self)); +} + +static void +ce_page_vpn_class_init (CEPageVpnClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class); + + object_class->dispose = ce_page_vpn_dispose; + + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/network/vpn-page.ui"); + + gtk_widget_class_bind_template_child (widget_class, CEPageVpn, failure_label); + gtk_widget_class_bind_template_child (widget_class, CEPageVpn, name_entry); +} + +static void +ce_page_iface_init (CEPageInterface *iface) +{ + iface->get_security_setting = ce_page_vpn_get_security_setting; + iface->get_title = ce_page_vpn_get_title; + iface->validate = ce_page_vpn_validate; +} + +static void +finish_setup (CEPageVpn *self, gpointer unused, GError *error, gpointer user_data) +{ + const char *vpn_type; + + self->setting_connection = nm_connection_get_setting_connection (self->connection); + self->setting_vpn = nm_connection_get_setting_vpn (self->connection); + vpn_type = nm_setting_vpn_get_service_type (self->setting_vpn); + + self->plugin = vpn_get_plugin_by_service (vpn_type); + if (self->plugin) + load_vpn_plugin (self); + + connect_vpn_page (self); +} + +CEPageVpn * +ce_page_vpn_new (NMConnection *connection) +{ + CEPageVpn *self; + + self = CE_PAGE_VPN (g_object_new (ce_page_vpn_get_type (), NULL)); + + self->connection = g_object_ref (connection); + + g_signal_connect (self, "initialized", G_CALLBACK (finish_setup), NULL); + + return self; +} diff --git a/panels/network/connection-editor/ce-page-vpn.h b/panels/network/connection-editor/ce-page-vpn.h new file mode 100644 index 0000000..f194080 --- /dev/null +++ b/panels/network/connection-editor/ce-page-vpn.h @@ -0,0 +1,33 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2013 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. + */ + +#pragma once + +#include <gtk/gtk.h> +#include <NetworkManager.h> + +G_BEGIN_DECLS + +G_DECLARE_FINAL_TYPE (CEPageVpn, ce_page_vpn, CE, PAGE_VPN, GtkBox) + +CEPageVpn *ce_page_vpn_new (NMConnection *connection); + +G_END_DECLS diff --git a/panels/network/connection-editor/ce-page-wifi.c b/panels/network/connection-editor/ce-page-wifi.c new file mode 100644 index 0000000..7246170 --- /dev/null +++ b/panels/network/connection-editor/ce-page-wifi.c @@ -0,0 +1,212 @@ +/* -*- 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 <glib/gi18n.h> +#include <NetworkManager.h> +#include <net/if_arp.h> + +#include "ce-page.h" +#include "ce-page-wifi.h" +#include "ui-helpers.h" + +struct _CEPageWifi +{ + GtkGrid parent; + + GtkComboBoxText *bssid_combo; + GtkComboBoxText *cloned_mac_combo; + GtkComboBoxText *mac_combo; + GtkEntry *ssid_entry; + + NMClient *client; + NMSettingWireless *setting; +}; + +static void ce_page_iface_init (CEPageInterface *); + +G_DEFINE_TYPE_WITH_CODE (CEPageWifi, ce_page_wifi, GTK_TYPE_GRID, + G_IMPLEMENT_INTERFACE (ce_page_get_type (), ce_page_iface_init)) + +static void +connect_wifi_page (CEPageWifi *self) +{ + GBytes *ssid; + g_autofree gchar *utf8_ssid = NULL; + GPtrArray *bssid_array; + gchar **bssid_list; + const char *s_bssid_str; + gchar **mac_list; + const gchar *s_mac_str; + const gchar *cloned_mac; + gint i; + + ssid = nm_setting_wireless_get_ssid (self->setting); + if (ssid) + utf8_ssid = nm_utils_ssid_to_utf8 (g_bytes_get_data (ssid, NULL), g_bytes_get_size (ssid)); + else + utf8_ssid = g_strdup (""); + gtk_entry_set_text (self->ssid_entry, utf8_ssid); + + g_signal_connect_object (self->ssid_entry, "changed", G_CALLBACK (ce_page_changed), self, G_CONNECT_SWAPPED); + + bssid_array = g_ptr_array_new (); + for (i = 0; i < nm_setting_wireless_get_num_seen_bssids (self->setting); i++) { + g_ptr_array_add (bssid_array, g_strdup (nm_setting_wireless_get_seen_bssid (self->setting, i))); + } + g_ptr_array_add (bssid_array, NULL); + bssid_list = (gchar **) g_ptr_array_free (bssid_array, FALSE); + s_bssid_str = nm_setting_wireless_get_bssid (self->setting); + ce_page_setup_mac_combo (self->bssid_combo, s_bssid_str, bssid_list); + g_strfreev (bssid_list); + g_signal_connect_object (self->bssid_combo, "changed", G_CALLBACK (ce_page_changed), self, G_CONNECT_SWAPPED); + + mac_list = ce_page_get_mac_list (self->client, NM_TYPE_DEVICE_WIFI, + NM_DEVICE_WIFI_PERMANENT_HW_ADDRESS); + s_mac_str = nm_setting_wireless_get_mac_address (self->setting); + ce_page_setup_mac_combo (self->mac_combo, s_mac_str, mac_list); + g_strfreev (mac_list); + g_signal_connect_object (self->mac_combo, "changed", G_CALLBACK (ce_page_changed), self, G_CONNECT_SWAPPED); + + cloned_mac = nm_setting_wireless_get_cloned_mac_address (self->setting); + ce_page_setup_cloned_mac_combo (self->cloned_mac_combo, cloned_mac); + g_signal_connect_object (self->cloned_mac_combo, "changed", G_CALLBACK (ce_page_changed), self, G_CONNECT_SWAPPED); +} + +static void +ui_to_setting (CEPageWifi *self) +{ + g_autoptr(GBytes) ssid = NULL; + const gchar *utf8_ssid, *bssid; + GtkWidget *entry; + g_autofree gchar *device_mac = NULL; + g_autofree gchar *cloned_mac = NULL; + + utf8_ssid = gtk_entry_get_text (self->ssid_entry); + if (!utf8_ssid || !*utf8_ssid) + ssid = NULL; + else { + ssid = g_bytes_new_static (utf8_ssid, strlen (utf8_ssid)); + } + entry = gtk_bin_get_child (GTK_BIN (self->bssid_combo)); + bssid = gtk_entry_get_text (GTK_ENTRY (entry)); + if (*bssid == '\0') + bssid = NULL; + entry = gtk_bin_get_child (GTK_BIN (self->mac_combo)); + device_mac = ce_page_trim_address (gtk_entry_get_text (GTK_ENTRY (entry))); + cloned_mac = ce_page_cloned_mac_get (self->cloned_mac_combo); + + g_object_set (self->setting, + NM_SETTING_WIRELESS_SSID, ssid, + NM_SETTING_WIRELESS_BSSID, bssid, + NM_SETTING_WIRELESS_MAC_ADDRESS, device_mac, + NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, cloned_mac, + NULL); +} + +static const gchar * +ce_page_wifi_get_title (CEPage *page) +{ + return _("Identity"); +} + +static gboolean +ce_page_wifi_class_validate (CEPage *parent, + NMConnection *connection, + GError **error) +{ + CEPageWifi *self = (CEPageWifi *) parent; + GtkWidget *entry; + gboolean ret = TRUE; + + entry = gtk_bin_get_child (GTK_BIN (self->bssid_combo)); + if (!ce_page_address_is_valid (gtk_entry_get_text (GTK_ENTRY (entry)))) { + widget_set_error (entry); + ret = FALSE; + } else { + widget_unset_error (entry); + } + + entry = gtk_bin_get_child (GTK_BIN (self->mac_combo)); + if (!ce_page_address_is_valid (gtk_entry_get_text (GTK_ENTRY (entry)))) { + widget_set_error (entry); + ret = FALSE; + } else { + widget_unset_error (entry); + } + + if (!ce_page_cloned_mac_combo_valid (self->cloned_mac_combo)) { + widget_set_error (gtk_bin_get_child (GTK_BIN (self->cloned_mac_combo))); + ret = FALSE; + } else { + widget_unset_error (gtk_bin_get_child (GTK_BIN (self->cloned_mac_combo))); + } + + if (!ret) + return ret; + + ui_to_setting (CE_PAGE_WIFI (self)); + + return ret; +} + +static void +ce_page_wifi_init (CEPageWifi *self) +{ + gtk_widget_init_template (GTK_WIDGET (self)); +} + +static void +ce_page_wifi_class_init (CEPageWifiClass *klass) +{ + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/network/wifi-page.ui"); + + gtk_widget_class_bind_template_child (widget_class, CEPageWifi, bssid_combo); + gtk_widget_class_bind_template_child (widget_class, CEPageWifi, cloned_mac_combo); + gtk_widget_class_bind_template_child (widget_class, CEPageWifi, mac_combo); + gtk_widget_class_bind_template_child (widget_class, CEPageWifi, ssid_entry); +} + +static void +ce_page_iface_init (CEPageInterface *iface) +{ + iface->get_title = ce_page_wifi_get_title; + iface->validate = ce_page_wifi_class_validate; +} + +CEPageWifi * +ce_page_wifi_new (NMConnection *connection, + NMClient *client) +{ + CEPageWifi *self; + + self = CE_PAGE_WIFI (g_object_new (ce_page_wifi_get_type (), NULL)); + + self->client = client; + self->setting = nm_connection_get_setting_wireless (connection); + + connect_wifi_page (self); + + return self; +} diff --git a/panels/network/connection-editor/ce-page-wifi.h b/panels/network/connection-editor/ce-page-wifi.h new file mode 100644 index 0000000..8beabad --- /dev/null +++ b/panels/network/connection-editor/ce-page-wifi.h @@ -0,0 +1,32 @@ +/* -*- 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. + */ + +#pragma once + +#include <gtk/gtk.h> +#include <NetworkManager.h> + +G_DECLARE_FINAL_TYPE (CEPageWifi, ce_page_wifi, CE, PAGE_WIFI, GtkGrid) + +CEPageWifi *ce_page_wifi_new (NMConnection *connection, + NMClient *client); + +G_END_DECLS 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; +} diff --git a/panels/network/connection-editor/ce-page.h b/panels/network/connection-editor/ce-page.h new file mode 100644 index 0000000..a180afb --- /dev/null +++ b/panels/network/connection-editor/ce-page.h @@ -0,0 +1,79 @@ +/* -*- 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. + */ + +#pragma once + +#include <glib-object.h> + +#include <NetworkManager.h> + +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +G_DECLARE_INTERFACE (CEPage, ce_page, CE, PAGE, GObject) + +struct _CEPageInterface +{ + GTypeInterface g_iface; + + gboolean (*validate) (CEPage *page, NMConnection *connection, GError **error); + const gchar *(*get_title) (CEPage *page); + const gchar *(*get_security_setting) (CEPage *page); +}; + +const gchar *ce_page_get_title (CEPage *page); +const gchar *ce_page_get_security_setting (CEPage *page); +gboolean ce_page_validate (CEPage *page, + NMConnection *connection, + GError **error); +void ce_page_changed (CEPage *page); +void ce_page_complete_init (CEPage *page, + NMConnection *connection, + const gchar *setting_name, + GVariant *variant, + GError *error); + +gchar **ce_page_get_mac_list (NMClient *client, + GType device_type, + const gchar *mac_property); +void ce_page_setup_mac_combo (GtkComboBoxText *combo, + const gchar *current_mac, + gchar **mac_list); +void ce_page_setup_cloned_mac_combo (GtkComboBoxText *combo, + const char *current); +gint ce_get_property_default (NMSetting *setting, + const gchar *property_name); +gboolean ce_page_address_is_valid (const gchar *addr); +gchar *ce_page_trim_address (const gchar *addr); +char *ce_page_cloned_mac_get (GtkComboBoxText *combo); +gboolean ce_page_cloned_mac_combo_valid (GtkComboBoxText *combo); + +typedef enum { + NAME_FORMAT_TYPE, + NAME_FORMAT_PROFILE +} NameFormat; + +gchar * ce_page_get_next_available_name (const GPtrArray *connections, + NameFormat format, + const gchar *type_name); + +G_END_DECLS diff --git a/panels/network/connection-editor/connection-editor.gresource.xml b/panels/network/connection-editor/connection-editor.gresource.xml new file mode 100644 index 0000000..3d06f5a --- /dev/null +++ b/panels/network/connection-editor/connection-editor.gresource.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<gresources> + <gresource prefix="/org/gnome/control-center/network"> + <file preprocess="xml-stripblanks">8021x-security-page.ui</file> + <file preprocess="xml-stripblanks">connection-editor.ui</file> + <file preprocess="xml-stripblanks">details-page.ui</file> + <file preprocess="xml-stripblanks">ethernet-page.ui</file> + <file preprocess="xml-stripblanks">ip4-page.ui</file> + <file preprocess="xml-stripblanks">ip6-page.ui</file> + <file preprocess="xml-stripblanks">security-page.ui</file> + <file preprocess="xml-stripblanks">vpn-page.ui</file> + <file preprocess="xml-stripblanks">wifi-page.ui</file> + </gresource> +</gresources> diff --git a/panels/network/connection-editor/connection-editor.ui b/panels/network/connection-editor/connection-editor.ui new file mode 100644 index 0000000..17a096f --- /dev/null +++ b/panels/network/connection-editor/connection-editor.ui @@ -0,0 +1,108 @@ +<?xml version="1.0" encoding="UTF-8"?> +<interface> + <!-- interface-requires gtk+ 3.0 --> + <template class="NetConnectionEditor" parent="GtkDialog"> + <property name="can_focus">False</property> + <property name="border_width">0</property> + <property name="resizable">False</property> + <property name="modal">True</property> + <property name="default_width">500</property> + <property name="default_height">300</property> + <property name="type_hint">dialog</property> + <!-- This doesn't seem to work for a template, so it is also hardcoded. --> + <property name="use_header_bar">1</property> + <signal name="delete-event" handler="delete_event_cb" swapped="yes"/> + <child type="action"> + <object class="GtkButton" id="cancel_button"> + <property name="label" translatable="yes">_Cancel</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="use_underline">True</property> + <signal name="clicked" handler="cancel_clicked_cb" object="NetConnectionEditor" swapped="yes"/> + </object> + </child> + <child type="action"> + <object class="GtkButton" id="apply_button"> + <property name="label" translatable="yes">_Apply</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="can_default">True</property> + <property name="receives_default">True</property> + <property name="use_underline">True</property> + <signal name="clicked" handler="apply_clicked_cb" object="NetConnectionEditor" swapped="yes"/> + </object> + </child> + <child internal-child="vbox"> + <object class="GtkBox"> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <property name="spacing">0</property> + <property name="border_width">0</property> + <child> + <object class="GtkStack" id="toplevel_stack"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <child> + <object class="GtkNotebook" id="notebook"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="show_border">False</property> + </object> + </child> + <child> + <object class="GtkBox" id="add_connection_box"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="vexpand">True</property> + <child> + <object class="GtkBox"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <property name="vexpand">True</property> + <child> + <object class="GtkFrame" id="add_connection_frame"> + <property name="width_request">300</property> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label_xalign">0</property> + <property name="shadow_type">in</property> + <property name="vexpand">True</property> + <property name="valign">start</property> + <child> + <placeholder/> + </child> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + </object> + <packing> + <property name="expand">True</property> + <property name="position">0</property> + </packing> + </child> + </object> + <packing> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + </object> + </child> + <action-widgets> + <action-widget response="cancel">cancel_button</action-widget> + <action-widget response="apply" default="true">apply_button</action-widget> + </action-widgets> + </template> +</interface> diff --git a/panels/network/connection-editor/details-page.ui b/panels/network/connection-editor/details-page.ui new file mode 100644 index 0000000..fba3aa7 --- /dev/null +++ b/panels/network/connection-editor/details-page.ui @@ -0,0 +1,483 @@ +<?xml version="1.0" encoding="UTF-8"?> +<interface> + <!-- interface-requires gtk+ 3.0 --> + <template class="CEPageDetails" parent="GtkGrid"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="margin_start">24</property> + <property name="margin_end">24</property> + <property name="margin_top">24</property> + <property name="margin_bottom">24</property> + <property name="row_spacing">12</property> + <property name="column_spacing">12</property> + <child> + <object class="GtkLabel" id="strength_heading_label"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="xalign">1</property> + <property name="label" translatable="yes">Signal Strength</property> + <property name="mnemonic_widget">strength_label</property> + <style> + <class name="dim-label"/> + </style> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">0</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="strength_label"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hexpand">True</property> + <property name="xalign">0</property> + <property name="label">Weak</property> + <property name="selectable">True</property> + <property name="max-width-chars">50</property> + <property name="ellipsize">end</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">0</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="speed_heading_label"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="xalign">1</property> + <property name="label" translatable="yes">Link speed</property> + <property name="mnemonic_widget">speed_label</property> + <style> + <class name="dim-label"/> + </style> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">1</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="speed_label"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="xalign">0</property> + <property name="label">1Mb/sec</property> + <property name="selectable">True</property> + <property name="hexpand">True</property> + <property name="max-width-chars">50</property> + <property name="ellipsize">end</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">1</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="security_heading_label"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="xalign">1</property> + <property name="label" translatable="yes">Security</property> + <property name="mnemonic_widget">security_label</property> + <style> + <class name="dim-label"/> + </style> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">2</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="ipv4_heading_label"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="xalign">1</property> + <property name="label" translatable="yes">IPv4 Address</property> + <property name="mnemonic_widget">ipv4_label</property> + <style> + <class name="dim-label"/> + </style> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">3</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="ipv6_heading_label"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="xalign">1</property> + <property name="label" translatable="yes">IPv6 Address</property> + <property name="mnemonic_widget">ipv6_label</property> + <style> + <class name="dim-label"/> + </style> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">4</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="mac_heading_label"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="xalign">1</property> + <property name="label" translatable="yes">Hardware Address</property> + <property name="mnemonic_widget">mac_label</property> + <style> + <class name="dim-label"/> + </style> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">5</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="freq_heading_label"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="xalign">1</property> + <property name="label" translatable="yes">Supported Frequencies</property> + <property name="mnemonic_widget">freq_label</property> + <style> + <class name="dim-label"/> + </style> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">6</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="route_heading_label"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="xalign">1</property> + <property name="label" translatable="yes">Default Route</property> + <property name="mnemonic_widget">route_label</property> + <style> + <class name="dim-label"/> + </style> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">7</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="dns_heading_label"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="xalign">1</property> + <property name="yalign">0</property> + <property name="label" translatable="yes">DNS</property> + <property name="mnemonic_widget">dns_label</property> + <style> + <class name="dim-label"/> + </style> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">8</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="last_used_heading_label"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="xalign">1</property> + <property name="label" translatable="yes">Last Used</property> + <property name="mnemonic_widget">last_used_label</property> + <style> + <class name="dim-label"/> + </style> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">9</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="security_label"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="xalign">0</property> + <property name="label">WPA</property> + <property name="selectable">True</property> + <property name="hexpand">True</property> + <property name="max-width-chars">50</property> + <property name="ellipsize">end</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">2</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="ipv4_label"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="xalign">0</property> + <property name="label">127.0.0.1</property> + <property name="selectable">True</property> + <property name="hexpand">True</property> + <property name="max-width-chars">50</property> + <property name="ellipsize">end</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">3</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="ipv6_label"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="xalign">0</property> + <property name="label">::1</property> + <property name="selectable">True</property> + <property name="hexpand">True</property> + <property name="max-width-chars">50</property> + <property name="ellipsize">end</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">4</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="mac_label"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="xalign">0</property> + <property name="label">AA:BB:CC:DD:55:66:77:88</property> + <property name="selectable">True</property> + <property name="hexpand">True</property> + <property name="max-width-chars">50</property> + <property name="ellipsize">end</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">5</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="freq_label"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="xalign">0</property> + <property name="label">2.4 GHz / 5 GHz</property> + <property name="selectable">True</property> + <property name="hexpand">True</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">6</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="route_label"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="xalign">0</property> + <property name="label">127.0.0.1</property> + <property name="selectable">True</property> + <property name="hexpand">True</property> + <property name="max-width-chars">50</property> + <property name="ellipsize">end</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">7</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="dns_label"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="xalign">0</property> + <property name="yalign">0</property> + <property name="label">127.0.0.1</property> + <property name="wrap">True</property> + <property name="selectable">True</property> + <property name="hexpand">True</property> + <property name="max-width-chars">50</property> + <property name="ellipsize">end</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">8</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="last_used_label"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="xalign">0</property> + <property name="label">today</property> + <property name="selectable">True</property> + <property name="hexpand">True</property> + <property name="max-width-chars">50</property> + <property name="ellipsize">end</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">9</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkCheckButton" id="auto_connect_check"> + <property name="label" translatable="yes">Connect _automatically</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="valign">end</property> + <property name="use_underline">True</property> + <property name="xalign">0</property> + <property name="draw_indicator">True</property> + <property name="margin_top">12</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">10</property> + <property name="width">2</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkCheckButton" id="all_user_check"> + <property name="label" translatable="yes">Make available to _other users</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="use_underline">True</property> + <property name="xalign">0</property> + <property name="draw_indicator">True</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">11</property> + <property name="width">2</property> + <property name="height">1</property> + </packing> + </child> + + <!-- "Restrict Data Usage" section --> + <child> + <object class="GtkCheckButton" id="restrict_data_check"> + <property name="can_focus">True</property> + <property name="margin_bottom">12</property> + + <child> + <object class="GtkBox"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="orientation">vertical</property> + + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">_Metered connection: has data limits or can incur charges</property> + <property name="hexpand">True</property> + <property name="use_underline">True</property> + </object> + </child> + + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">Software updates and other large downloads will not be started automatically.</property> + <property name="wrap">True</property> + <property name="max_width_chars">60</property> + <style> + <class name="dim-label" /> + </style> + <attributes> + <attribute name="scale" value="0.8" /> + </attributes> + </object> + </child> + </object> + </child> + + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">13</property> + <property name="width">2</property> + <property name="height">1</property> + </packing> + </child> + + <child> + <object class="GtkButton" id="forget_button"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="use_underline">True</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="halign">end</property> + <property name="valign">end</property> + <style> + <class name="destructive-action" /> + </style> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">14</property> + <property name="width">2</property> + <property name="height">1</property> + </packing> + </child> + </template> +</interface> diff --git a/panels/network/connection-editor/ethernet-page.ui b/panels/network/connection-editor/ethernet-page.ui new file mode 100644 index 0000000..4f7331b --- /dev/null +++ b/panels/network/connection-editor/ethernet-page.ui @@ -0,0 +1,177 @@ +<?xml version="1.0" encoding="UTF-8"?> +<interface> + <!-- interface-requires gtk+ 3.0 --> + <object class="GtkAdjustment" id="mtu_adjustment"> + <property name="upper">10000</property> + <property name="step_increment">1</property> + <property name="page_increment">10</property> + </object> + <template class="CEPageEthernet" parent="GtkGrid"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="margin_start">50</property> + <property name="margin_end">50</property> + <property name="margin_top">12</property> + <property name="margin_bottom">12</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="row_spacing">10</property> + <property name="column_spacing">6</property> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="xalign">1</property> + <property name="label" translatable="yes">_Name</property> + <property name="use_underline">True</property> + <property name="mnemonic_widget">name_entry</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">0</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkEntry" id="name_entry"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="invisible_char">●</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">0</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="xalign">1</property> + <property name="label" translatable="yes">_MAC Address</property> + <property name="use_underline">True</property> + <property name="mnemonic_widget">mac_combo</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">1</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkComboBoxText" id="mac_combo"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="has_entry">True</property> + <property name="entry_text_column">0</property> + <property name="id_column">1</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">1</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkComboBoxText" id="cloned_mac_combo"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="has_entry">True</property> + <property name="hexpand">True</property> + <property name="active_id">0</property> + <child internal-child="entry"> + <object class="GtkEntry"> + <property name="can_focus">True</property> + </object> + </child> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">2</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="xalign">1</property> + <property name="label" translatable="yes">M_TU</property> + <property name="use_underline">True</property> + <property name="mnemonic_widget">mtu_spin</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">3</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="valign">center</property> + <property name="xalign">1</property> + <property name="label" translatable="yes">_Cloned Address</property> + <property name="use_underline">True</property> + <property name="mnemonic_widget">cloned_mac_combo</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">2</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="mtu_label"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">bytes</property> + </object> + <packing> + <property name="left_attach">2</property> + <property name="top_attach">3</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkSpinButton" id="mtu_spin"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="invisible_char">●</property> + <property name="invisible_char_set">True</property> + <property name="adjustment">mtu_adjustment</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">3</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <placeholder/> + </child> + <child> + <placeholder/> + </child> + <child> + <placeholder/> + </child> + <child> + <placeholder/> + </child> + <child> + <placeholder/> + </child> + </template> +</interface> diff --git a/panels/network/connection-editor/ip4-page.ui b/panels/network/connection-editor/ip4-page.ui new file mode 100644 index 0000000..60f9b30 --- /dev/null +++ b/panels/network/connection-editor/ip4-page.ui @@ -0,0 +1,442 @@ +<?xml version="1.0" encoding="UTF-8"?> +<interface> + <!-- interface-requires gtk+ 3.0 --> + <template class="CEPageIP4" parent="GtkScrolledWindow"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">never</property> + <child> + <object class="GtkViewport"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <child> + <object class="GtkGrid"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="margin_start">24</property> + <property name="margin_end">24</property> + <property name="margin_top">24</property> + <property name="margin_bottom">24</property> + <property name="orientation">vertical</property> + <property name="row-spacing">6</property> + <property name="column-spacing">6</property> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">IPv_4 Method</property> + <property name="use_underline">True</property> + <property name="mnemonic_widget">disabled_radio</property> + <property name="xalign">0.0</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="top-attach">0</property> + <property name="left-attach">0</property> + </packing> + </child> + <child> + <object class="GtkRadioButton" id="automatic_radio"> + <property name="label" translatable="yes">Automatic (DHCP)</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="draw_indicator">True</property> + </object> + <packing> + <property name="top-attach">0</property> + <property name="left-attach">1</property> + </packing> + </child> + <child> + <object class="GtkRadioButton" id="local_radio"> + <property name="label" translatable="yes">Link-Local Only</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="draw_indicator">True</property> + <property name="group">automatic_radio</property> + </object> + <packing> + <property name="top-attach">0</property> + <property name="left-attach">2</property> + </packing> + </child> + <child> + <object class="GtkRadioButton" id="manual_radio"> + <property name="label" translatable="yes">Manual</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="draw_indicator">True</property> + <property name="group">automatic_radio</property> + </object> + <packing> + <property name="top-attach">1</property> + <property name="left-attach">1</property> + </packing> + </child> + <child> + <object class="GtkRadioButton" id="disabled_radio"> + <property name="label" translatable="yes">Disable</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="draw_indicator">True</property> + <property name="group">automatic_radio</property> + </object> + <packing> + <property name="top-attach">1</property> + <property name="left-attach">2</property> + </packing> + </child> + <child> + <object class="GtkRadioButton" id="shared_radio"> + <property name="label" translatable="yes">Shared to other computers</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="draw_indicator">True</property> + <property name="group">automatic_radio</property> + </object> + <packing> + <property name="top-attach">2</property> + <property name="left-attach">1</property> + </packing> + </child> + <child> + <object class="GtkBox" id="content_box"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <property name="spacing">6</property> + <child> + <object class="GtkBox" id="address_box"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">Addresses</property> + <property name="margin_top">24</property> + <property name="margin_bottom">8</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + </child> + <child> + <object class="GtkBox"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="orientation">horizontal</property> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="label" translatable="yes">Address</property> + <style> + <class name="dim-label" /> + </style> + <attributes> + <attribute name="scale" value="0.8"/> + </attributes> + </object> + </child> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="label" translatable="yes">Netmask</property> + <style> + <class name="dim-label" /> + </style> + <attributes> + <attribute name="scale" value="0.8"/> + </attributes> + </object> + </child> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="label" translatable="yes">Gateway</property> + <style> + <class name="dim-label" /> + </style> + <attributes> + <attribute name="scale" value="0.8"/> + </attributes> + </object> + </child> + + <!-- This invisible box is used to add some width in the + end of the header row, assuming the space used by the + delete button in the rows --> + <child> + <object class="GtkBox" id="address_stub_box"> + <property name="visible">True</property> + <property name="can_focus">False</property> + </object> + </child> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkBox"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="margin_top">24</property> + <property name="spacing">6</property> + <child> + <object class="GtkLabel" id="dns_label"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">DNS</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + </child> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="xalign">1</property> + <property name="label" translatable="yes">Automatic</property> + </object> + </child> + <child> + <object class="GtkSwitch" id="auto_dns_switch"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="halign">end</property> + <property name="valign">center</property> + <child internal-child="accessible"> + <object class="AtkObject"> + <property name="accessible-name" translatable="yes">Automatic DNS</property> + </object> + </child> + </object> + </child> + </object> + <packing> + <property name="position">2</property> + </packing> + </child> + <child> + <object class="GtkEntry" id="dns_entry"> + <property name="visible">True</property> + <property name="can_focus">True</property> + </object> + <packing> + <property name="position">3</property> + </packing> + </child> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">Separate IP addresses with commas</property> + <style> + <class name="dim-label" /> + </style> + <attributes> + <attribute name="scale" value="0.8"/> + </attributes> + </object> + </child> + <child> + <object class="GtkBox"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="margin_top">24</property> + <property name="spacing">6</property> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="xalign">0</property> + <property name="hexpand">True</property> + <property name="label" translatable="yes">Routes</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + </child> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="xalign">1</property> + <property name="label" translatable="yes">Automatic</property> + </object> + </child> + <child> + <object class="GtkSwitch" id="auto_routes_switch"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="halign">end</property> + <property name="valign">center</property> + <child internal-child="accessible"> + <object class="AtkObject"> + <property name="accessible-name" translatable="yes">Automatic Routes</property> + </object> + </child> + </object> + </child> + </object> + <packing> + <property name="position">5</property> + </packing> + </child> + <child> + <object class="GtkBox" id="routes_box"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkBox"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="orientation">horizontal</property> + <child> + <object class="GtkLabel" id="routes_address_label"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="label" translatable="yes">Address</property> + <style> + <class name="dim-label" /> + </style> + <attributes> + <attribute name="scale" value="0.8"/> + </attributes> + </object> + </child> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="label" translatable="yes">Netmask</property> + <style> + <class name="dim-label" /> + </style> + <attributes> + <attribute name="scale" value="0.8"/> + </attributes> + </object> + </child> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="label" translatable="yes">Gateway</property> + <style> + <class name="dim-label" /> + </style> + <attributes> + <attribute name="scale" value="0.8"/> + </attributes> + </object> + </child> + <child> + <object class="GtkLabel" id="routes_metric_label"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" comments="Translators: Please see https://en.wikipedia.org/wiki/Metrics_(networking)">Metric</property> + <style> + <class name="dim-label" /> + </style> + <attributes> + <attribute name="scale" value="0.8"/> + </attributes> + </object> + </child> + + <!-- This invisible box is used to add some width in the + end of the header row, assuming the space used by the + delete button in the rows --> + <child> + <object class="GtkBox" id="routes_stub_box"> + <property name="visible">True</property> + <property name="can_focus">False</property> + </object> + </child> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">6</property> + </packing> + </child> + <child> + <object class="GtkCheckButton" id="never_default_check"> + <property name="label" translatable="yes">Use this connection _only for resources on its network</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="use_underline">True</property> + <property name="xalign">0</property> + <property name="draw_indicator">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">7</property> + </packing> + </child> + </object> + <packing> + <property name="top-attach">3</property> + <property name="left-attach">0</property> + <property name="width">3</property> + </packing> + </child> + </object> + </child> + </object> + </child> + </template> + <object class="GtkSizeGroup" id="routes_metric_sizegroup"> + <property name="mode">horizontal</property> + <widgets> + <widget name="routes_metric_label" /> + </widgets> + </object> + <object class="GtkSizeGroup" id="routes_sizegroup"> + <property name="mode">horizontal</property> + <widgets> + <widget name="routes_stub_box" /> + </widgets> + </object> + <object class="GtkSizeGroup" id="address_sizegroup"> + <property name="mode">horizontal</property> + <widgets> + <widget name="address_stub_box" /> + </widgets> + </object> +</interface> diff --git a/panels/network/connection-editor/ip6-page.ui b/panels/network/connection-editor/ip6-page.ui new file mode 100644 index 0000000..ed3f211 --- /dev/null +++ b/panels/network/connection-editor/ip6-page.ui @@ -0,0 +1,456 @@ +<?xml version="1.0" encoding="UTF-8"?> +<interface> + <!-- interface-requires gtk+ 3.0 --> + <template class="CEPageIP6" parent="GtkScrolledWindow"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">never</property> + <child> + <object class="GtkViewport"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <child> + <object class="GtkGrid"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="margin_start">24</property> + <property name="margin_end">24</property> + <property name="margin_top">24</property> + <property name="margin_bottom">24</property> + <property name="orientation">vertical</property> + <property name="row-spacing">6</property> + <property name="column-spacing">6</property> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">IPv_6 Method</property> + <property name="use_underline">True</property> + <property name="mnemonic_widget">disabled_radio</property> + <property name="xalign">0.0</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="top-attach">0</property> + <property name="left-attach">0</property> + </packing> + </child> + <child> + <object class="GtkRadioButton" id="automatic_radio"> + <property name="label" translatable="yes">Automatic</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="draw_indicator">True</property> + </object> + <packing> + <property name="top-attach">0</property> + <property name="left-attach">1</property> + </packing> + </child> + <child> + <object class="GtkRadioButton" id="dhcp_radio"> + <property name="label" translatable="yes">Automatic, DHCP only</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="draw_indicator">True</property> + <property name="group">automatic_radio</property> + </object> + <packing> + <property name="top-attach">0</property> + <property name="left-attach">2</property> + </packing> + </child> + <child> + <object class="GtkRadioButton" id="local_radio"> + <property name="label" translatable="yes">Link-Local Only</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="draw_indicator">True</property> + <property name="group">automatic_radio</property> + </object> + <packing> + <property name="top-attach">1</property> + <property name="left-attach">1</property> + </packing> + </child> + <child> + <object class="GtkRadioButton" id="manual_radio"> + <property name="label" translatable="yes">Manual</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="draw_indicator">True</property> + <property name="group">automatic_radio</property> + </object> + <packing> + <property name="top-attach">1</property> + <property name="left-attach">2</property> + </packing> + </child> + <child> + <object class="GtkRadioButton" id="disabled_radio"> + <property name="label" translatable="yes">Disable</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="draw_indicator">True</property> + <property name="group">automatic_radio</property> + </object> + <packing> + <property name="top-attach">2</property> + <property name="left-attach">1</property> + </packing> + </child> + <child> + <object class="GtkRadioButton" id="shared_radio"> + <property name="label" translatable="yes">Shared to other computers</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="draw_indicator">True</property> + <property name="group">automatic_radio</property> + </object> + <packing> + <property name="top-attach">2</property> + <property name="left-attach">2</property> + </packing> + </child> + <child> + <object class="GtkBox" id="content_box"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <property name="spacing">6</property> + <child> + <object class="GtkBox" id="address_box"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">Addresses</property> + <property name="margin_top">24</property> + <property name="margin_bottom">8</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + </child> + <child> + <object class="GtkBox"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="orientation">horizontal</property> + <child> + <object class="GtkLabel" id="address_address_label"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="label" translatable="yes">Address</property> + <style> + <class name="dim-label" /> + </style> + <attributes> + <attribute name="scale" value="0.8"/> + </attributes> + </object> + </child> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="label" translatable="yes">Prefix</property> + <style> + <class name="dim-label" /> + </style> + <attributes> + <attribute name="scale" value="0.8"/> + </attributes> + </object> + </child> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="label" translatable="yes">Gateway</property> + <style> + <class name="dim-label" /> + </style> + <attributes> + <attribute name="scale" value="0.8"/> + </attributes> + </object> + </child> + + <!-- This invisible box is used to add some width in the + end of the header row, assuming the space used by the + delete button in the rows --> + <child> + <object class="GtkBox" id="address_stub_box"> + <property name="visible">True</property> + <property name="can_focus">False</property> + </object> + </child> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkBox"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="margin_top">24</property> + <property name="spacing">6</property> + <child> + <object class="GtkLabel" id="dns_label"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">DNS</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + </child> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="xalign">1</property> + <property name="label" translatable="yes">Automatic</property> + </object> + </child> + <child> + <object class="GtkSwitch" id="auto_dns_switch"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="halign">end</property> + <property name="valign">center</property> + <child internal-child="accessible"> + <object class="AtkObject"> + <property name="accessible-name" translatable="yes">Automatic DNS</property> + </object> + </child> + </object> + </child> + </object> + <packing> + <property name="position">2</property> + </packing> + </child> + <child> + <object class="GtkEntry" id="dns_entry"> + <property name="visible">True</property> + <property name="can_focus">True</property> + </object> + <packing> + <property name="position">3</property> + </packing> + </child> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">Separate IP addresses with commas</property> + <style> + <class name="dim-label" /> + </style> + <attributes> + <attribute name="scale" value="0.8"/> + </attributes> + </object> + </child> + <child> + <object class="GtkBox"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="margin_top">24</property> + <property name="spacing">6</property> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="xalign">0</property> + <property name="hexpand">True</property> + <property name="label" translatable="yes">Routes</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + </child> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="xalign">1</property> + <property name="label" translatable="yes">Automatic</property> + </object> + </child> + <child> + <object class="GtkSwitch" id="auto_routes_switch"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="halign">end</property> + <property name="valign">center</property> + <child internal-child="accessible"> + <object class="AtkObject"> + <property name="accessible-name" translatable="yes">Automatic Routes</property> + </object> + </child> + </object> + </child> + </object> + <packing> + <property name="position">5</property> + </packing> + </child> + <child> + <object class="GtkBox" id="routes_box"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkBox"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="orientation">horizontal</property> + <child> + <object class="GtkLabel" id="routes_address_label"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="label" translatable="yes">Address</property> + <style> + <class name="dim-label" /> + </style> + <attributes> + <attribute name="scale" value="0.8"/> + </attributes> + </object> + </child> + <child> + <object class="GtkLabel" id="routes_prefix_label"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="label" translatable="yes">Prefix</property> + <style> + <class name="dim-label" /> + </style> + <attributes> + <attribute name="scale" value="0.8"/> + </attributes> + </object> + </child> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="label" translatable="yes">Gateway</property> + <style> + <class name="dim-label" /> + </style> + <attributes> + <attribute name="scale" value="0.8"/> + </attributes> + </object> + </child> + <child> + <object class="GtkLabel" id="routes_metric_label"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes" comments="Translators: Please see https://en.wikipedia.org/wiki/Metrics_(networking)">Metric</property> + <style> + <class name="dim-label" /> + </style> + <attributes> + <attribute name="scale" value="0.8"/> + </attributes> + </object> + </child> + + <!-- This invisible box is used to add some width in the + end of the header row, assuming the space used by the + delete button in the rows --> + <child> + <object class="GtkBox" id="routes_stub_box"> + <property name="visible">True</property> + <property name="can_focus">False</property> + </object> + </child> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">6</property> + </packing> + </child> + <child> + <object class="GtkCheckButton" id="never_default_check"> + <property name="label" translatable="yes">Use this connection _only for resources on its network</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="use_underline">True</property> + <property name="xalign">0</property> + <property name="draw_indicator">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">7</property> + </packing> + </child> + </object> + <packing> + <property name="top-attach">3</property> + <property name="left-attach">0</property> + <property name="width">3</property> + </packing> + </child> + </object> + </child> + </object> + </child> + </template> + <object class="GtkSizeGroup" id="routes_metric_sizegroup"> + <property name="mode">horizontal</property> + <widgets> + <widget name="routes_metric_label" /> + </widgets> + </object> + <object class="GtkSizeGroup" id="routes_sizegroup"> + <property name="mode">horizontal</property> + <widgets> + <widget name="routes_stub_box" /> + </widgets> + </object> + <object class="GtkSizeGroup" id="address_sizegroup"> + <property name="mode">horizontal</property> + <widgets> + <widget name="address_stub_box" /> + </widgets> + </object> +</interface> diff --git a/panels/network/connection-editor/meson.build b/panels/network/connection-editor/meson.build new file mode 100644 index 0000000..856833f --- /dev/null +++ b/panels/network/connection-editor/meson.build @@ -0,0 +1,46 @@ +name = 'connection-editor' + +sources = files( + 'ce-page-8021x-security.c', + 'ce-page-details.c', + 'ce-page-ethernet.c', + 'ce-page-ip4.c', + 'ce-page-ip6.c', + 'ce-page-security.c', + 'ce-page-vpn.c', + 'ce-page-wifi.c', + 'ce-page.c', + 'net-connection-editor.c', + 'vpn-helpers.c' +) + +resource_data = files( + '8021x-security-page.ui', + 'connection-editor.ui', + 'details-page.ui', + 'ethernet-page.ui', + 'ip4-page.ui', + 'ip6-page.ui', + 'security-page.ui', + 'vpn-page.ui', + 'wifi-page.ui' +) + +c_name = 'net-' + name + +sources += gnome.compile_resources( + c_name + '-resources', + name + '.gresource.xml', + c_name: c_name.underscorify(), + dependencies: resource_data, + export: true +) + +libconnection_editor = static_library( + name, + sources: sources, + include_directories: [top_inc, common_inc, network_inc, wireless_security_inc], + dependencies: deps, + c_args: cflags, + link_with: libwireless_security +) diff --git a/panels/network/connection-editor/net-connection-editor.c b/panels/network/connection-editor/net-connection-editor.c new file mode 100644 index 0000000..b231caf --- /dev/null +++ b/panels/network/connection-editor/net-connection-editor.c @@ -0,0 +1,868 @@ +/* -*- 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 <glib-object.h> +#include <glib/gi18n.h> + +#include <NetworkManager.h> + +#include "list-box-helper.h" +#include "net-connection-editor.h" +#include "net-connection-editor-resources.h" +#include "ce-page.h" +#include "ce-page-details.h" +#include "ce-page-wifi.h" +#include "ce-page-ip4.h" +#include "ce-page-ip6.h" +#include "ce-page-security.h" +#include "ce-page-ethernet.h" +#include "ce-page-8021x-security.h" +#include "ce-page-vpn.h" +#include "vpn-helpers.h" +#include "eap-method.h" + +enum { + DONE, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +struct _NetConnectionEditor +{ + GtkDialog parent; + + GtkBox *add_connection_box; + GtkFrame *add_connection_frame; + GtkButton *apply_button; + GtkButton *cancel_button; + GtkNotebook *notebook; + GtkStack *toplevel_stack; + + GtkWidget *parent_window; + NMClient *client; + NMDevice *device; + + NMConnection *connection; + NMConnection *orig_connection; + gboolean is_new_connection; + gboolean is_changed; + NMAccessPoint *ap; + + GSList *initializing_pages; + GSList *pages; + + NMClientPermissionResult can_modify; + + gboolean title_set; + gboolean show_when_initialized; +}; + +G_DEFINE_TYPE (NetConnectionEditor, net_connection_editor, GTK_TYPE_DIALOG) + +static void page_changed (NetConnectionEditor *self); + +static void +cancel_editing (NetConnectionEditor *self) +{ + gtk_widget_hide (GTK_WIDGET (self)); + g_signal_emit (self, signals[DONE], 0, FALSE); +} + +static void +delete_event_cb (NetConnectionEditor *self) +{ + cancel_editing (self); +} + +static void +cancel_clicked_cb (NetConnectionEditor *self) +{ + cancel_editing (self); +} + +static void +update_connection (NetConnectionEditor *self) +{ + g_autoptr(GVariant) settings = NULL; + + settings = nm_connection_to_dbus (self->connection, NM_CONNECTION_SERIALIZE_ALL); + nm_connection_replace_settings (self->orig_connection, settings, NULL); +} + +static void +update_complete (NetConnectionEditor *self, + gboolean success) +{ + gtk_widget_hide (GTK_WIDGET (self)); + g_signal_emit (self, signals[DONE], 0, success); +} + +static void +updated_connection_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + NetConnectionEditor *self; + g_autoptr(GError) error = NULL; + gboolean success = TRUE; + + if (!nm_remote_connection_commit_changes_finish (NM_REMOTE_CONNECTION (source_object), + res, &error)) { + g_warning ("Failed to commit changes: %s", error->message); + success = FALSE; + //return; FIXME return if cancelled + } + + nm_connection_clear_secrets (NM_CONNECTION (source_object)); + + self = user_data; + update_complete (self, success); +} + +static void +added_connection_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + NetConnectionEditor *self; + g_autoptr(GError) error = NULL; + gboolean success = TRUE; + + if (!nm_client_add_connection_finish (NM_CLIENT (source_object), res, &error)) { + g_warning ("Failed to add connection: %s", error->message); + success = FALSE; + /* Leave the editor open */ + // return; FIXME return if cancelled + } + + self = user_data; + update_complete (self, success); +} + +static void +apply_clicked_cb (NetConnectionEditor *self) +{ + update_connection (self); + + eap_method_ca_cert_ignore_save (self->connection); + + if (self->is_new_connection) { + nm_client_add_connection_async (self->client, + self->orig_connection, + TRUE, + NULL, + added_connection_cb, + self); + } else { + nm_remote_connection_commit_changes_async (NM_REMOTE_CONNECTION (self->orig_connection), + TRUE, + NULL, + updated_connection_cb, self); + } +} + +static void +net_connection_editor_init (NetConnectionEditor *self) +{ + gtk_widget_init_template (GTK_WIDGET (self)); +} + +static void +net_connection_editor_finalize (GObject *object) +{ + NetConnectionEditor *self = NET_CONNECTION_EDITOR (object); + GSList *l; + + for (l = self->pages; l != NULL; l = l->next) + g_signal_handlers_disconnect_by_func (l->data, page_changed, self); + + g_clear_object (&self->connection); + g_clear_object (&self->orig_connection); + g_clear_object (&self->parent_window); + g_clear_object (&self->device); + g_clear_object (&self->client); + g_clear_object (&self->ap); + + G_OBJECT_CLASS (net_connection_editor_parent_class)->finalize (object); +} + +static void +net_connection_editor_class_init (NetConnectionEditorClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class); + + g_resources_register (net_connection_editor_get_resource ()); + + object_class->finalize = net_connection_editor_finalize; + + signals[DONE] = g_signal_new ("done", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, NULL, + NULL, + G_TYPE_NONE, 1, G_TYPE_BOOLEAN); + + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/network/connection-editor.ui"); + + gtk_widget_class_bind_template_child (widget_class, NetConnectionEditor, add_connection_box); + gtk_widget_class_bind_template_child (widget_class, NetConnectionEditor, add_connection_frame); + gtk_widget_class_bind_template_child (widget_class, NetConnectionEditor, apply_button); + gtk_widget_class_bind_template_child (widget_class, NetConnectionEditor, cancel_button); + gtk_widget_class_bind_template_child (widget_class, NetConnectionEditor, notebook); + gtk_widget_class_bind_template_child (widget_class, NetConnectionEditor, toplevel_stack); + + gtk_widget_class_bind_template_callback (widget_class, delete_event_cb); + gtk_widget_class_bind_template_callback (widget_class, cancel_clicked_cb); + gtk_widget_class_bind_template_callback (widget_class, apply_clicked_cb); +} + +static void +net_connection_editor_error_dialog (NetConnectionEditor *self, + const char *primary_text, + const char *secondary_text) +{ + GtkWidget *dialog; + GtkWindow *parent; + + if (gtk_widget_is_visible (GTK_WIDGET (self))) + parent = GTK_WINDOW (self); + else + parent = GTK_WINDOW (self->parent_window); + + dialog = gtk_message_dialog_new (parent, + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + "%s", primary_text); + + if (secondary_text) { + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + "%s", secondary_text); + } + + g_signal_connect (dialog, "delete-event", G_CALLBACK (gtk_widget_destroy), NULL); + g_signal_connect (dialog, "response", G_CALLBACK (gtk_widget_destroy), NULL); + gtk_dialog_run (GTK_DIALOG (dialog)); +} + +static void +net_connection_editor_do_fallback (NetConnectionEditor *self, const gchar *type) +{ + g_autofree gchar *cmdline = NULL; + g_autoptr(GError) error = NULL; + + if (self->is_new_connection) { + cmdline = g_strdup_printf ("nm-connection-editor --type='%s' --create", type); + } else { + cmdline = g_strdup_printf ("nm-connection-editor --edit='%s'", + nm_connection_get_uuid (self->connection)); + } + + g_spawn_command_line_async (cmdline, &error); + + if (error) + net_connection_editor_error_dialog (self, + _("Unable to open connection editor"), + error->message); + + g_signal_emit (self, signals[DONE], 0, FALSE); +} + +static void +net_connection_editor_update_title (NetConnectionEditor *self) +{ + g_autofree gchar *id = NULL; + + if (self->title_set) + return; + + if (self->is_new_connection) { + if (self->device) { + id = g_strdup (_("New Profile")); + } else { + /* Leave it set to "Add New Connection" */ + return; + } + } else { + NMSettingWireless *sw; + sw = nm_connection_get_setting_wireless (self->connection); + if (sw) { + GBytes *ssid; + ssid = nm_setting_wireless_get_ssid (sw); + id = nm_utils_ssid_to_utf8 (g_bytes_get_data (ssid, NULL), g_bytes_get_size (ssid)); + } else { + id = g_strdup (nm_connection_get_id (self->connection)); + } + } + gtk_window_set_title (GTK_WINDOW (self), id); +} + +static gboolean +editor_is_initialized (NetConnectionEditor *self) +{ + return self->initializing_pages == NULL; +} + +static void +update_sensitivity (NetConnectionEditor *self) +{ + NMSettingConnection *sc; + gboolean sensitive; + GSList *l; + + if (!editor_is_initialized (self)) + return; + + sc = nm_connection_get_setting_connection (self->connection); + + if (nm_setting_connection_get_read_only (sc)) { + sensitive = FALSE; + } else { + sensitive = self->can_modify; + } + + for (l = self->pages; l; l = l->next) + gtk_widget_set_sensitive (GTK_WIDGET (l->data), sensitive); +} + +static void +validate (NetConnectionEditor *self) +{ + gboolean valid = FALSE; + GSList *l; + + if (!editor_is_initialized (self)) + goto done; + + valid = TRUE; + for (l = self->pages; l; l = l->next) { + g_autoptr(GError) error = NULL; + + if (!ce_page_validate (CE_PAGE (l->data), self->connection, &error)) { + valid = FALSE; + if (error) { + g_debug ("Invalid setting %s: %s", ce_page_get_title (CE_PAGE (l->data)), error->message); + } else { + g_debug ("Invalid setting %s", ce_page_get_title (CE_PAGE (l->data))); + } + } + } + + update_sensitivity (self); +done: + gtk_widget_set_sensitive (GTK_WIDGET (self->apply_button), valid && self->is_changed); +} + +static void +page_changed (NetConnectionEditor *self) +{ + if (editor_is_initialized (self)) + self->is_changed = TRUE; + validate (self); +} + +static gboolean +idle_validate (gpointer user_data) +{ + validate (NET_CONNECTION_EDITOR (user_data)); + + return G_SOURCE_REMOVE; +} + +static void +recheck_initialization (NetConnectionEditor *self) +{ + if (!editor_is_initialized (self)) + return; + + gtk_notebook_set_current_page (self->notebook, 0); + + if (self->show_when_initialized) + gtk_window_present (GTK_WINDOW (self)); + + g_idle_add (idle_validate, self); +} + +static void +page_initialized (NetConnectionEditor *self, GError *error, CEPage *page) +{ + GtkWidget *label; + gint position; + GList *children, *l; + gint i; + + position = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (page), "position")); + g_object_set_data (G_OBJECT (page), "position", GINT_TO_POINTER (position)); + children = gtk_container_get_children (GTK_CONTAINER (self->notebook)); + for (l = children, i = 0; l; l = l->next, i++) { + gint pos = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (l->data), "position")); + if (pos > position) + break; + } + g_list_free (children); + + label = gtk_label_new (ce_page_get_title (page)); + + gtk_notebook_insert_page (self->notebook, GTK_WIDGET (page), label, i); + + self->initializing_pages = g_slist_remove (self->initializing_pages, page); + self->pages = g_slist_append (self->pages, page); + + recheck_initialization (self); +} + +typedef struct { + NetConnectionEditor *editor; + CEPage *page; + const gchar *setting_name; +} GetSecretsInfo; + +static void +get_secrets_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + NMRemoteConnection *connection; + g_autofree GetSecretsInfo *info = user_data; + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) variant = NULL; + + connection = NM_REMOTE_CONNECTION (source_object); + variant = nm_remote_connection_get_secrets_finish (connection, res, &error); + + if (!variant && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + ce_page_complete_init (info->page, info->editor->connection, info->setting_name, variant, g_steal_pointer (&error)); +} + +static void +get_secrets_for_page (NetConnectionEditor *self, + CEPage *page, + const gchar *setting_name) +{ + GetSecretsInfo *info; + + info = g_new0 (GetSecretsInfo, 1); + info->editor = self; + info->page = page; + info->setting_name = setting_name; + + nm_remote_connection_get_secrets_async (NM_REMOTE_CONNECTION (self->orig_connection), + setting_name, + NULL, //FIXME + get_secrets_cb, + info); +} + +static void +add_page (NetConnectionEditor *self, CEPage *page) +{ + gint position; + + position = g_slist_length (self->initializing_pages); + g_object_set_data (G_OBJECT (page), "position", GINT_TO_POINTER (position)); + + self->initializing_pages = g_slist_append (self->initializing_pages, page); + + g_signal_connect_object (page, "changed", G_CALLBACK (page_changed), self, G_CONNECT_SWAPPED); + g_signal_connect_object (page, "initialized", G_CALLBACK (page_initialized), self, G_CONNECT_SWAPPED); +} + +static void +net_connection_editor_set_connection (NetConnectionEditor *self, + NMConnection *connection) +{ + GSList *pages, *l; + NMSettingConnection *sc; + const gchar *type; + gboolean is_wired; + gboolean is_wifi; + gboolean is_vpn; + + self->is_new_connection = !nm_client_get_connection_by_uuid (self->client, + nm_connection_get_uuid (connection)); + + if (self->is_new_connection) { + gtk_button_set_label (self->apply_button, _("_Add")); + self->is_changed = TRUE; + } + + self->connection = nm_simple_connection_new_clone (connection); + self->orig_connection = g_object_ref (connection); + + net_connection_editor_update_title (self); + + eap_method_ca_cert_ignore_load (self->connection); + + sc = nm_connection_get_setting_connection (connection); + type = nm_setting_connection_get_connection_type (sc); + + is_wired = g_str_equal (type, NM_SETTING_WIRED_SETTING_NAME); + is_wifi = g_str_equal (type, NM_SETTING_WIRELESS_SETTING_NAME); + is_vpn = g_str_equal (type, NM_SETTING_VPN_SETTING_NAME); + + if (!self->is_new_connection) + add_page (self, CE_PAGE (ce_page_details_new (self->connection, self->device, self->ap, self))); + + if (is_wifi) + add_page (self, CE_PAGE (ce_page_wifi_new (self->connection, self->client))); + else if (is_wired) + add_page (self, CE_PAGE (ce_page_ethernet_new (self->connection, self->client))); + else if (is_vpn) + add_page (self, CE_PAGE (ce_page_vpn_new (self->connection))); + else { + /* Unsupported type */ + net_connection_editor_do_fallback (self, type); + return; + } + + add_page (self, CE_PAGE (ce_page_ip4_new (self->connection, self->client))); + add_page (self, CE_PAGE (ce_page_ip6_new (self->connection, self->client))); + + if (is_wifi) + add_page (self, CE_PAGE (ce_page_security_new (self->connection))); + else if (is_wired) + add_page (self, CE_PAGE (ce_page_8021x_security_new (self->connection))); + + pages = g_slist_copy (self->initializing_pages); + for (l = pages; l; l = l->next) { + CEPage *page = l->data; + const gchar *security_setting; + + security_setting = ce_page_get_security_setting (page); + if (!security_setting || self->is_new_connection) { + ce_page_complete_init (page, NULL, NULL, NULL, NULL); + } else { + get_secrets_for_page (self, page, security_setting); + } + } + g_slist_free (pages); +} + +static NMConnection * +complete_vpn_connection (NetConnectionEditor *self, NMConnection *connection) +{ + NMSettingConnection *s_con; + NMSetting *s_type; + + if (!connection) + connection = nm_simple_connection_new (); + + s_con = nm_connection_get_setting_connection (connection); + if (!s_con) { + s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ()); + nm_connection_add_setting (connection, NM_SETTING (s_con)); + } + + if (!nm_setting_connection_get_uuid (s_con)) { + g_autofree gchar *uuid = nm_utils_uuid_generate (); + g_object_set (s_con, + NM_SETTING_CONNECTION_UUID, uuid, + NULL); + } + + if (!nm_setting_connection_get_id (s_con)) { + const GPtrArray *connections; + g_autofree gchar *id = NULL; + + connections = nm_client_get_connections (self->client); + id = ce_page_get_next_available_name (connections, NAME_FORMAT_TYPE, _("VPN")); + g_object_set (s_con, + NM_SETTING_CONNECTION_ID, id, + NULL); + } + + s_type = nm_connection_get_setting (connection, NM_TYPE_SETTING_VPN); + if (!s_type) { + s_type = g_object_new (NM_TYPE_SETTING_VPN, NULL); + nm_connection_add_setting (connection, s_type); + } + + if (!nm_setting_connection_get_connection_type (s_con)) { + g_object_set (s_con, + NM_SETTING_CONNECTION_TYPE, nm_setting_get_name (s_type), + NULL); + } + + return connection; +} + +static void +finish_add_connection (NetConnectionEditor *self, NMConnection *connection) +{ + GtkBin *frame; + + frame = GTK_BIN (self->add_connection_frame); + gtk_widget_destroy (gtk_bin_get_child (frame)); + + gtk_stack_set_visible_child (self->toplevel_stack, GTK_WIDGET (self->notebook)); + gtk_widget_show (GTK_WIDGET (self->apply_button)); + + if (connection) + net_connection_editor_set_connection (self, connection); +} + +static void +vpn_import_complete (NMConnection *connection, gpointer user_data) +{ + NetConnectionEditor *self = user_data; + + if (!connection) { + /* The import code shows its own error dialogs. */ + g_signal_emit (self, signals[DONE], 0, FALSE); + return; + } + + complete_vpn_connection (self, connection); + finish_add_connection (self, connection); +} + +static void +vpn_type_activated (NetConnectionEditor *self, GtkWidget *row) +{ + const char *service_name = g_object_get_data (G_OBJECT (row), "service_name"); + NMConnection *connection; + NMSettingVpn *s_vpn; + NMSettingConnection *s_con; + + if (!strcmp (service_name, "import")) { + vpn_import (GTK_WINDOW (self), vpn_import_complete, self); + return; + } + + connection = complete_vpn_connection (self, NULL); + s_vpn = nm_connection_get_setting_vpn (connection); + g_object_set (s_vpn, NM_SETTING_VPN_SERVICE_TYPE, service_name, NULL); + + /* Mark the connection as private to this user, and non-autoconnect */ + s_con = nm_connection_get_setting_connection (connection); + g_object_set (s_con, NM_SETTING_CONNECTION_AUTOCONNECT, FALSE, NULL); + nm_setting_connection_add_permission (s_con, "user", g_get_user_name (), NULL); + + finish_add_connection (self, connection); +} + +static void +select_vpn_type (NetConnectionEditor *self, GtkListBox *list) +{ + GSList *vpn_plugins, *iter; + GList *l; + GList *children; + GtkWidget *row, *row_box; + GtkWidget *name_label, *desc_label; + + /* Get the available VPN types */ + vpn_plugins = vpn_get_plugins (); + + /* Remove the previous menu contents */ + children = gtk_container_get_children (GTK_CONTAINER (list)); + for (l = children; l != NULL; l = l->next) + gtk_widget_destroy (l->data); + + /* Add the VPN types */ + for (iter = vpn_plugins; iter; iter = iter->next) { + NMVpnEditorPlugin *plugin = nm_vpn_plugin_info_get_editor_plugin (iter->data); + g_autofree gchar *name = NULL; + g_autofree gchar *desc = NULL; + g_autofree gchar *desc_markup = NULL; + g_autofree gchar *service_name = NULL; + GtkStyleContext *context; + + g_object_get (plugin, + NM_VPN_EDITOR_PLUGIN_NAME, &name, + NM_VPN_EDITOR_PLUGIN_DESCRIPTION, &desc, + NM_VPN_EDITOR_PLUGIN_SERVICE, &service_name, + NULL); + desc_markup = g_markup_printf_escaped ("<span size='smaller'>%s</span>", desc); + + row = gtk_list_box_row_new (); + + row_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); + gtk_widget_set_margin_start (row_box, 12); + gtk_widget_set_margin_end (row_box, 12); + gtk_widget_set_margin_top (row_box, 12); + gtk_widget_set_margin_bottom (row_box, 12); + + name_label = gtk_label_new (name); + gtk_widget_set_halign (name_label, GTK_ALIGN_START); + gtk_box_pack_start (GTK_BOX (row_box), name_label, FALSE, TRUE, 0); + + desc_label = gtk_label_new (NULL); + gtk_label_set_markup (GTK_LABEL (desc_label), desc_markup); + gtk_label_set_line_wrap (GTK_LABEL (desc_label), TRUE); + gtk_widget_set_halign (desc_label, GTK_ALIGN_START); + context = gtk_widget_get_style_context (desc_label); + gtk_style_context_add_class (context, "dim-label"); + gtk_box_pack_start (GTK_BOX (row_box), desc_label, FALSE, TRUE, 0); + + gtk_container_add (GTK_CONTAINER (row), row_box); + gtk_widget_show_all (row); + g_object_set_data_full (G_OBJECT (row), "service_name", g_steal_pointer (&service_name), g_free); + gtk_container_add (GTK_CONTAINER (list), row); + } + + /* Import */ + row = gtk_list_box_row_new (); + + row_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); + gtk_widget_set_margin_start (row_box, 12); + gtk_widget_set_margin_end (row_box, 12); + gtk_widget_set_margin_top (row_box, 12); + gtk_widget_set_margin_bottom (row_box, 12); + + name_label = gtk_label_new (_("Import from file…")); + gtk_widget_set_halign (name_label, GTK_ALIGN_START); + gtk_box_pack_start (GTK_BOX (row_box), name_label, FALSE, TRUE, 0); + + gtk_container_add (GTK_CONTAINER (row), row_box); + gtk_widget_show_all (row); + g_object_set_data (G_OBJECT (row), "service_name", "import"); + gtk_container_add (GTK_CONTAINER (list), row); + + g_signal_connect_object (list, "row-activated", + G_CALLBACK (vpn_type_activated), self, G_CONNECT_SWAPPED); +} + +static void +net_connection_editor_add_connection (NetConnectionEditor *self) +{ + GtkContainer *frame; + GtkListBox *list; + + frame = GTK_CONTAINER (self->add_connection_frame); + + list = GTK_LIST_BOX (gtk_list_box_new ()); + gtk_list_box_set_selection_mode (list, GTK_SELECTION_NONE); + gtk_list_box_set_header_func (list, cc_list_box_update_header_func, NULL, NULL); + + select_vpn_type (self, list); + + gtk_widget_show_all (GTK_WIDGET (list)); + gtk_container_add (frame, GTK_WIDGET (list)); + + gtk_stack_set_visible_child (self->toplevel_stack, GTK_WIDGET (self->add_connection_box)); + gtk_widget_hide (GTK_WIDGET (self->apply_button)); + gtk_window_set_title (GTK_WINDOW (self), _("Add VPN")); +} + +static void +permission_changed (NetConnectionEditor *self, + NMClientPermission permission, + NMClientPermissionResult result) +{ + if (permission != NM_CLIENT_PERMISSION_SETTINGS_MODIFY_SYSTEM) + return; + + if (result == NM_CLIENT_PERMISSION_RESULT_YES || result == NM_CLIENT_PERMISSION_RESULT_AUTH) + self->can_modify = TRUE; + else + self->can_modify = FALSE; + + validate (self); +} + +NetConnectionEditor * +net_connection_editor_new (GtkWindow *parent_window, + NMConnection *connection, + NMDevice *device, + NMAccessPoint *ap, + NMClient *client) +{ + NetConnectionEditor *self; + + self = g_object_new (net_connection_editor_get_type (), + /* This doesn't seem to work for a template, so it is also hardcoded. */ + "use-header-bar", 1, + NULL); + + if (parent_window) { + self->parent_window = GTK_WIDGET (g_object_ref (parent_window)); + gtk_window_set_transient_for (GTK_WINDOW (self), + parent_window); + } + if (ap) + self->ap = g_object_ref (ap); + if (device) + self->device = g_object_ref (device); + self->client = g_object_ref (client); + + self->can_modify = nm_client_get_permission_result (client, NM_CLIENT_PERMISSION_SETTINGS_MODIFY_SYSTEM); + g_signal_connect_object (self->client, "permission-changed", + G_CALLBACK (permission_changed), self, G_CONNECT_SWAPPED); + + if (connection) + net_connection_editor_set_connection (self, connection); + else + net_connection_editor_add_connection (self); + + return self; +} + +void +net_connection_editor_run (NetConnectionEditor *self) +{ + if (!editor_is_initialized (self)) { + self->show_when_initialized = TRUE; + return; + } + gtk_window_present (GTK_WINDOW (self)); +} + +static void +forgotten_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + NMRemoteConnection *connection = NM_REMOTE_CONNECTION (source_object); + NetConnectionEditor *self = user_data; + g_autoptr(GError) error = NULL; + + if (!nm_remote_connection_delete_finish (connection, res, &error)) { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("Failed to delete connection %s: %s", + nm_connection_get_id (NM_CONNECTION (connection)), + error->message); + return; + } + + cancel_editing (self); +} + +void +net_connection_editor_forget (NetConnectionEditor *self) +{ + nm_remote_connection_delete_async (NM_REMOTE_CONNECTION (self->orig_connection), + NULL, forgotten_cb, self); +} + +void +net_connection_editor_set_title (NetConnectionEditor *self, + const gchar *title) +{ + gtk_window_set_title (GTK_WINDOW (self), title); + self->title_set = TRUE; +} diff --git a/panels/network/connection-editor/net-connection-editor.h b/panels/network/connection-editor/net-connection-editor.h new file mode 100644 index 0000000..ba4bf34 --- /dev/null +++ b/panels/network/connection-editor/net-connection-editor.h @@ -0,0 +1,42 @@ +/* -*- 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. + */ + +#pragma once + +#include <gtk/gtk.h> +#include <NetworkManager.h> + +G_BEGIN_DECLS + +G_DECLARE_FINAL_TYPE (NetConnectionEditor, net_connection_editor, NET, CONNECTION_EDITOR, GtkDialog) + +NetConnectionEditor *net_connection_editor_new (GtkWindow *parent_window, + NMConnection *connection, + NMDevice *device, + NMAccessPoint *ap, + NMClient *client); +void net_connection_editor_set_title (NetConnectionEditor *editor, + const gchar *title); +void net_connection_editor_run (NetConnectionEditor *editor); +void net_connection_editor_forget (NetConnectionEditor *editor); + +G_END_DECLS + diff --git a/panels/network/connection-editor/security-page.ui b/panels/network/connection-editor/security-page.ui new file mode 100644 index 0000000..f35c250 --- /dev/null +++ b/panels/network/connection-editor/security-page.ui @@ -0,0 +1,61 @@ +<?xml version="1.0" encoding="UTF-8"?> +<interface> + <!-- interface-requires gtk+ 3.0 --> + <template class="CEPageSecurity" parent="GtkGrid"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="margin_start">50</property> + <property name="margin_end">50</property> + <property name="margin_top">12</property> + <property name="margin_bottom">12</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="row_spacing">10</property> + <property name="column_spacing">6</property> + <child> + <object class="GtkLabel" id="security_label"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="xalign">1</property> + <property name="label" translatable="yes">S_ecurity</property> + <property name="use_underline">True</property> + <property name="mnemonic_widget">security_combo</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">0</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkComboBox" id="security_combo"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">0</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkBox" id="box"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <child> + <placeholder/> + </child> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">1</property> + <property name="width">2</property> + <property name="height">1</property> + </packing> + </child> + </template> +</interface> diff --git a/panels/network/connection-editor/vpn-helpers.c b/panels/network/connection-editor/vpn-helpers.c new file mode 100644 index 0000000..8ef486b --- /dev/null +++ b/panels/network/connection-editor/vpn-helpers.c @@ -0,0 +1,318 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager Connection editor -- Connection editor for NetworkManager + * + * Dan Williams <dcbw@redhat.com> + * + * 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. + * + * (C) Copyright 2008 Red Hat, Inc. + */ + +#include "config.h" + +#include <string.h> +#include <glib.h> +#include <gmodule.h> +#include <gtk/gtk.h> +#include <glib/gi18n.h> +#include <NetworkManager.h> + +#include "vpn-helpers.h" + +NMVpnEditorPlugin * +vpn_get_plugin_by_service (const char *service) +{ + NMVpnPluginInfo *plugin_info; + + g_return_val_if_fail (service != NULL, NULL); + + plugin_info = nm_vpn_plugin_info_list_find_by_service (vpn_get_plugins (), service); + if (plugin_info) + return nm_vpn_plugin_info_get_editor_plugin (plugin_info); + return NULL; +} + +static gint +_sort_vpn_plugins (NMVpnPluginInfo *aa, NMVpnPluginInfo *bb) +{ + return strcmp (nm_vpn_plugin_info_get_name (aa), nm_vpn_plugin_info_get_name (bb)); +} + +GSList * +vpn_get_plugins (void) +{ + static gboolean plugins_loaded = FALSE; + static GSList *plugins = NULL; + GSList *p; + + if (G_LIKELY (plugins_loaded)) + return plugins; + plugins_loaded = TRUE; + + p = nm_vpn_plugin_info_list_load (); + plugins = NULL; + while (p) { + g_autoptr(NMVpnPluginInfo) plugin_info = NM_VPN_PLUGIN_INFO (p->data); + g_autoptr(GError) error = NULL; + + /* load the editor plugin, and preserve only those NMVpnPluginInfo that can + * successfully load the plugin. */ + if (nm_vpn_plugin_info_load_editor_plugin (plugin_info, &error)) + plugins = g_slist_prepend (plugins, g_steal_pointer (&plugin_info)); + else { + if ( !nm_vpn_plugin_info_get_plugin (plugin_info) + && nm_vpn_plugin_info_lookup_property (plugin_info, NM_VPN_PLUGIN_INFO_KF_GROUP_GNOME, "properties")) { + g_message ("vpn: (%s,%s) cannot load legacy-only plugin", + nm_vpn_plugin_info_get_name (plugin_info), + nm_vpn_plugin_info_get_filename (plugin_info)); + } else if (g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) { + g_message ("vpn: (%s,%s) file \"%s\" not found. Did you install the client package?", + nm_vpn_plugin_info_get_name (plugin_info), + nm_vpn_plugin_info_get_filename (plugin_info), + nm_vpn_plugin_info_get_plugin (plugin_info)); + } else { + g_warning ("vpn: (%s,%s) could not load plugin: %s", + nm_vpn_plugin_info_get_name (plugin_info), + nm_vpn_plugin_info_get_filename (plugin_info), + error->message); + } + } + p = g_slist_delete_link (p, p); + } + + /* sort the list of plugins alphabetically. */ + plugins = g_slist_sort (plugins, (GCompareFunc) _sort_vpn_plugins); + return plugins; +} + +typedef struct { + VpnImportCallback callback; + gpointer user_data; +} ActionInfo; + +static void +import_vpn_from_file_cb (GtkWidget *dialog, gint response, gpointer user_data) +{ + g_autofree gchar *filename = NULL; + ActionInfo *info = (ActionInfo *) user_data; + NMConnection *connection = NULL; + g_autoptr(GError) error = NULL; + GSList *iter; + + if (response != GTK_RESPONSE_ACCEPT) + goto out; + + filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); + if (!filename) { + g_warning ("%s: didn't get a filename back from the chooser!", __func__); + goto out; + } + + for (iter = vpn_get_plugins (); !connection && iter; iter = iter->next) { + NMVpnEditorPlugin *plugin; + + plugin = nm_vpn_plugin_info_get_editor_plugin (iter->data); + g_clear_error (&error); + connection = nm_vpn_editor_plugin_import (plugin, filename, &error); + } + + if (!connection) { + GtkWidget *err_dialog; + g_autofree gchar *bname = g_path_get_basename (filename); + + err_dialog = gtk_message_dialog_new (GTK_WINDOW (dialog), + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + _("Cannot import VPN connection")); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (err_dialog), + _("The file “%s” could not be read or does not contain recognized VPN connection information\n\nError: %s."), + bname, error ? error->message : "unknown error"); + g_signal_connect (err_dialog, "delete-event", G_CALLBACK (gtk_widget_destroy), NULL); + g_signal_connect (err_dialog, "response", G_CALLBACK (gtk_widget_destroy), NULL); + gtk_dialog_run (GTK_DIALOG (err_dialog)); + } + +out: + gtk_widget_hide (dialog); + gtk_widget_destroy (dialog); + + info->callback (connection, info->user_data); + g_free (info); +} + +static void +destroy_import_chooser (GtkWidget *dialog, gpointer user_data) +{ + ActionInfo *info = (ActionInfo *) user_data; + + gtk_widget_destroy (dialog); + info->callback (NULL, info->user_data); + g_free (info); +} + +void +vpn_import (GtkWindow *parent, VpnImportCallback callback, gpointer user_data) +{ + GtkWidget *dialog; + ActionInfo *info; + const char *home_folder; + + dialog = gtk_file_chooser_dialog_new (_("Select file to import"), + parent, + GTK_FILE_CHOOSER_ACTION_OPEN, + _("_Cancel"), GTK_RESPONSE_CANCEL, + _("_Open"), GTK_RESPONSE_ACCEPT, + NULL); + gtk_window_set_modal (GTK_WINDOW (dialog), TRUE); + home_folder = g_get_home_dir (); + gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), home_folder); + + info = g_malloc0 (sizeof (ActionInfo)); + info->callback = callback; + info->user_data = user_data; + + g_signal_connect (G_OBJECT (dialog), "close", G_CALLBACK (destroy_import_chooser), info); + g_signal_connect (G_OBJECT (dialog), "response", G_CALLBACK (import_vpn_from_file_cb), info); + gtk_widget_show_all (dialog); + gtk_window_present (GTK_WINDOW (dialog)); +} + +static void +export_vpn_to_file_cb (GtkWidget *dialog, gint response, gpointer user_data) +{ + g_autoptr(NMConnection) connection = NM_CONNECTION (user_data); + char *filename = NULL; + g_autoptr(GError) error = NULL; + NMVpnEditorPlugin *plugin; + NMSettingConnection *s_con = NULL; + NMSettingVpn *s_vpn = NULL; + const char *service_type; + const char *id = NULL; + gboolean success = FALSE; + + if (response != GTK_RESPONSE_ACCEPT) + goto out; + + filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); + if (!filename) { + g_set_error (&error, G_IO_ERROR, G_IO_ERROR_FAILED, "no filename"); + goto done; + } + + if (g_file_test (filename, G_FILE_TEST_EXISTS)) { + int replace_response; + GtkWidget *replace_dialog; + g_autofree gchar *bname = NULL; + + bname = g_path_get_basename (filename); + replace_dialog = gtk_message_dialog_new (NULL, + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_CANCEL, + _("A file named “%s” already exists."), + bname); + gtk_dialog_add_buttons (GTK_DIALOG (replace_dialog), _("_Replace"), GTK_RESPONSE_OK, NULL); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (replace_dialog), + _("Do you want to replace %s with the VPN connection you are saving?"), bname); + replace_response = gtk_dialog_run (GTK_DIALOG (replace_dialog)); + gtk_widget_destroy (replace_dialog); + if (replace_response != GTK_RESPONSE_OK) + goto out; + } + + s_con = nm_connection_get_setting_connection (connection); + id = s_con ? nm_setting_connection_get_id (s_con) : NULL; + if (!id) { + g_set_error (&error, G_IO_ERROR, G_IO_ERROR_FAILED, "connection setting invalid"); + goto done; + } + + s_vpn = nm_connection_get_setting_vpn (connection); + service_type = s_vpn ? nm_setting_vpn_get_service_type (s_vpn) : NULL; + + if (!service_type) { + g_set_error (&error, G_IO_ERROR, G_IO_ERROR_FAILED, "VPN setting invalid"); + goto done; + } + + plugin = vpn_get_plugin_by_service (service_type); + if (plugin) + success = nm_vpn_editor_plugin_export (plugin, filename, connection, &error); + +done: + if (!success) { + GtkWidget *err_dialog; + g_autofree gchar *bname = filename ? g_path_get_basename (filename) : g_strdup ("(none)"); + + err_dialog = gtk_message_dialog_new (NULL, + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + _("Cannot export VPN connection")); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (err_dialog), + _("The VPN connection “%s” could not be exported to %s.\n\nError: %s."), + id ? id : "(unknown)", bname, error ? error->message : "unknown error"); + g_signal_connect (err_dialog, "delete-event", G_CALLBACK (gtk_widget_destroy), NULL); + g_signal_connect (err_dialog, "response", G_CALLBACK (gtk_widget_destroy), NULL); + gtk_widget_show_all (err_dialog); + gtk_window_present (GTK_WINDOW (err_dialog)); + } + +out: + gtk_widget_hide (dialog); + gtk_widget_destroy (dialog); +} + +void +vpn_export (NMConnection *connection) +{ + GtkWidget *dialog; + NMVpnEditorPlugin *plugin; + NMSettingVpn *s_vpn = NULL; + const char *service_type; + const char *home_folder; + + s_vpn = nm_connection_get_setting_vpn (connection); + service_type = s_vpn ? nm_setting_vpn_get_service_type (s_vpn) : NULL; + + if (!service_type) { + g_warning ("%s: invalid VPN connection!", __func__); + return; + } + + dialog = gtk_file_chooser_dialog_new (_("Export VPN connection"), + NULL, + GTK_FILE_CHOOSER_ACTION_SAVE, + _("_Cancel"), GTK_RESPONSE_CANCEL, + _("_Save"), GTK_RESPONSE_ACCEPT, + NULL); + home_folder = g_get_home_dir (); + gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), home_folder); + + plugin = vpn_get_plugin_by_service (service_type); + if (plugin) { + g_autofree gchar *suggested = NULL; + + suggested = nm_vpn_editor_plugin_get_suggested_filename (plugin, connection); + if (suggested) + gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog), suggested); + } + + g_signal_connect (G_OBJECT (dialog), "close", G_CALLBACK (gtk_widget_destroy), NULL); + g_signal_connect (G_OBJECT (dialog), "response", G_CALLBACK (export_vpn_to_file_cb), g_object_ref (connection)); + gtk_widget_show_all (dialog); + gtk_window_present (GTK_WINDOW (dialog)); +} diff --git a/panels/network/connection-editor/vpn-helpers.h b/panels/network/connection-editor/vpn-helpers.h new file mode 100644 index 0000000..578f68c --- /dev/null +++ b/panels/network/connection-editor/vpn-helpers.h @@ -0,0 +1,39 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager Connection editor -- Connection editor for NetworkManager + * + * Dan Williams <dcbw@redhat.com> + * + * 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. + * + * (C) Copyright 2008 Red Hat, Inc. + */ + +#ifndef _VPN_HELPERS_H_ +#define _VPN_HELPERS_H_ + +#include <glib.h> +#include <gtk/gtk.h> +#include <NetworkManager.h> + +GSList *vpn_get_plugins (void); + +NMVpnEditorPlugin *vpn_get_plugin_by_service (const char *service); + +typedef void (*VpnImportCallback) (NMConnection *connection, gpointer user_data); +void vpn_import (GtkWindow *parent, VpnImportCallback callback, gpointer user_data); + +void vpn_export (NMConnection *connection); + +#endif /* _VPN_HELPERS_H_ */ diff --git a/panels/network/connection-editor/vpn-page.ui b/panels/network/connection-editor/vpn-page.ui new file mode 100644 index 0000000..682e7a7 --- /dev/null +++ b/panels/network/connection-editor/vpn-page.ui @@ -0,0 +1,70 @@ +<?xml version="1.0" encoding="UTF-8"?> +<interface> + <!-- interface-requires gtk+ 3.0 --> + <template class="CEPageVpn" parent="GtkBox"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="margin_start">50</property> + <property name="margin_end">50</property> + <property name="margin_top">12</property> + <property name="margin_bottom">12</property> + <property name="orientation">vertical</property> + <property name="spacing">10</property> + <child> + <object class="GtkBox"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="spacing">6</property> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="xalign">1</property> + <property name="label" translatable="yes">_Name</property> + <property name="use_underline">True</property> + <property name="mnemonic_widget">name_entry</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkEntry" id="name_entry"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="invisible_char">●</property> + <property name="invisible_char_set">True</property> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="failure_label"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">(Error: unable to load VPN connection editor)</property> + <attributes> + <attribute name="style" value="italic"/> + </attributes> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">3</property> + </packing> + </child> + </template> +</interface> diff --git a/panels/network/connection-editor/wifi-page.ui b/panels/network/connection-editor/wifi-page.ui new file mode 100644 index 0000000..e156780 --- /dev/null +++ b/panels/network/connection-editor/wifi-page.ui @@ -0,0 +1,144 @@ +<?xml version="1.0" encoding="UTF-8"?> +<interface> + <!-- interface-requires gtk+ 3.0 --> + <template class="CEPageWifi" parent="GtkGrid"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="margin_start">50</property> + <property name="margin_end">50</property> + <property name="margin_top">12</property> + <property name="margin_bottom">12</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="row_spacing">10</property> + <property name="column_spacing">6</property> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="xalign">1</property> + <property name="label" translatable="yes">_SSID</property> + <property name="use_underline">True</property> + <property name="mnemonic_widget">ssid_entry</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">0</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="xalign">1</property> + <property name="label" translatable="yes">_BSSID</property> + <property name="use_underline">True</property> + <property name="mnemonic_widget">bssid_combo</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">1</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkEntry" id="ssid_entry"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hexpand">True</property> + <property name="invisible_char">●</property> + <property name="invisible_char_set">True</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">0</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="xalign">1</property> + <property name="label" translatable="yes">_MAC Address</property> + <property name="use_underline">True</property> + <property name="mnemonic_widget">mac_combo</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">2</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkComboBoxText" id="cloned_mac_combo"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="has_entry">True</property> + <property name="hexpand">True</property> + <property name="active_id">0</property> + <child internal-child="entry"> + <object class="GtkEntry"> + <property name="can_focus">True</property> + </object> + </child> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">3</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">_Cloned Address</property> + <property name="use_underline">True</property> + <property name="mnemonic_widget">cloned_mac_combo</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">3</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkComboBoxText" id="bssid_combo"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="has_entry">True</property> + <property name="entry_text_column">0</property> + <property name="id_column">1</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">1</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkComboBoxText" id="mac_combo"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="has_entry">True</property> + <property name="entry_text_column">0</property> + <property name="id_column">1</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">2</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + </template> +</interface> |