/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
 *
 * Copyright (C) 2010-2011 Richard Hughes <richard@hughsie.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 */

#include <config.h>
#include <glib/gi18n.h>
#include <arpa/inet.h>
#include <stdlib.h>

#include "cc-network-panel.h"

#include "nm-remote-settings.h"
#include "nm-client.h"
#include "nm-device.h"
#include "nm-device-ethernet.h"
#include "nm-device-modem.h"
#include "nm-device-wifi.h"
#include "nm-utils.h"
#include "nm-active-connection.h"
#include "nm-vpn-connection.h"
#include "nm-setting-ip4-config.h"
#include "nm-setting-ip6-config.h"
#include "nm-setting-connection.h"
#include "nm-setting-vpn.h"
#include "nm-setting-wireless.h"

#include "net-object.h"
#include "net-device.h"
#include "net-vpn.h"

#include "panel-common.h"
#include "panel-cell-renderer-mode.h"
#include "panel-cell-renderer-signal.h"
#include "panel-cell-renderer-security.h"

G_DEFINE_DYNAMIC_TYPE (CcNetworkPanel, cc_network_panel, CC_TYPE_PANEL)

#define NETWORK_PANEL_PRIVATE(o) \
        (G_TYPE_INSTANCE_GET_PRIVATE ((o), CC_TYPE_NETWORK_PANEL, CcNetworkPanelPrivate))

struct _CcNetworkPanelPrivate
{
        GCancellable     *cancellable;
        GSettings        *proxy_settings;
        GtkBuilder       *builder;
        NMClient         *client;
        NMRemoteSettings *remote_settings;
        gboolean          updating_device;
};

enum {
        PANEL_DEVICES_COLUMN_ICON,
        PANEL_DEVICES_COLUMN_TITLE,
        PANEL_DEVICES_COLUMN_SORT,
        PANEL_DEVICES_COLUMN_OBJECT,
        PANEL_DEVICES_COLUMN_LAST
};

enum {
        PANEL_WIRELESS_COLUMN_ID,
        PANEL_WIRELESS_COLUMN_TITLE,
        PANEL_WIRELESS_COLUMN_SORT,
        PANEL_WIRELESS_COLUMN_STRENGTH,
        PANEL_WIRELESS_COLUMN_MODE,
        PANEL_WIRELESS_COLUMN_SECURITY,
        PANEL_WIRELESS_COLUMN_LAST
};

static void     refresh_ui      (CcNetworkPanel *panel);
static NetObject *find_in_model_by_id (CcNetworkPanel *panel, const gchar *id);

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 void
cc_network_panel_set_property (GObject      *object,
                               guint         property_id,
                               const GValue *value,
                               GParamSpec   *pspec)
{
        switch (property_id) {
        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->proxy_settings) {
                g_object_unref (priv->proxy_settings);
                priv->proxy_settings = NULL;
        }
        if (priv->cancellable != NULL) {
                g_cancellable_cancel (priv->cancellable);
                g_object_unref (priv->cancellable);
                priv->cancellable = NULL;
        }
        if (priv->builder != NULL) {
                g_object_unref (priv->builder);
                priv->builder = NULL;
        }
        if (priv->client != NULL) {
                g_object_unref (priv->client);
                priv->client = NULL;
        }
        if (priv->remote_settings != NULL) {
                g_object_unref (priv->remote_settings);
                priv->remote_settings = NULL;
        }

        G_OBJECT_CLASS (cc_network_panel_parent_class)->dispose (object);
}

static void
cc_network_panel_finalize (GObject *object)
{
        G_OBJECT_CLASS (cc_network_panel_parent_class)->finalize (object);
}

static void
cc_network_panel_class_init (CcNetworkPanelClass *klass)
{
        GObjectClass *object_class = G_OBJECT_CLASS (klass);

        g_type_class_add_private (klass, sizeof (CcNetworkPanelPrivate));

        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;
}

static void
cc_network_panel_class_finalize (CcNetworkPanelClass *klass)
{
}

static void
check_wpad_warning (CcNetworkPanel *panel)
{
        GtkWidget *widget;
        gchar *autoconfig_url = NULL;
        GString *string = NULL;
        gboolean ret = FALSE;
        guint mode;

        string = g_string_new ("");

        /* check we're using 'Automatic' */
        mode = g_settings_get_enum (panel->priv->proxy_settings, "mode");
        if (mode != 2)
                goto out;

        /* see if the PAC is blank */
        autoconfig_url = g_settings_get_string (panel->priv->proxy_settings,
                                                "autoconfig-url");
        ret = autoconfig_url == NULL ||
              autoconfig_url[0] == '\0';
        if (!ret)
                goto out;

        g_string_append (string, "<small>");

        /* TRANSLATORS: this is when the use leaves the PAC textbox blank */
        g_string_append (string, _("Web Proxy Autodiscovery is used when a Configuration URL is not provided."));

        g_string_append (string, " ");

        /* TRANSLATORS: WPAD is bad: if you enable it on an untrusted
         * network, then anyone else on that network can tell your
         * machine that it should proxy all of your web traffic
         * through them. */
        g_string_append (string, _("This is not recommended for untrusted public networks."));
        g_string_append (string, "</small>");
out:
        widget = GTK_WIDGET (gtk_builder_get_object (panel->priv->builder,
                                                     "label_proxy_warning"));
        gtk_label_set_markup (GTK_LABEL (widget), string->str);
        g_free (autoconfig_url);
        g_string_free (string, TRUE);
}

static void
panel_settings_changed (GSettings      *settings,
                        const gchar    *key,
                        CcNetworkPanel *panel)
{
        check_wpad_warning (panel);
}

static NetObject *
get_selected_object (CcNetworkPanel *panel)
{
        GtkWidget *widget;
        GtkTreeSelection *selection;
        GtkTreeModel *model;
        GtkTreeIter iter;
        NetObject *object = NULL;

        widget = GTK_WIDGET (gtk_builder_get_object (panel->priv->builder,
                                                     "treeview_devices"));
        selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
        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
panel_proxy_mode_combo_setup_widgets (CcNetworkPanel *panel, guint value)
{
        GtkWidget *widget;

        /* hide or show the PAC text box */
        widget = GTK_WIDGET (gtk_builder_get_object (panel->priv->builder,
                                                     "heading_proxy_url"));
        gtk_widget_set_visible (widget, value == 2);
        widget = GTK_WIDGET (gtk_builder_get_object (panel->priv->builder,
                                                     "entry_proxy_url"));
        gtk_widget_set_visible (widget, value == 2);

        /* hide or show the manual entry text boxes */
        widget = GTK_WIDGET (gtk_builder_get_object (panel->priv->builder,
                                                     "heading_proxy_http"));
        gtk_widget_set_visible (widget, value == 1);
        widget = GTK_WIDGET (gtk_builder_get_object (panel->priv->builder,
                                                     "entry_proxy_http"));
        gtk_widget_set_visible (widget, value == 1);
        widget = GTK_WIDGET (gtk_builder_get_object (panel->priv->builder,
                                                     "spinbutton_proxy_http"));
        gtk_widget_set_visible (widget, value == 1);

        widget = GTK_WIDGET (gtk_builder_get_object (panel->priv->builder,
                                                     "heading_proxy_https"));
        gtk_widget_set_visible (widget, value == 1);
        widget = GTK_WIDGET (gtk_builder_get_object (panel->priv->builder,
                                                     "entry_proxy_https"));
        gtk_widget_set_visible (widget, value == 1);
        widget = GTK_WIDGET (gtk_builder_get_object (panel->priv->builder,
                                                     "spinbutton_proxy_https"));
        gtk_widget_set_visible (widget, value == 1);
        widget = GTK_WIDGET (gtk_builder_get_object (panel->priv->builder,
                                                     "heading_proxy_ftp"));
        gtk_widget_set_visible (widget, value == 1);
        widget = GTK_WIDGET (gtk_builder_get_object (panel->priv->builder,
                                                     "entry_proxy_ftp"));
        gtk_widget_set_visible (widget, value == 1);
        widget = GTK_WIDGET (gtk_builder_get_object (panel->priv->builder,
                                                     "spinbutton_proxy_ftp"));
        gtk_widget_set_visible (widget, value == 1);
        widget = GTK_WIDGET (gtk_builder_get_object (panel->priv->builder,
                                                     "heading_proxy_socks"));
        gtk_widget_set_visible (widget, value == 1);
        widget = GTK_WIDGET (gtk_builder_get_object (panel->priv->builder,
                                                     "entry_proxy_socks"));
        gtk_widget_set_visible (widget, value == 1);
        widget = GTK_WIDGET (gtk_builder_get_object (panel->priv->builder,
                                                     "spinbutton_proxy_socks"));
        gtk_widget_set_visible (widget, value == 1);

        /* perhaps show the wpad warning */
        check_wpad_warning (panel);
}

static void
panel_proxy_mode_combo_changed_cb (GtkWidget *widget, CcNetworkPanel *panel)
{
        gboolean ret;
        gint value;
        GtkTreeIter iter;
        GtkTreeModel *model;

        /* no selection */
        ret = gtk_combo_box_get_active_iter (GTK_COMBO_BOX (widget), &iter);
        if (!ret)
                return;

        /* get entry */
        model = gtk_combo_box_get_model (GTK_COMBO_BOX (widget));
        gtk_tree_model_get (model, &iter,
                            1, &value,
                            -1);

        /* set */
        g_settings_set_enum (panel->priv->proxy_settings, "mode", value);

        /* hide or show the correct widgets */
        panel_proxy_mode_combo_setup_widgets (panel, value);
}

static void
panel_set_value_for_combo (CcNetworkPanel *panel, GtkComboBox *combo_box, gint value)
{
        gboolean ret;
        gint value_tmp;
        GtkTreeIter iter;
        GtkTreeModel *model;

        /* get entry */
        model = gtk_combo_box_get_model (combo_box);
        ret = gtk_tree_model_get_iter_first (model, &iter);
        if (!ret)
                return;

        /* try to make the UI match the setting */
        do {
                gtk_tree_model_get (model, &iter,
                                    1, &value_tmp,
                                    -1);
                if (value == value_tmp) {
                        gtk_combo_box_set_active_iter (combo_box, &iter);
                        break;
                }
        } while (gtk_tree_model_iter_next (model, &iter));

        /* hide or show the correct widgets */
        panel_proxy_mode_combo_setup_widgets (panel, value);
}

static void
select_first_device (CcNetworkPanel *panel)
{
        GtkTreePath *path;
        GtkWidget *widget;
        GtkTreeSelection *selection;

        widget = GTK_WIDGET (gtk_builder_get_object (panel->priv->builder,
                                                     "treeview_devices"));
        selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));

        /* 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
panel_device_got_modem_manager_cb (GObject *source_object,
                                   GAsyncResult *res,
                                   gpointer user_data)
{
        GError *error = NULL;
        GVariant *result = NULL;
        GDBusProxy *proxy;
        NMDevice *device = (NMDevice *) user_data;

        proxy = g_dbus_proxy_new_for_bus_finish (res, &error);
        if (proxy == NULL) {
                g_warning ("Error creating ModemManager proxy: %s",
                           error->message);
                g_error_free (error);
                goto out;
        }

        /* get the IMEI */
        result = g_dbus_proxy_get_cached_property (proxy,
                                                   "EquipmentIdentifier");

        /* save */
        g_object_set_data_full (G_OBJECT (device),
                                "ControlCenter::EquipmentIdentifier",
                                g_variant_dup_string (result, NULL),
                                g_free);
