gnome-control-center/panels/network/cc-network-panel.c
Bastien Nocera ddc35609bd network: Hide the Virtualisation bridge
There's no need for anyone to poke at the libvirt bridge that's not an
admin. It should probably be locked down more as well, but that would be
NetworkManager's business.
2014-05-19 17:55:47 +02:00

1506 lines
56 KiB
C

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
*
* Copyright (C) 2010-2012 Richard Hughes <richard@hughsie.com>
* Copyright (C) 2012 Thomas Bechtold <thomasbechtold@jpberlin.de>
* Copyright (C) 2013 Aleksander Morgado <aleksander@gnu.org>
*
* 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 <config.h>
#include <glib/gi18n.h>
#include <stdlib.h>
#include "cc-network-panel.h"
#include "cc-network-resources.h"
#include "nm-remote-settings.h"
#include "nm-client.h"
#include "nm-device.h"
#include "nm-device-modem.h"
#include "nm-ui-utils.h"
#include "net-device.h"
#include "net-device-mobile.h"
#include "net-device-wifi.h"
#include "net-device-ethernet.h"
#include "net-device-bond.h"
#include "net-device-bridge.h"
#include "net-object.h"
#include "net-proxy.h"
#include "net-virtual-device.h"
#include "net-vpn.h"
#include "panel-common.h"
#include "network-dialogs.h"
#include "connection-editor/net-connection-editor.h"
#include <libmm-glib.h>
CC_PANEL_REGISTER (CcNetworkPanel, cc_network_panel)
#define NETWORK_PANEL_PRIVATE(o) \
(G_TYPE_INSTANCE_GET_PRIVATE ((o), CC_TYPE_NETWORK_PANEL, CcNetworkPanelPrivate))
typedef enum {
OPERATION_NULL,
OPERATION_SHOW_DEVICE,
OPERATION_CREATE_WIFI,
OPERATION_CONNECT_HIDDEN,
OPERATION_CONNECT_8021X,
OPERATION_CONNECT_MOBILE
} CmdlineOperation;
struct _CcNetworkPanelPrivate
{
GCancellable *cancellable;
GtkBuilder *builder;
GtkWidget *treeview;
NMClient *client;
MMManager *modem_manager;
NMRemoteSettings *remote_settings;
gboolean updating_device;
guint nm_warning_idle;
guint refresh_idle;
/* Killswitch stuff */
GDBusProxy *rfkill_proxy;
GtkWidget *kill_switch_header;
GtkSwitch *rfkill_switch;
/* wireless dialog stuff */
CmdlineOperation arg_operation;
gchar *arg_device;
gchar *arg_access_point;
gboolean operation_done;
};
enum {
PANEL_DEVICES_COLUMN_ICON,
PANEL_DEVICES_COLUMN_TITLE,
PANEL_DEVICES_COLUMN_SORT,
PANEL_DEVICES_COLUMN_OBJECT,
PANEL_DEVICES_COLUMN_LAST
};
enum {
PROP_0,
PROP_PARAMETERS
};
static NetObject *find_in_model_by_id (CcNetworkPanel *panel, const gchar *id, GtkTreeIter *iter_out);
static void handle_argv (CcNetworkPanel *panel);
static void
cc_network_panel_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
switch (property_id) {
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
}
static CmdlineOperation
cmdline_operation_from_string (const gchar *string)
{
if (g_strcmp0 (string, "create-wifi") == 0)
return OPERATION_CREATE_WIFI;
if (g_strcmp0 (string, "connect-hidden-wifi") == 0)
return OPERATION_CONNECT_HIDDEN;
if (g_strcmp0 (string, "connect-8021x-wifi") == 0)
return OPERATION_CONNECT_8021X;
if (g_strcmp0 (string, "connect-3g") == 0)
return OPERATION_CONNECT_MOBILE;
if (g_strcmp0 (string, "show-device") == 0)
return OPERATION_SHOW_DEVICE;
g_warning ("Invalid additional argument %s", string);
return OPERATION_NULL;
}
static void
reset_command_line_args (CcNetworkPanel *self)
{
self->priv->arg_operation = OPERATION_NULL;
g_clear_pointer (&self->priv->arg_device, g_free);
g_clear_pointer (&self->priv->arg_access_point, g_free);
}
static gboolean
verify_argv (CcNetworkPanel *self,
const char **args)
{
switch (self->priv->arg_operation) {
case OPERATION_CONNECT_MOBILE:
case OPERATION_CONNECT_8021X:
case OPERATION_SHOW_DEVICE:
if (self->priv->arg_device == NULL) {
g_warning ("Operation %s requires an object path", args[0]);
return FALSE;
}
default:
return TRUE;
}
}
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)) {
if (g_variant_is_of_type (v, G_VARIANT_TYPE_STRING))
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 void
cc_network_panel_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
CcNetworkPanel *self = CC_NETWORK_PANEL (object);
CcNetworkPanelPrivate *priv = self->priv;
switch (property_id) {
case PROP_PARAMETERS: {
GVariant *parameters;
reset_command_line_args (self);
parameters = g_value_get_variant (value);
if (parameters) {
GPtrArray *array;
const gchar **args;
array = variant_av_to_string_array (parameters);
args = (const gchar **) array->pdata;
g_debug ("Invoked with operation %s", args[0]);
if (args[0])
priv->arg_operation = cmdline_operation_from_string (args[0]);
if (args[0] && args[1])
priv->arg_device = g_strdup (args[1]);
if (args[0] && args[1] && args[2])
priv->arg_access_point = g_strdup (args[2]);
if (verify_argv (self, (const char **) args) == FALSE) {
reset_command_line_args (self);
g_ptr_array_unref (array);
return;
}
g_ptr_array_unref (array);
g_debug ("Calling handle_argv() after setting property");
handle_argv (self);
}
break;
}
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
}
static void
cc_network_panel_dispose (GObject *object)
{
CcNetworkPanelPrivate *priv = CC_NETWORK_PANEL (object)->priv;
if (priv->cancellable != NULL)
g_cancellable_cancel (priv->cancellable);
g_clear_object (&priv->cancellable);
g_clear_object (&priv->builder);
g_clear_object (&priv->client);
g_clear_object (&priv->modem_manager);
g_clear_object (&priv->remote_settings);
g_clear_object (&priv->kill_switch_header);
priv->rfkill_switch = NULL;
if (priv->refresh_idle != 0) {
g_source_remove (priv->refresh_idle);
priv->refresh_idle = 0;
}
if (priv->nm_warning_idle != 0) {
g_source_remove (priv->nm_warning_idle);
priv->nm_warning_idle = 0;
}
G_OBJECT_CLASS (cc_network_panel_parent_class)->dispose (object);
}
static void
cc_network_panel_finalize (GObject *object)
{
CcNetworkPanel *panel = CC_NETWORK_PANEL (object);
reset_command_line_args (panel);
G_OBJECT_CLASS (cc_network_panel_parent_class)->finalize (object);
}
static const char *
cc_network_panel_get_help_uri (CcPanel *panel)
{
return "help:gnome-help/net";
}
static void
cc_network_panel_notify_enable_active_cb (GtkSwitch *sw,
GParamSpec *pspec,
CcNetworkPanel *panel)
{
CcNetworkPanelPrivate *priv = panel->priv;
gboolean enable;
enable = gtk_switch_get_active (sw);
g_dbus_proxy_call (priv->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,
priv->cancellable,
NULL, NULL);
}
static void
sync_airplane_mode_switch (CcNetworkPanel *panel)
{
GVariant *result;
gboolean enabled;
gboolean hw_enabled;
result = g_dbus_proxy_get_cached_property (panel->priv->rfkill_proxy, "HasAirplaneMode");
enabled = g_variant_get_boolean (result);
gtk_widget_set_visible (GTK_WIDGET (panel->priv->kill_switch_header), enabled);
if (!enabled)
return;
result = g_dbus_proxy_get_cached_property (panel->priv->rfkill_proxy, "AirplaneMode");
enabled = g_variant_get_boolean (result);
result = g_dbus_proxy_get_cached_property (panel->priv->rfkill_proxy, "HardwareAirplaneMode");
hw_enabled = !!g_variant_get_boolean (result);
enabled |= hw_enabled;
if (enabled != gtk_switch_get_active (panel->priv->rfkill_switch)) {
g_signal_handlers_block_by_func (panel->priv->rfkill_switch,
cc_network_panel_notify_enable_active_cb,
panel);
gtk_switch_set_active (panel->priv->rfkill_switch, enabled);
g_signal_handlers_unblock_by_func (panel->priv->rfkill_switch,
cc_network_panel_notify_enable_active_cb,
panel);
}
gtk_widget_set_sensitive (GTK_WIDGET (panel->priv->rfkill_switch), !hw_enabled);
}
static void
on_property_change (GDBusProxy *proxy,
GVariant *changed_properties,
GVariant *invalidated_properties,
gpointer user_data)
{
sync_airplane_mode_switch (CC_NETWORK_PANEL (user_data));
}
static void
got_rfkill_proxy_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
{
GError *error = NULL;
CcNetworkPanel *panel = CC_NETWORK_PANEL (user_data);
panel->priv->rfkill_proxy = g_dbus_proxy_new_for_bus_finish (res, &error);
if (panel->priv->rfkill_proxy == NULL) {
g_printerr ("Error creating rfkill proxy: %s\n", error->message);
g_error_free (error);
return;
}
g_signal_connect (panel->priv->rfkill_proxy, "g-properties-changed",
G_CALLBACK (on_property_change), panel);
sync_airplane_mode_switch (panel);
}
static void
cc_network_panel_constructed (GObject *object)
{
CcNetworkPanel *panel = CC_NETWORK_PANEL (object);
GtkWidget *box;
GtkWidget *label;
GtkWidget *widget;
G_OBJECT_CLASS (cc_network_panel_parent_class)->constructed (object);
/* add kill switch widgets */
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 3);
/* TRANSLATORS: this is to disable the radio hardware in the
* network panel */
label = gtk_label_new_with_mnemonic (_("Air_plane Mode"));
gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0);
gtk_widget_set_visible (label, TRUE);
widget = gtk_switch_new ();
gtk_widget_set_valign (widget, GTK_ALIGN_CENTER);
gtk_label_set_mnemonic_widget (GTK_LABEL (label), widget);
gtk_box_pack_start (GTK_BOX (box), widget, FALSE, FALSE, 4);
gtk_widget_show_all (box);
panel->priv->rfkill_switch = GTK_SWITCH (widget);
cc_shell_embed_widget_in_header (cc_panel_get_shell (CC_PANEL (panel)), box);
panel->priv->kill_switch_header = g_object_ref (box);
g_dbus_proxy_new_for_bus (G_BUS_TYPE_SESSION,
G_DBUS_PROXY_FLAGS_NONE,
NULL,
"org.gnome.SettingsDaemon.Rfkill",
"/org/gnome/SettingsDaemon/Rfkill",
"org.gnome.SettingsDaemon.Rfkill",
panel->priv->cancellable,
got_rfkill_proxy_cb,
panel);
g_signal_connect (panel->priv->rfkill_switch, "notify::active",
G_CALLBACK (cc_network_panel_notify_enable_active_cb),
panel);
}
static void
cc_network_panel_class_init (CcNetworkPanelClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
CcPanelClass *panel_class = CC_PANEL_CLASS (klass);
g_type_class_add_private (klass, sizeof (CcNetworkPanelPrivate));
panel_class->get_help_uri = cc_network_panel_get_help_uri;
object_class->get_property = cc_network_panel_get_property;
object_class->set_property = cc_network_panel_set_property;
object_class->dispose = cc_network_panel_dispose;
object_class->finalize = cc_network_panel_finalize;
object_class->constructed = cc_network_panel_constructed;
g_object_class_override_property (object_class, PROP_PARAMETERS, "parameters");
}
static NetObject *
get_selected_object (CcNetworkPanel *panel)
{
GtkTreeSelection *selection;
GtkTreeModel *model;
GtkTreeIter iter;
NetObject *object = NULL;
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (panel->priv->treeview));
if (!gtk_tree_selection_get_selected (selection, &model, &iter)) {
return NULL;
}
gtk_tree_model_get (model, &iter,
PANEL_DEVICES_COLUMN_OBJECT, &object,
-1);
return object;
}
static void
select_first_device (CcNetworkPanel *panel)
{
GtkTreePath *path;
GtkTreeSelection *selection;
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (panel->priv->treeview));
/* select the first device */
path = gtk_tree_path_new_from_string ("0");
gtk_tree_selection_select_path (selection, path);
gtk_tree_path_free (path);
}
static void
select_tree_iter (CcNetworkPanel *panel, GtkTreeIter *iter)
{
GtkTreeSelection *selection;
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (panel->priv->treeview));
gtk_tree_selection_select_iter (selection, iter);
}
static void
object_removed_cb (NetObject *object, CcNetworkPanel *panel)
{
gboolean ret;
NetObject *object_tmp;
GtkTreeIter iter;
GtkTreeModel *model;
GtkTreeSelection *selection;
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (panel->priv->treeview));
/* remove device from model */
model = GTK_TREE_MODEL (gtk_builder_get_object (panel->priv->builder,
"liststore_devices"));
ret = gtk_tree_model_get_iter_first (model, &iter);
if (!ret)
return;
/* get the other elements */
do {
gtk_tree_model_get (model, &iter,
PANEL_DEVICES_COLUMN_OBJECT, &object_tmp,
-1);
if (g_strcmp0 (net_object_get_id (object),
net_object_get_id (object_tmp)) == 0) {
g_object_unref (object_tmp);
if (!gtk_list_store_remove (GTK_LIST_STORE (model), &iter))
gtk_tree_model_get_iter_first (model, &iter);
gtk_tree_selection_select_iter (selection, &iter);
break;
}
g_object_unref (object_tmp);
} while (gtk_tree_model_iter_next (model, &iter));
}
GPtrArray *
cc_network_panel_get_devices (CcNetworkPanel *panel)
{
GPtrArray *devices;
GtkTreeModel *model;
GtkTreeIter iter;
NetObject *object;
devices = g_ptr_array_new_with_free_func (g_object_unref);
model = GTK_TREE_MODEL (gtk_builder_get_object (panel->priv->builder,
"liststore_devices"));
if (!gtk_tree_model_get_iter_first (model, &iter))
return devices;
do {
gtk_tree_model_get (model, &iter,
PANEL_DEVICES_COLUMN_OBJECT, &object,
-1);
if (NET_IS_DEVICE (object))
g_ptr_array_add (devices, object);
} while (gtk_tree_model_iter_next (model, &iter));
return devices;
}
static void
panel_refresh_device_titles (CcNetworkPanel *panel)
{
GPtrArray *ndarray, *nmdarray;
NetDevice **devices;
NMDevice **nm_devices, *nm_device;
gchar **titles;
gint i, num_devices;
ndarray = cc_network_panel_get_devices (panel);
if (!ndarray->len) {
g_ptr_array_free (ndarray, TRUE);
return;
}
nmdarray = g_ptr_array_new ();
for (i = 0; i < ndarray->len; i++) {
nm_device = net_device_get_nm_device (ndarray->pdata[i]);
if (nm_device)
g_ptr_array_add (nmdarray, nm_device);
else
g_ptr_array_remove_index (ndarray, i--);
}
devices = (NetDevice **)ndarray->pdata;
nm_devices = (NMDevice **)nmdarray->pdata;
num_devices = ndarray->len;
titles = nma_utils_disambiguate_device_names (nm_devices, num_devices);
for (i = 0; i < num_devices; i++) {
net_object_set_title (NET_OBJECT (devices[i]), titles[i]);
g_free (titles[i]);
}
g_free (titles);
g_ptr_array_free (ndarray, TRUE);
g_ptr_array_free (nmdarray, TRUE);
}
static gboolean
handle_argv_for_device (CcNetworkPanel *panel,
NMDevice *device,
GtkTreeIter *iter)
{
CcNetworkPanelPrivate *priv = panel->priv;
NMDeviceType type;
GtkWidget *toplevel = cc_shell_get_toplevel (cc_panel_get_shell (CC_PANEL (panel)));
if (priv->arg_operation == OPERATION_NULL)
return TRUE;
type = nm_device_get_device_type (device);
if (type == NM_DEVICE_TYPE_WIFI &&
(priv->arg_operation == OPERATION_CREATE_WIFI ||
priv->arg_operation == OPERATION_CONNECT_HIDDEN)) {
g_debug ("Selecting wifi device");
select_tree_iter (panel, iter);
if (priv->arg_operation == OPERATION_CREATE_WIFI)
cc_network_panel_create_wifi_network (toplevel, priv->client, priv->remote_settings);
else
cc_network_panel_connect_to_hidden_network (toplevel, priv->client, priv->remote_settings);
reset_command_line_args (panel); /* done */
return TRUE;
} else if (g_strcmp0 (nm_object_get_path (NM_OBJECT (device)), priv->arg_device) == 0) {
if (priv->arg_operation == OPERATION_CONNECT_MOBILE) {
cc_network_panel_connect_to_3g_network (toplevel, priv->client, priv->remote_settings, device);
reset_command_line_args (panel); /* done */
select_tree_iter (panel, iter);
return TRUE;
} else if (priv->arg_operation == OPERATION_CONNECT_8021X) {
cc_network_panel_connect_to_8021x_network (toplevel, priv->client, priv->remote_settings, device, priv->arg_access_point);
reset_command_line_args (panel); /* done */
select_tree_iter (panel, iter);
return TRUE;
}
else if (priv->arg_operation == OPERATION_SHOW_DEVICE) {
select_tree_iter (panel, iter);
reset_command_line_args (panel); /* done */
return TRUE;
}
}
return FALSE;
}
static gboolean
handle_argv_for_connection (CcNetworkPanel *panel,
NMConnection *connection,
GtkTreeIter *iter)
{
CcNetworkPanelPrivate *priv = panel->priv;
if (priv->arg_operation == OPERATION_NULL)
return TRUE;
if (priv->arg_operation != OPERATION_SHOW_DEVICE)
return FALSE;
if (g_strcmp0 (nm_connection_get_path (connection), priv->arg_device) == 0) {
reset_command_line_args (panel);
select_tree_iter (panel, iter);
return TRUE;
}
return FALSE;
}
static void
handle_argv (CcNetworkPanel *panel)
{
GtkTreeModel *model;
GtkTreeIter iter;
gboolean ret;
if (panel->priv->arg_operation == OPERATION_NULL)
return;
model = GTK_TREE_MODEL (gtk_builder_get_object (panel->priv->builder,
"liststore_devices"));
ret = gtk_tree_model_get_iter_first (model, &iter);
while (ret) {
GObject *object_tmp;
NMDevice *device;
NMConnection *connection;
gboolean done = FALSE;
gtk_tree_model_get (model, &iter,
PANEL_DEVICES_COLUMN_OBJECT, &object_tmp,
-1);
if (NET_IS_DEVICE (object_tmp)) {
g_object_get (object_tmp, "nm-device", &device, NULL);
done = handle_argv_for_device (panel, device, &iter);
g_object_unref (device);
} else if (NET_IS_VPN (object_tmp) || NET_IS_VIRTUAL_DEVICE (object_tmp)) {
g_object_get (object_tmp, "connection", &connection, NULL);
done = handle_argv_for_connection (panel, connection, &iter);
g_object_unref (connection);
}
g_object_unref (object_tmp);
if (done)
return;
ret = gtk_tree_model_iter_next (model, &iter);
}
g_debug ("Could not handle argv operation, no matching device yet?");
}
static void
state_changed_cb (NMDevice *device,
NMDeviceState new_state,
NMDeviceState old_state,
NMDeviceStateReason reason,
CcNetworkPanel *panel)
{
GtkListStore *store;
GtkTreeIter iter;
if (!find_in_model_by_id (panel, nm_device_get_udi (device), &iter)) {
return;
}
store = GTK_LIST_STORE (gtk_builder_get_object (panel->priv->builder,
"liststore_devices"));
gtk_list_store_set (store, &iter,
PANEL_DEVICES_COLUMN_ICON, panel_device_to_icon_name (device, TRUE),
-1);
}
static gboolean
panel_add_device (CcNetworkPanel *panel, NMDevice *device)
{
GtkListStore *liststore_devices;
GtkTreeIter iter;
NMDeviceType type;
NetDevice *net_device;
CcNetworkPanelPrivate *priv = panel->priv;
GtkNotebook *notebook;
GtkSizeGroup *size_group;
GType device_g_type;
if (!nm_device_get_managed (device))
goto out;
/* do we have an existing object with this id? */
if (find_in_model_by_id (panel, nm_device_get_udi (device), NULL) != NULL)
goto out;
type = nm_device_get_device_type (device);
g_debug ("device %s type %i path %s",
nm_device_get_udi (device), type, nm_object_get_path (NM_OBJECT (device)));
/* map the NMDeviceType to the GType */
switch (type) {
case NM_DEVICE_TYPE_ETHERNET:
device_g_type = NET_TYPE_DEVICE_ETHERNET;
break;
case NM_DEVICE_TYPE_MODEM:
device_g_type = NET_TYPE_DEVICE_MOBILE;
break;
case NM_DEVICE_TYPE_WIFI:
device_g_type = NET_TYPE_DEVICE_WIFI;
break;
case NM_DEVICE_TYPE_BOND:
case NM_DEVICE_TYPE_BRIDGE:
case NM_DEVICE_TYPE_VLAN:
goto out;
default:
device_g_type = NET_TYPE_DEVICE_SIMPLE;
break;
}
/* create device */
net_device = g_object_new (device_g_type,
"panel", panel,
"removable", FALSE,
"cancellable", panel->priv->cancellable,
"client", panel->priv->client,
"remote-settings", panel->priv->remote_settings,
"nm-device", device,
"id", nm_device_get_udi (device),
NULL);
if (type == NM_DEVICE_TYPE_MODEM &&
g_str_has_prefix (nm_device_get_udi (device), "/org/freedesktop/ModemManager1/Modem/")) {
GDBusObject *modem_object;
if (priv->modem_manager == NULL) {
g_warning ("Cannot grab information for modem at %s: No ModemManager support",
nm_device_get_udi (device));
goto out;
}
modem_object = g_dbus_object_manager_get_object (G_DBUS_OBJECT_MANAGER (priv->modem_manager),
nm_device_get_udi (device));
if (modem_object == NULL) {
g_warning ("Cannot grab information for modem at %s: Not found",
nm_device_get_udi (device));
goto out;
}
/* Set the modem object in the NetDeviceMobile */
g_object_set (net_device,
"mm-object", modem_object,
NULL);
g_object_unref (modem_object);
}
/* add as a panel */
if (device_g_type != NET_TYPE_DEVICE) {
notebook = GTK_NOTEBOOK (gtk_builder_get_object (panel->priv->builder,
"notebook_types"));
size_group = GTK_SIZE_GROUP (gtk_builder_get_object (panel->priv->builder,
"sizegroup1"));
net_object_add_to_notebook (NET_OBJECT (net_device),
notebook,
size_group);
}
liststore_devices = GTK_LIST_STORE (gtk_builder_get_object (priv->builder,
"liststore_devices"));
g_signal_connect_object (net_device, "removed",
G_CALLBACK (object_removed_cb), panel, 0);
gtk_list_store_append (liststore_devices, &iter);
gtk_list_store_set (liststore_devices,
&iter,
PANEL_DEVICES_COLUMN_ICON, panel_device_to_icon_name (device, TRUE),
PANEL_DEVICES_COLUMN_SORT, panel_device_to_sortable_string (device),
PANEL_DEVICES_COLUMN_OBJECT, net_device,
-1);
g_object_unref (net_device);
g_signal_connect (device, "state-changed",
G_CALLBACK (state_changed_cb), panel);
out:
return FALSE;
}
static void
panel_remove_device (CcNetworkPanel *panel, NMDevice *device)
{
gboolean ret;
NetObject *object_tmp;
GtkTreeIter iter;
GtkTreeModel *model;
/* remove device from model */
model = GTK_TREE_MODEL (gtk_builder_get_object (panel->priv->builder,
"liststore_devices"));
ret = gtk_tree_model_get_iter_first (model, &iter);
if (!ret)
return;
/* get the other elements */
do {
gtk_tree_model_get (model, &iter,
PANEL_DEVICES_COLUMN_OBJECT, &object_tmp,
-1);
if (g_strcmp0 (net_object_get_id (object_tmp),
nm_device_get_udi (device)) == 0) {
gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
g_object_unref (object_tmp);
break;
}
g_object_unref (object_tmp);
} while (gtk_tree_model_iter_next (model, &iter));
}
static void
get_object_title (GtkTreeViewColumn *column,
GtkCellRenderer *cell,
GtkTreeModel *model,
GtkTreeIter *iter,
gpointer data)
{
NetObject *object;
gtk_tree_model_get (model, iter,
PANEL_DEVICES_COLUMN_OBJECT, &object,
-1);
if (!object)
return;
g_object_set (cell, "text", net_object_get_title (object), NULL);
g_object_unref (object);
}
static void
panel_add_devices_columns (CcNetworkPanel *panel, GtkTreeView *treeview)
{
CcNetworkPanelPrivate *priv = panel->priv;
GtkCellRenderer *renderer;
GtkListStore *liststore_devices;
GtkTreeViewColumn *column;
/* image */
renderer = gtk_cell_renderer_pixbuf_new ();
g_object_set (renderer,
"width", 32,
"xalign", 1.0,
"stock-size", GTK_ICON_SIZE_MENU,
"follow-state", TRUE,
NULL);
gtk_cell_renderer_set_padding (renderer, 4, 10);
column = gtk_tree_view_column_new_with_attributes ("icon", renderer,
"icon-name", PANEL_DEVICES_COLUMN_ICON,
NULL);
gtk_tree_view_append_column (treeview, column);
/* column for text */
renderer = gtk_cell_renderer_text_new ();
g_object_set (renderer,
"wrap-mode", PANGO_WRAP_WORD,
"ellipsize", PANGO_ELLIPSIZE_END,
NULL);
column = gtk_tree_view_column_new_with_attributes ("title", renderer, NULL);
gtk_tree_view_column_set_cell_data_func (GTK_TREE_VIEW_COLUMN (column),
renderer,
get_object_title,
NULL, NULL);
gtk_tree_view_column_set_sort_column_id (column, PANEL_DEVICES_COLUMN_SORT);
liststore_devices = GTK_LIST_STORE (gtk_builder_get_object (priv->builder,
"liststore_devices"));
gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (liststore_devices),
PANEL_DEVICES_COLUMN_SORT,
GTK_SORT_ASCENDING);
gtk_tree_view_append_column (treeview, column);
gtk_tree_view_column_set_expand (column, TRUE);
}
static void
nm_devices_treeview_clicked_cb (GtkTreeSelection *selection, CcNetworkPanel *panel)
{
CcNetworkPanelPrivate *priv = panel->priv;
const gchar *id_tmp;
const gchar *needle;
GList *l;
GList *panels = NULL;
GtkNotebook *notebook;
GtkTreeIter iter;
GtkTreeModel *model;
GtkWidget *widget;
guint i = 0;
NetObject *object = NULL;
if (!gtk_tree_selection_get_selected (selection, &model, &iter)) {
g_debug ("no row selected");
goto out;
}
/* find the widget in the notebook that matches the object ID */
object = get_selected_object (panel);
needle = net_object_get_id (object);
notebook = GTK_NOTEBOOK (gtk_builder_get_object (priv->builder,
"notebook_types"));
panels = gtk_container_get_children (GTK_CONTAINER (notebook));
for (l = panels; l != NULL; l = l->next) {
widget = GTK_WIDGET (l->data);
id_tmp = g_object_get_data (G_OBJECT (widget), "NetObject::id");
if (g_strcmp0 (needle, id_tmp) == 0) {
gtk_notebook_set_current_page (notebook, i);
/* object is deletable? */
widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
"remove_toolbutton"));
gtk_widget_set_sensitive (widget,
net_object_get_removable (object));
break;
}
i++;
}
g_object_unref (object);
out:
g_list_free (panels);
}
static void
panel_add_proxy_device (CcNetworkPanel *panel)
{
gchar *title;
GtkListStore *liststore_devices;
GtkTreeIter iter;
NetProxy *proxy;
GtkNotebook *notebook;
GtkSizeGroup *size_group;
/* add proxy to notebook */
proxy = net_proxy_new ();
notebook = GTK_NOTEBOOK (gtk_builder_get_object (panel->priv->builder,
"notebook_types"));
size_group = GTK_SIZE_GROUP (gtk_builder_get_object (panel->priv->builder,
"sizegroup1"));
net_object_add_to_notebook (NET_OBJECT (proxy),
notebook,
size_group);
/* add proxy to device list */
liststore_devices = GTK_LIST_STORE (gtk_builder_get_object (panel->priv->builder,
"liststore_devices"));
title = g_strdup_printf ("%s", _("Network proxy"));
net_object_set_title (NET_OBJECT (proxy), title);
gtk_list_store_append (liststore_devices, &iter);
gtk_list_store_set (liststore_devices,
&iter,
PANEL_DEVICES_COLUMN_ICON, "preferences-system-network-symbolic",
PANEL_DEVICES_COLUMN_SORT, "9",
PANEL_DEVICES_COLUMN_OBJECT, proxy,
-1);
g_free (title);
g_object_unref (proxy);
}
static void
connection_state_changed (NMActiveConnection *c, GParamSpec *pspec, CcNetworkPanel *panel)
{
}
static void
active_connections_changed (NMClient *client, GParamSpec *pspec, gpointer user_data)
{
CcNetworkPanel *panel = user_data;
const GPtrArray *connections;
int i, j;
g_debug ("Active connections changed:");
connections = nm_client_get_active_connections (client);
for (i = 0; connections && (i < connections->len); i++) {
NMActiveConnection *connection;
const GPtrArray *devices;
connection = g_ptr_array_index (connections, i);
g_debug (" %s", nm_object_get_path (NM_OBJECT (connection)));
devices = nm_active_connection_get_devices (connection);
for (j = 0; devices && j < devices->len; j++)
g_debug (" %s", nm_device_get_udi (g_ptr_array_index (devices, j)));
if (NM_IS_VPN_CONNECTION (connection))
g_debug (" VPN base connection: %s", nm_active_connection_get_specific_object (connection));
if (g_object_get_data (G_OBJECT (connection), "has-state-changed-handler") == NULL) {
g_signal_connect_object (connection, "notify::state",
G_CALLBACK (connection_state_changed), panel, 0);
g_object_set_data (G_OBJECT (connection), "has-state-changed-handler", GINT_TO_POINTER (TRUE));
}
}
}
static void
device_added_cb (NMClient *client, NMDevice *device, CcNetworkPanel *panel)
{
g_debug ("New device added");
panel_add_device (panel, device);
panel_refresh_device_titles (panel);
}
static void
device_removed_cb (NMClient *client, NMDevice *device, CcNetworkPanel *panel)
{
g_debug ("Device removed");
panel_remove_device (panel, device);
panel_refresh_device_titles (panel);
}
static void
manager_running (NMClient *client, GParamSpec *pspec, gpointer user_data)
{
const GPtrArray *devices;
int i;
NMDevice *device_tmp;
GtkListStore *liststore_devices;
gboolean selected = FALSE;
CcNetworkPanel *panel = CC_NETWORK_PANEL (user_data);
/* clear all devices we added */
if (!nm_client_get_manager_running (client)) {
g_debug ("NM disappeared");
liststore_devices = GTK_LIST_STORE (gtk_builder_get_object (panel->priv->builder,
"liststore_devices"));
gtk_list_store_clear (liststore_devices);
panel_add_proxy_device (panel);
goto out;
}
g_debug ("coldplugging devices");
devices = nm_client_get_devices (client);
if (devices == NULL) {
g_debug ("No devices to add");
return;
}
for (i = 0; i < devices->len; i++) {
device_tmp = g_ptr_array_index (devices, i);
selected = panel_add_device (panel, device_tmp) || selected;
}
out:
if (!selected) {
/* select the first device */
select_first_device (panel);
}
panel_refresh_device_titles (panel);
g_debug ("Calling handle_argv() after cold-plugging devices");
handle_argv (panel);
}
static NetObject *
find_in_model_by_id (CcNetworkPanel *panel, const gchar *id, GtkTreeIter *iter_out)
{
gboolean ret;
NetObject *object_tmp;
GtkTreeIter iter;
GtkTreeModel *model;
NetObject *object = NULL;
/* find in model */
model = GTK_TREE_MODEL (gtk_builder_get_object (panel->priv->builder,
"liststore_devices"));
ret = gtk_tree_model_get_iter_first (model, &iter);
if (!ret)
goto out;
/* get the other elements */
ret = FALSE;
do {
gtk_tree_model_get (model, &iter,
PANEL_DEVICES_COLUMN_OBJECT, &object_tmp,
-1);
if (object_tmp != NULL) {
g_debug ("got %s", net_object_get_id (object_tmp));
if (g_strcmp0 (net_object_get_id (object_tmp), id) == 0)
object = object_tmp;
g_object_unref (object_tmp);
}
} while (object == NULL && gtk_tree_model_iter_next (model, &iter));
out:
if (iter_out)
*iter_out = iter;
return object;
}
static void
panel_add_vpn_device (CcNetworkPanel *panel, NMConnection *connection)
{
gchar *title;
GtkListStore *liststore_devices;
GtkTreeIter iter;
NetVpn *net_vpn;
const gchar *id;
GtkNotebook *notebook;
GtkSizeGroup *size_group;
/* does already exist */
id = nm_connection_get_path (connection);
if (find_in_model_by_id (panel, id, NULL) != NULL)
return;
/* add as a virtual object */
net_vpn = g_object_new (NET_TYPE_VPN,
"panel", panel,
"removable", TRUE,
"id", id,
"connection", connection,
"client", panel->priv->client,
"remote-settings", panel->priv->remote_settings,
NULL);
g_signal_connect_object (net_vpn, "removed",
G_CALLBACK (object_removed_cb), panel, 0);
/* add as a panel */
notebook = GTK_NOTEBOOK (gtk_builder_get_object (panel->priv->builder,
"notebook_types"));
size_group = GTK_SIZE_GROUP (gtk_builder_get_object (panel->priv->builder,
"sizegroup1"));
net_object_add_to_notebook (NET_OBJECT (net_vpn),
notebook,
size_group);
liststore_devices = GTK_LIST_STORE (gtk_builder_get_object (panel->priv->builder,
"liststore_devices"));
title = g_strdup_printf (_("%s VPN"), nm_connection_get_id (connection));
net_object_set_title (NET_OBJECT (net_vpn), title);
gtk_list_store_append (liststore_devices, &iter);
gtk_list_store_set (liststore_devices,
&iter,
PANEL_DEVICES_COLUMN_ICON, "network-vpn-symbolic",
PANEL_DEVICES_COLUMN_SORT, "5",
PANEL_DEVICES_COLUMN_OBJECT, net_vpn,
-1);
g_free (title);
}
static void
panel_add_virtual_device (CcNetworkPanel *panel, NMConnection *connection)
{
gchar *title;
GtkListStore *liststore_devices;
GtkTreeIter iter;
NetVirtualDevice *net_virt;
const gchar *id;
GtkNotebook *notebook;
GtkSizeGroup *size_group;
NMSettingConnection *s_con;
const gchar *connection_type;
GType device_g_type;
/* does already exist */
id = nm_connection_get_path (connection);
if (find_in_model_by_id (panel, id, NULL) != NULL)
return;
/* map the NMConnection to a NetDevice GType */
s_con = nm_connection_get_setting_connection (connection);
connection_type = nm_setting_connection_get_connection_type (s_con);
if (!strcmp (connection_type, NM_SETTING_BOND_SETTING_NAME))
device_g_type = NET_TYPE_DEVICE_BOND;
else if (!strcmp (connection_type, NM_SETTING_BRIDGE_SETTING_NAME))
device_g_type = NET_TYPE_DEVICE_BRIDGE;
else
device_g_type = NET_TYPE_VIRTUAL_DEVICE;
/* add as a virtual object */
net_virt = g_object_new (device_g_type,
"panel", panel,
"removable", TRUE,
"id", id,
"connection", connection,
"client", panel->priv->client,
"remote-settings", panel->priv->remote_settings,
NULL);
g_signal_connect_object (net_virt, "removed",
G_CALLBACK (object_removed_cb), panel, 0);
/* add as a panel */
notebook = GTK_NOTEBOOK (gtk_builder_get_object (panel->priv->builder,
"notebook_types"));
size_group = GTK_SIZE_GROUP (gtk_builder_get_object (panel->priv->builder,
"sizegroup1"));
net_object_add_to_notebook (NET_OBJECT (net_virt),
notebook,
size_group);
liststore_devices = GTK_LIST_STORE (gtk_builder_get_object (panel->priv->builder,
"liststore_devices"));
title = nma_utils_get_connection_device_name (connection);
net_object_set_title (NET_OBJECT (net_virt), title);
gtk_list_store_append (liststore_devices, &iter);
gtk_list_store_set (liststore_devices,
&iter,
PANEL_DEVICES_COLUMN_ICON, "network-wired-symbolic",
PANEL_DEVICES_COLUMN_SORT, "2",
PANEL_DEVICES_COLUMN_OBJECT, net_virt,
-1);
g_free (title);
}
static void
add_connection (CcNetworkPanel *panel,
NMConnection *connection)
{
NMSettingConnection *s_con;
const gchar *type, *iface;
s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection,
NM_TYPE_SETTING_CONNECTION));
type = nm_setting_connection_get_connection_type (s_con);
iface = nm_connection_get_virtual_iface_name (connection);
if (g_strcmp0 (type, "vpn") != 0 && iface == NULL)
return;
#ifdef HAVE_NM_UNSTABLE
/* Don't add the libvirtd bridge to the UI */
if (g_strcmp0 (nm_setting_connection_get_interface_name (s_con), "virbr0") == 0)
return;
#endif
g_debug ("add %s/%s remote connection: %s",
type, g_type_name_from_instance ((GTypeInstance*)connection),
nm_connection_get_path (connection));
if (iface)
panel_add_virtual_device (panel, connection);
else
panel_add_vpn_device (panel, connection);
}
static void
notify_new_connection_cb (NMRemoteSettings *settings,
NMRemoteConnection *connection,
CcNetworkPanel *panel)
{
add_connection (panel, NM_CONNECTION (connection));
}
static void
notify_connections_read_cb (NMRemoteSettings *settings,
CcNetworkPanel *panel)
{
GSList *list, *iter;
NMConnection *connection;
list = nm_remote_settings_list_connections (settings);
g_debug ("%p has %i remote connections",
panel, g_slist_length (list));
for (iter = list; iter; iter = g_slist_next (iter)) {
connection = NM_CONNECTION (iter->data);
add_connection (panel, connection);
}
g_slist_free (list);
g_debug ("Calling handle_argv() after cold-plugging connections");
handle_argv (panel);
}
static gboolean
display_version_warning_idle (CcNetworkPanel *panel)
{
GtkWidget *dialog;
GtkWidget *image;
GtkWindow *window;
const char *message;
/* TRANSLATORS: the user is running a NM that is not API compatible */
message = _("The system network services are not compatible with this version.");
window = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (panel)));
dialog = gtk_message_dialog_new (window,
GTK_DIALOG_MODAL,
GTK_MESSAGE_ERROR,
GTK_BUTTONS_CLOSE,
"%s",
message);
image = gtk_image_new_from_icon_name ("computer-fail", GTK_ICON_SIZE_DIALOG);
gtk_widget_show (image);
gtk_message_dialog_set_image (GTK_MESSAGE_DIALOG (dialog), image);
gtk_dialog_run (GTK_DIALOG (dialog));
gtk_widget_destroy (dialog);
return FALSE;
}
static gboolean
panel_check_network_manager_version (CcNetworkPanel *panel)
{
const gchar *version;
gchar **split = NULL;
guint major = 0;
guint micro = 0;
guint minor = 0;
gboolean ret = TRUE;
/* parse running version */
version = nm_client_get_version (panel->priv->client);
if (version != NULL) {
split = g_strsplit (version, ".", -1);
major = atoi (split[0]);
minor = atoi (split[1]);
micro = atoi (split[2]);
}
/* is it too new or old */
if (major > 0 || major > 9 || (minor <= 8 && micro < 992)) {
ret = FALSE;
/* do modal dialog in idle so we don't block startup */
panel->priv->nm_warning_idle = g_idle_add ((GSourceFunc)display_version_warning_idle, panel);
}
g_strfreev (split);
return ret;
}
static void
editor_done (NetConnectionEditor *editor,
gboolean success,
gpointer user_data)
{
g_object_unref (editor);
}
static void
add_connection_cb (GtkToolButton *button, CcNetworkPanel *panel)
{
NetConnectionEditor *editor;
GtkWindow *toplevel;
toplevel = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (panel)));
editor = net_connection_editor_new (toplevel, NULL, NULL, NULL,
panel->priv->client,
panel->priv->remote_settings);
g_signal_connect (editor, "done", G_CALLBACK (editor_done), panel);
net_connection_editor_run (editor);
}
static void
remove_connection (GtkToolButton *button, CcNetworkPanel *panel)
{
NetObject *object;
/* get current device */
object = get_selected_object (panel);
if (object == NULL)
return;
/* delete the object */
net_object_delete (object);
g_object_unref (object);
}
static void
on_toplevel_map (GtkWidget *widget,
CcNetworkPanel *panel)
{
gboolean ret;
/* is the user compiling against a new version, but running an
* old daemon version? */
ret = panel_check_network_manager_version (panel);
if (ret) {
manager_running (panel->priv->client, NULL, panel);
} else {
/* just select the proxy settings */
select_first_device (panel);
}
}
static void
cc_network_panel_init (CcNetworkPanel *panel)
{
DBusGConnection *bus = NULL;
GError *error = NULL;
GtkStyleContext *context;
GtkTreeSelection *selection;
GtkWidget *widget;
GtkWidget *toplevel;
GDBusConnection *system_bus;
panel->priv = NETWORK_PANEL_PRIVATE (panel);
g_resources_register (cc_network_get_resource ());
panel->priv->builder = gtk_builder_new ();
gtk_builder_add_from_resource (panel->priv->builder,
"/org/gnome/control-center/network/network.ui",
&error);
if (error != NULL) {
g_warning ("Could not load interface file: %s", error->message);
g_error_free (error);
return;
}
panel->priv->cancellable = g_cancellable_new ();
panel->priv->treeview = GTK_WIDGET (gtk_builder_get_object (panel->priv->builder,
"treeview_devices"));
panel_add_devices_columns (panel, GTK_TREE_VIEW (panel->priv->treeview));
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (panel->priv->treeview));
gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
g_signal_connect (selection, "changed",
G_CALLBACK (nm_devices_treeview_clicked_cb), panel);
widget = GTK_WIDGET (gtk_builder_get_object (panel->priv->builder,
"devices_scrolledwindow"));
gtk_widget_set_size_request (widget, 200, -1);
context = gtk_widget_get_style_context (widget);
gtk_style_context_set_junction_sides (context, GTK_JUNCTION_BOTTOM);
widget = GTK_WIDGET (gtk_builder_get_object (panel->priv->builder,
"devices_toolbar"));
context = gtk_widget_get_style_context (widget);
gtk_style_context_set_junction_sides (context, GTK_JUNCTION_TOP);
/* add the virtual proxy device */
panel_add_proxy_device (panel);
/* use NetworkManager client */
panel->priv->client = nm_client_new ();
g_signal_connect (panel->priv->client, "notify::" NM_CLIENT_MANAGER_RUNNING,
G_CALLBACK (manager_running), panel);
g_signal_connect (panel->priv->client, "notify::" NM_CLIENT_ACTIVE_CONNECTIONS,
G_CALLBACK (active_connections_changed), panel);
g_signal_connect (panel->priv->client, "device-added",
G_CALLBACK (device_added_cb), panel);
g_signal_connect (panel->priv->client, "device-removed",
G_CALLBACK (device_removed_cb), panel);
/* Setup ModemManager client */
system_bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
if (system_bus == NULL) {
g_warning ("Error connecting to system D-Bus: %s",
error->message);
g_clear_error (&error);
} else {
panel->priv->modem_manager = mm_manager_new_sync (system_bus,
G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE,
NULL,
&error);
if (panel->priv->modem_manager == NULL) {
g_warning ("Error connecting to ModemManager: %s",
error->message);
g_clear_error (&error);
}
g_object_unref (system_bus);
}
widget = GTK_WIDGET (gtk_builder_get_object (panel->priv->builder,
"add_toolbutton"));
g_signal_connect (widget, "clicked",
G_CALLBACK (add_connection_cb), panel);
/* disable for now, until we actually show removable connections */
widget = GTK_WIDGET (gtk_builder_get_object (panel->priv->builder,
"remove_toolbutton"));
g_signal_connect (widget, "clicked",
G_CALLBACK (remove_connection), panel);
/* add remote settings such as VPN settings as virtual devices */
bus = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error);
if (bus == NULL) {
g_warning ("Error connecting to system D-Bus: %s",
error->message);
g_error_free (error);
}
panel->priv->remote_settings = nm_remote_settings_new (bus);
g_signal_connect (panel->priv->remote_settings, NM_REMOTE_SETTINGS_CONNECTIONS_READ,
G_CALLBACK (notify_connections_read_cb), panel);
g_signal_connect (panel->priv->remote_settings, NM_REMOTE_SETTINGS_NEW_CONNECTION,
G_CALLBACK (notify_new_connection_cb), panel);
toplevel = gtk_widget_get_toplevel (GTK_WIDGET (panel));
g_signal_connect_after (toplevel, "map", G_CALLBACK (on_toplevel_map), panel);
/* hide implementation details */
widget = GTK_WIDGET (gtk_builder_get_object (panel->priv->builder,
"notebook_types"));
gtk_notebook_set_show_tabs (GTK_NOTEBOOK (widget), FALSE);
widget = GTK_WIDGET (gtk_builder_get_object (panel->priv->builder,
"vbox1"));
gtk_container_add (GTK_CONTAINER (panel), widget);
}