diff options
Diffstat (limited to 'panels/network/cc-wifi-panel.c')
-rw-r--r-- | panels/network/cc-wifi-panel.c | 1081 |
1 files changed, 1081 insertions, 0 deletions
diff --git a/panels/network/cc-wifi-panel.c b/panels/network/cc-wifi-panel.c new file mode 100644 index 0000000..8608a7f --- /dev/null +++ b/panels/network/cc-wifi-panel.c @@ -0,0 +1,1081 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2017 Georges Basile Stavracas Neto <georges.stavracas@gmail.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, see <http://www.gnu.org/licenses/>. + * + */ + +#include "cc-network-resources.h" +#include "cc-wifi-panel.h" +#include "cc-qr-code.h" +#include "net-device-wifi.h" +#include "network-dialogs.h" +#include "panel-common.h" + +#include "shell/cc-application.h" +#include "shell/cc-debug.h" +#include "shell/cc-object-storage.h" + +#include <glib/gi18n.h> +#include <NetworkManager.h> + +#define QR_IMAGE_SIZE 180 + +typedef enum +{ + OPERATION_NULL, + OPERATION_SHOW_DEVICE, + OPERATION_CREATE_WIFI, + OPERATION_CONNECT_HIDDEN, + OPERATION_CONNECT_8021X +} CmdlineOperation; + +struct _CcWifiPanel +{ + CcPanel parent; + + /* RFKill (Airplane Mode) */ + GDBusProxy *rfkill_proxy; + GtkSwitch *rfkill_switch; + GtkWidget *rfkill_widget; + + /* Main widgets */ + GtkStack *center_stack; + GtkStack *header_stack; + GtkBox *hotspot_box; + GtkLabel *list_label; + GtkStack *main_stack; + GtkWidget *spinner; + GtkStack *stack; + GtkImage *wifi_qr_image; + CcQrCode *qr_code; + + NMClient *client; + + GPtrArray *devices; + + GBinding *spinner_binding; + + /* Command-line arguments */ + CmdlineOperation arg_operation; + gchar *arg_device; + gchar *arg_access_point; +}; + +static void rfkill_switch_notify_activate_cb (GtkSwitch *rfkill_switch, + GParamSpec *pspec, + CcWifiPanel *self); + +static void update_devices_names (CcWifiPanel *self); + +G_DEFINE_TYPE (CcWifiPanel, cc_wifi_panel, CC_TYPE_PANEL) + +enum +{ + PROP_0, + PROP_PARAMETERS, + N_PROPS +}; + +/* Static init function */ + +static void +update_panel_visibility (NMClient *client) +{ + const GPtrArray *devices; + CcApplication *application; + gboolean visible; + guint i; + + CC_TRACE_MSG ("Updating Wi-Fi panel visibility"); + + devices = nm_client_get_devices (client); + visible = FALSE; + + for (i = 0; devices && i < devices->len; i++) + { + NMDevice *device = g_ptr_array_index (devices, i); + + visible |= NM_IS_DEVICE_WIFI (device); + + if (visible) + break; + } + + /* Set the new visibility */ + application = CC_APPLICATION (g_application_get_default ()); + cc_shell_model_set_panel_visibility (cc_application_get_model (application), + "wifi", + visible ? CC_PANEL_VISIBLE : CC_PANEL_VISIBLE_IN_SEARCH); + + g_debug ("Wi-Fi panel visible: %s", visible ? "yes" : "no"); +} + +void +cc_wifi_panel_static_init_func (void) +{ + g_autoptr(NMClient) client = NULL; + + g_debug ("Monitoring NetworkManager for Wi-Fi devices"); + + /* Create and store a NMClient instance if it doesn't exist yet */ + if (!cc_object_storage_has_object (CC_OBJECT_NMCLIENT)) + { + g_autoptr(NMClient) new_client = nm_client_new (NULL, NULL); + cc_object_storage_add_object (CC_OBJECT_NMCLIENT, new_client); + } + + client = cc_object_storage_get_object (CC_OBJECT_NMCLIENT); + + /* Update the panel visibility and monitor for changes */ + + g_signal_connect (client, "device-added", G_CALLBACK (update_panel_visibility), NULL); + g_signal_connect (client, "device-removed", G_CALLBACK (update_panel_visibility), NULL); + + update_panel_visibility (client); +} + +/* Auxiliary methods */ + +static gchar * +escape_string (const gchar *str, + gboolean quote) +{ + GString *string; + const char *next; + + if (!str) + return NULL; + + string = g_string_new (""); + if (quote) + g_string_append_c (string, '"'); + + while ((next = strpbrk (str, "\\;,:\""))) + { + g_string_append_len (string, str, next - str); + g_string_append_c (string, '\\'); + g_string_append_c (string, *next); + str = next + 1; + } + + g_string_append (string, str); + if (quote) + g_string_append_c (string, '"'); + + return g_string_free (string, FALSE); +} + +static const gchar * +get_connection_security_type (NMConnection *c) +{ + NMSettingWirelessSecurity *setting; + const char *key_mgmt; + + g_return_val_if_fail (c, "nopass"); + + setting = nm_connection_get_setting_wireless_security (c); + + if (!setting) + return "nopass"; + + key_mgmt = nm_setting_wireless_security_get_key_mgmt (setting); + + /* No IEEE 802.1x */ + if (g_strcmp0 (key_mgmt, "none") == 0) + return "WEP"; + + if (g_strcmp0 (key_mgmt, "wpa-none") == 0 || + g_strcmp0 (key_mgmt, "wpa-psk") == 0) + return "WPA"; + + return "nopass"; +} + +static gchar * +get_wifi_password (NMConnection *c) +{ + NMSettingWirelessSecurity *setting; + g_autoptr(GVariant) secrets = NULL; + g_autoptr(GError) error = NULL; + const gchar *sec_type, *password; + gint wep_index; + + g_assert (NM_IS_REMOTE_CONNECTION (c)); + + sec_type = get_connection_security_type (c); + setting = nm_connection_get_setting_wireless_security (c); + + if (g_str_equal (sec_type, "nopass")) + return NULL; + + secrets = nm_remote_connection_get_secrets (NM_REMOTE_CONNECTION (c), + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NULL, &error); + if (!error) + nm_connection_update_secrets (c, + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + secrets, &error); + if (error) + { + g_warning ("Error: %s", error->message); + return NULL; + } + + if (g_str_equal (sec_type, "WEP")) + { + wep_index = nm_setting_wireless_security_get_wep_tx_keyidx (setting); + password = nm_setting_wireless_security_get_wep_key (setting, wep_index); + } + else + { + password = nm_setting_wireless_security_get_psk (setting); + } + + return escape_string (password, FALSE); +} + +/* Generate a string representing the hotspot @connection + * An example generated text: + * WIFI:S:hotspot;T:WPA;P:my-valid-pass;H:true; + * Where, + * S = ssid, T = security, P = password, H = hidden (Optional) + * + * See https://github.com/zxing/zxing/wiki/Barcode-Contents#wi-fi-network-config-android-ios-11 + */ +static gchar * +get_qr_string_for_hotspot (NMClient *nm_client, + NMConnection *c) +{ + NMSettingWireless *setting; + g_autofree char *ssid_text = NULL; + g_autofree char *escaped_ssid = NULL; + g_autofree char *password_str = NULL; + GString *string; + GBytes *ssid; + gboolean hidden; + + g_assert (NM_IS_CLIENT (nm_client)); + g_assert (NM_IS_REMOTE_CONNECTION (c)); + + setting = nm_connection_get_setting_wireless (c); + ssid = nm_setting_wireless_get_ssid (setting); + + if (!ssid) + return NULL; + + string = g_string_new ("WIFI:S:"); + + /* SSID */ + ssid_text = nm_utils_ssid_to_utf8 (g_bytes_get_data (ssid, NULL), + g_bytes_get_size (ssid)); + escaped_ssid = escape_string (ssid_text, FALSE); + g_string_append (string, escaped_ssid); + g_string_append_c (string, ';'); + + /* Security type */ + g_string_append (string, "T:"); + g_string_append (string, get_connection_security_type (c)); + g_string_append_c (string, ';'); + + /* Password */ + g_string_append (string, "P:"); + password_str = get_wifi_password (c); + if (password_str) + g_string_append (string, password_str); + g_string_append_c (string, ';'); + + /* WiFi Hidden */ + hidden = nm_setting_wireless_get_hidden (setting); + if (hidden) + g_string_append (string, "H:true"); + g_string_append_c (string, ';'); + + return g_string_free (string, FALSE); +} + +static NMConnection * +wifi_device_get_hotspot (CcWifiPanel *self, + NMDevice *device) +{ + NMSettingIPConfig *ip4_setting; + NMConnection *c; + + g_assert (CC_IS_WIFI_PANEL (self)); + g_assert (NM_IS_DEVICE (device)); + + if (nm_device_get_active_connection (device) == NULL) + return NULL; + + c = net_device_get_find_connection (self->client, device); + if (c == NULL) + return NULL; + + ip4_setting = nm_connection_get_setting_ip4_config (c); + if (g_strcmp0 (nm_setting_ip_config_get_method (ip4_setting), + NM_SETTING_IP4_CONFIG_METHOD_SHARED) != 0) + return NULL; + + return c; +} + +static void +wifi_panel_update_qr_image_cb (CcWifiPanel *self) +{ + NetDeviceWifi *child; + NMConnection *hotspot; + NMDevice *device; + + g_assert (CC_IS_WIFI_PANEL (self)); + + child = NET_DEVICE_WIFI (gtk_stack_get_visible_child (self->stack)); + device = net_device_wifi_get_device (child); + hotspot = wifi_device_get_hotspot (self, device); + + if (hotspot) + { + g_autofree gchar *str = NULL; + + if (!self->qr_code) + self->qr_code = cc_qr_code_new (); + + str = get_qr_string_for_hotspot (self->client, hotspot); + if (cc_qr_code_set_text (self->qr_code, str)) + { + cairo_surface_t *surface; + gint scale; + + scale = gtk_widget_get_scale_factor (GTK_WIDGET (self->wifi_qr_image)); + surface = cc_qr_code_get_surface (self->qr_code, QR_IMAGE_SIZE, scale); + gtk_image_set_from_surface (self->wifi_qr_image, surface); + } + } + + gtk_widget_set_visible (GTK_WIDGET (self->hotspot_box), hotspot != NULL); + gtk_widget_set_opacity (GTK_WIDGET (self->list_label), hotspot == NULL); + gtk_widget_set_opacity (GTK_WIDGET (self->spinner), hotspot == NULL); +} + +static void +add_wifi_device (CcWifiPanel *self, + NMDevice *device) +{ + GtkWidget *header_widget; + NetDeviceWifi *net_device; + + /* Create the NetDevice */ + net_device = net_device_wifi_new (CC_PANEL (self), + self->client, + device); + gtk_widget_show (GTK_WIDGET (net_device)); + + /* And add to the header widgets */ + header_widget = net_device_wifi_get_header_widget (net_device); + + gtk_stack_add_named (self->header_stack, header_widget, nm_device_get_udi (device)); + + /* Setup custom title properties */ + g_ptr_array_add (self->devices, net_device); + + update_devices_names (self); + + /* Needs to be added after the device is added to the self->devices array */ + gtk_stack_add_titled (self->stack, GTK_WIDGET (net_device), + nm_device_get_udi (device), + nm_device_get_description (device)); + g_signal_connect_object (device, "state-changed", + G_CALLBACK (wifi_panel_update_qr_image_cb), + self, + G_CONNECT_SWAPPED); +} + +static void +remove_wifi_device (CcWifiPanel *self, + NMDevice *device) +{ + GtkWidget *child; + const gchar *id; + guint i; + + id = nm_device_get_udi (device); + + /* Remove from the devices list */ + for (i = 0; i < self->devices->len; i++) + { + NetDeviceWifi *net_device = g_ptr_array_index (self->devices, i); + + if (net_device_wifi_get_device (net_device) == device) + { + g_ptr_array_remove (self->devices, net_device); + break; + } + } + + /* Destroy all stack pages related to this device */ + child = gtk_stack_get_child_by_name (self->stack, id); + gtk_widget_destroy (child); + + child = gtk_stack_get_child_by_name (self->header_stack, id); + gtk_widget_destroy (child); + + /* Update the title widget */ + update_devices_names (self); +} + +static void +check_main_stack_page (CcWifiPanel *self) +{ + const gchar *nm_version; + gboolean airplane_mode_active; + gboolean wireless_enabled; + + nm_version = nm_client_get_version (self->client); + wireless_enabled = nm_client_wireless_get_enabled (self->client); + airplane_mode_active = gtk_switch_get_active (self->rfkill_switch); + + if (!nm_version) + gtk_stack_set_visible_child_name (self->main_stack, "nm-not-running"); + else if (!wireless_enabled && airplane_mode_active) + gtk_stack_set_visible_child_name (self->main_stack, "airplane-mode"); + else if (!wireless_enabled || self->devices->len == 0) + gtk_stack_set_visible_child_name (self->main_stack, "no-wifi-devices"); + else + gtk_stack_set_visible_child_name (self->main_stack, "wifi-connections"); +} + +static void +load_wifi_devices (CcWifiPanel *self) +{ + const GPtrArray *devices; + guint i; + + devices = nm_client_get_devices (self->client); + + /* Cold-plug existing devices */ + if (devices) + { + for (i = 0; i < devices->len; i++) + { + NMDevice *device; + + device = g_ptr_array_index (devices, i); + if (!NM_IS_DEVICE_WIFI (device) || !nm_device_get_managed (device)) + continue; + add_wifi_device (self, device); + } + } + + check_main_stack_page (self); +} + +static inline gboolean +get_cached_rfkill_property (CcWifiPanel *self, + const gchar *property) +{ + g_autoptr(GVariant) result = NULL; + + result = g_dbus_proxy_get_cached_property (self->rfkill_proxy, property); + return result ? g_variant_get_boolean (result) : FALSE; +} + +static void +sync_airplane_mode_switch (CcWifiPanel *self) +{ + gboolean enabled, should_show, hw_enabled; + + enabled = get_cached_rfkill_property (self, "HasAirplaneMode"); + should_show = get_cached_rfkill_property (self, "ShouldShowAirplaneMode"); + + gtk_widget_set_visible (GTK_WIDGET (self->rfkill_widget), enabled && should_show); + if (!enabled || !should_show) + return; + + enabled = get_cached_rfkill_property (self, "AirplaneMode"); + hw_enabled = get_cached_rfkill_property (self, "HardwareAirplaneMode"); + + enabled |= hw_enabled; + + if (enabled != gtk_switch_get_active (self->rfkill_switch)) + { + g_signal_handlers_block_by_func (self->rfkill_switch, + rfkill_switch_notify_activate_cb, + self); + gtk_switch_set_active (self->rfkill_switch, enabled); + check_main_stack_page (self); + g_signal_handlers_unblock_by_func (self->rfkill_switch, + rfkill_switch_notify_activate_cb, + self); + } + + gtk_widget_set_sensitive (GTK_WIDGET (self->rfkill_switch), !hw_enabled); + + check_main_stack_page (self); +} + +static void +update_devices_names (CcWifiPanel *self) +{ + guint number_of_devices = self->devices->len; + + if (number_of_devices == 1) + { + GtkWidget *title_widget; + NetDeviceWifi *net_device; + + net_device = g_ptr_array_index (self->devices, 0); + title_widget = net_device_wifi_get_title_widget (net_device); + + gtk_stack_add_named (self->center_stack, title_widget, "single"); + gtk_stack_set_visible_child_name (self->center_stack, "single"); + + net_device_wifi_set_title (net_device, _("Wi-Fi")); + } + else + { + GtkWidget *single_page_widget; + guint i; + + for (i = 0; i < number_of_devices; i++) + { + NetDeviceWifi *net_device; + NMDevice *device; + + net_device = g_ptr_array_index (self->devices, i); + device = net_device_wifi_get_device (net_device); + + net_device_wifi_set_title (net_device, nm_device_get_description (device)); + } + + /* Remove the widget at the "single" page */ + single_page_widget = gtk_stack_get_child_by_name (self->center_stack, "single"); + + if (single_page_widget) + { + g_object_ref (single_page_widget); + gtk_container_remove (GTK_CONTAINER (self->center_stack), single_page_widget); + g_object_unref (single_page_widget); + } + + /* Show the stack-switcher page */ + gtk_stack_set_visible_child_name (self->center_stack, "many"); + } +} + +/* Command-line arguments */ + +static void +reset_command_line_args (CcWifiPanel *self) +{ + self->arg_operation = OPERATION_NULL; + g_clear_pointer (&self->arg_device, g_free); + g_clear_pointer (&self->arg_access_point, g_free); +} + +static gboolean +handle_argv_for_device (CcWifiPanel *self, NetDeviceWifi *net_device) +{ + GtkWidget *toplevel; + NMDevice *device; + gboolean ret; + + toplevel = cc_shell_get_toplevel (cc_panel_get_shell (CC_PANEL (self))); + device = net_device_wifi_get_device (net_device); + ret = FALSE; + + if (self->arg_operation == OPERATION_CREATE_WIFI) + { + cc_network_panel_create_wifi_network (toplevel, self->client); + ret = TRUE; + } + else if (self->arg_operation == OPERATION_CONNECT_HIDDEN) + { + cc_network_panel_connect_to_hidden_network (toplevel, self->client); + ret = TRUE; + } + else if (g_str_equal (nm_object_get_path (NM_OBJECT (device)), self->arg_device)) + { + if (self->arg_operation == OPERATION_CONNECT_8021X) + { + cc_network_panel_connect_to_8021x_network (toplevel, + self->client, + device, + self->arg_access_point); + ret = TRUE; + } + else if (self->arg_operation == OPERATION_SHOW_DEVICE) + { + gtk_stack_set_visible_child_name (self->stack, nm_device_get_udi (device)); + ret = TRUE; + } + } + + if (ret) + reset_command_line_args (self); + + return ret; +} + +static void +handle_argv (CcWifiPanel *self) +{ + guint i; + + if (self->arg_operation == OPERATION_NULL) + return; + + for (i = 0; i < self->devices->len; i++) + { + if (handle_argv_for_device (self, g_ptr_array_index (self->devices, i))) + break; + } +} + +static GPtrArray * +variant_av_to_string_array (GVariant *array) +{ + GVariantIter iter; + GVariant *v; + GPtrArray *strv; + gsize count; + + count = g_variant_iter_init (&iter, array); + strv = g_ptr_array_sized_new (count + 1); + + while (g_variant_iter_next (&iter, "v", &v)) + { + g_ptr_array_add (strv, (gpointer) g_variant_get_string (v, NULL)); + g_variant_unref (v); + } + + g_ptr_array_add (strv, NULL); /* NULL-terminate the strv data array */ + return strv; +} + +static gboolean +verify_argv (CcWifiPanel *self, + const char **args) +{ + switch (self->arg_operation) + { + case OPERATION_CONNECT_8021X: + case OPERATION_SHOW_DEVICE: + if (!self->arg_device) + { + g_warning ("Operation %s requires an object path", args[0]); + return FALSE; + } + default: + return TRUE; + } +} + +/* Callbacks */ + +static void +device_state_changed_cb (CcWifiPanel *self, GParamSpec *pspec, NMDevice *device) +{ + const gchar *id; + + id = nm_device_get_udi (device); + /* Don't add a device that has already been added */ + if (!NM_IS_DEVICE_WIFI (device) || !id) + return; + + if (nm_device_get_managed (device)) + { + if (gtk_stack_get_child_by_name (self->stack, id)) + return; + add_wifi_device (self, device); + check_main_stack_page (self); + } + else + { + if (!gtk_stack_get_child_by_name (self->stack, id)) + return; + remove_wifi_device (self, device); + check_main_stack_page (self); + } +} + +static void +device_added_cb (CcWifiPanel *self, NMDevice *device) +{ + if (!NM_IS_DEVICE_WIFI (device)) + return; + + if (nm_device_get_managed (device)) + { + add_wifi_device (self, device); + check_main_stack_page (self); + } + + g_signal_connect_object (device, + "notify::state", + G_CALLBACK (device_state_changed_cb), + self, + G_CONNECT_SWAPPED); +} + +static void +device_removed_cb (CcWifiPanel *self, NMDevice *device) +{ + const gchar *id; + + if (!NM_IS_DEVICE_WIFI (device)) + return; + + id = nm_device_get_udi (device); + /* Don't remove a device that has already been removed */ + if (!gtk_stack_get_child_by_name (self->stack, id)) + return; + + remove_wifi_device (self, device); + check_main_stack_page (self); + + g_signal_handlers_disconnect_by_func (device, + G_CALLBACK (device_state_changed_cb), + self); +} + +static void +wireless_enabled_cb (CcWifiPanel *self) +{ + check_main_stack_page (self); +} + +static void +on_rfkill_proxy_properties_changed_cb (CcWifiPanel *self) +{ + g_debug ("Rfkill properties changed"); + + sync_airplane_mode_switch (self); +} + +static void +rfkill_proxy_acquired_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + CcWifiPanel *self; + GDBusProxy *proxy; + g_autoptr(GError) error = NULL; + + proxy = cc_object_storage_create_dbus_proxy_finish (res, &error); + + if (error) + { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_printerr ("Error creating rfkill proxy: %s\n", error->message); + + return; + } + + self = CC_WIFI_PANEL (user_data); + + self->rfkill_proxy = proxy; + + g_signal_connect_object (proxy, + "g-properties-changed", + G_CALLBACK (on_rfkill_proxy_properties_changed_cb), + self, + G_CONNECT_SWAPPED); + + sync_airplane_mode_switch (self); +} + +static void +rfkill_switch_notify_activate_cb (GtkSwitch *rfkill_switch, + GParamSpec *pspec, + CcWifiPanel *self) +{ + gboolean enable; + + enable = gtk_switch_get_active (rfkill_switch); + + g_dbus_proxy_call (self->rfkill_proxy, + "org.freedesktop.DBus.Properties.Set", + g_variant_new_parsed ("('org.gnome.SettingsDaemon.Rfkill'," + "'AirplaneMode', %v)", + g_variant_new_boolean (enable)), + G_DBUS_CALL_FLAGS_NONE, + -1, + cc_panel_get_cancellable (CC_PANEL (self)), + NULL, + NULL); +} + +static void +on_stack_visible_child_changed_cb (GtkStack *stack, + GParamSpec *pspec, + CcWifiPanel *self) +{ + const gchar *visible_device_id = NULL; + guint i; + + wifi_panel_update_qr_image_cb (self); + + /* Remove previous bindings */ + g_clear_pointer (&self->spinner_binding, g_binding_unbind); + + visible_device_id = gtk_stack_get_visible_child_name (stack); + for (i = 0; i < self->devices->len; i++) + { + NetDeviceWifi *net_device = g_ptr_array_index (self->devices, i); + + if (g_strcmp0 (nm_device_get_udi (net_device_wifi_get_device (net_device)), visible_device_id) == 0) + { + self->spinner_binding = g_object_bind_property (net_device, + "scanning", + self->spinner, + "active", + G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); + break; + } + } +} + +static void +hotspot_stop_clicked_cb (CcWifiPanel *self) +{ + GtkWidget *dialog; + GtkWidget *window; + int response; + + g_assert (CC_IS_WIFI_PANEL (self)); + + window = gtk_widget_get_toplevel (GTK_WIDGET (self)); + dialog = gtk_message_dialog_new (GTK_WINDOW (window), + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_OTHER, + GTK_BUTTONS_NONE, + _("Stop hotspot and disconnect any users?")); + gtk_dialog_add_buttons (GTK_DIALOG (dialog), + _("_Cancel"), GTK_RESPONSE_CANCEL, + _("_Stop Hotspot"), GTK_RESPONSE_OK, + NULL); + + response = gtk_dialog_run (GTK_DIALOG (dialog)); + + if (response == GTK_RESPONSE_OK) + { + NetDeviceWifi *child; + + child = NET_DEVICE_WIFI (gtk_stack_get_visible_child (self->stack)); + net_device_wifi_turn_off_hotspot (child); + } + + gtk_widget_destroy (dialog); +} + +/* Overrides */ + +static const gchar * +cc_wifi_panel_get_help_uri (CcPanel *panel) +{ + return "help:gnome-help/net-wireless"; +} + +static GtkWidget * +cc_wifi_panel_get_title_widget (CcPanel *panel) +{ + CcWifiPanel *self = CC_WIFI_PANEL (panel); + + return GTK_WIDGET (self->center_stack); +} + +static void +cc_wifi_panel_constructed (GObject *object) +{ + CcWifiPanel *self; + CcShell *shell; + + self = CC_WIFI_PANEL (object); + shell = cc_panel_get_shell (CC_PANEL (object)); + + G_OBJECT_CLASS (cc_wifi_panel_parent_class)->constructed (object); + + cc_shell_embed_widget_in_header (shell, GTK_WIDGET (self->header_stack), GTK_POS_RIGHT); +} + +static void +cc_wifi_panel_finalize (GObject *object) +{ + CcWifiPanel *self = (CcWifiPanel *)object; + + g_clear_object (&self->client); + g_clear_object (&self->rfkill_proxy); + + g_clear_pointer (&self->devices, g_ptr_array_unref); + + reset_command_line_args (self); + + G_OBJECT_CLASS (cc_wifi_panel_parent_class)->finalize (object); +} + +static void +cc_wifi_panel_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); +} + +static void +cc_wifi_panel_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + CcWifiPanel *self = CC_WIFI_PANEL (object); + GVariant *parameters; + + switch (prop_id) + { + case PROP_PARAMETERS: + reset_command_line_args (self); + + parameters = g_value_get_variant (value); + + if (parameters) + { + g_autoptr(GPtrArray) array = NULL; + const gchar **args; + + array = variant_av_to_string_array (parameters); + args = (const gchar **) array->pdata; + + if (args[0]) + { + if (g_str_equal (args[0], "create-wifi")) + self->arg_operation = OPERATION_CREATE_WIFI; + else if (g_str_equal (args[0], "connect-hidden-wifi")) + self->arg_operation = OPERATION_CONNECT_HIDDEN; + else if (g_str_equal (args[0], "connect-8021x-wifi")) + self->arg_operation = OPERATION_CONNECT_8021X; + else if (g_str_equal (args[0], "show-device")) + self->arg_operation = OPERATION_SHOW_DEVICE; + else + self->arg_operation = OPERATION_NULL; + } + + if (args[0] && args[1]) + self->arg_device = g_strdup (args[1]); + if (args[0] && args[1] && args[2]) + self->arg_access_point = g_strdup (args[2]); + + if (!verify_argv (self, (const char **) args)) + { + reset_command_line_args (self); + return; + } + + handle_argv (self); + } + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +cc_wifi_panel_class_init (CcWifiPanelClass *klass) +{ + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + GObjectClass *object_class = G_OBJECT_CLASS (klass); + CcPanelClass *panel_class = CC_PANEL_CLASS (klass); + + panel_class->get_help_uri = cc_wifi_panel_get_help_uri; + panel_class->get_title_widget = cc_wifi_panel_get_title_widget; + + object_class->constructed = cc_wifi_panel_constructed; + object_class->finalize = cc_wifi_panel_finalize; + object_class->get_property = cc_wifi_panel_get_property; + object_class->set_property = cc_wifi_panel_set_property; + + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/network/cc-wifi-panel.ui"); + + gtk_widget_class_bind_template_child (widget_class, CcWifiPanel, center_stack); + gtk_widget_class_bind_template_child (widget_class, CcWifiPanel, header_stack); + gtk_widget_class_bind_template_child (widget_class, CcWifiPanel, hotspot_box); + gtk_widget_class_bind_template_child (widget_class, CcWifiPanel, list_label); + gtk_widget_class_bind_template_child (widget_class, CcWifiPanel, main_stack); + gtk_widget_class_bind_template_child (widget_class, CcWifiPanel, rfkill_switch); + gtk_widget_class_bind_template_child (widget_class, CcWifiPanel, rfkill_widget); + gtk_widget_class_bind_template_child (widget_class, CcWifiPanel, spinner); + gtk_widget_class_bind_template_child (widget_class, CcWifiPanel, stack); + gtk_widget_class_bind_template_child (widget_class, CcWifiPanel, wifi_qr_image); + + gtk_widget_class_bind_template_callback (widget_class, rfkill_switch_notify_activate_cb); + gtk_widget_class_bind_template_callback (widget_class, on_stack_visible_child_changed_cb); + gtk_widget_class_bind_template_callback (widget_class, hotspot_stop_clicked_cb); + + g_object_class_override_property (object_class, PROP_PARAMETERS, "parameters"); +} + +static void +cc_wifi_panel_init (CcWifiPanel *self) +{ + g_resources_register (cc_network_get_resource ()); + + gtk_widget_init_template (GTK_WIDGET (self)); + + self->devices = g_ptr_array_new (); + + /* Create and store a NMClient instance if it doesn't exist yet */ + if (!cc_object_storage_has_object (CC_OBJECT_NMCLIENT)) + { + g_autoptr(NMClient) client = nm_client_new (NULL, NULL); + cc_object_storage_add_object (CC_OBJECT_NMCLIENT, client); + } + + /* Load NetworkManager */ + self->client = cc_object_storage_get_object (CC_OBJECT_NMCLIENT); + + g_signal_connect_object (self->client, + "device-added", + G_CALLBACK (device_added_cb), + self, + G_CONNECT_SWAPPED); + + g_signal_connect_object (self->client, + "device-removed", + G_CALLBACK (device_removed_cb), + self, + G_CONNECT_SWAPPED); + + g_signal_connect_object (self->client, + "notify::wireless-enabled", + G_CALLBACK (wireless_enabled_cb), + self, + G_CONNECT_SWAPPED); + + /* Load Wi-Fi devices */ + load_wifi_devices (self); + + /* Acquire Airplane Mode proxy */ + cc_object_storage_create_dbus_proxy (G_BUS_TYPE_SESSION, + G_DBUS_PROXY_FLAGS_NONE, + "org.gnome.SettingsDaemon.Rfkill", + "/org/gnome/SettingsDaemon/Rfkill", + "org.gnome.SettingsDaemon.Rfkill", + cc_panel_get_cancellable (CC_PANEL (self)), + rfkill_proxy_acquired_cb, + self); + + /* Handle comment-line arguments after loading devices */ + handle_argv (self); +} |