out:
        if (result != NULL)
                g_variant_unref (result);
        if (proxy != NULL)
                g_object_unref (proxy);
        return;
}

static void
panel_get_registration_info_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
{
        gchar *operator_code = NULL;
        GError *error = NULL;
        guint registration_status;
        GVariant *result = NULL;
        gchar *operator_name = NULL;
        gchar *operator_name_safe = NULL;
        NMDevice *device = (NMDevice *) user_data;

        result = g_dbus_proxy_call_finish (G_DBUS_PROXY (source_object), res, &error);
        if (result == NULL) {
                g_warning ("Error getting registration info: %s\n",
                           error->message);
                g_error_free (error);
                return;
        }

        /* get values */
        g_variant_get (result, "((uss))",
                       &registration_status,
                       &operator_code,
                       &operator_name);
        if (operator_name != NULL && operator_name[0] != '\0')
                operator_name_safe = g_strescape (operator_name, NULL);

        /* save */
        g_object_set_data_full (G_OBJECT (device),
                                "ControlCenter::OperatorName",
                                operator_name_safe,
                                g_free);

        g_free (operator_name);
        g_free (operator_name_safe);
        g_free (operator_code);
        g_variant_unref (result);
}

static void
panel_device_got_modem_manager_gsm_cb (GObject *source_object,
                                       GAsyncResult *res,
                                       gpointer user_data)
{
        GError *error = NULL;
        GDBusProxy *proxy;
        NMDevice *device = (NMDevice *) user_data;

        proxy = g_dbus_proxy_new_for_bus_finish (res, &error);
        if (proxy == NULL) {
                g_warning ("Error creating ModemManager GSM proxy: %s\n",
                           error->message);
                g_error_free (error);
                goto out;
        }

        g_dbus_proxy_call (proxy,
                           "GetRegistrationInfo",
                           NULL,
                           G_DBUS_CALL_FLAGS_NONE,
                           -1,
                           NULL,
                           panel_get_registration_info_cb,
                           device);
out:
        if (proxy != NULL)
                g_object_unref (proxy);
        return;
}

static void
device_state_notify_changed_cb (NMDevice *device,
                                GParamSpec *pspec,
                                CcNetworkPanel *panel)
{
        refresh_ui (panel);
}

static void
object_changed_cb (NetObject *object, CcNetworkPanel *panel)
{
        refresh_ui (panel);
}

static void
object_removed_cb (NetObject *object, CcNetworkPanel *panel)
{
        gboolean ret;
        NetObject *object_tmp;
        GtkTreeIter iter;
        GtkTreeModel *model;
        GtkWidget *widget;
        GtkTreeSelection *selection;

        widget = GTK_WIDGET (gtk_builder_get_object (panel->priv->builder,
                                                     "treeview_devices"));
        selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));

        /* 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));
}

static void
register_object_interest (CcNetworkPanel *panel, NetObject *object)
{
        g_signal_connect (object,
                          "changed",
                          G_CALLBACK (object_changed_cb),
                          panel);
        g_signal_connect (object,
                          "removed",
                          G_CALLBACK (object_removed_cb),
                          panel);
}

static void
panel_add_device (CcNetworkPanel *panel, NMDevice *device)
{
        GtkListStore *liststore_devices;
        GtkTreeIter iter;
        gchar *title = NULL;
        NMDeviceType type;
        NetDevice *net_device;
        CcNetworkPanelPrivate *priv = panel->priv;

        /* do we have an existing object with this id? */
        if (find_in_model_by_id (panel, nm_device_get_udi (device)) != NULL)
                goto out;

        /* we don't support bluetooth devices yet -- no mockup */
        type = nm_device_get_device_type (device);
        if (type == NM_DEVICE_TYPE_BT)
                goto out;

        g_debug ("device %s type %i",
                 nm_device_get_udi (device),
                 nm_device_get_device_type (device));
        g_signal_connect (G_OBJECT (device), "notify::state",
                          (GCallback) device_state_notify_changed_cb, panel);

        /* do we have to get additonal data from ModemManager */
        if (type == NM_DEVICE_TYPE_MODEM) {
                g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
                                          G_DBUS_PROXY_FLAGS_NONE,
                                          NULL,
                                          "org.freedesktop.ModemManager",
                                          nm_device_get_udi (device),
                                          "org.freedesktop.ModemManager.Modem",
                                          panel->priv->cancellable,
                                          panel_device_got_modem_manager_cb,
                                          device);
                g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
                                          G_DBUS_PROXY_FLAGS_NONE,
                                          NULL,
                                          "org.freedesktop.ModemManager",
                                          nm_device_get_udi (device),
                                          "org.freedesktop.ModemManager.Modem.Gsm.Network",
                                          panel->priv->cancellable,
                                          panel_device_got_modem_manager_gsm_cb,
                                          device);
        }

        /* make title a bit bigger */
        title = g_strdup_printf ("<span size=\"large\">%s</span>",
                                 panel_device_to_localized_string (device));

        liststore_devices = GTK_LIST_STORE (gtk_builder_get_object (priv->builder,
                                            "liststore_devices"));
        net_device = net_device_new ();
        net_device_set_nm_device (net_device, device);
        net_object_set_id (NET_OBJECT (net_device), nm_device_get_udi (device));
        register_object_interest (panel, NET_OBJECT (net_device));
        gtk_list_store_append (liststore_devices, &iter);
        gtk_list_store_set (liststore_devices,
                            &iter,
                            PANEL_DEVICES_COLUMN_ICON, panel_device_to_icon_name (device),
                            PANEL_DEVICES_COLUMN_SORT, panel_device_to_sortable_string (device),
                            PANEL_DEVICES_COLUMN_TITLE, title,
                            PANEL_DEVICES_COLUMN_OBJECT, net_device,
                            -1);
out:
        g_free (title);
}

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
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, "stock-size", GTK_ICON_SIZE_DND, NULL);
        column = gtk_tree_view_column_new_with_attributes ("", 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,
                      NULL);
        column = gtk_tree_view_column_new_with_attributes ("", renderer,
                                                           "markup", PANEL_DEVICES_COLUMN_TITLE,
                                                           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
panel_set_widget_data (CcNetworkPanel *panel,
                       const gchar *sub_pane,
                       const gchar *widget_suffix,
                       const gchar *value)
{
        gchar *heading_id;
        gchar *label_id = NULL;
        GtkWidget *heading;
        GtkWidget *widget;
        CcNetworkPanelPrivate *priv = panel->priv;

        /* hide the row if there is no value */
        heading_id = g_strdup_printf ("heading_%s_%s", sub_pane, widget_suffix);
        label_id = g_strdup_printf ("label_%s_%s", sub_pane, widget_suffix);
        heading = GTK_WIDGET (gtk_builder_get_object (priv->builder, heading_id));
        widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, label_id));
        if (heading == NULL || widget == NULL) {
                g_critical ("no widgets %s, %s found", heading_id, label_id);
                return;
        }
        g_free (heading_id);
        g_free (label_id);

        if (value == NULL) {
                gtk_widget_hide (heading);
                gtk_widget_hide (widget);
        } else {
                /* there exists a value */
                gtk_widget_show (heading);
                gtk_widget_show (widget);
                gtk_label_set_label (GTK_LABEL (widget), value);
        }
}

static void
panel_set_widget_heading (CcNetworkPanel *panel,
                          const gchar *sub_pane,
                          const gchar *widget_suffix,
                          const gchar *heading)
{
        gchar *label_id = NULL;
        GtkWidget *widget;

        label_id = g_strdup_printf ("heading_%s_%s", sub_pane, widget_suffix);
        widget = GTK_WIDGET (gtk_builder_get_object (panel->priv->builder, label_id));
        if (widget)
                gtk_label_set_label (GTK_LABEL (widget), heading);
        g_free (label_id);
}

