// SPDX-License-Identifier: GPL-2.0+
/* NetworkManager Applet -- allow user control over networking
*
* Dan Williams <dcbw@redhat.com>
*
* Copyright 2008 - 2014 Red Hat, Inc.
*/
#include "nm-default.h"
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/ether.h>
#include <ctype.h>
#include "applet.h"
#include "applet-device-wifi.h"
#include "ap-menu-item.h"
#include "utils.h"
#include "nma-wifi-dialog.h"
#include "mobile-helpers.h"
#define ACTIVE_AP_TAG "active-ap"
static void wifi_dialog_response_cb (GtkDialog *dialog, gint response, gpointer user_data);
static NMAccessPoint *update_active_ap (NMDevice *device, NMDeviceState state, NMApplet *applet);
/*****************************************************************************/
typedef struct {
NMApplet *applet;
NMDevice *device;
NMAccessPoint *ap;
gulong signal_id;
} ActiveAPData;
static void _active_ap_set (NMApplet *applet, NMDevice *device, NMAccessPoint *ap);
static void _active_ap_set_weakref (gpointer data, GObject *where_the_object_was);
static void
_active_ap_set_notify (NMAccessPoint *ap, GParamSpec *pspec, gpointer user_data)
{
ActiveAPData *d = user_data;
g_return_if_fail (NM_IS_ACCESS_POINT (ap));
g_return_if_fail (d);
g_return_if_fail (NM_IS_APPLET (d->applet));
g_return_if_fail (NM_IS_DEVICE (d->device));
g_return_if_fail (d->ap == ap);
g_return_if_fail (d->signal_id);
applet_schedule_update_icon (d->applet);
}
static void
_active_ap_data_free (ActiveAPData *d)
{
if (d->device)
g_object_weak_unref ((GObject *) d->device, _active_ap_set_weakref, d);
if (d->ap) {
g_object_weak_unref ((GObject *) d->ap, _active_ap_set_weakref, d);
g_signal_handler_disconnect (d->ap, d->signal_id);
}
g_slice_free (ActiveAPData, d);
}
static NMAccessPoint *
_active_ap_get (NMApplet *applet, NMDevice *device)
{
GSList *list, *iter;
g_return_val_if_fail (NM_IS_APPLET (applet), NULL);
g_return_val_if_fail (NM_IS_DEVICE (device), NULL);
list = g_object_get_data ((GObject *) applet, ACTIVE_AP_TAG);
for (iter = list; iter; iter = iter->next) {
ActiveAPData *d = iter->data;
if (device == d->device && d->ap)
return d->ap;
}
return NULL;
}
static void
_active_ap_set_destroy (gpointer data)
{
g_slist_free_full (data, (GDestroyNotify) _active_ap_data_free);
}
static void
_active_ap_set_weakref (gpointer data, GObject *where_the_object_was)
{
ActiveAPData *d = data;
NMApplet *applet = d->applet;
if ((GObject *) d->ap == where_the_object_was)
d->ap = NULL;
else if ((GObject *) d->device == where_the_object_was)
d->device = NULL;
else
g_return_if_reached ();
_active_ap_set (applet, NULL, NULL);
applet_schedule_update_icon (applet);
}
static void
_active_ap_set (NMApplet *applet, NMDevice *device, NMAccessPoint *ap)
{
GSList *list, *iter, *list0, *pcurrent;
ActiveAPData *d;
g_return_if_fail (NM_IS_APPLET (applet));
g_return_if_fail (!device || NM_IS_DEVICE (device));
g_return_if_fail (!ap || NM_IS_ACCESS_POINT (ap));
list0 = g_object_get_data ((GObject *) applet, ACTIVE_AP_TAG);
list = list0;
remove_empty:
pcurrent = NULL;
for (iter = list; iter; iter = iter->next) {
d = iter->data;
if (!d->device || !d->ap) {
_active_ap_data_free (d);
list = g_slist_delete_link (list, iter);
goto remove_empty;
}
if (device && d->device == device)
pcurrent = iter;
}
if (!device)
goto out;
if (!ap) {
if (pcurrent) {
_active_ap_data_free (pcurrent->data);
list = g_slist_delete_link (list, pcurrent);
}
goto out;
}
if (pcurrent) {
d = pcurrent->data;
if (d->ap == ap)
goto out;
g_object_weak_unref ((GObject *) d->ap, _active_ap_set_weakref, d);
g_signal_handler_disconnect (d->ap, d->signal_id);
} else {
d = g_slice_new (ActiveAPData);
d->applet = applet;
d->device = device;
g_object_weak_ref ((GObject *) device, _active_ap_set_weakref, d);
list = g_slist_append (list, d);
}
d->ap = ap;
g_object_weak_ref ((GObject *) ap, _active_ap_set_weakref, d);
d->signal_id = g_signal_connect (ap,
"notify::" NM_ACCESS_POINT_STRENGTH,
G_CALLBACK (_active_ap_set_notify),
d);
out:
if (list0 != list) {
g_object_replace_data ((GObject *) applet, ACTIVE_AP_TAG,
list0, list,
_active_ap_set_destroy, NULL);
}
}
/*****************************************************************************/
static void
show_ignore_focus_stealing_prevention (GtkWidget *widget)
{
gtk_widget_realize (widget);
gtk_widget_show (widget);
gtk_window_present (GTK_WINDOW (widget));
}
gboolean
applet_wifi_connect_to_hidden_network (NMApplet *applet)
{
GtkWidget *dialog;
dialog = nma_wifi_dialog_new_for_hidden (applet->nm_client);
if (dialog) {
g_signal_connect (dialog, "response",
G_CALLBACK (wifi_dialog_response_cb),
applet);
show_ignore_focus_stealing_prevention (dialog);
}
return !!dialog;
}
void
nma_menu_add_hidden_network_item (GtkWidget *menu, NMApplet *applet)
{
GtkWidget *menu_item;
GtkWidget *label;
menu_item = gtk_menu_item_new ();
label = gtk_label_new_with_mnemonic (_("_Connect to Hidden Wi-Fi Network…"));
gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
gtk_container_add (GTK_CONTAINER (menu_item), label);
gtk_widget_show_all (menu_item);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
g_signal_connect_swapped (menu_item, "activate",
G_CALLBACK (applet_wifi_connect_to_hidden_network),
applet);
}
gboolean
applet_wifi_can_create_wifi_network (NMApplet *applet)
{
gboolean disabled, allowed = FALSE;
NMClientPermissionResult perm;
/* FIXME: check WIFI_SHARE_PROTECTED too, and make the wifi dialog
* handle the permissions as well so that admins can restrict open network
* creation separately from protected network creation.
*/
perm = nm_client_get_permission_result (applet->nm_client, NM_CLIENT_PERMISSION_WIFI_SHARE_OPEN);
if (perm == NM_CLIENT_PERMISSION_RESULT_YES || perm == NM_CLIENT_PERMISSION_RESULT_AUTH) {
disabled = g_settings_get_boolean (applet->gsettings, PREF_DISABLE_WIFI_CREATE);
if (!disabled)
allowed = TRUE;
}
return allowed;
}
gboolean
applet_wifi_create_wifi_network (NMApplet *applet)
{
GtkWidget *dialog;
dialog = nma_wifi_dialog_new_for_create (applet->nm_client);
if (dialog) {
g_signal_connect (dialog, "response",
G_CALLBACK (wifi_dialog_response_cb),
applet);
show_ignore_focus_stealing_prevention (dialog);
}
return !!dialog;
}
void
nma_menu_add_create_network_item (GtkWidget *menu, NMApplet *applet)
{
GtkWidget *menu_item;
GtkWidget *label;
menu_item = gtk_menu_item_new ();
label = gtk_label_new_with_mnemonic (_("Create _New Wi-Fi Network…"));
gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
gtk_container_add (GTK_CONTAINER (menu_item), label);
gtk_widget_show_all (menu_item);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
g_signal_connect_swapped (menu_item, "activate",
G_CALLBACK (applet_wifi_create_wifi_network),
applet);
if (!applet_wifi_can_create_wifi_network (applet))
gtk_widget_set_sensitive (GTK_WIDGET (menu_item), FALSE);
}
typedef struct {
NMApplet *applet;
NMDeviceWifi *device;
NMAccessPoint *ap;
NMConnection *connection;
} WifiMenuItemInfo;
static void
wifi_menu_item_info_destroy (gpointer data, GClosure *closure)
{
WifiMenuItemInfo *info = (WifiMenuItemInfo *) data;
g_object_unref (G_OBJECT (info->device));
g_object_unref (G_OBJECT (info->ap));
if (info->connection)
g_object_unref (G_OBJECT (info->connection));
g_slice_free (WifiMenuItemInfo, data);
}
/*
* NOTE: this list should *not* contain networks that you would like to
* automatically roam to like "Starbucks" or "AT&T" or "T-Mobile HotSpot".
*/
static const char *manf_default_ssids[] = {
"linksys",
"linksys-a",
"linksys-g",
"default",
"belkin54g",
"NETGEAR",
"o2DSL",
"WLAN",
"ALICE-WLAN",
NULL
};
static gboolean
is_ssid_in_list (GBytes *ssid, const char **list)
{
while (*list) {
if (g_bytes_get_size (ssid) == strlen (*list)) {
if (!memcmp (*list, g_bytes_get_data (ssid, NULL), g_bytes_get_size (ssid)))
return TRUE;
}
list++;
}
return FALSE;
}
static gboolean
is_manufacturer_default_ssid (GBytes *ssid)
{
return is_ssid_in_list (ssid, manf_default_ssids);
}
static char *
get_ssid_utf8 (NMAccessPoint *ap)
{
char *ssid_utf8 = NULL;
GBytes *ssid;
if (ap) {
ssid = nm_access_point_get_ssid (ap);
if (ssid)
ssid_utf8 = nm_utils_ssid_to_utf8 (g_bytes_get_data (ssid, NULL), g_bytes_get_size (ssid));
}
if (!ssid_utf8)
ssid_utf8 = g_strdup (_("(none)"));
return ssid_utf8;
}
/* List known trojan networks that should never be shown to the user */
static const char *blacklisted_ssids[] = {
/* http://www.npr.org/templates/story/story.php?storyId=130451369 */
"Free Public Wi-Fi",
NULL
};
static gboolean
is_blacklisted_ssid (GBytes *ssid)
{
return is_ssid_in_list (ssid, blacklisted_ssids);
}
static void
clamp_ap_to_bssid (NMAccessPoint *ap, NMSettingWireless *s_wifi)
{
const char *str_bssid;
struct ether_addr *eth_addr;
GByteArray *bssid;
/* For a certain list of known ESSIDs which are commonly preset by ISPs
* and manufacturers and often unchanged by users, lock the connection
* to the BSSID so that we don't try to auto-connect to your grandma's
* neighbor's Wi-Fi.
*/
str_bssid = nm_access_point_get_bssid (ap);
if (str_bssid) {
eth_addr = ether_aton (str_bssid);
if (eth_addr) {
bssid = g_byte_array_sized_new (ETH_ALEN);
g_byte_array_append (bssid, eth_addr->ether_addr_octet, ETH_ALEN);
g_object_set (G_OBJECT (s_wifi),
NM_SETTING_WIRELESS_BSSID, bssid,
NULL);
g_byte_array_free (bssid, TRUE);
}
}
}
typedef struct {
NMApplet *applet;
AppletNewAutoConnectionCallback callback;
gpointer callback_data;
} MoreInfo;
static void
more_info_wifi_dialog_response_cb (GtkDialog *foo,
gint response,
gpointer user_data)
{
NMAWifiDialog *dialog = NMA_WIFI_DIALOG (foo);
MoreInfo *info = user_data;
NMConnection *connection = NULL;
NMDevice *device = NULL;
NMAccessPoint *ap = NULL;
if (response != GTK_RESPONSE_OK) {
info->callback (NULL, FALSE, TRUE, info->callback_data);
goto done;
}
/* nma_wifi_dialog_get_connection() returns a connection with the
* refcount incremented, so the caller must remember to unref it.
*/
connection = nma_wifi_dialog_get_connection (dialog, &device, &ap);
g_assert (connection);
g_assert (device);
info->callback (connection, TRUE, FALSE, info->callback_data);
/* Balance nma_wifi_dialog_get_connection() */
g_object_unref (connection);
done:
g_free (info);
gtk_widget_hide (GTK_WIDGET (dialog));
gtk_widget_destroy (GTK_WIDGET (dialog));
}
static gboolean
can_get_permission (NMApplet *applet, NMClientPermission perm)
{
if ( applet->permissions[perm] == NM_CLIENT_PERMISSION_RESULT_YES
|| applet->permissions[perm] == NM_CLIENT_PERMISSION_RESULT_AUTH)
return TRUE;
return FALSE;
}
static gboolean
wifi_new_auto_connection (NMDevice *device,
gpointer dclass_data,
AppletNewAutoConnectionCallback callback,
gpointer callback_data)
{
WifiMenuItemInfo *info = (WifiMenuItemInfo *) dclass_data;
NMApplet *applet;
NMAccessPoint *ap;
NMConnection *connection;
NMSettingConnection *s_con;
NMSettingWireless *s_wifi = NULL;
NMSettingWirelessSecurity *s_wsec;
NMSetting8021x *s_8021x = NULL;
GBytes *ssid;
NM80211ApSecurityFlags wpa_flags, rsn_flags;
GtkWidget *dialog;
MoreInfo *more_info;
char *uuid;
g_return_val_if_fail (dclass_data, FALSE);
g_return_val_if_fail (NM_IS_DEVICE (device), FALSE);
g_return_val_if_fail (NM_IS_ACCESS_POINT (info->ap), FALSE);
g_return_val_if_fail (NM_IS_APPLET (info->applet), FALSE);
applet = info->applet;
ap = info->ap;
connection = nm_simple_connection_new ();
/* Make the new connection available only for the current user */
s_con = (NMSettingConnection *) nm_setting_connection_new ();
nm_setting_connection_add_permission (s_con, "user", g_get_user_name (), NULL);
nm_connection_add_setting (connection, NM_SETTING (s_con));
ssid = nm_access_point_get_ssid (ap);
if ( (nm_access_point_get_mode (ap) == NM_802_11_MODE_INFRA)
&& (is_manufacturer_default_ssid (ssid) == TRUE)) {
/* Lock connection to this AP if it's a manufacturer-default SSID
* so that we don't randomly connect to some other 'linksys'
*/
s_wifi = (NMSettingWireless *) nm_setting_wireless_new ();
clamp_ap_to_bssid (ap, s_wifi);
nm_connection_add_setting (connection, NM_SETTING (s_wifi));
}
/* If the AP is WPA[2]-Enterprise then we need to set up a minimal 802.1x
* setting and ask the user for more information.
*/
rsn_flags = nm_access_point_get_rsn_flags (ap);
wpa_flags = nm_access_point_get_wpa_flags (ap);
if ( (rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_802_1X)
|| (wpa_flags & NM_802_11_AP_SEC_KEY_MGMT_802_1X)) {
/* Need a UUID for the "always ask" stuff in the Dialog of Doom */
uuid = nm_utils_uuid_generate ();
g_object_set (s_con, NM_SETTING_CONNECTION_UUID, uuid, NULL);
g_free (uuid);
if (!s_wifi) {
s_wifi = (NMSettingWireless *) nm_setting_wireless_new ();
nm_connection_add_setting (connection, NM_SETTING (s_wifi));
}
g_object_set (s_wifi,
NM_SETTING_WIRELESS_SSID, ssid,
NULL);
s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new ();
g_object_set (s_wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-eap", NULL);
nm_connection_add_setting (connection, NM_SETTING (s_wsec));
s_8021x = (NMSetting8021x *) nm_setting_802_1x_new ();
nm_setting_802_1x_add_eap_method (s_8021x, "ttls");
g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTH, "mschapv2", NULL);
nm_connection_add_setting (connection, NM_SETTING (s_8021x));
}
/* If it's an 802.1x connection, we need more information, so pop up the
* Dialog Of Doom.
*/
if (s_8021x) {
if (!can_get_permission (applet, NM_CLIENT_PERMISSION_SETTINGS_MODIFY_SYSTEM) &&
!can_get_permission (applet, NM_CLIENT_PERMISSION_SETTINGS_MODIFY_OWN)) {
const char *text = _("Failed to add new connection");
const char *err_text = _("Insufficient privileges.");
g_warning ("%s: %s", text, err_text);
utils_show_error_dialog (_("Connection failure"), text, err_text, FALSE, NULL);
g_clear_object (&connection);
return FALSE;
}
more_info = g_malloc0 (sizeof (*more_info));
more_info->applet = applet;
more_info->callback = callback;
more_info->callback_data = callback_data;
dialog = nma_wifi_dialog_new (applet->nm_client, connection, device, ap, FALSE);
if (dialog) {
g_signal_connect (dialog, "response",
G_CALLBACK (more_info_wifi_dialog_response_cb),
more_info);
show_ignore_focus_stealing_prevention (dialog);
}
} else {
/* Everything else can just get activated right away */
callback (connection, TRUE, FALSE, callback_data);
}
return TRUE;
}
static void
wifi_menu_item_activate (GtkMenuItem *item, gpointer user_data)
{
WifiMenuItemInfo *info = (WifiMenuItemInfo *) user_data;
const char *specific_object = NULL;
if (info->ap)
specific_object = nm_object_get_path (NM_OBJECT (info->ap));
applet_menu_item_activate_helper (NM_DEVICE (info->device),
info->connection,
specific_object ? specific_object : "/",
info->applet,
user_data);
}
struct dup_data {
NMDevice *device;
NMNetworkMenuItem *found;
char *hash;
};
static void
find_duplicate (gpointer d, gpointer user_data)
{
struct dup_data * data = (struct dup_data *) user_data;
NMDevice *device;
const char *hash;
GtkWidget *widget = GTK_WIDGET (d);
g_assert (d && widget);
g_return_if_fail (data);
g_return_if_fail (data->hash);
if (data->found || !NM_IS_NETWORK_MENU_ITEM (widget))
return;
device = g_object_get_data (G_OBJECT (widget), "device");
if (NM_DEVICE (device) != data->device)
return;
hash = nm_network_menu_item_get_hash (NM_NETWORK_MENU_ITEM (widget));
if (hash && (strcmp (hash, data->hash) == 0))
data->found = NM_NETWORK_MENU_ITEM (widget);
}
static NMNetworkMenuItem *
create_new_ap_item (NMDeviceWifi *device,
NMAccessPoint *ap,
struct dup_data *dup_data,
const GPtrArray *connections,
NMApplet *applet)
{
WifiMenuItemInfo *info;
int i;
GtkWidget *item;
GPtrArray *dev_connections;
GPtrArray *ap_connections;
dev_connections = nm_device_filter_connections (NM_DEVICE (device), connections);
ap_connections = nm_access_point_filter_connections (ap, dev_connections);
g_ptr_array_unref (dev_connections);
item = nm_network_menu_item_new (ap,
nm_device_wifi_get_capabilities (device),
dup_data->hash,
ap_connections->len != 0,
applet);
g_object_set_data (G_OBJECT (item), "device", NM_DEVICE (device));
/* If there's only one connection, don't show the submenu */
if (ap_connections->len > 1) {
GtkWidget *submenu;
submenu = gtk_menu_new ();
for (i = 0; i < ap_connections->len; i++) {
NMConnection *connection = NM_CONNECTION (ap_connections->pdata[i]);
NMSettingConnection *s_con;
GtkWidget *subitem;
s_con = nm_connection_get_setting_connection (connection);
subitem = gtk_menu_item_new_with_label (nm_setting_connection_get_id (s_con));
info = g_slice_new0 (WifiMenuItemInfo);
info->applet = applet;
info->device = g_object_ref (device);
info->ap = g_object_ref (ap);
info->connection = g_object_ref (connection);
g_signal_connect_data (subitem, "activate",
G_CALLBACK (wifi_menu_item_activate),
info,
wifi_menu_item_info_destroy, 0);
gtk_menu_shell_append (GTK_MENU_SHELL (submenu), GTK_WIDGET (subitem));
gtk_widget_show (subitem);
}
gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu);
} else {
NMConnection *connection;
info = g_slice_new0 (WifiMenuItemInfo);
info->applet = applet;
info->device = g_object_ref (device);
info->ap = g_object_ref (ap);
if (ap_connections->len == 1) {
connection = NM_CONNECTION (ap_connections->pdata[0]);
info->connection = g_object_ref (connection);
}
g_signal_connect_data (GTK_WIDGET (item),
"activate",
G_CALLBACK (wifi_menu_item_activate),
info,
wifi_menu_item_info_destroy,
0);
}
g_ptr_array_unref (ap_connections);
return NM_NETWORK_MENU_ITEM (item);
}
static NMNetworkMenuItem *
get_menu_item_for_ap (NMDeviceWifi *device,
NMAccessPoint *ap,
const GPtrArray *connections,
GSList *menu_list,
NMApplet *applet)
{
GBytes *ssid;
struct dup_data dup_data = { NULL, NULL };
/* Don't add BSSs that hide their SSID or are blacklisted */
ssid = nm_access_point_get_ssid (ap);
if ( !ssid
|| nm_utils_is_empty_ssid (g_bytes_get_data (ssid, NULL), g_bytes_get_size (ssid))
|| is_blacklisted_ssid (ssid))
return NULL;
/* Find out if this AP is a member of a larger network that all uses the
* same SSID and security settings. If so, we'll already have a menu item
* for this SSID, so just update that item's strength and add this AP to
* menu item's duplicate list.
*/
dup_data.found = NULL;
dup_data.hash = g_object_get_data (G_OBJECT (ap), "hash");
g_return_val_if_fail (dup_data.hash != NULL, NULL);
dup_data.device = NM_DEVICE (device);
g_slist_foreach (menu_list, find_duplicate, &dup_data);
if (dup_data.found) {
nm_network_menu_item_set_strength (dup_data.found, nm_access_point_get_strength (ap), applet);
nm_network_menu_item_add_dupe (dup_data.found, ap);
return NULL;
}
return create_new_ap_item (device, ap, &dup_data, connections, applet);
}
static gint
sort_by_name (gconstpointer tmpa, gconstpointer tmpb)
{
NMNetworkMenuItem *a = NM_NETWORK_MENU_ITEM (tmpa);
NMNetworkMenuItem *b = NM_NETWORK_MENU_ITEM (tmpb);
const char *a_ssid, *b_ssid;
gboolean a_adhoc, b_adhoc;
int i;
if (a && !b)
return 1;
else if (!a && b)
return -1;
else if (a == b)
return 0;
a_ssid = nm_network_menu_item_get_ssid (a);
b_ssid = nm_network_menu_item_get_ssid (b);
if (a_ssid && !b_ssid)
return 1;
if (b_ssid && !a_ssid)
return -1;
if (a_ssid && b_ssid) {
i = g_ascii_strcasecmp (a_ssid, b_ssid);
if (i != 0)
return i;
}
/* If the names are the same, sort infrastructure APs first */
a_adhoc = nm_network_menu_item_get_is_adhoc (a);
b_adhoc = nm_network_menu_item_get_is_adhoc (b);
if (a_adhoc && !b_adhoc)
return 1;
else if (!a_adhoc && b_adhoc)
return -1;
return 0;
}
/* Sort menu items for the top-level menu:
* 1) whether there's a saved connection or not
* a) sort alphabetically within #1
* 2) encrypted without a saved connection
* 3) unencrypted without a saved connection
*/
static gint
sort_toplevel (gconstpointer tmpa, gconstpointer tmpb)
{
NMNetworkMenuItem *a = NM_NETWORK_MENU_ITEM (tmpa);
NMNetworkMenuItem *b = NM_NETWORK_MENU_ITEM (tmpb);
gboolean a_fave, b_fave;
if (a && !b)
return 1;
else if (!a && b)
return -1;
else if (a == b)
return 0;
a_fave = nm_network_menu_item_get_has_connections (a);
b_fave = nm_network_menu_item_get_has_connections (b);
/* Items with a saved connection first */
if (a_fave && !b_fave)
return -1;
else if (!a_fave && b_fave)
return 1;
else if (!a_fave && !b_fave) {
gboolean a_enc = nm_network_menu_item_get_is_encrypted (a);
gboolean b_enc = nm_network_menu_item_get_is_encrypted (b);
/* If neither item has a saved connection, sort by encryption */
if (a_enc && !b_enc)
return -1;
else if (!a_enc && b_enc)
return 1;
}
/* For all other cases (both have saved connections, both are encrypted, or
* both are unencrypted) just sort by name.
*/
return sort_by_name (a, b);
}
static void
wifi_add_menu_item (NMDevice *device,
gboolean multiple_devices,
const GPtrArray *connections,
NMConnection *active,
GtkWidget *menu,
NMApplet *applet)
{
NMDeviceWifi *wdev;
char *text;
const GPtrArray *aps;
int i;
NMAccessPoint *active_ap = NULL;
GSList *iter;
gboolean wifi_enabled = TRUE;
gboolean wifi_hw_enabled = TRUE;
GSList *menu_items = NULL; /* All menu items we'll be adding */
NMNetworkMenuItem *item, *active_item = NULL;
GtkWidget *widget;
wdev = NM_DEVICE_WIFI (device);
aps = nm_device_wifi_get_access_points (wdev);
if (multiple_devices) {
const char *desc;
desc = nm_device_get_description (device);
if (aps && aps->len > 1)
text = g_strdup_printf (_("Wi-Fi Networks (%s)"), desc);
else
text = g_strdup_printf (_("Wi-Fi Network (%s)"), desc);
} else
text = g_strdup (ngettext ("Wi-Fi Network", "Wi-Fi Networks", aps ? aps->len : 0));
widget = applet_menu_item_create_device_item_helper (device, applet, text);
g_free (text);
gtk_widget_set_sensitive (widget, FALSE);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), widget);
gtk_widget_show (widget);
/* Add the active AP if we're connected to something and the device is available */
if (!nma_menu_device_check_unusable (device)) {
active_ap = nm_device_wifi_get_active_access_point (wdev);
if (active_ap) {
active_item = item = get_menu_item_for_ap (wdev, active_ap, connections, NULL, applet);
if (item) {
nm_network_menu_item_set_active (item, TRUE);
menu_items = g_slist_append (menu_items, item);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), GTK_WIDGET (item));
gtk_widget_show_all (GTK_WIDGET (item));
}
}
}
/* Notify user of unmanaged or unavailable device */
wifi_enabled = nm_client_wireless_get_enabled (applet->nm_client);
wifi_hw_enabled = nm_client_wireless_hardware_get_enabled (applet->nm_client);
widget = nma_menu_device_get_menu_item (device, applet,
wifi_hw_enabled ?
(wifi_enabled ? NULL : _("Wi-Fi is disabled")) :
_("Wi-Fi is disabled by hardware switch"));
if (widget) {
gtk_menu_shell_append (GTK_MENU_SHELL (menu), widget);
gtk_widget_show (widget);
}
/* If disabled or rfkilled or whatever, nothing left to do */
if (nma_menu_device_check_unusable (device))
goto out;
/* Create menu items for the rest of the APs */
for (i = 0; aps && (i < aps->len); i++) {
NMAccessPoint *ap = g_ptr_array_index (aps, i);
item = get_menu_item_for_ap (wdev, ap, connections, menu_items, applet);
if (item)
menu_items = g_slist_append (menu_items, item);
}
/* Now remove the active AP item from the list, as we've already dealt with
* it. (Needed it when creating menu items for the rest of the APs though
* to ensure duplicate APs are handled correctly)
*/
if (active_item)
menu_items = g_slist_remove (menu_items, active_item);
/* Sort all the rest of the menu items for the top-level menu */
menu_items = g_slist_sort (menu_items, sort_toplevel);
if (g_slist_length (menu_items)) {
GSList *submenu_items = NULL;
GSList *topmenu_items = NULL;
guint32 num_for_toplevel = 5;
applet_menu_item_add_complex_separator_helper (menu, applet, _("Available"));
if (g_slist_length (menu_items) == (num_for_toplevel + 1))
num_for_toplevel++;
/* Add the first 5 APs (or 6 if there are only 6 total) from the sorted
* toplevel list.
*/
for (iter = menu_items; iter && num_for_toplevel; iter = g_slist_next (iter)) {
topmenu_items = g_slist_append (topmenu_items, iter->data);
num_for_toplevel--;
submenu_items = iter->next;
}
topmenu_items = g_slist_sort (topmenu_items, sort_by_name);
for (iter = topmenu_items; iter; iter = g_slist_next (iter)) {
gtk_menu_shell_append (GTK_MENU_SHELL (menu), GTK_WIDGET (iter->data));
gtk_widget_show_all (GTK_WIDGET (iter->data));
}
g_slist_free (topmenu_items);
topmenu_items = NULL;
/* If there are any submenu items, make a submenu for those */
if (submenu_items) {
GtkWidget *subitem, *submenu;
GSList *sorted_subitems;
subitem = gtk_menu_item_new_with_mnemonic (_("More networks"));
submenu = gtk_menu_new ();
gtk_menu_item_set_submenu (GTK_MENU_ITEM (subitem), submenu);
/* Sort the subitems alphabetically */
sorted_subitems = g_slist_copy (submenu_items);
sorted_subitems = g_slist_sort (sorted_subitems, sort_by_name);
/* And add the rest to the submenu */
for (iter = sorted_subitems; iter; iter = g_slist_next (iter))
gtk_menu_shell_append (GTK_MENU_SHELL (submenu), GTK_WIDGET (iter->data));
g_slist_free (sorted_subitems);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), subitem);
gtk_widget_show_all (subitem);
}
}
out:
g_slist_free (menu_items);
}
static void
notify_active_ap_changed_cb (NMDeviceWifi *device,
GParamSpec *pspec,
NMApplet *applet)
{
NMRemoteConnection *connection;
NMSettingWireless *s_wireless;
NMAccessPoint *new;
GBytes *ssid_ap, *ssid;
NMDeviceState state;
state = nm_device_get_state (NM_DEVICE (device));
new = update_active_ap (NM_DEVICE (device), state, applet);
if (!new || (state != NM_DEVICE_STATE_ACTIVATED))
return;
connection = applet_get_exported_connection_for_device (NM_DEVICE (device), applet);
if (!connection)
return;
s_wireless = nm_connection_get_setting_wireless (NM_CONNECTION (connection));
if (!s_wireless)
return;
ssid_ap = nm_access_point_get_ssid (new);
ssid = nm_setting_wireless_get_ssid (s_wireless);
if ( !ssid_ap
|| !ssid
|| !nm_utils_same_ssid (g_bytes_get_data (ssid, NULL), g_bytes_get_size (ssid),
g_bytes_get_data (ssid_ap, NULL), g_bytes_get_size (ssid_ap),
TRUE))
return;
applet_schedule_update_icon (applet);
}
static void
add_hash_to_ap (NMAccessPoint *ap)
{
char *hash;
hash = utils_hash_ap (nm_access_point_get_ssid (ap),
nm_access_point_get_mode (ap),
nm_access_point_get_flags (ap),
nm_access_point_get_wpa_flags (ap),
nm_access_point_get_rsn_flags (ap));
g_object_set_data_full (G_OBJECT (ap), "hash", hash, (GDestroyNotify) g_free);
}
static void
notify_ap_prop_changed_cb (NMAccessPoint *ap,
GParamSpec *pspec,
NMApplet *applet)
{
const char *prop = g_param_spec_get_name (pspec);
if ( !strcmp (prop, NM_ACCESS_POINT_FLAGS)
|| !strcmp (prop, NM_ACCESS_POINT_WPA_FLAGS)
|| !strcmp (prop, NM_ACCESS_POINT_RSN_FLAGS)
|| !strcmp (prop, NM_ACCESS_POINT_SSID)
|| !strcmp (prop, NM_ACCESS_POINT_FREQUENCY)
|| !strcmp (prop, NM_ACCESS_POINT_MODE)) {
add_hash_to_ap (ap);
}
}
static void
wifi_available_dont_show_cb (NotifyNotification *notify,
gchar *id,
gpointer user_data)
{
NMApplet *applet = NM_APPLET (user_data);
if (!id || strcmp (id, "dont-show"))
return;
g_settings_set_boolean (applet->gsettings,
PREF_SUPPRESS_WIFI_NETWORKS_AVAILABLE,
TRUE);
}
struct ap_notification_data
{
NMApplet *applet;
NMDeviceWifi *device;
guint id;
gulong last_notification_time;
guint new_con_id;
};
/* Scan the list of access points, looking for the case where we have no
* known (i.e. autoconnect) access points, but we do have unknown ones.
*
* If we find one, notify the user.
*/
static gboolean
idle_check_avail_access_point_notification (gpointer datap)
{
struct ap_notification_data *data = datap;
NMApplet *applet = data->applet;
NMDeviceWifi *device = data->device;
int i;
const GPtrArray *aps;
GPtrArray *all_connections;
GPtrArray *connections;
GTimeVal timeval;
gboolean have_unused_access_point = FALSE;
gboolean have_no_autoconnect_points = TRUE;
data->id = 0;
if (nm_client_get_state (data->applet->nm_client) != NM_STATE_DISCONNECTED)
return FALSE;
if (nm_device_get_state (NM_DEVICE (device)) != NM_DEVICE_STATE_DISCONNECTED)
return FALSE;
g_get_current_time (&timeval);
if ((timeval.tv_sec - data->last_notification_time) < 60*60) /* Notify at most once an hour */
return FALSE;
all_connections = applet_get_all_connections (applet);
connections = nm_device_filter_connections (NM_DEVICE (device), all_connections);
g_ptr_array_unref (all_connections);
aps = nm_device_wifi_get_access_points (device);
for (i = 0; i < aps->len; i++) {
NMAccessPoint *ap = aps->pdata[i];
GPtrArray *ap_connections;
int a;
gboolean is_autoconnect = FALSE;
if (!nm_access_point_get_ssid (ap))
continue;
ap_connections = nm_access_point_filter_connections (ap, connections);
for (a = 0; a < ap_connections->len; a++) {
NMConnection *connection = NM_CONNECTION (ap_connections->pdata[a]);
NMSettingConnection *s_con;
s_con = nm_connection_get_setting_connection (connection);
if (nm_setting_connection_get_autoconnect (s_con)) {
is_autoconnect = TRUE;
break;
}
}
g_ptr_array_unref (ap_connections);
if (!is_autoconnect)
have_unused_access_point = TRUE;
else
have_no_autoconnect_points = FALSE;
}
g_ptr_array_unref (connections);
if (!(have_unused_access_point && have_no_autoconnect_points))
return FALSE;
/* Avoid notifying too often */
g_get_current_time (&timeval);
data->last_notification_time = timeval.tv_sec;
applet_do_notify (applet,
NOTIFY_URGENCY_LOW,
_("Wi-Fi Networks Available"),
_("Use the network menu to connect to a Wi-Fi network"),
"nm-device-wireless",
"dont-show",
_("Don’t show this message again"),
wifi_available_dont_show_cb,
applet);
return FALSE;
}
static void
queue_avail_access_point_notification (NMDevice *device)
{
struct ap_notification_data *data;
data = g_object_get_data (G_OBJECT (device), "notify-wifi-avail-data");
if (data->id != 0)
return;
if (g_settings_get_boolean (data->applet->gsettings,
PREF_SUPPRESS_WIFI_NETWORKS_AVAILABLE))
return;
data->id = g_timeout_add_seconds (3, idle_check_avail_access_point_notification, data);
}
static void
access_point_added_cb (NMDeviceWifi *device,
NMAccessPoint *ap,
gpointer user_data)
{
NMApplet *applet = NM_APPLET (user_data);
add_hash_to_ap (ap);
g_signal_connect (G_OBJECT (ap),
"notify",
G_CALLBACK (notify_ap_prop_changed_cb),
applet);
queue_avail_access_point_notification (NM_DEVICE (device));
applet_schedule_update_menu (applet);
}
static void
access_point_removed_cb (NMDeviceWifi *device,
NMAccessPoint *ap,
gpointer user_data)
{
NMApplet *applet = NM_APPLET (user_data);
NMAccessPoint *old;
/* If this AP was the active AP, make sure ACTIVE_AP_TAG gets cleared from
* its device.
*/
old = _active_ap_get (applet, (NMDevice *) device);
if (old == ap) {
_active_ap_set (applet, (NMDevice *) device, NULL);
applet_schedule_update_icon (applet);
}
applet_schedule_update_menu (applet);
}
static void
on_new_connection (NMClient *client,
NMRemoteConnection *connection,
gpointer datap)
{
struct ap_notification_data *data = datap;
queue_avail_access_point_notification (NM_DEVICE (data->device));
}
static void
free_ap_notification_data (gpointer user_data)
{
struct ap_notification_data *data = user_data;
NMClient *client = data->applet->nm_client;
nm_clear_g_source (&data->id);
if (client)
g_signal_handler_disconnect (client, data->new_con_id);
memset (data, 0, sizeof (*data));
g_free (data);
}
static void
wifi_device_added (NMDevice *device, NMApplet *applet)
{
NMDeviceWifi *wdev = NM_DEVICE_WIFI (device);
const GPtrArray *aps;
int i;
struct ap_notification_data *data;
guint id;
g_signal_connect (wdev,
"notify::" NM_DEVICE_WIFI_ACTIVE_ACCESS_POINT,
G_CALLBACK (notify_active_ap_changed_cb),
applet);
g_signal_connect (wdev,
"access-point-added",
G_CALLBACK (access_point_added_cb),
applet);
g_signal_connect (wdev,
"access-point-removed",
G_CALLBACK (access_point_removed_cb),
applet);
/* Now create the per-device hooks for watching for available wifi
* connections.
*/
data = g_new0 (struct ap_notification_data, 1);
data->applet = applet;
data->device = wdev;
/* We also need to hook up to the client to find out when we have new connections
* that might be candididates. Keep the ID around so we can disconnect
* when the device is destroyed.
*/
id = g_signal_connect (applet->nm_client,
NM_CLIENT_CONNECTION_ADDED,
G_CALLBACK (on_new_connection),
data);
data->new_con_id = id;
g_object_set_data_full (G_OBJECT (wdev), "notify-wifi-avail-data",
data, free_ap_notification_data);
queue_avail_access_point_notification (device);
/* Hash all APs this device knows about */
aps = nm_device_wifi_get_access_points (wdev);
for (i = 0; aps && (i < aps->len); i++)
add_hash_to_ap (g_ptr_array_index (aps, i));
}
static NMAccessPoint *
update_active_ap (NMDevice *device, NMDeviceState state, NMApplet *applet)
{
NMAccessPoint *new = NULL;
if (state == NM_DEVICE_STATE_PREPARE ||
state == NM_DEVICE_STATE_CONFIG ||
state == NM_DEVICE_STATE_IP_CONFIG ||
state == NM_DEVICE_STATE_NEED_AUTH ||
state == NM_DEVICE_STATE_ACTIVATED) {
new = nm_device_wifi_get_active_access_point (NM_DEVICE_WIFI (device));
}
_active_ap_set (applet, device, new);
return new;
}
static void
wifi_device_state_changed (NMDevice *device,
NMDeviceState new_state,
NMDeviceState old_state,
NMDeviceStateReason reason,
NMApplet *applet)
{
update_active_ap (device, new_state, applet);
if (new_state == NM_DEVICE_STATE_DISCONNECTED)
queue_avail_access_point_notification (device);
}
static void
wifi_notify_connected (NMDevice *device,
const char *msg,
NMApplet *applet)
{
NMAccessPoint *ap;
char *esc_ssid;
char *ssid_msg;
const char *signal_strength_icon;
ap = _active_ap_get (applet, device);
esc_ssid = get_ssid_utf8 (ap);
if (!ap)
signal_strength_icon = "nm-device-wireless";
else
signal_strength_icon = mobile_helper_get_quality_icon_name (nm_access_point_get_strength (ap));
ssid_msg = g_strdup_printf (_("You are now connected to the Wi-Fi network “%s”."), esc_ssid);
applet_do_notify_with_pref (applet, _("Connection Established"),
ssid_msg, signal_strength_icon,
PREF_DISABLE_CONNECTED_NOTIFICATIONS);
g_free (ssid_msg);
g_free (esc_ssid);
}
static void
wifi_get_icon (NMDevice *device,
NMDeviceState state,
NMConnection *connection,
GdkPixbuf **out_pixbuf,
const char **out_icon_name,
char **tip,
NMApplet *applet)
{
NMSettingConnection *s_con;
NMAccessPoint *ap;
const char *id;
guint8 strength;
g_return_if_fail (out_icon_name && !*out_icon_name);
g_return_if_fail (tip && !*tip);
ap = _active_ap_get (applet, device);
id = nm_device_get_iface (device);
if (connection) {
s_con = nm_connection_get_setting_connection (connection);
id = nm_setting_connection_get_id (s_con);
}
switch (state) {
case NM_DEVICE_STATE_PREPARE:
*tip = g_strdup_printf (_("Preparing Wi-Fi network connection “%s”…"), id);
break;
case NM_DEVICE_STATE_CONFIG:
*tip = g_strdup_printf (_("Configuring Wi-Fi network connection “%s”…"), id);
break;
case NM_DEVICE_STATE_NEED_AUTH:
*tip = g_strdup_printf (_("User authentication required for Wi-Fi network “%s”…"), id);
break;
case NM_DEVICE_STATE_IP_CONFIG:
*tip = g_strdup_printf (_("Requesting a Wi-Fi network address for “%s”…"), id);
break;
case NM_DEVICE_STATE_ACTIVATED:
strength = ap ? nm_access_point_get_strength (ap) : 0;
strength = MIN (strength, 100);
*out_icon_name = mobile_helper_get_quality_icon_name (strength);
if (ap) {
char *ssid = get_ssid_utf8 (ap);
*tip = g_strdup_printf (_("Wi-Fi network connection “%s” active: %s (%d%%)"),
id, ssid, strength);
g_free (ssid);
} else
*tip = g_strdup_printf (_("Wi-Fi network connection “%s” active"), id);
break;
default:
break;
}
}
static void
activate_existing_cb (GObject *client,
GAsyncResult *result,
gpointer user_data)
{
GError *error = NULL;
NMActiveConnection *active;
active = nm_client_activate_connection_finish (NM_CLIENT (client), result, &error);
g_clear_object (&active);
if (error) {
const char *text = _("Failed to activate connection");
char *err_text = g_strdup_printf ("(%d) %s", error->code,
error->message ? error->message : _("Unknown error"));
g_warning ("%s: %s", text, err_text);
utils_show_error_dialog (_("Connection failure"), text, err_text, FALSE, NULL);
g_free (err_text);
g_error_free (error);
}
applet_schedule_update_icon (NM_APPLET (user_data));
}
static void
activate_new_cb (GObject *client,
GAsyncResult *result,
gpointer user_data)
{
GError *error = NULL;
NMActiveConnection *active;
active = nm_client_add_and_activate_connection_finish (NM_CLIENT (client), result, &error);
g_clear_object (&active);
if (error) {
const char *text = _("Failed to add new connection");
char *err_text = g_strdup_printf ("(%d) %s", error->code,
error->message ? error->message : _("Unknown error"));
g_warning ("%s: %s", text, err_text);
utils_show_error_dialog (_("Connection failure"), text, err_text, FALSE, NULL);
g_free (err_text);
g_error_free (error);
}
applet_schedule_update_icon (NM_APPLET (user_data));
}
static void
wifi_dialog_response_cb (GtkDialog *foo,
gint response,
gpointer user_data)
{
NMAWifiDialog *dialog = NMA_WIFI_DIALOG (foo);
NMApplet *applet = NM_APPLET (user_data);
NMConnection *connection = NULL, *fuzzy_match = NULL;
NMDevice *device = NULL;
NMAccessPoint *ap = NULL;
GPtrArray *all;
int i;
if (response != GTK_RESPONSE_OK)
goto done;
/* nma_wifi_dialog_get_connection() returns a connection with the
* refcount incremented, so the caller must remember to unref it.
*/
connection = nma_wifi_dialog_get_connection (dialog, &device, &ap);
g_assert (connection);
g_assert (device);
/* Find a similar connection and use that instead */
all = applet_get_all_connections (applet);
for (i = 0; i < all->len; i++) {
if (nm_connection_compare (connection,
NM_CONNECTION (all->pdata[i]),
(NM_SETTING_COMPARE_FLAG_FUZZY | NM_SETTING_COMPARE_FLAG_IGNORE_ID))) {
fuzzy_match = NM_CONNECTION (all->pdata[i]);
break;
}
}
g_ptr_array_unref (all);
if (fuzzy_match) {
nm_client_activate_connection_async (applet->nm_client,
fuzzy_match,
device,
ap ? nm_object_get_path (NM_OBJECT (ap)) : NULL,
NULL,
activate_existing_cb,
applet);
} else {
NMSetting *s_con;
NMSettingWireless *s_wifi = NULL;
const char *mode = NULL;
/* Entirely new connection */
/* Don't autoconnect adhoc networks by default for now */
s_wifi = nm_connection_get_setting_wireless (connection);
if (s_wifi)
mode = nm_setting_wireless_get_mode (s_wifi);
if (g_strcmp0 (mode, "adhoc") == 0 || g_strcmp0 (mode, "ap") == 0) {
s_con = nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION);
if (!s_con) {
s_con = nm_setting_connection_new ();
nm_connection_add_setting (connection, s_con);
}
g_object_set (G_OBJECT (s_con), NM_SETTING_CONNECTION_AUTOCONNECT, FALSE, NULL);
}
nm_client_add_and_activate_connection_async (applet->nm_client,
connection,
device,
ap ? nm_object_get_path (NM_OBJECT (ap)) : NULL,
NULL,
activate_new_cb,
applet);
}
/* Balance nma_wifi_dialog_get_connection() */
g_object_unref (connection);
done:
gtk_widget_hide (GTK_WIDGET (dialog));
gtk_widget_destroy (GTK_WIDGET (dialog));
}
static GVariant *
remove_unwanted_secrets (GVariant *secrets, gboolean keep_8021X)
{
GVariant *copy, *setting_dict;
const char *setting_name;
GVariantBuilder conn_builder;
GVariantIter conn_iter;
g_variant_builder_init (&conn_builder, NM_VARIANT_TYPE_CONNECTION);
g_variant_iter_init (&conn_iter, secrets);
while (g_variant_iter_next (&conn_iter, "{&s@a{sv}}", &setting_name, &setting_dict)) {
if ( !strcmp (setting_name, NM_SETTING_WIRELESS_SECURITY_SETTING_NAME)
|| (!strcmp (setting_name, NM_SETTING_802_1X_SETTING_NAME) && keep_8021X))
g_variant_builder_add (&conn_builder, "{s@a{sv}}", setting_name, setting_dict);
g_variant_unref (setting_dict);
}
copy = g_variant_builder_end (&conn_builder);
g_variant_unref (secrets);
return copy;
}
typedef struct {
SecretsRequest req;
GtkWidget *dialog;
} NMWifiInfo;
static void
free_wifi_info (SecretsRequest *req)
{
NMWifiInfo *info = (NMWifiInfo *) req;
if (info->dialog) {
gtk_widget_hide (info->dialog);
gtk_widget_destroy (info->dialog);
info->dialog = NULL;
}
}
static void
get_secrets_dialog_response_cb (GtkDialog *foo,
gint response,
gpointer user_data)
{
SecretsRequest *req = user_data;
NMWifiInfo *info = (NMWifiInfo *) req;
NMAWifiDialog *dialog = NMA_WIFI_DIALOG (info->dialog);
NMConnection *connection = NULL;
NMSettingWirelessSecurity *s_wireless_sec;
GVariant *secrets = NULL;
const char *key_mgmt, *auth_alg;
gboolean keep_8021X = FALSE;
GError *error = NULL;
if (response != GTK_RESPONSE_OK) {
g_set_error (&error,
NM_SECRET_AGENT_ERROR,
NM_SECRET_AGENT_ERROR_USER_CANCELED,
"%s.%d (%s): canceled",
__FILE__, __LINE__, __func__);
goto done;
}
connection = nma_wifi_dialog_get_connection (dialog, NULL, NULL);
if (!connection) {
g_set_error (&error,
NM_SECRET_AGENT_ERROR,
NM_SECRET_AGENT_ERROR_FAILED,
"%s.%d (%s): couldn't get connection from Wi-Fi dialog.",
__FILE__, __LINE__, __func__);
goto done;
}
/* Second-guess which setting NM wants secrets for. */
s_wireless_sec = nm_connection_get_setting_wireless_security (connection);
if (!s_wireless_sec) {
g_set_error (&error,
NM_SECRET_AGENT_ERROR,
NM_SECRET_AGENT_ERROR_INVALID_CONNECTION,
"%s.%d (%s): requested setting '802-11-wireless-security'"
" didn't exist in the connection.",
__FILE__, __LINE__, __func__);
goto done; /* Unencrypted */
}
secrets = nm_connection_to_dbus (connection, NM_CONNECTION_SERIALIZE_ONLY_SECRETS);
if (!secrets) {
g_set_error (&error,
NM_SECRET_AGENT_ERROR,
NM_SECRET_AGENT_ERROR_FAILED,
"%s.%d (%s): failed to hash connection '%s'.",
__FILE__, __LINE__, __func__, nm_connection_get_id (connection));
goto done;
}
/* If the user chose an 802.1x-based auth method, return 802.1x secrets,
* not wireless secrets. Can happen with Dynamic WEP, because NM doesn't
* know the capabilities of the AP (since Dynamic WEP APs don't broadcast
* beacons), and therefore defaults to requesting WEP secrets from the
* wireless-security setting, not the 802.1x setting.
*/
key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wireless_sec);
if (!strcmp (key_mgmt, "ieee8021x") || !strcmp (key_mgmt, "wpa-eap")) {
/* LEAP secrets aren't in the 802.1x setting */
auth_alg = nm_setting_wireless_security_get_auth_alg (s_wireless_sec);
if (!auth_alg || strcmp (auth_alg, "leap")) {
NMSetting8021x *s_8021x;
s_8021x = nm_connection_get_setting_802_1x (connection);
if (!s_8021x) {
g_set_error (&error,
NM_SECRET_AGENT_ERROR,
NM_SECRET_AGENT_ERROR_INVALID_CONNECTION,
"%s.%d (%s): requested setting '802-1x' didn't"
" exist in the connection.",
__FILE__, __LINE__, __func__);
goto done;
}
keep_8021X = TRUE;
}
}
/* Remove all not-relevant secrets (inner dicts) */
secrets = remove_unwanted_secrets (secrets, keep_8021X);
g_variant_take_ref (secrets);
done:
applet_secrets_request_complete (req, secrets, error);
applet_secrets_request_free (req);
if (secrets)
g_variant_unref (secrets);
if (connection)
nm_connection_clear_secrets (connection);
}
static gboolean
wifi_get_secrets (SecretsRequest *req, GError **error)
{
NMWifiInfo *info = (NMWifiInfo *) req;
g_return_val_if_fail (!info->dialog, FALSE);
info->dialog = nma_wifi_dialog_new_for_secrets (req->applet->nm_client,
req->connection,
req->setting_name,
(const char *const*) req->hints);
if (info->dialog) {
applet_secrets_request_set_free_func (req, free_wifi_info);
g_signal_connect (info->dialog, "response",
G_CALLBACK (get_secrets_dialog_response_cb),
info);
show_ignore_focus_stealing_prevention (info->dialog);
} else {
g_set_error (error,
NM_SECRET_AGENT_ERROR,
NM_SECRET_AGENT_ERROR_FAILED,
"%s.%d (%s): couldn't display secrets UI",
__FILE__, __LINE__, __func__);
}
return !!info->dialog;
}
NMADeviceClass *
applet_device_wifi_get_class (NMApplet *applet)
{
NMADeviceClass *dclass;
dclass = g_slice_new0 (NMADeviceClass);
if (!dclass)
return NULL;
dclass->new_auto_connection = wifi_new_auto_connection;
dclass->add_menu_item = wifi_add_menu_item;
dclass->device_added = wifi_device_added;
dclass->device_state_changed = wifi_device_state_changed;
dclass->notify_connected = wifi_notify_connected;
dclass->get_icon = wifi_get_icon;
dclass->get_secrets = wifi_get_secrets;
dclass->secrets_request_size = sizeof (NMWifiInfo);
return dclass;
}