static guint
get_access_point_security (NMAccessPoint *ap)
{
        NM80211ApFlags flags;
        NM80211ApSecurityFlags wpa_flags;
        NM80211ApSecurityFlags rsn_flags;
        guint type;

        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);

        if (!(flags & NM_802_11_AP_FLAGS_PRIVACY) &&
            wpa_flags == NM_802_11_AP_SEC_NONE &&
            rsn_flags == NM_802_11_AP_SEC_NONE)
                type = NM_AP_SEC_NONE;
        else if ((flags & NM_802_11_AP_FLAGS_PRIVACY) &&
                 wpa_flags == NM_802_11_AP_SEC_NONE &&
                 rsn_flags == NM_802_11_AP_SEC_NONE)
                type = NM_AP_SEC_WEP;
        else if (!(flags & NM_802_11_AP_FLAGS_PRIVACY) &&
                 wpa_flags != NM_802_11_AP_SEC_NONE &&
                 rsn_flags != NM_802_11_AP_SEC_NONE)
                type = NM_AP_SEC_WPA;
        else
                type = NM_AP_SEC_WPA2;

        return type;
}

static void
add_access_point (CcNetworkPanel *panel, NMAccessPoint *ap, NMAccessPoint *active)
{
        CcNetworkPanelPrivate *priv = panel->priv;
        const GByteArray *ssid;
        const gchar *ssid_text;
        const gchar *object_path;
        GtkListStore *liststore_wireless_network;
        GtkTreeIter treeiter;
        GtkWidget *widget;

        ssid = nm_access_point_get_ssid (ap);
        if (ssid == NULL)
                return;
        ssid_text = nm_utils_escape_ssid (ssid->data, ssid->len);

        liststore_wireless_network = GTK_LIST_STORE (gtk_builder_get_object (priv->builder,
                                                     "liststore_wireless_network"));

        object_path = nm_object_get_path (NM_OBJECT (ap));
        gtk_list_store_append (liststore_wireless_network, &treeiter);
        gtk_list_store_set (liststore_wireless_network,
                            &treeiter,
                            PANEL_WIRELESS_COLUMN_ID, object_path,
                            PANEL_WIRELESS_COLUMN_TITLE, ssid_text,
                            PANEL_WIRELESS_COLUMN_SORT, ssid_text,
                            PANEL_WIRELESS_COLUMN_STRENGTH, nm_access_point_get_strength (ap),
                            PANEL_WIRELESS_COLUMN_MODE, nm_access_point_get_mode (ap),
                            PANEL_WIRELESS_COLUMN_SECURITY, get_access_point_security (ap),
                            -1);

        /* is this what we're on already? */
        if (active == NULL)
                return;
        if (g_strcmp0 (object_path,
                       nm_object_get_path (NM_OBJECT (active))) == 0) {
                widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
                                                             "combobox_wireless_network_name"));
                gtk_combo_box_set_active_iter (GTK_COMBO_BOX (widget), &treeiter);
        }
}

static void
add_access_point_other (CcNetworkPanel *panel)
{
        CcNetworkPanelPrivate *priv = panel->priv;
        GtkListStore *liststore_wireless_network;
        GtkTreeIter treeiter;

        liststore_wireless_network = GTK_LIST_STORE (gtk_builder_get_object (priv->builder,
                                                     "liststore_wireless_network"));

        gtk_list_store_append (liststore_wireless_network, &treeiter);
        gtk_list_store_set (liststore_wireless_network,
                            &treeiter,
                            PANEL_WIRELESS_COLUMN_ID, "ap-other...",
                            /* TRANSLATORS: this is when the access point is not listed
                             * in the dropdown (or hidden) and the user has to select
                             * another entry manually */
                            PANEL_WIRELESS_COLUMN_TITLE, C_("Wireless access point", "Other..."),
                            /* always last */
                            PANEL_WIRELESS_COLUMN_SORT, "",
                            PANEL_WIRELESS_COLUMN_STRENGTH, 0,
                            PANEL_WIRELESS_COLUMN_MODE, NM_802_11_MODE_UNKNOWN,
                            PANEL_WIRELESS_COLUMN_SECURITY, NM_AP_SEC_UNKNOWN,
                            -1);
}

#if 0
static gchar *
ip4_address_as_string (guint32 ip)
{
        char buf[INET_ADDRSTRLEN+1];
        struct in_addr tmp_addr;

        memset (&buf, '\0', sizeof (buf));
        tmp_addr.s_addr = ip;

        if (inet_ntop (AF_INET, &tmp_addr, buf, INET_ADDRSTRLEN)) {
                return g_strdup (buf);
        } else {
                g_warning ("error converting IP4 address 0x%X",
                           ntohl (tmp_addr.s_addr));
                return NULL;
        }
}

static void
panel_show_ip4_config (NMIP4Config *cfg)
{
        gchar *tmp;
        const GArray *array;
        const GPtrArray *ptr_array;
        GSList *iter;
        int i;

        for (iter = (GSList *) nm_ip4_config_get_addresses (cfg); iter; iter = g_slist_next (iter)) {
                NMIP4Address *addr = iter->data;
                guint32 u;

                tmp = ip4_address_as_string (nm_ip4_address_get_address (addr));
                g_debug ("IP4 address: %s", tmp);
                g_free (tmp);

                u = nm_ip4_address_get_prefix (addr);
                tmp = ip4_address_as_string (nm_utils_ip4_prefix_to_netmask (u));
                g_debug ("IP4 prefix: %d (%s)", u, tmp);
                g_free (tmp);

                tmp = ip4_address_as_string (nm_ip4_address_get_gateway (addr));
                g_debug ("IP4 gateway: %s", tmp);
                g_free (tmp);
        }

        array = nm_ip4_config_get_nameservers (cfg);
        if (array) {
                g_debug ("IP4 DNS:");
                for (i = 0; i < array->len; i++) {
                        tmp = ip4_address_as_string (g_array_index (array, guint32, i));
                        g_debug ("\t%s", tmp);
                        g_free (tmp);
                }
        }

        ptr_array = nm_ip4_config_get_domains (cfg);
        if (ptr_array) {
                g_debug ("IP4 domains:");
                for (i = 0; i < ptr_array->len; i++)
                        g_debug ("\t%s", (const char *) g_ptr_array_index (ptr_array, i));
        }

        array = nm_ip4_config_get_wins_servers (cfg);
        if (array) {
                g_debug ("IP4 WINS:");
                for (i = 0; i < array->len; i++) {
                        tmp = ip4_address_as_string (g_array_index (array, guint32, i));
                        g_debug ("\t%s", tmp);
                        g_free (tmp);
                }
        }
}
#endif

static GPtrArray *
panel_get_strongest_unique_aps (const GPtrArray *aps)
{
        const GByteArray *ssid;
        const GByteArray *ssid_tmp;
        GPtrArray *aps_unique = NULL;
        gboolean add_ap;
        guint i;
        guint j;
        NMAccessPoint *ap;
        NMAccessPoint *ap_tmp;

        /* we will have multiple entries for typical hotspots, just
         * filter to the one with the strongest signal */
        aps_unique = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
        if (aps != NULL)
                for (i = 0; i < aps->len; i++) {
                        ap = NM_ACCESS_POINT (g_ptr_array_index (aps, i));
                        ssid = nm_access_point_get_ssid (ap);
                        add_ap = TRUE;

                        /* get already added list */
                        for (j=0; j<aps_unique->len; j++) {
                                ap_tmp = NM_ACCESS_POINT (g_ptr_array_index (aps_unique, j));
                                ssid_tmp = nm_access_point_get_ssid (ap_tmp);
        
                                /* is this the same type and data? */
                                if (nm_utils_same_ssid (ssid, ssid_tmp, TRUE)) {

                                        g_debug ("found duplicate: %s",
                                                 nm_utils_escape_ssid (ssid_tmp->data,
                                                                       ssid_tmp->len));

                                        /* the new access point is stronger */
                                        if (nm_access_point_get_strength (ap) >
                                            nm_access_point_get_strength (ap_tmp)) {
                                                g_debug ("removing %s",
                                                         nm_utils_escape_ssid (ssid_tmp->data,
                                                                               ssid_tmp->len));
                                                g_ptr_array_remove (aps_unique, ap_tmp);
                                                add_ap = TRUE;
                                        } else {
                                                add_ap = FALSE;
                                        }

                                        break;
                                }
                        }
                        if (add_ap) {
                                g_debug ("adding %s",
                                         nm_utils_escape_ssid (ssid->data,
                                                               ssid->len));
                                g_ptr_array_add (aps_unique, g_object_ref (ap));
                        }
                }
        return aps_unique;
}

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) {
                /* 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 {
                /* TRANSLATORS: this no (!) WiFi security */
                g_string_append (str, _("None"));
        }
        return g_string_free (str, FALSE);
}

static gchar *
get_ipv6_config_address_as_string (NMIP6Config *ip6_config)
{
        const GSList *list;
        const struct in6_addr *addr;
        gchar *str = NULL;
        gchar tmp[1024];
        NMIP6Address *address;

        /* get address */
        list = nm_ip6_config_get_addresses (ip6_config);
        if (list == NULL)
                goto out;

        /* we only care about one address */
        address = list->data;
        addr = nm_ip6_address_get_address (address);
        if (addr == NULL)
                goto out;
        inet_ntop (AF_INET6, addr, tmp, sizeof(tmp));
        str = g_strdup (tmp);
out:
        return str;
}

static NMConnection *
find_connection_for_device (CcNetworkPanel *panel,
                            NMDevice       *device)
{
        const GPtrArray *connections;
        const GPtrArray *devices;
        NMActiveConnection *c;
        gint i;

        connections = nm_client_get_active_connections (panel->priv->client);

        for (i = 0; i < connections->len; i++) {
                c = (NMActiveConnection *)connections->pdata[i];

                devices = nm_active_connection_get_devices (c);
                if (devices->pdata[0] == device) {
                        return (NMConnection *)nm_remote_settings_get_connection_by_path (panel->priv->remote_settings, nm_active_connection_get_connection (c));
                }
        }

        return NULL;
}

static void
device_off_toggled (GtkSwitch      *sw,
                    GParamSpec     *pspec,
                    CcNetworkPanel *panel)
{
        NMDevice *device;
        gboolean active;
        NetObject *object;

        if (panel->priv->updating_device)
                return;

        active = gtk_switch_get_active (sw);

        object = get_selected_object (panel);
        if (NET_IS_VPN (object)) {

                NMConnection *connection;

                connection = net_vpn_get_connection (NET_VPN (object));
                if (active)
                        nm_client_activate_connection (panel->priv->client,
                                                       connection, NULL, NULL,
                                                       NULL, NULL);
                else {
                        const gchar *path;
                        NMActiveConnection *a;
                        const GPtrArray *acs;
                        gint i;

                        path = nm_connection_get_path (connection);

                        acs = nm_client_get_active_connections (panel->priv->client);
                        for (i = 0; i < acs->len; i++) {
                                a = (NMActiveConnection*)acs->pdata[i];
                                if (strcmp (nm_active_connection_get_connection (a), path) == 0) {
                                        nm_client_deactivate_connection (panel->priv->client, a);
                                        break;
                                }
                        }
                }
        }

        if (NET_IS_DEVICE (object)) {
                device = net_device_get_nm_device (NET_DEVICE (object));
                switch (nm_device_get_device_type (device)) {
                case NM_DEVICE_TYPE_ETHERNET:
                        if (active) {
                                GSList *list, *filtered;

                                /* look for an existing connection we can use */
                                list = nm_remote_settings_list_connections (panel->priv->remote_settings);
                                filtered = nm_device_filter_connections (device, list);
                                if (filtered) {
                                        nm_client_activate_connection (panel->priv->client,
                                                                       (NMConnection *)filtered->data,
                                                                       device,
                                                                       NULL,
                                                                       NULL, NULL);
                                }
                                else {
                                        nm_client_add_and_activate_connection (panel->priv->client,
                                                                               NULL,
                                                                               device,
                                                                               NULL,
                                                                               NULL, NULL);
                                }

                                g_slist_free (list);
                                g_slist_free (filtered);
                        } else {
                                nm_device_disconnect (device, NULL, NULL);
                        }
                        break;
                case NM_DEVICE_TYPE_WIFI:
                        nm_client_wireless_set_enabled (panel->priv->client, active);
                        break;
                case NM_DEVICE_TYPE_WIMAX:
                        nm_client_wimax_set_enabled (panel->priv->client, active);
                        break;
                default: ;
                        /* FIXME: handle other device types */
                }
        }
}

static void
wireless_enabled_toggled (NMClient       *client,
                          GParamSpec     *pspec,
                          CcNetworkPanel *panel)
{
        gboolean enabled;
        GtkSwitch *sw;
        NMDevice *device;
        NetObject *object;

        object = get_selected_object (panel);
        if (object == NULL)
                return;
        device = net_device_get_nm_device (NET_DEVICE (object));

        if (nm_device_get_device_type (device) != NM_DEVICE_TYPE_WIFI)
                return;

        enabled = nm_client_wireless_get_enabled (client);
        sw = GTK_SWITCH (gtk_builder_get_object (panel->priv->builder,
                                                 "device_wireless_off_switch"));

        panel->priv->updating_device = TRUE;
        gtk_switch_set_active (sw, enabled);
        panel->priv->updating_device = FALSE;
}

static void
wimax_enabled_toggled (NMClient       *client,
                       GParamSpec     *pspec,
                       CcNetworkPanel *panel)
{
        gboolean enabled;
        GtkSwitch *sw;
        NMDevice *device;
        NetObject *object;

        object = get_selected_object (panel);
        if (object == NULL)
                return;
        device = net_device_get_nm_device (NET_DEVICE (object));

        if (nm_device_get_device_type (device) != NM_DEVICE_TYPE_WIMAX)
                return;

        enabled = nm_client_wimax_get_enabled (client);
        sw = GTK_SWITCH (gtk_builder_get_object (panel->priv->builder,
                                                 "device_wimax_off_switch"));

        panel->priv->updating_device = TRUE;
        gtk_switch_set_active (sw, enabled);
        panel->priv->updating_device = FALSE;
}

static void
update_off_switch_from_device_state (GtkSwitch *sw, NMDeviceState state, CcNetworkPanel *panel)
{
        panel->priv->updating_device = TRUE;
        switch (state) {
                case NM_DEVICE_STATE_UNMANAGED:
                case NM_DEVICE_STATE_UNAVAILABLE:
                case NM_DEVICE_STATE_DISCONNECTED:
                case NM_DEVICE_STATE_DEACTIVATING:
                case NM_DEVICE_STATE_FAILED:
                        gtk_switch_set_active (sw, FALSE);
                        break;
                default:
                        gtk_switch_set_active (sw, TRUE);
                        break;
        }
        panel->priv->updating_device = FALSE;
}

static void
refresh_header_ui (CcNetworkPanel *panel, NMDevice *device, const char *page_name)
{
        GtkWidget *widget;
        char *wid_name;
        const char *str;
        NMDeviceState state;
        NMDeviceType type;

        type = nm_device_get_device_type (device);

        /* set header icon */
        wid_name = g_strdup_printf ("image_%s_device", page_name);
        widget = GTK_WIDGET (gtk_builder_get_object (panel->priv->builder, wid_name));
        g_free (wid_name);
        gtk_image_set_from_icon_name (GTK_IMAGE (widget),
                                      panel_device_to_icon_name (device),
                                      GTK_ICON_SIZE_DIALOG);

        /* set device kind */
        wid_name = g_strdup_printf ("label_%s_device", page_name);
        widget = GTK_WIDGET (gtk_builder_get_object (panel->priv->builder, wid_name));
        g_free (wid_name);
        gtk_label_set_label (GTK_LABEL (widget),
                             panel_device_to_localized_string (device));


        /* set device state */
        state = nm_device_get_state (device);
        wid_name = g_strdup_printf ("label_%s_status", page_name);
        widget = GTK_WIDGET (gtk_builder_get_object (panel->priv->builder, wid_name));
        g_free (wid_name);
        str = panel_device_state_to_localized_string (device);
        gtk_label_set_label (GTK_LABEL (widget), str);


        /* set up the device on/off switch */
        wid_name = g_strdup_printf ("device_%s_off_switch", page_name);
        widget = GTK_WIDGET (gtk_builder_get_object (panel->priv->builder, wid_name));
        g_free (wid_name);

        /* keep this in sync with the signal handler setup in cc_network_panel_init */
        switch (type) {
        case NM_DEVICE_TYPE_ETHERNET:
                gtk_widget_set_visible (widget,
                                        state != NM_DEVICE_STATE_UNAVAILABLE
                                        && state != NM_DEVICE_STATE_UNMANAGED);
                update_off_switch_from_device_state (GTK_SWITCH (widget), state, panel);
                break;
        case NM_DEVICE_TYPE_WIFI:
                gtk_widget_show (widget);
                wireless_enabled_toggled (panel->priv->client, NULL, panel);
                break;
        case NM_DEVICE_TYPE_WIMAX:
                gtk_widget_show (widget);
                wimax_enabled_toggled (panel->priv->client, NULL, panel);
                break;
        default:
                gtk_widget_hide (widget);
                break;
        }

        /* set up options button */
        wid_name = g_strdup_printf ("button_%s_options", page_name);
        widget = GTK_WIDGET (gtk_builder_get_object (panel->priv->builder, wid_name));
        g_free (wid_name);
        if (widget != NULL) {
                gtk_widget_set_sensitive (widget, state == NM_DEVICE_STATE_ACTIVATED);
        }
}

static void
device_refresh_ethernet_ui (CcNetworkPanel *panel, NetDevice *device)
{
        guint speed;
        NMDeviceState state;
        const char *str;
        gchar *str_tmp;
        NMDevice *nm_device;

        nm_device = net_device_get_nm_device (device);
        state = nm_device_get_state (nm_device);

        refresh_header_ui (panel, nm_device, "wired");

        /* speed */
        speed = nm_device_ethernet_get_speed (NM_DEVICE_ETHERNET (nm_device));
        if (state == NM_DEVICE_STATE_UNAVAILABLE)
                str_tmp = NULL;
        else if (speed  > 0)
                /* Translators: network device speed */
                str_tmp = g_strdup_printf (_("%d Mb/s"), speed);
        else
                str_tmp = g_strdup ("");
        panel_set_widget_data (panel,
                               "wired",
                               "speed",
                               str_tmp);
        g_free (str_tmp);

        /* device MAC */
        str = nm_device_ethernet_get_hw_address (NM_DEVICE_ETHERNET (nm_device));
        panel_set_widget_data (panel,
                               "wired",
                               "mac",
                               str);

}

static void
device_refresh_wifi_ui (CcNetworkPanel *panel, NetDevice *device)
{
        GtkWidget *widget;
        guint speed;
        const GPtrArray *aps;
        GPtrArray *aps_unique = NULL;
        GtkWidget *heading;
        NMDeviceState state;
        NMAccessPoint *ap;
        NMAccessPoint *active_ap;
        const char *str;
        gchar *str_tmp;
        GtkListStore *liststore_wireless_network;
        guint i;
        NMDevice *nm_device;

        nm_device = net_device_get_nm_device (device);
        state = nm_device_get_state (nm_device);

        refresh_header_ui (panel, nm_device, "wireless");

        /* speed */
        speed = nm_device_wifi_get_bitrate (NM_DEVICE_WIFI (nm_device));
        if (state == NM_DEVICE_STATE_UNAVAILABLE)
                str_tmp = NULL;
        else if (speed > 0)
                str_tmp = g_strdup_printf (_("%d Mb/s"),
                                           speed / 1000);
        else
                str_tmp = g_strdup ("");
        panel_set_widget_data (panel,
                               "wireless",
                               "speed",
                               str_tmp);
        g_free (str_tmp);

        /* device MAC */
        str = nm_device_wifi_get_hw_address (NM_DEVICE_WIFI (nm_device));
        panel_set_widget_data (panel,
                               "wireless",
                               "mac",
                               str);
        /* security */
        active_ap = nm_device_wifi_get_active_access_point (NM_DEVICE_WIFI (nm_device));
        if (state == NM_DEVICE_STATE_UNAVAILABLE)
                str_tmp = NULL;
        else if (active_ap != NULL)
                str_tmp = get_ap_security_string (active_ap);
        else
                str_tmp = g_strdup ("");
        panel_set_widget_data (panel,
                               "wireless",
                               "security",
                               str_tmp);
        g_free (str_tmp);

        heading = GTK_WIDGET (gtk_builder_get_object (panel->priv->builder,
                                                      "heading_wireless_network_name"));
        widget = GTK_WIDGET (gtk_builder_get_object (panel->priv->builder,
                                                     "combobox_wireless_network_name"));
        /* populate access point dropdown */
        if (state == NM_DEVICE_STATE_UNAVAILABLE) {
                gtk_widget_hide (heading);
                gtk_widget_hide (widget);
        } else {
                gtk_widget_show (heading);
                gtk_widget_show (widget);
                liststore_wireless_network = GTK_LIST_STORE (gtk_builder_get_object (panel->priv->builder,
                                                                                     "liststore_wireless_network"));
                panel->priv->updating_device = TRUE;
                gtk_list_store_clear (liststore_wireless_network);
                aps = nm_device_wifi_get_access_points (NM_DEVICE_WIFI (nm_device));
                aps_unique = panel_get_strongest_unique_aps (aps);

                for (i = 0; i < aps_unique->len; i++) {
                        ap = NM_ACCESS_POINT (g_ptr_array_index (aps_unique, i));
                        add_access_point (panel, ap, active_ap);
                }
                add_access_point_other (panel);
                if (active_ap == NULL) {
                        widget = GTK_WIDGET (gtk_builder_get_object (panel->priv->builder,
                                                                     "combobox_wireless_network_name"));
                        gtk_combo_box_set_active_iter (GTK_COMBO_BOX (widget), NULL);
                        gtk_entry_set_text (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (widget))), "");
                }

                panel->priv->updating_device = FALSE;

                g_ptr_array_unref (aps_unique);
        }

}

static void
device_refresh_wimax_ui (CcNetworkPanel *panel, NetDevice *device)
{
        NMDevice *nm_device;

        nm_device = net_device_get_nm_device (device);
        refresh_header_ui (panel, nm_device, "wimax");
}

static void
device_refresh_modem_ui (CcNetworkPanel *panel, NetDevice *device)
{
        NMDeviceModemCapabilities caps;
        NMDevice *nm_device;
        const char *str;

        nm_device = net_device_get_nm_device (device);

        refresh_header_ui (panel, nm_device, "mobilebb");

        caps = nm_device_modem_get_current_capabilities (NM_DEVICE_MODEM (nm_device));

        if ((caps & NM_DEVICE_MODEM_CAPABILITY_GSM_UMTS) ||
            (caps & NM_DEVICE_MODEM_CAPABILITY_CDMA_EVDO)) {
                /* IMEI */
                str = g_object_get_data (G_OBJECT (device),
                                         "ControlCenter::EquipmentIdentifier");
                panel_set_widget_data (panel,
                                       "mobilebb",
                                       "imei",
                                       str);

                /* operator name */
                str = g_object_get_data (G_OBJECT (device),
                                         "ControlCenter::OperatorName");
                panel_set_widget_data (panel,
                                       "mobilebb",
                                       "provider",
                                       str);

                /* device speed */
                panel_set_widget_data (panel,
                                       "mobilebb",
                                       "speed",
                                       NULL);
        }
}

static void
nm_device_refresh_device_ui (CcNetworkPanel *panel, NetDevice *device)
{
        CcNetworkPanelPrivate *priv = panel->priv;
        const gchar *str;
        const gchar *sub_pane = NULL;
        gchar *str_tmp;
        GHashTable *options = NULL;
        GtkWidget *widget;
        NMDeviceType type;
        NMDHCP4Config *config_dhcp4 = NULL;
        NMIP6Config *ip6_config = NULL;
        NMDevice *nm_device;
        gboolean has_ip4;
        gboolean has_ip6;

        /* we have a new device */
        nm_device = net_device_get_nm_device (device);
        type = nm_device_get_device_type (nm_device);
        g_debug ("device %s type %i", nm_device_get_udi (nm_device), type);

        widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "notebook_types"));

        switch (type) {
        case NM_DEVICE_TYPE_ETHERNET:
                gtk_notebook_set_current_page (GTK_NOTEBOOK (widget), 0);
                sub_pane = "wired";
                device_refresh_ethernet_ui (panel, device);
                break;
        case NM_DEVICE_TYPE_WIFI:
                gtk_notebook_set_current_page (GTK_NOTEBOOK (widget), 1);
                sub_pane = "wireless";
                device_refresh_wifi_ui (panel, device);
                break;
        case NM_DEVICE_TYPE_WIMAX:
                device_refresh_wimax_ui (panel, device);
                break;
        case NM_DEVICE_TYPE_MODEM:
                {
                        NMDeviceModemCapabilities caps = nm_device_modem_get_current_capabilities (NM_DEVICE_MODEM (nm_device));
                        if ((caps & NM_DEVICE_MODEM_CAPABILITY_GSM_UMTS) ||
                            (caps & NM_DEVICE_MODEM_CAPABILITY_CDMA_EVDO)) {
                                gtk_notebook_set_current_page (GTK_NOTEBOOK (widget), 4);
                                sub_pane = "mobilebb";
                                device_refresh_modem_ui (panel, device);
                        }
                }
                break;
        default:
                g_assert_not_reached ();
                break;
        }

        if (sub_pane == NULL)
                goto out;

        /* get IP4 parameters */
        config_dhcp4 = nm_device_get_dhcp4_config (nm_device);
        if (config_dhcp4 != NULL) {
                g_object_get (G_OBJECT (config_dhcp4),
                              NM_DHCP4_CONFIG_OPTIONS, &options,
                              NULL);

                /* IPv4 address */
                str = nm_dhcp4_config_get_one_option (config_dhcp4,
                                                      "ip_address");
                panel_set_widget_data (panel,
                                       sub_pane,
                                       "ipv4",
                                       str);
                has_ip4 = str != NULL;

                /* IPv4 DNS */
                panel_set_widget_data (panel,
                                       sub_pane,
                                       "dns",
                                       nm_dhcp4_config_get_one_option (config_dhcp4,
                                                                       "domain_name_servers"));

                /* IPv4 route */
                panel_set_widget_data (panel,
                                       sub_pane,
                                       "route",
                                       nm_dhcp4_config_get_one_option (config_dhcp4,
                                                                       "routers"));

                /* IPv4 netmask */
                if (type == NM_DEVICE_TYPE_ETHERNET) {
                        panel_set_widget_data (panel,
                                               sub_pane,
                                               "subnet",
                                               nm_dhcp4_config_get_one_option (config_dhcp4,
                                                                               "subnet_mask"));
                }
        } else {
                /* IPv4 address */
                panel_set_widget_data (panel,
                                       sub_pane,
                                       "ipv4",
                                       NULL);
                has_ip4 = FALSE;

                /* IPv4 DNS */
                panel_set_widget_data (panel,
                                       sub_pane,
                                       "dns",
                                       NULL);

                /* IPv4 route */
                panel_set_widget_data (panel,
                                       sub_pane,
                                       "route",
                                       NULL);

                /* IPv4 netmask */
                if (type == NM_DEVICE_TYPE_ETHERNET) {
                        panel_set_widget_data (panel,
                                               sub_pane,
                                               "subnet",
                                               NULL);
                }
        }

        /* get IP6 parameters */
        ip6_config = nm_device_get_ip6_config (nm_device);
        if (ip6_config != NULL) {

                /* IPv6 address */
                str_tmp = get_ipv6_config_address_as_string (ip6_config);
                panel_set_widget_data (panel, sub_pane, "ipv6", str_tmp);
                has_ip6 = str_tmp != NULL;
                g_free (str_tmp);
        } else {
                panel_set_widget_data (panel, sub_pane, "ipv6", NULL);
                has_ip6 = FALSE;
        }

        if (has_ip4 && has_ip6) {
                panel_set_widget_heading (panel, sub_pane, "ipv4", _("IPv4 Address"));
                panel_set_widget_heading (panel, sub_pane, "ipv6", _("IPv6 Address"));
        }
        else if (has_ip4) {
                panel_set_widget_heading (panel, sub_pane, "ipv4", _("IP Address"));
        }
        else if (has_ip6) {
                panel_set_widget_heading (panel, sub_pane, "ipv6", _("IP Address"));
        }
out: ;
}

static void
nm_device_refresh_vpn_ui (CcNetworkPanel *panel, NetVpn *vpn)
{
        GtkWidget *widget;
        GtkWidget *sw;
        const gchar *sub_pane = "vpn";
        const gchar *status;
        CcNetworkPanelPrivate *priv = panel->priv;
        const GPtrArray *acs;
        NMActiveConnection *a;
        gint i;
        const gchar *path;
        const gchar *apath;
        NMVPNConnectionState state;

        sw = GTK_WIDGET (gtk_builder_get_object (priv->builder,
                                                 "device_vpn_off_switch"));
        gtk_widget_set_visible (sw, TRUE);

        widget = GTK_WIDGET (gtk_builder_get_object (panel->priv->builder,
                                                     "button_vpn_options"));
        gtk_widget_set_visible (widget, TRUE);
        gtk_widget_set_sensitive (widget, TRUE);

        /* use proxy note page */
        widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
                                                     "notebook_types"));
        gtk_notebook_set_current_page (GTK_NOTEBOOK (widget), 3);

        /* set VPN icon */
        widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
                                                     "image_vpn_device"));
        gtk_image_set_from_icon_name (GTK_IMAGE (widget),
                                      "network-vpn",
                                      GTK_ICON_SIZE_DIALOG);

        /* use title */
        widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
                                                     "label_vpn_device"));
        gtk_label_set_label (GTK_LABEL (widget), net_object_get_title (NET_OBJECT (vpn)));

        /* use status */
        state = net_vpn_get_state (vpn);

        acs = nm_client_get_active_connections (priv->client);
        if (acs != NULL) {
                path = nm_connection_get_path (net_vpn_get_connection (vpn));
                for (i = 0; i < acs->len; i++) {
                        a = (NMActiveConnection*)acs->pdata[i];

                        apath = nm_active_connection_get_connection (a);
                        if (NM_IS_VPN_CONNECTION (a) && strcmp (apath, path) == 0) {
                                state = nm_vpn_connection_get_vpn_state (NM_VPN_CONNECTION (a));
                                break;
                        }
                }
        }

        widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
                                                     "label_vpn_status"));
        status = panel_vpn_state_to_localized_string (state);
        gtk_label_set_label (GTK_LABEL (widget), status);
        priv->updating_device = TRUE;
        gtk_switch_set_active (GTK_SWITCH (sw),
                               state != NM_VPN_CONNECTION_STATE_FAILED &&
                               state != NM_VPN_CONNECTION_STATE_DISCONNECTED);
        priv->updating_device = FALSE;

        /* gateway */
        panel_set_widget_data (panel,
                               sub_pane,
                               "gateway",
                                net_vpn_get_gateway (vpn));

        /* groupname */
        panel_set_widget_data (panel,
                               sub_pane,
                               "group_name",
                                net_vpn_get_id (vpn));

        /* username */
        panel_set_widget_data (panel,
                               sub_pane,
                               "username",
                                net_vpn_get_username (vpn));

        /* password */
        panel_set_widget_data (panel,
                               sub_pane,
                               "group_password",
                                net_vpn_get_password (vpn));
}

static void
refresh_ui (CcNetworkPanel *panel)
{
        GtkTreeSelection *selection;
        GtkTreeIter iter;
        GtkTreeModel *model;
        GtkWidget *widget;
        NetObject *object = NULL;
        CcNetworkPanelPrivate *priv = panel->priv;

        widget = GTK_WIDGET (gtk_builder_get_object (panel->priv->builder,
                                                     "treeview_devices"));
        selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));

        /* will only work in single or browse selection mode! */
        if (!gtk_tree_selection_get_selected (selection, &model, &iter)) {
                g_debug ("no row selected");
                goto out;
        }

        object = get_selected_object (panel);

        /* this is the proxy settings device */
        if (object == NULL) {

                /* set header to something sane */
                widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
                                                             "image_proxy_device"));
                gtk_image_set_from_icon_name (GTK_IMAGE (widget),
                                              "preferences-system-network",
                                              GTK_ICON_SIZE_DIALOG);
                widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
                                                             "label_proxy_device"));
                gtk_label_set_label (GTK_LABEL (widget),
                                     _("Proxy"));
                widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
                                                             "label_proxy_status"));
                gtk_label_set_label (GTK_LABEL (widget), "");

                widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
                                                             "notebook_types"));
                gtk_notebook_set_current_page (GTK_NOTEBOOK (widget), 2);

                /* hide the switch until we get some more detail in the mockup */
                widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
                                                             "device_proxy_off_switch"));
                if (widget != NULL)
                        gtk_widget_hide (widget);

                /* we shoulnd't be able to delete the proxy device */
                widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
                                                             "remove_toolbutton"));
                gtk_widget_set_sensitive (widget, FALSE);
                goto out;
        }

        /* VPN */
        if (NET_IS_VPN (object)) {
                nm_device_refresh_vpn_ui (panel, NET_VPN (object));

                /* we're able to remove the VPN connection */
                widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
                                                             "remove_toolbutton"));
                gtk_widget_set_sensitive (widget, TRUE);
                goto out;
        }

        /* device */
        if (NET_IS_DEVICE (object)) {

                /* we're not yet able to remove the connection */
                widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
                                                             "remove_toolbutton"));
                gtk_widget_set_sensitive (widget, FALSE);

                /* refresh device */
                nm_device_refresh_device_ui (panel, NET_DEVICE (object));
        }
out:
        return;
}

static void
nm_devices_treeview_clicked_cb (GtkTreeSelection *selection, CcNetworkPanel *panel)
{
        refresh_ui (panel);
}

static void
panel_add_proxy_device (CcNetworkPanel *panel)
{
        gchar *title;
        GtkListStore *liststore_devices;
        GtkTreeIter iter;

        liststore_devices = GTK_LIST_STORE (gtk_builder_get_object (panel->priv->builder,
                                            "liststore_devices"));
        title = g_strdup_printf ("<span size=\"large\">%s</span>",
                                 _("Network proxy"));

        gtk_list_store_append (liststore_devices, &iter);
        gtk_list_store_set (liststore_devices,
                            &iter,
                            PANEL_DEVICES_COLUMN_ICON, "preferences-system-network",
                            PANEL_DEVICES_COLUMN_TITLE, title,
                            PANEL_DEVICES_COLUMN_SORT, "9",
                            PANEL_DEVICES_COLUMN_OBJECT, NULL,
                            -1);
        g_free (title);
}

static void
cc_network_panel_notify_enable_active_cb (GtkSwitch *sw,
                                          GParamSpec *pspec,
                                          CcNetworkPanel *panel)
{
        gboolean enable;

        /* set enabled state */
        enable = !gtk_switch_get_active (sw);
        nm_client_wireless_set_enabled (panel->priv->client, enable);
}

static void
connection_state_changed (NMActiveConnection *c, GParamSpec *pspec, CcNetworkPanel *panel)
{
        refresh_ui (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));
                }
        }

        refresh_ui (panel);
}

static void
device_added_cb (NMClient *client, NMDevice *device, CcNetworkPanel *panel)
{
        g_debug ("New device added");
        panel_add_device (panel, device);
}

static void
device_removed_cb (NMClient *client, NMDevice *device, CcNetworkPanel *panel)
{
        g_debug ("Device removed");
        panel_remove_device (panel, device);
}

static void
manager_running (NMClient *client, GParamSpec *pspec, gpointer user_data)
{
        const GPtrArray *devices;
        int i;
        NMDevice *device_tmp;
        GtkListStore *liststore_devices;
        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);
                panel_add_device (panel, device_tmp);
        }
out:
        /* select the first device */
        select_first_device (panel);
}

static NetObject *
find_in_model_by_id (CcNetworkPanel *panel, const gchar *id)
{
        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:
        return object;
}

static void
panel_add_vpn_device (CcNetworkPanel *panel, NMConnection *connection)
{
        gchar *title;
        gchar *title_markup;
        GtkListStore *liststore_devices;
        GtkTreeIter iter;
        NetVpn *net_vpn;
        const gchar *id;

        /* does already exist */
        id = nm_connection_get_path (connection);
        if (find_in_model_by_id (panel, id) != NULL)
                return;

        /* add as a virtual object */
        net_vpn = net_vpn_new ();
        net_vpn_set_connection (net_vpn, connection);
        net_object_set_id (NET_OBJECT (net_vpn), id);
        register_object_interest (panel, NET_OBJECT (net_vpn));

        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));
        title_markup = g_strdup_printf ("<span size=\"large\">%s</span>",
                                        title);

        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",
                            PANEL_DEVICES_COLUMN_TITLE, title_markup,
                            PANEL_DEVICES_COLUMN_SORT, "5",
                            PANEL_DEVICES_COLUMN_OBJECT, net_vpn,
                            -1);
        g_free (title);
        g_free (title_markup);
}

static void
add_connection (CcNetworkPanel *panel,
                NMConnection *connection)
{
        NMSettingConnection *s_con;
        const gchar *type;

        s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection,
                                                                  NM_TYPE_SETTING_CONNECTION));
        type = nm_setting_connection_get_connection_type (s_con);
        if (g_strcmp0 (type, "vpn") != 0)
                return;
        g_debug ("add %s/%s remote connection: %s",
                 type, g_type_name_from_instance ((GTypeInstance*)connection),
                 nm_connection_get_path (connection));
        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);
        }
}

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 */
                g_idle_add ((GSourceFunc)display_version_warning_idle, panel);
        }

        g_strfreev (split);
        return ret;
}

static void
edit_connection (GtkButton *button, CcNetworkPanel *panel)
{
        NMConnection *c;
        const gchar *uuid;
        gchar *cmdline;
        GError *error;
        NetObject *object;
        NMDevice *device;

        object = get_selected_object (panel);
        if (object == NULL)
                return;
        else if (NET_IS_VPN (object)) {
                c = net_vpn_get_connection (NET_VPN (object));
        }
        else {
                device = net_device_get_nm_device (NET_DEVICE (object));
                c = find_connection_for_device (panel, device);
        }

        uuid = nm_connection_get_uuid (c);

        cmdline = g_strdup_printf ("nm-connection-editor --edit %s", uuid);
        g_debug ("Launching '%s'\n", cmdline);

        error = NULL;
        if (!g_spawn_command_line_async (cmdline, &error)) {
                g_warning ("Failed to launch nm-connection-editor: %s", error->message);
                g_error_free (error);
        }

        g_free (cmdline);
}

static void
add_connection_cb (GtkToolButton *button, CcNetworkPanel *panel)
{
        GtkWidget *dialog;
        gint response;

        dialog = GTK_WIDGET (gtk_builder_get_object (panel->priv->builder,
                                                     "connection_type_dialog"));
        gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (panel))));

        response = gtk_dialog_run (GTK_DIALOG (dialog));

        gtk_widget_hide (dialog);

        if (response == GTK_RESPONSE_OK) {
                GtkComboBox *combo;
                GtkTreeModel *model;
                GtkTreeIter iter;
                gchar *type;
                gchar *cmdline;
                GError *error;

                combo = GTK_COMBO_BOX (gtk_builder_get_object (panel->priv->builder,
                                                               "connection_type_combo"));
                model = gtk_combo_box_get_model (combo);
                gtk_combo_box_get_active_iter (combo, &iter);
                type = NULL;
                gtk_tree_model_get (model, &iter, 1, &type, -1);

                cmdline = g_strdup_printf ("nm-connection-editor --create --type %s", type);
                g_debug ("Launching '%s'\n", cmdline);

                error = NULL;
                if (!g_spawn_command_line_async (cmdline, &error)) {
                        g_warning ("Failed to launch nm-connection-editor: %s", error->message);
                        g_error_free (error);
                }
                g_free (cmdline);
                g_free (type);
        }
}

static void
remove_connection (GtkToolButton *button, CcNetworkPanel *panel)
{
        NetObject *object;
        NMConnection *connection;

        /* get current device */
        object = get_selected_object (panel);
        if (object == NULL)
                return;

        /* VPN */
        if (NET_IS_VPN (object)) {
                connection = net_vpn_get_connection (NET_VPN (object));
                nm_remote_connection_delete (NM_REMOTE_CONNECTION (connection), NULL, panel);
                return;
        }
}

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
connection_activate_cb (NMClient *client,
                        NMActiveConnection *connection,
                        GError *error,
                        gpointer user_data)
{
        CcNetworkPanel *panel = user_data;

        if (connection == NULL) {
                /* failed to activate */
                refresh_ui (panel);
        }
}

static void
connection_add_activate_cb (NMClient *client,
                            NMActiveConnection *connection,
                            const char *path,
                            GError *error,
                            gpointer user_data)
{
        connection_activate_cb (client, connection, error, user_data);
}

static void
connect_to_hidden_network_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
{
        GError *error = NULL;
        GVariant *result = NULL;

        result = g_dbus_proxy_call_finish (G_DBUS_PROXY (source_object), res, &error);
        if (result == NULL) {
                g_warning ("failed to connect to hidden network: %s",
                           error->message);
                g_error_free (error);
                return;
        }
}

static void
connect_to_hidden_network (CcNetworkPanel *panel)
{
        GDBusProxy *proxy;
        GVariant *res = NULL;
        GError *error = NULL;

        /* connect to NM applet */
        proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
                                               G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
                                               G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
                                               NULL,
                                               "org.gnome.network_manager_applet",
                                               "/org/gnome/network_manager_applet",
                                               "org.gnome.network_manager_applet",
                                               panel->priv->cancellable,
                                               &error);
        if (proxy == NULL) {
                g_warning ("failed to connect to NM applet: %s",
                           error->message);
                g_error_free (error);
                goto out;
        }

        /* try to show the hidden network UI */
        g_dbus_proxy_call (proxy,
                           "ConnectToHiddenNetwork",
                           NULL,
                           G_DBUS_CALL_FLAGS_NONE,
                           5000, /* don't wait forever */
                           panel->priv->cancellable,
                           connect_to_hidden_network_cb,
                           panel);
out:
        if (proxy != NULL)
                g_object_unref (proxy);
        if (res != NULL)
                g_variant_unref (res);
}

static void
wireless_ap_changed_cb (GtkComboBox *combo_box, CcNetworkPanel *panel)
{
        const GByteArray *ssid;
        const gchar *ssid_tmp;
        gboolean ret;
        gchar *object_path = NULL;
        gchar *ssid_target = NULL;
        GSList *list, *l;
        GSList *filtered;
        GtkTreeIter iter;
        GtkTreeModel *model;
        NetObject *object;
        NMConnection *connection;
        NMConnection *connection_activate = NULL;
        NMDevice *device;
        NMSettingWireless *setting_wireless;

        if (panel->priv->updating_device)
                goto out;

        ret = gtk_combo_box_get_active_iter (combo_box, &iter);
        if (!ret)
                goto out;

        object = get_selected_object (panel);
        if (object == NULL)
                goto out;

        device = net_device_get_nm_device (NET_DEVICE (object));
        if (device == NULL)
                goto out;

        /* get entry */
        model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box));
        gtk_tree_model_get (model, &iter,
                            PANEL_WIRELESS_COLUMN_ID, &object_path,
                            PANEL_WIRELESS_COLUMN_TITLE, &ssid_target,
                            -1);
        g_debug ("try to connect to WIFI network %s [%s]",
                 ssid_target, object_path);
        if (g_strcmp0 (object_path, "ap-other...") == 0) {
                connect_to_hidden_network (panel);
                goto out;
        }

        /* look for an existing connection we can use */
        list = nm_remote_settings_list_connections (panel->priv->remote_settings);
        g_debug ("%i existing remote connections available",
                 g_slist_length (list));
        filtered = nm_device_filter_connections (device, list);
        g_debug ("%i suitable remote connections to check",
                 g_slist_length (filtered));
        for (l = filtered; l; l = g_slist_next (l)) {
                connection = NM_CONNECTION (l->data);
                setting_wireless = nm_connection_get_setting_wireless (connection);
                if (!NM_IS_SETTING_WIRELESS (setting_wireless))
                        continue;
                ssid = nm_setting_wireless_get_ssid (setting_wireless);
                if (ssid == NULL)
                        continue;
                ssid_tmp = nm_utils_escape_ssid (ssid->data, ssid->len);
                if (g_strcmp0 (ssid_target, ssid_tmp) == 0) {
                        g_debug ("we found an existing connection %s to activate!",
                                 nm_connection_get_id (connection));
                        connection_activate = connection;
                        break;
                }
        }

        g_slist_free (list);
        g_slist_free (filtered);

        /* activate the connection */
        if (connection_activate != NULL) {
                nm_client_activate_connection (panel->priv->client,
                                               connection_activate,
                                               device, NULL,
                                               connection_activate_cb, panel);
                goto out;
        }

        /* create one, as it's missing */
        g_debug ("no existing connection found for %s, creating",
                 ssid_target);
        nm_client_add_and_activate_connection (panel->priv->client,
                                               NULL,
                                               device, object_path,
                                               connection_add_activate_cb, panel);
out:
        g_free (ssid_target);
        g_free (object_path);
}

static gint
wireless_ap_model_sort_cb (GtkTreeModel *model,
                           GtkTreeIter *a,
                           GtkTreeIter *b,
                           gpointer user_data)
{
        gchar *str_a;
        gchar *str_b;
        gint retval;

        gtk_tree_model_get (model, a,
                            PANEL_WIRELESS_COLUMN_SORT, &str_a,
                            -1);
        gtk_tree_model_get (model, b,
                            PANEL_WIRELESS_COLUMN_SORT, &str_b,
                            -1);

        /* special case blank entries to the bottom */
        if (g_strcmp0 (str_a, "") == 0) {
                retval = 1;
                goto out;
        }
        if (g_strcmp0 (str_b, "") == 0) {
                retval = -1;
                goto out;
        }

        /* case sensitive search like before */
        g_debug ("compare %s with %s", str_a, str_b);
        retval = g_strcmp0 (str_a, str_b);
out:
        g_free (str_a);
        g_free (str_b);
        return retval;
}

static void
cc_network_panel_init (CcNetworkPanel *panel)
{
        DBusGConnection *bus = NULL;
        gboolean ret;
        GError *error = NULL;
        gint value;
        GSettings *settings_tmp;
        GtkAdjustment *adjustment;
        GtkCellRenderer *renderer;
        GtkComboBox *combobox;
        GtkStyleContext *context;
        GtkTreeSelection *selection;
        GtkTreeSortable *sortable;
        GtkWidget *widget;
        GtkWidget *toplevel;

        panel->priv = NETWORK_PANEL_PRIVATE (panel);

        panel->priv->builder = gtk_builder_new ();
        gtk_builder_add_from_file (panel->priv->builder,
                                   GNOMECC_UI_DIR "/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->proxy_settings = g_settings_new ("org.gnome.system.proxy");
        g_signal_connect (panel->priv->proxy_settings,
                          "changed",
                          G_CALLBACK (panel_settings_changed),
                          panel);

        /* actions */
        value = g_settings_get_enum (panel->priv->proxy_settings, "mode");
        widget = GTK_WIDGET (gtk_builder_get_object (panel->priv->builder,
                                                     "combobox_proxy_mode"));
        panel_set_value_for_combo (panel, GTK_COMBO_BOX (widget), value);
        g_signal_connect (widget, "changed",
                          G_CALLBACK (panel_proxy_mode_combo_changed_cb),
                          panel);

        /* bind the proxy values */
        widget = GTK_WIDGET (gtk_builder_get_object (panel->priv->builder,
                                                     "entry_proxy_url"));
        g_settings_bind (panel->priv->proxy_settings, "autoconfig-url",
                         widget, "text",
                         G_SETTINGS_BIND_DEFAULT);

        /* bind the proxy values */
        settings_tmp = g_settings_new ("org.gnome.system.proxy.http");
        widget = GTK_WIDGET (gtk_builder_get_object (panel->priv->builder,
                                                     "entry_proxy_http"));
        g_settings_bind (settings_tmp, "host",
                         widget, "text",
                         G_SETTINGS_BIND_DEFAULT);
        adjustment = GTK_ADJUSTMENT (gtk_builder_get_object (panel->priv->builder,
                                                             "adjustment_proxy_port_http"));
        g_settings_bind (settings_tmp, "port",
                         adjustment, "value",
                         G_SETTINGS_BIND_DEFAULT);
        g_object_unref (settings_tmp);

        /* bind the proxy values */
        settings_tmp = g_settings_new ("org.gnome.system.proxy.https");
        widget = GTK_WIDGET (gtk_builder_get_object (panel->priv->builder,
                                                     "entry_proxy_https"));
        g_settings_bind (settings_tmp, "host",
                         widget, "text",
                         G_SETTINGS_BIND_DEFAULT);
        adjustment = GTK_ADJUSTMENT (gtk_builder_get_object (panel->priv->builder,
                                                             "adjustment_proxy_port_https"));
        g_settings_bind (settings_tmp, "port",
                         adjustment, "value",
                         G_SETTINGS_BIND_DEFAULT);
        g_object_unref (settings_tmp);

        /* bind the proxy values */
        settings_tmp = g_settings_new ("org.gnome.system.proxy.ftp");
        widget = GTK_WIDGET (gtk_builder_get_object (panel->priv->builder,
                                                     "entry_proxy_ftp"));
        g_settings_bind (settings_tmp, "host",
                         widget, "text",
                         G_SETTINGS_BIND_DEFAULT);
        adjustment = GTK_ADJUSTMENT (gtk_builder_get_object (panel->priv->builder,
                                                             "adjustment_proxy_port_ftp"));
        g_settings_bind (settings_tmp, "port",
                         adjustment, "value",
                         G_SETTINGS_BIND_DEFAULT);
        g_object_unref (settings_tmp);

        /* bind the proxy values */
        settings_tmp = g_settings_new ("org.gnome.system.proxy.socks");
        widget = GTK_WIDGET (gtk_builder_get_object (panel->priv->builder,
                                                     "entry_proxy_socks"));
        g_settings_bind (settings_tmp, "host",
                         widget, "text",
                         G_SETTINGS_BIND_DEFAULT);
        adjustment = GTK_ADJUSTMENT (gtk_builder_get_object (panel->priv->builder,
                                                             "adjustment_proxy_port_socks"));
        g_settings_bind (settings_tmp, "port",
                         adjustment, "value",
                         G_SETTINGS_BIND_DEFAULT);
        g_object_unref (settings_tmp);

        widget = GTK_WIDGET (gtk_builder_get_object (panel->priv->builder,
                                                     "treeview_devices"));
        panel_add_devices_columns (panel, GTK_TREE_VIEW (widget));
        selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
        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"));
        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);

        /* setup wireless combobox model */
        combobox = GTK_COMBO_BOX (gtk_builder_get_object (panel->priv->builder,
                                                          "combobox_wireless_network_name"));
        g_signal_connect (combobox, "changed",
                          G_CALLBACK (wireless_ap_changed_cb),
                          panel);

        renderer = panel_cell_renderer_mode_new ();
        gtk_cell_renderer_set_padding (renderer, 4, 0);
        gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combobox),
                                    renderer,
                                    FALSE);
        gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combobox), renderer,
                                        "mode", PANEL_WIRELESS_COLUMN_MODE,
                                        NULL);

        renderer = panel_cell_renderer_security_new ();
        gtk_cell_renderer_set_padding (renderer, 4, 0);
        gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combobox),
                                    renderer,
                                    FALSE);
        gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combobox), renderer,
                                        "security", PANEL_WIRELESS_COLUMN_SECURITY,
                                        NULL);

        /* sort networks in drop down */
        sortable = GTK_TREE_SORTABLE (gtk_builder_get_object (panel->priv->builder,
                                                              "liststore_wireless_network"));
        gtk_tree_sortable_set_sort_column_id (sortable,
                                              PANEL_WIRELESS_COLUMN_SORT,
                                              GTK_SORT_ASCENDING);
        gtk_tree_sortable_set_sort_func (sortable,
                                         PANEL_WIRELESS_COLUMN_SORT,
                                         wireless_ap_model_sort_cb,
                                         sortable,
                                         NULL);

        renderer = panel_cell_renderer_signal_new ();
        gtk_cell_renderer_set_padding (renderer, 4, 0);
        gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combobox),
                                    renderer,
                                    FALSE);
        gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combobox), renderer,
                                        "signal", PANEL_WIRELESS_COLUMN_STRENGTH,
                                        NULL);

        /* 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);

        widget = GTK_WIDGET (gtk_builder_get_object (panel->priv->builder,
                                                     "device_wired_off_switch"));
        g_signal_connect (widget, "notify::active",
                          G_CALLBACK (device_off_toggled), panel);
        widget = GTK_WIDGET (gtk_builder_get_object (panel->priv->builder,
                                                     "device_wireless_off_switch"));
        g_signal_connect (widget, "notify::active",
                          G_CALLBACK (device_off_toggled), panel);
        widget = GTK_WIDGET (gtk_builder_get_object (panel->priv->builder,
                                                     "device_mobilebb_off_switch"));
        g_signal_connect (widget, "notify::active",
                          G_CALLBACK (device_off_toggled), panel);
        widget = GTK_WIDGET (gtk_builder_get_object (panel->priv->builder,
                                                     "device_vpn_off_switch"));
        g_signal_connect (widget, "notify::active",
                          G_CALLBACK (device_off_toggled), panel);
        widget = GTK_WIDGET (gtk_builder_get_object (panel->priv->builder,
                                                     "device_proxy_off_switch"));
        g_signal_connect (widget, "notify::active",
                          G_CALLBACK (device_off_toggled), panel);

        g_signal_connect (panel->priv->client, "notify::wireless-enabled",
                          G_CALLBACK (wireless_enabled_toggled), panel);
        g_signal_connect (panel->priv->client, "notify::wimax-enabled",
                          G_CALLBACK (wimax_enabled_toggled), panel);

        widget = GTK_WIDGET (gtk_builder_get_object (panel->priv->builder,
                                                     "button_wired_options"));
        g_signal_connect (widget, "clicked",
                          G_CALLBACK (edit_connection), panel);

        widget = GTK_WIDGET (gtk_builder_get_object (panel->priv->builder,
                                                     "button_wireless_options"));
        g_signal_connect (widget, "clicked",
                          G_CALLBACK (edit_connection), panel);

        widget = GTK_WIDGET (gtk_builder_get_object (panel->priv->builder,
                                                     "button_vpn_options"));
        g_signal_connect (widget, "clicked",
                          G_CALLBACK (edit_connection), panel);

        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);

        /* nothing to unlock yet */
        widget = GTK_WIDGET (gtk_builder_get_object (panel->priv->builder,
                                                     "button_unlock"));
        gtk_widget_set_sensitive (widget, FALSE);

        widget = GTK_WIDGET (gtk_builder_get_object (panel->priv->builder,
                                                     "switch_flight_mode"));
        ret = nm_client_wireless_get_enabled (panel->priv->client);
        gtk_switch_set_active (GTK_SWITCH (widget), !ret);
        g_signal_connect (GTK_SWITCH (widget), "notify::active",
                          G_CALLBACK (cc_network_panel_notify_enable_active_cb),
                          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);

        /* hide stuff that doesn't work yet */
        widget = GTK_WIDGET (gtk_builder_get_object (panel->priv->builder,
                                                     "button_unlock"));
        gtk_widget_hide (widget);

        widget = GTK_WIDGET (gtk_builder_get_object (panel->priv->builder,
                                                     "vbox1"));
        gtk_widget_reparent (widget, (GtkWidget *) panel);
}

void
cc_network_panel_register (GIOModule *module)
{
        cc_network_panel_register_type (G_TYPE_MODULE (module));
        g_io_extension_point_implement (CC_SHELL_PANEL_EXTENSION_POINT,
                                        CC_TYPE_NETWORK_PANEL,
                                        "network", 0);
}
