summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libnm-core/NetworkManager.h563
-rw-r--r--libnm-core/NetworkManagerVPN.h302
-rw-r--r--libnm-core/crypto.c749
-rw-r--r--libnm-core/crypto.h145
-rw-r--r--libnm-core/crypto_gnutls.c493
-rw-r--r--libnm-core/crypto_nss.c562
-rw-r--r--libnm-core/nm-connection.c2189
-rw-r--r--libnm-core/nm-connection.h256
-rw-r--r--libnm-core/nm-param-spec-specialized.c972
-rw-r--r--libnm-core/nm-param-spec-specialized.h43
-rw-r--r--libnm-core/nm-setting-8021x.c3706
-rw-r--r--libnm-core/nm-setting-8021x.h297
-rw-r--r--libnm-core/nm-setting-adsl.c465
-rw-r--r--libnm-core/nm-setting-adsl.h96
-rw-r--r--libnm-core/nm-setting-bluetooth.c297
-rw-r--r--libnm-core/nm-setting-bluetooth.h100
-rw-r--r--libnm-core/nm-setting-bond.c804
-rw-r--r--libnm-core/nm-setting-bond.h117
-rw-r--r--libnm-core/nm-setting-bridge-port.c304
-rw-r--r--libnm-core/nm-setting-bridge-port.h86
-rw-r--r--libnm-core/nm-setting-bridge.c576
-rw-r--r--libnm-core/nm-setting-bridge.h102
-rw-r--r--libnm-core/nm-setting-cdma.c356
-rw-r--r--libnm-core/nm-setting-cdma.h87
-rw-r--r--libnm-core/nm-setting-connection.c1333
-rw-r--r--libnm-core/nm-setting-connection.h151
-rw-r--r--libnm-core/nm-setting-dcb.c1207
-rw-r--r--libnm-core/nm-setting-dcb.h187
-rw-r--r--libnm-core/nm-setting-generic.c100
-rw-r--r--libnm-core/nm-setting-generic.h78
-rw-r--r--libnm-core/nm-setting-gsm.c723
-rw-r--r--libnm-core/nm-setting-gsm.h204
-rw-r--r--libnm-core/nm-setting-infiniband.c472
-rw-r--r--libnm-core/nm-setting-infiniband.h85
-rw-r--r--libnm-core/nm-setting-ip4-config.c1895
-rw-r--r--libnm-core/nm-setting-ip4-config.h229
-rw-r--r--libnm-core/nm-setting-ip6-config.c1683
-rw-r--r--libnm-core/nm-setting-ip6-config.h256
-rw-r--r--libnm-core/nm-setting-olpc-mesh.c274
-rw-r--r--libnm-core/nm-setting-olpc-mesh.h82
-rw-r--r--libnm-core/nm-setting-ppp.c823
-rw-r--r--libnm-core/nm-setting-ppp.h115
-rw-r--r--libnm-core/nm-setting-pppoe.c342
-rw-r--r--libnm-core/nm-setting-pppoe.h87
-rw-r--r--libnm-core/nm-setting-private.h125
-rw-r--r--libnm-core/nm-setting-serial.c320
-rw-r--r--libnm-core/nm-setting-serial.h89
-rw-r--r--libnm-core/nm-setting-team-port.c184
-rw-r--r--libnm-core/nm-setting-team-port.h79
-rw-r--r--libnm-core/nm-setting-team.c254
-rw-r--r--libnm-core/nm-setting-team.h81
-rw-r--r--libnm-core/nm-setting-vlan.c841
-rw-r--r--libnm-core/nm-setting-vlan.h156
-rw-r--r--libnm-core/nm-setting-vpn.c870
-rw-r--r--libnm-core/nm-setting-vpn.h115
-rw-r--r--libnm-core/nm-setting-wimax.c262
-rw-r--r--libnm-core/nm-setting-wimax.h73
-rw-r--r--libnm-core/nm-setting-wired.c1032
-rw-r--r--libnm-core/nm-setting-wired.h131
-rw-r--r--libnm-core/nm-setting-wireless-security.c1603
-rw-r--r--libnm-core/nm-setting-wireless-security.h178
-rw-r--r--libnm-core/nm-setting-wireless.c1245
-rw-r--r--libnm-core/nm-setting-wireless.h174
-rw-r--r--libnm-core/nm-setting.c1445
-rw-r--r--libnm-core/nm-setting.h317
-rw-r--r--libnm-core/nm-utils-private.h65
-rw-r--r--libnm-core/nm-utils.c2531
-rw-r--r--libnm-core/nm-utils.h180
-rw-r--r--libnm-core/nm-value-transforms.c593
-rw-r--r--libnm-core/nm-version.h.in123
-rw-r--r--libnm-core/tests/test-crypto.c384
-rw-r--r--libnm-core/tests/test-general.c2681
-rw-r--r--libnm-core/tests/test-secrets.c754
-rw-r--r--libnm-core/tests/test-setting-8021x.c443
-rw-r--r--libnm-core/tests/test-setting-dcb.c328
-rw-r--r--libnm-core/tests/test-settings-defaults.c134
-rw-r--r--libnm/nm-access-point.c673
-rw-r--r--libnm/nm-access-point.h96
-rw-r--r--libnm/nm-active-connection.c851
-rw-r--r--libnm/nm-active-connection.h105
-rw-r--r--libnm/nm-client.c2442
-rw-r--r--libnm/nm-client.h255
-rw-r--r--libnm/nm-dbus-helpers-private.h36
-rw-r--r--libnm/nm-dbus-helpers.c99
-rw-r--r--libnm/nm-device-adsl.c242
-rw-r--r--libnm/nm-device-adsl.h75
-rw-r--r--libnm/nm-device-bond.c347
-rw-r--r--libnm/nm-device-bond.h82
-rw-r--r--libnm/nm-device-bridge.c359
-rw-r--r--libnm/nm-device-bridge.h84
-rw-r--r--libnm/nm-device-bt.c373
-rw-r--r--libnm/nm-device-bt.h90
-rw-r--r--libnm/nm-device-ethernet.c392
-rw-r--r--libnm/nm-device-ethernet.h87
-rw-r--r--libnm/nm-device-generic.c284
-rw-r--r--libnm/nm-device-generic.h79
-rw-r--r--libnm/nm-device-infiniband.c311
-rw-r--r--libnm/nm-device-infiniband.h82
-rw-r--r--libnm/nm-device-modem.c291
-rw-r--r--libnm/nm-device-modem.h79
-rw-r--r--libnm/nm-device-olpc-mesh.c326
-rw-r--r--libnm/nm-device-olpc-mesh.h81
-rw-r--r--libnm/nm-device-private.h26
-rw-r--r--libnm/nm-device-team.c353
-rw-r--r--libnm/nm-device-team.h85
-rw-r--r--libnm/nm-device-vlan.c353
-rw-r--r--libnm/nm-device-vlan.h86
-rw-r--r--libnm/nm-device-wifi.c838
-rw-r--r--libnm/nm-device-wifi.h115
-rw-r--r--libnm/nm-device-wimax.c755
-rw-r--r--libnm/nm-device-wimax.h97
-rw-r--r--libnm/nm-device.c2310
-rw-r--r--libnm/nm-device.h186
-rw-r--r--libnm/nm-dhcp4-config.c215
-rw-r--r--libnm/nm-dhcp4-config.h66
-rw-r--r--libnm/nm-dhcp6-config.c215
-rw-r--r--libnm/nm-dhcp6-config.h66
-rw-r--r--libnm/nm-ip4-config.c467
-rw-r--r--libnm/nm-ip4-config.h79
-rw-r--r--libnm/nm-ip6-config.c493
-rw-r--r--libnm/nm-ip6-config.h81
-rw-r--r--libnm/nm-object-cache.c88
-rw-r--r--libnm/nm-object-cache.h37
-rw-r--r--libnm/nm-object-private.h92
-rw-r--r--libnm/nm-object.c1445
-rw-r--r--libnm/nm-object.h91
-rw-r--r--libnm/nm-remote-connection-private.h33
-rw-r--r--libnm/nm-remote-connection.c963
-rw-r--r--libnm/nm-remote-connection.h149
-rw-r--r--libnm/nm-remote-settings.c1566
-rw-r--r--libnm/nm-remote-settings.h162
-rw-r--r--libnm/nm-secret-agent.c1077
-rw-r--r--libnm/nm-secret-agent.h307
-rw-r--r--libnm/nm-types-private.h37
-rw-r--r--libnm/nm-types.c419
-rw-r--r--libnm/nm-types.h54
-rw-r--r--libnm/nm-vpn-connection.c268
-rw-r--r--libnm/nm-vpn-connection.h73
-rw-r--r--libnm/nm-vpn-plugin-ui-interface.c247
-rw-r--r--libnm/nm-vpn-plugin-ui-interface.h229
-rw-r--r--libnm/nm-vpn-plugin-utils.c189
-rw-r--r--libnm/nm-vpn-plugin-utils.h39
-rw-r--r--libnm/nm-vpn-plugin.c1066
-rw-r--r--libnm/nm-vpn-plugin.h175
-rw-r--r--libnm/nm-wimax-nsp.c334
-rw-r--r--libnm/nm-wimax-nsp.h82
-rw-r--r--libnm/tests/common.c119
-rw-r--r--libnm/tests/common.h31
-rw-r--r--libnm/tests/test-nm-client.c898
-rw-r--r--libnm/tests/test-remote-settings-client.c456
150 files changed, 67616 insertions, 0 deletions
diff --git a/libnm-core/NetworkManager.h b/libnm-core/NetworkManager.h
new file mode 100644
index 0000000000..08b4e6cf6b
--- /dev/null
+++ b/libnm-core/NetworkManager.h
@@ -0,0 +1,563 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2004 - 2014 Red Hat, Inc.
+ */
+
+/* Definitions related to NetworkManager's D-Bus interfaces.
+ *
+ * Note that although this header is installed as part of libnm-util, it is also
+ * used by some external code that does not link to libnm-util.
+ */
+
+#ifndef NETWORK_MANAGER_H
+#define NETWORK_MANAGER_H
+
+#include "nm-version.h"
+
+/*
+ * dbus services details
+ */
+#define NM_DBUS_SERVICE "org.freedesktop.NetworkManager"
+
+#define NM_DBUS_PATH "/org/freedesktop/NetworkManager"
+#define NM_DBUS_INTERFACE "org.freedesktop.NetworkManager"
+#define NM_DBUS_INTERFACE_DEVICE NM_DBUS_INTERFACE ".Device"
+#define NM_DBUS_INTERFACE_DEVICE_WIRED NM_DBUS_INTERFACE_DEVICE ".Wired"
+#define NM_DBUS_INTERFACE_DEVICE_ADSL NM_DBUS_INTERFACE_DEVICE ".Adsl"
+#define NM_DBUS_INTERFACE_DEVICE_WIRELESS NM_DBUS_INTERFACE_DEVICE ".Wireless"
+#define NM_DBUS_INTERFACE_DEVICE_BLUETOOTH NM_DBUS_INTERFACE_DEVICE ".Bluetooth"
+#define NM_DBUS_INTERFACE_DEVICE_OLPC_MESH NM_DBUS_INTERFACE_DEVICE ".OlpcMesh"
+#define NM_DBUS_PATH_ACCESS_POINT NM_DBUS_PATH "/AccessPoint"
+#define NM_DBUS_INTERFACE_ACCESS_POINT NM_DBUS_INTERFACE ".AccessPoint"
+#define NM_DBUS_INTERFACE_DEVICE_MODEM NM_DBUS_INTERFACE_DEVICE ".Modem"
+#define NM_DBUS_INTERFACE_DEVICE_WIMAX NM_DBUS_INTERFACE_DEVICE ".WiMax"
+#define NM_DBUS_INTERFACE_WIMAX_NSP NM_DBUS_INTERFACE ".WiMax.Nsp"
+#define NM_DBUS_PATH_WIMAX_NSP NM_DBUS_PATH "/Nsp"
+#define NM_DBUS_INTERFACE_ACTIVE_CONNECTION NM_DBUS_INTERFACE ".Connection.Active"
+#define NM_DBUS_INTERFACE_IP4_CONFIG NM_DBUS_INTERFACE ".IP4Config"
+#define NM_DBUS_INTERFACE_DHCP4_CONFIG NM_DBUS_INTERFACE ".DHCP4Config"
+#define NM_DBUS_INTERFACE_IP6_CONFIG NM_DBUS_INTERFACE ".IP6Config"
+#define NM_DBUS_INTERFACE_DHCP6_CONFIG NM_DBUS_INTERFACE ".DHCP6Config"
+#define NM_DBUS_INTERFACE_DEVICE_INFINIBAND NM_DBUS_INTERFACE_DEVICE ".Infiniband"
+#define NM_DBUS_INTERFACE_DEVICE_BOND NM_DBUS_INTERFACE_DEVICE ".Bond"
+#define NM_DBUS_INTERFACE_DEVICE_TEAM NM_DBUS_INTERFACE_DEVICE ".Team"
+#define NM_DBUS_INTERFACE_DEVICE_VLAN NM_DBUS_INTERFACE_DEVICE ".Vlan"
+#define NM_DBUS_INTERFACE_DEVICE_BRIDGE NM_DBUS_INTERFACE_DEVICE ".Bridge"
+#define NM_DBUS_INTERFACE_DEVICE_GENERIC NM_DBUS_INTERFACE_DEVICE ".Generic"
+#define NM_DBUS_INTERFACE_DEVICE_VETH NM_DBUS_INTERFACE_DEVICE ".Veth"
+#define NM_DBUS_INTERFACE_DEVICE_TUN NM_DBUS_INTERFACE_DEVICE ".Tun"
+#define NM_DBUS_INTERFACE_DEVICE_MACVLAN NM_DBUS_INTERFACE_DEVICE ".Macvlan"
+#define NM_DBUS_INTERFACE_DEVICE_VXLAN NM_DBUS_INTERFACE_DEVICE ".Vxlan"
+#define NM_DBUS_INTERFACE_DEVICE_GRE NM_DBUS_INTERFACE_DEVICE ".Gre"
+
+
+#define NM_DBUS_IFACE_SETTINGS "org.freedesktop.NetworkManager.Settings"
+#define NM_DBUS_PATH_SETTINGS "/org/freedesktop/NetworkManager/Settings"
+
+#define NM_DBUS_IFACE_SETTINGS_CONNECTION "org.freedesktop.NetworkManager.Settings.Connection"
+#define NM_DBUS_PATH_SETTINGS_CONNECTION "/org/freedesktop/NetworkManager/Settings/Connection"
+#define NM_DBUS_IFACE_SETTINGS_CONNECTION_SECRETS "org.freedesktop.NetworkManager.Settings.Connection.Secrets"
+
+#define NM_DBUS_INTERFACE_AGENT_MANAGER NM_DBUS_INTERFACE ".AgentManager"
+#define NM_DBUS_PATH_AGENT_MANAGER "/org/freedesktop/NetworkManager/AgentManager"
+
+#define NM_DBUS_INTERFACE_SECRET_AGENT NM_DBUS_INTERFACE ".SecretAgent"
+#define NM_DBUS_PATH_SECRET_AGENT "/org/freedesktop/NetworkManager/SecretAgent"
+
+/**
+ * NMState:
+ * @NM_STATE_UNKNOWN: networking state is unknown
+ * @NM_STATE_ASLEEP: networking is not enabled
+ * @NM_STATE_DISCONNECTED: there is no active network connection
+ * @NM_STATE_DISCONNECTING: network connections are being cleaned up
+ * @NM_STATE_CONNECTING: a network connection is being started
+ * @NM_STATE_CONNECTED_LOCAL: there is only local IPv4 and/or IPv6 connectivity
+ * @NM_STATE_CONNECTED_SITE: there is only site-wide IPv4 and/or IPv6 connectivity
+ * @NM_STATE_CONNECTED_GLOBAL: there is global IPv4 and/or IPv6 Internet connectivity
+ *
+ * #NMState values indicate the current overall networking state.
+ *
+ * (Corresponds to the NM_STATE type in nm-manager.xml.)
+ **/
+typedef enum {
+ NM_STATE_UNKNOWN = 0,
+ NM_STATE_ASLEEP = 10,
+ NM_STATE_DISCONNECTED = 20,
+ NM_STATE_DISCONNECTING = 30,
+ NM_STATE_CONNECTING = 40,
+ NM_STATE_CONNECTED_LOCAL = 50,
+ NM_STATE_CONNECTED_SITE = 60,
+ NM_STATE_CONNECTED_GLOBAL = 70
+} NMState;
+
+/* For backwards compat */
+#define NM_STATE_CONNECTED NM_STATE_CONNECTED_GLOBAL
+
+/**
+ * NMConnectivityState:
+ * @NM_CONNECTIVITY_UNKNOWN: Network connectivity is unknown.
+ * @NM_CONNECTIVITY_NONE: The host is not connected to any network.
+ * @NM_CONNECTIVITY_PORTAL: The host is behind a captive portal and
+ * cannot reach the full Internet.
+ * @NM_CONNECTIVITY_LIMITED: The host is connected to a network, but
+ * does not appear to be able to reach the full Internet.
+ * @NM_CONNECTIVITY_FULL: The host is connected to a network, and
+ * appears to be able to reach the full Internet.
+ *
+ * (Corresponds to the NM_CONNECTIVITY type in nm-manager.xml.)
+ *
+ * Since: 0.9.8.6
+ */
+typedef enum {
+ NM_CONNECTIVITY_UNKNOWN,
+ NM_CONNECTIVITY_NONE,
+ NM_CONNECTIVITY_PORTAL,
+ NM_CONNECTIVITY_LIMITED,
+ NM_CONNECTIVITY_FULL
+} NMConnectivityState;
+
+/**
+ * NMDeviceType:
+ * @NM_DEVICE_TYPE_UNKNOWN: unknown device
+ * @NM_DEVICE_TYPE_GENERIC: generic support for unrecognized device types
+ * @NM_DEVICE_TYPE_ETHERNET: a wired ethernet device
+ * @NM_DEVICE_TYPE_WIFI: an 802.11 WiFi device
+ * @NM_DEVICE_TYPE_UNUSED1: not used
+ * @NM_DEVICE_TYPE_UNUSED2: not used
+ * @NM_DEVICE_TYPE_BT: a Bluetooth device supporting PAN or DUN access protocols
+ * @NM_DEVICE_TYPE_OLPC_MESH: an OLPC XO mesh networking device
+ * @NM_DEVICE_TYPE_WIMAX: an 802.16e Mobile WiMAX broadband device
+ * @NM_DEVICE_TYPE_MODEM: a modem supporting analog telephone, CDMA/EVDO,
+ * GSM/UMTS, or LTE network access protocols
+ * @NM_DEVICE_TYPE_INFINIBAND: an IP-over-InfiniBand device
+ * @NM_DEVICE_TYPE_BOND: a bond master interface
+ * @NM_DEVICE_TYPE_VLAN: an 802.1Q VLAN interface
+ * @NM_DEVICE_TYPE_ADSL: ADSL modem
+ * @NM_DEVICE_TYPE_BRIDGE: a bridge master interface
+ * @NM_DEVICE_TYPE_TEAM: a team master interface
+ *
+ * #NMDeviceType values indicate the type of hardware represented by
+ * an #NMDevice.
+ *
+ * (Corresponds to the NM_DEVICE_TYPE type in nm-device.xml.)
+ **/
+typedef enum {
+ NM_DEVICE_TYPE_UNKNOWN = 0,
+ NM_DEVICE_TYPE_ETHERNET = 1,
+ NM_DEVICE_TYPE_WIFI = 2,
+ NM_DEVICE_TYPE_UNUSED1 = 3,
+ NM_DEVICE_TYPE_UNUSED2 = 4,
+ NM_DEVICE_TYPE_BT = 5, /* Bluetooth */
+ NM_DEVICE_TYPE_OLPC_MESH = 6,
+ NM_DEVICE_TYPE_WIMAX = 7,
+ NM_DEVICE_TYPE_MODEM = 8,
+ NM_DEVICE_TYPE_INFINIBAND = 9,
+ NM_DEVICE_TYPE_BOND = 10,
+ NM_DEVICE_TYPE_VLAN = 11,
+ NM_DEVICE_TYPE_ADSL = 12,
+ NM_DEVICE_TYPE_BRIDGE = 13,
+ NM_DEVICE_TYPE_GENERIC = 14,
+ NM_DEVICE_TYPE_TEAM = 15,
+} NMDeviceType;
+
+/**
+ * NMDeviceCapabilities:
+ * @NM_DEVICE_CAP_NONE: device has no special capabilities
+ * @NM_DEVICE_CAP_NM_SUPPORTED: NetworkManager supports this device
+ * @NM_DEVICE_CAP_CARRIER_DETECT: this device can indicate carrier status
+ * @NM_DEVICE_CAP_IS_SOFTWARE: this device is a software device
+ *
+ * General device capability flags.
+ *
+ * (Corresponds to the NM_DEVICE_CAP type in nm-device-wifi.xml.)
+ **/
+typedef enum {
+ NM_DEVICE_CAP_NONE = 0x00000000,
+ NM_DEVICE_CAP_NM_SUPPORTED = 0x00000001,
+ NM_DEVICE_CAP_CARRIER_DETECT = 0x00000002,
+ NM_DEVICE_CAP_IS_SOFTWARE = 0x00000004,
+} NMDeviceCapabilities;
+
+
+/**
+ * NMDeviceWifiCapabilities:
+ * @NM_WIFI_DEVICE_CAP_NONE: device has no encryption/authentication capabilities
+ * @NM_WIFI_DEVICE_CAP_CIPHER_WEP40: device supports 40/64-bit WEP encryption
+ * @NM_WIFI_DEVICE_CAP_CIPHER_WEP104: device supports 104/128-bit WEP encryption
+ * @NM_WIFI_DEVICE_CAP_CIPHER_TKIP: device supports TKIP encryption
+ * @NM_WIFI_DEVICE_CAP_CIPHER_CCMP: device supports AES/CCMP encryption
+ * @NM_WIFI_DEVICE_CAP_WPA: device supports WPA1 authentication
+ * @NM_WIFI_DEVICE_CAP_RSN: device supports WPA2/RSN authentication
+ * @NM_WIFI_DEVICE_CAP_AP: device supports Access Point mode
+ * @NM_WIFI_DEVICE_CAP_ADHOC: device supports Ad-Hoc mode
+ *
+ * 802.11 specific device encryption and authentication capabilities.
+ *
+ * (Corresponds to the NM_802_11_DEVICE_CAP type in nm-device-wifi.xml.)
+ **/
+typedef enum {
+ NM_WIFI_DEVICE_CAP_NONE = 0x00000000,
+ NM_WIFI_DEVICE_CAP_CIPHER_WEP40 = 0x00000001,
+ NM_WIFI_DEVICE_CAP_CIPHER_WEP104 = 0x00000002,
+ NM_WIFI_DEVICE_CAP_CIPHER_TKIP = 0x00000004,
+ NM_WIFI_DEVICE_CAP_CIPHER_CCMP = 0x00000008,
+ NM_WIFI_DEVICE_CAP_WPA = 0x00000010,
+ NM_WIFI_DEVICE_CAP_RSN = 0x00000020,
+ NM_WIFI_DEVICE_CAP_AP = 0x00000040,
+ NM_WIFI_DEVICE_CAP_ADHOC = 0x00000080
+} NMDeviceWifiCapabilities;
+
+
+/**
+ * NM80211ApFlags:
+ * @NM_802_11_AP_FLAGS_NONE: access point has no special capabilities
+ * @NM_802_11_AP_FLAGS_PRIVACY: access point requires authentication and
+ * encryption (usually means WEP)
+ *
+ * 802.11 access point flags.
+ *
+ * (Corresponds to the NM_802_11_AP_FLAGS type in nm-access-point.xml.)
+ **/
+typedef enum {
+ NM_802_11_AP_FLAGS_NONE = 0x00000000,
+ NM_802_11_AP_FLAGS_PRIVACY = 0x00000001
+} NM80211ApFlags;
+
+/**
+ * NM80211ApSecurityFlags:
+ * @NM_802_11_AP_SEC_NONE: the access point has no special security requirements
+ * @NM_802_11_AP_SEC_PAIR_WEP40: 40/64-bit WEP is supported for
+ * pairwise/unicast encryption
+ * @NM_802_11_AP_SEC_PAIR_WEP104: 104/128-bit WEP is supported for
+ * pairwise/unicast encryption
+ * @NM_802_11_AP_SEC_PAIR_TKIP: TKIP is supported for pairwise/unicast encryption
+ * @NM_802_11_AP_SEC_PAIR_CCMP: AES/CCMP is supported for pairwise/unicast encryption
+ * @NM_802_11_AP_SEC_GROUP_WEP40: 40/64-bit WEP is supported for group/broadcast
+ * encryption
+ * @NM_802_11_AP_SEC_GROUP_WEP104: 104/128-bit WEP is supported for
+ * group/broadcast encryption
+ * @NM_802_11_AP_SEC_GROUP_TKIP: TKIP is supported for group/broadcast encryption
+ * @NM_802_11_AP_SEC_GROUP_CCMP: AES/CCMP is supported for group/broadcast
+ * encryption
+ * @NM_802_11_AP_SEC_KEY_MGMT_PSK: WPA/RSN Pre-Shared Key encryption is
+ * supported
+ * @NM_802_11_AP_SEC_KEY_MGMT_802_1X: 802.1x authentication and key management
+ * is supported
+ *
+ * 802.11 access point security and authentication flags. These flags describe
+ * the current security requirements of an access point as determined from the
+ * access point's beacon.
+ *
+ * (Corresponds to the NM_802_11_AP_SEC type in nm-access-point.xml.)
+ **/
+typedef enum {
+ NM_802_11_AP_SEC_NONE = 0x00000000,
+ NM_802_11_AP_SEC_PAIR_WEP40 = 0x00000001,
+ NM_802_11_AP_SEC_PAIR_WEP104 = 0x00000002,
+ NM_802_11_AP_SEC_PAIR_TKIP = 0x00000004,
+ NM_802_11_AP_SEC_PAIR_CCMP = 0x00000008,
+ NM_802_11_AP_SEC_GROUP_WEP40 = 0x00000010,
+ NM_802_11_AP_SEC_GROUP_WEP104 = 0x00000020,
+ NM_802_11_AP_SEC_GROUP_TKIP = 0x00000040,
+ NM_802_11_AP_SEC_GROUP_CCMP = 0x00000080,
+ NM_802_11_AP_SEC_KEY_MGMT_PSK = 0x00000100,
+ NM_802_11_AP_SEC_KEY_MGMT_802_1X = 0x00000200
+} NM80211ApSecurityFlags;
+
+/**
+ * NM80211Mode:
+ * @NM_802_11_MODE_UNKNOWN: the device or access point mode is unknown
+ * @NM_802_11_MODE_ADHOC: for both devices and access point objects, indicates
+ * the object is part of an Ad-Hoc 802.11 network without a central
+ * coordinating access point.
+ * @NM_802_11_MODE_INFRA: the device or access point is in infrastructure mode.
+ * For devices, this indicates the device is an 802.11 client/station. For
+ * access point objects, this indicates the object is an access point that
+ * provides connectivity to clients.
+ * @NM_802_11_MODE_AP: the device is an access point/hotspot. Not valid for
+ * access point objects; used only for hotspot mode on the local machine.
+ *
+ * Indicates the 802.11 mode an access point or device is currently in.
+ *
+ * (Corresponds to the NM_802_11_MODE type in generic-types.xml.)
+ **/
+typedef enum {
+ NM_802_11_MODE_UNKNOWN = 0,
+ NM_802_11_MODE_ADHOC,
+ NM_802_11_MODE_INFRA,
+ NM_802_11_MODE_AP
+} NM80211Mode;
+
+/**
+ * NMBluetoothCapabilities:
+ * @NM_BT_CAPABILITY_NONE: device has no usable capabilities
+ * @NM_BT_CAPABILITY_DUN: device provides Dial-Up Networking capability
+ * @NM_BT_CAPABILITY_NAP: device provides Network Access Point capability
+ *
+ * #NMBluetoothCapabilities values indicate the usable capabilities of a
+ * Bluetooth device.
+ *
+ * (Corresponds to the NM_BT_CAPABILITY type in nm-device-bt.xml.)
+ **/
+typedef enum {
+ NM_BT_CAPABILITY_NONE = 0x00000000,
+ NM_BT_CAPABILITY_DUN = 0x00000001,
+ NM_BT_CAPABILITY_NAP = 0x00000002,
+} NMBluetoothCapabilities;
+
+/**
+ * NMDeviceModemCapabilities:
+ * @NM_DEVICE_MODEM_CAPABILITY_NONE: modem has no usable capabilities
+ * @NM_DEVICE_MODEM_CAPABILITY_POTS: modem uses the analog wired telephone
+ * network and is not a wireless/cellular device
+ * @NM_DEVICE_MODEM_CAPABILITY_CDMA_EVDO: modem supports at least one of CDMA
+ * 1xRTT, EVDO revision 0, EVDO revision A, or EVDO revision B
+ * @NM_DEVICE_MODEM_CAPABILITY_GSM_UMTS: modem supports at least one of GSM,
+ * GPRS, EDGE, UMTS, HSDPA, HSUPA, or HSPA+ packet switched data capability
+ * @NM_DEVICE_MODEM_CAPABILITY_LTE: modem has LTE data capability
+ *
+ * #NMDeviceModemCapabilities values indicate the generic radio access
+ * technology families a modem device supports. For more information on the
+ * specific access technologies the device supports use the ModemManager D-Bus
+ * API.
+ *
+ * (Corresponds to the NM_DEVICE_MODEM_CAPABILITY type in nm-device-modem.xml.)
+ **/
+typedef enum {
+ NM_DEVICE_MODEM_CAPABILITY_NONE = 0x00000000,
+ NM_DEVICE_MODEM_CAPABILITY_POTS = 0x00000001,
+ NM_DEVICE_MODEM_CAPABILITY_CDMA_EVDO = 0x00000002,
+ NM_DEVICE_MODEM_CAPABILITY_GSM_UMTS = 0x00000004,
+ NM_DEVICE_MODEM_CAPABILITY_LTE = 0x00000008,
+} NMDeviceModemCapabilities;
+
+
+/**
+ * NMDeviceState:
+ * @NM_DEVICE_STATE_UNKNOWN: the device's state is unknown
+ * @NM_DEVICE_STATE_UNMANAGED: the device is recognized, but not managed by
+ * NetworkManager
+ * @NM_DEVICE_STATE_UNAVAILABLE: the device is managed by NetworkManager, but
+ * is not available for use. Reasons may include the wireless switched off,
+ * missing firmware, no ethernet carrier, missing supplicant or modem manager,
+ * etc.
+ * @NM_DEVICE_STATE_DISCONNECTED: the device can be activated, but is currently
+ * idle and not connected to a network.
+ * @NM_DEVICE_STATE_PREPARE: the device is preparing the connection to the
+ * network. This may include operations like changing the MAC address,
+ * setting physical link properties, and anything else required to connect
+ * to the requested network.
+ * @NM_DEVICE_STATE_CONFIG: the device is connecting to the requested network.
+ * This may include operations like associating with the WiFi AP, dialing
+ * the modem, connecting to the remote Bluetooth device, etc.
+ * @NM_DEVICE_STATE_NEED_AUTH: the device requires more information to continue
+ * connecting to the requested network. This includes secrets like WiFi
+ * passphrases, login passwords, PIN codes, etc.
+ * @NM_DEVICE_STATE_IP_CONFIG: the device is requesting IPv4 and/or IPv6
+ * addresses and routing information from the network.
+ * @NM_DEVICE_STATE_IP_CHECK: the device is checking whether further action is
+ * required for the requested network connection. This may include checking
+ * whether only local network access is available, whether a captive portal
+ * is blocking access to the Internet, etc.
+ * @NM_DEVICE_STATE_SECONDARIES: the device is waiting for a secondary
+ * connection (like a VPN) which must activated before the device can be
+ * activated
+ * @NM_DEVICE_STATE_ACTIVATED: the device has a network connection, either local
+ * or global.
+ * @NM_DEVICE_STATE_DEACTIVATING: a disconnection from the current network
+ * connection was requested, and the device is cleaning up resources used for
+ * that connection. The network connection may still be valid.
+ * @NM_DEVICE_STATE_FAILED: the device failed to connect to the requested
+ * network and is cleaning up the connection request
+ *
+ * (Corresponds to the NM_DEVICE_STATE type in nm-device.xml.)
+ **/
+typedef enum {
+ NM_DEVICE_STATE_UNKNOWN = 0,
+ NM_DEVICE_STATE_UNMANAGED = 10,
+ NM_DEVICE_STATE_UNAVAILABLE = 20,
+ NM_DEVICE_STATE_DISCONNECTED = 30,
+ NM_DEVICE_STATE_PREPARE = 40,
+ NM_DEVICE_STATE_CONFIG = 50,
+ NM_DEVICE_STATE_NEED_AUTH = 60,
+ NM_DEVICE_STATE_IP_CONFIG = 70,
+ NM_DEVICE_STATE_IP_CHECK = 80,
+ NM_DEVICE_STATE_SECONDARIES = 90,
+ NM_DEVICE_STATE_ACTIVATED = 100,
+ NM_DEVICE_STATE_DEACTIVATING = 110,
+ NM_DEVICE_STATE_FAILED = 120
+} NMDeviceState;
+
+
+/**
+ * NMDeviceStateReason:
+ * @NM_DEVICE_STATE_REASON_NONE: No reason given
+ * @NM_DEVICE_STATE_REASON_UNKNOWN: Unknown error
+ * @NM_DEVICE_STATE_REASON_NOW_MANAGED: Device is now managed
+ * @NM_DEVICE_STATE_REASON_NOW_UNMANAGED: Device is now unmanaged
+ * @NM_DEVICE_STATE_REASON_CONFIG_FAILED: The device could not be readied for configuration
+ * @NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE: IP configuration could not be reserved (no available address, timeout, etc)
+ * @NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED: The IP config is no longer valid
+ * @NM_DEVICE_STATE_REASON_NO_SECRETS: Secrets were required, but not provided
+ * @NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT: 802.1x supplicant disconnected
+ * @NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED: 802.1x supplicant configuration failed
+ * @NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED: 802.1x supplicant failed
+ * @NM_DEVICE_STATE_REASON_SUPPLICANT_TIMEOUT: 802.1x supplicant took too long to authenticate
+ * @NM_DEVICE_STATE_REASON_PPP_START_FAILED: PPP service failed to start
+ * @NM_DEVICE_STATE_REASON_PPP_DISCONNECT: PPP service disconnected
+ * @NM_DEVICE_STATE_REASON_PPP_FAILED: PPP failed
+ * @NM_DEVICE_STATE_REASON_DHCP_START_FAILED: DHCP client failed to start
+ * @NM_DEVICE_STATE_REASON_DHCP_ERROR: DHCP client error
+ * @NM_DEVICE_STATE_REASON_DHCP_FAILED: DHCP client failed
+ * @NM_DEVICE_STATE_REASON_SHARED_START_FAILED: Shared connection service failed to start
+ * @NM_DEVICE_STATE_REASON_SHARED_FAILED: Shared connection service failed
+ * @NM_DEVICE_STATE_REASON_AUTOIP_START_FAILED: AutoIP service failed to start
+ * @NM_DEVICE_STATE_REASON_AUTOIP_ERROR: AutoIP service error
+ * @NM_DEVICE_STATE_REASON_AUTOIP_FAILED: AutoIP service failed
+ * @NM_DEVICE_STATE_REASON_MODEM_BUSY: The line is busy
+ * @NM_DEVICE_STATE_REASON_MODEM_NO_DIAL_TONE: No dial tone
+ * @NM_DEVICE_STATE_REASON_MODEM_NO_CARRIER: No carrier could be established
+ * @NM_DEVICE_STATE_REASON_MODEM_DIAL_TIMEOUT: The dialing request timed out
+ * @NM_DEVICE_STATE_REASON_MODEM_DIAL_FAILED: The dialing attempt failed
+ * @NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED: Modem initialization failed
+ * @NM_DEVICE_STATE_REASON_GSM_APN_FAILED: Failed to select the specified APN
+ * @NM_DEVICE_STATE_REASON_GSM_REGISTRATION_NOT_SEARCHING: Not searching for networks
+ * @NM_DEVICE_STATE_REASON_GSM_REGISTRATION_DENIED: Network registration denied
+ * @NM_DEVICE_STATE_REASON_GSM_REGISTRATION_TIMEOUT: Network registration timed out
+ * @NM_DEVICE_STATE_REASON_GSM_REGISTRATION_FAILED: Failed to register with the requested network
+ * @NM_DEVICE_STATE_REASON_GSM_PIN_CHECK_FAILED: PIN check failed
+ * @NM_DEVICE_STATE_REASON_FIRMWARE_MISSING: Necessary firmware for the device may be missing
+ * @NM_DEVICE_STATE_REASON_REMOVED: The device was removed
+ * @NM_DEVICE_STATE_REASON_SLEEPING: NetworkManager went to sleep
+ * @NM_DEVICE_STATE_REASON_CONNECTION_REMOVED: The device's active connection disappeared
+ * @NM_DEVICE_STATE_REASON_USER_REQUESTED: Device disconnected by user or client
+ * @NM_DEVICE_STATE_REASON_CARRIER: Carrier/link changed
+ * @NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED: The device's existing connection was assumed
+ * @NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE: The supplicant is now available
+ * @NM_DEVICE_STATE_REASON_MODEM_NOT_FOUND: The modem could not be found
+ * @NM_DEVICE_STATE_REASON_BT_FAILED: The Bluetooth connection failed or timed out
+ * @NM_DEVICE_STATE_REASON_GSM_SIM_NOT_INSERTED: GSM Modem's SIM Card not inserted
+ * @NM_DEVICE_STATE_REASON_GSM_SIM_PIN_REQUIRED: GSM Modem's SIM Pin required
+ * @NM_DEVICE_STATE_REASON_GSM_SIM_PUK_REQUIRED: GSM Modem's SIM Puk required
+ * @NM_DEVICE_STATE_REASON_GSM_SIM_WRONG: GSM Modem's SIM wrong
+ * @NM_DEVICE_STATE_REASON_INFINIBAND_MODE: InfiniBand device does not support connected mode
+ * @NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED: A dependency of the connection failed
+ * @NM_DEVICE_STATE_REASON_BR2684_FAILED: Problem with the RFC 2684 Ethernet over ADSL bridge
+ * @NM_DEVICE_STATE_REASON_MODEM_MANAGER_UNAVAILABLE: ModemManager not running
+ * @NM_DEVICE_STATE_REASON_SSID_NOT_FOUND: The WiFi network could not be found
+ * @NM_DEVICE_STATE_REASON_SECONDARY_CONNECTION_FAILED: A secondary connection of the base connection failed
+ * @NM_DEVICE_STATE_REASON_DCB_FCOE_FAILED: DCB or FCoE setup failed
+ * @NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED: teamd control failed
+ * @NM_DEVICE_STATE_REASON_MODEM_FAILED: Modem failed or no longer available
+ * @NM_DEVICE_STATE_REASON_MODEM_AVAILABLE: Modem now ready and available
+ * @NM_DEVICE_STATE_REASON_SIM_PIN_INCORRECT: SIM PIN was incorrect
+ *
+ * Device state change reason codes
+ *
+ * (Corresponds to the NM_DEVICE_STATE_REASON type in nm-device.xml.)
+ */
+typedef enum {
+ NM_DEVICE_STATE_REASON_NONE = 0,
+ NM_DEVICE_STATE_REASON_UNKNOWN = 1,
+ NM_DEVICE_STATE_REASON_NOW_MANAGED = 2,
+ NM_DEVICE_STATE_REASON_NOW_UNMANAGED = 3,
+ NM_DEVICE_STATE_REASON_CONFIG_FAILED = 4,
+ NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE = 5,
+ NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED = 6,
+ NM_DEVICE_STATE_REASON_NO_SECRETS = 7,
+ NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT = 8,
+ NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED = 9,
+ NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED = 10,
+ NM_DEVICE_STATE_REASON_SUPPLICANT_TIMEOUT = 11,
+ NM_DEVICE_STATE_REASON_PPP_START_FAILED = 12,
+ NM_DEVICE_STATE_REASON_PPP_DISCONNECT = 13,
+ NM_DEVICE_STATE_REASON_PPP_FAILED = 14,
+ NM_DEVICE_STATE_REASON_DHCP_START_FAILED = 15,
+ NM_DEVICE_STATE_REASON_DHCP_ERROR = 16,
+ NM_DEVICE_STATE_REASON_DHCP_FAILED = 17,
+ NM_DEVICE_STATE_REASON_SHARED_START_FAILED = 18,
+ NM_DEVICE_STATE_REASON_SHARED_FAILED = 19,
+ NM_DEVICE_STATE_REASON_AUTOIP_START_FAILED = 20,
+ NM_DEVICE_STATE_REASON_AUTOIP_ERROR = 21,
+ NM_DEVICE_STATE_REASON_AUTOIP_FAILED = 22,
+ NM_DEVICE_STATE_REASON_MODEM_BUSY = 23,
+ NM_DEVICE_STATE_REASON_MODEM_NO_DIAL_TONE = 24,
+ NM_DEVICE_STATE_REASON_MODEM_NO_CARRIER = 25,
+ NM_DEVICE_STATE_REASON_MODEM_DIAL_TIMEOUT = 26,
+ NM_DEVICE_STATE_REASON_MODEM_DIAL_FAILED = 27,
+ NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED = 28,
+ NM_DEVICE_STATE_REASON_GSM_APN_FAILED = 29,
+ NM_DEVICE_STATE_REASON_GSM_REGISTRATION_NOT_SEARCHING = 30,
+ NM_DEVICE_STATE_REASON_GSM_REGISTRATION_DENIED = 31,
+ NM_DEVICE_STATE_REASON_GSM_REGISTRATION_TIMEOUT = 32,
+ NM_DEVICE_STATE_REASON_GSM_REGISTRATION_FAILED = 33,
+ NM_DEVICE_STATE_REASON_GSM_PIN_CHECK_FAILED = 34,
+ NM_DEVICE_STATE_REASON_FIRMWARE_MISSING = 35,
+ NM_DEVICE_STATE_REASON_REMOVED = 36,
+ NM_DEVICE_STATE_REASON_SLEEPING = 37,
+ NM_DEVICE_STATE_REASON_CONNECTION_REMOVED = 38,
+ NM_DEVICE_STATE_REASON_USER_REQUESTED = 39,
+ NM_DEVICE_STATE_REASON_CARRIER = 40,
+ NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED = 41,
+ NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE = 42,
+ NM_DEVICE_STATE_REASON_MODEM_NOT_FOUND = 43,
+ NM_DEVICE_STATE_REASON_BT_FAILED = 44,
+ NM_DEVICE_STATE_REASON_GSM_SIM_NOT_INSERTED = 45,
+ NM_DEVICE_STATE_REASON_GSM_SIM_PIN_REQUIRED = 46,
+ NM_DEVICE_STATE_REASON_GSM_SIM_PUK_REQUIRED = 47,
+ NM_DEVICE_STATE_REASON_GSM_SIM_WRONG = 48,
+ NM_DEVICE_STATE_REASON_INFINIBAND_MODE = 49,
+ NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED = 50,
+ NM_DEVICE_STATE_REASON_BR2684_FAILED = 51,
+ NM_DEVICE_STATE_REASON_MODEM_MANAGER_UNAVAILABLE = 52,
+ NM_DEVICE_STATE_REASON_SSID_NOT_FOUND = 53,
+ NM_DEVICE_STATE_REASON_SECONDARY_CONNECTION_FAILED = 54,
+ NM_DEVICE_STATE_REASON_DCB_FCOE_FAILED = 55,
+ NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED = 56,
+ NM_DEVICE_STATE_REASON_MODEM_FAILED = 57,
+ NM_DEVICE_STATE_REASON_MODEM_AVAILABLE = 58,
+ NM_DEVICE_STATE_REASON_SIM_PIN_INCORRECT = 59,
+
+ NM_DEVICE_STATE_REASON_LAST = 0xFFFF
+} NMDeviceStateReason;
+
+
+/**
+ * NMActiveConnectionState:
+ * @NM_ACTIVE_CONNECTION_STATE_UNKNOWN: the state of the connection is unknown
+ * @NM_ACTIVE_CONNECTION_STATE_ACTIVATING: a network connection is being prepared
+ * @NM_ACTIVE_CONNECTION_STATE_ACTIVATED: there is a connection to the network
+ * @NM_ACTIVE_CONNECTION_STATE_DEACTIVATING: the network connection is being
+ * torn down and cleaned up
+ * @NM_ACTIVE_CONNECTION_STATE_DEACTIVATED: the network connection is disconnected
+ * and will be removed
+ *
+ * #NMActiveConnectionState values indicate the state of a connection to a
+ * specific network while it is starting, connected, or disconnecting from that
+ * network.
+ *
+ * (Corresponds to the NM_ACTIVE_CONNECTION_STATE type in nm-active-connection.xml.)
+ **/
+typedef enum {
+ NM_ACTIVE_CONNECTION_STATE_UNKNOWN = 0,
+ NM_ACTIVE_CONNECTION_STATE_ACTIVATING,
+ NM_ACTIVE_CONNECTION_STATE_ACTIVATED,
+ NM_ACTIVE_CONNECTION_STATE_DEACTIVATING,
+ NM_ACTIVE_CONNECTION_STATE_DEACTIVATED
+} NMActiveConnectionState;
+
+#endif /* NETWORK_MANAGER_H */
diff --git a/libnm-core/NetworkManagerVPN.h b/libnm-core/NetworkManagerVPN.h
new file mode 100644
index 0000000000..f316572a08
--- /dev/null
+++ b/libnm-core/NetworkManagerVPN.h
@@ -0,0 +1,302 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2004 Red Hat, Inc.
+ */
+
+/* D-Bus-related definitions for NetworkManager VPN plugins.
+ *
+ * Note that although this header is installed as part of libnm-util, it is also
+ * used by some external code that does not link to libnm-util.
+ */
+
+#ifndef NETWORK_MANAGER_VPN_H
+#define NETWORK_MANAGER_VPN_H
+
+/*
+ * dbus services details
+ */
+#define NM_DBUS_PATH_VPN "/org/freedesktop/NetworkManager/VPN/Manager"
+#define NM_DBUS_INTERFACE_VPN "org.freedesktop.NetworkManager.VPN.Manager"
+
+#define NM_DBUS_PATH_VPN_CONNECTION "/org/freedesktop/NetworkManager/VPN/Connection"
+#define NM_DBUS_INTERFACE_VPN_CONNECTION "org.freedesktop.NetworkManager.VPN.Connection"
+
+#define NM_VPN_DBUS_PLUGIN_PATH "/org/freedesktop/NetworkManager/VPN/Plugin"
+#define NM_VPN_DBUS_PLUGIN_INTERFACE "org.freedesktop.NetworkManager.VPN.Plugin"
+
+/*
+ * VPN Errors
+ */
+#define NM_DBUS_NO_ACTIVE_VPN_CONNECTION "org.freedesktop.NetworkManager.VPNConnections.NoActiveVPNConnection"
+#define NM_DBUS_NO_VPN_CONNECTIONS "org.freedesktop.NetworkManager.VPNConnections.NoVPNConnections"
+#define NM_DBUS_INVALID_VPN_CONNECTION "org.freedesktop.NetworkManager.VPNConnections.InvalidVPNConnection"
+
+#define NM_DBUS_VPN_ERROR_PREFIX "org.freedesktop.NetworkManager.VPN.Error"
+#define NM_DBUS_VPN_STARTING_IN_PROGRESS "StartingInProgress"
+#define NM_DBUS_VPN_ALREADY_STARTED "AlreadyStarted"
+#define NM_DBUS_VPN_STOPPING_IN_PROGRESS "StoppingInProgress"
+#define NM_DBUS_VPN_ALREADY_STOPPED "AlreadyStopped"
+#define NM_DBUS_VPN_WRONG_STATE "WrongState"
+#define NM_DBUS_VPN_BAD_ARGUMENTS "BadArguments"
+#define NM_DBUS_VPN_INTERACTIVE_NOT_SUPPORTED "InteractiveNotSupported"
+
+
+/*
+ * VPN daemon signals
+ */
+#define NM_DBUS_VPN_SIGNAL_LOGIN_BANNER "LoginBanner"
+#define NM_DBUS_VPN_SIGNAL_LOGIN_FAILED "LoginFailed"
+#define NM_DBUS_VPN_SIGNAL_LAUNCH_FAILED "LaunchFailed"
+#define NM_DBUS_VPN_SIGNAL_CONNECT_FAILED "ConnectFailed"
+#define NM_DBUS_VPN_SIGNAL_VPN_CONFIG_BAD "VPNConfigBad"
+#define NM_DBUS_VPN_SIGNAL_IP_CONFIG_BAD "IPConfigBad"
+#define NM_DBUS_VPN_SIGNAL_STATE_CHANGE "StateChange"
+#define NM_DBUS_VPN_SIGNAL_IP4_CONFIG "IP4Config"
+
+/**
+ * NMVPNServiceState:
+ * @NM_VPN_SERVICE_STATE_UNKNOWN: The state of the VPN plugin is unknown.
+ * @NM_VPN_SERVICE_STATE_INIT: The VPN plugin is initialized.
+ * @NM_VPN_SERVICE_STATE_SHUTDOWN: Not used.
+ * @NM_VPN_SERVICE_STATE_STARTING: The plugin is attempting to connect to a VPN server.
+ * @NM_VPN_SERVICE_STATE_STARTED: The plugin has connected to a VPN server.
+ * @NM_VPN_SERVICE_STATE_STOPPING: The plugin is disconnecting from the VPN server.
+ * @NM_VPN_SERVICE_STATE_STOPPED: The plugin has disconnected from the VPN server.
+ *
+ * VPN daemon states
+ *
+ * (Corresponds to the NM_VPN_SERVICE_STATE type in nm-vpn-connection.xml.)
+ */
+typedef enum NMVPNServiceState {
+ NM_VPN_SERVICE_STATE_UNKNOWN = 0,
+ NM_VPN_SERVICE_STATE_INIT,
+ NM_VPN_SERVICE_STATE_SHUTDOWN,
+ NM_VPN_SERVICE_STATE_STARTING,
+ NM_VPN_SERVICE_STATE_STARTED,
+ NM_VPN_SERVICE_STATE_STOPPING,
+ NM_VPN_SERVICE_STATE_STOPPED
+} NMVPNServiceState;
+
+
+/**
+ * NMVPNConnectionState:
+ * @NM_VPN_CONNECTION_STATE_UNKNOWN: The state of the VPN connection is
+ * unknown.
+ * @NM_VPN_CONNECTION_STATE_PREPARE: The VPN connection is preparing to
+ * connect.
+ * @NM_VPN_CONNECTION_STATE_NEED_AUTH: The VPN connection needs authorization
+ * credentials.
+ * @NM_VPN_CONNECTION_STATE_CONNECT: The VPN connection is being established.
+ * @NM_VPN_CONNECTION_STATE_IP_CONFIG_GET: The VPN connection is getting an IP
+ * address.
+ * @NM_VPN_CONNECTION_STATE_ACTIVATED: The VPN connection is active.
+ * @NM_VPN_CONNECTION_STATE_FAILED: The VPN connection failed.
+ * @NM_VPN_CONNECTION_STATE_DISCONNECTED: The VPN connection is disconnected.
+ *
+ * VPN connection states
+ *
+ * (Corresponds to the NM_VPN_CONNECTION_STATE type in nm-vpn-connection.xml.)
+ */
+typedef enum NMVPNConnectionState {
+ NM_VPN_CONNECTION_STATE_UNKNOWN = 0,
+ NM_VPN_CONNECTION_STATE_PREPARE,
+ NM_VPN_CONNECTION_STATE_NEED_AUTH,
+ NM_VPN_CONNECTION_STATE_CONNECT,
+ NM_VPN_CONNECTION_STATE_IP_CONFIG_GET,
+ NM_VPN_CONNECTION_STATE_ACTIVATED,
+ NM_VPN_CONNECTION_STATE_FAILED,
+ NM_VPN_CONNECTION_STATE_DISCONNECTED
+} NMVPNConnectionState;
+
+/**
+ * NMVPNConnectionStateReason:
+ * @NM_VPN_CONNECTION_STATE_REASON_UNKNOWN: The reason for the VPN connection
+ * state change is unknown.
+ * @NM_VPN_CONNECTION_STATE_REASON_NONE: No reason was given for the VPN
+ * connection state change.
+ * @NM_VPN_CONNECTION_STATE_REASON_USER_DISCONNECTED: The VPN connection changed
+ * state because the user disconnected it.
+ * @NM_VPN_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED: The VPN connection
+ * changed state because the device it was using was disconnected.
+ * @NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED: The service providing the
+ * VPN connection was stopped.
+ * @NM_VPN_CONNECTION_STATE_REASON_IP_CONFIG_INVALID: The IP config of the VPN
+ * connection was invalid.
+ * @NM_VPN_CONNECTION_STATE_REASON_CONNECT_TIMEOUT: The connection attempt to
+ * the VPN service timed out.
+ * @NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_TIMEOUT: A timeout occurred
+ * while starting the service providing the VPN connection.
+ * @NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED: Starting the service
+ * starting the service providing the VPN connection failed.
+ * @NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS: Necessary secrets for the VPN
+ * connection were not provided.
+ * @NM_VPN_CONNECTION_STATE_REASON_LOGIN_FAILED: Authentication to the VPN
+ * server failed.
+ * @NM_VPN_CONNECTION_STATE_REASON_CONNECTION_REMOVED: The connection was
+ * deleted from settings.
+ *
+ * VPN connection state reasons
+ *
+ * (Corresponds to the NM_VPN_CONNECTION_STATE_REASON type in nm-vpn-connection.xml.)
+ */
+typedef enum NMVPNConnectionStateReason {
+ NM_VPN_CONNECTION_STATE_REASON_UNKNOWN = 0,
+ NM_VPN_CONNECTION_STATE_REASON_NONE,
+ NM_VPN_CONNECTION_STATE_REASON_USER_DISCONNECTED,
+ NM_VPN_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED,
+ NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED,
+ NM_VPN_CONNECTION_STATE_REASON_IP_CONFIG_INVALID,
+ NM_VPN_CONNECTION_STATE_REASON_CONNECT_TIMEOUT,
+ NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_TIMEOUT,
+ NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED,
+ NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS,
+ NM_VPN_CONNECTION_STATE_REASON_LOGIN_FAILED,
+ NM_VPN_CONNECTION_STATE_REASON_CONNECTION_REMOVED
+} NMVPNConnectionStateReason;
+
+/**
+ * NMVPNPluginFailure:
+ * @NM_VPN_PLUGIN_FAILURE_LOGIN_FAILED: Login failed.
+ * @NM_VPN_PLUGIN_FAILURE_CONNECT_FAILED: Connect failed.
+ * @NM_VPN_PLUGIN_FAILURE_BAD_IP_CONFIG: Invalid IP configuration returned from
+ * the VPN plugin.
+ *
+ * VPN plugin failure reasons
+ *
+ * (Corresponds to the NM_VPN_PLUGIN_FAILURE type in nm-vpn-plugin.xml.)
+ */
+typedef enum {
+ NM_VPN_PLUGIN_FAILURE_LOGIN_FAILED,
+ NM_VPN_PLUGIN_FAILURE_CONNECT_FAILED,
+ NM_VPN_PLUGIN_FAILURE_BAD_IP_CONFIG
+} NMVPNPluginFailure;
+
+
+/*** Generic config ***/
+
+/* string: VPN interface name (tun0, tap0, etc) */
+#define NM_VPN_PLUGIN_CONFIG_TUNDEV "tundev"
+
+/* string: Login message */
+#define NM_VPN_PLUGIN_CONFIG_BANNER "banner"
+
+/* uint32 / array of uint8: IP address of the public external VPN gateway (network byte order) */
+#define NM_VPN_PLUGIN_CONFIG_EXT_GATEWAY "gateway"
+
+/* uint32: Maximum Transfer Unit that the VPN interface should use */
+#define NM_VPN_PLUGIN_CONFIG_MTU "mtu"
+
+/* boolean: Has IP4 configuration? */
+#define NM_VPN_PLUGIN_CONFIG_HAS_IP4 "has-ip4"
+
+/* boolean: Has IP6 configuration? */
+#define NM_VPN_PLUGIN_CONFIG_HAS_IP6 "has-ip6"
+
+
+/*** Ip4Config ***/
+
+/* uint32: IP address of the internal gateway of the subnet the VPN interface is
+ * on, if the VPN uses subnet configuration (network byte order)
+ */
+#define NM_VPN_PLUGIN_IP4_CONFIG_INT_GATEWAY "internal-gateway"
+
+/* uint32: internal IP address of the local VPN interface (network byte order) */
+#define NM_VPN_PLUGIN_IP4_CONFIG_ADDRESS "address"
+
+/* uint32: IP address of the other side of Point-to-Point connection if the VPN
+ * uses Point-to-Point configuration. (network byte order)
+ */
+#define NM_VPN_PLUGIN_IP4_CONFIG_PTP "ptp"
+
+/* uint32: IP prefix of the VPN interface; 1 - 32 inclusive */
+#define NM_VPN_PLUGIN_IP4_CONFIG_PREFIX "prefix"
+
+/* array of uint32: IP addresses of DNS servers for the VPN (network byte order) */
+#define NM_VPN_PLUGIN_IP4_CONFIG_DNS "dns"
+
+/* array of uint32: IP addresses of NBNS/WINS servers for the VPN (network byte order) */
+#define NM_VPN_PLUGIN_IP4_CONFIG_NBNS "nbns"
+
+/* uint32: Message Segment Size that the VPN interface should use */
+#define NM_VPN_PLUGIN_IP4_CONFIG_MSS "mss"
+
+/* string: DNS domain name */
+#define NM_VPN_PLUGIN_IP4_CONFIG_DOMAIN "domain"
+
+/* array of strings: DNS domain names */
+#define NM_VPN_PLUGIN_IP4_CONFIG_DOMAINS "domains"
+
+/* [ip4 routes]: custom routes the client should apply, in the format used
+ * by nm_utils_ip4_routes_to/from_gvalue
+ */
+#define NM_VPN_PLUGIN_IP4_CONFIG_ROUTES "routes"
+
+/* boolean: prevent this VPN connection from ever getting the default route */
+#define NM_VPN_PLUGIN_IP4_CONFIG_NEVER_DEFAULT "never-default"
+
+/* Deprecated */
+#define NM_VPN_PLUGIN_IP4_CONFIG_GATEWAY NM_VPN_PLUGIN_IP4_CONFIG_EXT_GATEWAY
+
+/* Legacy IP4 items; these are included in the IP4 config by older plugins,
+ * but in the generic config by newer plugins.
+ */
+
+#define NM_VPN_PLUGIN_IP4_CONFIG_BANNER NM_VPN_PLUGIN_CONFIG_BANNER
+#define NM_VPN_PLUGIN_IP4_CONFIG_EXT_GATEWAY NM_VPN_PLUGIN_CONFIG_EXT_GATEWAY
+#define NM_VPN_PLUGIN_IP4_CONFIG_MTU NM_VPN_PLUGIN_CONFIG_MTU
+#define NM_VPN_PLUGIN_IP4_CONFIG_TUNDEV NM_VPN_PLUGIN_CONFIG_TUNDEV
+
+
+/*** Ip6Config ***/
+
+/* array of uint8: IP address of the internal gateway of the subnet the VPN interface is
+ * on, if the VPN uses subnet configuration (network byte order)
+ */
+#define NM_VPN_PLUGIN_IP6_CONFIG_INT_GATEWAY "internal-gateway"
+
+/* array of uint8: internal IP address of the local VPN interface (network byte order) */
+#define NM_VPN_PLUGIN_IP6_CONFIG_ADDRESS "address"
+
+/* array of uint8: IP address of the other side of Point-to-Point connection if the VPN
+ * uses Point-to-Point configuration. (network byte order)
+ */
+#define NM_VPN_PLUGIN_IP6_CONFIG_PTP "ptp"
+
+/* uint32: prefix length of the VPN interface; 1 - 128 inclusive */
+#define NM_VPN_PLUGIN_IP6_CONFIG_PREFIX "prefix"
+
+/* array of array of uint8: IP addresses of DNS servers for the VPN (network byte order) */
+#define NM_VPN_PLUGIN_IP6_CONFIG_DNS "dns"
+
+/* uint32: Message Segment Size that the VPN interface should use */
+#define NM_VPN_PLUGIN_IP6_CONFIG_MSS "mss"
+
+/* string: DNS domain name */
+#define NM_VPN_PLUGIN_IP6_CONFIG_DOMAIN "domain"
+
+/* array of strings: DNS domain names */
+#define NM_VPN_PLUGIN_IP6_CONFIG_DOMAINS "domains"
+
+/* [ip6 routes]: custom routes the client should apply, in the format used
+ * by nm_utils_ip6_routes_to/from_gvalue
+ */
+#define NM_VPN_PLUGIN_IP6_CONFIG_ROUTES "routes"
+
+/* boolean: prevent this VPN connection from ever getting the default route */
+#define NM_VPN_PLUGIN_IP6_CONFIG_NEVER_DEFAULT "never-default"
+
+#endif /* NETWORK_MANAGER_VPN_H */
diff --git a/libnm-core/crypto.c b/libnm-core/crypto.c
new file mode 100644
index 0000000000..d5f7608d6e
--- /dev/null
+++ b/libnm-core/crypto.c
@@ -0,0 +1,749 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * Dan Williams <dcbw@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2011 Red Hat, Inc.
+ */
+
+#include "config.h"
+
+#include <glib.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <glib/gi18n.h>
+
+#include "crypto.h"
+
+GQuark
+_nm_crypto_error_quark (void)
+{
+ static GQuark quark;
+
+ if (G_UNLIKELY (!quark))
+ quark = g_quark_from_static_string ("nm-crypto-error-quark");
+ return quark;
+}
+
+
+#define PEM_RSA_KEY_BEGIN "-----BEGIN RSA PRIVATE KEY-----"
+#define PEM_RSA_KEY_END "-----END RSA PRIVATE KEY-----"
+
+#define PEM_DSA_KEY_BEGIN "-----BEGIN DSA PRIVATE KEY-----"
+#define PEM_DSA_KEY_END "-----END DSA PRIVATE KEY-----"
+
+#define PEM_CERT_BEGIN "-----BEGIN CERTIFICATE-----"
+#define PEM_CERT_END "-----END CERTIFICATE-----"
+
+#define PEM_PKCS8_ENC_KEY_BEGIN "-----BEGIN ENCRYPTED PRIVATE KEY-----"
+#define PEM_PKCS8_ENC_KEY_END "-----END ENCRYPTED PRIVATE KEY-----"
+
+#define PEM_PKCS8_DEC_KEY_BEGIN "-----BEGIN PRIVATE KEY-----"
+#define PEM_PKCS8_DEC_KEY_END "-----END PRIVATE KEY-----"
+
+static gboolean
+find_tag (const char *tag,
+ const GByteArray *array,
+ gsize start_at,
+ gsize *out_pos)
+{
+ gsize i, taglen;
+ gsize len = array->len - start_at;
+
+ g_return_val_if_fail (out_pos != NULL, FALSE);
+
+ taglen = strlen (tag);
+ if (len >= taglen) {
+ for (i = 0; i < len - taglen + 1; i++) {
+ if (memcmp (array->data + start_at + i, tag, taglen) == 0) {
+ *out_pos = start_at + i;
+ return TRUE;
+ }
+ }
+ }
+ return FALSE;
+}
+
+#define DEK_INFO_TAG "DEK-Info: "
+#define PROC_TYPE_TAG "Proc-Type: "
+
+static GByteArray *
+parse_old_openssl_key_file (const GByteArray *contents,
+ int key_type,
+ char **out_cipher,
+ char **out_iv,
+ GError **error)
+{
+ GByteArray *bindata = NULL;
+ char **lines = NULL;
+ char **ln = NULL;
+ gsize start = 0, end = 0;
+ GString *str = NULL;
+ int enc_tags = 0;
+ char *iv = NULL;
+ char *cipher = NULL;
+ unsigned char *tmp = NULL;
+ gsize tmp_len = 0;
+ const char *start_tag;
+ const char *end_tag;
+ guint8 save_end = 0;
+
+ switch (key_type) {
+ case NM_CRYPTO_KEY_TYPE_RSA:
+ start_tag = PEM_RSA_KEY_BEGIN;
+ end_tag = PEM_RSA_KEY_END;
+ break;
+ case NM_CRYPTO_KEY_TYPE_DSA:
+ start_tag = PEM_DSA_KEY_BEGIN;
+ end_tag = PEM_DSA_KEY_END;
+ break;
+ default:
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_UNKNOWN_KEY_TYPE,
+ "Unknown key type %d",
+ key_type);
+ g_assert_not_reached ();
+ return NULL;
+ }
+
+ if (!find_tag (start_tag, contents, 0, &start))
+ goto parse_error;
+
+ start += strlen (start_tag);
+ if (!find_tag (end_tag, contents, start, &end)) {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
+ _("PEM key file had no end tag '%s'."),
+ end_tag);
+ goto parse_error;
+ }
+
+ save_end = contents->data[end];
+ contents->data[end] = '\0';
+ lines = g_strsplit ((const char *) (contents->data + start), "\n", 0);
+ contents->data[end] = save_end;
+
+ if (!lines || g_strv_length (lines) <= 1) {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
+ _("Doesn't look like a PEM private key file."));
+ goto parse_error;
+ }
+
+ str = g_string_new_len (NULL, end - start);
+ for (ln = lines; *ln; ln++) {
+ char *p = *ln;
+
+ /* Chug leading spaces */
+ p = g_strstrip (p);
+ if (!*p)
+ continue;
+
+ if (!strncmp (p, PROC_TYPE_TAG, strlen (PROC_TYPE_TAG))) {
+ if (enc_tags++ != 0) {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
+ _("Malformed PEM file: Proc-Type was not first tag."));
+ goto parse_error;
+ }
+
+ p += strlen (PROC_TYPE_TAG);
+ if (strcmp (p, "4,ENCRYPTED")) {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
+ _("Malformed PEM file: unknown Proc-Type tag '%s'."),
+ p);
+ goto parse_error;
+ }
+ } else if (!strncmp (p, DEK_INFO_TAG, strlen (DEK_INFO_TAG))) {
+ char *comma;
+
+ if (enc_tags++ != 1) {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
+ _("Malformed PEM file: DEK-Info was not the second tag."));
+ goto parse_error;
+ }
+
+ p += strlen (DEK_INFO_TAG);
+
+ /* Grab the IV first */
+ comma = strchr (p, ',');
+ if (!comma || (*(comma + 1) == '\0')) {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
+ _("Malformed PEM file: no IV found in DEK-Info tag."));
+ goto parse_error;
+ }
+ *comma++ = '\0';
+ if (!g_ascii_isxdigit (*comma)) {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
+ _("Malformed PEM file: invalid format of IV in DEK-Info tag."));
+ goto parse_error;
+ }
+ iv = g_strdup (comma);
+
+ /* Get the private key cipher */
+ if (!strcasecmp (p, "DES-EDE3-CBC")) {
+ cipher = g_strdup (p);
+ } else if (!strcasecmp (p, "DES-CBC")) {
+ cipher = g_strdup (p);
+ } else if (!strcasecmp (p, "AES-128-CBC")) {
+ cipher = g_strdup (p);
+ } else {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_UNKNOWN_KEY_TYPE,
+ _("Malformed PEM file: unknown private key cipher '%s'."),
+ p);
+ goto parse_error;
+ }
+ } else {
+ if ((enc_tags != 0) && (enc_tags != 2)) {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
+ "Malformed PEM file: both Proc-Type and DEK-Info tags are required.");
+ goto parse_error;
+ }
+ g_string_append (str, p);
+ }
+ }
+
+ tmp = g_base64_decode (str->str, &tmp_len);
+ if (tmp == NULL || !tmp_len) {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_DECODE_FAILED,
+ _("Could not decode private key."));
+ goto parse_error;
+ }
+ g_string_free (str, TRUE);
+
+ if (lines)
+ g_strfreev (lines);
+
+ bindata = g_byte_array_sized_new (tmp_len);
+ g_byte_array_append (bindata, tmp, tmp_len);
+ g_free (tmp);
+
+ *out_iv = iv;
+ *out_cipher = cipher;
+ return bindata;
+
+parse_error:
+ g_free (tmp);
+ g_free (cipher);
+ g_free (iv);
+ if (str)
+ g_string_free (str, TRUE);
+ if (lines)
+ g_strfreev (lines);
+ return NULL;
+}
+
+static GByteArray *
+parse_pkcs8_key_file (const GByteArray *contents,
+ gboolean *out_encrypted,
+ GError **error)
+{
+ GByteArray *key = NULL;
+ gsize start = 0, end = 0;
+ unsigned char *der = NULL;
+ guint8 save_end;
+ gsize length = 0;
+ const char *start_tag = NULL, *end_tag = NULL;
+ gboolean encrypted = FALSE;
+
+ /* Try encrypted first, decrypted next */
+ if (find_tag (PEM_PKCS8_ENC_KEY_BEGIN, contents, 0, &start)) {
+ start_tag = PEM_PKCS8_ENC_KEY_BEGIN;
+ end_tag = PEM_PKCS8_ENC_KEY_END;
+ encrypted = TRUE;
+ } else if (find_tag (PEM_PKCS8_DEC_KEY_BEGIN, contents, 0, &start)) {
+ start_tag = PEM_PKCS8_DEC_KEY_BEGIN;
+ end_tag = PEM_PKCS8_DEC_KEY_END;
+ encrypted = FALSE;
+ } else {
+ g_set_error_literal (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
+ _("Failed to find expected PKCS#8 start tag."));
+ return NULL;
+ }
+
+ start += strlen (start_tag);
+ if (!find_tag (end_tag, contents, start, &end)) {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
+ _("Failed to find expected PKCS#8 end tag '%s'."),
+ end_tag);
+ return NULL;
+ }
+
+ /* g_base64_decode() wants a NULL-terminated string */
+ save_end = contents->data[end];
+ contents->data[end] = '\0';
+ der = g_base64_decode ((const char *) (contents->data + start), &length);
+ contents->data[end] = save_end;
+
+ if (der && length) {
+ key = g_byte_array_sized_new (length);
+ g_byte_array_append (key, der, length);
+ g_assert (key->len == length);
+ *out_encrypted = encrypted;
+ } else {
+ g_set_error_literal (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_DECODE_FAILED,
+ _("Failed to decode PKCS#8 private key."));
+ }
+
+ g_free (der);
+ return key;
+}
+
+static GByteArray *
+file_to_g_byte_array (const char *filename, GError **error)
+{
+ char *contents;
+ GByteArray *array = NULL;
+ gsize length = 0;
+
+ if (g_file_get_contents (filename, &contents, &length, error)) {
+ array = g_byte_array_sized_new (length);
+ g_byte_array_append (array, (guint8 *) contents, length);
+ g_assert (array->len == length);
+ g_free (contents);
+ }
+ return array;
+}
+
+/*
+ * Convert a hex string into bytes.
+ */
+static char *
+convert_iv (const char *src,
+ gsize *out_len,
+ GError **error)
+{
+ int num;
+ int i;
+ char conv[3];
+ char *c;
+
+ g_return_val_if_fail (src != NULL, NULL);
+
+ num = strlen (src);
+ if (num % 2) {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_RAW_IV_INVALID,
+ _("IV must be an even number of bytes in length."));
+ return NULL;
+ }
+
+ num /= 2;
+ c = g_malloc0 (num + 1);
+
+ conv[2] = '\0';
+ for (i = 0; i < num; i++) {
+ conv[0] = src[(i * 2)];
+ conv[1] = src[(i * 2) + 1];
+ if (!g_ascii_isxdigit (conv[0]) || !g_ascii_isxdigit (conv[1])) {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_RAW_IV_INVALID,
+ _("IV contains non-hexadecimal digits."));
+ goto error;
+ }
+
+ c[i] = strtol(conv, NULL, 16);
+ }
+ *out_len = num;
+ return c;
+
+error:
+ g_free (c);
+ return NULL;
+}
+
+static char *
+make_des_aes_key (const char *cipher,
+ const char *salt,
+ const gsize salt_len,
+ const char *password,
+ gsize *out_len,
+ GError **error)
+{
+ char *key;
+ guint32 digest_len;
+
+ g_return_val_if_fail (cipher != NULL, NULL);
+ g_return_val_if_fail (salt != NULL, NULL);
+ g_return_val_if_fail (salt_len >= 8, NULL);
+ g_return_val_if_fail (password != NULL, NULL);
+ g_return_val_if_fail (out_len != NULL, NULL);
+
+ if (!strcmp (cipher, "DES-EDE3-CBC"))
+ digest_len = 24;
+ else if (!strcmp (cipher, "DES-CBC"))
+ digest_len = 8;
+ else if (!strcmp (cipher, "AES-128-CBC"))
+ digest_len = 16;
+ else {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_UNKNOWN_CIPHER,
+ _("Private key cipher '%s' was unknown."),
+ cipher);
+ return NULL;
+ }
+
+ if (password[0] == '\0')
+ return NULL;
+
+ key = g_malloc0 (digest_len + 1);
+
+ if (!crypto_md5_hash (salt,
+ salt_len,
+ password,
+ strlen (password),
+ key,
+ digest_len,
+ error))
+ goto error;
+
+ *out_len = digest_len;
+ return key;
+
+error:
+ if (key) {
+ /* Don't leak stale key material */
+ memset (key, 0, digest_len);
+ g_free (key);
+ }
+ return NULL;
+}
+
+static GByteArray *
+decrypt_key (const char *cipher,
+ int key_type,
+ GByteArray *data,
+ const char *iv,
+ const char *password,
+ GError **error)
+{
+ char *bin_iv = NULL;
+ gsize bin_iv_len = 0;
+ char *key = NULL;
+ gsize key_len = 0;
+ char *output = NULL;
+ gsize decrypted_len = 0;
+ GByteArray *decrypted = NULL;
+
+ g_return_val_if_fail (password != NULL, NULL);
+
+ bin_iv = convert_iv (iv, &bin_iv_len, error);
+ if (!bin_iv)
+ return NULL;
+
+ /* Convert the password and IV into a DES or AES key */
+ key = make_des_aes_key (cipher, bin_iv, bin_iv_len, password, &key_len, error);
+ if (!key || !key_len)
+ goto out;
+
+ output = crypto_decrypt (cipher, key_type,
+ data,
+ bin_iv, bin_iv_len,
+ key, key_len,
+ &decrypted_len,
+ error);
+ if (output && decrypted_len) {
+ decrypted = g_byte_array_sized_new (decrypted_len);
+ g_byte_array_append (decrypted, (guint8 *) output, decrypted_len);
+ }
+
+out:
+ /* Don't leak stale key material */
+ if (key)
+ memset (key, 0, key_len);
+ g_free (output);
+ g_free (key);
+ g_free (bin_iv);
+
+ return decrypted;
+}
+
+GByteArray *
+crypto_decrypt_private_key_data (const GByteArray *contents,
+ const char *password,
+ NMCryptoKeyType *out_key_type,
+ GError **error)
+{
+ GByteArray *decrypted = NULL;
+ NMCryptoKeyType key_type = NM_CRYPTO_KEY_TYPE_RSA;
+ GByteArray *data;
+ char *iv = NULL;
+ char *cipher = NULL;
+
+ g_return_val_if_fail (contents != NULL, NULL);
+ if (out_key_type)
+ g_return_val_if_fail (*out_key_type == NM_CRYPTO_KEY_TYPE_UNKNOWN, NULL);
+
+ /* OpenSSL non-standard legacy PEM files */
+
+ /* Try RSA keys first */
+ data = parse_old_openssl_key_file (contents, key_type, &cipher, &iv, error);
+ if (!data) {
+ g_clear_error (error);
+
+ /* DSA next */
+ key_type = NM_CRYPTO_KEY_TYPE_DSA;
+ data = parse_old_openssl_key_file (contents, key_type, &cipher, &iv, error);
+ if (!data) {
+ g_clear_error (error);
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
+ _("Unable to determine private key type."));
+ }
+ }
+
+ if (data) {
+ /* return the key type even if decryption failed */
+ if (out_key_type)
+ *out_key_type = key_type;
+
+ if (password) {
+ decrypted = decrypt_key (cipher,
+ key_type,
+ data,
+ iv,
+ password,
+ error);
+ }
+ g_byte_array_free (data, TRUE);
+ }
+
+ g_free (cipher);
+ g_free (iv);
+
+ return decrypted;
+}
+
+GByteArray *
+crypto_decrypt_private_key (const char *file,
+ const char *password,
+ NMCryptoKeyType *out_key_type,
+ GError **error)
+{
+ GByteArray *contents;
+ GByteArray *key = NULL;
+
+ contents = file_to_g_byte_array (file, error);
+ if (contents) {
+ key = crypto_decrypt_private_key_data (contents, password, out_key_type, error);
+ g_byte_array_free (contents, TRUE);
+ }
+ return key;
+}
+
+static GByteArray *
+extract_pem_cert_data (GByteArray *contents, GError **error)
+{
+ GByteArray *cert = NULL;
+ gsize start = 0, end = 0;
+ unsigned char *der = NULL;
+ guint8 save_end;
+ gsize length = 0;
+
+ if (!find_tag (PEM_CERT_BEGIN, contents, 0, &start)) {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
+ _("PEM certificate had no start tag '%s'."),
+ PEM_CERT_BEGIN);
+ goto done;
+ }
+
+ start += strlen (PEM_CERT_BEGIN);
+ if (!find_tag (PEM_CERT_END, contents, start, &end)) {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
+ _("PEM certificate had no end tag '%s'."),
+ PEM_CERT_END);
+ goto done;
+ }
+
+ /* g_base64_decode() wants a NULL-terminated string */
+ save_end = contents->data[end];
+ contents->data[end] = '\0';
+ der = g_base64_decode ((const char *) (contents->data + start), &length);
+ contents->data[end] = save_end;
+
+ if (der && length) {
+ cert = g_byte_array_sized_new (length);
+ g_byte_array_append (cert, der, length);
+ g_assert (cert->len == length);
+ } else {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_DECODE_FAILED,
+ _("Failed to decode certificate."));
+ }
+
+done:
+ g_free (der);
+ return cert;
+}
+
+GByteArray *
+crypto_load_and_verify_certificate (const char *file,
+ NMCryptoFileFormat *out_file_format,
+ GError **error)
+{
+ GByteArray *array, *contents;
+
+ g_return_val_if_fail (file != NULL, NULL);
+ g_return_val_if_fail (out_file_format != NULL, NULL);
+ g_return_val_if_fail (*out_file_format == NM_CRYPTO_FILE_FORMAT_UNKNOWN, NULL);
+
+ contents = file_to_g_byte_array (file, error);
+ if (!contents)
+ return NULL;
+
+ /* Check for PKCS#12 */
+ if (crypto_is_pkcs12_data (contents)) {
+ *out_file_format = NM_CRYPTO_FILE_FORMAT_PKCS12;
+ return contents;
+ }
+
+ /* Check for plain DER format */
+ if (contents->len > 2 && contents->data[0] == 0x30 && contents->data[1] == 0x82) {
+ *out_file_format = crypto_verify_cert (contents->data, contents->len, error);
+ } else {
+ array = extract_pem_cert_data (contents, error);
+ if (!array) {
+ g_byte_array_free (contents, TRUE);
+ return NULL;
+ }
+
+ *out_file_format = crypto_verify_cert (array->data, array->len, error);
+ g_byte_array_free (array, TRUE);
+ }
+
+ if (*out_file_format != NM_CRYPTO_FILE_FORMAT_X509) {
+ g_byte_array_free (contents, TRUE);
+ contents = NULL;
+ }
+
+ return contents;
+}
+
+gboolean
+crypto_is_pkcs12_data (const GByteArray *data)
+{
+ GError *error = NULL;
+ gboolean success;
+
+ g_return_val_if_fail (data != NULL, FALSE);
+
+ success = crypto_verify_pkcs12 (data, NULL, &error);
+ if (success == FALSE) {
+ /* If the error was just a decryption error, then it's pkcs#12 */
+ if (error) {
+ if (g_error_matches (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED))
+ success = TRUE;
+ g_error_free (error);
+ }
+ }
+ return success;
+}
+
+gboolean
+crypto_is_pkcs12_file (const char *file, GError **error)
+{
+ GByteArray *contents;
+ gboolean success = FALSE;
+
+ g_return_val_if_fail (file != NULL, FALSE);
+
+ contents = file_to_g_byte_array (file, error);
+ if (contents) {
+ success = crypto_is_pkcs12_data (contents);
+ g_byte_array_free (contents, TRUE);
+ }
+ return success;
+}
+
+/* Verifies that a private key can be read, and if a password is given, that
+ * the private key can be decrypted with that password.
+ */
+NMCryptoFileFormat
+crypto_verify_private_key_data (const GByteArray *contents,
+ const char *password,
+ GError **error)
+{
+ GByteArray *tmp;
+ NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
+ NMCryptoKeyType ktype = NM_CRYPTO_KEY_TYPE_UNKNOWN;
+ gboolean is_encrypted = FALSE;
+
+ g_return_val_if_fail (contents != NULL, FALSE);
+
+ /* Check for PKCS#12 first */
+ if (crypto_is_pkcs12_data (contents)) {
+ if (!password || crypto_verify_pkcs12 (contents, password, error))
+ format = NM_CRYPTO_FILE_FORMAT_PKCS12;
+ } else {
+ /* Maybe it's PKCS#8 */
+ tmp = parse_pkcs8_key_file (contents, &is_encrypted, error);
+ if (tmp) {
+ if (crypto_verify_pkcs8 (tmp, is_encrypted, password, error))
+ format = NM_CRYPTO_FILE_FORMAT_RAW_KEY;
+ } else {
+ g_clear_error (error);
+
+ /* Or it's old-style OpenSSL */
+ tmp = crypto_decrypt_private_key_data (contents, password, &ktype, error);
+ if (tmp)
+ format = NM_CRYPTO_FILE_FORMAT_RAW_KEY;
+ else if (!password && (ktype != NM_CRYPTO_KEY_TYPE_UNKNOWN))
+ format = NM_CRYPTO_FILE_FORMAT_RAW_KEY;
+ }
+
+ if (tmp) {
+ /* Don't leave decrypted key data around */
+ memset (tmp->data, 0, tmp->len);
+ g_byte_array_free (tmp, TRUE);
+ }
+ }
+
+ return format;
+}
+
+NMCryptoFileFormat
+crypto_verify_private_key (const char *filename,
+ const char *password,
+ GError **error)
+{
+ GByteArray *contents;
+ NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
+
+ g_return_val_if_fail (filename != NULL, FALSE);
+
+ contents = file_to_g_byte_array (filename, error);
+ if (contents) {
+ format = crypto_verify_private_key_data (contents, password, error);
+ g_byte_array_free (contents, TRUE);
+ }
+ return format;
+}
diff --git a/libnm-core/crypto.h b/libnm-core/crypto.h
new file mode 100644
index 0000000000..edb79007f1
--- /dev/null
+++ b/libnm-core/crypto.h
@@ -0,0 +1,145 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * Dan Williams <dcbw@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2014 Red Hat, Inc.
+ */
+
+#ifndef __CRYPTO_H__
+#define __CRYPTO_H__
+
+#include <glib.h>
+
+#define MD5_HASH_LEN 20
+#define CIPHER_DES_EDE3_CBC "DES-EDE3-CBC"
+#define CIPHER_DES_CBC "DES-CBC"
+#define CIPHER_AES_CBC "AES-128-CBC"
+
+enum {
+ NM_CRYPTO_ERR_NONE = 0,
+ NM_CRYPTO_ERR_INIT_FAILED,
+ NM_CRYPTO_ERR_CANT_READ_FILE,
+ NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
+ NM_CRYPTO_ERR_CERT_FORMAT_INVALID,
+ NM_CRYPTO_ERR_DECODE_FAILED,
+ NM_CRYPTO_ERR_OUT_OF_MEMORY,
+ NM_CRYPTO_ERR_UNKNOWN_KEY_TYPE,
+ NM_CRYPTO_ERR_UNKNOWN_CIPHER,
+ NM_CRYPTO_ERR_RAW_IV_INVALID,
+ NM_CRYPTO_ERR_MD5_INIT_FAILED,
+ NM_CRYPTO_ERR_CIPHER_INIT_FAILED,
+ NM_CRYPTO_ERR_CIPHER_SET_KEY_FAILED,
+ NM_CRYPTO_ERR_CIPHER_SET_IV_FAILED,
+ NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED,
+ NM_CRYPTO_ERR_INVALID_PASSWORD,
+ NM_CRYPTO_ERR_CIPHER_ENCRYPT_FAILED,
+ NM_CRYPTO_ERR_RANDOMIZE_FAILED
+};
+
+typedef enum {
+ NM_CRYPTO_KEY_TYPE_UNKNOWN = 0,
+ NM_CRYPTO_KEY_TYPE_RSA,
+ NM_CRYPTO_KEY_TYPE_DSA
+} NMCryptoKeyType;
+
+typedef enum {
+ NM_CRYPTO_FILE_FORMAT_UNKNOWN = 0,
+ NM_CRYPTO_FILE_FORMAT_X509,
+ NM_CRYPTO_FILE_FORMAT_RAW_KEY,
+ NM_CRYPTO_FILE_FORMAT_PKCS12
+} NMCryptoFileFormat;
+
+#define NM_CRYPTO_ERROR _nm_crypto_error_quark ()
+GQuark _nm_crypto_error_quark (void);
+
+gboolean crypto_init (GError **error);
+
+void crypto_deinit (void);
+
+GByteArray *crypto_decrypt_private_key_data (const GByteArray *contents,
+ const char *password,
+ NMCryptoKeyType *out_key_type,
+ GError **error);
+
+GByteArray *crypto_decrypt_private_key (const char *file,
+ const char *password,
+ NMCryptoKeyType *out_key_type,
+ GError **error);
+
+GByteArray *crypto_load_and_verify_certificate (const char *file,
+ NMCryptoFileFormat *out_file_format,
+ GError **error);
+
+gboolean crypto_is_pkcs12_file (const char *file, GError **error);
+
+gboolean crypto_is_pkcs12_data (const GByteArray *data);
+
+NMCryptoFileFormat crypto_verify_private_key_data (const GByteArray *contents,
+ const char *password,
+ GError **error);
+
+NMCryptoFileFormat crypto_verify_private_key (const char *file,
+ const char *password,
+ GError **error);
+
+/* Internal utils API bits for crypto providers */
+
+gboolean crypto_md5_hash (const char *salt,
+ const gsize salt_len,
+ const char *password,
+ gsize password_len,
+ char *buffer,
+ gsize buflen,
+ GError **error);
+
+char * crypto_decrypt (const char *cipher,
+ int key_type,
+ GByteArray *data,
+ const char *iv,
+ const gsize iv_len,
+ const char *key,
+ const gsize key_len,
+ gsize *out_len,
+ GError **error);
+
+char * crypto_encrypt (const char *cipher,
+ const GByteArray *data,
+ const char *iv,
+ gsize iv_len,
+ const char *key,
+ gsize key_len,
+ gsize *out_len,
+ GError **error);
+
+gboolean crypto_randomize (void *buffer, gsize buffer_len, GError **error);
+
+NMCryptoFileFormat crypto_verify_cert (const unsigned char *data,
+ gsize len,
+ GError **error);
+
+gboolean crypto_verify_pkcs12 (const GByteArray *data,
+ const char *password,
+ GError **error);
+
+gboolean crypto_verify_pkcs8 (const GByteArray *data,
+ gboolean is_encrypted,
+ const char *password,
+ GError **error);
+
+#endif /* __CRYPTO_H__ */
diff --git a/libnm-core/crypto_gnutls.c b/libnm-core/crypto_gnutls.c
new file mode 100644
index 0000000000..be16d135af
--- /dev/null
+++ b/libnm-core/crypto_gnutls.c
@@ -0,0 +1,493 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager Wireless Applet -- Display wireless access points and allow user control
+ *
+ * Dan Williams <dcbw@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2009 Red Hat, Inc.
+ */
+
+#include "config.h"
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include <gcrypt.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+#include <gnutls/pkcs12.h>
+
+#include "crypto.h"
+
+#define SALT_LEN 8
+
+static gboolean initialized = FALSE;
+
+gboolean
+crypto_init (GError **error)
+{
+ if (initialized)
+ return TRUE;
+
+ if (gnutls_global_init() != 0) {
+ gnutls_global_deinit();
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_INIT_FAILED,
+ "%s",
+ _("Failed to initialize the crypto engine."));
+ return FALSE;
+ }
+
+ initialized = TRUE;
+ return TRUE;
+}
+
+void
+crypto_deinit (void)
+{
+}
+
+gboolean
+crypto_md5_hash (const char *salt,
+ const gsize salt_len,
+ const char *password,
+ gsize password_len,
+ char *buffer,
+ gsize buflen,
+ GError **error)
+{
+ gcry_md_hd_t ctx;
+ gcry_error_t err;
+ int nkey = buflen;
+ const gsize digest_len = 16;
+ int count = 0;
+ char digest[MD5_HASH_LEN];
+ char *p = buffer;
+
+ if (salt)
+ g_return_val_if_fail (salt_len >= SALT_LEN, FALSE);
+
+ g_return_val_if_fail (password != NULL, FALSE);
+ g_return_val_if_fail (password_len > 0, FALSE);
+ g_return_val_if_fail (buffer != NULL, FALSE);
+ g_return_val_if_fail (buflen > 0, FALSE);
+
+ err = gcry_md_open (&ctx, GCRY_MD_MD5, 0);
+ if (err) {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_MD5_INIT_FAILED,
+ _("Failed to initialize the MD5 engine: %s / %s."),
+ gcry_strsource (err), gcry_strerror (err));
+ return FALSE;
+ }
+
+ while (nkey > 0) {
+ int i = 0;
+
+ if (count++)
+ gcry_md_write (ctx, digest, digest_len);
+ gcry_md_write (ctx, password, password_len);
+ if (salt)
+ gcry_md_write (ctx, salt, SALT_LEN); /* Only use 8 bytes of salt */
+ gcry_md_final (ctx);
+ memcpy (digest, gcry_md_read (ctx, 0), digest_len);
+ gcry_md_reset (ctx);
+
+ while (nkey && (i < digest_len)) {
+ *(p++) = digest[i++];
+ nkey--;
+ }
+ }
+
+ memset (digest, 0, sizeof (digest));
+ gcry_md_close (ctx);
+ return TRUE;
+}
+
+char *
+crypto_decrypt (const char *cipher,
+ int key_type,
+ GByteArray *data,
+ const char *iv,
+ const gsize iv_len,
+ const char *key,
+ const gsize key_len,
+ gsize *out_len,
+ GError **error)
+{
+ gcry_cipher_hd_t ctx;
+ gcry_error_t err;
+ int cipher_mech, i;
+ char *output = NULL;
+ gboolean success = FALSE;
+ gsize pad_len, real_iv_len;
+
+ if (!strcmp (cipher, CIPHER_DES_EDE3_CBC)) {
+ cipher_mech = GCRY_CIPHER_3DES;
+ real_iv_len = SALT_LEN;
+ } else if (!strcmp (cipher, CIPHER_DES_CBC)) {
+ cipher_mech = GCRY_CIPHER_DES;
+ real_iv_len = SALT_LEN;
+ } else if (!strcmp (cipher, CIPHER_AES_CBC)) {
+ cipher_mech = GCRY_CIPHER_AES;
+ real_iv_len = 16;
+ } else {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_UNKNOWN_CIPHER,
+ _("Private key cipher '%s' was unknown."),
+ cipher);
+ return NULL;
+ }
+
+ if (iv_len < real_iv_len) {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_RAW_IV_INVALID,
+ _("Invalid IV length (must be at least %zd)."),
+ real_iv_len);
+ return NULL;
+ }
+
+ output = g_malloc0 (data->len);
+
+ err = gcry_cipher_open (&ctx, cipher_mech, GCRY_CIPHER_MODE_CBC, 0);
+ if (err) {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_CIPHER_INIT_FAILED,
+ _("Failed to initialize the decryption cipher context: %s / %s."),
+ gcry_strsource (err), gcry_strerror (err));
+ goto out;
+ }
+
+ err = gcry_cipher_setkey (ctx, key, key_len);
+ if (err) {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_CIPHER_SET_KEY_FAILED,
+ _("Failed to set symmetric key for decryption: %s / %s."),
+ gcry_strsource (err), gcry_strerror (err));
+ goto out;
+ }
+
+ err = gcry_cipher_setiv (ctx, iv, iv_len);
+ if (err) {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_CIPHER_SET_IV_FAILED,
+ _("Failed to set IV for decryption: %s / %s."),
+ gcry_strsource (err), gcry_strerror (err));
+ goto out;
+ }
+
+ err = gcry_cipher_decrypt (ctx, output, data->len, data->data, data->len);
+ if (err) {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED,
+ _("Failed to decrypt the private key: %s / %s."),
+ gcry_strsource (err), gcry_strerror (err));
+ goto out;
+ }
+ pad_len = output[data->len - 1];
+
+ /* Check if the padding at the end of the decrypted data is valid */
+ if (pad_len == 0 || pad_len > real_iv_len) {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED,
+ _("Failed to decrypt the private key: unexpected padding length."));
+ goto out;
+ }
+
+ /* Validate tail padding; last byte is the padding size, and all pad bytes
+ * should contain the padding size.
+ */
+ for (i = 1; i <= pad_len; ++i) {
+ if (output[data->len - i] != pad_len) {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED,
+ _("Failed to decrypt the private key."));
+ goto out;
+ }
+ }
+
+ *out_len = data->len - pad_len;
+ success = TRUE;
+
+out:
+ if (!success) {
+ if (output) {
+ /* Don't expose key material */
+ memset (output, 0, data->len);
+ g_free (output);
+ output = NULL;
+ }
+ }
+ gcry_cipher_close (ctx);
+ return output;
+}
+
+char *
+crypto_encrypt (const char *cipher,
+ const GByteArray *data,
+ const char *iv,
+ const gsize iv_len,
+ const char *key,
+ gsize key_len,
+ gsize *out_len,
+ GError **error)
+{
+ gcry_cipher_hd_t ctx;
+ gcry_error_t err;
+ int cipher_mech;
+ char *output = NULL;
+ gboolean success = FALSE;
+ gsize padded_buf_len, pad_len, output_len;
+ char *padded_buf = NULL;
+ guint32 i;
+ gsize salt_len;
+
+ if (!strcmp (cipher, CIPHER_DES_EDE3_CBC)) {
+ cipher_mech = GCRY_CIPHER_3DES;
+ salt_len = SALT_LEN;
+ } else if (!strcmp (cipher, CIPHER_AES_CBC)) {
+ cipher_mech = GCRY_CIPHER_AES;
+ salt_len = iv_len;
+ } else {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_UNKNOWN_CIPHER,
+ _("Private key cipher '%s' was unknown."),
+ cipher);
+ return NULL;
+ }
+
+ /* If data->len % ivlen == 0, then we add another complete block
+ * onto the end so that the decrypter knows there's padding.
+ */
+ pad_len = iv_len - (data->len % iv_len);
+ output_len = padded_buf_len = data->len + pad_len;
+ padded_buf = g_malloc0 (padded_buf_len);
+
+ memcpy (padded_buf, data->data, data->len);
+ for (i = 0; i < pad_len; i++)
+ padded_buf[data->len + i] = (guint8) (pad_len & 0xFF);
+
+ output = g_malloc0 (output_len);
+
+ err = gcry_cipher_open (&ctx, cipher_mech, GCRY_CIPHER_MODE_CBC, 0);
+ if (err) {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_CIPHER_INIT_FAILED,
+ _("Failed to initialize the encryption cipher context: %s / %s."),
+ gcry_strsource (err), gcry_strerror (err));
+ goto out;
+ }
+
+ err = gcry_cipher_setkey (ctx, key, key_len);
+ if (err) {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_CIPHER_SET_KEY_FAILED,
+ _("Failed to set symmetric key for encryption: %s / %s."),
+ gcry_strsource (err), gcry_strerror (err));
+ goto out;
+ }
+
+ /* gcrypt only wants 8 bytes of the IV (same as the DES block length) */
+ err = gcry_cipher_setiv (ctx, iv, salt_len);
+ if (err) {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_CIPHER_SET_IV_FAILED,
+ _("Failed to set IV for encryption: %s / %s."),
+ gcry_strsource (err), gcry_strerror (err));
+ goto out;
+ }
+
+ err = gcry_cipher_encrypt (ctx, output, output_len, padded_buf, padded_buf_len);
+ if (err) {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED,
+ _("Failed to encrypt the data: %s / %s."),
+ gcry_strsource (err), gcry_strerror (err));
+ goto out;
+ }
+
+ *out_len = output_len;
+ success = TRUE;
+
+out:
+ if (padded_buf) {
+ memset (padded_buf, 0, padded_buf_len);
+ g_free (padded_buf);
+ padded_buf = NULL;
+ }
+
+ if (!success) {
+ if (output) {
+ /* Don't expose key material */
+ memset (output, 0, output_len);
+ g_free (output);
+ output = NULL;
+ }
+ }
+ gcry_cipher_close (ctx);
+ return output;
+}
+
+NMCryptoFileFormat
+crypto_verify_cert (const unsigned char *data,
+ gsize len,
+ GError **error)
+{
+ gnutls_x509_crt_t der;
+ gnutls_datum_t dt;
+ int err;
+
+ err = gnutls_x509_crt_init (&der);
+ if (err < 0) {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_CERT_FORMAT_INVALID,
+ _("Error initializing certificate data: %s"),
+ gnutls_strerror (err));
+ return NM_CRYPTO_FILE_FORMAT_UNKNOWN;
+ }
+
+ /* Try DER first */
+ dt.data = (unsigned char *) data;
+ dt.size = len;
+ err = gnutls_x509_crt_import (der, &dt, GNUTLS_X509_FMT_DER);
+ if (err == GNUTLS_E_SUCCESS) {
+ gnutls_x509_crt_deinit (der);
+ return NM_CRYPTO_FILE_FORMAT_X509;
+ }
+
+ /* And PEM next */
+ err = gnutls_x509_crt_import (der, &dt, GNUTLS_X509_FMT_PEM);
+ gnutls_x509_crt_deinit (der);
+ if (err == GNUTLS_E_SUCCESS)
+ return NM_CRYPTO_FILE_FORMAT_X509;
+
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_CERT_FORMAT_INVALID,
+ _("Couldn't decode certificate: %s"),
+ gnutls_strerror (err));
+ return NM_CRYPTO_FILE_FORMAT_UNKNOWN;
+}
+
+gboolean
+crypto_verify_pkcs12 (const GByteArray *data,
+ const char *password,
+ GError **error)
+{
+ gnutls_pkcs12_t p12;
+ gnutls_datum_t dt;
+ gboolean success = FALSE;
+ int err;
+
+ g_return_val_if_fail (data != NULL, FALSE);
+
+ dt.data = (unsigned char *) data->data;
+ dt.size = data->len;
+
+ err = gnutls_pkcs12_init (&p12);
+ if (err < 0) {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_DECODE_FAILED,
+ _("Couldn't initialize PKCS#12 decoder: %s"),
+ gnutls_strerror (err));
+ return FALSE;
+ }
+
+ /* DER first */
+ err = gnutls_pkcs12_import (p12, &dt, GNUTLS_X509_FMT_DER, 0);
+ if (err < 0) {
+ /* PEM next */
+ err = gnutls_pkcs12_import (p12, &dt, GNUTLS_X509_FMT_PEM, 0);
+ if (err < 0) {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
+ _("Couldn't decode PKCS#12 file: %s"),
+ gnutls_strerror (err));
+ goto out;
+ }
+ }
+
+ err = gnutls_pkcs12_verify_mac (p12, password);
+ if (err == GNUTLS_E_SUCCESS)
+ success = TRUE;
+ else {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED,
+ _("Couldn't verify PKCS#12 file: %s"),
+ gnutls_strerror (err));
+ }
+
+out:
+ gnutls_pkcs12_deinit (p12);
+ return success;
+}
+
+gboolean
+crypto_verify_pkcs8 (const GByteArray *data,
+ gboolean is_encrypted,
+ const char *password,
+ GError **error)
+{
+ gnutls_x509_privkey_t p8;
+ gnutls_datum_t dt;
+ int err;
+
+ g_return_val_if_fail (data != NULL, FALSE);
+
+ dt.data = (unsigned char *) data->data;
+ dt.size = data->len;
+
+ err = gnutls_x509_privkey_init (&p8);
+ if (err < 0) {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_DECODE_FAILED,
+ _("Couldn't initialize PKCS#8 decoder: %s"),
+ gnutls_strerror (err));
+ return FALSE;
+ }
+
+ err = gnutls_x509_privkey_import_pkcs8 (p8,
+ &dt,
+ GNUTLS_X509_FMT_DER,
+ is_encrypted ? password : NULL,
+ is_encrypted ? 0 : GNUTLS_PKCS_PLAIN);
+ gnutls_x509_privkey_deinit (p8);
+
+ if (err < 0) {
+ if (err == GNUTLS_E_UNKNOWN_CIPHER_TYPE) {
+ /* HACK: gnutls doesn't support all the cipher types that openssl
+ * can use with PKCS#8, so if we encounter one, we have to assume
+ * the given password works. gnutls needs to unsuckify, apparently.
+ * Specifically, by default openssl uses pbeWithMD5AndDES-CBC
+ * which gnutls does not support.
+ */
+ } else {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
+ _("Couldn't decode PKCS#8 file: %s"),
+ gnutls_strerror (err));
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+gboolean
+crypto_randomize (void *buffer, gsize buffer_len, GError **error)
+{
+ gcry_randomize (buffer, buffer_len, GCRY_STRONG_RANDOM);
+ return TRUE;
+}
diff --git a/libnm-core/crypto_nss.c b/libnm-core/crypto_nss.c
new file mode 100644
index 0000000000..1e589ea709
--- /dev/null
+++ b/libnm-core/crypto_nss.c
@@ -0,0 +1,562 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * Dan Williams <dcbw@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2009 Red Hat, Inc.
+ */
+
+#include "config.h"
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include <prinit.h>
+#include <nss.h>
+#include <pk11pub.h>
+#include <pkcs11t.h>
+#include <cert.h>
+#include <prerror.h>
+#include <p12.h>
+#include <ciferfam.h>
+#include <p12plcy.h>
+
+#include "crypto.h"
+
+static gboolean initialized = FALSE;
+
+gboolean
+crypto_init (GError **error)
+{
+ SECStatus ret;
+
+ if (initialized)
+ return TRUE;
+
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 1);
+ ret = NSS_NoDB_Init (NULL);
+ if (ret != SECSuccess) {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_INIT_FAILED,
+ _("Failed to initialize the crypto engine: %d."),
+ PR_GetError ());
+ PR_Cleanup ();
+ return FALSE;
+ }
+
+ SEC_PKCS12EnableCipher(PKCS12_RC4_40, 1);
+ SEC_PKCS12EnableCipher(PKCS12_RC4_128, 1);
+ SEC_PKCS12EnableCipher(PKCS12_RC2_CBC_40, 1);
+ SEC_PKCS12EnableCipher(PKCS12_RC2_CBC_128, 1);
+ SEC_PKCS12EnableCipher(PKCS12_DES_56, 1);
+ SEC_PKCS12EnableCipher(PKCS12_DES_EDE3_168, 1);
+ SEC_PKCS12SetPreferredCipher(PKCS12_DES_EDE3_168, 1);
+
+ initialized = TRUE;
+ return TRUE;
+}
+
+void
+crypto_deinit (void)
+{
+}
+
+gboolean
+crypto_md5_hash (const char *salt,
+ const gsize salt_len,
+ const char *password,
+ gsize password_len,
+ char *buffer,
+ gsize buflen,
+ GError **error)
+{
+ PK11Context *ctx;
+ int nkey = buflen;
+ unsigned int digest_len;
+ int count = 0;
+ char digest[MD5_HASH_LEN];
+ char *p = buffer;
+
+ if (salt)
+ g_return_val_if_fail (salt_len >= 8, FALSE);
+
+ g_return_val_if_fail (password != NULL, FALSE);
+ g_return_val_if_fail (password_len > 0, FALSE);
+ g_return_val_if_fail (buffer != NULL, FALSE);
+ g_return_val_if_fail (buflen > 0, FALSE);
+
+ ctx = PK11_CreateDigestContext (SEC_OID_MD5);
+ if (!ctx) {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_MD5_INIT_FAILED,
+ _("Failed to initialize the MD5 context: %d."),
+ PORT_GetError ());
+ return FALSE;
+ }
+
+ while (nkey > 0) {
+ int i = 0;
+
+ PK11_DigestBegin (ctx);
+ if (count++)
+ PK11_DigestOp (ctx, (const unsigned char *) digest, digest_len);
+ PK11_DigestOp (ctx, (const unsigned char *) password, password_len);
+ if (salt)
+ PK11_DigestOp (ctx, (const unsigned char *) salt, 8); /* Only use 8 bytes of salt */
+ PK11_DigestFinal (ctx, (unsigned char *) digest, &digest_len, sizeof (digest));
+
+ while (nkey && (i < digest_len)) {
+ *(p++) = digest[i++];
+ nkey--;
+ }
+ }
+
+ memset (digest, 0, sizeof (digest));
+ PK11_DestroyContext (ctx, PR_TRUE);
+ return TRUE;
+}
+
+char *
+crypto_decrypt (const char *cipher,
+ int key_type,
+ GByteArray *data,
+ const char *iv,
+ const gsize iv_len,
+ const char *key,
+ const gsize key_len,
+ gsize *out_len,
+ GError **error)
+{
+ char *output = NULL;
+ int decrypted_len = 0;
+ CK_MECHANISM_TYPE cipher_mech;
+ PK11SlotInfo *slot = NULL;
+ SECItem key_item;
+ PK11SymKey *sym_key = NULL;
+ SECItem *sec_param = NULL;
+ PK11Context *ctx = NULL;
+ SECStatus s;
+ gboolean success = FALSE;
+ unsigned int pad_len = 0, extra = 0;
+ guint32 i, real_iv_len = 0;
+
+ if (!strcmp (cipher, CIPHER_DES_EDE3_CBC)) {
+ cipher_mech = CKM_DES3_CBC_PAD;
+ real_iv_len = 8;
+ } else if (!strcmp (cipher, CIPHER_DES_CBC)) {
+ cipher_mech = CKM_DES_CBC_PAD;
+ real_iv_len = 8;
+ } else if (!strcmp (cipher, CIPHER_AES_CBC)) {
+ cipher_mech = CKM_AES_CBC_PAD;
+ real_iv_len = 16;
+ } else {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_UNKNOWN_CIPHER,
+ _("Private key cipher '%s' was unknown."),
+ cipher);
+ return NULL;
+ }
+
+ if (iv_len < real_iv_len) {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_RAW_IV_INVALID,
+ _("Invalid IV length (must be at least %d)."),
+ real_iv_len);
+ return NULL;
+ }
+
+ output = g_malloc0 (data->len);
+
+ slot = PK11_GetBestSlot (cipher_mech, NULL);
+ if (!slot) {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_CIPHER_INIT_FAILED,
+ _("Failed to initialize the decryption cipher slot."));
+ goto out;
+ }
+
+ key_item.data = (unsigned char *) key;
+ key_item.len = key_len;
+ sym_key = PK11_ImportSymKey (slot, cipher_mech, PK11_OriginUnwrap, CKA_DECRYPT, &key_item, NULL);
+ if (!sym_key) {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_CIPHER_SET_KEY_FAILED,
+ _("Failed to set symmetric key for decryption."));
+ goto out;
+ }
+
+ key_item.data = (unsigned char *) iv;
+ key_item.len = real_iv_len;
+ sec_param = PK11_ParamFromIV (cipher_mech, &key_item);
+ if (!sec_param) {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_CIPHER_SET_IV_FAILED,
+ _("Failed to set IV for decryption."));
+ goto out;
+ }
+
+ ctx = PK11_CreateContextBySymKey (cipher_mech, CKA_DECRYPT, sym_key, sec_param);
+ if (!ctx) {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_CIPHER_INIT_FAILED,
+ _("Failed to initialize the decryption context."));
+ goto out;
+ }
+
+ s = PK11_CipherOp (ctx,
+ (unsigned char *) output,
+ &decrypted_len,
+ data->len,
+ data->data,
+ data->len);
+ if (s != SECSuccess) {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED,
+ _("Failed to decrypt the private key: %d."),
+ PORT_GetError ());
+ goto out;
+ }
+
+ if (decrypted_len > data->len) {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED,
+ _("Failed to decrypt the private key: decrypted data too large."));
+ goto out;
+ }
+
+ s = PK11_DigestFinal (ctx,
+ (unsigned char *) (output + decrypted_len),
+ &extra,
+ data->len - decrypted_len);
+ if (s != SECSuccess) {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED,
+ _("Failed to finalize decryption of the private key: %d."),
+ PORT_GetError ());
+ goto out;
+ }
+ decrypted_len += extra;
+ pad_len = data->len - decrypted_len;
+
+ /* Check if the padding at the end of the decrypted data is valid */
+ if (pad_len == 0 || pad_len > real_iv_len) {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED,
+ _("Failed to decrypt the private key: unexpected padding length."));
+ goto out;
+ }
+
+ /* Validate tail padding; last byte is the padding size, and all pad bytes
+ * should contain the padding size.
+ */
+ for (i = pad_len; i > 0; i--) {
+ if (output[data->len - i] != pad_len) {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED,
+ _("Failed to decrypt the private key."));
+ goto out;
+ }
+ }
+
+ *out_len = decrypted_len;
+ success = TRUE;
+
+out:
+ if (ctx)
+ PK11_DestroyContext (ctx, PR_TRUE);
+ if (sym_key)
+ PK11_FreeSymKey (sym_key);
+ if (sec_param)
+ SECITEM_FreeItem (sec_param, PR_TRUE);
+ if (slot)
+ PK11_FreeSlot (slot);
+
+ if (!success) {
+ if (output) {
+ /* Don't expose key material */
+ memset (output, 0, data->len);
+ g_free (output);
+ output = NULL;
+ }
+ }
+ return output;
+}
+
+char *
+crypto_encrypt (const char *cipher,
+ const GByteArray *data,
+ const char *iv,
+ gsize iv_len,
+ const char *key,
+ gsize key_len,
+ gsize *out_len,
+ GError **error)
+{
+ SECStatus ret;
+ CK_MECHANISM_TYPE cipher_mech = CKM_DES3_CBC_PAD;
+ PK11SlotInfo *slot = NULL;
+ SECItem key_item = { .data = (unsigned char *) key, .len = key_len };
+ SECItem iv_item = { .data = (unsigned char *) iv, .len = iv_len };
+ PK11SymKey *sym_key = NULL;
+ SECItem *sec_param = NULL;
+ PK11Context *ctx = NULL;
+ unsigned char *output, *padded_buf;
+ gsize output_len;
+ int encrypted_len = 0, i;
+ gboolean success = FALSE;
+ gsize padded_buf_len, pad_len;
+
+ if (!strcmp (cipher, CIPHER_DES_EDE3_CBC))
+ cipher_mech = CKM_DES3_CBC_PAD;
+ else if (!strcmp (cipher, CIPHER_AES_CBC))
+ cipher_mech = CKM_AES_CBC_PAD;
+ else {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_UNKNOWN_CIPHER,
+ _("Private key cipher '%s' was unknown."),
+ cipher);
+ return NULL;
+ }
+
+ /* If data->len % ivlen == 0, then we add another complete block
+ * onto the end so that the decrypter knows there's padding.
+ */
+ pad_len = iv_len - (data->len % iv_len);
+ output_len = padded_buf_len = data->len + pad_len;
+ padded_buf = g_malloc0 (padded_buf_len);
+
+ memcpy (padded_buf, data->data, data->len);
+ for (i = 0; i < pad_len; i++)
+ padded_buf[data->len + i] = (guint8) (pad_len & 0xFF);
+
+ output = g_malloc0 (output_len);
+
+ slot = PK11_GetBestSlot (cipher_mech, NULL);
+ if (!slot) {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_CIPHER_INIT_FAILED,
+ _("Failed to initialize the encryption cipher slot."));
+ goto out;
+ }
+
+ sym_key = PK11_ImportSymKey (slot, cipher_mech, PK11_OriginUnwrap, CKA_ENCRYPT, &key_item, NULL);
+ if (!sym_key) {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_CIPHER_SET_KEY_FAILED,
+ _("Failed to set symmetric key for encryption."));
+ goto out;
+ }
+
+ sec_param = PK11_ParamFromIV (cipher_mech, &iv_item);
+ if (!sec_param) {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_CIPHER_SET_IV_FAILED,
+ _("Failed to set IV for encryption."));
+ goto out;
+ }
+
+ ctx = PK11_CreateContextBySymKey (cipher_mech, CKA_ENCRYPT, sym_key, sec_param);
+ if (!ctx) {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_CIPHER_INIT_FAILED,
+ _("Failed to initialize the encryption context."));
+ goto out;
+ }
+
+ ret = PK11_CipherOp (ctx, output, &encrypted_len, output_len, padded_buf, padded_buf_len);
+ if (ret != SECSuccess) {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_CIPHER_ENCRYPT_FAILED,
+ _("Failed to encrypt: %d."),
+ PORT_GetError ());
+ goto out;
+ }
+
+ if (encrypted_len != output_len) {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_CIPHER_ENCRYPT_FAILED,
+ _("Unexpected amount of data after encrypting."));
+ goto out;
+ }
+
+ *out_len = encrypted_len;
+ success = TRUE;
+
+out:
+ if (ctx)
+ PK11_DestroyContext (ctx, PR_TRUE);
+ if (sym_key)
+ PK11_FreeSymKey (sym_key);
+ if (sec_param)
+ SECITEM_FreeItem (sec_param, PR_TRUE);
+ if (slot)
+ PK11_FreeSlot (slot);
+
+ memset (padded_buf, 0, padded_buf_len);
+ g_free (padded_buf);
+
+ if (!success) {
+ memset (output, 0, output_len);
+ g_free (output);
+ output = NULL;
+ }
+ return (char *) output;
+}
+
+NMCryptoFileFormat
+crypto_verify_cert (const unsigned char *data,
+ gsize len,
+ GError **error)
+{
+ CERTCertificate *cert;
+
+ /* Try DER/PEM first */
+ cert = CERT_DecodeCertFromPackage ((char *) data, len);
+ if (!cert) {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_CERT_FORMAT_INVALID,
+ _("Couldn't decode certificate: %d"),
+ PORT_GetError());
+ return NM_CRYPTO_FILE_FORMAT_UNKNOWN;
+ }
+
+ CERT_DestroyCertificate (cert);
+ return NM_CRYPTO_FILE_FORMAT_X509;
+}
+
+gboolean
+crypto_verify_pkcs12 (const GByteArray *data,
+ const char *password,
+ GError **error)
+{
+ SEC_PKCS12DecoderContext *p12ctx = NULL;
+ SECItem pw = { 0 };
+ PK11SlotInfo *slot = NULL;
+ SECStatus s;
+ char *ucs2_password;
+ glong ucs2_chars = 0;
+#ifndef WORDS_BIGENDIAN
+ guint16 *p;
+#endif /* WORDS_BIGENDIAN */
+
+ if (error)
+ g_return_val_if_fail (*error == NULL, FALSE);
+
+ /* PKCS#12 passwords are apparently UCS2 BIG ENDIAN, and NSS doesn't do
+ * any conversions for us.
+ */
+ if (password && strlen (password)) {
+ ucs2_password = (char *) g_utf8_to_utf16 (password, strlen (password), NULL, &ucs2_chars, NULL);
+ if (!ucs2_password || !ucs2_chars) {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_INVALID_PASSWORD,
+ _("Couldn't convert password to UCS2: %d"),
+ PORT_GetError());
+ return FALSE;
+ }
+
+ ucs2_chars *= 2; /* convert # UCS2 characters -> bytes */
+ pw.data = PORT_ZAlloc(ucs2_chars + 2);
+ memcpy (pw.data, ucs2_password, ucs2_chars);
+ pw.len = ucs2_chars + 2; /* include terminating NULL */
+
+ memset (ucs2_password, 0, ucs2_chars);
+ g_free (ucs2_password);
+
+#ifndef WORDS_BIGENDIAN
+ for (p = (guint16 *) pw.data; p < (guint16 *) (pw.data + pw.len); p++)
+ *p = GUINT16_SWAP_LE_BE (*p);
+#endif /* WORDS_BIGENDIAN */
+ } else {
+ /* NULL password */
+ pw.data = NULL;
+ pw.len = 0;
+ }
+
+ slot = PK11_GetInternalKeySlot();
+ p12ctx = SEC_PKCS12DecoderStart (&pw, slot, NULL, NULL, NULL, NULL, NULL, NULL);
+ if (!p12ctx) {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_DECODE_FAILED,
+ _("Couldn't initialize PKCS#12 decoder: %d"),
+ PORT_GetError());
+ goto error;
+ }
+
+ s = SEC_PKCS12DecoderUpdate (p12ctx, data->data, data->len);
+ if (s != SECSuccess) {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
+ _("Couldn't decode PKCS#12 file: %d"),
+ PORT_GetError());
+ goto error;
+ }
+
+ s = SEC_PKCS12DecoderVerify (p12ctx);
+ if (s != SECSuccess) {
+ g_set_error (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED,
+ _("Couldn't verify PKCS#12 file: %d"),
+ PORT_GetError());
+ goto error;
+ }
+
+ SEC_PKCS12DecoderFinish (p12ctx);
+ SECITEM_ZfreeItem (&pw, PR_FALSE);
+ return TRUE;
+
+error:
+ if (p12ctx)
+ SEC_PKCS12DecoderFinish (p12ctx);
+
+ if (slot)
+ PK11_FreeSlot(slot);
+
+ SECITEM_ZfreeItem (&pw, PR_FALSE);
+ return FALSE;
+}
+
+gboolean
+crypto_verify_pkcs8 (const GByteArray *data,
+ gboolean is_encrypted,
+ const char *password,
+ GError **error)
+{
+ g_return_val_if_fail (data != NULL, FALSE);
+
+ /* NSS apparently doesn't do PKCS#8 natively, but you have to put the
+ * PKCS#8 key into a PKCS#12 file and import that?? So until we figure
+ * all that out, we can only assume the password is valid.
+ */
+ return TRUE;
+}
+
+gboolean
+crypto_randomize (void *buffer, gsize buffer_len, GError **error)
+{
+ SECStatus s;
+
+ s = PK11_GenerateRandom (buffer, buffer_len);
+ if (s != SECSuccess) {
+ g_set_error_literal (error, NM_CRYPTO_ERROR,
+ NM_CRYPTO_ERR_RANDOMIZE_FAILED,
+ _("Could not generate random data."));
+ return FALSE;
+ }
+ return TRUE;
+}
diff --git a/libnm-core/nm-connection.c b/libnm-core/nm-connection.c
new file mode 100644
index 0000000000..7e26309c57
--- /dev/null
+++ b/libnm-core/nm-connection.c
@@ -0,0 +1,2189 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2013 Red Hat, Inc.
+ * Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#include <glib-object.h>
+#include <glib/gi18n.h>
+#include <dbus/dbus-glib.h>
+#include <string.h>
+#include "nm-connection.h"
+#include "nm-utils.h"
+#include "nm-dbus-glib-types.h"
+#include "nm-setting-private.h"
+
+#include "nm-setting-8021x.h"
+#include "nm-setting-bluetooth.h"
+#include "nm-setting-connection.h"
+#include "nm-setting-infiniband.h"
+#include "nm-setting-ip4-config.h"
+#include "nm-setting-ip6-config.h"
+#include "nm-setting-ppp.h"
+#include "nm-setting-pppoe.h"
+#include "nm-setting-wimax.h"
+#include "nm-setting-wired.h"
+#include "nm-setting-adsl.h"
+#include "nm-setting-wireless.h"
+#include "nm-setting-wireless-security.h"
+#include "nm-setting-serial.h"
+#include "nm-setting-vpn.h"
+#include "nm-setting-olpc-mesh.h"
+#include "nm-setting-bond.h"
+#include "nm-setting-team.h"
+#include "nm-setting-team-port.h"
+#include "nm-setting-bridge.h"
+#include "nm-setting-bridge-port.h"
+#include "nm-setting-vlan.h"
+#include "nm-setting-serial.h"
+#include "nm-setting-gsm.h"
+#include "nm-setting-cdma.h"
+
+/**
+ * SECTION:nm-connection
+ * @short_description: Describes a connection to specific network or provider
+ * @include: nm-connection.h
+ *
+ * An #NMConnection describes all the settings and configuration values that
+ * are necessary to configure network devices for operation on a specific
+ * network. Connections are the fundamental operating object for
+ * NetworkManager; no device is connected without a #NMConnection, or
+ * disconnected without having been connected with a #NMConnection.
+ *
+ * Each #NMConnection contains a list of #NMSetting objects usually referenced
+ * by name (using nm_connection_get_setting_by_name()) or by type (with
+ * nm_connection_get_setting()). The settings describe the actual parameters
+ * with which the network devices are configured, including device-specific
+ * parameters (MTU, SSID, APN, channel, rate, etc) and IP-level parameters
+ * (addresses, routes, addressing methods, etc).
+ *
+ */
+
+/**
+ * nm_connection_error_quark:
+ *
+ * Registers an error quark for #NMConnection if necessary.
+ *
+ * Returns: the error quark used for #NMConnection errors.
+ **/
+GQuark
+nm_connection_error_quark (void)
+{
+ static GQuark quark;
+
+ if (G_UNLIKELY (!quark))
+ quark = g_quark_from_static_string ("nm-connection-error-quark");
+ return quark;
+}
+
+typedef struct {
+ GHashTable *settings;
+
+ /* D-Bus path of the connection, if any */
+ char *path;
+} NMConnectionPrivate;
+
+#define NM_CONNECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_CONNECTION, NMConnectionPrivate))
+
+G_DEFINE_TYPE (NMConnection, nm_connection, G_TYPE_OBJECT)
+
+enum {
+ PROP_0,
+ PROP_PATH,
+
+ LAST_PROP
+};
+
+enum {
+ SECRETS_UPDATED,
+ SECRETS_CLEARED,
+ CHANGED,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+
+static NMSettingVerifyResult _nm_connection_verify (NMConnection *connection, GError **error);
+
+
+/*************************************************************/
+
+/**
+ * nm_connection_lookup_setting_type:
+ * @name: a setting name
+ *
+ * Returns the #GType of the setting's class for a given setting name.
+ *
+ * Returns: the #GType of the setting's class
+ **/
+GType
+nm_connection_lookup_setting_type (const char *name)
+{
+ return _nm_setting_lookup_setting_type (name);
+}
+
+/**
+ * nm_connection_lookup_setting_type_by_quark:
+ * @error_quark: a setting error quark
+ *
+ * Returns the #GType of the setting's class for a given setting error quark.
+ * Useful for figuring out which setting a returned error is for.
+ *
+ * Returns: the #GType of the setting's class
+ **/
+GType
+nm_connection_lookup_setting_type_by_quark (GQuark error_quark)
+{
+ return _nm_setting_lookup_setting_type_by_quark (error_quark);
+}
+
+/**
+ * nm_connection_create_setting:
+ * @name: a setting name
+ *
+ * Create a new #NMSetting object of the desired type, given a setting name.
+ *
+ * Returns: (transfer full): the new setting object, or %NULL if the setting name was unknown
+ **/
+NMSetting *
+nm_connection_create_setting (const char *name)
+{
+ GType type;
+ NMSetting *setting = NULL;
+
+ g_return_val_if_fail (name != NULL, NULL);
+
+ type = nm_connection_lookup_setting_type (name);
+ if (type)
+ setting = (NMSetting *) g_object_new (type, NULL);
+
+ return setting;
+}
+
+static void
+setting_changed_cb (NMSetting *setting,
+ GParamSpec *pspec,
+ NMConnection *self)
+{
+ g_signal_emit (self, signals[CHANGED], 0);
+}
+
+static void
+_nm_connection_add_setting (NMConnection *connection, NMSetting *setting)
+{
+ g_hash_table_insert (NM_CONNECTION_GET_PRIVATE (connection)->settings,
+ (gpointer) G_OBJECT_TYPE_NAME (setting),
+ setting);
+ /* Listen for property changes so we can emit the 'changed' signal */
+ g_signal_connect (setting, "notify", (GCallback) setting_changed_cb, connection);
+}
+
+/**
+ * nm_connection_add_setting:
+ * @connection: a #NMConnection
+ * @setting: (transfer full): the #NMSetting to add to the connection object
+ *
+ * Adds a #NMSetting to the connection, replacing any previous #NMSetting of the
+ * same name which has previously been added to the #NMConnection. The
+ * connection takes ownership of the #NMSetting object and does not increase
+ * the setting object's reference count.
+ **/
+void
+nm_connection_add_setting (NMConnection *connection, NMSetting *setting)
+{
+ g_return_if_fail (NM_IS_CONNECTION (connection));
+ g_return_if_fail (NM_IS_SETTING (setting));
+
+ _nm_connection_add_setting (connection, setting);
+ g_signal_emit (connection, signals[CHANGED], 0);
+}
+
+/**
+ * nm_connection_remove_setting:
+ * @connection: a #NMConnection
+ * @setting_type: the #GType of the setting object to remove
+ *
+ * Removes the #NMSetting with the given #GType from the #NMConnection. This
+ * operation dereferences the #NMSetting object.
+ **/
+void
+nm_connection_remove_setting (NMConnection *connection, GType setting_type)
+{
+ NMConnectionPrivate *priv;
+ NMSetting *setting;
+ const char *setting_name;
+
+ g_return_if_fail (NM_IS_CONNECTION (connection));
+ g_return_if_fail (g_type_is_a (setting_type, NM_TYPE_SETTING));
+
+ priv = NM_CONNECTION_GET_PRIVATE (connection);
+ setting_name = g_type_name (setting_type);
+ setting = g_hash_table_lookup (priv->settings, setting_name);
+ if (setting) {
+ g_signal_handlers_disconnect_by_func (setting, setting_changed_cb, connection);
+ g_hash_table_remove (priv->settings, setting_name);
+ g_signal_emit (connection, signals[CHANGED], 0);
+ }
+}
+
+/**
+ * nm_connection_get_setting:
+ * @connection: a #NMConnection
+ * @setting_type: the #GType of the setting object to return
+ *
+ * Gets the #NMSetting with the given #GType, if one has been previously added
+ * to the #NMConnection.
+ *
+ * Returns: (transfer none): the #NMSetting, or %NULL if no setting of that type was previously
+ * added to the #NMConnection
+ **/
+NMSetting *
+nm_connection_get_setting (NMConnection *connection, GType setting_type)
+{
+ g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+ g_return_val_if_fail (g_type_is_a (setting_type, NM_TYPE_SETTING), NULL);
+
+ return (NMSetting *) g_hash_table_lookup (NM_CONNECTION_GET_PRIVATE (connection)->settings,
+ g_type_name (setting_type));
+}
+
+/**
+ * nm_connection_get_setting_by_name:
+ * @connection: a #NMConnection
+ * @name: a setting name
+ *
+ * Gets the #NMSetting with the given name, if one has been previously added
+ * the #NMConnection.
+ *
+ * Returns: (transfer none): the #NMSetting, or %NULL if no setting with that name was previously
+ * added to the #NMConnection
+ **/
+NMSetting *
+nm_connection_get_setting_by_name (NMConnection *connection, const char *name)
+{
+ GType type;
+
+ g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+ g_return_val_if_fail (name != NULL, NULL);
+
+ type = nm_connection_lookup_setting_type (name);
+
+ return type ? nm_connection_get_setting (connection, type) : NULL;
+}
+
+/* not exposed until we actually need it */
+static NMSetting *
+_get_type_setting (NMConnection *connection)
+{
+ NMSettingConnection *s_con;
+ const char *type;
+ NMSetting *base;
+
+ g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+
+ s_con = nm_connection_get_setting_connection (connection);
+ g_assert (s_con);
+
+ type = nm_setting_connection_get_connection_type (s_con);
+ g_assert (type);
+
+ base = nm_connection_get_setting_by_name (connection, type);
+ g_assert (base);
+
+ return base;
+}
+
+static gboolean
+validate_permissions_type (GHashTable *hash, GError **error)
+{
+ GHashTable *s_con;
+ GValue *permissions;
+
+ /* Ensure the connection::permissions item (if present) is the correct
+ * type, otherwise the g_object_set() will throw a warning and ignore the
+ * error, leaving us with no permissions.
+ */
+ s_con = g_hash_table_lookup (hash, NM_SETTING_CONNECTION_SETTING_NAME);
+ if (s_con) {
+ permissions = g_hash_table_lookup (s_con, NM_SETTING_CONNECTION_PERMISSIONS);
+ if (permissions) {
+ if ( !G_VALUE_HOLDS (permissions, G_TYPE_STRV)
+ && !G_VALUE_HOLDS (permissions, DBUS_TYPE_G_LIST_OF_STRING)) {
+ g_set_error_literal (error,
+ NM_SETTING_ERROR,
+ NM_SETTING_ERROR_PROPERTY_TYPE_MISMATCH,
+ "Wrong permissions property type; should be a list of strings.");
+ return FALSE;
+ }
+ }
+ }
+ return TRUE;
+}
+
+static gboolean
+hash_to_connection (NMConnection *connection, GHashTable *new, GError **error)
+{
+ GHashTableIter iter;
+ const char *setting_name;
+ GHashTable *setting_hash;
+ gboolean changed, valid;
+ NMConnectionPrivate *priv = NM_CONNECTION_GET_PRIVATE (connection);
+
+ if ((changed = g_hash_table_size (priv->settings) > 0))
+ g_hash_table_remove_all (priv->settings);
+
+ g_hash_table_iter_init (&iter, new);
+ while (g_hash_table_iter_next (&iter, (gpointer) &setting_name, (gpointer) &setting_hash)) {
+ GType type = nm_connection_lookup_setting_type (setting_name);
+
+ if (type) {
+ NMSetting *setting = nm_setting_new_from_hash (type, setting_hash);
+
+ if (setting) {
+ _nm_connection_add_setting (connection, setting);
+ changed = TRUE;
+ }
+ }
+ }
+
+ valid = nm_connection_verify (connection, error);
+ if (changed)
+ g_signal_emit (connection, signals[CHANGED], 0);
+ return valid;
+}
+
+/**
+ * nm_connection_replace_settings:
+ * @connection: a #NMConnection
+ * @new_settings: (element-type utf8 GLib.HashTable): a #GHashTable of settings
+ * @error: location to store error, or %NULL
+ *
+ * Returns: %TRUE if the settings were valid and added to the connection, %FALSE
+ * if they were not
+ **/
+gboolean
+nm_connection_replace_settings (NMConnection *connection,
+ GHashTable *new_settings,
+ GError **error)
+{
+ gboolean valid = FALSE;
+
+ g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE);
+ g_return_val_if_fail (new_settings != NULL, FALSE);
+ if (error)
+ g_return_val_if_fail (*error == NULL, FALSE);
+
+ if (validate_permissions_type (new_settings, error))
+ valid = hash_to_connection (connection, new_settings, error);
+ return valid;
+}
+
+/**
+ * nm_connection_replace_settings_from_connection:
+ * @connection: a #NMConnection
+ * @new_connection: a #NMConnection to replace the settings of @connection with
+ * @error: location to store error, or %NULL
+ *
+ * Deep-copies the settings of @new_conenction and replaces the settings of @connection
+ * with the copied settings.
+ *
+ * Returns: %TRUE if the settings were valid and added to the connection, %FALSE
+ * if they were not
+ *
+ * Since: 0.9.10
+ **/
+gboolean
+nm_connection_replace_settings_from_connection (NMConnection *connection,
+ NMConnection *new_connection,
+ GError **error)
+{
+ NMConnectionPrivate *priv;
+ GHashTableIter iter;
+ NMSetting *setting;
+ gboolean changed, valid;
+
+ g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE);
+ g_return_val_if_fail (NM_IS_CONNECTION (new_connection), FALSE);
+ if (error)
+ g_return_val_if_fail (*error == NULL, FALSE);
+
+ /* When 'connection' and 'new_connection' are the same object simply return
+ * in order not to destroy 'connection' */
+ if (connection == new_connection)
+ return TRUE;
+
+ /* No need to validate permissions like nm_connection_replace_settings()
+ * since we're dealing with an NMConnection which has already done that.
+ */
+
+ priv = NM_CONNECTION_GET_PRIVATE (connection);
+ if ((changed = g_hash_table_size (priv->settings) > 0))
+ g_hash_table_remove_all (priv->settings);
+
+ if (g_hash_table_size (NM_CONNECTION_GET_PRIVATE (new_connection)->settings)) {
+ g_hash_table_iter_init (&iter, NM_CONNECTION_GET_PRIVATE (new_connection)->settings);
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer) &setting))
+ _nm_connection_add_setting (connection, nm_setting_duplicate (setting));
+ changed = TRUE;
+ }
+
+ valid = nm_connection_verify (connection, error);
+ if (changed)
+ g_signal_emit (connection, signals[CHANGED], 0);
+ return valid;
+}
+
+/**
+ * nm_connection_compare:
+ * @a: a #NMConnection
+ * @b: a second #NMConnection to compare with the first
+ * @flags: compare flags, e.g. %NM_SETTING_COMPARE_FLAG_EXACT
+ *
+ * Compares two #NMConnection objects for similarity, with comparison behavior
+ * modified by a set of flags. See nm_setting_compare() for a description of
+ * each flag's behavior.
+ *
+ * Returns: %TRUE if the comparison succeeds, %FALSE if it does not
+ **/
+gboolean
+nm_connection_compare (NMConnection *a,
+ NMConnection *b,
+ NMSettingCompareFlags flags)
+{
+ GHashTableIter iter;
+ NMSetting *src;
+
+ if (a == b)
+ return TRUE;
+ if (!a || !b)
+ return FALSE;
+
+ /* B / A: ensure settings in B that are not in A make the comparison fail */
+ if (g_hash_table_size (NM_CONNECTION_GET_PRIVATE (a)->settings) !=
+ g_hash_table_size (NM_CONNECTION_GET_PRIVATE (b)->settings))
+ return FALSE;
+
+ /* A / B: ensure all settings in A match corresponding ones in B */
+ g_hash_table_iter_init (&iter, NM_CONNECTION_GET_PRIVATE (a)->settings);
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer) &src)) {
+ NMSetting *cmp = nm_connection_get_setting (b, G_OBJECT_TYPE (src));
+
+ if (!cmp || !nm_setting_compare (src, cmp, flags))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+static void
+diff_one_connection (NMConnection *a,
+ NMConnection *b,
+ NMSettingCompareFlags flags,
+ gboolean invert_results,
+ GHashTable *diffs)
+{
+ NMConnectionPrivate *priv = NM_CONNECTION_GET_PRIVATE (a);
+ GHashTableIter iter;
+ NMSetting *a_setting = NULL;
+
+ g_hash_table_iter_init (&iter, priv->settings);
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer) &a_setting)) {
+ NMSetting *b_setting = NULL;
+ const char *setting_name = nm_setting_get_name (a_setting);
+ GHashTable *results;
+ gboolean new_results = TRUE;
+
+ if (b)
+ b_setting = nm_connection_get_setting (b, G_OBJECT_TYPE (a_setting));
+
+ results = g_hash_table_lookup (diffs, setting_name);
+ if (results)
+ new_results = FALSE;
+
+ if (!nm_setting_diff (a_setting, b_setting, flags, invert_results, &results)) {
+ if (new_results)
+ g_hash_table_insert (diffs, g_strdup (setting_name), results);
+ }
+ }
+}
+
+/**
+ * nm_connection_diff:
+ * @a: a #NMConnection
+ * @b: a second #NMConnection to compare with the first
+ * @flags: compare flags, e.g. %NM_SETTING_COMPARE_FLAG_EXACT
+ * @out_settings: (element-type utf8 GLib.HashTable): if the
+ * connections differ, on return a hash table mapping setting names to
+ * second-level GHashTable (utf8 to guint32), which contains the key names that
+ * differ mapped to one or more of %NMSettingDiffResult as a bitfield
+ *
+ * Compares two #NMConnection objects for similarity, with comparison behavior
+ * modified by a set of flags. See nm_setting_compare() for a description of
+ * each flag's behavior. If the connections differ, settings and keys within
+ * each setting that differ are added to the returned @out_settings hash table.
+ * No values are returned, only key names.
+ *
+ * Returns: %TRUE if the connections contain the same values, %FALSE if they do
+ * not
+ **/
+gboolean
+nm_connection_diff (NMConnection *a,
+ NMConnection *b,
+ NMSettingCompareFlags flags,
+ GHashTable **out_settings)
+{
+ GHashTable *diffs;
+
+ g_return_val_if_fail (NM_IS_CONNECTION (a), FALSE);
+ g_return_val_if_fail (out_settings != NULL, FALSE);
+ g_return_val_if_fail (*out_settings == NULL, FALSE);
+ if (b)
+ g_return_val_if_fail (NM_IS_CONNECTION (b), FALSE);
+
+ if (a == b)
+ return TRUE;
+
+ diffs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_hash_table_destroy);
+
+ /* Diff A to B, then B to A to capture keys in B that aren't in A */
+ diff_one_connection (a, b, flags, FALSE, diffs);
+ if (b)
+ diff_one_connection (b, a, flags, TRUE, diffs);
+
+ if (g_hash_table_size (diffs) == 0)
+ g_hash_table_destroy (diffs);
+ else
+ *out_settings = diffs;
+
+ return *out_settings ? FALSE : TRUE;
+}
+
+static gboolean
+_normalize_virtual_iface_name (NMConnection *self)
+{
+ NMConnectionPrivate *priv = NM_CONNECTION_GET_PRIVATE (self);
+ GHashTableIter h_iter;
+ NMSetting *setting;
+ NMSettingConnection *s_con;
+ const char *interface_name;
+ char *virtual_iface_name = NULL;
+ gboolean was_modified = FALSE;
+ const char *prop_name = NULL;
+
+ /* search for settings that might need normalization of the interface name. */
+ g_hash_table_iter_init (&h_iter, priv->settings);
+ while ( !prop_name
+ && g_hash_table_iter_next (&h_iter, NULL, (void **) &setting)) {
+ if (NM_IS_SETTING_BOND (setting))
+ prop_name = NM_SETTING_BOND_INTERFACE_NAME;
+ else if (NM_IS_SETTING_BRIDGE (setting))
+ prop_name = NM_SETTING_BRIDGE_INTERFACE_NAME;
+ else if (NM_IS_SETTING_TEAM (setting))
+ prop_name = NM_SETTING_TEAM_INTERFACE_NAME;
+ else if (NM_IS_SETTING_VLAN (setting))
+ prop_name = NM_SETTING_VLAN_INTERFACE_NAME;
+ }
+ if (!prop_name)
+ return FALSE;
+
+ s_con = nm_connection_get_setting_connection (self);
+ g_return_val_if_fail (s_con, FALSE);
+
+ interface_name = nm_setting_connection_get_interface_name (s_con);
+
+ /* read the potential virtual_iface_name from the setting. */
+ g_object_get (setting, prop_name, &virtual_iface_name, NULL);
+
+ if (g_strcmp0 (interface_name, virtual_iface_name) != 0) {
+ if (interface_name) {
+ /* interface_name is set and overwrites the virtual_iface_name. */
+ g_object_set (setting, prop_name, interface_name, NULL);
+ } else {
+ /* interface in NMSettingConnection must be set. */
+ g_object_set (s_con, NM_SETTING_CONNECTION_INTERFACE_NAME, virtual_iface_name, NULL);
+ }
+ was_modified = TRUE;
+ }
+
+ g_free (virtual_iface_name);
+
+ return was_modified;
+}
+
+static gboolean
+_normalize_ip_config (NMConnection *self, GHashTable *parameters)
+{
+ NMSettingConnection *s_con = nm_connection_get_setting_connection (self);
+ const char *default_ip4_method = NM_SETTING_IP4_CONFIG_METHOD_AUTO;
+ const char *default_ip6_method = NULL;
+ NMSettingIP4Config *s_ip4;
+ NMSettingIP6Config *s_ip6;
+ NMSetting *setting;
+
+ if (parameters)
+ default_ip6_method = g_hash_table_lookup (parameters, NM_CONNECTION_NORMALIZE_PARAM_IP6_CONFIG_METHOD);
+ if (!default_ip6_method)
+ default_ip6_method = NM_SETTING_IP6_CONFIG_METHOD_AUTO;
+
+ s_ip4 = nm_connection_get_setting_ip4_config (self);
+ s_ip6 = nm_connection_get_setting_ip6_config (self);
+
+ if (nm_setting_connection_get_master (s_con)) {
+ /* Slave connections don't have IP configuration. */
+
+ if (s_ip4)
+ nm_connection_remove_setting (self, NM_TYPE_SETTING_IP4_CONFIG);
+
+ if (s_ip6)
+ nm_connection_remove_setting (self, NM_TYPE_SETTING_IP6_CONFIG);
+
+ return s_ip4 || s_ip6;
+ } else {
+ /* Ensure all non-slave connections have IP4 and IP6 settings objects. If no
+ * IP6 setting was specified, then assume that means IP6 config is allowed
+ * to fail. But if no IP4 setting was specified, assume the caller was just
+ * being lazy.
+ */
+ if (!s_ip4) {
+ setting = nm_setting_ip4_config_new ();
+
+ g_object_set (setting,
+ NM_SETTING_IP4_CONFIG_METHOD, default_ip4_method,
+ NULL);
+ nm_connection_add_setting (self, setting);
+ }
+ if (!s_ip6) {
+ setting = nm_setting_ip6_config_new ();
+
+ g_object_set (setting,
+ NM_SETTING_IP6_CONFIG_METHOD, default_ip6_method,
+ NM_SETTING_IP6_CONFIG_MAY_FAIL, TRUE,
+ NULL);
+ nm_connection_add_setting (self, setting);
+ }
+ return !s_ip4 || !s_ip6;
+ }
+}
+
+/**
+ * nm_connection_verify:
+ * @connection: the #NMConnection to verify
+ * @error: location to store error, or %NULL
+ *
+ * Validates the connection and all its settings. Each setting's properties
+ * have allowed values, and some values are dependent on other values. For
+ * example, if a Wi-Fi connection is security enabled, the #NMSettingWireless
+ * setting object's 'security' property must contain the setting name of the
+ * #NMSettingWirelessSecurity object, which must also be present in the
+ * connection for the connection to be valid. As another example, the
+ * #NMSettingWired object's 'mac-address' property must be a validly formatted
+ * MAC address. The returned #GError contains information about which
+ * setting and which property failed validation, and how it failed validation.
+ *
+ * Returns: %TRUE if the connection is valid, %FALSE if it is not
+ **/
+gboolean
+nm_connection_verify (NMConnection *connection, GError **error)
+{
+ NMSettingVerifyResult result;
+
+ result = _nm_connection_verify (connection, error);
+
+ /* we treat normalizable connections as valid. */
+ if (result == NM_SETTING_VERIFY_NORMALIZABLE)
+ g_clear_error (error);
+
+ return result == NM_SETTING_VERIFY_SUCCESS || result == NM_SETTING_VERIFY_NORMALIZABLE;
+}
+
+static NMSettingVerifyResult
+_nm_connection_verify (NMConnection *connection, GError **error)
+{
+ NMConnectionPrivate *priv;
+ NMSettingConnection *s_con;
+ NMSettingIP4Config *s_ip4;
+ NMSettingIP6Config *s_ip6;
+ GHashTableIter iter;
+ gpointer value;
+ GSList *all_settings = NULL, *setting_i;
+ NMSettingVerifyResult success = NM_SETTING_VERIFY_ERROR;
+ NMSetting *base;
+ const char *ctype;
+ GError *normalizable_error = NULL;
+ NMSettingVerifyResult normalizable_error_type = NM_SETTING_VERIFY_SUCCESS;
+
+ if (error)
+ g_return_val_if_fail (*error == NULL, NM_SETTING_VERIFY_ERROR);
+
+ if (!NM_IS_CONNECTION (connection)) {
+ g_set_error_literal (error,
+ NM_SETTING_CONNECTION_ERROR,
+ NM_SETTING_CONNECTION_ERROR_UNKNOWN,
+ "invalid connection; failed verification");
+ g_return_val_if_fail (NM_IS_CONNECTION (connection), NM_SETTING_VERIFY_ERROR);
+ }
+
+ priv = NM_CONNECTION_GET_PRIVATE (connection);
+
+ /* First, make sure there's at least 'connection' setting */
+ s_con = nm_connection_get_setting_connection (connection);
+ if (!s_con) {
+ g_set_error_literal (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_CONNECTION_SETTING_NOT_FOUND,
+ "connection setting not found");
+ goto EXIT;
+ }
+
+ /* Build up the list of settings */
+ g_hash_table_iter_init (&iter, priv->settings);
+ while (g_hash_table_iter_next (&iter, NULL, &value)) {
+ /* Order NMSettingConnection so that it will be verified first.
+ * The reason is, that NMSettingConnection:verify() modifies the connection
+ * by setting NMSettingConnection:interface_name. So we want to call that
+ * verify() first, because the order can affect the outcome.
+ * Another reason is, that errors in this setting might be more fundamental
+ * and should be checked and reported with higher priority.
+ * Another reason is, that some settings look especially at the
+ * NMSettingConnection, so they find it first in the all_settings list. */
+ if (value == s_con)
+ all_settings = g_slist_append (all_settings, value);
+ else
+ all_settings = g_slist_prepend (all_settings, value);
+ }
+ all_settings = g_slist_reverse (all_settings);
+
+ /* Now, run the verify function of each setting */
+ for (setting_i = all_settings; setting_i; setting_i = setting_i->next) {
+ GError *verify_error = NULL;
+ NMSettingVerifyResult verify_result;
+
+ /* verify all settings. We stop if we find the first non-normalizable
+ * @NM_SETTING_VERIFY_ERROR. If we find normalizable errors we continue
+ * but remember the error to return it to the user.
+ * @NM_SETTING_VERIFY_NORMALIZABLE_ERROR has a higher priority then
+ * @NM_SETTING_VERIFY_NORMALIZABLE, so, if we encounter such an error type,
+ * we remember it instead (to return it as output).
+ **/
+ verify_result = _nm_setting_verify (NM_SETTING (setting_i->data), all_settings, &verify_error);
+ if (verify_result == NM_SETTING_VERIFY_NORMALIZABLE ||
+ verify_result == NM_SETTING_VERIFY_NORMALIZABLE_ERROR) {
+ if ( verify_result == NM_SETTING_VERIFY_NORMALIZABLE_ERROR
+ && normalizable_error_type == NM_SETTING_VERIFY_NORMALIZABLE) {
+ /* NORMALIZABLE_ERROR has higher priority. */
+ g_clear_error (&normalizable_error);
+ }
+ if (!normalizable_error) {
+ g_propagate_error (&normalizable_error, verify_error);
+ verify_error = NULL;
+ normalizable_error_type = verify_result;
+ }
+ } else if (verify_result != NM_SETTING_VERIFY_SUCCESS) {
+ g_propagate_error (error, verify_error);
+ g_slist_free (all_settings);
+ g_return_val_if_fail (verify_result == NM_SETTING_VERIFY_ERROR, success);
+ goto EXIT;
+ }
+ g_clear_error (&verify_error);
+ }
+ g_slist_free (all_settings);
+
+ /* Now make sure the given 'type' setting can actually be the base setting
+ * of the connection. Can't have type=ppp for example.
+ */
+ ctype = nm_setting_connection_get_connection_type (s_con);
+ if (!ctype) {
+ g_set_error_literal (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_CONNECTION_TYPE_INVALID,
+ "connection type missing");
+ goto EXIT;
+ }
+
+ base = nm_connection_get_setting_by_name (connection, ctype);
+ if (!base) {
+ g_set_error_literal (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_CONNECTION_TYPE_INVALID,
+ "base setting GType not found");
+ goto EXIT;
+ }
+
+ if (!_nm_setting_is_base_type (base)) {
+ g_set_error (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_CONNECTION_TYPE_INVALID,
+ "connection type '%s' is not a base type",
+ ctype);
+ goto EXIT;
+ }
+
+ s_ip4 = nm_connection_get_setting_ip4_config (connection);
+ s_ip6 = nm_connection_get_setting_ip6_config (connection);
+
+ if (nm_setting_connection_get_master (s_con)) {
+ if ((normalizable_error_type == NM_SETTING_VERIFY_SUCCESS ||
+ (normalizable_error_type == NM_SETTING_VERIFY_NORMALIZABLE)) && (s_ip4 || s_ip6)) {
+ g_clear_error (&normalizable_error);
+ g_set_error (&normalizable_error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_SETTING,
+ "slave connection cannot have an IP%c setting",
+ s_ip4 ? '4' : '6');
+ /* having a slave with IP config *was* and is a verify() error. */
+ normalizable_error_type = NM_SETTING_VERIFY_NORMALIZABLE_ERROR;
+ }
+ } else {
+ if (normalizable_error_type == NM_SETTING_VERIFY_SUCCESS && (!s_ip4 || !s_ip6)) {
+ g_set_error (&normalizable_error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_SETTING_NOT_FOUND,
+ "connection needs an IP%c setting",
+ !s_ip4 ? '4' : '6');
+ /* having a master without IP config was not a verify() error, accept
+ * it for backward compatibility. */
+ normalizable_error_type = NM_SETTING_VERIFY_NORMALIZABLE;
+ }
+ }
+
+ if (normalizable_error_type != NM_SETTING_VERIFY_SUCCESS) {
+ g_propagate_error (error, normalizable_error);
+ normalizable_error = NULL;
+ success = normalizable_error_type;
+ } else
+ success = NM_SETTING_VERIFY_SUCCESS;
+
+EXIT:
+ g_clear_error (&normalizable_error);
+ return success;
+}
+
+/**
+ * nm_connection_normalize:
+ * @connection: the #NMConnection to normalize
+ * @parameters: (allow-none) (element-type utf8 gpointer): a #GHashTable with
+ * normalization parameters to allow customization of the normalization by providing
+ * specific arguments. Unknown arguments will be ignored and the default will be
+ * used. The keys must be strings, hashed by g_str_hash() and g_str_equal() functions.
+ * The values are opaque and depend on the parameter name.
+ * @modified: (out) (allow-none): outputs whether any settings were modified.
+ * @error: location to store error, or %NULL. Contains the reason,
+ * why the connection is invalid, if the function returns an error.
+ *
+ * Does some basic normalization and fixup of well known inconsistencies
+ * and deprecated fields. If the connection was modified in any way,
+ * the output parameter @modified is set %TRUE.
+ *
+ * Finally the connection will be verified and %TRUE returns if the connection
+ * is valid. As this function only performs some specific normalization steps
+ * it cannot repair all connections. If the connection has errors that
+ * cannot be normalized, the connection will not be modified.
+ *
+ * Returns: %TRUE if the connection is valid, %FALSE if it is not
+ *
+ * Since: 1.0
+ **/
+gboolean
+nm_connection_normalize (NMConnection *connection,
+ GHashTable *parameters,
+ gboolean *modified,
+ GError **error)
+{
+ NMSettingVerifyResult success;
+ gboolean was_modified = FALSE;
+ GError *normalizable_error = NULL;
+
+ success = _nm_connection_verify (connection, &normalizable_error);
+
+ if (success == NM_SETTING_VERIFY_ERROR ||
+ success == NM_SETTING_VERIFY_SUCCESS) {
+ if (normalizable_error)
+ g_propagate_error (error, normalizable_error);
+ goto EXIT;
+ }
+ g_assert (success == NM_SETTING_VERIFY_NORMALIZABLE || success == NM_SETTING_VERIFY_NORMALIZABLE_ERROR);
+ g_clear_error (&normalizable_error);
+
+ /* Try to perform all kind of normalizations on the settings to fix it.
+ * We only do this, after verifying that the connection contains no un-normalizable
+ * errors, because in that case we rather fail without touching the settings. */
+
+ was_modified |= _normalize_virtual_iface_name (connection);
+ was_modified |= _normalize_ip_config (connection, parameters);
+
+ /* Verify anew. */
+ success = _nm_connection_verify (connection, error);
+
+ /* we would expect, that after normalization, the connection can be verified. */
+ g_return_val_if_fail (success == NM_SETTING_VERIFY_SUCCESS, success);
+
+ /* we would expect, that the connection was modified during normalization. */
+ g_return_val_if_fail (was_modified, success);
+
+EXIT:
+ if (modified)
+ *modified = was_modified;
+
+ return success == NM_SETTING_VERIFY_SUCCESS;
+}
+
+/**
+ * nm_connection_update_secrets:
+ * @connection: the #NMConnection
+ * @setting_name: the setting object name to which the secrets apply
+ * @secrets: (element-type utf8 GObject.Value): a #GHashTable mapping
+ * string:#GValue of setting property names and secrets of the given @setting_name
+ * @error: location to store error, or %NULL
+ *
+ * Update the specified setting's secrets, given a hash table of secrets
+ * intended for that setting (deserialized from D-Bus for example). Will also
+ * extract the given setting's secrets hash if given a hash of hashes, as would
+ * be returned from nm_connection_to_hash(). If @setting_name is %NULL, expects
+ * a fully serialized #NMConnection as returned by nm_connection_to_hash() and
+ * will update all secrets from all settings contained in @secrets.
+ *
+ * Returns: %TRUE if the secrets were successfully updated, %FALSE if the update
+ * failed (tried to update secrets for a setting that doesn't exist, etc)
+ **/
+gboolean
+nm_connection_update_secrets (NMConnection *connection,
+ const char *setting_name,
+ GHashTable *secrets,
+ GError **error)
+{
+ NMSetting *setting;
+ gboolean success = TRUE, updated = FALSE;
+ GHashTable *setting_hash = NULL;
+ GHashTableIter iter;
+ const char *key;
+ gboolean hashed_connection = FALSE;
+ int success_detail;
+
+ g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE);
+ g_return_val_if_fail (secrets != NULL, FALSE);
+ if (error)
+ g_return_val_if_fail (*error == NULL, FALSE);
+
+ /* Empty @secrets means success */
+ if (g_hash_table_size (secrets) == 0)
+ return TRUE;
+
+ /* For backwards compatibility, this function accepts either a hashed
+ * connection (GHashTable of GHashTables of GValues) or a single hashed
+ * setting (GHashTable of GValues).
+ */
+ g_hash_table_iter_init (&iter, secrets);
+ while (g_hash_table_iter_next (&iter, (gpointer) &key, NULL)) {
+ if (_nm_setting_lookup_setting_type (key) != G_TYPE_INVALID) {
+ /* @secrets looks like a hashed connection */
+ hashed_connection = TRUE;
+ break;
+ }
+ }
+
+ if (setting_name) {
+ /* Update just one setting's secrets */
+ setting = nm_connection_get_setting_by_name (connection, setting_name);
+ if (!setting) {
+ g_set_error_literal (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_SETTING_NOT_FOUND,
+ setting_name);
+ return FALSE;
+ }
+
+ if (hashed_connection) {
+ setting_hash = g_hash_table_lookup (secrets, setting_name);
+ if (!setting_hash) {
+ /* The hashed connection that didn't contain any secrets for
+ * @setting_name; just return success.
+ */
+ return TRUE;
+ }
+ }
+
+ g_signal_handlers_block_by_func (setting, (GCallback) setting_changed_cb, connection);
+ success_detail = _nm_setting_update_secrets (setting,
+ setting_hash ? setting_hash : secrets,
+ error);
+ g_signal_handlers_unblock_by_func (setting, (GCallback) setting_changed_cb, connection);
+
+ if (success_detail == NM_SETTING_UPDATE_SECRET_ERROR)
+ return FALSE;
+ if (success_detail == NM_SETTING_UPDATE_SECRET_SUCCESS_MODIFIED)
+ updated = TRUE;
+ } else {
+ if (!hashed_connection) {
+ g_set_error_literal (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_SETTING_NOT_FOUND,
+ key);
+ return FALSE;
+ }
+
+ /* check first, whether all the settings exist... */
+ g_hash_table_iter_init (&iter, secrets);
+ while (g_hash_table_iter_next (&iter, (gpointer) &key, NULL)) {
+ setting = nm_connection_get_setting_by_name (connection, key);
+ if (!setting) {
+ g_set_error_literal (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_SETTING_NOT_FOUND,
+ key);
+ return FALSE;
+ }
+ }
+
+ /* Update each setting with any secrets from the hashed connection */
+ g_hash_table_iter_init (&iter, secrets);
+ while (g_hash_table_iter_next (&iter, (gpointer) &key, (gpointer) &setting_hash)) {
+ /* Update the secrets for this setting */
+ setting = nm_connection_get_setting_by_name (connection, key);
+
+ g_signal_handlers_block_by_func (setting, (GCallback) setting_changed_cb, connection);
+ success_detail = _nm_setting_update_secrets (setting, setting_hash, error);
+ g_signal_handlers_unblock_by_func (setting, (GCallback) setting_changed_cb, connection);
+
+ if (success_detail == NM_SETTING_UPDATE_SECRET_ERROR) {
+ success = FALSE;
+ break;
+ }
+ if (success_detail == NM_SETTING_UPDATE_SECRET_SUCCESS_MODIFIED)
+ updated = TRUE;
+ }
+ }
+
+ if (updated) {
+ g_signal_emit (connection, signals[SECRETS_UPDATED], 0, setting_name);
+ g_signal_emit (connection, signals[CHANGED], 0);
+ }
+
+ return success;
+}
+
+/**
+ * nm_connection_need_secrets:
+ * @connection: the #NMConnection
+ * @hints: (out) (element-type utf8) (allow-none) (transfer container):
+ * the address of a pointer to a #GPtrArray, initialized to %NULL, which on
+ * return points to an allocated #GPtrArray containing the property names of
+ * secrets of the #NMSetting which may be required; the caller owns the array
+ * and must free the array itself with g_ptr_array_free(), but not free its
+ * elements
+ *
+ * Returns the name of the first setting object in the connection which would
+ * need secrets to make a successful connection. The returned hints are only
+ * intended as a guide to what secrets may be required, because in some
+ * circumstances, there is no way to conclusively determine exactly which
+ * secrets are needed.
+ *
+ * Returns: the setting name of the #NMSetting object which has invalid or
+ * missing secrets
+ **/
+const char *
+nm_connection_need_secrets (NMConnection *connection,
+ GPtrArray **hints)
+{
+ NMConnectionPrivate *priv;
+ GHashTableIter hiter;
+ GSList *settings = NULL;
+ GSList *iter;
+ const char *name = NULL;
+ NMSetting *setting;
+
+ g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+ if (hints)
+ g_return_val_if_fail (*hints == NULL, NULL);
+
+ priv = NM_CONNECTION_GET_PRIVATE (connection);
+
+ /* Get list of settings in priority order */
+ g_hash_table_iter_init (&hiter, priv->settings);
+ while (g_hash_table_iter_next (&hiter, NULL, (gpointer) &setting))
+ settings = g_slist_insert_sorted (settings, setting, _nm_setting_compare_priority);
+
+ for (iter = settings; iter; iter = g_slist_next (iter)) {
+ GPtrArray *secrets;
+
+ setting = NM_SETTING (iter->data);
+ secrets = nm_setting_need_secrets (setting);
+ if (secrets) {
+ if (hints)
+ *hints = secrets;
+ else
+ g_ptr_array_free (secrets, TRUE);
+
+ name = nm_setting_get_name (setting);
+ break;
+ }
+ }
+
+ g_slist_free (settings);
+ return name;
+}
+
+/**
+ * nm_connection_clear_secrets:
+ * @connection: the #NMConnection
+ *
+ * Clears and frees any secrets that may be stored in the connection, to avoid
+ * keeping secret data in memory when not needed.
+ **/
+void
+nm_connection_clear_secrets (NMConnection *connection)
+{
+ GHashTableIter iter;
+ NMSetting *setting;
+ gboolean changed = FALSE;
+
+ g_return_if_fail (NM_IS_CONNECTION (connection));
+
+ g_hash_table_iter_init (&iter, NM_CONNECTION_GET_PRIVATE (connection)->settings);
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer) &setting)) {
+ g_signal_handlers_block_by_func (setting, (GCallback) setting_changed_cb, connection);
+ changed |= _nm_setting_clear_secrets (setting);
+ g_signal_handlers_unblock_by_func (setting, (GCallback) setting_changed_cb, connection);
+ }
+
+ g_signal_emit (connection, signals[SECRETS_CLEARED], 0);
+ if (changed)
+ g_signal_emit (connection, signals[CHANGED], 0);
+}
+
+/**
+ * nm_connection_clear_secrets_with_flags:
+ * @connection: the #NMConnection
+ * @func: (scope call): function to be called to determine whether a
+ * specific secret should be cleared or not
+ * @user_data: caller-supplied data passed to @func
+ *
+ * Clears and frees secrets determined by @func.
+ **/
+void
+nm_connection_clear_secrets_with_flags (NMConnection *connection,
+ NMSettingClearSecretsWithFlagsFn func,
+ gpointer user_data)
+{
+ GHashTableIter iter;
+ NMSetting *setting;
+ gboolean changed = FALSE;
+
+ g_return_if_fail (NM_IS_CONNECTION (connection));
+
+ g_hash_table_iter_init (&iter, NM_CONNECTION_GET_PRIVATE (connection)->settings);
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer) &setting)) {
+ g_signal_handlers_block_by_func (setting, (GCallback) setting_changed_cb, connection);
+ changed |= _nm_setting_clear_secrets_with_flags (setting, func, user_data);
+ g_signal_handlers_unblock_by_func (setting, (GCallback) setting_changed_cb, connection);
+ }
+
+ g_signal_emit (connection, signals[SECRETS_CLEARED], 0);
+ if (changed)
+ g_signal_emit (connection, signals[CHANGED], 0);
+}
+
+/**
+ * nm_connection_to_hash:
+ * @connection: the #NMConnection
+ * @flags: hash flags, e.g. %NM_SETTING_HASH_FLAG_ALL
+ *
+ * Converts the #NMConnection into a #GHashTable describing the connection,
+ * suitable for marshalling over D-Bus or serializing. The hash table mapping
+ * is string:#GHashTable with each element in the returned hash representing
+ * a #NMSetting object. The keys are setting object names, and the values
+ * are #GHashTables mapping string:GValue, each of which represents the
+ * properties of the #NMSetting object.
+ *
+ * Returns: (transfer full) (element-type utf8 GLib.HashTable): a new
+ * #GHashTable describing the connection, its settings, and each setting's
+ * properties. The caller owns the hash table and must unref the hash table
+ * with g_hash_table_unref() when it is no longer needed.
+ **/
+GHashTable *
+nm_connection_to_hash (NMConnection *connection, NMSettingHashFlags flags)
+{
+ NMConnectionPrivate *priv;
+ GHashTableIter iter;
+ gpointer key, data;
+ GHashTable *ret, *setting_hash;
+
+ g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+
+ ret = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, (GDestroyNotify) g_hash_table_destroy);
+
+ priv = NM_CONNECTION_GET_PRIVATE (connection);
+
+ /* Add each setting's hash to the main hash */
+ g_hash_table_iter_init (&iter, priv->settings);
+ while (g_hash_table_iter_next (&iter, &key, &data)) {
+ NMSetting *setting = NM_SETTING (data);
+
+ setting_hash = nm_setting_to_hash (setting, flags);
+ if (setting_hash)
+ g_hash_table_insert (ret, g_strdup (nm_setting_get_name (setting)), setting_hash);
+ }
+
+ /* Don't send empty hashes */
+ if (g_hash_table_size (ret) < 1) {
+ g_hash_table_destroy (ret);
+ ret = NULL;
+ }
+
+ return ret;
+}
+
+/**
+ * nm_connection_is_type:
+ * @connection: the #NMConnection
+ * @type: a setting name to check the connection's type against (like
+ * %NM_SETTING_WIRELESS_SETTING_NAME or %NM_SETTING_WIRED_SETTING_NAME)
+ *
+ * A convenience function to check if the given @connection is a particular
+ * type (ie wired, Wi-Fi, ppp, etc). Checks the #NMSettingConnection:type
+ * property of the connection and matches that against @type.
+ *
+ * Returns: %TRUE if the connection is of the given @type, %FALSE if not
+ **/
+gboolean
+nm_connection_is_type (NMConnection *connection, const char *type)
+{
+ NMSettingConnection *s_con;
+ const char *type2;
+
+ g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE);
+ g_return_val_if_fail (type != NULL, FALSE);
+
+ s_con = nm_connection_get_setting_connection (connection);
+ if (!s_con)
+ return FALSE;
+
+ type2 = nm_setting_connection_get_connection_type (s_con);
+
+ return (g_strcmp0 (type2, type) == 0);
+}
+
+/**
+ * nm_connection_for_each_setting_value:
+ * @connection: the #NMConnection
+ * @func: (scope call): user-supplied function called for each setting's property
+ * @user_data: user data passed to @func at each invocation
+ *
+ * Iterates over the properties of each #NMSetting object in the #NMConnection,
+ * calling the supplied user function for each property.
+ **/
+void
+nm_connection_for_each_setting_value (NMConnection *connection,
+ NMSettingValueIterFn func,
+ gpointer user_data)
+{
+ GHashTableIter iter;
+ gpointer value;
+
+ g_return_if_fail (NM_IS_CONNECTION (connection));
+ g_return_if_fail (func != NULL);
+
+ g_hash_table_iter_init (&iter, NM_CONNECTION_GET_PRIVATE (connection)->settings);
+ while (g_hash_table_iter_next (&iter, NULL, &value))
+ nm_setting_enumerate_values (NM_SETTING (value), func, user_data);
+}
+
+/**
+ * nm_connection_dump:
+ * @connection: the #NMConnection
+ *
+ * Print the connection to stdout. For debugging purposes ONLY, should NOT
+ * be used for serialization of the connection or machine-parsed in any way. The
+ * output format is not guaranteed to be stable and may change at any time.
+ **/
+void
+nm_connection_dump (NMConnection *connection)
+{
+ GHashTableIter iter;
+ NMSetting *setting;
+ const char *setting_name;
+ char *str;
+
+ if (!connection)
+ return;
+
+ g_hash_table_iter_init (&iter, NM_CONNECTION_GET_PRIVATE (connection)->settings);
+ while (g_hash_table_iter_next (&iter, (gpointer) &setting_name, (gpointer) &setting)) {
+ str = nm_setting_to_string (setting);
+ g_print ("%s\n", str);
+ g_free (str);
+ }
+}
+
+/**
+ * nm_connection_set_path:
+ * @connection: the #NMConnection
+ * @path: the D-Bus path of the connection as given by the settings service
+ * which provides the connection
+ *
+ * Sets the D-Bus path of the connection. This property is not serialized, and
+ * is only for the reference of the caller. Sets the #NMConnection:path
+ * property.
+ **/
+void
+nm_connection_set_path (NMConnection *connection, const char *path)
+{
+ NMConnectionPrivate *priv;
+
+ g_return_if_fail (NM_IS_CONNECTION (connection));
+
+ priv = NM_CONNECTION_GET_PRIVATE (connection);
+
+ g_free (priv->path);
+ priv->path = NULL;
+
+ if (path)
+ priv->path = g_strdup (path);
+}
+
+/**
+ * nm_connection_get_path:
+ * @connection: the #NMConnection
+ *
+ * Returns the connection's D-Bus path.
+ *
+ * Returns: the D-Bus path of the connection, previously set by a call to
+ * nm_connection_set_path().
+ **/
+const char *
+nm_connection_get_path (NMConnection *connection)
+{
+ g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+
+ return NM_CONNECTION_GET_PRIVATE (connection)->path;
+}
+
+/**
+ * nm_connection_get_interface_name:
+ * @connection: The #NMConnection
+ *
+ * Returns the interface name as stored in NMSettingConnection:interface_name.
+ * If the connection contains no NMSettingConnection, it will return %NULL.
+ *
+ * For hardware devices and software devices created outside of NetworkManager,
+ * this name is used to match the device. for software devices created by
+ * NetworkManager, this is the name of the created interface.
+ *
+ * Returns: Name of the kernel interface or %NULL
+ *
+ * Since: 1.0
+ */
+const char *
+nm_connection_get_interface_name (NMConnection *connection)
+{
+ NMSettingConnection *s_con;
+
+ g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+
+ s_con = nm_connection_get_setting_connection (connection);
+
+ return s_con ? nm_setting_connection_get_interface_name (s_con) : NULL;
+}
+
+/**
+ * nm_connection_get_virtual_iface_name:
+ * @connection: The #NMConnection
+ *
+ * Returns the name of the virtual kernel interface which the connection
+ * needs to use if specified in the settings. This function abstracts all
+ * connection types which require this functionality. For all other
+ * connection types, this function will return %NULL.
+ *
+ * Returns: Name of the kernel interface or %NULL
+ */
+const char *
+nm_connection_get_virtual_iface_name (NMConnection *connection)
+{
+ NMSetting *base;
+
+ g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+
+ base = _get_type_setting (connection);
+ g_assert (base);
+
+ return nm_setting_get_virtual_iface_name (base);
+}
+
+/**
+ * nm_connection_new:
+ *
+ * Creates a new #NMConnection object with no #NMSetting objects.
+ *
+ * Returns: the new empty #NMConnection object
+ **/
+NMConnection *
+nm_connection_new (void)
+{
+ return (NMConnection *) g_object_new (NM_TYPE_CONNECTION, NULL);
+}
+
+/**
+ * nm_connection_new_from_hash:
+ * @hash: (element-type utf8 GLib.HashTable): the #GHashTable describing
+ * the connection
+ * @error: on unsuccessful return, an error
+ *
+ * Creates a new #NMConnection from a hash table describing the connection. See
+ * nm_connection_to_hash() for a description of the expected hash table.
+ *
+ * Returns: the new #NMConnection object, populated with settings created
+ * from the values in the hash table, or %NULL if the connection failed to
+ * validate
+ **/
+NMConnection *
+nm_connection_new_from_hash (GHashTable *hash, GError **error)
+{
+ NMConnection *connection;
+
+ g_return_val_if_fail (hash != NULL, NULL);
+
+ if (!validate_permissions_type (hash, error))
+ return NULL;
+
+ connection = nm_connection_new ();
+ if (!hash_to_connection (connection, hash, error)) {
+ g_object_unref (connection);
+ return NULL;
+ }
+ return connection;
+}
+
+/**
+ * nm_connection_duplicate:
+ * @connection: the #NMConnection to duplicate
+ *
+ * Duplicates a #NMConnection.
+ *
+ * Returns: (transfer full): a new #NMConnection containing the same settings and properties
+ * as the source #NMConnection
+ **/
+NMConnection *
+nm_connection_duplicate (NMConnection *connection)
+{
+ NMConnection *dup;
+ GHashTableIter iter;
+ NMSetting *setting;
+
+ g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+
+ dup = nm_connection_new ();
+ nm_connection_set_path (dup, nm_connection_get_path (connection));
+
+ g_hash_table_iter_init (&iter, NM_CONNECTION_GET_PRIVATE (connection)->settings);
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer) &setting))
+ nm_connection_add_setting (dup, nm_setting_duplicate (setting));
+
+ return dup;
+}
+
+/**
+ * nm_connection_get_uuid:
+ * @connection: the #NMConnection
+ *
+ * A shortcut to return the UUID from the connection's #NMSettingConnection.
+ *
+ * Returns: the UUID from the connection's 'connection' setting
+ **/
+const char *
+nm_connection_get_uuid (NMConnection *connection)
+{
+ NMSettingConnection *s_con;
+
+ g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+
+ s_con = nm_connection_get_setting_connection (connection);
+ g_return_val_if_fail (s_con != NULL, NULL);
+
+ return nm_setting_connection_get_uuid (s_con);
+}
+
+/**
+ * nm_connection_get_id:
+ * @connection: the #NMConnection
+ *
+ * A shortcut to return the ID from the connection's #NMSettingConnection.
+ *
+ * Returns: the ID from the connection's 'connection' setting
+ **/
+const char *
+nm_connection_get_id (NMConnection *connection)
+{
+ NMSettingConnection *s_con;
+
+ g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+
+ s_con = nm_connection_get_setting_connection (connection);
+ g_return_val_if_fail (s_con != NULL, NULL);
+
+ return nm_setting_connection_get_id (s_con);
+}
+
+/**
+ * nm_connection_get_connection_type:
+ * @connection: the #NMConnection
+ *
+ * A shortcut to return the type from the connection's #NMSettingConnection.
+ *
+ * Returns: the type from the connection's 'connection' setting
+ *
+ * Since: 0.9.10
+ **/
+const char *
+nm_connection_get_connection_type (NMConnection *connection)
+{
+ NMSettingConnection *s_con;
+
+ g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+
+ s_con = nm_connection_get_setting_connection (connection);
+ g_return_val_if_fail (s_con != NULL, NULL);
+
+ return nm_setting_connection_get_connection_type (s_con);
+}
+
+/**
+ * nm_connection_get_virtual_device_description:
+ * @connection: an #NMConnection for a virtual device type
+ *
+ * Returns the name that nm_device_disambiguate_names() would
+ * return for the virtual device that would be created for @connection.
+ * Eg, "VLAN (eth1.1)".
+ *
+ * Returns: (transfer full): the name of @connection's device,
+ * or %NULL if @connection is not a virtual connection type
+ *
+ * Since: 0.9.10
+ */
+char *
+nm_connection_get_virtual_device_description (NMConnection *connection)
+{
+ const char *iface, *type, *display_type;
+ NMSettingConnection *s_con;
+
+ iface = nm_connection_get_virtual_iface_name (connection);
+ if (!iface)
+ return NULL;
+
+ s_con = nm_connection_get_setting_connection (connection);
+ g_return_val_if_fail (s_con != NULL, NULL);
+ type = nm_setting_connection_get_connection_type (s_con);
+
+ if (!strcmp (type, NM_SETTING_BOND_SETTING_NAME))
+ display_type = _("Bond");
+ else if (!strcmp (type, NM_SETTING_TEAM_SETTING_NAME))
+ display_type = _("Team");
+ else if (!strcmp (type, NM_SETTING_BRIDGE_SETTING_NAME))
+ display_type = _("Bridge");
+ else if (!strcmp (type, NM_SETTING_VLAN_SETTING_NAME))
+ display_type = _("VLAN");
+ else {
+ g_warning ("Unrecognized virtual device type '%s'", type);
+ display_type = type;
+ }
+
+ return g_strdup_printf ("%s (%s)", display_type, iface);
+}
+
+/*************************************************************/
+
+/**
+ * nm_connection_get_setting_802_1x:
+ * @connection: the #NMConnection
+ *
+ * A shortcut to return any #NMSetting8021x the connection might contain.
+ *
+ * Returns: (transfer none): an #NMSetting8021x if the connection contains one, otherwise %NULL
+ **/
+NMSetting8021x *
+nm_connection_get_setting_802_1x (NMConnection *connection)
+{
+ g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+
+ return (NMSetting8021x *) nm_connection_get_setting (connection, NM_TYPE_SETTING_802_1X);
+}
+
+/**
+ * nm_connection_get_setting_bluetooth:
+ * @connection: the #NMConnection
+ *
+ * A shortcut to return any #NMSettingBluetooth the connection might contain.
+ *
+ * Returns: (transfer none): an #NMSettingBluetooth if the connection contains one, otherwise %NULL
+ **/
+NMSettingBluetooth *
+nm_connection_get_setting_bluetooth (NMConnection *connection)
+{
+ g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+
+ return (NMSettingBluetooth *) nm_connection_get_setting (connection, NM_TYPE_SETTING_BLUETOOTH);
+}
+
+/**
+ * nm_connection_get_setting_bond:
+ * @connection: the #NMConnection
+ *
+ * A shortcut to return any #NMSettingBond the connection might contain.
+ *
+ * Returns: (transfer none): an #NMSettingBond if the connection contains one, otherwise %NULL
+ **/
+NMSettingBond *
+nm_connection_get_setting_bond (NMConnection *connection)
+{
+ g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+
+ return (NMSettingBond *) nm_connection_get_setting (connection, NM_TYPE_SETTING_BOND);
+}
+
+/**
+ * nm_connection_get_setting_team:
+ * @connection: the #NMConnection
+ *
+ * A shortcut to return any #NMSettingTeam the connection might contain.
+ *
+ * Returns: (transfer none): an #NMSettingTeam if the connection contains one, otherwise %NULL
+ *
+ * Since: 0.9.10
+ **/
+NMSettingTeam *
+nm_connection_get_setting_team (NMConnection *connection)
+{
+ g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+
+ return (NMSettingTeam *) nm_connection_get_setting (connection, NM_TYPE_SETTING_TEAM);
+}
+
+/**
+ * nm_connection_get_setting_team_port:
+ * @connection: the #NMConnection
+ *
+ * A shortcut to return any #NMSettingTeamPort the connection might contain.
+ *
+ * Returns: (transfer none): an #NMSettingTeamPort if the connection contains one, otherwise %NULL
+ *
+ * Since: 0.9.10
+ **/
+NMSettingTeamPort *
+nm_connection_get_setting_team_port (NMConnection *connection)
+{
+ g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+
+ return (NMSettingTeamPort *) nm_connection_get_setting (connection, NM_TYPE_SETTING_TEAM_PORT);
+}
+
+/**
+ * nm_connection_get_setting_bridge:
+ * @connection: the #NMConnection
+ *
+ * A shortcut to return any #NMSettingBridge the connection might contain.
+ *
+ * Returns: (transfer none): an #NMSettingBridge if the connection contains one, otherwise %NULL
+ **/
+NMSettingBridge *
+nm_connection_get_setting_bridge (NMConnection *connection)
+{
+ g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+
+ return (NMSettingBridge *) nm_connection_get_setting (connection, NM_TYPE_SETTING_BRIDGE);
+}
+
+/**
+ * nm_connection_get_setting_cdma:
+ * @connection: the #NMConnection
+ *
+ * A shortcut to return any #NMSettingCdma the connection might contain.
+ *
+ * Returns: (transfer none): an #NMSettingCdma if the connection contains one, otherwise %NULL
+ **/
+NMSettingCdma *
+nm_connection_get_setting_cdma (NMConnection *connection)
+{
+ g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+
+ return (NMSettingCdma *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CDMA);
+}
+
+/**
+ * nm_connection_get_setting_connection:
+ * @connection: the #NMConnection
+ *
+ * A shortcut to return any #NMSettingConnection the connection might contain.
+ *
+ * Returns: (transfer none): an #NMSettingConnection if the connection contains one, otherwise %NULL
+ **/
+NMSettingConnection *
+nm_connection_get_setting_connection (NMConnection *connection)
+{
+ g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+
+ return (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION);
+}
+
+/**
+ * nm_connection_get_setting_dcb:
+ * @connection: the #NMConnection
+ *
+ * A shortcut to return any #NMSettingDcb the connection might contain.
+ *
+ * Returns: (transfer none): an #NMSettingDcb if the connection contains one, otherwise NULL
+ *
+ * Since: 0.9.10
+ **/
+NMSettingDcb *
+nm_connection_get_setting_dcb (NMConnection *connection)
+{
+ g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+
+ return (NMSettingDcb *) nm_connection_get_setting (connection, NM_TYPE_SETTING_DCB);
+}
+
+/**
+ * nm_connection_get_setting_generic:
+ * @connection: the #NMConnection
+ *
+ * A shortcut to return any #NMSettingGeneric the connection might contain.
+ *
+ * Returns: (transfer none): an #NMSettingGeneric if the connection contains one, otherwise NULL
+ *
+ * Since: 0.9.10
+ **/
+NMSettingGeneric *
+nm_connection_get_setting_generic (NMConnection *connection)
+{
+ g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+
+ return (NMSettingGeneric *) nm_connection_get_setting (connection, NM_TYPE_SETTING_GENERIC);
+}
+
+/**
+ * nm_connection_get_setting_gsm:
+ * @connection: the #NMConnection
+ *
+ * A shortcut to return any #NMSettingGsm the connection might contain.
+ *
+ * Returns: (transfer none): an #NMSettingGsm if the connection contains one, otherwise %NULL
+ **/
+NMSettingGsm *
+nm_connection_get_setting_gsm (NMConnection *connection)
+{
+ g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+
+ return (NMSettingGsm *) nm_connection_get_setting (connection, NM_TYPE_SETTING_GSM);
+}
+
+/**
+ * nm_connection_get_setting_infiniband:
+ * @connection: the #NMConnection
+ *
+ * A shortcut to return any #NMSettingInfiniband the connection might contain.
+ *
+ * Returns: (transfer none): an #NMSettingInfiniband if the connection contains one, otherwise %NULL
+ **/
+NMSettingInfiniband *
+nm_connection_get_setting_infiniband (NMConnection *connection)
+{
+ g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+
+ return (NMSettingInfiniband *) nm_connection_get_setting (connection, NM_TYPE_SETTING_INFINIBAND);
+}
+
+/**
+ * nm_connection_get_setting_ip4_config:
+ * @connection: the #NMConnection
+ *
+ * A shortcut to return any #NMSettingIP4Config the connection might contain.
+ *
+ * Returns: (transfer none): an #NMSettingIP4Config if the connection contains one, otherwise %NULL
+ **/
+NMSettingIP4Config *
+nm_connection_get_setting_ip4_config (NMConnection *connection)
+{
+ g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+
+ return (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG);
+}
+
+/**
+ * nm_connection_get_setting_ip6_config:
+ * @connection: the #NMConnection
+ *
+ * A shortcut to return any #NMSettingIP6Config the connection might contain.
+ *
+ * Returns: (transfer none): an #NMSettingIP6Config if the connection contains one, otherwise %NULL
+ **/
+NMSettingIP6Config *
+nm_connection_get_setting_ip6_config (NMConnection *connection)
+{
+ g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+
+ return (NMSettingIP6Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP6_CONFIG);
+}
+
+/**
+ * nm_connection_get_setting_olpc_mesh:
+ * @connection: the #NMConnection
+ *
+ * A shortcut to return any #NMSettingOlpcMesh the connection might contain.
+ *
+ * Returns: (transfer none): an #NMSettingOlpcMesh if the connection contains one, otherwise %NULL
+ **/
+NMSettingOlpcMesh *
+nm_connection_get_setting_olpc_mesh (NMConnection *connection)
+{
+ g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+
+ return (NMSettingOlpcMesh *) nm_connection_get_setting (connection, NM_TYPE_SETTING_OLPC_MESH);
+}
+
+/**
+ * nm_connection_get_setting_ppp:
+ * @connection: the #NMConnection
+ *
+ * A shortcut to return any #NMSettingPPP the connection might contain.
+ *
+ * Returns: (transfer none): an #NMSettingPPP if the connection contains one, otherwise %NULL
+ **/
+NMSettingPPP *
+nm_connection_get_setting_ppp (NMConnection *connection)
+{
+ g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+
+ return (NMSettingPPP *) nm_connection_get_setting (connection, NM_TYPE_SETTING_PPP);
+}
+
+/**
+ * nm_connection_get_setting_pppoe:
+ * @connection: the #NMConnection
+ *
+ * A shortcut to return any #NMSettingPPPOE the connection might contain.
+ *
+ * Returns: (transfer none): an #NMSettingPPPOE if the connection contains one, otherwise %NULL
+ **/
+NMSettingPPPOE *
+nm_connection_get_setting_pppoe (NMConnection *connection)
+{
+ g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+
+ return (NMSettingPPPOE *) nm_connection_get_setting (connection, NM_TYPE_SETTING_PPPOE);
+}
+
+/**
+ * nm_connection_get_setting_serial:
+ * @connection: the #NMConnection
+ *
+ * A shortcut to return any #NMSettingSerial the connection might contain.
+ *
+ * Returns: (transfer none): an #NMSettingSerial if the connection contains one, otherwise %NULL
+ **/
+NMSettingSerial *
+nm_connection_get_setting_serial (NMConnection *connection)
+{
+ g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+
+ return (NMSettingSerial *) nm_connection_get_setting (connection, NM_TYPE_SETTING_SERIAL);
+}
+
+/**
+ * nm_connection_get_setting_vpn:
+ * @connection: the #NMConnection
+ *
+ * A shortcut to return any #NMSettingVPN the connection might contain.
+ *
+ * Returns: (transfer none): an #NMSettingVPN if the connection contains one, otherwise %NULL
+ **/
+NMSettingVPN *
+nm_connection_get_setting_vpn (NMConnection *connection)
+{
+ g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+
+ return (NMSettingVPN *) nm_connection_get_setting (connection, NM_TYPE_SETTING_VPN);
+}
+
+/**
+ * nm_connection_get_setting_wimax:
+ * @connection: the #NMConnection
+ *
+ * A shortcut to return any #NMSettingWimax the connection might contain.
+ *
+ * Returns: (transfer none): an #NMSettingWimax if the connection contains one, otherwise %NULL
+ **/
+NMSettingWimax *
+nm_connection_get_setting_wimax (NMConnection *connection)
+{
+ g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+
+ return (NMSettingWimax *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIMAX);
+}
+
+/**
+ * nm_connection_get_setting_wired:
+ * @connection: the #NMConnection
+ *
+ * A shortcut to return any #NMSettingWired the connection might contain.
+ *
+ * Returns: (transfer none): an #NMSettingWired if the connection contains one, otherwise %NULL
+ **/
+NMSettingWired *
+nm_connection_get_setting_wired (NMConnection *connection)
+{
+ g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+
+ return (NMSettingWired *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRED);
+}
+
+/**
+ * nm_connection_get_setting_adsl:
+ * @connection: the #NMConnection
+ *
+ * A shortcut to return any #NMSettingAdsl the connection might contain.
+ *
+ * Returns: (transfer none): an #NMSettingAdsl if the connection contains one, otherwise %NULL
+ **/
+NMSettingAdsl *
+nm_connection_get_setting_adsl (NMConnection *connection)
+{
+ g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+
+ return (NMSettingAdsl *) nm_connection_get_setting (connection, NM_TYPE_SETTING_ADSL);
+}
+
+/**
+ * nm_connection_get_setting_wireless:
+ * @connection: the #NMConnection
+ *
+ * A shortcut to return any #NMSettingWireless the connection might contain.
+ *
+ * Returns: (transfer none): an #NMSettingWireless if the connection contains one, otherwise %NULL
+ **/
+NMSettingWireless *
+nm_connection_get_setting_wireless (NMConnection *connection)
+{
+ g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+
+ return (NMSettingWireless *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS);
+}
+
+/**
+ * nm_connection_get_setting_wireless_security:
+ * @connection: the #NMConnection
+ *
+ * A shortcut to return any #NMSettingWirelessSecurity the connection might contain.
+ *
+ * Returns: (transfer none): an #NMSettingWirelessSecurity if the connection contains one, otherwise %NULL
+ **/
+NMSettingWirelessSecurity *
+nm_connection_get_setting_wireless_security (NMConnection *connection)
+{
+ g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+
+ return (NMSettingWirelessSecurity *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS_SECURITY);
+}
+
+/**
+ * nm_connection_get_setting_bridge_port:
+ * @connection: the #NMConnection
+ *
+ * A shortcut to return any #NMSettingBridgePort the connection might contain.
+ *
+ * Returns: (transfer none): an #NMSettingBridgePort if the connection contains one, otherwise %NULL
+ **/
+NMSettingBridgePort *
+nm_connection_get_setting_bridge_port (NMConnection *connection)
+{
+ g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+
+ return (NMSettingBridgePort *) nm_connection_get_setting (connection, NM_TYPE_SETTING_BRIDGE_PORT);
+}
+
+/**
+ * nm_connection_get_setting_vlan:
+ * @connection: the #NMConnection
+ *
+ * A shortcut to return any #NMSettingVlan the connection might contain.
+ *
+ * Returns: (transfer none): an #NMSettingVlan if the connection contains one, otherwise %NULL
+ **/
+NMSettingVlan *
+nm_connection_get_setting_vlan (NMConnection *connection)
+{
+ g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+
+ return (NMSettingVlan *) nm_connection_get_setting (connection, NM_TYPE_SETTING_VLAN);
+}
+
+/*************************************************************/
+
+static void
+nm_connection_init (NMConnection *connection)
+{
+ NMConnectionPrivate *priv = NM_CONNECTION_GET_PRIVATE (connection);
+
+ priv->settings = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref);
+}
+
+static void
+dispose (GObject *object)
+{
+ NMConnection *self = NM_CONNECTION (object);
+ NMConnectionPrivate *priv = NM_CONNECTION_GET_PRIVATE (self);
+ GHashTableIter iter;
+ NMSetting *setting;
+
+ g_hash_table_iter_init (&iter, priv->settings);
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer) &setting)) {
+ g_signal_handlers_disconnect_by_func (setting, setting_changed_cb, self);
+ g_hash_table_iter_remove (&iter);
+ }
+
+ G_OBJECT_CLASS (nm_connection_parent_class)->dispose (object);
+}
+
+static void
+finalize (GObject *object)
+{
+ NMConnection *connection = NM_CONNECTION (object);
+ NMConnectionPrivate *priv = NM_CONNECTION_GET_PRIVATE (connection);
+
+ g_hash_table_destroy (priv->settings);
+ g_free (priv->path);
+
+ G_OBJECT_CLASS (nm_connection_parent_class)->finalize (object);
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ NMConnection *connection = NM_CONNECTION (object);
+
+ switch (prop_id) {
+ case PROP_PATH:
+ nm_connection_set_path (connection, g_value_get_string (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ NMConnection *connection = NM_CONNECTION (object);
+
+ switch (prop_id) {
+ case PROP_PATH:
+ g_value_set_string (value, nm_connection_get_path (connection));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nm_connection_class_init (NMConnectionClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (NMConnectionPrivate));
+
+ /* virtual methods */
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+ object_class->dispose = dispose;
+ object_class->finalize = finalize;
+
+ /* Properties */
+
+ /**
+ * NMConnection:path:
+ *
+ * The connection's D-Bus path, used only by the calling process as a record
+ * of the D-Bus path of the connection as provided by a settings service.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PATH,
+ g_param_spec_string (NM_CONNECTION_PATH, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ /* Signals */
+
+ /**
+ * NMConnection::secrets-updated:
+ * @connection: the object on which the signal is emitted
+ * @setting_name: the setting name of the #NMSetting for which secrets were
+ * updated
+ *
+ * The ::secrets-updated signal is emitted when the secrets of a setting
+ * have been changed.
+ */
+ signals[SECRETS_UPDATED] =
+ g_signal_new (NM_CONNECTION_SECRETS_UPDATED,
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NMConnectionClass, secrets_updated),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE, 1,
+ G_TYPE_STRING);
+
+ /**
+ * NMConnection::secrets-cleared:
+ * @connection: the object on which the signal is emitted
+ *
+ * The ::secrets-cleared signal is emitted when the secrets of a connection
+ * are cleared.
+ */
+ signals[SECRETS_CLEARED] =
+ g_signal_new (NM_CONNECTION_SECRETS_CLEARED,
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0, NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ /**
+ * NMConnection::changed:
+ * @connection: the object on which the signal is emitted
+ *
+ * The ::changed signal is emitted when any property of any property
+ * (including secrets) of any setting of the connection is modified,
+ * or when settings are added or removed.
+ *
+ * Since: 0.9.10
+ */
+ signals[CHANGED] =
+ g_signal_new (NM_CONNECTION_CHANGED,
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0, NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+}
diff --git a/libnm-core/nm-connection.h b/libnm-core/nm-connection.h
new file mode 100644
index 0000000000..2cee2be560
--- /dev/null
+++ b/libnm-core/nm-connection.h
@@ -0,0 +1,256 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2013 Red Hat, Inc.
+ * Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#ifndef NM_CONNECTION_H
+#define NM_CONNECTION_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <nm-setting.h>
+
+#include <nm-setting-8021x.h>
+#include <nm-setting-bluetooth.h>
+#include <nm-setting-bond.h>
+#include <nm-setting-team.h>
+#include <nm-setting-team-port.h>
+#include <nm-setting-bridge.h>
+#include <nm-setting-bridge-port.h>
+#include <nm-setting-cdma.h>
+#include <nm-setting-connection.h>
+#include <nm-setting-dcb.h>
+#include <nm-setting-generic.h>
+#include <nm-setting-gsm.h>
+#include <nm-setting-infiniband.h>
+#include <nm-setting-ip4-config.h>
+#include <nm-setting-ip6-config.h>
+#include <nm-setting-olpc-mesh.h>
+#include <nm-setting-ppp.h>
+#include <nm-setting-pppoe.h>
+#include <nm-setting-serial.h>
+#include <nm-setting-vpn.h>
+#include <nm-setting-wimax.h>
+#include <nm-setting-wired.h>
+#include <nm-setting-adsl.h>
+#include <nm-setting-wireless.h>
+#include <nm-setting-wireless-security.h>
+#include <nm-setting-vlan.h>
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_CONNECTION (nm_connection_get_type ())
+#define NM_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_CONNECTION, NMConnection))
+#define NM_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_CONNECTION, NMConnectionClass))
+#define NM_IS_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_CONNECTION))
+#define NM_IS_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_CONNECTION))
+#define NM_CONNECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_CONNECTION, NMConnectionClass))
+
+/* Signals */
+#define NM_CONNECTION_SECRETS_UPDATED "secrets-updated"
+#define NM_CONNECTION_SECRETS_CLEARED "secrets-cleared"
+#define NM_CONNECTION_CHANGED "changed"
+
+/* Properties */
+#define NM_CONNECTION_PATH "path"
+
+/**
+ * NMConnectionError:
+ * @NM_CONNECTION_ERROR_UNKNOWN: unknown or unclassified error
+ * @NM_CONNECTION_ERROR_CONNECTION_SETTING_NOT_FOUND: the #NMConnection object
+ * did not contain the required #NMSettingConnection object, which must be
+ * present for all connections
+ * @NM_CONNECTION_ERROR_CONNECTION_TYPE_INVALID: the 'type' property of the
+ * 'connection' setting did not point to a valid connection base type; ie
+ * it was not a hardware-related setting like #NMSettingWired or
+ * #NMSettingWireless.
+ * @NM_CONNECTION_ERROR_SETTING_NOT_FOUND: the #NMConnection object
+ * did not contain the specified #NMSetting object
+ *@NM_CONNECTION_ERROR_INVALID_SETTING: the #NMConnection object contains
+ * a conflicting setting object
+ *
+ * Describes errors that may result from operations involving a #NMConnection.
+ *
+ **/
+typedef enum
+{
+ NM_CONNECTION_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/
+ NM_CONNECTION_ERROR_CONNECTION_SETTING_NOT_FOUND, /*< nick=ConnectionSettingNotFound >*/
+ NM_CONNECTION_ERROR_CONNECTION_TYPE_INVALID, /*< nick=ConnectionTypeInvalid >*/
+ NM_CONNECTION_ERROR_SETTING_NOT_FOUND, /*< nick=SettingNotFound >*/
+ NM_CONNECTION_ERROR_INVALID_SETTING, /*< nick=InvalidSetting >*/
+} NMConnectionError;
+
+/*
+ * NM_CONNECTION_NORMALIZE_PARAM_IP6_CONFIG_METHOD: overwrite the ip6 method
+ * when normalizing ip6 configuration. If omited, this defaults to
+ * @NM_SETTING_IP6_CONFIG_METHOD_AUTO.
+ */
+#define NM_CONNECTION_NORMALIZE_PARAM_IP6_CONFIG_METHOD "ip6-config-method"
+
+#define NM_CONNECTION_ERROR nm_connection_error_quark ()
+GQuark nm_connection_error_quark (void);
+
+/**
+ * NMConnection:
+ *
+ * The NMConnection struct contains only private data.
+ * It should only be accessed through the functions described below.
+ */
+typedef struct {
+ GObject parent;
+} NMConnection;
+
+typedef struct {
+ GObjectClass parent;
+
+ /* Signals */
+ void (*secrets_updated) (NMConnection *connection, const char * setting);
+} NMConnectionClass;
+
+GType nm_connection_get_type (void);
+
+NMConnection *nm_connection_new (void);
+
+NMConnection *nm_connection_new_from_hash (GHashTable *hash, GError **error);
+
+NMConnection *nm_connection_duplicate (NMConnection *connection);
+
+NMSetting *nm_connection_create_setting (const char *name);
+
+void nm_connection_add_setting (NMConnection *connection,
+ NMSetting *setting);
+
+void nm_connection_remove_setting (NMConnection *connection,
+ GType setting_type);
+
+NMSetting *nm_connection_get_setting (NMConnection *connection,
+ GType setting_type);
+
+NMSetting *nm_connection_get_setting_by_name (NMConnection *connection,
+ const char *name);
+
+gboolean nm_connection_replace_settings (NMConnection *connection,
+ GHashTable *new_settings,
+ GError **error);
+
+NM_AVAILABLE_IN_0_9_10
+gboolean nm_connection_replace_settings_from_connection (NMConnection *connection,
+ NMConnection *new_connection,
+ GError **error);
+
+gboolean nm_connection_compare (NMConnection *a,
+ NMConnection *b,
+ NMSettingCompareFlags flags);
+
+gboolean nm_connection_diff (NMConnection *a,
+ NMConnection *b,
+ NMSettingCompareFlags flags,
+ GHashTable **out_settings);
+
+gboolean nm_connection_verify (NMConnection *connection, GError **error);
+NM_AVAILABLE_IN_1_0
+gboolean nm_connection_normalize (NMConnection *connection,
+ GHashTable *parameters,
+ gboolean *modified,
+ GError **error);
+
+const char * nm_connection_need_secrets (NMConnection *connection,
+ GPtrArray **hints);
+
+void nm_connection_clear_secrets (NMConnection *connection);
+
+void nm_connection_clear_secrets_with_flags (NMConnection *connection,
+ NMSettingClearSecretsWithFlagsFn func,
+ gpointer user_data);
+
+gboolean nm_connection_update_secrets (NMConnection *connection,
+ const char *setting_name,
+ GHashTable *secrets,
+ GError **error);
+
+void nm_connection_set_path (NMConnection *connection,
+ const char *path);
+
+const char * nm_connection_get_path (NMConnection *connection);
+
+const char * nm_connection_get_virtual_iface_name (NMConnection *connection);
+
+NM_AVAILABLE_IN_1_0
+const char * nm_connection_get_interface_name (NMConnection *connection);
+
+gboolean nm_connection_is_type (NMConnection *connection, const char *type);
+
+void nm_connection_for_each_setting_value (NMConnection *connection,
+ NMSettingValueIterFn func,
+ gpointer user_data);
+
+GHashTable *nm_connection_to_hash (NMConnection *connection,
+ NMSettingHashFlags flags);
+
+void nm_connection_dump (NMConnection *connection);
+
+GType nm_connection_lookup_setting_type (const char *name);
+
+GType nm_connection_lookup_setting_type_by_quark (GQuark error_quark);
+
+/* Helpers */
+const char * nm_connection_get_uuid (NMConnection *connection);
+const char * nm_connection_get_id (NMConnection *connection);
+NM_AVAILABLE_IN_0_9_10
+const char * nm_connection_get_connection_type (NMConnection *connection);
+
+NM_AVAILABLE_IN_0_9_10
+char * nm_connection_get_virtual_device_description (NMConnection *connection);
+
+NMSetting8021x * nm_connection_get_setting_802_1x (NMConnection *connection);
+NMSettingBluetooth * nm_connection_get_setting_bluetooth (NMConnection *connection);
+NMSettingBond * nm_connection_get_setting_bond (NMConnection *connection);
+NM_AVAILABLE_IN_0_9_10
+NMSettingTeam * nm_connection_get_setting_team (NMConnection *connection);
+NM_AVAILABLE_IN_0_9_10
+NMSettingTeamPort * nm_connection_get_setting_team_port (NMConnection *connection);
+NMSettingBridge * nm_connection_get_setting_bridge (NMConnection *connection);
+NMSettingBridgePort * nm_connection_get_setting_bridge_port (NMConnection *connection);
+NMSettingCdma * nm_connection_get_setting_cdma (NMConnection *connection);
+NMSettingConnection * nm_connection_get_setting_connection (NMConnection *connection);
+NM_AVAILABLE_IN_0_9_10
+NMSettingDcb * nm_connection_get_setting_dcb (NMConnection *connection);
+NM_AVAILABLE_IN_0_9_10
+NMSettingGeneric * nm_connection_get_setting_generic (NMConnection *connection);
+NMSettingGsm * nm_connection_get_setting_gsm (NMConnection *connection);
+NMSettingInfiniband * nm_connection_get_setting_infiniband (NMConnection *connection);
+NMSettingIP4Config * nm_connection_get_setting_ip4_config (NMConnection *connection);
+NMSettingIP6Config * nm_connection_get_setting_ip6_config (NMConnection *connection);
+NMSettingOlpcMesh * nm_connection_get_setting_olpc_mesh (NMConnection *connection);
+NMSettingPPP * nm_connection_get_setting_ppp (NMConnection *connection);
+NMSettingPPPOE * nm_connection_get_setting_pppoe (NMConnection *connection);
+NMSettingSerial * nm_connection_get_setting_serial (NMConnection *connection);
+NMSettingVPN * nm_connection_get_setting_vpn (NMConnection *connection);
+NMSettingWimax * nm_connection_get_setting_wimax (NMConnection *connection);
+NMSettingAdsl * nm_connection_get_setting_adsl (NMConnection *connection);
+NMSettingWired * nm_connection_get_setting_wired (NMConnection *connection);
+NMSettingWireless * nm_connection_get_setting_wireless (NMConnection *connection);
+NMSettingWirelessSecurity *nm_connection_get_setting_wireless_security (NMConnection *connection);
+NMSettingVlan * nm_connection_get_setting_vlan (NMConnection *connection);
+
+G_END_DECLS
+
+#endif /* NM_CONNECTION_H */
diff --git a/libnm-core/nm-param-spec-specialized.c b/libnm-core/nm-param-spec-specialized.c
new file mode 100644
index 0000000000..2b4ca230a0
--- /dev/null
+++ b/libnm-core/nm-param-spec-specialized.c
@@ -0,0 +1,972 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2011 Red Hat, Inc.
+ * Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#include "nm-param-spec-specialized.h"
+#include "nm-glib-compat.h"
+
+struct _NMParamSpecSpecialized {
+ GParamSpec parent;
+};
+
+#include <string.h>
+#include <math.h>
+#include <netinet/in.h>
+#include <dbus/dbus-glib.h>
+
+#include "nm-dbus-glib-types.h"
+
+/***********************************************************/
+/* _gvalues_compare */
+
+static gint _gvalues_compare (const GValue *value1, const GValue *value2);
+
+static gboolean
+type_is_fixed_size (GType type, gsize *tsize)
+{
+ switch (type) {
+ case G_TYPE_CHAR:
+ if (tsize) *tsize = sizeof (char);
+ return TRUE;
+ case G_TYPE_UCHAR:
+ if (tsize) *tsize = sizeof (guchar);
+ return TRUE;
+ case G_TYPE_BOOLEAN:
+ if (tsize) *tsize = sizeof (gboolean);
+ return TRUE;
+ case G_TYPE_LONG:
+ if (tsize) *tsize = sizeof (glong);
+ return TRUE;
+ case G_TYPE_ULONG:
+ if (tsize) *tsize = sizeof (gulong);
+ return TRUE;
+ case G_TYPE_INT:
+ if (tsize) *tsize = sizeof (gint);
+ return TRUE;
+ case G_TYPE_UINT:
+ if (tsize) *tsize = sizeof (guint);
+ return TRUE;
+ case G_TYPE_INT64:
+ if (tsize) *tsize = sizeof (gint64);
+ return TRUE;
+ case G_TYPE_UINT64:
+ if (tsize) *tsize = sizeof (guint64);
+ return TRUE;
+ case G_TYPE_FLOAT:
+ if (tsize) *tsize = sizeof (gfloat);
+ return TRUE;
+ case G_TYPE_DOUBLE:
+ if (tsize) *tsize = sizeof (gdouble);
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+#define FLOAT_FACTOR 0.00000001
+
+static gint
+_gvalues_compare_fixed (const GValue *value1, const GValue *value2)
+{
+ int ret = 0;
+
+ switch (G_VALUE_TYPE (value1)) {
+ case G_TYPE_CHAR: {
+ gchar val1 = g_value_get_schar (value1);
+ gchar val2 = g_value_get_schar (value2);
+ if (val1 != val2)
+ ret = val1 < val2 ? -1 : val1 > val2;
+ break;
+ }
+ case G_TYPE_UCHAR: {
+ guchar val1 = g_value_get_uchar (value1);
+ guchar val2 = g_value_get_uchar (value2);
+ if (val1 != val2)
+ ret = val1 < val2 ? -1 : val1 > val2;
+ break;
+ }
+ case G_TYPE_BOOLEAN: {
+ gboolean val1 = g_value_get_boolean (value1);
+ gboolean val2 = g_value_get_boolean (value2);
+ if (val1 != val2)
+ ret = val1 < val2 ? -1 : val1 > val2;
+ break;
+ }
+ case G_TYPE_LONG: {
+ glong val1 = g_value_get_long (value1);
+ glong val2 = g_value_get_long (value2);
+ if (val1 != val2)
+ ret = val1 < val2 ? -1 : val1 > val2;
+ break;
+ }
+ case G_TYPE_ULONG: {
+ gulong val1 = g_value_get_ulong (value1);
+ gulong val2 = g_value_get_ulong (value2);
+ if (val1 != val2)
+ ret = val1 < val2 ? -1 : val1 > val2;
+ break;
+ }
+ case G_TYPE_INT: {
+ gint val1 = g_value_get_int (value1);
+ gint val2 = g_value_get_int (value2);
+ if (val1 != val2)
+ ret = val1 < val2 ? -1 : val1 > val2;
+ break;
+ }
+ case G_TYPE_UINT: {
+ guint val1 = g_value_get_uint (value1);
+ guint val2 = g_value_get_uint (value2);
+ if (val1 != val2)
+ ret = val1 < val2 ? -1 : val1 > val2;
+ break;
+ }
+ case G_TYPE_INT64: {
+ gint64 val1 = g_value_get_int64 (value1);
+ gint64 val2 = g_value_get_int64 (value2);
+ if (val1 != val2)
+ ret = val1 < val2 ? -1 : val1 > val2;
+ break;
+ }
+ case G_TYPE_UINT64: {
+ guint64 val1 = g_value_get_uint64 (value1);
+ guint64 val2 = g_value_get_uint64 (value2);
+ if (val1 != val2)
+ ret = val1 < val2 ? -1 : val1 > val2;
+ break;
+ }
+ case G_TYPE_FLOAT: {
+ gfloat val1 = g_value_get_float (value1);
+ gfloat val2 = g_value_get_float (value2);
+ /* Can't use == or != here due to inexactness of FP */
+ if (fabsf (val1 - val2) > FLOAT_FACTOR)
+ ret = val1 < val2 ? -1 : val1 > val2;
+ break;
+ }
+ case G_TYPE_DOUBLE: {
+ gdouble val1 = g_value_get_double (value1);
+ gdouble val2 = g_value_get_double (value2);
+ if (fabs (val1 - val2) > FLOAT_FACTOR)
+ ret = val1 < val2 ? -1 : val1 > val2;
+ break;
+ }
+ default:
+ g_warning ("Unhandled fixed size type '%s'", G_VALUE_TYPE_NAME (value1));
+ }
+
+ return ret;
+}
+
+static gint
+_gvalues_compare_string (const GValue *value1, const GValue *value2)
+{
+ const char *str1 = g_value_get_string (value1);
+ const char *str2 = g_value_get_string (value2);
+
+ if (str1 == str2)
+ return 0;
+
+ if (!str1)
+ return 1;
+ if (!str2)
+ return -1;
+
+ return strcmp (str1, str2);
+}
+
+static gint
+_gvalues_compare_strv (const GValue *value1, const GValue *value2)
+{
+ char **strv1;
+ char **strv2;
+ gint ret;
+ guint i = 0;
+
+ strv1 = (char **) g_value_get_boxed (value1);
+ strv2 = (char **) g_value_get_boxed (value2);
+
+ while (strv1[i] && strv2[i]) {
+ ret = strcmp (strv1[i], strv2[i]);
+ if (ret)
+ return ret;
+ i++;
+ }
+
+ if (strv1[i] == NULL && strv2[i] == NULL)
+ return 0;
+
+ if (strv1[i])
+ return 1;
+
+ return -1;
+}
+
+static void
+_gvalue_destroy (gpointer data)
+{
+ GValue *value = (GValue *) data;
+
+ g_value_unset (value);
+ g_slice_free (GValue, value);
+}
+
+static GValue *
+_gvalue_dup (const GValue *value)
+{
+ GValue *dup;
+
+ dup = g_slice_new0 (GValue);
+ g_value_init (dup, G_VALUE_TYPE (value));
+ g_value_copy (value, dup);
+
+ return dup;
+}
+
+static void
+iterate_collection (const GValue *value, gpointer user_data)
+{
+ GSList **list = (GSList **) user_data;
+
+ *list = g_slist_prepend (*list, _gvalue_dup (value));
+}
+
+static gint
+_gvalues_compare_collection (const GValue *value1, const GValue *value2)
+{
+ gint ret;
+ guint len1;
+ guint len2;
+ GType value_type = dbus_g_type_get_collection_specialization (G_VALUE_TYPE (value1));
+ gsize element_size = 0;
+
+ if (type_is_fixed_size (value_type, &element_size)) {
+ gpointer data1 = NULL;
+ gpointer data2 = NULL;
+
+ dbus_g_type_collection_get_fixed ((GValue *) value1, &data1, &len1);
+ dbus_g_type_collection_get_fixed ((GValue *) value2, &data2, &len2);
+
+ if (len1 != len2)
+ ret = len1 < len2 ? -1 : len1 > len2;
+ else
+ ret = memcmp (data1, data2, len1 * element_size);
+ } else {
+ GSList *list1 = NULL;
+ GSList *list2 = NULL;
+
+ dbus_g_type_collection_value_iterate (value1, iterate_collection, &list1);
+ len1 = g_slist_length (list1);
+ dbus_g_type_collection_value_iterate (value2, iterate_collection, &list2);
+ len2 = g_slist_length (list2);
+
+ if (len1 != len2)
+ ret = len1 < len2 ? -1 : len1 > len2;
+ else {
+ GSList *iter1;
+ GSList *iter2;
+
+ for (iter1 = list1, iter2 = list2, ret = 0;
+ ret == 0 && iter1 && iter2;
+ iter1 = iter1->next, iter2 = iter2->next)
+ ret = _gvalues_compare ((GValue *) iter1->data, (GValue *) iter2->data);
+ }
+
+ g_slist_free_full (list1, _gvalue_destroy);
+ g_slist_free_full (list2, _gvalue_destroy);
+ }
+
+ return ret;
+}
+
+static void
+iterate_map (const GValue *key_val,
+ const GValue *value_val,
+ gpointer user_data)
+{
+ GHashTable **hash = (GHashTable **) user_data;
+
+ g_hash_table_insert (*hash, g_value_dup_string (key_val), _gvalue_dup (value_val));
+}
+
+typedef struct {
+ GHashTable *hash2;
+ gint ret;
+} CompareMapInfo;
+
+static void
+compare_one_map_item (gpointer key, gpointer val, gpointer user_data)
+{
+ CompareMapInfo *info = (CompareMapInfo *) user_data;
+ GValue *value2;
+
+ if (info->ret)
+ return;
+
+ value2 = (GValue *) g_hash_table_lookup (info->hash2, key);
+ if (value2)
+ info->ret = _gvalues_compare ((GValue *) val, value2);
+ else
+ info->ret = 1;
+}
+
+static gint
+_gvalues_compare_map (const GValue *value1, const GValue *value2)
+{
+ GHashTable *hash1 = NULL;
+ GHashTable *hash2 = NULL;
+ guint len1;
+ guint len2;
+ gint ret = 0;
+
+ if (dbus_g_type_get_map_key_specialization (G_VALUE_TYPE (value1)) != G_TYPE_STRING) {
+ g_warning ("Can not compare maps with '%s' for keys",
+ g_type_name (dbus_g_type_get_map_key_specialization (G_VALUE_TYPE (value1))));
+ return 0;
+ }
+
+ hash1 = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, _gvalue_destroy);
+ dbus_g_type_map_value_iterate (value1, iterate_map, &hash1);
+ len1 = g_hash_table_size (hash1);
+
+ hash2 = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, _gvalue_destroy);
+ dbus_g_type_map_value_iterate (value2, iterate_map, &hash2);
+ len2 = g_hash_table_size (hash2);
+
+ if (len1 != len2)
+ ret = len1 < len2 ? -1 : len1 > len2;
+ else {
+ CompareMapInfo info;
+
+ info.ret = 0;
+ info.hash2 = hash2;
+ g_hash_table_foreach (hash1, compare_one_map_item, &info);
+ ret = info.ret;
+ }
+
+ g_hash_table_destroy (hash1);
+ g_hash_table_destroy (hash2);
+
+ return ret;
+}
+
+static gint
+_gvalue_ip6_address_compare (const GValue *value1, const GValue *value2)
+{
+ GValueArray *values1, *values2;
+ GValue *tmp_val;
+ GByteArray *addr1, *addr2;
+ guint32 prefix1, prefix2;
+ GByteArray *gw1, *gw2;
+ gint ret = 0;
+ int i;
+
+ /* IP6 addresses are GValueArrays (see nm-dbus-glib-types.h) */
+ values1 = g_value_get_boxed (value1);
+ values2 = g_value_get_boxed (value2);
+
+ /* Since they are NM IPv6 address structures, we expect both
+ * to contain two elements as specified in nm-dbus-glib-types.h.
+ */
+ g_return_val_if_fail (values1->n_values == 3, 0);
+ g_return_val_if_fail (values2->n_values == 3, 0);
+
+ /* First struct IPv6 address */
+ tmp_val = g_value_array_get_nth (values1, 0);
+ addr1 = g_value_get_boxed (tmp_val);
+ /* First struct IPv6 prefix */
+ tmp_val = g_value_array_get_nth (values1, 1);
+ prefix1 = g_value_get_uint (tmp_val);
+ /* First struct IPv6 gateway */
+ tmp_val = g_value_array_get_nth (values1, 2);
+ gw1 = g_value_get_boxed (tmp_val);
+
+ /* Second struct IPv6 address */
+ tmp_val = g_value_array_get_nth (values2, 0);
+ addr2 = g_value_get_boxed (tmp_val);
+ /* Second struct IPv6 prefix */
+ tmp_val = g_value_array_get_nth (values2, 1);
+ prefix2 = g_value_get_uint (tmp_val);
+ /* Second struct IPv6 gateway */
+ tmp_val = g_value_array_get_nth (values2, 2);
+ gw2 = g_value_get_boxed (tmp_val);
+
+ /* Compare IPv6 addresses */
+ if (prefix1 != prefix2)
+ return prefix1 < prefix2 ? -1 : prefix1 > prefix2;
+
+ if (!IN6_ARE_ADDR_EQUAL ((struct in6_addr *)addr1->data, (struct in6_addr *)addr2->data)) {
+ for (i = 0; ret == 0 && i < addr1->len; i++)
+ ret = addr1->data[i] < addr2->data[i] ? -1 : addr1->data[i] > addr2->data[i];
+ }
+
+ if (!IN6_ARE_ADDR_EQUAL ((struct in6_addr *) gw1->data, (struct in6_addr *) gw2->data)) {
+ for (i = 0; ret == 0 && i < gw1->len; i++)
+ ret = gw1->data[i] < gw2->data[i] ? -1 : gw1->data[i] > gw2->data[i];
+ }
+
+ return ret;
+}
+
+static gint
+_gvalue_ip6_route_compare (const GValue *value1, const GValue *value2)
+{
+ GValueArray *values1, *values2;
+ GValue *tmp_val;
+ GByteArray *dest1, *dest2;
+ GByteArray *next_hop1, *next_hop2;
+ guint32 prefix1, prefix2;
+ guint32 metric1, metric2;
+ gint ret = 0;
+ int i;
+
+ /* IP6 routes are GValueArrays (see nm-dbus-glib-types.h) */
+ values1 = g_value_get_boxed (value1);
+ values2 = g_value_get_boxed (value2);
+
+ /* Since they are NM IPv6 route structures, we expect both
+ * to contain 4 elements as specified in nm-dbus-glib-types.h.
+ */
+ g_return_val_if_fail (values1->n_values == 4, 0);
+ g_return_val_if_fail (values2->n_values == 4, 0);
+
+ /* First struct IPv6 route */
+ tmp_val = g_value_array_get_nth (values1, 0);
+ dest1 = g_value_get_boxed (tmp_val);
+ tmp_val = g_value_array_get_nth (values1, 1);
+ prefix1 = g_value_get_uint (tmp_val);
+ tmp_val = g_value_array_get_nth (values1, 2);
+ next_hop1 = g_value_get_boxed (tmp_val);
+ tmp_val = g_value_array_get_nth (values1, 3);
+ metric1 = g_value_get_uint (tmp_val);
+
+ /* Second struct IPv6 route */
+ tmp_val = g_value_array_get_nth (values2, 0);
+ dest2 = g_value_get_boxed (tmp_val);
+ tmp_val = g_value_array_get_nth (values2, 1);
+ prefix2 = g_value_get_uint (tmp_val);
+ tmp_val = g_value_array_get_nth (values2, 2);
+ next_hop2 = g_value_get_boxed (tmp_val);
+ tmp_val = g_value_array_get_nth (values2, 3);
+ metric2 = g_value_get_uint (tmp_val);
+
+ /* Compare the routes */
+ if (prefix1 != prefix2)
+ return prefix1 < prefix2 ? -1 : prefix1 > prefix2;
+
+ if (!IN6_ARE_ADDR_EQUAL ((struct in6_addr *)dest1->data, (struct in6_addr *)dest2->data)) {
+ for (i = 0; ret == 0 && i < dest1->len; i++)
+ ret = dest1->data[i] < dest2->data[i] ? -1 : dest1->data[i] > dest2->data[i];
+ }
+
+ if (!IN6_ARE_ADDR_EQUAL ((struct in6_addr *)next_hop1->data, (struct in6_addr *)next_hop2->data)) {
+ for (i = 0; ret == 0 && i < next_hop1->len; i++)
+ ret = next_hop1->data[i] < next_hop2->data[i] ? -1 : next_hop1->data[i] > next_hop2->data[i];
+ }
+
+ if (metric1 != metric2)
+ ret = metric1 < metric2 ? -1 : metric1 > metric2;
+
+ return ret;
+}
+
+static gint
+_gvalues_compare_struct (const GValue *value1, const GValue *value2)
+{
+ /* value1 and value2 must contain the same type since
+ * _gvalues_compare() enforced that already.
+ */
+
+ if (G_VALUE_HOLDS (value1, DBUS_TYPE_G_IP6_ADDRESS)) {
+ return _gvalue_ip6_address_compare (value1, value2);
+ } else if (G_VALUE_HOLDS (value1, DBUS_TYPE_G_IP6_ROUTE)) {
+ return _gvalue_ip6_route_compare (value1, value2);
+ } else {
+ g_warning ("Don't know how to compare structures");
+ return (value1 == value2);
+ }
+}
+
+gint
+_gvalues_compare (const GValue *value1, const GValue *value2)
+{
+ GType type1;
+ GType type2;
+ gint ret;
+
+ if (value1 == value2)
+ return 0;
+ if (!value1)
+ return 1;
+ if (!value2)
+ return -1;
+
+ type1 = G_VALUE_TYPE (value1);
+ type2 = G_VALUE_TYPE (value2);
+
+ if (type1 != type2)
+ return type1 < type2 ? -1 : type1 > type2;
+
+ if (type_is_fixed_size (type1, NULL))
+ ret = _gvalues_compare_fixed (value1, value2);
+ else if (type1 == G_TYPE_STRING)
+ ret = _gvalues_compare_string (value1, value2);
+ else if (G_VALUE_HOLDS_BOXED (value1)) {
+ gpointer p1 = g_value_get_boxed (value1);
+ gpointer p2 = g_value_get_boxed (value2);
+
+ if (p1 == p2)
+ ret = 0; /* Exactly the same values */
+ else if (!p1)
+ ret = 1; /* The comparision functions below don't handle NULLs */
+ else if (!p2)
+ ret = -1; /* The comparision functions below don't handle NULLs */
+ else if (type1 == G_TYPE_STRV)
+ ret = _gvalues_compare_strv (value1, value2);
+ else if (dbus_g_type_is_collection (type1))
+ ret = _gvalues_compare_collection (value1, value2);
+ else if (dbus_g_type_is_map (type1))
+ ret = _gvalues_compare_map (value1, value2);
+ else if (dbus_g_type_is_struct (type1))
+ ret = _gvalues_compare_struct (value1, value2);
+ else if (type1 == G_TYPE_VALUE)
+ ret = _gvalues_compare ((GValue *) g_value_get_boxed (value1), (GValue *) g_value_get_boxed (value2));
+ else {
+ g_warning ("Don't know how to compare boxed types '%s'", g_type_name (type1));
+ ret = value1 == value2;
+ }
+ } else {
+ g_warning ("Don't know how to compare types '%s'", g_type_name (type1));
+ ret = value1 == value2;
+ }
+
+ return ret;
+}
+
+/***********************************************************/
+
+static void
+param_specialized_init (GParamSpec *pspec)
+{
+}
+
+static void
+param_specialized_set_default (GParamSpec *pspec, GValue *value)
+{
+ value->data[0].v_pointer = NULL;
+}
+
+static gboolean
+param_specialized_validate (GParamSpec *pspec, GValue *value)
+{
+ NMParamSpecSpecialized *sspec = NM_PARAM_SPEC_SPECIALIZED (pspec);
+ GType value_type = G_VALUE_TYPE (value);
+ gboolean changed = FALSE;
+
+ if (!g_value_type_compatible (value_type, G_PARAM_SPEC_VALUE_TYPE (sspec))) {
+ g_value_reset (value);
+ changed = TRUE;
+ }
+
+ return changed;
+}
+
+static gint
+param_specialized_values_cmp (GParamSpec *pspec,
+ const GValue *value1,
+ const GValue *value2)
+{
+ return _gvalues_compare (value1, value2);
+}
+
+GType
+_nm_param_spec_specialized_get_type (void)
+{
+ static GType type;
+
+ if (G_UNLIKELY (type) == 0) {
+ static const GParamSpecTypeInfo pspec_info = {
+ sizeof (NMParamSpecSpecialized),
+ 0,
+ param_specialized_init,
+ G_TYPE_OBJECT, /* value_type */
+ NULL, /* finalize */
+ param_specialized_set_default,
+ param_specialized_validate,
+ param_specialized_values_cmp,
+ };
+ type = g_param_type_register_static ("NMParamSpecSpecialized", &pspec_info);
+ }
+
+ return type;
+}
+
+GParamSpec *
+_nm_param_spec_specialized (const char *name,
+ const char *nick,
+ const char *blurb,
+ GType specialized_type,
+ GParamFlags flags)
+{
+ NMParamSpecSpecialized *pspec;
+
+ g_return_val_if_fail (g_type_is_a (specialized_type, G_TYPE_BOXED), NULL);
+
+ pspec = g_param_spec_internal (NM_TYPE_PARAM_SPEC_SPECIALIZED,
+ name, nick, blurb, flags);
+
+ G_PARAM_SPEC (pspec)->value_type = specialized_type;
+
+ return G_PARAM_SPEC (pspec);
+}
+
+/***********************************************************/
+/* Tests */
+
+#if 0
+
+static void
+compare_ints (void)
+{
+ GValue value1 = G_VALUE_INIT;
+ GValue value2 = G_VALUE_INIT;
+
+ g_value_init (&value1, G_TYPE_INT);
+ g_value_init (&value2, G_TYPE_INT);
+
+ g_value_set_int (&value1, 5);
+ g_value_set_int (&value2, 5);
+ g_print ("Comparing ints 5 and 5: %d\n", _gvalues_compare (&value1, &value2));
+
+ g_value_set_int (&value2, 10);
+ g_print ("Comparing ints 5 and 10: %d\n", _gvalues_compare (&value1, &value2));
+
+ g_value_set_int (&value2, 1);
+ g_print ("Comparing ints 5 and 1: %d\n", _gvalues_compare (&value1, &value2));
+}
+
+static void
+compare_strings (void)
+{
+ GValue value1 = G_VALUE_INIT;
+ GValue value2 = G_VALUE_INIT;
+ const char *str1 = "hello";
+ const char *str2 = "world";
+
+ g_value_init (&value1, G_TYPE_STRING);
+ g_value_init (&value2, G_TYPE_STRING);
+
+ g_value_set_string (&value1, str1);
+ g_value_set_string (&value2, str1);
+ g_print ("Comparing identical strings: %d\n", _gvalues_compare (&value1, &value2));
+
+ g_value_set_string (&value2, str2);
+ g_print ("Comparing different strings: %d\n", _gvalues_compare (&value1, &value2));
+}
+
+static void
+compare_strv (void)
+{
+ GValue value1 = G_VALUE_INIT;
+ GValue value2 = G_VALUE_INIT;
+ char *strv1[] = { "foo", "bar", "baz", NULL };
+ char *strv2[] = { "foo", "bar", "bar", NULL };
+ char *strv3[] = { "foo", "bar", NULL };
+ char *strv4[] = { "foo", "bar", "baz", "bam", NULL };
+
+ g_value_init (&value1, G_TYPE_STRV);
+ g_value_init (&value2, G_TYPE_STRV);
+
+ g_value_set_boxed (&value1, strv1);
+ g_value_set_boxed (&value2, strv1);
+ g_print ("Comparing identical strv's: %d\n", _gvalues_compare (&value1, &value2));
+
+ g_value_set_boxed (&value2, strv2);
+ g_print ("Comparing different strv's: %d\n", _gvalues_compare (&value1, &value2));
+
+ g_value_set_boxed (&value2, strv3);
+ g_print ("Comparing different len (smaller) strv's: %d\n", _gvalues_compare (&value1, &value2));
+
+ g_value_set_boxed (&value2, strv4);
+ g_print ("Comparing different len (longer) strv's: %d\n", _gvalues_compare (&value1, &value2));
+}
+
+static void
+compare_garrays (void)
+{
+ GArray *array1;
+ GArray *array2;
+ GValue value1 = G_VALUE_INIT;
+ GValue value2 = G_VALUE_INIT;
+ int i;
+
+ g_value_init (&value1, DBUS_TYPE_G_UINT_ARRAY);
+ array1 = g_array_new (FALSE, FALSE, sizeof (guint32));
+
+ g_value_init (&value2, DBUS_TYPE_G_UINT_ARRAY);
+ array2 = g_array_new (FALSE, FALSE, sizeof (guint32));
+
+ for (i = 0; i < 5; i++) {
+ g_array_append_val (array1, i);
+ g_array_append_val (array2, i);
+ }
+
+ g_value_set_boxed (&value1, array1);
+ g_value_set_boxed (&value2, array2);
+
+ g_print ("Comparing identical arrays's: %d\n", _gvalues_compare (&value1, &value2));
+
+ g_array_remove_index (array2, 0);
+ g_value_set_boxed (&value2, array2);
+ g_print ("Comparing different length arrays's: %d\n", _gvalues_compare (&value1, &value2));
+
+ i = 7;
+ g_array_prepend_val (array2, i);
+ g_value_set_boxed (&value2, array2);
+ g_print ("Comparing different arrays's: %d\n", _gvalues_compare (&value1, &value2));
+}
+
+static void
+compare_ptrarrays (void)
+{
+ GPtrArray *array1;
+ GPtrArray *array2;
+ GValue value1 = G_VALUE_INIT;
+ GValue value2 = G_VALUE_INIT;
+
+ g_value_init (&value1, dbus_g_type_get_collection ("GPtrArray", G_TYPE_STRING));
+ array1 = g_ptr_array_new ();
+
+ g_value_init (&value2, dbus_g_type_get_collection ("GPtrArray", G_TYPE_STRING));
+ array2 = g_ptr_array_new ();
+
+ g_ptr_array_add (array1, "hello");
+ g_ptr_array_add (array1, "world");
+ g_value_set_boxed (&value1, array1);
+
+ g_ptr_array_add (array2, "hello");
+ g_ptr_array_add (array2, "world");
+ g_value_set_boxed (&value2, array2);
+
+ g_print ("Comparing identical ptr arrays's: %d\n", _gvalues_compare (&value1, &value2));
+
+ g_ptr_array_add (array2, "boo");
+ g_value_set_boxed (&value2, array2);
+ g_print ("Comparing different len ptr arrays's: %d\n", _gvalues_compare (&value1, &value2));
+
+ g_ptr_array_add (array1, "booz");
+ g_value_set_boxed (&value1, array1);
+ g_print ("Comparing different ptr arrays's: %d\n", _gvalues_compare (&value1, &value2));
+}
+
+static void
+compare_str_hash (void)
+{
+ GHashTable *hash1;
+ GHashTable *hash2;
+ GValue value1 = G_VALUE_INIT;
+ GValue value2 = G_VALUE_INIT;
+
+ g_value_init (&value1, dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_STRING));
+ g_value_init (&value2, dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_STRING));
+
+ hash1 = g_hash_table_new (g_str_hash, g_str_equal);
+ hash2 = g_hash_table_new (g_str_hash, g_str_equal);
+
+ g_hash_table_insert (hash1, "key1", "hello");
+ g_hash_table_insert (hash1, "key2", "world");
+
+ g_hash_table_insert (hash2, "key1", "hello");
+ g_hash_table_insert (hash2, "key2", "world");
+
+ g_value_set_boxed (&value1, hash1);
+ g_value_set_boxed (&value2, hash2);
+ g_print ("Comparing identical str hashes: %d\n", _gvalues_compare (&value1, &value2));
+
+ g_hash_table_remove (hash2, "key2");
+ g_value_set_boxed (&value2, hash2);
+ g_print ("Comparing different length str hashes: %d\n", _gvalues_compare (&value1, &value2));
+
+ g_hash_table_insert (hash2, "key2", "moon");
+ g_value_set_boxed (&value2, hash2);
+ g_print ("Comparing different str hashes: %d\n", _gvalues_compare (&value1, &value2));
+}
+
+static GValue *
+str_to_gvalue (const char *str)
+{
+ GValue *value;
+
+ value = g_slice_new0 (GValue);
+ g_value_init (value, G_TYPE_STRING);
+ g_value_set_string (value, str);
+
+ return value;
+}
+
+static GValue *
+int_to_gvalue (int i)
+{
+ GValue *value;
+
+ value = g_slice_new0 (GValue);
+ g_value_init (value, G_TYPE_INT);
+ g_value_set_int (value, i);
+
+ return value;
+}
+
+static void
+compare_gvalue_hash (void)
+{
+ GHashTable *hash1;
+ GHashTable *hash2;
+ GValue value1 = G_VALUE_INIT;
+ GValue value2 = G_VALUE_INIT;
+
+ g_value_init (&value1, dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_VALUE));
+ g_value_init (&value2, dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_VALUE));
+
+ hash1 = g_hash_table_new (g_str_hash, g_str_equal);
+ hash2 = g_hash_table_new (g_str_hash, g_str_equal);
+
+ g_hash_table_insert (hash1, "key1", str_to_gvalue ("hello"));
+ g_hash_table_insert (hash1, "key2", int_to_gvalue (5));
+
+ g_hash_table_insert (hash2, "key1", str_to_gvalue ("hello"));
+ g_hash_table_insert (hash2, "key2", int_to_gvalue (5));
+
+ g_value_set_boxed (&value1, hash1);
+ g_value_set_boxed (&value2, hash2);
+ g_print ("Comparing identical gvalue hashes: %d\n", _gvalues_compare (&value1, &value2));
+
+ g_hash_table_remove (hash2, "key2");
+ g_value_set_boxed (&value2, hash2);
+ g_print ("Comparing different length str hashes: %d\n", _gvalues_compare (&value1, &value2));
+
+ g_hash_table_insert (hash2, "key2", str_to_gvalue ("moon"));
+ g_value_set_boxed (&value2, hash2);
+ g_print ("Comparing different str hashes: %d\n", _gvalues_compare (&value1, &value2));
+}
+
+static void
+compare_ip6_addresses (void)
+{
+ GValueArray *array1;
+ GValueArray *array2;
+ GValueArray *array3;
+ GByteArray *ba1;
+ GByteArray *ba2;
+ GByteArray *ba3;
+ GValue element = G_VALUE_INIT;
+ GValue value1 = G_VALUE_INIT;
+ GValue value2 = G_VALUE_INIT;
+ struct in6_addr addr1;
+ struct in6_addr addr2;
+ struct in6_addr addr3;
+ guint32 prefix1 = 64;
+ guint32 prefix2 = 64;
+ guint32 prefix3 = 0;
+
+ inet_pton (AF_INET6, "1:2:3:4:5:6:7:8", &addr1, sizeof (struct in6_addr));
+ inet_pton (AF_INET6, "ffff:2:3:4:5:6:7:8", &addr2, sizeof (struct in6_addr));
+ inet_pton (AF_INET6, "::", &addr3, sizeof (struct in6_addr));
+
+ /* address 1 */
+ ba1 = g_byte_array_new ();
+ array1 = g_value_array_new (2);
+ g_value_init (&element, DBUS_TYPE_G_UCHAR_ARRAY);
+ g_byte_array_append (ba1, (guint8 *) addr1.s6_addr, 16);
+ g_value_take_boxed (&element, ba1);
+ g_value_array_append (array1, &element);
+ g_value_unset (&element);
+
+ g_value_init (&element, G_TYPE_UINT);
+ g_value_set_uint (&element, prefix1);
+ g_value_array_append (array1, &element);
+ g_value_unset (&element);
+
+ /* address 2 */
+ ba2 = g_byte_array_new ();
+ array2 = g_value_array_new (2);
+ g_value_init (&element, DBUS_TYPE_G_UCHAR_ARRAY);
+ g_byte_array_append (ba2, (guint8 *) addr2.s6_addr, 16);
+ g_value_take_boxed (&element, ba2);
+ g_value_array_append (array2, &element);
+ g_value_unset (&element);
+
+ g_value_init (&element, G_TYPE_UINT);
+ g_value_set_uint (&element, prefix2);
+ g_value_array_append (array2, &element);
+ g_value_unset (&element);
+
+ /* address 3 */
+ ba3 = g_byte_array_new ();
+ array3 = g_value_array_new (2);
+ g_value_init (&element, DBUS_TYPE_G_UCHAR_ARRAY);
+ g_byte_array_append (ba3, (guint8 *) addr3.s6_addr, 16);
+ g_value_take_boxed (&element, ba3);
+ g_value_array_append (array3, &element);
+ g_value_unset (&element);
+
+ g_value_init (&element, G_TYPE_UINT);
+ g_value_set_uint (&element, prefix3);
+ g_value_array_append (array3, &element);
+ g_value_unset (&element);
+
+ g_value_init (&value1, DBUS_TYPE_G_IP6_ADDRESS);
+ g_value_init (&value2, DBUS_TYPE_G_IP6_ADDRESS);
+
+ g_value_set_boxed (&value1, array1);
+ g_value_set_boxed (&value2, array1);
+ g_print ("Comparing identical IPv6 address structures: %d\n", _gvalues_compare (&value1, &value2));
+
+ g_value_set_boxed (&value1, array1);
+ g_value_set_boxed (&value2, array2);
+ g_print ("Comparing different IPv6 address structures: %d\n", _gvalues_compare (&value1, &value2));
+
+ g_value_set_boxed (&value1, array1);
+ g_value_set_boxed (&value2, array3);
+ g_print ("Comparing different IPv6 address structures: %d\n", _gvalues_compare (&value1, &value2));
+}
+
+int
+main (int argc, char *argv[])
+{
+ DBusGConnection *bus;
+
+#if !GLIB_CHECK_VERSION (2, 35, 0)
+ g_type_init ();
+#endif
+
+ bus = dbus_g_bus_get (DBUS_BUS_SESSION, NULL);
+
+ compare_ints ();
+ compare_strings ();
+ compare_strv ();
+ compare_garrays ();
+ compare_ptrarrays ();
+ compare_str_hash ();
+ compare_gvalue_hash ();
+ compare_ip6_addresses ();
+
+ return 0;
+}
+
+#endif
diff --git a/libnm-core/nm-param-spec-specialized.h b/libnm-core/nm-param-spec-specialized.h
new file mode 100644
index 0000000000..7803e919ae
--- /dev/null
+++ b/libnm-core/nm-param-spec-specialized.h
@@ -0,0 +1,43 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2008 Red Hat, Inc.
+ * Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#ifndef NM_PARAM_SPEC_SPECIALIZED_H
+#define NM_PARAM_SPEC_SPECIALIZED_H
+
+#include <glib-object.h>
+
+typedef struct _NMParamSpecSpecialized NMParamSpecSpecialized;
+
+#define NM_TYPE_PARAM_SPEC_SPECIALIZED (_nm_param_spec_specialized_get_type ())
+
+#define NM_IS_PARAM_SPEC_SPECIALIZED(pspec) (G_TYPE_CHECK_INSTANCE_TYPE ((pspec), NM_TYPE_PARAM_SPEC_SPECIALIZED))
+#define NM_PARAM_SPEC_SPECIALIZED(pspec) (G_TYPE_CHECK_INSTANCE_CAST ((pspec), NM_TYPE_PARAM_SPEC_SPECIALIZED, NMParamSpecSpecialized))
+
+GType _nm_param_spec_specialized_get_type (void);
+
+GParamSpec *_nm_param_spec_specialized (const char *name,
+ const char *nick,
+ const char *blurb,
+ GType specialized_type,
+ GParamFlags flags);
+
+#endif /* NM_PARAM_SPEC_SPECIALIZED_H */
diff --git a/libnm-core/nm-setting-8021x.c b/libnm-core/nm-setting-8021x.c
new file mode 100644
index 0000000000..daa5d6b0c7
--- /dev/null
+++ b/libnm-core/nm-setting-8021x.c
@@ -0,0 +1,3706 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2013 Red Hat, Inc.
+ * Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#include <string.h>
+#include <dbus/dbus-glib.h>
+#include <glib/gi18n.h>
+
+#include "nm-setting-8021x.h"
+#include "nm-param-spec-specialized.h"
+#include "nm-utils.h"
+#include "nm-dbus-glib-types.h"
+#include "crypto.h"
+#include "nm-utils-private.h"
+#include "nm-setting-private.h"
+
+/**
+ * SECTION:nm-setting-8021x
+ * @short_description: Describes 802.1x-authenticated connection properties
+ * @include: nm-setting-8021x.h
+ *
+ * The #NMSetting8021x object is a #NMSetting subclass that describes
+ * properties necessary for connection to 802.1x-authenticated networks, such as
+ * WPA and WPA2 Enterprise Wi-Fi networks and wired 802.1x networks. 802.1x
+ * connections typically use certificates and/or EAP authentication methods to
+ * securely verify, identify, and authenticate the client to the network itself,
+ * instead of simply relying on a widely shared static key.
+ *
+ * It's a good idea to read up on wpa_supplicant configuration before using this
+ * setting extensively, since most of the options here correspond closely with
+ * the relevant wpa_supplicant configuration options.
+ *
+ * Furthermore, to get a good idea of 802.1x, EAP, TLS, TTLS, etc and their
+ * applications to Wi-Fi and wired networks, you'll want to get copies of the
+ * following books.
+ *
+ * 802.11 Wireless Networks: The Definitive Guide, Second Edition
+ * Author: Matthew Gast
+ * ISBN: 978-0596100520
+ *
+ * Cisco Wireless LAN Security
+ * Authors: Krishna Sankar, Sri Sundaralingam, Darrin Miller, and Andrew Balinsky
+ * ISBN: 978-1587051548
+ **/
+
+#define SCHEME_PATH "file://"
+
+/**
+ * nm_setting_802_1x_error_quark:
+ *
+ * Registers an error quark for #NMSetting8021x if necessary.
+ *
+ * Returns: the error quark used for #NMSetting8021x errors.
+ **/
+GQuark
+nm_setting_802_1x_error_quark (void)
+{
+ static GQuark quark;
+
+ if (G_UNLIKELY (!quark))
+ quark = g_quark_from_static_string ("nm-setting-802-1x-error-quark");
+ return quark;
+}
+
+G_DEFINE_TYPE_WITH_CODE (NMSetting8021x, nm_setting_802_1x, NM_TYPE_SETTING,
+ _nm_register_setting (NM_SETTING_802_1X_SETTING_NAME,
+ g_define_type_id,
+ 2,
+ NM_SETTING_802_1X_ERROR))
+NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_802_1X)
+
+#define NM_SETTING_802_1X_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_802_1X, NMSetting8021xPrivate))
+
+G_STATIC_ASSERT ( (NM_SETTING_802_1X_CK_FORMAT_UNKNOWN == (NMSetting8021xCKFormat) NM_CRYPTO_FILE_FORMAT_UNKNOWN) );
+G_STATIC_ASSERT ( (NM_SETTING_802_1X_CK_FORMAT_X509 == (NMSetting8021xCKFormat) NM_CRYPTO_FILE_FORMAT_X509) );
+G_STATIC_ASSERT ( (NM_SETTING_802_1X_CK_FORMAT_RAW_KEY == (NMSetting8021xCKFormat) NM_CRYPTO_FILE_FORMAT_RAW_KEY) );
+G_STATIC_ASSERT ( (NM_SETTING_802_1X_CK_FORMAT_PKCS12 == (NMSetting8021xCKFormat) NM_CRYPTO_FILE_FORMAT_PKCS12) );
+
+typedef struct {
+ GSList *eap; /* GSList of strings */
+ char *identity;
+ char *anonymous_identity;
+ char *pac_file;
+ GByteArray *ca_cert;
+ char *ca_path;
+ char *subject_match;
+ GSList *altsubject_matches;
+ GByteArray *client_cert;
+ char *phase1_peapver;
+ char *phase1_peaplabel;
+ char *phase1_fast_provisioning;
+ char *phase2_auth;
+ char *phase2_autheap;
+ GByteArray *phase2_ca_cert;
+ char *phase2_ca_path;
+ char *phase2_subject_match;
+ GSList *phase2_altsubject_matches;
+ GByteArray *phase2_client_cert;
+ char *password;
+ NMSettingSecretFlags password_flags;
+ GByteArray *password_raw;
+ NMSettingSecretFlags password_raw_flags;
+ char *pin;
+ NMSettingSecretFlags pin_flags;
+ GByteArray *private_key;
+ char *private_key_password;
+ NMSettingSecretFlags private_key_password_flags;
+ GByteArray *phase2_private_key;
+ char *phase2_private_key_password;
+ NMSettingSecretFlags phase2_private_key_password_flags;
+ gboolean system_ca_certs;
+} NMSetting8021xPrivate;
+
+enum {
+ PROP_0,
+ PROP_EAP,
+ PROP_IDENTITY,
+ PROP_ANONYMOUS_IDENTITY,
+ PROP_PAC_FILE,
+ PROP_CA_CERT,
+ PROP_CA_PATH,
+ PROP_SUBJECT_MATCH,
+ PROP_ALTSUBJECT_MATCHES,
+ PROP_CLIENT_CERT,
+ PROP_PHASE1_PEAPVER,
+ PROP_PHASE1_PEAPLABEL,
+ PROP_PHASE1_FAST_PROVISIONING,
+ PROP_PHASE2_AUTH,
+ PROP_PHASE2_AUTHEAP,
+ PROP_PHASE2_CA_CERT,
+ PROP_PHASE2_CA_PATH,
+ PROP_PHASE2_SUBJECT_MATCH,
+ PROP_PHASE2_ALTSUBJECT_MATCHES,
+ PROP_PHASE2_CLIENT_CERT,
+ PROP_PASSWORD,
+ PROP_PASSWORD_FLAGS,
+ PROP_PASSWORD_RAW,
+ PROP_PASSWORD_RAW_FLAGS,
+ PROP_PRIVATE_KEY,
+ PROP_PRIVATE_KEY_PASSWORD,
+ PROP_PRIVATE_KEY_PASSWORD_FLAGS,
+ PROP_PHASE2_PRIVATE_KEY,
+ PROP_PHASE2_PRIVATE_KEY_PASSWORD,
+ PROP_PHASE2_PRIVATE_KEY_PASSWORD_FLAGS,
+ PROP_PIN,
+ PROP_PIN_FLAGS,
+ PROP_SYSTEM_CA_CERTS,
+
+ LAST_PROP
+};
+
+/**
+ * nm_setting_802_1x_new:
+ *
+ * Creates a new #NMSetting8021x object with default values.
+ *
+ * Returns: the new empty #NMSetting8021x object
+ **/
+NMSetting *
+nm_setting_802_1x_new (void)
+{
+ return (NMSetting *) g_object_new (NM_TYPE_SETTING_802_1X, NULL);
+}
+
+/**
+ * nm_setting_802_1x_get_num_eap_methods:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns the number of eap methods allowed for use when connecting to the
+ * network. Generally only one EAP method is used. Use the functions
+ * nm_setting_802_1x_get_eap_method(), nm_setting_802_1x_add_eap_method(),
+ * and nm_setting_802_1x_remove_eap_method() for adding, removing, and retrieving
+ * allowed EAP methods.
+ *
+ * Returns: the number of allowed EAP methods
+ **/
+guint32
+nm_setting_802_1x_get_num_eap_methods (NMSetting8021x *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), 0);
+
+ return g_slist_length (NM_SETTING_802_1X_GET_PRIVATE (setting)->eap);
+}
+
+/**
+ * nm_setting_802_1x_get_eap_method:
+ * @setting: the #NMSetting8021x
+ * @i: the index of the EAP method name to return
+ *
+ * Returns the name of the allowed EAP method at index @i.
+ *
+ * Returns: the name of the allowed EAP method at index @i
+ **/
+const char *
+nm_setting_802_1x_get_eap_method (NMSetting8021x *setting, guint32 i)
+{
+ NMSetting8021xPrivate *priv;
+
+ g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+ priv = NM_SETTING_802_1X_GET_PRIVATE (setting);
+ g_return_val_if_fail (i <= g_slist_length (priv->eap), NULL);
+
+ return (const char *) g_slist_nth_data (priv->eap, i);
+}
+
+/**
+ * nm_setting_802_1x_add_eap_method:
+ * @setting: the #NMSetting8021x
+ * @eap: the name of the EAP method to allow for this connection
+ *
+ * Adds an allowed EAP method. The setting is not valid until at least one
+ * EAP method has been added. See #NMSetting8021x:eap property for a list of
+ * allowed EAP methods.
+ *
+ * Returns: %TRUE if the EAP method was successfully added, %FALSE if it was
+ * not a valid method or if it was already allowed.
+ **/
+gboolean
+nm_setting_802_1x_add_eap_method (NMSetting8021x *setting, const char *eap)
+{
+ NMSetting8021xPrivate *priv;
+ GSList *iter;
+
+ g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), FALSE);
+ g_return_val_if_fail (eap != NULL, FALSE);
+
+ priv = NM_SETTING_802_1X_GET_PRIVATE (setting);
+ for (iter = priv->eap; iter; iter = g_slist_next (iter)) {
+ if (!strcmp (eap, (char *) iter->data))
+ return FALSE;
+ }
+
+ priv->eap = g_slist_append (priv->eap, g_ascii_strdown (eap, -1));
+ g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_EAP);
+ return TRUE;
+}
+
+/**
+ * nm_setting_802_1x_remove_eap_method:
+ * @setting: the #NMSetting8021x
+ * @i: the index of the EAP method to remove
+ *
+ * Removes the allowed EAP method at the specified index.
+ **/
+void
+nm_setting_802_1x_remove_eap_method (NMSetting8021x *setting, guint32 i)
+{
+ NMSetting8021xPrivate *priv;
+ GSList *elt;
+
+ g_return_if_fail (NM_IS_SETTING_802_1X (setting));
+
+ priv = NM_SETTING_802_1X_GET_PRIVATE (setting);
+ elt = g_slist_nth (priv->eap, i);
+ g_return_if_fail (elt != NULL);
+
+ g_free (elt->data);
+ priv->eap = g_slist_delete_link (priv->eap, elt);
+ g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_EAP);
+}
+
+/**
+ * nm_setting_802_1x_remove_eap_method_by_value:
+ * @setting: the #NMSetting8021x
+ * @eap: the name of the EAP method to remove
+ *
+ * Removes the allowed EAP method @method.
+ *
+ * Returns: %TRUE if the EAP method was founs and removed, %FALSE if it was not.
+ *
+ * Since: 0.9.10
+ **/
+gboolean
+nm_setting_802_1x_remove_eap_method_by_value (NMSetting8021x *setting,
+ const char *eap)
+{
+ NMSetting8021xPrivate *priv;
+ GSList *iter;
+
+ g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), FALSE);
+ g_return_val_if_fail (eap != NULL, FALSE);
+
+ priv = NM_SETTING_802_1X_GET_PRIVATE (setting);
+ for (iter = priv->eap; iter; iter = g_slist_next (iter)) {
+ if (!strcmp (eap, (char *) iter->data)) {
+ priv->eap = g_slist_delete_link (priv->eap, iter);
+ g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_EAP);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/**
+ * nm_setting_802_1x_clear_eap_methods:
+ * @setting: the #NMSetting8021x
+ *
+ * Clears all allowed EAP methods.
+ **/
+void
+nm_setting_802_1x_clear_eap_methods (NMSetting8021x *setting)
+{
+ NMSetting8021xPrivate *priv;
+
+ g_return_if_fail (NM_IS_SETTING_802_1X (setting));
+
+ priv = NM_SETTING_802_1X_GET_PRIVATE (setting);
+ g_slist_free_full (priv->eap, g_free);
+ priv->eap = NULL;
+ g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_EAP);
+}
+
+/**
+ * nm_setting_802_1x_get_identity:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns the identifier used by some EAP methods (like TLS) to
+ * authenticate the user. Often this is a username or login name.
+ *
+ * Returns: the user identifier
+ **/
+const char *
+nm_setting_802_1x_get_identity (NMSetting8021x *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+ return NM_SETTING_802_1X_GET_PRIVATE (setting)->identity;
+}
+
+/**
+ * nm_setting_802_1x_get_anonymous_identity:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns the anonymous identifier used by some EAP methods (like TTLS) to
+ * authenticate the user in the outer unencrypted "phase 1" authentication. The
+ * inner "phase 2" authentication will use the #NMSetting8021x:identity in
+ * a secure form, if applicable for that EAP method.
+ *
+ * Returns: the anonymous identifier
+ **/
+const char *
+nm_setting_802_1x_get_anonymous_identity (NMSetting8021x *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+ return NM_SETTING_802_1X_GET_PRIVATE (setting)->anonymous_identity;
+}
+
+/**
+ * nm_setting_802_1x_get_pac_file:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns the file containing PAC credentials used by EAP-FAST method.
+ *
+ * Returns: the PAC file
+ **/
+const char *
+nm_setting_802_1x_get_pac_file (NMSetting8021x *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+ return NM_SETTING_802_1X_GET_PRIVATE (setting)->pac_file;
+}
+
+/**
+ * nm_setting_802_1x_get_ca_path:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns the path of the CA certificate directory if previously set. Systems
+ * will often have a directory that contains multiple individual CA certificates
+ * which the supplicant can then add to the verification chain. This may be
+ * used in addition to the #NMSetting8021x:ca-cert property to add more CA
+ * certificates for verifying the network to client.
+ *
+ * Returns: the CA certificate directory path
+ **/
+const char *
+nm_setting_802_1x_get_ca_path (NMSetting8021x *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+ return NM_SETTING_802_1X_GET_PRIVATE (setting)->ca_path;
+}
+
+/**
+ * nm_setting_802_1x_get_system_ca_certs:
+ * @setting: the #NMSetting8021x
+ *
+ * Sets the #NMSetting8021x:system-ca-certs property. The
+ * #NMSetting8021x:ca-path and #NMSetting8021x:phase2-ca-path
+ * properties are ignored if the #NMSetting8021x:system-ca-certs property is
+ * %TRUE, in which case a system-wide CA certificate directory specified at
+ * compile time (using the --system-ca-path configure option) is used in place
+ * of these properties.
+ *
+ * Returns: %TRUE if a system CA certificate path should be used, %FALSE if not
+ **/
+gboolean
+nm_setting_802_1x_get_system_ca_certs (NMSetting8021x *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), FALSE);
+
+ return NM_SETTING_802_1X_GET_PRIVATE (setting)->system_ca_certs;
+}
+
+static NMSetting8021xCKScheme
+get_cert_scheme (GByteArray *array)
+{
+ if (!array || !array->len)
+ return NM_SETTING_802_1X_CK_SCHEME_UNKNOWN;
+
+ if ( (array->len > strlen (SCHEME_PATH))
+ && !memcmp (array->data, SCHEME_PATH, strlen (SCHEME_PATH)))
+ return NM_SETTING_802_1X_CK_SCHEME_PATH;
+
+ return NM_SETTING_802_1X_CK_SCHEME_BLOB;
+}
+
+/**
+ * nm_setting_802_1x_get_ca_cert_scheme:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns the scheme used to store the CA certificate. If the returned scheme
+ * is %NM_SETTING_802_1X_CK_SCHEME_BLOB, use nm_setting_802_1x_get_ca_cert_blob();
+ * if %NM_SETTING_802_1X_CK_SCHEME_PATH, use nm_setting_802_1x_get_ca_cert_path().
+ *
+ * Returns: scheme used to store the CA certificate (blob or path)
+ **/
+NMSetting8021xCKScheme
+nm_setting_802_1x_get_ca_cert_scheme (NMSetting8021x *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_802_1X_CK_SCHEME_UNKNOWN);
+
+ return get_cert_scheme (NM_SETTING_802_1X_GET_PRIVATE (setting)->ca_cert);
+}
+
+/**
+ * nm_setting_802_1x_get_ca_cert_blob:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns the CA certificate blob if the CA certificate is stored using the
+ * %NM_SETTING_802_1X_CK_SCHEME_BLOB scheme. Not all EAP methods use a
+ * CA certificate (LEAP for example), and those that can take advantage of the
+ * CA certificate allow it to be unset. Note that lack of a CA certificate
+ * reduces security by allowing man-in-the-middle attacks, because the identity
+ * of the network cannot be confirmed by the client.
+ *
+ * Returns: the CA certificate data
+ **/
+const GByteArray *
+nm_setting_802_1x_get_ca_cert_blob (NMSetting8021x *setting)
+{
+ NMSetting8021xCKScheme scheme;
+
+ g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+ scheme = nm_setting_802_1x_get_ca_cert_scheme (setting);
+ g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB, NULL);
+
+ return NM_SETTING_802_1X_GET_PRIVATE (setting)->ca_cert;
+}
+
+/**
+ * nm_setting_802_1x_get_ca_cert_path:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns the CA certificate path if the CA certificate is stored using the
+ * %NM_SETTING_802_1X_CK_SCHEME_PATH scheme. Not all EAP methods use a
+ * CA certificate (LEAP for example), and those that can take advantage of the
+ * CA certificate allow it to be unset. Note that lack of a CA certificate
+ * reduces security by allowing man-in-the-middle attacks, because the identity
+ * of the network cannot be confirmed by the client.
+ *
+ * Returns: path to the CA certificate file
+ **/
+const char *
+nm_setting_802_1x_get_ca_cert_path (NMSetting8021x *setting)
+{
+ NMSetting8021xCKScheme scheme;
+
+ g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+ scheme = nm_setting_802_1x_get_ca_cert_scheme (setting);
+ g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH, NULL);
+
+ return (const char *) (NM_SETTING_802_1X_GET_PRIVATE (setting)->ca_cert->data + strlen (SCHEME_PATH));
+}
+
+static GByteArray *
+path_to_scheme_value (const char *path)
+{
+ GByteArray *array;
+
+ g_return_val_if_fail (path != NULL, NULL);
+
+ /* Add the path scheme tag to the front, then the fielname */
+ array = g_byte_array_sized_new (strlen (path) + strlen (SCHEME_PATH) + 1);
+ g_assert (array);
+ g_byte_array_append (array, (const guint8 *) SCHEME_PATH, strlen (SCHEME_PATH));
+ g_byte_array_append (array, (const guint8 *) path, strlen (path));
+ g_byte_array_append (array, (const guint8 *) "\0", 1);
+ return array;
+}
+
+/**
+ * nm_setting_802_1x_set_ca_cert:
+ * @setting: the #NMSetting8021x
+ * @cert_path: when @scheme is set to either %NM_SETTING_802_1X_CK_SCHEME_PATH
+ * or %NM_SETTING_802_1X_CK_SCHEME_BLOB, pass the path of the CA certificate
+ * file (PEM or DER format). The path must be UTF-8 encoded; use
+ * g_filename_to_utf8() to convert if needed. Passing %NULL with any @scheme
+ * clears the CA certificate.
+ * @scheme: desired storage scheme for the certificate
+ * @out_format: on successful return, the type of the certificate added
+ * @error: on unsuccessful return, an error
+ *
+ * Reads a certificate from disk and sets the #NMSetting8021x:ca-cert property
+ * with the raw certificate data if using the %NM_SETTING_802_1X_CK_SCHEME_BLOB
+ * scheme, or with the path to the certificate file if using the
+ * %NM_SETTING_802_1X_CK_SCHEME_PATH scheme.
+ *
+ * Returns: %TRUE if the operation succeeded, %FALSE if it was unsuccessful
+ **/
+gboolean
+nm_setting_802_1x_set_ca_cert (NMSetting8021x *setting,
+ const char *cert_path,
+ NMSetting8021xCKScheme scheme,
+ NMSetting8021xCKFormat *out_format,
+ GError **error)
+{
+ NMSetting8021xPrivate *priv;
+ NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
+ GByteArray *data;
+
+ g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), FALSE);
+
+ if (cert_path) {
+ g_return_val_if_fail (g_utf8_validate (cert_path, -1, NULL), FALSE);
+ g_return_val_if_fail ( scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB
+ || scheme == NM_SETTING_802_1X_CK_SCHEME_PATH,
+ FALSE);
+ }
+
+ if (out_format)
+ g_return_val_if_fail (*out_format == NM_SETTING_802_1X_CK_FORMAT_UNKNOWN, FALSE);
+
+ priv = NM_SETTING_802_1X_GET_PRIVATE (setting);
+
+ /* Clear out any previous ca_cert blob */
+ if (priv->ca_cert) {
+ g_byte_array_free (priv->ca_cert, TRUE);
+ priv->ca_cert = NULL;
+ }
+
+ if (!cert_path) {
+ g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_CA_CERT);
+ return TRUE;
+ }
+
+ data = crypto_load_and_verify_certificate (cert_path, &format, error);
+ if (data) {
+ /* wpa_supplicant can only use raw x509 CA certs */
+ if (format == NM_CRYPTO_FILE_FORMAT_X509) {
+ if (out_format)
+ *out_format = NM_SETTING_802_1X_CK_FORMAT_X509;
+
+ if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB)
+ priv->ca_cert = g_byte_array_ref (data);
+ else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH)
+ priv->ca_cert = path_to_scheme_value (cert_path);
+ else
+ g_assert_not_reached ();
+ } else {
+ g_set_error_literal (error,
+ NM_SETTING_802_1X_ERROR,
+ NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
+ _("CA certificate must be in X.509 format"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_CA_CERT);
+ }
+ g_byte_array_unref (data);
+ }
+
+ g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_CA_CERT);
+ return priv->ca_cert != NULL;
+}
+
+/**
+ * nm_setting_802_1x_get_subject_match:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns: the #NMSetting8021x:subject-match property. This is the
+ * substring to be matched against the subject of the authentication
+ * server certificate, or %NULL no subject verification is to be
+ * performed.
+ **/
+const char *
+nm_setting_802_1x_get_subject_match (NMSetting8021x *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+ return NM_SETTING_802_1X_GET_PRIVATE (setting)->subject_match;
+}
+
+/**
+ * nm_setting_802_1x_get_num_altsubject_matches:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns the number of entries in the
+ * #NMSetting8021x:altsubject-matches property of this setting.
+ *
+ * Returns: the number of altsubject-matches entries.
+ **/
+guint32
+nm_setting_802_1x_get_num_altsubject_matches (NMSetting8021x *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), 0);
+
+ return g_slist_length (NM_SETTING_802_1X_GET_PRIVATE (setting)->altsubject_matches);
+}
+
+/**
+ * nm_setting_802_1x_get_altsubject_match:
+ * @setting: the #NMSettingConnection
+ * @i: the zero-based index of the array of altSubjectName matches
+ *
+ * Returns the altSubjectName match at index @i.
+ *
+ * Returns: the altSubjectName match at index @i
+ **/
+const char *
+nm_setting_802_1x_get_altsubject_match (NMSetting8021x *setting, guint32 i)
+{
+ NMSetting8021xPrivate *priv;
+
+ g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+ priv = NM_SETTING_802_1X_GET_PRIVATE (setting);
+ g_return_val_if_fail (i <= g_slist_length (priv->altsubject_matches), NULL);
+
+ return (const char *) g_slist_nth_data (priv->altsubject_matches, i);
+}
+
+/**
+ * nm_setting_802_1x_add_altsubject_match:
+ * @setting: the #NMSetting8021x
+ * @altsubject_match: the altSubjectName to allow for this connection
+ *
+ * Adds an allowed alternate subject name match. Until at least one
+ * match is added, the altSubjectName of the remote authentication
+ * server is not verified.
+ *
+ * Returns: %TRUE if the alternative subject name match was
+ * successfully added, %FALSE if it was already allowed.
+ **/
+gboolean
+nm_setting_802_1x_add_altsubject_match (NMSetting8021x *setting,
+ const char *altsubject_match)
+{
+ NMSetting8021xPrivate *priv;
+ GSList *iter;
+
+ g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), FALSE);
+ g_return_val_if_fail (altsubject_match != NULL, FALSE);
+
+ priv = NM_SETTING_802_1X_GET_PRIVATE (setting);
+ for (iter = priv->altsubject_matches; iter; iter = g_slist_next (iter)) {
+ if (!strcmp (altsubject_match, (char *) iter->data))
+ return FALSE;
+ }
+
+ priv->altsubject_matches = g_slist_append (priv->altsubject_matches,
+ g_strdup (altsubject_match));
+ g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_ALTSUBJECT_MATCHES);
+ return TRUE;
+}
+
+/**
+ * nm_setting_802_1x_remove_altsubject_match:
+ * @setting: the #NMSetting8021x
+ * @i: the index of the altSubjectName match to remove
+ *
+ * Removes the allowed altSubjectName at the specified index.
+ **/
+void
+nm_setting_802_1x_remove_altsubject_match (NMSetting8021x *setting, guint32 i)
+{
+ NMSetting8021xPrivate *priv;
+ GSList *elt;
+
+ g_return_if_fail (NM_IS_SETTING_802_1X (setting));
+
+ priv = NM_SETTING_802_1X_GET_PRIVATE (setting);
+ elt = g_slist_nth (priv->altsubject_matches, i);
+ g_return_if_fail (elt != NULL);
+
+ g_free (elt->data);
+ priv->altsubject_matches = g_slist_delete_link (priv->altsubject_matches, elt);
+ g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_ALTSUBJECT_MATCHES);
+}
+
+/**
+ * nm_setting_802_1x_remove_altsubject_match_by_value:
+ * @setting: the #NMSetting8021x
+ * @altsubject_match: the altSubjectName to remove
+ *
+ * Removes the allowed altSubjectName @altsubject_match.
+ *
+ * Returns: %TRUE if the alternative subject name match was found and removed,
+ * %FALSE if it was not.
+ *
+ * Since: 0.9.10
+ **/
+gboolean
+nm_setting_802_1x_remove_altsubject_match_by_value (NMSetting8021x *setting,
+ const char *altsubject_match)
+{
+ NMSetting8021xPrivate *priv;
+ GSList *iter;
+
+ g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), FALSE);
+ g_return_val_if_fail (altsubject_match != NULL, FALSE);
+
+ priv = NM_SETTING_802_1X_GET_PRIVATE (setting);
+ for (iter = priv->altsubject_matches; iter; iter = g_slist_next (iter)) {
+ if (!strcmp (altsubject_match, (char *) iter->data)) {
+ priv->altsubject_matches = g_slist_delete_link (priv->altsubject_matches, iter);
+ g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_ALTSUBJECT_MATCHES);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/**
+ * nm_setting_802_1x_clear_altsubject_matches:
+ * @setting: the #NMSetting8021x
+ *
+ * Clears all altSubjectName matches.
+ **/
+void
+nm_setting_802_1x_clear_altsubject_matches (NMSetting8021x *setting)
+{
+ NMSetting8021xPrivate *priv;
+
+ g_return_if_fail (NM_IS_SETTING_802_1X (setting));
+
+ priv = NM_SETTING_802_1X_GET_PRIVATE (setting);
+ g_slist_free_full (priv->altsubject_matches, g_free);
+ priv->altsubject_matches = NULL;
+ g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_ALTSUBJECT_MATCHES);
+}
+
+/**
+ * nm_setting_802_1x_get_client_cert_scheme:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns the scheme used to store the client certificate. If the returned scheme
+ * is %NM_SETTING_802_1X_CK_SCHEME_BLOB, use nm_setting_802_1x_get_client_cert_blob();
+ * if %NM_SETTING_802_1X_CK_SCHEME_PATH, use nm_setting_802_1x_get_client_cert_path().
+ *
+ * Returns: scheme used to store the client certificate (blob or path)
+ **/
+NMSetting8021xCKScheme
+nm_setting_802_1x_get_client_cert_scheme (NMSetting8021x *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_802_1X_CK_SCHEME_UNKNOWN);
+
+ return get_cert_scheme (NM_SETTING_802_1X_GET_PRIVATE (setting)->client_cert);
+}
+
+/**
+ * nm_setting_802_1x_get_client_cert_blob:
+ * @setting: the #NMSetting8021x
+ *
+ * Client certificates are used to identify the connecting client to the network
+ * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x
+ * authentication method.
+ *
+ * Returns: the client certificate data
+ **/
+const GByteArray *
+nm_setting_802_1x_get_client_cert_blob (NMSetting8021x *setting)
+{
+ NMSetting8021xCKScheme scheme;
+
+ g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+ scheme = nm_setting_802_1x_get_client_cert_scheme (setting);
+ g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB, NULL);
+
+ return NM_SETTING_802_1X_GET_PRIVATE (setting)->client_cert;
+}
+
+/**
+ * nm_setting_802_1x_get_client_cert_path:
+ * @setting: the #NMSetting8021x
+ *
+ * Client certificates are used to identify the connecting client to the network
+ * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x
+ * authentication method.
+ *
+ * Returns: path to the client certificate file
+ **/
+const char *
+nm_setting_802_1x_get_client_cert_path (NMSetting8021x *setting)
+{
+ NMSetting8021xCKScheme scheme;
+
+ g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+ scheme = nm_setting_802_1x_get_client_cert_scheme (setting);
+ g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH, NULL);
+
+ return (const char *) (NM_SETTING_802_1X_GET_PRIVATE (setting)->client_cert->data + strlen (SCHEME_PATH));
+}
+
+/**
+ * nm_setting_802_1x_set_client_cert:
+ * @setting: the #NMSetting8021x
+ * @cert_path: when @scheme is set to either %NM_SETTING_802_1X_CK_SCHEME_PATH
+ * or %NM_SETTING_802_1X_CK_SCHEME_BLOB, pass the path of the client
+ * certificate file (PEM, DER, or PKCS#12 format). The path must be UTF-8
+ * encoded; use g_filename_to_utf8() to convert if needed. Passing %NULL with
+ * any @scheme clears the client certificate.
+ * @scheme: desired storage scheme for the certificate
+ * @out_format: on successful return, the type of the certificate added
+ * @error: on unsuccessful return, an error
+ *
+ * Reads a certificate from disk and sets the #NMSetting8021x:client-cert
+ * property with the raw certificate data if using the
+ * %NM_SETTING_802_1X_CK_SCHEME_BLOB scheme, or with the path to the certificate
+ * file if using the %NM_SETTING_802_1X_CK_SCHEME_PATH scheme.
+ *
+ * Client certificates are used to identify the connecting client to the network
+ * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x
+ * authentication method.
+ *
+ * Returns: %TRUE if the operation succeeded, %FALSE if it was unsuccessful
+ **/
+gboolean
+nm_setting_802_1x_set_client_cert (NMSetting8021x *setting,
+ const char *cert_path,
+ NMSetting8021xCKScheme scheme,
+ NMSetting8021xCKFormat *out_format,
+ GError **error)
+{
+ NMSetting8021xPrivate *priv;
+ NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
+ GByteArray *data;
+
+ g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), FALSE);
+
+ if (cert_path) {
+ g_return_val_if_fail (g_utf8_validate (cert_path, -1, NULL), FALSE);
+ g_return_val_if_fail ( scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB
+ || scheme == NM_SETTING_802_1X_CK_SCHEME_PATH,
+ FALSE);
+ }
+
+ if (out_format)
+ g_return_val_if_fail (*out_format == NM_SETTING_802_1X_CK_FORMAT_UNKNOWN, FALSE);
+
+ priv = NM_SETTING_802_1X_GET_PRIVATE (setting);
+
+ /* Clear out any previous ca_cert blob */
+ if (priv->client_cert) {
+ g_byte_array_free (priv->client_cert, TRUE);
+ priv->client_cert = NULL;
+ }
+
+ if (!cert_path) {
+ g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_CLIENT_CERT);
+ return TRUE;
+ }
+
+ data = crypto_load_and_verify_certificate (cert_path, &format, error);
+ if (data) {
+ gboolean valid = FALSE;
+
+ switch (format) {
+ case NM_CRYPTO_FILE_FORMAT_X509:
+ if (out_format)
+ *out_format = NM_SETTING_802_1X_CK_FORMAT_X509;
+ valid = TRUE;
+ break;
+ case NM_CRYPTO_FILE_FORMAT_PKCS12:
+ if (out_format)
+ *out_format = NM_SETTING_802_1X_CK_FORMAT_PKCS12;
+ valid = TRUE;
+ break;
+ default:
+ g_set_error_literal (error,
+ NM_SETTING_802_1X_ERROR,
+ NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
+ _("invalid certificate format"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_CLIENT_CERT);
+ break;
+ }
+
+ if (valid) {
+ if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB)
+ priv->client_cert = g_byte_array_ref (data);
+ else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH)
+ priv->client_cert = path_to_scheme_value (cert_path);
+ else
+ g_assert_not_reached ();
+ }
+ g_byte_array_unref (data);
+ }
+
+ g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_CLIENT_CERT);
+ return priv->client_cert != NULL;
+}
+
+/**
+ * nm_setting_802_1x_get_phase1_peapver:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns: the "phase 1" PEAP version to be used when authenticating with
+ * EAP-PEAP as contained in the #NMSetting8021x:phase1-peapver property. Valid
+ * values are %NULL (unset), "0" (PEAP version 0), and "1" (PEAP version 1).
+ **/
+const char *
+nm_setting_802_1x_get_phase1_peapver (NMSetting8021x *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+ return NM_SETTING_802_1X_GET_PRIVATE (setting)->phase1_peapver;
+}
+
+/**
+ * nm_setting_802_1x_get_phase1_peaplabel:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns: whether the "phase 1" PEAP label is new-style or old-style, to be
+ * used when authenticating with EAP-PEAP, as contained in the
+ * #NMSetting8021x:phase1-peaplabel property. Valid values are %NULL (unset),
+ * "0" (use old-style label), and "1" (use new-style label). See the
+ * wpa_supplicant documentation for more details.
+ **/
+const char *
+nm_setting_802_1x_get_phase1_peaplabel (NMSetting8021x *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+ return NM_SETTING_802_1X_GET_PRIVATE (setting)->phase1_peaplabel;
+}
+
+/**
+ * nm_setting_802_1x_get_phase1_fast_provisioning:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns: whether "phase 1" PEAP fast provisioning should be used, as specified
+ * by the #NMSetting8021x:phase1-fast-provisioning property. See the
+ * wpa_supplicant documentation for more details.
+ **/
+const char *
+nm_setting_802_1x_get_phase1_fast_provisioning (NMSetting8021x *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+ return NM_SETTING_802_1X_GET_PRIVATE (setting)->phase1_fast_provisioning;
+}
+
+/**
+ * nm_setting_802_1x_get_phase2_auth:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns: the "phase 2" non-EAP (ex MD5) allowed authentication method as
+ * specified by the #NMSetting8021x:phase2-auth property.
+ **/
+const char *
+nm_setting_802_1x_get_phase2_auth (NMSetting8021x *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+ return NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_auth;
+}
+
+/**
+ * nm_setting_802_1x_get_phase2_autheap:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns: the "phase 2" EAP-based (ex TLS) allowed authentication method as
+ * specified by the #NMSetting8021x:phase2-autheap property.
+ **/
+const char *
+nm_setting_802_1x_get_phase2_autheap (NMSetting8021x *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+ return NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_autheap;
+}
+
+/**
+ * nm_setting_802_1x_get_phase2_ca_path:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns the path of the "phase 2" CA certificate directory if previously set.
+ * Systems will often have a directory that contains multiple individual CA
+ * certificates which the supplicant can then add to the verification chain.
+ * This may be used in addition to the #NMSetting8021x:phase2-ca-cert property
+ * to add more CA certificates for verifying the network to client.
+ *
+ * Returns: the "phase 2" CA certificate directory path
+ **/
+const char *
+nm_setting_802_1x_get_phase2_ca_path (NMSetting8021x *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+ return NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_ca_path;
+}
+
+/**
+ * nm_setting_802_1x_get_phase2_ca_cert_scheme:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns the scheme used to store the "phase 2" CA certificate. If the
+ * returned scheme is %NM_SETTING_802_1X_CK_SCHEME_BLOB, use
+ * nm_setting_802_1x_get_ca_cert_blob(); if %NM_SETTING_802_1X_CK_SCHEME_PATH,
+ * use nm_setting_802_1x_get_ca_cert_path().
+ *
+ * Returns: scheme used to store the "phase 2" CA certificate (blob or path)
+ **/
+NMSetting8021xCKScheme
+nm_setting_802_1x_get_phase2_ca_cert_scheme (NMSetting8021x *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_802_1X_CK_SCHEME_UNKNOWN);
+
+ return get_cert_scheme (NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_ca_cert);
+}
+
+/**
+ * nm_setting_802_1x_get_phase2_ca_cert_blob:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns the "phase 2" CA certificate blob if the CA certificate is stored
+ * using the %NM_SETTING_802_1X_CK_SCHEME_BLOB scheme. Not all EAP methods use
+ * a CA certificate (LEAP for example), and those that can take advantage of the
+ * CA certificate allow it to be unset. Note that lack of a CA certificate
+ * reduces security by allowing man-in-the-middle attacks, because the identity
+ * of the network cannot be confirmed by the client.
+ *
+ * Returns: the "phase 2" CA certificate data
+ **/
+const GByteArray *
+nm_setting_802_1x_get_phase2_ca_cert_blob (NMSetting8021x *setting)
+{
+ NMSetting8021xCKScheme scheme;
+
+ g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+ scheme = nm_setting_802_1x_get_phase2_ca_cert_scheme (setting);
+ g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB, NULL);
+
+ return NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_ca_cert;
+}
+
+/**
+ * nm_setting_802_1x_get_phase2_ca_cert_path:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns the "phase 2" CA certificate path if the CA certificate is stored
+ * using the %NM_SETTING_802_1X_CK_SCHEME_PATH scheme. Not all EAP methods use
+ * a CA certificate (LEAP for example), and those that can take advantage of the
+ * CA certificate allow it to be unset. Note that lack of a CA certificate
+ * reduces security by allowing man-in-the-middle attacks, because the identity
+ * of the network cannot be confirmed by the client.
+ *
+ * Returns: path to the "phase 2" CA certificate file
+ **/
+const char *
+nm_setting_802_1x_get_phase2_ca_cert_path (NMSetting8021x *setting)
+{
+ NMSetting8021xCKScheme scheme;
+
+ g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+ scheme = nm_setting_802_1x_get_phase2_ca_cert_scheme (setting);
+ g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH, NULL);
+
+ return (const char *) (NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_ca_cert->data + strlen (SCHEME_PATH));
+}
+
+/**
+ * nm_setting_802_1x_set_phase2_ca_cert:
+ * @setting: the #NMSetting8021x
+ * @cert_path: when @scheme is set to either %NM_SETTING_802_1X_CK_SCHEME_PATH
+ * or %NM_SETTING_802_1X_CK_SCHEME_BLOB, pass the path of the "phase2" CA
+ * certificate file (PEM or DER format). The path must be UTF-8 encoded; use
+ * g_filename_to_utf8() to convert if needed. Passing %NULL with any @scheme
+ * clears the "phase2" CA certificate.
+ * @scheme: desired storage scheme for the certificate
+ * @out_format: on successful return, the type of the certificate added
+ * @error: on unsuccessful return, an error
+ *
+ * Reads a certificate from disk and sets the #NMSetting8021x:phase2-ca-cert
+ * property with the raw certificate data if using the
+ * %NM_SETTING_802_1X_CK_SCHEME_BLOB scheme, or with the path to the certificate
+ * file if using the %NM_SETTING_802_1X_CK_SCHEME_PATH scheme.
+ *
+ * Returns: %TRUE if the operation succeeded, %FALSE if it was unsuccessful
+ **/
+gboolean
+nm_setting_802_1x_set_phase2_ca_cert (NMSetting8021x *setting,
+ const char *cert_path,
+ NMSetting8021xCKScheme scheme,
+ NMSetting8021xCKFormat *out_format,
+ GError **error)
+{
+ NMSetting8021xPrivate *priv;
+ NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
+ GByteArray *data;
+
+ g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), FALSE);
+
+ if (cert_path) {
+ g_return_val_if_fail (g_utf8_validate (cert_path, -1, NULL), FALSE);
+ g_return_val_if_fail ( scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB
+ || scheme == NM_SETTING_802_1X_CK_SCHEME_PATH,
+ FALSE);
+ }
+
+ if (out_format)
+ g_return_val_if_fail (*out_format == NM_SETTING_802_1X_CK_FORMAT_UNKNOWN, FALSE);
+
+ priv = NM_SETTING_802_1X_GET_PRIVATE (setting);
+
+ /* Clear out any previous ca_cert blob */
+ if (priv->phase2_ca_cert) {
+ g_byte_array_free (priv->phase2_ca_cert, TRUE);
+ priv->phase2_ca_cert = NULL;
+ }
+
+ if (!cert_path) {
+ g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_CA_CERT);
+ return TRUE;
+ }
+
+ data = crypto_load_and_verify_certificate (cert_path, &format, error);
+ if (data) {
+ /* wpa_supplicant can only use raw x509 CA certs */
+ if (format == NM_CRYPTO_FILE_FORMAT_X509) {
+ if (out_format)
+ *out_format = NM_SETTING_802_1X_CK_FORMAT_X509;
+
+ if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB)
+ priv->phase2_ca_cert = g_byte_array_ref (data);
+ else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH)
+ priv->phase2_ca_cert = path_to_scheme_value (cert_path);
+ else
+ g_assert_not_reached ();
+ } else {
+ g_set_error_literal (error,
+ NM_SETTING_802_1X_ERROR,
+ NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
+ _("invalid certificate format"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PHASE2_CA_CERT);
+ }
+ g_byte_array_unref (data);
+ }
+
+ g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_CA_CERT);
+ return priv->phase2_ca_cert != NULL;
+}
+
+/**
+ * nm_setting_802_1x_get_phase2_subject_match:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns: the #NMSetting8021x:phase2-subject-match property. This is
+ * the substring to be matched against the subject of the "phase 2"
+ * authentication server certificate, or %NULL no subject verification
+ * is to be performed.
+ **/
+const char *
+nm_setting_802_1x_get_phase2_subject_match (NMSetting8021x *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+ return NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_subject_match;
+}
+
+/**
+ * nm_setting_802_1x_get_num_phase2_altsubject_matches:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns the number of entries in the
+ * #NMSetting8021x:phase2-altsubject-matches property of this setting.
+ *
+ * Returns: the number of phase2-altsubject-matches entries.
+ **/
+guint32
+nm_setting_802_1x_get_num_phase2_altsubject_matches (NMSetting8021x *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), 0);
+
+ return g_slist_length (NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_altsubject_matches);
+}
+
+/**
+ * nm_setting_802_1x_get_phase2_altsubject_match:
+ * @setting: the #NMSettingConnection
+ * @i: the zero-based index of the array of "phase 2" altSubjectName matches
+ *
+ * Returns the "phase 2" altSubjectName match at index @i.
+ *
+ * Returns: the "phase 2" altSubjectName match at index @i
+ **/
+const char *
+nm_setting_802_1x_get_phase2_altsubject_match (NMSetting8021x *setting, guint32 i)
+{
+ NMSetting8021xPrivate *priv;
+
+ g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+ priv = NM_SETTING_802_1X_GET_PRIVATE (setting);
+ g_return_val_if_fail (i <= g_slist_length (priv->phase2_altsubject_matches), NULL);
+
+ return (const char *) g_slist_nth_data (priv->phase2_altsubject_matches, i);
+}
+
+/**
+ * nm_setting_802_1x_add_phase2_altsubject_match:
+ * @setting: the #NMSetting8021x
+ * @phase2_altsubject_match: the "phase 2" altSubjectName to allow for this
+ * connection
+ *
+ * Adds an allowed alternate subject name match for "phase 2". Until
+ * at least one match is added, the altSubjectName of the "phase 2"
+ * remote authentication server is not verified.
+ *
+ * Returns: %TRUE if the "phase 2" alternative subject name match was
+ * successfully added, %FALSE if it was already allowed.
+ **/
+gboolean
+nm_setting_802_1x_add_phase2_altsubject_match (NMSetting8021x *setting,
+ const char *phase2_altsubject_match)
+{
+ NMSetting8021xPrivate *priv;
+ GSList *iter;
+
+ g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), FALSE);
+ g_return_val_if_fail (phase2_altsubject_match != NULL, FALSE);
+
+ priv = NM_SETTING_802_1X_GET_PRIVATE (setting);
+ for (iter = priv->phase2_altsubject_matches; iter; iter = g_slist_next (iter)) {
+ if (!strcmp (phase2_altsubject_match, (char *) iter->data))
+ return FALSE;
+ }
+
+ priv->phase2_altsubject_matches = g_slist_append (priv->phase2_altsubject_matches,
+ g_strdup (phase2_altsubject_match));
+ g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_ALTSUBJECT_MATCHES);
+ return TRUE;
+}
+
+/**
+ * nm_setting_802_1x_remove_phase2_altsubject_match:
+ * @setting: the #NMSetting8021x
+ * @i: the index of the "phase 2" altSubjectName match to remove
+ *
+ * Removes the allowed "phase 2" altSubjectName at the specified index.
+ **/
+void
+nm_setting_802_1x_remove_phase2_altsubject_match (NMSetting8021x *setting, guint32 i)
+{
+ NMSetting8021xPrivate *priv;
+ GSList *elt;
+
+ g_return_if_fail (NM_IS_SETTING_802_1X (setting));
+
+ priv = NM_SETTING_802_1X_GET_PRIVATE (setting);
+ elt = g_slist_nth (priv->phase2_altsubject_matches, i);
+ g_return_if_fail (elt != NULL);
+
+ g_free (elt->data);
+ priv->phase2_altsubject_matches = g_slist_delete_link (priv->phase2_altsubject_matches, elt);
+ g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_ALTSUBJECT_MATCHES);
+}
+
+
+/**
+ * nm_setting_802_1x_remove_phase2_altsubject_match_by_value:
+ * @setting: the #NMSetting8021x
+ * @phase2_altsubject_match: the "phase 2" altSubjectName to remove
+ *
+ * Removes the allowed "phase 2" altSubjectName @phase2_altsubject_match.
+ *
+ * Returns: %TRUE if the alternative subject name match for "phase 2" was found and removed,
+ * %FALSE if it was not.
+ *
+ * Since: 0.9.10
+ **/
+gboolean
+nm_setting_802_1x_remove_phase2_altsubject_match_by_value (NMSetting8021x *setting,
+ const char *phase2_altsubject_match)
+{
+ NMSetting8021xPrivate *priv;
+ GSList *iter;
+
+ g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), FALSE);
+ g_return_val_if_fail (phase2_altsubject_match != NULL, FALSE);
+
+ priv = NM_SETTING_802_1X_GET_PRIVATE (setting);
+ for (iter = priv->phase2_altsubject_matches; iter; iter = g_slist_next (iter)) {
+ if (!strcmp (phase2_altsubject_match, (char *) iter->data)) {
+ priv->phase2_altsubject_matches = g_slist_delete_link (priv->phase2_altsubject_matches, iter);
+ g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_ALTSUBJECT_MATCHES);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/**
+ * nm_setting_802_1x_clear_phase2_altsubject_matches:
+ * @setting: the #NMSetting8021x
+ *
+ * Clears all "phase 2" altSubjectName matches.
+ **/
+void
+nm_setting_802_1x_clear_phase2_altsubject_matches (NMSetting8021x *setting)
+{
+ NMSetting8021xPrivate *priv;
+
+ g_return_if_fail (NM_IS_SETTING_802_1X (setting));
+
+ priv = NM_SETTING_802_1X_GET_PRIVATE (setting);
+ g_slist_free_full (priv->phase2_altsubject_matches, g_free);
+ priv->phase2_altsubject_matches = NULL;
+ g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_ALTSUBJECT_MATCHES);
+}
+
+/**
+ * nm_setting_802_1x_get_phase2_client_cert_scheme:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns the scheme used to store the "phase 2" client certificate. If the
+ * returned scheme is %NM_SETTING_802_1X_CK_SCHEME_BLOB, use
+ * nm_setting_802_1x_get_client_cert_blob(); if
+ * %NM_SETTING_802_1X_CK_SCHEME_PATH, use
+ * nm_setting_802_1x_get_client_cert_path().
+ *
+ * Returns: scheme used to store the "phase 2" client certificate (blob or path)
+ **/
+NMSetting8021xCKScheme
+nm_setting_802_1x_get_phase2_client_cert_scheme (NMSetting8021x *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_802_1X_CK_SCHEME_UNKNOWN);
+
+ return get_cert_scheme (NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_client_cert);
+}
+
+/**
+ * nm_setting_802_1x_get_phase2_client_cert_blob:
+ * @setting: the #NMSetting8021x
+ *
+ * Client certificates are used to identify the connecting client to the network
+ * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x
+ * authentication method.
+ *
+ * Returns: the "phase 2" client certificate data
+ **/
+const GByteArray *
+nm_setting_802_1x_get_phase2_client_cert_blob (NMSetting8021x *setting)
+{
+ NMSetting8021xCKScheme scheme;
+
+ g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+ scheme = nm_setting_802_1x_get_phase2_client_cert_scheme (setting);
+ g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB, NULL);
+
+ return NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_client_cert;
+}
+
+/**
+ * nm_setting_802_1x_get_phase2_client_cert_path:
+ * @setting: the #NMSetting8021x
+ *
+ * Client certificates are used to identify the connecting client to the network
+ * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x
+ * authentication method.
+ *
+ * Returns: path to the "phase 2" client certificate file
+ **/
+const char *
+nm_setting_802_1x_get_phase2_client_cert_path (NMSetting8021x *setting)
+{
+ NMSetting8021xCKScheme scheme;
+
+ g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+ scheme = nm_setting_802_1x_get_phase2_client_cert_scheme (setting);
+ g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH, NULL);
+
+ return (const char *) (NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_client_cert->data + strlen (SCHEME_PATH));
+}
+
+/**
+ * nm_setting_802_1x_set_phase2_client_cert:
+ * @setting: the #NMSetting8021x
+ * @cert_path: when @scheme is set to either %NM_SETTING_802_1X_CK_SCHEME_PATH
+ * or %NM_SETTING_802_1X_CK_SCHEME_BLOB, pass the path of the "phase2" client
+ * certificate file (PEM, DER, or PKCS#12 format). The path must be UTF-8
+ * encoded; use g_filename_to_utf8() to convert if needed. Passing %NULL with
+ * any @scheme clears the "phase2" client certificate.
+ * @scheme: desired storage scheme for the certificate
+ * @out_format: on successful return, the type of the certificate added
+ * @error: on unsuccessful return, an error
+ *
+ * Reads a certificate from disk and sets the #NMSetting8021x:phase2-client-cert
+ * property with the raw certificate data if using the
+ * %NM_SETTING_802_1X_CK_SCHEME_BLOB scheme, or with the path to the certificate
+ * file if using the %NM_SETTING_802_1X_CK_SCHEME_PATH scheme.
+ *
+ * Client certificates are used to identify the connecting client to the network
+ * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x
+ * authentication method.
+ *
+ * Returns: %TRUE if the operation succeeded, %FALSE if it was unsuccessful
+ **/
+gboolean
+nm_setting_802_1x_set_phase2_client_cert (NMSetting8021x *setting,
+ const char *cert_path,
+ NMSetting8021xCKScheme scheme,
+ NMSetting8021xCKFormat *out_format,
+ GError **error)
+{
+ NMSetting8021xPrivate *priv;
+ NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
+ GByteArray *data;
+
+ g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), FALSE);
+
+ if (cert_path) {
+ g_return_val_if_fail (g_utf8_validate (cert_path, -1, NULL), FALSE);
+ g_return_val_if_fail ( scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB
+ || scheme == NM_SETTING_802_1X_CK_SCHEME_PATH,
+ FALSE);
+ }
+
+ if (out_format)
+ g_return_val_if_fail (*out_format == NM_SETTING_802_1X_CK_FORMAT_UNKNOWN, FALSE);
+
+ priv = NM_SETTING_802_1X_GET_PRIVATE (setting);
+
+ /* Clear out any previous ca_cert blob */
+ if (priv->phase2_client_cert) {
+ g_byte_array_free (priv->phase2_client_cert, TRUE);
+ priv->phase2_client_cert = NULL;
+ }
+
+ if (!cert_path) {
+ g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_CLIENT_CERT);
+ return TRUE;
+ }
+
+ data = crypto_load_and_verify_certificate (cert_path, &format, error);
+ if (data) {
+ gboolean valid = FALSE;
+
+ /* wpa_supplicant can only use raw x509 CA certs */
+ switch (format) {
+ case NM_CRYPTO_FILE_FORMAT_X509:
+ if (out_format)
+ *out_format = NM_SETTING_802_1X_CK_FORMAT_X509;
+ valid = TRUE;
+ break;
+ case NM_CRYPTO_FILE_FORMAT_PKCS12:
+ if (out_format)
+ *out_format = NM_SETTING_802_1X_CK_FORMAT_PKCS12;
+ valid = TRUE;
+ break;
+ default:
+ g_set_error_literal (error,
+ NM_SETTING_802_1X_ERROR,
+ NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
+ _("invalid certificate format"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PHASE2_CLIENT_CERT);
+ break;
+ }
+
+ if (valid) {
+ if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB)
+ priv->phase2_client_cert = g_byte_array_ref (data);
+ else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH)
+ priv->phase2_client_cert = path_to_scheme_value (cert_path);
+ else
+ g_assert_not_reached ();
+ }
+ g_byte_array_unref (data);
+ }
+
+ g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_CLIENT_CERT);
+ return priv->phase2_client_cert != NULL;
+}
+
+/**
+ * nm_setting_802_1x_get_password:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns: the password used by the authentication method, if any, as specified
+ * by the #NMSetting8021x:password property
+ **/
+const char *
+nm_setting_802_1x_get_password (NMSetting8021x *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+ return NM_SETTING_802_1X_GET_PRIVATE (setting)->password;
+}
+
+/**
+ * nm_setting_802_1x_get_password_flags:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns: the #NMSettingSecretFlags pertaining to the #NMSetting8021x:password
+ **/
+NMSettingSecretFlags
+nm_setting_802_1x_get_password_flags (NMSetting8021x *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_SECRET_FLAG_NONE);
+
+ return NM_SETTING_802_1X_GET_PRIVATE (setting)->password_flags;
+}
+
+/**
+ * nm_setting_802_1x_get_password_raw:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns: the password used by the authentication method as a
+ * UTF-8-encoded array of bytes, as specified by the
+ * #NMSetting8021x:password-raw property
+ **/
+const GByteArray *
+nm_setting_802_1x_get_password_raw (NMSetting8021x *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+ return NM_SETTING_802_1X_GET_PRIVATE (setting)->password_raw;
+}
+
+/**
+ * nm_setting_802_1x_get_password_raw_flags:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns: the #NMSettingSecretFlags pertaining to the
+ * #NMSetting8021x:password-raw
+ **/
+NMSettingSecretFlags
+nm_setting_802_1x_get_password_raw_flags (NMSetting8021x *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_SECRET_FLAG_NONE);
+
+ return NM_SETTING_802_1X_GET_PRIVATE (setting)->password_raw_flags;
+}
+
+/**
+ * nm_setting_802_1x_get_pin:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns: the PIN used by the authentication method, if any, as specified
+ * by the #NMSetting8021x:pin property
+ **/
+const char *
+nm_setting_802_1x_get_pin (NMSetting8021x *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+ return NM_SETTING_802_1X_GET_PRIVATE (setting)->pin;
+}
+
+/**
+ * nm_setting_802_1x_get_pin_flags:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns: the #NMSettingSecretFlags pertaining to the
+ * #NMSetting8021x:pin
+ **/
+NMSettingSecretFlags
+nm_setting_802_1x_get_pin_flags (NMSetting8021x *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_SECRET_FLAG_NONE);
+
+ return NM_SETTING_802_1X_GET_PRIVATE (setting)->pin_flags;
+}
+
+/**
+ * nm_setting_802_1x_get_private_key_scheme:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns the scheme used to store the private key. If the returned scheme is
+ * %NM_SETTING_802_1X_CK_SCHEME_BLOB, use
+ * nm_setting_802_1x_get_client_cert_blob(); if
+ * %NM_SETTING_802_1X_CK_SCHEME_PATH, use
+ * nm_setting_802_1x_get_client_cert_path().
+ *
+ * Returns: scheme used to store the private key (blob or path)
+ **/
+NMSetting8021xCKScheme
+nm_setting_802_1x_get_private_key_scheme (NMSetting8021x *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_802_1X_CK_SCHEME_UNKNOWN);
+
+ return get_cert_scheme (NM_SETTING_802_1X_GET_PRIVATE (setting)->private_key);
+}
+
+/**
+ * nm_setting_802_1x_get_private_key_blob:
+ * @setting: the #NMSetting8021x
+ *
+ * Private keys are used to authenticate the connecting client to the network
+ * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x
+ * authentication method.
+ *
+ * WARNING: the private key property is not a "secret" property, and thus
+ * unencrypted private key data may be readable by unprivileged users. Private
+ * keys should always be encrypted with a private key password.
+ *
+ * Returns: the private key data
+ **/
+const GByteArray *
+nm_setting_802_1x_get_private_key_blob (NMSetting8021x *setting)
+{
+ NMSetting8021xCKScheme scheme;
+
+ g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+ scheme = nm_setting_802_1x_get_private_key_scheme (setting);
+ g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB, NULL);
+
+ return NM_SETTING_802_1X_GET_PRIVATE (setting)->private_key;
+}
+
+/**
+ * nm_setting_802_1x_get_private_key_path:
+ * @setting: the #NMSetting8021x
+ *
+ * Private keys are used to authenticate the connecting client to the network
+ * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x
+ * authentication method.
+ *
+ * Returns: path to the private key file
+ **/
+const char *
+nm_setting_802_1x_get_private_key_path (NMSetting8021x *setting)
+{
+ NMSetting8021xCKScheme scheme;
+
+ g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+ scheme = nm_setting_802_1x_get_private_key_scheme (setting);
+ g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH, NULL);
+
+ return (const char *) (NM_SETTING_802_1X_GET_PRIVATE (setting)->private_key->data + strlen (SCHEME_PATH));
+}
+
+static GByteArray *
+file_to_byte_array (const char *filename)
+{
+ char *contents;
+ GByteArray *array = NULL;
+ gsize length = 0;
+
+ if (g_file_get_contents (filename, &contents, &length, NULL)) {
+ array = g_byte_array_sized_new (length);
+ g_byte_array_append (array, (guint8 *) contents, length);
+ g_assert (array->len == length);
+ g_free (contents);
+ }
+ return array;
+}
+
+/**
+ * nm_setting_802_1x_set_private_key:
+ * @setting: the #NMSetting8021x
+ * @key_path: when @scheme is set to either %NM_SETTING_802_1X_CK_SCHEME_PATH or
+ * %NM_SETTING_802_1X_CK_SCHEME_BLOB, pass the path of the private key file
+ * (PEM, DER, or PKCS#12 format). The path must be UTF-8 encoded; use
+ * g_filename_to_utf8() to convert if needed. Passing %NULL with any @scheme
+ * clears the private key.
+ * @password: password used to decrypt the private key, or %NULL if the password
+ * is unknown. If the password is given but fails to decrypt the private key,
+ * an error is returned.
+ * @scheme: desired storage scheme for the private key
+ * @out_format: on successful return, the type of the private key added
+ * @error: on unsuccessful return, an error
+ *
+ * Private keys are used to authenticate the connecting client to the network
+ * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x
+ * authentication method.
+ *
+ * This function reads a private key from disk and sets the
+ * #NMSetting8021x:private-key property with the private key file data if using
+ * the %NM_SETTING_802_1X_CK_SCHEME_BLOB scheme, or with the path to the private
+ * key file if using the %NM_SETTING_802_1X_CK_SCHEME_PATH scheme.
+ *
+ * If @password is given, this function attempts to decrypt the private key to
+ * verify that @password is correct, and if it is, updates the
+ * #NMSetting8021x:private-key-password property with the given @password. If
+ * the decryption is unsuccessful, %FALSE is returned, @error is set, and no
+ * internal data is changed. If no @password is given, the private key is
+ * assumed to be valid, no decryption is performed, and the password may be set
+ * at a later time.
+ *
+ * WARNING: the private key property is not a "secret" property, and thus
+ * unencrypted private key data using the BLOB scheme may be readable by
+ * unprivileged users. Private keys should always be encrypted with a private
+ * key password to prevent unauthorized access to unencrypted private key data.
+ *
+ * Returns: %TRUE if the operation succeeded, %FALSE if it was unsuccessful
+ **/
+gboolean
+nm_setting_802_1x_set_private_key (NMSetting8021x *setting,
+ const char *key_path,
+ const char *password,
+ NMSetting8021xCKScheme scheme,
+ NMSetting8021xCKFormat *out_format,
+ GError **error)
+{
+ NMSetting8021xPrivate *priv;
+ NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
+ gboolean key_cleared = FALSE, password_cleared = FALSE;
+ GError *local_err = NULL;
+
+ g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), FALSE);
+
+ if (key_path) {
+ g_return_val_if_fail (g_utf8_validate (key_path, -1, NULL), FALSE);
+ g_return_val_if_fail ( scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB
+ || scheme == NM_SETTING_802_1X_CK_SCHEME_PATH,
+ FALSE);
+ }
+
+ if (out_format)
+ g_return_val_if_fail (*out_format == NM_SETTING_802_1X_CK_FORMAT_UNKNOWN, FALSE);
+
+ /* Ensure the private key is a recognized format and if the password was
+ * given, that it decrypts the private key.
+ */
+ if (key_path) {
+ format = crypto_verify_private_key (key_path, password, &local_err);
+ if (format == NM_CRYPTO_FILE_FORMAT_UNKNOWN) {
+ g_set_error_literal (error,
+ NM_SETTING_802_1X_ERROR,
+ NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
+ local_err ? local_err->message : _("invalid private key"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PRIVATE_KEY);
+ g_clear_error (&local_err);
+ return FALSE;
+ }
+ }
+
+ priv = NM_SETTING_802_1X_GET_PRIVATE (setting);
+
+ /* Clear out any previous private key data */
+ if (priv->private_key) {
+ /* Try not to leave the private key around in memory */
+ memset (priv->private_key->data, 0, priv->private_key->len);
+ g_byte_array_free (priv->private_key, TRUE);
+ priv->private_key = NULL;
+ key_cleared = TRUE;
+ }
+
+ if (priv->private_key_password) {
+ g_free (priv->private_key_password);
+ priv->private_key_password = NULL;
+ password_cleared = TRUE;
+ }
+
+ if (key_path == NULL) {
+ if (key_cleared)
+ g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PRIVATE_KEY);
+ if (password_cleared)
+ g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD);
+ return TRUE;
+ }
+
+ priv->private_key_password = g_strdup (password);
+ if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) {
+ /* Shouldn't fail this since we just verified the private key above */
+ priv->private_key = file_to_byte_array (key_path);
+ g_assert (priv->private_key);
+ } else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH)
+ priv->private_key = path_to_scheme_value (key_path);
+ else
+ g_assert_not_reached ();
+
+ /* As required by NM and wpa_supplicant, set the client-cert
+ * property to the same PKCS#12 data.
+ */
+ g_assert (format != NM_CRYPTO_FILE_FORMAT_UNKNOWN);
+ if (format == NM_CRYPTO_FILE_FORMAT_PKCS12) {
+ if (priv->client_cert)
+ g_byte_array_free (priv->client_cert, TRUE);
+
+ priv->client_cert = g_byte_array_sized_new (priv->private_key->len);
+ g_byte_array_append (priv->client_cert, priv->private_key->data, priv->private_key->len);
+ g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_CLIENT_CERT);
+ }
+
+ g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PRIVATE_KEY);
+ if (password_cleared || password)
+ g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD);
+
+ if (out_format)
+ *out_format = (NMSetting8021xCKFormat) format;
+ return priv->private_key != NULL;
+}
+
+/**
+ * nm_setting_802_1x_get_private_key_password:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns: the private key password used to decrypt the private key if
+ * previously set with nm_setting_802_1x_set_private_key(), or the
+ * #NMSetting8021x:private-key-password property.
+ **/
+const char *
+nm_setting_802_1x_get_private_key_password (NMSetting8021x *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+ return NM_SETTING_802_1X_GET_PRIVATE (setting)->private_key_password;
+}
+
+/**
+ * nm_setting_802_1x_get_private_key_password_flags:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns: the #NMSettingSecretFlags pertaining to the
+ * #NMSetting8021x:private-key-password
+ **/
+NMSettingSecretFlags
+nm_setting_802_1x_get_private_key_password_flags (NMSetting8021x *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_SECRET_FLAG_NONE);
+
+ return NM_SETTING_802_1X_GET_PRIVATE (setting)->private_key_password_flags;
+}
+
+/**
+ * nm_setting_802_1x_get_private_key_format:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns: the data format of the private key data stored in the
+ * #NMSetting8021x:private-key property
+ **/
+NMSetting8021xCKFormat
+nm_setting_802_1x_get_private_key_format (NMSetting8021x *setting)
+{
+ NMSetting8021xPrivate *priv;
+ const char *path;
+ GError *error = NULL;
+
+ g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_802_1X_CK_FORMAT_UNKNOWN);
+ priv = NM_SETTING_802_1X_GET_PRIVATE (setting);
+
+ if (!priv->private_key)
+ return NM_SETTING_802_1X_CK_FORMAT_UNKNOWN;
+
+ switch (nm_setting_802_1x_get_private_key_scheme (setting)) {
+ case NM_SETTING_802_1X_CK_SCHEME_BLOB:
+ if (crypto_is_pkcs12_data (priv->private_key))
+ return NM_SETTING_802_1X_CK_FORMAT_PKCS12;
+ return NM_SETTING_802_1X_CK_FORMAT_RAW_KEY;
+ case NM_SETTING_802_1X_CK_SCHEME_PATH:
+ path = nm_setting_802_1x_get_private_key_path (setting);
+ if (crypto_is_pkcs12_file (path, &error))
+ return NM_SETTING_802_1X_CK_FORMAT_PKCS12;
+ if (error) {
+ /* Couldn't read the file or something */
+ g_error_free (error);
+ return NM_SETTING_802_1X_CK_FORMAT_UNKNOWN;
+ }
+ return NM_SETTING_802_1X_CK_FORMAT_RAW_KEY;
+ default:
+ break;
+ }
+
+ return NM_SETTING_802_1X_CK_FORMAT_UNKNOWN;
+}
+
+/**
+ * nm_setting_802_1x_get_phase2_private_key_password:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns: the private key password used to decrypt the private key if
+ * previously set with nm_setting_802_1x_set_phase2_private_key() or the
+ * #NMSetting8021x:phase2-private-key-password property.
+ **/
+const char *
+nm_setting_802_1x_get_phase2_private_key_password (NMSetting8021x *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+ return NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_private_key_password;
+}
+
+/**
+ * nm_setting_802_1x_get_phase2_private_key_password_flags:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns: the #NMSettingSecretFlags pertaining to the
+ * #NMSetting8021x:phase2-private-key-password
+ **/
+NMSettingSecretFlags
+nm_setting_802_1x_get_phase2_private_key_password_flags (NMSetting8021x *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_SECRET_FLAG_NONE);
+
+ return NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_private_key_password_flags;
+}
+
+/**
+ * nm_setting_802_1x_get_phase2_private_key_scheme:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns the scheme used to store the "phase 2" private key. If the returned
+ * scheme is %NM_SETTING_802_1X_CK_SCHEME_BLOB, use
+ * nm_setting_802_1x_get_client_cert_blob(); if
+ * %NM_SETTING_802_1X_CK_SCHEME_PATH, use
+ * nm_setting_802_1x_get_client_cert_path().
+ *
+ * Returns: scheme used to store the "phase 2" private key (blob or path)
+ **/
+NMSetting8021xCKScheme
+nm_setting_802_1x_get_phase2_private_key_scheme (NMSetting8021x *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_802_1X_CK_SCHEME_UNKNOWN);
+
+ return get_cert_scheme (NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_private_key);
+}
+
+/**
+ * nm_setting_802_1x_get_phase2_private_key_blob:
+ * @setting: the #NMSetting8021x
+ *
+ * Private keys are used to authenticate the connecting client to the network
+ * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x
+ * authentication method.
+ *
+ * WARNING: the phase2 private key property is not a "secret" property, and thus
+ * unencrypted private key data may be readable by unprivileged users. Private
+ * keys should always be encrypted with a private key password.
+ *
+ * Returns: the "phase 2" private key data
+ **/
+const GByteArray *
+nm_setting_802_1x_get_phase2_private_key_blob (NMSetting8021x *setting)
+{
+ NMSetting8021xCKScheme scheme;
+
+ g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+ scheme = nm_setting_802_1x_get_phase2_private_key_scheme (setting);
+ g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB, NULL);
+
+ return NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_private_key;
+}
+
+/**
+ * nm_setting_802_1x_get_phase2_private_key_path:
+ * @setting: the #NMSetting8021x
+ *
+ * Private keys are used to authenticate the connecting client to the network
+ * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x
+ * authentication method.
+ *
+ * Returns: path to the "phase 2" private key file
+ **/
+const char *
+nm_setting_802_1x_get_phase2_private_key_path (NMSetting8021x *setting)
+{
+ NMSetting8021xCKScheme scheme;
+
+ g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NULL);
+
+ scheme = nm_setting_802_1x_get_phase2_private_key_scheme (setting);
+ g_return_val_if_fail (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH, NULL);
+
+ return (const char *) (NM_SETTING_802_1X_GET_PRIVATE (setting)->phase2_private_key->data + strlen (SCHEME_PATH));
+}
+
+/**
+ * nm_setting_802_1x_set_phase2_private_key:
+ * @setting: the #NMSetting8021x
+ * @key_path: when @scheme is set to either %NM_SETTING_802_1X_CK_SCHEME_PATH or
+ * %NM_SETTING_802_1X_CK_SCHEME_BLOB, pass the path of the "phase2" private
+ * key file (PEM, DER, or PKCS#12 format). The path must be UTF-8 encoded;
+ * use g_filename_to_utf8() to convert if needed. Passing %NULL with any
+ * @scheme clears the private key.
+ * @password: password used to decrypt the private key, or %NULL if the password
+ * is unknown. If the password is given but fails to decrypt the private key,
+ * an error is returned.
+ * @scheme: desired storage scheme for the private key
+ * @out_format: on successful return, the type of the private key added
+ * @error: on unsuccessful return, an error
+ *
+ * Private keys are used to authenticate the connecting client to the network
+ * when EAP-TLS is used as either the "phase 1" or "phase 2" 802.1x
+ * authentication method.
+ *
+ * This function reads a private key from disk and sets the
+ * #NMSetting8021x:phase2-private-key property with the private key file data if
+ * using the %NM_SETTING_802_1X_CK_SCHEME_BLOB scheme, or with the path to the
+ * private key file if using the %NM_SETTING_802_1X_CK_SCHEME_PATH scheme.
+ *
+ * If @password is given, this function attempts to decrypt the private key to
+ * verify that @password is correct, and if it is, updates the
+ * #NMSetting8021x:phase2-private-key-password property with the given
+ * @password. If the decryption is unsuccessful, %FALSE is returned, @error is
+ * set, and no internal data is changed. If no @password is given, the private
+ * key is assumed to be valid, no decryption is performed, and the password may
+ * be set at a later time.
+ *
+ * WARNING: the "phase2" private key property is not a "secret" property, and
+ * thus unencrypted private key data using the BLOB scheme may be readable by
+ * unprivileged users. Private keys should always be encrypted with a private
+ * key password to prevent unauthorized access to unencrypted private key data.
+ *
+ * Returns: %TRUE if the operation succeeded, %FALSE if it was unsuccessful
+ **/
+gboolean
+nm_setting_802_1x_set_phase2_private_key (NMSetting8021x *setting,
+ const char *key_path,
+ const char *password,
+ NMSetting8021xCKScheme scheme,
+ NMSetting8021xCKFormat *out_format,
+ GError **error)
+{
+ NMSetting8021xPrivate *priv;
+ NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
+ gboolean key_cleared = FALSE, password_cleared = FALSE;
+ GError *local_err = NULL;
+
+ g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), FALSE);
+
+ if (key_path) {
+ g_return_val_if_fail (g_utf8_validate (key_path, -1, NULL), FALSE);
+ g_return_val_if_fail ( scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB
+ || scheme == NM_SETTING_802_1X_CK_SCHEME_PATH,
+ FALSE);
+ }
+
+ if (out_format)
+ g_return_val_if_fail (*out_format == NM_SETTING_802_1X_CK_FORMAT_UNKNOWN, FALSE);
+
+ /* Ensure the private key is a recognized format and if the password was
+ * given, that it decrypts the private key.
+ */
+ if (key_path) {
+ format = crypto_verify_private_key (key_path, password, &local_err);
+ if (format == NM_CRYPTO_FILE_FORMAT_UNKNOWN) {
+ g_set_error_literal (error,
+ NM_SETTING_802_1X_ERROR,
+ NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
+ local_err ? local_err->message : _("invalid phase2 private key"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY);
+ g_clear_error (&local_err);
+ return FALSE;
+ }
+ }
+
+ priv = NM_SETTING_802_1X_GET_PRIVATE (setting);
+
+ /* Clear out any previous private key data */
+ if (priv->phase2_private_key) {
+ /* Try not to leave the private key around in memory */
+ memset (priv->phase2_private_key->data, 0, priv->phase2_private_key->len);
+ g_byte_array_free (priv->phase2_private_key, TRUE);
+ priv->phase2_private_key = NULL;
+ key_cleared = TRUE;
+ }
+
+ if (priv->phase2_private_key_password) {
+ g_free (priv->phase2_private_key_password);
+ priv->phase2_private_key_password = NULL;
+ password_cleared = TRUE;
+ }
+
+ if (key_path == NULL) {
+ if (key_cleared)
+ g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_PRIVATE_KEY);
+ if (password_cleared)
+ g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD);
+ return TRUE;
+ }
+
+ priv->phase2_private_key_password = g_strdup (password);
+ if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) {
+ /* Shouldn't fail this since we just verified the private key above */
+ priv->phase2_private_key = file_to_byte_array (key_path);
+ g_assert (priv->phase2_private_key);
+ } else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH)
+ priv->phase2_private_key = path_to_scheme_value (key_path);
+ else
+ g_assert_not_reached ();
+
+ /* As required by NM and wpa_supplicant, set the client-cert
+ * property to the same PKCS#12 data.
+ */
+ g_assert (format != NM_CRYPTO_FILE_FORMAT_UNKNOWN);
+ if (format == NM_CRYPTO_FILE_FORMAT_PKCS12) {
+ if (priv->phase2_client_cert)
+ g_byte_array_free (priv->phase2_client_cert, TRUE);
+
+ priv->phase2_client_cert = g_byte_array_sized_new (priv->phase2_private_key->len);
+ g_byte_array_append (priv->phase2_client_cert, priv->phase2_private_key->data, priv->phase2_private_key->len);
+ g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_CLIENT_CERT);
+ }
+
+ g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_PRIVATE_KEY);
+ if (password_cleared || password)
+ g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD);
+
+ if (out_format)
+ *out_format = (NMSetting8021xCKFormat) format;
+ return priv->phase2_private_key != NULL;
+}
+
+/**
+ * nm_setting_802_1x_get_phase2_private_key_format:
+ * @setting: the #NMSetting8021x
+ *
+ * Returns: the data format of the "phase 2" private key data stored in the
+ * #NMSetting8021x:phase2-private-key property
+ **/
+NMSetting8021xCKFormat
+nm_setting_802_1x_get_phase2_private_key_format (NMSetting8021x *setting)
+{
+ NMSetting8021xPrivate *priv;
+ const char *path;
+ GError *error = NULL;
+
+ g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), NM_SETTING_802_1X_CK_FORMAT_UNKNOWN);
+ priv = NM_SETTING_802_1X_GET_PRIVATE (setting);
+
+ if (!priv->phase2_private_key)
+ return NM_SETTING_802_1X_CK_FORMAT_UNKNOWN;
+
+ switch (nm_setting_802_1x_get_phase2_private_key_scheme (setting)) {
+ case NM_SETTING_802_1X_CK_SCHEME_BLOB:
+ if (crypto_is_pkcs12_data (priv->phase2_private_key))
+ return NM_SETTING_802_1X_CK_FORMAT_PKCS12;
+ return NM_SETTING_802_1X_CK_FORMAT_RAW_KEY;
+ case NM_SETTING_802_1X_CK_SCHEME_PATH:
+ path = nm_setting_802_1x_get_phase2_private_key_path (setting);
+ if (crypto_is_pkcs12_file (path, &error))
+ return NM_SETTING_802_1X_CK_FORMAT_PKCS12;
+ if (error) {
+ /* Couldn't read the file or something */
+ g_error_free (error);
+ return NM_SETTING_802_1X_CK_FORMAT_UNKNOWN;
+ }
+ return NM_SETTING_802_1X_CK_FORMAT_RAW_KEY;
+ default:
+ break;
+ }
+
+ return NM_SETTING_802_1X_CK_FORMAT_UNKNOWN;
+}
+
+static void
+need_secrets_password (NMSetting8021x *self,
+ GPtrArray *secrets,
+ gboolean phase2)
+{
+ NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE (self);
+
+ if ( (!priv->password || !strlen (priv->password))
+ && (!priv->password_raw || !priv->password_raw->len)) {
+ g_ptr_array_add (secrets, NM_SETTING_802_1X_PASSWORD);
+ g_ptr_array_add (secrets, NM_SETTING_802_1X_PASSWORD_RAW);
+ }
+}
+
+static void
+need_secrets_sim (NMSetting8021x *self,
+ GPtrArray *secrets,
+ gboolean phase2)
+{
+ NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE (self);
+
+ if (!priv->pin || !strlen (priv->pin))
+ g_ptr_array_add (secrets, NM_SETTING_802_1X_PIN);
+}
+
+static gboolean
+need_private_key_password (const GByteArray *blob,
+ const char *path,
+ const char *password)
+{
+ NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
+
+ /* Private key password is required */
+ if (password) {
+ if (path)
+ format = crypto_verify_private_key (path, password, NULL);
+ else if (blob)
+ format = crypto_verify_private_key_data (blob, password, NULL);
+ else
+ g_warning ("%s: unknown private key password scheme", __func__);
+ }
+
+ return (format == NM_CRYPTO_FILE_FORMAT_UNKNOWN);
+}
+
+static void
+need_secrets_tls (NMSetting8021x *self,
+ GPtrArray *secrets,
+ gboolean phase2)
+{
+ NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE (self);
+ NMSetting8021xCKScheme scheme;
+ const GByteArray *blob = NULL;
+ const char *path = NULL;
+
+ if (phase2) {
+ scheme = nm_setting_802_1x_get_phase2_private_key_scheme (self);
+ if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH)
+ path = nm_setting_802_1x_get_phase2_private_key_path (self);
+ else if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB)
+ blob = nm_setting_802_1x_get_phase2_private_key_blob (self);
+ else {
+ g_warning ("%s: unknown phase2 private key scheme %d", __func__, scheme);
+ g_ptr_array_add (secrets, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY);
+ return;
+ }
+
+ if (need_private_key_password (blob, path, priv->phase2_private_key_password))
+ g_ptr_array_add (secrets, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD);
+ } else {
+ scheme = nm_setting_802_1x_get_private_key_scheme (self);
+ if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH)
+ path = nm_setting_802_1x_get_private_key_path (self);
+ else if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB)
+ blob = nm_setting_802_1x_get_private_key_blob (self);
+ else {
+ g_warning ("%s: unknown private key scheme %d", __func__, scheme);
+ g_ptr_array_add (secrets, NM_SETTING_802_1X_PRIVATE_KEY);
+ return;
+ }
+
+ if (need_private_key_password (blob, path, priv->private_key_password))
+ g_ptr_array_add (secrets, NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD);
+ }
+}
+
+static gboolean
+verify_tls (NMSetting8021x *self, gboolean phase2, GError **error)
+{
+ NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE (self);
+
+ if (phase2) {
+ if (!priv->phase2_client_cert) {
+ g_set_error_literal (error,
+ NM_SETTING_802_1X_ERROR,
+ NM_SETTING_802_1X_ERROR_MISSING_PROPERTY,
+ _("property is missing"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PHASE2_CLIENT_CERT);
+ return FALSE;
+ } else if (!priv->phase2_client_cert->len) {
+ g_set_error_literal (error,
+ NM_SETTING_802_1X_ERROR,
+ NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
+ _("property is empty"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PHASE2_CLIENT_CERT);
+ return FALSE;
+ }
+
+ /* Private key is required for TLS */
+ if (!priv->phase2_private_key) {
+ g_set_error_literal (error,
+ NM_SETTING_802_1X_ERROR,
+ NM_SETTING_802_1X_ERROR_MISSING_PROPERTY,
+ _("property is missing"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY);
+ return FALSE;
+ } else if (!priv->phase2_private_key->len) {
+ g_set_error_literal (error,
+ NM_SETTING_802_1X_ERROR,
+ NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
+ _("property is empty"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY);
+ return FALSE;
+ }
+
+ /* If the private key is PKCS#12, check that it matches the client cert */
+ if (crypto_is_pkcs12_data (priv->phase2_private_key)) {
+ if (priv->phase2_private_key->len != priv->phase2_client_cert->len) {
+ g_set_error (error,
+ NM_SETTING_802_1X_ERROR,
+ NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
+ _("has to match '%s' property for PKCS#12"),
+ NM_SETTING_802_1X_PHASE2_PRIVATE_KEY);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PHASE2_CLIENT_CERT);
+ return FALSE;
+ }
+
+ if (memcmp (priv->phase2_private_key->data,
+ priv->phase2_client_cert->data,
+ priv->phase2_private_key->len)) {
+ g_set_error (error,
+ NM_SETTING_802_1X_ERROR,
+ NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
+ _("has to match '%s' property for PKCS#12"),
+ NM_SETTING_802_1X_PHASE2_PRIVATE_KEY);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PHASE2_CLIENT_CERT);
+ return FALSE;
+ }
+ }
+ } else {
+ if (!priv->client_cert) {
+ g_set_error_literal (error,
+ NM_SETTING_802_1X_ERROR,
+ NM_SETTING_802_1X_ERROR_MISSING_PROPERTY,
+ _("property is missing"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_CLIENT_CERT);
+ return FALSE;
+ } else if (!priv->client_cert->len) {
+ g_set_error_literal (error,
+ NM_SETTING_802_1X_ERROR,
+ NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
+ _("property is empty"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_CLIENT_CERT);
+ return FALSE;
+ }
+
+ /* Private key is required for TLS */
+ if (!priv->private_key) {
+ g_set_error_literal (error,
+ NM_SETTING_802_1X_ERROR,
+ NM_SETTING_802_1X_ERROR_MISSING_PROPERTY,
+ _("property is missing"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PRIVATE_KEY);
+ return FALSE;
+ } else if (!priv->private_key->len) {
+ g_set_error_literal (error,
+ NM_SETTING_802_1X_ERROR,
+ NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
+ _("property is empty"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PRIVATE_KEY);
+ return FALSE;
+ }
+
+ /* If the private key is PKCS#12, check that it matches the client cert */
+ if (crypto_is_pkcs12_data (priv->private_key)) {
+ if (priv->private_key->len != priv->client_cert->len) {
+ g_set_error (error,
+ NM_SETTING_802_1X_ERROR,
+ NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
+ _("has to match '%s' property for PKCS#12"),
+ NM_SETTING_802_1X_PRIVATE_KEY);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_CLIENT_CERT);
+ return FALSE;
+ }
+
+ if (memcmp (priv->private_key->data,
+ priv->client_cert->data,
+ priv->private_key->len)) {
+ g_set_error (error,
+ NM_SETTING_802_1X_ERROR,
+ NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
+ _("has to match '%s' property for PKCS#12"),
+ NM_SETTING_802_1X_PRIVATE_KEY);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_CLIENT_CERT);
+ return FALSE;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean
+verify_ttls (NMSetting8021x *self, gboolean phase2, GError **error)
+{
+ NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE (self);
+
+ if ( (!priv->identity || !strlen (priv->identity))
+ && (!priv->anonymous_identity || !strlen (priv->anonymous_identity))) {
+ if (!priv->identity) {
+ g_set_error_literal (error,
+ NM_SETTING_802_1X_ERROR,
+ NM_SETTING_802_1X_ERROR_MISSING_PROPERTY,
+ _("property is missing"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_IDENTITY);
+ } else if (!strlen (priv->identity)) {
+ g_set_error_literal (error,
+ NM_SETTING_802_1X_ERROR,
+ NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
+ _("property is empty"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_IDENTITY);
+ } else if (!priv->anonymous_identity) {
+ g_set_error_literal (error,
+ NM_SETTING_802_1X_ERROR,
+ NM_SETTING_802_1X_ERROR_MISSING_PROPERTY,
+ _("property is missing"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_ANONYMOUS_IDENTITY);
+ } else {
+ g_set_error_literal (error,
+ NM_SETTING_802_1X_ERROR,
+ NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
+ _("property is empty"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_ANONYMOUS_IDENTITY);
+ }
+ return FALSE;
+ }
+
+ if ( (!priv->phase2_auth || !strlen (priv->phase2_auth))
+ && (!priv->phase2_autheap || !strlen (priv->phase2_autheap))) {
+ if (!priv->phase2_auth) {
+ g_set_error_literal (error,
+ NM_SETTING_802_1X_ERROR,
+ NM_SETTING_802_1X_ERROR_MISSING_PROPERTY,
+ _("property is missing"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PHASE2_AUTH);
+ } else if (!strlen (priv->phase2_auth)) {
+ g_set_error_literal (error,
+ NM_SETTING_802_1X_ERROR,
+ NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
+ _("property is empty"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PHASE2_AUTH);
+ } else if (!priv->phase2_autheap) {
+ g_set_error_literal (error,
+ NM_SETTING_802_1X_ERROR,
+ NM_SETTING_802_1X_ERROR_MISSING_PROPERTY,
+ _("property is missing"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PHASE2_AUTHEAP);
+ } else {
+ g_set_error_literal (error,
+ NM_SETTING_802_1X_ERROR,
+ NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
+ _("property is empty"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PHASE2_AUTHEAP);
+ }
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+verify_identity (NMSetting8021x *self, gboolean phase2, GError **error)
+{
+ NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE (self);
+
+ if (!priv->identity) {
+ g_set_error_literal (error,
+ NM_SETTING_802_1X_ERROR,
+ NM_SETTING_802_1X_ERROR_MISSING_PROPERTY,
+ _("property is missing"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_IDENTITY);
+ return FALSE;
+ } else if (!strlen (priv->identity)) {
+ g_set_error_literal (error,
+ NM_SETTING_802_1X_ERROR,
+ NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
+ _("property is empty"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_IDENTITY);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* Implemented below... */
+static void need_secrets_phase2 (NMSetting8021x *self,
+ GPtrArray *secrets,
+ gboolean phase2);
+
+
+typedef void (*EAPMethodNeedSecretsFunc) (NMSetting8021x *self,
+ GPtrArray *secrets,
+ gboolean phase2);
+
+typedef gboolean (*EAPMethodValidateFunc)(NMSetting8021x *self,
+ gboolean phase2,
+ GError **error);
+
+typedef struct {
+ const char *method;
+ EAPMethodNeedSecretsFunc ns_func;
+ EAPMethodValidateFunc v_func;
+} EAPMethodsTable;
+
+static EAPMethodsTable eap_methods_table[] = {
+ { "leap", need_secrets_password, verify_identity },
+ { "pwd", need_secrets_password, verify_identity },
+ { "md5", need_secrets_password, verify_identity },
+ { "pap", need_secrets_password, verify_identity },
+ { "chap", need_secrets_password, verify_identity },
+ { "mschap", need_secrets_password, verify_identity },
+ { "mschapv2", need_secrets_password, verify_identity },
+ { "fast", need_secrets_password, verify_identity },
+ { "tls", need_secrets_tls, verify_tls },
+ { "peap", need_secrets_phase2, verify_ttls },
+ { "ttls", need_secrets_phase2, verify_ttls },
+ { "sim", need_secrets_sim, NULL },
+ { "gtc", need_secrets_password, verify_identity },
+ { "otp", NULL, NULL }, // FIXME: implement
+ { NULL, NULL, NULL }
+};
+
+static void
+need_secrets_phase2 (NMSetting8021x *self,
+ GPtrArray *secrets,
+ gboolean phase2)
+{
+ NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE (self);
+ char *method = NULL;
+ int i;
+
+ g_return_if_fail (phase2 == FALSE);
+
+ /* Check phase2_auth and phase2_autheap */
+ method = priv->phase2_auth;
+ if (!method && priv->phase2_autheap)
+ method = priv->phase2_autheap;
+
+ if (!method) {
+ g_warning ("Couldn't find EAP method.");
+ g_assert_not_reached();
+ return;
+ }
+
+ /* Ask the configured phase2 method if it needs secrets */
+ for (i = 0; eap_methods_table[i].method; i++) {
+ if (eap_methods_table[i].ns_func == NULL)
+ continue;
+ if (!strcmp (eap_methods_table[i].method, method)) {
+ (*eap_methods_table[i].ns_func) (self, secrets, TRUE);
+ break;
+ }
+ }
+}
+
+
+static GPtrArray *
+need_secrets (NMSetting *setting)
+{
+ NMSetting8021x *self = NM_SETTING_802_1X (setting);
+ NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE (self);
+ GSList *iter;
+ GPtrArray *secrets;
+ gboolean eap_method_found = FALSE;
+
+ secrets = g_ptr_array_sized_new (4);
+
+ /* Ask each configured EAP method if it needs secrets */
+ for (iter = priv->eap; iter && !eap_method_found; iter = g_slist_next (iter)) {
+ const char *method = (const char *) iter->data;
+ int i;
+
+ for (i = 0; eap_methods_table[i].method; i++) {
+ if (eap_methods_table[i].ns_func == NULL)
+ continue;
+ if (!strcmp (eap_methods_table[i].method, method)) {
+ (*eap_methods_table[i].ns_func) (self, secrets, FALSE);
+
+ /* Only break out of the outer loop if this EAP method
+ * needed secrets.
+ */
+ if (secrets->len > 0)
+ eap_method_found = TRUE;
+ break;
+ }
+ }
+ }
+
+ if (secrets->len == 0) {
+ g_ptr_array_free (secrets, TRUE);
+ secrets = NULL;
+ }
+
+ return secrets;
+}
+
+static gboolean
+verify_cert (GByteArray *array, const char *prop_name, GError **error)
+{
+ if (!array)
+ return TRUE;
+
+ switch (get_cert_scheme (array)) {
+ case NM_SETTING_802_1X_CK_SCHEME_BLOB:
+ return TRUE;
+ case NM_SETTING_802_1X_CK_SCHEME_PATH:
+ /* For path-based schemes, verify that the path is zero-terminated */
+ if (array->data[array->len - 1] == '\0') {
+ /* And ensure it's UTF-8 valid too so we can pass it through
+ * D-Bus and stuff like that.
+ */
+ if (g_utf8_validate ((const char *) (array->data + strlen (SCHEME_PATH)), -1, NULL))
+ return TRUE;
+ }
+ break;
+ default:
+ break;
+ }
+
+ g_set_error_literal (error,
+ NM_SETTING_802_1X_ERROR,
+ NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
+ _("property is invalid"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, prop_name);
+ return FALSE;
+}
+
+static gboolean
+verify (NMSetting *setting, GSList *all_settings, GError **error)
+{
+ NMSetting8021x *self = NM_SETTING_802_1X (setting);
+ NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE (self);
+ const char *valid_eap[] = { "leap", "md5", "tls", "peap", "ttls", "sim", "fast", "pwd", NULL };
+ const char *valid_phase1_peapver[] = { "0", "1", NULL };
+ const char *valid_phase1_peaplabel[] = { "0", "1", NULL };
+ const char *valid_phase1_fast_pac[] = { "0", "1", "2", "3", NULL };
+ const char *valid_phase2_auth[] = { "pap", "chap", "mschap", "mschapv2", "gtc", "otp", "md5", "tls", NULL };
+ const char *valid_phase2_autheap[] = { "md5", "mschapv2", "otp", "gtc", "tls", NULL };
+ GSList *iter;
+
+ if (error)
+ g_return_val_if_fail (*error == NULL, FALSE);
+
+ if (!priv->eap) {
+ g_set_error_literal (error,
+ NM_SETTING_802_1X_ERROR,
+ NM_SETTING_802_1X_ERROR_MISSING_PROPERTY,
+ _("property is missing"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_EAP);
+ return FALSE;
+ }
+
+ if (!_nm_utils_string_slist_validate (priv->eap, valid_eap)) {
+ g_set_error_literal (error,
+ NM_SETTING_802_1X_ERROR,
+ NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
+ _("property is invalid"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_EAP);
+ return FALSE;
+ }
+
+ /* Ask each configured EAP method if its valid */
+ for (iter = priv->eap; iter; iter = g_slist_next (iter)) {
+ const char *method = (const char *) iter->data;
+ int i;
+
+ for (i = 0; eap_methods_table[i].method; i++) {
+ if (eap_methods_table[i].v_func == NULL)
+ continue;
+ if (!strcmp (eap_methods_table[i].method, method)) {
+ if (!(*eap_methods_table[i].v_func) (self, FALSE, error))
+ return FALSE;
+ break;
+ }
+ }
+ }
+
+ if (priv->phase1_peapver && !_nm_utils_string_in_list (priv->phase1_peapver, valid_phase1_peapver)) {
+ g_set_error (error,
+ NM_SETTING_802_1X_ERROR,
+ NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
+ _("'%s' is not a valid value for the property"),
+ priv->phase1_peapver);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PHASE1_PEAPVER);
+ return FALSE;
+ }
+
+ if (priv->phase1_peaplabel && !_nm_utils_string_in_list (priv->phase1_peaplabel, valid_phase1_peaplabel)) {
+ g_set_error (error,
+ NM_SETTING_802_1X_ERROR,
+ NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
+ _("'%s' is not a valid value for the property"),
+ priv->phase1_peaplabel);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PHASE1_PEAPLABEL);
+ return FALSE;
+ }
+
+ if (priv->phase1_fast_provisioning && !_nm_utils_string_in_list (priv->phase1_fast_provisioning, valid_phase1_fast_pac)) {
+ g_set_error (error,
+ NM_SETTING_802_1X_ERROR,
+ NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
+ _("'%s' is not a valid value for the property"),
+ priv->phase1_fast_provisioning);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PHASE1_FAST_PROVISIONING);
+ return FALSE;
+ }
+
+ if (priv->phase2_auth && !_nm_utils_string_in_list (priv->phase2_auth, valid_phase2_auth)) {
+ g_set_error (error,
+ NM_SETTING_802_1X_ERROR,
+ NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
+ _("'%s' is not a valid value for the property"),
+ priv->phase2_auth);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PHASE2_AUTH);
+ return FALSE;
+ }
+
+ if (priv->phase2_autheap && !_nm_utils_string_in_list (priv->phase2_autheap, valid_phase2_autheap)) {
+ g_set_error (error,
+ NM_SETTING_802_1X_ERROR,
+ NM_SETTING_802_1X_ERROR_INVALID_PROPERTY,
+ _("'%s' is not a valid value for the property"),
+ priv->phase2_autheap);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PHASE2_AUTHEAP);
+ return FALSE;
+ }
+
+ if (!verify_cert (priv->ca_cert, NM_SETTING_802_1X_CA_CERT, error))
+ return FALSE;
+ if (!verify_cert (priv->phase2_ca_cert, NM_SETTING_802_1X_PHASE2_CA_CERT, error))
+ return FALSE;
+
+ if (!verify_cert (priv->client_cert, NM_SETTING_802_1X_CLIENT_CERT, error))
+ return FALSE;
+ if (!verify_cert (priv->phase2_client_cert, NM_SETTING_802_1X_PHASE2_CLIENT_CERT, error))
+ return FALSE;
+
+ if (!verify_cert (priv->private_key, NM_SETTING_802_1X_PRIVATE_KEY, error))
+ return FALSE;
+ if (!verify_cert (priv->phase2_private_key, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY, error))
+ return FALSE;
+
+ /* FIXME: finish */
+
+ return TRUE;
+}
+
+static void
+nm_setting_802_1x_init (NMSetting8021x *setting)
+{
+}
+
+static void
+finalize (GObject *object)
+{
+ NMSetting8021x *self = NM_SETTING_802_1X (object);
+ NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE (self);
+
+ /* Strings first. g_free() already checks for NULLs so we don't have to */
+
+ g_free (priv->identity);
+ g_free (priv->anonymous_identity);
+ g_free (priv->ca_path);
+ g_free (priv->subject_match);
+ g_free (priv->phase1_peapver);
+ g_free (priv->phase1_peaplabel);
+ g_free (priv->phase1_fast_provisioning);
+ g_free (priv->phase2_auth);
+ g_free (priv->phase2_autheap);
+ g_free (priv->phase2_ca_path);
+ g_free (priv->phase2_subject_match);
+ g_free (priv->password);
+ if (priv->password_raw)
+ g_byte_array_free (priv->password_raw, TRUE);
+ g_free (priv->pin);
+
+ g_slist_free_full (priv->eap, g_free);
+ g_slist_free_full (priv->altsubject_matches, g_free);
+ g_slist_free_full (priv->phase2_altsubject_matches, g_free);
+
+ if (priv->ca_cert)
+ g_byte_array_free (priv->ca_cert, TRUE);
+ if (priv->client_cert)
+ g_byte_array_free (priv->client_cert, TRUE);
+ if (priv->private_key)
+ g_byte_array_free (priv->private_key, TRUE);
+ g_free (priv->private_key_password);
+ if (priv->phase2_ca_cert)
+ g_byte_array_free (priv->phase2_ca_cert, TRUE);
+ if (priv->phase2_client_cert)
+ g_byte_array_free (priv->phase2_client_cert, TRUE);
+ if (priv->phase2_private_key)
+ g_byte_array_free (priv->phase2_private_key, TRUE);
+ g_free (priv->phase2_private_key_password);
+
+ G_OBJECT_CLASS (nm_setting_802_1x_parent_class)->finalize (object);
+}
+
+static GByteArray *
+set_cert_prop_helper (const GValue *value, const char *prop_name, GError **error)
+{
+ gboolean valid;
+ GByteArray *data = NULL;
+
+ data = g_value_dup_boxed (value);
+ /* Verify the new data */
+ if (data) {
+ valid = verify_cert (data, prop_name, error);
+ if (!valid) {
+ g_byte_array_free (data, TRUE);
+ data = NULL;
+ }
+ }
+ return data;
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ NMSetting8021x *setting = NM_SETTING_802_1X (object);
+ NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE (setting);
+ GError *error = NULL;
+
+ switch (prop_id) {
+ case PROP_EAP:
+ g_slist_free_full (priv->eap, g_free);
+ priv->eap = g_value_dup_boxed (value);
+ break;
+ case PROP_IDENTITY:
+ g_free (priv->identity);
+ priv->identity = g_value_dup_string (value);
+ break;
+ case PROP_ANONYMOUS_IDENTITY:
+ g_free (priv->anonymous_identity);
+ priv->anonymous_identity = g_value_dup_string (value);
+ break;
+ case PROP_PAC_FILE:
+ g_free (priv->pac_file);
+ priv->pac_file = g_value_dup_string (value);
+ break;
+ case PROP_CA_CERT:
+ if (priv->ca_cert) {
+ g_byte_array_free (priv->ca_cert, TRUE);
+ priv->ca_cert = NULL;
+ }
+ priv->ca_cert = set_cert_prop_helper (value, NM_SETTING_802_1X_CA_CERT, &error);
+ if (error) {
+ g_warning ("Error setting certificate (invalid data): (%d) %s",
+ error->code, error->message);
+ g_error_free (error);
+ }
+ break;
+ case PROP_CA_PATH:
+ g_free (priv->ca_path);
+ priv->ca_path = g_value_dup_string (value);
+ break;
+ case PROP_SUBJECT_MATCH:
+ g_free (priv->subject_match);
+ priv->subject_match = g_value_dup_string (value);
+ break;
+ case PROP_ALTSUBJECT_MATCHES:
+ g_slist_free_full (priv->altsubject_matches, g_free);
+ priv->altsubject_matches = g_value_dup_boxed (value);
+ break;
+ case PROP_CLIENT_CERT:
+ if (priv->client_cert) {
+ g_byte_array_free (priv->client_cert, TRUE);
+ priv->client_cert = NULL;
+ }
+ priv->client_cert = set_cert_prop_helper (value, NM_SETTING_802_1X_CLIENT_CERT, &error);
+ if (error) {
+ g_warning ("Error setting certificate (invalid data): (%d) %s",
+ error->code, error->message);
+ g_error_free (error);
+ }
+ break;
+ case PROP_PHASE1_PEAPVER:
+ g_free (priv->phase1_peapver);
+ priv->phase1_peapver = g_value_dup_string (value);
+ break;
+ case PROP_PHASE1_PEAPLABEL:
+ g_free (priv->phase1_peaplabel);
+ priv->phase1_peaplabel = g_value_dup_string (value);
+ break;
+ case PROP_PHASE1_FAST_PROVISIONING:
+ g_free (priv->phase1_fast_provisioning);
+ priv->phase1_fast_provisioning = g_value_dup_string (value);
+ break;
+ case PROP_PHASE2_AUTH:
+ g_free (priv->phase2_auth);
+ priv->phase2_auth = g_value_dup_string (value);
+ break;
+ case PROP_PHASE2_AUTHEAP:
+ g_free (priv->phase2_autheap);
+ priv->phase2_autheap = g_value_dup_string (value);
+ break;
+ case PROP_PHASE2_CA_CERT:
+ if (priv->phase2_ca_cert) {
+ g_byte_array_free (priv->phase2_ca_cert, TRUE);
+ priv->phase2_ca_cert = NULL;
+ }
+ priv->phase2_ca_cert = set_cert_prop_helper (value, NM_SETTING_802_1X_PHASE2_CA_CERT, &error);
+ if (error) {
+ g_warning ("Error setting certificate (invalid data): (%d) %s",
+ error->code, error->message);
+ g_error_free (error);
+ }
+ break;
+ case PROP_PHASE2_CA_PATH:
+ g_free (priv->phase2_ca_path);
+ priv->phase2_ca_path = g_value_dup_string (value);
+ break;
+ case PROP_PHASE2_SUBJECT_MATCH:
+ g_free (priv->phase2_subject_match);
+ priv->phase2_subject_match = g_value_dup_string (value);
+ break;
+ case PROP_PHASE2_ALTSUBJECT_MATCHES:
+ g_slist_free_full (priv->phase2_altsubject_matches, g_free);
+ priv->phase2_altsubject_matches = g_value_dup_boxed (value);
+ break;
+ case PROP_PHASE2_CLIENT_CERT:
+ if (priv->phase2_client_cert) {
+ g_byte_array_free (priv->phase2_client_cert, TRUE);
+ priv->phase2_client_cert = NULL;
+ }
+ priv->phase2_client_cert = set_cert_prop_helper (value, NM_SETTING_802_1X_PHASE2_CLIENT_CERT, &error);
+ if (error) {
+ g_warning ("Error setting certificate (invalid data): (%d) %s",
+ error->code, error->message);
+ g_error_free (error);
+ }
+ break;
+ case PROP_PASSWORD:
+ g_free (priv->password);
+ priv->password = g_value_dup_string (value);
+ break;
+ case PROP_PASSWORD_FLAGS:
+ priv->password_flags = g_value_get_uint (value);
+ break;
+ case PROP_PASSWORD_RAW:
+ if (priv->password_raw)
+ g_byte_array_free (priv->password_raw, TRUE);
+ priv->password_raw = g_value_dup_boxed (value);
+ break;
+ case PROP_PASSWORD_RAW_FLAGS:
+ priv->password_raw_flags = g_value_get_uint (value);
+ break;
+ case PROP_PRIVATE_KEY:
+ if (priv->private_key) {
+ g_byte_array_free (priv->private_key, TRUE);
+ priv->private_key = NULL;
+ }
+ priv->private_key = set_cert_prop_helper (value, NM_SETTING_802_1X_PRIVATE_KEY, &error);
+ if (error) {
+ g_warning ("Error setting private key (invalid data): (%d) %s",
+ error->code, error->message);
+ g_error_free (error);
+ }
+ break;
+ case PROP_PRIVATE_KEY_PASSWORD:
+ g_free (priv->private_key_password);
+ priv->private_key_password = g_value_dup_string (value);
+ break;
+ case PROP_PRIVATE_KEY_PASSWORD_FLAGS:
+ priv->private_key_password_flags = g_value_get_uint (value);
+ break;
+ case PROP_PHASE2_PRIVATE_KEY:
+ if (priv->phase2_private_key) {
+ g_byte_array_free (priv->phase2_private_key, TRUE);
+ priv->phase2_private_key = NULL;
+ }
+ priv->phase2_private_key = set_cert_prop_helper (value, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY, &error);
+ if (error) {
+ g_warning ("Error setting private key (invalid data): (%d) %s",
+ error->code, error->message);
+ g_error_free (error);
+ }
+ break;
+ case PROP_PHASE2_PRIVATE_KEY_PASSWORD:
+ g_free (priv->phase2_private_key_password);
+ priv->phase2_private_key_password = g_value_dup_string (value);
+ break;
+ case PROP_PHASE2_PRIVATE_KEY_PASSWORD_FLAGS:
+ priv->phase2_private_key_password_flags = g_value_get_uint (value);
+ break;
+ case PROP_PIN:
+ g_free (priv->pin);
+ priv->pin = g_value_dup_string (value);
+ break;
+ case PROP_PIN_FLAGS:
+ priv->pin_flags = g_value_get_uint (value);
+ break;
+ case PROP_SYSTEM_CA_CERTS:
+ priv->system_ca_certs = g_value_get_boolean (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ NMSetting8021x *setting = NM_SETTING_802_1X (object);
+ NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE (setting);
+
+ switch (prop_id) {
+ case PROP_EAP:
+ g_value_set_boxed (value, priv->eap);
+ break;
+ case PROP_IDENTITY:
+ g_value_set_string (value, priv->identity);
+ break;
+ case PROP_ANONYMOUS_IDENTITY:
+ g_value_set_string (value, priv->anonymous_identity);
+ break;
+ case PROP_PAC_FILE:
+ g_value_set_string (value, priv->pac_file);
+ break;
+ case PROP_CA_CERT:
+ g_value_set_boxed (value, priv->ca_cert);
+ break;
+ case PROP_CA_PATH:
+ g_value_set_string (value, priv->ca_path);
+ break;
+ case PROP_SUBJECT_MATCH:
+ g_value_set_string (value, priv->subject_match);
+ break;
+ case PROP_ALTSUBJECT_MATCHES:
+ g_value_set_boxed (value, priv->altsubject_matches);
+ break;
+ case PROP_CLIENT_CERT:
+ g_value_set_boxed (value, priv->client_cert);
+ break;
+ case PROP_PHASE1_PEAPVER:
+ g_value_set_string (value, priv->phase1_peapver);
+ break;
+ case PROP_PHASE1_PEAPLABEL:
+ g_value_set_string (value, priv->phase1_peaplabel);
+ break;
+ case PROP_PHASE1_FAST_PROVISIONING:
+ g_value_set_string (value, priv->phase1_fast_provisioning);
+ break;
+ case PROP_PHASE2_AUTH:
+ g_value_set_string (value, priv->phase2_auth);
+ break;
+ case PROP_PHASE2_AUTHEAP:
+ g_value_set_string (value, priv->phase2_autheap);
+ break;
+ case PROP_PHASE2_CA_CERT:
+ g_value_set_boxed (value, priv->phase2_ca_cert);
+ break;
+ case PROP_PHASE2_CA_PATH:
+ g_value_set_string (value, priv->phase2_ca_path);
+ break;
+ case PROP_PHASE2_SUBJECT_MATCH:
+ g_value_set_string (value, priv->phase2_subject_match);
+ break;
+ case PROP_PHASE2_ALTSUBJECT_MATCHES:
+ g_value_set_boxed (value, priv->phase2_altsubject_matches);
+ break;
+ case PROP_PHASE2_CLIENT_CERT:
+ g_value_set_boxed (value, priv->phase2_client_cert);
+ break;
+ case PROP_PASSWORD:
+ g_value_set_string (value, priv->password);
+ break;
+ case PROP_PASSWORD_FLAGS:
+ g_value_set_uint (value, priv->password_flags);
+ break;
+ case PROP_PASSWORD_RAW:
+ g_value_set_boxed (value, priv->password_raw);
+ break;
+ case PROP_PASSWORD_RAW_FLAGS:
+ g_value_set_uint (value, priv->password_raw_flags);
+ break;
+ case PROP_PRIVATE_KEY:
+ g_value_set_boxed (value, priv->private_key);
+ break;
+ case PROP_PRIVATE_KEY_PASSWORD:
+ g_value_set_string (value, priv->private_key_password);
+ break;
+ case PROP_PRIVATE_KEY_PASSWORD_FLAGS:
+ g_value_set_uint (value, priv->private_key_password_flags);
+ break;
+ case PROP_PHASE2_PRIVATE_KEY:
+ g_value_set_boxed (value, priv->phase2_private_key);
+ break;
+ case PROP_PHASE2_PRIVATE_KEY_PASSWORD:
+ g_value_set_string (value, priv->phase2_private_key_password);
+ break;
+ case PROP_PHASE2_PRIVATE_KEY_PASSWORD_FLAGS:
+ g_value_set_uint (value, priv->phase2_private_key_password_flags);
+ break;
+ case PROP_PIN:
+ g_value_set_string (value, priv->pin);
+ break;
+ case PROP_PIN_FLAGS:
+ g_value_set_uint (value, priv->pin_flags);
+ break;
+ case PROP_SYSTEM_CA_CERTS:
+ g_value_set_boolean (value, priv->system_ca_certs);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nm_setting_802_1x_class_init (NMSetting8021xClass *setting_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
+ NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+ GError *error = NULL;
+
+ g_type_class_add_private (setting_class, sizeof (NMSetting8021xPrivate));
+
+ /* virtual methods */
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+ object_class->finalize = finalize;
+
+ parent_class->verify = verify;
+ parent_class->need_secrets = need_secrets;
+
+ /* Properties */
+
+ /**
+ * NMSetting8021x:eap:
+ *
+ * The allowed EAP method to be used when authenticating to the network with
+ * 802.1x. Valid methods are: "leap", "md5", "tls", "peap", "ttls", "pwd",
+ * and "fast". Each method requires different configuration using the
+ * properties of this setting; refer to wpa_supplicant documentation for the
+ * allowed combinations.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_EAP,
+ _nm_param_spec_specialized (NM_SETTING_802_1X_EAP, "", "",
+ DBUS_TYPE_G_LIST_OF_STRING,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSetting8021x:identity:
+ *
+ * Identity string for EAP authentication methods. Often the user's user or
+ * login name.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_IDENTITY,
+ g_param_spec_string (NM_SETTING_802_1X_IDENTITY, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSetting8021x:anonymous-identity:
+ *
+ * Anonymous identity string for EAP authentication methods. Used as the
+ * unencrypted identity with EAP types that support different tunneled
+ * identity like EAP-TTLS.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_ANONYMOUS_IDENTITY,
+ g_param_spec_string (NM_SETTING_802_1X_ANONYMOUS_IDENTITY, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSetting8021x:pac-file:
+ *
+ * UTF-8 encoded file path containing PAC for EAP-FAST.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PAC_FILE,
+ g_param_spec_string (NM_SETTING_802_1X_PAC_FILE, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSetting8021x:ca-cert:
+ *
+ * Contains the CA certificate if used by the EAP method specified in the
+ * #NMSetting8021x:eap property.
+ *
+ * Certificate data is specified using a "scheme"; two are currently
+ * supported: blob and path. When using the blob scheme (which is backwards
+ * compatible with NM 0.7.x) this property should be set to the
+ * certificate's DER encoded data. When using the path scheme, this property
+ * should be set to the full UTF-8 encoded path of the certificate, prefixed
+ * with the string "file://" and ending with a terminating NUL byte. This
+ * property can be unset even if the EAP method supports CA certificates,
+ * but this allows man-in-the-middle attacks and is NOT recommended.
+ *
+ * Setting this property directly is discouraged; use the
+ * nm_setting_802_1x_set_ca_cert() function instead.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_CA_CERT,
+ _nm_param_spec_specialized (NM_SETTING_802_1X_CA_CERT, "", "",
+ DBUS_TYPE_G_UCHAR_ARRAY,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSetting8021x:ca-path:
+ *
+ * UTF-8 encoded path to a directory containing PEM or DER formatted
+ * certificates to be added to the verification chain in addition to the
+ * certificate specified in the #NMSetting8021x:ca-cert property.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_CA_PATH,
+ g_param_spec_string (NM_SETTING_802_1X_CA_PATH, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSetting8021x:subject-match:
+ *
+ * Substring to be matched against the subject of the certificate presented
+ * by the authentication server. When unset, no verification of the
+ * authentication server certificate's subject is performed.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_SUBJECT_MATCH,
+ g_param_spec_string (NM_SETTING_802_1X_SUBJECT_MATCH, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSetting8021x:altsubject-matches:
+ *
+ * List of strings to be matched against the altSubjectName of the
+ * certificate presented by the authentication server. If the list is empty,
+ * no verification of the server certificate's altSubjectName is performed.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_ALTSUBJECT_MATCHES,
+ _nm_param_spec_specialized (NM_SETTING_802_1X_ALTSUBJECT_MATCHES, "", "",
+ DBUS_TYPE_G_LIST_OF_STRING,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSetting8021x:client-cert:
+ *
+ * Contains the client certificate if used by the EAP method specified in
+ * the #NMSetting8021x:eap property.
+ *
+ * Certificate data is specified using a "scheme"; two are currently
+ * supported: blob and path. When using the blob scheme (which is backwards
+ * compatible with NM 0.7.x) this property should be set to the
+ * certificate's DER encoded data. When using the path scheme, this property
+ * should be set to the full UTF-8 encoded path of the certificate, prefixed
+ * with the string "file://" and ending with a terminating NUL byte.
+ *
+ * Setting this property directly is discouraged; use the
+ * nm_setting_802_1x_set_client_cert() function instead.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_CLIENT_CERT,
+ _nm_param_spec_specialized (NM_SETTING_802_1X_CLIENT_CERT, "", "",
+ DBUS_TYPE_G_UCHAR_ARRAY,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSetting8021x:phase1-peapver:
+ *
+ * Forces which PEAP version is used when PEAP is set as the EAP method in
+ * the #NMSetting8021x:eap property. When unset, the version reported by
+ * the server will be used. Sometimes when using older RADIUS servers, it
+ * is necessary to force the client to use a particular PEAP version. To do
+ * so, this property may be set to "0" or "1" to force that specific PEAP
+ * version.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PHASE1_PEAPVER,
+ g_param_spec_string (NM_SETTING_802_1X_PHASE1_PEAPVER, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSetting8021x:phase1-peaplabel:
+ *
+ * Forces use of the new PEAP label during key derivation. Some RADIUS
+ * servers may require forcing the new PEAP label to interoperate with
+ * PEAPv1. Set to "1" to force use of the new PEAP label. See the
+ * wpa_supplicant documentation for more details.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PHASE1_PEAPLABEL,
+ g_param_spec_string (NM_SETTING_802_1X_PHASE1_PEAPLABEL, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSetting8021x:phase1-fast-provisioning:
+ *
+ * Enables or disables in-line provisioning of EAP-FAST credentials when
+ * FAST is specified as the EAP method in the #NMSetting8021x:eap property.
+ * Recognized values are "0" (disabled), "1" (allow unauthenticated
+ * provisioning), "2" (allow authenticated provisioning), and "3" (allow
+ * both authenticated and unauthenticated provisioning). See the
+ * wpa_supplicant documentation for more details.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PHASE1_FAST_PROVISIONING,
+ g_param_spec_string (NM_SETTING_802_1X_PHASE1_FAST_PROVISIONING, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSetting8021x:phase2-auth:
+ *
+ * Specifies the allowed "phase 2" inner non-EAP authentication methods when
+ * an EAP method that uses an inner TLS tunnel is specified in the
+ * #NMSetting8021x:eap property. Recognized non-EAP "phase 2" methods are
+ * "pap", "chap", "mschap", "mschapv2", "gtc", "otp", "md5", and "tls".
+ * Each "phase 2" inner method requires specific parameters for successful
+ * authentication; see the wpa_supplicant documentation for more details.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PHASE2_AUTH,
+ g_param_spec_string (NM_SETTING_802_1X_PHASE2_AUTH, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSetting8021x:phase2-autheap:
+ *
+ * Specifies the allowed "phase 2" inner EAP-based authentication methods
+ * when an EAP method that uses an inner TLS tunnel is specified in the
+ * #NMSetting8021x:eap property. Recognized EAP-based "phase 2" methods are
+ * "md5", "mschapv2", "otp", "gtc", and "tls". Each "phase 2" inner method
+ * requires specific parameters for successful authentication; see the
+ * wpa_supplicant documentation for more details.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PHASE2_AUTHEAP,
+ g_param_spec_string (NM_SETTING_802_1X_PHASE2_AUTHEAP, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSetting8021x:phase2-ca-cert:
+ *
+ * Contains the "phase 2" CA certificate if used by the EAP method specified
+ * in the #NMSetting8021x:phase2-auth or #NMSetting8021x:phase2-autheap
+ * properties.
+ *
+ * Certificate data is specified using a "scheme"; two are currently
+ * supported: blob and path. When using the blob scheme (which is backwards
+ * compatible with NM 0.7.x) this property should be set to the
+ * certificate's DER encoded data. When using the path scheme, this property
+ * should be set to the full UTF-8 encoded path of the certificate, prefixed
+ * with the string "file://" and ending with a terminating NUL byte. This
+ * property can be unset even if the EAP method supports CA certificates,
+ * but this allows man-in-the-middle attacks and is NOT recommended.
+ *
+ * Setting this property directly is discouraged; use the
+ * nm_setting_802_1x_set_phase2_ca_cert() function instead.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PHASE2_CA_CERT,
+ _nm_param_spec_specialized (NM_SETTING_802_1X_PHASE2_CA_CERT, "", "",
+ DBUS_TYPE_G_UCHAR_ARRAY,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSetting8021x:phase2-ca-path:
+ *
+ * UTF-8 encoded path to a directory containing PEM or DER formatted
+ * certificates to be added to the verification chain in addition to the
+ * certificate specified in the #NMSetting8021x:phase2-ca-cert property.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PHASE2_CA_PATH,
+ g_param_spec_string (NM_SETTING_802_1X_PHASE2_CA_PATH, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSetting8021x:phase2-subject-match:
+ *
+ * Substring to be matched against the subject of the certificate presented
+ * by the authentication server during the inner "phase 2"
+ * authentication. When unset, no verification of the authentication server
+ * certificate's subject is performed.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PHASE2_SUBJECT_MATCH,
+ g_param_spec_string (NM_SETTING_802_1X_PHASE2_SUBJECT_MATCH, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSetting8021x:phase2-altsubject-matches:
+ *
+ * List of strings to be matched against the altSubjectName of the
+ * certificate presented by the authentication server during the inner
+ * "phase 2" authentication. If the list is empty, no verification of the
+ * server certificate's altSubjectName is performed.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PHASE2_ALTSUBJECT_MATCHES,
+ _nm_param_spec_specialized (NM_SETTING_802_1X_PHASE2_ALTSUBJECT_MATCHES, "", "",
+ DBUS_TYPE_G_LIST_OF_STRING,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSetting8021x:phase2-client-cert:
+ *
+ * Contains the "phase 2" client certificate if used by the EAP method
+ * specified in the #NMSetting8021x:phase2-auth or
+ * #NMSetting8021x:phase2-autheap properties.
+ *
+ * Certificate data is specified using a "scheme"; two are currently
+ * supported: blob and path. When using the blob scheme (which is backwards
+ * compatible with NM 0.7.x) this property should be set to the
+ * certificate's DER encoded data. When using the path scheme, this property
+ * should be set to the full UTF-8 encoded path of the certificate, prefixed
+ * with the string "file://" and ending with a terminating NUL byte. This
+ * property can be unset even if the EAP method supports CA certificates,
+ * but this allows man-in-the-middle attacks and is NOT recommended.
+ *
+ * Setting this property directly is discouraged; use the
+ * nm_setting_802_1x_set_phase2_client_cert() function instead.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PHASE2_CLIENT_CERT,
+ _nm_param_spec_specialized (NM_SETTING_802_1X_PHASE2_CLIENT_CERT, "", "",
+ DBUS_TYPE_G_UCHAR_ARRAY,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSetting8021x:password:
+ *
+ * UTF-8 encoded password used for EAP authentication methods. If both the
+ * #NMSetting8021x:password property and the #NMSetting8021x:password-raw
+ * property are specified, #NMSetting8021x:password is preferred.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PASSWORD,
+ g_param_spec_string (NM_SETTING_802_1X_PASSWORD, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ NM_SETTING_PARAM_SECRET |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSetting8021x:password-flags:
+ *
+ * Flags indicating how to handle the #NMSetting8021x:password property.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PASSWORD_FLAGS,
+ g_param_spec_uint (NM_SETTING_802_1X_PASSWORD_FLAGS, "", "",
+ NM_SETTING_SECRET_FLAG_NONE,
+ NM_SETTING_SECRET_FLAGS_ALL,
+ NM_SETTING_SECRET_FLAG_NONE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSetting8021x:password-raw:
+ *
+ * Password used for EAP authentication methods, given as a byte array to
+ * allow passwords in other encodings than UTF-8 to be used. If both the
+ * #NMSetting8021x:password property and the #NMSetting8021x:password-raw
+ * property are specified, #NMSetting8021x:password is preferred.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PASSWORD_RAW,
+ _nm_param_spec_specialized (NM_SETTING_802_1X_PASSWORD_RAW, "", "",
+ DBUS_TYPE_G_UCHAR_ARRAY,
+ G_PARAM_READWRITE |
+ NM_SETTING_PARAM_SECRET |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSetting8021x:password-raw-flags:
+ *
+ * Flags indicating how to handle the #NMSetting8021x:password-raw property.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PASSWORD_RAW_FLAGS,
+ g_param_spec_uint (NM_SETTING_802_1X_PASSWORD_RAW_FLAGS, "", "",
+ NM_SETTING_SECRET_FLAG_NONE,
+ NM_SETTING_SECRET_FLAGS_ALL,
+ NM_SETTING_SECRET_FLAG_NONE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSetting8021x:private-key:
+ *
+ * Contains the private key when the #NMSetting8021x:eap property is set to
+ * "tls".
+ *
+ * Key data is specified using a "scheme"; two are currently supported: blob
+ * and path. When using the blob scheme and private keys, this property
+ * should be set to the key's encrypted PEM encoded data. When using private
+ * keys with the path scheme, this property should be set to the full UTF-8
+ * encoded path of the key, prefixed with the string "file://" and ending
+ * with a terminating NUL byte. When using PKCS#12 format private keys and
+ * the blob scheme, this property should be set to the PKCS#12 data and the
+ * #NMSetting8021x:private-key-password property must be set to password
+ * used to decrypt the PKCS#12 certificate and key. When using PKCS#12 files
+ * and the path scheme, this property should be set to the full UTF-8
+ * encoded path of the key, prefixed with the string "file://" and and
+ * ending with a terminating NUL byte, and as with the blob scheme the
+ * "private-key-password" property must be set to the password used to
+ * decode the PKCS#12 private key and certificate.
+ *
+ * Setting this property directly is discouraged; use the
+ * nm_setting_802_1x_set_private_key() function instead.
+ *
+ * WARNING: #NMSetting8021x:private-key is not a "secret" property, and thus
+ * unencrypted private key data using the BLOB scheme may be readable by
+ * unprivileged users. Private keys should always be encrypted with a
+ * private key password to prevent unauthorized access to unencrypted
+ * private key data.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PRIVATE_KEY,
+ _nm_param_spec_specialized (NM_SETTING_802_1X_PRIVATE_KEY, "", "",
+ DBUS_TYPE_G_UCHAR_ARRAY,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSetting8021x:private-key-password:
+ *
+ * The password used to decrypt the private key specified in the
+ * #NMSetting8021x:private-key property when the private key either uses the
+ * path scheme, or if the private key is a PKCS#12 format key. Setting this
+ * property directly is not generally necessary except when returning
+ * secrets to NetworkManager; it is generally set automatically when setting
+ * the private key by the nm_setting_802_1x_set_private_key() function.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PRIVATE_KEY_PASSWORD,
+ g_param_spec_string (NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ NM_SETTING_PARAM_SECRET |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSetting8021x:private-key-password-flags:
+ *
+ * Flags indicating how to handle the #NMSetting8021x:private-key-password
+ * property.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PRIVATE_KEY_PASSWORD_FLAGS,
+ g_param_spec_uint (NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD_FLAGS, "", "",
+ NM_SETTING_SECRET_FLAG_NONE,
+ NM_SETTING_SECRET_FLAGS_ALL,
+ NM_SETTING_SECRET_FLAG_NONE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSetting8021x:phase2-private-key:
+ *
+ * Contains the "phase 2" inner private key when the
+ * #NMSetting8021x:phase2-auth or #NMSetting8021x:phase2-autheap property is
+ * set to "tls".
+ *
+ * Key data is specified using a "scheme"; two are currently supported: blob
+ * and path. When using the blob scheme and private keys, this property
+ * should be set to the key's encrypted PEM encoded data. When using private
+ * keys with the path scheme, this property should be set to the full UTF-8
+ * encoded path of the key, prefixed with the string "file://" and ending
+ * with a terminating NUL byte. When using PKCS#12 format private keys and
+ * the blob scheme, this property should be set to the PKCS#12 data and the
+ * #NMSetting8021x:phase2-private-key-password property must be set to
+ * password used to decrypt the PKCS#12 certificate and key. When using
+ * PKCS#12 files and the path scheme, this property should be set to the
+ * full UTF-8 encoded path of the key, prefixed with the string "file://"
+ * and and ending with a terminating NUL byte, and as with the blob scheme
+ * the #NMSetting8021x:phase2-private-key-password property must be set to
+ * the password used to decode the PKCS#12 private key and certificate.
+ *
+ * Setting this property directly is discouraged; use the
+ * nm_setting_802_1x_set_phase2_private_key() function instead.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PHASE2_PRIVATE_KEY,
+ _nm_param_spec_specialized (NM_SETTING_802_1X_PHASE2_PRIVATE_KEY, "", "",
+ DBUS_TYPE_G_UCHAR_ARRAY,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSetting8021x:phase2-private-key-password:
+ *
+ * The password used to decrypt the "phase 2" private key specified in the
+ * #NMSetting8021x:phase2-private-key property when the private key either
+ * uses the path scheme, or is a PKCS#12 format key. Setting this property
+ * directly is not generally necessary except when returning secrets to
+ * NetworkManager; it is generally set automatically when setting the
+ * private key by the nm_setting_802_1x_set_phase2_private_key() function.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PHASE2_PRIVATE_KEY_PASSWORD,
+ g_param_spec_string (NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ NM_SETTING_PARAM_SECRET |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSetting8021x:phase2-private-key-password-flags:
+ *
+ * Flags indicating how to handle the
+ * #NMSetting8021x:phase2-private-key-password property.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PHASE2_PRIVATE_KEY_PASSWORD_FLAGS,
+ g_param_spec_uint (NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD_FLAGS, "", "",
+ NM_SETTING_SECRET_FLAG_NONE,
+ NM_SETTING_SECRET_FLAGS_ALL,
+ NM_SETTING_SECRET_FLAG_NONE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSetting8021x:pin:
+ *
+ * PIN used for EAP authentication methods.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PIN,
+ g_param_spec_string (NM_SETTING_802_1X_PIN, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ NM_SETTING_PARAM_SECRET |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSetting8021x:pin-flags:
+ *
+ * Flags indicating how to handle the #NMSetting8021x:pin property.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PIN_FLAGS,
+ g_param_spec_uint (NM_SETTING_802_1X_PIN_FLAGS, "", "",
+ NM_SETTING_SECRET_FLAG_NONE,
+ NM_SETTING_SECRET_FLAGS_ALL,
+ NM_SETTING_SECRET_FLAG_NONE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSetting8021x:system-ca-certs:
+ *
+ * When %TRUE, overrides the #NMSetting8021x:ca-path and
+ * #NMSetting8021x:phase2-ca-path properties using the system CA directory
+ * specified at configure time with the --system-ca-path switch. The
+ * certificates in this directory are added to the verification chain in
+ * addition to any certificates specified by the #NMSetting8021x:ca-cert and
+ * #NMSetting8021x:phase2-ca-cert properties.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_SYSTEM_CA_CERTS,
+ g_param_spec_boolean (NM_SETTING_802_1X_SYSTEM_CA_CERTS, "", "",
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ /* Initialize crypto lbrary. */
+ if (!nm_utils_init (&error)) {
+ g_warning ("Couldn't initilize nm-utils/crypto system: %d %s",
+ error->code, error->message);
+ g_error_free (error);
+ }
+}
diff --git a/libnm-core/nm-setting-8021x.h b/libnm-core/nm-setting-8021x.h
new file mode 100644
index 0000000000..2121f63b62
--- /dev/null
+++ b/libnm-core/nm-setting-8021x.h
@@ -0,0 +1,297 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2014 Red Hat, Inc.
+ * Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#ifndef NM_SETTING_8021X_H
+#define NM_SETTING_8021X_H
+
+#include <nm-setting.h>
+
+G_BEGIN_DECLS
+
+/**
+ * NMSetting8021xCKFormat:
+ * @NM_SETTING_802_1X_CK_FORMAT_UNKNOWN: unknown file format
+ * @NM_SETTING_802_1X_CK_FORMAT_X509: file contains an X.509 format certificate
+ * @NM_SETTING_802_1X_CK_FORMAT_RAW_KEY: file contains an old-style OpenSSL PEM
+ * or DER private key
+ * @NM_SETTING_802_1X_CK_FORMAT_PKCS12: file contains a PKCS#12 certificate
+ * and private key
+ *
+ * #NMSetting8021xCKFormat values indicate the general type of a certificate
+ * or private key
+ */
+typedef enum { /*< underscore_name=nm_setting_802_1x_ck_format >*/
+ NM_SETTING_802_1X_CK_FORMAT_UNKNOWN = 0,
+ NM_SETTING_802_1X_CK_FORMAT_X509,
+ NM_SETTING_802_1X_CK_FORMAT_RAW_KEY,
+ NM_SETTING_802_1X_CK_FORMAT_PKCS12
+} NMSetting8021xCKFormat;
+
+/**
+ * NMSetting8021xCKScheme:
+ * @NM_SETTING_802_1X_CK_SCHEME_UNKNOWN: unknown certificate or private key
+ * scheme
+ * @NM_SETTING_802_1X_CK_SCHEME_BLOB: certificate or key is stored as the raw
+ * item data
+ * @NM_SETTING_802_1X_CK_SCHEME_PATH: certificate or key is stored as a path
+ * to a file containing the certificate or key data
+ *
+ * #NMSetting8021xCKScheme values indicate how a certificate or private key is
+ * stored in the setting properties, either as a blob of the item's data, or as
+ * a path to a certificate or private key file on the filesystem
+ */
+typedef enum { /*< underscore_name=nm_setting_802_1x_ck_scheme >*/
+ NM_SETTING_802_1X_CK_SCHEME_UNKNOWN = 0,
+ NM_SETTING_802_1X_CK_SCHEME_BLOB,
+ NM_SETTING_802_1X_CK_SCHEME_PATH
+} NMSetting8021xCKScheme;
+
+
+#define NM_TYPE_SETTING_802_1X (nm_setting_802_1x_get_type ())
+#define NM_SETTING_802_1X(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_802_1X, NMSetting8021x))
+#define NM_SETTING_802_1X_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_802_1X, NMSetting8021xClass))
+#define NM_IS_SETTING_802_1X(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_802_1X))
+#define NM_IS_SETTING_802_1X_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_802_1X))
+#define NM_SETTING_802_1X_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_802_1X, NMSetting8021xClass))
+
+#define NM_SETTING_802_1X_SETTING_NAME "802-1x"
+
+/**
+ * NMSetting8021xError:
+ * @NM_SETTING_802_1X_ERROR_UNKNOWN: unknown or unclassified error
+ * @NM_SETTING_802_1X_ERROR_INVALID_PROPERTY: the property was invalid
+ * @NM_SETTING_802_1X_ERROR_MISSING_PROPERTY: the property was missing and is
+ * required
+ */
+typedef enum { /*< underscore_name=nm_setting_802_1x_error >*/
+ NM_SETTING_802_1X_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/
+ NM_SETTING_802_1X_ERROR_INVALID_PROPERTY, /*< nick=InvalidProperty >*/
+ NM_SETTING_802_1X_ERROR_MISSING_PROPERTY /*< nick=MissingProperty >*/
+} NMSetting8021xError;
+
+#define NM_SETTING_802_1X_ERROR nm_setting_802_1x_error_quark ()
+GQuark nm_setting_802_1x_error_quark (void);
+
+
+#define NM_SETTING_802_1X_EAP "eap"
+#define NM_SETTING_802_1X_IDENTITY "identity"
+#define NM_SETTING_802_1X_ANONYMOUS_IDENTITY "anonymous-identity"
+#define NM_SETTING_802_1X_PAC_FILE "pac-file"
+#define NM_SETTING_802_1X_CA_CERT "ca-cert"
+#define NM_SETTING_802_1X_CA_PATH "ca-path"
+#define NM_SETTING_802_1X_SUBJECT_MATCH "subject-match"
+#define NM_SETTING_802_1X_ALTSUBJECT_MATCHES "altsubject-matches"
+#define NM_SETTING_802_1X_CLIENT_CERT "client-cert"
+#define NM_SETTING_802_1X_PHASE1_PEAPVER "phase1-peapver"
+#define NM_SETTING_802_1X_PHASE1_PEAPLABEL "phase1-peaplabel"
+#define NM_SETTING_802_1X_PHASE1_FAST_PROVISIONING "phase1-fast-provisioning"
+#define NM_SETTING_802_1X_PHASE2_AUTH "phase2-auth"
+#define NM_SETTING_802_1X_PHASE2_AUTHEAP "phase2-autheap"
+#define NM_SETTING_802_1X_PHASE2_CA_CERT "phase2-ca-cert"
+#define NM_SETTING_802_1X_PHASE2_CA_PATH "phase2-ca-path"
+#define NM_SETTING_802_1X_PHASE2_SUBJECT_MATCH "phase2-subject-match"
+#define NM_SETTING_802_1X_PHASE2_ALTSUBJECT_MATCHES "phase2-altsubject-matches"
+#define NM_SETTING_802_1X_PHASE2_CLIENT_CERT "phase2-client-cert"
+#define NM_SETTING_802_1X_PASSWORD "password"
+#define NM_SETTING_802_1X_PASSWORD_FLAGS "password-flags"
+#define NM_SETTING_802_1X_PASSWORD_RAW "password-raw"
+#define NM_SETTING_802_1X_PASSWORD_RAW_FLAGS "password-raw-flags"
+#define NM_SETTING_802_1X_PRIVATE_KEY "private-key"
+#define NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD "private-key-password"
+#define NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD_FLAGS "private-key-password-flags"
+#define NM_SETTING_802_1X_PHASE2_PRIVATE_KEY "phase2-private-key"
+#define NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD "phase2-private-key-password"
+#define NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD_FLAGS "phase2-private-key-password-flags"
+#define NM_SETTING_802_1X_PIN "pin"
+#define NM_SETTING_802_1X_PIN_FLAGS "pin-flags"
+#define NM_SETTING_802_1X_SYSTEM_CA_CERTS "system-ca-certs"
+
+/* PRIVATE KEY NOTE: when setting PKCS#12 private keys directly via properties
+ * using the "blob" scheme, the data must be passed in PKCS#12 binary format.
+ * In this case, the appropriate "client-cert" (or "phase2-client-cert")
+ * property of the NMSetting8021x object must also contain the exact same
+ * PKCS#12 binary data that the private key does. This is because the
+ * PKCS#12 file contains both the private key and client certificate, so both
+ * properties need to be set to the same thing. When using the "path" scheme,
+ * just set both the private-key and client-cert properties to the same path.
+ *
+ * When setting OpenSSL-derived "traditional" format (ie S/MIME style, not
+ * PKCS#8) RSA and DSA keys directly via properties with the "blob" scheme, they
+ * should be passed to NetworkManager in PEM format with the "DEK-Info" and
+ * "Proc-Type" tags intact. Decrypted private keys should not be used as this
+ * is insecure and could allow unprivileged users to access the decrypted
+ * private key data.
+ *
+ * When using the "path" scheme, just set the private-key and client-cert
+ * properties to the paths to their respective objects.
+ */
+
+typedef struct {
+ NMSetting parent;
+} NMSetting8021x;
+
+typedef struct {
+ NMSettingClass parent;
+
+ /* Padding for future expansion */
+ void (*_reserved1) (void);
+ void (*_reserved2) (void);
+ void (*_reserved3) (void);
+ void (*_reserved4) (void);
+} NMSetting8021xClass;
+
+GType nm_setting_802_1x_get_type (void);
+
+NMSetting *nm_setting_802_1x_new (void);
+
+guint32 nm_setting_802_1x_get_num_eap_methods (NMSetting8021x *setting);
+const char * nm_setting_802_1x_get_eap_method (NMSetting8021x *setting, guint32 i);
+gboolean nm_setting_802_1x_add_eap_method (NMSetting8021x *setting, const char *eap);
+void nm_setting_802_1x_remove_eap_method (NMSetting8021x *setting, guint32 i);
+NM_AVAILABLE_IN_0_9_10
+gboolean nm_setting_802_1x_remove_eap_method_by_value (NMSetting8021x *setting, const char *eap);
+void nm_setting_802_1x_clear_eap_methods (NMSetting8021x *setting);
+
+const char * nm_setting_802_1x_get_identity (NMSetting8021x *setting);
+
+const char * nm_setting_802_1x_get_anonymous_identity (NMSetting8021x *setting);
+
+const char * nm_setting_802_1x_get_pac_file (NMSetting8021x *setting);
+
+gboolean nm_setting_802_1x_get_system_ca_certs (NMSetting8021x *setting);
+const char * nm_setting_802_1x_get_ca_path (NMSetting8021x *setting);
+const char * nm_setting_802_1x_get_phase2_ca_path (NMSetting8021x *setting);
+
+NMSetting8021xCKScheme nm_setting_802_1x_get_ca_cert_scheme (NMSetting8021x *setting);
+const GByteArray * nm_setting_802_1x_get_ca_cert_blob (NMSetting8021x *setting);
+const char * nm_setting_802_1x_get_ca_cert_path (NMSetting8021x *setting);
+gboolean nm_setting_802_1x_set_ca_cert (NMSetting8021x *setting,
+ const char *cert_path,
+ NMSetting8021xCKScheme scheme,
+ NMSetting8021xCKFormat *out_format,
+ GError **error);
+
+const char * nm_setting_802_1x_get_subject_match (NMSetting8021x *setting);
+
+guint32 nm_setting_802_1x_get_num_altsubject_matches (NMSetting8021x *setting);
+const char * nm_setting_802_1x_get_altsubject_match (NMSetting8021x *setting,
+ guint32 i);
+gboolean nm_setting_802_1x_add_altsubject_match (NMSetting8021x *setting,
+ const char *altsubject_match);
+void nm_setting_802_1x_remove_altsubject_match (NMSetting8021x *setting,
+ guint32 i);
+NM_AVAILABLE_IN_0_9_10
+gboolean nm_setting_802_1x_remove_altsubject_match_by_value (NMSetting8021x *setting,
+ const char *altsubject_match);
+void nm_setting_802_1x_clear_altsubject_matches (NMSetting8021x *setting);
+
+NMSetting8021xCKScheme nm_setting_802_1x_get_client_cert_scheme (NMSetting8021x *setting);
+const GByteArray * nm_setting_802_1x_get_client_cert_blob (NMSetting8021x *setting);
+const char * nm_setting_802_1x_get_client_cert_path (NMSetting8021x *setting);
+gboolean nm_setting_802_1x_set_client_cert (NMSetting8021x *setting,
+ const char *cert_path,
+ NMSetting8021xCKScheme scheme,
+ NMSetting8021xCKFormat *out_format,
+ GError **error);
+
+const char * nm_setting_802_1x_get_phase1_peapver (NMSetting8021x *setting);
+
+const char * nm_setting_802_1x_get_phase1_peaplabel (NMSetting8021x *setting);
+
+const char * nm_setting_802_1x_get_phase1_fast_provisioning (NMSetting8021x *setting);
+
+const char * nm_setting_802_1x_get_phase2_auth (NMSetting8021x *setting);
+
+const char * nm_setting_802_1x_get_phase2_autheap (NMSetting8021x *setting);
+
+NMSetting8021xCKScheme nm_setting_802_1x_get_phase2_ca_cert_scheme (NMSetting8021x *setting);
+const GByteArray * nm_setting_802_1x_get_phase2_ca_cert_blob (NMSetting8021x *setting);
+const char * nm_setting_802_1x_get_phase2_ca_cert_path (NMSetting8021x *setting);
+gboolean nm_setting_802_1x_set_phase2_ca_cert (NMSetting8021x *setting,
+ const char *cert_path,
+ NMSetting8021xCKScheme scheme,
+ NMSetting8021xCKFormat *out_format,
+ GError **error);
+
+const char * nm_setting_802_1x_get_phase2_subject_match (NMSetting8021x *setting);
+
+guint32 nm_setting_802_1x_get_num_phase2_altsubject_matches (NMSetting8021x *setting);
+const char * nm_setting_802_1x_get_phase2_altsubject_match (NMSetting8021x *setting,
+ guint32 i);
+gboolean nm_setting_802_1x_add_phase2_altsubject_match (NMSetting8021x *setting,
+ const char *phase2_altsubject_match);
+void nm_setting_802_1x_remove_phase2_altsubject_match (NMSetting8021x *setting,
+ guint32 i);
+NM_AVAILABLE_IN_0_9_10
+gboolean nm_setting_802_1x_remove_phase2_altsubject_match_by_value (NMSetting8021x *setting,
+ const char *phase2_altsubject_match);
+void nm_setting_802_1x_clear_phase2_altsubject_matches (NMSetting8021x *setting);
+
+NMSetting8021xCKScheme nm_setting_802_1x_get_phase2_client_cert_scheme (NMSetting8021x *setting);
+const GByteArray * nm_setting_802_1x_get_phase2_client_cert_blob (NMSetting8021x *setting);
+const char * nm_setting_802_1x_get_phase2_client_cert_path (NMSetting8021x *setting);
+gboolean nm_setting_802_1x_set_phase2_client_cert (NMSetting8021x *setting,
+ const char *cert_path,
+ NMSetting8021xCKScheme scheme,
+ NMSetting8021xCKFormat *out_format,
+ GError **error);
+
+const char * nm_setting_802_1x_get_password (NMSetting8021x *setting);
+NMSettingSecretFlags nm_setting_802_1x_get_password_flags (NMSetting8021x *setting);
+const GByteArray * nm_setting_802_1x_get_password_raw (NMSetting8021x *setting);
+NMSettingSecretFlags nm_setting_802_1x_get_password_raw_flags (NMSetting8021x *setting);
+
+const char * nm_setting_802_1x_get_pin (NMSetting8021x *setting);
+NMSettingSecretFlags nm_setting_802_1x_get_pin_flags (NMSetting8021x *setting);
+
+NMSetting8021xCKScheme nm_setting_802_1x_get_private_key_scheme (NMSetting8021x *setting);
+const GByteArray * nm_setting_802_1x_get_private_key_blob (NMSetting8021x *setting);
+const char * nm_setting_802_1x_get_private_key_path (NMSetting8021x *setting);
+gboolean nm_setting_802_1x_set_private_key (NMSetting8021x *setting,
+ const char *key_path,
+ const char *password,
+ NMSetting8021xCKScheme scheme,
+ NMSetting8021xCKFormat *out_format,
+ GError **error);
+const char * nm_setting_802_1x_get_private_key_password (NMSetting8021x *setting);
+NMSettingSecretFlags nm_setting_802_1x_get_private_key_password_flags (NMSetting8021x *setting);
+
+NMSetting8021xCKFormat nm_setting_802_1x_get_private_key_format (NMSetting8021x *setting);
+
+NMSetting8021xCKScheme nm_setting_802_1x_get_phase2_private_key_scheme (NMSetting8021x *setting);
+const GByteArray * nm_setting_802_1x_get_phase2_private_key_blob (NMSetting8021x *setting);
+const char * nm_setting_802_1x_get_phase2_private_key_path (NMSetting8021x *setting);
+gboolean nm_setting_802_1x_set_phase2_private_key (NMSetting8021x *setting,
+ const char *key_path,
+ const char *password,
+ NMSetting8021xCKScheme scheme,
+ NMSetting8021xCKFormat *out_format,
+ GError **error);
+const char * nm_setting_802_1x_get_phase2_private_key_password (NMSetting8021x *setting);
+NMSettingSecretFlags nm_setting_802_1x_get_phase2_private_key_password_flags (NMSetting8021x *setting);
+
+NMSetting8021xCKFormat nm_setting_802_1x_get_phase2_private_key_format (NMSetting8021x *setting);
+
+
+G_END_DECLS
+
+#endif /* NM_SETTING_8021X_H */
diff --git a/libnm-core/nm-setting-adsl.c b/libnm-core/nm-setting-adsl.c
new file mode 100644
index 0000000000..0a8eba7c0d
--- /dev/null
+++ b/libnm-core/nm-setting-adsl.c
@@ -0,0 +1,465 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2011 - 2013 Red Hat, Inc.
+ */
+
+#include <string.h>
+#include <glib/gi18n.h>
+
+#include "nm-setting-adsl.h"
+#include "nm-setting-ppp.h"
+#include "nm-setting-private.h"
+#include "nm-utils.h"
+
+/**
+ * SECTION:nm-setting-adsl
+ * @short_description: Describes ADSL-based properties
+ * @include: nm-setting-adsl.h
+ *
+ * The #NMSettingAdsl object is a #NMSetting subclass that describes
+ * properties of ADSL connections.
+ */
+
+/**
+ * nm_setting_adsl_error_quark:
+ *
+ * Registers an error quark for #NMSettingAdsl if necessary.
+ *
+ * Returns: the error quark used for #NMSettingAdsl errors.
+ **/
+GQuark
+nm_setting_adsl_error_quark (void)
+{
+ static GQuark quark;
+
+ if (G_UNLIKELY (!quark))
+ quark = g_quark_from_static_string ("nm-setting-adsl-error-quark");
+ return quark;
+}
+
+G_DEFINE_TYPE_WITH_CODE (NMSettingAdsl, nm_setting_adsl, NM_TYPE_SETTING,
+ _nm_register_setting (NM_SETTING_ADSL_SETTING_NAME,
+ g_define_type_id,
+ 1,
+ NM_SETTING_ADSL_ERROR))
+NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_ADSL)
+
+#define NM_SETTING_ADSL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_ADSL, NMSettingAdslPrivate))
+
+typedef struct {
+ char * username;
+ char * password;
+ NMSettingSecretFlags password_flags;
+ char * protocol;
+ char * encapsulation;
+ guint32 vpi;
+ guint32 vci;
+} NMSettingAdslPrivate;
+
+enum {
+ PROP_0,
+ PROP_USERNAME,
+ PROP_PASSWORD,
+ PROP_PASSWORD_FLAGS,
+ PROP_PROTOCOL,
+ PROP_ENCAPSULATION,
+ PROP_VPI,
+ PROP_VCI,
+
+ LAST_PROP
+};
+
+/**
+ * nm_setting_adsl_new:
+ *
+ * Creates a new #NMSettingAdsl object with default values.
+ *
+ * Returns: the new empty #NMSettingAdsl object
+ **/
+NMSetting *
+nm_setting_adsl_new (void)
+{
+ return (NMSetting *) g_object_new (NM_TYPE_SETTING_ADSL, NULL);
+}
+
+/**
+ * nm_setting_adsl_get_username:
+ * @setting: the #NMSettingAdsl
+ *
+ * Returns: the #NMSettingAdsl:username property of the setting
+ **/
+const char *
+nm_setting_adsl_get_username (NMSettingAdsl *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_ADSL (setting), NULL);
+
+ return NM_SETTING_ADSL_GET_PRIVATE (setting)->username;
+}
+
+/**
+ * nm_setting_adsl_get_password:
+ * @setting: the #NMSettingAdsl
+ *
+ * Returns: the #NMSettingAdsl:password property of the setting
+ **/
+const char *
+nm_setting_adsl_get_password (NMSettingAdsl *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_ADSL (setting), NULL);
+
+ return NM_SETTING_ADSL_GET_PRIVATE (setting)->password;
+}
+
+/**
+ * nm_setting_adsl_get_password_flags:
+ * @setting: the #NMSettingAdsl
+ *
+ * Returns: the #NMSettingSecretFlags pertaining to the #NMSettingAdsl:password
+ **/
+NMSettingSecretFlags
+nm_setting_adsl_get_password_flags (NMSettingAdsl *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_ADSL (setting), NM_SETTING_SECRET_FLAG_NONE);
+
+ return NM_SETTING_ADSL_GET_PRIVATE (setting)->password_flags;
+}
+
+/**
+ * nm_setting_adsl_get_protocol:
+ * @setting: the #NMSettingAdsl
+ *
+ * Returns: the #NMSettingAdsl:protocol property of the setting
+ **/
+const char *
+nm_setting_adsl_get_protocol (NMSettingAdsl *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_ADSL (setting), NULL);
+
+ return NM_SETTING_ADSL_GET_PRIVATE (setting)->protocol;
+}
+
+/**
+ * nm_setting_adsl_get_encapsulation:
+ * @setting: the #NMSettingAdsl
+ *
+ * Returns: the #NMSettingAdsl:encapsulation property of the setting
+ **/
+const char *
+nm_setting_adsl_get_encapsulation (NMSettingAdsl *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_ADSL (setting), NULL);
+
+ return NM_SETTING_ADSL_GET_PRIVATE (setting)->encapsulation;
+}
+
+/**
+ * nm_setting_adsl_get_vpi:
+ * @setting: the #NMSettingAdsl
+ *
+ * Returns: the #NMSettingAdsl:vpi property of the setting
+ **/
+guint32
+nm_setting_adsl_get_vpi (NMSettingAdsl *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_ADSL (setting), 0);
+
+ return NM_SETTING_ADSL_GET_PRIVATE (setting)->vpi;
+}
+
+/**
+ * nm_setting_adsl_get_vci:
+ * @setting: the #NMSettingAdsl
+ *
+ * Returns: the #NMSettingAdsl:vci property of the setting
+ **/
+guint32
+nm_setting_adsl_get_vci (NMSettingAdsl *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_ADSL (setting), 0);
+
+ return NM_SETTING_ADSL_GET_PRIVATE (setting)->vci;
+}
+
+static gboolean
+verify (NMSetting *setting, GSList *all_settings, GError **error)
+{
+ NMSettingAdslPrivate *priv = NM_SETTING_ADSL_GET_PRIVATE (setting);
+
+ if (!priv->username) {
+ g_set_error_literal (error,
+ NM_SETTING_ADSL_ERROR,
+ NM_SETTING_ADSL_ERROR_MISSING_PROPERTY,
+ _("property is missing"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_ADSL_SETTING_NAME, NM_SETTING_ADSL_USERNAME);
+ return FALSE;
+ } else if (!strlen (priv->username)) {
+ g_set_error_literal (error,
+ NM_SETTING_ADSL_ERROR,
+ NM_SETTING_ADSL_ERROR_INVALID_PROPERTY,
+ _("property is empty"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_ADSL_SETTING_NAME, NM_SETTING_ADSL_USERNAME);
+ return FALSE;
+ }
+
+ if (priv->password && !strlen (priv->password)) {
+ g_set_error_literal (error,
+ NM_SETTING_ADSL_ERROR,
+ NM_SETTING_ADSL_ERROR_INVALID_PROPERTY,
+ _("property is empty"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_ADSL_SETTING_NAME, NM_SETTING_ADSL_PASSWORD);
+ return FALSE;
+ }
+
+ if (strcmp (priv->protocol, NM_SETTING_ADSL_PROTOCOL_PPPOA) &&
+ strcmp (priv->protocol, NM_SETTING_ADSL_PROTOCOL_PPPOE) &&
+ strcmp (priv->protocol, NM_SETTING_ADSL_PROTOCOL_IPOATM)) {
+ g_set_error (error,
+ NM_SETTING_ADSL_ERROR,
+ NM_SETTING_ADSL_ERROR_INVALID_PROPERTY,
+ _("'%s' is not a valid value for the property"),
+ priv->protocol);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_ADSL_SETTING_NAME, NM_SETTING_ADSL_PROTOCOL);
+ return FALSE;
+ }
+
+ if (strcmp (priv->encapsulation, NM_SETTING_ADSL_ENCAPSULATION_VCMUX) &&
+ strcmp (priv->encapsulation, NM_SETTING_ADSL_ENCAPSULATION_LLC) ) {
+ g_set_error (error,
+ NM_SETTING_ADSL_ERROR,
+ NM_SETTING_ADSL_ERROR_INVALID_PROPERTY,
+ _("'%s' is not a valid value for the property"),
+ priv->encapsulation);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_ADSL_SETTING_NAME, NM_SETTING_ADSL_ENCAPSULATION);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static GPtrArray *
+need_secrets (NMSetting *setting)
+{
+ NMSettingAdslPrivate *priv = NM_SETTING_ADSL_GET_PRIVATE (setting);
+ GPtrArray *secrets = NULL;
+
+ if (priv->password)
+ return NULL;
+
+ if (!(priv->password_flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED)) {
+ secrets = g_ptr_array_sized_new (1);
+ g_ptr_array_add (secrets, NM_SETTING_ADSL_PASSWORD);
+ }
+
+ return secrets;
+}
+
+static void
+nm_setting_adsl_init (NMSettingAdsl *setting)
+{
+}
+
+static void
+finalize (GObject *object)
+{
+ NMSettingAdslPrivate *priv = NM_SETTING_ADSL_GET_PRIVATE (object);
+
+ g_free (priv->username);
+ g_free (priv->password);
+ g_free (priv->protocol);
+ g_free (priv->encapsulation);
+
+ G_OBJECT_CLASS (nm_setting_adsl_parent_class)->finalize (object);
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ NMSettingAdslPrivate *priv = NM_SETTING_ADSL_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_USERNAME:
+ g_free (priv->username);
+ priv->username = g_value_dup_string (value);
+ break;
+ case PROP_PASSWORD:
+ g_free (priv->password);
+ priv->password = g_value_dup_string (value);
+ break;
+ case PROP_PASSWORD_FLAGS:
+ priv->password_flags = g_value_get_uint (value);
+ break;
+ case PROP_PROTOCOL:
+ g_free (priv->protocol);
+ priv->protocol = g_ascii_strdown (g_value_get_string (value), -1);
+ break;
+ case PROP_ENCAPSULATION:
+ g_free (priv->encapsulation);
+ priv->encapsulation = g_ascii_strdown (g_value_get_string (value), -1);
+ break;
+ case PROP_VPI:
+ priv->vpi = g_value_get_uint (value);
+ break;
+ case PROP_VCI:
+ priv->vci = g_value_get_uint (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ NMSettingAdsl *setting = NM_SETTING_ADSL (object);
+
+ switch (prop_id) {
+ case PROP_USERNAME:
+ g_value_set_string (value, nm_setting_adsl_get_username (setting));
+ break;
+ case PROP_PASSWORD:
+ g_value_set_string (value, nm_setting_adsl_get_password (setting));
+ break;
+ case PROP_PASSWORD_FLAGS:
+ g_value_set_uint (value, nm_setting_adsl_get_password_flags (setting));
+ break;
+ case PROP_PROTOCOL:
+ g_value_set_string (value, nm_setting_adsl_get_protocol (setting));
+ break;
+ case PROP_ENCAPSULATION:
+ g_value_set_string (value, nm_setting_adsl_get_encapsulation (setting));
+ break;
+ case PROP_VPI:
+ g_value_set_uint (value, nm_setting_adsl_get_vpi (setting));
+ break;
+ case PROP_VCI:
+ g_value_set_uint (value, nm_setting_adsl_get_vci (setting));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nm_setting_adsl_class_init (NMSettingAdslClass *setting_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
+ NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+
+ g_type_class_add_private (setting_class, sizeof (NMSettingAdslPrivate));
+
+ /* virtual methods */
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+ object_class->finalize = finalize;
+ parent_class->verify = verify;
+ parent_class->need_secrets = need_secrets;
+
+ /* Properties */
+
+ /**
+ * NMSettingAdsl:username:
+ *
+ * Username used to authenticate with the ADSL service.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_USERNAME,
+ g_param_spec_string (NM_SETTING_ADSL_USERNAME, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingAdsl:password:
+ *
+ * Password used to authenticate with the ADSL service.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PASSWORD,
+ g_param_spec_string (NM_SETTING_ADSL_PASSWORD, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ NM_SETTING_PARAM_SECRET |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingAdsl:password-flags:
+ *
+ * Flags indicating how to handle the #NMSettingAdsl:password property.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PASSWORD_FLAGS,
+ g_param_spec_uint (NM_SETTING_ADSL_PASSWORD_FLAGS, "", "",
+ NM_SETTING_SECRET_FLAG_NONE,
+ NM_SETTING_SECRET_FLAGS_ALL,
+ NM_SETTING_SECRET_FLAG_NONE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingAdsl:protocol:
+ *
+ * ADSL connection protocol. Can be "pppoa", "pppoe" or "ipoatm".
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PROTOCOL,
+ g_param_spec_string (NM_SETTING_ADSL_PROTOCOL, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingAdsl:encapsulation:
+ *
+ * Encapsulation of ADSL connection. Can be "vcmux" or "llc".
+ **/
+ g_object_class_install_property
+ (object_class, PROP_ENCAPSULATION,
+ g_param_spec_string (NM_SETTING_ADSL_ENCAPSULATION, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingAdsl:vpi:
+ *
+ * VPI of ADSL connection
+ **/
+ g_object_class_install_property
+ (object_class, PROP_VPI,
+ g_param_spec_uint (NM_SETTING_ADSL_VPI, "", "",
+ 0, 65536, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingAdsl:vci:
+ *
+ * VCI of ADSL connection
+ **/
+ g_object_class_install_property
+ (object_class, PROP_VCI,
+ g_param_spec_uint (NM_SETTING_ADSL_VCI, "", "",
+ 0, 65536, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/libnm-core/nm-setting-adsl.h b/libnm-core/nm-setting-adsl.h
new file mode 100644
index 0000000000..82af6ebdd0
--- /dev/null
+++ b/libnm-core/nm-setting-adsl.h
@@ -0,0 +1,96 @@
+/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2008 Red Hat, Inc.
+ */
+
+#ifndef NM_SETTING_ADSL_H
+#define NM_SETTING_ADSL_H
+
+#include <nm-setting.h>
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_SETTING_ADSL (nm_setting_adsl_get_type ())
+#define NM_SETTING_ADSL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_ADSL, NMSettingAdsl))
+#define NM_SETTING_ADSL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_ADSL, NMSettingAdslClass))
+#define NM_IS_SETTING_ADSL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_ADSL))
+#define NM_IS_SETTING_ADSL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_ADSL))
+#define NM_SETTING_ADSL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_ADSL, NMSettingAdslClass))
+
+#define NM_SETTING_ADSL_SETTING_NAME "adsl"
+
+/**
+ * NMSettingAdslError:
+ * @NM_SETTING_ADSL_ERROR_UNKNOWN: unknown or unclassified error
+ * @NM_SETTING_ADSL_ERROR_INVALID_PROPERTY: the property was invalid
+ * @NM_SETTING_ADSL_ERROR_MISSING_PROPERTY: the property was missing and is
+ * required
+ */
+typedef enum {
+ NM_SETTING_ADSL_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/
+ NM_SETTING_ADSL_ERROR_INVALID_PROPERTY, /*< nick=InvalidProperty >*/
+ NM_SETTING_ADSL_ERROR_MISSING_PROPERTY /*< nick=MissingProperty >*/
+} NMSettingAdslError;
+
+#define NM_SETTING_ADSL_ERROR nm_setting_adsl_error_quark ()
+GQuark nm_setting_adsl_error_quark (void);
+
+#define NM_SETTING_ADSL_USERNAME "username"
+#define NM_SETTING_ADSL_PASSWORD "password"
+#define NM_SETTING_ADSL_PASSWORD_FLAGS "password-flags"
+#define NM_SETTING_ADSL_PROTOCOL "protocol"
+#define NM_SETTING_ADSL_ENCAPSULATION "encapsulation"
+#define NM_SETTING_ADSL_VPI "vpi"
+#define NM_SETTING_ADSL_VCI "vci"
+
+#define NM_SETTING_ADSL_PROTOCOL_PPPOA "pppoa"
+#define NM_SETTING_ADSL_PROTOCOL_PPPOE "pppoe"
+#define NM_SETTING_ADSL_PROTOCOL_IPOATM "ipoatm"
+
+#define NM_SETTING_ADSL_ENCAPSULATION_VCMUX "vcmux"
+#define NM_SETTING_ADSL_ENCAPSULATION_LLC "llc"
+
+typedef struct {
+ NMSetting parent;
+} NMSettingAdsl;
+
+typedef struct {
+ NMSettingClass parent;
+
+ /* Padding for future expansion */
+ void (*_reserved1) (void);
+ void (*_reserved2) (void);
+ void (*_reserved3) (void);
+ void (*_reserved4) (void);
+} NMSettingAdslClass;
+
+GType nm_setting_adsl_get_type (void);
+
+NMSetting *nm_setting_adsl_new (void);
+const char *nm_setting_adsl_get_username (NMSettingAdsl *setting);
+const char *nm_setting_adsl_get_password (NMSettingAdsl *setting);
+const char *nm_setting_adsl_get_protocol (NMSettingAdsl *setting);
+const char *nm_setting_adsl_get_encapsulation (NMSettingAdsl *setting);
+guint32 nm_setting_adsl_get_vpi (NMSettingAdsl *setting);
+guint32 nm_setting_adsl_get_vci (NMSettingAdsl *setting);
+NMSettingSecretFlags nm_setting_adsl_get_password_flags (NMSettingAdsl *setting);
+
+G_END_DECLS
+
+#endif /* NM_SETTING_ADSL_H */
diff --git a/libnm-core/nm-setting-bluetooth.c b/libnm-core/nm-setting-bluetooth.c
new file mode 100644
index 0000000000..506ecbddee
--- /dev/null
+++ b/libnm-core/nm-setting-bluetooth.c
@@ -0,0 +1,297 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2013 Red Hat, Inc.
+ * Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#include <string.h>
+#include <net/ethernet.h>
+#include <glib/gi18n.h>
+
+#include "nm-param-spec-specialized.h"
+#include "nm-dbus-glib-types.h"
+#include "nm-setting-bluetooth.h"
+#include "nm-setting-cdma.h"
+#include "nm-setting-gsm.h"
+#include "nm-setting-private.h"
+
+/**
+ * SECTION:nm-setting-bluetooth
+ * @short_description: Describes Bluetooth connection properties
+ * @include: nm-setting-bluetooth.h
+ *
+ * The #NMSettingBluetooth object is a #NMSetting subclass that describes
+ * properties necessary for connection to devices that provide network
+ * connections via the Bluetooth Dial-Up Networking (DUN) and Network Access
+ * Point (NAP) profiles.
+ **/
+
+/**
+ * nm_setting_bluetooth_error_quark:
+ *
+ * Registers an error quark for #NMSettingBluetooth if necessary.
+ *
+ * Returns: the error quark used for #NMSettingBluetooth errors.
+ **/
+GQuark
+nm_setting_bluetooth_error_quark (void)
+{
+ static GQuark quark;
+
+ if (G_UNLIKELY (!quark))
+ quark = g_quark_from_static_string ("nm-setting-bluetooth-error-quark");
+ return quark;
+}
+
+
+G_DEFINE_TYPE_WITH_CODE (NMSettingBluetooth, nm_setting_bluetooth, NM_TYPE_SETTING,
+ _nm_register_setting (NM_SETTING_BLUETOOTH_SETTING_NAME,
+ g_define_type_id,
+ 1,
+ NM_SETTING_BLUETOOTH_ERROR))
+NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_BLUETOOTH)
+
+#define NM_SETTING_BLUETOOTH_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_BLUETOOTH, NMSettingBluetoothPrivate))
+
+typedef struct {
+ GByteArray *bdaddr;
+ char *type;
+} NMSettingBluetoothPrivate;
+
+enum {
+ PROP_0,
+ PROP_BDADDR,
+ PROP_TYPE,
+
+ LAST_PROP
+};
+
+/**
+ * nm_setting_bluetooth_new:
+ *
+ * Creates a new #NMSettingBluetooth object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingBluetooth object
+ **/
+NMSetting *nm_setting_bluetooth_new (void)
+{
+ return (NMSetting *) g_object_new (NM_TYPE_SETTING_BLUETOOTH, NULL);
+}
+
+/**
+ * nm_setting_bluetooth_get_connection_type:
+ * @setting: the #NMSettingBluetooth
+ *
+ * Returns the connection method for communicating with the remote device (i.e.
+ * either DUN to a DUN-capable device or PANU to a NAP-capable device).
+ *
+ * Returns: the type, either %NM_SETTING_BLUETOOTH_PANU or %NM_SETTING_BLUETOOTH_DUN
+ **/
+const char *
+nm_setting_bluetooth_get_connection_type (NMSettingBluetooth *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_BLUETOOTH (setting), NULL);
+
+ return NM_SETTING_BLUETOOTH_GET_PRIVATE (setting)->type;
+}
+
+/**
+ * nm_setting_bluetooth_get_bdaddr:
+ * @setting: the #NMSettingBluetooth
+ *
+ * Gets the Bluetooth address of the remote device which this setting
+ * describes a connection to.
+ *
+ * Returns: the Bluetooth address
+ **/
+const GByteArray *
+nm_setting_bluetooth_get_bdaddr (NMSettingBluetooth *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_BLUETOOTH (setting), NULL);
+
+ return NM_SETTING_BLUETOOTH_GET_PRIVATE (setting)->bdaddr;
+}
+
+static gboolean
+verify (NMSetting *setting, GSList *all_settings, GError **error)
+{
+ NMSettingBluetoothPrivate *priv = NM_SETTING_BLUETOOTH_GET_PRIVATE (setting);
+
+ if (!priv->bdaddr) {
+ g_set_error_literal (error,
+ NM_SETTING_BLUETOOTH_ERROR,
+ NM_SETTING_BLUETOOTH_ERROR_MISSING_PROPERTY,
+ _("property is missing"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_BLUETOOTH_SETTING_NAME, NM_SETTING_BLUETOOTH_BDADDR);
+ return FALSE;
+ }
+
+ if (priv->bdaddr && priv->bdaddr->len != ETH_ALEN) {
+ g_set_error_literal (error,
+ NM_SETTING_BLUETOOTH_ERROR,
+ NM_SETTING_BLUETOOTH_ERROR_INVALID_PROPERTY,
+ _("property is invalid"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_BLUETOOTH_SETTING_NAME, NM_SETTING_BLUETOOTH_BDADDR);
+ return FALSE;
+ }
+
+ if (!priv->type) {
+ g_set_error_literal (error,
+ NM_SETTING_BLUETOOTH_ERROR,
+ NM_SETTING_BLUETOOTH_ERROR_MISSING_PROPERTY,
+ _("property is missing"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_BLUETOOTH_SETTING_NAME, NM_SETTING_BLUETOOTH_TYPE);
+ return FALSE;
+ } else if (!g_str_equal (priv->type, NM_SETTING_BLUETOOTH_TYPE_DUN) &&
+ !g_str_equal (priv->type, NM_SETTING_BLUETOOTH_TYPE_PANU)) {
+ g_set_error (error,
+ NM_SETTING_BLUETOOTH_ERROR,
+ NM_SETTING_BLUETOOTH_ERROR_INVALID_PROPERTY,
+ _("'%s' is not a valid value for the property"),
+ priv->type);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_BLUETOOTH_SETTING_NAME, NM_SETTING_BLUETOOTH_TYPE);
+ return FALSE;
+ }
+
+ /* Make sure the corresponding 'type' setting is present */
+ if ( all_settings
+ && !strcmp (priv->type, NM_SETTING_BLUETOOTH_TYPE_DUN)) {
+ gboolean gsm = FALSE, cdma = FALSE;
+
+ gsm = !!nm_setting_find_in_list (all_settings, NM_SETTING_GSM_SETTING_NAME);
+ cdma = !!nm_setting_find_in_list (all_settings, NM_SETTING_CDMA_SETTING_NAME);
+
+ if (!gsm && !cdma) {
+ g_set_error (error,
+ NM_SETTING_BLUETOOTH_ERROR,
+ NM_SETTING_BLUETOOTH_ERROR_TYPE_SETTING_NOT_FOUND,
+ _("requires '%s' or '%s' setting"),
+ NM_SETTING_GSM_SETTING_NAME, NM_SETTING_CDMA_SETTING_NAME);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_BLUETOOTH_SETTING_NAME, NM_SETTING_BLUETOOTH_TYPE);
+ return FALSE;
+ }
+ }
+ /* PANU doesn't need a 'type' setting since no further configuration
+ * is required at the interface level.
+ */
+
+ return TRUE;
+}
+
+static void
+nm_setting_bluetooth_init (NMSettingBluetooth *setting)
+{
+}
+
+static void
+finalize (GObject *object)
+{
+ NMSettingBluetoothPrivate *priv = NM_SETTING_BLUETOOTH_GET_PRIVATE (object);
+
+ if (priv->bdaddr)
+ g_byte_array_free (priv->bdaddr, TRUE);
+ g_free (priv->type);
+
+ G_OBJECT_CLASS (nm_setting_bluetooth_parent_class)->finalize (object);
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ NMSettingBluetoothPrivate *priv = NM_SETTING_BLUETOOTH_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_BDADDR:
+ if (priv->bdaddr)
+ g_byte_array_free (priv->bdaddr, TRUE);
+ priv->bdaddr = g_value_dup_boxed (value);
+ break;
+ case PROP_TYPE:
+ g_free (priv->type);
+ priv->type = g_value_dup_string (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ NMSettingBluetooth *setting = NM_SETTING_BLUETOOTH (object);
+
+ switch (prop_id) {
+ case PROP_BDADDR:
+ g_value_set_boxed (value, nm_setting_bluetooth_get_bdaddr (setting));
+ break;
+ case PROP_TYPE:
+ g_value_set_string (value, nm_setting_bluetooth_get_connection_type (setting));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nm_setting_bluetooth_class_init (NMSettingBluetoothClass *setting_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
+ NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+
+ g_type_class_add_private (setting_class, sizeof (NMSettingBluetoothPrivate));
+
+ /* virtual methods */
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+ object_class->finalize = finalize;
+ parent_class->verify = verify;
+
+ /* Properties */
+
+ /**
+ * NMSettingBluetooth:bdaddr:
+ *
+ * The Bluetooth address of the device.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_BDADDR,
+ _nm_param_spec_specialized (NM_SETTING_BLUETOOTH_BDADDR, "", "",
+ DBUS_TYPE_G_UCHAR_ARRAY,
+ G_PARAM_READWRITE |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingBluetooth:type:
+ *
+ * Either "dun" for Dial-Up Networking connections or "panu" for Personal
+ * Area Networking connections to devices supporting the NAP profile.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_TYPE,
+ g_param_spec_string (NM_SETTING_BLUETOOTH_TYPE, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/libnm-core/nm-setting-bluetooth.h b/libnm-core/nm-setting-bluetooth.h
new file mode 100644
index 0000000000..caf12158c0
--- /dev/null
+++ b/libnm-core/nm-setting-bluetooth.h
@@ -0,0 +1,100 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2009 Red Hat, Inc.
+ * Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#ifndef NM_SETTING_BLUETOOTH_H
+#define NM_SETTING_BLUETOOTH_H
+
+#include "nm-setting.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_SETTING_BLUETOOTH (nm_setting_bluetooth_get_type ())
+#define NM_SETTING_BLUETOOTH(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_BLUETOOTH, NMSettingBluetooth))
+#define NM_SETTING_BLUETOOTH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_BLUETOOTH, NMSettingBluetoothClass))
+#define NM_IS_SETTING_BLUETOOTH(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_BLUETOOTH))
+#define NM_IS_SETTING_BLUETOOTH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_BLUETOOTH))
+#define NM_SETTING_BLUETOOTH_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_BLUETOOTH, NMSettingBluetoothClass))
+
+#define NM_SETTING_BLUETOOTH_SETTING_NAME "bluetooth"
+
+/**
+ * NMSettingBluetoothError:
+ * @NM_SETTING_BLUETOOTH_ERROR_UNKNOWN: unknown or unclassified error
+ * @NM_SETTING_BLUETOOTH_ERROR_INVALID_PROPERTY: the property was invalid
+ * @NM_SETTING_BLUETOOTH_ERROR_MISSING_PROPERTY: the property was missing and is
+ * required
+ * @NM_SETTING_BLUETOOTH_ERROR_TYPE_SETTING_NOT_FOUND: the connection
+ * did not contain a required type setting, ie for DUN connections the connection
+ * must also contain an #NMSettingGsm or #NMSettingCdma as appropriate
+ */
+typedef enum {
+ NM_SETTING_BLUETOOTH_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/
+ NM_SETTING_BLUETOOTH_ERROR_INVALID_PROPERTY, /*< nick=InvalidProperty >*/
+ NM_SETTING_BLUETOOTH_ERROR_MISSING_PROPERTY, /*< nick=MissingProperty >*/
+ NM_SETTING_BLUETOOTH_ERROR_TYPE_SETTING_NOT_FOUND, /*< nick=TypeSettingNotFound >*/
+} NMSettingBluetoothError;
+
+#define NM_SETTING_BLUETOOTH_ERROR nm_setting_bluetooth_error_quark ()
+GQuark nm_setting_bluetooth_error_quark (void);
+
+#define NM_SETTING_BLUETOOTH_BDADDR "bdaddr"
+#define NM_SETTING_BLUETOOTH_TYPE "type"
+
+/**
+ * NM_SETTING_BLUETOOTH_TYPE_DUN:
+ *
+ * Connection type describing a connection to devices that support the Bluetooth
+ * DUN profile.
+ */
+#define NM_SETTING_BLUETOOTH_TYPE_DUN "dun"
+
+/**
+ * NM_SETTING_BLUETOOTH_TYPE_PANU:
+ *
+ * Connection type describing a connection to devices that support the Bluetooth
+ * NAP (Network Access Point) protocol, which accepts connections via PANU.
+ */
+#define NM_SETTING_BLUETOOTH_TYPE_PANU "panu"
+
+typedef struct {
+ NMSetting parent;
+} NMSettingBluetooth;
+
+typedef struct {
+ NMSettingClass parent;
+
+ /* Padding for future expansion */
+ void (*_reserved1) (void);
+ void (*_reserved2) (void);
+ void (*_reserved3) (void);
+ void (*_reserved4) (void);
+} NMSettingBluetoothClass;
+
+GType nm_setting_bluetooth_get_type (void);
+
+NMSetting * nm_setting_bluetooth_new (void);
+const GByteArray *nm_setting_bluetooth_get_bdaddr (NMSettingBluetooth *setting);
+const char * nm_setting_bluetooth_get_connection_type (NMSettingBluetooth *setting);
+
+G_END_DECLS
+
+#endif /* NM_SETTING_BLUETOOTH_H */
diff --git a/libnm-core/nm-setting-bond.c b/libnm-core/nm-setting-bond.c
new file mode 100644
index 0000000000..10766f564d
--- /dev/null
+++ b/libnm-core/nm-setting-bond.c
@@ -0,0 +1,804 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2011 - 2013 Red Hat, Inc.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <dbus/dbus-glib.h>
+#include <glib/gi18n.h>
+
+#include "nm-setting-bond.h"
+#include "nm-param-spec-specialized.h"
+#include "nm-utils.h"
+#include "nm-utils-private.h"
+#include "nm-dbus-glib-types.h"
+#include "nm-setting-private.h"
+
+/**
+ * SECTION:nm-setting-bond
+ * @short_description: Describes connection properties for bonds
+ * @include: nm-setting-bond.h
+ *
+ * The #NMSettingBond object is a #NMSetting subclass that describes properties
+ * necessary for bond connections.
+ **/
+
+/**
+ * nm_setting_bond_error_quark:
+ *
+ * Registers an error quark for #NMSettingBond if necessary.
+ *
+ * Returns: the error quark used for #NMSettingBond errors.
+ **/
+GQuark
+nm_setting_bond_error_quark (void)
+{
+ static GQuark quark;
+
+ if (G_UNLIKELY (!quark))
+ quark = g_quark_from_static_string ("nm-setting-bond-error-quark");
+ return quark;
+}
+
+
+G_DEFINE_TYPE_WITH_CODE (NMSettingBond, nm_setting_bond, NM_TYPE_SETTING,
+ _nm_register_setting (NM_SETTING_BOND_SETTING_NAME,
+ g_define_type_id,
+ 1,
+ NM_SETTING_BOND_ERROR))
+NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_BOND)
+
+#define NM_SETTING_BOND_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_BOND, NMSettingBondPrivate))
+
+typedef struct {
+ char *interface_name;
+ GHashTable *options;
+} NMSettingBondPrivate;
+
+enum {
+ PROP_0,
+ PROP_INTERFACE_NAME,
+ PROP_OPTIONS,
+ LAST_PROP
+};
+
+enum {
+ TYPE_INT,
+ TYPE_STR,
+ TYPE_BOTH,
+ TYPE_IP,
+ TYPE_IFNAME,
+};
+
+typedef struct {
+ const char *opt;
+ const char *val;
+ guint opt_type;
+ guint min;
+ guint max;
+ char *list[10];
+} BondDefault;
+
+static const BondDefault defaults[] = {
+ { NM_SETTING_BOND_OPTION_MODE, "balance-rr", TYPE_BOTH, 0, 6,
+ { "balance-rr", "active-backup", "balance-xor", "broadcast", "802.3ad", "balance-tlb", "balance-alb", NULL } },
+ { NM_SETTING_BOND_OPTION_MIIMON, "100", TYPE_INT, 0, G_MAXINT },
+ { NM_SETTING_BOND_OPTION_DOWNDELAY, "0", TYPE_INT, 0, G_MAXINT },
+ { NM_SETTING_BOND_OPTION_UPDELAY, "0", TYPE_INT, 0, G_MAXINT },
+ { NM_SETTING_BOND_OPTION_ARP_INTERVAL, "0", TYPE_INT, 0, G_MAXINT },
+ { NM_SETTING_BOND_OPTION_ARP_IP_TARGET, "", TYPE_IP },
+ { NM_SETTING_BOND_OPTION_ARP_VALIDATE, "0", TYPE_BOTH, 0, 3,
+ { "none", "active", "backup", "all", NULL } },
+ { NM_SETTING_BOND_OPTION_PRIMARY, "", TYPE_IFNAME },
+ { NM_SETTING_BOND_OPTION_PRIMARY_RESELECT, "0", TYPE_BOTH, 0, 2,
+ { "always", "better", "failure", NULL } },
+ { NM_SETTING_BOND_OPTION_FAIL_OVER_MAC, "0", TYPE_BOTH, 0, 2,
+ { "none", "active", "follow", NULL } },
+ { NM_SETTING_BOND_OPTION_USE_CARRIER, "1", TYPE_INT, 0, 1 },
+ { NM_SETTING_BOND_OPTION_AD_SELECT, "0", TYPE_BOTH, 0, 2,
+ { "stable", "bandwidth", "count", NULL } },
+ { NM_SETTING_BOND_OPTION_XMIT_HASH_POLICY, "0", TYPE_BOTH, 0, 2,
+ { "layer2", "layer3+4", "layer2+3", NULL } },
+ { NM_SETTING_BOND_OPTION_RESEND_IGMP, "1", TYPE_INT, 0, 255 },
+};
+
+/**
+ * nm_setting_bond_new:
+ *
+ * Creates a new #NMSettingBond object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingBond object
+ **/
+NMSetting *
+nm_setting_bond_new (void)
+{
+ return (NMSetting *) g_object_new (NM_TYPE_SETTING_BOND, NULL);
+}
+
+/**
+ * nm_setting_bond_get_interface_name:
+ * @setting: the #NMSettingBond
+ *
+ * Returns: the #NMSettingBond:interface-name property of the setting
+ **/
+const char *
+nm_setting_bond_get_interface_name (NMSettingBond *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_BOND (setting), NULL);
+
+ return NM_SETTING_BOND_GET_PRIVATE (setting)->interface_name;
+}
+
+/**
+ * nm_setting_bond_get_num_options:
+ * @setting: the #NMSettingBond
+ *
+ * Returns the number of options that should be set for this bond when it
+ * is activated. This can be used to retrieve each option individually
+ * using nm_setting_bond_get_option().
+ *
+ * Returns: the number of bonding options
+ **/
+guint32
+nm_setting_bond_get_num_options (NMSettingBond *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_BOND (setting), 0);
+
+ return g_hash_table_size (NM_SETTING_BOND_GET_PRIVATE (setting)->options);
+}
+
+/**
+ * nm_setting_bond_get_option:
+ * @setting: the #NMSettingBond
+ * @idx: index of the desired option, from 0 to
+ * nm_setting_bond_get_num_options() - 1
+ * @out_name: (out): on return, the name of the bonding option; this
+ * value is owned by the setting and should not be modified
+ * @out_value: (out): on return, the value of the name of the bonding
+ * option; this value is owned by the setting and should not be modified
+ *
+ * Given an index, return the value of the bonding option at that index. Indexes
+ * are *not* guaranteed to be static across modifications to options done by
+ * nm_setting_bond_add_option() and nm_setting_bond_remove_option(),
+ * and should not be used to refer to options except for short periods of time
+ * such as during option iteration.
+ *
+ * Returns: %TRUE on success if the index was valid and an option was found,
+ * %FALSE if the index was invalid (ie, greater than the number of options
+ * currently held by the setting)
+ **/
+gboolean
+nm_setting_bond_get_option (NMSettingBond *setting,
+ guint32 idx,
+ const char **out_name,
+ const char **out_value)
+{
+ NMSettingBondPrivate *priv;
+ GList *keys;
+ const char *_key = NULL, *_value = NULL;
+
+ g_return_val_if_fail (NM_IS_SETTING_BOND (setting), FALSE);
+
+ priv = NM_SETTING_BOND_GET_PRIVATE (setting);
+
+ if (idx >= nm_setting_bond_get_num_options (setting))
+ return FALSE;
+
+ keys = g_hash_table_get_keys (priv->options);
+ _key = g_list_nth_data (keys, idx);
+ _value = g_hash_table_lookup (priv->options, _key);
+
+ if (out_name)
+ *out_name = _key;
+ if (out_value)
+ *out_value = _value;
+
+ g_list_free (keys);
+ return TRUE;
+}
+
+static gboolean
+validate_int (const char *name, const char *value, const BondDefault *def)
+{
+ glong num;
+ guint i;
+
+ for (i = 0; i < strlen (value); i++) {
+ if (!g_ascii_isdigit (value[i]) && value[i] != '-')
+ return FALSE;
+ }
+
+ errno = 0;
+ num = strtol (value, NULL, 10);
+ if (errno)
+ return FALSE;
+ if (num < def->min || num > def->max)
+ return FALSE;
+
+ return TRUE;
+}
+
+static gboolean
+validate_list (const char *name, const char *value, const BondDefault *def)
+{
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (def->list) && def->list[i]; i++) {
+ if (g_strcmp0 (def->list[i], value) == 0)
+ return TRUE;
+ }
+
+ /* empty validation list means all values pass */
+ return def->list[0] == NULL ? TRUE : FALSE;
+}
+
+static gboolean
+validate_ip (const char *name, const char *value)
+{
+ char **ips, **iter;
+ gboolean success = TRUE;
+ struct in_addr addr;
+
+ if (!value || !value[0])
+ return FALSE;
+
+ ips = g_strsplit_set (value, ",", 0);
+ for (iter = ips; iter && *iter && success; iter++)
+ success = !!inet_aton (*iter, &addr);
+ g_strfreev (ips);
+
+ return success;
+}
+
+static gboolean
+validate_ifname (const char *name, const char *value)
+{
+ if (!value || !value[0])
+ return FALSE;
+
+ return nm_utils_iface_valid_name (value);
+}
+
+/**
+ * nm_setting_bond_validate_option:
+ * @name: the name of the option to validate
+ * @value: the value of the option to validate
+ *
+ * Checks whether @name is a valid bond option and @value is a valid value for
+ * the @name. If @value is %NULL, the function only validates the option name.
+ *
+ * Returns: %TRUE, if the @value is valid for the given name.
+ * If the @name is not a valid option, %FALSE will be returned.
+ *
+ * Since: 0.9.10
+ **/
+gboolean
+nm_setting_bond_validate_option (const char *name,
+ const char *value)
+{
+ guint i;
+
+ if (!name || !name[0])
+ return FALSE;
+
+ for (i = 0; i < G_N_ELEMENTS (defaults); i++) {
+ if (g_strcmp0 (defaults[i].opt, name) == 0) {
+ if (value == NULL)
+ return TRUE;
+ switch (defaults[i].opt_type) {
+ case TYPE_INT:
+ return validate_int (name, value, &defaults[i]);
+ case TYPE_STR:
+ return validate_list (name, value, &defaults[i]);
+ case TYPE_BOTH:
+ return ( validate_int (name, value, &defaults[i])
+ || validate_list (name, value, &defaults[i]));
+ case TYPE_IP:
+ return validate_ip (name, value);
+ case TYPE_IFNAME:
+ return validate_ifname (name, value);
+ }
+ return FALSE;
+ }
+ }
+ return FALSE;
+}
+
+/**
+ * nm_setting_bond_get_option_by_name:
+ * @setting: the #NMSettingBond
+ * @name: the option name for which to retrieve the value
+ *
+ * Returns the value associated with the bonding option specified by
+ * @name, if it exists.
+ *
+ * Returns: the value, or %NULL if the key/value pair was never added to the
+ * setting; the value is owned by the setting and must not be modified
+ **/
+const char *
+nm_setting_bond_get_option_by_name (NMSettingBond *setting,
+ const char *name)
+{
+ g_return_val_if_fail (NM_IS_SETTING_BOND (setting), NULL);
+
+ if (!nm_setting_bond_validate_option (name, NULL))
+ return NULL;
+
+ return g_hash_table_lookup (NM_SETTING_BOND_GET_PRIVATE (setting)->options, name);
+}
+
+/**
+ * nm_setting_bond_add_option:
+ * @setting: the #NMSettingBond
+ * @name: name for the option
+ * @value: value for the option
+ *
+ * Add an option to the table. The option is compared to an internal list
+ * of allowed options. Option names may contain only alphanumeric characters
+ * (ie [a-zA-Z0-9]). Adding a new name replaces any existing name/value pair
+ * that may already exist.
+ *
+ * The order of how to set several options is relevant because there are options
+ * that conflict with each other.
+ *
+ * Returns: %TRUE if the option was valid and was added to the internal option
+ * list, %FALSE if it was not.
+ **/
+gboolean
+nm_setting_bond_add_option (NMSettingBond *setting,
+ const char *name,
+ const char *value)
+{
+ NMSettingBondPrivate *priv;
+
+ g_return_val_if_fail (NM_IS_SETTING_BOND (setting), FALSE);
+
+ if (!value || !nm_setting_bond_validate_option (name, value))
+ return FALSE;
+
+ priv = NM_SETTING_BOND_GET_PRIVATE (setting);
+
+ g_hash_table_insert (priv->options, g_strdup (name), g_strdup (value));
+
+ if ( !strcmp (name, NM_SETTING_BOND_OPTION_MIIMON)
+ && strcmp (value, "0") != 0) {
+ g_hash_table_remove (priv->options, NM_SETTING_BOND_OPTION_ARP_INTERVAL);
+ g_hash_table_remove (priv->options, NM_SETTING_BOND_OPTION_ARP_IP_TARGET);
+ } else if ( !strcmp (name, NM_SETTING_BOND_OPTION_ARP_INTERVAL)
+ && strcmp (value, "0") != 0) {
+ g_hash_table_remove (priv->options, NM_SETTING_BOND_OPTION_MIIMON);
+ g_hash_table_remove (priv->options, NM_SETTING_BOND_OPTION_DOWNDELAY);
+ g_hash_table_remove (priv->options, NM_SETTING_BOND_OPTION_UPDELAY);
+ }
+
+ g_object_notify (G_OBJECT (setting), NM_SETTING_BOND_OPTIONS);
+
+ return TRUE;
+}
+
+/**
+ * nm_setting_bond_remove_option:
+ * @setting: the #NMSettingBond
+ * @name: name of the option to remove
+ *
+ * Remove the bonding option referenced by @name from the internal option
+ * list.
+ *
+ * Returns: %TRUE if the option was found and removed from the internal option
+ * list, %FALSE if it was not.
+ **/
+gboolean
+nm_setting_bond_remove_option (NMSettingBond *setting,
+ const char *name)
+{
+ gboolean found;
+
+ g_return_val_if_fail (NM_IS_SETTING_BOND (setting), FALSE);
+
+ if (!nm_setting_bond_validate_option (name, NULL))
+ return FALSE;
+
+ found = g_hash_table_remove (NM_SETTING_BOND_GET_PRIVATE (setting)->options, name);
+ if (found)
+ g_object_notify (G_OBJECT (setting), NM_SETTING_BOND_OPTIONS);
+ return found;
+}
+
+/**
+ * nm_setting_bond_get_valid_options:
+ * @setting: the #NMSettingBond
+ *
+ * Returns a list of valid bond options.
+ *
+ * Returns: (transfer none): a %NULL-terminated array of strings of valid bond options.
+ **/
+const char **
+nm_setting_bond_get_valid_options (NMSettingBond *setting)
+{
+ static const char *array[G_N_ELEMENTS (defaults) + 1] = { NULL };
+ int i;
+
+ /* initialize the array once */
+ if (G_UNLIKELY (array[0] == NULL)) {
+ for (i = 0; i < G_N_ELEMENTS (defaults); i++)
+ array[i] = defaults[i].opt;
+ array[i] = NULL;
+ }
+ return array;
+}
+
+/**
+ * nm_setting_bond_get_option_default:
+ * @setting: the #NMSettingBond
+ * @name: the name of the option
+ *
+ * Returns: the value of the bond option if not overridden by an entry in
+ * the #NMSettingBond:options property.
+ **/
+const char *
+nm_setting_bond_get_option_default (NMSettingBond *setting, const char *name)
+{
+ guint i;
+
+ g_return_val_if_fail (NM_IS_SETTING_BOND (setting), NULL);
+ g_return_val_if_fail (nm_setting_bond_validate_option (name, NULL), NULL);
+
+ for (i = 0; i < G_N_ELEMENTS (defaults); i++) {
+ if (g_strcmp0 (defaults[i].opt, name) == 0)
+ return defaults[i].val;
+ }
+ /* Any option that passes nm_setting_bond_validate_option() should also be found in defaults */
+ g_assert_not_reached ();
+}
+
+static gboolean
+verify (NMSetting *setting, GSList *all_settings, GError **error)
+{
+ NMSettingBondPrivate *priv = NM_SETTING_BOND_GET_PRIVATE (setting);
+ GHashTableIter iter;
+ const char *key, *value;
+ const char *valid_modes[] = { "balance-rr",
+ "active-backup",
+ "balance-xor",
+ "broadcast",
+ "802.3ad",
+ "balance-tlb",
+ "balance-alb",
+ NULL };
+ int miimon = 0, arp_interval = 0;
+ const char *arp_ip_target = NULL;
+ const char *primary;
+
+ g_hash_table_iter_init (&iter, priv->options);
+ while (g_hash_table_iter_next (&iter, (gpointer) &key, (gpointer) &value)) {
+ if (!value[0] || !nm_setting_bond_validate_option (key, value)) {
+ g_set_error (error,
+ NM_SETTING_BOND_ERROR,
+ NM_SETTING_BOND_ERROR_INVALID_OPTION,
+ _("invalid option '%s' or its value '%s'"),
+ key, value);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS);
+ return FALSE;
+ }
+ }
+
+ value = g_hash_table_lookup (priv->options, NM_SETTING_BOND_OPTION_MIIMON);
+ if (value)
+ miimon = atoi (value);
+ value = g_hash_table_lookup (priv->options, NM_SETTING_BOND_OPTION_ARP_INTERVAL);
+ if (value)
+ arp_interval = atoi (value);
+
+ /* Can only set one of miimon and arp_interval */
+ if (miimon > 0 && arp_interval > 0) {
+ g_set_error (error,
+ NM_SETTING_BOND_ERROR,
+ NM_SETTING_BOND_ERROR_INVALID_OPTION,
+ _("only one of '%s' and '%s' can be set"),
+ NM_SETTING_BOND_OPTION_MIIMON,
+ NM_SETTING_BOND_OPTION_ARP_INTERVAL);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS);
+ }
+
+ value = g_hash_table_lookup (priv->options, NM_SETTING_BOND_OPTION_MODE);
+ if (!value) {
+ g_set_error (error,
+ NM_SETTING_BOND_ERROR,
+ NM_SETTING_BOND_ERROR_MISSING_OPTION,
+ _("mandatory option '%s' is missing"),
+ NM_SETTING_BOND_OPTION_MODE);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS);
+ return FALSE;
+ }
+ if (!_nm_utils_string_in_list (value, valid_modes)) {
+ g_set_error (error,
+ NM_SETTING_BOND_ERROR,
+ NM_SETTING_BOND_ERROR_INVALID_OPTION,
+ _("'%s' is not a valid value for '%s'"),
+ value, NM_SETTING_BOND_OPTION_MODE);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS);
+ return FALSE;
+ }
+
+ /* Make sure mode is compatible with other settings */
+ if ( strcmp (value, "balance-alb") == 0
+ || strcmp (value, "balance-tlb") == 0) {
+ if (arp_interval > 0) {
+ g_set_error (error,
+ NM_SETTING_BOND_ERROR,
+ NM_SETTING_BOND_ERROR_INVALID_OPTION,
+ _("'%s=%s' is incompatible with '%s > 0'"),
+ NM_SETTING_BOND_OPTION_MODE, value, NM_SETTING_BOND_OPTION_ARP_INTERVAL);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS);
+ return FALSE;
+ }
+ }
+
+ primary = g_hash_table_lookup (priv->options, NM_SETTING_BOND_OPTION_PRIMARY);
+ if (strcmp (value, "active-backup") == 0) {
+ if (primary && !nm_utils_iface_valid_name (primary)) {
+ g_set_error (error,
+ NM_SETTING_BOND_ERROR,
+ NM_SETTING_BOND_ERROR_INVALID_OPTION,
+ _("'%s' is not a valid interface name for '%s' option"),
+ primary, NM_SETTING_BOND_OPTION_PRIMARY);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS);
+ return FALSE;
+ }
+ } else {
+ if (primary) {
+ g_set_error (error,
+ NM_SETTING_BOND_ERROR,
+ NM_SETTING_BOND_ERROR_INVALID_OPTION,
+ _("'%s' option is only valid for '%s=%s'"),
+ NM_SETTING_BOND_OPTION_PRIMARY,
+ NM_SETTING_BOND_OPTION_MODE, "active-backup");
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS);
+ return FALSE;
+ }
+ }
+
+ if (nm_setting_find_in_list (all_settings, NM_SETTING_INFINIBAND_SETTING_NAME)) {
+ if (strcmp (value, "active-backup") != 0) {
+ g_set_error (error,
+ NM_SETTING_BOND_ERROR,
+ NM_SETTING_BOND_ERROR_INVALID_OPTION,
+ _("'%s=%s' is not a valid configuration for '%s'"),
+ NM_SETTING_BOND_OPTION_MODE, value, NM_SETTING_INFINIBAND_SETTING_NAME);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS);
+ return FALSE;
+ }
+ }
+
+ if (miimon == 0) {
+ /* updelay and downdelay can only be used with miimon */
+ if (g_hash_table_lookup (priv->options, NM_SETTING_BOND_OPTION_UPDELAY)) {
+ g_set_error (error,
+ NM_SETTING_BOND_ERROR,
+ NM_SETTING_BOND_ERROR_INVALID_OPTION,
+ _("'%s' option requires '%s' option to be set"),
+ NM_SETTING_BOND_OPTION_UPDELAY, NM_SETTING_BOND_OPTION_MIIMON);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS);
+ return FALSE;
+ }
+ if (g_hash_table_lookup (priv->options, NM_SETTING_BOND_OPTION_DOWNDELAY)) {
+ g_set_error (error,
+ NM_SETTING_BOND_ERROR,
+ NM_SETTING_BOND_ERROR_INVALID_OPTION,
+ _("'%s' option requires '%s' option to be set"),
+ NM_SETTING_BOND_OPTION_DOWNDELAY, NM_SETTING_BOND_OPTION_MIIMON);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS);
+ return FALSE;
+ }
+ }
+
+ /* arp_ip_target can only be used with arp_interval, and must
+ * contain a comma-separated list of IPv4 addresses.
+ */
+ arp_ip_target = g_hash_table_lookup (priv->options, NM_SETTING_BOND_OPTION_ARP_IP_TARGET);
+ if (arp_interval > 0) {
+ char **addrs;
+ guint32 addr;
+ int i;
+
+ if (!arp_ip_target) {
+ g_set_error (error,
+ NM_SETTING_BOND_ERROR,
+ NM_SETTING_BOND_ERROR_MISSING_OPTION,
+ _("'%s' option requires '%s' option to be set"),
+ NM_SETTING_BOND_OPTION_ARP_INTERVAL, NM_SETTING_BOND_OPTION_ARP_IP_TARGET);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS);
+ return FALSE;
+ }
+
+ addrs = g_strsplit (arp_ip_target, ",", -1);
+ if (!addrs[0]) {
+ g_set_error (error,
+ NM_SETTING_BOND_ERROR,
+ NM_SETTING_BOND_ERROR_INVALID_OPTION,
+ _("'%s' option is empty"),
+ NM_SETTING_BOND_OPTION_ARP_IP_TARGET);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS);
+ g_strfreev (addrs);
+ return FALSE;
+ }
+
+ for (i = 0; addrs[i]; i++) {
+ if (!inet_pton (AF_INET, addrs[i], &addr)) {
+ g_set_error (error,
+ NM_SETTING_BOND_ERROR,
+ NM_SETTING_BOND_ERROR_INVALID_OPTION,
+ _("'%s' is not a valid IPv4 address for '%s' option"),
+ NM_SETTING_BOND_OPTION_ARP_IP_TARGET, addrs[i]);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS);
+ g_strfreev (addrs);
+ return FALSE;
+ }
+ }
+ g_strfreev (addrs);
+ } else {
+ if (arp_ip_target) {
+ g_set_error (error,
+ NM_SETTING_BOND_ERROR,
+ NM_SETTING_BOND_ERROR_INVALID_OPTION,
+ _("'%s' option requires '%s' option to be set"),
+ NM_SETTING_BOND_OPTION_ARP_IP_TARGET, NM_SETTING_BOND_OPTION_ARP_INTERVAL);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS);
+ return FALSE;
+ }
+ }
+
+ return _nm_setting_verify_deprecated_virtual_iface_name (
+ priv->interface_name, FALSE,
+ NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_INTERFACE_NAME,
+ NM_SETTING_BOND_ERROR,
+ NM_SETTING_BOND_ERROR_INVALID_PROPERTY,
+ NM_SETTING_BOND_ERROR_MISSING_PROPERTY,
+ all_settings, error);
+}
+
+static const char *
+get_virtual_iface_name (NMSetting *setting)
+{
+ NMSettingBond *self = NM_SETTING_BOND (setting);
+
+ return nm_setting_bond_get_interface_name (self);
+}
+
+static void
+nm_setting_bond_init (NMSettingBond *setting)
+{
+ NMSettingBondPrivate *priv = NM_SETTING_BOND_GET_PRIVATE (setting);
+
+ priv->options = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+
+ /* Default values: */
+ nm_setting_bond_add_option (setting, NM_SETTING_BOND_OPTION_MODE, "balance-rr");
+}
+
+static void
+finalize (GObject *object)
+{
+ NMSettingBondPrivate *priv = NM_SETTING_BOND_GET_PRIVATE (object);
+
+ g_free (priv->interface_name);
+ g_hash_table_destroy (priv->options);
+
+ G_OBJECT_CLASS (nm_setting_bond_parent_class)->finalize (object);
+}
+
+static void
+copy_hash (gpointer key, gpointer value, gpointer user_data)
+{
+ g_hash_table_insert ((GHashTable *) user_data, g_strdup (key), g_strdup (value));
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ NMSettingBondPrivate *priv = NM_SETTING_BOND_GET_PRIVATE (object);
+ GHashTable *new_hash;
+
+ switch (prop_id) {
+ case PROP_INTERFACE_NAME:
+ g_free (priv->interface_name);
+ priv->interface_name = g_value_dup_string (value);
+ break;
+ case PROP_OPTIONS:
+ /* Must make a deep copy of the hash table here... */
+ g_hash_table_remove_all (priv->options);
+ new_hash = g_value_get_boxed (value);
+ if (new_hash)
+ g_hash_table_foreach (new_hash, copy_hash, priv->options);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ NMSettingBondPrivate *priv = NM_SETTING_BOND_GET_PRIVATE (object);
+ NMSettingBond *setting = NM_SETTING_BOND (object);
+
+ switch (prop_id) {
+ case PROP_INTERFACE_NAME:
+ g_value_set_string (value, nm_setting_bond_get_interface_name (setting));
+ break;
+ case PROP_OPTIONS:
+ g_value_set_boxed (value, priv->options);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nm_setting_bond_class_init (NMSettingBondClass *setting_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
+ NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+
+ g_type_class_add_private (setting_class, sizeof (NMSettingBondPrivate));
+
+ /* virtual methods */
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+ object_class->finalize = finalize;
+ parent_class->verify = verify;
+ parent_class->get_virtual_iface_name = get_virtual_iface_name;
+
+ /* Properties */
+ /**
+ * NMSettingBond:interface-name:
+ *
+ * The name of the virtual in-kernel bonding network interface
+ **/
+ g_object_class_install_property
+ (object_class, PROP_INTERFACE_NAME,
+ g_param_spec_string (NM_SETTING_BOND_INTERFACE_NAME, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingBond:options:
+ *
+ * Dictionary of key/value pairs of bonding options. Both keys and values
+ * must be strings. Option names must contain only alphanumeric characters
+ * (ie, [a-zA-Z0-9]).
+ **/
+ g_object_class_install_property
+ (object_class, PROP_OPTIONS,
+ _nm_param_spec_specialized (NM_SETTING_BOND_OPTIONS, "", "",
+ DBUS_TYPE_G_MAP_OF_STRING,
+ G_PARAM_READWRITE |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/libnm-core/nm-setting-bond.h b/libnm-core/nm-setting-bond.h
new file mode 100644
index 0000000000..e7807380bf
--- /dev/null
+++ b/libnm-core/nm-setting-bond.h
@@ -0,0 +1,117 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2011 - 2013 Red Hat, Inc.
+ */
+
+#ifndef NM_SETTING_BOND_H
+#define NM_SETTING_BOND_H
+
+#include <nm-setting.h>
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_SETTING_BOND (nm_setting_bond_get_type ())
+#define NM_SETTING_BOND(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_BOND, NMSettingBond))
+#define NM_SETTING_BOND_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_BOND, NMSettingBondClass))
+#define NM_IS_SETTING_BOND(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_BOND))
+#define NM_IS_SETTING_BOND_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_BOND))
+#define NM_SETTING_BOND_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_BOND, NMSettingBondClass))
+
+#define NM_SETTING_BOND_SETTING_NAME "bond"
+
+/**
+ * NMSettingBondError:
+ * @NM_SETTING_BOND_ERROR_UNKNOWN: unknown or unclassified error
+ * @NM_SETTING_BOND_ERROR_INVALID_PROPERTY: the property was invalid
+ * @NM_SETTING_BOND_ERROR_MISSING_PROPERTY: the property was missing and is
+ * required
+ */
+typedef enum {
+ NM_SETTING_BOND_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/
+ NM_SETTING_BOND_ERROR_INVALID_PROPERTY, /*< nick=InvalidProperty >*/
+ NM_SETTING_BOND_ERROR_MISSING_PROPERTY, /*< nick=MissingProperty >*/
+ NM_SETTING_BOND_ERROR_INVALID_OPTION, /*< nick=InvalidOption >*/
+ NM_SETTING_BOND_ERROR_MISSING_OPTION, /*< nick=MissingOption >*/
+} NMSettingBondError;
+
+#define NM_SETTING_BOND_ERROR nm_setting_bond_error_quark ()
+GQuark nm_setting_bond_error_quark (void);
+
+#define NM_SETTING_BOND_INTERFACE_NAME "interface-name"
+#define NM_SETTING_BOND_OPTIONS "options"
+
+/* Valid options for the 'options' property */
+#define NM_SETTING_BOND_OPTION_MODE "mode"
+#define NM_SETTING_BOND_OPTION_MIIMON "miimon"
+#define NM_SETTING_BOND_OPTION_DOWNDELAY "downdelay"
+#define NM_SETTING_BOND_OPTION_UPDELAY "updelay"
+#define NM_SETTING_BOND_OPTION_ARP_INTERVAL "arp_interval"
+#define NM_SETTING_BOND_OPTION_ARP_IP_TARGET "arp_ip_target"
+#define NM_SETTING_BOND_OPTION_ARP_VALIDATE "arp_validate"
+#define NM_SETTING_BOND_OPTION_PRIMARY "primary"
+#define NM_SETTING_BOND_OPTION_PRIMARY_RESELECT "primary_reselect"
+#define NM_SETTING_BOND_OPTION_FAIL_OVER_MAC "fail_over_mac"
+#define NM_SETTING_BOND_OPTION_USE_CARRIER "use_carrier"
+#define NM_SETTING_BOND_OPTION_AD_SELECT "ad_select"
+#define NM_SETTING_BOND_OPTION_XMIT_HASH_POLICY "xmit_hash_policy"
+#define NM_SETTING_BOND_OPTION_RESEND_IGMP "resend_igmp"
+
+typedef struct {
+ NMSetting parent;
+} NMSettingBond;
+
+typedef struct {
+ NMSettingClass parent;
+
+ /* Padding for future expansion */
+ void (*_reserved1) (void);
+ void (*_reserved2) (void);
+ void (*_reserved3) (void);
+ void (*_reserved4) (void);
+} NMSettingBondClass;
+
+GType nm_setting_bond_get_type (void);
+
+NMSetting * nm_setting_bond_new (void);
+const char * nm_setting_bond_get_interface_name (NMSettingBond *setting);
+guint32 nm_setting_bond_get_num_options (NMSettingBond *setting);
+gboolean nm_setting_bond_get_option (NMSettingBond *setting,
+ guint32 idx,
+ const char **out_name,
+ const char **out_value);
+const char * nm_setting_bond_get_option_by_name (NMSettingBond *setting,
+ const char *name);
+gboolean nm_setting_bond_add_option (NMSettingBond *setting,
+ const char *name,
+ const char *value);
+gboolean nm_setting_bond_remove_option (NMSettingBond *setting,
+ const char *name);
+
+NM_AVAILABLE_IN_0_9_10
+gboolean nm_setting_bond_validate_option (const char *name,
+ const char *value);
+
+const char **nm_setting_bond_get_valid_options (NMSettingBond *setting);
+
+const char * nm_setting_bond_get_option_default (NMSettingBond *setting,
+ const char *name);
+
+G_END_DECLS
+
+#endif /* NM_SETTING_BOND_H */
diff --git a/libnm-core/nm-setting-bridge-port.c b/libnm-core/nm-setting-bridge-port.c
new file mode 100644
index 0000000000..496de581ab
--- /dev/null
+++ b/libnm-core/nm-setting-bridge-port.c
@@ -0,0 +1,304 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2012 - 2013 Red Hat, Inc.
+ */
+
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <dbus/dbus-glib.h>
+#include <glib/gi18n.h>
+
+#include "nm-setting-bridge-port.h"
+#include "nm-utils.h"
+#include "nm-utils-private.h"
+#include "nm-setting-private.h"
+
+/**
+ * SECTION:nm-setting-bridge-port
+ * @short_description: Describes connection properties for bridge ports
+ * @include: nm-setting-bridge-port.h
+ *
+ * The #NMSettingBridgePort object is a #NMSetting subclass that describes
+ * optional properties that apply to bridge ports.
+ *
+ * Since: 0.9.8
+ **/
+
+/**
+ * nm_setting_bridge_port_error_quark:
+ *
+ * Registers an error quark for #NMSettingBridgePort if necessary.
+ *
+ * Returns: the error quark used for #NMSettingBridgePort errors.
+ *
+ * Since: 0.9.8
+ **/
+GQuark
+nm_setting_bridge_port_error_quark (void)
+{
+ static GQuark quark;
+
+ if (G_UNLIKELY (!quark))
+ quark = g_quark_from_static_string ("nm-setting-bridge-port-error-quark");
+ return quark;
+}
+
+G_DEFINE_TYPE_WITH_CODE (NMSettingBridgePort, nm_setting_bridge_port, NM_TYPE_SETTING,
+ _nm_register_setting (NM_SETTING_BRIDGE_PORT_SETTING_NAME,
+ g_define_type_id,
+ 3,
+ NM_SETTING_BRIDGE_PORT_ERROR))
+NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_BRIDGE_PORT)
+
+#define NM_SETTING_BRIDGE_PORT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_BRIDGE_PORT, NMSettingBridgePortPrivate))
+
+typedef struct {
+ guint16 priority;
+ guint16 path_cost;
+ gboolean hairpin_mode;
+} NMSettingBridgePortPrivate;
+
+enum {
+ PROP_0,
+ PROP_PRIORITY,
+ PROP_PATH_COST,
+ PROP_HAIRPIN_MODE,
+ LAST_PROP
+};
+
+/**************************************************************************/
+
+/**
+ * nm_setting_bridge_port_get_priority:
+ * @setting: the #NMSettingBridgePort
+ *
+ * Returns: the #NMSettingBridgePort:priority property of the setting
+ *
+ * Since: 0.9.8
+ **/
+guint16
+nm_setting_bridge_port_get_priority (NMSettingBridgePort *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_BRIDGE_PORT (setting), 0);
+
+ return NM_SETTING_BRIDGE_PORT_GET_PRIVATE (setting)->priority;
+}
+
+/**
+ * nm_setting_bridge_port_get_path_cost:
+ * @setting: the #NMSettingBridgePort
+ *
+ * Returns: the #NMSettingBridgePort:path-cost property of the setting
+ *
+ * Since: 0.9.8
+ **/
+guint16
+nm_setting_bridge_port_get_path_cost (NMSettingBridgePort *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_BRIDGE_PORT (setting), 0);
+
+ return NM_SETTING_BRIDGE_PORT_GET_PRIVATE (setting)->path_cost;
+}
+
+/**
+ * nm_setting_bridge_port_get_hairpin_mode:
+ * @setting: the #NMSettingBridgePort
+ *
+ * Returns: the #NMSettingBridgePort:hairpin-mode property of the setting
+ *
+ * Since: 0.9.8
+ **/
+gboolean
+nm_setting_bridge_port_get_hairpin_mode (NMSettingBridgePort *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_BRIDGE_PORT (setting), FALSE);
+
+ return NM_SETTING_BRIDGE_PORT_GET_PRIVATE (setting)->hairpin_mode;
+}
+
+/**************************************************************************/
+
+#define BR_MAX_PORT_PRIORITY 63
+#define BR_DEF_PRIORITY 32
+
+#define BR_MIN_PATH_COST 1
+#define BR_MAX_PATH_COST 65535
+
+static gboolean
+verify (NMSetting *setting, GSList *all_settings, GError **error)
+{
+ NMSettingBridgePortPrivate *priv = NM_SETTING_BRIDGE_PORT_GET_PRIVATE (setting);
+
+ if (priv->priority > BR_MAX_PORT_PRIORITY) {
+ g_set_error (error,
+ NM_SETTING_BRIDGE_PORT_ERROR,
+ NM_SETTING_BRIDGE_PORT_ERROR_INVALID_PROPERTY,
+ _("'%d' is not a valid value for the property (should be <= %d)"),
+ priv->priority, BR_MAX_PORT_PRIORITY);
+ g_prefix_error (error, "%s.%s: ",
+ NM_SETTING_BRIDGE_PORT_SETTING_NAME,
+ NM_SETTING_BRIDGE_PORT_PRIORITY);
+ return FALSE;
+ }
+
+ if (priv->path_cost > BR_MAX_PATH_COST) {
+ g_set_error (error,
+ NM_SETTING_BRIDGE_PORT_ERROR,
+ NM_SETTING_BRIDGE_PORT_ERROR_INVALID_PROPERTY,
+ _("'%d' is not a valid value for the property (should be <= %d)"),
+ priv->path_cost, BR_MAX_PATH_COST);
+ g_prefix_error (error, "%s.%s: ",
+ NM_SETTING_BRIDGE_PORT_SETTING_NAME,
+ NM_SETTING_BRIDGE_PORT_PATH_COST);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**************************************************************************/
+
+/**
+ * nm_setting_bridge_port_new:
+ *
+ * Creates a new #NMSettingBridgePort object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingBridgePort object
+ *
+ * Since: 0.9.8
+ **/
+NMSetting *
+nm_setting_bridge_port_new (void)
+{
+ return (NMSetting *) g_object_new (NM_TYPE_SETTING_BRIDGE_PORT, NULL);
+}
+
+static void
+nm_setting_bridge_port_init (NMSettingBridgePort *setting)
+{
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ NMSettingBridgePortPrivate *priv = NM_SETTING_BRIDGE_PORT_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_PRIORITY:
+ priv->priority = (guint16) (g_value_get_uint (value) & 0xFFFF);
+ break;
+ case PROP_PATH_COST:
+ priv->path_cost = (guint16) (g_value_get_uint (value) & 0xFFFF);
+ break;
+ case PROP_HAIRPIN_MODE:
+ priv->hairpin_mode = g_value_get_boolean (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ NMSettingBridgePortPrivate *priv = NM_SETTING_BRIDGE_PORT_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_PRIORITY:
+ g_value_set_uint (value, priv->priority);
+ break;
+ case PROP_PATH_COST:
+ g_value_set_uint (value, priv->path_cost);
+ break;
+ case PROP_HAIRPIN_MODE:
+ g_value_set_boolean (value, priv->hairpin_mode);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nm_setting_bridge_port_class_init (NMSettingBridgePortClass *setting_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
+ NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+
+ g_type_class_add_private (setting_class, sizeof (NMSettingBridgePortPrivate));
+
+ /* virtual methods */
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+ parent_class->verify = verify;
+
+ /* Properties */
+ /**
+ * NMSettingBridgePort:priority:
+ *
+ * The Spanning Tree Protocol (STP) priority of this bridge port.
+ *
+ * Since: 0.9.8
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PRIORITY,
+ g_param_spec_uint (NM_SETTING_BRIDGE_PORT_PRIORITY, "", "",
+ 0, BR_MAX_PORT_PRIORITY, BR_DEF_PRIORITY,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingBridgePort:path-cost:
+ *
+ * The Spanning Tree Protocol (STP) port cost for destinations via this
+ * port.
+ *
+ * Since: 0.9.8
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PATH_COST,
+ g_param_spec_uint (NM_SETTING_BRIDGE_PORT_PATH_COST, "", "",
+ 0, BR_MAX_PATH_COST, 100,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingBridgePort:hairpin-mode:
+ *
+ * Enables or disabled "hairpin mode" for the port, which allows frames to
+ * be sent back out through the port the frame was received on.
+ *
+ * Since: 0.9.8
+ **/
+ g_object_class_install_property
+ (object_class, PROP_HAIRPIN_MODE,
+ g_param_spec_boolean (NM_SETTING_BRIDGE_PORT_HAIRPIN_MODE, "", "",
+ FALSE,
+ G_PARAM_READWRITE |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/libnm-core/nm-setting-bridge-port.h b/libnm-core/nm-setting-bridge-port.h
new file mode 100644
index 0000000000..fcaf22ed09
--- /dev/null
+++ b/libnm-core/nm-setting-bridge-port.h
@@ -0,0 +1,86 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2012 Red Hat, Inc.
+ */
+
+#ifndef NM_SETTING_BRIDGE_PORT_H
+#define NM_SETTING_BRIDGE_PORT_H
+
+#include <nm-setting.h>
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_SETTING_BRIDGE_PORT (nm_setting_bridge_port_get_type ())
+#define NM_SETTING_BRIDGE_PORT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_BRIDGE_PORT, NMSettingBridgePort))
+#define NM_SETTING_BRIDGE_PORT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_BRIDGE_PORT, NMSettingBridgePortClass))
+#define NM_IS_SETTING_BRIDGE_PORT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_BRIDGE_PORT))
+#define NM_IS_SETTING_BRIDGE_PORT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_BRIDGE_PORT))
+#define NM_SETTING_BRIDGE_PORT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_BRIDGE_PORT, NMSettingBridgePortClass))
+
+#define NM_SETTING_BRIDGE_PORT_SETTING_NAME "bridge-port"
+
+/**
+ * NMSettingBridgePortError:
+ * @NM_SETTING_BRIDGE_PORT_ERROR_UNKNOWN: unknown or unclassified error
+ * @NM_SETTING_BRIDGE_PORT_ERROR_INVALID_PROPERTY: the property was invalid
+ * @NM_SETTING_BRIDGE_PORT_ERROR_MISSING_PROPERTY: the property was missing and
+ * is required
+ *
+ * Since: 0.9.8
+ */
+typedef enum {
+ NM_SETTING_BRIDGE_PORT_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/
+ NM_SETTING_BRIDGE_PORT_ERROR_INVALID_PROPERTY, /*< nick=InvalidProperty >*/
+ NM_SETTING_BRIDGE_PORT_ERROR_MISSING_PROPERTY, /*< nick=MissingProperty >*/
+} NMSettingBridgePortError;
+
+#define NM_SETTING_BRIDGE_PORT_ERROR nm_setting_bridge_port_error_quark ()
+GQuark nm_setting_bridge_port_error_quark (void);
+
+#define NM_SETTING_BRIDGE_PORT_PRIORITY "priority"
+#define NM_SETTING_BRIDGE_PORT_PATH_COST "path-cost"
+#define NM_SETTING_BRIDGE_PORT_HAIRPIN_MODE "hairpin-mode"
+
+typedef struct {
+ NMSetting parent;
+} NMSettingBridgePort;
+
+typedef struct {
+ NMSettingClass parent;
+
+ /* Padding for future expansion */
+ void (*_reserved1) (void);
+ void (*_reserved2) (void);
+ void (*_reserved3) (void);
+ void (*_reserved4) (void);
+} NMSettingBridgePortClass;
+
+GType nm_setting_bridge_port_get_type (void);
+
+NMSetting * nm_setting_bridge_port_new (void);
+
+guint16 nm_setting_bridge_port_get_priority (NMSettingBridgePort *setting);
+
+guint16 nm_setting_bridge_port_get_path_cost (NMSettingBridgePort *setting);
+
+gboolean nm_setting_bridge_port_get_hairpin_mode (NMSettingBridgePort *setting);
+
+G_END_DECLS
+
+#endif /* NM_SETTING_BRIDGE_PORT_H */
diff --git a/libnm-core/nm-setting-bridge.c b/libnm-core/nm-setting-bridge.c
new file mode 100644
index 0000000000..789660036f
--- /dev/null
+++ b/libnm-core/nm-setting-bridge.c
@@ -0,0 +1,576 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2011 - 2013 Red Hat, Inc.
+ */
+
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <dbus/dbus-glib.h>
+#include <glib/gi18n.h>
+#include <linux/if_ether.h>
+
+#include "nm-setting-bridge.h"
+#include "nm-param-spec-specialized.h"
+#include "nm-setting-private.h"
+#include "nm-utils.h"
+#include "nm-utils-private.h"
+#include "nm-dbus-glib-types.h"
+
+/**
+ * SECTION:nm-setting-bridge
+ * @short_description: Describes connection properties for bridges
+ * @include: nm-setting-bridge.h
+ *
+ * The #NMSettingBridge object is a #NMSetting subclass that describes properties
+ * necessary for bridging connections.
+ *
+ * Since: 0.9.8
+ **/
+
+/**
+ * nm_setting_bridge_error_quark:
+ *
+ * Registers an error quark for #NMSettingBridge if necessary.
+ *
+ * Returns: the error quark used for #NMSettingBridge errors.
+ *
+ * Since: 0.9.8
+ **/
+GQuark
+nm_setting_bridge_error_quark (void)
+{
+ static GQuark quark;
+
+ if (G_UNLIKELY (!quark))
+ quark = g_quark_from_static_string ("nm-setting-bridge-error-quark");
+ return quark;
+}
+
+
+G_DEFINE_TYPE_WITH_CODE (NMSettingBridge, nm_setting_bridge, NM_TYPE_SETTING,
+ _nm_register_setting (NM_SETTING_BRIDGE_SETTING_NAME,
+ g_define_type_id,
+ 1,
+ NM_SETTING_BRIDGE_ERROR))
+NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_BRIDGE)
+
+#define NM_SETTING_BRIDGE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_BRIDGE, NMSettingBridgePrivate))
+
+typedef struct {
+ char * interface_name;
+ GByteArray *mac_address;
+ gboolean stp;
+ guint16 priority;
+ guint16 forward_delay;
+ guint16 hello_time;
+ guint16 max_age;
+ guint32 ageing_time;
+} NMSettingBridgePrivate;
+
+enum {
+ PROP_0,
+ PROP_INTERFACE_NAME,
+ PROP_MAC_ADDRESS,
+ PROP_STP,
+ PROP_PRIORITY,
+ PROP_FORWARD_DELAY,
+ PROP_HELLO_TIME,
+ PROP_MAX_AGE,
+ PROP_AGEING_TIME,
+ LAST_PROP
+};
+
+/**
+ * nm_setting_bridge_new:
+ *
+ * Creates a new #NMSettingBridge object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingBridge object
+ *
+ * Since: 0.9.8
+ **/
+NMSetting *
+nm_setting_bridge_new (void)
+{
+ return (NMSetting *) g_object_new (NM_TYPE_SETTING_BRIDGE, NULL);
+}
+
+/**
+ * nm_setting_bridge_get_interface_name:
+ * @setting: the #NMSettingBridge
+ *
+ * Returns: the #NMSettingBridge:interface-name property of the setting
+ *
+ * Since: 0.9.8
+ **/
+const char *
+nm_setting_bridge_get_interface_name (NMSettingBridge *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_BRIDGE (setting), 0);
+
+ return NM_SETTING_BRIDGE_GET_PRIVATE (setting)->interface_name;
+}
+
+/**
+ * nm_setting_bridge_get_mac_address:
+ * @setting: the #NMSettingBridge
+ *
+ * Returns: the #NMSettingBridge:mac-address property of the setting
+ *
+ * Since: 0.9.10
+ **/
+const GByteArray *
+nm_setting_bridge_get_mac_address (NMSettingBridge *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_BRIDGE (setting), NULL);
+
+ return NM_SETTING_BRIDGE_GET_PRIVATE (setting)->mac_address;
+}
+
+/**
+ * nm_setting_bridge_get_stp:
+ * @setting: the #NMSettingBridge
+ *
+ * Returns: the #NMSettingBridge:stp property of the setting
+ *
+ * Since: 0.9.8
+ **/
+gboolean
+nm_setting_bridge_get_stp (NMSettingBridge *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_BRIDGE (setting), FALSE);
+
+ return NM_SETTING_BRIDGE_GET_PRIVATE (setting)->stp;
+}
+
+/**
+ * nm_setting_bridge_get_priority:
+ * @setting: the #NMSettingBridge
+ *
+ * Returns: the #NMSettingBridge:priority property of the setting
+ *
+ * Since: 0.9.8
+ **/
+guint16
+nm_setting_bridge_get_priority (NMSettingBridge *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_BRIDGE (setting), 0);
+
+ return NM_SETTING_BRIDGE_GET_PRIVATE (setting)->priority;
+}
+
+/**
+ * nm_setting_bridge_get_forward_delay:
+ * @setting: the #NMSettingBridge
+ *
+ * Returns: the #NMSettingBridge:forward-delay property of the setting
+ *
+ * Since: 0.9.8
+ **/
+guint16
+nm_setting_bridge_get_forward_delay (NMSettingBridge *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_BRIDGE (setting), 0);
+
+ return NM_SETTING_BRIDGE_GET_PRIVATE (setting)->forward_delay;
+}
+
+/**
+ * nm_setting_bridge_get_hello_time:
+ * @setting: the #NMSettingBridge
+ *
+ * Returns: the #NMSettingBridge:hello-time property of the setting
+ *
+ * Since: 0.9.8
+ **/
+guint16
+nm_setting_bridge_get_hello_time (NMSettingBridge *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_BRIDGE (setting), 0);
+
+ return NM_SETTING_BRIDGE_GET_PRIVATE (setting)->hello_time;
+}
+
+/**
+ * nm_setting_bridge_get_max_age:
+ * @setting: the #NMSettingBridge
+ *
+ * Returns: the #NMSettingBridge:max-age property of the setting
+ *
+ * Since: 0.9.8
+ **/
+guint16
+nm_setting_bridge_get_max_age (NMSettingBridge *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_BRIDGE (setting), 0);
+
+ return NM_SETTING_BRIDGE_GET_PRIVATE (setting)->max_age;
+}
+
+/**
+ * nm_setting_bridge_get_ageing_time:
+ * @setting: the #NMSettingBridge
+ *
+ * Returns: the #NMSettingBridge:ageing-time property of the setting
+ *
+ * Since: 0.9.8
+ **/
+guint
+nm_setting_bridge_get_ageing_time (NMSettingBridge *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_BRIDGE (setting), 0);
+
+ return NM_SETTING_BRIDGE_GET_PRIVATE (setting)->ageing_time;
+}
+
+/* IEEE 802.1D-1998 timer values */
+#define BR_MIN_HELLO_TIME 1
+#define BR_MAX_HELLO_TIME 10
+
+#define BR_MIN_FORWARD_DELAY 2
+#define BR_MAX_FORWARD_DELAY 30
+
+#define BR_MIN_MAX_AGE 6
+#define BR_MAX_MAX_AGE 40
+
+/* IEEE 802.1D-1998 Table 7.4 */
+#define BR_MIN_AGEING_TIME 0
+#define BR_MAX_AGEING_TIME 1000000
+
+static inline gboolean
+check_range (guint32 val,
+ guint32 min,
+ guint32 max,
+ const char *prop,
+ GError **error)
+{
+ if ((val != 0) && (val < min || val > max)) {
+ g_set_error (error,
+ NM_SETTING_BRIDGE_ERROR,
+ NM_SETTING_BRIDGE_ERROR_INVALID_PROPERTY,
+ _("value '%d' is out of range <%d-%d>"),
+ val, min, max);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_BRIDGE_SETTING_NAME, prop);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean
+verify (NMSetting *setting, GSList *all_settings, GError **error)
+{
+ NMSettingBridgePrivate *priv = NM_SETTING_BRIDGE_GET_PRIVATE (setting);
+
+ if (priv->mac_address && priv->mac_address->len != ETH_ALEN) {
+ g_set_error_literal (error,
+ NM_SETTING_BRIDGE_ERROR,
+ NM_SETTING_BRIDGE_ERROR_INVALID_PROPERTY,
+ _("is not a valid MAC address"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_BRIDGE_SETTING_NAME, NM_SETTING_BRIDGE_MAC_ADDRESS);
+ return FALSE;
+ }
+
+ if (!check_range (priv->forward_delay,
+ BR_MIN_FORWARD_DELAY,
+ BR_MAX_FORWARD_DELAY,
+ NM_SETTING_BRIDGE_FORWARD_DELAY,
+ error))
+ return FALSE;
+
+ if (!check_range (priv->hello_time,
+ BR_MIN_HELLO_TIME,
+ BR_MAX_HELLO_TIME,
+ NM_SETTING_BRIDGE_HELLO_TIME,
+ error))
+ return FALSE;
+
+ if (!check_range (priv->max_age,
+ BR_MIN_MAX_AGE,
+ BR_MAX_MAX_AGE,
+ NM_SETTING_BRIDGE_MAX_AGE,
+ error))
+ return FALSE;
+
+ if (!check_range (priv->ageing_time,
+ BR_MIN_AGEING_TIME,
+ BR_MAX_AGEING_TIME,
+ NM_SETTING_BRIDGE_AGEING_TIME,
+ error))
+ return FALSE;
+
+ return _nm_setting_verify_deprecated_virtual_iface_name (
+ priv->interface_name, FALSE,
+ NM_SETTING_BRIDGE_SETTING_NAME, NM_SETTING_BRIDGE_INTERFACE_NAME,
+ NM_SETTING_BRIDGE_ERROR,
+ NM_SETTING_BRIDGE_ERROR_INVALID_PROPERTY,
+ NM_SETTING_BRIDGE_ERROR_MISSING_PROPERTY,
+ all_settings, error);
+}
+
+static const char *
+get_virtual_iface_name (NMSetting *setting)
+{
+ NMSettingBridge *self = NM_SETTING_BRIDGE (setting);
+
+ return nm_setting_bridge_get_interface_name (self);
+}
+
+static void
+nm_setting_bridge_init (NMSettingBridge *setting)
+{
+}
+
+static void
+finalize (GObject *object)
+{
+ NMSettingBridgePrivate *priv = NM_SETTING_BRIDGE_GET_PRIVATE (object);
+
+ g_free (priv->interface_name);
+
+ if (priv->mac_address)
+ g_byte_array_free (priv->mac_address, TRUE);
+
+ G_OBJECT_CLASS (nm_setting_bridge_parent_class)->finalize (object);
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ NMSettingBridgePrivate *priv = NM_SETTING_BRIDGE_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_INTERFACE_NAME:
+ g_free (priv->interface_name);
+ priv->interface_name = g_value_dup_string (value);
+ break;
+ case PROP_MAC_ADDRESS:
+ if (priv->mac_address)
+ g_byte_array_free (priv->mac_address, TRUE);
+ priv->mac_address = g_value_dup_boxed (value);
+ break;
+ case PROP_STP:
+ priv->stp = g_value_get_boolean (value);
+ break;
+ case PROP_PRIORITY:
+ priv->priority = (guint16) g_value_get_uint (value);
+ break;
+ case PROP_FORWARD_DELAY:
+ priv->forward_delay = (guint16) g_value_get_uint (value);
+ break;
+ case PROP_HELLO_TIME:
+ priv->hello_time = (guint16) g_value_get_uint (value);
+ break;
+ case PROP_MAX_AGE:
+ priv->max_age = (guint16) g_value_get_uint (value);
+ break;
+ case PROP_AGEING_TIME:
+ priv->ageing_time = g_value_get_uint (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ NMSettingBridgePrivate *priv = NM_SETTING_BRIDGE_GET_PRIVATE (object);
+ NMSettingBridge *setting = NM_SETTING_BRIDGE (object);
+
+ switch (prop_id) {
+ case PROP_INTERFACE_NAME:
+ g_value_set_string (value, nm_setting_bridge_get_interface_name (setting));
+ break;
+ case PROP_MAC_ADDRESS:
+ g_value_set_boxed (value, nm_setting_bridge_get_mac_address (setting));
+ break;
+ case PROP_STP:
+ g_value_set_boolean (value, priv->stp);
+ break;
+ case PROP_PRIORITY:
+ g_value_set_uint (value, priv->priority);
+ break;
+ case PROP_FORWARD_DELAY:
+ g_value_set_uint (value, priv->forward_delay);
+ break;
+ case PROP_HELLO_TIME:
+ g_value_set_uint (value, priv->hello_time);
+ break;
+ case PROP_MAX_AGE:
+ g_value_set_uint (value, priv->max_age);
+ break;
+ case PROP_AGEING_TIME:
+ g_value_set_uint (value, priv->ageing_time);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nm_setting_bridge_class_init (NMSettingBridgeClass *setting_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
+ NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+
+ g_type_class_add_private (setting_class, sizeof (NMSettingBridgePrivate));
+
+ /* virtual methods */
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+ object_class->finalize = finalize;
+ parent_class->verify = verify;
+ parent_class->get_virtual_iface_name = get_virtual_iface_name;
+
+ /* Properties */
+ /**
+ * NMSettingBridge:interface-name:
+ *
+ * The name of the virtual in-kernel bridging network interface
+ *
+ * Since: 0.9.8
+ **/
+ g_object_class_install_property
+ (object_class, PROP_INTERFACE_NAME,
+ g_param_spec_string (NM_SETTING_BRIDGE_INTERFACE_NAME, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingBridge:mac-address:
+ *
+ * If specified, the MAC address of bridge. When creating a new bridge, this
+ * MAC address will be set. When matching an existing (outside
+ * NetworkManager created) bridge, this MAC address must match.
+ *
+ * Since: 0.9.10
+ **/
+ g_object_class_install_property
+ (object_class, PROP_MAC_ADDRESS,
+ _nm_param_spec_specialized (NM_SETTING_BRIDGE_MAC_ADDRESS, "", "",
+ DBUS_TYPE_G_UCHAR_ARRAY,
+ G_PARAM_READWRITE |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingBridge:stp:
+ *
+ * Controls whether Spanning Tree Protocol (STP) is enabled for this bridge.
+ *
+ * Since: 0.9.8
+ **/
+ g_object_class_install_property
+ (object_class, PROP_STP,
+ g_param_spec_boolean (NM_SETTING_BRIDGE_STP, "", "",
+ TRUE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingBridge:priority:
+ *
+ * Sets the Spanning Tree Protocol (STP) priority for this bridge. Lower
+ * values are "better"; the lowest priority bridge will be elected the root
+ * bridge.
+ *
+ * Since: 0.9.8
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PRIORITY,
+ g_param_spec_uint (NM_SETTING_BRIDGE_PRIORITY, "", "",
+ 0, G_MAXUINT16, 0x8000,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingBridge:forward-delay:
+ *
+ * The Spanning Tree Protocol (STP) forwarding delay, in seconds.
+ *
+ * Since: 0.9.8
+ **/
+ g_object_class_install_property
+ (object_class, PROP_FORWARD_DELAY,
+ g_param_spec_uint (NM_SETTING_BRIDGE_FORWARD_DELAY, "", "",
+ 0, BR_MAX_FORWARD_DELAY, 15,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingBridge:hello-time:
+ *
+ * The Spanning Tree Protocol (STP) hello time, in seconds.
+ *
+ * Since: 0.9.8
+ **/
+ g_object_class_install_property
+ (object_class, PROP_HELLO_TIME,
+ g_param_spec_uint (NM_SETTING_BRIDGE_HELLO_TIME, "", "",
+ 0, BR_MAX_HELLO_TIME, 2,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingBridge:max-age:
+ *
+ * The Spanning Tree Protocol (STP) maximum message age, in seconds.
+ *
+ * Since: 0.9.8
+ **/
+ g_object_class_install_property
+ (object_class, PROP_MAX_AGE,
+ g_param_spec_uint (NM_SETTING_BRIDGE_MAX_AGE, "", "",
+ 0, BR_MAX_MAX_AGE, 20,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingBridge:ageing-time:
+ *
+ * The Ethernet MAC address aging time, in seconds.
+ *
+ * Since: 0.9.8
+ **/
+ g_object_class_install_property
+ (object_class, PROP_AGEING_TIME,
+ g_param_spec_uint (NM_SETTING_BRIDGE_AGEING_TIME, "", "",
+ 0, BR_MAX_AGEING_TIME, 300,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/libnm-core/nm-setting-bridge.h b/libnm-core/nm-setting-bridge.h
new file mode 100644
index 0000000000..f6b59262b8
--- /dev/null
+++ b/libnm-core/nm-setting-bridge.h
@@ -0,0 +1,102 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2011 - 2012 Red Hat, Inc.
+ */
+
+#ifndef NM_SETTING_BRIDGE_H
+#define NM_SETTING_BRIDGE_H
+
+#include <nm-setting.h>
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_SETTING_BRIDGE (nm_setting_bridge_get_type ())
+#define NM_SETTING_BRIDGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_BRIDGE, NMSettingBridge))
+#define NM_SETTING_BRIDGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_BRIDGE, NMSettingBridgeClass))
+#define NM_IS_SETTING_BRIDGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_BRIDGE))
+#define NM_IS_SETTING_BRIDGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_BRIDGE))
+#define NM_SETTING_BRIDGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_BRIDGE, NMSettingBridgeClass))
+
+#define NM_SETTING_BRIDGE_SETTING_NAME "bridge"
+
+/**
+ * NMSettingBridgeError:
+ * @NM_SETTING_BRIDGE_ERROR_UNKNOWN: unknown or unclassified error
+ * @NM_SETTING_BRIDGE_ERROR_INVALID_PROPERTY: the property was invalid
+ * @NM_SETTING_BRIDGE_ERROR_MISSING_PROPERTY: the property was missing and is
+ * required
+ *
+ * Since: 0.9.8
+ */
+typedef enum {
+ NM_SETTING_BRIDGE_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/
+ NM_SETTING_BRIDGE_ERROR_INVALID_PROPERTY, /*< nick=InvalidProperty >*/
+ NM_SETTING_BRIDGE_ERROR_MISSING_PROPERTY, /*< nick=MissingProperty >*/
+} NMSettingBridgeError;
+
+#define NM_SETTING_BRIDGE_ERROR nm_setting_bridge_error_quark ()
+GQuark nm_setting_bridge_error_quark (void);
+
+#define NM_SETTING_BRIDGE_INTERFACE_NAME "interface-name"
+#define NM_SETTING_BRIDGE_MAC_ADDRESS "mac-address"
+#define NM_SETTING_BRIDGE_STP "stp"
+#define NM_SETTING_BRIDGE_PRIORITY "priority"
+#define NM_SETTING_BRIDGE_FORWARD_DELAY "forward-delay"
+#define NM_SETTING_BRIDGE_HELLO_TIME "hello-time"
+#define NM_SETTING_BRIDGE_MAX_AGE "max-age"
+#define NM_SETTING_BRIDGE_AGEING_TIME "ageing-time"
+
+typedef struct {
+ NMSetting parent;
+} NMSettingBridge;
+
+typedef struct {
+ NMSettingClass parent;
+
+ /* Padding for future expansion */
+ void (*_reserved1) (void);
+ void (*_reserved2) (void);
+ void (*_reserved3) (void);
+ void (*_reserved4) (void);
+} NMSettingBridgeClass;
+
+GType nm_setting_bridge_get_type (void);
+
+NMSetting * nm_setting_bridge_new (void);
+
+const char * nm_setting_bridge_get_interface_name (NMSettingBridge *setting);
+
+NM_AVAILABLE_IN_0_9_10
+const GByteArray *nm_setting_bridge_get_mac_address (NMSettingBridge *setting);
+
+gboolean nm_setting_bridge_get_stp (NMSettingBridge *setting);
+
+guint16 nm_setting_bridge_get_priority (NMSettingBridge *setting);
+
+guint16 nm_setting_bridge_get_forward_delay (NMSettingBridge *setting);
+
+guint16 nm_setting_bridge_get_hello_time (NMSettingBridge *setting);
+
+guint16 nm_setting_bridge_get_max_age (NMSettingBridge *setting);
+
+guint32 nm_setting_bridge_get_ageing_time (NMSettingBridge *setting);
+
+G_END_DECLS
+
+#endif /* NM_SETTING_BRIDGE_H */
diff --git a/libnm-core/nm-setting-cdma.c b/libnm-core/nm-setting-cdma.c
new file mode 100644
index 0000000000..976a59680a
--- /dev/null
+++ b/libnm-core/nm-setting-cdma.c
@@ -0,0 +1,356 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2013 Red Hat, Inc.
+ */
+
+#include <string.h>
+#include <glib/gi18n.h>
+
+#include "nm-setting-cdma.h"
+#include "nm-utils.h"
+#include "nm-setting-private.h"
+
+/**
+ * SECTION:nm-setting-cdma
+ * @short_description: Describes CDMA-based mobile broadband properties
+ * @include: nm-setting-cdma.h
+ *
+ * The #NMSettingCdma object is a #NMSetting subclass that describes
+ * properties that allow connections to IS-95-based mobile broadband
+ * networks, including those using CDMA2000/EVDO technology.
+ */
+
+/**
+ * nm_setting_cdma_error_quark:
+ *
+ * Registers an error quark for #NMSettingCdma if necessary.
+ *
+ * Returns: the error quark used for #NMSettingCdma errors.
+ **/
+GQuark
+nm_setting_cdma_error_quark (void)
+{
+ static GQuark quark;
+
+ if (G_UNLIKELY (!quark))
+ quark = g_quark_from_static_string ("nm-setting-cdma-error-quark");
+ return quark;
+}
+
+
+G_DEFINE_TYPE_WITH_CODE (NMSettingCdma, nm_setting_cdma, NM_TYPE_SETTING,
+ _nm_register_setting (NM_SETTING_CDMA_SETTING_NAME,
+ g_define_type_id,
+ 1,
+ NM_SETTING_CDMA_ERROR))
+NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_CDMA)
+
+#define NM_SETTING_CDMA_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_CDMA, NMSettingCdmaPrivate))
+
+typedef struct {
+ char *number; /* For dialing, duh */
+ char *username;
+ char *password;
+ NMSettingSecretFlags password_flags;
+} NMSettingCdmaPrivate;
+
+enum {
+ PROP_0,
+ PROP_NUMBER,
+ PROP_USERNAME,
+ PROP_PASSWORD,
+ PROP_PASSWORD_FLAGS,
+
+ LAST_PROP
+};
+
+/**
+ * nm_setting_cdma_new:
+ *
+ * Creates a new #NMSettingCdma object with default values.
+ *
+ * Returns: the new empty #NMSettingCdma object
+ **/
+NMSetting *
+nm_setting_cdma_new (void)
+{
+ return (NMSetting *) g_object_new (NM_TYPE_SETTING_CDMA, NULL);
+}
+
+/**
+ * nm_setting_cdma_get_number:
+ * @setting: the #NMSettingCdma
+ *
+ * Returns: the #NMSettingCdma:number property of the setting
+ **/
+const char *
+nm_setting_cdma_get_number (NMSettingCdma *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_CDMA (setting), NULL);
+
+ return NM_SETTING_CDMA_GET_PRIVATE (setting)->number;
+}
+
+/**
+ * nm_setting_cdma_get_username:
+ * @setting: the #NMSettingCdma
+ *
+ * Returns: the #NMSettingCdma:username property of the setting
+ **/
+const char *
+nm_setting_cdma_get_username (NMSettingCdma *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_CDMA (setting), NULL);
+
+ return NM_SETTING_CDMA_GET_PRIVATE (setting)->username;
+}
+
+/**
+ * nm_setting_cdma_get_password:
+ * @setting: the #NMSettingCdma
+ *
+ * Returns: the #NMSettingCdma:password property of the setting
+ **/
+const char *
+nm_setting_cdma_get_password (NMSettingCdma *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_CDMA (setting), NULL);
+
+ return NM_SETTING_CDMA_GET_PRIVATE (setting)->password;
+}
+
+/**
+ * nm_setting_cdma_get_password_flags:
+ * @setting: the #NMSettingCdma
+ *
+ * Returns: the #NMSettingSecretFlags pertaining to the #NMSettingCdma:password
+ **/
+NMSettingSecretFlags
+nm_setting_cdma_get_password_flags (NMSettingCdma *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_CDMA (setting), NM_SETTING_SECRET_FLAG_NONE);
+
+ return NM_SETTING_CDMA_GET_PRIVATE (setting)->password_flags;
+}
+
+static gboolean
+verify (NMSetting *setting, GSList *all_settings, GError **error)
+{
+ NMSettingCdmaPrivate *priv = NM_SETTING_CDMA_GET_PRIVATE (setting);
+
+ if (!priv->number) {
+ g_set_error_literal (error,
+ NM_SETTING_CDMA_ERROR,
+ NM_SETTING_CDMA_ERROR_MISSING_PROPERTY,
+ _("property is missing"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_CDMA_SETTING_NAME, NM_SETTING_CDMA_NUMBER);
+ return FALSE;
+ } else if (!strlen (priv->number)) {
+ g_set_error_literal (error,
+ NM_SETTING_CDMA_ERROR,
+ NM_SETTING_CDMA_ERROR_INVALID_PROPERTY,
+ _("property is empty'"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_CDMA_SETTING_NAME, NM_SETTING_CDMA_NUMBER);
+ return FALSE;
+ }
+
+ if (priv->username && !strlen (priv->username)) {
+ g_set_error_literal (error,
+ NM_SETTING_CDMA_ERROR,
+ NM_SETTING_CDMA_ERROR_INVALID_PROPERTY,
+ _("property is empty"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_CDMA_SETTING_NAME, NM_SETTING_CDMA_USERNAME);
+ return FALSE;
+ }
+
+ if (priv->password && !strlen (priv->password)) {
+ g_set_error_literal (error,
+ NM_SETTING_CDMA_ERROR,
+ NM_SETTING_CDMA_ERROR_INVALID_PROPERTY,
+ _("property is empty"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_CDMA_SETTING_NAME, NM_SETTING_CDMA_PASSWORD);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static GPtrArray *
+need_secrets (NMSetting *setting)
+{
+ NMSettingCdmaPrivate *priv = NM_SETTING_CDMA_GET_PRIVATE (setting);
+ GPtrArray *secrets = NULL;
+
+ if (priv->password)
+ return NULL;
+
+ if (priv->username) {
+ if (!(priv->password_flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED)) {
+ secrets = g_ptr_array_sized_new (1);
+ g_ptr_array_add (secrets, NM_SETTING_CDMA_PASSWORD);
+ }
+ }
+
+ return secrets;
+}
+
+static void
+nm_setting_cdma_init (NMSettingCdma *setting)
+{
+}
+
+static void
+finalize (GObject *object)
+{
+ NMSettingCdmaPrivate *priv = NM_SETTING_CDMA_GET_PRIVATE (object);
+
+ g_free (priv->number);
+ g_free (priv->username);
+ g_free (priv->password);
+
+ G_OBJECT_CLASS (nm_setting_cdma_parent_class)->finalize (object);
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ NMSettingCdmaPrivate *priv = NM_SETTING_CDMA_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_NUMBER:
+ g_free (priv->number);
+ priv->number = g_value_dup_string (value);
+ break;
+ case PROP_USERNAME:
+ g_free (priv->username);
+ priv->username = g_value_dup_string (value);
+ break;
+ case PROP_PASSWORD:
+ g_free (priv->password);
+ priv->password = g_value_dup_string (value);
+ break;
+ case PROP_PASSWORD_FLAGS:
+ priv->password_flags = g_value_get_uint (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ NMSettingCdma *setting = NM_SETTING_CDMA (object);
+
+ switch (prop_id) {
+ case PROP_NUMBER:
+ g_value_set_string (value, nm_setting_cdma_get_number (setting));
+ break;
+ case PROP_USERNAME:
+ g_value_set_string (value, nm_setting_cdma_get_username (setting));
+ break;
+ case PROP_PASSWORD:
+ g_value_set_string (value, nm_setting_cdma_get_password (setting));
+ break;
+ case PROP_PASSWORD_FLAGS:
+ g_value_set_uint (value, nm_setting_cdma_get_password_flags (setting));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nm_setting_cdma_class_init (NMSettingCdmaClass *setting_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
+ NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+
+ g_type_class_add_private (setting_class, sizeof (NMSettingCdmaPrivate));
+
+ /* virtual methods */
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+ object_class->finalize = finalize;
+ parent_class->verify = verify;
+ parent_class->need_secrets = need_secrets;
+
+ /* Properties */
+
+ /**
+ * NMSettingCdma:number:
+ *
+ * The number to dial to establish the connection to the CDMA-based mobile
+ * broadband network, if any. If not specified, the default number (#777)
+ * is used when required.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_NUMBER,
+ g_param_spec_string (NM_SETTING_CDMA_NUMBER, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingCdma:username:
+ *
+ * The username used to authenticate with the network, if required. Many
+ * providers do not require a username, or accept any username. But if a
+ * username is required, it is specified here.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_USERNAME,
+ g_param_spec_string (NM_SETTING_CDMA_USERNAME, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingCdma:password:
+ *
+ * The password used to authenticate with the network, if required. Many
+ * providers do not require a password, or accept any password. But if a
+ * password is required, it is specified here.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PASSWORD,
+ g_param_spec_string (NM_SETTING_CDMA_PASSWORD, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ NM_SETTING_PARAM_SECRET |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingCdma:password-flags:
+ *
+ * Flags indicating how to handle the #NMSettingCdma:password property.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PASSWORD_FLAGS,
+ g_param_spec_uint (NM_SETTING_CDMA_PASSWORD_FLAGS, "", "",
+ NM_SETTING_SECRET_FLAG_NONE,
+ NM_SETTING_SECRET_FLAGS_ALL,
+ NM_SETTING_SECRET_FLAG_NONE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/libnm-core/nm-setting-cdma.h b/libnm-core/nm-setting-cdma.h
new file mode 100644
index 0000000000..eee4420592
--- /dev/null
+++ b/libnm-core/nm-setting-cdma.h
@@ -0,0 +1,87 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2011 Red Hat, Inc.
+ * Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#ifndef NM_SETTING_CDMA_H
+#define NM_SETTING_CDMA_H
+
+#include <nm-setting.h>
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_SETTING_CDMA (nm_setting_cdma_get_type ())
+#define NM_SETTING_CDMA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_CDMA, NMSettingCdma))
+#define NM_SETTING_CDMA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_CDMA, NMSettingCdmaClass))
+#define NM_IS_SETTING_CDMA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_CDMA))
+#define NM_IS_SETTING_CDMA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_CDMA))
+#define NM_SETTING_CDMA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_CDMA, NMSettingCdmaClass))
+
+#define NM_SETTING_CDMA_SETTING_NAME "cdma"
+
+/**
+ * NMSettingCdmaError:
+ * @NM_SETTING_CDMA_ERROR_UNKNOWN: unknown or unclassified error
+ * @NM_SETTING_CDMA_ERROR_INVALID_PROPERTY: the property was invalid
+ * @NM_SETTING_CDMA_ERROR_MISSING_PROPERTY: the property was missing and is
+ * required
+ * @NM_SETTING_CDMA_ERROR_MISSING_SERIAL_SETTING: the required #NMSettingSerial
+ * is missing in the connection
+ */
+typedef enum {
+ NM_SETTING_CDMA_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/
+ NM_SETTING_CDMA_ERROR_INVALID_PROPERTY, /*< nick=InvalidProperty >*/
+ NM_SETTING_CDMA_ERROR_MISSING_PROPERTY, /*< nick=MissingProperty >*/
+ NM_SETTING_CDMA_ERROR_MISSING_SERIAL_SETTING /*< nick=MissingSerialSetting >*/
+} NMSettingCdmaError;
+
+#define NM_SETTING_CDMA_ERROR nm_setting_cdma_error_quark ()
+GQuark nm_setting_cdma_error_quark (void);
+
+#define NM_SETTING_CDMA_NUMBER "number"
+#define NM_SETTING_CDMA_USERNAME "username"
+#define NM_SETTING_CDMA_PASSWORD "password"
+#define NM_SETTING_CDMA_PASSWORD_FLAGS "password-flags"
+
+typedef struct {
+ NMSetting parent;
+} NMSettingCdma;
+
+typedef struct {
+ NMSettingClass parent;
+
+ /* Padding for future expansion */
+ void (*_reserved1) (void);
+ void (*_reserved2) (void);
+ void (*_reserved3) (void);
+ void (*_reserved4) (void);
+} NMSettingCdmaClass;
+
+GType nm_setting_cdma_get_type (void);
+
+NMSetting *nm_setting_cdma_new (void);
+const char *nm_setting_cdma_get_number (NMSettingCdma *setting);
+const char *nm_setting_cdma_get_username (NMSettingCdma *setting);
+const char *nm_setting_cdma_get_password (NMSettingCdma *setting);
+NMSettingSecretFlags nm_setting_cdma_get_password_flags (NMSettingCdma *setting);
+
+G_END_DECLS
+
+#endif /* NM_SETTING_CDMA_H */
diff --git a/libnm-core/nm-setting-connection.c b/libnm-core/nm-setting-connection.c
new file mode 100644
index 0000000000..f206bbb032
--- /dev/null
+++ b/libnm-core/nm-setting-connection.c
@@ -0,0 +1,1333 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2013 Red Hat, Inc.
+ * Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#include <string.h>
+#include <glib/gi18n.h>
+
+#include "nm-utils.h"
+#include "nm-dbus-glib-types.h"
+#include "nm-param-spec-specialized.h"
+#include "nm-setting-connection.h"
+#include "nm-setting-private.h"
+
+/**
+ * SECTION:nm-setting-connection
+ * @short_description: Describes general connection properties
+ * @include: nm-setting-connection.h
+ *
+ * The #NMSettingConnection object is a #NMSetting subclass that describes
+ * properties that apply to all #NMConnection objects, regardless of what type
+ * of network connection they describe. Each #NMConnection object must contain
+ * a #NMSettingConnection setting.
+ **/
+
+/**
+ * nm_setting_connection_error_quark:
+ *
+ * Registers an error quark for #NMSettingConnection if necessary.
+ *
+ * Returns: the error quark used for #NMSettingConnection errors.
+ **/
+GQuark
+nm_setting_connection_error_quark (void)
+{
+ static GQuark quark;
+
+ if (G_UNLIKELY (!quark))
+ quark = g_quark_from_static_string ("nm-setting-connection-error-quark");
+ return quark;
+}
+
+
+G_DEFINE_TYPE_WITH_CODE (NMSettingConnection, nm_setting_connection, NM_TYPE_SETTING,
+ _nm_register_setting (NM_SETTING_CONNECTION_SETTING_NAME,
+ g_define_type_id,
+ 0,
+ NM_SETTING_CONNECTION_ERROR))
+NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_CONNECTION)
+
+#define NM_SETTING_CONNECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_CONNECTION, NMSettingConnectionPrivate))
+
+typedef enum {
+ PERM_TYPE_USER = 0,
+} PermType;
+
+typedef struct {
+ guint8 ptype;
+ char *item;
+} Permission;
+
+typedef struct {
+ char *id;
+ char *uuid;
+ char *interface_name;
+ char *type;
+ char *master;
+ char *slave_type;
+ GSList *permissions; /* list of Permission structs */
+ gboolean autoconnect;
+ guint64 timestamp;
+ gboolean read_only;
+ char *zone;
+ GSList *secondaries; /* secondary connections to activate with the base connection */
+ guint gateway_ping_timeout;
+} NMSettingConnectionPrivate;
+
+enum {
+ PROP_0,
+ PROP_ID,
+ PROP_UUID,
+ PROP_INTERFACE_NAME,
+ PROP_TYPE,
+ PROP_PERMISSIONS,
+ PROP_AUTOCONNECT,
+ PROP_TIMESTAMP,
+ PROP_READ_ONLY,
+ PROP_ZONE,
+ PROP_MASTER,
+ PROP_SLAVE_TYPE,
+ PROP_SECONDARIES,
+ PROP_GATEWAY_PING_TIMEOUT,
+
+ LAST_PROP
+};
+
+/***********************************************************************/
+
+#define PERM_USER_PREFIX "user:"
+
+static Permission *
+permission_new_from_str (const char *str)
+{
+ Permission *p;
+ const char *last_colon;
+ size_t ulen = 0, i;
+
+ g_return_val_if_fail (strncmp (str, PERM_USER_PREFIX, strlen (PERM_USER_PREFIX)) == 0, NULL);
+ str += strlen (PERM_USER_PREFIX);
+
+ last_colon = strrchr (str, ':');
+ if (last_colon) {
+ /* Ensure that somebody didn't pass "user::" */
+ g_return_val_if_fail (last_colon > str, NULL);
+
+ /* Reject :[detail] for now */
+ g_return_val_if_fail (*(last_colon + 1) == '\0', NULL);
+
+ /* Make sure we don't include detail in the username */
+ ulen = last_colon - str;
+ } else
+ ulen = strlen (str);
+
+ /* Sanity check the length of the username */
+ g_return_val_if_fail (ulen < 100, NULL);
+
+ /* Make sure there's no ':' in the username */
+ for (i = 0; i < ulen; i++)
+ g_return_val_if_fail (str[i] != ':', NULL);
+
+ /* And the username must be valid UTF-8 */
+ g_return_val_if_fail (g_utf8_validate (str, -1, NULL) == TRUE, NULL);
+
+ /* Yay, valid... create the new permission */
+ p = g_slice_new0 (Permission);
+ p->ptype = PERM_TYPE_USER;
+ if (last_colon) {
+ p->item = g_malloc (ulen + 1);
+ memcpy (p->item, str, ulen);
+ p->item[ulen] = '\0';
+ } else
+ p->item = g_strdup (str);
+
+ return p;
+}
+
+static Permission *
+permission_new (const char *uname)
+{
+ Permission *p;
+
+ g_return_val_if_fail (uname, NULL);
+ g_return_val_if_fail (uname[0] != '\0', NULL);
+ g_return_val_if_fail (strchr (uname, ':') == NULL, NULL);
+ g_return_val_if_fail (g_utf8_validate (uname, -1, NULL) == TRUE, NULL);
+
+ /* Yay, valid... create the new permission */
+ p = g_slice_new0 (Permission);
+ p->ptype = PERM_TYPE_USER;
+ p->item = g_strdup (uname);
+ return p;
+}
+
+static char *
+permission_to_string (Permission *p)
+{
+ return g_strdup_printf (PERM_USER_PREFIX "%s:", p->item);
+}
+
+static void
+permission_free (Permission *p)
+{
+ g_free (p->item);
+ memset (p, 0, sizeof (*p));
+ g_slice_free (Permission, p);
+}
+
+/***********************************************************************/
+
+/**
+ * nm_setting_connection_new:
+ *
+ * Creates a new #NMSettingConnection object with default values.
+ *
+ * Returns: the new empty #NMSettingConnection object
+ **/
+NMSetting *nm_setting_connection_new (void)
+{
+ return (NMSetting *) g_object_new (NM_TYPE_SETTING_CONNECTION, NULL);
+}
+
+/**
+ * nm_setting_connection_get_id:
+ * @setting: the #NMSettingConnection
+ *
+ * Returns the #NMSettingConnection:id property of the connection.
+ *
+ * Returns: the connection ID
+ **/
+const char *
+nm_setting_connection_get_id (NMSettingConnection *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting), NULL);
+
+ return NM_SETTING_CONNECTION_GET_PRIVATE (setting)->id;
+}
+
+/**
+ * nm_setting_connection_get_uuid:
+ * @setting: the #NMSettingConnection
+ *
+ * Returns the #NMSettingConnection:uuid property of the connection.
+ *
+ * Returns: the connection UUID
+ **/
+const char *
+nm_setting_connection_get_uuid (NMSettingConnection *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting), NULL);
+
+ return NM_SETTING_CONNECTION_GET_PRIVATE (setting)->uuid;
+}
+
+/**
+ * nm_setting_connection_get_interface_name:
+ * @setting: the #NMSettingConnection
+ *
+ * Returns the #NMSettingConnection:interface-name property of the connection.
+ *
+ * Returns: the connection's interface name
+ *
+ * Since: 0.9.10
+ **/
+const char *
+nm_setting_connection_get_interface_name (NMSettingConnection *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting), NULL);
+
+ return NM_SETTING_CONNECTION_GET_PRIVATE (setting)->interface_name;
+}
+
+/**
+ * nm_setting_connection_get_connection_type:
+ * @setting: the #NMSettingConnection
+ *
+ * Returns the #NMSettingConnection:type property of the connection.
+ *
+ * Returns: the connection type
+ **/
+const char *
+nm_setting_connection_get_connection_type (NMSettingConnection *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting), NULL);
+
+ return NM_SETTING_CONNECTION_GET_PRIVATE (setting)->type;
+}
+
+
+/**
+ * nm_setting_connection_get_num_permissions:
+ * @setting: the #NMSettingConnection
+ *
+ * Returns the number of entires in the #NMSettingConnection:permissions
+ * property of this setting.
+ *
+ * Returns: the number of permissions entires
+ */
+guint32
+nm_setting_connection_get_num_permissions (NMSettingConnection *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting), 0);
+
+ return g_slist_length (NM_SETTING_CONNECTION_GET_PRIVATE (setting)->permissions);
+}
+
+/**
+ * nm_setting_connection_get_permission:
+ * @setting: the #NMSettingConnection
+ * @idx: the zero-based index of the permissions entry
+ * @out_ptype: on return, the permission type (at this time, always "user")
+ * @out_pitem: on return, the permission item (formatted accoring to @ptype, see
+ * #NMSettingConnection:permissions for more detail
+ * @out_detail: on return, the permission detail (at this time, always %NULL)
+ *
+ * Retrieve one of the entries of the #NMSettingConnection:permissions property
+ * of this setting.
+ *
+ * Returns: %TRUE if a permission was returned, %FALSE if @idx was invalid
+ */
+gboolean
+nm_setting_connection_get_permission (NMSettingConnection *setting,
+ guint32 idx,
+ const char **out_ptype,
+ const char **out_pitem,
+ const char **out_detail)
+{
+ NMSettingConnectionPrivate *priv;
+ Permission *p;
+
+ g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting), FALSE);
+
+ priv = NM_SETTING_CONNECTION_GET_PRIVATE (setting);
+
+ g_return_val_if_fail (idx < g_slist_length (priv->permissions), FALSE);
+
+ p = g_slist_nth_data (priv->permissions, idx);
+ if (out_ptype)
+ *out_ptype = "user";
+ if (out_pitem)
+ *out_pitem = p->item;
+ if (out_detail)
+ *out_detail = NULL;
+
+ return TRUE;
+}
+
+/**
+ * nm_setting_connection_permissions_user_allowed:
+ * @setting: the #NMSettingConnection
+ * @uname: the user name to check permissions for
+ *
+ * Checks whether the given username is allowed to view/access this connection.
+ *
+ * Returns: %TRUE if the requested user is allowed to view this connection,
+ * %FALSE if the given user is not allowed to view this connection
+ */
+gboolean
+nm_setting_connection_permissions_user_allowed (NMSettingConnection *setting,
+ const char *uname)
+{
+ NMSettingConnectionPrivate *priv;
+ GSList *iter;
+
+ g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting), FALSE);
+ g_return_val_if_fail (uname != NULL, FALSE);
+ g_return_val_if_fail (*uname != '\0', FALSE);
+
+ priv = NM_SETTING_CONNECTION_GET_PRIVATE (setting);
+
+ /* If no permissions, visible to all */
+ if (priv->permissions == NULL)
+ return TRUE;
+
+ /* Find the username in the permissions list */
+ for (iter = priv->permissions; iter; iter = g_slist_next (iter)) {
+ Permission *p = iter->data;
+
+ if (strcmp (uname, p->item) == 0)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ * nm_setting_connection_add_permission:
+ * @setting: the #NMSettingConnection
+ * @ptype: the permission type; at this time only "user" is supported
+ * @pitem: the permission item formatted as required for @ptype
+ * @detail: (allow-none): unused at this time; must be %NULL
+ *
+ * Adds a permission to the connection's permission list. At this time, only
+ * the "user" permission type is supported, and @pitem must be a username. See
+ * #NMSettingConnection:permissions: for more details.
+ *
+ * Returns: %TRUE if the permission was unique and was successfully added to the
+ * list, %FALSE if @ptype or @pitem was invalid or it the permission was already
+ * present in the list
+ */
+gboolean
+nm_setting_connection_add_permission (NMSettingConnection *setting,
+ const char *ptype,
+ const char *pitem,
+ const char *detail)
+{
+ NMSettingConnectionPrivate *priv;
+ Permission *p;
+ GSList *iter;
+
+ g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting), FALSE);
+ g_return_val_if_fail (ptype, FALSE);
+ g_return_val_if_fail (strlen (ptype) > 0, FALSE);
+ g_return_val_if_fail (detail == NULL, FALSE);
+
+ /* Only "user" for now... */
+ g_return_val_if_fail (strcmp (ptype, "user") == 0, FALSE);
+
+ priv = NM_SETTING_CONNECTION_GET_PRIVATE (setting);
+
+ /* No dupes */
+ for (iter = priv->permissions; iter; iter = g_slist_next (iter)) {
+ p = iter->data;
+ if (strcmp (pitem, p->item) == 0)
+ return FALSE;
+ }
+
+ p = permission_new (pitem);
+ g_return_val_if_fail (p != NULL, FALSE);
+ priv->permissions = g_slist_append (priv->permissions, p);
+ g_object_notify (G_OBJECT (setting), NM_SETTING_CONNECTION_PERMISSIONS);
+
+ return TRUE;
+}
+
+/**
+ * nm_setting_connection_remove_permission:
+ * @setting: the #NMSettingConnection
+ * @idx: the zero-based index of the permission to remove
+ *
+ * Removes the permission at index @idx from the connection.
+ */
+void
+nm_setting_connection_remove_permission (NMSettingConnection *setting,
+ guint32 idx)
+{
+ NMSettingConnectionPrivate *priv;
+ GSList *iter;
+
+ g_return_if_fail (NM_IS_SETTING_CONNECTION (setting));
+
+ priv = NM_SETTING_CONNECTION_GET_PRIVATE (setting);
+ iter = g_slist_nth (priv->permissions, idx);
+ g_return_if_fail (iter != NULL);
+
+ permission_free ((Permission *) iter->data);
+ priv->permissions = g_slist_delete_link (priv->permissions, iter);
+ g_object_notify (G_OBJECT (setting), NM_SETTING_CONNECTION_PERMISSIONS);
+}
+
+/**
+ * nm_setting_connection_remove_permission_by_value:
+ * @setting: the #NMSettingConnection
+ * @ptype: the permission type; at this time only "user" is supported
+ * @pitem: the permission item formatted as required for @ptype
+ * @detail: (allow-none): unused at this time; must be %NULL
+ *
+ * Removes the permission from the connection.
+ * At this time, only the "user" permission type is supported, and @pitem must
+ * be a username. See #NMSettingConnection:permissions: for more details.
+ *
+ * Returns: %TRUE if the permission was found and removed; %FALSE if it was not.
+ *
+ * Since: 0.9.10
+ */
+gboolean
+nm_setting_connection_remove_permission_by_value (NMSettingConnection *setting,
+ const char *ptype,
+ const char *pitem,
+ const char *detail)
+{
+ NMSettingConnectionPrivate *priv;
+ Permission *p;
+ GSList *iter;
+
+ g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting), FALSE);
+ g_return_val_if_fail (ptype, FALSE);
+ g_return_val_if_fail (strlen (ptype) > 0, FALSE);
+ g_return_val_if_fail (detail == NULL, FALSE);
+
+ /* Only "user" for now... */
+ g_return_val_if_fail (strcmp (ptype, "user") == 0, FALSE);
+
+ priv = NM_SETTING_CONNECTION_GET_PRIVATE (setting);
+ for (iter = priv->permissions; iter; iter = g_slist_next (iter)) {
+ p = iter->data;
+ if (strcmp (pitem, p->item) == 0) {
+ permission_free ((Permission *) iter->data);
+ priv->permissions = g_slist_delete_link (priv->permissions, iter);
+ g_object_notify (G_OBJECT (setting), NM_SETTING_CONNECTION_PERMISSIONS);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/**
+ * nm_setting_connection_get_autoconnect:
+ * @setting: the #NMSettingConnection
+ *
+ * Returns the #NMSettingConnection:autoconnect property of the connection.
+ *
+ * Returns: the connection's autoconnect behavior
+ **/
+gboolean
+nm_setting_connection_get_autoconnect (NMSettingConnection *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting), FALSE);
+
+ return NM_SETTING_CONNECTION_GET_PRIVATE (setting)->autoconnect;
+}
+
+/**
+ * nm_setting_connection_get_timestamp:
+ * @setting: the #NMSettingConnection
+ *
+ * Returns the #NMSettingConnection:timestamp property of the connection.
+ *
+ * Returns: the connection's timestamp
+ **/
+guint64
+nm_setting_connection_get_timestamp (NMSettingConnection *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting), 0);
+
+ return NM_SETTING_CONNECTION_GET_PRIVATE (setting)->timestamp;
+}
+
+/**
+ * nm_setting_connection_get_read_only:
+ * @setting: the #NMSettingConnection
+ *
+ * Returns the #NMSettingConnection:read-only property of the connection.
+ *
+ * Returns: %TRUE if the connection is read-only, %FALSE if it is not
+ **/
+gboolean
+nm_setting_connection_get_read_only (NMSettingConnection *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting), TRUE);
+
+ return NM_SETTING_CONNECTION_GET_PRIVATE (setting)->read_only;
+}
+
+/**
+ * nm_setting_connection_get_zone:
+ * @setting: the #NMSettingConnection
+ *
+ * Returns the #NMSettingConnection:zone property of the connection.
+ *
+ * Returns: the trust level of a connection
+ **/
+const char *
+nm_setting_connection_get_zone (NMSettingConnection *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting), NULL);
+
+ return NM_SETTING_CONNECTION_GET_PRIVATE (setting)->zone;
+}
+
+/**
+ * nm_setting_connection_get_master:
+ * @setting: the #NMSettingConnection
+ *
+ * Returns the #NMSettingConnection:master property of the connection.
+ *
+ * Returns: interface name of the master device or UUID of the master
+ * connection.
+ */
+const char *
+nm_setting_connection_get_master (NMSettingConnection *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting), NULL);
+
+ return NM_SETTING_CONNECTION_GET_PRIVATE (setting)->master;
+}
+
+/**
+ * nm_setting_connection_get_slave_type:
+ * @setting: the #NMSettingConnection
+ *
+ * Returns the #NMSettingConnection:slave-type property of the connection.
+ *
+ * Returns: the type of slave this connection is, if any
+ */
+const char *
+nm_setting_connection_get_slave_type (NMSettingConnection *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting), NULL);
+
+ return NM_SETTING_CONNECTION_GET_PRIVATE (setting)->slave_type;
+}
+
+/**
+ * nm_setting_connection_is_slave_type:
+ * @setting: the #NMSettingConnection
+ * @type: the setting name (ie #NM_SETTING_BOND_SETTING_NAME) to be matched
+ * against @setting's slave type
+ *
+ * Returns: %TRUE if connection is of the given slave @type
+ */
+gboolean
+nm_setting_connection_is_slave_type (NMSettingConnection *setting,
+ const char *type)
+{
+ g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting), FALSE);
+
+ return !g_strcmp0 (NM_SETTING_CONNECTION_GET_PRIVATE (setting)->slave_type, type);
+}
+
+/**
+ * nm_setting_connection_get_num_secondaries:
+ * @setting: the #NMSettingConnection
+ *
+ * Returns: the number of configured secondary connection UUIDs
+ *
+ * Since: 0.9.8
+ **/
+guint32
+nm_setting_connection_get_num_secondaries (NMSettingConnection *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting), 0);
+
+ return g_slist_length (NM_SETTING_CONNECTION_GET_PRIVATE (setting)->secondaries);
+}
+
+/**
+ * nm_setting_connection_get_secondary:
+ * @setting: the #NMSettingConnection
+ * @idx: the zero-based index of the secondary connection UUID entry
+ *
+ * Returns: the secondary connection UUID at index @idx
+ *
+ * Since: 0.9.8
+ **/
+const char *
+nm_setting_connection_get_secondary (NMSettingConnection *setting, guint32 idx)
+{
+ NMSettingConnectionPrivate *priv;
+
+ g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting), NULL);
+
+ priv = NM_SETTING_CONNECTION_GET_PRIVATE (setting);
+ g_return_val_if_fail (idx <= g_slist_length (priv->secondaries), NULL);
+
+ return (const char *) g_slist_nth_data (priv->secondaries, idx);
+}
+
+/**
+ * nm_setting_connection_add_secondary:
+ * @setting: the #NMSettingConnection
+ * @sec_uuid: the secondary connection UUID to add
+ *
+ * Adds a new secondary connetion UUID to the setting.
+ *
+ * Returns: %TRUE if the secondary connection UUID was added; %FALSE if the UUID
+ * was already present
+ *
+ * Since: 0.9.8
+ **/
+gboolean
+nm_setting_connection_add_secondary (NMSettingConnection *setting,
+ const char *sec_uuid)
+{
+ NMSettingConnectionPrivate *priv;
+ GSList *iter;
+
+ g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting), FALSE);
+ g_return_val_if_fail (sec_uuid != NULL, FALSE);
+ g_return_val_if_fail (sec_uuid[0] != '\0', FALSE);
+
+ priv = NM_SETTING_CONNECTION_GET_PRIVATE (setting);
+ for (iter = priv->secondaries; iter; iter = g_slist_next (iter)) {
+ if (!strcmp (sec_uuid, (char *) iter->data))
+ return FALSE;
+ }
+
+ priv->secondaries = g_slist_append (priv->secondaries, g_strdup (sec_uuid));
+ g_object_notify (G_OBJECT (setting), NM_SETTING_CONNECTION_SECONDARIES);
+ return TRUE;
+}
+
+/**
+ * nm_setting_connection_remove_secondary:
+ * @setting: the #NMSettingConnection
+ * @idx: index number of the secondary connection UUID
+ *
+ * Removes the secondary coonnection UUID at index @idx.
+ *
+ * Since: 0.9.8
+ **/
+void
+nm_setting_connection_remove_secondary (NMSettingConnection *setting, guint32 idx)
+{
+ NMSettingConnectionPrivate *priv;
+ GSList *elt;
+
+ g_return_if_fail (NM_IS_SETTING_CONNECTION (setting));
+
+ priv = NM_SETTING_CONNECTION_GET_PRIVATE (setting);
+ elt = g_slist_nth (priv->secondaries, idx);
+ g_return_if_fail (elt != NULL);
+
+ g_free (elt->data);
+ priv->secondaries = g_slist_delete_link (priv->secondaries, elt);
+ g_object_notify (G_OBJECT (setting), NM_SETTING_CONNECTION_SECONDARIES);
+}
+
+/**
+ * nm_setting_connection_remove_secondary_by_value:
+ * @setting: the #NMSettingConnection
+ * @sec_uuid: the secondary connection UUID to remove
+ *
+ * Removes the secondary coonnection UUID @sec_uuid.
+ *
+ * Returns: %TRUE if the secondary connection UUID was found and removed; %FALSE if it was not.
+ *
+ * Since: 0.9.10
+ **/
+gboolean
+nm_setting_connection_remove_secondary_by_value (NMSettingConnection *setting,
+ const char *sec_uuid)
+{
+ NMSettingConnectionPrivate *priv;
+ GSList *iter;
+
+ g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting), FALSE);
+ g_return_val_if_fail (sec_uuid != NULL, FALSE);
+ g_return_val_if_fail (sec_uuid[0] != '\0', FALSE);
+
+ priv = NM_SETTING_CONNECTION_GET_PRIVATE (setting);
+ for (iter = priv->secondaries; iter; iter = g_slist_next (iter)) {
+ if (!strcmp (sec_uuid, (char *) iter->data)) {
+ priv->secondaries = g_slist_delete_link (priv->secondaries, iter);
+ g_object_notify (G_OBJECT (setting), NM_SETTING_CONNECTION_SECONDARIES);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/**
+ * nm_setting_connection_get_gateway_ping_timeout:
+ * @setting: the #NMSettingConnection
+ *
+ * Returns: the value contained in the #NMSettingConnection:gateway-ping-timeout
+ * property.
+ *
+ * Since: 0.9.10
+ **/
+guint32
+nm_setting_connection_get_gateway_ping_timeout (NMSettingConnection *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting), 0);
+
+ return NM_SETTING_CONNECTION_GET_PRIVATE (setting)->gateway_ping_timeout;
+}
+
+
+static gboolean
+verify (NMSetting *setting, GSList *all_settings, GError **error)
+{
+ NMSettingConnectionPrivate *priv = NM_SETTING_CONNECTION_GET_PRIVATE (setting);
+ gboolean is_slave;
+ GSList *iter;
+
+ if (!priv->id) {
+ g_set_error_literal (error,
+ NM_SETTING_CONNECTION_ERROR,
+ NM_SETTING_CONNECTION_ERROR_MISSING_PROPERTY,
+ _("property is missing"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_ID);
+ return FALSE;
+ } else if (!strlen (priv->id)) {
+ g_set_error_literal (error,
+ NM_SETTING_CONNECTION_ERROR,
+ NM_SETTING_CONNECTION_ERROR_INVALID_PROPERTY,
+ _("property is empty"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_ID);
+ return FALSE;
+ }
+
+ if (!priv->uuid) {
+ g_set_error_literal (error,
+ NM_SETTING_CONNECTION_ERROR,
+ NM_SETTING_CONNECTION_ERROR_MISSING_PROPERTY,
+ _("property is missing"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_UUID);
+ return FALSE;
+ } else if (!nm_utils_is_uuid (priv->uuid)) {
+ g_set_error (error,
+ NM_SETTING_CONNECTION_ERROR,
+ NM_SETTING_CONNECTION_ERROR_INVALID_PROPERTY,
+ _("'%s' is not a valid UUID"),
+ priv->uuid);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_UUID);
+ return FALSE;
+ }
+
+ /* FIXME: previously, verify() set the NMSettingConnection:interface_name property,
+ * thus modifying the setting. verify() should not do this, but keep this not to change
+ * behaviour.
+ */
+ if (!priv->interface_name) {
+ for (iter = all_settings; iter; iter = iter->next) {
+ NMSetting *s_current = iter->data;
+ char *virtual_iface_name = NULL;
+
+ if (NM_IS_SETTING_BOND (s_current))
+ g_object_get (s_current, NM_SETTING_BOND_INTERFACE_NAME, &virtual_iface_name, NULL);
+ else if (NM_IS_SETTING_BRIDGE (s_current))
+ g_object_get (s_current, NM_SETTING_BRIDGE_INTERFACE_NAME, &virtual_iface_name, NULL);
+ else if (NM_IS_SETTING_TEAM (s_current))
+ g_object_get (s_current, NM_SETTING_TEAM_INTERFACE_NAME, &virtual_iface_name, NULL);
+ else if (NM_IS_SETTING_VLAN (s_current))
+ g_object_get (s_current, NM_SETTING_VLAN_INTERFACE_NAME, &virtual_iface_name, NULL);
+ /* For NMSettingInfiniband, virtual_iface_name has no backing field.
+ * No need to set the (unset) interface_name to the default value.
+ **/
+
+ if (virtual_iface_name) {
+ if (nm_utils_iface_valid_name (virtual_iface_name)) {
+ /* found a new interface name. */
+ priv->interface_name = virtual_iface_name;
+ break;
+ }
+ g_free (virtual_iface_name);
+ }
+ }
+ }
+
+ if (priv->interface_name) {
+ if (!nm_utils_iface_valid_name (priv->interface_name)) {
+ g_set_error (error,
+ NM_SETTING_CONNECTION_ERROR,
+ NM_SETTING_CONNECTION_ERROR_INVALID_PROPERTY,
+ _("'%s' is not a valid interface name"),
+ priv->interface_name);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_INTERFACE_NAME);
+ return FALSE;
+ }
+ }
+
+ if (!priv->type) {
+ g_set_error_literal (error,
+ NM_SETTING_CONNECTION_ERROR,
+ NM_SETTING_CONNECTION_ERROR_MISSING_PROPERTY,
+ _("property is missing"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_TYPE);
+ return FALSE;
+ } else if (!strlen (priv->type)) {
+ g_set_error_literal (error,
+ NM_SETTING_CONNECTION_ERROR,
+ NM_SETTING_CONNECTION_ERROR_INVALID_PROPERTY,
+ _("property is empty"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_TYPE);
+ return FALSE;
+ }
+
+ /* Make sure the corresponding 'type' item is present */
+ if (all_settings && !nm_setting_find_in_list (all_settings, priv->type)) {
+ g_set_error (error,
+ NM_SETTING_CONNECTION_ERROR,
+ NM_SETTING_CONNECTION_ERROR_TYPE_SETTING_NOT_FOUND,
+ _("requires presence of '%s' setting in the connection"),
+ priv->type);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_TYPE);
+ return FALSE;
+ }
+
+ is_slave = ( priv->slave_type
+ && ( !strcmp (priv->slave_type, NM_SETTING_BOND_SETTING_NAME)
+ || !strcmp (priv->slave_type, NM_SETTING_BRIDGE_SETTING_NAME)
+ || !strcmp (priv->slave_type, NM_SETTING_TEAM_SETTING_NAME)));
+
+ if (priv->slave_type && !is_slave) {
+ g_set_error (error,
+ NM_SETTING_CONNECTION_ERROR,
+ NM_SETTING_CONNECTION_ERROR_INVALID_PROPERTY,
+ _("Unknown slave type '%s'"), priv->slave_type);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_SLAVE_TYPE);
+ return NM_SETTING_VERIFY_ERROR;
+ }
+
+ if (is_slave) {
+ if (!priv->master) {
+ g_set_error_literal (error,
+ NM_SETTING_CONNECTION_ERROR,
+ NM_SETTING_CONNECTION_ERROR_MISSING_PROPERTY,
+ _("Slave connections need a valid '" NM_SETTING_CONNECTION_MASTER "' property"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_MASTER);
+ return NM_SETTING_VERIFY_ERROR;
+ }
+ } else {
+ if (priv->master) {
+ g_set_error_literal (error,
+ NM_SETTING_CONNECTION_ERROR,
+ NM_SETTING_CONNECTION_ERROR_MISSING_PROPERTY,
+ _("Cannot set '" NM_SETTING_CONNECTION_MASTER "' without '" NM_SETTING_CONNECTION_SLAVE_TYPE "'"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_SLAVE_TYPE);
+ return NM_SETTING_VERIFY_ERROR;
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean
+compare_property (NMSetting *setting,
+ NMSetting *other,
+ const GParamSpec *prop_spec,
+ NMSettingCompareFlags flags)
+{
+ /* Handle ignore ID */
+ if ( (flags & NM_SETTING_COMPARE_FLAG_IGNORE_ID)
+ && g_strcmp0 (prop_spec->name, NM_SETTING_CONNECTION_ID) == 0)
+ return TRUE;
+
+ /* Otherwise chain up to parent to handle generic compare */
+ return NM_SETTING_CLASS (nm_setting_connection_parent_class)->compare_property (setting, other, prop_spec, flags);
+}
+
+static void
+nm_setting_connection_init (NMSettingConnection *setting)
+{
+}
+
+static void
+finalize (GObject *object)
+{
+ NMSettingConnectionPrivate *priv = NM_SETTING_CONNECTION_GET_PRIVATE (object);
+
+ g_free (priv->id);
+ g_free (priv->uuid);
+ g_free (priv->interface_name);
+ g_free (priv->type);
+ g_free (priv->zone);
+ g_free (priv->master);
+ g_free (priv->slave_type);
+ g_slist_free_full (priv->permissions, (GDestroyNotify) permission_free);
+ g_slist_free_full (priv->secondaries, g_free);
+
+ G_OBJECT_CLASS (nm_setting_connection_parent_class)->finalize (object);
+}
+
+static GSList *
+perm_stringlist_to_permlist (GSList *strlist)
+{
+ GSList *list = NULL, *iter;
+
+ for (iter = strlist; iter; iter = g_slist_next (iter)) {
+ Permission *p;
+
+ p = permission_new_from_str ((const char *) iter->data);
+ if (p)
+ list = g_slist_append (list, p);
+ }
+
+ return list;
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ NMSettingConnectionPrivate *priv = NM_SETTING_CONNECTION_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_ID:
+ g_free (priv->id);
+ priv->id = g_value_dup_string (value);
+ break;
+ case PROP_UUID:
+ g_free (priv->uuid);
+ priv->uuid = g_value_dup_string (value);
+ break;
+ case PROP_INTERFACE_NAME:
+ g_free (priv->interface_name);
+ priv->interface_name = g_value_dup_string (value);
+ break;
+ case PROP_TYPE:
+ g_free (priv->type);
+ priv->type = g_value_dup_string (value);
+ break;
+ case PROP_PERMISSIONS:
+ g_slist_free_full (priv->permissions, (GDestroyNotify) permission_free);
+ priv->permissions = perm_stringlist_to_permlist (g_value_get_boxed (value));
+ break;
+ case PROP_AUTOCONNECT:
+ priv->autoconnect = g_value_get_boolean (value);
+ break;
+ case PROP_TIMESTAMP:
+ priv->timestamp = g_value_get_uint64 (value);
+ break;
+ case PROP_READ_ONLY:
+ priv->read_only = g_value_get_boolean (value);
+ break;
+ case PROP_ZONE:
+ g_free (priv->zone);
+ priv->zone = g_value_dup_string (value);
+ break;
+ case PROP_MASTER:
+ g_free (priv->master);
+ priv->master = g_value_dup_string (value);
+ break;
+ case PROP_SLAVE_TYPE:
+ g_free (priv->slave_type);
+ priv->slave_type = g_value_dup_string (value);
+ break;
+ case PROP_SECONDARIES:
+ g_slist_free_full (priv->secondaries, g_free);
+ priv->secondaries = g_value_dup_boxed (value);
+ break;
+ case PROP_GATEWAY_PING_TIMEOUT:
+ priv->gateway_ping_timeout = g_value_get_uint (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static GSList *
+perm_permlist_to_stringlist (GSList *permlist)
+{
+ GSList *list = NULL, *iter;
+
+ for (iter = permlist; iter; iter = g_slist_next (iter))
+ list = g_slist_append (list, permission_to_string ((Permission *) iter->data));
+ return list;
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ NMSettingConnection *setting = NM_SETTING_CONNECTION (object);
+ NMSettingConnectionPrivate *priv = NM_SETTING_CONNECTION_GET_PRIVATE (setting);
+
+ switch (prop_id) {
+ case PROP_ID:
+ g_value_set_string (value, nm_setting_connection_get_id (setting));
+ break;
+ case PROP_UUID:
+ g_value_set_string (value, nm_setting_connection_get_uuid (setting));
+ break;
+ case PROP_INTERFACE_NAME:
+ g_value_set_string (value, nm_setting_connection_get_interface_name (setting));
+ break;
+ case PROP_TYPE:
+ g_value_set_string (value, nm_setting_connection_get_connection_type (setting));
+ break;
+ case PROP_PERMISSIONS:
+ g_value_take_boxed (value, perm_permlist_to_stringlist (priv->permissions));
+ break;
+ case PROP_AUTOCONNECT:
+ g_value_set_boolean (value, nm_setting_connection_get_autoconnect (setting));
+ break;
+ case PROP_TIMESTAMP:
+ g_value_set_uint64 (value, nm_setting_connection_get_timestamp (setting));
+ break;
+ case PROP_READ_ONLY:
+ g_value_set_boolean (value, nm_setting_connection_get_read_only (setting));
+ break;
+ case PROP_ZONE:
+ g_value_set_string (value, nm_setting_connection_get_zone (setting));
+ break;
+ case PROP_MASTER:
+ g_value_set_string (value, nm_setting_connection_get_master (setting));
+ break;
+ case PROP_SLAVE_TYPE:
+ g_value_set_string (value, nm_setting_connection_get_slave_type (setting));
+ break;
+ case PROP_SECONDARIES:
+ g_value_set_boxed (value, priv->secondaries);
+ break;
+ case PROP_GATEWAY_PING_TIMEOUT:
+ g_value_set_uint (value, priv->gateway_ping_timeout);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nm_setting_connection_class_init (NMSettingConnectionClass *setting_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
+ NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+
+ g_type_class_add_private (setting_class, sizeof (NMSettingConnectionPrivate));
+
+ /* virtual methods */
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+ object_class->finalize = finalize;
+ parent_class->verify = verify;
+ parent_class->compare_property = compare_property;
+
+ /* Properties */
+
+ /**
+ * NMSettingConnection:id:
+ *
+ * A human readable unique identifier for the connection, like "Work Wi-Fi"
+ * or "T-Mobile 3G".
+ **/
+ g_object_class_install_property
+ (object_class, PROP_ID,
+ g_param_spec_string (NM_SETTING_CONNECTION_ID, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ NM_SETTING_PARAM_FUZZY_IGNORE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingConnection:uuid:
+ *
+ * A universally unique identifier for the connection, for example generated
+ * with libuuid. It should be assigned when the connection is created, and
+ * never changed as long as the connection still applies to the same
+ * network. For example, it should not be changed when the
+ * #NMSettingConnection:id property or #NMSettingIP4Config changes, but
+ * might need to be re-created when the Wi-Fi SSID, mobile broadband network
+ * provider, or #NMSettingConnection:type property changes.
+ *
+ * The UUID must be in the format "2815492f-7e56-435e-b2e9-246bd7cdc664"
+ * (ie, contains only hexadecimal characters and "-"). A suitable UUID may
+ * be generated by nm_utils_uuid_generate() or
+ * nm_utils_uuid_generate_from_string().
+ **/
+ g_object_class_install_property
+ (object_class, PROP_UUID,
+ g_param_spec_string (NM_SETTING_CONNECTION_UUID, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ NM_SETTING_PARAM_FUZZY_IGNORE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingConnection:interface-name:
+ *
+ * The name of the network interface this connection is bound to. If not
+ * set, then the connection can be attached to any interface of the
+ * appropriate type (subject to restrictions imposed by other settings).
+ *
+ * For software devices this specifies the name of the created device.
+ *
+ * For connection types where interface names cannot easily be made
+ * persistent (e.g. mobile broadband or USB Ethernet), this property should
+ * not be used. Setting this property restricts the interfaces a connection
+ * can be used with, and if interface names change or are reordered the
+ * connection may be applied to the wrong interface.
+ *
+ * Since: 0.9.10
+ **/
+ g_object_class_install_property
+ (object_class, PROP_INTERFACE_NAME,
+ g_param_spec_string (NM_SETTING_CONNECTION_INTERFACE_NAME, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingConnection:type:
+ *
+ * Base type of the connection. For hardware-dependent connections, should
+ * contain the setting name of the hardware-type specific setting (ie,
+ * "802-3-ethernet" or "802-11-wireless" or "bluetooth", etc), and for
+ * non-hardware dependent connections like VPN or otherwise, should contain
+ * the setting name of that setting type (ie, "vpn" or "bridge", etc).
+ **/
+ g_object_class_install_property
+ (object_class, PROP_TYPE,
+ g_param_spec_string (NM_SETTING_CONNECTION_TYPE, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingConnection:permissions:
+ *
+ * An array of strings defining what access a given user has to this
+ * connection. If this is %NULL or empty, all users are allowed to access
+ * this connection. Otherwise a user is allowed to access this connection
+ * if and only if they are in this list. Each entry is of the form
+ * "[type]:[id]:[reserved]"; for example, "user:dcbw:blah".
+ *
+ * At this time only the "user" [type] is allowed. Any other values are
+ * ignored and reserved for future use. [id] is the username that this
+ * permission refers to, which may not contain the ":" character. Any
+ * [reserved] information present must be ignored and is reserved for future
+ * use. All of [type], [id], and [reserved] must be valid UTF-8.
+ */
+ g_object_class_install_property
+ (object_class, PROP_PERMISSIONS,
+ _nm_param_spec_specialized (NM_SETTING_CONNECTION_PERMISSIONS, "", "",
+ DBUS_TYPE_G_LIST_OF_STRING,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingConnection:autoconnect:
+ *
+ * Whether or not the connection should be automatically connected by
+ * NetworkManager when the resources for the connection are available.
+ * %TRUE to automatically activate the connection, %FALSE to require manual
+ * intervention to activate the connection.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_AUTOCONNECT,
+ g_param_spec_boolean (NM_SETTING_CONNECTION_AUTOCONNECT, "", "",
+ TRUE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ NM_SETTING_PARAM_FUZZY_IGNORE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingConnection:timestamp:
+ *
+ * The time, in seconds since the Unix Epoch, that the connection was last
+ * _successfully_ fully activated.
+ *
+ * NetworkManager updates the connection timestamp periodically when the
+ * connection is active to ensure that an active connection has the latest
+ * timestamp. The property is only meant for reading (changes to this
+ * property will not be preserved).
+ **/
+ g_object_class_install_property
+ (object_class, PROP_TIMESTAMP,
+ g_param_spec_uint64 (NM_SETTING_CONNECTION_TIMESTAMP, "", "",
+ 0, G_MAXUINT64, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ NM_SETTING_PARAM_FUZZY_IGNORE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingConnection:read-only:
+ *
+ * %FALSE if the connection can be modified using the provided settings
+ * service's D-Bus interface with the right privileges, or %TRUE if the
+ * connection is read-only and cannot be modified.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_READ_ONLY,
+ g_param_spec_boolean (NM_SETTING_CONNECTION_READ_ONLY, "", "",
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ NM_SETTING_PARAM_FUZZY_IGNORE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingConnection:zone:
+ *
+ * The trust level of a the connection. Free form case-insensitive string
+ * (for example "Home", "Work", "Public"). %NULL or unspecified zone means
+ * the connection will be placed in the default zone as defined by the
+ * firewall.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_ZONE,
+ g_param_spec_string (NM_SETTING_CONNECTION_ZONE, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ NM_SETTING_PARAM_FUZZY_IGNORE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingConnection:master:
+ *
+ * Interface name of the master device or UUID of the master connection.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_MASTER,
+ g_param_spec_string (NM_SETTING_CONNECTION_MASTER, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ NM_SETTING_PARAM_FUZZY_IGNORE |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingConnection:slave-type:
+ *
+ * Setting name of the device type of this slave's master connection (eg,
+ * %NM_SETTING_BOND_SETTING_NAME), or %NULL if this connection is not a
+ * slave.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_SLAVE_TYPE,
+ g_param_spec_string (NM_SETTING_CONNECTION_SLAVE_TYPE, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ NM_SETTING_PARAM_FUZZY_IGNORE |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingConnection:secondaries:
+ *
+ * List of connection UUIDs that should be activated when the base
+ * connection itself is activated. Currently only VPN connections are
+ * supported.
+ *
+ * Since: 0.9.8
+ **/
+ g_object_class_install_property
+ (object_class, PROP_SECONDARIES,
+ _nm_param_spec_specialized (NM_SETTING_CONNECTION_SECONDARIES, "", "",
+ DBUS_TYPE_G_LIST_OF_STRING,
+ G_PARAM_READWRITE |
+ NM_SETTING_PARAM_FUZZY_IGNORE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingConnection:gateway-ping-timeout:
+ *
+ * If greater than zero, delay success of IP addressing until either the
+ * timeout is reached, or an IP gateway replies to a ping.
+ *
+ * Since: 0.9.10
+ **/
+ g_object_class_install_property
+ (object_class, PROP_GATEWAY_PING_TIMEOUT,
+ g_param_spec_uint (NM_SETTING_CONNECTION_GATEWAY_PING_TIMEOUT, "", "",
+ 0, 30, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/libnm-core/nm-setting-connection.h b/libnm-core/nm-setting-connection.h
new file mode 100644
index 0000000000..143fa11d58
--- /dev/null
+++ b/libnm-core/nm-setting-connection.h
@@ -0,0 +1,151 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2014 Red Hat, Inc.
+ * Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#ifndef NM_SETTING_CONNECTION_H
+#define NM_SETTING_CONNECTION_H
+
+#include "nm-setting.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_SETTING_CONNECTION (nm_setting_connection_get_type ())
+#define NM_SETTING_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_CONNECTION, NMSettingConnection))
+#define NM_SETTING_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_CONNECTION, NMSettingConnectionClass))
+#define NM_IS_SETTING_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_CONNECTION))
+#define NM_IS_SETTING_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_CONNECTION))
+#define NM_SETTING_CONNECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_CONNECTION, NMSettingConnectionClass))
+
+#define NM_SETTING_CONNECTION_SETTING_NAME "connection"
+
+/**
+ * NMSettingConnectionError:
+ * @NM_SETTING_CONNECTION_ERROR_UNKNOWN: unknown or unclassified error
+ * @NM_SETTING_CONNECTION_ERROR_INVALID_PROPERTY: the property's value is
+ * invalid
+ * @NM_SETTING_CONNECTION_ERROR_MISSING_PROPERTY: a required property is not
+ * present
+ * @NM_SETTING_CONNECTION_ERROR_TYPE_SETTING_NOT_FOUND: the #NMSetting object
+ * referenced by the setting name contained in the
+ * #NMSettingConnection:type property was not present in the #NMConnection
+ * @NM_SETTING_CONNECTION_ERROR_IP_CONFIG_NOT_ALLOWED: ip configuration is not
+ * allowed to be present.
+ *
+ * Describes errors that may result from operations involving a
+ * #NMSettingConnection.
+ *
+ **/
+typedef enum
+{
+ NM_SETTING_CONNECTION_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/
+ NM_SETTING_CONNECTION_ERROR_INVALID_PROPERTY, /*< nick=InvalidProperty >*/
+ NM_SETTING_CONNECTION_ERROR_MISSING_PROPERTY, /*< nick=MissingProperty >*/
+ NM_SETTING_CONNECTION_ERROR_TYPE_SETTING_NOT_FOUND, /*< nick=TypeSettingNotFound >*/
+ NM_SETTING_CONNECTION_ERROR_IP_CONFIG_NOT_ALLOWED, /*< nick=IpConfigNotAllowed >*/
+} NMSettingConnectionError;
+
+#define NM_SETTING_CONNECTION_ERROR nm_setting_connection_error_quark ()
+GQuark nm_setting_connection_error_quark (void);
+
+#define NM_SETTING_CONNECTION_ID "id"
+#define NM_SETTING_CONNECTION_UUID "uuid"
+#define NM_SETTING_CONNECTION_INTERFACE_NAME "interface-name"
+#define NM_SETTING_CONNECTION_TYPE "type"
+#define NM_SETTING_CONNECTION_AUTOCONNECT "autoconnect"
+#define NM_SETTING_CONNECTION_TIMESTAMP "timestamp"
+#define NM_SETTING_CONNECTION_READ_ONLY "read-only"
+#define NM_SETTING_CONNECTION_PERMISSIONS "permissions"
+#define NM_SETTING_CONNECTION_ZONE "zone"
+#define NM_SETTING_CONNECTION_MASTER "master"
+#define NM_SETTING_CONNECTION_SLAVE_TYPE "slave-type"
+#define NM_SETTING_CONNECTION_SECONDARIES "secondaries"
+#define NM_SETTING_CONNECTION_GATEWAY_PING_TIMEOUT "gateway-ping-timeout"
+
+/**
+ * NMSettingConnection:
+ *
+ * The NMSettingConnection struct contains only private data.
+ * It should only be accessed through the functions described below.
+ */
+typedef struct {
+ NMSetting parent;
+} NMSettingConnection;
+
+typedef struct {
+ NMSettingClass parent;
+
+ /* Padding for future expansion */
+ void (*_reserved1) (void);
+ void (*_reserved2) (void);
+ void (*_reserved3) (void);
+ void (*_reserved4) (void);
+} NMSettingConnectionClass;
+
+GType nm_setting_connection_get_type (void);
+
+NMSetting * nm_setting_connection_new (void);
+const char *nm_setting_connection_get_id (NMSettingConnection *setting);
+const char *nm_setting_connection_get_uuid (NMSettingConnection *setting);
+NM_AVAILABLE_IN_0_9_10
+const char *nm_setting_connection_get_interface_name (NMSettingConnection *setting);
+const char *nm_setting_connection_get_connection_type (NMSettingConnection *setting);
+gboolean nm_setting_connection_get_autoconnect (NMSettingConnection *setting);
+guint64 nm_setting_connection_get_timestamp (NMSettingConnection *setting);
+gboolean nm_setting_connection_get_read_only (NMSettingConnection *setting);
+
+guint32 nm_setting_connection_get_num_permissions (NMSettingConnection *setting);
+gboolean nm_setting_connection_get_permission (NMSettingConnection *setting,
+ guint32 idx,
+ const char **out_ptype,
+ const char **out_pitem,
+ const char **out_detail);
+const char *nm_setting_connection_get_zone (NMSettingConnection *setting);
+gboolean nm_setting_connection_permissions_user_allowed (NMSettingConnection *setting, const char *uname);
+gboolean nm_setting_connection_add_permission (NMSettingConnection *setting,
+ const char *ptype,
+ const char *pitem,
+ const char *detail);
+void nm_setting_connection_remove_permission (NMSettingConnection *setting,
+ guint32 idx);
+NM_AVAILABLE_IN_0_9_10
+gboolean nm_setting_connection_remove_permission_by_value (NMSettingConnection *setting,
+ const char *ptype,
+ const char *pitem,
+ const char *detail);
+
+const char *nm_setting_connection_get_master (NMSettingConnection *setting);
+gboolean nm_setting_connection_is_slave_type (NMSettingConnection *setting,
+ const char *type);
+const char *nm_setting_connection_get_slave_type (NMSettingConnection *setting);
+
+guint32 nm_setting_connection_get_num_secondaries (NMSettingConnection *setting);
+const char *nm_setting_connection_get_secondary (NMSettingConnection *setting, guint32 idx);
+gboolean nm_setting_connection_add_secondary (NMSettingConnection *setting, const char *sec_uuid);
+void nm_setting_connection_remove_secondary (NMSettingConnection *setting, guint32 idx);
+NM_AVAILABLE_IN_0_9_10
+gboolean nm_setting_connection_remove_secondary_by_value (NMSettingConnection *setting, const char *sec_uuid);
+
+NM_AVAILABLE_IN_0_9_10
+guint32 nm_setting_connection_get_gateway_ping_timeout (NMSettingConnection *setting);
+
+G_END_DECLS
+
+#endif /* NM_SETTING_CONNECTION_H */
diff --git a/libnm-core/nm-setting-dcb.c b/libnm-core/nm-setting-dcb.c
new file mode 100644
index 0000000000..4839f7fe75
--- /dev/null
+++ b/libnm-core/nm-setting-dcb.c
@@ -0,0 +1,1207 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#include <string.h>
+#include <dbus/dbus-glib.h>
+#include <glib/gi18n.h>
+
+#include "nm-setting-dcb.h"
+#include "nm-param-spec-specialized.h"
+#include "nm-utils.h"
+#include "nm-utils-private.h"
+#include "nm-dbus-glib-types.h"
+#include "nm-setting-private.h"
+
+/**
+ * SECTION:nm-setting-dcb
+ * @short_description: Connection properties for Data Center Bridging
+ * @include: nm-setting-dcb.h
+ *
+ * The #NMSettingDcb object is a #NMSetting subclass that describes properties
+ * for enabling and using Data Center Bridging (DCB) on Ethernet networks.
+ * DCB is a set of protocols (including 802.1Qbb, 802.1Qaz, 802.1Qau, and
+ * 802.1AB) to eliminate packet loss in Ethernet networks and support the use
+ * of storage technologies like Fibre Channel over Ethernet (FCoE) and iSCSI.
+ *
+ * Since: 0.9.10
+ **/
+
+/**
+ * nm_setting_dcb_error_quark:
+ *
+ * Registers an error quark for #NMSettingDcb if necessary.
+ *
+ * Returns: the error quark used for #NMSettingDcb errors.
+ *
+ * Since: 0.9.10
+ **/
+GQuark
+nm_setting_dcb_error_quark (void)
+{
+ static GQuark quark;
+
+ if (G_UNLIKELY (!quark))
+ quark = g_quark_from_static_string ("nm-setting-dcb-error-quark");
+ return quark;
+}
+
+
+G_DEFINE_TYPE_WITH_CODE (NMSettingDcb, nm_setting_dcb, NM_TYPE_SETTING,
+ _nm_register_setting (NM_SETTING_DCB_SETTING_NAME,
+ g_define_type_id,
+ 2,
+ NM_SETTING_DCB_ERROR))
+NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_DCB)
+
+#define NM_SETTING_DCB_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_DCB, NMSettingDcbPrivate))
+
+typedef struct {
+ NMSettingDcbFlags app_fcoe_flags;
+ gint app_fcoe_priority;
+ const char * app_fcoe_mode;
+
+ NMSettingDcbFlags app_iscsi_flags;
+ gint app_iscsi_priority;
+
+ NMSettingDcbFlags app_fip_flags;
+ gint app_fip_priority;
+
+ /* Priority Flow Control */
+ NMSettingDcbFlags pfc_flags;
+ guint pfc[8];
+
+ /* Priority Groups */
+ NMSettingDcbFlags priority_group_flags;
+ guint priority_group_id[8];
+ guint priority_group_bandwidth[8];
+ guint priority_bandwidth[8];
+ guint priority_strict[8];
+ guint priority_traffic_class[8];
+} NMSettingDcbPrivate;
+
+enum {
+ PROP_0,
+ PROP_APP_FCOE_FLAGS,
+ PROP_APP_FCOE_PRIORITY,
+ PROP_APP_FCOE_MODE,
+
+ PROP_APP_ISCSI_FLAGS,
+ PROP_APP_ISCSI_PRIORITY,
+
+ PROP_APP_FIP_FLAGS,
+ PROP_APP_FIP_PRIORITY,
+
+ PROP_PFC_FLAGS,
+ PROP_PFC,
+
+ PROP_PRIORITY_GROUP_FLAGS,
+ PROP_PRIORITY_GROUP_ID,
+ PROP_PRIORITY_GROUP_BANDWIDTH,
+ PROP_PRIORITY_BANDWIDTH,
+ PROP_PRIORITY_STRICT,
+ PROP_PRIORITY_TRAFFIC_CLASS,
+
+ LAST_PROP
+};
+
+/**
+ * nm_setting_dcb_new:
+ *
+ * Creates a new #NMSettingDcb object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingDcb object
+ *
+ * Since: 0.9.10
+ **/
+NMSetting *
+nm_setting_dcb_new (void)
+{
+ return (NMSetting *) g_object_new (NM_TYPE_SETTING_DCB, NULL);
+}
+
+/**
+ * nm_setting_dcb_get_app_fcoe_flags:
+ * @setting: the #NMSettingDcb
+ *
+ * Returns: the #NMSettingDcb:app-fcoe-flags property of the setting
+ *
+ * Since: 0.9.10
+ **/
+NMSettingDcbFlags
+nm_setting_dcb_get_app_fcoe_flags (NMSettingDcb *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_DCB (setting), 0);
+
+ return NM_SETTING_DCB_GET_PRIVATE (setting)->app_fcoe_flags;
+}
+
+/**
+ * nm_setting_dcb_get_app_fcoe_priority:
+ * @setting: the #NMSettingDcb
+ *
+ * Returns: the #NMSettingDcb:app-fcoe-priority property of the setting
+ *
+ * Since: 0.9.10
+ **/
+gint
+nm_setting_dcb_get_app_fcoe_priority (NMSettingDcb *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_DCB (setting), 0);
+
+ return NM_SETTING_DCB_GET_PRIVATE (setting)->app_fcoe_priority;
+}
+
+/**
+ * nm_setting_dcb_get_app_fcoe_mode:
+ * @setting: the #NMSettingDcb
+ *
+ * Returns: the #NMSettingDcb:app-fcoe-mode property of the setting
+ *
+ * Since: 0.9.10
+ **/
+const char *
+nm_setting_dcb_get_app_fcoe_mode (NMSettingDcb *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_DCB (setting), NULL);
+
+ return NM_SETTING_DCB_GET_PRIVATE (setting)->app_fcoe_mode;
+}
+
+/**
+ * nm_setting_dcb_get_app_iscsi_flags:
+ * @setting: the #NMSettingDcb
+ *
+ * Returns: the #NMSettingDcb:app-iscsi-flags property of the setting
+ *
+ * Since: 0.9.10
+ **/
+NMSettingDcbFlags
+nm_setting_dcb_get_app_iscsi_flags (NMSettingDcb *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_DCB (setting), 0);
+
+ return NM_SETTING_DCB_GET_PRIVATE (setting)->app_iscsi_flags;
+}
+
+/**
+ * nm_setting_dcb_get_app_iscsi_priority:
+ * @setting: the #NMSettingDcb
+ *
+ * Returns: the #NMSettingDcb:app-iscsi-priority property of the setting
+ *
+ * Since: 0.9.10
+ **/
+gint
+nm_setting_dcb_get_app_iscsi_priority (NMSettingDcb *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_DCB (setting), 0);
+
+ return NM_SETTING_DCB_GET_PRIVATE (setting)->app_iscsi_priority;
+}
+
+/**
+ * nm_setting_dcb_get_app_fip_flags:
+ * @setting: the #NMSettingDcb
+ *
+ * Returns: the #NMSettingDcb:app-fip-flags property of the setting
+ *
+ * Since: 0.9.10
+ **/
+NMSettingDcbFlags
+nm_setting_dcb_get_app_fip_flags (NMSettingDcb *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_DCB (setting), 0);
+
+ return NM_SETTING_DCB_GET_PRIVATE (setting)->app_fip_flags;
+}
+
+/**
+ * nm_setting_dcb_get_app_fip_priority:
+ * @setting: the #NMSettingDcb
+ *
+ * Returns: the #NMSettingDcb:app-fip-priority property of the setting
+ *
+ * Since: 0.9.10
+ **/
+gint
+nm_setting_dcb_get_app_fip_priority (NMSettingDcb *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_DCB (setting), 0);
+
+ return NM_SETTING_DCB_GET_PRIVATE (setting)->app_fip_priority;
+}
+
+/**
+ * nm_setting_dcb_get_priority_flow_control_flags:
+ * @setting: the #NMSettingDcb
+ *
+ * Returns: the #NMSettingDcb:priority-flow-control-flags property of the setting
+ *
+ * Since: 0.9.10
+ **/
+NMSettingDcbFlags
+nm_setting_dcb_get_priority_flow_control_flags (NMSettingDcb *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_DCB (setting), 0);
+
+ return NM_SETTING_DCB_GET_PRIVATE (setting)->pfc_flags;
+}
+
+/**
+ * nm_setting_dcb_get_priority_flow_control:
+ * @setting: the #NMSettingDcb
+ * @user_priority: the User Priority (0 - 7) to retrieve flow control for
+ *
+ * Returns: %TRUE if flow control is enabled for the given @user_priority,
+ * %FALSE if not enabled
+ *
+ * Since: 0.9.10
+ **/
+gboolean
+nm_setting_dcb_get_priority_flow_control (NMSettingDcb *setting, guint user_priority)
+{
+ g_return_val_if_fail (NM_IS_SETTING_DCB (setting), FALSE);
+ g_return_val_if_fail (user_priority <= 7, FALSE);
+
+ return !!NM_SETTING_DCB_GET_PRIVATE (setting)->pfc[user_priority];
+}
+
+/**
+ * nm_setting_dcb_set_priority_flow_control:
+ * @setting: the #NMSettingDcb
+ * @user_priority: the User Priority (0 - 7) to set flow control for
+ * @enabled: %TRUE to enable flow control for this priority, %FALSE to disable it
+ *
+ * These values are only valid when #NMSettingDcb:priority-flow-control includes
+ * the %NM_SETTING_DCB_FLAG_ENABLE flag.
+ *
+ * Since: 0.9.10
+ **/
+void
+nm_setting_dcb_set_priority_flow_control (NMSettingDcb *setting,
+ guint user_priority,
+ gboolean enabled)
+{
+ NMSettingDcbPrivate *priv;
+ guint uint_enabled = enabled ? 1 : 0;
+
+ g_return_if_fail (NM_IS_SETTING_DCB (setting));
+ g_return_if_fail (user_priority <= 7);
+
+ priv = NM_SETTING_DCB_GET_PRIVATE (setting);
+ if (priv->pfc[user_priority] != uint_enabled) {
+ priv->pfc[user_priority] = uint_enabled;
+ g_object_notify (G_OBJECT (setting), NM_SETTING_DCB_PRIORITY_FLOW_CONTROL);
+ }
+}
+
+/**
+ * nm_setting_dcb_get_priority_group_flags:
+ * @setting: the #NMSettingDcb
+ *
+ * Returns: the #NMSettingDcb:priority-group-flags property of the setting
+ *
+ * Since: 0.9.10
+ **/
+NMSettingDcbFlags
+nm_setting_dcb_get_priority_group_flags (NMSettingDcb *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_DCB (setting), 0);
+
+ return NM_SETTING_DCB_GET_PRIVATE (setting)->priority_group_flags;
+}
+
+/**
+ * nm_setting_dcb_get_priority_group_id:
+ * @setting: the #NMSettingDcb
+ * @user_priority: the User Priority (0 - 7) to retrieve the group ID for
+ *
+ * Returns: the group number @user_priority is assigned to. These values are
+ * only valid when #NMSettingDcb:priority-group-flags includes the
+ * %NM_SETTING_DCB_FLAG_ENABLE flag.
+ *
+ * Since: 0.9.10
+ **/
+guint
+nm_setting_dcb_get_priority_group_id (NMSettingDcb *setting, guint user_priority)
+{
+ g_return_val_if_fail (NM_IS_SETTING_DCB (setting), 0);
+ g_return_val_if_fail (user_priority <= 7, 0);
+
+ return NM_SETTING_DCB_GET_PRIVATE (setting)->priority_group_id[user_priority];
+}
+
+/**
+ * nm_setting_dcb_set_priority_group_id:
+ * @setting: the #NMSettingDcb
+ * @user_priority: the User Priority (0 - 7) to set flow control for
+ * @group_id: the group (0 - 7) to assign @user_priority to, or 15 for the
+ * unrestricted group.
+ *
+ * These values are only valid when #NMSettingDcb:priority-group-flags includes
+ * the %NM_SETTING_DCB_FLAG_ENABLE flag.
+ *
+ * Since: 0.9.10
+ **/
+void
+nm_setting_dcb_set_priority_group_id (NMSettingDcb *setting,
+ guint user_priority,
+ guint group_id)
+{
+ NMSettingDcbPrivate *priv;
+
+ g_return_if_fail (NM_IS_SETTING_DCB (setting));
+ g_return_if_fail (user_priority <= 7);
+ g_return_if_fail (group_id <= 7 || group_id == 15);
+
+ priv = NM_SETTING_DCB_GET_PRIVATE (setting);
+ if (priv->priority_group_id[user_priority] != group_id) {
+ priv->priority_group_id[user_priority] = group_id;
+ g_object_notify (G_OBJECT (setting), NM_SETTING_DCB_PRIORITY_GROUP_ID);
+ }
+}
+
+/**
+ * nm_setting_dcb_get_priority_group_bandwidth:
+ * @setting: the #NMSettingDcb
+ * @group_id: the priority group (0 - 7) to retrieve the bandwidth percentage for
+ *
+ * Returns: the bandwidth percentage assigned to @group_id. These values are
+ * only valid when #NMSettingDcb:priority-group-flags includes the
+ * %NM_SETTING_DCB_FLAG_ENABLE flag.
+ *
+ * Since: 0.9.10
+ **/
+guint
+nm_setting_dcb_get_priority_group_bandwidth (NMSettingDcb *setting, guint group_id)
+{
+ g_return_val_if_fail (NM_IS_SETTING_DCB (setting), 0);
+ g_return_val_if_fail (group_id <= 7, FALSE);
+
+ return NM_SETTING_DCB_GET_PRIVATE (setting)->priority_group_bandwidth[group_id];
+}
+
+/**
+ * nm_setting_dcb_set_priority_group_bandwidth:
+ * @setting: the #NMSettingDcb
+ * @group_id: the priority group (0 - 7) to set the bandwidth percentage for
+ * @bandwidth_percent: the bandwidth percentage (0 - 100) to assign to @group_id to
+ *
+ * These values are only valid when #NMSettingDcb:priority-group-flags includes
+ * the %NM_SETTING_DCB_FLAG_ENABLE flag.
+ *
+ * Since: 0.9.10
+ **/
+void
+nm_setting_dcb_set_priority_group_bandwidth (NMSettingDcb *setting,
+ guint group_id,
+ guint bandwidth_percent)
+{
+ NMSettingDcbPrivate *priv;
+
+ g_return_if_fail (NM_IS_SETTING_DCB (setting));
+ g_return_if_fail (group_id <= 7);
+ g_return_if_fail (bandwidth_percent <= 100);
+
+ priv = NM_SETTING_DCB_GET_PRIVATE (setting);
+ if (priv->priority_group_bandwidth[group_id] != bandwidth_percent) {
+ priv->priority_group_bandwidth[group_id] = bandwidth_percent;
+ g_object_notify (G_OBJECT (setting), NM_SETTING_DCB_PRIORITY_GROUP_BANDWIDTH);
+ }
+}
+
+/**
+ * nm_setting_dcb_get_priority_bandwidth:
+ * @setting: the #NMSettingDcb
+ * @user_priority: the User Priority (0 - 7) to retrieve the group bandwidth percentage for
+ *
+ * Returns: the allowed bandwidth percentage of @user_priority in its priority group.
+ * These values are only valid when #NMSettingDcb:priority-group-flags includes the
+ * %NM_SETTING_DCB_FLAG_ENABLE flag.
+ *
+ * Since: 0.9.10
+ **/
+guint
+nm_setting_dcb_get_priority_bandwidth (NMSettingDcb *setting, guint user_priority)
+{
+ g_return_val_if_fail (NM_IS_SETTING_DCB (setting), 0);
+ g_return_val_if_fail (user_priority <= 7, FALSE);
+
+ return NM_SETTING_DCB_GET_PRIVATE (setting)->priority_bandwidth[user_priority];
+}
+
+/**
+ * nm_setting_dcb_set_priority_bandwidth:
+ * @setting: the #NMSettingDcb
+ * @user_priority: the User Priority (0 - 7) to set the bandwidth percentage for
+ * @bandwidth_percent: the bandwidth percentage (0 - 100) that @user_priority is
+ * allowed to use within its priority group
+ *
+ * These values are only valid when #NMSettingDcb:priority-group-flags includes
+ * the %NM_SETTING_DCB_FLAG_ENABLE flag.
+ *
+ * Since: 0.9.10
+ **/
+void
+nm_setting_dcb_set_priority_bandwidth (NMSettingDcb *setting,
+ guint user_priority,
+ guint bandwidth_percent)
+{
+ NMSettingDcbPrivate *priv;
+
+ g_return_if_fail (NM_IS_SETTING_DCB (setting));
+ g_return_if_fail (user_priority <= 7);
+ g_return_if_fail (bandwidth_percent <= 100);
+
+ priv = NM_SETTING_DCB_GET_PRIVATE (setting);
+ if (priv->priority_bandwidth[user_priority] != bandwidth_percent) {
+ priv->priority_bandwidth[user_priority] = bandwidth_percent;
+ g_object_notify (G_OBJECT (setting), NM_SETTING_DCB_PRIORITY_BANDWIDTH);
+ }
+}
+
+/**
+ * nm_setting_dcb_get_priority_strict_bandwidth:
+ * @setting: the #NMSettingDcb
+ * @user_priority: the User Priority (0 - 7) to retrieve strict bandwidth for
+ *
+ * Returns: %TRUE if @user_priority may use all of the bandwidth allocated to its
+ * assigned group, or %FALSE if not. These values are only valid when
+ * #NMSettingDcb:priority-group-flags includes the %NM_SETTING_DCB_FLAG_ENABLE flag.
+ *
+ * Since: 0.9.10
+ **/
+gboolean
+nm_setting_dcb_get_priority_strict_bandwidth (NMSettingDcb *setting, guint user_priority)
+{
+ g_return_val_if_fail (NM_IS_SETTING_DCB (setting), 0);
+ g_return_val_if_fail (user_priority <= 7, FALSE);
+
+ return !!NM_SETTING_DCB_GET_PRIVATE (setting)->priority_strict[user_priority];
+}
+
+/**
+ * nm_setting_dcb_set_priority_strict_bandwidth:
+ * @setting: the #NMSettingDcb
+ * @user_priority: the User Priority (0 - 7) to set strict bandwidth for
+ * @strict: %TRUE to allow @user_priority to use all the bandwidth allocated to
+ * its priority group, or %FALSE if not
+ *
+ * These values are only valid when #NMSettingDcb:priority-group-flags includes
+ * the %NM_SETTING_DCB_FLAG_ENABLE flag.
+ *
+ * Since: 0.9.10
+ **/
+void
+nm_setting_dcb_set_priority_strict_bandwidth (NMSettingDcb *setting,
+ guint user_priority,
+ gboolean strict)
+{
+ NMSettingDcbPrivate *priv;
+ guint uint_strict = strict ? 1 : 0;
+
+ g_return_if_fail (NM_IS_SETTING_DCB (setting));
+ g_return_if_fail (user_priority <= 7);
+
+ priv = NM_SETTING_DCB_GET_PRIVATE (setting);
+ if (priv->priority_strict[user_priority] != uint_strict) {
+ priv->priority_strict[user_priority] = uint_strict;
+ g_object_notify (G_OBJECT (setting), NM_SETTING_DCB_PRIORITY_STRICT_BANDWIDTH);
+ }
+}
+
+/**
+ * nm_setting_dcb_get_priority_traffic_class:
+ * @setting: the #NMSettingDcb
+ * @user_priority: the User Priority (0 - 7) to retrieve the traffic class for
+ *
+ * Returns: the traffic class assigned to @user_priority. These values are only
+ * valid when #NMSettingDcb:priority-group-flags includes the
+ * %NM_SETTING_DCB_FLAG_ENABLE flag.
+ *
+ * Since: 0.9.10
+ **/
+guint
+nm_setting_dcb_get_priority_traffic_class (NMSettingDcb *setting, guint user_priority)
+{
+ g_return_val_if_fail (NM_IS_SETTING_DCB (setting), 0);
+ g_return_val_if_fail (user_priority <= 7, FALSE);
+
+ return NM_SETTING_DCB_GET_PRIVATE (setting)->priority_traffic_class[user_priority];
+}
+
+/**
+ * nm_setting_dcb_set_priority_traffic_clas:
+ * @setting: the #NMSettingDcb
+ * @user_priority: the User Priority (0 - 7) to set the bandwidth percentage for
+ * @traffic_class: the traffic_class (0 - 7) that @user_priority should map to
+ *
+ * These values are only valid when #NMSettingDcb:priority-group-flags includes
+ * the %NM_SETTING_DCB_FLAG_ENABLE flag.
+ *
+ * Since: 0.9.10
+ **/
+void
+nm_setting_dcb_set_priority_traffic_class (NMSettingDcb *setting,
+ guint user_priority,
+ guint traffic_class)
+{
+ NMSettingDcbPrivate *priv;
+
+ g_return_if_fail (NM_IS_SETTING_DCB (setting));
+ g_return_if_fail (user_priority <= 7);
+ g_return_if_fail (traffic_class <= 7);
+
+ priv = NM_SETTING_DCB_GET_PRIVATE (setting);
+ if (priv->priority_traffic_class[user_priority] != traffic_class) {
+ priv->priority_traffic_class[user_priority] = traffic_class;
+ g_object_notify (G_OBJECT (setting), NM_SETTING_DCB_PRIORITY_TRAFFIC_CLASS);
+ }
+}
+
+/******************************************************************/
+
+#define DCB_FLAGS_ALL (NM_SETTING_DCB_FLAG_ENABLE | \
+ NM_SETTING_DCB_FLAG_ADVERTISE | \
+ NM_SETTING_DCB_FLAG_WILLING)
+
+static gboolean
+check_dcb_flags (NMSettingDcbFlags flags, const char *prop_name, GError **error)
+{
+ if (flags & ~DCB_FLAGS_ALL) {
+ g_set_error_literal (error,
+ NM_SETTING_DCB_ERROR,
+ NM_SETTING_DCB_ERROR_INVALID_PROPERTY,
+ _("flags invalid"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_DCB_SETTING_NAME, prop_name);
+ return FALSE;
+ }
+
+ if (!(flags & NM_SETTING_DCB_FLAG_ENABLE) && (flags & ~NM_SETTING_DCB_FLAG_ENABLE)) {
+ g_set_error_literal (error,
+ NM_SETTING_DCB_ERROR,
+ NM_SETTING_DCB_ERROR_INVALID_PROPERTY,
+ _("flags invalid - disabled"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_DCB_SETTING_NAME, prop_name);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+check_uint_array (const guint *array,
+ guint len,
+ NMSettingDcbFlags flags,
+ guint max,
+ guint extra,
+ gboolean sum_pct,
+ const char *prop_name,
+ GError **error)
+{
+ guint i, sum = 0;
+
+ /* Ensure each element is <= to max or equals extra */
+ for (i = 0; i < len; i++) {
+ if (!(flags & NM_SETTING_DCB_FLAG_ENABLE) && array[i]) {
+ g_set_error_literal (error,
+ NM_SETTING_DCB_ERROR,
+ NM_SETTING_DCB_ERROR_INVALID_PROPERTY,
+ _("property invalid (not enabled)"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_DCB_SETTING_NAME, prop_name);
+ return FALSE;
+ }
+
+ if ((array[i] > max) && (array[i] != extra)) {
+ g_set_error_literal (error,
+ NM_SETTING_DCB_ERROR,
+ NM_SETTING_DCB_ERROR_INVALID_PROPERTY,
+ _("element invalid"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_DCB_SETTING_NAME, prop_name);
+ return FALSE;
+ }
+ sum += array[i];
+ }
+
+ /* Verify sum of percentages */
+ if (sum_pct) {
+ if (flags & NM_SETTING_DCB_FLAG_ENABLE) {
+ /* If the feature is enabled, sum must equal 100% */
+ if (sum != 100) {
+ g_set_error_literal (error,
+ NM_SETTING_DCB_ERROR,
+ NM_SETTING_DCB_ERROR_INVALID_PROPERTY,
+ _("sum not 100%"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_DCB_SETTING_NAME, prop_name);
+ return FALSE;
+ }
+ } else {
+ /* If the feature is disabled, sum must equal 0%, which was checked
+ * by the for() loop above.
+ */
+ g_assert_cmpint (sum, ==, 0);
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean
+check_priority (gint val,
+ NMSettingDcbFlags flags,
+ const char *prop_name,
+ GError **error)
+{
+ if (!(flags & NM_SETTING_DCB_FLAG_ENABLE) && (val >= 0)) {
+ g_set_error_literal (error,
+ NM_SETTING_DCB_ERROR,
+ NM_SETTING_DCB_ERROR_INVALID_PROPERTY,
+ _("property invalid (not enabled)"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_DCB_SETTING_NAME, prop_name);
+ return FALSE;
+ }
+
+ if (val < -1 || val > 7) {
+ g_set_error_literal (error,
+ NM_SETTING_DCB_ERROR,
+ NM_SETTING_DCB_ERROR_INVALID_PROPERTY,
+ _("property invalid"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_DCB_SETTING_NAME, prop_name);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean
+verify (NMSetting *setting, GSList *all_settings, GError **error)
+{
+ NMSettingDcbPrivate *priv = NM_SETTING_DCB_GET_PRIVATE (setting);
+
+ if (!check_dcb_flags (priv->app_fcoe_flags, NM_SETTING_DCB_APP_FCOE_FLAGS, error))
+ return FALSE;
+
+ if (!check_priority (priv->app_fcoe_priority, priv->app_fcoe_flags, NM_SETTING_DCB_APP_FCOE_PRIORITY, error))
+ return FALSE;
+
+ if (!priv->app_fcoe_mode) {
+ g_set_error_literal (error,
+ NM_SETTING_DCB_ERROR,
+ NM_SETTING_DCB_ERROR_MISSING_PROPERTY,
+ _("property missing"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_DCB_SETTING_NAME, NM_SETTING_DCB_APP_FCOE_MODE);
+ return FALSE;
+ }
+
+ if (strcmp (priv->app_fcoe_mode, NM_SETTING_DCB_FCOE_MODE_FABRIC) &&
+ strcmp (priv->app_fcoe_mode, NM_SETTING_DCB_FCOE_MODE_VN2VN)) {
+ g_set_error_literal (error,
+ NM_SETTING_DCB_ERROR,
+ NM_SETTING_DCB_ERROR_INVALID_PROPERTY,
+ _("property invalid"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_DCB_SETTING_NAME, NM_SETTING_DCB_APP_FCOE_MODE);
+ return FALSE;
+ }
+
+ if (!check_dcb_flags (priv->app_iscsi_flags, NM_SETTING_DCB_APP_ISCSI_FLAGS, error))
+ return FALSE;
+
+ if (!check_priority (priv->app_iscsi_priority, priv->app_iscsi_flags, NM_SETTING_DCB_APP_ISCSI_PRIORITY, error))
+ return FALSE;
+
+ if (!check_dcb_flags (priv->app_fip_flags, NM_SETTING_DCB_APP_FIP_FLAGS, error))
+ return FALSE;
+
+ if (!check_priority (priv->app_fip_priority, priv->app_fip_flags, NM_SETTING_DCB_APP_FIP_PRIORITY, error))
+ return FALSE;
+
+ if (!check_dcb_flags (priv->pfc_flags, NM_SETTING_DCB_PRIORITY_FLOW_CONTROL_FLAGS, error))
+ return FALSE;
+
+ if (!check_uint_array (priv->pfc, G_N_ELEMENTS (priv->pfc), priv->pfc_flags, 1, 0, FALSE, NM_SETTING_DCB_PRIORITY_FLOW_CONTROL, error))
+ return FALSE;
+
+ if (!check_dcb_flags (priv->priority_group_flags, NM_SETTING_DCB_PRIORITY_GROUP_FLAGS, error))
+ return FALSE;
+
+ if (!check_uint_array (priv->priority_group_id,
+ G_N_ELEMENTS (priv->priority_group_id),
+ priv->priority_group_flags,
+ 7,
+ 15,
+ FALSE,
+ NM_SETTING_DCB_PRIORITY_GROUP_ID,
+ error))
+ return FALSE;
+
+ if (!check_uint_array (priv->priority_group_bandwidth,
+ G_N_ELEMENTS (priv->priority_group_bandwidth),
+ priv->priority_group_flags,
+ 100,
+ 0,
+ TRUE,
+ NM_SETTING_DCB_PRIORITY_GROUP_BANDWIDTH,
+ error))
+ return FALSE;
+
+ /* FIXME: sum bandwidths in each group */
+ if (!check_uint_array (priv->priority_bandwidth,
+ G_N_ELEMENTS (priv->priority_bandwidth),
+ priv->priority_group_flags,
+ 100,
+ 0,
+ FALSE,
+ NM_SETTING_DCB_PRIORITY_BANDWIDTH,
+ error))
+ return FALSE;
+
+ if (!check_uint_array (priv->priority_strict,
+ G_N_ELEMENTS (priv->priority_strict),
+ priv->priority_group_flags,
+ 1,
+ 0,
+ FALSE,
+ NM_SETTING_DCB_PRIORITY_STRICT_BANDWIDTH,
+ error))
+ return FALSE;
+
+ if (!check_uint_array (priv->priority_traffic_class,
+ G_N_ELEMENTS (priv->priority_traffic_class),
+ priv->priority_group_flags,
+ 7,
+ 0,
+ FALSE,
+ NM_SETTING_DCB_PRIORITY_TRAFFIC_CLASS,
+ error))
+ return FALSE;
+
+ return TRUE;
+}
+
+/******************************************************************/
+
+static void
+nm_setting_dcb_init (NMSettingDcb *setting)
+{
+}
+
+static inline void
+set_uint_array (const GValue *v, uint *a, size_t len)
+{
+ GArray *src = g_value_get_boxed (v);
+ const guint total_len = len * sizeof (a[0]);
+
+ memset (a, 0, total_len);
+ if (src) {
+ g_return_if_fail (g_array_get_element_size (src) == sizeof (a[0]));
+ g_return_if_fail (src->len == len);
+ memcpy (a, src->data, total_len);
+ }
+}
+#define SET_UINT_ARRAY(v, a) set_uint_array (v, a, G_N_ELEMENTS (a))
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ NMSettingDcbPrivate *priv = NM_SETTING_DCB_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_APP_FCOE_FLAGS:
+ priv->app_fcoe_flags = g_value_get_uint (value);
+ break;
+ case PROP_APP_FCOE_PRIORITY:
+ priv->app_fcoe_priority = g_value_get_int (value);
+ break;
+ case PROP_APP_FCOE_MODE:
+ priv->app_fcoe_mode = g_value_dup_string (value);
+ break;
+ case PROP_APP_ISCSI_FLAGS:
+ priv->app_iscsi_flags = g_value_get_uint (value);
+ break;
+ case PROP_APP_ISCSI_PRIORITY:
+ priv->app_iscsi_priority = g_value_get_int (value);
+ break;
+ case PROP_APP_FIP_FLAGS:
+ priv->app_fip_flags = g_value_get_uint (value);
+ break;
+ case PROP_APP_FIP_PRIORITY:
+ priv->app_fip_priority = g_value_get_int (value);
+ break;
+ case PROP_PFC_FLAGS:
+ priv->pfc_flags = g_value_get_uint (value);
+ break;
+ case PROP_PFC:
+ SET_UINT_ARRAY (value, priv->pfc);
+ break;
+ case PROP_PRIORITY_GROUP_FLAGS:
+ priv->priority_group_flags = g_value_get_uint (value);
+ break;
+ case PROP_PRIORITY_GROUP_ID:
+ SET_UINT_ARRAY (value, priv->priority_group_id);
+ break;
+ case PROP_PRIORITY_GROUP_BANDWIDTH:
+ SET_UINT_ARRAY (value, priv->priority_group_bandwidth);
+ break;
+ case PROP_PRIORITY_BANDWIDTH:
+ SET_UINT_ARRAY (value, priv->priority_bandwidth);
+ break;
+ case PROP_PRIORITY_STRICT:
+ SET_UINT_ARRAY (value, priv->priority_strict);
+ break;
+ case PROP_PRIORITY_TRAFFIC_CLASS:
+ SET_UINT_ARRAY (value, priv->priority_traffic_class);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+#define TAKE_UINT_ARRAY(v, a) \
+{ \
+ guint len = G_N_ELEMENTS (a); \
+ GArray *dst = g_array_sized_new (FALSE, TRUE, sizeof (guint), len); \
+ g_array_append_vals (dst, (a), len); \
+ g_value_take_boxed (v, dst); \
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ NMSettingDcb *setting = NM_SETTING_DCB (object);
+ NMSettingDcbPrivate *priv = NM_SETTING_DCB_GET_PRIVATE (setting);
+
+ switch (prop_id) {
+ case PROP_APP_FCOE_FLAGS:
+ g_value_set_uint (value, priv->app_fcoe_flags);
+ break;
+ case PROP_APP_FCOE_PRIORITY:
+ g_value_set_int (value, priv->app_fcoe_priority);
+ break;
+ case PROP_APP_FCOE_MODE:
+ g_value_set_string (value, priv->app_fcoe_mode);
+ break;
+ case PROP_APP_ISCSI_FLAGS:
+ g_value_set_uint (value, priv->app_iscsi_flags);
+ break;
+ case PROP_APP_ISCSI_PRIORITY:
+ g_value_set_int (value, priv->app_iscsi_priority);
+ break;
+ case PROP_APP_FIP_FLAGS:
+ g_value_set_uint (value, priv->app_fip_flags);
+ break;
+ case PROP_APP_FIP_PRIORITY:
+ g_value_set_int (value, priv->app_fip_priority);
+ break;
+ case PROP_PFC_FLAGS:
+ g_value_set_uint (value, priv->pfc_flags);
+ break;
+ case PROP_PFC:
+ TAKE_UINT_ARRAY (value, priv->pfc);
+ break;
+ case PROP_PRIORITY_GROUP_FLAGS:
+ g_value_set_uint (value, priv->priority_group_flags);
+ break;
+ case PROP_PRIORITY_GROUP_ID:
+ TAKE_UINT_ARRAY (value, priv->priority_group_id);
+ break;
+ case PROP_PRIORITY_GROUP_BANDWIDTH:
+ TAKE_UINT_ARRAY (value, priv->priority_group_bandwidth);
+ break;
+ case PROP_PRIORITY_BANDWIDTH:
+ TAKE_UINT_ARRAY (value, priv->priority_bandwidth);
+ break;
+ case PROP_PRIORITY_STRICT:
+ TAKE_UINT_ARRAY (value, priv->priority_strict);
+ break;
+ case PROP_PRIORITY_TRAFFIC_CLASS:
+ TAKE_UINT_ARRAY (value, priv->priority_traffic_class);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nm_setting_dcb_class_init (NMSettingDcbClass *setting_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
+ NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+
+ g_type_class_add_private (setting_class, sizeof (NMSettingDcbPrivate));
+
+ /* virtual methods */
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+ parent_class->verify = verify;
+
+ /* Properties */
+ /**
+ * NMSettingDcb:app-fcoe-flags:
+ *
+ * Specifies the #NMSettingDcbFlags for the DCB FCoE application. Flags may
+ * be any combination of %NM_SETTING_DCB_FLAG_ENABLE,
+ * %NM_SETTING_DCB_FLAG_ADVERTISE, and %NM_SETTING_DCB_FLAG_WILLING.
+ *
+ * Since: 0.9.10
+ **/
+ g_object_class_install_property
+ (object_class, PROP_APP_FCOE_FLAGS,
+ g_param_spec_uint (NM_SETTING_DCB_APP_FCOE_FLAGS, "", "",
+ 0, DCB_FLAGS_ALL, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingDcb:app-fcoe-priority:
+ *
+ * The highest User Priority (0 - 7) which FCoE frames should use, or -1 for
+ * default priority. Only used when the #NMSettingDcb:app-fcoe-flags
+ * property includes the %NM_SETTING_DCB_FLAG_ENABLE flag.
+ *
+ * Since: 0.9.10
+ **/
+ g_object_class_install_property
+ (object_class, PROP_APP_FCOE_PRIORITY,
+ g_param_spec_int (NM_SETTING_DCB_APP_FCOE_PRIORITY, "", "",
+ -1, 7, -1,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingDcb:app-fcoe-mode:
+ *
+ * The FCoE controller mode; either %NM_SETTING_DCB_FCOE_MODE_FABRIC
+ * (default) or %NM_SETTING_DCB_FCOE_MODE_VN2VN.
+ *
+ * Since: 0.9.10
+ **/
+ g_object_class_install_property
+ (object_class, PROP_APP_FCOE_MODE,
+ g_param_spec_string (NM_SETTING_DCB_APP_FCOE_MODE, "", "",
+ NM_SETTING_DCB_FCOE_MODE_FABRIC,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingDcb:app-iscsi-flags:
+ *
+ * Specifies the #NMSettingDcbFlags for the DCB iSCSI application. Flags
+ * may be any combination of %NM_SETTING_DCB_FLAG_ENABLE,
+ * %NM_SETTING_DCB_FLAG_ADVERTISE, and %NM_SETTING_DCB_FLAG_WILLING.
+ *
+ * Since: 0.9.10
+ **/
+ g_object_class_install_property
+ (object_class, PROP_APP_ISCSI_FLAGS,
+ g_param_spec_uint (NM_SETTING_DCB_APP_ISCSI_FLAGS, "", "",
+ 0, DCB_FLAGS_ALL, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingDcb:app-iscsi-priority:
+ *
+ * The highest User Priority (0 - 7) which iSCSI frames should use, or -1
+ * for default priority. Only used when the #NMSettingDcb:app-iscsi-flags
+ * property includes the %NM_SETTING_DCB_FLAG_ENABLE flag.
+ *
+ * Since: 0.9.10
+ **/
+ g_object_class_install_property
+ (object_class, PROP_APP_ISCSI_PRIORITY,
+ g_param_spec_int (NM_SETTING_DCB_APP_ISCSI_PRIORITY, "", "",
+ -1, 7, -1,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingDcb:app-fip-flags:
+ *
+ * Specifies the #NMSettingDcbFlags for the DCB FIP application. Flags may
+ * be any combination of %NM_SETTING_DCB_FLAG_ENABLE,
+ * %NM_SETTING_DCB_FLAG_ADVERTISE, and %NM_SETTING_DCB_FLAG_WILLING.
+ *
+ * Since: 0.9.10
+ **/
+ g_object_class_install_property
+ (object_class, PROP_APP_FIP_FLAGS,
+ g_param_spec_uint (NM_SETTING_DCB_APP_FIP_FLAGS, "", "",
+ 0, DCB_FLAGS_ALL, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingDcb:app-fip-priority:
+ *
+ * The highest User Priority (0 - 7) which FIP frames should use, or -1 for
+ * default priority. Only used when the #NMSettingDcb:app-fip-flags
+ * property includes the %NM_SETTING_DCB_FLAG_ENABLE flag.
+ *
+ * Since: 0.9.10
+ **/
+ g_object_class_install_property
+ (object_class, PROP_APP_FIP_PRIORITY,
+ g_param_spec_int (NM_SETTING_DCB_APP_FIP_PRIORITY, "", "",
+ -1, 7, -1,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingDcb:priority-flow-control-flags:
+ *
+ * Specifies the #NMSettingDcbFlags for DCB Priority Flow Control (PFC).
+ * Flags may be any combination of %NM_SETTING_DCB_FLAG_ENABLE,
+ * %NM_SETTING_DCB_FLAG_ADVERTISE, and %NM_SETTING_DCB_FLAG_WILLING.
+ *
+ * Since: 0.9.10
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PFC_FLAGS,
+ g_param_spec_uint (NM_SETTING_DCB_PRIORITY_FLOW_CONTROL_FLAGS, "", "",
+ 0, DCB_FLAGS_ALL, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingDcb:priority-flow-control:
+ *
+ * An array of 8 uint values, where the array index corresponds to the User
+ * Priority (0 - 7) and the value indicates whether or not the corresponding
+ * priority should transmit priority pause. Allowed values are 0 (do not
+ * transmit pause) and 1 (transmit pause).
+ *
+ * Since: 0.9.10
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PFC,
+ _nm_param_spec_specialized (NM_SETTING_DCB_PRIORITY_FLOW_CONTROL, "", "",
+ DBUS_TYPE_G_UINT_ARRAY,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingDcb:priority-group-flags:
+ *
+ * Specifies the #NMSettingDcbFlags for DCB Priority Groups. Flags may be
+ * any combination of %NM_SETTING_DCB_FLAG_ENABLE,
+ * %NM_SETTING_DCB_FLAG_ADVERTISE, and %NM_SETTING_DCB_FLAG_WILLING.
+ *
+ * Since: 0.9.10
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PRIORITY_GROUP_FLAGS,
+ g_param_spec_uint (NM_SETTING_DCB_PRIORITY_GROUP_FLAGS, "", "",
+ 0, DCB_FLAGS_ALL, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingDcb:priority-group-id:
+ *
+ * An array of 8 uint values, where the array index corresponds to the User
+ * Priority (0 - 7) and the value indicates the Priority Group ID. Allowed
+ * Priority Group ID values are 0 - 7 or 15 for the unrestricted group.
+ *
+ * Since: 0.9.10
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PRIORITY_GROUP_ID,
+ _nm_param_spec_specialized (NM_SETTING_DCB_PRIORITY_GROUP_ID, "", "",
+ DBUS_TYPE_G_UINT_ARRAY,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingDcb:priority-group-bandwidth:
+ *
+ * An array of 8 uint values, where the array index corresponds to the
+ * Priority Group ID (0 - 7) and the value indicates the percentage of link
+ * bandwidth allocated to that group. Allowed values are 0 - 100, and the
+ * sum of all values must total 100 percent.
+ *
+ * Since: 0.9.10
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PRIORITY_GROUP_BANDWIDTH,
+ _nm_param_spec_specialized (NM_SETTING_DCB_PRIORITY_GROUP_BANDWIDTH, "", "",
+ DBUS_TYPE_G_UINT_ARRAY,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingDcb:priority-bandwidth:
+ *
+ * An array of 8 uint values, where the array index corresponds to the User
+ * Priority (0 - 7) and the value indicates the percentage of bandwidth of
+ * the priority's assigned group that the priority may use. The sum of all
+ * percentages for priorities which belong to the same group must total 100
+ * percent.
+ *
+ * Since: 0.9.10
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PRIORITY_BANDWIDTH,
+ _nm_param_spec_specialized (NM_SETTING_DCB_PRIORITY_BANDWIDTH, "", "",
+ DBUS_TYPE_G_UINT_ARRAY,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingDcb:priority-strict-bandwidth:
+ *
+ * An array of 8 uint values, where the array index corresponds to the User
+ * Priority (0 - 7) and the value indicates whether or not the priority may
+ * use all of the bandwidth allocated to its assigned group. Allowed values
+ * are 0 (the priority may not utilize all bandwidth) or 1 (the priority may
+ * utilize all bandwidth).
+ *
+ * Since: 0.9.10
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PRIORITY_STRICT,
+ _nm_param_spec_specialized (NM_SETTING_DCB_PRIORITY_STRICT_BANDWIDTH, "", "",
+ DBUS_TYPE_G_UINT_ARRAY,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingDcb:priority-traffic-class:
+ *
+ * An array of 8 uint values, where the array index corresponds to the User
+ * Priority (0 - 7) and the value indicates the traffic class (0 - 7) to
+ * which the priority is mapped.
+ *
+ * Since: 0.9.10
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PRIORITY_TRAFFIC_CLASS,
+ _nm_param_spec_specialized (NM_SETTING_DCB_PRIORITY_TRAFFIC_CLASS, "", "",
+ DBUS_TYPE_G_UINT_ARRAY,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/libnm-core/nm-setting-dcb.h b/libnm-core/nm-setting-dcb.h
new file mode 100644
index 0000000000..ac34521e0d
--- /dev/null
+++ b/libnm-core/nm-setting-dcb.h
@@ -0,0 +1,187 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NM_SETTING_DCB_H
+#define NM_SETTING_DCB_H
+
+#include <nm-setting.h>
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_SETTING_DCB (nm_setting_dcb_get_type ())
+#define NM_SETTING_DCB(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_DCB, NMSettingDcb))
+#define NM_SETTING_DCB_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_DCB, NMSettingDcbClass))
+#define NM_IS_SETTING_DCB(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_DCB))
+#define NM_IS_SETTING_DCB_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_DCB))
+#define NM_SETTING_DCB_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_DCB, NMSettingDcbClass))
+
+#define NM_SETTING_DCB_SETTING_NAME "dcb"
+
+/**
+ * NMSettingDcbError:
+ * @NM_SETTING_DCB_ERROR_UNKNOWN: unknown or unclassified error
+ * @NM_SETTING_DCB_ERROR_INVALID_PROPERTY: the property was invalid
+ * @NM_SETTING_DCB_ERROR_MISSING_PROPERTY: the property was missing and is
+ * required
+ */
+typedef enum {
+ NM_SETTING_DCB_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/
+ NM_SETTING_DCB_ERROR_INVALID_PROPERTY, /*< nick=InvalidProperty >*/
+ NM_SETTING_DCB_ERROR_MISSING_PROPERTY /*< nick=MissingProperty >*/
+} NMSettingDcbError;
+
+#define NM_SETTING_DCB_ERROR nm_setting_dcb_error_quark ()
+GQuark nm_setting_dcb_error_quark (void);
+
+/**
+ * NMSettingDcbFlags:
+ * @NM_SETTING_DCB_FLAG_NONE: no flag
+ * @NM_SETTING_DCB_FLAG_ENABLE: the feature is enabled
+ * @NM_SETTING_DCB_FLAG_ADVERTISE: the feature is advertised
+ * @NM_SETTING_DCB_FLAG_WILLING: the feature is willing to change based on
+ * peer configuration advertisements
+ *
+ * DCB feature flags.
+ *
+ * Since: 0.9.10
+ **/
+typedef enum {
+ NM_SETTING_DCB_FLAG_NONE = 0x00000000,
+ NM_SETTING_DCB_FLAG_ENABLE = 0x00000001,
+ NM_SETTING_DCB_FLAG_ADVERTISE = 0x00000002,
+ NM_SETTING_DCB_FLAG_WILLING = 0x00000004
+} NMSettingDcbFlags;
+
+/**
+ * NM_SETTING_DCB_FCOE_MODE_FABRIC:
+ *
+ * Indicates that the FCoE controller should use "fabric" mode (default)
+ *
+ * Since: 0.9.10
+ */
+#define NM_SETTING_DCB_FCOE_MODE_FABRIC "fabric"
+
+/**
+ * NM_SETTING_DCB_FCOE_MODE_VN2VN:
+ *
+ * Indicates that the FCoE controller should use "VN2VN" mode.
+ *
+ * Since: 0.9.10
+ */
+#define NM_SETTING_DCB_FCOE_MODE_VN2VN "vn2vn"
+
+
+/* Properties */
+#define NM_SETTING_DCB_APP_FCOE_FLAGS "app-fcoe-flags"
+#define NM_SETTING_DCB_APP_FCOE_PRIORITY "app-fcoe-priority"
+#define NM_SETTING_DCB_APP_FCOE_MODE "app-fcoe-mode"
+
+#define NM_SETTING_DCB_APP_ISCSI_FLAGS "app-iscsi-flags"
+#define NM_SETTING_DCB_APP_ISCSI_PRIORITY "app-iscsi-priority"
+
+#define NM_SETTING_DCB_APP_FIP_FLAGS "app-fip-flags"
+#define NM_SETTING_DCB_APP_FIP_PRIORITY "app-fip-priority"
+
+#define NM_SETTING_DCB_PRIORITY_FLOW_CONTROL_FLAGS "priority-flow-control-flags"
+#define NM_SETTING_DCB_PRIORITY_FLOW_CONTROL "priority-flow-control"
+
+#define NM_SETTING_DCB_PRIORITY_GROUP_FLAGS "priority-group-flags"
+#define NM_SETTING_DCB_PRIORITY_GROUP_ID "priority-group-id"
+#define NM_SETTING_DCB_PRIORITY_GROUP_BANDWIDTH "priority-group-bandwidth"
+#define NM_SETTING_DCB_PRIORITY_BANDWIDTH "priority-bandwidth"
+#define NM_SETTING_DCB_PRIORITY_STRICT_BANDWIDTH "priority-strict-bandwidth"
+#define NM_SETTING_DCB_PRIORITY_TRAFFIC_CLASS "priority-traffic-class"
+
+
+typedef struct {
+ NMSetting parent;
+} NMSettingDcb;
+
+typedef struct {
+ NMSettingClass parent;
+
+ /* Padding for future expansion */
+ void (*_reserved1) (void);
+ void (*_reserved2) (void);
+ void (*_reserved3) (void);
+ void (*_reserved4) (void);
+} NMSettingDcbClass;
+
+NM_AVAILABLE_IN_0_9_10
+GType nm_setting_dcb_get_type (void);
+
+NM_AVAILABLE_IN_0_9_10
+NMSetting * nm_setting_dcb_new (void);
+
+NMSettingDcbFlags nm_setting_dcb_get_app_fcoe_flags (NMSettingDcb *setting);
+gint nm_setting_dcb_get_app_fcoe_priority (NMSettingDcb *setting);
+const char * nm_setting_dcb_get_app_fcoe_mode (NMSettingDcb *setting);
+
+NMSettingDcbFlags nm_setting_dcb_get_app_iscsi_flags (NMSettingDcb *setting);
+gint nm_setting_dcb_get_app_iscsi_priority (NMSettingDcb *setting);
+
+NMSettingDcbFlags nm_setting_dcb_get_app_fip_flags (NMSettingDcb *setting);
+gint nm_setting_dcb_get_app_fip_priority (NMSettingDcb *setting);
+
+/* Priority Flow Control */
+NMSettingDcbFlags nm_setting_dcb_get_priority_flow_control_flags (NMSettingDcb *setting);
+gboolean nm_setting_dcb_get_priority_flow_control (NMSettingDcb *setting,
+ guint user_priority);
+void nm_setting_dcb_set_priority_flow_control (NMSettingDcb *setting,
+ guint user_priority,
+ gboolean enabled);
+
+/* Priority Groups */
+NMSettingDcbFlags nm_setting_dcb_get_priority_group_flags (NMSettingDcb *setting);
+
+guint nm_setting_dcb_get_priority_group_id (NMSettingDcb *setting,
+ guint user_priority);
+void nm_setting_dcb_set_priority_group_id (NMSettingDcb *setting,
+ guint user_priority,
+ guint group_id);
+
+guint nm_setting_dcb_get_priority_group_bandwidth (NMSettingDcb *setting,
+ guint group_id);
+void nm_setting_dcb_set_priority_group_bandwidth (NMSettingDcb *setting,
+ guint group_id,
+ guint bandwidth_percent);
+
+guint nm_setting_dcb_get_priority_bandwidth (NMSettingDcb *setting,
+ guint user_priority);
+void nm_setting_dcb_set_priority_bandwidth (NMSettingDcb *setting,
+ guint user_priority,
+ guint bandwidth_percent);
+
+gboolean nm_setting_dcb_get_priority_strict_bandwidth (NMSettingDcb *setting,
+ guint user_priority);
+void nm_setting_dcb_set_priority_strict_bandwidth (NMSettingDcb *setting,
+ guint user_priority,
+ gboolean strict);
+
+guint nm_setting_dcb_get_priority_traffic_class (NMSettingDcb *setting,
+ guint user_priority);
+void nm_setting_dcb_set_priority_traffic_class (NMSettingDcb *setting,
+ guint user_priority,
+ guint traffic_class);
+
+G_END_DECLS
+
+#endif /* NM_SETTING_DCB_H */
diff --git a/libnm-core/nm-setting-generic.c b/libnm-core/nm-setting-generic.c
new file mode 100644
index 0000000000..e32ae4971f
--- /dev/null
+++ b/libnm-core/nm-setting-generic.c
@@ -0,0 +1,100 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#include "config.h"
+
+#include "nm-setting-generic.h"
+#include "nm-setting-private.h"
+
+/**
+ * SECTION:nm-setting-generic
+ * @short_description: Describes connection properties for generic devices
+ * @include: nm-setting-generic.h
+ *
+ * The #NMSettingGeneric object is a #NMSetting subclass that describes
+ * optional properties that apply to "generic" devices (ie, devices that
+ * NetworkManager does not specifically recognize).
+ *
+ * There are currently no properties on this object; it exists only to be
+ * the "connection type" setting on #NMConnections for generic devices.
+ *
+ * Since: 0.9.10
+ **/
+
+/**
+ * nm_setting_generic_error_quark:
+ *
+ * Registers an error quark for #NMSettingGeneric if necessary.
+ *
+ * Returns: the error quark used for #NMSettingGeneric errors.
+ *
+ * Since: 0.9.10
+ **/
+GQuark
+nm_setting_generic_error_quark (void)
+{
+ static GQuark quark;
+
+ if (G_UNLIKELY (!quark))
+ quark = g_quark_from_static_string ("nm-setting-generic-error-quark");
+ return quark;
+}
+
+G_DEFINE_TYPE_WITH_CODE (NMSettingGeneric, nm_setting_generic, NM_TYPE_SETTING,
+ _nm_register_setting (NM_SETTING_GENERIC_SETTING_NAME,
+ g_define_type_id,
+ 1,
+ NM_SETTING_GENERIC_ERROR))
+NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_GENERIC)
+
+#define NM_SETTING_GENERIC_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_GENERIC, NMSettingGenericPrivate))
+
+typedef struct {
+ int dummy;
+} NMSettingGenericPrivate;
+
+/**************************************************************************/
+
+/**
+ * nm_setting_generic_new:
+ *
+ * Creates a new #NMSettingGeneric object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingGeneric object
+ *
+ * Since: 0.9.10
+ **/
+NMSetting *
+nm_setting_generic_new (void)
+{
+ return (NMSetting *) g_object_new (NM_TYPE_SETTING_GENERIC, NULL);
+}
+
+static void
+nm_setting_generic_init (NMSettingGeneric *setting)
+{
+}
+
+static void
+nm_setting_generic_class_init (NMSettingGenericClass *setting_class)
+{
+ g_type_class_add_private (setting_class, sizeof (NMSettingGenericPrivate));
+}
diff --git a/libnm-core/nm-setting-generic.h b/libnm-core/nm-setting-generic.h
new file mode 100644
index 0000000000..1cabae088c
--- /dev/null
+++ b/libnm-core/nm-setting-generic.h
@@ -0,0 +1,78 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NM_SETTING_GENERIC_H
+#define NM_SETTING_GENERIC_H
+
+#include <nm-setting.h>
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_SETTING_GENERIC (nm_setting_generic_get_type ())
+#define NM_SETTING_GENERIC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_GENERIC, NMSettingGeneric))
+#define NM_SETTING_GENERIC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_GENERIC, NMSettingGenericClass))
+#define NM_IS_SETTING_GENERIC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_GENERIC))
+#define NM_IS_SETTING_GENERIC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_GENERIC))
+#define NM_SETTING_GENERIC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_GENERIC, NMSettingGenericClass))
+
+#define NM_SETTING_GENERIC_SETTING_NAME "generic"
+
+/**
+ * NMSettingGenericError:
+ * @NM_SETTING_GENERIC_ERROR_UNKNOWN: unknown or unclassified error
+ * @NM_SETTING_GENERIC_ERROR_INVALID_PROPERTY: the property was invalid
+ * @NM_SETTING_GENERIC_ERROR_MISSING_PROPERTY: the property was missing and
+ * is required
+ *
+ * Since: 0.9.10
+ */
+typedef enum {
+ NM_SETTING_GENERIC_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/
+ NM_SETTING_GENERIC_ERROR_INVALID_PROPERTY, /*< nick=InvalidProperty >*/
+ NM_SETTING_GENERIC_ERROR_MISSING_PROPERTY, /*< nick=MissingProperty >*/
+} NMSettingGenericError;
+
+#define NM_SETTING_GENERIC_ERROR nm_setting_generic_error_quark ()
+GQuark nm_setting_generic_error_quark (void);
+
+typedef struct {
+ NMSetting parent;
+} NMSettingGeneric;
+
+typedef struct {
+ NMSettingClass parent;
+
+ /* Padding for future expansion */
+ void (*_reserved1) (void);
+ void (*_reserved2) (void);
+ void (*_reserved3) (void);
+ void (*_reserved4) (void);
+} NMSettingGenericClass;
+
+NM_AVAILABLE_IN_0_9_10
+GType nm_setting_generic_get_type (void);
+
+NM_AVAILABLE_IN_0_9_10
+NMSetting * nm_setting_generic_new (void);
+
+G_END_DECLS
+
+#endif /* NM_SETTING_GENERIC_H */
diff --git a/libnm-core/nm-setting-gsm.c b/libnm-core/nm-setting-gsm.c
new file mode 100644
index 0000000000..51ad390249
--- /dev/null
+++ b/libnm-core/nm-setting-gsm.c
@@ -0,0 +1,723 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2013 Red Hat, Inc.
+ * Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#include <string.h>
+#include <glib/gi18n.h>
+
+#include "nm-setting-gsm.h"
+#include "nm-utils.h"
+#include "nm-setting-private.h"
+
+/**
+ * SECTION:nm-setting-gsm
+ * @short_description: Describes GSM/3GPP-based mobile broadband properties
+ * @include: nm-setting-gsm.h
+ *
+ * The #NMSettingGsm object is a #NMSetting subclass that describes
+ * properties that allow connections to 3GPP-based mobile broadband
+ * networks, including those using GPRS/EDGE and UMTS/HSPA technology.
+ */
+
+/**
+ * nm_setting_gsm_error_quark:
+ *
+ * Registers an error quark for #NMSettingGsm if necessary.
+ *
+ * Returns: the error quark used for #NMSettingGsm errors.
+ **/
+GQuark
+nm_setting_gsm_error_quark (void)
+{
+ static GQuark quark;
+
+ if (G_UNLIKELY (!quark))
+ quark = g_quark_from_static_string ("nm-setting-gsm-error-quark");
+ return quark;
+}
+
+
+G_DEFINE_TYPE_WITH_CODE (NMSettingGsm, nm_setting_gsm, NM_TYPE_SETTING,
+ _nm_register_setting (NM_SETTING_GSM_SETTING_NAME,
+ g_define_type_id,
+ 1,
+ NM_SETTING_GSM_ERROR))
+NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_GSM)
+
+#define NM_SETTING_GSM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_GSM, NMSettingGsmPrivate))
+
+typedef struct {
+ char *number; /* For dialing, duh */
+ char *username;
+ char *password;
+ NMSettingSecretFlags password_flags;
+
+ char *apn; /* NULL for dynamic */
+ char *network_id; /* for manual registration or NULL for automatic */
+ int network_type; /* One of the NM_SETTING_GSM_NETWORK_TYPE_* */
+ guint32 allowed_bands; /* A bitfield of NM_SETTING_GSM_BAND_* */
+
+ char *pin;
+ NMSettingSecretFlags pin_flags;
+
+ gboolean home_only;
+} NMSettingGsmPrivate;
+
+enum {
+ PROP_0,
+ PROP_NUMBER,
+ PROP_USERNAME,
+ PROP_PASSWORD,
+ PROP_PASSWORD_FLAGS,
+ PROP_APN,
+ PROP_NETWORK_ID,
+ PROP_NETWORK_TYPE,
+ PROP_PIN,
+ PROP_PIN_FLAGS,
+ PROP_ALLOWED_BANDS,
+ PROP_HOME_ONLY,
+
+ LAST_PROP
+};
+
+/**
+ * nm_setting_gsm_new:
+ *
+ * Creates a new #NMSettingGsm object with default values.
+ *
+ * Returns: the new empty #NMSettingGsm object
+ **/
+NMSetting *
+nm_setting_gsm_new (void)
+{
+ return (NMSetting *) g_object_new (NM_TYPE_SETTING_GSM, NULL);
+}
+
+/**
+ * nm_setting_gsm_get_number:
+ * @setting: the #NMSettingGsm
+ *
+ * Returns: the #NMSettingGsm:number property of the setting
+ **/
+const char *
+nm_setting_gsm_get_number (NMSettingGsm *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_GSM (setting), NULL);
+
+ return NM_SETTING_GSM_GET_PRIVATE (setting)->number;
+}
+
+/**
+ * nm_setting_gsm_get_username:
+ * @setting: the #NMSettingGsm
+ *
+ * Returns: the #NMSettingGsm:username property of the setting
+ **/
+const char *
+nm_setting_gsm_get_username (NMSettingGsm *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_GSM (setting), NULL);
+
+ return NM_SETTING_GSM_GET_PRIVATE (setting)->username;
+}
+
+/**
+ * nm_setting_gsm_get_password:
+ * @setting: the #NMSettingGsm
+ *
+ * Returns: the #NMSettingGsm:password property of the setting
+ **/
+const char *
+nm_setting_gsm_get_password (NMSettingGsm *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_GSM (setting), NULL);
+
+ return NM_SETTING_GSM_GET_PRIVATE (setting)->password;
+}
+
+/**
+ * nm_setting_gsm_get_password_flags:
+ * @setting: the #NMSettingGsm
+ *
+ * Returns: the #NMSettingSecretFlags pertaining to the #NMSettingGsm:password
+ **/
+NMSettingSecretFlags
+nm_setting_gsm_get_password_flags (NMSettingGsm *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_GSM (setting), NM_SETTING_SECRET_FLAG_NONE);
+
+ return NM_SETTING_GSM_GET_PRIVATE (setting)->password_flags;
+}
+
+/**
+ * nm_setting_gsm_get_apn:
+ * @setting: the #NMSettingGsm
+ *
+ * Returns: the #NMSettingGsm:apn property of the setting
+ **/
+const char *
+nm_setting_gsm_get_apn (NMSettingGsm *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_GSM (setting), NULL);
+
+ return NM_SETTING_GSM_GET_PRIVATE (setting)->apn;
+}
+
+/**
+ * nm_setting_gsm_get_network_id:
+ * @setting: the #NMSettingGsm
+ *
+ * Returns: the #NMSettingGsm:network-id property of the setting
+ **/
+const char *
+nm_setting_gsm_get_network_id (NMSettingGsm *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_GSM (setting), NULL);
+
+ return NM_SETTING_GSM_GET_PRIVATE (setting)->network_id;
+}
+
+/**
+ * nm_setting_gsm_get_network_type:
+ * @setting: the #NMSettingGsm
+ *
+ * Returns: the #NMSettingGsm:network-type property of the setting
+ *
+ * Deprecated: 0.9.10: No longer used. Network type setting should be done talking to ModemManager directly.
+ **/
+int
+nm_setting_gsm_get_network_type (NMSettingGsm *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_GSM (setting), -1);
+
+ return NM_SETTING_GSM_GET_PRIVATE (setting)->network_type;
+}
+
+/**
+ * nm_setting_gsm_get_allowed_bands:
+ * @setting: the #NMSettingGsm
+ *
+ * Returns: the #NMSettingGsm:allowed-bands property of the setting
+ *
+ * Deprecated: 0.9.10: No longer used. Bands setting should be done talking to ModemManager directly.
+ **/
+guint32
+nm_setting_gsm_get_allowed_bands (NMSettingGsm *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_GSM (setting), NM_SETTING_GSM_BAND_UNKNOWN);
+
+ return NM_SETTING_GSM_GET_PRIVATE (setting)->allowed_bands;
+}
+
+/**
+ * nm_setting_gsm_get_pin:
+ * @setting: the #NMSettingGsm
+ *
+ * Returns: the #NMSettingGsm:pin property of the setting
+ **/
+const char *
+nm_setting_gsm_get_pin (NMSettingGsm *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_GSM (setting), NULL);
+
+ return NM_SETTING_GSM_GET_PRIVATE (setting)->pin;
+}
+
+/**
+ * nm_setting_gsm_get_pin_flags:
+ * @setting: the #NMSettingGsm
+ *
+ * Returns: the #NMSettingSecretFlags pertaining to the #NMSettingGsm:pin
+ **/
+NMSettingSecretFlags
+nm_setting_gsm_get_pin_flags (NMSettingGsm *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_GSM (setting), NM_SETTING_SECRET_FLAG_NONE);
+
+ return NM_SETTING_GSM_GET_PRIVATE (setting)->pin_flags;
+}
+
+/**
+ * nm_setting_gsm_get_home_only:
+ * @setting: the #NMSettingGsm
+ *
+ * Returns: the #NMSettingGsm:home-only property of the setting
+ **/
+gboolean
+nm_setting_gsm_get_home_only (NMSettingGsm *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_GSM (setting), FALSE);
+
+ return NM_SETTING_GSM_GET_PRIVATE (setting)->home_only;
+}
+
+static gboolean
+verify (NMSetting *setting, GSList *all_settings, GError **error)
+{
+ NMSettingGsmPrivate *priv = NM_SETTING_GSM_GET_PRIVATE (setting);
+
+ if (priv->number && !priv->number[0]) {
+ g_set_error_literal (error,
+ NM_SETTING_GSM_ERROR,
+ NM_SETTING_GSM_ERROR_INVALID_PROPERTY,
+ _("property is empty"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_GSM_SETTING_NAME, NM_SETTING_GSM_NUMBER);
+ return FALSE;
+ }
+
+ if (priv->apn) {
+ guint32 apn_len = strlen (priv->apn);
+ guint32 i;
+
+ if (apn_len < 1 || apn_len > 64) {
+ g_set_error (error,
+ NM_SETTING_GSM_ERROR,
+ NM_SETTING_GSM_ERROR_INVALID_PROPERTY,
+ _("property value '%s' is empty or too long (>64)"),
+ priv->apn);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_GSM_SETTING_NAME, NM_SETTING_GSM_APN);
+ return FALSE;
+ }
+
+ /* APNs roughly follow the same rules as DNS domain names. Allowed
+ * characters are a-z, 0-9, . and -. GSM 03.03 Section 9.1 states:
+ *
+ * The syntax of the APN shall follow the Name Syntax defined in
+ * RFC 2181 [14] and RFC 1035 [15]. The APN consists of one or
+ * more labels. Each label is coded as one octet length field
+ * followed by that number of octets coded as 8 bit ASCII characters.
+ * Following RFC 1035 [15] the labels should consist only of the
+ * alphabetic characters (A-Z and a-z), digits (0-9) and the
+ * dash (-). The case of alphabetic characters is not significant.
+ *
+ * A dot (.) is commonly used to separate parts of the APN, and
+ * apparently the underscore (_) is used as well. RFC 2181 indicates
+ * that no restrictions of any kind are placed on DNS labels, and thus
+ * it would appear that none are placed on APNs either, but many modems
+ * and networks will fail to accept APNs that include odd characters
+ * like space ( ) and such.
+ */
+ for (i = 0; i < apn_len; i++) {
+ if ( !g_ascii_isalnum (priv->apn[i])
+ && (priv->apn[i] != '.')
+ && (priv->apn[i] != '_')
+ && (priv->apn[i] != '-')) {
+ g_set_error (error,
+ NM_SETTING_GSM_ERROR,
+ NM_SETTING_GSM_ERROR_INVALID_PROPERTY,
+ _("'%s' contains invalid char(s) (use [A-Za-z._-])"),
+ priv->apn);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_GSM_SETTING_NAME, NM_SETTING_GSM_APN);
+ return FALSE;
+ }
+ }
+ }
+
+ if (priv->username && !strlen (priv->username)) {
+ g_set_error_literal (error,
+ NM_SETTING_GSM_ERROR,
+ NM_SETTING_GSM_ERROR_INVALID_PROPERTY,
+ _("property is empty"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_GSM_SETTING_NAME, NM_SETTING_GSM_USERNAME);
+ return FALSE;
+ }
+
+ if (priv->password && !strlen (priv->password)) {
+ g_set_error_literal (error,
+ NM_SETTING_GSM_ERROR,
+ NM_SETTING_GSM_ERROR_INVALID_PROPERTY,
+ _("property is empty"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_GSM_SETTING_NAME, NM_SETTING_GSM_USERNAME);
+ return FALSE;
+ }
+
+ if (priv->network_id) {
+ guint32 nid_len = strlen (priv->network_id);
+ guint32 i;
+
+ /* Accept both 5 and 6 digit MCC/MNC codes */
+ if ((nid_len < 5) || (nid_len > 6)) {
+ g_set_error (error,
+ NM_SETTING_GSM_ERROR,
+ NM_SETTING_GSM_ERROR_INVALID_PROPERTY,
+ _("'%s' length is invalid (should be 5 or 6 digits)"),
+ priv->network_id);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_GSM_SETTING_NAME, NM_SETTING_GSM_NETWORK_ID);
+ return FALSE;
+ }
+
+ for (i = 0; i < nid_len; i++) {
+ if (!g_ascii_isdigit (priv->network_id[i])) {
+ g_set_error (error,
+ NM_SETTING_GSM_ERROR,
+ NM_SETTING_GSM_ERROR_INVALID_PROPERTY,
+ _("'%s' is not a number"),
+ priv->network_id);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_GSM_SETTING_NAME, NM_SETTING_GSM_NETWORK_ID);
+ return FALSE;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+static GPtrArray *
+need_secrets (NMSetting *setting)
+{
+ NMSettingGsmPrivate *priv = NM_SETTING_GSM_GET_PRIVATE (setting);
+ GPtrArray *secrets = NULL;
+
+ if (priv->password)
+ return NULL;
+
+ if (priv->username) {
+ if (!(priv->password_flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED)) {
+ secrets = g_ptr_array_sized_new (1);
+ g_ptr_array_add (secrets, NM_SETTING_GSM_PASSWORD);
+ }
+ }
+
+ return secrets;
+}
+
+static void
+nm_setting_gsm_init (NMSettingGsm *setting)
+{
+}
+
+static void
+finalize (GObject *object)
+{
+ NMSettingGsmPrivate *priv = NM_SETTING_GSM_GET_PRIVATE (object);
+
+ g_free (priv->number);
+ g_free (priv->username);
+ g_free (priv->password);
+ g_free (priv->apn);
+ g_free (priv->network_id);
+ g_free (priv->pin);
+
+ G_OBJECT_CLASS (nm_setting_gsm_parent_class)->finalize (object);
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ NMSettingGsmPrivate *priv = NM_SETTING_GSM_GET_PRIVATE (object);
+ char *tmp;
+
+ switch (prop_id) {
+ case PROP_NUMBER:
+ g_free (priv->number);
+ priv->number = g_value_dup_string (value);
+ break;
+ case PROP_USERNAME:
+ g_free (priv->username);
+ priv->username = g_value_dup_string (value);
+ break;
+ case PROP_PASSWORD:
+ g_free (priv->password);
+ priv->password = g_value_dup_string (value);
+ break;
+ case PROP_PASSWORD_FLAGS:
+ priv->password_flags = g_value_get_uint (value);
+ break;
+ case PROP_APN:
+ g_free (priv->apn);
+ priv->apn = NULL;
+ tmp = g_value_dup_string (value);
+ if (tmp)
+ priv->apn = g_strstrip (tmp);
+ break;
+ case PROP_NETWORK_ID:
+ g_free (priv->network_id);
+ priv->network_id = NULL;
+ tmp = g_value_dup_string (value);
+ if (tmp)
+ priv->network_id = g_strstrip (tmp);
+ break;
+ case PROP_NETWORK_TYPE:
+ priv->network_type = g_value_get_int (value);
+ break;
+ case PROP_ALLOWED_BANDS:
+ priv->allowed_bands = g_value_get_uint (value);
+ break;
+ case PROP_PIN:
+ g_free (priv->pin);
+ priv->pin = g_value_dup_string (value);
+ break;
+ case PROP_PIN_FLAGS:
+ priv->pin_flags = g_value_get_uint (value);
+ break;
+ case PROP_HOME_ONLY:
+ priv->home_only = g_value_get_boolean (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ NMSettingGsm *setting = NM_SETTING_GSM (object);
+
+ switch (prop_id) {
+ case PROP_NUMBER:
+ g_value_set_string (value, nm_setting_gsm_get_number (setting));
+ break;
+ case PROP_USERNAME:
+ g_value_set_string (value, nm_setting_gsm_get_username (setting));
+ break;
+ case PROP_PASSWORD:
+ g_value_set_string (value, nm_setting_gsm_get_password (setting));
+ break;
+ case PROP_PASSWORD_FLAGS:
+ g_value_set_uint (value, nm_setting_gsm_get_password_flags (setting));
+ break;
+ case PROP_APN:
+ g_value_set_string (value, nm_setting_gsm_get_apn (setting));
+ break;
+ case PROP_NETWORK_ID:
+ g_value_set_string (value, nm_setting_gsm_get_network_id (setting));
+ break;
+ case PROP_NETWORK_TYPE:
+ g_value_set_int (value, NM_SETTING_GSM_GET_PRIVATE (setting)->network_type);
+ break;
+ case PROP_ALLOWED_BANDS:
+ g_value_set_uint (value, NM_SETTING_GSM_GET_PRIVATE (setting)->allowed_bands);
+ break;
+ case PROP_PIN:
+ g_value_set_string (value, nm_setting_gsm_get_pin (setting));
+ break;
+ case PROP_PIN_FLAGS:
+ g_value_set_uint (value, nm_setting_gsm_get_pin_flags (setting));
+ break;
+ case PROP_HOME_ONLY:
+ g_value_set_boolean (value, nm_setting_gsm_get_home_only (setting));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nm_setting_gsm_class_init (NMSettingGsmClass *setting_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
+ NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+
+ g_type_class_add_private (setting_class, sizeof (NMSettingGsmPrivate));
+
+ /* virtual methods */
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+ object_class->finalize = finalize;
+ parent_class->verify = verify;
+ parent_class->need_secrets = need_secrets;
+
+ /* Properties */
+
+ /**
+ * NMSettingGsm:number:
+ *
+ * Number to dial when establishing a PPP data session with the GSM-based
+ * mobile broadband network. Many modems do not require PPP for connections
+ * to the mobile network and thus this property should be left blank, which
+ * allows NetworkManager to select the appropriate settings automatically.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_NUMBER,
+ g_param_spec_string (NM_SETTING_GSM_NUMBER, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingGsm:username:
+ *
+ * The username used to authenticate with the network, if required. Many
+ * providers do not require a username, or accept any username. But if a
+ * username is required, it is specified here.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_USERNAME,
+ g_param_spec_string (NM_SETTING_GSM_USERNAME, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingGsm:password:
+ *
+ * The password used to authenticate with the network, if required. Many
+ * providers do not require a password, or accept any password. But if a
+ * password is required, it is specified here.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PASSWORD,
+ g_param_spec_string (NM_SETTING_GSM_PASSWORD, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ NM_SETTING_PARAM_SECRET |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingGsm:password-flags:
+ *
+ * Flags indicating how to handle the #NMSettingGsm:password property.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PASSWORD_FLAGS,
+ g_param_spec_uint (NM_SETTING_GSM_PASSWORD_FLAGS, "", "",
+ NM_SETTING_SECRET_FLAG_NONE,
+ NM_SETTING_SECRET_FLAGS_ALL,
+ NM_SETTING_SECRET_FLAG_NONE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingGsm:apn:
+ *
+ * The GPRS Access Point Name specifying the APN used when establishing a
+ * data session with the GSM-based network. The APN often determines how
+ * the user will be billed for their network usage and whether the user has
+ * access to the Internet or just a provider-specific walled-garden, so it
+ * is important to use the correct APN for the user's mobile broadband plan.
+ * The APN may only be composed of the characters a-z, 0-9, ., and - per GSM
+ * 03.60 Section 14.9.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_APN,
+ g_param_spec_string (NM_SETTING_GSM_APN, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingGsm:network-id:
+ *
+ * The Network ID (GSM LAI format, ie MCC-MNC) to force specific network
+ * registration. If the Network ID is specified, NetworkManager will
+ * attempt to force the device to register only on the specified network.
+ * This can be used to ensure that the device does not roam when direct
+ * roaming control of the device is not otherwise possible.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_NETWORK_ID,
+ g_param_spec_string (NM_SETTING_GSM_NETWORK_ID, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingGsm:network-type:
+ *
+ * Network preference to force the device to only use specific network
+ * technologies. The permitted values are %NM_SETTING_GSM_NETWORK_TYPE_ANY,
+ * %NM_SETTING_GSM_NETWORK_TYPE_UMTS_HSPA,
+ * %NM_SETTING_GSM_NETWORK_TYPE_GPRS_EDGE,
+ * %NM_SETTING_GSM_NETWORK_TYPE_PREFER_UMTS_HSPA,
+ * %NM_SETTING_GSM_NETWORK_TYPE_PREFER_GPRS_EDGE,
+ * %NM_SETTING_GSM_NETWORK_TYPE_PREFER_4G, and
+ * %NM_SETTING_GSM_NETWORK_TYPE_4G. Note that not all devices allow network
+ * preference control.
+ *
+ * Deprecated: 0.9.10: No longer used. Network type setting should be done
+ * by talking to ModemManager directly.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_NETWORK_TYPE,
+ g_param_spec_int (NM_SETTING_GSM_NETWORK_TYPE, "", "",
+ NM_SETTING_GSM_NETWORK_TYPE_ANY,
+ NM_SETTING_GSM_NETWORK_TYPE_4G,
+ NM_SETTING_GSM_NETWORK_TYPE_ANY,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingGsm:allowed-bands:
+ *
+ * Bitfield of allowed frequency bands. Note that not all devices allow
+ * frequency band control. Permitted values are those specified by
+ * #NMSettingGsmNetworkBand.
+ *
+ * Deprecated: 0.9.10: No longer used. Band setting should be done by
+ * talking to ModemManager directly.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_ALLOWED_BANDS,
+ g_param_spec_uint (NM_SETTING_GSM_ALLOWED_BANDS, "", "",
+ NM_SETTING_GSM_BAND_UNKNOWN,
+ NM_SETTING_GSM_BANDS_MAX,
+ NM_SETTING_GSM_BAND_ANY,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingGsm:pin:
+ *
+ * If the SIM is locked with a PIN it must be unlocked before any other
+ * operations are requested. Specify the PIN here to allow operation of the
+ * device.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PIN,
+ g_param_spec_string (NM_SETTING_GSM_PIN, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ NM_SETTING_PARAM_SECRET |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingGsm:pin-flags:
+ *
+ * Flags indicating how to handle the #NMSettingGsm:pin property.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PIN_FLAGS,
+ g_param_spec_uint (NM_SETTING_GSM_PIN_FLAGS, "", "",
+ NM_SETTING_SECRET_FLAG_NONE,
+ NM_SETTING_SECRET_FLAGS_ALL,
+ NM_SETTING_SECRET_FLAG_NONE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingGsm:home-only:
+ *
+ * When %TRUE, only connections to the home network will be allowed.
+ * Connections to roaming networks will not be made.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_HOME_ONLY,
+ g_param_spec_boolean (NM_SETTING_GSM_HOME_ONLY, "", "",
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/libnm-core/nm-setting-gsm.h b/libnm-core/nm-setting-gsm.h
new file mode 100644
index 0000000000..564a8b4b9d
--- /dev/null
+++ b/libnm-core/nm-setting-gsm.h
@@ -0,0 +1,204 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2011 Red Hat, Inc.
+ * Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#ifndef NM_SETTING_GSM_H
+#define NM_SETTING_GSM_H
+
+#include <nm-setting.h>
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_SETTING_GSM (nm_setting_gsm_get_type ())
+#define NM_SETTING_GSM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_GSM, NMSettingGsm))
+#define NM_SETTING_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_GSM, NMSettingGsmClass))
+#define NM_IS_SETTING_GSM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_GSM))
+#define NM_IS_SETTING_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_GSM))
+#define NM_SETTING_GSM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_GSM, NMSettingGsmClass))
+
+#define NM_SETTING_GSM_SETTING_NAME "gsm"
+
+/**
+ * NMSettingGsmError:
+ * @NM_SETTING_GSM_ERROR_UNKNOWN: unknown or unclassified error
+ * @NM_SETTING_GSM_ERROR_INVALID_PROPERTY: the property was invalid
+ * @NM_SETTING_GSM_ERROR_MISSING_PROPERTY: the property was missing and is
+ * required
+ * @NM_SETTING_GSM_ERROR_MISSING_SERIAL_SETTING: the required #NMSettingSerial
+ * is missing in the connection
+ */
+typedef enum {
+ NM_SETTING_GSM_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/
+ NM_SETTING_GSM_ERROR_INVALID_PROPERTY, /*< nick=InvalidProperty >*/
+ NM_SETTING_GSM_ERROR_MISSING_PROPERTY, /*< nick=MissingProperty >*/
+ NM_SETTING_GSM_ERROR_MISSING_SERIAL_SETTING /*< nick=MissingSerialSetting >*/
+} NMSettingGsmError;
+
+#define NM_SETTING_GSM_ERROR nm_setting_gsm_error_quark ()
+GQuark nm_setting_gsm_error_quark (void);
+
+#define NM_SETTING_GSM_NUMBER "number"
+#define NM_SETTING_GSM_USERNAME "username"
+#define NM_SETTING_GSM_PASSWORD "password"
+#define NM_SETTING_GSM_PASSWORD_FLAGS "password-flags"
+#define NM_SETTING_GSM_APN "apn"
+#define NM_SETTING_GSM_NETWORK_ID "network-id"
+#define NM_SETTING_GSM_PIN "pin"
+#define NM_SETTING_GSM_PIN_FLAGS "pin-flags"
+#define NM_SETTING_GSM_HOME_ONLY "home-only"
+
+/* Deprecated */
+#define NM_SETTING_GSM_ALLOWED_BANDS "allowed-bands"
+#define NM_SETTING_GSM_NETWORK_TYPE "network-type"
+
+/**
+ * NMSettingGsmNetworkType:
+ * @NM_SETTING_GSM_NETWORK_TYPE_ANY: any access technology may be used
+ * @NM_SETTING_GSM_NETWORK_TYPE_UMTS_HSPA: only 3G-type (UMTS and HSPA)
+ * technologies may be used
+ * @NM_SETTING_GSM_NETWORK_TYPE_GPRS_EDGE: only 2G-type (GPRS and EDGE)
+ * technologies may be used
+ * @NM_SETTING_GSM_NETWORK_TYPE_PREFER_UMTS_HSPA: 3G-type technologies are
+ * preferred but 2G-type technologies may be used as a fallback
+ * @NM_SETTING_GSM_NETWORK_TYPE_PREFER_GPRS_EDGE: 2G-type technologies are
+ * preferred but 3G-type technologies may be used as a fallback
+ * @NM_SETTING_GSM_NETWORK_TYPE_PREFER_4G: 4G/LTE-type technologies are
+ * preferred but 3G/2/-type technologies may be used as a fallback
+ * @NM_SETTING_GSM_NETWORK_TYPE_4G: only 4G/LTE type
+ * technologies may be used
+ *
+ * #NMSettingGsmNetworkType values indicate the allowed access technologies
+ * the device may use when connecting to this network.
+ *
+ * Deprecated: 0.9.10: No longer used.
+ */
+typedef enum {
+ NM_SETTING_GSM_NETWORK_TYPE_ANY = -1,
+ NM_SETTING_GSM_NETWORK_TYPE_UMTS_HSPA = 0,
+ NM_SETTING_GSM_NETWORK_TYPE_GPRS_EDGE = 1,
+ NM_SETTING_GSM_NETWORK_TYPE_PREFER_UMTS_HSPA = 2,
+ NM_SETTING_GSM_NETWORK_TYPE_PREFER_GPRS_EDGE = 3,
+ NM_SETTING_GSM_NETWORK_TYPE_PREFER_4G = 4,
+ NM_SETTING_GSM_NETWORK_TYPE_4G = 5
+} NMSettingGsmNetworkType;
+
+/**
+ * NMSettingGsmNetworkBand:
+ * @NM_SETTING_GSM_BAND_UNKNOWN: unknown or no band specified
+ * @NM_SETTING_GSM_BAND_ANY: any band is allowed
+ * @NM_SETTING_GSM_BAND_EGSM: 900 MHz original GSM band
+ * @NM_SETTING_GSM_BAND_DCS: 1800 MHz DCS band
+ * @NM_SETTING_GSM_BAND_PCS: US 1900 MHz PCS band
+ * @NM_SETTING_GSM_BAND_G850: US 850 MHz Cellular band
+ * @NM_SETTING_GSM_BAND_U2100: WCDMA 3GPP UMTS 2100 MHz (Class I)
+ * @NM_SETTING_GSM_BAND_U1800: WCDMA 3GPP UMTS 1800 MHz (Class III)
+ * @NM_SETTING_GSM_BAND_U17IV: WCDMA 3GPP AWS 1700/2100 MHz (Class IV)
+ * @NM_SETTING_GSM_BAND_U800: WCDMA 3GPP UMTS 800 MHz (Class VI)
+ * @NM_SETTING_GSM_BAND_U850: WCDMA 3GPP UMTS 850 MHz (Class V)
+ * @NM_SETTING_GSM_BAND_U900: WCDMA 3GPP UMTS 900 MHz (Class VIII)
+ * @NM_SETTING_GSM_BAND_U17IX: WCDMA 3GPP UMTS 1700 MHz (Class IX)
+ * @NM_SETTING_GSM_BAND_U1900: WCDMA 3GPP UMTS 1900 MHz (Class II)
+ * @NM_SETTING_GSM_BAND_U2600: WCDMA 3GPP UMTS 2600 MHz (Class VII, internal)
+ *
+ * #NMSettingGsmNetworkBand values indicate the allowed frequency bands
+ * the device may use when connecting to this network.
+ *
+ * Deprecated: 0.9.10: No longer used.
+ */
+typedef enum {
+ NM_SETTING_GSM_BAND_UNKNOWN = 0x00000000,
+ NM_SETTING_GSM_BAND_ANY = 0x00000001,
+ NM_SETTING_GSM_BAND_EGSM = 0x00000002, /* 900 MHz */
+ NM_SETTING_GSM_BAND_DCS = 0x00000004, /* 1800 MHz */
+ NM_SETTING_GSM_BAND_PCS = 0x00000008, /* 1900 MHz */
+ NM_SETTING_GSM_BAND_G850 = 0x00000010, /* 850 MHz */
+ NM_SETTING_GSM_BAND_U2100 = 0x00000020, /* WCDMA 3GPP UMTS 2100 MHz (Class I) */
+ NM_SETTING_GSM_BAND_U1800 = 0x00000040, /* WCDMA 3GPP UMTS 1800 MHz (Class III) */
+ NM_SETTING_GSM_BAND_U17IV = 0x00000080, /* WCDMA 3GPP AWS 1700/2100 MHz (Class IV) */
+ NM_SETTING_GSM_BAND_U800 = 0x00000100, /* WCDMA 3GPP UMTS 800 MHz (Class VI) */
+ NM_SETTING_GSM_BAND_U850 = 0x00000200, /* WCDMA 3GPP UMTS 850 MHz (Class V) */
+ NM_SETTING_GSM_BAND_U900 = 0x00000400, /* WCDMA 3GPP UMTS 900 MHz (Class VIII) */
+ NM_SETTING_GSM_BAND_U17IX = 0x00000800, /* WCDMA 3GPP UMTS 1700 MHz (Class IX) */
+ NM_SETTING_GSM_BAND_U1900 = 0x00001000, /* WCDMA 3GPP UMTS 1900 MHz (Class II) */
+ NM_SETTING_GSM_BAND_U2600 = 0x00002000, /* WCDMA 3GPP UMTS 2600 MHz (Class VII, internal) */
+} NMSettingGsmNetworkBand;
+
+/**
+ * NM_SETTING_GSM_BANDS_MAX:
+ *
+ * #NM_SETTING_GSM_BANDS_MAX macro indicate the maximal value that can be used
+ * as the allowed frequency bands (#NMSettingGsm:allowed-bands property).
+ *
+ * Deprecated: 0.9.10: No longer used.
+ */
+#define NM_SETTING_GSM_BANDS_MAX ( NM_SETTING_GSM_BAND_UNKNOWN \
+ | NM_SETTING_GSM_BAND_ANY \
+ | NM_SETTING_GSM_BAND_EGSM \
+ | NM_SETTING_GSM_BAND_DCS \
+ | NM_SETTING_GSM_BAND_PCS \
+ | NM_SETTING_GSM_BAND_G850 \
+ | NM_SETTING_GSM_BAND_U2100 \
+ | NM_SETTING_GSM_BAND_U1800 \
+ | NM_SETTING_GSM_BAND_U17IV \
+ | NM_SETTING_GSM_BAND_U800 \
+ | NM_SETTING_GSM_BAND_U850 \
+ | NM_SETTING_GSM_BAND_U900 \
+ | NM_SETTING_GSM_BAND_U17IX \
+ | NM_SETTING_GSM_BAND_U1900 \
+ | NM_SETTING_GSM_BAND_U2600)
+
+typedef struct {
+ NMSetting parent;
+} NMSettingGsm;
+
+typedef struct {
+ NMSettingClass parent;
+
+ /* Padding for future expansion */
+ void (*_reserved1) (void);
+ void (*_reserved2) (void);
+ void (*_reserved3) (void);
+ void (*_reserved4) (void);
+} NMSettingGsmClass;
+
+GType nm_setting_gsm_get_type (void);
+
+NMSetting *nm_setting_gsm_new (void);
+const char *nm_setting_gsm_get_number (NMSettingGsm *setting);
+const char *nm_setting_gsm_get_username (NMSettingGsm *setting);
+const char *nm_setting_gsm_get_password (NMSettingGsm *setting);
+const char *nm_setting_gsm_get_apn (NMSettingGsm *setting);
+const char *nm_setting_gsm_get_network_id (NMSettingGsm *setting);
+const char *nm_setting_gsm_get_pin (NMSettingGsm *setting);
+gboolean nm_setting_gsm_get_home_only (NMSettingGsm *setting);
+
+NMSettingSecretFlags nm_setting_gsm_get_pin_flags (NMSettingGsm *setting);
+NMSettingSecretFlags nm_setting_gsm_get_password_flags (NMSettingGsm *setting);
+
+/* Deprecated */
+NM_DEPRECATED_IN_0_9_10
+int nm_setting_gsm_get_network_type (NMSettingGsm *setting);
+NM_DEPRECATED_IN_0_9_10
+guint32 nm_setting_gsm_get_allowed_bands (NMSettingGsm *setting);
+
+G_END_DECLS
+
+#endif /* NM_SETTING_GSM_H */
diff --git a/libnm-core/nm-setting-infiniband.c b/libnm-core/nm-setting-infiniband.c
new file mode 100644
index 0000000000..4e470e561b
--- /dev/null
+++ b/libnm-core/nm-setting-infiniband.c
@@ -0,0 +1,472 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2011 - 2013 Red Hat, Inc.
+ */
+
+#include <stdlib.h>
+#include <dbus/dbus-glib.h>
+#include <linux/if_infiniband.h>
+#include <glib/gi18n.h>
+
+#include "nm-setting-infiniband.h"
+#include "nm-param-spec-specialized.h"
+#include "nm-utils.h"
+#include "nm-utils-private.h"
+#include "nm-setting-private.h"
+#include "nm-setting-connection.h"
+
+/**
+ * SECTION:nm-setting-infiniband
+ * @short_description: Describes connection properties for IP-over-InfiniBand networks
+ * @include: nm-setting-infiniband.h
+ *
+ * The #NMSettingInfiniband object is a #NMSetting subclass that describes properties
+ * necessary for connection to IP-over-InfiniBand networks.
+ **/
+
+/**
+ * nm_setting_infiniband_error_quark:
+ *
+ * Registers an error quark for #NMSettingInfiniband if necessary.
+ *
+ * Returns: the error quark used for #NMSettingInfiniband errors.
+ **/
+GQuark
+nm_setting_infiniband_error_quark (void)
+{
+ static GQuark quark;
+
+ if (G_UNLIKELY (!quark))
+ quark = g_quark_from_static_string ("nm-setting-infiniband-error-quark");
+ return quark;
+}
+
+G_DEFINE_TYPE_WITH_CODE (NMSettingInfiniband, nm_setting_infiniband, NM_TYPE_SETTING,
+ _nm_register_setting (NM_SETTING_INFINIBAND_SETTING_NAME,
+ g_define_type_id,
+ 1,
+ NM_SETTING_INFINIBAND_ERROR))
+NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_INFINIBAND)
+
+#define NM_SETTING_INFINIBAND_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_INFINIBAND, NMSettingInfinibandPrivate))
+
+typedef struct {
+ GByteArray *mac_address;
+ char *transport_mode;
+ guint32 mtu;
+ int p_key;
+ char *parent, *virtual_iface_name;
+} NMSettingInfinibandPrivate;
+
+enum {
+ PROP_0,
+ PROP_MAC_ADDRESS,
+ PROP_MTU,
+ PROP_TRANSPORT_MODE,
+ PROP_P_KEY,
+ PROP_PARENT,
+
+ LAST_PROP
+};
+
+/**
+ * nm_setting_infiniband_new:
+ *
+ * Creates a new #NMSettingInfiniband object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingInfiniband object
+ **/
+NMSetting *
+nm_setting_infiniband_new (void)
+{
+ return (NMSetting *) g_object_new (NM_TYPE_SETTING_INFINIBAND, NULL);
+}
+
+/**
+ * nm_setting_infiniband_get_mac_address:
+ * @setting: the #NMSettingInfiniband
+ *
+ * Returns: the #NMSettingInfiniband:mac-address property of the setting
+ **/
+const GByteArray *
+nm_setting_infiniband_get_mac_address (NMSettingInfiniband *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_INFINIBAND (setting), NULL);
+
+ return NM_SETTING_INFINIBAND_GET_PRIVATE (setting)->mac_address;
+}
+
+/**
+ * nm_setting_infiniband_get_mtu:
+ * @setting: the #NMSettingInfiniband
+ *
+ * Returns: the #NMSettingInfiniband:mtu property of the setting
+ **/
+guint32
+nm_setting_infiniband_get_mtu (NMSettingInfiniband *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_INFINIBAND (setting), 0);
+
+ return NM_SETTING_INFINIBAND_GET_PRIVATE (setting)->mtu;
+}
+
+/**
+ * nm_setting_infiniband_get_transport_mode:
+ * @setting: the #NMSettingInfiniband
+ *
+ * Returns the transport mode for this device. Either 'datagram' or
+ * 'connected'.
+ *
+ * Returns: the IPoIB transport mode
+ **/
+const char *
+nm_setting_infiniband_get_transport_mode (NMSettingInfiniband *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_INFINIBAND (setting), NULL);
+
+ return NM_SETTING_INFINIBAND_GET_PRIVATE (setting)->transport_mode;
+}
+
+/**
+ * nm_setting_infiniband_get_p_key:
+ * @setting: the #NMSettingInfiniband
+ *
+ * Returns the P_Key to use for this device. A value of -1 means to
+ * use the default P_Key (aka "the P_Key at index 0"). Otherwise it is
+ * a 16-bit unsigned integer.
+ *
+ * Returns: the IPoIB P_Key
+ **/
+int
+nm_setting_infiniband_get_p_key (NMSettingInfiniband *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_INFINIBAND (setting), -1);
+
+ return NM_SETTING_INFINIBAND_GET_PRIVATE (setting)->p_key;
+}
+
+/**
+ * nm_setting_infiniband_get_parent:
+ * @setting: the #NMSettingInfiniband
+ *
+ * Returns the parent interface name for this device, if set.
+ *
+ * Returns: the parent interface name
+ **/
+const char *
+nm_setting_infiniband_get_parent (NMSettingInfiniband *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_INFINIBAND (setting), NULL);
+
+ return NM_SETTING_INFINIBAND_GET_PRIVATE (setting)->parent;
+}
+
+static const char *
+get_virtual_iface_name (NMSetting *setting)
+{
+ NMSettingInfinibandPrivate *priv = NM_SETTING_INFINIBAND_GET_PRIVATE (setting);
+
+ if (priv->p_key == -1 || !priv->parent)
+ return NULL;
+
+ if (!priv->virtual_iface_name)
+ priv->virtual_iface_name = g_strdup_printf ("%s.%04x", priv->parent, priv->p_key);
+
+ return NM_SETTING_INFINIBAND_GET_PRIVATE (setting)->virtual_iface_name;
+}
+
+static gboolean
+verify (NMSetting *setting, GSList *all_settings, GError **error)
+{
+ NMSettingConnection *s_con;
+ NMSettingInfinibandPrivate *priv = NM_SETTING_INFINIBAND_GET_PRIVATE (setting);
+
+ if (priv->mac_address && priv->mac_address->len != INFINIBAND_ALEN) {
+ g_set_error_literal (error,
+ NM_SETTING_INFINIBAND_ERROR,
+ NM_SETTING_INFINIBAND_ERROR_INVALID_PROPERTY,
+ _("property is invalid"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_INFINIBAND_SETTING_NAME, NM_SETTING_INFINIBAND_MAC_ADDRESS);
+ return FALSE;
+ }
+
+ if (!g_strcmp0 (priv->transport_mode, "datagram")) {
+ if (priv->mtu > 2044)
+ priv->mtu = 2044;
+ } else if (!g_strcmp0 (priv->transport_mode, "connected")) {
+ if (priv->mtu > 65520)
+ priv->mtu = 65520;
+ } else {
+ g_set_error_literal (error,
+ NM_SETTING_INFINIBAND_ERROR,
+ NM_SETTING_INFINIBAND_ERROR_INVALID_PROPERTY,
+ _("property is invalid"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_INFINIBAND_SETTING_NAME, NM_SETTING_INFINIBAND_TRANSPORT_MODE);
+ return FALSE;
+ }
+
+ if (priv->parent) {
+ if (!nm_utils_iface_valid_name (priv->parent)) {
+ g_set_error_literal (error,
+ NM_SETTING_INFINIBAND_ERROR,
+ NM_SETTING_INFINIBAND_ERROR_INVALID_PROPERTY,
+ _("not a valid interface name"));
+ g_prefix_error (error, "%s: ", NM_SETTING_INFINIBAND_PARENT);
+ return FALSE;
+ }
+ if (priv->p_key == -1) {
+ g_set_error_literal (error,
+ NM_SETTING_INFINIBAND_ERROR,
+ NM_SETTING_INFINIBAND_ERROR_INVALID_PROPERTY,
+ _("Must specify a P_Key if specifying parent"));
+ g_prefix_error (error, "%s: ", NM_SETTING_INFINIBAND_PARENT);
+ }
+ }
+
+ if (priv->p_key != -1) {
+ if (!priv->mac_address && !priv->parent) {
+ g_set_error_literal (error,
+ NM_SETTING_INFINIBAND_ERROR,
+ NM_SETTING_INFINIBAND_ERROR_MISSING_PROPERTY,
+ _("InfiniBand P_Key connection did not specify parent interface name"));
+ g_prefix_error (error, "%s: ", NM_SETTING_INFINIBAND_PARENT);
+ return FALSE;
+ }
+ }
+
+ s_con = NM_SETTING_CONNECTION (nm_setting_find_in_list (all_settings, NM_SETTING_CONNECTION_SETTING_NAME));
+ if (s_con) {
+ const char *interface_name = nm_setting_connection_get_interface_name (s_con);
+
+ if (!interface_name)
+ ;
+ else if (!nm_utils_iface_valid_name (interface_name)) {
+ /* report the error for NMSettingConnection:interface-name, because
+ * it's that property that is invalid -- although we currently verify()
+ * NMSettingInfiniband.
+ **/
+ g_set_error (error,
+ NM_SETTING_CONNECTION_ERROR,
+ NM_SETTING_CONNECTION_ERROR_INVALID_PROPERTY,
+ _("'%s' is not a valid interface name"),
+ interface_name);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_INTERFACE_NAME);
+ return FALSE;
+ } else {
+ if (priv->p_key != -1) {
+ if (!priv->virtual_iface_name)
+ priv->virtual_iface_name = g_strdup_printf ("%s.%04x", priv->parent, priv->p_key);
+
+ if (strcmp (interface_name, priv->virtual_iface_name) != 0) {
+ /* We don't support renaming software infiniband devices. Later we might, but
+ * for now just reject such connections.
+ **/
+ g_set_error (error,
+ NM_SETTING_CONNECTION_ERROR,
+ NM_SETTING_CONNECTION_ERROR_INVALID_PROPERTY,
+ _("interface name of software infiniband device must be '%s' or unset (instead it is '%s')"),
+ priv->virtual_iface_name, interface_name);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_INTERFACE_NAME);
+ return FALSE;
+ }
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+static void
+nm_setting_infiniband_init (NMSettingInfiniband *setting)
+{
+}
+
+static void
+finalize (GObject *object)
+{
+ NMSettingInfinibandPrivate *priv = NM_SETTING_INFINIBAND_GET_PRIVATE (object);
+
+ g_free (priv->transport_mode);
+ if (priv->mac_address)
+ g_byte_array_free (priv->mac_address, TRUE);
+ g_free (priv->parent);
+ g_free (priv->virtual_iface_name);
+
+ G_OBJECT_CLASS (nm_setting_infiniband_parent_class)->finalize (object);
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ NMSettingInfinibandPrivate *priv = NM_SETTING_INFINIBAND_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_MAC_ADDRESS:
+ if (priv->mac_address)
+ g_byte_array_free (priv->mac_address, TRUE);
+ priv->mac_address = g_value_dup_boxed (value);
+ break;
+ case PROP_MTU:
+ priv->mtu = g_value_get_uint (value);
+ break;
+ case PROP_TRANSPORT_MODE:
+ g_free (priv->transport_mode);
+ priv->transport_mode = g_value_dup_string (value);
+ break;
+ case PROP_P_KEY:
+ priv->p_key = g_value_get_int (value);
+ g_clear_pointer (&priv->virtual_iface_name, g_free);
+ break;
+ case PROP_PARENT:
+ g_free (priv->parent);
+ priv->parent = g_value_dup_string (value);
+ g_clear_pointer (&priv->virtual_iface_name, g_free);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ NMSettingInfiniband *setting = NM_SETTING_INFINIBAND (object);
+
+ switch (prop_id) {
+ case PROP_MAC_ADDRESS:
+ g_value_set_boxed (value, nm_setting_infiniband_get_mac_address (setting));
+ break;
+ case PROP_MTU:
+ g_value_set_uint (value, nm_setting_infiniband_get_mtu (setting));
+ break;
+ case PROP_TRANSPORT_MODE:
+ g_value_set_string (value, nm_setting_infiniband_get_transport_mode (setting));
+ break;
+ case PROP_P_KEY:
+ g_value_set_int (value, nm_setting_infiniband_get_p_key (setting));
+ break;
+ case PROP_PARENT:
+ g_value_set_string (value, nm_setting_infiniband_get_parent (setting));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nm_setting_infiniband_class_init (NMSettingInfinibandClass *setting_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
+ NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+
+ g_type_class_add_private (setting_class, sizeof (NMSettingInfinibandPrivate));
+
+ /* virtual methods */
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+ object_class->finalize = finalize;
+
+ parent_class->verify = verify;
+ parent_class->get_virtual_iface_name = get_virtual_iface_name;
+
+ /* Properties */
+ /**
+ * NMSettingInfiniband:mac-address:
+ *
+ * If specified, this connection will only apply to the IPoIB device whose
+ * permanent MAC address matches. This property does not change the MAC
+ * address of the device (i.e. MAC spoofing).
+ **/
+ g_object_class_install_property
+ (object_class, PROP_MAC_ADDRESS,
+ _nm_param_spec_specialized (NM_SETTING_INFINIBAND_MAC_ADDRESS, "", "",
+ DBUS_TYPE_G_UCHAR_ARRAY,
+ G_PARAM_READWRITE |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingInfiniband:mtu:
+ *
+ * If non-zero, only transmit packets of the specified size or smaller,
+ * breaking larger packets up into multiple frames.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_MTU,
+ g_param_spec_uint (NM_SETTING_INFINIBAND_MTU, "", "",
+ 0, G_MAXUINT32, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ NM_SETTING_PARAM_FUZZY_IGNORE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingInfiniband:transport-mode:
+ *
+ * The IP-over-InfiniBand transport mode. Either "datagram" or
+ * "connected".
+ **/
+ g_object_class_install_property
+ (object_class, PROP_TRANSPORT_MODE,
+ g_param_spec_string (NM_SETTING_INFINIBAND_TRANSPORT_MODE, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingInfiniband:p-key:
+ *
+ * The InfiniBand P_Key to use for this device. A value of -1 means to use
+ * the default P_Key (aka "the P_Key at index 0"). Otherwise it is a 16-bit
+ * unsigned integer, whose high bit is set if it is a "full membership"
+ * P_Key.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_P_KEY,
+ g_param_spec_int (NM_SETTING_INFINIBAND_P_KEY, "", "",
+ -1, 0xFFFF, -1,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingInfiniband:parent:
+ *
+ * The interface name of the parent device of this device. Normally %NULL,
+ * but if the #NMSettingInfiniband:p_key property is set, then you must
+ * specify the base device by setting either this property or
+ * #NMSettingInfiniband:mac-address.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PARENT,
+ g_param_spec_string (NM_SETTING_INFINIBAND_PARENT, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+
+}
diff --git a/libnm-core/nm-setting-infiniband.h b/libnm-core/nm-setting-infiniband.h
new file mode 100644
index 0000000000..dff3845036
--- /dev/null
+++ b/libnm-core/nm-setting-infiniband.h
@@ -0,0 +1,85 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2011 Red Hat, Inc.
+ */
+
+#ifndef NM_SETTING_INFINIBAND_H
+#define NM_SETTING_INFINIBAND_H
+
+#include <nm-setting.h>
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_SETTING_INFINIBAND (nm_setting_infiniband_get_type ())
+#define NM_SETTING_INFINIBAND(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_INFINIBAND, NMSettingInfiniband))
+#define NM_SETTING_INFINIBAND_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_INFINIBAND, NMSettingInfinibandClass))
+#define NM_IS_SETTING_INFINIBAND(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_INFINIBAND))
+#define NM_IS_SETTING_INFINIBAND_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_INFINIBAND))
+#define NM_SETTING_INFINIBAND_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_INFINIBAND, NMSettingInfinibandClass))
+
+#define NM_SETTING_INFINIBAND_SETTING_NAME "infiniband"
+
+/**
+ * NMSettingInfinibandError:
+ * @NM_SETTING_INFINIBAND_ERROR_UNKNOWN: unknown or unclassified error
+ * @NM_SETTING_INFINIBAND_ERROR_INVALID_PROPERTY: the property was invalid
+ * @NM_SETTING_INFINIBAND_ERROR_MISSING_PROPERTY: the property was missing and is
+ * required
+ */
+typedef enum {
+ NM_SETTING_INFINIBAND_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/
+ NM_SETTING_INFINIBAND_ERROR_INVALID_PROPERTY, /*< nick=InvalidProperty >*/
+ NM_SETTING_INFINIBAND_ERROR_MISSING_PROPERTY /*< nick=MissingProperty >*/
+} NMSettingInfinibandError;
+
+#define NM_SETTING_INFINIBAND_ERROR nm_setting_infiniband_error_quark ()
+GQuark nm_setting_infiniband_error_quark (void);
+
+#define NM_SETTING_INFINIBAND_MAC_ADDRESS "mac-address"
+#define NM_SETTING_INFINIBAND_MTU "mtu"
+#define NM_SETTING_INFINIBAND_TRANSPORT_MODE "transport-mode"
+#define NM_SETTING_INFINIBAND_P_KEY "p-key"
+#define NM_SETTING_INFINIBAND_PARENT "parent"
+
+typedef struct {
+ NMSetting parent;
+} NMSettingInfiniband;
+
+typedef struct {
+ NMSettingClass parent;
+
+ /* Padding for future expansion */
+ void (*_reserved1) (void);
+ void (*_reserved2) (void);
+ void (*_reserved3) (void);
+ void (*_reserved4) (void);
+} NMSettingInfinibandClass;
+
+GType nm_setting_infiniband_get_type (void);
+
+NMSetting * nm_setting_infiniband_new (void);
+const GByteArray *nm_setting_infiniband_get_mac_address (NMSettingInfiniband *setting);
+guint32 nm_setting_infiniband_get_mtu (NMSettingInfiniband *setting);
+const char * nm_setting_infiniband_get_transport_mode (NMSettingInfiniband *setting);
+int nm_setting_infiniband_get_p_key (NMSettingInfiniband *setting);
+const char * nm_setting_infiniband_get_parent (NMSettingInfiniband *setting);
+
+G_END_DECLS
+
+#endif /* NM_SETTING_INFINIBAND_H */
diff --git a/libnm-core/nm-setting-ip4-config.c b/libnm-core/nm-setting-ip4-config.c
new file mode 100644
index 0000000000..bc2ef8731a
--- /dev/null
+++ b/libnm-core/nm-setting-ip4-config.c
@@ -0,0 +1,1895 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2014 Red Hat, Inc.
+ * Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#include <string.h>
+#include <dbus/dbus-glib.h>
+#include <glib/gi18n.h>
+
+#include "nm-setting-ip4-config.h"
+#include "nm-param-spec-specialized.h"
+#include "nm-utils.h"
+#include "nm-dbus-glib-types.h"
+#include "nm-glib-compat.h"
+#include "nm-setting-private.h"
+
+
+/**
+ * SECTION:nm-setting-ip4-config
+ * @short_description: Describes IPv4 addressing, routing, and name service properties
+ * @include: nm-setting-ip4-config.h
+ *
+ * The #NMSettingIP4Config object is a #NMSetting subclass that describes
+ * properties related to IPv4 addressing, routing, and Domain Name Service
+ **/
+
+/**
+ * nm_setting_ip4_config_error_quark:
+ *
+ * Registers an error quark for #NMSettingIP4Config if necessary.
+ *
+ * Returns: the error quark used for #NMSettingIP4Config errors.
+ **/
+GQuark
+nm_setting_ip4_config_error_quark (void)
+{
+ static GQuark quark;
+
+ if (G_UNLIKELY (!quark))
+ quark = g_quark_from_static_string ("nm-setting-ip4-config-error-quark");
+ return quark;
+}
+
+G_DEFINE_BOXED_TYPE (NMIP4Address, nm_ip4_address, nm_ip4_address_dup, nm_ip4_address_unref)
+G_DEFINE_BOXED_TYPE (NMIP4Route, nm_ip4_route, nm_ip4_route_dup, nm_ip4_route_unref)
+
+G_DEFINE_TYPE_WITH_CODE (NMSettingIP4Config, nm_setting_ip4_config, NM_TYPE_SETTING,
+ _nm_register_setting (NM_SETTING_IP4_CONFIG_SETTING_NAME,
+ g_define_type_id,
+ 4,
+ NM_SETTING_IP4_CONFIG_ERROR))
+NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_IP4_CONFIG)
+
+#define NM_SETTING_IP4_CONFIG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_IP4_CONFIG, NMSettingIP4ConfigPrivate))
+
+typedef struct {
+ char *method;
+ GArray *dns; /* array of guint32; elements in network byte order */
+ GSList *dns_search; /* list of strings */
+ GSList *addresses; /* array of NMIP4Address */
+ GSList *address_labels; /* list of strings */
+ GSList *routes; /* array of NMIP4Route */
+ gboolean ignore_auto_routes;
+ gboolean ignore_auto_dns;
+ char *dhcp_client_id;
+ gboolean dhcp_send_hostname;
+ char *dhcp_hostname;
+ gboolean never_default;
+ gboolean may_fail;
+} NMSettingIP4ConfigPrivate;
+
+enum {
+ PROP_0,
+ PROP_METHOD,
+ PROP_DNS,
+ PROP_DNS_SEARCH,
+ PROP_ADDRESSES,
+ PROP_ADDRESS_LABELS,
+ PROP_ROUTES,
+ PROP_IGNORE_AUTO_ROUTES,
+ PROP_IGNORE_AUTO_DNS,
+ PROP_DHCP_CLIENT_ID,
+ PROP_DHCP_SEND_HOSTNAME,
+ PROP_DHCP_HOSTNAME,
+ PROP_NEVER_DEFAULT,
+ PROP_MAY_FAIL,
+
+ LAST_PROP
+};
+
+/**
+ * nm_setting_ip4_config_new:
+ *
+ * Creates a new #NMSettingIP4Config object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingIP4Config object
+ **/
+NMSetting *
+nm_setting_ip4_config_new (void)
+{
+ return (NMSetting *) g_object_new (NM_TYPE_SETTING_IP4_CONFIG, NULL);
+}
+
+/**
+ * nm_setting_ip4_config_get_method:
+ * @setting: the #NMSettingIP4Config
+ *
+ * Returns: the #NMSettingIP4Config:method property of the setting
+ **/
+const char *
+nm_setting_ip4_config_get_method (NMSettingIP4Config *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), NULL);
+
+ return NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting)->method;
+}
+
+/**
+ * nm_setting_ip4_config_get_num_dns:
+ * @setting: the #NMSettingIP4Config
+ *
+ * Returns: the number of configured DNS servers
+ **/
+guint32
+nm_setting_ip4_config_get_num_dns (NMSettingIP4Config *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), 0);
+
+ return NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting)->dns->len;
+}
+
+/**
+ * nm_setting_ip4_config_get_dns:
+ * @setting: the #NMSettingIP4Config
+ * @i: index number of the DNS server to return
+ *
+ * Returns: the IPv4 address (network byte order) of the DNS server at index
+ * @i
+ **/
+guint32
+nm_setting_ip4_config_get_dns (NMSettingIP4Config *setting, guint32 i)
+{
+ NMSettingIP4ConfigPrivate *priv;
+
+ g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), 0);
+
+ priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting);
+ g_return_val_if_fail (i <= priv->dns->len, 0);
+
+ return g_array_index (priv->dns, guint32, i);
+}
+
+/**
+ * nm_setting_ip4_config_add_dns:
+ * @setting: the #NMSettingIP4Config
+ * @dns: the IPv4 address (network byte order) of the DNS server to add
+ *
+ * Adds a new DNS server to the setting.
+ *
+ * Returns: %TRUE if the DNS server was added; %FALSE if the server was already
+ * known
+ **/
+gboolean
+nm_setting_ip4_config_add_dns (NMSettingIP4Config *setting, guint32 dns)
+{
+ NMSettingIP4ConfigPrivate *priv;
+ int i;
+
+ g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), FALSE);
+
+ priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting);
+ for (i = 0; i < priv->dns->len; i++) {
+ if (dns == g_array_index (priv->dns, guint32, i))
+ return FALSE;
+ }
+
+ g_array_append_val (priv->dns, dns);
+ g_object_notify (G_OBJECT (setting), NM_SETTING_IP4_CONFIG_DNS);
+ return TRUE;
+}
+
+/**
+ * nm_setting_ip4_config_remove_dns:
+ * @setting: the #NMSettingIP4Config
+ * @i: index number of the DNS server to remove
+ *
+ * Removes the DNS server at index @i.
+ **/
+void
+nm_setting_ip4_config_remove_dns (NMSettingIP4Config *setting, guint32 i)
+{
+ NMSettingIP4ConfigPrivate *priv;
+
+ g_return_if_fail (NM_IS_SETTING_IP4_CONFIG (setting));
+
+ priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting);
+ g_return_if_fail (i <= priv->dns->len);
+
+ g_array_remove_index (priv->dns, i);
+ g_object_notify (G_OBJECT (setting), NM_SETTING_IP4_CONFIG_DNS);
+}
+
+/**
+ * nm_setting_ip4_config_remove_dns_by_value:
+ * @setting: the #NMSettingIP4Config
+ * @dns: the DNS server to remove
+ *
+ * Removes the DNS server @dns.
+ *
+ * Returns: %TRUE if the DNS server was found and removed; %FALSE if it was not.
+ * domain was already known
+ *
+ * Since: 0.9.10
+ **/
+gboolean
+nm_setting_ip4_config_remove_dns_by_value (NMSettingIP4Config *setting, guint32 dns)
+{
+ NMSettingIP4ConfigPrivate *priv;
+ int i;
+
+ g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), FALSE);
+
+ priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting);
+ for (i = 0; i < priv->dns->len; i++) {
+ if (dns == g_array_index (priv->dns, guint32, i)) {
+ g_array_remove_index (priv->dns, i);
+ g_object_notify (G_OBJECT (setting), NM_SETTING_IP4_CONFIG_DNS);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/**
+ * nm_setting_ip4_config_clear_dns:
+ * @setting: the #NMSettingIP4Config
+ *
+ * Removes all configured DNS servers.
+ **/
+void
+nm_setting_ip4_config_clear_dns (NMSettingIP4Config *setting)
+{
+ NMSettingIP4ConfigPrivate *priv;
+
+ g_return_if_fail (NM_IS_SETTING_IP4_CONFIG (setting));
+
+ priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting);
+ g_array_remove_range (priv->dns, 0, priv->dns->len);
+ g_object_notify (G_OBJECT (setting), NM_SETTING_IP4_CONFIG_DNS);
+}
+
+/**
+ * nm_setting_ip4_config_get_num_dns_searches:
+ * @setting: the #NMSettingIP4Config
+ *
+ * Returns: the number of configured DNS search domains
+ **/
+guint32
+nm_setting_ip4_config_get_num_dns_searches (NMSettingIP4Config *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), 0);
+
+ return g_slist_length (NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting)->dns_search);
+}
+
+/**
+ * nm_setting_ip4_config_get_dns_search:
+ * @setting: the #NMSettingIP4Config
+ * @i: index number of the DNS search domain to return
+ *
+ * Returns: the DNS search domain at index @i
+ **/
+const char *
+nm_setting_ip4_config_get_dns_search (NMSettingIP4Config *setting, guint32 i)
+{
+ NMSettingIP4ConfigPrivate *priv;
+
+ g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), NULL);
+
+ priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting);
+ g_return_val_if_fail (i <= g_slist_length (priv->dns_search), NULL);
+
+ return (const char *) g_slist_nth_data (priv->dns_search, i);
+}
+
+/**
+ * nm_setting_ip4_config_add_dns_search:
+ * @setting: the #NMSettingIP4Config
+ * @dns_search: the search domain to add
+ *
+ * Adds a new DNS search domain to the setting.
+ *
+ * Returns: %TRUE if the DNS search domain was added; %FALSE if the search
+ * domain was already known
+ **/
+gboolean
+nm_setting_ip4_config_add_dns_search (NMSettingIP4Config *setting,
+ const char *dns_search)
+{
+ NMSettingIP4ConfigPrivate *priv;
+ GSList *iter;
+
+ g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), FALSE);
+ g_return_val_if_fail (dns_search != NULL, FALSE);
+ g_return_val_if_fail (dns_search[0] != '\0', FALSE);
+
+ priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting);
+ for (iter = priv->dns_search; iter; iter = g_slist_next (iter)) {
+ if (!strcmp (dns_search, (char *) iter->data))
+ return FALSE;
+ }
+
+ priv->dns_search = g_slist_append (priv->dns_search, g_strdup (dns_search));
+ g_object_notify (G_OBJECT (setting), NM_SETTING_IP4_CONFIG_DNS_SEARCH);
+ return TRUE;
+}
+
+/**
+ * nm_setting_ip4_config_remove_dns_search:
+ * @setting: the #NMSettingIP4Config
+ * @i: index number of the DNS search domain
+ *
+ * Removes the DNS search domain at index @i.
+ **/
+void
+nm_setting_ip4_config_remove_dns_search (NMSettingIP4Config *setting, guint32 i)
+{
+ NMSettingIP4ConfigPrivate *priv;
+ GSList *elt;
+
+ g_return_if_fail (NM_IS_SETTING_IP4_CONFIG (setting));
+
+ priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting);
+ elt = g_slist_nth (priv->dns_search, i);
+ g_return_if_fail (elt != NULL);
+
+ g_free (elt->data);
+ priv->dns_search = g_slist_delete_link (priv->dns_search, elt);
+ g_object_notify (G_OBJECT (setting), NM_SETTING_IP4_CONFIG_DNS_SEARCH);
+}
+
+/**
+ * nm_setting_ip4_config_remove_dns_search_by_value:
+ * @setting: the #NMSettingIP4Config
+ * @dns_search: the search domain to remove
+ *
+ * Removes the DNS search domain @dns_search.
+ *
+ * Returns: %TRUE if the DNS search domain was found and removed; %FALSE if it was not.
+ *
+ * Since 0.9.10
+ **/
+gboolean
+nm_setting_ip4_config_remove_dns_search_by_value (NMSettingIP4Config *setting,
+ const char *dns_search)
+{
+ NMSettingIP4ConfigPrivate *priv;
+ GSList *iter;
+
+ g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), FALSE);
+ g_return_val_if_fail (dns_search != NULL, FALSE);
+ g_return_val_if_fail (dns_search[0] != '\0', FALSE);
+
+ priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting);
+ for (iter = priv->dns_search; iter; iter = g_slist_next (iter)) {
+ if (!strcmp (dns_search, (char *) iter->data)) {
+ priv->dns_search = g_slist_delete_link (priv->dns_search, iter);
+ g_object_notify (G_OBJECT (setting), NM_SETTING_IP4_CONFIG_DNS_SEARCH);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/**
+ * nm_setting_ip4_config_clear_dns_searches:
+ * @setting: the #NMSettingIP4Config
+ *
+ * Removes all configured DNS search domains.
+ **/
+void
+nm_setting_ip4_config_clear_dns_searches (NMSettingIP4Config *setting)
+{
+ g_return_if_fail (NM_IS_SETTING_IP4_CONFIG (setting));
+
+ g_slist_free_full (NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting)->dns_search, g_free);
+ NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting)->dns_search = NULL;
+ g_object_notify (G_OBJECT (setting), NM_SETTING_IP4_CONFIG_DNS_SEARCH);
+}
+
+/**
+ * nm_setting_ip4_config_get_num_addresses:
+ * @setting: the #NMSettingIP4Config
+ *
+ * Returns: the number of configured addresses
+ **/
+guint32
+nm_setting_ip4_config_get_num_addresses (NMSettingIP4Config *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), 0);
+
+ return g_slist_length (NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting)->addresses);
+}
+
+/**
+ * nm_setting_ip4_config_get_address:
+ * @setting: the #NMSettingIP4Config
+ * @i: index number of the address to return
+ *
+ * Returns: the address at index @i
+ **/
+NMIP4Address *
+nm_setting_ip4_config_get_address (NMSettingIP4Config *setting, guint32 i)
+{
+ NMSettingIP4ConfigPrivate *priv;
+
+ g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), NULL);
+
+ priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting);
+ g_return_val_if_fail (i <= g_slist_length (priv->addresses), NULL);
+
+ return (NMIP4Address *) g_slist_nth_data (priv->addresses, i);
+}
+
+const char *
+nm_setting_ip4_config_get_address_label (NMSettingIP4Config *setting, guint32 i)
+{
+ NMSettingIP4ConfigPrivate *priv;
+
+ g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), NULL);
+
+ priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting);
+ g_return_val_if_fail (i <= g_slist_length (priv->address_labels), NULL);
+
+ return (const char *) g_slist_nth_data (priv->address_labels, i);
+}
+
+/**
+ * nm_setting_ip4_config_add_address:
+ * @setting: the #NMSettingIP4Config
+ * @address: the new address to add
+ *
+ * Adds a new IPv4 address and associated information to the setting. The
+ * given address is duplicated internally and is not changed by this function.
+ *
+ * Returns: %TRUE if the address was added; %FALSE if the address was already
+ * known.
+ **/
+gboolean
+nm_setting_ip4_config_add_address (NMSettingIP4Config *setting,
+ NMIP4Address *address)
+{
+ return nm_setting_ip4_config_add_address_with_label (setting, address, NULL);
+}
+
+gboolean
+nm_setting_ip4_config_add_address_with_label (NMSettingIP4Config *setting,
+ NMIP4Address *address,
+ const char *label)
+{
+ NMSettingIP4ConfigPrivate *priv;
+ NMIP4Address *copy;
+ GSList *iter;
+
+ g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), FALSE);
+ g_return_val_if_fail (address != NULL, FALSE);
+
+ priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting);
+ for (iter = priv->addresses; iter; iter = g_slist_next (iter)) {
+ if (nm_ip4_address_compare ((NMIP4Address *) iter->data, address))
+ return FALSE;
+ }
+
+ copy = nm_ip4_address_dup (address);
+ priv->addresses = g_slist_append (priv->addresses, copy);
+ priv->address_labels = g_slist_append (priv->address_labels, g_strdup (label));
+
+ g_object_notify (G_OBJECT (setting), NM_SETTING_IP4_CONFIG_ADDRESSES);
+ return TRUE;
+}
+
+/**
+ * nm_setting_ip4_config_remove_address:
+ * @setting: the #NMSettingIP4Config
+ * @i: index number of the address to remove
+ *
+ * Removes the address at index @i.
+ **/
+void
+nm_setting_ip4_config_remove_address (NMSettingIP4Config *setting, guint32 i)
+{
+ NMSettingIP4ConfigPrivate *priv;
+ GSList *addr, *label;
+
+ g_return_if_fail (NM_IS_SETTING_IP4_CONFIG (setting));
+
+ priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting);
+ addr = g_slist_nth (priv->addresses, i);
+ label = g_slist_nth (priv->address_labels, i);
+ g_return_if_fail (addr != NULL && label != NULL);
+
+ nm_ip4_address_unref ((NMIP4Address *) addr->data);
+ priv->addresses = g_slist_delete_link (priv->addresses, addr);
+ if (label->data)
+ g_free (label->data);
+ priv->address_labels = g_slist_delete_link (priv->address_labels, label);
+
+ g_object_notify (G_OBJECT (setting), NM_SETTING_IP4_CONFIG_ADDRESSES);
+}
+
+/**
+ * nm_setting_ip4_config_remove_address_by_value:
+ * @setting: the #NMSettingIP4Config
+ * @address: the IP address to remove
+ *
+ * Removes the address @address.
+ *
+ * Returns: %TRUE if the address was found and removed; %FALSE if it was not.
+ *
+ * Since: 0.9.10
+ **/
+gboolean
+nm_setting_ip4_config_remove_address_by_value (NMSettingIP4Config *setting,
+ NMIP4Address *address)
+{
+ NMSettingIP4ConfigPrivate *priv;
+ GSList *iter;
+
+ g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), FALSE);
+ g_return_val_if_fail (address != NULL, FALSE);
+
+ priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting);
+ for (iter = priv->addresses; iter; iter = g_slist_next (iter)) {
+ if (nm_ip4_address_compare ((NMIP4Address *) iter->data, address)) {
+ nm_ip4_address_unref ((NMIP4Address *) iter->data);
+ priv->addresses = g_slist_delete_link (priv->addresses, iter);
+ g_object_notify (G_OBJECT (setting), NM_SETTING_IP4_CONFIG_ADDRESSES);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/**
+ * nm_setting_ip4_config_clear_addresses:
+ * @setting: the #NMSettingIP4Config
+ *
+ * Removes all configured addresses.
+ **/
+void
+nm_setting_ip4_config_clear_addresses (NMSettingIP4Config *setting)
+{
+ NMSettingIP4ConfigPrivate *priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting);
+
+ g_return_if_fail (NM_IS_SETTING_IP4_CONFIG (setting));
+
+ g_slist_free_full (priv->addresses, (GDestroyNotify) nm_ip4_address_unref);
+ priv->addresses = NULL;
+ g_slist_free_full (priv->address_labels, g_free);
+ priv->address_labels = NULL;
+ g_object_notify (G_OBJECT (setting), NM_SETTING_IP4_CONFIG_ADDRESSES);
+}
+
+/**
+ * nm_setting_ip4_config_get_num_routes:
+ * @setting: the #NMSettingIP4Config
+ *
+ * Returns: the number of configured routes
+ **/
+guint32
+nm_setting_ip4_config_get_num_routes (NMSettingIP4Config *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), 0);
+
+ return g_slist_length (NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting)->routes);
+}
+
+/**
+ * nm_setting_ip4_config_get_route:
+ * @setting: the #NMSettingIP4Config
+ * @i: index number of the route to return
+ *
+ * Returns: the route at index @i
+ **/
+NMIP4Route *
+nm_setting_ip4_config_get_route (NMSettingIP4Config *setting, guint32 i)
+{
+ NMSettingIP4ConfigPrivate *priv;
+
+ g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), NULL);
+
+ priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting);
+ g_return_val_if_fail (i <= g_slist_length (priv->routes), NULL);
+
+ return (NMIP4Route *) g_slist_nth_data (priv->routes, i);
+}
+
+/**
+ * nm_setting_ip4_config_add_route:
+ * @setting: the #NMSettingIP4Config
+ * @route: the route to add
+ *
+ * Adds a new IPv4 route and associated information to the setting. The
+ * given route is duplicated internally and is not changed by this function.
+ *
+ * Returns: %TRUE if the route was added; %FALSE if the route was already known.
+ **/
+gboolean
+nm_setting_ip4_config_add_route (NMSettingIP4Config *setting,
+ NMIP4Route *route)
+{
+ NMSettingIP4ConfigPrivate *priv;
+ NMIP4Route *copy;
+ GSList *iter;
+
+ g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), FALSE);
+ g_return_val_if_fail (route != NULL, FALSE);
+
+ priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting);
+ for (iter = priv->routes; iter; iter = g_slist_next (iter)) {
+ if (nm_ip4_route_compare ((NMIP4Route *) iter->data, route))
+ return FALSE;
+ }
+
+ copy = nm_ip4_route_dup (route);
+ priv->routes = g_slist_append (priv->routes, copy);
+ g_object_notify (G_OBJECT (setting), NM_SETTING_IP4_CONFIG_ROUTES);
+ return TRUE;
+}
+
+/**
+ * nm_setting_ip4_config_remove_route:
+ * @setting: the #NMSettingIP4Config
+ * @i: index number of the route
+ *
+ * Removes the route at index @i.
+ **/
+void
+nm_setting_ip4_config_remove_route (NMSettingIP4Config *setting, guint32 i)
+{
+ NMSettingIP4ConfigPrivate *priv;
+ GSList *elt;
+
+ g_return_if_fail (NM_IS_SETTING_IP4_CONFIG (setting));
+
+ priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting);
+ elt = g_slist_nth (priv->routes, i);
+ g_return_if_fail (elt != NULL);
+
+ nm_ip4_route_unref ((NMIP4Route *) elt->data);
+ priv->routes = g_slist_delete_link (priv->routes, elt);
+ g_object_notify (G_OBJECT (setting), NM_SETTING_IP4_CONFIG_ROUTES);
+}
+
+/**
+ * nm_setting_ip4_config_remove_route_by_value:
+ * @setting: the #NMSettingIP4Config
+ * @route: the route to remove
+ *
+ * Removes the route @route.
+ *
+ * Returns: %TRUE if the route was found and removed; %FALSE if it was not.
+ *
+ * Since: 0.9.10
+ **/
+gboolean
+nm_setting_ip4_config_remove_route_by_value (NMSettingIP4Config *setting,
+ NMIP4Route *route)
+{
+ NMSettingIP4ConfigPrivate *priv;
+ GSList *iter;
+
+ g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), FALSE);
+ g_return_val_if_fail (route != NULL, FALSE);
+
+ priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting);
+ for (iter = priv->routes; iter; iter = g_slist_next (iter)) {
+ if (nm_ip4_route_compare ((NMIP4Route *) iter->data, route)) {
+ nm_ip4_route_unref ((NMIP4Route *) iter->data);
+ priv->routes = g_slist_delete_link (priv->routes, iter);
+ g_object_notify (G_OBJECT (setting), NM_SETTING_IP4_CONFIG_ROUTES);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/**
+ * nm_setting_ip4_config_clear_routes:
+ * @setting: the #NMSettingIP4Config
+ *
+ * Removes all configured routes.
+ **/
+void
+nm_setting_ip4_config_clear_routes (NMSettingIP4Config *setting)
+{
+ NMSettingIP4ConfigPrivate *priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting);
+
+ g_return_if_fail (NM_IS_SETTING_IP4_CONFIG (setting));
+
+ g_slist_free_full (priv->routes, (GDestroyNotify) nm_ip4_route_unref);
+ priv->routes = NULL;
+ g_object_notify (G_OBJECT (setting), NM_SETTING_IP4_CONFIG_ROUTES);
+}
+
+/**
+ * nm_setting_ip4_config_get_ignore_auto_routes:
+ * @setting: the #NMSettingIP4Config
+ *
+ * Returns the value contained in the #NMSettingIP4Config:ignore-auto-routes
+ * property.
+ *
+ * Returns: %TRUE if automatically configured (ie via DHCP) routes should be
+ * ignored.
+ **/
+gboolean
+nm_setting_ip4_config_get_ignore_auto_routes (NMSettingIP4Config *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), FALSE);
+
+ return NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting)->ignore_auto_routes;
+}
+
+/**
+ * nm_setting_ip4_config_get_ignore_auto_dns:
+ * @setting: the #NMSettingIP4Config
+ *
+ * Returns the value contained in the #NMSettingIP4Config:ignore-auto-dns
+ * property.
+ *
+ * Returns: %TRUE if automatically configured (ie via DHCP) DNS information
+ * should be ignored.
+ **/
+gboolean
+nm_setting_ip4_config_get_ignore_auto_dns (NMSettingIP4Config *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), FALSE);
+
+ return NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting)->ignore_auto_dns;
+}
+
+/**
+ * nm_setting_ip4_config_get_dhcp_client_id:
+ * @setting: the #NMSettingIP4Config
+ *
+ * Returns the value contained in the #NMSettingIP4Config:dhcp-client-id
+ * property.
+ *
+ * Returns: the configured Client ID to send to the DHCP server when requesting
+ * addresses via DHCP.
+ **/
+const char *
+nm_setting_ip4_config_get_dhcp_client_id (NMSettingIP4Config *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), NULL);
+
+ return NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting)->dhcp_client_id;
+}
+
+/**
+ * nm_setting_ip4_config_get_dhcp_send_hostname:
+ * @setting: the #NMSettingIP4Config
+ *
+ * Returns the value contained in the #NMSettingIP4Config:dhcp-send-hostname
+ * property.
+ *
+ * Returns: %TRUE if NetworkManager should send the machine hostname to the
+ * DHCP server when requesting addresses to allow the server to automatically
+ * update DNS information for this machine.
+ **/
+gboolean
+nm_setting_ip4_config_get_dhcp_send_hostname (NMSettingIP4Config *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), FALSE);
+
+ return NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting)->dhcp_send_hostname;
+}
+
+/**
+ * nm_setting_ip4_config_get_dhcp_hostname:
+ * @setting: the #NMSettingIP4Config
+ *
+ * Returns the value contained in the #NMSettingIP4Config:dhcp-hostname
+ * property.
+ *
+ * Returns: the configured hostname to send to the DHCP server
+ **/
+const char *
+nm_setting_ip4_config_get_dhcp_hostname (NMSettingIP4Config *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), NULL);
+
+ return NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting)->dhcp_hostname;
+}
+
+/**
+ * nm_setting_ip4_config_get_never_default:
+ * @setting: the #NMSettingIP4Config
+ *
+ * Returns the value contained in the #NMSettingIP4Config:never-default
+ * property.
+ *
+ * Returns: %TRUE if this connection should never be the default connection
+ * for IPv4 addressing
+ **/
+gboolean
+nm_setting_ip4_config_get_never_default (NMSettingIP4Config *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), FALSE);
+
+ return NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting)->never_default;
+}
+
+/**
+ * nm_setting_ip4_config_get_may_fail:
+ * @setting: the #NMSettingIP4Config
+ *
+ * Returns the value contained in the #NMSettingIP4Config:may-fail
+ * property.
+ *
+ * Returns: %TRUE if this connection doesn't require IPv4 addressing to complete
+ * for the connection to succeed.
+ **/
+gboolean
+nm_setting_ip4_config_get_may_fail (NMSettingIP4Config *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), FALSE);
+
+ return NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting)->may_fail;
+}
+
+static gboolean
+verify_label (const char *label)
+{
+ const char *p;
+ char *iface;
+
+ p = strchr (label, ':');
+ if (!p)
+ return FALSE;
+ iface = g_strndup (label, p - label);
+ if (!nm_utils_iface_valid_name (iface)) {
+ g_free (iface);
+ return FALSE;
+ }
+ g_free (iface);
+
+ for (p++; *p; p++) {
+ if (!g_ascii_isalnum (*p) && *p != '_')
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+verify (NMSetting *setting, GSList *all_settings, GError **error)
+{
+ NMSettingIP4ConfigPrivate *priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting);
+ GSList *iter, *l_iter;
+ int i;
+
+ if (!priv->method) {
+ g_set_error_literal (error,
+ NM_SETTING_IP4_CONFIG_ERROR,
+ NM_SETTING_IP4_CONFIG_ERROR_MISSING_PROPERTY,
+ _("property is missing"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_IP4_CONFIG_SETTING_NAME, NM_SETTING_IP4_CONFIG_METHOD);
+ return FALSE;
+ }
+
+ if (!strcmp (priv->method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) {
+ if (!priv->addresses) {
+ g_set_error (error,
+ NM_SETTING_IP4_CONFIG_ERROR,
+ NM_SETTING_IP4_CONFIG_ERROR_MISSING_PROPERTY,
+ _("this property cannot be empty for '%s=%s'"),
+ NM_SETTING_IP4_CONFIG_METHOD, priv->method);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_IP4_CONFIG_SETTING_NAME, NM_SETTING_IP4_CONFIG_ADDRESSES);
+ return FALSE;
+ }
+ } else if ( !strcmp (priv->method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL)
+ || !strcmp (priv->method, NM_SETTING_IP4_CONFIG_METHOD_SHARED)
+ || !strcmp (priv->method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED)) {
+ if (priv->dns && priv->dns->len) {
+ g_set_error (error,
+ NM_SETTING_IP4_CONFIG_ERROR,
+ NM_SETTING_IP4_CONFIG_ERROR_NOT_ALLOWED_FOR_METHOD,
+ _("this property is not allowed for '%s=%s'"),
+ NM_SETTING_IP4_CONFIG_METHOD, priv->method);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_IP4_CONFIG_SETTING_NAME, NM_SETTING_IP4_CONFIG_DNS);
+ return FALSE;
+ }
+
+ if (g_slist_length (priv->dns_search)) {
+ g_set_error (error,
+ NM_SETTING_IP4_CONFIG_ERROR,
+ NM_SETTING_IP4_CONFIG_ERROR_NOT_ALLOWED_FOR_METHOD,
+ _("this property is not allowed for '%s=%s'"),
+ NM_SETTING_IP4_CONFIG_METHOD, priv->method);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_IP4_CONFIG_SETTING_NAME, NM_SETTING_IP4_CONFIG_DNS_SEARCH);
+ return FALSE;
+ }
+
+ /* Shared allows IP addresses; link-local and disabled do not */
+ if (strcmp (priv->method, NM_SETTING_IP4_CONFIG_METHOD_SHARED) != 0) {
+ if (g_slist_length (priv->addresses)) {
+ g_set_error (error,
+ NM_SETTING_IP4_CONFIG_ERROR,
+ NM_SETTING_IP4_CONFIG_ERROR_NOT_ALLOWED_FOR_METHOD,
+ _("this property is not allowed for '%s=%s'"),
+ NM_SETTING_IP4_CONFIG_METHOD, priv->method);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_IP4_CONFIG_SETTING_NAME, NM_SETTING_IP4_CONFIG_ADDRESSES);
+ return FALSE;
+ }
+ }
+ } else if (!strcmp (priv->method, NM_SETTING_IP4_CONFIG_METHOD_AUTO)) {
+ /* nothing to do */
+ } else {
+ g_set_error_literal (error,
+ NM_SETTING_IP4_CONFIG_ERROR,
+ NM_SETTING_IP4_CONFIG_ERROR_INVALID_PROPERTY,
+ _("property is invalid"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_IP4_CONFIG_SETTING_NAME, NM_SETTING_IP4_CONFIG_METHOD);
+ return FALSE;
+ }
+
+ if (priv->dhcp_client_id && !strlen (priv->dhcp_client_id)) {
+ g_set_error_literal (error,
+ NM_SETTING_IP4_CONFIG_ERROR,
+ NM_SETTING_IP4_CONFIG_ERROR_INVALID_PROPERTY,
+ _("property is empty"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_IP4_CONFIG_SETTING_NAME, NM_SETTING_IP4_CONFIG_DHCP_CLIENT_ID);
+ return FALSE;
+ }
+
+ if (priv->dhcp_hostname && !strlen (priv->dhcp_hostname)) {
+ g_set_error_literal (error,
+ NM_SETTING_IP4_CONFIG_ERROR,
+ NM_SETTING_IP4_CONFIG_ERROR_INVALID_PROPERTY,
+ _("property is empty"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_IP4_CONFIG_SETTING_NAME, NM_SETTING_IP4_CONFIG_DHCP_HOSTNAME);
+ return FALSE;
+ }
+
+ /* Validate addresses */
+ for (iter = priv->addresses, l_iter = priv->address_labels, i = 0;
+ iter && l_iter;
+ iter = g_slist_next (iter), l_iter = g_slist_next (l_iter), i++) {
+ NMIP4Address *addr = (NMIP4Address *) iter->data;
+ const char *label = (const char *) l_iter->data;
+ guint32 prefix = nm_ip4_address_get_prefix (addr);
+
+ if (!nm_ip4_address_get_address (addr)) {
+ g_set_error (error,
+ NM_SETTING_IP4_CONFIG_ERROR,
+ NM_SETTING_IP4_CONFIG_ERROR_INVALID_PROPERTY,
+ _("%d. IPv4 address is invalid"),
+ i+1);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_IP4_CONFIG_SETTING_NAME, NM_SETTING_IP4_CONFIG_ADDRESSES);
+ return FALSE;
+ }
+
+ if (!prefix || prefix > 32) {
+ g_set_error (error,
+ NM_SETTING_IP4_CONFIG_ERROR,
+ NM_SETTING_IP4_CONFIG_ERROR_INVALID_PROPERTY,
+ _("%d. IPv4 address has invalid prefix"),
+ i+1);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_IP4_CONFIG_SETTING_NAME, NM_SETTING_IP4_CONFIG_ADDRESSES);
+ return FALSE;
+ }
+
+ if (label && !verify_label (label)) {
+ g_set_error (error,
+ NM_SETTING_IP4_CONFIG_ERROR,
+ NM_SETTING_IP4_CONFIG_ERROR_INVALID_PROPERTY,
+ _("%d. IPv4 address has invalid label '%s'"),
+ i+1, label);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_IP4_CONFIG_SETTING_NAME, "address-labels");
+ return FALSE;
+ }
+ }
+
+ if (iter || l_iter) {
+ g_set_error (error,
+ NM_SETTING_IP4_CONFIG_ERROR,
+ NM_SETTING_IP4_CONFIG_ERROR_INVALID_PROPERTY,
+ _("IPv4 address / label count mismatch (%d vs %d)"),
+ g_slist_length (priv->addresses),
+ g_slist_length (priv->address_labels));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_IP4_CONFIG_SETTING_NAME, "address-labels");
+ return FALSE;
+ }
+
+ /* Validate routes */
+ for (iter = priv->routes, i = 0; iter; iter = g_slist_next (iter), i++) {
+ NMIP4Route *route = (NMIP4Route *) iter->data;
+ guint32 prefix = nm_ip4_route_get_prefix (route);
+
+ if (!nm_ip4_route_get_dest (route)) {
+ g_set_error (error,
+ NM_SETTING_IP4_CONFIG_ERROR,
+ NM_SETTING_IP4_CONFIG_ERROR_INVALID_PROPERTY,
+ _("%d. route is invalid"),
+ i+1);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_IP4_CONFIG_SETTING_NAME, NM_SETTING_IP4_CONFIG_ROUTES);
+ return FALSE;
+ }
+
+ if (!prefix || prefix > 32) {
+ g_set_error (error,
+ NM_SETTING_IP4_CONFIG_ERROR,
+ NM_SETTING_IP4_CONFIG_ERROR_INVALID_PROPERTY,
+ _("%d. route has invalid prefix"),
+ i+1);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_IP4_CONFIG_SETTING_NAME, NM_SETTING_IP4_CONFIG_ROUTES);
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+
+static void
+nm_setting_ip4_config_init (NMSettingIP4Config *setting)
+{
+ NMSettingIP4ConfigPrivate *priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting);
+
+
+ priv->dns = g_array_sized_new (FALSE, TRUE, sizeof (guint32), 3);
+}
+
+static void
+finalize (GObject *object)
+{
+ NMSettingIP4Config *self = NM_SETTING_IP4_CONFIG (object);
+ NMSettingIP4ConfigPrivate *priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (self);
+
+ g_free (priv->method);
+ g_free (priv->dhcp_hostname);
+ g_free (priv->dhcp_client_id);
+
+ g_array_free (priv->dns, TRUE);
+
+ g_slist_free_full (priv->dns_search, g_free);
+ g_slist_free_full (priv->addresses, (GDestroyNotify) nm_ip4_address_unref);
+ g_slist_free_full (priv->address_labels, g_free);
+ g_slist_free_full (priv->routes, (GDestroyNotify) nm_ip4_route_unref);
+
+ G_OBJECT_CLASS (nm_setting_ip4_config_parent_class)->finalize (object);
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ NMSettingIP4Config *setting = NM_SETTING_IP4_CONFIG (object);
+ NMSettingIP4ConfigPrivate *priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting);
+ GSList *iter;
+
+ switch (prop_id) {
+ case PROP_METHOD:
+ g_free (priv->method);
+ priv->method = g_value_dup_string (value);
+ break;
+ case PROP_DNS:
+ g_array_free (priv->dns, TRUE);
+ priv->dns = g_value_dup_boxed (value);
+ if (!priv->dns)
+ priv->dns = g_array_sized_new (FALSE, TRUE, sizeof (guint32), 3);
+ break;
+ case PROP_DNS_SEARCH:
+ g_slist_free_full (priv->dns_search, g_free);
+ priv->dns_search = g_value_dup_boxed (value);
+ break;
+ case PROP_ADDRESSES:
+ g_slist_free_full (priv->addresses, (GDestroyNotify) nm_ip4_address_unref);
+ priv->addresses = nm_utils_ip4_addresses_from_gvalue (value);
+
+ if (g_slist_length (priv->addresses) != g_slist_length (priv->address_labels)) {
+ g_slist_free_full (priv->address_labels, g_free);
+ priv->address_labels = NULL;
+ for (iter = priv->addresses; iter; iter = iter->next)
+ priv->address_labels = g_slist_prepend (priv->address_labels, NULL);
+ }
+ break;
+ case PROP_ADDRESS_LABELS:
+ g_slist_free_full (priv->address_labels, g_free);
+ priv->address_labels = g_value_dup_boxed (value);
+ /* NULLs get converted to "" when this is sent over D-Bus. */
+ for (iter = priv->address_labels; iter; iter = iter->next) {
+ if (!g_strcmp0 (iter->data, "")) {
+ g_free (iter->data);
+ iter->data = NULL;
+ }
+ }
+ break;
+ case PROP_ROUTES:
+ g_slist_free_full (priv->routes, (GDestroyNotify) nm_ip4_route_unref);
+ priv->routes = nm_utils_ip4_routes_from_gvalue (value);
+ break;
+ case PROP_IGNORE_AUTO_ROUTES:
+ priv->ignore_auto_routes = g_value_get_boolean (value);
+ break;
+ case PROP_IGNORE_AUTO_DNS:
+ priv->ignore_auto_dns = g_value_get_boolean (value);
+ break;
+ case PROP_DHCP_CLIENT_ID:
+ g_free (priv->dhcp_client_id);
+ priv->dhcp_client_id = g_value_dup_string (value);
+ break;
+ case PROP_DHCP_SEND_HOSTNAME:
+ priv->dhcp_send_hostname = g_value_get_boolean (value);
+ break;
+ case PROP_DHCP_HOSTNAME:
+ g_free (priv->dhcp_hostname);
+ priv->dhcp_hostname = g_value_dup_string (value);
+ break;
+ case PROP_NEVER_DEFAULT:
+ priv->never_default = g_value_get_boolean (value);
+ break;
+ case PROP_MAY_FAIL:
+ priv->may_fail = g_value_get_boolean (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ NMSettingIP4Config *setting = NM_SETTING_IP4_CONFIG (object);
+ NMSettingIP4ConfigPrivate *priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting);
+
+ switch (prop_id) {
+ case PROP_METHOD:
+ g_value_set_string (value, nm_setting_ip4_config_get_method (setting));
+ break;
+ case PROP_DNS:
+ g_value_set_boxed (value, priv->dns);
+ break;
+ case PROP_DNS_SEARCH:
+ g_value_set_boxed (value, priv->dns_search);
+ break;
+ case PROP_ADDRESSES:
+ nm_utils_ip4_addresses_to_gvalue (priv->addresses, value);
+ break;
+ case PROP_ADDRESS_LABELS:
+ g_value_set_boxed (value, priv->address_labels);
+ break;
+ case PROP_ROUTES:
+ nm_utils_ip4_routes_to_gvalue (priv->routes, value);
+ break;
+ case PROP_IGNORE_AUTO_ROUTES:
+ g_value_set_boolean (value, nm_setting_ip4_config_get_ignore_auto_routes (setting));
+ break;
+ case PROP_IGNORE_AUTO_DNS:
+ g_value_set_boolean (value, nm_setting_ip4_config_get_ignore_auto_dns (setting));
+ break;
+ case PROP_DHCP_CLIENT_ID:
+ g_value_set_string (value, nm_setting_ip4_config_get_dhcp_client_id (setting));
+ break;
+ case PROP_DHCP_SEND_HOSTNAME:
+ g_value_set_boolean (value, nm_setting_ip4_config_get_dhcp_send_hostname (setting));
+ break;
+ case PROP_DHCP_HOSTNAME:
+ g_value_set_string (value, nm_setting_ip4_config_get_dhcp_hostname (setting));
+ break;
+ case PROP_NEVER_DEFAULT:
+ g_value_set_boolean (value, priv->never_default);
+ break;
+ case PROP_MAY_FAIL:
+ g_value_set_boolean (value, priv->may_fail);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nm_setting_ip4_config_class_init (NMSettingIP4ConfigClass *setting_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
+ NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+
+ g_type_class_add_private (setting_class, sizeof (NMSettingIP4ConfigPrivate));
+
+ /* virtual methods */
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+ object_class->finalize = finalize;
+ parent_class->verify = verify;
+
+ /* Properties */
+ /**
+ * NMSettingIP4Config:method:
+ *
+ * IPv4 configuration method. If "auto" is specified then the appropriate
+ * automatic method (DHCP, PPP, etc) is used for the interface and most
+ * other properties can be left unset. If "link-local" is specified, then a
+ * link-local address in the 169.254/16 range will be assigned to the
+ * interface. If "manual" is specified, static IP addressing is used and at
+ * least one IP address must be given in the "addresses" property. If
+ * "shared" is specified (indicating that this connection will provide
+ * network access to other computers) then the interface is assigned an
+ * address in the 10.42.x.1/24 range and a DHCP and forwarding DNS server
+ * are started, and the interface is NAT-ed to the current default network
+ * connection. "disabled" means IPv4 will not be used on this connection.
+ * This property must be set.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_METHOD,
+ g_param_spec_string (NM_SETTING_IP4_CONFIG_METHOD, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingIP4Config:dns:
+ *
+ * List of DNS servers (network byte order). For the "auto" method, these
+ * DNS servers are appended to those (if any) returned by automatic
+ * configuration. DNS servers cannot be used with the "shared",
+ * "link-local", or "disabled" methods as there is no upstream network. In
+ * all other methods, these DNS servers are used as the only DNS servers for
+ * this connection.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_DNS,
+ _nm_param_spec_specialized (NM_SETTING_IP4_CONFIG_DNS, "", "",
+ DBUS_TYPE_G_UINT_ARRAY,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingIP4Config:dns-search:
+ *
+ * List of DNS search domains. For the "auto" method, these search domains
+ * are appended to those returned by automatic configuration. Search domains
+ * cannot be used with the "shared", "link-local", or "disabled" methods as
+ * there is no upstream network. In all other methods, these search domains
+ * are used as the only search domains for this connection.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_DNS_SEARCH,
+ _nm_param_spec_specialized (NM_SETTING_IP4_CONFIG_DNS_SEARCH, "", "",
+ DBUS_TYPE_G_LIST_OF_STRING,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingIP4Config:addresses:
+ *
+ * Array of IPv4 address structures. Each IPv4 address structure is
+ * composed of 3 32-bit values; the first being the IPv4 address (network
+ * byte order), the second the prefix (1 - 32), and last the IPv4 gateway
+ * (network byte order). The gateway may be left as 0 if no gateway exists
+ * for that subnet. For the "auto" method, given IP addresses are appended
+ * to those returned by automatic configuration. Addresses cannot be used
+ * with the "shared", "link-local", or "disabled" methods as addressing is
+ * either automatic or disabled with these methods.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_ADDRESSES,
+ _nm_param_spec_specialized (NM_SETTING_IP4_CONFIG_ADDRESSES, "", "",
+ DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UINT,
+ G_PARAM_READWRITE |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingIP4Config:address-labels:
+ *
+ * Internal use only.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_ADDRESS_LABELS,
+ _nm_param_spec_specialized ("address-labels", "", "",
+ DBUS_TYPE_G_LIST_OF_STRING,
+ G_PARAM_READWRITE |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingIP4Config:routes:
+ *
+ * Array of IPv4 route structures. Each IPv4 route structure is composed of
+ * 4 32-bit values; the first being the destination IPv4 network or address
+ * (network byte order), the second the destination network or address
+ * prefix (1 - 32), the third being the next-hop (network byte order) if
+ * any, and the fourth being the route metric. For the "auto" method, given
+ * IP routes are appended to those returned by automatic configuration.
+ * Routes cannot be used with the "shared", "link-local", or "disabled"
+ * methods because there is no upstream network.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_ROUTES,
+ _nm_param_spec_specialized (NM_SETTING_IP4_CONFIG_ROUTES, "", "",
+ DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UINT,
+ G_PARAM_READWRITE |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingIP4Config:ignore-auto-routes:
+ *
+ * When the method is set to "auto" and this property to %TRUE,
+ * automatically configured routes are ignored and only routes specified in
+ * the #NMSettingIP4Config:routes property, if any, are used.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_IGNORE_AUTO_ROUTES,
+ g_param_spec_boolean (NM_SETTING_IP4_CONFIG_IGNORE_AUTO_ROUTES, "", "",
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingIP4Config:ignore-auto-dns:
+ *
+ * When the method is set to "auto" and this property to %TRUE,
+ * automatically configured nameservers and search domains are ignored and
+ * only nameservers and search domains specified in the
+ * #NMSettingIP4Config:dns and #NMSettingIP4Config:dns-search properties, if
+ * any, are used.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_IGNORE_AUTO_DNS,
+ g_param_spec_boolean (NM_SETTING_IP4_CONFIG_IGNORE_AUTO_DNS, "", "",
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingIP4Config:dhcp-client-id:
+ *
+ * A string sent to the DHCP server to identify the local machine which the
+ * DHCP server may use to customize the DHCP lease and options.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_DHCP_CLIENT_ID,
+ g_param_spec_string (NM_SETTING_IP4_CONFIG_DHCP_CLIENT_ID, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingIP4Config:dhcp-send-hostname:
+ *
+ * If %TRUE, a hostname is sent to the DHCP server when acquiring a lease.
+ * Some DHCP servers use this hostname to update DNS databases, essentially
+ * providing a static hostname for the computer. If the
+ * #NMSettingIP4Config:dhcp-hostname property is empty and this property is
+ * %TRUE, the current persistent hostname of the computer is sent.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_DHCP_SEND_HOSTNAME,
+ g_param_spec_boolean (NM_SETTING_IP4_CONFIG_DHCP_SEND_HOSTNAME, "", "",
+ TRUE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingIP4Config:dhcp-hostname:
+ *
+ * If the #NMSettingIP4Config:dhcp-send-hostname property is %TRUE, then the
+ * specified name will be sent to the DHCP server when acquiring a lease.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_DHCP_HOSTNAME,
+ g_param_spec_string (NM_SETTING_IP4_CONFIG_DHCP_HOSTNAME, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingIP4Config:never-default:
+ *
+ * If %TRUE, this connection will never be the default IPv4 connection,
+ * meaning it will never be assigned the default route by NetworkManager.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_NEVER_DEFAULT,
+ g_param_spec_boolean (NM_SETTING_IP4_CONFIG_NEVER_DEFAULT, "", "",
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingIP4Config:may-fail:
+ *
+ * If %TRUE, allow overall network configuration to proceed even if IPv4
+ * configuration times out. Note that at least one IP configuration must
+ * succeed or overall network configuration will still fail. For example,
+ * in IPv6-only networks, setting this property to %TRUE allows the overall
+ * network configuration to succeed if IPv4 configuration fails but IPv6
+ * configuration completes successfully.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_MAY_FAIL,
+ g_param_spec_boolean (NM_SETTING_IP4_CONFIG_MAY_FAIL, "", "",
+ TRUE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+}
+
+
+struct NMIP4Address {
+ guint32 refcount;
+ guint32 address; /* network byte order */
+ guint32 prefix;
+ guint32 gateway; /* network byte order */
+};
+
+/**
+ * nm_ip4_address_new:
+ *
+ * Creates and returns a new #NMIP4Address object.
+ *
+ * Returns: (transfer full): the new empty #NMIP4Address object
+ **/
+NMIP4Address *
+nm_ip4_address_new (void)
+{
+ NMIP4Address *address;
+
+ address = g_malloc0 (sizeof (NMIP4Address));
+ address->refcount = 1;
+ return address;
+}
+
+/**
+ * nm_ip4_address_dup:
+ * @source: the #NMIP4Address object to copy
+ *
+ * Copies a given #NMIP4Address object and returns the copy.
+ *
+ * Returns: (transfer full): the copy of the given #NMIP4Address copy
+ **/
+NMIP4Address *
+nm_ip4_address_dup (NMIP4Address *source)
+{
+ NMIP4Address *address;
+
+ g_return_val_if_fail (source != NULL, NULL);
+ g_return_val_if_fail (source->refcount > 0, NULL);
+
+ address = nm_ip4_address_new ();
+ address->address = source->address;
+ address->prefix = source->prefix;
+ address->gateway = source->gateway;
+
+ return address;
+}
+
+/**
+ * nm_ip4_address_ref:
+ * @address: the #NMIP4Address
+ *
+ * Increases the reference count of the object.
+ **/
+void
+nm_ip4_address_ref (NMIP4Address *address)
+{
+ g_return_if_fail (address != NULL);
+ g_return_if_fail (address->refcount > 0);
+
+ address->refcount++;
+}
+
+/**
+ * nm_ip4_address_unref:
+ * @address: the #NMIP4Address
+ *
+ * Decreases the reference count of the object. If the reference count
+ * reaches zero, the object will be destroyed.
+ **/
+void
+nm_ip4_address_unref (NMIP4Address *address)
+{
+ g_return_if_fail (address != NULL);
+ g_return_if_fail (address->refcount > 0);
+
+ address->refcount--;
+ if (address->refcount == 0) {
+ memset (address, 0, sizeof (NMIP4Address));
+ g_free (address);
+ }
+}
+
+/**
+ * nm_ip4_address_compare:
+ * @address: the #NMIP4Address
+ * @other: the #NMIP4Address to compare @address to.
+ *
+ * Determines if two #NMIP4Address objects contain the same values.
+ *
+ * Returns: %TRUE if the objects contain the same values, %FALSE if they do not.
+ **/
+gboolean
+nm_ip4_address_compare (NMIP4Address *address, NMIP4Address *other)
+{
+ g_return_val_if_fail (address != NULL, FALSE);
+ g_return_val_if_fail (address->refcount > 0, FALSE);
+
+ g_return_val_if_fail (other != NULL, FALSE);
+ g_return_val_if_fail (other->refcount > 0, FALSE);
+
+ if ( address->address != other->address
+ || address->prefix != other->prefix
+ || address->gateway != other->gateway)
+ return FALSE;
+ return TRUE;
+}
+
+/**
+ * nm_ip4_address_get_address:
+ * @address: the #NMIP4Address
+ *
+ * Gets the IPv4 address property of this address object.
+ *
+ * Returns: the IPv4 address in network byte order
+ **/
+guint32
+nm_ip4_address_get_address (NMIP4Address *address)
+{
+ g_return_val_if_fail (address != NULL, 0);
+ g_return_val_if_fail (address->refcount > 0, 0);
+
+ return address->address;
+}
+
+/**
+ * nm_ip4_address_set_address:
+ * @address: the #NMIP4Address
+ * @addr: the IPv4 address in network byte order
+ *
+ * Sets the IPv4 address property of this object.
+ **/
+void
+nm_ip4_address_set_address (NMIP4Address *address, guint32 addr)
+{
+ g_return_if_fail (address != NULL);
+ g_return_if_fail (address->refcount > 0);
+
+ address->address = addr;
+}
+
+/**
+ * nm_ip4_address_get_prefix:
+ * @address: the #NMIP4Address
+ *
+ * Gets the IPv4 address prefix (ie "24" or "30" etc) property of this address
+ * object.
+ *
+ * Returns: the IPv4 address prefix
+ **/
+guint32
+nm_ip4_address_get_prefix (NMIP4Address *address)
+{
+ g_return_val_if_fail (address != NULL, 0);
+ g_return_val_if_fail (address->refcount > 0, 0);
+
+ return address->prefix;
+}
+
+/**
+ * nm_ip4_address_set_prefix:
+ * @address: the #NMIP4Address
+ * @prefix: the address prefix, a number between 1 and 32 inclusive
+ *
+ * Sets the IPv4 address prefix.
+ **/
+void
+nm_ip4_address_set_prefix (NMIP4Address *address, guint32 prefix)
+{
+ g_return_if_fail (address != NULL);
+ g_return_if_fail (address->refcount > 0);
+ g_return_if_fail (prefix <= 32);
+ g_return_if_fail (prefix > 0);
+
+ address->prefix = prefix;
+}
+
+/**
+ * nm_ip4_address_get_gateway:
+ * @address: the #NMIP4Address
+ *
+ * Gets the IPv4 default gateway property of this address object.
+ *
+ * Returns: the IPv4 gateway address in network byte order
+ **/
+guint32
+nm_ip4_address_get_gateway (NMIP4Address *address)
+{
+ g_return_val_if_fail (address != NULL, 0);
+ g_return_val_if_fail (address->refcount > 0, 0);
+
+ return address->gateway;
+}
+
+/**
+ * nm_ip4_address_set_gateway:
+ * @address: the #NMIP4Address
+ * @gateway: the IPv4 default gateway in network byte order
+ *
+ * Sets the IPv4 default gateway property of this address object.
+ **/
+void
+nm_ip4_address_set_gateway (NMIP4Address *address, guint32 gateway)
+{
+ g_return_if_fail (address != NULL);
+ g_return_if_fail (address->refcount > 0);
+
+ address->gateway = gateway;
+}
+
+
+struct NMIP4Route {
+ guint32 refcount;
+
+ guint32 dest; /* network byte order */
+ guint32 prefix;
+ guint32 next_hop; /* network byte order */
+ guint32 metric; /* lower metric == more preferred */
+};
+
+/**
+ * nm_ip4_route_new:
+ *
+ * Creates and returns a new #NMIP4Route object.
+ *
+ * Returns: (transfer full): the new empty #NMIP4Route object
+ **/
+NMIP4Route *
+nm_ip4_route_new (void)
+{
+ NMIP4Route *route;
+
+ route = g_malloc0 (sizeof (NMIP4Route));
+ route->refcount = 1;
+ return route;
+}
+
+/**
+ * nm_ip4_route_dup:
+ * @source: the #NMIP4Route object to copy
+ *
+ * Copies a given #NMIP4Route object and returns the copy.
+ *
+ * Returns: (transfer full): the copy of the given #NMIP4Route copy
+ **/
+NMIP4Route *
+nm_ip4_route_dup (NMIP4Route *source)
+{
+ NMIP4Route *route;
+
+ g_return_val_if_fail (source != NULL, NULL);
+ g_return_val_if_fail (source->refcount > 0, NULL);
+
+ route = nm_ip4_route_new ();
+ route->dest = source->dest;
+ route->prefix = source->prefix;
+ route->next_hop = source->next_hop;
+ route->metric = source->metric;
+
+ return route;
+}
+
+/**
+ * nm_ip4_route_ref:
+ * @route: the #NMIP4Route
+ *
+ * Increases the reference count of the object.
+ **/
+void
+nm_ip4_route_ref (NMIP4Route *route)
+{
+ g_return_if_fail (route != NULL);
+ g_return_if_fail (route->refcount > 0);
+
+ route->refcount++;
+}
+
+/**
+ * nm_ip4_route_unref:
+ * @route: the #NMIP4Route
+ *
+ * Decreases the reference count of the object. If the reference count
+ * reaches zero, the object will be destroyed.
+ **/
+void
+nm_ip4_route_unref (NMIP4Route *route)
+{
+ g_return_if_fail (route != NULL);
+ g_return_if_fail (route->refcount > 0);
+
+ route->refcount--;
+ if (route->refcount == 0) {
+ memset (route, 0, sizeof (NMIP4Route));
+ g_free (route);
+ }
+}
+
+/**
+ * nm_ip4_route_compare:
+ * @route: the #NMIP4Route
+ * @other: the #NMIP4Route to compare @route to.
+ *
+ * Determines if two #NMIP4Route objects contain the same values.
+ *
+ * Returns: %TRUE if the objects contain the same values, %FALSE if they do not.
+ **/
+gboolean
+nm_ip4_route_compare (NMIP4Route *route, NMIP4Route *other)
+{
+ g_return_val_if_fail (route != NULL, FALSE);
+ g_return_val_if_fail (route->refcount > 0, FALSE);
+
+ g_return_val_if_fail (other != NULL, FALSE);
+ g_return_val_if_fail (other->refcount > 0, FALSE);
+
+ if ( route->dest != other->dest
+ || route->prefix != other->prefix
+ || route->next_hop != other->next_hop
+ || route->metric != other->metric)
+ return FALSE;
+ return TRUE;
+}
+
+/**
+ * nm_ip4_route_get_dest:
+ * @route: the #NMIP4Route
+ *
+ * Gets the IPv4 destination address property of this route object.
+ *
+ * Returns: the IPv4 address in network byte order
+ **/
+guint32
+nm_ip4_route_get_dest (NMIP4Route *route)
+{
+ g_return_val_if_fail (route != NULL, 0);
+ g_return_val_if_fail (route->refcount > 0, 0);
+
+ return route->dest;
+}
+
+/**
+ * nm_ip4_route_set_dest:
+ * @route: the #NMIP4Route
+ * @dest: the destination address in network byte order
+ *
+ * Sets the IPv4 destination address property of this route object.
+ **/
+void
+nm_ip4_route_set_dest (NMIP4Route *route, guint32 dest)
+{
+ g_return_if_fail (route != NULL);
+ g_return_if_fail (route->refcount > 0);
+
+ route->dest = dest;
+}
+
+/**
+ * nm_ip4_route_get_prefix:
+ * @route: the #NMIP4Route
+ *
+ * Gets the IPv4 prefix (ie "24" or "30" etc) of this route.
+ *
+ * Returns: the IPv4 prefix
+ **/
+guint32
+nm_ip4_route_get_prefix (NMIP4Route *route)
+{
+ g_return_val_if_fail (route != NULL, 0);
+ g_return_val_if_fail (route->refcount > 0, 0);
+
+ return route->prefix;
+}
+
+/**
+ * nm_ip4_route_set_prefix:
+ * @route: the #NMIP4Route
+ * @prefix: the prefix, a number between 1 and 32 inclusive
+ *
+ * Sets the IPv4 prefix of this route.
+ **/
+void
+nm_ip4_route_set_prefix (NMIP4Route *route, guint32 prefix)
+{
+ g_return_if_fail (route != NULL);
+ g_return_if_fail (route->refcount > 0);
+ g_return_if_fail (prefix <= 32);
+ g_return_if_fail (prefix > 0);
+
+ route->prefix = prefix;
+}
+
+/**
+ * nm_ip4_route_get_next_hop:
+ * @route: the #NMIP4Route
+ *
+ * Gets the IPv4 address of the next hop of this route.
+ *
+ * Returns: the IPv4 address in network byte order
+ **/
+guint32
+nm_ip4_route_get_next_hop (NMIP4Route *route)
+{
+ g_return_val_if_fail (route != NULL, 0);
+ g_return_val_if_fail (route->refcount > 0, 0);
+
+ return route->next_hop;
+}
+
+/**
+ * nm_ip4_route_set_next_hop:
+ * @route: the #NMIP4Route
+ * @next_hop: the IPv4 address of the next hop in network byte order
+ *
+ * Sets the IPv4 address of the next hop of this route.
+ **/
+void
+nm_ip4_route_set_next_hop (NMIP4Route *route, guint32 next_hop)
+{
+ g_return_if_fail (route != NULL);
+ g_return_if_fail (route->refcount > 0);
+
+ route->next_hop = next_hop;
+}
+
+/**
+ * nm_ip4_route_get_metric:
+ * @route: the #NMIP4Route
+ *
+ * Gets the route metric property of this route object; lower values indicate
+ * "better" or more preferred routes.
+ *
+ * Returns: the route metric
+ **/
+guint32
+nm_ip4_route_get_metric (NMIP4Route *route)
+{
+ g_return_val_if_fail (route != NULL, 0);
+ g_return_val_if_fail (route->refcount > 0, 0);
+
+ return route->metric;
+}
+
+/**
+ * nm_ip4_route_set_metric:
+ * @route: the #NMIP4Route
+ * @metric: the route metric
+ *
+ * Sets the route metric property of this route object; lower values indicate
+ * "better" or more preferred routes.
+ **/
+void
+nm_ip4_route_set_metric (NMIP4Route *route, guint32 metric)
+{
+ g_return_if_fail (route != NULL);
+ g_return_if_fail (route->refcount > 0);
+
+ route->metric = metric;
+}
diff --git a/libnm-core/nm-setting-ip4-config.h b/libnm-core/nm-setting-ip4-config.h
new file mode 100644
index 0000000000..1b7c23072d
--- /dev/null
+++ b/libnm-core/nm-setting-ip4-config.h
@@ -0,0 +1,229 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2014 Red Hat, Inc.
+ * Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#ifndef NM_SETTING_IP4_CONFIG_H
+#define NM_SETTING_IP4_CONFIG_H
+
+#include "nm-setting.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_SETTING_IP4_CONFIG (nm_setting_ip4_config_get_type ())
+#define NM_SETTING_IP4_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_IP4_CONFIG, NMSettingIP4Config))
+#define NM_SETTING_IP4_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_IP4CONFIG, NMSettingIP4ConfigClass))
+#define NM_IS_SETTING_IP4_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_IP4_CONFIG))
+#define NM_IS_SETTING_IP4_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_IP4_CONFIG))
+#define NM_SETTING_IP4_CONFIG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_IP4_CONFIG, NMSettingIP4ConfigClass))
+
+#define NM_SETTING_IP4_CONFIG_SETTING_NAME "ipv4"
+
+/**
+ * NMSettingIP4ConfigError:
+ * @NM_SETTING_IP4_CONFIG_ERROR_UNKNOWN: unknown or unclassified error
+ * @NM_SETTING_IP4_CONFIG_ERROR_INVALID_PROPERTY: the property was invalid
+ * @NM_SETTING_IP4_CONFIG_ERROR_MISSING_PROPERTY: the property was missing and is
+ * required
+ * @NM_SETTING_IP4_CONFIG_ERROR_NOT_ALLOWED_FOR_METHOD: the property's value is
+ * not valid with the given IP4 method
+ */
+typedef enum {
+ NM_SETTING_IP4_CONFIG_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/
+ NM_SETTING_IP4_CONFIG_ERROR_INVALID_PROPERTY, /*< nick=InvalidProperty >*/
+ NM_SETTING_IP4_CONFIG_ERROR_MISSING_PROPERTY, /*< nick=MissingProperty >*/
+ NM_SETTING_IP4_CONFIG_ERROR_NOT_ALLOWED_FOR_METHOD /*< nick=NotAllowedForMethod >*/
+} NMSettingIP4ConfigError;
+
+#define NM_SETTING_IP4_CONFIG_ERROR nm_setting_ip4_config_error_quark ()
+GQuark nm_setting_ip4_config_error_quark (void);
+
+#define NM_SETTING_IP4_CONFIG_METHOD "method"
+#define NM_SETTING_IP4_CONFIG_DNS "dns"
+#define NM_SETTING_IP4_CONFIG_DNS_SEARCH "dns-search"
+#define NM_SETTING_IP4_CONFIG_ADDRESSES "addresses"
+#define NM_SETTING_IP4_CONFIG_ROUTES "routes"
+#define NM_SETTING_IP4_CONFIG_IGNORE_AUTO_ROUTES "ignore-auto-routes"
+#define NM_SETTING_IP4_CONFIG_IGNORE_AUTO_DNS "ignore-auto-dns"
+#define NM_SETTING_IP4_CONFIG_DHCP_CLIENT_ID "dhcp-client-id"
+#define NM_SETTING_IP4_CONFIG_DHCP_SEND_HOSTNAME "dhcp-send-hostname"
+#define NM_SETTING_IP4_CONFIG_DHCP_HOSTNAME "dhcp-hostname"
+#define NM_SETTING_IP4_CONFIG_NEVER_DEFAULT "never-default"
+#define NM_SETTING_IP4_CONFIG_MAY_FAIL "may-fail"
+
+/**
+ * NM_SETTING_IP4_CONFIG_METHOD_AUTO:
+ *
+ * IPv4 configuration should be automatically determined via a method appropriate
+ * for the hardware interface, ie DHCP or PPP or some other device-specific
+ * manner.
+ */
+#define NM_SETTING_IP4_CONFIG_METHOD_AUTO "auto"
+
+/**
+ * NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL:
+ *
+ * IPv4 configuration should be automatically configured for link-local-only
+ * operation.
+ */
+#define NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL "link-local"
+
+/**
+ * NM_SETTING_IP4_CONFIG_METHOD_MANUAL:
+ *
+ * All necessary IPv4 configuration (addresses, prefix, DNS, etc) is specified
+ * in the setting's properties.
+ */
+#define NM_SETTING_IP4_CONFIG_METHOD_MANUAL "manual"
+
+/**
+ * NM_SETTING_IP4_CONFIG_METHOD_SHARED:
+ *
+ * This connection specifies configuration that allows other computers to
+ * connect through it to the default network (usually the Internet). The
+ * connection's interface will be assigned a private address, and a DHCP server,
+ * caching DNS server, and Network Address Translation (NAT) functionality will
+ * be started on this connection's interface to allow other devices to connect
+ * through that interface to the default network.
+ */
+#define NM_SETTING_IP4_CONFIG_METHOD_SHARED "shared"
+
+/**
+ * NM_SETTING_IP4_CONFIG_METHOD_DISABLED:
+ *
+ * This connection does not use or require IPv4 address and it should be disabled.
+ */
+#define NM_SETTING_IP4_CONFIG_METHOD_DISABLED "disabled"
+
+typedef struct NMIP4Address NMIP4Address;
+
+GType nm_ip4_address_get_type (void);
+
+NMIP4Address * nm_ip4_address_new (void);
+NMIP4Address * nm_ip4_address_dup (NMIP4Address *source);
+void nm_ip4_address_ref (NMIP4Address *address);
+void nm_ip4_address_unref (NMIP4Address *address);
+/* Return TRUE if addresses are identical */
+gboolean nm_ip4_address_compare (NMIP4Address *address, NMIP4Address *other);
+
+guint32 nm_ip4_address_get_address (NMIP4Address *address);
+void nm_ip4_address_set_address (NMIP4Address *address,
+ guint32 addr); /* network byte order */
+
+guint32 nm_ip4_address_get_prefix (NMIP4Address *address);
+void nm_ip4_address_set_prefix (NMIP4Address *address,
+ guint32 prefix);
+
+guint32 nm_ip4_address_get_gateway (NMIP4Address *address);
+void nm_ip4_address_set_gateway (NMIP4Address *address,
+ guint32 gateway); /* network byte order */
+
+typedef struct NMIP4Route NMIP4Route;
+
+GType nm_ip4_route_get_type (void);
+
+NMIP4Route * nm_ip4_route_new (void);
+NMIP4Route * nm_ip4_route_dup (NMIP4Route *source);
+void nm_ip4_route_ref (NMIP4Route *route);
+void nm_ip4_route_unref (NMIP4Route *route);
+/* Return TRUE if routes are identical */
+gboolean nm_ip4_route_compare (NMIP4Route *route, NMIP4Route *other);
+
+guint32 nm_ip4_route_get_dest (NMIP4Route *route);
+void nm_ip4_route_set_dest (NMIP4Route *route,
+ guint32 dest); /* network byte order */
+
+guint32 nm_ip4_route_get_prefix (NMIP4Route *route);
+void nm_ip4_route_set_prefix (NMIP4Route *route,
+ guint32 prefix);
+
+guint32 nm_ip4_route_get_next_hop (NMIP4Route *route);
+void nm_ip4_route_set_next_hop (NMIP4Route *route,
+ guint32 next_hop); /* network byte order */
+
+guint32 nm_ip4_route_get_metric (NMIP4Route *route);
+void nm_ip4_route_set_metric (NMIP4Route *route,
+ guint32 metric);
+
+
+typedef struct {
+ NMSetting parent;
+} NMSettingIP4Config;
+
+typedef struct {
+ NMSettingClass parent;
+
+ /* Padding for future expansion */
+ void (*_reserved1) (void);
+ void (*_reserved2) (void);
+ void (*_reserved3) (void);
+ void (*_reserved4) (void);
+} NMSettingIP4ConfigClass;
+
+GType nm_setting_ip4_config_get_type (void);
+
+NMSetting * nm_setting_ip4_config_new (void);
+const char * nm_setting_ip4_config_get_method (NMSettingIP4Config *setting);
+
+guint32 nm_setting_ip4_config_get_num_dns (NMSettingIP4Config *setting);
+guint32 nm_setting_ip4_config_get_dns (NMSettingIP4Config *setting, guint32 i);
+gboolean nm_setting_ip4_config_add_dns (NMSettingIP4Config *setting, guint32 dns);
+void nm_setting_ip4_config_remove_dns (NMSettingIP4Config *setting, guint32 i);
+NM_AVAILABLE_IN_0_9_10
+gboolean nm_setting_ip4_config_remove_dns_by_value (NMSettingIP4Config *setting, guint32 dns);
+void nm_setting_ip4_config_clear_dns (NMSettingIP4Config *setting);
+
+guint32 nm_setting_ip4_config_get_num_dns_searches (NMSettingIP4Config *setting);
+const char * nm_setting_ip4_config_get_dns_search (NMSettingIP4Config *setting, guint32 i);
+gboolean nm_setting_ip4_config_add_dns_search (NMSettingIP4Config *setting, const char *dns_search);
+void nm_setting_ip4_config_remove_dns_search (NMSettingIP4Config *setting, guint32 i);
+NM_AVAILABLE_IN_0_9_10
+gboolean nm_setting_ip4_config_remove_dns_search_by_value (NMSettingIP4Config *setting, const char *dns_search);
+void nm_setting_ip4_config_clear_dns_searches (NMSettingIP4Config *setting);
+
+guint32 nm_setting_ip4_config_get_num_addresses (NMSettingIP4Config *setting);
+NMIP4Address *nm_setting_ip4_config_get_address (NMSettingIP4Config *setting, guint32 i);
+gboolean nm_setting_ip4_config_add_address (NMSettingIP4Config *setting, NMIP4Address *address);
+void nm_setting_ip4_config_remove_address (NMSettingIP4Config *setting, guint32 i);
+NM_AVAILABLE_IN_0_9_10
+gboolean nm_setting_ip4_config_remove_address_by_value (NMSettingIP4Config *setting, NMIP4Address *address);
+void nm_setting_ip4_config_clear_addresses (NMSettingIP4Config *setting);
+
+guint32 nm_setting_ip4_config_get_num_routes (NMSettingIP4Config *setting);
+NMIP4Route * nm_setting_ip4_config_get_route (NMSettingIP4Config *setting, guint32 i);
+gboolean nm_setting_ip4_config_add_route (NMSettingIP4Config *setting, NMIP4Route *route);
+void nm_setting_ip4_config_remove_route (NMSettingIP4Config *setting, guint32 i);
+NM_AVAILABLE_IN_0_9_10
+gboolean nm_setting_ip4_config_remove_route_by_value (NMSettingIP4Config *setting, NMIP4Route *route);
+void nm_setting_ip4_config_clear_routes (NMSettingIP4Config *setting);
+
+gboolean nm_setting_ip4_config_get_ignore_auto_routes (NMSettingIP4Config *setting);
+gboolean nm_setting_ip4_config_get_ignore_auto_dns (NMSettingIP4Config *setting);
+const char * nm_setting_ip4_config_get_dhcp_client_id (NMSettingIP4Config *setting);
+gboolean nm_setting_ip4_config_get_dhcp_send_hostname (NMSettingIP4Config *setting);
+const char * nm_setting_ip4_config_get_dhcp_hostname (NMSettingIP4Config *setting);
+
+gboolean nm_setting_ip4_config_get_never_default (NMSettingIP4Config *setting);
+
+gboolean nm_setting_ip4_config_get_may_fail (NMSettingIP4Config *setting);
+
+G_END_DECLS
+
+#endif /* NM_SETTING_IP4_CONFIG_H */
diff --git a/libnm-core/nm-setting-ip6-config.c b/libnm-core/nm-setting-ip6-config.c
new file mode 100644
index 0000000000..49c32dd662
--- /dev/null
+++ b/libnm-core/nm-setting-ip6-config.c
@@ -0,0 +1,1683 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2014 Red Hat, Inc.
+ */
+
+#include <string.h>
+#include <dbus/dbus-glib.h>
+#include <glib/gi18n.h>
+
+#include "nm-setting-ip6-config.h"
+#include "nm-param-spec-specialized.h"
+#include "nm-utils.h"
+#include "nm-dbus-glib-types.h"
+#include "nm-glib-compat.h"
+#include "nm-setting-private.h"
+
+/**
+ * SECTION:nm-setting-ip6-config
+ * @short_description: Describes IPv6 addressing, routing, and name service properties
+ * @include: nm-setting-ip6-config.h
+ *
+ * The #NMSettingIP6Config object is a #NMSetting subclass that describes
+ * properties related to IPv6 addressing, routing, and Domain Name Service
+ **/
+
+/**
+ * nm_setting_ip6_config_error_quark:
+ *
+ * Registers an error quark for #NMSettingIP6Config if necessary.
+ *
+ * Returns: the error quark used for #NMSettingIP6Config errors.
+ **/
+GQuark
+nm_setting_ip6_config_error_quark (void)
+{
+ static GQuark quark;
+
+ if (G_UNLIKELY (!quark))
+ quark = g_quark_from_static_string ("nm-setting-ip6-config-error-quark");
+ return quark;
+}
+
+G_DEFINE_BOXED_TYPE (NMIP6Address, nm_ip6_address, nm_ip6_address_dup, nm_ip6_address_unref)
+G_DEFINE_BOXED_TYPE (NMIP6Route, nm_ip6_route, nm_ip6_route_dup, nm_ip6_route_unref)
+
+G_DEFINE_TYPE_WITH_CODE (NMSettingIP6Config, nm_setting_ip6_config, NM_TYPE_SETTING,
+ _nm_register_setting (NM_SETTING_IP6_CONFIG_SETTING_NAME,
+ g_define_type_id,
+ 4,
+ NM_SETTING_IP6_CONFIG_ERROR))
+NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_IP6_CONFIG)
+
+#define NM_SETTING_IP6_CONFIG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_IP6_CONFIG, NMSettingIP6ConfigPrivate))
+
+typedef struct {
+ char *method;
+ char *dhcp_hostname;
+ GSList *dns; /* array of struct in6_addr */
+ GSList *dns_search; /* list of strings */
+ GSList *addresses; /* array of NMIP6Address */
+ GSList *routes; /* array of NMIP6Route */
+ gboolean ignore_auto_routes;
+ gboolean ignore_auto_dns;
+ gboolean never_default;
+ gboolean may_fail;
+ NMSettingIP6ConfigPrivacy ip6_privacy;
+} NMSettingIP6ConfigPrivate;
+
+
+enum {
+ PROP_0,
+ PROP_METHOD,
+ PROP_DHCP_HOSTNAME,
+ PROP_DNS,
+ PROP_DNS_SEARCH,
+ PROP_ADDRESSES,
+ PROP_ROUTES,
+ PROP_IGNORE_AUTO_ROUTES,
+ PROP_IGNORE_AUTO_DNS,
+ PROP_NEVER_DEFAULT,
+ PROP_MAY_FAIL,
+ PROP_IP6_PRIVACY,
+
+ LAST_PROP
+};
+
+/**
+ * nm_setting_ip6_config_new:
+ *
+ * Creates a new #NMSettingIP6Config object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingIP6Config object
+ **/
+NMSetting *
+nm_setting_ip6_config_new (void)
+{
+ return (NMSetting *) g_object_new (NM_TYPE_SETTING_IP6_CONFIG, NULL);
+}
+
+/**
+ * nm_setting_ip6_config_get_method:
+ * @setting: the #NMSettingIP6Config
+ *
+ * Returns: the #NMSettingIP6Config:method property of the setting
+ **/
+const char *
+nm_setting_ip6_config_get_method (NMSettingIP6Config *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), NULL);
+
+ return NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting)->method;
+}
+
+/**
+ * nm_setting_ip6_config_get_dhcp_hostname:
+ * @setting: the #NMSettingIP6Config
+ *
+ * Returns the value contained in the #NMSettingIP6Config:dhcp-hostname
+ * property.
+ *
+ * Returns: the configured hostname to send to the DHCP server
+ *
+ * Since: 0.9.8
+ **/
+const char *
+nm_setting_ip6_config_get_dhcp_hostname (NMSettingIP6Config *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), NULL);
+
+ return NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting)->dhcp_hostname;
+}
+
+/**
+ * nm_setting_ip6_config_get_num_dns:
+ * @setting: the #NMSettingIP6Config
+ *
+ * Returns: the number of configured DNS servers
+ **/
+guint32
+nm_setting_ip6_config_get_num_dns (NMSettingIP6Config *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), 0);
+
+ return g_slist_length (NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting)->dns);
+}
+
+/**
+ * nm_setting_ip6_config_get_dns:
+ * @setting: the #NMSettingIP6Config
+ * @i: index number of the DNS server to return
+ *
+ * Returns: (transfer none): the IPv6 address of the DNS server at index @i
+ **/
+const struct in6_addr *
+nm_setting_ip6_config_get_dns (NMSettingIP6Config *setting, guint32 i)
+{
+ NMSettingIP6ConfigPrivate *priv;
+
+
+ g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), NULL);
+
+ priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting);
+ g_return_val_if_fail (i <= g_slist_length (priv->dns), NULL);
+
+ return (const struct in6_addr *) g_slist_nth_data (priv->dns, i);
+}
+
+/**
+ * nm_setting_ip6_config_add_dns:
+ * @setting: the #NMSettingIP6Config
+ * @dns: the IPv6 address of the DNS server to add
+ *
+ * Adds a new DNS server to the setting.
+ *
+ * Returns: %TRUE if the DNS server was added; %FALSE if the server was already
+ * known
+ **/
+gboolean
+nm_setting_ip6_config_add_dns (NMSettingIP6Config *setting, const struct in6_addr *addr)
+{
+ NMSettingIP6ConfigPrivate *priv;
+ struct in6_addr *copy;
+ GSList *iter;
+
+ g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), FALSE);
+
+ priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting);
+ for (iter = priv->dns; iter; iter = g_slist_next (iter)) {
+ if (!memcmp (addr, (struct in6_addr *) iter->data, sizeof (struct in6_addr)))
+ return FALSE;
+ }
+
+ copy = g_malloc0 (sizeof (struct in6_addr));
+ memcpy (copy, addr, sizeof (struct in6_addr));
+ priv->dns = g_slist_append (priv->dns, copy);
+ g_object_notify (G_OBJECT (setting), NM_SETTING_IP6_CONFIG_DNS);
+
+ return TRUE;
+}
+
+/**
+ * nm_setting_ip6_config_remove_dns:
+ * @setting: the #NMSettingIP6Config
+ * @i: index number of the DNS server to remove
+ *
+ * Removes the DNS server at index @i.
+ **/
+void
+nm_setting_ip6_config_remove_dns (NMSettingIP6Config *setting, guint32 i)
+{
+ NMSettingIP6ConfigPrivate *priv;
+ GSList *elt;
+
+ g_return_if_fail (NM_IS_SETTING_IP6_CONFIG (setting));
+
+ priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting);
+ elt = g_slist_nth (priv->dns, i);
+ g_return_if_fail (elt != NULL);
+
+ g_free (elt->data);
+ priv->dns = g_slist_delete_link (priv->dns, elt);
+ g_object_notify (G_OBJECT (setting), NM_SETTING_IP6_CONFIG_DNS);
+}
+
+/**
+ * nm_setting_ip6_config_remove_dns_by_value:
+ * @setting: the #NMSettingIP6Config
+ * @dns: the IPv6 address of the DNS server to remove
+ *
+ * Removes the DNS server at index @i.
+ *
+ * Returns: %TRUE if the DNS server was found and removed; %FALSE if it was not.
+ *
+ * Since: 0.9.10
+ **/
+gboolean
+nm_setting_ip6_config_remove_dns_by_value (NMSettingIP6Config *setting,
+ const struct in6_addr *addr)
+{
+ NMSettingIP6ConfigPrivate *priv;
+ GSList *iter;
+
+ g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), FALSE);
+
+ priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting);
+ for (iter = priv->dns; iter; iter = g_slist_next (iter)) {
+ if (!memcmp (addr, (struct in6_addr *) iter->data, sizeof (struct in6_addr))) {
+ priv->dns = g_slist_delete_link (priv->dns, iter);
+ g_object_notify (G_OBJECT (setting), NM_SETTING_IP6_CONFIG_DNS);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/**
+ * nm_setting_ip6_config_clear_dns:
+ * @setting: the #NMSettingIP6Config
+ *
+ * Removes all configured DNS servers.
+ **/
+void
+nm_setting_ip6_config_clear_dns (NMSettingIP6Config *setting)
+{
+ g_return_if_fail (NM_IS_SETTING_IP6_CONFIG (setting));
+
+ g_slist_free_full (NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting)->dns, g_free);
+ NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting)->dns = NULL;
+ g_object_notify (G_OBJECT (setting), NM_SETTING_IP6_CONFIG_DNS);
+}
+
+/**
+ * nm_setting_ip6_config_get_num_dns_searches:
+ * @setting: the #NMSettingIP6Config
+ *
+ * Returns: the number of configured DNS search domains
+ **/
+guint32
+nm_setting_ip6_config_get_num_dns_searches (NMSettingIP6Config *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), 0);
+
+ return g_slist_length (NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting)->dns_search);
+}
+
+/**
+ * nm_setting_ip6_config_get_dns_search:
+ * @setting: the #NMSettingIP6Config
+ * @i: index number of the DNS search domain to return
+ *
+ * Returns: the DNS search domain at index @i
+ **/
+const char *
+nm_setting_ip6_config_get_dns_search (NMSettingIP6Config *setting, guint32 i)
+{
+ NMSettingIP6ConfigPrivate *priv;
+
+ g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), NULL);
+
+ priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting);
+ g_return_val_if_fail (i <= g_slist_length (priv->dns_search), NULL);
+
+ return (const char *) g_slist_nth_data (priv->dns_search, i);
+}
+
+/**
+ * nm_setting_ip6_config_add_dns_search:
+ * @setting: the #NMSettingIP6Config
+ * @dns_search: the search domain to add
+ *
+ * Adds a new DNS search domain to the setting.
+ *
+ * Returns: %TRUE if the DNS search domain was added; %FALSE if the search
+ * domain was already known
+ **/
+gboolean
+nm_setting_ip6_config_add_dns_search (NMSettingIP6Config *setting,
+ const char *dns_search)
+{
+ NMSettingIP6ConfigPrivate *priv;
+ GSList *iter;
+
+ g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), FALSE);
+ g_return_val_if_fail (dns_search != NULL, FALSE);
+ g_return_val_if_fail (dns_search[0] != '\0', FALSE);
+
+ priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting);
+ for (iter = priv->dns_search; iter; iter = g_slist_next (iter)) {
+ if (!strcmp (dns_search, (char *) iter->data))
+ return FALSE;
+ }
+
+ priv->dns_search = g_slist_append (priv->dns_search, g_strdup (dns_search));
+ g_object_notify (G_OBJECT (setting), NM_SETTING_IP6_CONFIG_DNS_SEARCH);
+ return TRUE;
+}
+
+/**
+ * nm_setting_ip6_config_remove_dns_search:
+ * @setting: the #NMSettingIP6Config
+ * @i: index number of the DNS search domain
+ *
+ * Removes the DNS search domain at index @i.
+ **/
+void
+nm_setting_ip6_config_remove_dns_search (NMSettingIP6Config *setting, guint32 i)
+{
+ NMSettingIP6ConfigPrivate *priv;
+ GSList *elt;
+
+ g_return_if_fail (NM_IS_SETTING_IP6_CONFIG (setting));
+
+ priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting);
+ elt = g_slist_nth (priv->dns_search, i);
+ g_return_if_fail (elt != NULL);
+
+ g_free (elt->data);
+ priv->dns_search = g_slist_delete_link (priv->dns_search, elt);
+ g_object_notify (G_OBJECT (setting), NM_SETTING_IP6_CONFIG_DNS_SEARCH);
+}
+
+/**
+ * nm_setting_ip6_config_remove_dns_search_by_value:
+ * @setting: the #NMSettingIP6Config
+ * @dns_search: the search domain to remove
+ *
+ * Removes the DNS search domain @dns_search.
+ *
+ * Returns: %TRUE if the DNS search domain was found and removed; %FALSE if it was not.
+ *
+ * Since 0.9.10
+ **/
+gboolean
+nm_setting_ip6_config_remove_dns_search_by_value (NMSettingIP6Config *setting,
+ const char *dns_search)
+{
+ NMSettingIP6ConfigPrivate *priv;
+ GSList *iter;
+
+ g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), FALSE);
+ g_return_val_if_fail (dns_search != NULL, FALSE);
+ g_return_val_if_fail (dns_search[0] != '\0', FALSE);
+
+ priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting);
+ for (iter = priv->dns_search; iter; iter = g_slist_next (iter)) {
+ if (!strcmp (dns_search, (char *) iter->data)) {
+ priv->dns_search = g_slist_delete_link (priv->dns_search, iter);
+ g_object_notify (G_OBJECT (setting), NM_SETTING_IP6_CONFIG_DNS_SEARCH);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/**
+ * nm_setting_ip6_config_clear_dns_searches:
+ * @setting: the #NMSettingIP6Config
+ *
+ * Removes all configured DNS search domains.
+ **/
+void
+nm_setting_ip6_config_clear_dns_searches (NMSettingIP6Config *setting)
+{
+ g_return_if_fail (NM_IS_SETTING_IP6_CONFIG (setting));
+
+ g_slist_free_full (NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting)->dns_search, g_free);
+ NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting)->dns_search = NULL;
+ g_object_notify (G_OBJECT (setting), NM_SETTING_IP6_CONFIG_DNS_SEARCH);
+}
+
+/**
+ * nm_setting_ip6_config_get_num_addresses:
+ * @setting: the #NMSettingIP6Config
+ *
+ * Returns: the number of configured addresses
+ **/
+guint32
+nm_setting_ip6_config_get_num_addresses (NMSettingIP6Config *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), 0);
+
+ return g_slist_length (NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting)->addresses);
+}
+
+/**
+ * nm_setting_ip6_config_get_address:
+ * @setting: the #NMSettingIP6Config
+ * @i: index number of the address to return
+ *
+ * Returns: the address at index @i
+ **/
+NMIP6Address *
+nm_setting_ip6_config_get_address (NMSettingIP6Config *setting, guint32 i)
+{
+ NMSettingIP6ConfigPrivate *priv;
+
+ g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), NULL);
+
+ priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting);
+ g_return_val_if_fail (i <= g_slist_length (priv->addresses), NULL);
+
+ return (NMIP6Address *) g_slist_nth_data (priv->addresses, i);
+}
+
+/**
+ * nm_setting_ip6_config_add_address:
+ * @setting: the #NMSettingIP6Config
+ * @address: the new address to add
+ *
+ * Adds a new IPv6 address and associated information to the setting. The
+ * given address is duplicated internally and is not changed by this function.
+ *
+ * Returns: %TRUE if the address was added; %FALSE if the address was already
+ * known.
+ **/
+gboolean
+nm_setting_ip6_config_add_address (NMSettingIP6Config *setting,
+ NMIP6Address *address)
+{
+ NMSettingIP6ConfigPrivate *priv;
+ NMIP6Address *copy;
+ GSList *iter;
+
+ g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), FALSE);
+ g_return_val_if_fail (address != NULL, FALSE);
+
+ priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting);
+ for (iter = priv->addresses; iter; iter = g_slist_next (iter)) {
+ if (nm_ip6_address_compare ((NMIP6Address *) iter->data, address))
+ return FALSE;
+ }
+
+ copy = nm_ip6_address_dup (address);
+ priv->addresses = g_slist_append (priv->addresses, copy);
+ g_object_notify (G_OBJECT (setting), NM_SETTING_IP6_CONFIG_ADDRESSES);
+ return TRUE;
+}
+
+/**
+ * nm_setting_ip6_config_remove_address:
+ * @setting: the #NMSettingIP6Config
+ * @i: index number of the address to remove
+ *
+ * Removes the address at index @i.
+ **/
+void
+nm_setting_ip6_config_remove_address (NMSettingIP6Config *setting, guint32 i)
+{
+ NMSettingIP6ConfigPrivate *priv;
+ GSList *elt;
+
+ g_return_if_fail (NM_IS_SETTING_IP6_CONFIG (setting));
+
+ priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting);
+ elt = g_slist_nth (priv->addresses, i);
+ g_return_if_fail (elt != NULL);
+
+ nm_ip6_address_unref ((NMIP6Address *) elt->data);
+ priv->addresses = g_slist_delete_link (priv->addresses, elt);
+ g_object_notify (G_OBJECT (setting), NM_SETTING_IP6_CONFIG_ADDRESSES);
+}
+
+/**
+ * nm_setting_ip6_config_remove_address_by_value:
+ * @setting: the #NMSettingIP6Config
+ * @address: the address to remove
+ *
+ * Removes the address @address.
+ *
+ * Returns: %TRUE if the address was found and removed; %FALSE if it was not.
+ *
+ * Since: 0.9.10
+ **/
+gboolean
+nm_setting_ip6_config_remove_address_by_value (NMSettingIP6Config *setting,
+ NMIP6Address *address)
+{
+ NMSettingIP6ConfigPrivate *priv;
+ GSList *iter;
+
+ g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), FALSE);
+ g_return_val_if_fail (address != NULL, FALSE);
+
+ priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting);
+ for (iter = priv->addresses; iter; iter = g_slist_next (iter)) {
+ if (nm_ip6_address_compare ((NMIP6Address *) iter->data, address)) {
+ priv->addresses = g_slist_delete_link (priv->addresses, iter);
+ g_object_notify (G_OBJECT (setting), NM_SETTING_IP6_CONFIG_ADDRESSES);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/**
+ * nm_setting_ip6_config_clear_addresses:
+ * @setting: the #NMSettingIP6Config
+ *
+ * Removes all configured addresses.
+ **/
+void
+nm_setting_ip6_config_clear_addresses (NMSettingIP6Config *setting)
+{
+ NMSettingIP6ConfigPrivate *priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting);
+
+ g_return_if_fail (NM_IS_SETTING_IP6_CONFIG (setting));
+
+ g_slist_free_full (priv->addresses, (GDestroyNotify) nm_ip6_address_unref);
+ priv->addresses = NULL;
+ g_object_notify (G_OBJECT (setting), NM_SETTING_IP6_CONFIG_ADDRESSES);
+}
+
+/**
+ * nm_setting_ip6_config_get_num_routes:
+ * @setting: the #NMSettingIP6Config
+ *
+ * Returns: the number of configured routes
+ **/
+guint32
+nm_setting_ip6_config_get_num_routes (NMSettingIP6Config *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), 0);
+
+ return g_slist_length (NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting)->routes);
+}
+
+/**
+ * nm_setting_ip6_config_get_route:
+ * @setting: the #NMSettingIP6Config
+ * @i: index number of the route to return
+ *
+ * Returns: the route at index @i
+ **/
+NMIP6Route *
+nm_setting_ip6_config_get_route (NMSettingIP6Config *setting, guint32 i)
+{
+ NMSettingIP6ConfigPrivate *priv;
+
+ g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), NULL);
+
+ priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting);
+ g_return_val_if_fail (i <= g_slist_length (priv->routes), NULL);
+
+ return (NMIP6Route *) g_slist_nth_data (priv->routes, i);
+}
+
+/**
+ * nm_setting_ip6_config_add_route:
+ * @setting: the #NMSettingIP6Config
+ * @route: the route to add
+ *
+ * Adds a new IPv6 route and associated information to the setting. The
+ * given route is duplicated internally and is not changed by this function.
+ *
+ * Returns: %TRUE if the route was added; %FALSE if the route was already known.
+ **/
+gboolean
+nm_setting_ip6_config_add_route (NMSettingIP6Config *setting,
+ NMIP6Route *route)
+{
+ NMSettingIP6ConfigPrivate *priv;
+ NMIP6Route *copy;
+ GSList *iter;
+
+ g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), FALSE);
+ g_return_val_if_fail (route != NULL, FALSE);
+
+ priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting);
+ for (iter = priv->routes; iter; iter = g_slist_next (iter)) {
+ if (nm_ip6_route_compare ((NMIP6Route *) iter->data, route))
+ return FALSE;
+ }
+
+ copy = nm_ip6_route_dup (route);
+ priv->routes = g_slist_append (priv->routes, copy);
+ g_object_notify (G_OBJECT (setting), NM_SETTING_IP6_CONFIG_ROUTES);
+ return TRUE;
+}
+
+/**
+ * nm_setting_ip6_config_remove_route:
+ * @setting: the #NMSettingIP6Config
+ * @i: index number of the route
+ *
+ * Removes the route at index @i.
+ **/
+void
+nm_setting_ip6_config_remove_route (NMSettingIP6Config *setting, guint32 i)
+{
+ NMSettingIP6ConfigPrivate *priv;
+ GSList *elt;
+
+ g_return_if_fail (NM_IS_SETTING_IP6_CONFIG (setting));
+
+ priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting);
+ elt = g_slist_nth (priv->routes, i);
+ g_return_if_fail (elt != NULL);
+
+ nm_ip6_route_unref ((NMIP6Route *) elt->data);
+ priv->routes = g_slist_delete_link (priv->routes, elt);
+ g_object_notify (G_OBJECT (setting), NM_SETTING_IP6_CONFIG_ROUTES);
+}
+
+/**
+ * nm_setting_ip6_config_remove_route_by_value:
+ * @setting: the #NMSettingIP6Config
+ * @route: the route to remove
+ *
+ * Removes the route @route.
+ *
+ * Returns: %TRUE if the route was found and removed; %FALSE if it was not.
+ *
+ * Since: 0.9.10
+ **/
+gboolean
+nm_setting_ip6_config_remove_route_by_value (NMSettingIP6Config *setting,
+ NMIP6Route *route)
+{
+ NMSettingIP6ConfigPrivate *priv;
+ GSList *iter;
+
+ g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), FALSE);
+ g_return_val_if_fail (route != NULL, FALSE);
+
+ priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting);
+ for (iter = priv->routes; iter; iter = g_slist_next (iter)) {
+ if (nm_ip6_route_compare ((NMIP6Route *) iter->data, route)) {
+ nm_ip6_route_unref ((NMIP6Route *) iter->data);
+ priv->routes = g_slist_delete_link (priv->routes, iter);
+ g_object_notify (G_OBJECT (setting), NM_SETTING_IP6_CONFIG_ROUTES);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/**
+ * nm_setting_ip6_config_clear_routes:
+ * @setting: the #NMSettingIP6Config
+ *
+ * Removes all configured routes.
+ **/
+void
+nm_setting_ip6_config_clear_routes (NMSettingIP6Config *setting)
+{
+ NMSettingIP6ConfigPrivate *priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting);
+
+ g_return_if_fail (NM_IS_SETTING_IP6_CONFIG (setting));
+
+ g_slist_free_full (priv->routes, (GDestroyNotify) nm_ip6_route_unref);
+ priv->routes = NULL;
+ g_object_notify (G_OBJECT (setting), NM_SETTING_IP6_CONFIG_ROUTES);
+}
+
+/**
+ * nm_setting_ip6_config_get_ignore_auto_routes:
+ * @setting: the #NMSettingIP6Config
+ *
+ * Returns the value contained in the #NMSettingIP6Config:ignore-auto-routes
+ * property.
+ *
+ * Returns: %TRUE if automatically configured (ie via DHCP) routes should be
+ * ignored.
+ **/
+gboolean
+nm_setting_ip6_config_get_ignore_auto_routes (NMSettingIP6Config *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), FALSE);
+
+ return NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting)->ignore_auto_routes;
+}
+
+/**
+ * nm_setting_ip6_config_get_ignore_auto_dns:
+ * @setting: the #NMSettingIP6Config
+ *
+ * Returns the value contained in the #NMSettingIP6Config:ignore-auto-dns
+ * property.
+ *
+ * Returns: %TRUE if automatically configured (ie via DHCP or router
+ * advertisements) DNS information should be ignored.
+ **/
+gboolean
+nm_setting_ip6_config_get_ignore_auto_dns (NMSettingIP6Config *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), FALSE);
+
+ return NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting)->ignore_auto_dns;
+}
+
+/**
+ * nm_setting_ip6_config_get_never_default:
+ * @setting: the #NMSettingIP6Config
+ *
+ * Returns the value contained in the #NMSettingIP6Config:never-default
+ * property.
+ *
+ * Returns: %TRUE if this connection should never be the default connection
+ * for IPv6 addressing
+ **/
+gboolean
+nm_setting_ip6_config_get_never_default (NMSettingIP6Config *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), FALSE);
+
+ return NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting)->never_default;
+}
+
+/**
+ * nm_setting_ip6_config_get_may_fail:
+ * @setting: the #NMSettingIP6Config
+ *
+ * Returns the value contained in the #NMSettingIP6Config:may-fail
+ * property.
+ *
+ * Returns: %TRUE if this connection doesn't require IPv6 addressing to complete
+ * for the connection to succeed.
+ **/
+gboolean
+nm_setting_ip6_config_get_may_fail (NMSettingIP6Config *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), FALSE);
+
+ return NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting)->may_fail;
+}
+
+/**
+ * nm_setting_ip6_config_get_ip6_privacy:
+ * @setting: the #NMSettingIP6Config
+ *
+ * Returns the value contained in the #NMSettingIP6Config:ip6-privacy
+ * property.
+ *
+ * Returns: IPv6 Privacy Extensions configuration value (#NMSettingIP6ConfigPrivacy).
+ **/
+NMSettingIP6ConfigPrivacy
+nm_setting_ip6_config_get_ip6_privacy (NMSettingIP6Config *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN);
+
+ return NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting)->ip6_privacy;
+}
+
+static gboolean
+verify (NMSetting *setting, GSList *all_settings, GError **error)
+{
+ NMSettingIP6ConfigPrivate *priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting);
+
+ if (!priv->method) {
+ g_set_error_literal (error,
+ NM_SETTING_IP6_CONFIG_ERROR,
+ NM_SETTING_IP6_CONFIG_ERROR_MISSING_PROPERTY,
+ _("property is missing"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_IP6_CONFIG_SETTING_NAME, NM_SETTING_IP6_CONFIG_METHOD);
+ return FALSE;
+ }
+
+ if (!strcmp (priv->method, NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) {
+ if (!priv->addresses) {
+ g_set_error (error,
+ NM_SETTING_IP6_CONFIG_ERROR,
+ NM_SETTING_IP6_CONFIG_ERROR_MISSING_PROPERTY,
+ _("this property cannot be empty for '%s=%s'"),
+ NM_SETTING_IP6_CONFIG_METHOD, priv->method);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_IP6_CONFIG_SETTING_NAME, NM_SETTING_IP6_CONFIG_ADDRESSES);
+ return FALSE;
+ }
+ } else if ( !strcmp (priv->method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE)
+ || !strcmp (priv->method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL)
+ || !strcmp (priv->method, NM_SETTING_IP6_CONFIG_METHOD_SHARED)) {
+ if (g_slist_length (priv->dns)) {
+ g_set_error (error,
+ NM_SETTING_IP6_CONFIG_ERROR,
+ NM_SETTING_IP6_CONFIG_ERROR_NOT_ALLOWED_FOR_METHOD,
+ _("'%s' not allowed for %s=%s"),
+ _("this property is not allowed for '%s=%s'"),
+ NM_SETTING_IP6_CONFIG_METHOD, priv->method);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_IP6_CONFIG_SETTING_NAME, NM_SETTING_IP6_CONFIG_DNS);
+ return FALSE;
+ }
+
+ if (g_slist_length (priv->dns_search)) {
+ g_set_error (error,
+ NM_SETTING_IP6_CONFIG_ERROR,
+ NM_SETTING_IP6_CONFIG_ERROR_NOT_ALLOWED_FOR_METHOD,
+ _("this property is not allowed for '%s=%s'"),
+ NM_SETTING_IP6_CONFIG_METHOD, priv->method);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_IP6_CONFIG_SETTING_NAME, NM_SETTING_IP6_CONFIG_DNS_SEARCH);
+ return FALSE;
+ }
+
+ if (g_slist_length (priv->addresses)) {
+ g_set_error (error,
+ NM_SETTING_IP6_CONFIG_ERROR,
+ NM_SETTING_IP6_CONFIG_ERROR_NOT_ALLOWED_FOR_METHOD,
+ _("this property is not allowed for '%s=%s'"),
+ NM_SETTING_IP6_CONFIG_METHOD, priv->method);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_IP6_CONFIG_SETTING_NAME, NM_SETTING_IP6_CONFIG_ADDRESSES);
+ return FALSE;
+ }
+ } else if ( !strcmp (priv->method, NM_SETTING_IP6_CONFIG_METHOD_AUTO)
+ || !strcmp (priv->method, NM_SETTING_IP6_CONFIG_METHOD_DHCP)) {
+ /* nothing to do */
+ } else {
+ g_set_error_literal (error,
+ NM_SETTING_IP6_CONFIG_ERROR,
+ NM_SETTING_IP6_CONFIG_ERROR_INVALID_PROPERTY,
+ _("property is invalid"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_IP6_CONFIG_SETTING_NAME, NM_SETTING_IP6_CONFIG_METHOD);
+ return FALSE;
+ }
+
+ if (priv->dhcp_hostname && !strlen (priv->dhcp_hostname)) {
+ g_set_error_literal (error,
+ NM_SETTING_IP6_CONFIG_ERROR,
+ NM_SETTING_IP6_CONFIG_ERROR_INVALID_PROPERTY,
+ _("property is missing"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_IP6_CONFIG_SETTING_NAME, NM_SETTING_IP6_CONFIG_DHCP_HOSTNAME);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+static void
+nm_setting_ip6_config_init (NMSettingIP6Config *setting)
+{
+}
+
+static void
+finalize (GObject *object)
+{
+ NMSettingIP6ConfigPrivate *priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (object);
+
+ g_free (priv->method);
+ g_free (priv->dhcp_hostname);
+
+ g_slist_free_full (priv->dns, g_free);
+ g_slist_free_full (priv->dns_search, g_free);
+ g_slist_free_full (priv->addresses, g_free);
+ g_slist_free_full (priv->routes, g_free);
+
+ G_OBJECT_CLASS (nm_setting_ip6_config_parent_class)->finalize (object);
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ NMSettingIP6ConfigPrivate *priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_METHOD:
+ g_free (priv->method);
+ priv->method = g_value_dup_string (value);
+ break;
+ case PROP_DNS:
+ g_slist_free_full (priv->dns, g_free);
+ priv->dns = nm_utils_ip6_dns_from_gvalue (value);
+ break;
+ case PROP_DNS_SEARCH:
+ g_slist_free_full (priv->dns_search, g_free);
+ priv->dns_search = g_value_dup_boxed (value);
+ break;
+ case PROP_ADDRESSES:
+ g_slist_free_full (priv->addresses, g_free);
+ priv->addresses = nm_utils_ip6_addresses_from_gvalue (value);
+ break;
+ case PROP_ROUTES:
+ g_slist_free_full (priv->routes, g_free);
+ priv->routes = nm_utils_ip6_routes_from_gvalue (value);
+ break;
+ case PROP_IGNORE_AUTO_ROUTES:
+ priv->ignore_auto_routes = g_value_get_boolean (value);
+ break;
+ case PROP_IGNORE_AUTO_DNS:
+ priv->ignore_auto_dns = g_value_get_boolean (value);
+ break;
+ case PROP_DHCP_HOSTNAME:
+ g_free (priv->dhcp_hostname);
+ priv->dhcp_hostname = g_value_dup_string (value);
+ break;
+ case PROP_NEVER_DEFAULT:
+ priv->never_default = g_value_get_boolean (value);
+ break;
+ case PROP_MAY_FAIL:
+ priv->may_fail = g_value_get_boolean (value);
+ break;
+ case PROP_IP6_PRIVACY:
+ priv->ip6_privacy = g_value_get_int (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ NMSettingIP6ConfigPrivate *priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_METHOD:
+ g_value_set_string (value, priv->method);
+ break;
+ case PROP_DNS:
+ nm_utils_ip6_dns_to_gvalue (priv->dns, value);
+ break;
+ case PROP_DNS_SEARCH:
+ g_value_set_boxed (value, priv->dns_search);
+ break;
+ case PROP_ADDRESSES:
+ nm_utils_ip6_addresses_to_gvalue (priv->addresses, value);
+ break;
+ case PROP_ROUTES:
+ nm_utils_ip6_routes_to_gvalue (priv->routes, value);
+ break;
+ case PROP_IGNORE_AUTO_ROUTES:
+ g_value_set_boolean (value, priv->ignore_auto_routes);
+ break;
+ case PROP_IGNORE_AUTO_DNS:
+ g_value_set_boolean (value, priv->ignore_auto_dns);
+ break;
+ case PROP_DHCP_HOSTNAME:
+ g_value_set_string (value, priv->dhcp_hostname);
+ break;
+ case PROP_NEVER_DEFAULT:
+ g_value_set_boolean (value, priv->never_default);
+ break;
+ case PROP_MAY_FAIL:
+ g_value_set_boolean (value, priv->may_fail);
+ break;
+ case PROP_IP6_PRIVACY:
+ g_value_set_int (value, priv->ip6_privacy);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nm_setting_ip6_config_class_init (NMSettingIP6ConfigClass *setting_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
+ NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+
+ g_type_class_add_private (setting_class, sizeof (NMSettingIP6ConfigPrivate));
+
+ /* virtual methods */
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+ object_class->finalize = finalize;
+ parent_class->verify = verify;
+
+ /* Properties */
+ /**
+ * NMSettingIP6Config:method:
+ *
+ * IPv6 configuration method. If "auto" is specified then the appropriate
+ * automatic method (PPP, router advertisement, etc) is used for the device
+ * and most other properties can be left unset. To force the use of DHCP
+ * only, specify "dhcp"; this method is only valid for Ethernet- based
+ * hardware. If "link-local" is specified, then an IPv6 link-local address
+ * will be assigned to the interface. If "manual" is specified, static IP
+ * addressing is used and at least one IP address must be given in the
+ * "addresses" property. If "ignore" is specified, IPv6 configuration is
+ * not done. This property must be set. Note: the "shared" method is not
+ * yet supported.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_METHOD,
+ g_param_spec_string (NM_SETTING_IP6_CONFIG_METHOD, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingIP6Config:dhcp-hostname:
+ *
+ * The specified name will be sent to the DHCP server when acquiring a
+ * lease.
+ *
+ * Since: 0.9.8
+ **/
+ g_object_class_install_property
+ (object_class, PROP_DHCP_HOSTNAME,
+ g_param_spec_string (NM_SETTING_IP6_CONFIG_DHCP_HOSTNAME, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingIP6Config:dns:
+ *
+ * Array of DNS servers, where each member of the array is a byte array
+ * containing the IPv6 address of the DNS server (in network byte order).
+ * For the "auto" method, these DNS servers are appended to those (if any)
+ * returned by automatic configuration. DNS servers cannot be used with the
+ * "shared" or "link-local" methods as there is no usptream network. In all
+ * other methods, these DNS servers are used as the only DNS servers for
+ * this connection.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_DNS,
+ _nm_param_spec_specialized (NM_SETTING_IP6_CONFIG_DNS, "", "",
+ DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UCHAR,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingIP6Config:dns-search:
+ *
+ * List of DNS search domains. For the "auto" method, these search domains
+ * are appended to those returned by automatic configuration. Search domains
+ * cannot be used with the "shared" or "link-local" methods as there is no
+ * upstream network. In all other methods, these search domains are used as
+ * the only search domains for this connection.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_DNS_SEARCH,
+ _nm_param_spec_specialized (NM_SETTING_IP6_CONFIG_DNS_SEARCH, "", "",
+ DBUS_TYPE_G_LIST_OF_STRING,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingIP6Config:addresses:
+ *
+ * Array of IPv6 address structures. Each IPv6 address structure is
+ * composed of 3 members, the first being a byte array containing the IPv6
+ * address (network byte order), the second a 32-bit integer containing the
+ * IPv6 address prefix, and the third a byte array containing the IPv6
+ * address (network byte order) of the gateway associated with this address,
+ * if any. If no gateway is given, the third element should be given as all
+ * zeros. For the "auto" method, given IP addresses are appended to those
+ * returned by automatic configuration. Addresses cannot be used with the
+ * "shared" or "link-local" methods as the interface is automatically
+ * assigned an address with these methods.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_ADDRESSES,
+ _nm_param_spec_specialized (NM_SETTING_IP6_CONFIG_ADDRESSES, "", "",
+ DBUS_TYPE_G_ARRAY_OF_IP6_ADDRESS,
+ G_PARAM_READWRITE |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingIP6Config:routes:
+ *
+ * Array of IPv6 route structures. Each IPv6 route structure is composed of
+ * 4 members; the first being the destination IPv6 network or address
+ * (network byte order) as a byte array, the second the destination network
+ * or address IPv6 prefix, the third being the next-hop IPv6 address
+ * (network byte order) if any, and the fourth being the route metric. For
+ * the "auto" method, given IP routes are appended to those returned by
+ * automatic configuration. Routes cannot be used with the "shared" or
+ * "link-local" methods because there is no upstream network.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_ROUTES,
+ _nm_param_spec_specialized (NM_SETTING_IP6_CONFIG_ROUTES, "", "",
+ DBUS_TYPE_G_ARRAY_OF_IP6_ROUTE,
+ G_PARAM_READWRITE |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingIP6Config:ignore-auto-routes:
+ *
+ * When the method is set to "auto" or "dhcp" and this property is set to
+ * %TRUE, automatically configured routes are ignored and only routes
+ * specified in the #NMSettingIP6Config:routes property, if any, are used.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_IGNORE_AUTO_ROUTES,
+ g_param_spec_boolean (NM_SETTING_IP6_CONFIG_IGNORE_AUTO_ROUTES, "", "",
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingIP6Config:ignore-auto-dns:
+ *
+ * When the method is set to "auto" or "dhcp" and this property is set to
+ * %TRUE, automatically configured nameservers and search domains are
+ * ignored and only nameservers and search domains specified in the
+ * #NMSettingIP6Config:dns and #NMSettingIP6Config:dns-search properties, if
+ * any, are used.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_IGNORE_AUTO_DNS,
+ g_param_spec_boolean (NM_SETTING_IP6_CONFIG_IGNORE_AUTO_DNS, "", "",
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingIP6Config:never-default:
+ *
+ * If %TRUE, this connection will never be the default IPv6 connection,
+ * meaning it will never be assigned the default IPv6 route by
+ * NetworkManager.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_NEVER_DEFAULT,
+ g_param_spec_boolean (NM_SETTING_IP6_CONFIG_NEVER_DEFAULT, "", "",
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingIP6Config:may-fail:
+ *
+ * If %TRUE, allow overall network configuration to proceed even if IPv6
+ * configuration times out. Note that at least one IP configuration must
+ * succeed or overall network configuration will still fail. For example,
+ * in IPv4-only networks, setting this property to %TRUE allows the overall
+ * network configuration to succeed if IPv6 configuration fails but IPv4
+ * configuration completes successfully.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_MAY_FAIL,
+ g_param_spec_boolean (NM_SETTING_IP6_CONFIG_MAY_FAIL, "", "",
+ TRUE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingIP6Config:ip6-privacy:
+ *
+ * Configure IPv6 Privacy Extensions for SLAAC, described in RFC4941. If
+ * enabled, it makes the kernel generate a temporary IPv6 address in
+ * addition to the public one generated from MAC address via modified
+ * EUI-64. This enhances privacy, but could cause problems in some
+ * applications, on the other hand. The permitted values are: 0: disabled,
+ * 1: enabled (prefer public address), 2: enabled (prefer temporary
+ * addresses).
+ **/
+ g_object_class_install_property
+ (object_class, PROP_IP6_PRIVACY,
+ g_param_spec_int (NM_SETTING_IP6_CONFIG_IP6_PRIVACY, "", "",
+ NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN,
+ NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR,
+ NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+}
+
+/********************************************************************/
+
+struct NMIP6Address {
+ guint32 refcount;
+ struct in6_addr address;
+ guint32 prefix;
+ struct in6_addr gateway;
+};
+
+/**
+ * nm_ip6_address_new:
+ *
+ * Creates and returns a new #NMIP6Address object.
+ *
+ * Returns: (transfer full): the new empty #NMIP6Address object
+ **/
+NMIP6Address *
+nm_ip6_address_new (void)
+{
+ NMIP6Address *address;
+
+ address = g_malloc0 (sizeof (NMIP6Address));
+ address->refcount = 1;
+ return address;
+}
+
+/**
+ * nm_ip6_address_dup:
+ * @source: the #NMIP6Address object to copy
+ *
+ * Copies a given #NMIP6Address object and returns the copy.
+ *
+ * Returns: (transfer full): the copy of the given #NMIP6Address copy
+ **/
+NMIP6Address *
+nm_ip6_address_dup (NMIP6Address *source)
+{
+ NMIP6Address *address;
+
+ g_return_val_if_fail (source != NULL, NULL);
+ g_return_val_if_fail (source->refcount > 0, NULL);
+
+ address = nm_ip6_address_new ();
+ address->prefix = source->prefix;
+ memcpy (&address->address, &source->address, sizeof (struct in6_addr));
+ memcpy (&address->gateway, &source->gateway, sizeof (struct in6_addr));
+
+ return address;
+}
+
+/**
+ * nm_ip6_address_ref:
+ * @address: the #NMIP6Address
+ *
+ * Increases the reference count of the object.
+ **/
+void
+nm_ip6_address_ref (NMIP6Address *address)
+{
+ g_return_if_fail (address != NULL);
+ g_return_if_fail (address->refcount > 0);
+
+ address->refcount++;
+}
+
+/**
+ * nm_ip6_address_unref:
+ * @address: the #NMIP6Address
+ *
+ * Decreases the reference count of the object. If the reference count
+ * reaches zero, the object will be destroyed.
+ **/
+void
+nm_ip6_address_unref (NMIP6Address *address)
+{
+ g_return_if_fail (address != NULL);
+ g_return_if_fail (address->refcount > 0);
+
+ address->refcount--;
+ if (address->refcount == 0) {
+ memset (address, 0, sizeof (NMIP6Address));
+ g_free (address);
+ }
+}
+
+/**
+ * nm_ip6_address_compare:
+ * @address: the #NMIP6Address
+ * @other: the #NMIP6Address to compare @address to.
+ *
+ * Determines if two #NMIP6Address objects contain the same values.
+ *
+ * Returns: %TRUE if the objects contain the same values, %FALSE if they do not.
+ **/
+gboolean
+nm_ip6_address_compare (NMIP6Address *address, NMIP6Address *other)
+{
+ g_return_val_if_fail (address != NULL, FALSE);
+ g_return_val_if_fail (address->refcount > 0, FALSE);
+
+ g_return_val_if_fail (other != NULL, FALSE);
+ g_return_val_if_fail (other->refcount > 0, FALSE);
+
+ if ( memcmp (&address->address, &other->address, sizeof (struct in6_addr))
+ || address->prefix != other->prefix
+ || memcmp (&address->gateway, &other->gateway, sizeof (struct in6_addr)))
+ return FALSE;
+ return TRUE;
+}
+
+/**
+ * nm_ip6_address_get_address:
+ * @address: the #NMIP6Address
+ *
+ * Gets the IPv6 address property of this address object.
+ *
+ * Returns: (array fixed-size=16) (element-type guint8) (transfer none):
+ * the IPv6 address
+ **/
+const struct in6_addr *
+nm_ip6_address_get_address (NMIP6Address *address)
+{
+ g_return_val_if_fail (address != NULL, NULL);
+ g_return_val_if_fail (address->refcount > 0, NULL);
+
+ return &address->address;
+}
+
+/**
+ * nm_ip6_address_set_address:
+ * @address: the #NMIP6Address
+ * @addr: the IPv6 address
+ *
+ * Sets the IPv6 address property of this object.
+ **/
+void
+nm_ip6_address_set_address (NMIP6Address *address, const struct in6_addr *addr)
+{
+ g_return_if_fail (address != NULL);
+ g_return_if_fail (address->refcount > 0);
+ g_return_if_fail (addr != NULL);
+
+ memcpy (&address->address, addr, sizeof (struct in6_addr));
+}
+
+/**
+ * nm_ip6_address_get_prefix:
+ * @address: the #NMIP6Address
+ *
+ * Gets the IPv6 address prefix property of this address object.
+ *
+ * Returns: the IPv6 address prefix
+ **/
+guint32
+nm_ip6_address_get_prefix (NMIP6Address *address)
+{
+ g_return_val_if_fail (address != NULL, 0);
+ g_return_val_if_fail (address->refcount > 0, 0);
+
+ return address->prefix;
+}
+
+/**
+ * nm_ip6_address_set_prefix:
+ * @address: the #NMIP6Address
+ * @prefix: the address prefix, a number between 0 and 128 inclusive
+ *
+ * Sets the IPv6 address prefix.
+ **/
+void
+nm_ip6_address_set_prefix (NMIP6Address *address, guint32 prefix)
+{
+ g_return_if_fail (address != NULL);
+ g_return_if_fail (address->refcount > 0);
+ g_return_if_fail (prefix <= 128);
+ g_return_if_fail (prefix > 0);
+
+ address->prefix = prefix;
+}
+
+/**
+ * nm_ip6_address_get_gateway:
+ * @address: the #NMIP6Address
+ *
+ * Gets the IPv6 default gateway property of this address object.
+ *
+ * Returns: (array fixed-size=16) (element-type guint8) (transfer none):
+ * the IPv6 gateway address
+ **/
+const struct in6_addr *
+nm_ip6_address_get_gateway (NMIP6Address *address)
+{
+ g_return_val_if_fail (address != NULL, NULL);
+ g_return_val_if_fail (address->refcount > 0, NULL);
+
+ return &address->gateway;
+}
+
+/**
+ * nm_ip6_address_set_gateway:
+ * @address: the #NMIP6Address
+ * @gateway: the IPv6 default gateway
+ *
+ * Sets the IPv6 default gateway property of this address object.
+ **/
+void
+nm_ip6_address_set_gateway (NMIP6Address *address, const struct in6_addr *gateway)
+{
+ g_return_if_fail (address != NULL);
+ g_return_if_fail (address->refcount > 0);
+ g_return_if_fail (gateway != NULL);
+
+ memcpy (&address->gateway, gateway, sizeof (struct in6_addr));
+}
+
+/********************************************************************/
+
+struct NMIP6Route {
+ guint32 refcount;
+
+ struct in6_addr dest;
+ guint32 prefix;
+ struct in6_addr next_hop;
+ guint32 metric; /* lower metric == more preferred */
+};
+
+/**
+ * nm_ip6_route_new:
+ *
+ * Creates and returns a new #NMIP6Route object.
+ *
+ * Returns: (transfer full): the new empty #NMIP6Route object
+ **/
+NMIP6Route *
+nm_ip6_route_new (void)
+{
+ NMIP6Route *route;
+
+ route = g_malloc0 (sizeof (NMIP6Route));
+ route->refcount = 1;
+ return route;
+}
+
+/**
+ * nm_ip6_route_dup:
+ * @source: the #NMIP6Route object to copy
+ *
+ * Copies a given #NMIP6Route object and returns the copy.
+ *
+ * Returns: (transfer full): the copy of the given #NMIP6Route copy
+ **/
+NMIP6Route *
+nm_ip6_route_dup (NMIP6Route *source)
+{
+ NMIP6Route *route;
+
+ g_return_val_if_fail (source != NULL, NULL);
+ g_return_val_if_fail (source->refcount > 0, NULL);
+
+ route = nm_ip6_route_new ();
+ route->prefix = source->prefix;
+ route->metric = source->metric;
+ memcpy (&route->dest, &source->dest, sizeof (struct in6_addr));
+ memcpy (&route->next_hop, &source->next_hop, sizeof (struct in6_addr));
+
+ return route;
+}
+
+/**
+ * nm_ip6_route_ref:
+ * @route: the #NMIP6Route
+ *
+ * Increases the reference count of the object.
+ **/
+void
+nm_ip6_route_ref (NMIP6Route *route)
+{
+ g_return_if_fail (route != NULL);
+ g_return_if_fail (route->refcount > 0);
+
+ route->refcount++;
+}
+
+/**
+ * nm_ip6_route_unref:
+ * @route: the #NMIP6Route
+ *
+ * Decreases the reference count of the object. If the reference count
+ * reaches zero, the object will be destroyed.
+ **/
+void
+nm_ip6_route_unref (NMIP6Route *route)
+{
+ g_return_if_fail (route != NULL);
+ g_return_if_fail (route->refcount > 0);
+
+ route->refcount--;
+ if (route->refcount == 0) {
+ memset (route, 0, sizeof (NMIP6Route));
+ g_free (route);
+ }
+}
+
+/**
+ * nm_ip6_route_compare:
+ * @route: the #NMIP6Route
+ * @other: the #NMIP6Route to compare @route to.
+ *
+ * Determines if two #NMIP6Route objects contain the same values.
+ *
+ * Returns: %TRUE if the objects contain the same values, %FALSE if they do not.
+ **/
+gboolean
+nm_ip6_route_compare (NMIP6Route *route, NMIP6Route *other)
+{
+ g_return_val_if_fail (route != NULL, FALSE);
+ g_return_val_if_fail (route->refcount > 0, FALSE);
+
+ g_return_val_if_fail (other != NULL, FALSE);
+ g_return_val_if_fail (other->refcount > 0, FALSE);
+
+ if ( memcmp (&route->dest, &other->dest, sizeof (struct in6_addr))
+ || route->prefix != other->prefix
+ || memcmp (&route->next_hop, &other->next_hop, sizeof (struct in6_addr))
+ || route->metric != other->metric)
+ return FALSE;
+ return TRUE;
+}
+
+/**
+ * nm_ip6_route_get_dest:
+ * @route: the #NMIP6Route
+ *
+ * Gets the IPv6 destination address property of this route object.
+ *
+ * Returns: (array fixed-size=16) (element-type guint8) (transfer none):
+ * the IPv6 address of destination
+ **/
+const struct in6_addr *
+nm_ip6_route_get_dest (NMIP6Route *route)
+{
+ g_return_val_if_fail (route != NULL, NULL);
+ g_return_val_if_fail (route->refcount > 0, NULL);
+
+ return &route->dest;
+}
+
+/**
+ * nm_ip6_route_set_dest:
+ * @route: the #NMIP6Route
+ * @dest: the destination address
+ *
+ * Sets the IPv6 destination address property of this route object.
+ **/
+void
+nm_ip6_route_set_dest (NMIP6Route *route, const struct in6_addr *dest)
+{
+ g_return_if_fail (route != NULL);
+ g_return_if_fail (route->refcount > 0);
+ g_return_if_fail (dest != NULL);
+
+ memcpy (&route->dest, dest, sizeof (struct in6_addr));
+}
+
+/**
+ * nm_ip6_route_get_prefix:
+ * @route: the #NMIP6Route
+ *
+ * Gets the IPv6 prefix (ie "32" or "64" etc) of this route.
+ *
+ * Returns: the IPv6 prefix
+ **/
+guint32
+nm_ip6_route_get_prefix (NMIP6Route *route)
+{
+ g_return_val_if_fail (route != NULL, 0);
+ g_return_val_if_fail (route->refcount > 0, 0);
+
+ return route->prefix;
+}
+
+/**
+ * nm_ip6_route_set_prefix:
+ * @route: the #NMIP6Route
+ * @prefix: the prefix, a number between 1 and 128 inclusive
+ *
+ * Sets the IPv6 prefix of this route.
+ **/
+void
+nm_ip6_route_set_prefix (NMIP6Route *route, guint32 prefix)
+{
+ g_return_if_fail (route != NULL);
+ g_return_if_fail (route->refcount > 0);
+ g_return_if_fail (prefix <= 128);
+ g_return_if_fail (prefix > 0);
+
+ route->prefix = prefix;
+}
+
+/**
+ * nm_ip6_route_get_next_hop:
+ * @route: the #NMIP6Route
+ *
+ * Gets the IPv6 address of the next hop of this route.
+ *
+ * Returns: (array fixed-size=16) (element-type guint8) (transfer none):
+ * the IPv6 address of next hop
+ **/
+const struct in6_addr *
+nm_ip6_route_get_next_hop (NMIP6Route *route)
+{
+ g_return_val_if_fail (route != NULL, NULL);
+ g_return_val_if_fail (route->refcount > 0, NULL);
+
+ return &route->next_hop;
+}
+
+/**
+ * nm_ip6_route_set_next_hop:
+ * @route: the #NMIP6Route
+ * @next_hop: the IPv6 address of the next hop
+ *
+ * Sets the IPv6 address of the next hop of this route.
+ **/
+void
+nm_ip6_route_set_next_hop (NMIP6Route *route, const struct in6_addr *next_hop)
+{
+ g_return_if_fail (route != NULL);
+ g_return_if_fail (route->refcount > 0);
+ g_return_if_fail (next_hop != NULL);
+
+ memcpy (&route->next_hop, next_hop, sizeof (struct in6_addr));
+}
+
+/**
+ * nm_ip6_route_get_metric:
+ * @route: the #NMIP6Route
+ *
+ * Gets the route metric property of this route object; lower values indicate
+ * "better" or more preferred routes.
+ *
+ * Returns: the route metric
+ **/
+guint32
+nm_ip6_route_get_metric (NMIP6Route *route)
+{
+ g_return_val_if_fail (route != NULL, 0);
+ g_return_val_if_fail (route->refcount > 0, 0);
+
+ return route->metric;
+}
+
+/**
+ * nm_ip6_route_set_metric:
+ * @route: the #NMIP6Route
+ * @metric: the route metric
+ *
+ * Sets the route metric property of this route object; lower values indicate
+ * "better" or more preferred routes.
+ **/
+void
+nm_ip6_route_set_metric (NMIP6Route *route, guint32 metric)
+{
+ g_return_if_fail (route != NULL);
+ g_return_if_fail (route->refcount > 0);
+
+ route->metric = metric;
+}
diff --git a/libnm-core/nm-setting-ip6-config.h b/libnm-core/nm-setting-ip6-config.h
new file mode 100644
index 0000000000..4486a40e26
--- /dev/null
+++ b/libnm-core/nm-setting-ip6-config.h
@@ -0,0 +1,256 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2014 Red Hat, Inc.
+ */
+
+#ifndef NM_SETTING_IP6_CONFIG_H
+#define NM_SETTING_IP6_CONFIG_H
+
+#include <arpa/inet.h>
+
+#include "nm-setting.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_SETTING_IP6_CONFIG (nm_setting_ip6_config_get_type ())
+#define NM_SETTING_IP6_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_IP6_CONFIG, NMSettingIP6Config))
+#define NM_SETTING_IP6_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_IP6CONFIG, NMSettingIP6ConfigClass))
+#define NM_IS_SETTING_IP6_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_IP6_CONFIG))
+#define NM_IS_SETTING_IP6_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_IP6_CONFIG))
+#define NM_SETTING_IP6_CONFIG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_IP6_CONFIG, NMSettingIP6ConfigClass))
+
+#define NM_SETTING_IP6_CONFIG_SETTING_NAME "ipv6"
+
+/**
+ * NMSettingIP6ConfigError:
+ * @NM_SETTING_IP6_CONFIG_ERROR_UNKNOWN: unknown or unclassified error
+ * @NM_SETTING_IP6_CONFIG_ERROR_INVALID_PROPERTY: the property was invalid
+ * @NM_SETTING_IP6_CONFIG_ERROR_MISSING_PROPERTY: the property was missing and is
+ * required
+ * @NM_SETTING_IP6_CONFIG_ERROR_NOT_ALLOWED_FOR_METHOD: the property's value is
+ * not valid with the given IPv6 method
+ */
+typedef enum {
+ NM_SETTING_IP6_CONFIG_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/
+ NM_SETTING_IP6_CONFIG_ERROR_INVALID_PROPERTY, /*< nick=InvalidProperty >*/
+ NM_SETTING_IP6_CONFIG_ERROR_MISSING_PROPERTY, /*< nick=MissingProperty >*/
+ NM_SETTING_IP6_CONFIG_ERROR_NOT_ALLOWED_FOR_METHOD /*< nick=NotAllowedForMethod >*/
+} NMSettingIP6ConfigError;
+
+#define NM_SETTING_IP6_CONFIG_ERROR nm_setting_ip6_config_error_quark ()
+GQuark nm_setting_ip6_config_error_quark (void);
+
+#define NM_SETTING_IP6_CONFIG_METHOD "method"
+#define NM_SETTING_IP6_CONFIG_DNS "dns"
+#define NM_SETTING_IP6_CONFIG_DNS_SEARCH "dns-search"
+#define NM_SETTING_IP6_CONFIG_ADDRESSES "addresses"
+#define NM_SETTING_IP6_CONFIG_ROUTES "routes"
+#define NM_SETTING_IP6_CONFIG_IGNORE_AUTO_ROUTES "ignore-auto-routes"
+#define NM_SETTING_IP6_CONFIG_IGNORE_AUTO_DNS "ignore-auto-dns"
+#define NM_SETTING_IP6_CONFIG_NEVER_DEFAULT "never-default"
+#define NM_SETTING_IP6_CONFIG_MAY_FAIL "may-fail"
+#define NM_SETTING_IP6_CONFIG_IP6_PRIVACY "ip6-privacy"
+#define NM_SETTING_IP6_CONFIG_DHCP_HOSTNAME "dhcp-hostname"
+
+
+/**
+ * NM_SETTING_IP6_CONFIG_METHOD_IGNORE:
+ *
+ * IPv6 is not required or is handled by some other mechanism, and NetworkManager
+ * should not configure IPv6 for this connection.
+ */
+#define NM_SETTING_IP6_CONFIG_METHOD_IGNORE "ignore"
+
+/**
+ * NM_SETTING_IP6_CONFIG_METHOD_AUTO:
+ *
+ * IPv6 configuration should be automatically determined via a method appropriate
+ * for the hardware interface, ie router advertisements, DHCP, or PPP or some
+ * other device-specific manner.
+ */
+#define NM_SETTING_IP6_CONFIG_METHOD_AUTO "auto"
+
+/**
+ * NM_SETTING_IP6_CONFIG_METHOD_DHCP:
+ *
+ * IPv6 configuration should be automatically determined via DHCPv6 only and
+ * router advertisements should be ignored.
+ */
+#define NM_SETTING_IP6_CONFIG_METHOD_DHCP "dhcp"
+
+/**
+ * NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL:
+ *
+ * IPv6 configuration should be automatically configured for link-local-only
+ * operation.
+ */
+#define NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL "link-local"
+
+/**
+ * NM_SETTING_IP6_CONFIG_METHOD_MANUAL:
+ *
+ * All necessary IPv6 configuration (addresses, prefix, DNS, etc) is specified
+ * in the setting's properties.
+ */
+#define NM_SETTING_IP6_CONFIG_METHOD_MANUAL "manual"
+
+/**
+ * NM_SETTING_IP6_CONFIG_METHOD_SHARED:
+ *
+ * This connection specifies configuration that allows other computers to
+ * connect through it to the default network (usually the Internet). The
+ * connection's interface will be assigned a private address, and router
+ * advertisements, a caching DNS server, and Network Address Translation (NAT)
+ * functionality will be started on this connection's interface to allow other
+ * devices to connect through that interface to the default network. (not yet
+ * supported for IPv6)
+ */
+#define NM_SETTING_IP6_CONFIG_METHOD_SHARED "shared"
+
+/**
+ * NMSettingIP6ConfigPrivacy:
+ * @NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN: unknown or no value specified
+ * @NM_SETTING_IP6_CONFIG_PRIVACY_DISABLED: IPv6 Privacy Extensions are disabled
+ * @NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR: IPv6 Privacy Extensions
+ * are enabled, but public addresses are preferred over temporary addresses
+ * @NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR: IPv6 Privacy Extensions
+ * are enabled and temporary addresses are preferred over public addresses
+ *
+ * #NMSettingIP6ConfigPrivacy values indicate if and how IPv6 Privacy
+ * Extensions are used (RFC4941).
+ */
+typedef enum {
+ NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN = -1,
+ NM_SETTING_IP6_CONFIG_PRIVACY_DISABLED = 0,
+ NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR = 1,
+ NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR = 2
+} NMSettingIP6ConfigPrivacy;
+
+
+typedef struct NMIP6Address NMIP6Address;
+
+GType nm_ip6_address_get_type (void);
+
+NMIP6Address * nm_ip6_address_new (void);
+NMIP6Address * nm_ip6_address_dup (NMIP6Address *source);
+void nm_ip6_address_ref (NMIP6Address *address);
+void nm_ip6_address_unref (NMIP6Address *address);
+/* Return TRUE if addresses are identical */
+gboolean nm_ip6_address_compare (NMIP6Address *address, NMIP6Address *other);
+
+const struct in6_addr *nm_ip6_address_get_address (NMIP6Address *address);
+void nm_ip6_address_set_address (NMIP6Address *address,
+ const struct in6_addr *addr);
+
+guint32 nm_ip6_address_get_prefix (NMIP6Address *address);
+void nm_ip6_address_set_prefix (NMIP6Address *address,
+ guint32 prefix);
+
+const struct in6_addr *nm_ip6_address_get_gateway (NMIP6Address *address);
+void nm_ip6_address_set_gateway (NMIP6Address *address,
+ const struct in6_addr *gateway);
+
+typedef struct NMIP6Route NMIP6Route;
+
+GType nm_ip6_route_get_type (void);
+
+NMIP6Route * nm_ip6_route_new (void);
+NMIP6Route * nm_ip6_route_dup (NMIP6Route *source);
+void nm_ip6_route_ref (NMIP6Route *route);
+void nm_ip6_route_unref (NMIP6Route *route);
+/* Return TRUE if routes are identical */
+gboolean nm_ip6_route_compare (NMIP6Route *route, NMIP6Route *other);
+
+const struct in6_addr *nm_ip6_route_get_dest (NMIP6Route *route);
+void nm_ip6_route_set_dest (NMIP6Route *route,
+ const struct in6_addr *dest);
+
+guint32 nm_ip6_route_get_prefix (NMIP6Route *route);
+void nm_ip6_route_set_prefix (NMIP6Route *route,
+ guint32 prefix);
+
+const struct in6_addr *nm_ip6_route_get_next_hop (NMIP6Route *route);
+void nm_ip6_route_set_next_hop (NMIP6Route *route,
+ const struct in6_addr *next_hop);
+
+guint32 nm_ip6_route_get_metric (NMIP6Route *route);
+void nm_ip6_route_set_metric (NMIP6Route *route,
+ guint32 metric);
+
+typedef struct {
+ NMSetting parent;
+} NMSettingIP6Config;
+
+typedef struct {
+ NMSettingClass parent;
+
+ /* Padding for future expansion */
+ void (*_reserved1) (void);
+ void (*_reserved2) (void);
+ void (*_reserved3) (void);
+ void (*_reserved4) (void);
+} NMSettingIP6ConfigClass;
+
+GType nm_setting_ip6_config_get_type (void);
+
+NMSetting * nm_setting_ip6_config_new (void);
+const char * nm_setting_ip6_config_get_method (NMSettingIP6Config *setting);
+
+guint32 nm_setting_ip6_config_get_num_dns (NMSettingIP6Config *setting);
+const struct in6_addr *nm_setting_ip6_config_get_dns (NMSettingIP6Config *setting, guint32 i);
+gboolean nm_setting_ip6_config_add_dns (NMSettingIP6Config *setting, const struct in6_addr *dns);
+void nm_setting_ip6_config_remove_dns (NMSettingIP6Config *setting, guint32 i);
+NM_AVAILABLE_IN_0_9_10
+gboolean nm_setting_ip6_config_remove_dns_by_value (NMSettingIP6Config *setting, const struct in6_addr *dns);
+void nm_setting_ip6_config_clear_dns (NMSettingIP6Config *setting);
+
+guint32 nm_setting_ip6_config_get_num_dns_searches (NMSettingIP6Config *setting);
+const char * nm_setting_ip6_config_get_dns_search (NMSettingIP6Config *setting, guint32 i);
+gboolean nm_setting_ip6_config_add_dns_search (NMSettingIP6Config *setting, const char *dns_search);
+void nm_setting_ip6_config_remove_dns_search (NMSettingIP6Config *setting, guint32 i);
+NM_AVAILABLE_IN_0_9_10
+gboolean nm_setting_ip6_config_remove_dns_search_by_value (NMSettingIP6Config *setting, const char *dns_search);
+void nm_setting_ip6_config_clear_dns_searches (NMSettingIP6Config *setting);
+
+guint32 nm_setting_ip6_config_get_num_addresses (NMSettingIP6Config *setting);
+NMIP6Address * nm_setting_ip6_config_get_address (NMSettingIP6Config *setting, guint32 i);
+gboolean nm_setting_ip6_config_add_address (NMSettingIP6Config *setting, NMIP6Address *address);
+void nm_setting_ip6_config_remove_address (NMSettingIP6Config *setting, guint32 i);
+NM_AVAILABLE_IN_0_9_10
+gboolean nm_setting_ip6_config_remove_address_by_value (NMSettingIP6Config *setting, NMIP6Address *address);
+void nm_setting_ip6_config_clear_addresses (NMSettingIP6Config *setting);
+
+guint32 nm_setting_ip6_config_get_num_routes (NMSettingIP6Config *setting);
+NMIP6Route * nm_setting_ip6_config_get_route (NMSettingIP6Config *setting, guint32 i);
+gboolean nm_setting_ip6_config_add_route (NMSettingIP6Config *setting, NMIP6Route *route);
+void nm_setting_ip6_config_remove_route (NMSettingIP6Config *setting, guint32 i);
+NM_AVAILABLE_IN_0_9_10
+gboolean nm_setting_ip6_config_remove_route_by_value (NMSettingIP6Config *setting, NMIP6Route *route);
+void nm_setting_ip6_config_clear_routes (NMSettingIP6Config *setting);
+gboolean nm_setting_ip6_config_get_ignore_auto_routes (NMSettingIP6Config *setting);
+
+gboolean nm_setting_ip6_config_get_ignore_auto_dns (NMSettingIP6Config *setting);
+const char * nm_setting_ip6_config_get_dhcp_hostname (NMSettingIP6Config *setting);
+gboolean nm_setting_ip6_config_get_never_default (NMSettingIP6Config *setting);
+gboolean nm_setting_ip6_config_get_may_fail (NMSettingIP6Config *setting);
+NMSettingIP6ConfigPrivacy nm_setting_ip6_config_get_ip6_privacy (NMSettingIP6Config *setting);
+
+G_END_DECLS
+
+#endif /* NM_SETTING_IP6_CONFIG_H */
diff --git a/libnm-core/nm-setting-olpc-mesh.c b/libnm-core/nm-setting-olpc-mesh.c
new file mode 100644
index 0000000000..408f6833f5
--- /dev/null
+++ b/libnm-core/nm-setting-olpc-mesh.c
@@ -0,0 +1,274 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2013 Red Hat, Inc.
+ * Copyright 2007 - 2008 Novell, Inc.
+ * Copyright 2009 One Laptop per Child
+ */
+
+#include <string.h>
+#include <netinet/ether.h>
+#include <dbus/dbus-glib.h>
+#include <glib/gi18n.h>
+
+#include "NetworkManager.h"
+#include "nm-setting-olpc-mesh.h"
+#include "nm-param-spec-specialized.h"
+#include "nm-utils.h"
+#include "nm-dbus-glib-types.h"
+#include "nm-utils-private.h"
+#include "nm-setting-private.h"
+
+GQuark
+nm_setting_olpc_mesh_error_quark (void)
+{
+ static GQuark quark;
+
+ if (G_UNLIKELY (!quark))
+ quark = g_quark_from_static_string ("nm-setting-olpc-mesh-error-quark");
+ return quark;
+}
+
+static void nm_setting_olpc_mesh_init (NMSettingOlpcMesh *setting);
+
+G_DEFINE_TYPE_WITH_CODE (NMSettingOlpcMesh, nm_setting_olpc_mesh, NM_TYPE_SETTING,
+ _nm_register_setting (NM_SETTING_OLPC_MESH_SETTING_NAME,
+ g_define_type_id,
+ 1,
+ NM_SETTING_OLPC_MESH_ERROR))
+NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_OLPC_MESH)
+
+#define NM_SETTING_OLPC_MESH_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_OLPC_MESH, NMSettingOlpcMeshPrivate))
+
+typedef struct {
+ GByteArray *ssid;
+ guint32 channel;
+ GByteArray *dhcp_anycast_addr;
+} NMSettingOlpcMeshPrivate;
+
+enum {
+ PROP_0,
+ PROP_SSID,
+ PROP_CHANNEL,
+ PROP_DHCP_ANYCAST_ADDRESS,
+
+ LAST_PROP
+};
+
+/**
+ * nm_setting_olpc_mesh_new:
+ *
+ * Creates a new #NMSettingOlpcMesh object with default values.
+ *
+ * Returns: the new empty #NMSettingOlpcMesh object
+ **/
+NMSetting *nm_setting_olpc_mesh_new (void)
+{
+ return (NMSetting *) g_object_new (NM_TYPE_SETTING_OLPC_MESH, NULL);
+}
+
+static void
+nm_setting_olpc_mesh_init (NMSettingOlpcMesh *setting)
+{
+}
+
+const GByteArray *
+nm_setting_olpc_mesh_get_ssid (NMSettingOlpcMesh *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_OLPC_MESH (setting), NULL);
+
+ return NM_SETTING_OLPC_MESH_GET_PRIVATE (setting)->ssid;
+}
+
+guint32
+nm_setting_olpc_mesh_get_channel (NMSettingOlpcMesh *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_OLPC_MESH (setting), 0);
+
+ return NM_SETTING_OLPC_MESH_GET_PRIVATE (setting)->channel;
+}
+
+const GByteArray *
+nm_setting_olpc_mesh_get_dhcp_anycast_address (NMSettingOlpcMesh *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_OLPC_MESH (setting), NULL);
+
+ return NM_SETTING_OLPC_MESH_GET_PRIVATE (setting)->dhcp_anycast_addr;
+}
+
+static gboolean
+verify (NMSetting *setting, GSList *all_settings, GError **error)
+{
+ NMSettingOlpcMeshPrivate *priv = NM_SETTING_OLPC_MESH_GET_PRIVATE (setting);
+
+ if (!priv->ssid) {
+ g_set_error_literal (error,
+ NM_SETTING_OLPC_MESH_ERROR,
+ NM_SETTING_OLPC_MESH_ERROR_MISSING_PROPERTY,
+ _("property is missing"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_OLPC_MESH_SETTING_NAME, NM_SETTING_OLPC_MESH_SSID);
+ return FALSE;
+ }
+
+ if (!priv->ssid->len || priv->ssid->len > 32) {
+ g_set_error_literal (error,
+ NM_SETTING_OLPC_MESH_ERROR,
+ NM_SETTING_OLPC_MESH_ERROR_INVALID_PROPERTY,
+ _("SSID length is out of range <1-32> bytes"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_OLPC_MESH_SETTING_NAME, NM_SETTING_OLPC_MESH_SSID);
+ return FALSE;
+ }
+
+ if (priv->channel == 0 || priv->channel > 13) {
+ g_set_error (error,
+ NM_SETTING_OLPC_MESH_ERROR,
+ NM_SETTING_OLPC_MESH_ERROR_INVALID_PROPERTY,
+ _("'%d' is not a valid channel"),
+ priv->channel);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_OLPC_MESH_SETTING_NAME, NM_SETTING_OLPC_MESH_CHANNEL);
+ return FALSE;
+ }
+
+ if (priv->dhcp_anycast_addr && priv->dhcp_anycast_addr->len != ETH_ALEN) {
+ g_set_error_literal (error,
+ NM_SETTING_OLPC_MESH_ERROR,
+ NM_SETTING_OLPC_MESH_ERROR_INVALID_PROPERTY,
+ _("property is invalid"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_OLPC_MESH_SETTING_NAME, NM_SETTING_OLPC_MESH_DHCP_ANYCAST_ADDRESS);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+finalize (GObject *object)
+{
+ NMSettingOlpcMeshPrivate *priv = NM_SETTING_OLPC_MESH_GET_PRIVATE (object);
+
+ if (priv->ssid)
+ g_byte_array_free (priv->ssid, TRUE);
+ if (priv->dhcp_anycast_addr)
+ g_byte_array_free (priv->dhcp_anycast_addr, TRUE);
+
+ G_OBJECT_CLASS (nm_setting_olpc_mesh_parent_class)->finalize (object);
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ NMSettingOlpcMeshPrivate *priv = NM_SETTING_OLPC_MESH_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_SSID:
+ if (priv->ssid)
+ g_byte_array_free (priv->ssid, TRUE);
+ priv->ssid = g_value_dup_boxed (value);
+ break;
+ case PROP_CHANNEL:
+ priv->channel = g_value_get_uint (value);
+ break;
+ case PROP_DHCP_ANYCAST_ADDRESS:
+ if (priv->dhcp_anycast_addr)
+ g_byte_array_free (priv->dhcp_anycast_addr, TRUE);
+ priv->dhcp_anycast_addr = g_value_dup_boxed (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ NMSettingOlpcMesh *setting = NM_SETTING_OLPC_MESH (object);
+
+ switch (prop_id) {
+ case PROP_SSID:
+ g_value_set_boxed (value, nm_setting_olpc_mesh_get_ssid (setting));
+ break;
+ case PROP_CHANNEL:
+ g_value_set_uint (value, nm_setting_olpc_mesh_get_channel (setting));
+ break;
+ case PROP_DHCP_ANYCAST_ADDRESS:
+ g_value_set_boxed (value, nm_setting_olpc_mesh_get_dhcp_anycast_address (setting));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nm_setting_olpc_mesh_class_init (NMSettingOlpcMeshClass *setting_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
+ NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+
+ g_type_class_add_private (setting_class, sizeof (NMSettingOlpcMeshPrivate));
+
+ /* virtual methods */
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+ object_class->finalize = finalize;
+ parent_class->verify = verify;
+
+ /* Properties */
+ /**
+ * NMSettingOlpcMesh:ssid:
+ *
+ * SSID of the mesh network to join.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_SSID,
+ _nm_param_spec_specialized (NM_SETTING_OLPC_MESH_SSID, "", "",
+ DBUS_TYPE_G_UCHAR_ARRAY,
+ G_PARAM_READWRITE |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingOlpcMesh:channel:
+ *
+ * Channel on which the mesh network to join is located.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_CHANNEL,
+ g_param_spec_uint (NM_SETTING_OLPC_MESH_CHANNEL, "", "",
+ 0, G_MAXUINT32, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingOlpcMesh:dhcp-anycast-address:
+ *
+ * Anycast DHCP MAC address used when requesting an IP address via DHCP.
+ * The specific anycast address used determines which DHCP server class
+ * answers the request.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_DHCP_ANYCAST_ADDRESS,
+ _nm_param_spec_specialized (NM_SETTING_OLPC_MESH_DHCP_ANYCAST_ADDRESS, "", "",
+ DBUS_TYPE_G_UCHAR_ARRAY,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/libnm-core/nm-setting-olpc-mesh.h b/libnm-core/nm-setting-olpc-mesh.h
new file mode 100644
index 0000000000..54b268e2c6
--- /dev/null
+++ b/libnm-core/nm-setting-olpc-mesh.h
@@ -0,0 +1,82 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2008 Red Hat, Inc.
+ * Copyright 2007 - 2008 Novell, Inc.
+ * Copyright 2009 One Laptop per Child
+ */
+
+#ifndef NM_SETTING_OLPC_MESH_H
+#define NM_SETTING_OLPC_MESH_H
+
+#include <nm-setting.h>
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_SETTING_OLPC_MESH (nm_setting_olpc_mesh_get_type ())
+#define NM_SETTING_OLPC_MESH(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_OLPC_MESH, NMSettingOlpcMesh))
+#define NM_SETTING_OLPC_MESH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_OLPC_MESH, NMSettingOlpcMeshClass))
+#define NM_IS_SETTING_OLPC_MESH(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_OLPC_MESH))
+#define NM_IS_SETTING_OLPC_MESH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_OLPC_MESH))
+#define NM_SETTING_OLPC_MESH_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_OLPC_MESH, NMSettingOlpcMeshClass))
+
+#define NM_SETTING_OLPC_MESH_SETTING_NAME "802-11-olpc-mesh"
+
+/**
+ * NMSettingOlpcMeshError:
+ * @NM_SETTING_OLPC_MESH_ERROR_UNKNOWN: unknown or unclassified error
+ * @NM_SETTING_OLPC_MESH_ERROR_INVALID_PROPERTY: the property was invalid
+ * @NM_SETTING_OLPC_MESH_ERROR_MISSING_PROPERTY: the property was missing and is
+ * required
+ */
+typedef enum {
+ NM_SETTING_OLPC_MESH_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/
+ NM_SETTING_OLPC_MESH_ERROR_INVALID_PROPERTY, /*< nick=InvalidProperty >*/
+ NM_SETTING_OLPC_MESH_ERROR_MISSING_PROPERTY /*< nick=MissingProperty >*/
+} NMSettingOlpcMeshError;
+
+#define NM_SETTING_OLPC_MESH_ERROR nm_setting_olpc_mesh_error_quark ()
+GQuark nm_setting_olpc_mesh_error_quark (void);
+
+#define NM_SETTING_OLPC_MESH_SSID "ssid"
+#define NM_SETTING_OLPC_MESH_CHANNEL "channel"
+#define NM_SETTING_OLPC_MESH_DHCP_ANYCAST_ADDRESS "dhcp-anycast-address"
+
+typedef struct {
+ NMSetting parent;
+} NMSettingOlpcMesh;
+
+typedef struct {
+ NMSettingClass parent;
+
+ /* Padding for future expansion */
+ void (*_reserved1) (void);
+ void (*_reserved2) (void);
+ void (*_reserved3) (void);
+ void (*_reserved4) (void);
+} NMSettingOlpcMeshClass;
+
+GType nm_setting_olpc_mesh_get_type (void);
+
+NMSetting * nm_setting_olpc_mesh_new (void);
+const GByteArray *nm_setting_olpc_mesh_get_ssid (NMSettingOlpcMesh *setting);
+guint32 nm_setting_olpc_mesh_get_channel (NMSettingOlpcMesh *setting);
+const GByteArray *nm_setting_olpc_mesh_get_dhcp_anycast_address (NMSettingOlpcMesh *setting);
+
+G_END_DECLS
+
+#endif /* NM_SETTING_OLPC_MESH_H */
diff --git a/libnm-core/nm-setting-ppp.c b/libnm-core/nm-setting-ppp.c
new file mode 100644
index 0000000000..0e7c598b90
--- /dev/null
+++ b/libnm-core/nm-setting-ppp.c
@@ -0,0 +1,823 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2013 Red Hat, Inc.
+ * Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#include <glib/gi18n.h>
+
+#include "nm-setting-ppp.h"
+#include "nm-setting-private.h"
+
+/**
+ * SECTION:nm-setting-ppp
+ * @short_description: Describes connection properties for devices/networks
+ * that require PPP to deliver IP capability
+ * @include: nm-setting-ppp.h
+ *
+ * The #NMSettingPPP object is a #NMSetting subclass that describes properties
+ * necessary for connection to networks that require PPP transport, like PPPoE
+ * cable and DSL modems and some mobile broadband devices.
+ **/
+
+/**
+ * nm_setting_ppp_error_quark:
+ *
+ * Registers an error quark for #NMSettingPPP if necessary.
+ *
+ * Returns: the error quark used for #NMSettingPPP errors.
+ **/
+GQuark
+nm_setting_ppp_error_quark (void)
+{
+ static GQuark quark;
+
+ if (G_UNLIKELY (!quark))
+ quark = g_quark_from_static_string ("nm-setting-ppp-error-quark");
+ return quark;
+}
+
+
+G_DEFINE_TYPE_WITH_CODE (NMSettingPPP, nm_setting_ppp, NM_TYPE_SETTING,
+ _nm_register_setting (NM_SETTING_PPP_SETTING_NAME,
+ g_define_type_id,
+ 3,
+ NM_SETTING_PPP_ERROR))
+NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_PPP)
+
+#define NM_SETTING_PPP_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_PPP, NMSettingPPPPrivate))
+
+typedef struct {
+ gboolean noauth;
+ gboolean refuse_eap;
+ gboolean refuse_pap;
+ gboolean refuse_chap;
+ gboolean refuse_mschap;
+ gboolean refuse_mschapv2;
+ gboolean nobsdcomp;
+ gboolean nodeflate;
+ gboolean no_vj_comp;
+ gboolean require_mppe;
+ gboolean require_mppe_128;
+ gboolean mppe_stateful;
+ gboolean crtscts;
+ guint32 baud;
+ guint32 mru;
+ guint32 mtu;
+ guint32 lcp_echo_failure;
+ guint32 lcp_echo_interval;
+} NMSettingPPPPrivate;
+
+enum {
+ PROP_0,
+ PROP_NOAUTH,
+ PROP_REFUSE_EAP,
+ PROP_REFUSE_PAP,
+ PROP_REFUSE_CHAP,
+ PROP_REFUSE_MSCHAP,
+ PROP_REFUSE_MSCHAPV2,
+ PROP_NOBSDCOMP,
+ PROP_NODEFLATE,
+ PROP_NO_VJ_COMP,
+ PROP_REQUIRE_MPPE,
+ PROP_REQUIRE_MPPE_128,
+ PROP_MPPE_STATEFUL,
+ PROP_CRTSCTS,
+ PROP_BAUD,
+ PROP_MRU,
+ PROP_MTU,
+ PROP_LCP_ECHO_FAILURE,
+ PROP_LCP_ECHO_INTERVAL,
+
+ LAST_PROP
+};
+
+/**
+ * nm_setting_ppp_new:
+ *
+ * Creates a new #NMSettingPPP object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingPPP object
+ **/
+NMSetting *
+nm_setting_ppp_new (void)
+{
+ return (NMSetting *) g_object_new (NM_TYPE_SETTING_PPP, NULL);
+}
+
+/**
+ * nm_setting_ppp_get_noauth:
+ * @setting: the #NMSettingPPP
+ *
+ * Returns: the #NMSettingPPP:noauth property of the setting
+ **/
+gboolean
+nm_setting_ppp_get_noauth (NMSettingPPP *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_PPP (setting), FALSE);
+
+ return NM_SETTING_PPP_GET_PRIVATE (setting)->noauth;
+}
+
+/**
+ * nm_setting_ppp_get_refuse_eap:
+ * @setting: the #NMSettingPPP
+ *
+ * Returns: the #NMSettingPPP:refuse-eap property of the setting
+ **/
+gboolean
+nm_setting_ppp_get_refuse_eap (NMSettingPPP *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_PPP (setting), FALSE);
+
+ return NM_SETTING_PPP_GET_PRIVATE (setting)->refuse_eap;
+}
+
+/**
+ * nm_setting_ppp_get_refuse_pap:
+ * @setting: the #NMSettingPPP
+ *
+ * Returns: the #NMSettingPPP:refuse-pap property of the setting
+ **/
+gboolean
+nm_setting_ppp_get_refuse_pap (NMSettingPPP *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_PPP (setting), FALSE);
+
+ return NM_SETTING_PPP_GET_PRIVATE (setting)->refuse_pap;
+}
+
+/**
+ * nm_setting_ppp_get_refuse_chap:
+ * @setting: the #NMSettingPPP
+ *
+ * Returns: the #NMSettingPPP:refuse-chap property of the setting
+ **/
+gboolean
+nm_setting_ppp_get_refuse_chap (NMSettingPPP *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_PPP (setting), FALSE);
+
+ return NM_SETTING_PPP_GET_PRIVATE (setting)->refuse_chap;
+}
+
+/**
+ * nm_setting_ppp_get_refuse_mschap:
+ * @setting: the #NMSettingPPP
+ *
+ * Returns: the #NMSettingPPP:refuse-mschap property of the setting
+ **/
+gboolean
+nm_setting_ppp_get_refuse_mschap (NMSettingPPP *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_PPP (setting), FALSE);
+
+ return NM_SETTING_PPP_GET_PRIVATE (setting)->refuse_mschap;
+}
+
+/**
+ * nm_setting_ppp_get_refuse_mschapv2:
+ * @setting: the #NMSettingPPP
+ *
+ * Returns: the #NMSettingPPP:refuse-mschapv2 property of the setting
+ **/
+gboolean
+nm_setting_ppp_get_refuse_mschapv2 (NMSettingPPP *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_PPP (setting), FALSE);
+
+ return NM_SETTING_PPP_GET_PRIVATE (setting)->refuse_mschapv2;
+}
+
+/**
+ * nm_setting_ppp_get_nobsdcomp:
+ * @setting: the #NMSettingPPP
+ *
+ * Returns: the #NMSettingPPP:nobsdcomp property of the setting
+ **/
+gboolean
+nm_setting_ppp_get_nobsdcomp (NMSettingPPP *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_PPP (setting), FALSE);
+
+ return NM_SETTING_PPP_GET_PRIVATE (setting)->nobsdcomp;
+}
+
+/**
+ * nm_setting_ppp_get_nodeflate:
+ * @setting: the #NMSettingPPP
+ *
+ * Returns: the #NMSettingPPP:nodeflate property of the setting
+ **/
+gboolean
+nm_setting_ppp_get_nodeflate (NMSettingPPP *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_PPP (setting), FALSE);
+
+ return NM_SETTING_PPP_GET_PRIVATE (setting)->nodeflate;
+}
+
+/**
+ * nm_setting_ppp_get_no_vj_comp:
+ * @setting: the #NMSettingPPP
+ *
+ * Returns: the #NMSettingPPP:no-vj-comp property of the setting
+ **/
+gboolean
+nm_setting_ppp_get_no_vj_comp (NMSettingPPP *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_PPP (setting), FALSE);
+
+ return NM_SETTING_PPP_GET_PRIVATE (setting)->no_vj_comp;
+}
+
+/**
+ * nm_setting_ppp_get_require_mppe:
+ * @setting: the #NMSettingPPP
+ *
+ * Returns: the #NMSettingPPP:require-mppe property of the setting
+ **/
+gboolean
+nm_setting_ppp_get_require_mppe (NMSettingPPP *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_PPP (setting), FALSE);
+
+ return NM_SETTING_PPP_GET_PRIVATE (setting)->require_mppe;
+}
+
+/**
+ * nm_setting_ppp_get_require_mppe_128:
+ * @setting: the #NMSettingPPP
+ *
+ * Returns: the #NMSettingPPP:require-mppe-128 property of the setting
+ **/
+gboolean
+nm_setting_ppp_get_require_mppe_128 (NMSettingPPP *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_PPP (setting), FALSE);
+
+ return NM_SETTING_PPP_GET_PRIVATE (setting)->require_mppe_128;
+}
+
+/**
+ * nm_setting_ppp_get_mppe_stateful:
+ * @setting: the #NMSettingPPP
+ *
+ * Returns: the #NMSettingPPP:mppe-stateful property of the setting
+ **/
+gboolean
+nm_setting_ppp_get_mppe_stateful (NMSettingPPP *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_PPP (setting), FALSE);
+
+ return NM_SETTING_PPP_GET_PRIVATE (setting)->mppe_stateful;
+}
+
+/**
+ * nm_setting_ppp_get_crtscts:
+ * @setting: the #NMSettingPPP
+ *
+ * Returns: the #NMSettingPPP:crtscts property of the setting
+ **/
+gboolean
+nm_setting_ppp_get_crtscts (NMSettingPPP *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_PPP (setting), FALSE);
+
+ return NM_SETTING_PPP_GET_PRIVATE (setting)->crtscts;
+}
+
+/**
+ * nm_setting_ppp_get_baud:
+ * @setting: the #NMSettingPPP
+ *
+ * Returns: the #NMSettingPPP:baud property of the setting
+ **/
+guint32
+nm_setting_ppp_get_baud (NMSettingPPP *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_PPP (setting), 0);
+
+ return NM_SETTING_PPP_GET_PRIVATE (setting)->baud;
+}
+
+/**
+ * nm_setting_ppp_get_mru:
+ * @setting: the #NMSettingPPP
+ *
+ * Returns: the #NMSettingPPP:mru property of the setting
+ **/
+guint32
+nm_setting_ppp_get_mru (NMSettingPPP *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_PPP (setting), 0);
+
+ return NM_SETTING_PPP_GET_PRIVATE (setting)->mru;
+}
+
+/**
+ * nm_setting_ppp_get_mtu:
+ * @setting: the #NMSettingPPP
+ *
+ * Returns: the #NMSettingPPP:mtu property of the setting
+ **/
+guint32
+nm_setting_ppp_get_mtu (NMSettingPPP *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_PPP (setting), 0);
+
+ return NM_SETTING_PPP_GET_PRIVATE (setting)->mtu;
+}
+
+/**
+ * nm_setting_ppp_get_lcp_echo_failure:
+ * @setting: the #NMSettingPPP
+ *
+ * Returns: the #NMSettingPPP:lcp-echo-failure property of the setting
+ **/
+guint32
+nm_setting_ppp_get_lcp_echo_failure (NMSettingPPP *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_PPP (setting), 0);
+
+ return NM_SETTING_PPP_GET_PRIVATE (setting)->lcp_echo_failure;
+}
+
+/**
+ * nm_setting_ppp_get_lcp_echo_interval:
+ * @setting: the #NMSettingPPP
+ *
+ * Returns: the #NMSettingPPP:lcp-echo-interval property of the setting
+ **/
+guint32
+nm_setting_ppp_get_lcp_echo_interval (NMSettingPPP *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_PPP (setting), 0);
+
+ return NM_SETTING_PPP_GET_PRIVATE (setting)->lcp_echo_interval;
+}
+
+static gboolean
+verify (NMSetting *setting, GSList *all_settings, GError **error)
+{
+ NMSettingPPPPrivate *priv = NM_SETTING_PPP_GET_PRIVATE (setting);
+
+ /* FIXME: Do we even want this or can we just let pppd evaluate the options? */
+ if (priv->mru > 0) {
+ if (priv->mru < 128 || priv->mru > 16384) {
+ g_set_error (error,
+ NM_SETTING_PPP_ERROR,
+ NM_SETTING_PPP_ERROR_INVALID_PROPERTY,
+ _("'%d' is out of valid range <128-16384>"),
+ priv->mru);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_PPP_SETTING_NAME, NM_SETTING_PPP_MRU);
+ return FALSE;
+ }
+ }
+
+ if (priv->lcp_echo_failure > 0) {
+ /* lcp_echo_interval must also be non-zero */
+ if (priv->lcp_echo_interval == 0) {
+ g_set_error (error,
+ NM_SETTING_PPP_ERROR,
+ NM_SETTING_PPP_ERROR_INVALID_PROPERTY,
+ _("setting this property requires non-zero '%s' property"),
+ NM_SETTING_PPP_LCP_ECHO_INTERVAL);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_PPP_SETTING_NAME, NM_SETTING_PPP_LCP_ECHO_FAILURE);
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static void
+nm_setting_ppp_init (NMSettingPPP *setting)
+{
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ NMSettingPPPPrivate *priv = NM_SETTING_PPP_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_NOAUTH:
+ priv->noauth = g_value_get_boolean (value);
+ break;
+ case PROP_REFUSE_EAP:
+ priv->refuse_eap = g_value_get_boolean (value);
+ break;
+ case PROP_REFUSE_PAP:
+ priv->refuse_pap = g_value_get_boolean (value);
+ break;
+ case PROP_REFUSE_CHAP:
+ priv->refuse_chap = g_value_get_boolean (value);
+ break;
+ case PROP_REFUSE_MSCHAP:
+ priv->refuse_mschap = g_value_get_boolean (value);
+ break;
+ case PROP_REFUSE_MSCHAPV2:
+ priv->refuse_mschapv2 = g_value_get_boolean (value);
+ break;
+ case PROP_NOBSDCOMP:
+ priv->nobsdcomp = g_value_get_boolean (value);
+ break;
+ case PROP_NODEFLATE:
+ priv->nodeflate = g_value_get_boolean (value);
+ break;
+ case PROP_NO_VJ_COMP:
+ priv->no_vj_comp = g_value_get_boolean (value);
+ break;
+ case PROP_REQUIRE_MPPE:
+ priv->require_mppe = g_value_get_boolean (value);
+ break;
+ case PROP_REQUIRE_MPPE_128:
+ priv->require_mppe_128 = g_value_get_boolean (value);
+ break;
+ case PROP_MPPE_STATEFUL:
+ priv->mppe_stateful = g_value_get_boolean (value);
+ break;
+ case PROP_CRTSCTS:
+ priv->crtscts = g_value_get_boolean (value);
+ break;
+ case PROP_BAUD:
+ priv->baud = g_value_get_uint (value);
+ break;
+ case PROP_MRU:
+ priv->mru = g_value_get_uint (value);
+ break;
+ case PROP_MTU:
+ priv->mtu = g_value_get_uint (value);
+ break;
+ case PROP_LCP_ECHO_FAILURE:
+ priv->lcp_echo_failure = g_value_get_uint (value);
+ break;
+ case PROP_LCP_ECHO_INTERVAL:
+ priv->lcp_echo_interval = g_value_get_uint (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ NMSettingPPP *setting = NM_SETTING_PPP (object);
+
+ switch (prop_id) {
+ case PROP_NOAUTH:
+ g_value_set_boolean (value, nm_setting_ppp_get_noauth (setting));
+ break;
+ case PROP_REFUSE_EAP:
+ g_value_set_boolean (value, nm_setting_ppp_get_refuse_eap (setting));
+ break;
+ case PROP_REFUSE_PAP:
+ g_value_set_boolean (value, nm_setting_ppp_get_refuse_pap (setting));
+ break;
+ case PROP_REFUSE_CHAP:
+ g_value_set_boolean (value, nm_setting_ppp_get_refuse_chap (setting));
+ break;
+ case PROP_REFUSE_MSCHAP:
+ g_value_set_boolean (value, nm_setting_ppp_get_refuse_mschap (setting));
+ break;
+ case PROP_REFUSE_MSCHAPV2:
+ g_value_set_boolean (value, nm_setting_ppp_get_refuse_mschapv2 (setting));
+ break;
+ case PROP_NOBSDCOMP:
+ g_value_set_boolean (value, nm_setting_ppp_get_nobsdcomp (setting));
+ break;
+ case PROP_NODEFLATE:
+ g_value_set_boolean (value, nm_setting_ppp_get_nodeflate (setting));
+ break;
+ case PROP_NO_VJ_COMP:
+ g_value_set_boolean (value, nm_setting_ppp_get_no_vj_comp (setting));
+ break;
+ case PROP_REQUIRE_MPPE:
+ g_value_set_boolean (value, nm_setting_ppp_get_require_mppe (setting));
+ break;
+ case PROP_REQUIRE_MPPE_128:
+ g_value_set_boolean (value, nm_setting_ppp_get_require_mppe_128 (setting));
+ break;
+ case PROP_MPPE_STATEFUL:
+ g_value_set_boolean (value, nm_setting_ppp_get_mppe_stateful (setting));
+ break;
+ case PROP_CRTSCTS:
+ g_value_set_boolean (value, nm_setting_ppp_get_crtscts (setting));
+ break;
+ case PROP_BAUD:
+ g_value_set_uint (value, nm_setting_ppp_get_baud (setting));
+ break;
+ case PROP_MRU:
+ g_value_set_uint (value, nm_setting_ppp_get_mru (setting));
+ break;
+ case PROP_MTU:
+ g_value_set_uint (value, nm_setting_ppp_get_mtu (setting));
+ break;
+ case PROP_LCP_ECHO_FAILURE:
+ g_value_set_uint (value, nm_setting_ppp_get_lcp_echo_failure (setting));
+ break;
+ case PROP_LCP_ECHO_INTERVAL:
+ g_value_set_uint (value, nm_setting_ppp_get_lcp_echo_interval (setting));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nm_setting_ppp_class_init (NMSettingPPPClass *setting_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
+ NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+
+ g_type_class_add_private (setting_class, sizeof (NMSettingPPPPrivate));
+
+ /* virtual methods */
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+ parent_class->verify = verify;
+
+ /* Properties */
+ /**
+ * NMSettingPPP:noauth:
+ *
+ * If %TRUE, do not require the other side (usually the PPP server) to
+ * authenticate itself to the client. If %FALSE, require authentication
+ * from the remote side. In almost all cases, this should be %TRUE.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_NOAUTH,
+ g_param_spec_boolean (NM_SETTING_PPP_NOAUTH, "", "",
+ TRUE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingPPP:refuse-eap:
+ *
+ * If %TRUE, the EAP authentication method will not be used.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_REFUSE_EAP,
+ g_param_spec_boolean (NM_SETTING_PPP_REFUSE_EAP, "", "",
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingPPP:refuse-pap:
+ *
+ * If %TRUE, the PAP authentication method will not be used.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_REFUSE_PAP,
+ g_param_spec_boolean (NM_SETTING_PPP_REFUSE_PAP, "", "",
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingPPP:refuse-chap:
+ *
+ * If %TRUE, the CHAP authentication method will not be used.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_REFUSE_CHAP,
+ g_param_spec_boolean (NM_SETTING_PPP_REFUSE_CHAP, "", "",
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingPPP:refuse-mschap:
+ *
+ * If %TRUE, the MSCHAP authentication method will not be used.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_REFUSE_MSCHAP,
+ g_param_spec_boolean (NM_SETTING_PPP_REFUSE_MSCHAP, "", "",
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingPPP:refuse-mschapv2:
+ *
+ * If %TRUE, the MSCHAPv2 authentication method will not be used.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_REFUSE_MSCHAPV2,
+ g_param_spec_boolean (NM_SETTING_PPP_REFUSE_MSCHAPV2, "", "",
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingPPP:nobsdcomp:
+ *
+ * If %TRUE, BSD compression will not be requested.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_NOBSDCOMP,
+ g_param_spec_boolean (NM_SETTING_PPP_NOBSDCOMP, "", "",
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ NM_SETTING_PARAM_FUZZY_IGNORE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingPPP:nodeflate:
+ *
+ * If %TRUE, "deflate" compression will not be requested.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_NODEFLATE,
+ g_param_spec_boolean (NM_SETTING_PPP_NODEFLATE, "", "",
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ NM_SETTING_PARAM_FUZZY_IGNORE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingPPP:no-vj-comp:
+ *
+ * If %TRUE, Van Jacobsen TCP header compression will not be requested.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_NO_VJ_COMP,
+ g_param_spec_boolean (NM_SETTING_PPP_NO_VJ_COMP, "", "",
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ NM_SETTING_PARAM_FUZZY_IGNORE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingPPP:require-mppe:
+ *
+ * If %TRUE, MPPE (Microsoft Point-to-Point Encrpytion) will be required for
+ * the PPP session. If either 64-bit or 128-bit MPPE is not available the
+ * session will fail. Note that MPPE is not used on mobile broadband
+ * connections.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_REQUIRE_MPPE,
+ g_param_spec_boolean (NM_SETTING_PPP_REQUIRE_MPPE, "", "",
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingPPP:require-mppe-128:
+ *
+ * If %TRUE, 128-bit MPPE (Microsoft Point-to-Point Encrpytion) will be
+ * required for the PPP session, and the "require-mppe" property must also
+ * be set to %TRUE. If 128-bit MPPE is not available the session will fail.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_REQUIRE_MPPE_128,
+ g_param_spec_boolean (NM_SETTING_PPP_REQUIRE_MPPE_128, "", "",
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingPPP:mppe-stateful:
+ *
+ * If %TRUE, stateful MPPE is used. See pppd documentation for more
+ * information on stateful MPPE.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_MPPE_STATEFUL,
+ g_param_spec_boolean (NM_SETTING_PPP_MPPE_STATEFUL, "", "",
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingPPP:crtscts:
+ *
+ * If %TRUE, specify that pppd should set the serial port to use hardware
+ * flow control with RTS and CTS signals. This value should normally be set
+ * to %FALSE.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_CRTSCTS,
+ g_param_spec_boolean (NM_SETTING_PPP_CRTSCTS, "", "",
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingPPP:baud:
+ *
+ * If non-zero, instruct pppd to set the serial port to the specified
+ * baudrate. This value should normally be left as 0 to automatically
+ * choose the speed.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_BAUD,
+ g_param_spec_uint (NM_SETTING_PPP_BAUD, "", "",
+ 0, G_MAXUINT32, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ NM_SETTING_PARAM_FUZZY_IGNORE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingPPP:mru:
+ *
+ * If non-zero, instruct pppd to request that the peer send packets no
+ * larger than the specified size. If non-zero, the MRU should be between
+ * 128 and 16384.
+ */
+ g_object_class_install_property
+ (object_class, PROP_MRU,
+ g_param_spec_uint (NM_SETTING_PPP_MRU, "", "",
+ 0, 16384, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingPPP:mtu:
+ *
+ * If non-zero, instruct pppd to send packets no larger than the specified
+ * size.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_MTU,
+ g_param_spec_uint (NM_SETTING_PPP_MTU, "", "",
+ 0, G_MAXUINT32, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ NM_SETTING_PARAM_FUZZY_IGNORE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingPPP:lcp-echo-failure:
+ *
+ * If non-zero, instruct pppd to presume the connection to the peer has
+ * failed if the specified number of LCP echo-requests go unanswered by the
+ * peer. The "lcp-echo-interval" property must also be set to a non-zero
+ * value if this property is used.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_LCP_ECHO_FAILURE,
+ g_param_spec_uint (NM_SETTING_PPP_LCP_ECHO_FAILURE, "", "",
+ 0, G_MAXUINT32, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ NM_SETTING_PARAM_FUZZY_IGNORE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingPPP:lcp-echo-interval:
+ *
+ * If non-zero, instruct pppd to send an LCP echo-request frame to the peer
+ * every n seconds (where n is the specified value). Note that some PPP
+ * peers will respond to echo requests and some will not, and it is not
+ * possible to autodetect this.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_LCP_ECHO_INTERVAL,
+ g_param_spec_uint (NM_SETTING_PPP_LCP_ECHO_INTERVAL, "", "",
+ 0, G_MAXUINT32, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ NM_SETTING_PARAM_FUZZY_IGNORE |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/libnm-core/nm-setting-ppp.h b/libnm-core/nm-setting-ppp.h
new file mode 100644
index 0000000000..5733902b83
--- /dev/null
+++ b/libnm-core/nm-setting-ppp.h
@@ -0,0 +1,115 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2008 Red Hat, Inc.
+ * Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#ifndef NM_SETTING_PPP_H
+#define NM_SETTING_PPP_H
+
+#include <nm-setting.h>
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_SETTING_PPP (nm_setting_ppp_get_type ())
+#define NM_SETTING_PPP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_PPP, NMSettingPPP))
+#define NM_SETTING_PPP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_PPP, NMSettingPPPClass))
+#define NM_IS_SETTING_PPP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_PPP))
+#define NM_IS_SETTING_PPP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_PPP))
+#define NM_SETTING_PPP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_PPP, NMSettingPPPClass))
+
+#define NM_SETTING_PPP_SETTING_NAME "ppp"
+
+/**
+ * NMSettingPPPError:
+ * @NM_SETTING_PPP_ERROR_UNKNOWN: unknown or unclassified error
+ * @NM_SETTING_PPP_ERROR_INVALID_PROPERTY: the property was invalid
+ * @NM_SETTING_PPP_ERROR_MISSING_PROPERTY: the property was missing and is
+ * required
+ * @NM_SETTING_PPP_ERROR_REQUIRE_MPPE_NOT_ALLOWED: requiring MPPE is not compatible
+ * with other setting configuration parameters
+ */
+typedef enum {
+ NM_SETTING_PPP_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/
+ NM_SETTING_PPP_ERROR_INVALID_PROPERTY, /*< nick=InvalidProperty >*/
+ NM_SETTING_PPP_ERROR_MISSING_PROPERTY, /*< nick=MissingProperty >*/
+ NM_SETTING_PPP_ERROR_REQUIRE_MPPE_NOT_ALLOWED /*< nick=RequireMPPENotAllowed >*/
+} NMSettingPPPError;
+
+#define NM_SETTING_PPP_ERROR nm_setting_ppp_error_quark ()
+GQuark nm_setting_ppp_error_quark (void);
+
+#define NM_SETTING_PPP_NOAUTH "noauth"
+#define NM_SETTING_PPP_REFUSE_EAP "refuse-eap"
+#define NM_SETTING_PPP_REFUSE_PAP "refuse-pap"
+#define NM_SETTING_PPP_REFUSE_CHAP "refuse-chap"
+#define NM_SETTING_PPP_REFUSE_MSCHAP "refuse-mschap"
+#define NM_SETTING_PPP_REFUSE_MSCHAPV2 "refuse-mschapv2"
+#define NM_SETTING_PPP_NOBSDCOMP "nobsdcomp"
+#define NM_SETTING_PPP_NODEFLATE "nodeflate"
+#define NM_SETTING_PPP_NO_VJ_COMP "no-vj-comp"
+#define NM_SETTING_PPP_REQUIRE_MPPE "require-mppe"
+#define NM_SETTING_PPP_REQUIRE_MPPE_128 "require-mppe-128"
+#define NM_SETTING_PPP_MPPE_STATEFUL "mppe-stateful"
+#define NM_SETTING_PPP_CRTSCTS "crtscts"
+#define NM_SETTING_PPP_BAUD "baud"
+#define NM_SETTING_PPP_MRU "mru"
+#define NM_SETTING_PPP_MTU "mtu"
+#define NM_SETTING_PPP_LCP_ECHO_FAILURE "lcp-echo-failure"
+#define NM_SETTING_PPP_LCP_ECHO_INTERVAL "lcp-echo-interval"
+
+typedef struct {
+ NMSetting parent;
+} NMSettingPPP;
+
+typedef struct {
+ NMSettingClass parent;
+
+ /* Padding for future expansion */
+ void (*_reserved1) (void);
+ void (*_reserved2) (void);
+ void (*_reserved3) (void);
+ void (*_reserved4) (void);
+} NMSettingPPPClass;
+
+GType nm_setting_ppp_get_type (void);
+
+NMSetting *nm_setting_ppp_new (void);
+gboolean nm_setting_ppp_get_noauth (NMSettingPPP *setting);
+gboolean nm_setting_ppp_get_refuse_eap (NMSettingPPP *setting);
+gboolean nm_setting_ppp_get_refuse_pap (NMSettingPPP *setting);
+gboolean nm_setting_ppp_get_refuse_chap (NMSettingPPP *setting);
+gboolean nm_setting_ppp_get_refuse_mschap (NMSettingPPP *setting);
+gboolean nm_setting_ppp_get_refuse_mschapv2 (NMSettingPPP *setting);
+gboolean nm_setting_ppp_get_nobsdcomp (NMSettingPPP *setting);
+gboolean nm_setting_ppp_get_nodeflate (NMSettingPPP *setting);
+gboolean nm_setting_ppp_get_no_vj_comp (NMSettingPPP *setting);
+gboolean nm_setting_ppp_get_require_mppe (NMSettingPPP *setting);
+gboolean nm_setting_ppp_get_require_mppe_128 (NMSettingPPP *setting);
+gboolean nm_setting_ppp_get_mppe_stateful (NMSettingPPP *setting);
+gboolean nm_setting_ppp_get_crtscts (NMSettingPPP *setting);
+guint32 nm_setting_ppp_get_baud (NMSettingPPP *setting);
+guint32 nm_setting_ppp_get_mru (NMSettingPPP *setting);
+guint32 nm_setting_ppp_get_mtu (NMSettingPPP *setting);
+guint32 nm_setting_ppp_get_lcp_echo_failure (NMSettingPPP *setting);
+guint32 nm_setting_ppp_get_lcp_echo_interval (NMSettingPPP *setting);
+
+G_END_DECLS
+
+#endif /* NM_SETTING_PPP_H */
diff --git a/libnm-core/nm-setting-pppoe.c b/libnm-core/nm-setting-pppoe.c
new file mode 100644
index 0000000000..a3923336c1
--- /dev/null
+++ b/libnm-core/nm-setting-pppoe.c
@@ -0,0 +1,342 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2013 Red Hat, Inc.
+ * Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#include <string.h>
+#include <glib/gi18n.h>
+
+#include "nm-setting-pppoe.h"
+#include "nm-setting-ppp.h"
+#include "nm-setting-private.h"
+
+/**
+ * SECTION:nm-setting-pppoe
+ * @short_description: Describes PPPoE connection properties
+ * @include: nm-setting-pppoe.h
+ *
+ * The #NMSettingPPPOE object is a #NMSetting subclass that describes
+ * properties necessary for connection to networks that require PPPoE connections
+ * to provide IP transport, for example cable or DSL modems.
+ **/
+
+/**
+ * nm_setting_pppoe_error_quark:
+ *
+ * Registers an error quark for #NMSettingPPPOE if necessary.
+ *
+ * Returns: the error quark used for #NMSettingPPPOE errors.
+ **/
+GQuark
+nm_setting_pppoe_error_quark (void)
+{
+ static GQuark quark;
+
+ if (G_UNLIKELY (!quark))
+ quark = g_quark_from_static_string ("nm-setting-pppoe-error-quark");
+ return quark;
+}
+
+
+G_DEFINE_TYPE_WITH_CODE (NMSettingPPPOE, nm_setting_pppoe, NM_TYPE_SETTING,
+ _nm_register_setting (NM_SETTING_PPPOE_SETTING_NAME,
+ g_define_type_id,
+ 3,
+ NM_SETTING_PPPOE_ERROR))
+NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_PPPOE)
+
+#define NM_SETTING_PPPOE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_PPPOE, NMSettingPPPOEPrivate))
+
+typedef struct {
+ char *service;
+ char *username;
+ char *password;
+ NMSettingSecretFlags password_flags;
+} NMSettingPPPOEPrivate;
+
+enum {
+ PROP_0,
+ PROP_SERVICE,
+ PROP_USERNAME,
+ PROP_PASSWORD,
+ PROP_PASSWORD_FLAGS,
+
+ LAST_PROP
+};
+
+/**
+ * nm_setting_pppoe_new:
+ *
+ * Creates a new #NMSettingPPPOE object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingPPPOE object
+ **/
+NMSetting *
+nm_setting_pppoe_new (void)
+{
+ return (NMSetting *) g_object_new (NM_TYPE_SETTING_PPPOE, NULL);
+}
+
+/**
+ * nm_setting_pppoe_get_service:
+ * @setting: the #NMSettingPPPOE
+ *
+ * Returns: the #NMSettingPPPOE:service property of the setting
+ **/
+const char *
+nm_setting_pppoe_get_service (NMSettingPPPOE *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_PPPOE (setting), NULL);
+
+ return NM_SETTING_PPPOE_GET_PRIVATE (setting)->service;
+}
+
+/**
+ * nm_setting_pppoe_get_username:
+ * @setting: the #NMSettingPPPOE
+ *
+ * Returns: the #NMSettingPPPOE:username property of the setting
+ **/
+const char *
+nm_setting_pppoe_get_username (NMSettingPPPOE *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_PPPOE (setting), NULL);
+
+ return NM_SETTING_PPPOE_GET_PRIVATE (setting)->username;
+}
+
+/**
+ * nm_setting_pppoe_get_password:
+ * @setting: the #NMSettingPPPOE
+ *
+ * Returns: the #NMSettingPPPOE:password property of the setting
+ **/
+const char *
+nm_setting_pppoe_get_password (NMSettingPPPOE *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_PPPOE (setting), NULL);
+
+ return NM_SETTING_PPPOE_GET_PRIVATE (setting)->password;
+}
+
+/**
+ * nm_setting_pppoe_get_password_flags:
+ * @setting: the #NMSettingPPPOE
+ *
+ * Returns: the #NMSettingSecretFlags pertaining to the #NMSettingPPPOE:password
+ **/
+NMSettingSecretFlags
+nm_setting_pppoe_get_password_flags (NMSettingPPPOE *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_PPPOE (setting), NM_SETTING_SECRET_FLAG_NONE);
+
+ return NM_SETTING_PPPOE_GET_PRIVATE (setting)->password_flags;
+}
+
+static gboolean
+verify (NMSetting *setting, GSList *all_settings, GError **error)
+{
+ NMSettingPPPOEPrivate *priv = NM_SETTING_PPPOE_GET_PRIVATE (setting);
+
+ if (!priv->username) {
+ g_set_error_literal (error,
+ NM_SETTING_PPPOE_ERROR,
+ NM_SETTING_PPPOE_ERROR_MISSING_PROPERTY,
+ _("property is missing"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_PPPOE_SETTING_NAME, NM_SETTING_PPPOE_USERNAME);
+ return FALSE;
+ } else if (!strlen (priv->username)) {
+ g_set_error_literal (error,
+ NM_SETTING_PPPOE_ERROR,
+ NM_SETTING_PPPOE_ERROR_INVALID_PROPERTY,
+ _("property is empty"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_PPPOE_SETTING_NAME, NM_SETTING_PPPOE_USERNAME);
+ return FALSE;
+ }
+
+ if (priv->service && !strlen (priv->service)) {
+ g_set_error_literal (error,
+ NM_SETTING_PPPOE_ERROR,
+ NM_SETTING_PPPOE_ERROR_INVALID_PROPERTY,
+ _("property is empty"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_PPPOE_SETTING_NAME, NM_SETTING_PPPOE_SERVICE);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static GPtrArray *
+need_secrets (NMSetting *setting)
+{
+ NMSettingPPPOEPrivate *priv = NM_SETTING_PPPOE_GET_PRIVATE (setting);
+ GPtrArray *secrets = NULL;
+
+ if (priv->password)
+ return NULL;
+
+ if (!(priv->password_flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED)) {
+ secrets = g_ptr_array_sized_new (1);
+ g_ptr_array_add (secrets, NM_SETTING_PPPOE_PASSWORD);
+ }
+
+ return secrets;
+}
+
+static void
+nm_setting_pppoe_init (NMSettingPPPOE *setting)
+{
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ NMSettingPPPOEPrivate *priv = NM_SETTING_PPPOE_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_SERVICE:
+ g_free (priv->service);
+ priv->service = g_value_dup_string (value);
+ break;
+ case PROP_USERNAME:
+ g_free (priv->username);
+ priv->username = g_value_dup_string (value);
+ break;
+ case PROP_PASSWORD:
+ g_free (priv->password);
+ priv->password = g_value_dup_string (value);
+ break;
+ case PROP_PASSWORD_FLAGS:
+ priv->password_flags = g_value_get_uint (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ NMSettingPPPOE *setting = NM_SETTING_PPPOE (object);
+
+ switch (prop_id) {
+ case PROP_SERVICE:
+ g_value_set_string (value, nm_setting_pppoe_get_service (setting));
+ break;
+ case PROP_USERNAME:
+ g_value_set_string (value, nm_setting_pppoe_get_username (setting));
+ break;
+ case PROP_PASSWORD:
+ g_value_set_string (value, nm_setting_pppoe_get_password (setting));
+ break;
+ case PROP_PASSWORD_FLAGS:
+ g_value_set_uint (value, nm_setting_pppoe_get_password_flags (setting));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+finalize (GObject *object)
+{
+ NMSettingPPPOEPrivate *priv = NM_SETTING_PPPOE_GET_PRIVATE (object);
+
+ g_free (priv->username);
+ g_free (priv->password);
+ g_free (priv->service);
+
+ G_OBJECT_CLASS (nm_setting_pppoe_parent_class)->finalize (object);
+}
+
+static void
+nm_setting_pppoe_class_init (NMSettingPPPOEClass *setting_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
+ NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+
+ g_type_class_add_private (setting_class, sizeof (NMSettingPPPOEPrivate));
+
+ /* virtual methods */
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+ object_class->finalize = finalize;
+ parent_class->verify = verify;
+ parent_class->need_secrets = need_secrets;
+
+ /* Properties */
+ /**
+ * NMSettingPPPOE:service:
+ *
+ * If specified, instruct PPPoE to only initiate sessions with access
+ * concentrators that provide the specified service. For most providers,
+ * this should be left blank. It is only required if there are multiple
+ * access concentrators or a specific service is known to be required.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_SERVICE,
+ g_param_spec_string (NM_SETTING_PPPOE_SERVICE, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingPPPOE:username:
+ *
+ * Username used to authenticate with the PPPoE service.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_USERNAME,
+ g_param_spec_string (NM_SETTING_PPPOE_USERNAME, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingPPPOE:password:
+ *
+ * Password used to authenticate with the PPPoE service.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PASSWORD,
+ g_param_spec_string (NM_SETTING_PPPOE_PASSWORD, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ NM_SETTING_PARAM_SECRET |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingPPPOE:password-flags:
+ *
+ * Flags indicating how to handle the #NMSettingPPPOE:password property.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PASSWORD_FLAGS,
+ g_param_spec_uint (NM_SETTING_PPPOE_PASSWORD_FLAGS, "", "",
+ NM_SETTING_SECRET_FLAG_NONE,
+ NM_SETTING_SECRET_FLAGS_ALL,
+ NM_SETTING_SECRET_FLAG_NONE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/libnm-core/nm-setting-pppoe.h b/libnm-core/nm-setting-pppoe.h
new file mode 100644
index 0000000000..9d04af9e24
--- /dev/null
+++ b/libnm-core/nm-setting-pppoe.h
@@ -0,0 +1,87 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2011 Red Hat, Inc.
+ * Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#ifndef NM_SETTING_PPPOE_H
+#define NM_SETTING_PPPOE_H
+
+#include <nm-setting.h>
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_SETTING_PPPOE (nm_setting_pppoe_get_type ())
+#define NM_SETTING_PPPOE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_PPPOE, NMSettingPPPOE))
+#define NM_SETTING_PPPOE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_PPPOE, NMSettingPPPOEClass))
+#define NM_IS_SETTING_PPPOE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_PPPOE))
+#define NM_IS_SETTING_PPPOE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_PPPOE))
+#define NM_SETTING_PPPOE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_PPPOE, NMSettingPPPOEClass))
+
+#define NM_SETTING_PPPOE_SETTING_NAME "pppoe"
+
+/**
+ * NMSettingPPPOEError:
+ * @NM_SETTING_PPPOE_ERROR_UNKNOWN: unknown or unclassified error
+ * @NM_SETTING_PPPOE_ERROR_INVALID_PROPERTY: the property was invalid
+ * @NM_SETTING_PPPOE_ERROR_MISSING_PROPERTY: the property was missing and is
+ * required
+ * @NM_SETTING_PPPOE_ERROR_MISSING_PPP_SETTING: the connection
+ * did not contain a required PPP setting for PPP related options
+ */
+typedef enum {
+ NM_SETTING_PPPOE_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/
+ NM_SETTING_PPPOE_ERROR_INVALID_PROPERTY, /*< nick=InvalidProperty >*/
+ NM_SETTING_PPPOE_ERROR_MISSING_PROPERTY, /*< nick=MissingProperty >*/
+ NM_SETTING_PPPOE_ERROR_MISSING_PPP_SETTING /*< nick=MissingPPPSetting >*/
+} NMSettingPPPOEError;
+
+#define NM_SETTING_PPPOE_ERROR nm_setting_pppoe_error_quark ()
+GQuark nm_setting_pppoe_error_quark (void);
+
+#define NM_SETTING_PPPOE_SERVICE "service"
+#define NM_SETTING_PPPOE_USERNAME "username"
+#define NM_SETTING_PPPOE_PASSWORD "password"
+#define NM_SETTING_PPPOE_PASSWORD_FLAGS "password-flags"
+
+typedef struct {
+ NMSetting parent;
+} NMSettingPPPOE;
+
+typedef struct {
+ NMSettingClass parent;
+
+ /* Padding for future expansion */
+ void (*_reserved1) (void);
+ void (*_reserved2) (void);
+ void (*_reserved3) (void);
+ void (*_reserved4) (void);
+} NMSettingPPPOEClass;
+
+GType nm_setting_pppoe_get_type (void);
+
+NMSetting *nm_setting_pppoe_new (void);
+const char *nm_setting_pppoe_get_service (NMSettingPPPOE *setting);
+const char *nm_setting_pppoe_get_username (NMSettingPPPOE *setting);
+const char *nm_setting_pppoe_get_password (NMSettingPPPOE *setting);
+NMSettingSecretFlags nm_setting_pppoe_get_password_flags (NMSettingPPPOE *setting);
+
+G_END_DECLS
+
+#endif /* NM_SETTING_PPPOE_H */
diff --git a/libnm-core/nm-setting-private.h b/libnm-core/nm-setting-private.h
new file mode 100644
index 0000000000..99590b9e14
--- /dev/null
+++ b/libnm-core/nm-setting-private.h
@@ -0,0 +1,125 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2011 Red Hat, Inc.
+ */
+
+#ifndef NM_SETTING_PRIVATE_H
+#define NM_SETTING_PRIVATE_H
+
+#include "nm-setting.h"
+#include "nm-glib-compat.h"
+
+#define NM_SETTING_SECRET_FLAGS_ALL \
+ (NM_SETTING_SECRET_FLAG_NONE | \
+ NM_SETTING_SECRET_FLAG_AGENT_OWNED | \
+ NM_SETTING_SECRET_FLAG_NOT_SAVED | \
+ NM_SETTING_SECRET_FLAG_NOT_REQUIRED)
+
+/**
+ * NMSettingVerifyResult:
+ * @NM_SETTING_VERIFY_SUCCESS: the setting verifies successfully
+ * @NM_SETTING_VERIFY_ERROR: the setting has a serious misconfiguration
+ * @NM_SETTING_VERIFY_NORMALIZABLE: the setting is valid but has properties
+ * that should be normalized
+ * @NM_SETTING_VERIFY_NORMALIZABLE_ERROR: the setting is invalid but the
+ * errors can be fixed by nm_connection_normalize().
+ */
+typedef enum {
+ NM_SETTING_VERIFY_SUCCESS = TRUE,
+ NM_SETTING_VERIFY_ERROR = FALSE,
+ NM_SETTING_VERIFY_NORMALIZABLE = 2,
+ NM_SETTING_VERIFY_NORMALIZABLE_ERROR = 3,
+} NMSettingVerifyResult;
+
+void _nm_register_setting (const char *name,
+ const GType type,
+ const guint32 priority,
+ const GQuark error_quark);
+
+/* Ensure, that name is a compile time constant string. Put the function name in parenthesis to suppress expansion. */
+#define _nm_register_setting(name, type, priority, error_quark) _nm_register_setting ((name ""), type, priority, error_quark)
+
+gboolean _nm_setting_is_base_type (NMSetting *setting);
+gboolean _nm_setting_type_is_base_type (GType type);
+GType _nm_setting_lookup_setting_type (const char *name);
+GType _nm_setting_lookup_setting_type_by_quark (GQuark error_quark);
+gint _nm_setting_compare_priority (gconstpointer a, gconstpointer b);
+
+typedef enum NMSettingUpdateSecretResult {
+ NM_SETTING_UPDATE_SECRET_ERROR = FALSE,
+ NM_SETTING_UPDATE_SECRET_SUCCESS_MODIFIED = TRUE,
+ NM_SETTING_UPDATE_SECRET_SUCCESS_UNCHANGED = 2,
+} NMSettingUpdateSecretResult;
+
+NMSettingUpdateSecretResult _nm_setting_update_secrets (NMSetting *setting,
+ GHashTable *secrets,
+ GError **error);
+gboolean _nm_setting_clear_secrets (NMSetting *setting);
+gboolean _nm_setting_clear_secrets_with_flags (NMSetting *setting,
+ NMSettingClearSecretsWithFlagsFn func,
+ gpointer user_data);
+
+
+/* NM_SETTING_COMPARE_FLAG_INFERRABLE: check whether a device-generated
+ * connection can be replaced by a already-defined connection. This flag only
+ * takes into account properties marked with the %NM_SETTING_PARAM_INFERRABLE
+ * flag.
+ */
+#define NM_SETTING_COMPARE_FLAG_INFERRABLE 0x80000000
+
+/* The property of the #NMSetting should be considered during comparisons that
+ * use the %NM_SETTING_COMPARE_FLAG_INFERRABLE flag. Properties that don't have
+ * this flag, are ignored when doing an infrerrable comparison. This flag should
+ * be set on all properties that are read from the kernel or the system when a
+ * connection is generated. eg, IP addresses/routes can be read from the
+ * kernel, but the 'autoconnect' property cannot, so
+ * %NM_SETTING_IP4_CONFIG_ADDRESSES gets the INFERRABLE flag, but
+ * %NM_SETTING_CONNECTION_AUTOCONNECT would not.
+ *
+ * This flag should not be used with properties where the default cannot be
+ * read separately from the current value, like MTU or wired duplex mode.
+ */
+#define NM_SETTING_PARAM_INFERRABLE (1 << (4 + G_PARAM_USER_SHIFT))
+
+/* Ensure the setting's GType is registered at library load time */
+#define NM_SETTING_REGISTER_TYPE(x) \
+static void __attribute__((constructor)) register_setting (void) \
+{ g_type_init (); g_type_ensure (x); }
+
+NMSetting *nm_setting_find_in_list (GSList *settings_list, const char *setting_name);
+
+/* Private NMSettingIP4Config methods */
+#include "nm-setting-ip4-config.h"
+const char *nm_setting_ip4_config_get_address_label (NMSettingIP4Config *setting, guint32 i);
+gboolean nm_setting_ip4_config_add_address_with_label (NMSettingIP4Config *setting, NMIP4Address *address, const char *label);
+
+NMSettingVerifyResult _nm_setting_verify_deprecated_virtual_iface_name (const char *interface_name,
+ gboolean allow_missing,
+ const char *setting_name,
+ const char *setting_property,
+ GQuark error_quark,
+ gint e_invalid_property,
+ gint e_missing_property,
+ GSList *all_settings,
+ GError **error);
+
+NMSettingVerifyResult _nm_setting_verify (NMSetting *setting,
+ GSList *all_settings,
+ GError **error);
+
+#endif /* NM_SETTING_PRIVATE_H */
diff --git a/libnm-core/nm-setting-serial.c b/libnm-core/nm-setting-serial.c
new file mode 100644
index 0000000000..4a7ee89e65
--- /dev/null
+++ b/libnm-core/nm-setting-serial.c
@@ -0,0 +1,320 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2011 Red Hat, Inc.
+ * Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#include <string.h>
+
+#include "nm-setting-serial.h"
+#include "nm-glib-compat.h"
+#include "nm-setting-private.h"
+
+/**
+ * SECTION:nm-setting-serial
+ * @short_description: Describes connection properties for devices that use
+ * serial communications
+ * @include: nm-setting-serial.h
+ *
+ * The #NMSettingSerial object is a #NMSetting subclass that describes
+ * properties necessary for connections that may use serial communications,
+ * such as mobile broadband or analog telephone connections.
+ **/
+
+/**
+ * nm_setting_serial_error_quark:
+ *
+ * Registers an error quark for #NMSettingSerial if necessary.
+ *
+ * Returns: the error quark used for #NMSettingSerial errors.
+ **/
+GQuark
+nm_setting_serial_error_quark (void)
+{
+ static GQuark quark;
+
+ if (G_UNLIKELY (!quark))
+ quark = g_quark_from_static_string ("nm-setting-serial-error-quark");
+ return quark;
+}
+
+
+G_DEFINE_TYPE_WITH_CODE (NMSettingSerial, nm_setting_serial, NM_TYPE_SETTING,
+ _nm_register_setting (NM_SETTING_SERIAL_SETTING_NAME,
+ g_define_type_id,
+ 2,
+ NM_SETTING_SERIAL_ERROR))
+NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_SERIAL)
+
+#define NM_SETTING_SERIAL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_SERIAL, NMSettingSerialPrivate))
+
+typedef struct {
+ guint baud;
+ guint bits;
+ char parity;
+ guint stopbits;
+ guint64 send_delay;
+} NMSettingSerialPrivate;
+
+
+enum {
+ PROP_0,
+ PROP_BAUD,
+ PROP_BITS,
+ PROP_PARITY,
+ PROP_STOPBITS,
+ PROP_SEND_DELAY,
+
+ LAST_PROP
+};
+
+/**
+ * nm_setting_serial_new:
+ *
+ * Creates a new #NMSettingSerial object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingSerial object
+ **/
+NMSetting *
+nm_setting_serial_new (void)
+{
+ return (NMSetting *) g_object_new (NM_TYPE_SETTING_SERIAL, NULL);
+}
+
+/**
+ * nm_setting_serial_get_baud:
+ * @setting: the #NMSettingSerial
+ *
+ * Returns: the #NMSettingSerial:baud property of the setting
+ **/
+guint
+nm_setting_serial_get_baud (NMSettingSerial *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_SERIAL (setting), 0);
+
+ return NM_SETTING_SERIAL_GET_PRIVATE (setting)->baud;
+}
+
+/**
+ * nm_setting_serial_get_bits:
+ * @setting: the #NMSettingSerial
+ *
+ * Returns: the #NMSettingSerial:bits property of the setting
+ **/
+guint
+nm_setting_serial_get_bits (NMSettingSerial *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_SERIAL (setting), 0);
+
+ return NM_SETTING_SERIAL_GET_PRIVATE (setting)->bits;
+}
+
+/**
+ * nm_setting_serial_get_parity:
+ * @setting: the #NMSettingSerial
+ *
+ * Returns: the #NMSettingSerial:parity property of the setting
+ **/
+char
+nm_setting_serial_get_parity (NMSettingSerial *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_SERIAL (setting), 0);
+
+ return NM_SETTING_SERIAL_GET_PRIVATE (setting)->parity;
+}
+
+/**
+ * nm_setting_serial_get_stopbits:
+ * @setting: the #NMSettingSerial
+ *
+ * Returns: the #NMSettingSerial:stopbits property of the setting
+ **/
+guint
+nm_setting_serial_get_stopbits (NMSettingSerial *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_SERIAL (setting), 0);
+
+ return NM_SETTING_SERIAL_GET_PRIVATE (setting)->stopbits;
+}
+
+/**
+ * nm_setting_serial_get_send_delay:
+ * @setting: the #NMSettingSerial
+ *
+ * Returns: the #NMSettingSerial:send-delay property of the setting
+ **/
+guint64
+nm_setting_serial_get_send_delay (NMSettingSerial *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_SERIAL (setting), 0);
+
+ return NM_SETTING_SERIAL_GET_PRIVATE (setting)->send_delay;
+}
+
+static gboolean
+verify (NMSetting *setting, GSList *all_settings, GError **error)
+{
+ return TRUE;
+}
+
+static void
+nm_setting_serial_init (NMSettingSerial *setting)
+{
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ NMSettingSerialPrivate *priv = NM_SETTING_SERIAL_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_BAUD:
+ priv->baud = g_value_get_uint (value);
+ break;
+ case PROP_BITS:
+ priv->bits = g_value_get_uint (value);
+ break;
+ case PROP_PARITY:
+ priv->parity = g_value_get_schar (value);
+ break;
+ case PROP_STOPBITS:
+ priv->stopbits = g_value_get_uint (value);
+ break;
+ case PROP_SEND_DELAY:
+ priv->send_delay = g_value_get_uint64 (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ NMSettingSerial *setting = NM_SETTING_SERIAL (object);
+
+ switch (prop_id) {
+ case PROP_BAUD:
+ g_value_set_uint (value, nm_setting_serial_get_baud (setting));
+ break;
+ case PROP_BITS:
+ g_value_set_uint (value, nm_setting_serial_get_bits (setting));
+ break;
+ case PROP_PARITY:
+ g_value_set_schar (value, nm_setting_serial_get_parity (setting));
+ break;
+ case PROP_STOPBITS:
+ g_value_set_uint (value, nm_setting_serial_get_stopbits (setting));
+ break;
+ case PROP_SEND_DELAY:
+ g_value_set_uint64 (value, nm_setting_serial_get_send_delay (setting));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nm_setting_serial_class_init (NMSettingSerialClass *setting_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
+ NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+
+ g_type_class_add_private (setting_class, sizeof (NMSettingSerialPrivate));
+
+ /* virtual methods */
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+ parent_class->verify = verify;
+
+ /* Properties */
+
+ /**
+ * NMSettingSerial:baud:
+ *
+ * Speed to use for communication over the serial port. Note that this
+ * value usually has no effect for mobile broadband modems as they generally
+ * ignore speed settings and use the highest available speed.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_BAUD,
+ g_param_spec_uint (NM_SETTING_SERIAL_BAUD, "", "",
+ 0, G_MAXUINT, 57600,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingSerial:bits:
+ *
+ * Byte-width of the serial communication. The 8 in "8n1" for example.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_BITS,
+ g_param_spec_uint (NM_SETTING_SERIAL_BITS, "", "",
+ 5, 8, 8,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingSerial:parity:
+ *
+ * Parity setting of the serial port. Either 'E' for even parity, 'o' for
+ * odd parity, or 'n' for no parity.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PARITY,
+ g_param_spec_char (NM_SETTING_SERIAL_PARITY, "", "",
+ 'E', 'o', 'n',
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingSerial:stopbits:
+ *
+ * Number of stop bits for communication on the serial port. Either 1 or 2.
+ * The 1 in "8n1" for example.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_STOPBITS,
+ g_param_spec_uint (NM_SETTING_SERIAL_STOPBITS, "", "",
+ 1, 2, 1,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingSerial:send-delay:
+ *
+ * Time to delay between each byte sent to the modem, in microseconds.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_SEND_DELAY,
+ g_param_spec_uint64 (NM_SETTING_SERIAL_SEND_DELAY, "", "",
+ 0, G_MAXUINT64, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/libnm-core/nm-setting-serial.h b/libnm-core/nm-setting-serial.h
new file mode 100644
index 0000000000..44d46cf37a
--- /dev/null
+++ b/libnm-core/nm-setting-serial.h
@@ -0,0 +1,89 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2008 Red Hat, Inc.
+ * Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#ifndef NM_SETTING_SERIAL_H
+#define NM_SETTING_SERIAL_H
+
+#include <nm-setting.h>
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_SETTING_SERIAL (nm_setting_serial_get_type ())
+#define NM_SETTING_SERIAL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_SERIAL, NMSettingSerial))
+#define NM_SETTING_SERIAL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_SERIAL, NMSettingSerialClass))
+#define NM_IS_SETTING_SERIAL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_SERIAL))
+#define NM_IS_SETTING_SERIAL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_SERIAL))
+#define NM_SETTING_SERIAL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_SERIAL, NMSettingSerialClass))
+
+#define NM_SETTING_SERIAL_SETTING_NAME "serial"
+
+/**
+ * NMSettingSerialError:
+ * @NM_SETTING_SERIAL_ERROR_UNKNOWN: unknown or unclassified error
+ * @NM_SETTING_SERIAL_ERROR_INVALID_PROPERTY: the property was invalid
+ * @NM_SETTING_SERIAL_ERROR_MISSING_PROPERTY: the property was missing and is
+ * required
+ * @NM_SETTING_SERIAL_ERROR_MISSING_PPP_SETTING: one of the properties of the
+ * setting requires the connection to contain an #NMSettingPPP setting
+ */
+typedef enum {
+ NM_SETTING_SERIAL_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/
+ NM_SETTING_SERIAL_ERROR_INVALID_PROPERTY, /*< nick=InvalidProperty >*/
+ NM_SETTING_SERIAL_ERROR_MISSING_PROPERTY, /*< nick=MissingProperty >*/
+ NM_SETTING_SERIAL_ERROR_MISSING_PPP_SETTING /*< nick=MissingPPPSetting >*/
+} NMSettingSerialError;
+
+#define NM_SETTING_SERIAL_ERROR nm_setting_serial_error_quark ()
+GQuark nm_setting_serial_error_quark (void);
+
+#define NM_SETTING_SERIAL_BAUD "baud"
+#define NM_SETTING_SERIAL_BITS "bits"
+#define NM_SETTING_SERIAL_PARITY "parity"
+#define NM_SETTING_SERIAL_STOPBITS "stopbits"
+#define NM_SETTING_SERIAL_SEND_DELAY "send-delay"
+
+typedef struct {
+ NMSetting parent;
+} NMSettingSerial;
+
+typedef struct {
+ NMSettingClass parent;
+
+ /* Padding for future expansion */
+ void (*_reserved1) (void);
+ void (*_reserved2) (void);
+ void (*_reserved3) (void);
+ void (*_reserved4) (void);
+} NMSettingSerialClass;
+
+GType nm_setting_serial_get_type (void);
+
+NMSetting *nm_setting_serial_new (void);
+guint nm_setting_serial_get_baud (NMSettingSerial *setting);
+guint nm_setting_serial_get_bits (NMSettingSerial *setting);
+char nm_setting_serial_get_parity (NMSettingSerial *setting);
+guint nm_setting_serial_get_stopbits (NMSettingSerial *setting);
+guint64 nm_setting_serial_get_send_delay (NMSettingSerial *setting);
+
+G_END_DECLS
+
+#endif /* NM_SETTING_SERIAL_H */
diff --git a/libnm-core/nm-setting-team-port.c b/libnm-core/nm-setting-team-port.c
new file mode 100644
index 0000000000..ad9d8327ed
--- /dev/null
+++ b/libnm-core/nm-setting-team-port.c
@@ -0,0 +1,184 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2013 Jiri Pirko <jiri@resnulli.us>
+ */
+
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <dbus/dbus-glib.h>
+#include <glib/gi18n.h>
+
+#include "nm-setting-team-port.h"
+#include "nm-utils.h"
+#include "nm-utils-private.h"
+#include "nm-setting-private.h"
+
+/**
+ * SECTION:nm-setting-team-port
+ * @short_description: Describes connection properties for team ports
+ * @include: nm-setting-team-port.h
+ *
+ * The #NMSettingTeamPort object is a #NMSetting subclass that describes
+ * optional properties that apply to team ports.
+ *
+ * Since: 0.9.10
+ **/
+
+/**
+ * nm_setting_team_port_error_quark:
+ *
+ * Registers an error quark for #NMSettingTeamPort if necessary.
+ *
+ * Returns: the error quark used for #NMSettingTeamPort errors.
+ *
+ * Since: 0.9.10
+ **/
+GQuark
+nm_setting_team_port_error_quark (void)
+{
+ static GQuark quark;
+
+ if (G_UNLIKELY (!quark))
+ quark = g_quark_from_static_string ("nm-setting-team-port-error-quark");
+ return quark;
+}
+
+G_DEFINE_TYPE_WITH_CODE (NMSettingTeamPort, nm_setting_team_port, NM_TYPE_SETTING,
+ _nm_register_setting (NM_SETTING_TEAM_PORT_SETTING_NAME,
+ g_define_type_id,
+ 3,
+ NM_SETTING_TEAM_PORT_ERROR))
+NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_TEAM_PORT)
+
+#define NM_SETTING_TEAM_PORT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_TEAM_PORT, NMSettingTeamPortPrivate))
+
+typedef struct {
+ char *config;
+} NMSettingTeamPortPrivate;
+
+enum {
+ PROP_0,
+ PROP_CONFIG,
+ LAST_PROP
+};
+
+/**
+ * nm_setting_team_port_new:
+ *
+ * Creates a new #NMSettingTeamPort object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingTeamPort object
+ *
+ * Since: 0.9.10
+ **/
+NMSetting *
+nm_setting_team_port_new (void)
+{
+ return (NMSetting *) g_object_new (NM_TYPE_SETTING_TEAM_PORT, NULL);
+}
+
+/**
+ * nm_setting_team_port_get_config:
+ * @setting: the #NMSettingTeamPort
+ *
+ * Returns: the #NMSettingTeamPort:config property of the setting
+ *
+ * Since: 0.9.10
+ **/
+const char *
+nm_setting_team_port_get_config (NMSettingTeamPort *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_TEAM_PORT (setting), NULL);
+
+ return NM_SETTING_TEAM_PORT_GET_PRIVATE (setting)->config;
+}
+
+static gboolean
+verify (NMSetting *setting, GSList *all_settings, GError **error)
+{
+ return TRUE;
+}
+
+static void
+nm_setting_team_port_init (NMSettingTeamPort *setting)
+{
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ NMSettingTeamPortPrivate *priv = NM_SETTING_TEAM_PORT_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_CONFIG:
+ priv->config = g_value_dup_string (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ NMSettingTeamPort *setting = NM_SETTING_TEAM_PORT (object);
+
+ switch (prop_id) {
+ case PROP_CONFIG:
+ g_value_set_string (value, nm_setting_team_port_get_config (setting));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nm_setting_team_port_class_init (NMSettingTeamPortClass *setting_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
+ NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+
+ g_type_class_add_private (setting_class, sizeof (NMSettingTeamPortPrivate));
+
+ /* virtual methods */
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+ parent_class->verify = verify;
+
+ /* Properties */
+ /**
+ * NMSettingTeamPort:config:
+ *
+ * The JSON configuration for the team port. The property should contain raw
+ * JSON configuration data suitable for teamd, because the value is passed
+ * directly to teamd. If not specified, the default configuration is
+ * used. See man teamd.conf for the format details.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_CONFIG,
+ g_param_spec_string (NM_SETTING_TEAM_PORT_CONFIG, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/libnm-core/nm-setting-team-port.h b/libnm-core/nm-setting-team-port.h
new file mode 100644
index 0000000000..4e8022202a
--- /dev/null
+++ b/libnm-core/nm-setting-team-port.h
@@ -0,0 +1,79 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2013 Jiri Pirko <jiri@resnulli.us>
+ */
+
+#ifndef NM_SETTING_TEAM_PORT_H
+#define NM_SETTING_TEAM_PORT_H
+
+#include <nm-setting.h>
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_SETTING_TEAM_PORT (nm_setting_team_port_get_type ())
+#define NM_SETTING_TEAM_PORT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_TEAM_PORT, NMSettingTeamPort))
+#define NM_SETTING_TEAM_PORT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_TEAM_PORT, NMSettingTeamPortClass))
+#define NM_IS_SETTING_TEAM_PORT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_TEAM_PORT))
+#define NM_IS_SETTING_TEAM_PORT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_TEAM_PORT))
+#define NM_SETTING_TEAM_PORT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_TEAM_PORT, NMSettingTeamPortClass))
+
+#define NM_SETTING_TEAM_PORT_SETTING_NAME "team-port"
+
+/**
+ * NMSettingTeamPortError:
+ * @NM_SETTING_TEAM_PORT_ERROR_UNKNOWN: unknown or unclassified error
+ * @NM_SETTING_TEAM_PORT_ERROR_INVALID_PROPERTY: the property was invalid
+ * @NM_SETTING_TEAM_PORT_ERROR_MISSING_PROPERTY: the property was missing and
+ * is required
+ */
+typedef enum {
+ NM_SETTING_TEAM_PORT_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/
+ NM_SETTING_TEAM_PORT_ERROR_INVALID_PROPERTY, /*< nick=InvalidProperty >*/
+ NM_SETTING_TEAM_PORT_ERROR_MISSING_PROPERTY, /*< nick=MissingProperty >*/
+} NMSettingTeamPortError;
+
+#define NM_SETTING_TEAM_PORT_ERROR nm_setting_team_port_error_quark ()
+GQuark nm_setting_team_port_error_quark (void);
+
+#define NM_SETTING_TEAM_PORT_CONFIG "config"
+
+typedef struct {
+ NMSetting parent;
+} NMSettingTeamPort;
+
+typedef struct {
+ NMSettingClass parent;
+
+ /* Padding for future expansion */
+ void (*_reserved1) (void);
+ void (*_reserved2) (void);
+ void (*_reserved3) (void);
+ void (*_reserved4) (void);
+} NMSettingTeamPortClass;
+
+NM_AVAILABLE_IN_0_9_10
+GType nm_setting_team_port_get_type (void);
+
+NM_AVAILABLE_IN_0_9_10
+NMSetting * nm_setting_team_port_new (void);
+
+const char * nm_setting_team_port_get_config (NMSettingTeamPort *setting);
+
+G_END_DECLS
+
+#endif /* NM_SETTING_TEAM_PORT_H */
diff --git a/libnm-core/nm-setting-team.c b/libnm-core/nm-setting-team.c
new file mode 100644
index 0000000000..046ce51129
--- /dev/null
+++ b/libnm-core/nm-setting-team.c
@@ -0,0 +1,254 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2013 Jiri Pirko <jiri@resnulli.us>
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <dbus/dbus-glib.h>
+#include <glib/gi18n.h>
+
+#include "nm-setting-team.h"
+#include "nm-param-spec-specialized.h"
+#include "nm-utils.h"
+#include "nm-utils-private.h"
+#include "nm-dbus-glib-types.h"
+#include "nm-setting-private.h"
+
+/**
+ * SECTION:nm-setting-team
+ * @short_description: Describes connection properties for teams
+ * @include: nm-setting-team.h
+ *
+ * The #NMSettingTeam object is a #NMSetting subclass that describes properties
+ * necessary for team connections.
+ *
+ * Since: 0.9.10
+ **/
+
+/**
+ * nm_setting_team_error_quark:
+ *
+ * Registers an error quark for #NMSettingTeam if necessary.
+ *
+ * Returns: the error quark used for #NMSettingTeam errors.
+ *
+ * Since: 0.9.10
+ **/
+GQuark
+nm_setting_team_error_quark (void)
+{
+ static GQuark quark;
+
+ if (G_UNLIKELY (!quark))
+ quark = g_quark_from_static_string ("nm-setting-team-error-quark");
+ return quark;
+}
+
+
+G_DEFINE_TYPE_WITH_CODE (NMSettingTeam, nm_setting_team, NM_TYPE_SETTING,
+ _nm_register_setting (NM_SETTING_TEAM_SETTING_NAME,
+ g_define_type_id,
+ 1,
+ NM_SETTING_TEAM_ERROR))
+NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_TEAM)
+
+#define NM_SETTING_TEAM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_TEAM, NMSettingTeamPrivate))
+
+typedef struct {
+ char *interface_name;
+ char *config;
+} NMSettingTeamPrivate;
+
+enum {
+ PROP_0,
+ PROP_INTERFACE_NAME,
+ PROP_CONFIG,
+ LAST_PROP
+};
+
+/**
+ * nm_setting_team_new:
+ *
+ * Creates a new #NMSettingTeam object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingTeam object
+ *
+ * Since: 0.9.10
+ **/
+NMSetting *
+nm_setting_team_new (void)
+{
+ return (NMSetting *) g_object_new (NM_TYPE_SETTING_TEAM, NULL);
+}
+
+/**
+ * nm_setting_team_get_interface_name:
+ * @setting: the #NMSettingTeam
+ *
+ * Returns: the #NMSettingTeam:interface-name property of the setting
+ *
+ * Since: 0.9.10
+ **/
+const char *
+nm_setting_team_get_interface_name (NMSettingTeam *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_TEAM (setting), NULL);
+
+ return NM_SETTING_TEAM_GET_PRIVATE (setting)->interface_name;
+}
+
+/**
+ * nm_setting_team_get_config:
+ * @setting: the #NMSettingTeam
+ *
+ * Returns: the #NMSettingTeam:config property of the setting
+ *
+ * Since: 0.9.10
+ **/
+const char *
+nm_setting_team_get_config (NMSettingTeam *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_TEAM (setting), NULL);
+
+ return NM_SETTING_TEAM_GET_PRIVATE (setting)->config;
+}
+
+static gboolean
+verify (NMSetting *setting, GSList *all_settings, GError **error)
+{
+ NMSettingTeamPrivate *priv = NM_SETTING_TEAM_GET_PRIVATE (setting);
+
+ return _nm_setting_verify_deprecated_virtual_iface_name (
+ priv->interface_name, FALSE,
+ NM_SETTING_TEAM_SETTING_NAME, NM_SETTING_TEAM_INTERFACE_NAME,
+ NM_SETTING_TEAM_ERROR,
+ NM_SETTING_TEAM_ERROR_INVALID_PROPERTY,
+ NM_SETTING_TEAM_ERROR_MISSING_PROPERTY,
+ all_settings, error);
+}
+
+static const char *
+get_virtual_iface_name (NMSetting *setting)
+{
+ NMSettingTeam *self = NM_SETTING_TEAM (setting);
+
+ return nm_setting_team_get_interface_name (self);
+}
+
+static void
+nm_setting_team_init (NMSettingTeam *setting)
+{
+}
+
+static void
+finalize (GObject *object)
+{
+ NMSettingTeamPrivate *priv = NM_SETTING_TEAM_GET_PRIVATE (object);
+
+ g_free (priv->interface_name);
+ g_free (priv->config);
+
+ G_OBJECT_CLASS (nm_setting_team_parent_class)->finalize (object);
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ NMSettingTeamPrivate *priv = NM_SETTING_TEAM_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_INTERFACE_NAME:
+ g_free (priv->interface_name);
+ priv->interface_name = g_value_dup_string (value);
+ break;
+ case PROP_CONFIG:
+ g_free (priv->config);
+ priv->config = g_value_dup_string (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ NMSettingTeam *setting = NM_SETTING_TEAM (object);
+
+ switch (prop_id) {
+ case PROP_INTERFACE_NAME:
+ g_value_set_string (value, nm_setting_team_get_interface_name (setting));
+ break;
+ case PROP_CONFIG:
+ g_value_set_string (value, nm_setting_team_get_config (setting));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nm_setting_team_class_init (NMSettingTeamClass *setting_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
+ NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+
+ g_type_class_add_private (setting_class, sizeof (NMSettingTeamPrivate));
+
+ /* virtual methods */
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+ object_class->finalize = finalize;
+ parent_class->verify = verify;
+ parent_class->get_virtual_iface_name = get_virtual_iface_name;
+
+ /* Properties */
+ /**
+ * NMSettingTeam:interface-name:
+ *
+ * The name of the virtual in-kernel team network interface
+ **/
+ g_object_class_install_property
+ (object_class, PROP_INTERFACE_NAME,
+ g_param_spec_string (NM_SETTING_TEAM_INTERFACE_NAME, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingTeam:config:
+ *
+ * The JSON configuration for the team network interface. The property
+ * should contain raw JSON configuration data suitable for teamd, because
+ * the value is passed directly to teamd. If not specified, the default
+ * configuration is used. See man teamd.conf for the format details.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_CONFIG,
+ g_param_spec_string (NM_SETTING_TEAM_CONFIG, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/libnm-core/nm-setting-team.h b/libnm-core/nm-setting-team.h
new file mode 100644
index 0000000000..d67c8c71b6
--- /dev/null
+++ b/libnm-core/nm-setting-team.h
@@ -0,0 +1,81 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2013 Jiri Pirko <jiri@resnulli.us>
+ */
+
+#ifndef NM_SETTING_TEAM_H
+#define NM_SETTING_TEAM_H
+
+#include <nm-setting.h>
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_SETTING_TEAM (nm_setting_team_get_type ())
+#define NM_SETTING_TEAM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_TEAM, NMSettingTeam))
+#define NM_SETTING_TEAM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_TEAM, NMSettingTeamClass))
+#define NM_IS_SETTING_TEAM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_TEAM))
+#define NM_IS_SETTING_TEAM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_TEAM))
+#define NM_SETTING_TEAM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_TEAM, NMSettingTeamClass))
+
+#define NM_SETTING_TEAM_SETTING_NAME "team"
+
+/**
+ * NMSettingTeamError:
+ * @NM_SETTING_TEAM_ERROR_UNKNOWN: unknown or unclassified error
+ * @NM_SETTING_TEAM_ERROR_INVALID_PROPERTY: the property was invalid
+ * @NM_SETTING_TEAM_ERROR_MISSING_PROPERTY: the property was missing and is
+ * required
+ */
+typedef enum {
+ NM_SETTING_TEAM_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/
+ NM_SETTING_TEAM_ERROR_INVALID_PROPERTY, /*< nick=InvalidProperty >*/
+ NM_SETTING_TEAM_ERROR_MISSING_PROPERTY, /*< nick=MissingProperty >*/
+} NMSettingTeamError;
+
+#define NM_SETTING_TEAM_ERROR nm_setting_team_error_quark ()
+GQuark nm_setting_team_error_quark (void);
+
+#define NM_SETTING_TEAM_INTERFACE_NAME "interface-name"
+#define NM_SETTING_TEAM_CONFIG "config"
+
+typedef struct {
+ NMSetting parent;
+} NMSettingTeam;
+
+typedef struct {
+ NMSettingClass parent;
+
+ /* Padding for future expansion */
+ void (*_reserved1) (void);
+ void (*_reserved2) (void);
+ void (*_reserved3) (void);
+ void (*_reserved4) (void);
+} NMSettingTeamClass;
+
+NM_AVAILABLE_IN_0_9_10
+GType nm_setting_team_get_type (void);
+
+NM_AVAILABLE_IN_0_9_10
+NMSetting * nm_setting_team_new (void);
+
+const char * nm_setting_team_get_interface_name (NMSettingTeam *setting);
+const char * nm_setting_team_get_config (NMSettingTeam *setting);
+
+G_END_DECLS
+
+#endif /* NM_SETTING_TEAM_H */
diff --git a/libnm-core/nm-setting-vlan.c b/libnm-core/nm-setting-vlan.c
new file mode 100644
index 0000000000..21adfdb25d
--- /dev/null
+++ b/libnm-core/nm-setting-vlan.c
@@ -0,0 +1,841 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2011 - 2014 Red Hat, Inc.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <dbus/dbus-glib.h>
+#include <glib/gi18n.h>
+
+#include "nm-setting-vlan.h"
+#include "nm-param-spec-specialized.h"
+#include "nm-utils.h"
+#include "nm-dbus-glib-types.h"
+#include "nm-setting-connection.h"
+#include "nm-setting-private.h"
+
+/**
+ * SECTION:nm-setting-vlan
+ * @short_description: Describes connection properties for VLAN interfaces
+ * @include: nm-setting-vlan.h
+ *
+ * The #NMSettingVlan object is a #NMSetting subclass that describes properties
+ * necessary for connection to VLAN interfaces.
+ **/
+
+/**
+ * nm_setting_vlan_error_quark:
+ *
+ * Registers an error quark for #NMSettingVlan if necessary.
+ *
+ * Returns: the error quark used for #NMSettingVlan errors.
+ **/
+GQuark
+nm_setting_vlan_error_quark (void)
+{
+ static GQuark quark;
+
+ if (G_UNLIKELY (!quark))
+ quark = g_quark_from_static_string ("nm-setting-vlan-error-quark");
+ return quark;
+}
+
+G_DEFINE_TYPE_WITH_CODE (NMSettingVlan, nm_setting_vlan, NM_TYPE_SETTING,
+ _nm_register_setting (NM_SETTING_VLAN_SETTING_NAME,
+ g_define_type_id,
+ 1,
+ NM_SETTING_VLAN_ERROR))
+NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_VLAN)
+
+#define NM_SETTING_VLAN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_VLAN, NMSettingVlanPrivate))
+
+typedef struct {
+ char *interface_name;
+ char *parent;
+ guint32 id;
+ guint32 flags;
+ GSList *ingress_priority_map;
+ GSList *egress_priority_map;
+} NMSettingVlanPrivate;
+
+enum {
+ PROP_0,
+ PROP_INTERFACE_NAME,
+ PROP_PARENT,
+ PROP_ID,
+ PROP_FLAGS,
+ PROP_INGRESS_PRIORITY_MAP,
+ PROP_EGRESS_PRIORITY_MAP,
+ LAST_PROP
+};
+
+#define MAX_SKB_PRIO G_MAXUINT32
+#define MAX_8021P_PRIO 7 /* Max 802.1p priority */
+
+typedef struct {
+ guint32 from;
+ guint32 to;
+} PriorityMap;
+
+/**
+ * nm_setting_vlan_new:
+ *
+ * Creates a new #NMSettingVlan object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingVlan object
+ **/
+NMSetting *
+nm_setting_vlan_new (void)
+{
+ return (NMSetting *) g_object_new (NM_TYPE_SETTING_VLAN, NULL);
+}
+
+/**
+ * nm_setting_vlan_get_interface_name:
+ * @setting: the #NMSettingVlan
+ *
+ * Returns: the #NMSettingVlan:interface_name property of the setting
+ **/
+const char *
+nm_setting_vlan_get_interface_name (NMSettingVlan *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_VLAN (setting), NULL);
+ return NM_SETTING_VLAN_GET_PRIVATE (setting)->interface_name;
+}
+
+/**
+ * nm_setting_vlan_get_parent:
+ * @setting: the #NMSettingVlan
+ *
+ * Returns: the #NMSettingVlan:parent property of the setting
+ **/
+const char *
+nm_setting_vlan_get_parent (NMSettingVlan *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_VLAN (setting), NULL);
+ return NM_SETTING_VLAN_GET_PRIVATE (setting)->parent;
+}
+
+/**
+ * nm_setting_vlan_get_id:
+ * @setting: the #NMSettingVlan
+ *
+ * Returns: the #NMSettingVlan:id property of the setting
+ **/
+guint32
+nm_setting_vlan_get_id (NMSettingVlan *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_VLAN (setting), 0);
+ return NM_SETTING_VLAN_GET_PRIVATE (setting)->id;
+}
+
+/**
+ * nm_setting_vlan_get_flags:
+ * @setting: the #NMSettingVlan
+ *
+ * Returns: the #NMSettingVlan:flags property of the setting
+ **/
+guint32
+nm_setting_vlan_get_flags (NMSettingVlan *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_VLAN (setting), 0);
+ return NM_SETTING_VLAN_GET_PRIVATE (setting)->flags;
+}
+
+static guint32
+get_max_prio (NMVlanPriorityMap map, gboolean from)
+{
+ if (map == NM_VLAN_INGRESS_MAP)
+ return from ? MAX_8021P_PRIO : MAX_SKB_PRIO;
+ else if (map == NM_VLAN_EGRESS_MAP)
+ return from ? MAX_SKB_PRIO : MAX_8021P_PRIO;
+ g_assert_not_reached ();
+}
+
+static PriorityMap *
+priority_map_new_from_str (NMVlanPriorityMap map, const char *str)
+{
+ PriorityMap *p = NULL;
+ gchar **t = NULL;
+ guint32 len;
+ guint64 from, to;
+
+ g_return_val_if_fail (str && str[0], NULL);
+
+ t = g_strsplit (str, ":", 0);
+ len = g_strv_length (t);
+ if (len == 2) {
+ from = g_ascii_strtoull (t[0], NULL, 10);
+ to = g_ascii_strtoull (t[1], NULL, 10);
+
+ if ((from <= get_max_prio (map, TRUE)) && (to <= get_max_prio (map, FALSE))) {
+ p = g_malloc0 (sizeof (PriorityMap));
+ p->from = from;
+ p->to = to;
+ }
+ } else {
+ /* Warn */
+ g_warn_if_fail (len == 2);
+ }
+
+ g_strfreev (t);
+ return p;
+}
+
+static void
+priority_map_free (PriorityMap *map)
+{
+ g_return_if_fail (map != NULL);
+ g_free (map);
+}
+
+static GSList *
+get_map (NMSettingVlan *self, NMVlanPriorityMap map)
+{
+ if (map == NM_VLAN_INGRESS_MAP)
+ return NM_SETTING_VLAN_GET_PRIVATE (self)->ingress_priority_map;
+ else if (map == NM_VLAN_EGRESS_MAP)
+ return NM_SETTING_VLAN_GET_PRIVATE (self)->egress_priority_map;
+ g_assert_not_reached ();
+ return NULL;
+}
+
+static void
+set_map (NMSettingVlan *self, NMVlanPriorityMap map, GSList *list)
+{
+ if (map == NM_VLAN_INGRESS_MAP) {
+ NM_SETTING_VLAN_GET_PRIVATE (self)->ingress_priority_map = list;
+ g_object_notify (G_OBJECT (self), NM_SETTING_VLAN_INGRESS_PRIORITY_MAP);
+ } else if (map == NM_VLAN_EGRESS_MAP) {
+ NM_SETTING_VLAN_GET_PRIVATE (self)->egress_priority_map = list;
+ g_object_notify (G_OBJECT (self), NM_SETTING_VLAN_EGRESS_PRIORITY_MAP);
+ } else
+ g_assert_not_reached ();
+}
+
+/**
+ * nm_setting_vlan_add_priority_str:
+ * @setting: the #NMSettingVlan
+ * @map: the type of priority map
+ * @str: the string which contains a priority map, like "3:7"
+ *
+ * Adds a priority map entry into either the #NMSettingVlan:ingress_priority_map
+ * or the #NMSettingVlan:egress_priority_map properties. The priority map maps
+ * the Linux SKB priorities to 802.1p priorities.
+ *
+ * Returns: %TRUE if the entry was successfully added to the list, or it
+ * overwrote the old value, %FALSE if error
+ */
+gboolean
+nm_setting_vlan_add_priority_str (NMSettingVlan *setting,
+ NMVlanPriorityMap map,
+ const char *str)
+{
+ GSList *list = NULL, *iter = NULL;
+ PriorityMap *item = NULL;
+
+ g_return_val_if_fail (NM_IS_SETTING_VLAN (setting), FALSE);
+ g_return_val_if_fail (map == NM_VLAN_INGRESS_MAP || map == NM_VLAN_EGRESS_MAP, FALSE);
+ g_return_val_if_fail (str && str[0], FALSE);
+
+ list = get_map (setting, map);
+
+ item = priority_map_new_from_str (map, str);
+ g_return_val_if_fail (item != NULL, FALSE);
+
+ /* Duplicates get replaced */
+ for (iter = list; iter; iter = g_slist_next (iter)) {
+ PriorityMap *p = iter->data;
+
+ if (p->from == item->from) {
+ p->to = item->to;
+ g_free (item);
+ if (map == NM_VLAN_INGRESS_MAP)
+ g_object_notify (G_OBJECT (setting), NM_SETTING_VLAN_INGRESS_PRIORITY_MAP);
+ else
+ g_object_notify (G_OBJECT (setting), NM_SETTING_VLAN_EGRESS_PRIORITY_MAP);
+ return TRUE;
+ }
+ }
+
+ set_map (setting, map, g_slist_append (list, item));
+ return TRUE;
+}
+
+/**
+ * nm_setting_vlan_get_num_priorities:
+ * @setting: the #NMSettingVlan
+ * @map: the type of priority map
+ *
+ * Returns the number of entires in the
+ * #NMSettingVlan:ingress_priority_map or #NMSettingVlan:egress_priority_map
+ * properties of this setting.
+ *
+ * Returns: return the number of ingress/egress priority entries, -1 if error
+ **/
+gint32
+nm_setting_vlan_get_num_priorities (NMSettingVlan *setting, NMVlanPriorityMap map)
+{
+ g_return_val_if_fail (NM_IS_SETTING_VLAN (setting), -1);
+ g_return_val_if_fail (map == NM_VLAN_INGRESS_MAP || map == NM_VLAN_EGRESS_MAP, -1);
+
+ return g_slist_length (get_map (setting, map));
+}
+
+/**
+ * nm_setting_vlan_get_priority:
+ * @setting: the #NMSettingVlan
+ * @map: the type of priority map
+ * @idx: the zero-based index of the ingress/egress priority map entry
+ * @out_from: (out): on return the value of the priority map's 'from' item
+ * @out_to: (out): on return the value of priority map's 'to' item
+ *
+ * Retrieve one of the entries of the #NMSettingVlan:ingress_priority_map
+ * or #NMSettingVlan:egress_priority_map properties of this setting.
+ *
+ * Returns: %TRUE if a priority map was returned, %FALSE if error
+ **/
+gboolean
+nm_setting_vlan_get_priority (NMSettingVlan *setting,
+ NMVlanPriorityMap map,
+ guint32 idx,
+ guint32 *out_from,
+ guint32 *out_to)
+{
+ GSList *list = NULL;
+ PriorityMap *item = NULL;
+
+ g_return_val_if_fail (NM_IS_SETTING_VLAN (setting), FALSE);
+ g_return_val_if_fail (map == NM_VLAN_INGRESS_MAP || map == NM_VLAN_EGRESS_MAP, FALSE);
+ g_return_val_if_fail (out_from != NULL, FALSE);
+ g_return_val_if_fail (out_to != NULL, FALSE);
+
+ list = get_map (setting, map);
+ g_return_val_if_fail (idx < g_slist_length (list), FALSE);
+
+ item = g_slist_nth_data (list, idx);
+ g_assert (item);
+ *out_from = item->from;
+ *out_to = item->to;
+ return TRUE;
+}
+
+/**
+ * nm_setting_vlan_add_priority:
+ * @setting: the #NMSettingVlan
+ * @map: the type of priority map
+ * @from: the priority to map to @to
+ * @to: the priority to map @from to
+ *
+ * Adds a priority mapping to the #NMSettingVlan:ingress_priority_map or
+ * #NMSettingVlan:egress_priority_map properties of the setting. If @from is
+ * already in the given priority map, this function will overwrite the
+ * existing entry with the new @to.
+ *
+ * If @map is #NM_VLAN_INGRESS_MAP then @from is the incoming 802.1q VLAN
+ * Priority Code Point (PCP) value, and @to is the Linux SKB priority value.
+ *
+ * If @map is #NM_VLAN_EGRESS_MAP then @from is the Linux SKB priority value and
+ * @to is the outgoing 802.1q VLAN Priority Code Point (PCP) value.
+ *
+ * Returns: %TRUE if the new priority mapping was successfully added to the
+ * list, %FALSE if error
+ */
+gboolean
+nm_setting_vlan_add_priority (NMSettingVlan *setting,
+ NMVlanPriorityMap map,
+ guint32 from,
+ guint32 to)
+{
+ GSList *list = NULL, *iter = NULL;
+ PriorityMap *item;
+
+ g_return_val_if_fail (NM_IS_SETTING_VLAN (setting), FALSE);
+ g_return_val_if_fail (map == NM_VLAN_INGRESS_MAP || map == NM_VLAN_EGRESS_MAP, FALSE);
+
+ list = get_map (setting, map);
+ for (iter = list; iter; iter = g_slist_next (iter)) {
+ item = iter->data;
+ if (item->from == from) {
+ item->to = to;
+ if (map == NM_VLAN_INGRESS_MAP)
+ g_object_notify (G_OBJECT (setting), NM_SETTING_VLAN_INGRESS_PRIORITY_MAP);
+ else
+ g_object_notify (G_OBJECT (setting), NM_SETTING_VLAN_EGRESS_PRIORITY_MAP);
+ return TRUE;
+ }
+ }
+
+ item = g_malloc0 (sizeof (PriorityMap));
+ item->from = from;
+ item->to = to;
+ set_map (setting, map, g_slist_append (list, item));
+
+ return TRUE;
+}
+
+/**
+ * nm_setting_vlan_remove_priority:
+ * @setting: the #NMSettingVlan
+ * @map: the type of priority map
+ * @idx: the zero-based index of the priority map to remove
+ *
+ * Removes the priority map at index @idx from the
+ * #NMSettingVlan:ingress_priority_map or #NMSettingVlan:egress_priority_map
+ * properties.
+ */
+void
+nm_setting_vlan_remove_priority (NMSettingVlan *setting,
+ NMVlanPriorityMap map,
+ guint32 idx)
+{
+ GSList *list = NULL, *item = NULL;
+
+ g_return_if_fail (NM_IS_SETTING_VLAN (setting));
+ g_return_if_fail (map == NM_VLAN_INGRESS_MAP || map == NM_VLAN_EGRESS_MAP);
+
+ list = get_map (setting, map);
+ g_return_if_fail (idx < g_slist_length (list));
+
+ item = g_slist_nth (list, idx);
+ priority_map_free ((PriorityMap *) (item->data));
+ set_map (setting, map, g_slist_delete_link (list, item));
+}
+
+/**
+ * nm_setting_vlan_remove_priority_by_value:
+ * @setting: the #NMSettingVlan
+ * @map: the type of priority map
+ * @from: the priority to map to @to
+ * @to: the priority to map @from to
+ *
+ * Removes the priority map @form:@to from the #NMSettingVlan:ingress_priority_map
+ * or #NMSettingVlan:egress_priority_map (according to @map argument)
+ * properties.
+ *
+ * Returns: %TRUE if the priority mapping was found and removed; %FALSE if it was not.
+ *
+ * Since: 0.9.10
+ */
+gboolean
+nm_setting_vlan_remove_priority_by_value (NMSettingVlan *setting,
+ NMVlanPriorityMap map,
+ guint32 from,
+ guint32 to)
+{
+ GSList *list = NULL, *iter = NULL;
+ PriorityMap *item;
+
+ g_return_val_if_fail (NM_IS_SETTING_VLAN (setting), FALSE);
+ g_return_val_if_fail (map == NM_VLAN_INGRESS_MAP || map == NM_VLAN_EGRESS_MAP, FALSE);
+
+ list = get_map (setting, map);
+ for (iter = list; iter; iter = g_slist_next (iter)) {
+ item = iter->data;
+ if (item->from == from && item->to == to) {
+ priority_map_free ((PriorityMap *) (iter->data));
+ set_map (setting, map, g_slist_delete_link (list, iter));
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/**
+ * nm_setting_vlan_remove_priority_str_by_value:
+ * @setting: the #NMSettingVlan
+ * @map: the type of priority map
+ * @str: the string which contains a priority map, like "3:7"
+ *
+ * Removes the priority map @str from the #NMSettingVlan:ingress_priority_map
+ * or #NMSettingVlan:egress_priority_map (according to @map argument)
+ * properties.
+ *
+ * Returns: %TRUE if the priority mapping was found and removed; %FALSE if it was not.
+ *
+ * Since: 0.9.10
+ */
+gboolean
+nm_setting_vlan_remove_priority_str_by_value (NMSettingVlan *setting,
+ NMVlanPriorityMap map,
+ const char *str)
+{
+ PriorityMap *item;
+ gboolean found;
+
+ g_return_val_if_fail (NM_IS_SETTING_VLAN (setting), FALSE);
+ g_return_val_if_fail (map == NM_VLAN_INGRESS_MAP || map == NM_VLAN_EGRESS_MAP, FALSE);
+
+ item = priority_map_new_from_str (map, str);
+ if (!item)
+ return FALSE;
+
+ found = nm_setting_vlan_remove_priority_by_value (setting, map, item->from, item->to);
+ g_free (item);
+ return found;
+}
+
+/**
+ * nm_setting_vlan_clear_priorities:
+ * @setting: the #NMSettingVlan
+ * @map: the type of priority map
+ *
+ * Clear all the entires from #NMSettingVlan:ingress_priority_map or
+ * #NMSettingVlan:egress_priority_map properties.
+ */
+void
+nm_setting_vlan_clear_priorities (NMSettingVlan *setting, NMVlanPriorityMap map)
+{
+ GSList *list = NULL;
+
+ g_return_if_fail (NM_IS_SETTING_VLAN (setting));
+ g_return_if_fail (map == NM_VLAN_INGRESS_MAP || map == NM_VLAN_EGRESS_MAP);
+
+ list = get_map (setting, map);
+ g_slist_free_full (list, g_free);
+ set_map (setting, map, NULL);
+}
+
+/*********************************************************************/
+
+static void
+nm_setting_vlan_init (NMSettingVlan *setting)
+{
+}
+
+static gboolean
+verify (NMSetting *setting, GSList *all_settings, GError **error)
+{
+ NMSettingVlanPrivate *priv = NM_SETTING_VLAN_GET_PRIVATE (setting);
+ NMSettingConnection *s_con = NULL;
+ NMSettingWired *s_wired = NULL;
+ GSList *iter;
+
+ for (iter = all_settings; iter; iter = iter->next) {
+ if (NM_IS_SETTING_CONNECTION (iter->data))
+ s_con = iter->data;
+ else if (NM_IS_SETTING_WIRED (iter->data))
+ s_wired = iter->data;
+ }
+
+ if (priv->parent) {
+ if (nm_utils_is_uuid (priv->parent)) {
+ /* If we have an NMSettingConnection:master with slave-type="vlan",
+ * then it must be the same UUID.
+ */
+ if (s_con) {
+ const char *master = NULL, *slave_type = NULL;
+
+ slave_type = nm_setting_connection_get_slave_type (s_con);
+ if (!g_strcmp0 (slave_type, NM_SETTING_VLAN_SETTING_NAME))
+ master = nm_setting_connection_get_master (s_con);
+
+ if (master && g_strcmp0 (priv->parent, master) != 0) {
+ g_set_error (error,
+ NM_SETTING_VLAN_ERROR,
+ NM_SETTING_VLAN_ERROR_INVALID_PARENT,
+ _("'%s' value doesn't match '%s=%s'"),
+ priv->parent, NM_SETTING_CONNECTION_MASTER, master);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_VLAN_SETTING_NAME, NM_SETTING_VLAN_PARENT);
+ return FALSE;
+ }
+ }
+ } else if (!nm_utils_iface_valid_name (priv->parent)) {
+ /* parent must be either a UUID or an interface name */
+ g_set_error (error,
+ NM_SETTING_VLAN_ERROR,
+ NM_SETTING_VLAN_ERROR_INVALID_PROPERTY,
+ _("'%s' is neither an UUID nor an interface name"),
+ priv->parent);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_VLAN_SETTING_NAME, NM_SETTING_VLAN_PARENT);
+ return FALSE;
+ }
+ } else {
+ /* If parent is NULL, the parent must be specified via
+ * NMSettingWired:mac-address.
+ */
+ if (!s_wired || !nm_setting_wired_get_mac_address (s_wired)) {
+ g_set_error (error,
+ NM_SETTING_VLAN_ERROR,
+ NM_SETTING_VLAN_ERROR_MISSING_PROPERTY,
+ _("property is not specified and neither is '%s:%s'"),
+ NM_SETTING_WIRED_SETTING_NAME, NM_SETTING_WIRED_MAC_ADDRESS);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_VLAN_SETTING_NAME, NM_SETTING_VLAN_PARENT);
+ return FALSE;
+ }
+ }
+
+ if (priv->flags & ~(NM_VLAN_FLAG_REORDER_HEADERS |
+ NM_VLAN_FLAG_GVRP |
+ NM_VLAN_FLAG_LOOSE_BINDING)) {
+ g_set_error_literal (error,
+ NM_SETTING_VLAN_ERROR,
+ NM_SETTING_VLAN_ERROR_INVALID_PROPERTY,
+ _("flags are invalid"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_VLAN_SETTING_NAME, NM_SETTING_VLAN_FLAGS);
+ return FALSE;
+ }
+
+ /* If interface_name is specified, it must be a valid interface name. We
+ * don't check that it matches parent and/or id, because we allow
+ * renaming vlans to arbitrary names.
+ */
+ return _nm_setting_verify_deprecated_virtual_iface_name (
+ priv->interface_name, TRUE,
+ NM_SETTING_VLAN_SETTING_NAME, NM_SETTING_VLAN_INTERFACE_NAME,
+ NM_SETTING_VLAN_ERROR,
+ NM_SETTING_VLAN_ERROR_INVALID_PROPERTY,
+ NM_SETTING_VLAN_ERROR_MISSING_PROPERTY,
+ all_settings, error);
+}
+
+static const char *
+get_virtual_iface_name (NMSetting *setting)
+{
+ return nm_setting_vlan_get_interface_name (NM_SETTING_VLAN (setting));
+}
+
+static GSList *
+priority_stringlist_to_maplist (NMVlanPriorityMap map, GSList *strlist)
+{
+ GSList *list = NULL, *iter;
+
+ for (iter = strlist; iter; iter = g_slist_next (iter)) {
+ PriorityMap *item;
+
+ item = priority_map_new_from_str (map, (const char *) iter->data);
+ if (item)
+ list = g_slist_prepend (list, item);
+ }
+ return g_slist_reverse (list);
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ NMSettingVlan *setting = NM_SETTING_VLAN (object);
+ NMSettingVlanPrivate *priv = NM_SETTING_VLAN_GET_PRIVATE (setting);
+
+ switch (prop_id) {
+ case PROP_INTERFACE_NAME:
+ g_free (priv->interface_name);
+ priv->interface_name = g_value_dup_string (value);
+ break;
+ case PROP_PARENT:
+ g_free (priv->parent);
+ priv->parent = g_value_dup_string (value);
+ break;
+ case PROP_ID:
+ priv->id = g_value_get_uint (value);
+ break;
+ case PROP_FLAGS:
+ priv->flags = g_value_get_uint (value);
+ break;
+ case PROP_INGRESS_PRIORITY_MAP:
+ g_slist_free_full (priv->ingress_priority_map, g_free);
+ priv->ingress_priority_map =
+ priority_stringlist_to_maplist (NM_VLAN_INGRESS_MAP, g_value_get_boxed (value));
+ break;
+ case PROP_EGRESS_PRIORITY_MAP:
+ g_slist_free_full (priv->egress_priority_map, g_free);
+ priv->egress_priority_map =
+ priority_stringlist_to_maplist (NM_VLAN_EGRESS_MAP, g_value_get_boxed (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static GSList *
+priority_maplist_to_stringlist (GSList *list)
+{
+ GSList *strlist = NULL, *iter;
+
+ for (iter = list; iter; iter = g_slist_next (iter)) {
+ PriorityMap *item = iter->data;
+
+ strlist = g_slist_prepend (strlist, g_strdup_printf ("%d:%d", item->from, item->to));
+ }
+ return g_slist_reverse (strlist);
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ NMSettingVlan *setting = NM_SETTING_VLAN (object);
+ NMSettingVlanPrivate *priv = NM_SETTING_VLAN_GET_PRIVATE (setting);
+
+ switch (prop_id) {
+ case PROP_INTERFACE_NAME:
+ g_value_set_string (value, priv->interface_name);
+ break;
+ case PROP_PARENT:
+ g_value_set_string (value, priv->parent);
+ break;
+ case PROP_ID:
+ g_value_set_uint (value, priv->id);
+ break;
+ case PROP_FLAGS:
+ g_value_set_uint (value, priv->flags);
+ break;
+ case PROP_INGRESS_PRIORITY_MAP:
+ g_value_take_boxed (value, priority_maplist_to_stringlist (priv->ingress_priority_map));
+ break;
+ case PROP_EGRESS_PRIORITY_MAP:
+ g_value_take_boxed (value, priority_maplist_to_stringlist (priv->egress_priority_map));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+finalize (GObject *object)
+{
+ NMSettingVlan *setting = NM_SETTING_VLAN (object);
+ NMSettingVlanPrivate *priv = NM_SETTING_VLAN_GET_PRIVATE (setting);
+
+ g_free (priv->interface_name);
+ g_free (priv->parent);
+ g_slist_free_full (priv->ingress_priority_map, g_free);
+ g_slist_free_full (priv->egress_priority_map, g_free);
+
+ G_OBJECT_CLASS (nm_setting_vlan_parent_class)->finalize (object);
+}
+
+static void
+nm_setting_vlan_class_init (NMSettingVlanClass *setting_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
+ NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+
+ g_type_class_add_private (setting_class, sizeof (NMSettingVlanPrivate));
+
+ /* virtual methods */
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+ object_class->finalize = finalize;
+ parent_class->verify = verify;
+ parent_class->get_virtual_iface_name = get_virtual_iface_name;
+
+ /* Properties */
+
+ /**
+ * NMSettingVlan:interface-name:
+ *
+ * If given, specifies the kernel name of the VLAN interface. If not given,
+ * a default name will be constructed from the interface described by the
+ * parent interface and the #NMSettingVlan:id property, eg "eth2.1". The
+ * parent interface may be given by the #NMSettingVlan:parent property or by
+ * the #NMSettingWired:mac-address property of an #NMSettingWired setting.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_INTERFACE_NAME,
+ g_param_spec_string (NM_SETTING_VLAN_INTERFACE_NAME, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingVlan:parent:
+ *
+ * If given, specifies the parent interface name or parent connection UUID
+ * from which this VLAN interface should be created. If this property is
+ * not specified, the connection must contain an #NMSettingWired setting
+ * with a #NMSettingWired:mac-address property.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PARENT,
+ g_param_spec_string (NM_SETTING_VLAN_PARENT, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingVlan:id:
+ *
+ * The VLAN identifier that the interface created by this connection should
+ * be assigned.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_ID,
+ g_param_spec_uint (NM_SETTING_VLAN_ID, "", "",
+ 0, 4095, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingVlan:flags:
+ *
+ * One or more flags which control the behavior and features of the VLAN
+ * interface. Flags include %NM_VLAN_FLAG_REORDER_HEADERS (reordering of
+ * output packet headers), %NM_VLAN_FLAG_GVRP (use of the GVRP protocol),
+ * and %NM_VLAN_FLAG_LOOSE_BINDING (loose binding of the interface to its
+ * master device's operating state).
+ **/
+ g_object_class_install_property
+ (object_class, PROP_FLAGS,
+ g_param_spec_uint (NM_SETTING_VLAN_FLAGS, "", "",
+ 0, G_MAXUINT32, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingVlan:ingress-priority-map:
+ *
+ * For incoming packets, a list of mappings from 802.1p priorities to Linux
+ * SKB priorities. The mapping is given in the format "from:to" where both
+ * "from" and "to" are unsigned integers, ie "7:3".
+ **/
+ g_object_class_install_property
+ (object_class, PROP_INGRESS_PRIORITY_MAP,
+ _nm_param_spec_specialized (NM_SETTING_VLAN_INGRESS_PRIORITY_MAP, "", "",
+ DBUS_TYPE_G_LIST_OF_STRING,
+ G_PARAM_READWRITE |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingVlan:egress-priority-map:
+ *
+ * For outgoing packets, a list of mappings from Linux SKB priorities to
+ * 802.1p priorities. The mapping is given in the format "from:to" where
+ * both "from" and "to" are unsigned integers, ie "7:3".
+ **/
+ g_object_class_install_property
+ (object_class, PROP_EGRESS_PRIORITY_MAP,
+ _nm_param_spec_specialized (NM_SETTING_VLAN_EGRESS_PRIORITY_MAP, "", "",
+ DBUS_TYPE_G_LIST_OF_STRING,
+ G_PARAM_READWRITE |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/libnm-core/nm-setting-vlan.h b/libnm-core/nm-setting-vlan.h
new file mode 100644
index 0000000000..3e209480c8
--- /dev/null
+++ b/libnm-core/nm-setting-vlan.h
@@ -0,0 +1,156 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2011 - 2014 Red Hat, Inc.
+ */
+
+#ifndef NM_SETTING_VLAN_H
+#define NM_SETTING_VLAN_H
+
+#include "nm-setting.h"
+#include <linux/if_vlan.h>
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_SETTING_VLAN (nm_setting_vlan_get_type ())
+#define NM_SETTING_VLAN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_VLAN, NMSettingVlan))
+#define NM_SETTING_VLAN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_VLANCONFIG, NMSettingVlanClass))
+#define NM_IS_SETTING_VLAN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_VLAN))
+#define NM_IS_SETTING_VLAN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_VLAN))
+#define NM_SETTING_VLAN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_VLAN, NMSettingVlanClass))
+
+#define NM_SETTING_VLAN_SETTING_NAME "vlan"
+
+/**
+ * NMSettingVlanError:
+ * @NM_SETTING_VLAN_ERROR_UNKNOWN: unknown or unclassified error
+ * @NM_SETTING_VLAN_ERROR_INVALID_PROPERTY: the property was invalid
+ * @NM_SETTING_VLAN_ERROR_MISSING_PROPERTY: the property was missing and is
+ * required
+ * @NM_SETTING_VLAN_ERROR_INVALID_PARENT: the VLAN parent was specified
+ * inconsistently
+ */
+typedef enum {
+ NM_SETTING_VLAN_ERROR_UNKNOWN = 0, /*< nick=Unknown >*/
+ NM_SETTING_VLAN_ERROR_INVALID_PROPERTY, /*< nick=InvalidProperty >*/
+ NM_SETTING_VLAN_ERROR_MISSING_PROPERTY, /*< nick=MissingProperty >*/
+ NM_SETTING_VLAN_ERROR_INVALID_PARENT /*< nick=InvalidParent >*/
+} NMSettingVlanError;
+
+#define NM_SETTING_VLAN_ERROR nm_setting_vlan_error_quark ()
+GQuark nm_setting_vlan_error_quark (void);
+
+#define NM_SETTING_VLAN_INTERFACE_NAME "interface-name"
+#define NM_SETTING_VLAN_PARENT "parent"
+#define NM_SETTING_VLAN_ID "id"
+#define NM_SETTING_VLAN_FLAGS "flags"
+#define NM_SETTING_VLAN_INGRESS_PRIORITY_MAP "ingress-priority-map"
+#define NM_SETTING_VLAN_EGRESS_PRIORITY_MAP "egress-priority-map"
+
+typedef struct {
+ NMSetting parent;
+} NMSettingVlan;
+
+typedef struct {
+ NMSettingClass parent;
+
+ /* Padding for future expansion */
+ void (*_reserved1) (void);
+ void (*_reserved2) (void);
+ void (*_reserved3) (void);
+ void (*_reserved4) (void);
+} NMSettingVlanClass;
+
+/**
+ * NMVlanPriorityMap:
+ * @NM_VLAN_INGRESS_MAP: map for incoming data
+ * @NM_VLAN_EGRESS_MAP: map for outgoing data
+ *
+ * A selector for traffic priority maps; these map Linux SKB priorities
+ * to 802.1p priorities used in VLANs.
+ **/
+typedef enum {
+ NM_VLAN_INGRESS_MAP,
+ NM_VLAN_EGRESS_MAP
+} NMVlanPriorityMap;
+
+/**
+ * NMVlanFlags:
+ * @NM_VLAN_FLAG_REORDER_HEADERS: indicates that this interface should reorder
+ * outgoing packet headers to look more like a non-VLAN Ethernet interface
+ * @NM_VLAN_FLAG_GVRP: indicates that this interface should use GVRP to register
+ * itself with it's switch
+ * @NM_VLAN_FLAG_LOOSE_BINDING: indicates that this interface's operating
+ * state is tied to the underlying network interface but other details
+ * (like routing) are not.
+ *
+ * #NMVlanFlags values control the behavior of the VLAN interface.
+ **/
+typedef enum {
+ NM_VLAN_FLAG_REORDER_HEADERS = 0x1,
+ NM_VLAN_FLAG_GVRP = 0x2,
+ NM_VLAN_FLAG_LOOSE_BINDING = 0x4,
+
+ /* NOTE: if adding flags update nm-setting-vlan.c::verify() */
+} NMVlanFlags;
+
+GType nm_setting_vlan_get_type (void);
+NMSetting *nm_setting_vlan_new (void);
+
+const char *nm_setting_vlan_get_interface_name (NMSettingVlan *setting);
+const char *nm_setting_vlan_get_parent (NMSettingVlan *setting);
+guint32 nm_setting_vlan_get_id (NMSettingVlan *setting);
+guint32 nm_setting_vlan_get_flags (NMSettingVlan *setting);
+
+gint32 nm_setting_vlan_get_num_priorities (NMSettingVlan *setting, NMVlanPriorityMap map);
+
+gboolean nm_setting_vlan_get_priority (NMSettingVlan *setting,
+ NMVlanPriorityMap map,
+ guint32 idx,
+ guint32 *out_from,
+ guint32 *out_to);
+
+gboolean nm_setting_vlan_add_priority (NMSettingVlan *setting,
+ NMVlanPriorityMap map,
+ guint32 from,
+ guint32 to);
+
+void nm_setting_vlan_remove_priority (NMSettingVlan *setting,
+ NMVlanPriorityMap map,
+ guint32 idx);
+
+NM_AVAILABLE_IN_0_9_10
+gboolean nm_setting_vlan_remove_priority_by_value (NMSettingVlan *setting,
+ NMVlanPriorityMap map,
+ guint32 from,
+ guint32 to);
+
+NM_AVAILABLE_IN_0_9_10
+gboolean nm_setting_vlan_remove_priority_str_by_value (NMSettingVlan *setting,
+ NMVlanPriorityMap map,
+ const char *str);
+
+void nm_setting_vlan_clear_priorities (NMSettingVlan *setting, NMVlanPriorityMap map);
+
+gboolean nm_setting_vlan_add_priority_str (NMSettingVlan *setting,
+ NMVlanPriorityMap map,
+ const char *str);
+
+G_END_DECLS
+
+#endif /* NM_SETTING_VLAN_H */
diff --git a/libnm-core/nm-setting-vpn.c b/libnm-core/nm-setting-vpn.c
new file mode 100644
index 0000000000..3afc4dd93c
--- /dev/null
+++ b/libnm-core/nm-setting-vpn.c
@@ -0,0 +1,870 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2013 Red Hat, Inc.
+ * Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <dbus/dbus-glib.h>
+#include <glib/gi18n.h>
+
+#include "nm-setting-vpn.h"
+#include "nm-param-spec-specialized.h"
+#include "nm-utils.h"
+#include "nm-dbus-glib-types.h"
+#include "nm-setting-private.h"
+
+/**
+ * SECTION:nm-setting-vpn
+ * @short_description: Describes connection properties for Virtual Private Networks
+ * @include: nm-setting-vpn.h
+ *
+ * The #NMSettingVPN object is a #NMSetting subclass that describes properties
+ * necessary for connection to Virtual Private Networks. NetworkManager uses
+ * a plugin architecture to allow easier use of new VPN types, and this
+ * setting abstracts the configuration for those plugins. Since the configuration
+ * options are only known to the VPN plugins themselves, the VPN configuration
+ * options are stored as key/value pairs of strings rather than GObject
+ * properties.
+ **/
+
+/**
+ * nm_setting_vpn_error_quark:
+ *
+ * Registers an error quark for #NMSettingVPN if necessary.
+ *
+ * Returns: the error quark used for #NMSettingVPN errors.
+ **/
+GQuark
+nm_setting_vpn_error_quark (void)
+{
+ static GQuark quark;
+
+ if (G_UNLIKELY (!quark))
+ quark = g_quark_from_static_string ("nm-setting-vpn-error-quark");
+ return quark;
+}
+
+
+G_DEFINE_TYPE_WITH_CODE (NMSettingVPN, nm_setting_vpn, NM_TYPE_SETTING,
+ _nm_register_setting (NM_SETTING_VPN_SETTING_NAME,
+ g_define_type_id,
+ 1,
+ NM_SETTING_VPN_ERROR))
+NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_VPN)
+
+#define NM_SETTING_VPN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_VPN, NMSettingVPNPrivate))
+
+typedef struct {
+ char *service_type;
+
+ /* username of the user requesting this connection, thus
+ * it's really only valid for user connections, and it also
+ * should never be saved out to persistent config.
+ */
+ char *user_name;
+
+ /* The hash table is created at setting object
+ * init time and should not be replaced. It is
+ * a char * -> char * mapping, and both the key
+ * and value are owned by the hash table, and should
+ * be allocated with functions whose value can be
+ * freed with g_free(). Should not contain secrets.
+ */
+ GHashTable *data;
+
+ /* The hash table is created at setting object
+ * init time and should not be replaced. It is
+ * a char * -> char * mapping, and both the key
+ * and value are owned by the hash table, and should
+ * be allocated with functions whose value can be
+ * freed with g_free(). Should contain secrets only.
+ */
+ GHashTable *secrets;
+} NMSettingVPNPrivate;
+
+enum {
+ PROP_0,
+ PROP_SERVICE_TYPE,
+ PROP_USER_NAME,
+ PROP_DATA,
+ PROP_SECRETS,
+
+ LAST_PROP
+};
+
+/**
+ * nm_setting_vpn_new:
+ *
+ * Creates a new #NMSettingVPN object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingVPN object
+ **/
+NMSetting *
+nm_setting_vpn_new (void)
+{
+ return (NMSetting *) g_object_new (NM_TYPE_SETTING_VPN, NULL);
+}
+
+/**
+ * nm_setting_vpn_get_service_type:
+ * @setting: the #NMSettingVPN
+ *
+ * Returns the service name of the VPN, which identifies the specific VPN
+ * plugin that should be used to connect to this VPN.
+ *
+ * Returns: the VPN plugin's service name
+ **/
+const char *
+nm_setting_vpn_get_service_type (NMSettingVPN *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_VPN (setting), NULL);
+
+ return NM_SETTING_VPN_GET_PRIVATE (setting)->service_type;
+}
+
+/**
+ * nm_setting_vpn_get_user_name:
+ * @setting: the #NMSettingVPN
+ *
+ * Returns: the #NMSettingVPN:user-name property of the setting
+ **/
+const char *
+nm_setting_vpn_get_user_name (NMSettingVPN *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_VPN (setting), NULL);
+
+ return NM_SETTING_VPN_GET_PRIVATE (setting)->user_name;
+}
+
+/**
+ * nm_setting_vpn_get_num_data_items:
+ * @setting: the #NMSettingVPN
+ *
+ * Gets number of key/value pairs of VPN configuration data.
+ *
+ * Returns: the number of VPN plugin specific configuration data items
+ **/
+guint32
+nm_setting_vpn_get_num_data_items (NMSettingVPN *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_VPN (setting), 0);
+
+ return g_hash_table_size (NM_SETTING_VPN_GET_PRIVATE (setting)->data);
+}
+
+/**
+ * nm_setting_vpn_add_data_item:
+ * @setting: the #NMSettingVPN
+ * @key: a name that uniquely identifies the given value @item
+ * @item: the value to be referenced by @key
+ *
+ * Establishes a relationship between @key and @item internally in the
+ * setting which may be retrieved later. Should not be used to store passwords
+ * or other secrets, which is what nm_setting_vpn_add_secret() is for.
+ **/
+void
+nm_setting_vpn_add_data_item (NMSettingVPN *setting,
+ const char *key,
+ const char *item)
+{
+ g_return_if_fail (NM_IS_SETTING_VPN (setting));
+ g_return_if_fail (key != NULL);
+ g_return_if_fail (strlen (key) > 0);
+ g_return_if_fail (item != NULL);
+ g_return_if_fail (strlen (item) > 0);
+
+ g_hash_table_insert (NM_SETTING_VPN_GET_PRIVATE (setting)->data,
+ g_strdup (key), g_strdup (item));
+ g_object_notify (G_OBJECT (setting), NM_SETTING_VPN_DATA);
+}
+
+/**
+ * nm_setting_vpn_get_data_item:
+ * @setting: the #NMSettingVPN
+ * @key: the name of the data item to retrieve
+ *
+ * Retrieves the data item of a key/value relationship previously established
+ * by nm_setting_vpn_add_data_item().
+ *
+ * Returns: the data item, if any
+ **/
+const char *
+nm_setting_vpn_get_data_item (NMSettingVPN *setting, const char *key)
+{
+ g_return_val_if_fail (NM_IS_SETTING_VPN (setting), NULL);
+
+ return (const char *) g_hash_table_lookup (NM_SETTING_VPN_GET_PRIVATE (setting)->data, key);
+}
+
+/**
+ * nm_setting_vpn_remove_data_item:
+ * @setting: the #NMSettingVPN
+ * @key: the name of the data item to remove
+ *
+ * Deletes a key/value relationship previously established by
+ * nm_setting_vpn_add_data_item().
+ *
+ * Returns: %TRUE if the data item was found and removed from the internal list,
+ * %FALSE if it was not.
+ **/
+gboolean
+nm_setting_vpn_remove_data_item (NMSettingVPN *setting, const char *key)
+{
+ gboolean found;
+
+ g_return_val_if_fail (NM_IS_SETTING_VPN (setting), FALSE);
+
+ found = g_hash_table_remove (NM_SETTING_VPN_GET_PRIVATE (setting)->data, key);
+ if (found)
+ g_object_notify (G_OBJECT (setting), NM_SETTING_VPN_DATA);
+ return found;
+}
+
+static void
+foreach_item_helper (GHashTable *hash,
+ NMVPNIterFunc func,
+ gpointer user_data)
+{
+ GList *keys, *liter;
+ GSList *copied = NULL, *siter;
+
+ g_return_if_fail (hash != NULL);
+
+ /* Grab keys and copy them so that the callback func can modify
+ * the hash table items if it wants to.
+ */
+ keys = g_hash_table_get_keys (hash);
+ for (liter = keys; liter; liter = g_list_next (liter))
+ copied = g_slist_prepend (copied, g_strdup (liter->data));
+ copied = g_slist_reverse (copied);
+ g_list_free (keys);
+
+ for (siter = copied; siter; siter = g_slist_next (siter)) {
+ gpointer value;
+
+ value = g_hash_table_lookup (hash, siter->data);
+ func (siter->data, value, user_data);
+ }
+
+ g_slist_free_full (copied, g_free);
+}
+
+/**
+ * nm_setting_vpn_foreach_data_item:
+ * @setting: a #NMSettingVPN
+ * @func: (scope call): an user provided function
+ * @user_data: data to be passed to @func
+ *
+ * Iterates all data items stored in this setting. It is safe to add, remove,
+ * and modify data items inside @func, though any additions or removals made
+ * during iteration will not be part of the iteration.
+ */
+void
+nm_setting_vpn_foreach_data_item (NMSettingVPN *setting,
+ NMVPNIterFunc func,
+ gpointer user_data)
+{
+ g_return_if_fail (NM_IS_SETTING_VPN (setting));
+
+ foreach_item_helper (NM_SETTING_VPN_GET_PRIVATE (setting)->data, func, user_data);
+}
+
+/**
+ * nm_setting_vpn_get_num_secrets:
+ * @setting: the #NMSettingVPN
+ *
+ * Gets number of VPN plugin specific secrets in the setting.
+ *
+ * Returns: the number of VPN plugin specific secrets
+ **/
+guint32
+nm_setting_vpn_get_num_secrets (NMSettingVPN *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_VPN (setting), 0);
+
+ return g_hash_table_size (NM_SETTING_VPN_GET_PRIVATE (setting)->secrets);
+}
+
+/**
+ * nm_setting_vpn_add_secret:
+ * @setting: the #NMSettingVPN
+ * @key: a name that uniquely identifies the given secret @secret
+ * @secret: the secret to be referenced by @key
+ *
+ * Establishes a relationship between @key and @secret internally in the
+ * setting which may be retrieved later.
+ **/
+void
+nm_setting_vpn_add_secret (NMSettingVPN *setting,
+ const char *key,
+ const char *secret)
+{
+ g_return_if_fail (NM_IS_SETTING_VPN (setting));
+ g_return_if_fail (key != NULL);
+ g_return_if_fail (strlen (key) > 0);
+ g_return_if_fail (secret != NULL);
+ g_return_if_fail (strlen (secret) > 0);
+
+ g_hash_table_insert (NM_SETTING_VPN_GET_PRIVATE (setting)->secrets,
+ g_strdup (key), g_strdup (secret));
+ g_object_notify (G_OBJECT (setting), NM_SETTING_VPN_SECRETS);
+}
+
+/**
+ * nm_setting_vpn_get_secret:
+ * @setting: the #NMSettingVPN
+ * @key: the name of the secret to retrieve
+ *
+ * Retrieves the secret of a key/value relationship previously established
+ * by nm_setting_vpn_add_secret().
+ *
+ * Returns: the secret, if any
+ **/
+const char *
+nm_setting_vpn_get_secret (NMSettingVPN *setting, const char *key)
+{
+ g_return_val_if_fail (NM_IS_SETTING_VPN (setting), NULL);
+
+ return (const char *) g_hash_table_lookup (NM_SETTING_VPN_GET_PRIVATE (setting)->secrets, key);
+}
+
+/**
+ * nm_setting_vpn_remove_secret:
+ * @setting: the #NMSettingVPN
+ * @key: the name of the secret to remove
+ *
+ * Deletes a key/value relationship previously established by
+ * nm_setting_vpn_add_secret().
+ *
+ * Returns: %TRUE if the secret was found and removed from the internal list,
+ * %FALSE if it was not.
+ **/
+gboolean
+nm_setting_vpn_remove_secret (NMSettingVPN *setting, const char *key)
+{
+ gboolean found;
+
+ g_return_val_if_fail (NM_IS_SETTING_VPN (setting), FALSE);
+
+ found = g_hash_table_remove (NM_SETTING_VPN_GET_PRIVATE (setting)->secrets, key);
+ if (found)
+ g_object_notify (G_OBJECT (setting), NM_SETTING_VPN_SECRETS);
+ return found;
+}
+
+/**
+ * nm_setting_vpn_foreach_secret:
+ * @setting: a #NMSettingVPN
+ * @func: (scope call): an user provided function
+ * @user_data: data to be passed to @func
+ *
+ * Iterates all secrets stored in this setting. It is safe to add, remove,
+ * and modify secrets inside @func, though any additions or removals made during
+ * iteration will not be part of the iteration.
+ */
+void
+nm_setting_vpn_foreach_secret (NMSettingVPN *setting,
+ NMVPNIterFunc func,
+ gpointer user_data)
+{
+ g_return_if_fail (NM_IS_SETTING_VPN (setting));
+
+ foreach_item_helper (NM_SETTING_VPN_GET_PRIVATE (setting)->secrets, func, user_data);
+}
+
+static gboolean
+verify (NMSetting *setting, GSList *all_settings, GError **error)
+{
+ NMSettingVPNPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (setting);
+
+ if (!priv->service_type) {
+ g_set_error_literal (error,
+ NM_SETTING_VPN_ERROR,
+ NM_SETTING_VPN_ERROR_MISSING_PROPERTY,
+ _("property is missing"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_VPN_SETTING_NAME, NM_SETTING_VPN_SERVICE_TYPE);
+ return FALSE;
+ }
+
+ if (!strlen (priv->service_type)) {
+ g_set_error_literal (error,
+ NM_SETTING_VPN_ERROR,
+ NM_SETTING_VPN_ERROR_INVALID_PROPERTY,
+ _("property is empty"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_VPN_SETTING_NAME, NM_SETTING_VPN_SERVICE_TYPE);
+ return FALSE;
+ }
+
+ /* default username can be NULL, but can't be zero-length */
+ if (priv->user_name && !strlen (priv->user_name)) {
+ g_set_error_literal (error,
+ NM_SETTING_VPN_ERROR,
+ NM_SETTING_VPN_ERROR_INVALID_PROPERTY,
+ _("property is empty"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_VPN_SETTING_NAME, NM_SETTING_VPN_USER_NAME);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static NMSettingUpdateSecretResult
+update_secret_string (NMSetting *setting,
+ const char *key,
+ const char *value,
+ GError **error)
+{
+ NMSettingVPNPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (setting);
+
+ g_return_val_if_fail (key != NULL, NM_SETTING_UPDATE_SECRET_ERROR);
+ g_return_val_if_fail (value != NULL, NM_SETTING_UPDATE_SECRET_ERROR);
+
+ if (!value || !strlen (value)) {
+ g_set_error (error, NM_SETTING_ERROR,
+ NM_SETTING_ERROR_PROPERTY_TYPE_MISMATCH,
+ "Secret %s was empty", key);
+ return NM_SETTING_UPDATE_SECRET_ERROR;
+ }
+
+ if (g_strcmp0 (g_hash_table_lookup (priv->secrets, key), value) == 0)
+ return NM_SETTING_UPDATE_SECRET_SUCCESS_UNCHANGED;
+
+ g_hash_table_insert (priv->secrets, g_strdup (key), g_strdup (value));
+ return NM_SETTING_UPDATE_SECRET_SUCCESS_MODIFIED;
+}
+
+static NMSettingUpdateSecretResult
+update_secret_hash (NMSetting *setting,
+ GHashTable *secrets,
+ GError **error)
+{
+ NMSettingVPNPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (setting);
+ GHashTableIter iter;
+ const char *name, *value;
+ NMSettingUpdateSecretResult result = NM_SETTING_UPDATE_SECRET_SUCCESS_UNCHANGED;
+
+ g_return_val_if_fail (secrets != NULL, NM_SETTING_UPDATE_SECRET_ERROR);
+
+ /* Make sure the items are valid */
+ g_hash_table_iter_init (&iter, secrets);
+ while (g_hash_table_iter_next (&iter, (gpointer *) &name, (gpointer *) &value)) {
+ if (!name || !strlen (name)) {
+ g_set_error_literal (error, NM_SETTING_ERROR,
+ NM_SETTING_ERROR_PROPERTY_TYPE_MISMATCH,
+ "Secret name was empty");
+ return NM_SETTING_UPDATE_SECRET_ERROR;
+ }
+
+ if (!value || !strlen (value)) {
+ g_set_error (error, NM_SETTING_ERROR,
+ NM_SETTING_ERROR_PROPERTY_TYPE_MISMATCH,
+ "Secret %s value was empty", name);
+ return NM_SETTING_UPDATE_SECRET_ERROR;
+ }
+ }
+
+ /* Now add the items to the settings' secrets list */
+ g_hash_table_iter_init (&iter, secrets);
+ while (g_hash_table_iter_next (&iter, (gpointer *) &name, (gpointer *) &value)) {
+ if (value == NULL) {
+ g_warn_if_fail (value != NULL);
+ continue;
+ }
+ if (strlen (value) == 0) {
+ g_warn_if_fail (strlen (value) > 0);
+ continue;
+ }
+
+ if (g_strcmp0 (g_hash_table_lookup (priv->secrets, name), value) == 0)
+ continue;
+
+ g_hash_table_insert (priv->secrets, g_strdup (name), g_strdup (value));
+ result = NM_SETTING_UPDATE_SECRET_SUCCESS_MODIFIED;
+ }
+
+ return result;
+}
+
+static int
+update_one_secret (NMSetting *setting, const char *key, GValue *value, GError **error)
+{
+ NMSettingUpdateSecretResult success = NM_SETTING_UPDATE_SECRET_ERROR;
+
+ g_return_val_if_fail (key != NULL, NM_SETTING_UPDATE_SECRET_ERROR);
+ g_return_val_if_fail (value != NULL, NM_SETTING_UPDATE_SECRET_ERROR);
+
+ if (G_VALUE_HOLDS_STRING (value)) {
+ /* Passing the string properties individually isn't correct, and won't
+ * produce the correct result, but for some reason that's how it used
+ * to be done. So even though it's not correct, keep the code around
+ * for compatibility's sake.
+ */
+ success = update_secret_string (setting, key, g_value_get_string (value), error);
+ } else if (G_VALUE_HOLDS (value, DBUS_TYPE_G_MAP_OF_STRING)) {
+ if (strcmp (key, NM_SETTING_VPN_SECRETS) != 0) {
+ g_set_error (error, NM_SETTING_ERROR, NM_SETTING_ERROR_PROPERTY_NOT_SECRET,
+ "Property %s not a secret property", key);
+ } else
+ success = update_secret_hash (setting, g_value_get_boxed (value), error);
+ } else
+ g_set_error_literal (error, NM_SETTING_ERROR, NM_SETTING_ERROR_PROPERTY_TYPE_MISMATCH, key);
+
+ if (success == NM_SETTING_UPDATE_SECRET_SUCCESS_MODIFIED)
+ g_object_notify (G_OBJECT (setting), NM_SETTING_VPN_SECRETS);
+
+ return success;
+}
+
+static gboolean
+get_secret_flags (NMSetting *setting,
+ const char *secret_name,
+ gboolean verify_secret,
+ NMSettingSecretFlags *out_flags,
+ GError **error)
+{
+ NMSettingVPNPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (setting);
+ gboolean success = FALSE;
+ char *flags_key;
+ gpointer val;
+ unsigned long tmp;
+
+ flags_key = g_strdup_printf ("%s-flags", secret_name);
+ if (g_hash_table_lookup_extended (priv->data, flags_key, NULL, &val)) {
+ errno = 0;
+ tmp = strtoul ((const char *) val, NULL, 10);
+ if ((errno == 0) && (tmp <= NM_SETTING_SECRET_FLAGS_ALL)) {
+ if (out_flags)
+ *out_flags = (guint32) tmp;
+ success = TRUE;
+ } else {
+ g_set_error (error,
+ NM_SETTING_ERROR,
+ NM_SETTING_ERROR_PROPERTY_TYPE_MISMATCH,
+ "Failed to convert '%s' value '%s' to uint",
+ flags_key, (const char *) val);
+ }
+ } else {
+ g_set_error (error,
+ NM_SETTING_ERROR,
+ NM_SETTING_ERROR_PROPERTY_NOT_FOUND,
+ "Secret flags property '%s' not found", flags_key);
+ }
+ g_free (flags_key);
+ return success;
+}
+
+static gboolean
+set_secret_flags (NMSetting *setting,
+ const char *secret_name,
+ gboolean verify_secret,
+ NMSettingSecretFlags flags,
+ GError **error)
+{
+ g_hash_table_insert (NM_SETTING_VPN_GET_PRIVATE (setting)->data,
+ g_strdup_printf ("%s-flags", secret_name),
+ g_strdup_printf ("%u", flags));
+ g_object_notify (G_OBJECT (setting), NM_SETTING_VPN_SECRETS);
+ return TRUE;
+}
+
+static GPtrArray *
+need_secrets (NMSetting *setting)
+{
+ /* Assume that VPN connections need secrets since they almost always will */
+ return g_ptr_array_sized_new (1);
+}
+
+static gboolean
+compare_one_secret (NMSettingVPN *a,
+ NMSettingVPN *b,
+ NMSettingCompareFlags flags)
+{
+ GHashTable *a_secrets, *b_secrets;
+ GHashTableIter iter;
+ const char *key, *val;
+
+ a_secrets = NM_SETTING_VPN_GET_PRIVATE (a)->secrets;
+ b_secrets = NM_SETTING_VPN_GET_PRIVATE (b)->secrets;
+
+ g_hash_table_iter_init (&iter, a_secrets);
+ while (g_hash_table_iter_next (&iter, (gpointer) &key, (gpointer) &val)) {
+ NMSettingSecretFlags a_secret_flags = NM_SETTING_SECRET_FLAG_NONE;
+ NMSettingSecretFlags b_secret_flags = NM_SETTING_SECRET_FLAG_NONE;
+
+ nm_setting_get_secret_flags (NM_SETTING (a), key, &a_secret_flags, NULL);
+ nm_setting_get_secret_flags (NM_SETTING (b), key, &b_secret_flags, NULL);
+
+ /* If the secret flags aren't the same, the settings aren't the same */
+ if (a_secret_flags != b_secret_flags)
+ return FALSE;
+
+ if ( (flags & NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS)
+ && (a_secret_flags & NM_SETTING_SECRET_FLAG_AGENT_OWNED))
+ continue;
+
+ if ( (flags & NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS)
+ && (a_secret_flags & NM_SETTING_SECRET_FLAG_NOT_SAVED))
+ continue;
+
+ /* Now compare the values themselves */
+ if (g_strcmp0 (val, nm_setting_vpn_get_secret (b, key)) != 0)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+compare_property (NMSetting *setting,
+ NMSetting *other,
+ const GParamSpec *prop_spec,
+ NMSettingCompareFlags flags)
+{
+ gboolean same;
+
+ /* We only need to treat the 'secrets' property specially */
+ if (g_strcmp0 (prop_spec->name, NM_SETTING_VPN_SECRETS) != 0)
+ return NM_SETTING_CLASS (nm_setting_vpn_parent_class)->compare_property (setting, other, prop_spec, flags);
+
+ /* Compare A to B to ensure everything in A is found in B */
+ same = compare_one_secret (NM_SETTING_VPN (setting), NM_SETTING_VPN (other), flags);
+ if (same) {
+ /* And then B to A to ensure everything in B is also found in A */
+ same = compare_one_secret (NM_SETTING_VPN (other), NM_SETTING_VPN (setting), flags);
+ }
+
+ return same;
+}
+
+static gboolean
+clear_secrets_with_flags (NMSetting *setting,
+ GParamSpec *pspec,
+ NMSettingClearSecretsWithFlagsFn func,
+ gpointer user_data)
+{
+ NMSettingVPNPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (setting);
+ GHashTableIter iter;
+ const char *secret;
+ gboolean changed = TRUE;
+
+ if (priv->secrets == NULL)
+ return FALSE;
+
+ /* Iterate through secrets hash and check each entry */
+ g_hash_table_iter_init (&iter, priv->secrets);
+ while (g_hash_table_iter_next (&iter, (gpointer) &secret, NULL)) {
+ NMSettingSecretFlags flags = NM_SETTING_SECRET_FLAG_NONE;
+
+ nm_setting_get_secret_flags (setting, secret, &flags, NULL);
+ if (func (setting, pspec->name, flags, user_data) == TRUE) {
+ g_hash_table_iter_remove (&iter);
+ changed = TRUE;
+ }
+ }
+
+ if (changed)
+ g_object_notify (G_OBJECT (setting), NM_SETTING_VPN_SECRETS);
+
+ return changed;
+}
+
+static void
+destroy_one_secret (gpointer data)
+{
+ char *secret = (char *) data;
+
+ /* Don't leave the secret lying around in memory */
+ memset (secret, 0, strlen (secret));
+ g_free (secret);
+}
+
+static void
+nm_setting_vpn_init (NMSettingVPN *setting)
+{
+ NMSettingVPNPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (setting);
+
+ priv->data = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+ priv->secrets = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, destroy_one_secret);
+}
+
+static void
+finalize (GObject *object)
+{
+ NMSettingVPNPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (object);
+
+ g_free (priv->service_type);
+ g_free (priv->user_name);
+ g_hash_table_destroy (priv->data);
+ g_hash_table_destroy (priv->secrets);
+
+ G_OBJECT_CLASS (nm_setting_vpn_parent_class)->finalize (object);
+}
+
+static void
+copy_hash (gpointer key, gpointer value, gpointer user_data)
+{
+ g_return_if_fail (value != NULL);
+ g_return_if_fail (strlen (value));
+ g_hash_table_insert ((GHashTable *) user_data, g_strdup (key), g_strdup (value));
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ NMSettingVPNPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (object);
+ GHashTable *new_hash;
+
+ switch (prop_id) {
+ case PROP_SERVICE_TYPE:
+ g_free (priv->service_type);
+ priv->service_type = g_value_dup_string (value);
+ break;
+ case PROP_USER_NAME:
+ g_free (priv->user_name);
+ priv->user_name = g_value_dup_string (value);
+ break;
+ case PROP_DATA:
+ /* Must make a deep copy of the hash table here... */
+ g_hash_table_remove_all (priv->data);
+ new_hash = g_value_get_boxed (value);
+ if (new_hash)
+ g_hash_table_foreach (new_hash, copy_hash, priv->data);
+ break;
+ case PROP_SECRETS:
+ /* Must make a deep copy of the hash table here... */
+ g_hash_table_remove_all (priv->secrets);
+ new_hash = g_value_get_boxed (value);
+ if (new_hash)
+ g_hash_table_foreach (new_hash, copy_hash, priv->secrets);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ NMSettingVPN *setting = NM_SETTING_VPN (object);
+ NMSettingVPNPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (setting);
+
+ switch (prop_id) {
+ case PROP_SERVICE_TYPE:
+ g_value_set_string (value, nm_setting_vpn_get_service_type (setting));
+ break;
+ case PROP_USER_NAME:
+ g_value_set_string (value, nm_setting_vpn_get_user_name (setting));
+ break;
+ case PROP_DATA:
+ g_value_set_boxed (value, priv->data);
+ break;
+ case PROP_SECRETS:
+ g_value_set_boxed (value, priv->secrets);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nm_setting_vpn_class_init (NMSettingVPNClass *setting_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
+ NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+
+ g_type_class_add_private (setting_class, sizeof (NMSettingVPNPrivate));
+
+ /* virtual methods */
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+ object_class->finalize = finalize;
+
+ parent_class->verify = verify;
+ parent_class->update_one_secret = update_one_secret;
+ parent_class->get_secret_flags = get_secret_flags;
+ parent_class->set_secret_flags = set_secret_flags;
+ parent_class->need_secrets = need_secrets;
+ parent_class->compare_property = compare_property;
+ parent_class->clear_secrets_with_flags = clear_secrets_with_flags;
+
+ /* Properties */
+ /**
+ * NMSettingVPN:service-type:
+ *
+ * D-Bus service name of the VPN plugin that this setting uses to connect to
+ * its network. i.e. org.freedesktop.NetworkManager.vpnc for the vpnc
+ * plugin.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_SERVICE_TYPE,
+ g_param_spec_string (NM_SETTING_VPN_SERVICE_TYPE, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingVPN:user-name:
+ *
+ * If the VPN connection requires a user name for authentication, that name
+ * should be provided here. If the connection is available to more than one
+ * user, and the VPN requires each user to supply a different name, then
+ * leave this property empty. If this property is empty, NetworkManager
+ * will automatically supply the username of the user which requested the
+ * VPN connection.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_USER_NAME,
+ g_param_spec_string (NM_SETTING_VPN_USER_NAME, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingVPN:data:
+ *
+ * Dictionary of key/value pairs of VPN plugin specific data. Both keys and
+ * values must be strings.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_DATA,
+ _nm_param_spec_specialized (NM_SETTING_VPN_DATA, "", "",
+ DBUS_TYPE_G_MAP_OF_STRING,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingVPN:secrets:
+ *
+ * Dictionary of key/value pairs of VPN plugin specific secrets like
+ * passwords or private keys. Both keys and values must be strings.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_SECRETS,
+ _nm_param_spec_specialized (NM_SETTING_VPN_SECRETS, "", "",
+ DBUS_TYPE_G_MAP_OF_STRING,
+ G_PARAM_READWRITE |
+ NM_SETTING_PARAM_SECRET |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/libnm-core/nm-setting-vpn.h b/libnm-core/nm-setting-vpn.h
new file mode 100644
index 0000000000..3eb51ee9d6
--- /dev/null
+++ b/libnm-core/nm-setting-vpn.h
@@ -0,0 +1,115 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2013 Red Hat, Inc.
+ * Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#ifndef NM_SETTING_VPN_H
+#define NM_SETTING_VPN_H
+
+#include <nm-setting.h>
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_SETTING_VPN (nm_setting_vpn_get_type ())
+#define NM_SETTING_VPN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_VPN, NMSettingVPN))
+#define NM_SETTING_VPN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_VPN, NMSettingVPNClass))
+#define NM_IS_SETTING_VPN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_VPN))
+#define NM_IS_SETTING_VPN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_VPN))
+#define NM_SETTING_VPN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_VPN, NMSettingVPNClass))
+
+#define NM_SETTING_VPN_SETTING_NAME "vpn"
+
+/**
+ * NMSettingVpnError:
+ * @NM_SETTING_VPN_ERROR_UNKNOWN: unknown or unclassified error
+ * @NM_SETTING_VPN_ERROR_INVALID_PROPERTY: the property was invalid
+ * @NM_SETTING_VPN_ERROR_MISSING_PROPERTY: the property was missing and is
+ * required
+ */
+typedef enum {
+ NM_SETTING_VPN_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/
+ NM_SETTING_VPN_ERROR_INVALID_PROPERTY, /*< nick=InvalidProperty >*/
+ NM_SETTING_VPN_ERROR_MISSING_PROPERTY, /*< nick=MissingProperty >*/
+} NMSettingVpnError;
+
+#define NM_SETTING_VPN_ERROR nm_setting_vpn_error_quark ()
+GQuark nm_setting_vpn_error_quark (void);
+
+#define NM_SETTING_VPN_SERVICE_TYPE "service-type"
+#define NM_SETTING_VPN_USER_NAME "user-name"
+#define NM_SETTING_VPN_DATA "data"
+#define NM_SETTING_VPN_SECRETS "secrets"
+
+typedef struct {
+ NMSetting parent;
+} NMSettingVPN;
+
+typedef struct {
+ NMSettingClass parent;
+
+ /* Padding for future expansion */
+ void (*_reserved1) (void);
+ void (*_reserved2) (void);
+ void (*_reserved3) (void);
+ void (*_reserved4) (void);
+} NMSettingVPNClass;
+
+/**
+ * NMVPNIterFunc:
+ * @key: the name of the data or secret item
+ * @value: the value of the data or secret item
+ * @user_data: User data passed to nm_setting_vpn_foreach_data_item() or
+ * nm_setting_vpn_foreach_secret()
+ **/
+typedef void (*NMVPNIterFunc) (const char *key, const char *value, gpointer user_data);
+
+GType nm_setting_vpn_get_type (void);
+
+NMSetting *nm_setting_vpn_new (void);
+const char *nm_setting_vpn_get_service_type (NMSettingVPN *setting);
+const char *nm_setting_vpn_get_user_name (NMSettingVPN *setting);
+
+guint32 nm_setting_vpn_get_num_data_items (NMSettingVPN *setting);
+void nm_setting_vpn_add_data_item (NMSettingVPN *setting,
+ const char *key,
+ const char *item);
+const char * nm_setting_vpn_get_data_item (NMSettingVPN *setting,
+ const char *key);
+gboolean nm_setting_vpn_remove_data_item (NMSettingVPN *setting,
+ const char *key);
+void nm_setting_vpn_foreach_data_item (NMSettingVPN *setting,
+ NMVPNIterFunc func,
+ gpointer user_data);
+
+guint32 nm_setting_vpn_get_num_secrets (NMSettingVPN *setting);
+void nm_setting_vpn_add_secret (NMSettingVPN *setting,
+ const char *key,
+ const char *secret);
+const char * nm_setting_vpn_get_secret (NMSettingVPN *setting,
+ const char *key);
+gboolean nm_setting_vpn_remove_secret (NMSettingVPN *setting,
+ const char *key);
+void nm_setting_vpn_foreach_secret (NMSettingVPN *setting,
+ NMVPNIterFunc func,
+ gpointer user_data);
+
+G_END_DECLS
+
+#endif /* NM_SETTING_VPN_H */
diff --git a/libnm-core/nm-setting-wimax.c b/libnm-core/nm-setting-wimax.c
new file mode 100644
index 0000000000..1a4a6ec384
--- /dev/null
+++ b/libnm-core/nm-setting-wimax.c
@@ -0,0 +1,262 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2011 - 2013 Red Hat, Inc.
+ * Copyright 2009 Novell, Inc.
+ */
+
+#include <string.h>
+#include <net/ethernet.h>
+#include <dbus/dbus-glib.h>
+#include <glib/gi18n.h>
+
+#include "nm-setting-wimax.h"
+#include "nm-param-spec-specialized.h"
+#include "nm-setting-private.h"
+
+/**
+ * SECTION:nm-setting-wimax
+ * @short_description: Describes 802.16e Mobile WiMAX connection properties
+ * @include: nm-setting-wimax.h
+ *
+ * The #NMSettingWimax object is a #NMSetting subclass that describes properties
+ * necessary for connection to 802.16e Mobile WiMAX networks.
+ **/
+
+/**
+ * nm_setting_wimax_error_quark:
+ *
+ * Registers an error quark for #NMSettingWimax if necessary.
+ *
+ * Returns: the error quark used for #NMSettingWimax errors.
+ **/
+GQuark
+nm_setting_wimax_error_quark (void)
+{
+ static GQuark quark;
+
+ if (G_UNLIKELY (!quark))
+ quark = g_quark_from_static_string ("nm-setting-wimax-error-quark");
+ return quark;
+}
+
+
+G_DEFINE_TYPE_WITH_CODE (NMSettingWimax, nm_setting_wimax, NM_TYPE_SETTING,
+ _nm_register_setting (NM_SETTING_WIMAX_SETTING_NAME,
+ g_define_type_id,
+ 1,
+ NM_SETTING_WIMAX_ERROR))
+NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_WIMAX)
+
+#define NM_SETTING_WIMAX_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_WIMAX, NMSettingWimaxPrivate))
+
+typedef struct {
+ char *network_name;
+ GByteArray *mac_address;
+} NMSettingWimaxPrivate;
+
+enum {
+ PROP_0,
+ PROP_NETWORK_NAME,
+ PROP_MAC_ADDRESS,
+
+ LAST_PROP
+};
+
+/**
+ * nm_setting_wimax_new:
+ *
+ * Creates a new #NMSettingWimax object with default values.
+ *
+ * Returns: the new empty #NMSettingWimax object
+ **/
+NMSetting *
+nm_setting_wimax_new (void)
+{
+ return (NMSetting *) g_object_new (NM_TYPE_SETTING_WIMAX, NULL);
+}
+
+/**
+ * nm_setting_wimax_get_network_name:
+ * @setting: the #NMSettingWimax
+ *
+ * Returns the WiMAX NSP name (ex "Sprint" or "CLEAR") which identifies the
+ * specific WiMAX network this setting describes a connection to.
+ *
+ * Returns: the WiMAX NSP name
+ **/
+const char *
+nm_setting_wimax_get_network_name (NMSettingWimax *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_WIMAX (setting), NULL);
+
+ return NM_SETTING_WIMAX_GET_PRIVATE (setting)->network_name;
+}
+
+/**
+ * nm_setting_wimax_get_mac_address:
+ * @setting: the #NMSettingWimax
+ *
+ * Returns the MAC address of a WiMAX device which this connection is locked
+ * to.
+ *
+ * Returns: the MAC address
+ **/
+const GByteArray *
+nm_setting_wimax_get_mac_address (NMSettingWimax *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_WIMAX (setting), NULL);
+
+ return NM_SETTING_WIMAX_GET_PRIVATE (setting)->mac_address;
+}
+
+static gboolean
+verify (NMSetting *setting, GSList *all_settings, GError **error)
+{
+ NMSettingWimaxPrivate *priv = NM_SETTING_WIMAX_GET_PRIVATE (setting);
+
+ if (!priv->network_name) {
+ g_set_error_literal (error,
+ NM_SETTING_WIMAX_ERROR,
+ NM_SETTING_WIMAX_ERROR_MISSING_PROPERTY,
+ _("property is missing"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_WIMAX_SETTING_NAME, NM_SETTING_WIMAX_NETWORK_NAME);
+ return FALSE;
+ }
+
+ if (!strlen (priv->network_name)) {
+ g_set_error_literal (error,
+ NM_SETTING_WIMAX_ERROR,
+ NM_SETTING_WIMAX_ERROR_INVALID_PROPERTY,
+ _("property is empty"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_WIMAX_SETTING_NAME, NM_SETTING_WIMAX_NETWORK_NAME);
+ return FALSE;
+ }
+
+ if (priv->mac_address && priv->mac_address->len != ETH_ALEN) {
+ g_set_error_literal (error,
+ NM_SETTING_WIMAX_ERROR,
+ NM_SETTING_WIMAX_ERROR_INVALID_PROPERTY,
+ _("property is invalid"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_WIMAX_SETTING_NAME, NM_SETTING_WIMAX_MAC_ADDRESS);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+nm_setting_wimax_init (NMSettingWimax *setting)
+{
+}
+
+static void
+finalize (GObject *object)
+{
+ NMSettingWimaxPrivate *priv = NM_SETTING_WIMAX_GET_PRIVATE (object);
+
+ g_free (priv->network_name);
+ if (priv->mac_address)
+ g_byte_array_free (priv->mac_address, TRUE);
+
+ G_OBJECT_CLASS (nm_setting_wimax_parent_class)->finalize (object);
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ NMSettingWimaxPrivate *priv = NM_SETTING_WIMAX_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_NETWORK_NAME:
+ g_free (priv->network_name);
+ priv->network_name = g_value_dup_string (value);
+ break;
+ case PROP_MAC_ADDRESS:
+ if (priv->mac_address)
+ g_byte_array_free (priv->mac_address, TRUE);
+ priv->mac_address = g_value_dup_boxed (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ NMSettingWimax *setting = NM_SETTING_WIMAX (object);
+
+ switch (prop_id) {
+ case PROP_NETWORK_NAME:
+ g_value_set_string (value, nm_setting_wimax_get_network_name (setting));
+ break;
+ case PROP_MAC_ADDRESS:
+ g_value_set_boxed (value, nm_setting_wimax_get_mac_address (setting));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nm_setting_wimax_class_init (NMSettingWimaxClass *setting_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
+ NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+
+ g_type_class_add_private (setting_class, sizeof (NMSettingWimaxPrivate));
+
+ /* virtual methods */
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+ object_class->finalize = finalize;
+ parent_class->verify = verify;
+
+ /* Properties */
+ /**
+ * NMSettingWimax:network-name:
+ *
+ * Network Service Provider (NSP) name of the WiMAX network this connection
+ * should use.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_NETWORK_NAME,
+ g_param_spec_string (NM_SETTING_WIMAX_NETWORK_NAME, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingWimax:mac-address:
+ *
+ * If specified, this connection will only apply to the WiMAX device whose
+ * MAC address matches. This property does not change the MAC address of the
+ * device (known as MAC spoofing).
+ **/
+ g_object_class_install_property
+ (object_class, PROP_MAC_ADDRESS,
+ _nm_param_spec_specialized (NM_SETTING_WIMAX_MAC_ADDRESS, "", "",
+ DBUS_TYPE_G_UCHAR_ARRAY,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/libnm-core/nm-setting-wimax.h b/libnm-core/nm-setting-wimax.h
new file mode 100644
index 0000000000..781d717c2d
--- /dev/null
+++ b/libnm-core/nm-setting-wimax.h
@@ -0,0 +1,73 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2009 Novell, Inc.
+ */
+
+#ifndef NM_SETTING_WIMAX_H
+#define NM_SETTING_WIMAX_H
+
+#include <nm-setting.h>
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_SETTING_WIMAX (nm_setting_wimax_get_type ())
+#define NM_SETTING_WIMAX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_WIMAX, NMSettingWimax))
+#define NM_SETTING_WIMAX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_WIMAX, NMSettingWimaxClass))
+#define NM_IS_SETTING_WIMAX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_WIMAX))
+#define NM_IS_SETTING_WIMAX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_WIMAX))
+#define NM_SETTING_WIMAX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_WIMAX, NMSettingWimaxClass))
+
+#define NM_SETTING_WIMAX_SETTING_NAME "wimax"
+
+/**
+ * NMSettingWimaxError:
+ * @NM_SETTING_WIMAX_ERROR_UNKNOWN: unknown or unclassified error
+ * @NM_SETTING_WIMAX_ERROR_INVALID_PROPERTY: the property was invalid
+ * @NM_SETTING_WIMAX_ERROR_MISSING_PROPERTY: the property was missing and is
+ * required
+ */
+typedef enum {
+ NM_SETTING_WIMAX_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/
+ NM_SETTING_WIMAX_ERROR_INVALID_PROPERTY, /*< nick=InvalidProperty >*/
+ NM_SETTING_WIMAX_ERROR_MISSING_PROPERTY /*< nick=MissingProperty >*/
+} NMSettingWimaxError;
+
+#define NM_SETTING_WIMAX_ERROR nm_setting_wimax_error_quark ()
+GQuark nm_setting_wimax_error_quark (void);
+
+#define NM_SETTING_WIMAX_NETWORK_NAME "network-name"
+#define NM_SETTING_WIMAX_MAC_ADDRESS "mac-address"
+
+typedef struct {
+ NMSetting parent;
+} NMSettingWimax;
+
+typedef struct {
+ NMSettingClass parent;
+} NMSettingWimaxClass;
+
+GType nm_setting_wimax_get_type (void);
+
+NMSetting *nm_setting_wimax_new (void);
+const char *nm_setting_wimax_get_network_name (NMSettingWimax *setting);
+const GByteArray *nm_setting_wimax_get_mac_address (NMSettingWimax *setting);
+
+G_END_DECLS
+
+#endif /* NM_SETTING_WIMAX_H */
diff --git a/libnm-core/nm-setting-wired.c b/libnm-core/nm-setting-wired.c
new file mode 100644
index 0000000000..bc82bf3477
--- /dev/null
+++ b/libnm-core/nm-setting-wired.c
@@ -0,0 +1,1032 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2014 Red Hat, Inc.
+ * Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#include <string.h>
+#include <net/ethernet.h>
+#include <netinet/ether.h>
+#include <dbus/dbus-glib.h>
+#include <glib/gi18n.h>
+
+#include "nm-setting-wired.h"
+#include "nm-param-spec-specialized.h"
+#include "nm-utils.h"
+#include "nm-utils-private.h"
+#include "nm-dbus-glib-types.h"
+#include "nm-setting-private.h"
+
+/**
+ * SECTION:nm-setting-wired
+ * @short_description: Describes connection properties for Ethernet-based networks
+ * @include: nm-setting-wired.h
+ *
+ * The #NMSettingWired object is a #NMSetting subclass that describes properties
+ * necessary for connection to Ethernet networks.
+ **/
+
+/**
+ * nm_setting_wired_error_quark:
+ *
+ * Registers an error quark for #NMSettingWired if necessary.
+ *
+ * Returns: the error quark used for #NMSettingWired errors.
+ **/
+GQuark
+nm_setting_wired_error_quark (void)
+{
+ static GQuark quark;
+
+ if (G_UNLIKELY (!quark))
+ quark = g_quark_from_static_string ("nm-setting-wired-error-quark");
+ return quark;
+}
+
+
+G_DEFINE_TYPE_WITH_CODE (NMSettingWired, nm_setting_wired, NM_TYPE_SETTING,
+ _nm_register_setting (NM_SETTING_WIRED_SETTING_NAME,
+ g_define_type_id,
+ 1,
+ NM_SETTING_WIRED_ERROR))
+NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_WIRED)
+
+#define NM_SETTING_WIRED_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_WIRED, NMSettingWiredPrivate))
+
+typedef struct {
+ char *port;
+ guint32 speed;
+ char *duplex;
+ gboolean auto_negotiate;
+ GByteArray *device_mac_address;
+ GByteArray *cloned_mac_address;
+ GSList *mac_address_blacklist;
+ guint32 mtu;
+ GPtrArray *s390_subchannels;
+ char *s390_nettype;
+ GHashTable *s390_options;
+} NMSettingWiredPrivate;
+
+enum {
+ PROP_0,
+ PROP_PORT,
+ PROP_SPEED,
+ PROP_DUPLEX,
+ PROP_AUTO_NEGOTIATE,
+ PROP_MAC_ADDRESS,
+ PROP_CLONED_MAC_ADDRESS,
+ PROP_MAC_ADDRESS_BLACKLIST,
+ PROP_MTU,
+ PROP_S390_SUBCHANNELS,
+ PROP_S390_NETTYPE,
+ PROP_S390_OPTIONS,
+
+ LAST_PROP
+};
+
+static const char *valid_s390_opts[] = {
+ "portno", "layer2", "portname", "protocol", "priority_queueing",
+ "buffer_count", "isolation", "total", "inter", "inter_jumbo", "route4",
+ "route6", "fake_broadcast", "broadcast_mode", "canonical_macaddr",
+ "checksumming", "sniffer", "large_send", "ipato_enable", "ipato_invert4",
+ "ipato_add4", "ipato_invert6", "ipato_add6", "vipa_add4", "vipa_add6",
+ "rxip_add4", "rxip_add6", "lancmd_timeout", "ctcprot",
+ NULL
+};
+
+/**
+ * nm_setting_wired_new:
+ *
+ * Creates a new #NMSettingWired object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingWired object
+ **/
+NMSetting *
+nm_setting_wired_new (void)
+{
+ return (NMSetting *) g_object_new (NM_TYPE_SETTING_WIRED, NULL);
+}
+
+/**
+ * nm_setting_wired_get_port:
+ * @setting: the #NMSettingWired
+ *
+ * Returns: the #NMSettingWired:port property of the setting
+ **/
+const char *
+nm_setting_wired_get_port (NMSettingWired *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_WIRED (setting), NULL);
+
+ return NM_SETTING_WIRED_GET_PRIVATE (setting)->port;
+}
+
+/**
+ * nm_setting_wired_get_speed:
+ * @setting: the #NMSettingWired
+ *
+ * Returns: the #NMSettingWired:speed property of the setting
+ **/
+guint32
+nm_setting_wired_get_speed (NMSettingWired *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_WIRED (setting), 0);
+
+ return NM_SETTING_WIRED_GET_PRIVATE (setting)->speed;
+}
+
+/**
+ * nm_setting_wired_get_duplex:
+ * @setting: the #NMSettingWired
+ *
+ * Returns: the #NMSettingWired:duplex property of the setting
+ **/
+const char *
+nm_setting_wired_get_duplex (NMSettingWired *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_WIRED (setting), NULL);
+
+ return NM_SETTING_WIRED_GET_PRIVATE (setting)->duplex;
+}
+
+/**
+ * nm_setting_wired_get_auto_negotiate:
+ * @setting: the #NMSettingWired
+ *
+ * Returns: the #NMSettingWired:auto-negotiate property of the setting
+ **/
+gboolean
+nm_setting_wired_get_auto_negotiate (NMSettingWired *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_WIRED (setting), FALSE);
+
+ return NM_SETTING_WIRED_GET_PRIVATE (setting)->auto_negotiate;
+}
+
+/**
+ * nm_setting_wired_get_mac_address:
+ * @setting: the #NMSettingWired
+ *
+ * Returns: the #NMSettingWired:mac-address property of the setting
+ **/
+const GByteArray *
+nm_setting_wired_get_mac_address (NMSettingWired *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_WIRED (setting), NULL);
+
+ return NM_SETTING_WIRED_GET_PRIVATE (setting)->device_mac_address;
+}
+
+/**
+ * nm_setting_wired_get_cloned_mac_address:
+ * @setting: the #NMSettingWired
+ *
+ * Returns: the #NMSettingWired:cloned-mac-address property of the setting
+ **/
+const GByteArray *
+nm_setting_wired_get_cloned_mac_address (NMSettingWired *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_WIRED (setting), NULL);
+
+ return NM_SETTING_WIRED_GET_PRIVATE (setting)->cloned_mac_address;
+}
+
+/**
+ * nm_setting_wired_get_mac_address_blacklist:
+ * @setting: the #NMSettingWired
+ *
+ * Returns: (element-type GLib.ByteArray): the #NMSettingWired:mac-address-blacklist
+ * property of the setting
+ **/
+const GSList *
+nm_setting_wired_get_mac_address_blacklist (NMSettingWired *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_WIRED (setting), NULL);
+
+ return NM_SETTING_WIRED_GET_PRIVATE (setting)->mac_address_blacklist;
+}
+
+/**
+ * nm_setting_wired_get_num_mac_blacklist_items:
+ * @setting: the #NMSettingWired
+ *
+ * Returns: the number of blacklisted MAC addresses
+ *
+ * Since: 0.9.10
+ **/
+guint32
+nm_setting_wired_get_num_mac_blacklist_items (NMSettingWired *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_WIRED (setting), 0);
+
+ return g_slist_length (NM_SETTING_WIRED_GET_PRIVATE (setting)->mac_address_blacklist);
+}
+
+/**
+ * nm_setting_wired_get_mac_blacklist_item:
+ * @setting: the #NMSettingWired
+ * @idx: the zero-based index of the MAC address entry
+ *
+ * Returns: the blacklisted MAC address string (hex-digits-and-colons notation)
+ * at index @idx
+ *
+ * Since: 0.9.10
+ **/
+const char *
+nm_setting_wired_get_mac_blacklist_item (NMSettingWired *setting, guint32 idx)
+{
+ NMSettingWiredPrivate *priv;
+
+ g_return_val_if_fail (NM_IS_SETTING_WIRED (setting), NULL);
+
+ priv = NM_SETTING_WIRED_GET_PRIVATE (setting);
+ g_return_val_if_fail (idx <= g_slist_length (priv->mac_address_blacklist), NULL);
+
+ return (const char *) g_slist_nth_data (priv->mac_address_blacklist, idx);
+}
+
+/**
+ * nm_setting_wired_add_mac_blacklist_item:
+ * @setting: the #NMSettingWired
+ * @mac: the MAC address string (hex-digits-and-colons notation) to blacklist
+ *
+ * Adds a new MAC address to the #NMSettingWired:mac-address-blacklist property.
+ *
+ * Returns: %TRUE if the MAC address was added; %FALSE if the MAC address
+ * is invalid or was already present
+ *
+ * Since: 0.9.10
+ **/
+gboolean
+nm_setting_wired_add_mac_blacklist_item (NMSettingWired *setting, const char *mac)
+{
+ NMSettingWiredPrivate *priv;
+ GSList *iter;
+ guint8 buf[32];
+
+ g_return_val_if_fail (NM_IS_SETTING_WIRED (setting), FALSE);
+ g_return_val_if_fail (mac != NULL, FALSE);
+
+ if (!nm_utils_hwaddr_aton (mac, ARPHRD_ETHER, buf))
+ return FALSE;
+
+ priv = NM_SETTING_WIRED_GET_PRIVATE (setting);
+ for (iter = priv->mac_address_blacklist; iter; iter = g_slist_next (iter)) {
+ if (!strcasecmp (mac, (char *) iter->data))
+ return FALSE;
+ }
+
+ priv->mac_address_blacklist = g_slist_append (priv->mac_address_blacklist,
+ g_ascii_strup (mac, -1));
+ g_object_notify (G_OBJECT (setting), NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST);
+ return TRUE;
+}
+
+/**
+ * nm_setting_wired_remove_mac_blacklist_item:
+ * @setting: the #NMSettingWired
+ * @idx: index number of the MAC address
+ *
+ * Removes the MAC address at index @idx from the blacklist.
+ *
+ * Since: 0.9.10
+ **/
+void
+nm_setting_wired_remove_mac_blacklist_item (NMSettingWired *setting, guint32 idx)
+{
+ NMSettingWiredPrivate *priv;
+ GSList *elt;
+
+ g_return_if_fail (NM_IS_SETTING_WIRED (setting));
+
+ priv = NM_SETTING_WIRED_GET_PRIVATE (setting);
+ elt = g_slist_nth (priv->mac_address_blacklist, idx);
+ g_return_if_fail (elt != NULL);
+
+ g_free (elt->data);
+ priv->mac_address_blacklist = g_slist_delete_link (priv->mac_address_blacklist, elt);
+ g_object_notify (G_OBJECT (setting), NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST);
+}
+
+/**
+ * nm_setting_wired_remove_mac_blacklist_item_by_value:
+ * @setting: the #NMSettingWired
+ * @mac: the MAC address string (hex-digits-and-colons notation) to remove from
+ * the blacklist
+ *
+ * Removes the MAC address @mac from the blacklist.
+ *
+ * Returns: %TRUE if the MAC address was found and removed; %FALSE if it was not.
+ *
+ * Since: 0.9.10
+ **/
+gboolean
+nm_setting_wired_remove_mac_blacklist_item_by_value (NMSettingWired *setting, const char *mac)
+{
+ NMSettingWiredPrivate *priv;
+ GSList *iter;
+ guint8 buf[32];
+
+ g_return_val_if_fail (NM_IS_SETTING_WIRED (setting), FALSE);
+ g_return_val_if_fail (mac != NULL, FALSE);
+
+ if (!nm_utils_hwaddr_aton (mac, ARPHRD_ETHER, buf))
+ return FALSE;
+
+ priv = NM_SETTING_WIRED_GET_PRIVATE (setting);
+ for (iter = priv->mac_address_blacklist; iter; iter = g_slist_next (iter)) {
+ if (!strcasecmp (mac, (char *) iter->data)) {
+ priv->mac_address_blacklist = g_slist_delete_link (priv->mac_address_blacklist, iter);
+ g_object_notify (G_OBJECT (setting), NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/**
+ * nm_setting_wired_clear_mac_blacklist_items:
+ * @setting: the #NMSettingWired
+ *
+ * Removes all blacklisted MAC addresses.
+ *
+ * Since: 0.9.10
+ **/
+void
+nm_setting_wired_clear_mac_blacklist_items (NMSettingWired *setting)
+{
+ g_return_if_fail (NM_IS_SETTING_WIRED (setting));
+
+ g_slist_free_full (NM_SETTING_WIRED_GET_PRIVATE (setting)->mac_address_blacklist, g_free);
+ NM_SETTING_WIRED_GET_PRIVATE (setting)->mac_address_blacklist = NULL;
+ g_object_notify (G_OBJECT (setting), NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST);
+}
+
+/**
+ * nm_setting_wired_get_mtu:
+ * @setting: the #NMSettingWired
+ *
+ * Returns: the #NMSettingWired:mtu property of the setting
+ **/
+guint32
+nm_setting_wired_get_mtu (NMSettingWired *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_WIRED (setting), 0);
+
+ return NM_SETTING_WIRED_GET_PRIVATE (setting)->mtu;
+}
+
+/**
+ * nm_setting_wired_get_s390_subchannels:
+ * @setting: the #NMSettingWired
+ *
+ * Return the list of s390 subchannels that identify the device that this
+ * connection is applicable to. The connection should only be used in
+ * conjunction with that device.
+ *
+ * Returns: (element-type utf8): #GPtrArray of strings, each specifying one
+ * subchannel the s390 device uses to communicate to the host.
+ **/
+const GPtrArray *
+nm_setting_wired_get_s390_subchannels (NMSettingWired *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_WIRED (setting), NULL);
+
+ return NM_SETTING_WIRED_GET_PRIVATE (setting)->s390_subchannels;
+}
+
+/**
+ * nm_setting_wired_get_s390_nettype:
+ * @setting: the #NMSettingWired
+ *
+ * Returns the s390 device type this connection should apply to. Will be one
+ * of 'qeth', 'lcs', or 'ctc'.
+ *
+ * Returns: the s390 device type
+ **/
+const char *
+nm_setting_wired_get_s390_nettype (NMSettingWired *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_WIRED (setting), NULL);
+
+ return NM_SETTING_WIRED_GET_PRIVATE (setting)->s390_nettype;
+}
+
+/**
+ * nm_setting_wired_get_num_s390_options:
+ * @setting: the #NMSettingWired
+ *
+ * Returns the number of s390-specific options that should be set for this
+ * device when it is activated. This can be used to retrieve each s390
+ * option individually using nm_setting_wired_get_s390_option().
+ *
+ * Returns: the number of s390-specific device options
+ **/
+guint32
+nm_setting_wired_get_num_s390_options (NMSettingWired *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_WIRED (setting), 0);
+
+ return g_hash_table_size (NM_SETTING_WIRED_GET_PRIVATE (setting)->s390_options);
+}
+
+/**
+ * nm_setting_wired_get_s390_option:
+ * @setting: the #NMSettingWired
+ * @idx: index of the desired option, from 0 to
+ * nm_setting_wired_get_num_s390_options() - 1
+ * @out_key: (out): on return, the key name of the s390 specific option; this
+ * value is owned by the setting and should not be modified
+ * @out_value: (out): on return, the value of the key of the s390 specific
+ * option; this value is owned by the setting and should not be modified
+ *
+ * Given an index, return the value of the s390 option at that index. indexes
+ * are *not* guaranteed to be static across modifications to options done by
+ * nm_setting_wired_add_s390_option() and nm_setting_wired_remove_s390_option(),
+ * and should not be used to refer to options except for short periods of time
+ * such as during option iteration.
+ *
+ * Returns: %TRUE on success if the index was valid and an option was found,
+ * %FALSE if the index was invalid (ie, greater than the number of options
+ * currently held by the setting)
+ **/
+gboolean
+nm_setting_wired_get_s390_option (NMSettingWired *setting,
+ guint32 idx,
+ const char **out_key,
+ const char **out_value)
+{
+ NMSettingWiredPrivate *priv;
+ guint32 num_keys;
+ GList *keys;
+ const char *_key = NULL, *_value = NULL;
+
+ g_return_val_if_fail (NM_IS_SETTING_WIRED (setting), FALSE);
+
+ priv = NM_SETTING_WIRED_GET_PRIVATE (setting);
+
+ num_keys = nm_setting_wired_get_num_s390_options (setting);
+ g_return_val_if_fail (idx < num_keys, FALSE);
+
+ keys = g_hash_table_get_keys (priv->s390_options);
+ _key = g_list_nth_data (keys, idx);
+ _value = g_hash_table_lookup (priv->s390_options, _key);
+
+ if (out_key)
+ *out_key = _key;
+ if (out_value)
+ *out_value = _value;
+ return TRUE;
+}
+
+/**
+ * nm_setting_wired_get_s390_option_by_key:
+ * @setting: the #NMSettingWired
+ * @key: the key for which to retrieve the value
+ *
+ * Returns the value associated with the s390-specific option specified by
+ * @key, if it exists.
+ *
+ * Returns: the value, or %NULL if the key/value pair was never added to the
+ * setting; the value is owned by the setting and must not be modified
+ **/
+const char *
+nm_setting_wired_get_s390_option_by_key (NMSettingWired *setting,
+ const char *key)
+{
+ g_return_val_if_fail (NM_IS_SETTING_WIRED (setting), NULL);
+ g_return_val_if_fail (key != NULL, NULL);
+ g_return_val_if_fail (strlen (key), NULL);
+
+ return g_hash_table_lookup (NM_SETTING_WIRED_GET_PRIVATE (setting)->s390_options, key);
+}
+
+/**
+ * nm_setting_wired_add_s390_option:
+ * @setting: the #NMSettingWired
+ * @key: key name for the option
+ * @value: value for the option
+ *
+ * Add an option to the table. The option is compared to an internal list
+ * of allowed options. Key names may contain only alphanumeric characters
+ * (ie [a-zA-Z0-9]). Adding a new key replaces any existing key/value pair that
+ * may already exist.
+ *
+ * Returns: %TRUE if the option was valid and was added to the internal option
+ * list, %FALSE if it was not.
+ **/
+gboolean
+nm_setting_wired_add_s390_option (NMSettingWired *setting,
+ const char *key,
+ const char *value)
+{
+ size_t value_len;
+
+ g_return_val_if_fail (NM_IS_SETTING_WIRED (setting), FALSE);
+ g_return_val_if_fail (key != NULL, FALSE);
+ g_return_val_if_fail (strlen (key), FALSE);
+ g_return_val_if_fail (_nm_utils_string_in_list (key, valid_s390_opts), FALSE);
+ g_return_val_if_fail (value != NULL, FALSE);
+
+ value_len = strlen (value);
+ g_return_val_if_fail (value_len > 0 && value_len < 200, FALSE);
+
+ g_hash_table_insert (NM_SETTING_WIRED_GET_PRIVATE (setting)->s390_options,
+ g_strdup (key),
+ g_strdup (value));
+ g_object_notify (G_OBJECT (setting), NM_SETTING_WIRED_S390_OPTIONS);
+ return TRUE;
+}
+
+/**
+ * nm_setting_wired_remove_s390_option:
+ * @setting: the #NMSettingWired
+ * @key: key name for the option to remove
+ *
+ * Remove the s390-specific option referenced by @key from the internal option
+ * list.
+ *
+ * Returns: %TRUE if the option was found and removed from the internal option
+ * list, %FALSE if it was not.
+ **/
+gboolean
+nm_setting_wired_remove_s390_option (NMSettingWired *setting,
+ const char *key)
+{
+ gboolean found;
+
+ g_return_val_if_fail (NM_IS_SETTING_WIRED (setting), FALSE);
+ g_return_val_if_fail (key != NULL, FALSE);
+ g_return_val_if_fail (strlen (key), FALSE);
+
+ found = g_hash_table_remove (NM_SETTING_WIRED_GET_PRIVATE (setting)->s390_options, key);
+ if (found)
+ g_object_notify (G_OBJECT (setting), NM_SETTING_WIRED_S390_OPTIONS);
+ return found;
+}
+
+/**
+ * nm_setting_wired_get_valid_s390_options:
+ * @setting: the #NMSettingWired
+ *
+ * Returns a list of valid s390 options.
+ *
+ * Returns: (transfer none): a %NULL-terminated array of strings of valid s390 options.
+ *
+ * Since: 0.9.10
+ **/
+const char **
+nm_setting_wired_get_valid_s390_options (NMSettingWired *setting)
+{
+ return valid_s390_opts;
+}
+
+static gboolean
+verify (NMSetting *setting, GSList *all_settings, GError **error)
+{
+ NMSettingWiredPrivate *priv = NM_SETTING_WIRED_GET_PRIVATE (setting);
+ const char *valid_ports[] = { "tp", "aui", "bnc", "mii", NULL };
+ const char *valid_duplex[] = { "half", "full", NULL };
+ const char *valid_nettype[] = { "qeth", "lcs", "ctc", NULL };
+ GHashTableIter iter;
+ GSList* mac_blacklist_iter;
+ const char *key, *value;
+
+ if (priv->port && !_nm_utils_string_in_list (priv->port, valid_ports)) {
+ g_set_error (error,
+ NM_SETTING_WIRED_ERROR,
+ NM_SETTING_WIRED_ERROR_INVALID_PROPERTY,
+ _("'%s' is not a valid Ethernet port value"),
+ priv->port);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRED_SETTING_NAME, NM_SETTING_WIRED_PORT);
+ return FALSE;
+ }
+
+ if (priv->duplex && !_nm_utils_string_in_list (priv->duplex, valid_duplex)) {
+ g_set_error (error,
+ NM_SETTING_WIRED_ERROR,
+ NM_SETTING_WIRED_ERROR_INVALID_PROPERTY,
+ _("'%s' is not a valid duplex value"),
+ priv->duplex);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRED_SETTING_NAME, NM_SETTING_WIRED_DUPLEX);
+ return FALSE;
+ }
+
+ if (priv->device_mac_address && priv->device_mac_address->len != ETH_ALEN) {
+ g_set_error_literal (error,
+ NM_SETTING_WIRED_ERROR,
+ NM_SETTING_WIRED_ERROR_INVALID_PROPERTY,
+ _("is not a valid MAC address"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRED_SETTING_NAME, NM_SETTING_WIRED_MAC_ADDRESS);
+ return FALSE;
+ }
+
+ for (mac_blacklist_iter = priv->mac_address_blacklist; mac_blacklist_iter;
+ mac_blacklist_iter = mac_blacklist_iter->next) {
+ struct ether_addr addr;
+
+ if (!ether_aton_r (mac_blacklist_iter->data, &addr)) {
+ g_set_error (error,
+ NM_SETTING_WIRED_ERROR,
+ NM_SETTING_WIRED_ERROR_INVALID_PROPERTY,
+ _("'%s' is not a valid MAC address"),
+ (const char *) mac_blacklist_iter->data);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRED_SETTING_NAME, NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST);
+ return FALSE;
+ }
+ }
+
+ if ( priv->s390_subchannels
+ && !(priv->s390_subchannels->len == 3 || priv->s390_subchannels->len == 2)) {
+ g_set_error_literal (error,
+ NM_SETTING_WIRED_ERROR,
+ NM_SETTING_WIRED_ERROR_INVALID_PROPERTY,
+ _("property is invalid"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRED_SETTING_NAME, NM_SETTING_WIRED_S390_SUBCHANNELS);
+ return FALSE;
+ }
+
+ if (priv->s390_nettype && !_nm_utils_string_in_list (priv->s390_nettype, valid_nettype)) {
+ g_set_error_literal (error,
+ NM_SETTING_WIRED_ERROR,
+ NM_SETTING_WIRED_ERROR_INVALID_PROPERTY,
+ _("property is invalid"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRED_SETTING_NAME, NM_SETTING_WIRED_S390_NETTYPE);
+ return FALSE;
+ }
+
+ g_hash_table_iter_init (&iter, priv->s390_options);
+ while (g_hash_table_iter_next (&iter, (gpointer) &key, (gpointer) &value)) {
+ if ( !_nm_utils_string_in_list (key, valid_s390_opts)
+ || !strlen (value)
+ || (strlen (value) > 200)) {
+ g_set_error (error,
+ NM_SETTING_WIRED_ERROR,
+ NM_SETTING_WIRED_ERROR_INVALID_PROPERTY,
+ _("invalid '%s' or its value '%s'"),
+ key, value);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRED_SETTING_NAME, NM_SETTING_WIRED_S390_OPTIONS);
+ return FALSE;
+ }
+ }
+
+ if (priv->cloned_mac_address && priv->cloned_mac_address->len != ETH_ALEN) {
+ g_set_error_literal (error,
+ NM_SETTING_WIRED_ERROR,
+ NM_SETTING_WIRED_ERROR_INVALID_PROPERTY,
+ _("is not a valid MAC address"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRED_SETTING_NAME, NM_SETTING_WIRED_CLONED_MAC_ADDRESS);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+nm_setting_wired_init (NMSettingWired *setting)
+{
+ NMSettingWiredPrivate *priv = NM_SETTING_WIRED_GET_PRIVATE (setting);
+
+ priv->s390_options = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+}
+
+static void
+finalize (GObject *object)
+{
+ NMSettingWiredPrivate *priv = NM_SETTING_WIRED_GET_PRIVATE (object);
+
+ g_free (priv->port);
+ g_free (priv->duplex);
+ g_free (priv->s390_nettype);
+
+ g_hash_table_destroy (priv->s390_options);
+
+ if (priv->device_mac_address)
+ g_byte_array_free (priv->device_mac_address, TRUE);
+
+ if (priv->cloned_mac_address)
+ g_byte_array_free (priv->cloned_mac_address, TRUE);
+
+ g_slist_free_full (priv->mac_address_blacklist, g_free);
+
+ if (priv->s390_subchannels) {
+ g_ptr_array_set_free_func (priv->s390_subchannels, g_free);
+ g_ptr_array_free (priv->s390_subchannels, TRUE);
+ }
+
+ G_OBJECT_CLASS (nm_setting_wired_parent_class)->finalize (object);
+}
+
+static void
+copy_hash (gpointer key, gpointer value, gpointer user_data)
+{
+ g_hash_table_insert ((GHashTable *) user_data, g_strdup (key), g_strdup (value));
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ NMSettingWiredPrivate *priv = NM_SETTING_WIRED_GET_PRIVATE (object);
+ GHashTable *new_hash;
+
+ switch (prop_id) {
+ case PROP_PORT:
+ g_free (priv->port);
+ priv->port = g_value_dup_string (value);
+ break;
+ case PROP_SPEED:
+ priv->speed = g_value_get_uint (value);
+ break;
+ case PROP_DUPLEX:
+ g_free (priv->duplex);
+ priv->duplex = g_value_dup_string (value);
+ break;
+ case PROP_AUTO_NEGOTIATE:
+ priv->auto_negotiate = g_value_get_boolean (value);
+ break;
+ case PROP_MAC_ADDRESS:
+ if (priv->device_mac_address)
+ g_byte_array_free (priv->device_mac_address, TRUE);
+ priv->device_mac_address = g_value_dup_boxed (value);
+ break;
+ case PROP_CLONED_MAC_ADDRESS:
+ if (priv->cloned_mac_address)
+ g_byte_array_free (priv->cloned_mac_address, TRUE);
+ priv->cloned_mac_address = g_value_dup_boxed (value);
+ break;
+ case PROP_MAC_ADDRESS_BLACKLIST:
+ g_slist_free_full (priv->mac_address_blacklist, g_free);
+ priv->mac_address_blacklist = g_value_dup_boxed (value);
+ break;
+ case PROP_MTU:
+ priv->mtu = g_value_get_uint (value);
+ break;
+ case PROP_S390_SUBCHANNELS:
+ if (priv->s390_subchannels) {
+ g_ptr_array_set_free_func (priv->s390_subchannels, g_free);
+ g_ptr_array_free (priv->s390_subchannels, TRUE);
+ }
+ priv->s390_subchannels = g_value_dup_boxed (value);
+ break;
+ case PROP_S390_NETTYPE:
+ g_free (priv->s390_nettype);
+ priv->s390_nettype = g_value_dup_string (value);
+ break;
+ case PROP_S390_OPTIONS:
+ /* Must make a deep copy of the hash table here... */
+ g_hash_table_remove_all (priv->s390_options);
+ new_hash = g_value_get_boxed (value);
+ if (new_hash)
+ g_hash_table_foreach (new_hash, copy_hash, priv->s390_options);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ NMSettingWired *setting = NM_SETTING_WIRED (object);
+ NMSettingWiredPrivate *priv = NM_SETTING_WIRED_GET_PRIVATE (setting);
+
+ switch (prop_id) {
+ case PROP_PORT:
+ g_value_set_string (value, nm_setting_wired_get_port (setting));
+ break;
+ case PROP_SPEED:
+ g_value_set_uint (value, nm_setting_wired_get_speed (setting));
+ break;
+ case PROP_DUPLEX:
+ g_value_set_string (value, nm_setting_wired_get_duplex (setting));
+ break;
+ case PROP_AUTO_NEGOTIATE:
+ g_value_set_boolean (value, nm_setting_wired_get_auto_negotiate (setting));
+ break;
+ case PROP_MAC_ADDRESS:
+ g_value_set_boxed (value, nm_setting_wired_get_mac_address (setting));
+ break;
+ case PROP_CLONED_MAC_ADDRESS:
+ g_value_set_boxed (value, nm_setting_wired_get_cloned_mac_address (setting));
+ break;
+ case PROP_MAC_ADDRESS_BLACKLIST:
+ g_value_set_boxed (value, nm_setting_wired_get_mac_address_blacklist (setting));
+ break;
+ case PROP_MTU:
+ g_value_set_uint (value, nm_setting_wired_get_mtu (setting));
+ break;
+ case PROP_S390_SUBCHANNELS:
+ g_value_set_boxed (value, nm_setting_wired_get_s390_subchannels (setting));
+ break;
+ case PROP_S390_NETTYPE:
+ g_value_set_string (value, nm_setting_wired_get_s390_nettype (setting));
+ break;
+ case PROP_S390_OPTIONS:
+ g_value_set_boxed (value, priv->s390_options);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nm_setting_wired_class_init (NMSettingWiredClass *setting_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
+ NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+
+ g_type_class_add_private (setting_class, sizeof (NMSettingWiredPrivate));
+
+ /* virtual methods */
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+ object_class->finalize = finalize;
+ parent_class->verify = verify;
+
+ /* Properties */
+ /**
+ * NMSettingWired:port:
+ *
+ * Specific port type to use if multiple the device supports multiple
+ * attachment methods. One of "tp" (Twisted Pair), "aui" (Attachment Unit
+ * Interface), "bnc" (Thin Ethernet) or "mii" (Media Independent Interface.
+ * If the device supports only one port type, this setting is ignored.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PORT,
+ g_param_spec_string (NM_SETTING_WIRED_PORT, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingWired:speed:
+ *
+ * If non-zero, request that the device use only the specified speed. In
+ * Mbit/s, ie 100 == 100Mbit/s.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_SPEED,
+ g_param_spec_uint (NM_SETTING_WIRED_SPEED, "", "",
+ 0, G_MAXUINT32, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingWired:duplex:
+ *
+ * If specified, request that the device only use the specified duplex mode.
+ * Either "half" or "full".
+ **/
+ g_object_class_install_property
+ (object_class, PROP_DUPLEX,
+ g_param_spec_string (NM_SETTING_WIRED_DUPLEX, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingWired:auto-negotiate:
+ *
+ * If %TRUE, allow auto-negotiation of port speed and duplex mode. If
+ * %FALSE, do not allow auto-negotiation, in which case the "speed" and
+ * "duplex" properties should be set.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_AUTO_NEGOTIATE,
+ g_param_spec_boolean (NM_SETTING_WIRED_AUTO_NEGOTIATE, "", "",
+ TRUE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingWired:mac-address:
+ *
+ * If specified, this connection will only apply to the Ethernet device
+ * whose permanent MAC address matches. This property does not change the
+ * MAC address of the device (i.e. MAC spoofing).
+ **/
+ g_object_class_install_property
+ (object_class, PROP_MAC_ADDRESS,
+ _nm_param_spec_specialized (NM_SETTING_WIRED_MAC_ADDRESS, "", "",
+ DBUS_TYPE_G_UCHAR_ARRAY,
+ G_PARAM_READWRITE |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingWired:cloned-mac-address:
+ *
+ * If specified, request that the device use this MAC address instead of its
+ * permanent MAC address. This is known as MAC cloning or spoofing.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_CLONED_MAC_ADDRESS,
+ _nm_param_spec_specialized (NM_SETTING_WIRED_CLONED_MAC_ADDRESS, "", "",
+ DBUS_TYPE_G_UCHAR_ARRAY,
+ G_PARAM_READWRITE |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingWired:mac-address-blacklist:
+ *
+ * If specified, this connection will never apply to the Ethernet device
+ * whose permanent MAC address matches an address in the list. Each MAC
+ * address is in the standard hex-digits-and-colons notation
+ * (00:11:22:33:44:55).
+ **/
+ g_object_class_install_property
+ (object_class, PROP_MAC_ADDRESS_BLACKLIST,
+ _nm_param_spec_specialized (NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST, "", "",
+ DBUS_TYPE_G_LIST_OF_STRING,
+ G_PARAM_READWRITE |
+ NM_SETTING_PARAM_FUZZY_IGNORE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingWired:mtu:
+ *
+ * If non-zero, only transmit packets of the specified size or smaller,
+ * breaking larger packets up into multiple Ethernet frames.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_MTU,
+ g_param_spec_uint (NM_SETTING_WIRED_MTU, "", "",
+ 0, G_MAXUINT32, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ NM_SETTING_PARAM_FUZZY_IGNORE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingWired:s390-subchannels:
+ *
+ * Identifies specific subchannels that this network device uses for
+ * communication with z/VM or s390 host. Like the
+ * #NMSettingWired:mac-address property for non-z/VM devices, this property
+ * can be used to ensure this connection only applies to the network device
+ * that uses these subchannels. The list should contain exactly 3 strings,
+ * and each string may only be composed of hexadecimal characters and the
+ * period (.) character.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_S390_SUBCHANNELS,
+ _nm_param_spec_specialized (NM_SETTING_WIRED_S390_SUBCHANNELS, "", "",
+ DBUS_TYPE_G_ARRAY_OF_STRING,
+ G_PARAM_READWRITE |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingWired:s390-nettype:
+ *
+ * s390 network device type; one of "qeth", "lcs", or "ctc", representing
+ * the different types of virtual network devices available on s390 systems.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_S390_NETTYPE,
+ g_param_spec_string (NM_SETTING_WIRED_S390_NETTYPE, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingWired:s390-options:
+ *
+ * Dictionary of key/value pairs of s390-specific device options. Both keys
+ * and values must be strings. Allowed keys include "portno", "layer2",
+ * "portname", "protocol", among others. Key names must contain only
+ * alphanumeric characters (ie, [a-zA-Z0-9]).
+ **/
+ g_object_class_install_property
+ (object_class, PROP_S390_OPTIONS,
+ _nm_param_spec_specialized (NM_SETTING_WIRED_S390_OPTIONS, "", "",
+ DBUS_TYPE_G_MAP_OF_STRING,
+ G_PARAM_READWRITE |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/libnm-core/nm-setting-wired.h b/libnm-core/nm-setting-wired.h
new file mode 100644
index 0000000000..33d6d54dde
--- /dev/null
+++ b/libnm-core/nm-setting-wired.h
@@ -0,0 +1,131 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2014 Red Hat, Inc.
+ * Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#ifndef NM_SETTING_WIRED_H
+#define NM_SETTING_WIRED_H
+
+#include <nm-setting.h>
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_SETTING_WIRED (nm_setting_wired_get_type ())
+#define NM_SETTING_WIRED(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_WIRED, NMSettingWired))
+#define NM_SETTING_WIRED_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_WIRED, NMSettingWiredClass))
+#define NM_IS_SETTING_WIRED(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_WIRED))
+#define NM_IS_SETTING_WIRED_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_WIRED))
+#define NM_SETTING_WIRED_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_WIRED, NMSettingWiredClass))
+
+#define NM_SETTING_WIRED_SETTING_NAME "802-3-ethernet"
+
+/**
+ * NMSettingWiredError:
+ * @NM_SETTING_WIRED_ERROR_UNKNOWN: unknown or unclassified error
+ * @NM_SETTING_WIRED_ERROR_INVALID_PROPERTY: the property was invalid
+ * @NM_SETTING_WIRED_ERROR_MISSING_PROPERTY: the property was missing and is
+ * required
+ */
+typedef enum {
+ NM_SETTING_WIRED_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/
+ NM_SETTING_WIRED_ERROR_INVALID_PROPERTY, /*< nick=InvalidProperty >*/
+ NM_SETTING_WIRED_ERROR_MISSING_PROPERTY /*< nick=MissingProperty >*/
+} NMSettingWiredError;
+
+#define NM_SETTING_WIRED_ERROR nm_setting_wired_error_quark ()
+GQuark nm_setting_wired_error_quark (void);
+
+#define NM_SETTING_WIRED_PORT "port"
+#define NM_SETTING_WIRED_SPEED "speed"
+#define NM_SETTING_WIRED_DUPLEX "duplex"
+#define NM_SETTING_WIRED_AUTO_NEGOTIATE "auto-negotiate"
+#define NM_SETTING_WIRED_MAC_ADDRESS "mac-address"
+#define NM_SETTING_WIRED_CLONED_MAC_ADDRESS "cloned-mac-address"
+#define NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST "mac-address-blacklist"
+#define NM_SETTING_WIRED_MTU "mtu"
+#define NM_SETTING_WIRED_S390_SUBCHANNELS "s390-subchannels"
+#define NM_SETTING_WIRED_S390_NETTYPE "s390-nettype"
+#define NM_SETTING_WIRED_S390_OPTIONS "s390-options"
+
+typedef struct {
+ NMSetting parent;
+} NMSettingWired;
+
+typedef struct {
+ NMSettingClass parent;
+
+ /* Padding for future expansion */
+ void (*_reserved1) (void);
+ void (*_reserved2) (void);
+ void (*_reserved3) (void);
+ void (*_reserved4) (void);
+} NMSettingWiredClass;
+
+GType nm_setting_wired_get_type (void);
+
+NMSetting * nm_setting_wired_new (void);
+const char * nm_setting_wired_get_port (NMSettingWired *setting);
+guint32 nm_setting_wired_get_speed (NMSettingWired *setting);
+const char * nm_setting_wired_get_duplex (NMSettingWired *setting);
+gboolean nm_setting_wired_get_auto_negotiate (NMSettingWired *setting);
+const GByteArray *nm_setting_wired_get_mac_address (NMSettingWired *setting);
+const GByteArray *nm_setting_wired_get_cloned_mac_address (NMSettingWired *setting);
+
+const GSList *nm_setting_wired_get_mac_address_blacklist (NMSettingWired *setting);
+NM_AVAILABLE_IN_0_9_10
+guint32 nm_setting_wired_get_num_mac_blacklist_items (NMSettingWired *setting);
+NM_AVAILABLE_IN_0_9_10
+const char * nm_setting_wired_get_mac_blacklist_item (NMSettingWired *setting,
+ guint32 idx);
+NM_AVAILABLE_IN_0_9_10
+gboolean nm_setting_wired_add_mac_blacklist_item (NMSettingWired *setting,
+ const char *mac);
+NM_AVAILABLE_IN_0_9_10
+void nm_setting_wired_remove_mac_blacklist_item (NMSettingWired *setting,
+ guint32 idx);
+NM_AVAILABLE_IN_0_9_10
+gboolean nm_setting_wired_remove_mac_blacklist_item_by_value (NMSettingWired *setting,
+ const char *mac);
+NM_AVAILABLE_IN_0_9_10
+void nm_setting_wired_clear_mac_blacklist_items (NMSettingWired *setting);
+
+guint32 nm_setting_wired_get_mtu (NMSettingWired *setting);
+
+const GPtrArray * nm_setting_wired_get_s390_subchannels (NMSettingWired *setting);
+const char * nm_setting_wired_get_s390_nettype (NMSettingWired *setting);
+
+guint32 nm_setting_wired_get_num_s390_options (NMSettingWired *setting);
+gboolean nm_setting_wired_get_s390_option (NMSettingWired *setting,
+ guint32 idx,
+ const char **out_key,
+ const char **out_value);
+const char * nm_setting_wired_get_s390_option_by_key (NMSettingWired *setting,
+ const char *key);
+gboolean nm_setting_wired_add_s390_option (NMSettingWired *setting,
+ const char *key,
+ const char *value);
+gboolean nm_setting_wired_remove_s390_option (NMSettingWired *setting,
+ const char *key);
+NM_AVAILABLE_IN_0_9_10
+const char ** nm_setting_wired_get_valid_s390_options (NMSettingWired *setting);
+
+G_END_DECLS
+
+#endif /* NM_SETTING_WIRED_H */
diff --git a/libnm-core/nm-setting-wireless-security.c b/libnm-core/nm-setting-wireless-security.c
new file mode 100644
index 0000000000..2c46766476
--- /dev/null
+++ b/libnm-core/nm-setting-wireless-security.c
@@ -0,0 +1,1603 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2014 Red Hat, Inc.
+ * Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#include <config.h>
+#include <string.h>
+#include <dbus/dbus-glib.h>
+#include <glib/gi18n.h>
+
+#include "nm-setting-wireless-security.h"
+#include "nm-setting-8021x.h"
+#include "nm-param-spec-specialized.h"
+#include "nm-utils.h"
+#include "nm-dbus-glib-types.h"
+#include "nm-utils-private.h"
+#include "nm-setting-private.h"
+
+/**
+ * SECTION:nm-setting-wireless-security
+ * @short_description: Describes connection properties for Wi-Fi networks that
+ * use WEP, LEAP, WPA or WPA2/RSN security
+ * @include: nm-setting-wireless-security.h
+ *
+ * The #NMSettingWirelessSecurity object is a #NMSetting subclass that describes
+ * properties necessary for connection to encrypted Wi-Fi networks.
+ *
+ * It's a good idea to read up on wpa_supplicant configuration before using this
+ * setting extensively, since most of the options here correspond closely with
+ * the relevant wpa_supplicant configuration options. To get a better overview
+ * of how Wi-Fi security works, you may want to get copies of the following books.
+ *
+ * 802.11 Wireless Networks: The Definitive Guide, Second Edition
+ * Author: Matthew Gast
+ * ISBN: 978-0596100520
+ *
+ * Cisco Wireless LAN Security
+ * Authors: Krishna Sankar, Sri Sundaralingam, Darrin Miller, and Andrew Balinsky
+ * ISBN: 978-1587051548
+ **/
+
+/**
+ * nm_setting_wireless_security_error_quark:
+ *
+ * Registers an error quark for #NMSettingWired if necessary.
+ *
+ * Returns: the error quark used for #NMSettingWired errors.
+ **/
+GQuark
+nm_setting_wireless_security_error_quark (void)
+{
+ static GQuark quark;
+
+ if (G_UNLIKELY (!quark))
+ quark = g_quark_from_static_string ("nm-setting-wireless-security-error-quark");
+ return quark;
+}
+
+
+G_DEFINE_TYPE_WITH_CODE (NMSettingWirelessSecurity, nm_setting_wireless_security, NM_TYPE_SETTING,
+ _nm_register_setting (NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
+ g_define_type_id,
+ 2,
+ NM_SETTING_WIRELESS_SECURITY_ERROR))
+NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_WIRELESS_SECURITY)
+
+#define NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_WIRELESS_SECURITY, NMSettingWirelessSecurityPrivate))
+
+typedef struct {
+ char *key_mgmt;
+ char *auth_alg;
+ GSList *proto; /* GSList of strings */
+ GSList *pairwise; /* GSList of strings */
+ GSList *group; /* GSList of strings */
+
+ /* LEAP */
+ char *leap_username;
+ char *leap_password;
+ NMSettingSecretFlags leap_password_flags;
+
+ /* WEP */
+ char *wep_key0;
+ char *wep_key1;
+ char *wep_key2;
+ char *wep_key3;
+ NMSettingSecretFlags wep_key_flags;
+ NMWepKeyType wep_key_type;
+ guint32 wep_tx_keyidx;
+
+ /* WPA-PSK */
+ char *psk;
+ NMSettingSecretFlags psk_flags;
+} NMSettingWirelessSecurityPrivate;
+
+enum {
+ PROP_0,
+ PROP_KEY_MGMT,
+ PROP_WEP_TX_KEYIDX,
+ PROP_AUTH_ALG,
+ PROP_PROTO,
+ PROP_PAIRWISE,
+ PROP_GROUP,
+ PROP_LEAP_USERNAME,
+ PROP_WEP_KEY0,
+ PROP_WEP_KEY1,
+ PROP_WEP_KEY2,
+ PROP_WEP_KEY3,
+ PROP_WEP_KEY_FLAGS,
+ PROP_WEP_KEY_TYPE,
+ PROP_PSK,
+ PROP_PSK_FLAGS,
+ PROP_LEAP_PASSWORD,
+ PROP_LEAP_PASSWORD_FLAGS,
+
+ LAST_PROP
+};
+
+/**
+ * nm_setting_wireless_security_new:
+ *
+ * Creates a new #NMSettingWirelessSecurity object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingWirelessSecurity object
+ **/
+NMSetting *
+nm_setting_wireless_security_new (void)
+{
+ return (NMSetting *) g_object_new (NM_TYPE_SETTING_WIRELESS_SECURITY, NULL);
+}
+
+/**
+ * nm_setting_wireless_security_get_key_mgmt:
+ * @setting: the #NMSettingWirelessSecurity
+ *
+ * Returns: the #NMSettingWirelessSecurity:key-mgmt property of the setting
+ **/
+const char *
+nm_setting_wireless_security_get_key_mgmt (NMSettingWirelessSecurity *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), NULL);
+
+ return NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting)->key_mgmt;
+}
+
+/**
+ * nm_setting_wireless_security_get_num_protos:
+ * @setting: the #NMSettingWirelessSecurity
+ *
+ * Returns: the number of security protocols this connection allows when
+ * connecting to secure Wi-Fi networks
+ **/
+guint32
+nm_setting_wireless_security_get_num_protos (NMSettingWirelessSecurity *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), 0);
+
+ return g_slist_length (NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting)->proto);
+}
+
+/**
+ * nm_setting_wireless_security_get_proto:
+ * @setting: the #NMSettingWirelessSecurity
+ * @i: an index into the protocol list
+ *
+ * Returns: the protocol at index @i
+ **/
+const char *
+nm_setting_wireless_security_get_proto (NMSettingWirelessSecurity *setting, guint32 i)
+{
+ NMSettingWirelessSecurityPrivate *priv;
+
+ g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), NULL);
+
+ priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting);
+ g_return_val_if_fail (i <= g_slist_length (priv->proto), NULL);
+
+ return (const char *) g_slist_nth_data (priv->proto, i);
+}
+
+/**
+ * nm_setting_wireless_security_add_proto:
+ * @setting: the #NMSettingWirelessSecurity
+ * @proto: the protocol to add, one of "wpa" or "rsn"
+ *
+ * Adds a Wi-Fi security protocol (one of "wpa" or "rsn") to the allowed list;
+ * only protocols in this list will be used when finding and connecting to
+ * the Wi-Fi network specified by this connection. For example, if the
+ * protocol list contains only "wpa" but the access point for the SSID specified
+ * by this connection only supports WPA2/RSN, the connection cannot be used
+ * with the access point.
+ *
+ * Returns: %TRUE if the protocol was new and and was added to the allowed
+ * protocol list, or %FALSE if it was already in the list
+ **/
+gboolean
+nm_setting_wireless_security_add_proto (NMSettingWirelessSecurity *setting, const char *proto)
+{
+ NMSettingWirelessSecurityPrivate *priv;
+ GSList *iter;
+
+ g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), FALSE);
+ g_return_val_if_fail (proto != NULL, FALSE);
+
+ priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting);
+ for (iter = priv->proto; iter; iter = g_slist_next (iter)) {
+ if (strcasecmp (proto, (char *) iter->data) == 0)
+ return FALSE;
+ }
+
+ priv->proto = g_slist_append (priv->proto, g_ascii_strdown (proto, -1));
+ g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_SECURITY_PROTO);
+ return TRUE;
+}
+
+/**
+ * nm_setting_wireless_security_remove_proto:
+ * @setting: the #NMSettingWirelessSecurity
+ * @i: index of the protocol to remove
+ *
+ * Removes a protocol from the allowed protocol list.
+ **/
+void
+nm_setting_wireless_security_remove_proto (NMSettingWirelessSecurity *setting, guint32 i)
+{
+ NMSettingWirelessSecurityPrivate *priv;
+ GSList *elt;
+
+ g_return_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting));
+
+ priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting);
+ elt = g_slist_nth (priv->proto, i);
+ g_return_if_fail (elt != NULL);
+
+ g_free (elt->data);
+ priv->proto = g_slist_delete_link (priv->proto, elt);
+ g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_SECURITY_PROTO);
+}
+
+/**
+ * nm_setting_wireless_security_remove_proto_by_value:
+ * @setting: the #NMSettingWirelessSecurity
+ * @proto: the protocol to remove, one of "wpa" or "rsn"
+ *
+ * Removes a protocol from the allowed protocol list.
+ *
+ * Returns: %TRUE if the protocol was found and removed; %FALSE it it was not.
+ *
+ * Since: 0.9.10
+ **/
+gboolean
+nm_setting_wireless_security_remove_proto_by_value (NMSettingWirelessSecurity *setting,
+ const char *proto)
+{
+ NMSettingWirelessSecurityPrivate *priv;
+ GSList *iter;
+
+ g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), FALSE);
+ g_return_val_if_fail (proto != NULL, FALSE);
+
+ priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting);
+ for (iter = priv->proto; iter; iter = g_slist_next (iter)) {
+ if (strcasecmp (proto, (char *) iter->data) == 0) {
+ priv->proto = g_slist_delete_link (priv->proto, iter);
+ g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_SECURITY_PROTO);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/**
+ * nm_setting_wireless_security_clear_protos:
+ * @setting: the #NMSettingWirelessSecurity
+ *
+ * Removes all protocols from the allowed list. If there are no protocols
+ * specified then all protocols are allowed.
+ **/
+void
+nm_setting_wireless_security_clear_protos (NMSettingWirelessSecurity *setting)
+{
+ NMSettingWirelessSecurityPrivate *priv;
+
+ g_return_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting));
+
+ priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting);
+ g_slist_free_full (priv->proto, g_free);
+ priv->proto = NULL;
+ g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_SECURITY_PROTO);
+}
+
+/**
+ * nm_setting_wireless_security_get_num_pairwise:
+ * @setting: the #NMSettingWirelessSecurity
+ *
+ * Returns: the number of pairwise encryption algorithms in the allowed list
+ **/
+guint32
+nm_setting_wireless_security_get_num_pairwise (NMSettingWirelessSecurity *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), 0);
+
+ return g_slist_length (NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting)->pairwise);
+}
+
+/**
+ * nm_setting_wireless_security_get_pairwise:
+ * @setting: the #NMSettingWirelessSecurity
+ * @i: index of an item in the allowed pairwise encryption algorithm list
+ *
+ * Returns the allowed pairwise encryption algorithm from allowed algorithm
+ * list.
+ *
+ * Returns: the pairwise encryption algorithm at index @i
+ **/
+const char *
+nm_setting_wireless_security_get_pairwise (NMSettingWirelessSecurity *setting, guint32 i)
+{
+ NMSettingWirelessSecurityPrivate *priv;
+
+ g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), NULL);
+
+ priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting);
+ g_return_val_if_fail (i <= g_slist_length (priv->pairwise), NULL);
+
+ return (const char *) g_slist_nth_data (priv->pairwise, i);
+}
+
+/**
+ * nm_setting_wireless_security_add_pairwise:
+ * @setting: the #NMSettingWirelessSecurity
+ * @pairwise: the encryption algorithm to add, one of "tkip" or "ccmp"
+ *
+ * Adds an encryption algorithm to the list of allowed pairwise encryption
+ * algorithms. If the list is not empty, then only access points that support
+ * one or more of the encryption algorithms in the list will be considered
+ * compatible with this connection.
+ *
+ * Returns: %TRUE if the algorithm was added to the list, %FALSE if it was
+ * already in the list
+ **/
+gboolean
+nm_setting_wireless_security_add_pairwise (NMSettingWirelessSecurity *setting, const char *pairwise)
+{
+ NMSettingWirelessSecurityPrivate *priv;
+ GSList *iter;
+
+ g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), FALSE);
+ g_return_val_if_fail (pairwise != NULL, FALSE);
+
+ priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting);
+ for (iter = priv->pairwise; iter; iter = g_slist_next (iter)) {
+ if (strcasecmp (pairwise, (char *) iter->data) == 0)
+ return FALSE;
+ }
+
+ priv->pairwise = g_slist_append (priv->pairwise, g_ascii_strdown (pairwise, -1));
+ g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_SECURITY_PAIRWISE);
+ return TRUE;
+}
+
+/**
+ * nm_setting_wireless_security_remove_pairwise:
+ * @setting: the #NMSettingWirelessSecurity
+ * @i: the index of an item in the allowed pairwise encryption algorithm list
+ *
+ * Removes an encryption algorithm from the allowed pairwise encryption
+ * algorithm list.
+ **/
+void
+nm_setting_wireless_security_remove_pairwise (NMSettingWirelessSecurity *setting, guint32 i)
+{
+ NMSettingWirelessSecurityPrivate *priv;
+ GSList *elt;
+
+ g_return_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting));
+
+ priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting);
+ elt = g_slist_nth (priv->pairwise, i);
+ g_return_if_fail (elt != NULL);
+
+ g_free (elt->data);
+ priv->pairwise = g_slist_delete_link (priv->pairwise, elt);
+ g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_SECURITY_PAIRWISE);
+}
+
+/**
+ * nm_setting_wireless_security_remove_pairwise_by_value:
+ * @setting: the #NMSettingWirelessSecurity
+ * @pairwise: the encryption algorithm to remove, one of "tkip" or "ccmp"
+ *
+ * Removes an encryption algorithm from the allowed pairwise encryption
+ * algorithm list.
+ *
+ * Returns: %TRUE if the encryption algorith was found and removed; %FALSE it it was not.
+ *
+ * Since: 0.9.10
+ **/
+gboolean
+nm_setting_wireless_security_remove_pairwise_by_value (NMSettingWirelessSecurity *setting,
+ const char *pairwise)
+{
+ NMSettingWirelessSecurityPrivate *priv;
+ GSList *iter;
+
+ g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), FALSE);
+ g_return_val_if_fail (pairwise != NULL, FALSE);
+
+ priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting);
+ for (iter = priv->pairwise; iter; iter = g_slist_next (iter)) {
+ if (strcasecmp (pairwise, (char *) iter->data) == 0) {
+ priv->pairwise = g_slist_delete_link (priv->pairwise, iter);
+ g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_SECURITY_PAIRWISE);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/**
+ * nm_setting_wireless_security_clear_pairwise:
+ * @setting: the #NMSettingWirelessSecurity
+ *
+ * Removes all algorithms from the allowed list. If there are no algorithms
+ * specified then all pairwise encryption algorithms are allowed.
+ **/
+void
+nm_setting_wireless_security_clear_pairwise (NMSettingWirelessSecurity *setting)
+{
+ NMSettingWirelessSecurityPrivate *priv;
+
+ g_return_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting));
+
+ priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting);
+ g_slist_free_full (priv->pairwise, g_free);
+ priv->pairwise = NULL;
+ g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_SECURITY_PAIRWISE);
+}
+
+/**
+ * nm_setting_wireless_security_get_num_groups:
+ * @setting: the #NMSettingWirelessSecurity
+ *
+ * Returns: the number of groupwise encryption algorithms in the allowed list
+ **/
+guint32
+nm_setting_wireless_security_get_num_groups (NMSettingWirelessSecurity *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), 0);
+
+ return g_slist_length (NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting)->group);
+}
+
+/**
+ * nm_setting_wireless_security_get_group:
+ * @setting: the #NMSettingWirelessSecurity
+ * @i: index of an item in the allowed groupwise encryption algorithm list
+ *
+ * Returns the allowed groupwise encryption algorithm from allowed algorithm
+ * list.
+ *
+ * Returns: the groupwise encryption algorithm at index @i
+ **/
+const char *
+nm_setting_wireless_security_get_group (NMSettingWirelessSecurity *setting, guint32 i)
+{
+ NMSettingWirelessSecurityPrivate *priv;
+
+ g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), NULL);
+
+ priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting);
+ g_return_val_if_fail (i <= g_slist_length (priv->group), NULL);
+
+ return (const char *) g_slist_nth_data (priv->group, i);
+}
+
+/**
+ * nm_setting_wireless_security_add_group:
+ * @setting: the #NMSettingWirelessSecurity
+ * @group: the encryption algorithm to add, one of "wep40", "wep104",
+ * "tkip", or "ccmp"
+ *
+ * Adds an encryption algorithm to the list of allowed groupwise encryption
+ * algorithms. If the list is not empty, then only access points that support
+ * one or more of the encryption algorithms in the list will be considered
+ * compatible with this connection.
+ *
+ * Returns: %TRUE if the algorithm was added to the list, %FALSE if it was
+ * already in the list
+ **/
+gboolean
+nm_setting_wireless_security_add_group (NMSettingWirelessSecurity *setting, const char *group)
+{
+ NMSettingWirelessSecurityPrivate *priv;
+ GSList *iter;
+
+ g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), FALSE);
+ g_return_val_if_fail (group != NULL, FALSE);
+
+ priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting);
+ for (iter = priv->group; iter; iter = g_slist_next (iter)) {
+ if (strcasecmp (group, (char *) iter->data) == 0)
+ return FALSE;
+ }
+
+ priv->group = g_slist_append (priv->group, g_ascii_strdown (group, -1));
+ g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_SECURITY_GROUP);
+ return TRUE;
+}
+
+/**
+ * nm_setting_wireless_security_remove_group:
+ * @setting: the #NMSettingWirelessSecurity
+ * @i: the index of an item in the allowed groupwise encryption algorithm list
+ *
+ * Removes an encryption algorithm from the allowed groupwise encryption
+ * algorithm list.
+ **/
+void
+nm_setting_wireless_security_remove_group (NMSettingWirelessSecurity *setting, guint32 i)
+{
+ NMSettingWirelessSecurityPrivate *priv;
+ GSList *elt;
+
+ g_return_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting));
+
+ priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting);
+ elt = g_slist_nth (priv->group, i);
+ g_return_if_fail (elt != NULL);
+
+ g_free (elt->data);
+ priv->group = g_slist_delete_link (priv->group, elt);
+ g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_SECURITY_GROUP);
+}
+
+/**
+ * nm_setting_wireless_security_remove_group_by_value:
+ * @setting: the #NMSettingWirelessSecurity
+ * @group: the encryption algorithm to remove, one of "wep40", "wep104",
+ * "tkip", or "ccmp"
+ *
+ * Removes an encryption algorithm from the allowed groupwise encryption
+ * algorithm list.
+ *
+ * Returns: %TRUE if the algorithm was found and removed; %FALSE it it was not.
+ *
+ * Since: 0.9.10
+ **/
+gboolean
+nm_setting_wireless_security_remove_group_by_value (NMSettingWirelessSecurity *setting,
+ const char *group)
+{
+ NMSettingWirelessSecurityPrivate *priv;
+ GSList *iter;
+
+ g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), FALSE);
+ g_return_val_if_fail (group != NULL, FALSE);
+
+ priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting);
+ for (iter = priv->group; iter; iter = g_slist_next (iter)) {
+ if (strcasecmp (group, (char *) iter->data) == 0) {
+ priv->group = g_slist_delete_link (priv->group, iter);
+ g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_SECURITY_GROUP);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/**
+ * nm_setting_wireless_security_clear_groups:
+ * @setting: the #NMSettingWirelessSecurity
+ *
+ * Removes all algorithms from the allowed list. If there are no algorithms
+ * specified then all groupwise encryption algorithms are allowed.
+ **/
+void
+nm_setting_wireless_security_clear_groups (NMSettingWirelessSecurity *setting)
+{
+ NMSettingWirelessSecurityPrivate *priv;
+
+ g_return_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting));
+
+ priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting);
+ g_slist_free_full (priv->group, g_free);
+ priv->group = NULL;
+ g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_SECURITY_GROUP);
+}
+
+/**
+ * nm_setting_wireless_security_get_psk:
+ * @setting: the #NMSettingWirelessSecurity
+ *
+ * Returns: the #NMSettingWirelessSecurity:psk property of the setting
+ **/
+const char *
+nm_setting_wireless_security_get_psk (NMSettingWirelessSecurity *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), NULL);
+
+ return NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting)->psk;
+}
+
+/**
+ * nm_setting_wireless_security_get_psk_flags:
+ * @setting: the #NMSettingWirelessSecurity
+ *
+ * Returns: the #NMSettingSecretFlags pertaining to the
+ * #NMSettingWirelessSecurity:psk
+ **/
+NMSettingSecretFlags
+nm_setting_wireless_security_get_psk_flags (NMSettingWirelessSecurity *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), NM_SETTING_SECRET_FLAG_NONE);
+
+ return NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting)->psk_flags;
+}
+
+/**
+ * nm_setting_wireless_security_get_leap_username:
+ * @setting: the #NMSettingWirelessSecurity
+ *
+ * Returns: the #NMSettingWirelessSecurity:leap-username property of the setting
+ **/
+const char *
+nm_setting_wireless_security_get_leap_username (NMSettingWirelessSecurity *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), NULL);
+
+ return NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting)->leap_username;
+}
+
+/**
+ * nm_setting_wireless_security_get_leap_password:
+ * @setting: the #NMSettingWirelessSecurity
+ *
+ * Returns: the #NMSettingWirelessSecurity:leap-password property of the setting
+ **/
+const char *
+nm_setting_wireless_security_get_leap_password (NMSettingWirelessSecurity *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), NULL);
+
+ return NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting)->leap_password;
+}
+
+/**
+ * nm_setting_wireless_security_get_leap_password_flags:
+ * @setting: the #NMSettingWirelessSecurity
+ *
+ * Returns: the #NMSettingSecretFlags pertaining to the
+ * #NMSettingWirelessSecurity:leap-password
+ **/
+NMSettingSecretFlags
+nm_setting_wireless_security_get_leap_password_flags (NMSettingWirelessSecurity *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), NM_SETTING_SECRET_FLAG_NONE);
+
+ return NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting)->leap_password_flags;
+}
+
+/**
+ * nm_setting_wireless_security_get_wep_key:
+ * @setting: the #NMSettingWirelessSecurity
+ * @idx: the WEP key index (0..3 inclusive)
+ *
+ * Returns: the WEP key at the given index
+ **/
+const char *
+nm_setting_wireless_security_get_wep_key (NMSettingWirelessSecurity *setting, guint32 idx)
+{
+ NMSettingWirelessSecurityPrivate *priv;
+
+ g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), NULL);
+ g_return_val_if_fail (idx < 4, NULL);
+
+ priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting);
+ if (idx == 0)
+ return priv->wep_key0;
+ else if (idx == 1)
+ return priv->wep_key1;
+ else if (idx == 2)
+ return priv->wep_key2;
+ else if (idx == 3)
+ return priv->wep_key3;
+
+ g_assert_not_reached ();
+ return NULL;
+}
+
+/**
+ * nm_setting_wireless_security_set_wep_key:
+ * @setting: the #NMSettingWirelessSecurity
+ * @idx: the index of the key (0..3 inclusive)
+ * @key: the WEP key as a string, in either hexadecimal, ASCII, or passphrase
+ * form as determiend by the value of the #NMSettingWirelessSecurity:wep-key-type
+ * property.
+ *
+ * Sets a WEP key in the given index.
+ **/
+void
+nm_setting_wireless_security_set_wep_key (NMSettingWirelessSecurity *setting, guint32 idx, const char *key)
+{
+ NMSettingWirelessSecurityPrivate *priv;
+
+ g_return_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting));
+ g_return_if_fail (idx < 4);
+
+ priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting);
+ switch (idx) {
+ case 0:
+ g_free (priv->wep_key0);
+ priv->wep_key0 = g_strdup (key);
+ g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_SECURITY_WEP_KEY0);
+ break;
+ case 1:
+ g_free (priv->wep_key1);
+ priv->wep_key1 = g_strdup (key);
+ g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_SECURITY_WEP_KEY1);
+ break;
+ case 2:
+ g_free (priv->wep_key2);
+ priv->wep_key2 = g_strdup (key);
+ g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_SECURITY_WEP_KEY2);
+ break;
+ case 3:
+ g_free (priv->wep_key3);
+ priv->wep_key3 = g_strdup (key);
+ g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_SECURITY_WEP_KEY3);
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+/**
+ * nm_setting_wireless_security_get_wep_tx_keyidx:
+ * @setting: the #NMSettingWirelessSecurity
+ *
+ * Returns: the #NMSettingWirelessSecurity:wep-tx-keyidx property of the setting
+ **/
+guint32
+nm_setting_wireless_security_get_wep_tx_keyidx (NMSettingWirelessSecurity *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), 0);
+
+ return NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting)->wep_tx_keyidx;
+}
+
+/**
+ * nm_setting_wireless_security_get_auth_alg:
+ * @setting: the #NMSettingWirelessSecurity
+ *
+ * Returns: the #NMSettingWirelessSecurity:auth-alg property of the setting
+ **/
+const char *
+nm_setting_wireless_security_get_auth_alg (NMSettingWirelessSecurity *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), NULL);
+
+ return NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting)->auth_alg;
+}
+
+/**
+ * nm_setting_wireless_security_get_wep_key_flags:
+ * @setting: the #NMSettingWirelessSecurity
+ *
+ * Returns: the #NMSettingSecretFlags pertaining to the all WEP keys
+ **/
+NMSettingSecretFlags
+nm_setting_wireless_security_get_wep_key_flags (NMSettingWirelessSecurity *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), NM_SETTING_SECRET_FLAG_NONE);
+
+ return NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting)->wep_key_flags;
+}
+
+/**
+ * nm_setting_wireless_security_get_wep_key_type:
+ * @setting: the #NMSettingWirelessSecurity
+ *
+ * Returns: the #NMSettingWirelessSecurity:wep-key-type property of the setting
+ **/
+NMWepKeyType
+nm_setting_wireless_security_get_wep_key_type (NMSettingWirelessSecurity *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), 0);
+
+ return NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting)->wep_key_type;
+}
+
+static GPtrArray *
+need_secrets (NMSetting *setting)
+{
+ NMSettingWirelessSecurity *self = NM_SETTING_WIRELESS_SECURITY (setting);
+ NMSettingWirelessSecurityPrivate *priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (self);
+ GPtrArray *secrets;
+
+ secrets = g_ptr_array_sized_new (4);
+
+ g_assert (priv->key_mgmt);
+
+ /* Static WEP */
+ if (strcmp (priv->key_mgmt, "none") == 0) {
+ if ((priv->wep_tx_keyidx == 0) && !nm_utils_wep_key_valid (priv->wep_key0, priv->wep_key_type)) {
+ g_ptr_array_add (secrets, NM_SETTING_WIRELESS_SECURITY_WEP_KEY0);
+ return secrets;
+ }
+ if ((priv->wep_tx_keyidx == 1) && !nm_utils_wep_key_valid (priv->wep_key1, priv->wep_key_type)) {
+ g_ptr_array_add (secrets, NM_SETTING_WIRELESS_SECURITY_WEP_KEY1);
+ return secrets;
+ }
+ if ((priv->wep_tx_keyidx == 2) && !nm_utils_wep_key_valid (priv->wep_key2, priv->wep_key_type)) {
+ g_ptr_array_add (secrets, NM_SETTING_WIRELESS_SECURITY_WEP_KEY2);
+ return secrets;
+ }
+ if ((priv->wep_tx_keyidx == 3) && !nm_utils_wep_key_valid (priv->wep_key3, priv->wep_key_type)) {
+ g_ptr_array_add (secrets, NM_SETTING_WIRELESS_SECURITY_WEP_KEY3);
+ return secrets;
+ }
+ goto no_secrets;
+ }
+
+ /* WPA-PSK infrastructure and adhoc */
+ if ( (strcmp (priv->key_mgmt, "wpa-none") == 0)
+ || (strcmp (priv->key_mgmt, "wpa-psk") == 0)) {
+ if (!nm_utils_wpa_psk_valid (priv->psk)) {
+ g_ptr_array_add (secrets, NM_SETTING_WIRELESS_SECURITY_PSK);
+ return secrets;
+ }
+ goto no_secrets;
+ }
+
+ /* LEAP */
+ if ( priv->auth_alg
+ && !strcmp (priv->auth_alg, "leap")
+ && !strcmp (priv->key_mgmt, "ieee8021x")) {
+ if (!priv->leap_password || !strlen (priv->leap_password)) {
+ g_ptr_array_add (secrets, NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD);
+ return secrets;
+ }
+ goto no_secrets;
+ }
+
+ if ( (strcmp (priv->key_mgmt, "ieee8021x") == 0)
+ || (strcmp (priv->key_mgmt, "wpa-eap") == 0)) {
+ /* Let caller check the 802.1x setting for secrets */
+ goto no_secrets;
+ }
+
+ g_assert_not_reached ();
+ return secrets;
+
+no_secrets:
+ if (secrets)
+ g_ptr_array_free (secrets, TRUE);
+ return NULL;
+}
+
+static gboolean
+verify (NMSetting *setting, GSList *all_settings, GError **error)
+{
+ NMSettingWirelessSecurity *self = NM_SETTING_WIRELESS_SECURITY (setting);
+ NMSettingWirelessSecurityPrivate *priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (self);
+ const char *valid_key_mgmt[] = { "none", "ieee8021x", "wpa-none", "wpa-psk", "wpa-eap", NULL };
+ const char *valid_auth_algs[] = { "open", "shared", "leap", NULL };
+ const char *valid_protos[] = { "wpa", "rsn", NULL };
+ const char *valid_pairwise[] = { "tkip", "ccmp", NULL };
+ const char *valid_groups[] = { "wep40", "wep104", "tkip", "ccmp", NULL };
+
+ if (!priv->key_mgmt) {
+ g_set_error_literal (error,
+ NM_SETTING_WIRELESS_SECURITY_ERROR,
+ NM_SETTING_WIRELESS_SECURITY_ERROR_MISSING_PROPERTY,
+ _("property is missing"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT);
+ return FALSE;
+ }
+
+ if (!_nm_utils_string_in_list (priv->key_mgmt, valid_key_mgmt)) {
+ g_set_error (error,
+ NM_SETTING_WIRELESS_SECURITY_ERROR,
+ NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+ _("'%s' is not a valid value for the property"),
+ priv->key_mgmt);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT);
+ return FALSE;
+ }
+
+ if (priv->auth_alg && !strcmp (priv->auth_alg, "leap")) {
+ /* LEAP must use ieee8021x key management */
+ if (strcmp (priv->key_mgmt, "ieee8021x")) {
+ g_set_error (error,
+ NM_SETTING_WIRELESS_SECURITY_ERROR,
+ NM_SETTING_WIRELESS_SECURITY_ERROR_LEAP_REQUIRES_802_1X,
+ _("'%s' security requires '%s=%s'"),
+ "leap", NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x");
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, NM_SETTING_WIRELESS_SECURITY_AUTH_ALG);
+ return FALSE;
+ }
+ if (!priv->leap_username) {
+ g_set_error_literal (error,
+ NM_SETTING_WIRELESS_SECURITY_ERROR,
+ NM_SETTING_WIRELESS_SECURITY_ERROR_LEAP_REQUIRES_USERNAME,
+ _("property is empty"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME);
+ return FALSE;
+ }
+ if (priv->leap_password && !strlen (priv->leap_password)) {
+ g_set_error_literal (error,
+ NM_SETTING_WIRELESS_SECURITY_ERROR,
+ NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+ _("property is empty"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD);
+ return FALSE;
+ }
+ } else {
+ if ( (strcmp (priv->key_mgmt, "ieee8021x") == 0)
+ || (strcmp (priv->key_mgmt, "wpa-eap") == 0)) {
+ /* Need an 802.1x setting too */
+ if (!nm_setting_find_in_list (all_settings, NM_SETTING_802_1X_SETTING_NAME)) {
+ g_set_error (error,
+ NM_SETTING_WIRELESS_SECURITY_ERROR,
+ NM_SETTING_WIRELESS_SECURITY_ERROR_MISSING_802_1X_SETTING,
+ _("'%s' security requires '%s' setting presence"),
+ priv->key_mgmt, NM_SETTING_802_1X_SETTING_NAME);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT);
+ return FALSE;
+ }
+ }
+ }
+
+ if (priv->leap_username && !strlen (priv->leap_username)) {
+ g_set_error_literal (error,
+ NM_SETTING_WIRELESS_SECURITY_ERROR,
+ NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+ _("property is empty"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME);
+ return FALSE;
+ }
+
+ if (priv->wep_tx_keyidx > 3) {
+ g_set_error (error,
+ NM_SETTING_WIRELESS_SECURITY_ERROR,
+ NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+ _("'%d' value is out of range <0-3>"),
+ priv->wep_tx_keyidx);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, NM_SETTING_WIRELESS_SECURITY_WEP_TX_KEYIDX);
+ return FALSE;
+ }
+
+ if (priv->wep_key_type > NM_WEP_KEY_TYPE_LAST) {
+ g_set_error_literal (error,
+ NM_SETTING_WIRELESS_SECURITY_ERROR,
+ NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+ _("property is invalid"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE);
+ return FALSE;
+ }
+
+ if (priv->wep_key0 && !nm_utils_wep_key_valid (priv->wep_key0, priv->wep_key_type)) {
+ g_set_error_literal (error,
+ NM_SETTING_WIRELESS_SECURITY_ERROR,
+ NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+ _("property is invalid"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, NM_SETTING_WIRELESS_SECURITY_WEP_KEY0);
+ return FALSE;
+ }
+ if (priv->wep_key1 && !nm_utils_wep_key_valid (priv->wep_key1, priv->wep_key_type)) {
+ g_set_error_literal (error,
+ NM_SETTING_WIRELESS_SECURITY_ERROR,
+ NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+ _("property is invalid"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, NM_SETTING_WIRELESS_SECURITY_WEP_KEY1);
+ return FALSE;
+ }
+ if (priv->wep_key2 && !nm_utils_wep_key_valid (priv->wep_key2, priv->wep_key_type)) {
+ g_set_error_literal (error,
+ NM_SETTING_WIRELESS_SECURITY_ERROR,
+ NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+ _("property is invalid"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, NM_SETTING_WIRELESS_SECURITY_WEP_KEY2);
+ return FALSE;
+ }
+ if (priv->wep_key3 && !nm_utils_wep_key_valid (priv->wep_key3, priv->wep_key_type)) {
+ g_set_error_literal (error,
+ NM_SETTING_WIRELESS_SECURITY_ERROR,
+ NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+ _("property is invalid"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, NM_SETTING_WIRELESS_SECURITY_WEP_KEY3);
+ return FALSE;
+ }
+
+ if (priv->auth_alg && !_nm_utils_string_in_list (priv->auth_alg, valid_auth_algs)) {
+ g_set_error_literal (error,
+ NM_SETTING_WIRELESS_SECURITY_ERROR,
+ NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+ _("property is invalid"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, NM_SETTING_WIRELESS_SECURITY_AUTH_ALG);
+ return FALSE;
+ }
+
+ if (priv->psk && !nm_utils_wpa_psk_valid (priv->psk)) {
+ g_set_error_literal (error,
+ NM_SETTING_WIRELESS_SECURITY_ERROR,
+ NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+ _("property is invalid"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, NM_SETTING_WIRELESS_SECURITY_PSK);
+ return FALSE;
+ }
+
+ if (priv->proto && !_nm_utils_string_slist_validate (priv->proto, valid_protos)) {
+ g_set_error_literal (error,
+ NM_SETTING_WIRELESS_SECURITY_ERROR,
+ NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+ _("property is invalid"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, NM_SETTING_WIRELESS_SECURITY_PROTO);
+ return FALSE;
+ }
+
+ if (priv->pairwise) {
+ const char *wpa_none[] = { "wpa-none", NULL };
+
+ /* For ad-hoc connections, pairwise must be "none" */
+ if (_nm_utils_string_in_list (priv->key_mgmt, wpa_none)) {
+ GSList *iter;
+ gboolean found = FALSE;
+
+ for (iter = priv->pairwise; iter; iter = g_slist_next (iter)) {
+ if (!strcmp ((char *) iter->data, "none")) {
+ found = TRUE;
+ break;
+ }
+ }
+
+ /* pairwise cipher list didn't contain "none", which is invalid
+ * for WPA adhoc connections.
+ */
+ if (!found) {
+ g_set_error (error,
+ NM_SETTING_WIRELESS_SECURITY_ERROR,
+ NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+ _("'%s' connections require '%s' in this property"),
+ NM_SETTING_WIRELESS_MODE_ADHOC, "none");
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, NM_SETTING_WIRELESS_SECURITY_PAIRWISE);
+ return FALSE;
+ }
+ } else if (!_nm_utils_string_slist_validate (priv->pairwise, valid_pairwise)) {
+ g_set_error_literal (error,
+ NM_SETTING_WIRELESS_SECURITY_ERROR,
+ NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+ _("property is invalid"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, NM_SETTING_WIRELESS_SECURITY_PAIRWISE);
+ return FALSE;
+ }
+ }
+
+ if (priv->group && !_nm_utils_string_slist_validate (priv->group, valid_groups)) {
+ g_set_error_literal (error,
+ NM_SETTING_WIRELESS_SECURITY_ERROR,
+ NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+ _("property is invalid"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, NM_SETTING_WIRELESS_SECURITY_GROUP);
+ return FALSE;
+ }
+
+ /* Shared Key auth can only be used with WEP */
+ if (priv->auth_alg && !strcmp (priv->auth_alg, "shared")) {
+ if (priv->key_mgmt && strcmp (priv->key_mgmt, "none")) {
+ g_set_error (error,
+ NM_SETTING_WIRELESS_SECURITY_ERROR,
+ NM_SETTING_WIRELESS_SECURITY_ERROR_SHARED_KEY_REQUIRES_WEP,
+ _("'%s' can only be used with '%s=%s' (WEP)"),
+ "shared", NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "none");
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, NM_SETTING_WIRELESS_SECURITY_AUTH_ALG);
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean
+get_secret_flags (NMSetting *setting,
+ const char *secret_name,
+ gboolean verify_secret,
+ NMSettingSecretFlags *out_flags,
+ GError **error)
+{
+ NMSettingClass *setting_class;
+ gboolean verify_override = verify_secret;
+
+ /* There's only one 'flags' property for WEP keys, so alias all the WEP key
+ * property names to that flags property.
+ */
+ if ( !g_strcmp0 (secret_name, NM_SETTING_WIRELESS_SECURITY_WEP_KEY0)
+ || !g_strcmp0 (secret_name, NM_SETTING_WIRELESS_SECURITY_WEP_KEY1)
+ || !g_strcmp0 (secret_name, NM_SETTING_WIRELESS_SECURITY_WEP_KEY2)
+ || !g_strcmp0 (secret_name, NM_SETTING_WIRELESS_SECURITY_WEP_KEY3)) {
+ secret_name = "wep-key";
+ verify_override = FALSE; /* Already know it's a secret */
+ }
+
+ /* Chain up to superclass with modified key name */
+ setting_class = NM_SETTING_CLASS (nm_setting_wireless_security_parent_class);
+ return setting_class->get_secret_flags (setting, secret_name, verify_override, out_flags, error);
+}
+
+static gboolean
+set_secret_flags (NMSetting *setting,
+ const char *secret_name,
+ gboolean verify_secret,
+ NMSettingSecretFlags flags,
+ GError **error)
+{
+ NMSettingClass *setting_class;
+ gboolean verify_override = verify_secret;
+
+ /* There's only one 'flags' property for WEP keys, so alias all the WEP key
+ * property names to that flags property.
+ */
+ if ( !g_strcmp0 (secret_name, NM_SETTING_WIRELESS_SECURITY_WEP_KEY0)
+ || !g_strcmp0 (secret_name, NM_SETTING_WIRELESS_SECURITY_WEP_KEY1)
+ || !g_strcmp0 (secret_name, NM_SETTING_WIRELESS_SECURITY_WEP_KEY2)
+ || !g_strcmp0 (secret_name, NM_SETTING_WIRELESS_SECURITY_WEP_KEY3)) {
+ secret_name = "wep-key";
+ verify_override = FALSE; /* Already know it's a secret */
+ }
+
+ /* Chain up to superclass with modified key name */
+ setting_class = NM_SETTING_CLASS (nm_setting_wireless_security_parent_class);
+ return setting_class->set_secret_flags (setting, secret_name, verify_override, flags, error);
+}
+
+static void
+nm_setting_wireless_security_init (NMSettingWirelessSecurity *setting)
+{
+}
+
+static void
+finalize (GObject *object)
+{
+ NMSettingWirelessSecurity *self = NM_SETTING_WIRELESS_SECURITY (object);
+ NMSettingWirelessSecurityPrivate *priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (self);
+
+ /* Strings first. g_free() already checks for NULLs so we don't have to */
+
+ g_free (priv->key_mgmt);
+ g_free (priv->auth_alg);
+ g_free (priv->leap_username);
+ g_free (priv->wep_key0);
+ g_free (priv->wep_key1);
+ g_free (priv->wep_key2);
+ g_free (priv->wep_key3);
+ g_free (priv->psk);
+ g_free (priv->leap_password);
+
+ g_slist_free_full (priv->proto, g_free);
+ g_slist_free_full (priv->pairwise, g_free);
+ g_slist_free_full (priv->group, g_free);
+
+ G_OBJECT_CLASS (nm_setting_wireless_security_parent_class)->finalize (object);
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ NMSettingWirelessSecurity *setting = NM_SETTING_WIRELESS_SECURITY (object);
+ NMSettingWirelessSecurityPrivate *priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting);
+ const char *str;
+
+ switch (prop_id) {
+ case PROP_KEY_MGMT:
+ g_free (priv->key_mgmt);
+ str = g_value_get_string (value);
+ priv->key_mgmt = str ? g_ascii_strdown (str, -1) : NULL;
+ break;
+ case PROP_WEP_TX_KEYIDX:
+ priv->wep_tx_keyidx = g_value_get_uint (value);
+ break;
+ case PROP_AUTH_ALG:
+ g_free (priv->auth_alg);
+ str = g_value_get_string (value);
+ priv->auth_alg = str ? g_ascii_strdown (str, -1) : NULL;
+ break;
+ case PROP_PROTO:
+ g_slist_free_full (priv->proto, g_free);
+ priv->proto = g_value_dup_boxed (value);
+ break;
+ case PROP_PAIRWISE:
+ g_slist_free_full (priv->pairwise, g_free);
+ priv->pairwise = g_value_dup_boxed (value);
+ break;
+ case PROP_GROUP:
+ g_slist_free_full (priv->group, g_free);
+ priv->group = g_value_dup_boxed (value);
+ break;
+ case PROP_LEAP_USERNAME:
+ g_free (priv->leap_username);
+ priv->leap_username = g_value_dup_string (value);
+ break;
+ case PROP_WEP_KEY0:
+ g_free (priv->wep_key0);
+ priv->wep_key0 = g_value_dup_string (value);
+ break;
+ case PROP_WEP_KEY1:
+ g_free (priv->wep_key1);
+ priv->wep_key1 = g_value_dup_string (value);
+ break;
+ case PROP_WEP_KEY2:
+ g_free (priv->wep_key2);
+ priv->wep_key2 = g_value_dup_string (value);
+ break;
+ case PROP_WEP_KEY3:
+ g_free (priv->wep_key3);
+ priv->wep_key3 = g_value_dup_string (value);
+ break;
+ case PROP_WEP_KEY_FLAGS:
+ priv->wep_key_flags = g_value_get_uint (value);
+ break;
+ case PROP_PSK:
+ g_free (priv->psk);
+ priv->psk = g_value_dup_string (value);
+ break;
+ case PROP_PSK_FLAGS:
+ priv->psk_flags = g_value_get_uint (value);
+ break;
+ case PROP_LEAP_PASSWORD:
+ g_free (priv->leap_password);
+ priv->leap_password = g_value_dup_string (value);
+ break;
+ case PROP_LEAP_PASSWORD_FLAGS:
+ priv->leap_password_flags = g_value_get_uint (value);
+ break;
+ case PROP_WEP_KEY_TYPE:
+ priv->wep_key_type = g_value_get_uint (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ NMSettingWirelessSecurity *setting = NM_SETTING_WIRELESS_SECURITY (object);
+ NMSettingWirelessSecurityPrivate *priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting);
+
+ switch (prop_id) {
+ case PROP_KEY_MGMT:
+ g_value_set_string (value, priv->key_mgmt);
+ break;
+ case PROP_WEP_TX_KEYIDX:
+ g_value_set_uint (value, priv->wep_tx_keyidx);
+ break;
+ case PROP_AUTH_ALG:
+ g_value_set_string (value, priv->auth_alg);
+ break;
+ case PROP_PROTO:
+ g_value_set_boxed (value, priv->proto);
+ break;
+ case PROP_PAIRWISE:
+ g_value_set_boxed (value, priv->pairwise);
+ break;
+ case PROP_GROUP:
+ g_value_set_boxed (value, priv->group);
+ break;
+ case PROP_LEAP_USERNAME:
+ g_value_set_string (value, priv->leap_username);
+ break;
+ case PROP_WEP_KEY0:
+ g_value_set_string (value, priv->wep_key0);
+ break;
+ case PROP_WEP_KEY1:
+ g_value_set_string (value, priv->wep_key1);
+ break;
+ case PROP_WEP_KEY2:
+ g_value_set_string (value, priv->wep_key2);
+ break;
+ case PROP_WEP_KEY3:
+ g_value_set_string (value, priv->wep_key3);
+ break;
+ case PROP_WEP_KEY_FLAGS:
+ g_value_set_uint (value, priv->wep_key_flags);
+ break;
+ case PROP_PSK:
+ g_value_set_string (value, priv->psk);
+ break;
+ case PROP_PSK_FLAGS:
+ g_value_set_uint (value, priv->psk_flags);
+ break;
+ case PROP_LEAP_PASSWORD:
+ g_value_set_string (value, priv->leap_password);
+ break;
+ case PROP_LEAP_PASSWORD_FLAGS:
+ g_value_set_uint (value, priv->leap_password_flags);
+ break;
+ case PROP_WEP_KEY_TYPE:
+ g_value_set_uint (value, priv->wep_key_type);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nm_setting_wireless_security_class_init (NMSettingWirelessSecurityClass *setting_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
+ NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+
+ g_type_class_add_private (setting_class, sizeof (NMSettingWirelessSecurityPrivate));
+
+ /* virtual methods */
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+ object_class->finalize = finalize;
+
+ parent_class->verify = verify;
+ parent_class->need_secrets = need_secrets;
+ parent_class->get_secret_flags = get_secret_flags;
+ parent_class->set_secret_flags = set_secret_flags;
+
+ /* Properties */
+ /**
+ * NMSettingWirelessSecurity:key-mgmt:
+ *
+ * Key management used for the connection. One of "none" (WEP), "ieee8021x"
+ * (Dynamic WEP), "wpa-none" (Ad-Hoc WPA-PSK), "wpa-psk" (infrastructure
+ * WPA-PSK), or "wpa-eap" (WPA-Enterprise). This property must be set for
+ * any Wi-Fi connection that uses security.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_KEY_MGMT,
+ g_param_spec_string (NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ NM_SETTING_PARAM_REQUIRED |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingWirelessSecurity:wep-tx-keyidx:
+ *
+ * When static WEP is used (ie, key-mgmt = "none") and a non-default WEP key
+ * index is used by the AP, put that WEP key index here. Valid values are 0
+ * (default key) through 3. Note that some consumer access points (like the
+ * Linksys WRT54G) number the keys 1 - 4.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_WEP_TX_KEYIDX,
+ g_param_spec_uint (NM_SETTING_WIRELESS_SECURITY_WEP_TX_KEYIDX, "", "",
+ 0, 3, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingWirelessSecurity:auth-alg:
+ *
+ * When WEP is used (ie, key-mgmt = "none" or "ieee8021x") indicate the
+ * 802.11 authentication algorithm required by the AP here. One of "open"
+ * for Open System, "shared" for Shared Key, or "leap" for Cisco LEAP. When
+ * using Cisco LEAP (ie, key-mgmt = "ieee8021x" and auth-alg = "leap") the
+ * "leap-username" and "leap-password" properties must be specified.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_AUTH_ALG,
+ g_param_spec_string (NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingWirelessSecurity:proto:
+ *
+ * List of strings specifying the allowed WPA protocol versions to use.
+ * Each element may be one "wpa" (allow WPA) or "rsn" (allow WPA2/RSN). If
+ * not specified, both WPA and RSN connections are allowed.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PROTO,
+ _nm_param_spec_specialized (NM_SETTING_WIRELESS_SECURITY_PROTO, "", "",
+ DBUS_TYPE_G_LIST_OF_STRING,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingWirelessSecurity:pairwise:
+ *
+ * A list of pairwise encryption algorithms which prevents connections to
+ * Wi-Fi networks that do not utilize one of the algorithms in the list.
+ * For maximum compatibility leave this property empty. Each list element
+ * may be one of "tkip" or "ccmp".
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PAIRWISE,
+ _nm_param_spec_specialized (NM_SETTING_WIRELESS_SECURITY_PAIRWISE, "", "",
+ DBUS_TYPE_G_LIST_OF_STRING,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingWirelessSecurity:group:
+ *
+ * A list of group/broadcast encryption algorithms which prevents
+ * connections to Wi-Fi networks that do not utilize one of the algorithms
+ * in the list. For maximum compatibility leave this property empty. Each
+ * list element may be one of "wep40", "wep104", "tkip", or "ccmp".
+ **/
+ g_object_class_install_property
+ (object_class, PROP_GROUP,
+ _nm_param_spec_specialized (NM_SETTING_WIRELESS_SECURITY_GROUP, "", "",
+ DBUS_TYPE_G_LIST_OF_STRING,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingWirelessSecurity:leap-username:
+ *
+ * The login username for legacy LEAP connections (ie, key-mgmt =
+ * "ieee8021x" and auth-alg = "leap").
+ **/
+ g_object_class_install_property
+ (object_class, PROP_LEAP_USERNAME,
+ g_param_spec_string (NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingWirelessSecurity:wep-key0:
+ *
+ * Index 0 WEP key. This is the WEP key used in most networks. See the
+ * "wep-key-type" property for a description of how this key is interpreted.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_WEP_KEY0,
+ g_param_spec_string (NM_SETTING_WIRELESS_SECURITY_WEP_KEY0, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ NM_SETTING_PARAM_SECRET |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingWirelessSecurity:wep-key1:
+ *
+ * Index 1 WEP key. This WEP index is not used by most networks. See the
+ * "wep-key-type" property for a description of how this key is interpreted.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_WEP_KEY1,
+ g_param_spec_string (NM_SETTING_WIRELESS_SECURITY_WEP_KEY1, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ NM_SETTING_PARAM_SECRET |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingWirelessSecurity:wep-key2:
+ *
+ * Index 2 WEP key. This WEP index is not used by most networks. See the
+ * "wep-key-type" property for a description of how this key is interpreted.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_WEP_KEY2,
+ g_param_spec_string (NM_SETTING_WIRELESS_SECURITY_WEP_KEY2, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ NM_SETTING_PARAM_SECRET |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingWirelessSecurity:wep-key3:
+ *
+ * Index 3 WEP key. This WEP index is not used by most networks. See the
+ * "wep-key-type" property for a description of how this key is interpreted.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_WEP_KEY3,
+ g_param_spec_string (NM_SETTING_WIRELESS_SECURITY_WEP_KEY3, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ NM_SETTING_PARAM_SECRET |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingWirelessSecurity:wep-key-flags:
+ *
+ * Flags indicating how to handle the #NMSettingWirelessSecurity:wep-key0,
+ * #NMSettingWirelessSecurity:wep-key1, #NMSettingWirelessSecurity:wep-key2,
+ * and #NMSettingWirelessSecurity:wep-key3 properties.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_WEP_KEY_FLAGS,
+ g_param_spec_uint (NM_SETTING_WIRELESS_SECURITY_WEP_KEY_FLAGS, "", "",
+ NM_SETTING_SECRET_FLAG_NONE,
+ NM_SETTING_SECRET_FLAGS_ALL,
+ NM_SETTING_SECRET_FLAG_NONE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingWirelessSecurity:psk:
+ *
+ * Pre-Shared-Key for WPA networks. If the key is 64-characters long, it
+ * must contain only hexadecimal characters and is interpreted as a
+ * hexadecimal WPA key. Otherwise, the key must be between 8 and 63 ASCII
+ * characters (as specified in the 802.11i standard) and is interpreted as a
+ * WPA passphrase, and is hashed to derive the actual WPA-PSK used when
+ * connecting to the Wi-Fi network.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PSK,
+ g_param_spec_string (NM_SETTING_WIRELESS_SECURITY_PSK, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ NM_SETTING_PARAM_SECRET |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingWirelessSecurity:psk-flags:
+ *
+ * Flags indicating how to handle the #NMSettingWirelessSecurity:psk
+ * property.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PSK_FLAGS,
+ g_param_spec_uint (NM_SETTING_WIRELESS_SECURITY_PSK_FLAGS, "", "",
+ NM_SETTING_SECRET_FLAG_NONE,
+ NM_SETTING_SECRET_FLAGS_ALL,
+ NM_SETTING_SECRET_FLAG_NONE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingWirelessSecurity:leap-password:
+ *
+ * The login password for legacy LEAP connections (ie, key-mgmt =
+ * "ieee8021x" and auth-alg = "leap").
+ **/
+ g_object_class_install_property
+ (object_class, PROP_LEAP_PASSWORD,
+ g_param_spec_string (NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ NM_SETTING_PARAM_SECRET |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingWirelessSecurity:leap-password-flags:
+ *
+ * Flags indicating how to handle the
+ * #NMSettingWirelessSecurity:leap-password property.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_LEAP_PASSWORD_FLAGS,
+ g_param_spec_uint (NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD_FLAGS, "", "",
+ NM_SETTING_SECRET_FLAG_NONE,
+ NM_SETTING_SECRET_FLAGS_ALL,
+ NM_SETTING_SECRET_FLAG_NONE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingWirelessSecurity:wep-key-type:
+ *
+ * Controls the interpretation of WEP keys. Allowed values are
+ * %NM_WEP_KEY_TYPE_KEY, in which case the key is either a 10- or
+ * 26-character hexadecimal string, or a 5- or 13-character ASCII password;
+ * or %NM_WEP_KEY_TYPE_PASSPHRASE, in which case the passphrase is provided
+ * as a string and will be hashed using the de-facto MD5 method to derive
+ * the actual WEP key.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_WEP_KEY_TYPE,
+ g_param_spec_uint (NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, "", "",
+ NM_WEP_KEY_TYPE_UNKNOWN,
+ NM_WEP_KEY_TYPE_LAST,
+ NM_WEP_KEY_TYPE_UNKNOWN,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/libnm-core/nm-setting-wireless-security.h b/libnm-core/nm-setting-wireless-security.h
new file mode 100644
index 0000000000..97a456b048
--- /dev/null
+++ b/libnm-core/nm-setting-wireless-security.h
@@ -0,0 +1,178 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2014 Red Hat, Inc.
+ * Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#ifndef NM_SETTING_WIRELESS_SECURITY_H
+#define NM_SETTING_WIRELESS_SECURITY_H
+
+#include <nm-setting.h>
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_SETTING_WIRELESS_SECURITY (nm_setting_wireless_security_get_type ())
+#define NM_SETTING_WIRELESS_SECURITY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_WIRELESS_SECURITY, NMSettingWirelessSecurity))
+#define NM_SETTING_WIRELESS_SECURITY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_WIRELESS_SECURITY, NMSettingWirelessSecurityClass))
+#define NM_IS_SETTING_WIRELESS_SECURITY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_WIRELESS_SECURITY))
+#define NM_IS_SETTING_WIRELESS_SECURITY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_WIRELESS_SECURITY))
+#define NM_SETTING_WIRELESS_SECURITY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_WIRELESS_SECURITY, NMSettingWirelessSecurityClass))
+
+#define NM_SETTING_WIRELESS_SECURITY_SETTING_NAME "802-11-wireless-security"
+
+/**
+ * NMSettingWirelessSecurityError:
+ * @NM_SETTING_WIRELESS_SECURITY_ERROR_UNKNOWN: unknown or unclassified error
+ * @NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY: the property was invalid
+ * @NM_SETTING_WIRELESS_SECURITY_ERROR_MISSING_PROPERTY: the property was
+ * missing and is required
+ * @NM_SETTING_WIRELESS_SECURITY_ERROR_MISSING_802_1X_SETTING: a property contained
+ * a value that requires the connection to contain a #NMSetting8021x setting
+ * @NM_SETTING_WIRELESS_SECURITY_ERROR_LEAP_REQUIRES_802_1X: LEAP authentication
+ * was specified but key management was not set to "8021x"
+ * @NM_SETTING_WIRELESS_SECURITY_ERROR_LEAP_REQUIRES_USERNAME: LEAP authentication
+ * was specified but no LEAP username was given
+ * @NM_SETTING_WIRELESS_SECURITY_ERROR_SHARED_KEY_REQUIRES_WEP: Shared Key
+ * authentication was specified but the setting did not specify WEP as the
+ * encryption protocol
+ */
+typedef enum {
+ NM_SETTING_WIRELESS_SECURITY_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/
+ NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, /*< nick=InvalidProperty >*/
+ NM_SETTING_WIRELESS_SECURITY_ERROR_MISSING_PROPERTY, /*< nick=MissingProperty >*/
+ NM_SETTING_WIRELESS_SECURITY_ERROR_MISSING_802_1X_SETTING, /*< nick=Missing8021xSetting >*/
+ NM_SETTING_WIRELESS_SECURITY_ERROR_LEAP_REQUIRES_802_1X, /*< nick=LEAPRequires8021x >*/
+ NM_SETTING_WIRELESS_SECURITY_ERROR_LEAP_REQUIRES_USERNAME, /*< nick=LEAPRequiresUsername >*/
+ NM_SETTING_WIRELESS_SECURITY_ERROR_SHARED_KEY_REQUIRES_WEP /*< nick=SharedKeyRequiresWEP >*/
+} NMSettingWirelessSecurityError;
+
+#define NM_SETTING_WIRELESS_SECURITY_ERROR nm_setting_wireless_security_error_quark ()
+GQuark nm_setting_wireless_security_error_quark (void);
+
+/**
+ * NMWepKeyType:
+ * @NM_WEP_KEY_TYPE_UNKNOWN: unknown WEP key type
+ * @NM_WEP_KEY_TYPE_KEY: indicates a hexadecimal or ASCII formatted WEP key.
+ * Hex keys are either 10 or 26 hexadecimal characters (ie "5f782f2f5f" or
+ * "732f2d712e4a394a375d366931"), while ASCII keys are either 5 or 13 ASCII
+ * characters (ie "abcde" or "blahblah99$*1").
+ * @NM_WEP_KEY_TYPE_PASSPHRASE: indicates a WEP passphrase (ex "I bought a duck
+ * on my way back from the market 235Q&^%^*%") instead of a hexadecimal or ASCII
+ * key. Passphrases are between 8 and 64 characters inclusive and are hashed
+ * the actual WEP key using the MD5 hash algorithm.
+ * @NM_WEP_KEY_TYPE_LAST: placeholder value for bounds-checking
+ *
+ * The #NMWepKeyType values specify how any WEP keys present in the setting
+ * are intepreted. There are no standards governing how to hash the various WEP
+ * key/passphrase formats into the actual WEP key. Unfortunately some WEP keys
+ * can be interpreted in multiple ways, requring the setting to specify how to
+ * interpret the any WEP keys. For example, the key "732f2d712e4a394a375d366931"
+ * is both a valid Hexadecimal WEP key and a WEP passphrase. Further, many
+ * ASCII keys are also valid WEP passphrases, but since passphrases and ASCII
+ * keys are hashed differently to determine the actual WEP key the type must be
+ * specified.
+ */
+typedef enum {
+ NM_WEP_KEY_TYPE_UNKNOWN = 0,
+ NM_WEP_KEY_TYPE_KEY = 1, /* Hex or ASCII */
+ NM_WEP_KEY_TYPE_PASSPHRASE = 2, /* 104/128-bit Passphrase */
+
+ NM_WEP_KEY_TYPE_LAST = NM_WEP_KEY_TYPE_PASSPHRASE
+} NMWepKeyType;
+
+#define NM_SETTING_WIRELESS_SECURITY_KEY_MGMT "key-mgmt"
+#define NM_SETTING_WIRELESS_SECURITY_WEP_TX_KEYIDX "wep-tx-keyidx"
+#define NM_SETTING_WIRELESS_SECURITY_AUTH_ALG "auth-alg"
+#define NM_SETTING_WIRELESS_SECURITY_PROTO "proto"
+#define NM_SETTING_WIRELESS_SECURITY_PAIRWISE "pairwise"
+#define NM_SETTING_WIRELESS_SECURITY_GROUP "group"
+#define NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME "leap-username"
+#define NM_SETTING_WIRELESS_SECURITY_WEP_KEY0 "wep-key0"
+#define NM_SETTING_WIRELESS_SECURITY_WEP_KEY1 "wep-key1"
+#define NM_SETTING_WIRELESS_SECURITY_WEP_KEY2 "wep-key2"
+#define NM_SETTING_WIRELESS_SECURITY_WEP_KEY3 "wep-key3"
+#define NM_SETTING_WIRELESS_SECURITY_WEP_KEY_FLAGS "wep-key-flags"
+#define NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE "wep-key-type"
+#define NM_SETTING_WIRELESS_SECURITY_PSK "psk"
+#define NM_SETTING_WIRELESS_SECURITY_PSK_FLAGS "psk-flags"
+#define NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD "leap-password"
+#define NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD_FLAGS "leap-password-flags"
+
+typedef struct {
+ NMSetting parent;
+} NMSettingWirelessSecurity;
+
+typedef struct {
+ NMSettingClass parent;
+
+ /* Padding for future expansion */
+ void (*_reserved1) (void);
+ void (*_reserved2) (void);
+ void (*_reserved3) (void);
+ void (*_reserved4) (void);
+} NMSettingWirelessSecurityClass;
+
+GType nm_setting_wireless_security_get_type (void);
+
+NMSetting * nm_setting_wireless_security_new (void);
+
+const char *nm_setting_wireless_security_get_key_mgmt (NMSettingWirelessSecurity *setting);
+
+guint32 nm_setting_wireless_security_get_num_protos (NMSettingWirelessSecurity *setting);
+const char *nm_setting_wireless_security_get_proto (NMSettingWirelessSecurity *setting, guint32 i);
+gboolean nm_setting_wireless_security_add_proto (NMSettingWirelessSecurity *setting, const char *proto);
+void nm_setting_wireless_security_remove_proto (NMSettingWirelessSecurity *setting, guint32 i);
+NM_AVAILABLE_IN_0_9_10
+gboolean nm_setting_wireless_security_remove_proto_by_value (NMSettingWirelessSecurity *setting, const char *proto);
+void nm_setting_wireless_security_clear_protos (NMSettingWirelessSecurity *setting);
+
+guint32 nm_setting_wireless_security_get_num_pairwise (NMSettingWirelessSecurity *setting);
+const char *nm_setting_wireless_security_get_pairwise (NMSettingWirelessSecurity *setting, guint32 i);
+gboolean nm_setting_wireless_security_add_pairwise (NMSettingWirelessSecurity *setting, const char *pairwise);
+void nm_setting_wireless_security_remove_pairwise (NMSettingWirelessSecurity *setting, guint32 i);
+NM_AVAILABLE_IN_0_9_10
+gboolean nm_setting_wireless_security_remove_pairwise_by_value (NMSettingWirelessSecurity *setting, const char *pairwise);
+void nm_setting_wireless_security_clear_pairwise (NMSettingWirelessSecurity *setting);
+
+guint32 nm_setting_wireless_security_get_num_groups (NMSettingWirelessSecurity *setting);
+const char *nm_setting_wireless_security_get_group (NMSettingWirelessSecurity *setting, guint32 i);
+gboolean nm_setting_wireless_security_add_group (NMSettingWirelessSecurity *setting, const char *group);
+void nm_setting_wireless_security_remove_group (NMSettingWirelessSecurity *setting, guint32 i);
+NM_AVAILABLE_IN_0_9_10
+gboolean nm_setting_wireless_security_remove_group_by_value (NMSettingWirelessSecurity *setting, const char *group);
+void nm_setting_wireless_security_clear_groups (NMSettingWirelessSecurity *setting);
+
+const char *nm_setting_wireless_security_get_psk (NMSettingWirelessSecurity *setting);
+NMSettingSecretFlags nm_setting_wireless_security_get_psk_flags (NMSettingWirelessSecurity *setting);
+
+const char *nm_setting_wireless_security_get_leap_username (NMSettingWirelessSecurity *setting);
+const char *nm_setting_wireless_security_get_leap_password (NMSettingWirelessSecurity *setting);
+NMSettingSecretFlags nm_setting_wireless_security_get_leap_password_flags (NMSettingWirelessSecurity *setting);
+
+const char *nm_setting_wireless_security_get_wep_key (NMSettingWirelessSecurity *setting, guint32 idx);
+void nm_setting_wireless_security_set_wep_key (NMSettingWirelessSecurity *setting, guint32 idx, const char *key);
+guint32 nm_setting_wireless_security_get_wep_tx_keyidx (NMSettingWirelessSecurity *setting);
+const char *nm_setting_wireless_security_get_auth_alg (NMSettingWirelessSecurity *setting);
+
+NMSettingSecretFlags nm_setting_wireless_security_get_wep_key_flags (NMSettingWirelessSecurity *setting);
+NMWepKeyType nm_setting_wireless_security_get_wep_key_type (NMSettingWirelessSecurity *setting);
+
+G_END_DECLS
+
+#endif /* NM_SETTING_WIRELESS_SECURITY_H */
diff --git a/libnm-core/nm-setting-wireless.c b/libnm-core/nm-setting-wireless.c
new file mode 100644
index 0000000000..24fae397b3
--- /dev/null
+++ b/libnm-core/nm-setting-wireless.c
@@ -0,0 +1,1245 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2014 Red Hat, Inc.
+ * Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#include <string.h>
+#include <net/ethernet.h>
+#include <netinet/ether.h>
+#include <dbus/dbus-glib.h>
+#include <glib/gi18n.h>
+
+#include "NetworkManager.h"
+#include "nm-setting-wireless.h"
+#include "nm-param-spec-specialized.h"
+#include "nm-utils.h"
+#include "nm-dbus-glib-types.h"
+#include "nm-utils-private.h"
+#include "nm-setting-private.h"
+
+/**
+ * SECTION:nm-setting-wireless
+ * @short_description: Describes connection properties for 802.11 Wi-Fi networks
+ * @include: nm-setting-wireless.h
+ *
+ * The #NMSettingWireless object is a #NMSetting subclass that describes properties
+ * necessary for connection to 802.11 Wi-Fi networks.
+ **/
+
+/**
+ * nm_setting_wireless_error_quark:
+ *
+ * Registers an error quark for #NMSettingWireless if necessary.
+ *
+ * Returns: the error quark used for #NMSettingWireless errors.
+ **/
+GQuark
+nm_setting_wireless_error_quark (void)
+{
+ static GQuark quark;
+
+ if (G_UNLIKELY (!quark))
+ quark = g_quark_from_static_string ("nm-setting-wireless-error-quark");
+ return quark;
+}
+
+
+G_DEFINE_TYPE_WITH_CODE (NMSettingWireless, nm_setting_wireless, NM_TYPE_SETTING,
+ _nm_register_setting (NM_SETTING_WIRELESS_SETTING_NAME,
+ g_define_type_id,
+ 1,
+ NM_SETTING_WIRELESS_ERROR))
+NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_WIRELESS)
+
+#define NM_SETTING_WIRELESS_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_WIRELESS, NMSettingWirelessPrivate))
+
+typedef struct {
+ GByteArray *ssid;
+ char *mode;
+ char *band;
+ guint32 channel;
+ GByteArray *bssid;
+ guint32 rate;
+ guint32 tx_power;
+ GByteArray *device_mac_address;
+ GByteArray *cloned_mac_address;
+ GSList *mac_address_blacklist;
+ guint32 mtu;
+ GSList *seen_bssids;
+ char *security;
+ gboolean hidden;
+} NMSettingWirelessPrivate;
+
+enum {
+ PROP_0,
+ PROP_SSID,
+ PROP_MODE,
+ PROP_BAND,
+ PROP_CHANNEL,
+ PROP_BSSID,
+ PROP_RATE,
+ PROP_TX_POWER,
+ PROP_MAC_ADDRESS,
+ PROP_CLONED_MAC_ADDRESS,
+ PROP_MAC_ADDRESS_BLACKLIST,
+ PROP_MTU,
+ PROP_SEEN_BSSIDS,
+ PROP_SEC,
+ PROP_HIDDEN,
+
+ LAST_PROP
+};
+
+static gboolean
+match_cipher (const char *cipher,
+ const char *expected,
+ guint32 wpa_flags,
+ guint32 rsn_flags,
+ guint32 flag)
+{
+ if (strcmp (cipher, expected) != 0)
+ return FALSE;
+
+ if (!(wpa_flags & flag) && !(rsn_flags & flag))
+ return FALSE;
+
+ return TRUE;
+}
+
+/**
+ * nm_setting_wireless_ap_security_compatible:
+ * @s_wireless: a #NMSettingWireless
+ * @s_wireless_sec: a #NMSettingWirelessSecurity or %NULL
+ * @ap_flags: the %NM80211ApFlags of the given access point
+ * @ap_wpa: the %NM80211ApSecurityFlags of the given access point's WPA
+ * capabilities
+ * @ap_rsn: the %NM80211ApSecurityFlags of the given access point's WPA2/RSN
+ * capabilities
+ * @ap_mode: the 802.11 mode of the AP, either Ad-Hoc or Infrastructure
+ *
+ * Given a #NMSettingWireless and an optional #NMSettingWirelessSecurity,
+ * determine if the configuration given by the settings is compatible with
+ * the security of an access point using that access point's capability flags
+ * and mode. Useful for clients that wish to filter a set of connections
+ * against a set of access points and determine which connections are
+ * compatible with which access points.
+ *
+ * Returns: %TRUE if the given settings are compatible with the access point's
+ * security flags and mode, %FALSE if they are not.
+ */
+gboolean
+nm_setting_wireless_ap_security_compatible (NMSettingWireless *s_wireless,
+ NMSettingWirelessSecurity *s_wireless_sec,
+ NM80211ApFlags ap_flags,
+ NM80211ApSecurityFlags ap_wpa,
+ NM80211ApSecurityFlags ap_rsn,
+ NM80211Mode ap_mode)
+{
+ const char *key_mgmt = NULL, *cipher;
+ guint32 num, i;
+ gboolean found = FALSE;
+
+ g_return_val_if_fail (NM_IS_SETTING_WIRELESS (s_wireless), FALSE);
+
+ if (!s_wireless_sec) {
+ if ( (ap_flags & NM_802_11_AP_FLAGS_PRIVACY)
+ || (ap_wpa != NM_802_11_AP_SEC_NONE)
+ || (ap_rsn != NM_802_11_AP_SEC_NONE))
+ return FALSE;
+ return TRUE;
+ }
+
+ key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wireless_sec);
+ if (!key_mgmt)
+ return FALSE;
+
+ /* Static WEP */
+ if (!strcmp (key_mgmt, "none")) {
+ if ( !(ap_flags & NM_802_11_AP_FLAGS_PRIVACY)
+ || (ap_wpa != NM_802_11_AP_SEC_NONE)
+ || (ap_rsn != NM_802_11_AP_SEC_NONE))
+ return FALSE;
+ return TRUE;
+ }
+
+ /* Adhoc WPA */
+ if (!strcmp (key_mgmt, "wpa-none")) {
+ if (ap_mode != NM_802_11_MODE_ADHOC)
+ return FALSE;
+ /* FIXME: validate ciphers if they're in the beacon */
+ return TRUE;
+ }
+
+ /* Adhoc WPA2 (ie, RSN IBSS) */
+ if (ap_mode == NM_802_11_MODE_ADHOC) {
+ if (strcmp (key_mgmt, "wpa-psk"))
+ return FALSE;
+
+ /* Ensure the AP has RSN PSK capability */
+ if (!(ap_rsn & NM_802_11_AP_SEC_KEY_MGMT_PSK))
+ return FALSE;
+
+ /* Fall through and check ciphers in generic WPA-PSK code */
+ }
+
+ /* Dynamic WEP or LEAP */
+ if (!strcmp (key_mgmt, "ieee8021x")) {
+ if (!(ap_flags & NM_802_11_AP_FLAGS_PRIVACY))
+ return FALSE;
+
+ /* If the AP is advertising a WPA IE, make sure it supports WEP ciphers */
+ if (ap_wpa != NM_802_11_AP_SEC_NONE) {
+ if (!(ap_wpa & NM_802_11_AP_SEC_KEY_MGMT_802_1X))
+ return FALSE;
+
+ /* quick check; can't use AP if it doesn't support at least one
+ * WEP cipher in both pairwise and group suites.
+ */
+ if ( !(ap_wpa & (NM_802_11_AP_SEC_PAIR_WEP40 | NM_802_11_AP_SEC_PAIR_WEP104))
+ || !(ap_wpa & (NM_802_11_AP_SEC_GROUP_WEP40 | NM_802_11_AP_SEC_GROUP_WEP104)))
+ return FALSE;
+
+ /* Match at least one pairwise cipher with AP's capability if the
+ * wireless-security setting explicitly lists pairwise ciphers
+ */
+ num = nm_setting_wireless_security_get_num_pairwise (s_wireless_sec);
+ for (i = 0, found = FALSE; i < num; i++) {
+ cipher = nm_setting_wireless_security_get_pairwise (s_wireless_sec, i);
+ if ((found = match_cipher (cipher, "wep40", ap_wpa, ap_wpa, NM_802_11_AP_SEC_PAIR_WEP40)))
+ break;
+ if ((found = match_cipher (cipher, "wep104", ap_wpa, ap_wpa, NM_802_11_AP_SEC_PAIR_WEP104)))
+ break;
+ }
+ if (!found && num)
+ return FALSE;
+
+ /* Match at least one group cipher with AP's capability if the
+ * wireless-security setting explicitly lists group ciphers
+ */
+ num = nm_setting_wireless_security_get_num_groups (s_wireless_sec);
+ for (i = 0, found = FALSE; i < num; i++) {
+ cipher = nm_setting_wireless_security_get_group (s_wireless_sec, i);
+ if ((found = match_cipher (cipher, "wep40", ap_wpa, ap_wpa, NM_802_11_AP_SEC_GROUP_WEP40)))
+ break;
+ if ((found = match_cipher (cipher, "wep104", ap_wpa, ap_wpa, NM_802_11_AP_SEC_GROUP_WEP104)))
+ break;
+ }
+ if (!found && num)
+ return FALSE;
+ }
+ return TRUE;
+ }
+
+ /* WPA[2]-PSK and WPA[2] Enterprise */
+ if ( !strcmp (key_mgmt, "wpa-psk")
+ || !strcmp (key_mgmt, "wpa-eap")) {
+
+ if (!strcmp (key_mgmt, "wpa-psk")) {
+ if ( !(ap_wpa & NM_802_11_AP_SEC_KEY_MGMT_PSK)
+ && !(ap_rsn & NM_802_11_AP_SEC_KEY_MGMT_PSK))
+ return FALSE;
+ } else if (!strcmp (key_mgmt, "wpa-eap")) {
+ if ( !(ap_wpa & NM_802_11_AP_SEC_KEY_MGMT_802_1X)
+ && !(ap_rsn & NM_802_11_AP_SEC_KEY_MGMT_802_1X))
+ return FALSE;
+ }
+
+ // FIXME: should handle WPA and RSN separately here to ensure that
+ // if the Connection only uses WPA we don't match a cipher against
+ // the AP's RSN IE instead
+
+ /* Match at least one pairwise cipher with AP's capability if the
+ * wireless-security setting explicitly lists pairwise ciphers
+ */
+ num = nm_setting_wireless_security_get_num_pairwise (s_wireless_sec);
+ for (i = 0, found = FALSE; i < num; i++) {
+ cipher = nm_setting_wireless_security_get_pairwise (s_wireless_sec, i);
+ if ((found = match_cipher (cipher, "tkip", ap_wpa, ap_rsn, NM_802_11_AP_SEC_PAIR_TKIP)))
+ break;
+ if ((found = match_cipher (cipher, "ccmp", ap_wpa, ap_rsn, NM_802_11_AP_SEC_PAIR_CCMP)))
+ break;
+ }
+ if (!found && num)
+ return FALSE;
+
+ /* Match at least one group cipher with AP's capability if the
+ * wireless-security setting explicitly lists group ciphers
+ */
+ num = nm_setting_wireless_security_get_num_groups (s_wireless_sec);
+ for (i = 0, found = FALSE; i < num; i++) {
+ cipher = nm_setting_wireless_security_get_group (s_wireless_sec, i);
+
+ if ((found = match_cipher (cipher, "wep40", ap_wpa, ap_rsn, NM_802_11_AP_SEC_GROUP_WEP40)))
+ break;
+ if ((found = match_cipher (cipher, "wep104", ap_wpa, ap_rsn, NM_802_11_AP_SEC_GROUP_WEP104)))
+ break;
+ if ((found = match_cipher (cipher, "tkip", ap_wpa, ap_rsn, NM_802_11_AP_SEC_GROUP_TKIP)))
+ break;
+ if ((found = match_cipher (cipher, "ccmp", ap_wpa, ap_rsn, NM_802_11_AP_SEC_GROUP_CCMP)))
+ break;
+ }
+ if (!found && num)
+ return FALSE;
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ * nm_setting_wireless_new:
+ *
+ * Creates a new #NMSettingWireless object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingWireless object
+ **/
+NMSetting *
+nm_setting_wireless_new (void)
+{
+ return (NMSetting *) g_object_new (NM_TYPE_SETTING_WIRELESS, NULL);
+}
+
+/**
+ * nm_setting_wireless_get_ssid:
+ * @setting: the #NMSettingWireless
+ *
+ * Returns: the #NMSettingWireless:ssid property of the setting
+ **/
+const GByteArray *
+nm_setting_wireless_get_ssid (NMSettingWireless *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_WIRELESS (setting), NULL);
+
+ return NM_SETTING_WIRELESS_GET_PRIVATE (setting)->ssid;
+}
+
+/**
+ * nm_setting_wireless_get_mode:
+ * @setting: the #NMSettingWireless
+ *
+ * Returns: the #NMSettingWireless:mode property of the setting
+ **/
+const char *
+nm_setting_wireless_get_mode (NMSettingWireless *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_WIRELESS (setting), NULL);
+
+ return NM_SETTING_WIRELESS_GET_PRIVATE (setting)->mode;
+}
+
+/**
+ * nm_setting_wireless_get_band:
+ * @setting: the #NMSettingWireless
+ *
+ * Returns: the #NMSettingWireless:band property of the setting
+ **/
+const char *
+nm_setting_wireless_get_band (NMSettingWireless *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_WIRELESS (setting), NULL);
+
+ return NM_SETTING_WIRELESS_GET_PRIVATE (setting)->band;
+}
+
+/**
+ * nm_setting_wireless_get_channel:
+ * @setting: the #NMSettingWireless
+ *
+ * Returns: the #NMSettingWireless:channel property of the setting
+ **/
+guint32
+nm_setting_wireless_get_channel (NMSettingWireless *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_WIRELESS (setting), 0);
+
+ return NM_SETTING_WIRELESS_GET_PRIVATE (setting)->channel;
+}
+
+/**
+ * nm_setting_wireless_get_bssid:
+ * @setting: the #NMSettingWireless
+ *
+ * Returns: the #NMSettingWireless:bssid property of the setting
+ **/
+const GByteArray *
+nm_setting_wireless_get_bssid (NMSettingWireless *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_WIRELESS (setting), NULL);
+
+ return NM_SETTING_WIRELESS_GET_PRIVATE (setting)->bssid;
+}
+
+/**
+ * nm_setting_wireless_get_rate:
+ * @setting: the #NMSettingWireless
+ *
+ * Returns: the #NMSettingWireless:rate property of the setting
+ **/
+guint32
+nm_setting_wireless_get_rate (NMSettingWireless *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_WIRELESS (setting), 0);
+
+ return NM_SETTING_WIRELESS_GET_PRIVATE (setting)->rate;
+}
+
+/**
+ * nm_setting_wireless_get_tx_power:
+ * @setting: the #NMSettingWireless
+ *
+ * Returns: the #NMSettingWireless:tx-power property of the setting
+ **/
+guint32
+nm_setting_wireless_get_tx_power (NMSettingWireless *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_WIRELESS (setting), 0);
+
+ return NM_SETTING_WIRELESS_GET_PRIVATE (setting)->tx_power;
+}
+
+/**
+ * nm_setting_wireless_get_mac_address:
+ * @setting: the #NMSettingWireless
+ *
+ * Returns: the #NMSettingWireless:mac-address property of the setting
+ **/
+const GByteArray *
+nm_setting_wireless_get_mac_address (NMSettingWireless *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_WIRELESS (setting), NULL);
+
+ return NM_SETTING_WIRELESS_GET_PRIVATE (setting)->device_mac_address;
+}
+
+/**
+ * nm_setting_wireless_get_cloned_mac_address:
+ * @setting: the #NMSettingWireless
+ *
+ * Returns: the #NMSettingWireless:cloned-mac-address property of the setting
+ **/
+const GByteArray *
+nm_setting_wireless_get_cloned_mac_address (NMSettingWireless *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_WIRELESS (setting), NULL);
+
+ return NM_SETTING_WIRELESS_GET_PRIVATE (setting)->cloned_mac_address;
+}
+
+/**
+ * nm_setting_wireless_get_mac_address_blacklist:
+ * @setting: the #NMSettingWireless
+ *
+ * Returns: (element-type GLib.ByteArray): the
+ * #NMSettingWireless:mac-address-blacklist property of the setting
+ **/
+const GSList *
+nm_setting_wireless_get_mac_address_blacklist (NMSettingWireless *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_WIRELESS (setting), NULL);
+
+ return NM_SETTING_WIRELESS_GET_PRIVATE (setting)->mac_address_blacklist;
+}
+
+/**
+ * nm_setting_wireless_get_num_mac_blacklist_items:
+ * @setting: the #NMSettingWireless
+ *
+ * Returns: the number of blacklisted MAC addresses
+ *
+ * Since: 0.9.10
+ **/
+guint32
+nm_setting_wireless_get_num_mac_blacklist_items (NMSettingWireless *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_WIRELESS (setting), 0);
+
+ return g_slist_length (NM_SETTING_WIRELESS_GET_PRIVATE (setting)->mac_address_blacklist);
+}
+
+/**
+ * nm_setting_wireless_get_mac_blacklist_item:
+ * @setting: the #NMSettingWireless
+ * @idx: the zero-based index of the MAC address entry
+ *
+ * Returns: the blacklisted MAC address string (hex-digits-and-colons notation)
+ * at index @idx
+ *
+ * Since: 0.9.10
+ **/
+const char *
+nm_setting_wireless_get_mac_blacklist_item (NMSettingWireless *setting, guint32 idx)
+{
+ NMSettingWirelessPrivate *priv;
+
+ g_return_val_if_fail (NM_IS_SETTING_WIRELESS (setting), NULL);
+
+ priv = NM_SETTING_WIRELESS_GET_PRIVATE (setting);
+ g_return_val_if_fail (idx <= g_slist_length (priv->mac_address_blacklist), NULL);
+
+ return (const char *) g_slist_nth_data (priv->mac_address_blacklist, idx);
+}
+
+/**
+ * nm_setting_wireless_add_mac_blacklist_item:
+ * @setting: the #NMSettingWireless
+ * @mac: the MAC address string (hex-digits-and-colons notation) to blacklist
+ *
+ * Adds a new MAC address to the #NMSettingWireless:mac-address-blacklist property.
+ *
+ * Returns: %TRUE if the MAC address was added; %FALSE if the MAC address
+ * is invalid or was already present
+ *
+ * Since: 0.9.10
+ **/
+gboolean
+nm_setting_wireless_add_mac_blacklist_item (NMSettingWireless *setting, const char *mac)
+{
+ NMSettingWirelessPrivate *priv;
+ GSList *iter;
+ guint8 buf[32];
+
+ g_return_val_if_fail (NM_IS_SETTING_WIRELESS (setting), FALSE);
+ g_return_val_if_fail (mac != NULL, FALSE);
+
+ if (!nm_utils_hwaddr_aton (mac, ARPHRD_ETHER, buf))
+ return FALSE;
+
+ priv = NM_SETTING_WIRELESS_GET_PRIVATE (setting);
+ for (iter = priv->mac_address_blacklist; iter; iter = g_slist_next (iter)) {
+ if (!strcasecmp (mac, (char *) iter->data))
+ return FALSE;
+ }
+
+ priv->mac_address_blacklist = g_slist_append (priv->mac_address_blacklist,
+ g_ascii_strup (mac, -1));
+ g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST);
+ return TRUE;
+}
+
+/**
+ * nm_setting_wireless_remove_mac_blacklist_item:
+ * @setting: the #NMSettingWireless
+ * @idx: index number of the MAC address
+ *
+ * Removes the MAC address at index @idx from the blacklist.
+ *
+ * Since: 0.9.10
+ **/
+void
+nm_setting_wireless_remove_mac_blacklist_item (NMSettingWireless *setting, guint32 idx)
+{
+ NMSettingWirelessPrivate *priv;
+ GSList *elt;
+
+ g_return_if_fail (NM_IS_SETTING_WIRELESS (setting));
+
+ priv = NM_SETTING_WIRELESS_GET_PRIVATE (setting);
+ elt = g_slist_nth (priv->mac_address_blacklist, idx);
+ g_return_if_fail (elt != NULL);
+
+ g_free (elt->data);
+ priv->mac_address_blacklist = g_slist_delete_link (priv->mac_address_blacklist, elt);
+ g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST);
+}
+
+/**
+ * nm_setting_wireless_remove_mac_blacklist_item_by_value:
+ * @setting: the #NMSettingWireless
+ * @mac: the MAC address string (hex-digits-and-colons notation) to remove from
+ * the blacklist
+ *
+ * Removes the MAC address @mac from the blacklist.
+ *
+ * Returns: %TRUE if the MAC address was found and removed; %FALSE if it was not.
+ *
+ * Since: 0.9.10
+ **/
+gboolean
+nm_setting_wireless_remove_mac_blacklist_item_by_value (NMSettingWireless *setting, const char *mac)
+{
+ NMSettingWirelessPrivate *priv;
+ GSList *iter;
+ guint8 buf[32];
+
+ g_return_val_if_fail (NM_IS_SETTING_WIRELESS (setting), FALSE);
+ g_return_val_if_fail (mac != NULL, FALSE);
+
+ if (!nm_utils_hwaddr_aton (mac, ARPHRD_ETHER, buf))
+ return FALSE;
+
+ priv = NM_SETTING_WIRELESS_GET_PRIVATE (setting);
+ for (iter = priv->mac_address_blacklist; iter; iter = g_slist_next (iter)) {
+ if (!strcasecmp (mac, (char *) iter->data)) {
+ priv->mac_address_blacklist = g_slist_delete_link (priv->mac_address_blacklist, iter);
+ g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/**
+ * nm_setting_wireless_clear_mac_blacklist_items:
+ * @setting: the #NMSettingWireless
+ *
+ * Removes all blacklisted MAC addresses.
+ *
+ * Since: 0.9.10
+ **/
+void
+nm_setting_wireless_clear_mac_blacklist_items (NMSettingWireless *setting)
+{
+ g_return_if_fail (NM_IS_SETTING_WIRELESS (setting));
+
+ g_slist_free_full (NM_SETTING_WIRELESS_GET_PRIVATE (setting)->mac_address_blacklist, g_free);
+ NM_SETTING_WIRELESS_GET_PRIVATE (setting)->mac_address_blacklist = NULL;
+ g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST);
+}
+
+/**
+ * nm_setting_wireless_get_mtu:
+ * @setting: the #NMSettingWireless
+ *
+ * Returns: the #NMSettingWireless:mtu property of the setting
+ **/
+guint32
+nm_setting_wireless_get_mtu (NMSettingWireless *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_WIRELESS (setting), 0);
+
+ return NM_SETTING_WIRELESS_GET_PRIVATE (setting)->mtu;
+}
+
+/**
+ * nm_setting_wireless_get_security:
+ * @setting: the #NMSettingWireless
+ *
+ * Returns: the #NMSettingWireless:security property of the setting
+ *
+ * Deprecated: 0.9.10: No longer used. Security rescrictions are recognized by
+ * the presence of NM_SETTING_WIRELESS_SECURITY_SETTING_NAME in the connection.
+ **/
+const char *
+nm_setting_wireless_get_security (NMSettingWireless *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_WIRELESS (setting), NULL);
+
+ return NM_SETTING_WIRELESS_GET_PRIVATE (setting)->security;
+}
+
+/**
+ * nm_setting_wireless_get_hidden:
+ * @setting: the #NMSettingWireless
+ *
+ * Returns: the #NMSettingWireless:hidden property of the setting
+ **/
+gboolean
+nm_setting_wireless_get_hidden (NMSettingWireless *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_WIRELESS (setting), FALSE);
+
+ return NM_SETTING_WIRELESS_GET_PRIVATE (setting)->hidden;
+}
+
+/**
+ * nm_setting_wireless_add_seen_bssid:
+ * @setting: the #NMSettingWireless
+ * @bssid: the new BSSID to add to the list
+ *
+ * Adds a new Wi-Fi AP's BSSID to the previously seen BSSID list of the setting.
+ * NetworkManager now tracks previously seen BSSIDs internally so this function
+ * no longer has much use. Actually, changes you make using this function will
+ * not be preserved.
+ *
+ * Returns: %TRUE if @bssid was already known, %FALSE if not
+ **/
+gboolean
+nm_setting_wireless_add_seen_bssid (NMSettingWireless *setting,
+ const char *bssid)
+{
+ NMSettingWirelessPrivate *priv;
+ char *lower_bssid;
+ GSList *iter;
+ gboolean found = FALSE;
+
+ g_return_val_if_fail (NM_IS_SETTING_WIRELESS (setting), FALSE);
+ g_return_val_if_fail (bssid != NULL, FALSE);
+
+ lower_bssid = g_ascii_strdown (bssid, -1);
+ if (!lower_bssid)
+ return FALSE;
+
+ priv = NM_SETTING_WIRELESS_GET_PRIVATE (setting);
+
+ for (iter = priv->seen_bssids; iter; iter = iter->next) {
+ if (!strcmp ((char *) iter->data, lower_bssid)) {
+ found = TRUE;
+ break;
+ }
+ }
+
+ if (!found) {
+ priv->seen_bssids = g_slist_prepend (priv->seen_bssids, lower_bssid);
+ g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_SEEN_BSSIDS);
+ } else
+ g_free (lower_bssid);
+
+ return !found;
+}
+
+/**
+ * nm_setting_wireless_get_num_seen_bssids:
+ * @setting: the #NMSettingWireless
+ *
+ * Returns: the number of BSSIDs in the previously seen BSSID list
+ **/
+guint32
+nm_setting_wireless_get_num_seen_bssids (NMSettingWireless *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_WIRELESS (setting), 0);
+
+ return g_slist_length (NM_SETTING_WIRELESS_GET_PRIVATE (setting)->seen_bssids);
+}
+
+/**
+ * nm_setting_wireless_get_seen_bssid:
+ * @setting: the #NMSettingWireless
+ * @i: index of a BSSID in the previously seen BSSID list
+ *
+ * Returns: the BSSID at index @i
+ **/
+const char *
+nm_setting_wireless_get_seen_bssid (NMSettingWireless *setting,
+ guint32 i)
+{
+ g_return_val_if_fail (NM_IS_SETTING_WIRELESS (setting), NULL);
+
+ return (const char *) g_slist_nth_data (NM_SETTING_WIRELESS_GET_PRIVATE (setting)->seen_bssids, i);
+}
+
+static gboolean
+verify (NMSetting *setting, GSList *all_settings, GError **error)
+{
+ NMSettingWirelessPrivate *priv = NM_SETTING_WIRELESS_GET_PRIVATE (setting);
+ const char *valid_modes[] = { NM_SETTING_WIRELESS_MODE_INFRA, NM_SETTING_WIRELESS_MODE_ADHOC, NM_SETTING_WIRELESS_MODE_AP, NULL };
+ const char *valid_bands[] = { "a", "bg", NULL };
+ GSList *iter;
+
+ if (!priv->ssid) {
+ g_set_error_literal (error,
+ NM_SETTING_WIRELESS_ERROR,
+ NM_SETTING_WIRELESS_ERROR_MISSING_PROPERTY,
+ _("property is missing"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_SSID);
+ return FALSE;
+ }
+
+ if (!priv->ssid->len || priv->ssid->len > 32) {
+ g_set_error_literal (error,
+ NM_SETTING_WIRELESS_ERROR,
+ NM_SETTING_WIRELESS_ERROR_INVALID_PROPERTY,
+ _("SSID length is out of range <1-32> bytes"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_SSID);
+ return FALSE;
+ }
+
+ if (priv->mode && !_nm_utils_string_in_list (priv->mode, valid_modes)) {
+ g_set_error (error,
+ NM_SETTING_WIRELESS_ERROR,
+ NM_SETTING_WIRELESS_ERROR_INVALID_PROPERTY,
+ _("'%s' is not a valid Wi-Fi mode"),
+ priv->mode);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_MODE);
+ return FALSE;
+ }
+
+ if (priv->band && !_nm_utils_string_in_list (priv->band, valid_bands)) {
+ g_set_error (error,
+ NM_SETTING_WIRELESS_ERROR,
+ NM_SETTING_WIRELESS_ERROR_INVALID_PROPERTY,
+ _("'%s' is not a valid band"),
+ priv->band);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_BAND);
+ return FALSE;
+ }
+
+ if (priv->channel && !priv->band) {
+ g_set_error (error,
+ NM_SETTING_WIRELESS_ERROR,
+ NM_SETTING_WIRELESS_ERROR_CHANNEL_REQUIRES_BAND,
+ _("requires setting '%s' property"),
+ NM_SETTING_WIRELESS_BAND);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_CHANNEL);
+ return FALSE;
+ }
+
+ if (priv->channel) {
+ if (!nm_utils_wifi_is_channel_valid (priv->channel, priv->band)) {
+ g_set_error (error,
+ NM_SETTING_WIRELESS_ERROR,
+ NM_SETTING_WIRELESS_ERROR_INVALID_PROPERTY,
+ _("'%d' is not a valid channel"),
+ priv->channel);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_CHANNEL);
+ return FALSE;
+ }
+ }
+
+ if (priv->bssid && priv->bssid->len != ETH_ALEN) {
+ g_set_error_literal (error,
+ NM_SETTING_WIRELESS_ERROR,
+ NM_SETTING_WIRELESS_ERROR_INVALID_PROPERTY,
+ _("property is invalid"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_BSSID);
+ return FALSE;
+ }
+
+ if (priv->device_mac_address && priv->device_mac_address->len != ETH_ALEN) {
+ g_set_error_literal (error,
+ NM_SETTING_WIRELESS_ERROR,
+ NM_SETTING_WIRELESS_ERROR_INVALID_PROPERTY,
+ _("property is invalid"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_MAC_ADDRESS);
+ return FALSE;
+ }
+
+ if (priv->cloned_mac_address && priv->cloned_mac_address->len != ETH_ALEN) {
+ g_set_error_literal (error,
+ NM_SETTING_WIRELESS_ERROR,
+ NM_SETTING_WIRELESS_ERROR_INVALID_PROPERTY,
+ _("property is invalid"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS);
+ return FALSE;
+ }
+
+ for (iter = priv->mac_address_blacklist; iter; iter = iter->next) {
+ struct ether_addr addr;
+
+ if (!ether_aton_r (iter->data, &addr)) {
+ g_set_error (error,
+ NM_SETTING_WIRELESS_ERROR,
+ NM_SETTING_WIRELESS_ERROR_INVALID_PROPERTY,
+ _("'%s' is not a valid MAC address"),
+ (const char *) iter->data);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST);
+ return FALSE;
+ }
+ }
+
+ for (iter = priv->seen_bssids; iter; iter = iter->next) {
+ struct ether_addr addr;
+
+ if (!ether_aton_r (iter->data, &addr)) {
+ g_set_error (error,
+ NM_SETTING_WIRELESS_ERROR,
+ NM_SETTING_WIRELESS_ERROR_INVALID_PROPERTY,
+ _("'%s' is not a valid MAC address"),
+ (const char *) iter->data);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_SEEN_BSSIDS);
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static void
+nm_setting_wireless_init (NMSettingWireless *setting)
+{
+}
+
+static void
+finalize (GObject *object)
+{
+ NMSettingWirelessPrivate *priv = NM_SETTING_WIRELESS_GET_PRIVATE (object);
+
+ g_free (priv->mode);
+ g_free (priv->band);
+ g_free (priv->security);
+
+ if (priv->ssid)
+ g_byte_array_free (priv->ssid, TRUE);
+ if (priv->bssid)
+ g_byte_array_free (priv->bssid, TRUE);
+ if (priv->device_mac_address)
+ g_byte_array_free (priv->device_mac_address, TRUE);
+ if (priv->cloned_mac_address)
+ g_byte_array_free (priv->cloned_mac_address, TRUE);
+ g_slist_free_full (priv->mac_address_blacklist, g_free);
+ g_slist_free_full (priv->seen_bssids, g_free);
+
+ G_OBJECT_CLASS (nm_setting_wireless_parent_class)->finalize (object);
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ NMSettingWirelessPrivate *priv = NM_SETTING_WIRELESS_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_SSID:
+ if (priv->ssid)
+ g_byte_array_free (priv->ssid, TRUE);
+ priv->ssid = g_value_dup_boxed (value);
+ break;
+ case PROP_MODE:
+ g_free (priv->mode);
+ priv->mode = g_value_dup_string (value);
+ break;
+ case PROP_BAND:
+ g_free (priv->band);
+ priv->band = g_value_dup_string (value);
+ break;
+ case PROP_CHANNEL:
+ priv->channel = g_value_get_uint (value);
+ break;
+ case PROP_BSSID:
+ if (priv->bssid)
+ g_byte_array_free (priv->bssid, TRUE);
+ priv->bssid = g_value_dup_boxed (value);
+ break;
+ case PROP_RATE:
+ priv->rate = g_value_get_uint (value);
+ break;
+ case PROP_TX_POWER:
+ priv->tx_power = g_value_get_uint (value);
+ break;
+ case PROP_MAC_ADDRESS:
+ if (priv->device_mac_address)
+ g_byte_array_free (priv->device_mac_address, TRUE);
+ priv->device_mac_address = g_value_dup_boxed (value);
+ break;
+ case PROP_CLONED_MAC_ADDRESS:
+ if (priv->cloned_mac_address)
+ g_byte_array_free (priv->cloned_mac_address, TRUE);
+ priv->cloned_mac_address = g_value_dup_boxed (value);
+ break;
+ case PROP_MAC_ADDRESS_BLACKLIST:
+ g_slist_free_full (priv->mac_address_blacklist, g_free);
+ priv->mac_address_blacklist = g_value_dup_boxed (value);
+ break;
+ case PROP_MTU:
+ priv->mtu = g_value_get_uint (value);
+ break;
+ case PROP_SEEN_BSSIDS:
+ g_slist_free_full (priv->seen_bssids, g_free);
+ priv->seen_bssids = g_value_dup_boxed (value);
+ break;
+ case PROP_SEC:
+ g_free (priv->security);
+ priv->security = g_value_dup_string (value);
+ break;
+ case PROP_HIDDEN:
+ priv->hidden = g_value_get_boolean (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ NMSettingWireless *setting = NM_SETTING_WIRELESS (object);
+
+ switch (prop_id) {
+ case PROP_SSID:
+ g_value_set_boxed (value, nm_setting_wireless_get_ssid (setting));
+ break;
+ case PROP_MODE:
+ g_value_set_string (value, nm_setting_wireless_get_mode (setting));
+ break;
+ case PROP_BAND:
+ g_value_set_string (value, nm_setting_wireless_get_band (setting));
+ break;
+ case PROP_CHANNEL:
+ g_value_set_uint (value, nm_setting_wireless_get_channel (setting));
+ break;
+ case PROP_BSSID:
+ g_value_set_boxed (value, nm_setting_wireless_get_bssid (setting));
+ break;
+ case PROP_RATE:
+ g_value_set_uint (value, nm_setting_wireless_get_rate (setting));
+ break;
+ case PROP_TX_POWER:
+ g_value_set_uint (value, nm_setting_wireless_get_tx_power (setting));
+ break;
+ case PROP_MAC_ADDRESS:
+ g_value_set_boxed (value, nm_setting_wireless_get_mac_address (setting));
+ break;
+ case PROP_CLONED_MAC_ADDRESS:
+ g_value_set_boxed (value, nm_setting_wireless_get_cloned_mac_address (setting));
+ break;
+ case PROP_MAC_ADDRESS_BLACKLIST:
+ g_value_set_boxed (value, nm_setting_wireless_get_mac_address_blacklist (setting));
+ break;
+ case PROP_MTU:
+ g_value_set_uint (value, nm_setting_wireless_get_mtu (setting));
+ break;
+ case PROP_SEEN_BSSIDS:
+ g_value_set_boxed (value, NM_SETTING_WIRELESS_GET_PRIVATE (setting)->seen_bssids);
+ break;
+ case PROP_SEC:
+ g_value_set_string (value, NM_SETTING_WIRELESS_GET_PRIVATE (setting)->security);
+ break;
+ case PROP_HIDDEN:
+ g_value_set_boolean (value, nm_setting_wireless_get_hidden (setting));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nm_setting_wireless_class_init (NMSettingWirelessClass *setting_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
+ NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+
+ g_type_class_add_private (setting_class, sizeof (NMSettingWirelessPrivate));
+
+ /* virtual methods */
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+ object_class->finalize = finalize;
+ parent_class->verify = verify;
+
+ /* Properties */
+ /**
+ * NMSettingWireless:ssid:
+ *
+ * SSID of the Wi-Fi network. Must be specified.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_SSID,
+ _nm_param_spec_specialized (NM_SETTING_WIRELESS_SSID, "", "",
+ DBUS_TYPE_G_UCHAR_ARRAY,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingWireless:mode:
+ *
+ * Wi-Fi network mode; one of "infrastructure", "adhoc" or "ap". If blank,
+ * infrastructure is assumed.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_MODE,
+ g_param_spec_string (NM_SETTING_WIRELESS_MODE, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingWireless:band:
+ *
+ * 802.11 frequency band of the network. One of "a" for 5GHz 802.11a or
+ * "bg" for 2.4GHz 802.11. This will lock associations to the Wi-Fi network
+ * to the specific band, i.e. if "a" is specified, the device will not
+ * associate with the same network in the 2.4GHz band even if the network's
+ * settings are compatible. This setting depends on specific driver
+ * capability and may not work with all drivers.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_BAND,
+ g_param_spec_string (NM_SETTING_WIRELESS_BAND, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingWireless:channel:
+ *
+ * Wireless channel to use for the Wi-Fi connection. The device will only
+ * join (or create for Ad-Hoc networks) a Wi-Fi network on the specified
+ * channel. Because channel numbers overlap between bands, this property
+ * also requires the "band" property to be set.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_CHANNEL,
+ g_param_spec_uint (NM_SETTING_WIRELESS_CHANNEL, "", "",
+ 0, G_MAXUINT32, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingWireless:bssid:
+ *
+ * If specified, directs the device to only associate with the given access
+ * point. This capability is highly driver dependent and not supported by
+ * all devices. Note: this property does not control the BSSID used when
+ * creating an Ad-Hoc network and is unlikely to in the future.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_BSSID,
+ _nm_param_spec_specialized (NM_SETTING_WIRELESS_BSSID, "", "",
+ DBUS_TYPE_G_UCHAR_ARRAY,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingWireless:rate:
+ *
+ * If non-zero, directs the device to only use the specified bitrate for
+ * communication with the access point. Units are in Kb/s, ie 5500 = 5.5
+ * Mbit/s. This property is highly driver dependent and not all devices
+ * support setting a static bitrate.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_RATE,
+ g_param_spec_uint (NM_SETTING_WIRELESS_RATE, "", "",
+ 0, G_MAXUINT32, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ NM_SETTING_PARAM_FUZZY_IGNORE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingWireless:tx-power:
+ *
+ * If non-zero, directs the device to use the specified transmit power.
+ * Units are dBm. This property is highly driver dependent and not all
+ * devices support setting a static transmit power.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_TX_POWER,
+ g_param_spec_uint (NM_SETTING_WIRELESS_TX_POWER, "", "",
+ 0, G_MAXUINT32, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ NM_SETTING_PARAM_FUZZY_IGNORE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingWireless:mac-address:
+ *
+ * If specified, this connection will only apply to the Wi-Fi device whose
+ * permanent MAC address matches. This property does not change the MAC
+ * address of the device (i.e. MAC spoofing).
+ **/
+ g_object_class_install_property
+ (object_class, PROP_MAC_ADDRESS,
+ _nm_param_spec_specialized (NM_SETTING_WIRELESS_MAC_ADDRESS, "", "",
+ DBUS_TYPE_G_UCHAR_ARRAY,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingWireless:cloned-mac-address:
+ *
+ * If specified, request that the Wi-Fi device use this MAC address instead
+ * of its permanent MAC address. This is known as MAC cloning or spoofing.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_CLONED_MAC_ADDRESS,
+ _nm_param_spec_specialized (NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, "", "",
+ DBUS_TYPE_G_UCHAR_ARRAY,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingWireless:mac-address-blacklist:
+ *
+ * A list of permanent MAC addresses of Wi-Fi devices to which this
+ * connection should never apply. Each MAC address should be given in the
+ * standard hex-digits-and-colons notation (eg "00:11:22:33:44:55").
+ **/
+ g_object_class_install_property
+ (object_class, PROP_MAC_ADDRESS_BLACKLIST,
+ _nm_param_spec_specialized (NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST, "", "",
+ DBUS_TYPE_G_LIST_OF_STRING,
+ G_PARAM_READWRITE |
+ NM_SETTING_PARAM_FUZZY_IGNORE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingWireless:seen-bssids:
+ *
+ * A list of BSSIDs (each BSSID formatted as a MAC address like
+ * "00:11:22:33:44:55") that have been detected as part of the Wi-Fi
+ * network. NetworkManager internally tracks previously seen BSSIDs. The
+ * property is only meant for reading and reflects the BSSID list of
+ * NetworkManager. The changes you make to this property will not be
+ * preserved.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_SEEN_BSSIDS,
+ _nm_param_spec_specialized (NM_SETTING_WIRELESS_SEEN_BSSIDS, "", "",
+ DBUS_TYPE_G_LIST_OF_STRING,
+ G_PARAM_READWRITE |
+ NM_SETTING_PARAM_FUZZY_IGNORE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingWireless:mtu:
+ *
+ * If non-zero, only transmit packets of the specified size or smaller,
+ * breaking larger packets up into multiple Ethernet frames.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_MTU,
+ g_param_spec_uint (NM_SETTING_WIRELESS_MTU, "", "",
+ 0, G_MAXUINT32, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ NM_SETTING_PARAM_FUZZY_IGNORE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingWireless:security:
+ *
+ * If the wireless connection has any security restrictions, like 802.1x,
+ * WEP, or WPA, set this property to
+ * %NM_SETTING_WIRELESS_SECURITY_SETTING_NAME and ensure the connection
+ * contains a valid #NMSettingWirelessSecurity setting.
+ *
+ * Deprecated: 0.9.10: No longer used. Security restrictions are recognized
+ * by the presence of a #NMSettingWirelessSecurity setting in the
+ * connection.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_SEC,
+ g_param_spec_string (NM_SETTING_WIRELESS_SEC, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingWireless:hidden:
+ *
+ * If %TRUE, indicates this network is a non-broadcasting network that hides
+ * its SSID. In this case various workarounds may take place, such as
+ * probe-scanning the SSID for more reliable network discovery. However,
+ * these workarounds expose inherent insecurities with hidden SSID networks,
+ * and thus hidden SSID networks should be used with caution.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_HIDDEN,
+ g_param_spec_boolean (NM_SETTING_WIRELESS_HIDDEN, "", "",
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/libnm-core/nm-setting-wireless.h b/libnm-core/nm-setting-wireless.h
new file mode 100644
index 0000000000..18f78cff66
--- /dev/null
+++ b/libnm-core/nm-setting-wireless.h
@@ -0,0 +1,174 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2014 Red Hat, Inc.
+ * Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#ifndef NM_SETTING_WIRELESS_H
+#define NM_SETTING_WIRELESS_H
+
+#include <NetworkManager.h>
+#include <nm-setting.h>
+#include <nm-setting-wireless-security.h>
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_SETTING_WIRELESS (nm_setting_wireless_get_type ())
+#define NM_SETTING_WIRELESS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_WIRELESS, NMSettingWireless))
+#define NM_SETTING_WIRELESS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_WIRELESS, NMSettingWirelessClass))
+#define NM_IS_SETTING_WIRELESS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_WIRELESS))
+#define NM_IS_SETTING_WIRELESS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_WIRELESS))
+#define NM_SETTING_WIRELESS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_WIRELESS, NMSettingWirelessClass))
+
+#define NM_SETTING_WIRELESS_SETTING_NAME "802-11-wireless"
+
+/**
+ * NMSettingWirelessError:
+ * @NM_SETTING_WIRELESS_ERROR_UNKNOWN: unknown or unclassified error
+ * @NM_SETTING_WIRELESS_ERROR_INVALID_PROPERTY: the property was invalid
+ * @NM_SETTING_WIRELESS_ERROR_MISSING_PROPERTY: the property was missing and is
+ * required
+ * @NM_SETTING_WIRELESS_ERROR_MISSING_SECURITY_SETTING: property values require
+ * the presence of an #NMSettingWirelessSecurity object in the connection
+ * @NM_SETTING_WIRELESS_ERROR_CHANNEL_REQUIRES_BAND: the property channel was
+ * set to a value that requires the #NMSettingWireless:band property to be set
+ */
+typedef enum {
+ NM_SETTING_WIRELESS_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/
+ NM_SETTING_WIRELESS_ERROR_INVALID_PROPERTY, /*< nick=InvalidProperty >*/
+ NM_SETTING_WIRELESS_ERROR_MISSING_PROPERTY, /*< nick=MissingProperty >*/
+ NM_SETTING_WIRELESS_ERROR_MISSING_SECURITY_SETTING, /*< nick=MissingSecuritySetting >*/
+ NM_SETTING_WIRELESS_ERROR_CHANNEL_REQUIRES_BAND /*< nick=ChannelRequiresBand >*/
+} NMSettingWirelessError;
+
+#define NM_SETTING_WIRELESS_ERROR nm_setting_wireless_error_quark ()
+GQuark nm_setting_wireless_error_quark (void);
+
+#define NM_SETTING_WIRELESS_SSID "ssid"
+#define NM_SETTING_WIRELESS_MODE "mode"
+#define NM_SETTING_WIRELESS_BAND "band"
+#define NM_SETTING_WIRELESS_CHANNEL "channel"
+#define NM_SETTING_WIRELESS_BSSID "bssid"
+#define NM_SETTING_WIRELESS_RATE "rate"
+#define NM_SETTING_WIRELESS_TX_POWER "tx-power"
+#define NM_SETTING_WIRELESS_MAC_ADDRESS "mac-address"
+#define NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS "cloned-mac-address"
+#define NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST "mac-address-blacklist"
+#define NM_SETTING_WIRELESS_MTU "mtu"
+#define NM_SETTING_WIRELESS_SEEN_BSSIDS "seen-bssids"
+#define NM_SETTING_WIRELESS_HIDDEN "hidden"
+
+/* Deprecated */
+#define NM_SETTING_WIRELESS_SEC "security"
+
+/**
+ * NM_SETTING_WIRELESS_MODE_ADHOC:
+ *
+ * Indicates Ad-Hoc mode where no access point is expected to be present.
+ */
+#define NM_SETTING_WIRELESS_MODE_ADHOC "adhoc"
+
+/**
+ * NM_SETTING_WIRELESS_MODE_AP:
+ *
+ * Indicates AP/master mode where the wireless device is started as an access
+ * point/hotspot.
+ *
+ * Since: 0.9.8
+ */
+#define NM_SETTING_WIRELESS_MODE_AP "ap"
+
+/**
+ * NM_SETTING_WIRELESS_MODE_INFRA:
+ *
+ * Indicates infrastructure mode where an access point is expected to be present
+ * for this connection.
+ */
+#define NM_SETTING_WIRELESS_MODE_INFRA "infrastructure"
+
+typedef struct {
+ NMSetting parent;
+} NMSettingWireless;
+
+typedef struct {
+ NMSettingClass parent;
+
+ /* Padding for future expansion */
+ void (*_reserved1) (void);
+ void (*_reserved2) (void);
+ void (*_reserved3) (void);
+ void (*_reserved4) (void);
+} NMSettingWirelessClass;
+
+GType nm_setting_wireless_get_type (void);
+
+NMSetting *nm_setting_wireless_new (void);
+
+const GByteArray *nm_setting_wireless_get_ssid (NMSettingWireless *setting);
+const char *nm_setting_wireless_get_mode (NMSettingWireless *setting);
+const char *nm_setting_wireless_get_band (NMSettingWireless *setting);
+guint32 nm_setting_wireless_get_channel (NMSettingWireless *setting);
+const GByteArray *nm_setting_wireless_get_bssid (NMSettingWireless *setting);
+guint32 nm_setting_wireless_get_rate (NMSettingWireless *setting);
+guint32 nm_setting_wireless_get_tx_power (NMSettingWireless *setting);
+const GByteArray *nm_setting_wireless_get_mac_address (NMSettingWireless *setting);
+const GByteArray *nm_setting_wireless_get_cloned_mac_address (NMSettingWireless *setting);
+
+const GSList *nm_setting_wireless_get_mac_address_blacklist (NMSettingWireless *setting);
+NM_AVAILABLE_IN_0_9_10
+guint32 nm_setting_wireless_get_num_mac_blacklist_items (NMSettingWireless *setting);
+NM_AVAILABLE_IN_0_9_10
+const char * nm_setting_wireless_get_mac_blacklist_item (NMSettingWireless *setting,
+ guint32 idx);
+NM_AVAILABLE_IN_0_9_10
+gboolean nm_setting_wireless_add_mac_blacklist_item (NMSettingWireless *setting,
+ const char *mac);
+NM_AVAILABLE_IN_0_9_10
+void nm_setting_wireless_remove_mac_blacklist_item (NMSettingWireless *setting,
+ guint32 idx);
+NM_AVAILABLE_IN_0_9_10
+gboolean nm_setting_wireless_remove_mac_blacklist_item_by_value (NMSettingWireless *setting,
+ const char *mac);
+NM_AVAILABLE_IN_0_9_10
+void nm_setting_wireless_clear_mac_blacklist_items (NMSettingWireless *setting);
+
+guint32 nm_setting_wireless_get_mtu (NMSettingWireless *setting);
+gboolean nm_setting_wireless_get_hidden (NMSettingWireless *setting);
+
+gboolean nm_setting_wireless_add_seen_bssid (NMSettingWireless *setting,
+ const char *bssid);
+
+guint32 nm_setting_wireless_get_num_seen_bssids (NMSettingWireless *setting);
+const char *nm_setting_wireless_get_seen_bssid (NMSettingWireless *setting,
+ guint32 i);
+
+gboolean nm_setting_wireless_ap_security_compatible (NMSettingWireless *s_wireless,
+ NMSettingWirelessSecurity *s_wireless_sec,
+ NM80211ApFlags ap_flags,
+ NM80211ApSecurityFlags ap_wpa,
+ NM80211ApSecurityFlags ap_rsn,
+ NM80211Mode ap_mode);
+
+/* Deprecated */
+NM_DEPRECATED_IN_0_9_10
+const char *nm_setting_wireless_get_security (NMSettingWireless *setting);
+
+G_END_DECLS
+
+#endif /* NM_SETTING_WIRELESS_H */
diff --git a/libnm-core/nm-setting.c b/libnm-core/nm-setting.c
new file mode 100644
index 0000000000..94a0cac6e9
--- /dev/null
+++ b/libnm-core/nm-setting.c
@@ -0,0 +1,1445 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2011 Red Hat, Inc.
+ * Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#include <string.h>
+#include <glib/gi18n.h>
+
+#include "nm-setting.h"
+#include "nm-setting-private.h"
+#include "nm-setting-connection.h"
+#include "nm-utils.h"
+#include "nm-utils-private.h"
+
+/**
+ * SECTION:nm-setting
+ * @short_description: Describes related configuration information
+ * @include: nm-setting.h
+ *
+ * Each #NMSetting contains properties that describe configuration that applies
+ * to a specific network layer (like IPv4 or IPv6 configuration) or device type
+ * (like Ethernet, or Wi-Fi). A collection of individual settings together
+ * make up an #NMConnection. Each property is strongly typed and usually has
+ * a number of allowed values. See each #NMSetting subclass for a description
+ * of properties and allowed values.
+ */
+
+/**
+ * nm_setting_error_quark:
+ *
+ * Registers an error quark for #NMSetting if necessary.
+ *
+ * Returns: the error quark used for NMSetting errors.
+ **/
+GQuark
+nm_setting_error_quark (void)
+{
+ static GQuark quark;
+
+ if (G_UNLIKELY (!quark))
+ quark = g_quark_from_static_string ("nm-setting-error-quark");
+ return quark;
+}
+
+G_DEFINE_ABSTRACT_TYPE (NMSetting, nm_setting, G_TYPE_OBJECT)
+
+#define NM_SETTING_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING, NMSettingPrivate))
+
+typedef struct {
+ const char *name;
+ GType type;
+ guint32 priority;
+ GQuark error_quark;
+} SettingInfo;
+
+typedef struct {
+ const SettingInfo *info;
+} NMSettingPrivate;
+
+enum {
+ PROP_0,
+ PROP_NAME,
+
+ PROP_LAST
+};
+
+/*************************************************************/
+
+static GHashTable *registered_settings = NULL;
+static GHashTable *registered_settings_by_type = NULL;
+
+static gboolean
+_nm_gtype_equal (gconstpointer v1, gconstpointer v2)
+{
+ return *((const GType *) v1) == *((const GType *) v2);
+}
+static guint
+_nm_gtype_hash (gconstpointer v)
+{
+ return *((const GType *) v);
+}
+
+static void __attribute__((constructor))
+_ensure_registered (void)
+{
+ if (G_UNLIKELY (registered_settings == NULL)) {
+#if !GLIB_CHECK_VERSION (2, 35, 0)
+ g_type_init ();
+#endif
+ _nm_value_transforms_register ();
+ registered_settings = g_hash_table_new (g_str_hash, g_str_equal);
+ registered_settings_by_type = g_hash_table_new (_nm_gtype_hash, _nm_gtype_equal);
+ }
+}
+
+#define _ensure_setting_info(self, priv) \
+ G_STMT_START { \
+ NMSettingPrivate *_priv_esi = (priv); \
+ if (G_UNLIKELY (!_priv_esi->info)) { \
+ _priv_esi->info = _nm_setting_lookup_setting_by_type (G_OBJECT_TYPE (self)); \
+ g_assert (_priv_esi->info); \
+ } \
+ } G_STMT_END
+
+/*************************************************************/
+
+/*
+ * _nm_register_setting:
+ * @name: the name of the #NMSetting object to register
+ * @type: the #GType of the #NMSetting
+ * @priority: the sort priority of the setting, see below
+ * @error_quark: the setting's error quark
+ *
+ * INTERNAL ONLY: registers a setting's internal properties, like its priority
+ * and its error quark type, with libnm-util.
+ *
+ * A setting's priority should roughly follow the OSI layer model, but it also
+ * controls which settings get asked for secrets first. Thus settings which
+ * relate to things that must be working first, like hardware, should get a
+ * higher priority than things which layer on top of the hardware. For example,
+ * the GSM/CDMA settings should provide secrets before the PPP setting does,
+ * because a PIN is required to unlock the device before PPP can even start.
+ * Even settings without secrets should be assigned the right priority.
+ *
+ * 0: reserved for the Connection setting
+ *
+ * 1: hardware-related settings like Ethernet, Wi-Fi, InfiniBand, Bridge, etc.
+ * These priority 1 settings are also "base types", which means that at least
+ * one of them is required for the connection to be valid, and their name is
+ * valid in the 'type' property of the Connection setting.
+ *
+ * 2: hardware-related auxiliary settings that require a base setting to be
+ * successful first, like Wi-Fi security, 802.1x, etc.
+ *
+ * 3: hardware-independent settings that are required before IP connectivity
+ * can be established, like PPP, PPPoE, etc.
+ *
+ * 4: IP-level stuff
+ */
+void
+(_nm_register_setting) (const char *name,
+ const GType type,
+ const guint32 priority,
+ const GQuark error_quark)
+{
+ SettingInfo *info;
+
+ g_return_if_fail (name != NULL && *name);
+ g_return_if_fail (type != G_TYPE_INVALID);
+ g_return_if_fail (type != G_TYPE_NONE);
+ g_return_if_fail (error_quark != 0);
+ g_return_if_fail (priority <= 4);
+
+ _ensure_registered ();
+
+ if (G_LIKELY ((info = g_hash_table_lookup (registered_settings, name)))) {
+ g_return_if_fail (info->type == type);
+ g_return_if_fail (info->error_quark == error_quark);
+ g_return_if_fail (info->priority == priority);
+ g_return_if_fail (g_strcmp0 (info->name, name) == 0);
+ return;
+ }
+ g_return_if_fail (g_hash_table_lookup (registered_settings_by_type, &type) == NULL);
+
+ if (priority == 0)
+ g_assert_cmpstr (name, ==, NM_SETTING_CONNECTION_SETTING_NAME);
+
+ info = g_slice_new0 (SettingInfo);
+ info->type = type;
+ info->priority = priority;
+ info->error_quark = error_quark;
+ info->name = name;
+ g_hash_table_insert (registered_settings, (void *) info->name, info);
+ g_hash_table_insert (registered_settings_by_type, &info->type, info);
+}
+
+static const SettingInfo *
+_nm_setting_lookup_setting_by_type (GType type)
+{
+ _ensure_registered ();
+ return g_hash_table_lookup (registered_settings_by_type, &type);
+}
+
+static guint32
+_get_setting_type_priority (GType type)
+{
+ const SettingInfo *info;
+
+ g_return_val_if_fail (g_type_is_a (type, NM_TYPE_SETTING), G_MAXUINT32);
+
+ info = _nm_setting_lookup_setting_by_type (type);
+ return info->priority;
+}
+
+gboolean
+_nm_setting_type_is_base_type (GType type)
+{
+ /* Historical oddity: PPPoE is a base-type even though it's not
+ * priority 1. It needs to be sorted *after* lower-level stuff like
+ * Wi-Fi security or 802.1x for secrets, but it's still allowed as a
+ * base type.
+ */
+ return _get_setting_type_priority (type) == 1 || (type == NM_TYPE_SETTING_PPPOE);
+}
+
+gboolean
+_nm_setting_is_base_type (NMSetting *setting)
+{
+ return _nm_setting_type_is_base_type (G_OBJECT_TYPE (setting));
+}
+
+GType
+_nm_setting_lookup_setting_type (const char *name)
+{
+ SettingInfo *info;
+
+ g_return_val_if_fail (name != NULL, G_TYPE_NONE);
+
+ _ensure_registered ();
+
+ info = g_hash_table_lookup (registered_settings, name);
+ return info ? info->type : G_TYPE_INVALID;
+}
+
+GType
+_nm_setting_lookup_setting_type_by_quark (GQuark error_quark)
+{
+ SettingInfo *info;
+ GHashTableIter iter;
+
+ _ensure_registered ();
+
+ g_hash_table_iter_init (&iter, registered_settings);
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer) &info)) {
+ if (info->error_quark == error_quark)
+ return info->type;
+ }
+ return G_TYPE_INVALID;
+}
+
+gint
+_nm_setting_compare_priority (gconstpointer a, gconstpointer b)
+{
+ guint32 prio_a, prio_b;
+
+ prio_a = _get_setting_type_priority (G_OBJECT_TYPE (a));
+ prio_b = _get_setting_type_priority (G_OBJECT_TYPE (b));
+
+ if (prio_a < prio_b)
+ return -1;
+ else if (prio_a == prio_b)
+ return 0;
+ return 1;
+}
+
+/*************************************************************/
+
+static void
+destroy_gvalue (gpointer data)
+{
+ GValue *value = (GValue *) data;
+
+ g_value_unset (value);
+ g_slice_free (GValue, value);
+}
+
+/**
+ * nm_setting_to_hash:
+ * @setting: the #NMSetting
+ * @flags: hash flags, e.g. %NM_SETTING_HASH_FLAG_ALL
+ *
+ * Converts the #NMSetting into a #GHashTable mapping each setting property
+ * name to a GValue describing that property, suitable for marshalling over
+ * D-Bus or serializing. The mapping is string to GValue.
+ *
+ * Returns: (transfer full) (element-type utf8 GObject.Value): a new #GHashTable
+ * describing the setting's properties
+ **/
+GHashTable *
+nm_setting_to_hash (NMSetting *setting, NMSettingHashFlags flags)
+{
+ GHashTable *hash;
+ GParamSpec **property_specs;
+ guint n_property_specs;
+ guint i;
+
+ g_return_val_if_fail (NM_IS_SETTING (setting), NULL);
+
+ property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (setting), &n_property_specs);
+ if (!property_specs) {
+ g_warning ("%s: couldn't find property specs for object of type '%s'",
+ __func__, g_type_name (G_OBJECT_TYPE (setting)));
+ return NULL;
+ }
+
+ hash = g_hash_table_new_full (g_str_hash, g_str_equal,
+ (GDestroyNotify) g_free, destroy_gvalue);
+
+ for (i = 0; i < n_property_specs; i++) {
+ GParamSpec *prop_spec = property_specs[i];
+ GValue *value;
+
+ /* 'name' doesn't get serialized */
+ if (strcmp (g_param_spec_get_name (prop_spec), NM_SETTING_NAME) == 0)
+ continue;
+
+ if ( (flags & NM_SETTING_HASH_FLAG_NO_SECRETS)
+ && (prop_spec->flags & NM_SETTING_PARAM_SECRET))
+ continue;
+
+ if ( (flags & NM_SETTING_HASH_FLAG_ONLY_SECRETS)
+ && !(prop_spec->flags & NM_SETTING_PARAM_SECRET))
+ continue;
+
+ value = g_slice_new0 (GValue);
+ g_value_init (value, prop_spec->value_type);
+ g_object_get_property (G_OBJECT (setting), prop_spec->name, value);
+
+ /* Don't serialize values with default values */
+ if (!g_param_value_defaults (prop_spec, value))
+ g_hash_table_insert (hash, g_strdup (prop_spec->name), value);
+ else
+ destroy_gvalue (value);
+ }
+ g_free (property_specs);
+
+ /* Don't return empty hashes, except for base types */
+ if (g_hash_table_size (hash) < 1 && !_nm_setting_is_base_type (setting)) {
+ g_hash_table_destroy (hash);
+ hash = NULL;
+ }
+
+ return hash;
+}
+
+/**
+ * nm_setting_new_from_hash:
+ * @setting_type: the #NMSetting type which the hash contains properties for
+ * @hash: (element-type utf8 GObject.Value): the #GHashTable containing a
+ * string to GValue mapping of properties that apply to the setting
+ *
+ * Creates a new #NMSetting object and populates that object with the properties
+ * contained in the hash table, using each hash key as the property to set,
+ * and each hash value as the value to set that property to. Setting properties
+ * are strongly typed, thus the GValue type of the hash value must be correct.
+ * See the documentation on each #NMSetting object subclass for the correct
+ * property names and value types.
+ *
+ * Returns: a new #NMSetting object populated with the properties from the
+ * hash table, or %NULL on failure
+ **/
+NMSetting *
+nm_setting_new_from_hash (GType setting_type, GHashTable *hash)
+{
+ GHashTableIter iter;
+ NMSetting *setting;
+ const char *prop_name;
+ GValue *src_value;
+ GObjectClass *class;
+ guint n_params = 0;
+ GParameter *params;
+ int i;
+
+ g_return_val_if_fail (G_TYPE_IS_INSTANTIATABLE (setting_type), NULL);
+ g_return_val_if_fail (hash != NULL, NULL);
+
+ /* g_type_class_ref() ensures the setting class is created if it hasn't
+ * already been used.
+ */
+ class = g_type_class_ref (setting_type);
+ params = g_new0 (GParameter, g_hash_table_size (hash));
+
+ g_hash_table_iter_init (&iter, hash);
+ while (g_hash_table_iter_next (&iter, (gpointer) &prop_name, (gpointer) &src_value)) {
+ GValue *dst_value = &params[n_params].value;
+ GParamSpec *param_spec;
+
+ param_spec = g_object_class_find_property (class, prop_name);
+ if (!param_spec) {
+ /* Oh, we're so nice and only warn, maybe it should be a fatal error? */
+ g_warning ("Ignoring invalid property '%s'", prop_name);
+ continue;
+ }
+
+ g_value_init (dst_value, G_VALUE_TYPE (src_value));
+ if (g_value_transform (src_value, dst_value))
+ params[n_params++].name = prop_name;
+ else {
+ g_warning ("Ignoring property '%s' with invalid type (%s)",
+ prop_name, G_VALUE_TYPE_NAME (src_value));
+ g_value_unset (dst_value);
+ }
+ }
+
+ setting = (NMSetting *) g_object_newv (setting_type, n_params, params);
+
+ for (i = 0; i < n_params; i++)
+ g_value_unset (&params[i].value);
+
+ g_free (params);
+ g_type_class_unref (class);
+
+ return setting;
+}
+
+static void
+duplicate_setting (NMSetting *setting,
+ const char *name,
+ const GValue *value,
+ GParamFlags flags,
+ gpointer user_data)
+{
+ if ((flags & (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)) == G_PARAM_WRITABLE)
+ g_object_set_property (G_OBJECT (user_data), name, value);
+}
+
+/**
+ * nm_setting_duplicate:
+ * @setting: the #NMSetting to duplicate
+ *
+ * Duplicates a #NMSetting.
+ *
+ * Returns: (transfer full): a new #NMSetting containing the same properties and values as the
+ * source #NMSetting
+ **/
+NMSetting *
+nm_setting_duplicate (NMSetting *setting)
+{
+ GObject *dup;
+
+ g_return_val_if_fail (NM_IS_SETTING (setting), NULL);
+
+ dup = g_object_new (G_OBJECT_TYPE (setting), NULL);
+
+ g_object_freeze_notify (dup);
+ nm_setting_enumerate_values (setting, duplicate_setting, dup);
+ g_object_thaw_notify (dup);
+
+ return NM_SETTING (dup);
+}
+
+static gint
+find_setting_by_name (gconstpointer a, gconstpointer b)
+{
+ NMSetting *setting = NM_SETTING (a);
+ const char *str = (const char *) b;
+
+ return strcmp (nm_setting_get_name (setting), str);
+}
+
+NMSetting *
+nm_setting_find_in_list (GSList *settings_list,
+ const char *setting_name)
+{
+ GSList *found;
+
+ found = g_slist_find_custom (settings_list, setting_name, find_setting_by_name);
+ if (found)
+ return found->data;
+ else
+ return NULL;
+}
+
+/**
+ * nm_setting_get_name:
+ * @setting: the #NMSetting
+ *
+ * Returns the type name of the #NMSetting object
+ *
+ * Returns: a string containing the type name of the #NMSetting object,
+ * like 'ppp' or 'wireless' or 'wired'.
+ **/
+const char *
+nm_setting_get_name (NMSetting *setting)
+{
+ NMSettingPrivate *priv;
+
+ g_return_val_if_fail (NM_IS_SETTING (setting), NULL);
+ priv = NM_SETTING_GET_PRIVATE (setting);
+ _ensure_setting_info (setting, priv);
+ return priv->info->name;
+}
+
+/**
+ * nm_setting_verify:
+ * @setting: the #NMSetting to verify
+ * @all_settings: (element-type NMSetting): a #GSList of all settings
+ * in the connection from which @setting came
+ * @error: location to store error, or %NULL
+ *
+ * Validates the setting. Each setting's properties have allowed values, and
+ * some are dependent on other values (hence the need for @all_settings). The
+ * returned #GError contains information about which property of the setting
+ * failed validation, and in what way that property failed validation.
+ *
+ * Returns: %TRUE if the setting is valid, %FALSE if it is not
+ **/
+gboolean
+nm_setting_verify (NMSetting *setting, GSList *all_settings, GError **error)
+{
+ NMSettingVerifyResult result = _nm_setting_verify (setting, all_settings, error);
+
+ if (result == NM_SETTING_VERIFY_NORMALIZABLE)
+ g_clear_error (error);
+
+ return result == NM_SETTING_VERIFY_SUCCESS || result == NM_SETTING_VERIFY_NORMALIZABLE;
+}
+
+NMSettingVerifyResult
+_nm_setting_verify (NMSetting *setting, GSList *all_settings, GError **error)
+{
+ g_return_val_if_fail (NM_IS_SETTING (setting), NM_SETTING_VERIFY_ERROR);
+ g_return_val_if_fail (!error || *error == NULL, NM_SETTING_VERIFY_ERROR);
+
+ if (NM_SETTING_GET_CLASS (setting)->verify)
+ return NM_SETTING_GET_CLASS (setting)->verify (setting, all_settings, error);
+
+ return NM_SETTING_VERIFY_SUCCESS;
+}
+
+static gboolean
+compare_property (NMSetting *setting,
+ NMSetting *other,
+ const GParamSpec *prop_spec,
+ NMSettingCompareFlags flags)
+{
+ GValue value1 = G_VALUE_INIT;
+ GValue value2 = G_VALUE_INIT;
+ gboolean different;
+
+ /* Handle compare flags */
+ if (prop_spec->flags & NM_SETTING_PARAM_SECRET) {
+ NMSettingSecretFlags a_secret_flags = NM_SETTING_SECRET_FLAG_NONE;
+ NMSettingSecretFlags b_secret_flags = NM_SETTING_SECRET_FLAG_NONE;
+
+ nm_setting_get_secret_flags (setting, prop_spec->name, &a_secret_flags, NULL);
+ nm_setting_get_secret_flags (other, prop_spec->name, &b_secret_flags, NULL);
+
+ /* If the secret flags aren't the same the settings aren't the same */
+ if (a_secret_flags != b_secret_flags)
+ return FALSE;
+
+ /* Check for various secret flags that might cause us to ignore comparing
+ * this property.
+ */
+ if ( (flags & NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS)
+ && (a_secret_flags & NM_SETTING_SECRET_FLAG_AGENT_OWNED))
+ return TRUE;
+
+ if ( (flags & NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS)
+ && (a_secret_flags & NM_SETTING_SECRET_FLAG_NOT_SAVED))
+ return TRUE;
+ }
+
+ g_value_init (&value1, prop_spec->value_type);
+ g_object_get_property (G_OBJECT (setting), prop_spec->name, &value1);
+
+ g_value_init (&value2, prop_spec->value_type);
+ g_object_get_property (G_OBJECT (other), prop_spec->name, &value2);
+
+ different = g_param_values_cmp ((GParamSpec *) prop_spec, &value1, &value2);
+
+ g_value_unset (&value1);
+ g_value_unset (&value2);
+
+ return different == 0 ? TRUE : FALSE;
+}
+
+/**
+ * nm_setting_compare:
+ * @a: a #NMSetting
+ * @b: a second #NMSetting to compare with the first
+ * @flags: compare flags, e.g. %NM_SETTING_COMPARE_FLAG_EXACT
+ *
+ * Compares two #NMSetting objects for similarity, with comparison behavior
+ * modified by a set of flags. See the documentation for #NMSettingCompareFlags
+ * for a description of each flag's behavior.
+ *
+ * Returns: %TRUE if the comparison succeeds, %FALSE if it does not
+ **/
+gboolean
+nm_setting_compare (NMSetting *a,
+ NMSetting *b,
+ NMSettingCompareFlags flags)
+{
+ GParamSpec **property_specs;
+ guint n_property_specs;
+ gint same = TRUE;
+ guint i;
+
+ g_return_val_if_fail (NM_IS_SETTING (a), FALSE);
+ g_return_val_if_fail (NM_IS_SETTING (b), FALSE);
+
+ /* First check that both have the same type */
+ if (G_OBJECT_TYPE (a) != G_OBJECT_TYPE (b))
+ return FALSE;
+
+ /* And now all properties */
+ property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (a), &n_property_specs);
+ for (i = 0; i < n_property_specs && same; i++) {
+ GParamSpec *prop_spec = property_specs[i];
+
+ /* Fuzzy compare ignores secrets and properties defined with the FUZZY_IGNORE flag */
+ if ( (flags & NM_SETTING_COMPARE_FLAG_FUZZY)
+ && (prop_spec->flags & (NM_SETTING_PARAM_FUZZY_IGNORE | NM_SETTING_PARAM_SECRET)))
+ continue;
+
+ if ((flags & NM_SETTING_COMPARE_FLAG_INFERRABLE) && !(prop_spec->flags & NM_SETTING_PARAM_INFERRABLE))
+ continue;
+
+ if ( (flags & NM_SETTING_COMPARE_FLAG_IGNORE_SECRETS)
+ && (prop_spec->flags & NM_SETTING_PARAM_SECRET))
+ continue;
+
+ same = NM_SETTING_GET_CLASS (a)->compare_property (a, b, prop_spec, flags);
+ }
+ g_free (property_specs);
+
+ return same;
+}
+
+static inline gboolean
+should_compare_prop (NMSetting *setting,
+ const char *prop_name,
+ NMSettingCompareFlags comp_flags,
+ GParamFlags prop_flags)
+{
+ /* Fuzzy compare ignores secrets and properties defined with the FUZZY_IGNORE flag */
+ if ( (comp_flags & NM_SETTING_COMPARE_FLAG_FUZZY)
+ && (prop_flags & (NM_SETTING_PARAM_FUZZY_IGNORE | NM_SETTING_PARAM_SECRET)))
+ return FALSE;
+
+ if ((comp_flags & NM_SETTING_COMPARE_FLAG_INFERRABLE) && !(prop_flags & NM_SETTING_PARAM_INFERRABLE))
+ return FALSE;
+
+ if (prop_flags & NM_SETTING_PARAM_SECRET) {
+ NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE;
+
+ if (comp_flags & NM_SETTING_COMPARE_FLAG_IGNORE_SECRETS)
+ return FALSE;
+
+ nm_setting_get_secret_flags (setting, prop_name, &secret_flags, NULL);
+
+ if ( (comp_flags & NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS)
+ && (secret_flags & NM_SETTING_SECRET_FLAG_AGENT_OWNED))
+ return FALSE;
+
+ if ( (comp_flags & NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS)
+ && (secret_flags & NM_SETTING_SECRET_FLAG_NOT_SAVED))
+ return FALSE;
+ }
+
+ if ( (comp_flags & NM_SETTING_COMPARE_FLAG_IGNORE_ID)
+ && NM_IS_SETTING_CONNECTION (setting)
+ && !strcmp (prop_name, NM_SETTING_CONNECTION_ID))
+ return FALSE;
+
+ return TRUE;
+}
+
+/**
+ * nm_setting_diff:
+ * @a: a #NMSetting
+ * @b: a second #NMSetting to compare with the first
+ * @flags: compare flags, e.g. %NM_SETTING_COMPARE_FLAG_EXACT
+ * @invert_results: this parameter is used internally by libnm-util and should
+ * be set to %FALSE. If %TRUE inverts the meaning of the #NMSettingDiffResult.
+ * @results: (inout) (transfer full) (element-type utf8 guint32): if the
+ * settings differ, on return a hash table mapping the differing keys to one or
+ * more %NMSettingDiffResult values OR-ed together. If the settings do not
+ * differ, any hash table passed in is unmodified. If no hash table is passed
+ * in and the settings differ, a new one is created and returned.
+ *
+ * Compares two #NMSetting objects for similarity, with comparison behavior
+ * modified by a set of flags. See the documentation for #NMSettingCompareFlags
+ * for a description of each flag's behavior. If the settings differ, the keys
+ * of each setting that differ from the other are added to @results, mapped to
+ * one or more #NMSettingDiffResult values.
+ *
+ * Returns: %TRUE if the settings contain the same values, %FALSE if they do not
+ **/
+gboolean
+nm_setting_diff (NMSetting *a,
+ NMSetting *b,
+ NMSettingCompareFlags flags,
+ gboolean invert_results,
+ GHashTable **results)
+{
+ GParamSpec **property_specs;
+ guint n_property_specs;
+ guint i;
+ NMSettingDiffResult a_result = NM_SETTING_DIFF_RESULT_IN_A;
+ NMSettingDiffResult b_result = NM_SETTING_DIFF_RESULT_IN_B;
+ gboolean results_created = FALSE;
+
+ g_return_val_if_fail (results != NULL, FALSE);
+ g_return_val_if_fail (NM_IS_SETTING (a), FALSE);
+ if (b) {
+ g_return_val_if_fail (NM_IS_SETTING (b), FALSE);
+ g_return_val_if_fail (G_OBJECT_TYPE (a) == G_OBJECT_TYPE (b), FALSE);
+ }
+
+ /* If the caller is calling this function in a pattern like this to get
+ * complete diffs:
+ *
+ * nm_setting_diff (A, B, FALSE, &results);
+ * nm_setting_diff (B, A, TRUE, &results);
+ *
+ * and wants us to invert the results so that the second invocation comes
+ * out correctly, do that here.
+ */
+ if (invert_results) {
+ a_result = NM_SETTING_DIFF_RESULT_IN_B;
+ b_result = NM_SETTING_DIFF_RESULT_IN_A;
+ }
+
+ if (*results == NULL) {
+ *results = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+ results_created = TRUE;
+ }
+
+ /* And now all properties */
+ property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (a), &n_property_specs);
+
+ for (i = 0; i < n_property_specs; i++) {
+ GParamSpec *prop_spec = property_specs[i];
+ NMSettingDiffResult r = NM_SETTING_DIFF_RESULT_UNKNOWN, tmp;
+ gboolean different = TRUE;
+
+ /* Handle compare flags */
+ if (!should_compare_prop (a, prop_spec->name, flags, prop_spec->flags))
+ continue;
+ if (strcmp (prop_spec->name, NM_SETTING_NAME) == 0)
+ continue;
+
+ if (b) {
+ different = !NM_SETTING_GET_CLASS (a)->compare_property (a, b, prop_spec, flags);
+ if (different) {
+ GValue value = G_VALUE_INIT;
+
+ g_value_init (&value, prop_spec->value_type);
+ g_object_get_property (G_OBJECT (a), prop_spec->name, &value);
+ if (!g_param_value_defaults (prop_spec, &value))
+ r |= a_result;
+
+ g_value_reset (&value);
+ g_object_get_property (G_OBJECT (b), prop_spec->name, &value);
+ if (!g_param_value_defaults (prop_spec, &value))
+ r |= b_result;
+
+ g_value_unset (&value);
+ }
+ } else
+ r = a_result; /* only in A */
+
+ if (different) {
+ tmp = GPOINTER_TO_UINT (g_hash_table_lookup (*results, prop_spec->name));
+ g_hash_table_insert (*results, g_strdup (prop_spec->name), GUINT_TO_POINTER (tmp | r));
+ }
+ }
+ g_free (property_specs);
+
+ /* Don't return an empty hash table */
+ if (results_created && !g_hash_table_size (*results)) {
+ g_hash_table_destroy (*results);
+ *results = NULL;
+ }
+
+ return !(*results);
+}
+
+/**
+ * nm_setting_enumerate_values:
+ * @setting: the #NMSetting
+ * @func: (scope call): user-supplied function called for each property of the setting
+ * @user_data: user data passed to @func at each invocation
+ *
+ * Iterates over each property of the #NMSetting object, calling the supplied
+ * user function for each property.
+ **/
+void
+nm_setting_enumerate_values (NMSetting *setting,
+ NMSettingValueIterFn func,
+ gpointer user_data)
+{
+ GParamSpec **property_specs;
+ guint n_property_specs;
+ int i;
+
+ g_return_if_fail (NM_IS_SETTING (setting));
+ g_return_if_fail (func != NULL);
+
+ property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (setting), &n_property_specs);
+ for (i = 0; i < n_property_specs; i++) {
+ GParamSpec *prop_spec = property_specs[i];
+ GValue value = G_VALUE_INIT;
+
+ g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (prop_spec));
+ g_object_get_property (G_OBJECT (setting), prop_spec->name, &value);
+ func (setting, prop_spec->name, &value, prop_spec->flags, user_data);
+ g_value_unset (&value);
+ }
+
+ g_free (property_specs);
+}
+
+/**
+ * nm_setting_clear_secrets:
+ * @setting: the #NMSetting
+ *
+ * Resets and clears any secrets in the setting. Secrets should be added to the
+ * setting only when needed, and cleared immediately after use to prevent
+ * leakage of information.
+ **/
+void
+nm_setting_clear_secrets (NMSetting *setting)
+{
+ _nm_setting_clear_secrets (setting);
+}
+
+gboolean
+_nm_setting_clear_secrets (NMSetting *setting)
+{
+ GParamSpec **property_specs;
+ guint n_property_specs;
+ guint i;
+ gboolean changed = FALSE;
+
+ g_return_val_if_fail (NM_IS_SETTING (setting), FALSE);
+
+ property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (setting), &n_property_specs);
+
+ for (i = 0; i < n_property_specs; i++) {
+ GParamSpec *prop_spec = property_specs[i];
+
+ if (prop_spec->flags & NM_SETTING_PARAM_SECRET) {
+ GValue value = G_VALUE_INIT;
+
+ g_value_init (&value, prop_spec->value_type);
+ g_object_get_property (G_OBJECT (setting), prop_spec->name, &value);
+ if (!g_param_value_defaults (prop_spec, &value)) {
+ g_param_value_set_default (prop_spec, &value);
+ g_object_set_property (G_OBJECT (setting), prop_spec->name, &value);
+ changed = TRUE;
+ }
+ g_value_unset (&value);
+ }
+ }
+
+ g_free (property_specs);
+
+ return changed;
+}
+
+static gboolean
+clear_secrets_with_flags (NMSetting *setting,
+ GParamSpec *pspec,
+ NMSettingClearSecretsWithFlagsFn func,
+ gpointer user_data)
+{
+ NMSettingSecretFlags flags = NM_SETTING_SECRET_FLAG_NONE;
+ gboolean changed = FALSE;
+
+ /* Clear the secret if the user function says to do so */
+ nm_setting_get_secret_flags (setting, pspec->name, &flags, NULL);
+ if (func (setting, pspec->name, flags, user_data) == TRUE) {
+ GValue value = G_VALUE_INIT;
+
+ g_value_init (&value, pspec->value_type);
+ g_object_get_property (G_OBJECT (setting), pspec->name, &value);
+ if (!g_param_value_defaults (pspec, &value)) {
+ g_param_value_set_default (pspec, &value);
+ g_object_set_property (G_OBJECT (setting), pspec->name, &value);
+ changed = TRUE;
+ }
+ g_value_unset (&value);
+ }
+
+ return changed;
+}
+
+/**
+ * nm_setting_clear_secrets_with_flags:
+ * @setting: the #NMSetting
+ * @func: (scope call): function to be called to determine whether a
+ * specific secret should be cleared or not
+ * @user_data: caller-supplied data passed to @func
+ *
+ * Clears and frees secrets determined by @func.
+ **/
+void
+nm_setting_clear_secrets_with_flags (NMSetting *setting,
+ NMSettingClearSecretsWithFlagsFn func,
+ gpointer user_data)
+{
+ _nm_setting_clear_secrets_with_flags (setting, func, user_data);
+}
+
+gboolean
+_nm_setting_clear_secrets_with_flags (NMSetting *setting,
+ NMSettingClearSecretsWithFlagsFn func,
+ gpointer user_data)
+{
+ GParamSpec **property_specs;
+ guint n_property_specs;
+ guint i;
+ gboolean changed = FALSE;
+
+ g_return_val_if_fail (setting, FALSE);
+ g_return_val_if_fail (NM_IS_SETTING (setting), FALSE);
+ g_return_val_if_fail (func != NULL, FALSE);
+
+ property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (setting), &n_property_specs);
+ for (i = 0; i < n_property_specs; i++) {
+ if (property_specs[i]->flags & NM_SETTING_PARAM_SECRET) {
+ changed |= NM_SETTING_GET_CLASS (setting)->clear_secrets_with_flags (setting,
+ property_specs[i],
+ func,
+ user_data);
+ }
+ }
+
+ g_free (property_specs);
+ return changed;
+}
+
+/**
+ * nm_setting_need_secrets:
+ * @setting: the #NMSetting
+ *
+ * Returns an array of property names for each secret which may be required
+ * to make a successful connection. The returned hints are only intended as a
+ * guide to what secrets may be required, because in some circumstances, there
+ * is no way to conclusively determine exactly which secrets are needed.
+ *
+ * Returns: (transfer container) (element-type utf8): a #GPtrArray containing
+ * the property names of secrets of the #NMSetting which may be required; the
+ * caller owns the array and must free it with g_ptr_array_free(), but must not
+ * free the elements.
+ **/
+GPtrArray *
+nm_setting_need_secrets (NMSetting *setting)
+{
+ GPtrArray *secrets = NULL;
+
+ g_return_val_if_fail (NM_IS_SETTING (setting), NULL);
+
+ if (NM_SETTING_GET_CLASS (setting)->need_secrets)
+ secrets = NM_SETTING_GET_CLASS (setting)->need_secrets (setting);
+
+ return secrets;
+}
+
+static int
+update_one_secret (NMSetting *setting, const char *key, GValue *value, GError **error)
+{
+ GParamSpec *prop_spec;
+ GValue transformed_value = G_VALUE_INIT;
+
+ prop_spec = g_object_class_find_property (G_OBJECT_GET_CLASS (setting), key);
+ if (!prop_spec) {
+ g_set_error (error,
+ NM_SETTING_ERROR,
+ NM_SETTING_ERROR_PROPERTY_NOT_FOUND,
+ "%s", key);
+ return NM_SETTING_UPDATE_SECRET_ERROR;
+ }
+
+ /* Silently ignore non-secrets */
+ if (!(prop_spec->flags & NM_SETTING_PARAM_SECRET))
+ return NM_SETTING_UPDATE_SECRET_SUCCESS_UNCHANGED;
+
+ if (g_value_type_compatible (G_VALUE_TYPE (value), G_PARAM_SPEC_VALUE_TYPE (prop_spec))) {
+ if (G_VALUE_HOLDS_STRING (value) && G_IS_PARAM_SPEC_STRING (prop_spec)) {
+ /* String is expected to be a common case. Handle it specially and check whether
+ * the value is already set. Otherwise, we just reset the property and
+ * assume the value got modified. */
+ char *v;
+
+ g_object_get (G_OBJECT (setting), prop_spec->name, &v, NULL);
+ if (g_strcmp0 (v, g_value_get_string (value)) == 0) {
+ g_free (v);
+ return NM_SETTING_UPDATE_SECRET_SUCCESS_UNCHANGED;
+ }
+ g_free (v);
+ }
+ g_object_set_property (G_OBJECT (setting), prop_spec->name, value);
+ return NM_SETTING_UPDATE_SECRET_SUCCESS_MODIFIED;
+ }
+ if (g_value_transform (value, &transformed_value)) {
+ g_object_set_property (G_OBJECT (setting), prop_spec->name, &transformed_value);
+ g_value_unset (&transformed_value);
+ return NM_SETTING_UPDATE_SECRET_SUCCESS_MODIFIED;
+ }
+ g_set_error (error,
+ NM_SETTING_ERROR,
+ NM_SETTING_ERROR_PROPERTY_TYPE_MISMATCH,
+ "%s", key);
+ return NM_SETTING_UPDATE_SECRET_ERROR;
+}
+
+/**
+ * nm_setting_update_secrets:
+ * @setting: the #NMSetting
+ * @secrets: (element-type utf8 GObject.Value): a #GHashTable mapping
+ * string to #GValue of setting property names and secrets
+ * @error: location to store error, or %NULL
+ *
+ * Update the setting's secrets, given a hash table of secrets intended for that
+ * setting (deserialized from D-Bus for example).
+ *
+ * Returns: %TRUE if the secrets were successfully updated, %FALSE on failure to
+ * update one or more of the secrets.
+ **/
+gboolean
+nm_setting_update_secrets (NMSetting *setting, GHashTable *secrets, GError **error)
+{
+ return _nm_setting_update_secrets (setting, secrets, error) != NM_SETTING_UPDATE_SECRET_ERROR;
+}
+
+NMSettingUpdateSecretResult
+_nm_setting_update_secrets (NMSetting *setting, GHashTable *secrets, GError **error)
+{
+ GHashTableIter iter;
+ gpointer key, data;
+ GError *tmp_error = NULL;
+ NMSettingUpdateSecretResult result = NM_SETTING_UPDATE_SECRET_SUCCESS_UNCHANGED;
+
+ g_return_val_if_fail (NM_IS_SETTING (setting), NM_SETTING_UPDATE_SECRET_ERROR);
+ g_return_val_if_fail (secrets != NULL, NM_SETTING_UPDATE_SECRET_ERROR);
+ if (error)
+ g_return_val_if_fail (*error == NULL, NM_SETTING_UPDATE_SECRET_ERROR);
+
+ g_hash_table_iter_init (&iter, secrets);
+ while (g_hash_table_iter_next (&iter, &key, &data)) {
+ int success;
+ const char *secret_key = (const char *) key;
+ GValue *secret_value = (GValue *) data;
+
+ success = NM_SETTING_GET_CLASS (setting)->update_one_secret (setting, secret_key, secret_value, &tmp_error);
+ g_assert (!((success == NM_SETTING_UPDATE_SECRET_ERROR) ^ (!!tmp_error)));
+
+ if (success == NM_SETTING_UPDATE_SECRET_ERROR) {
+ g_propagate_error (error, tmp_error);
+ return NM_SETTING_UPDATE_SECRET_ERROR;
+ }
+
+ if (success == NM_SETTING_UPDATE_SECRET_SUCCESS_MODIFIED)
+ result = NM_SETTING_UPDATE_SECRET_SUCCESS_MODIFIED;
+ }
+
+ return result;
+}
+
+static gboolean
+is_secret_prop (NMSetting *setting, const char *secret_name, GError **error)
+{
+ GParamSpec *pspec;
+
+ pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (setting), secret_name);
+ if (!pspec) {
+ g_set_error (error,
+ NM_SETTING_ERROR,
+ NM_SETTING_ERROR_PROPERTY_NOT_FOUND,
+ "Secret %s not provided by this setting", secret_name);
+ return FALSE;
+ }
+
+ if (!(pspec->flags & NM_SETTING_PARAM_SECRET)) {
+ g_set_error (error,
+ NM_SETTING_ERROR,
+ NM_SETTING_ERROR_PROPERTY_NOT_SECRET,
+ "Property %s is not a secret", secret_name);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+get_secret_flags (NMSetting *setting,
+ const char *secret_name,
+ gboolean verify_secret,
+ NMSettingSecretFlags *out_flags,
+ GError **error)
+{
+ char *flags_prop;
+ NMSettingSecretFlags flags = NM_SETTING_SECRET_FLAG_NONE;
+
+ if (verify_secret)
+ g_return_val_if_fail (is_secret_prop (setting, secret_name, error), FALSE);
+
+ flags_prop = g_strdup_printf ("%s-flags", secret_name);
+ g_object_get (G_OBJECT (setting), flags_prop, &flags, NULL);
+ g_free (flags_prop);
+
+ if (out_flags)
+ *out_flags = flags;
+ return TRUE;
+}
+
+/**
+ * nm_setting_get_secret_flags:
+ * @setting: the #NMSetting
+ * @secret_name: the secret key name to get flags for
+ * @out_flags: on success, the #NMSettingSecretFlags for the secret
+ * @error: location to store error, or %NULL
+ *
+ * For a given secret, retrieves the #NMSettingSecretFlags describing how to
+ * handle that secret.
+ *
+ * Returns: %TRUE on success (if the given secret name was a valid property of
+ * this setting, and if that property is secret), %FALSE if not
+ **/
+gboolean
+nm_setting_get_secret_flags (NMSetting *setting,
+ const char *secret_name,
+ NMSettingSecretFlags *out_flags,
+ GError **error)
+{
+ g_return_val_if_fail (NM_IS_SETTING (setting), FALSE);
+ g_return_val_if_fail (secret_name != NULL, FALSE);
+
+ return NM_SETTING_GET_CLASS (setting)->get_secret_flags (setting, secret_name, TRUE, out_flags, error);
+}
+
+static gboolean
+set_secret_flags (NMSetting *setting,
+ const char *secret_name,
+ gboolean verify_secret,
+ NMSettingSecretFlags flags,
+ GError **error)
+{
+ char *flags_prop;
+
+ if (verify_secret)
+ g_return_val_if_fail (is_secret_prop (setting, secret_name, error), FALSE);
+
+ flags_prop = g_strdup_printf ("%s-flags", secret_name);
+ g_object_set (G_OBJECT (setting), flags_prop, flags, NULL);
+ g_free (flags_prop);
+ return TRUE;
+}
+
+/**
+ * nm_setting_set_secret_flags:
+ * @setting: the #NMSetting
+ * @secret_name: the secret key name to set flags for
+ * @flags: the #NMSettingSecretFlags for the secret
+ * @error: location to store error, or %NULL
+ *
+ * For a given secret, stores the #NMSettingSecretFlags describing how to
+ * handle that secret.
+ *
+ * Returns: %TRUE on success (if the given secret name was a valid property of
+ * this setting, and if that property is secret), %FALSE if not
+ **/
+gboolean
+nm_setting_set_secret_flags (NMSetting *setting,
+ const char *secret_name,
+ NMSettingSecretFlags flags,
+ GError **error)
+{
+ g_return_val_if_fail (NM_IS_SETTING (setting), FALSE);
+ g_return_val_if_fail (secret_name != NULL, FALSE);
+ g_return_val_if_fail (flags <= NM_SETTING_SECRET_FLAGS_ALL, FALSE);
+
+ return NM_SETTING_GET_CLASS (setting)->set_secret_flags (setting, secret_name, TRUE, flags, error);
+}
+
+/**
+ * nm_setting_to_string:
+ * @setting: the #NMSetting
+ *
+ * Convert the setting into a string. For debugging purposes ONLY, should NOT
+ * be used for serialization of the setting, or machine-parsed in any way. The
+ * output format is not guaranteed to be stable and may change at any time.
+ *
+ * Returns: an allocated string containing a textual representation of the
+ * setting's properties and values (including secrets!), which the caller should
+ * free with g_free()
+ **/
+char *
+nm_setting_to_string (NMSetting *setting)
+{
+ GString *string;
+ GParamSpec **property_specs;
+ guint n_property_specs;
+ guint i;
+
+ g_return_val_if_fail (NM_IS_SETTING (setting), NULL);
+
+ property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (setting), &n_property_specs);
+ if (!property_specs)
+ return NULL;
+
+ string = g_string_new (nm_setting_get_name (setting));
+ g_string_append_c (string, '\n');
+
+ for (i = 0; i < n_property_specs; i++) {
+ GParamSpec *prop_spec = property_specs[i];
+ GValue value = G_VALUE_INIT;
+ char *value_str;
+ gboolean is_default;
+
+ if (strcmp (prop_spec->name, NM_SETTING_NAME) == 0)
+ continue;
+
+ g_value_init (&value, prop_spec->value_type);
+ g_object_get_property (G_OBJECT (setting), prop_spec->name, &value);
+
+ value_str = g_strdup_value_contents (&value);
+ g_string_append_printf (string, "\t%s : %s", prop_spec->name, value_str);
+ g_free (value_str);
+
+ is_default = g_param_value_defaults (prop_spec, &value);
+ g_value_unset (&value);
+
+ g_string_append (string, " (");
+ g_string_append_c (string, 's');
+ if (is_default)
+ g_string_append_c (string, 'd');
+ g_string_append_c (string, ')');
+ g_string_append_c (string, '\n');
+ }
+
+ g_free (property_specs);
+ g_string_append_c (string, '\n');
+
+ return g_string_free (string, FALSE);
+}
+
+/**
+ * nm_setting_get_virtual_iface_name:
+ * @setting: the #NMSetting
+ *
+ * Returns the name of the virtual kernel interface which the connection
+ * needs to use if specified in the settings.
+ *
+ * Returns: Name of the virtual interface or %NULL if the setting does not
+ * support this feature
+ **/
+const char *
+nm_setting_get_virtual_iface_name (NMSetting *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING (setting), NULL);
+
+ if (NM_SETTING_GET_CLASS (setting)->get_virtual_iface_name)
+ return NM_SETTING_GET_CLASS (setting)->get_virtual_iface_name (setting);
+
+ return NULL;
+}
+
+
+NMSettingVerifyResult
+_nm_setting_verify_deprecated_virtual_iface_name (const char *interface_name,
+ gboolean allow_missing,
+ const char *setting_name,
+ const char *setting_property,
+ GQuark error_quark,
+ gint e_invalid_property,
+ gint e_missing_property,
+ GSList *all_settings,
+ GError **error)
+{
+ NMSettingConnection *s_con;
+ const char *con_name;
+
+ s_con = NM_SETTING_CONNECTION (nm_setting_find_in_list (all_settings, NM_SETTING_CONNECTION_SETTING_NAME));
+ con_name = s_con ? nm_setting_connection_get_interface_name (s_con) : NULL;
+ if (!interface_name && !con_name) {
+ if (allow_missing)
+ return NM_SETTING_VERIFY_SUCCESS;
+
+ g_set_error_literal (error,
+ NM_SETTING_CONNECTION_ERROR,
+ NM_SETTING_CONNECTION_ERROR_MISSING_PROPERTY,
+ _("property is missing"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_INTERFACE_NAME);
+ return NM_SETTING_VERIFY_ERROR;
+ }
+ if (!con_name && !nm_utils_iface_valid_name (interface_name)) {
+ /* the interface_name is invalid, we cannot normalize it. Only do this if !con_name,
+ * because if con_name is set, it can overwrite interface_name. */
+ g_set_error_literal (error,
+ error_quark,
+ e_invalid_property,
+ _("property is invalid"));
+ g_prefix_error (error, "%s.%s: ", setting_name, setting_property);
+ return NM_SETTING_VERIFY_ERROR;
+ }
+ if (!con_name) {
+ /* NMSettingConnection has interface not set, it should be normalized to interface_name */
+ g_set_error_literal (error,
+ NM_SETTING_CONNECTION_ERROR,
+ NM_SETTING_CONNECTION_ERROR_MISSING_PROPERTY,
+ _("property is missing"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_INTERFACE_NAME);
+ return NM_SETTING_VERIFY_NORMALIZABLE;
+ }
+ if (!nm_utils_iface_valid_name (con_name)) {
+ /* NMSettingConnection:interface_name is invalid, we cannot normalize it. */
+ g_set_error_literal (error,
+ NM_SETTING_CONNECTION_ERROR,
+ NM_SETTING_CONNECTION_ERROR_INVALID_PROPERTY,
+ _("property is invalid"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_INTERFACE_NAME);
+ return NM_SETTING_VERIFY_ERROR;
+ }
+ if (!interface_name) {
+ /* Normalize by setting NMSettingConnection:interface_name. */
+ g_set_error_literal (error,
+ error_quark,
+ e_missing_property,
+ _("property is missing"));
+ g_prefix_error (error, "%s.%s: ", setting_name, setting_property);
+ return NM_SETTING_VERIFY_NORMALIZABLE;
+ }
+ if (strcmp (con_name, interface_name) != 0) {
+ /* con_name and interface_name are different. It can be normalized by setting interface_name
+ * to con_name. */
+ g_set_error_literal (error,
+ error_quark,
+ e_missing_property,
+ _("property is invalid"));
+ g_prefix_error (error, "%s.%s: ", setting_name, setting_property);
+ /* we would like to make this a NORMALIZEABLE_ERROR, but that might
+ * break older connections. */
+ return NM_SETTING_VERIFY_NORMALIZABLE;
+ }
+
+ return NM_SETTING_VERIFY_SUCCESS;
+}
+
+/*****************************************************************************/
+
+static void
+nm_setting_init (NMSetting *setting)
+{
+}
+
+static GObject*
+constructor (GType type,
+ guint n_construct_params,
+ GObjectConstructParam *construct_params)
+{
+ GObject *object;
+
+ object = G_OBJECT_CLASS (nm_setting_parent_class)->constructor (type,
+ n_construct_params,
+ construct_params);
+
+ _ensure_setting_info (object, NM_SETTING_GET_PRIVATE (object));
+ return object;
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ NMSettingPrivate *priv = NM_SETTING_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_NAME:
+ /* The setter for NAME is deprecated and should not be used anymore.
+ * Keep the setter for NAME to remain backward compatible.
+ * Only assert that the caller does not try to set the name to a different value
+ * then the registered name, which would be extra wrong.
+ **/
+ _ensure_setting_info (object, priv);
+ g_return_if_fail (!g_strcmp0 (priv->info->name, g_value_get_string (value)));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ NMSetting *setting = NM_SETTING (object);
+
+ switch (prop_id) {
+ case PROP_NAME:
+ g_value_set_string (value, nm_setting_get_name (setting));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nm_setting_class_init (NMSettingClass *setting_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
+
+ g_type_class_add_private (setting_class, sizeof (NMSettingPrivate));
+
+ /* virtual methods */
+ object_class->constructor = constructor;
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+
+ setting_class->update_one_secret = update_one_secret;
+ setting_class->get_secret_flags = get_secret_flags;
+ setting_class->set_secret_flags = set_secret_flags;
+ setting_class->compare_property = compare_property;
+ setting_class->clear_secrets_with_flags = clear_secrets_with_flags;
+
+ /* Properties */
+
+ /**
+ * NMSetting:name:
+ *
+ * The setting's name, which uniquely identifies the setting within the
+ * connection. Each setting type has a name unique to that type, for
+ * example "ppp" or "wireless" or "wired".
+ **/
+ g_object_class_install_property
+ (object_class, PROP_NAME,
+ g_param_spec_string (NM_SETTING_NAME, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/libnm-core/nm-setting.h b/libnm-core/nm-setting.h
new file mode 100644
index 0000000000..b58137a6e7
--- /dev/null
+++ b/libnm-core/nm-setting.h
@@ -0,0 +1,317 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2011 Red Hat, Inc.
+ * Copyright 2007 - 2008 Novell, Inc.
+ */
+
+#ifndef NM_SETTING_H
+#define NM_SETTING_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <nm-version.h>
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_SETTING (nm_setting_get_type ())
+#define NM_SETTING(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING, NMSetting))
+#define NM_SETTING_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING, NMSettingClass))
+#define NM_IS_SETTING(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING))
+#define NM_IS_SETTING_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING))
+#define NM_SETTING_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING, NMSettingClass))
+
+/**
+ * NMSettingError:
+ * @NM_SETTING_ERROR_UNKNOWN: unknown or unclassified error
+ * @NM_SETTING_ERROR_PROPERTY_NOT_FOUND: a property required by the operation
+ * was not found; for example, an attempt to update an invalid secret
+ * @NM_SETTING_ERROR_PROPERTY_NOT_SECRET: an operation which requires a secret
+ * was attempted on a non-secret property
+ * @NM_SETTING_ERROR_PROPERTY_TYPE_MISMATCH: the operation requires a property
+ * of a specific type, or the value couldn't be transformed to the same type
+ * as the property being acted upon
+ *
+ * Describes errors that may result from operations involving a #NMSetting.
+ *
+ **/
+typedef enum
+{
+ NM_SETTING_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/
+ NM_SETTING_ERROR_PROPERTY_NOT_FOUND, /*< nick=PropertyNotFound >*/
+ NM_SETTING_ERROR_PROPERTY_NOT_SECRET, /*< nick=PropertyNotSecret >*/
+ NM_SETTING_ERROR_PROPERTY_TYPE_MISMATCH /*< nick=PropertyTypeMismatch >*/
+} NMSettingError;
+
+#define NM_SETTING_ERROR nm_setting_error_quark ()
+GQuark nm_setting_error_quark (void);
+
+
+/* DEPRECATED AND UNUSED */
+#define NM_SETTING_PARAM_SERIALIZE (1 << (0 + G_PARAM_USER_SHIFT))
+
+/* The property of the #NMSetting is required for the setting to be valid */
+#define NM_SETTING_PARAM_REQUIRED (1 << (1 + G_PARAM_USER_SHIFT))
+
+/* The property of the #NMSetting is a secret */
+#define NM_SETTING_PARAM_SECRET (1 << (2 + G_PARAM_USER_SHIFT))
+
+/* The property of the #NMSetting should be ignored during comparisons that
+ * use the %NM_SETTING_COMPARE_FLAG_FUZZY flag.
+ */
+#define NM_SETTING_PARAM_FUZZY_IGNORE (1 << (3 + G_PARAM_USER_SHIFT))
+
+/* Note: all non-glib GParamFlags bits are reserved by NetworkManager */
+
+
+#define NM_SETTING_NAME "name"
+
+/**
+ * NMSettingSecretFlags:
+ * @NM_SETTING_SECRET_FLAG_NONE: the system is responsible for providing and
+ * storing this secret (default)
+ * @NM_SETTING_SECRET_FLAG_AGENT_OWNED: a user secret agent is responsible
+ * for providing and storing this secret; when it is required agents will be
+ * asked to retrieve it
+ * @NM_SETTING_SECRET_FLAG_NOT_SAVED: this secret should not be saved, but
+ * should be requested from the user each time it is needed
+ * @NM_SETTING_SECRET_FLAG_NOT_REQUIRED: in situations where it cannot be
+ * automatically determined that the secret is required (some VPNs and PPP
+ * providers dont require all secrets) this flag indicates that the specific
+ * secret is not required
+ *
+ * These flags indicate specific behavior related to handling of a secret. Each
+ * secret has a corresponding set of these flags which indicate how the secret
+ * is to be stored and/or requested when it is needed.
+ *
+ **/
+typedef enum {
+ NM_SETTING_SECRET_FLAG_NONE = 0x00000000,
+ NM_SETTING_SECRET_FLAG_AGENT_OWNED = 0x00000001,
+ NM_SETTING_SECRET_FLAG_NOT_SAVED = 0x00000002,
+ NM_SETTING_SECRET_FLAG_NOT_REQUIRED = 0x00000004
+
+ /* NOTE: if adding flags, update nm-setting-private.h as well */
+} NMSettingSecretFlags;
+
+/**
+ * NMSettingCompareFlags:
+ * @NM_SETTING_COMPARE_FLAG_EXACT: match all properties exactly
+ * @NM_SETTING_COMPARE_FLAG_FUZZY: match only important attributes, like SSID,
+ * type, security settings, etc. Does not match, for example, connection ID
+ * or UUID.
+ * @NM_SETTING_COMPARE_FLAG_IGNORE_ID: ignore the connection's ID
+ * @NM_SETTING_COMPARE_FLAG_IGNORE_SECRETS: ignore all secrets
+ * @NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS: ignore secrets for which
+ * the secret's flags indicate the secret is owned by a user secret agent
+ * (ie, the secret's flag includes @NM_SETTING_SECRET_FLAG_AGENT_OWNED)
+ * @NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS: ignore secrets for which
+ * the secret's flags indicate the secret should not be saved to persistent
+ * storage (ie, the secret's flag includes @NM_SETTING_SECRET_FLAG_NOT_SAVED)
+ *
+ * These flags modify the comparison behavior when comparing two settings or
+ * two connections.
+ *
+ **/
+typedef enum {
+ NM_SETTING_COMPARE_FLAG_EXACT = 0x00000000,
+ NM_SETTING_COMPARE_FLAG_FUZZY = 0x00000001,
+ NM_SETTING_COMPARE_FLAG_IGNORE_ID = 0x00000002,
+ NM_SETTING_COMPARE_FLAG_IGNORE_SECRETS = 0x00000004,
+ NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS = 0x00000008,
+ NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS = 0x00000010
+
+ /* 0x80000000 is used for a private flag */
+} NMSettingCompareFlags;
+
+
+/**
+ * NMSetting:
+ *
+ * The NMSetting struct contains only private data.
+ * It should only be accessed through the functions described below.
+ */
+typedef struct {
+ GObject parent;
+} NMSetting;
+
+
+/**
+ * NMSettingClearSecretsWithFlagsFn:
+ * @setting: The setting for which secrets are being iterated
+ * @secret: The secret's name
+ * @flags: The secret's flags, eg %NM_SETTING_SECRET_FLAG_AGENT_OWNED
+ * @user_data: User data passed to nm_connection_clear_secrets_with_flags()
+ *
+ * Returns: %TRUE to clear the secret, %FALSE to not clear the secret
+ */
+typedef gboolean (*NMSettingClearSecretsWithFlagsFn) (NMSetting *setting,
+ const char *secret,
+ NMSettingSecretFlags flags,
+ gpointer user_data);
+
+typedef struct {
+ GObjectClass parent;
+
+ /* Virtual functions */
+ gint (*verify) (NMSetting *setting,
+ GSList *all_settings,
+ GError **error);
+
+ GPtrArray *(*need_secrets) (NMSetting *setting);
+
+ int (*update_one_secret) (NMSetting *setting,
+ const char *key,
+ GValue *value,
+ GError **error);
+
+ gboolean (*get_secret_flags) (NMSetting *setting,
+ const char *secret_name,
+ gboolean verify_secret,
+ NMSettingSecretFlags *out_flags,
+ GError **error);
+
+ gboolean (*set_secret_flags) (NMSetting *setting,
+ const char *secret_name,
+ gboolean verify_secret,
+ NMSettingSecretFlags flags,
+ GError **error);
+
+ /* Returns TRUE if the given property contains the same value in both settings */
+ gboolean (*compare_property) (NMSetting *setting,
+ NMSetting *other,
+ const GParamSpec *prop_spec,
+ NMSettingCompareFlags flags);
+
+ gboolean (*clear_secrets_with_flags) (NMSetting *setting,
+ GParamSpec *pspec,
+ NMSettingClearSecretsWithFlagsFn func,
+ gpointer user_data);
+
+ const char *(*get_virtual_iface_name) (NMSetting *setting);
+
+ /* Padding for future expansion */
+ void (*_reserved1) (void);
+} NMSettingClass;
+
+/**
+ * NMSettingValueIterFn:
+ * @setting: The setting for which properties are being iterated, given to
+ * nm_setting_enumerate_values()
+ * @key: The value/property name
+ * @value: The property's value
+ * @flags: The property's flags, like %NM_SETTING_PARAM_SECRET
+ * @user_data: User data passed to nm_setting_enumerate_values()
+ */
+typedef void (*NMSettingValueIterFn) (NMSetting *setting,
+ const char *key,
+ const GValue *value,
+ GParamFlags flags,
+ gpointer user_data);
+
+
+GType nm_setting_get_type (void);
+
+/**
+ * NMSettingHashFlags:
+ * @NM_SETTING_HASH_FLAG_ALL: hash all properties (including secrets)
+ * @NM_SETTING_HASH_FLAG_NO_SECRETS: do not include secrets
+ * @NM_SETTING_HASH_FLAG_ONLY_SECRETS: only hash secrets
+ *
+ * These flags determine which properties are added to the resulting hash
+ * when calling nm_setting_to_hash().
+ *
+ **/
+typedef enum {
+ NM_SETTING_HASH_FLAG_ALL = 0x00000000,
+ NM_SETTING_HASH_FLAG_NO_SECRETS = 0x00000001,
+ NM_SETTING_HASH_FLAG_ONLY_SECRETS = 0x00000002,
+} NMSettingHashFlags;
+
+GHashTable *nm_setting_to_hash (NMSetting *setting,
+ NMSettingHashFlags flags);
+
+NMSetting *nm_setting_new_from_hash (GType setting_type,
+ GHashTable *hash);
+
+NMSetting *nm_setting_duplicate (NMSetting *setting);
+
+const char *nm_setting_get_name (NMSetting *setting);
+
+gboolean nm_setting_verify (NMSetting *setting,
+ GSList *all_settings,
+ GError **error);
+
+gboolean nm_setting_compare (NMSetting *a,
+ NMSetting *b,
+ NMSettingCompareFlags flags);
+
+/**
+ * NMSettingDiffResult:
+ * @NM_SETTING_DIFF_RESULT_UNKNOWN: unknown result
+ * @NM_SETTING_DIFF_RESULT_IN_A: the property is present in setting A
+ * @NM_SETTING_DIFF_RESULT_IN_B: the property is present in setting B
+ *
+ * These values indicate the result of a setting difference operation.
+ **/
+typedef enum {
+ NM_SETTING_DIFF_RESULT_UNKNOWN = 0x00000000,
+ NM_SETTING_DIFF_RESULT_IN_A = 0x00000001,
+ NM_SETTING_DIFF_RESULT_IN_B = 0x00000002,
+} NMSettingDiffResult;
+
+gboolean nm_setting_diff (NMSetting *a,
+ NMSetting *b,
+ NMSettingCompareFlags flags,
+ gboolean invert_results,
+ GHashTable **results);
+
+void nm_setting_enumerate_values (NMSetting *setting,
+ NMSettingValueIterFn func,
+ gpointer user_data);
+
+char *nm_setting_to_string (NMSetting *setting);
+
+/* Secrets */
+void nm_setting_clear_secrets (NMSetting *setting);
+
+void nm_setting_clear_secrets_with_flags (NMSetting *setting,
+ NMSettingClearSecretsWithFlagsFn func,
+ gpointer user_data);
+
+GPtrArray *nm_setting_need_secrets (NMSetting *setting);
+gboolean nm_setting_update_secrets (NMSetting *setting,
+ GHashTable *secrets,
+ GError **error);
+
+gboolean nm_setting_get_secret_flags (NMSetting *setting,
+ const char *secret_name,
+ NMSettingSecretFlags *out_flags,
+ GError **error);
+
+gboolean nm_setting_set_secret_flags (NMSetting *setting,
+ const char *secret_name,
+ NMSettingSecretFlags flags,
+ GError **error);
+
+const char *nm_setting_get_virtual_iface_name (NMSetting *setting);
+
+G_END_DECLS
+
+#endif /* NM_SETTING_H */
diff --git a/libnm-core/nm-utils-private.h b/libnm-core/nm-utils-private.h
new file mode 100644
index 0000000000..6e6d7b7247
--- /dev/null
+++ b/libnm-core/nm-utils-private.h
@@ -0,0 +1,65 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2005 - 2008 Red Hat, Inc.
+ */
+
+#ifndef __NM_UTILS_PRIVATE_H__
+#define __NM_UTILS_PRIVATE_H__
+
+#include "nm-setting-private.h"
+
+gboolean _nm_utils_string_in_list (const char *str,
+ const char **valid_strings);
+
+gboolean _nm_utils_string_slist_validate (GSList *list,
+ const char **valid_values);
+
+gboolean _nm_utils_gvalue_array_validate (GValueArray *elements,
+ guint n_expected, ...);
+
+void _nm_value_transforms_register (void);
+
+/***********************************************************/
+
+typedef struct NMUtilsPrivateData {
+ const char * (*nm_setting_ip4_config_get_address_label) (NMSettingIP4Config *setting,
+ guint32 i);
+ gboolean (*nm_setting_ip4_config_add_address_with_label) (NMSettingIP4Config *setting,
+ NMIP4Address *address,
+ const char *label);
+} NMUtilsPrivateData;
+
+const NMUtilsPrivateData *nm_utils_get_private (void);
+
+/**
+ * NM_UTILS_PRIVATE_CALL:
+ * @call: a call to a private libnm-util function
+ *
+ * Used to call private libnm-util functions. Eg, if there was a
+ * private function called nm_foo_get_bar(), you could call it like:
+ *
+ * bar = NM_UTILS_PRIVATE_CALL (nm_foo_get_bar (foo, x, y, z));
+ *
+ * This macro only exists inside the NetworkManager source tree and
+ * is not part of the public API.
+ *
+ * Since: 0.9.10
+ */
+#define NM_UTILS_PRIVATE_CALL(call) (nm_utils_get_private ()->call)
+
+#endif
diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c
new file mode 100644
index 0000000000..753edd8473
--- /dev/null
+++ b/libnm-core/nm-utils.c
@@ -0,0 +1,2531 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2005 - 2013 Red Hat, Inc.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <netinet/ether.h>
+#include <linux/if_infiniband.h>
+#include <uuid/uuid.h>
+
+#include "nm-utils.h"
+#include "nm-utils-private.h"
+#include "nm-glib-compat.h"
+#include "nm-dbus-glib-types.h"
+#include "nm-setting-private.h"
+#include "crypto.h"
+
+/**
+ * SECTION:nm-utils
+ * @short_description: Utility functions
+ * @include: nm-utils.h
+ *
+ * A collection of utility functions for working with SSIDs, IP addresses, Wi-Fi
+ * access points and devices, among other things.
+ */
+
+struct EncodingTriplet
+{
+ const char *encoding1;
+ const char *encoding2;
+ const char *encoding3;
+};
+
+struct IsoLangToEncodings
+{
+ const char * lang;
+ struct EncodingTriplet encodings;
+};
+
+/* 5-letter language codes */
+static const struct IsoLangToEncodings isoLangEntries5[] =
+{
+ /* Simplified Chinese */
+ { "zh_cn", {"euc-cn", "gb2312", "gb18030"} }, /* PRC */
+ { "zh_sg", {"euc-cn", "gb2312", "gb18030"} }, /* Singapore */
+
+ /* Traditional Chinese */
+ { "zh_tw", {"big5", "euc-tw", NULL} }, /* Taiwan */
+ { "zh_hk", {"big5", "euc-tw", "big5-hkcs"} },/* Hong Kong */
+ { "zh_mo", {"big5", "euc-tw", NULL} }, /* Macau */
+
+ /* Table end */
+ { NULL, {NULL, NULL, NULL} }
+};
+
+/* 2-letter language codes; we don't care about the other 3 in this table */
+static const struct IsoLangToEncodings isoLangEntries2[] =
+{
+ /* Japanese */
+ { "ja", {"euc-jp", "shift_jis", "iso-2022-jp"} },
+
+ /* Korean */
+ { "ko", {"euc-kr", "iso-2022-kr", "johab"} },
+
+ /* Thai */
+ { "th", {"iso-8859-11","windows-874", NULL} },
+
+ /* Central European */
+ { "hu", {"iso-8859-2", "windows-1250", NULL} }, /* Hungarian */
+ { "cs", {"iso-8859-2", "windows-1250", NULL} }, /* Czech */
+ { "hr", {"iso-8859-2", "windows-1250", NULL} }, /* Croatian */
+ { "pl", {"iso-8859-2", "windows-1250", NULL} }, /* Polish */
+ { "ro", {"iso-8859-2", "windows-1250", NULL} }, /* Romanian */
+ { "sk", {"iso-8859-2", "windows-1250", NULL} }, /* Slovakian */
+ { "sl", {"iso-8859-2", "windows-1250", NULL} }, /* Slovenian */
+ { "sh", {"iso-8859-2", "windows-1250", NULL} }, /* Serbo-Croatian */
+
+ /* Cyrillic */
+ { "ru", {"koi8-r", "windows-1251", "iso-8859-5"} }, /* Russian */
+ { "be", {"koi8-r", "windows-1251", "iso-8859-5"} }, /* Belorussian */
+ { "bg", {"windows-1251","koi8-r", "iso-8859-5"} }, /* Bulgarian */
+ { "mk", {"koi8-r", "windows-1251", "iso-8859-5"} }, /* Macedonian */
+ { "sr", {"koi8-r", "windows-1251", "iso-8859-5"} }, /* Serbian */
+ { "uk", {"koi8-u", "koi8-r", "windows-1251"} }, /* Ukranian */
+
+ /* Arabic */
+ { "ar", {"iso-8859-6", "windows-1256", NULL} },
+
+ /* Baltic */
+ { "et", {"iso-8859-4", "windows-1257", NULL} }, /* Estonian */
+ { "lt", {"iso-8859-4", "windows-1257", NULL} }, /* Lithuanian */
+ { "lv", {"iso-8859-4", "windows-1257", NULL} }, /* Latvian */
+
+ /* Greek */
+ { "el", {"iso-8859-7", "windows-1253", NULL} },
+
+ /* Hebrew */
+ { "he", {"iso-8859-8", "windows-1255", NULL} },
+ { "iw", {"iso-8859-8", "windows-1255", NULL} },
+
+ /* Turkish */
+ { "tr", {"iso-8859-9", "windows-1254", NULL} },
+
+ /* Table end */
+ { NULL, {NULL, NULL, NULL} }
+};
+
+
+static GHashTable * langToEncodings5 = NULL;
+static GHashTable * langToEncodings2 = NULL;
+
+static void
+init_lang_to_encodings_hash (void)
+{
+ struct IsoLangToEncodings *enc;
+
+ if (G_UNLIKELY (langToEncodings5 == NULL)) {
+ /* Five-letter codes */
+ enc = (struct IsoLangToEncodings *) &isoLangEntries5[0];
+ langToEncodings5 = g_hash_table_new (g_str_hash, g_str_equal);
+ while (enc->lang) {
+ g_hash_table_insert (langToEncodings5, (gpointer) enc->lang,
+ (gpointer) &enc->encodings);
+ enc++;
+ }
+ }
+
+ if (G_UNLIKELY (langToEncodings2 == NULL)) {
+ /* Two-letter codes */
+ enc = (struct IsoLangToEncodings *) &isoLangEntries2[0];
+ langToEncodings2 = g_hash_table_new (g_str_hash, g_str_equal);
+ while (enc->lang) {
+ g_hash_table_insert (langToEncodings2, (gpointer) enc->lang,
+ (gpointer) &enc->encodings);
+ enc++;
+ }
+ }
+}
+
+
+static gboolean
+get_encodings_for_lang (const char *lang,
+ char **encoding1,
+ char **encoding2,
+ char **encoding3)
+{
+ struct EncodingTriplet * encodings;
+ gboolean success = FALSE;
+ char * tmp_lang;
+
+ g_return_val_if_fail (lang != NULL, FALSE);
+ g_return_val_if_fail (encoding1 != NULL, FALSE);
+ g_return_val_if_fail (encoding2 != NULL, FALSE);
+ g_return_val_if_fail (encoding3 != NULL, FALSE);
+
+ *encoding1 = "iso-8859-1";
+ *encoding2 = "windows-1251";
+ *encoding3 = NULL;
+
+ init_lang_to_encodings_hash ();
+
+ tmp_lang = g_strdup (lang);
+ if ((encodings = g_hash_table_lookup (langToEncodings5, tmp_lang))) {
+ *encoding1 = (char *) encodings->encoding1;
+ *encoding2 = (char *) encodings->encoding2;
+ *encoding3 = (char *) encodings->encoding3;
+ success = TRUE;
+ }
+
+ /* Truncate tmp_lang to length of 2 */
+ if (strlen (tmp_lang) > 2)
+ tmp_lang[2] = '\0';
+ if (!success && (encodings = g_hash_table_lookup (langToEncodings2, tmp_lang))) {
+ *encoding1 = (char *) encodings->encoding1;
+ *encoding2 = (char *) encodings->encoding2;
+ *encoding3 = (char *) encodings->encoding3;
+ success = TRUE;
+ }
+
+ g_free (tmp_lang);
+ return success;
+}
+
+/* init, deinit for libnm_util */
+
+static gboolean initialized = FALSE;
+
+/**
+ * nm_utils_init:
+ * @error: location to store error, or %NULL
+ *
+ * Initializes libnm-util; should be called when starting and program that
+ * uses libnm-util. Sets up an atexit() handler to ensure de-initialization
+ * is performed, but calling nm_utils_deinit() to explicitly deinitialize
+ * libnm-util can also be done. This function can be called more than once.
+ *
+ * Returns: %TRUE if the initialization was successful, %FALSE on failure.
+ **/
+gboolean
+nm_utils_init (GError **error)
+{
+ if (!initialized) {
+ initialized = TRUE;
+
+ if (!crypto_init (error))
+ return FALSE;
+
+ _nm_value_transforms_register ();
+ }
+ return TRUE;
+}
+
+/**
+ * nm_utils_deinit:
+ *
+ * Frees all resources used internally by libnm-util. This function is called
+ * from an atexit() handler, set up by nm_utils_init(), but is safe to be called
+ * more than once. Subsequent calls have no effect until nm_utils_init() is
+ * called again.
+ **/
+void
+nm_utils_deinit (void)
+{
+ if (initialized) {
+ crypto_deinit ();
+ initialized = FALSE;
+ }
+}
+
+/* ssid helpers */
+
+/**
+ * nm_utils_ssid_to_utf8:
+ * @ssid: a byte array containing the SSID data
+ *
+ * Wi-Fi SSIDs are byte arrays, they are _not_ strings. Thus, an SSID may
+ * contain embedded NULLs and other unprintable characters. Often it is
+ * useful to print the SSID out for debugging purposes, but that should be the
+ * _only_ use of this function. Do not use this function for any persistent
+ * storage of the SSID, since the printable SSID returned from this function
+ * cannot be converted back into the real SSID of the access point.
+ *
+ * This function does almost everything humanly possible to convert the input
+ * into a printable UTF-8 string, using roughly the following procedure:
+ *
+ * 1) if the input data is already UTF-8 safe, no conversion is performed
+ * 2) attempts to get the current system language from the LANG environment
+ * variable, and depending on the language, uses a table of alternative
+ * encodings to try. For example, if LANG=hu_HU, the table may first try
+ * the ISO-8859-2 encoding, and if that fails, try the Windows-1250 encoding.
+ * If all fallback encodings fail, replaces non-UTF-8 characters with '?'.
+ * 3) If the system language was unable to be determined, falls back to the
+ * ISO-8859-1 encoding, then to the Windows-1251 encoding.
+ * 4) If step 3 fails, replaces non-UTF-8 characters with '?'.
+ *
+ * Again, this function should be used for debugging and display purposes
+ * _only_.
+ *
+ * Returns: (transfer full): an allocated string containing a UTF-8
+ * representation of the SSID, which must be freed by the caller using g_free().
+ * Returns %NULL on errors.
+ **/
+char *
+nm_utils_ssid_to_utf8 (const GByteArray *ssid)
+{
+ char *converted = NULL;
+ char *lang, *e1 = NULL, *e2 = NULL, *e3 = NULL;
+
+ g_return_val_if_fail (ssid != NULL, NULL);
+
+ if (g_utf8_validate ((const gchar *) ssid->data, ssid->len, NULL))
+ return g_strndup ((const gchar *) ssid->data, ssid->len);
+
+ /* LANG may be a good encoding hint */
+ g_get_charset ((const char **)(&e1));
+ if ((lang = getenv ("LANG"))) {
+ char * dot;
+
+ lang = g_ascii_strdown (lang, -1);
+ if ((dot = strchr (lang, '.')))
+ *dot = '\0';
+
+ get_encodings_for_lang (lang, &e1, &e2, &e3);
+ g_free (lang);
+ }
+
+ converted = g_convert ((const gchar *) ssid->data, ssid->len, "UTF-8", e1, NULL, NULL, NULL);
+ if (!converted && e2)
+ converted = g_convert ((const gchar *) ssid->data, ssid->len, "UTF-8", e2, NULL, NULL, NULL);
+
+ if (!converted && e3)
+ converted = g_convert ((const gchar *) ssid->data, ssid->len, "UTF-8", e3, NULL, NULL, NULL);
+
+ if (!converted) {
+ converted = g_convert_with_fallback ((const gchar *) ssid->data, ssid->len,
+ "UTF-8", e1, "?", NULL, NULL, NULL);
+ }
+
+ return converted;
+}
+
+/* Shamelessly ripped from the Linux kernel ieee80211 stack */
+/**
+ * nm_utils_is_empty_ssid:
+ * @ssid: pointer to a buffer containing the SSID data
+ * @len: length of the SSID data in @ssid
+ *
+ * Different manufacturers use different mechanisms for not broadcasting the
+ * AP's SSID. This function attempts to detect blank/empty SSIDs using a
+ * number of known SSID-cloaking methods.
+ *
+ * Returns: %TRUE if the SSID is "empty", %FALSE if it is not
+ **/
+gboolean
+nm_utils_is_empty_ssid (const guint8 * ssid, int len)
+{
+ /* Single white space is for Linksys APs */
+ if (len == 1 && ssid[0] == ' ')
+ return TRUE;
+
+ /* Otherwise, if the entire ssid is 0, we assume it is hidden */
+ while (len--) {
+ if (ssid[len] != '\0')
+ return FALSE;
+ }
+ return TRUE;
+}
+
+#define ESSID_MAX_SIZE 32
+
+/**
+ * nm_utils_escape_ssid:
+ * @ssid: pointer to a buffer containing the SSID data
+ * @len: length of the SSID data in @ssid
+ *
+ * This function does a quick printable character conversion of the SSID, simply
+ * replacing embedded NULLs and non-printable characters with the hexadecimal
+ * representation of that character. Intended for debugging only, should not
+ * be used for display of SSIDs.
+ *
+ * Returns: pointer to the escaped SSID, which uses an internal static buffer
+ * and will be overwritten by subsequent calls to this function
+ **/
+const char *
+nm_utils_escape_ssid (const guint8 * ssid, guint32 len)
+{
+ static char escaped[ESSID_MAX_SIZE * 2 + 1];
+ const guint8 *s = ssid;
+ char *d = escaped;
+
+ if (nm_utils_is_empty_ssid (ssid, len)) {
+ memcpy (escaped, "<hidden>", sizeof ("<hidden>"));
+ return escaped;
+ }
+
+ len = MIN (len, (guint32) ESSID_MAX_SIZE);
+ while (len--) {
+ if (*s == '\0') {
+ *d++ = '\\';
+ *d++ = '0';
+ s++;
+ } else {
+ *d++ = *s++;
+ }
+ }
+ *d = '\0';
+ return escaped;
+}
+
+/**
+ * nm_utils_same_ssid:
+ * @ssid1: first SSID data to compare
+ * @ssid2: second SSID data to compare
+ * @ignore_trailing_null: %TRUE to ignore one trailing NULL byte
+ *
+ * Earlier versions of the Linux kernel added a NULL byte to the end of the
+ * SSID to enable easy printing of the SSID on the console or in a terminal,
+ * but this behavior was problematic (SSIDs are simply byte arrays, not strings)
+ * and thus was changed. This function compensates for that behavior at the
+ * cost of some compatibility with odd SSIDs that may legitimately have trailing
+ * NULLs, even though that is functionally pointless.
+ *
+ * Returns: %TRUE if the SSIDs are the same, %FALSE if they are not
+ **/
+gboolean
+nm_utils_same_ssid (const GByteArray * ssid1,
+ const GByteArray * ssid2,
+ gboolean ignore_trailing_null)
+{
+ guint32 ssid1_len, ssid2_len;
+
+ if (ssid1 == ssid2)
+ return TRUE;
+ if (!ssid1 || !ssid2)
+ return FALSE;
+
+ ssid1_len = ssid1->len;
+ ssid2_len = ssid2->len;
+ if (ssid1_len && ssid2_len && ignore_trailing_null) {
+ if (ssid1->data[ssid1_len - 1] == '\0')
+ ssid1_len--;
+ if (ssid2->data[ssid2_len - 1] == '\0')
+ ssid2_len--;
+ }
+
+ if (ssid1_len != ssid2_len)
+ return FALSE;
+
+ return memcmp (ssid1->data, ssid2->data, ssid1_len) == 0 ? TRUE : FALSE;
+}
+
+static void
+value_destroy (gpointer data)
+{
+ GValue *value = (GValue *) data;
+
+ g_value_unset (value);
+ g_slice_free (GValue, value);
+}
+
+static void
+value_dup (gpointer key, gpointer val, gpointer user_data)
+{
+ GHashTable *table = (GHashTable *) user_data;
+ GValue *value = (GValue *) val;
+ GValue *dup_value;
+
+ dup_value = g_slice_new0 (GValue);
+ g_value_init (dup_value, G_VALUE_TYPE (val));
+ g_value_copy (value, dup_value);
+
+ g_hash_table_insert (table, g_strdup ((char *) key), dup_value);
+}
+
+/**
+ * nm_utils_gvalue_hash_dup:
+ * @hash: a #GHashTable mapping string:GValue
+ *
+ * Utility function to duplicate a hash table of #GValues.
+ *
+ * Returns: (transfer container) (element-type utf8 GObject.Value): a newly allocated duplicated #GHashTable, caller must free the
+ * returned hash with g_hash_table_unref() or g_hash_table_destroy()
+ **/
+GHashTable *
+nm_utils_gvalue_hash_dup (GHashTable *hash)
+{
+ GHashTable *table;
+
+ g_return_val_if_fail (hash != NULL, NULL);
+
+ table = g_hash_table_new_full (g_str_hash, g_str_equal,
+ (GDestroyNotify) g_free,
+ value_destroy);
+
+ g_hash_table_foreach (hash, value_dup, table);
+
+ return table;
+}
+
+/**
+ * nm_utils_slist_free: (skip)
+ * @list: a #GSList
+ * @elem_destroy_fn: user function called for each element in @list
+ *
+ * Utility function to free a #GSList.
+ *
+ * Deprecated: use g_slist_free_full().
+ **/
+void
+nm_utils_slist_free (GSList *list, GDestroyNotify elem_destroy_fn)
+{
+ g_slist_free_full (list, elem_destroy_fn);
+}
+
+gboolean
+_nm_utils_string_in_list (const char *str, const char **valid_strings)
+{
+ int i;
+
+ for (i = 0; valid_strings[i]; i++)
+ if (strcmp (str, valid_strings[i]) == 0)
+ break;
+
+ return valid_strings[i] != NULL;
+}
+
+gboolean
+_nm_utils_string_slist_validate (GSList *list, const char **valid_values)
+{
+ GSList *iter;
+
+ for (iter = list; iter; iter = iter->next) {
+ if (!_nm_utils_string_in_list ((char *) iter->data, valid_values))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gboolean
+_nm_utils_gvalue_array_validate (GValueArray *elements, guint n_expected, ...)
+{
+ va_list args;
+ GValue *tmp;
+ int i;
+ gboolean valid = FALSE;
+
+ if (n_expected != elements->n_values)
+ return FALSE;
+
+ va_start (args, n_expected);
+ for (i = 0; i < n_expected; i++) {
+ tmp = g_value_array_get_nth (elements, i);
+ if (G_VALUE_TYPE (tmp) != va_arg (args, GType))
+ goto done;
+ }
+ valid = TRUE;
+
+done:
+ va_end (args);
+ return valid;
+}
+
+static gboolean
+device_supports_ap_ciphers (guint32 dev_caps,
+ guint32 ap_flags,
+ gboolean static_wep)
+{
+ gboolean have_pair = FALSE;
+ gboolean have_group = FALSE;
+ /* Device needs to support at least one pairwise and one group cipher */
+
+ /* Pairwise */
+ if (static_wep) {
+ /* Static WEP only uses group ciphers */
+ have_pair = TRUE;
+ } else {
+ if (dev_caps & NM_WIFI_DEVICE_CAP_CIPHER_WEP40)
+ if (ap_flags & NM_802_11_AP_SEC_PAIR_WEP40)
+ have_pair = TRUE;
+ if (dev_caps & NM_WIFI_DEVICE_CAP_CIPHER_WEP104)
+ if (ap_flags & NM_802_11_AP_SEC_PAIR_WEP104)
+ have_pair = TRUE;
+ if (dev_caps & NM_WIFI_DEVICE_CAP_CIPHER_TKIP)
+ if (ap_flags & NM_802_11_AP_SEC_PAIR_TKIP)
+ have_pair = TRUE;
+ if (dev_caps & NM_WIFI_DEVICE_CAP_CIPHER_CCMP)
+ if (ap_flags & NM_802_11_AP_SEC_PAIR_CCMP)
+ have_pair = TRUE;
+ }
+
+ /* Group */
+ if (dev_caps & NM_WIFI_DEVICE_CAP_CIPHER_WEP40)
+ if (ap_flags & NM_802_11_AP_SEC_GROUP_WEP40)
+ have_group = TRUE;
+ if (dev_caps & NM_WIFI_DEVICE_CAP_CIPHER_WEP104)
+ if (ap_flags & NM_802_11_AP_SEC_GROUP_WEP104)
+ have_group = TRUE;
+ if (!static_wep) {
+ if (dev_caps & NM_WIFI_DEVICE_CAP_CIPHER_TKIP)
+ if (ap_flags & NM_802_11_AP_SEC_GROUP_TKIP)
+ have_group = TRUE;
+ if (dev_caps & NM_WIFI_DEVICE_CAP_CIPHER_CCMP)
+ if (ap_flags & NM_802_11_AP_SEC_GROUP_CCMP)
+ have_group = TRUE;
+ }
+
+ return (have_pair && have_group);
+}
+
+/**
+ * nm_utils_ap_mode_security_valid:
+ * @type: the security type to check device capabilties against,
+ * e.g. #NMU_SEC_STATIC_WEP
+ * @wifi_caps: bitfield of the capabilities of the specific Wi-Fi device, e.g.
+ * #NM_WIFI_DEVICE_CAP_CIPHER_WEP40
+ *
+ * Given a set of device capabilities, and a desired security type to check
+ * against, determines whether the combination of device capabilities and
+ * desired security type are valid for AP/Hotspot connections.
+ *
+ * Returns: %TRUE if the device capabilities are compatible with the desired
+ * @type, %FALSE if they are not.
+ *
+ * Since: 0.9.8
+ **/
+gboolean
+nm_utils_ap_mode_security_valid (NMUtilsSecurityType type,
+ NMDeviceWifiCapabilities wifi_caps)
+{
+ if (!(wifi_caps & NM_WIFI_DEVICE_CAP_AP))
+ return FALSE;
+
+ /* Return TRUE for any security that wpa_supplicant's lightweight AP
+ * mode can handle: which is open, WEP, and WPA/WPA2 PSK.
+ */
+ switch (type) {
+ case NMU_SEC_NONE:
+ case NMU_SEC_STATIC_WEP:
+ case NMU_SEC_WPA_PSK:
+ case NMU_SEC_WPA2_PSK:
+ return TRUE;
+ default:
+ break;
+ }
+ return FALSE;
+}
+
+/**
+ * nm_utils_security_valid:
+ * @type: the security type to check AP flags and device capabilties against,
+ * e.g. #NMU_SEC_STATIC_WEP
+ * @wifi_caps: bitfield of the capabilities of the specific Wi-Fi device, e.g.
+ * #NM_WIFI_DEVICE_CAP_CIPHER_WEP40
+ * @have_ap: whether the @ap_flags, @ap_wpa, and @ap_rsn arguments are valid
+ * @adhoc: whether the capabilities being tested are from an Ad-Hoc AP (IBSS)
+ * @ap_flags: bitfield of AP capabilities, e.g. #NM_802_11_AP_FLAGS_PRIVACY
+ * @ap_wpa: bitfield of AP capabilties derived from the AP's WPA beacon,
+ * e.g. (#NM_802_11_AP_SEC_PAIR_TKIP | #NM_802_11_AP_SEC_KEY_MGMT_PSK)
+ * @ap_rsn: bitfield of AP capabilties derived from the AP's RSN/WPA2 beacon,
+ * e.g. (#NM_802_11_AP_SEC_PAIR_CCMP | #NM_802_11_AP_SEC_PAIR_TKIP)
+ *
+ * Given a set of device capabilities, and a desired security type to check
+ * against, determines whether the combination of device, desired security
+ * type, and AP capabilities intersect.
+ *
+ * NOTE: this function cannot handle checking security for AP/Hotspot mode;
+ * use nm_utils_ap_mode_security_valid() instead.
+ *
+ * Returns: %TRUE if the device capabilities and AP capabilties intersect and are
+ * compatible with the desired @type, %FALSE if they are not
+ **/
+gboolean
+nm_utils_security_valid (NMUtilsSecurityType type,
+ NMDeviceWifiCapabilities wifi_caps,
+ gboolean have_ap,
+ gboolean adhoc,
+ NM80211ApFlags ap_flags,
+ NM80211ApSecurityFlags ap_wpa,
+ NM80211ApSecurityFlags ap_rsn)
+{
+ gboolean good = TRUE;
+
+ if (!have_ap) {
+ if (type == NMU_SEC_NONE)
+ return TRUE;
+ if ( (type == NMU_SEC_STATIC_WEP)
+ || ((type == NMU_SEC_DYNAMIC_WEP) && !adhoc)
+ || ((type == NMU_SEC_LEAP) && !adhoc)) {
+ if (wifi_caps & (NM_WIFI_DEVICE_CAP_CIPHER_WEP40 | NM_WIFI_DEVICE_CAP_CIPHER_WEP104))
+ return TRUE;
+ else
+ return FALSE;
+ }
+ }
+
+ switch (type) {
+ case NMU_SEC_NONE:
+ g_assert (have_ap);
+ if (ap_flags & NM_802_11_AP_FLAGS_PRIVACY)
+ return FALSE;
+ if (ap_wpa || ap_rsn)
+ return FALSE;
+ break;
+ case NMU_SEC_LEAP: /* require PRIVACY bit for LEAP? */
+ if (adhoc)
+ return FALSE;
+ /* Fall through */
+ case NMU_SEC_STATIC_WEP:
+ g_assert (have_ap);
+ if (!(ap_flags & NM_802_11_AP_FLAGS_PRIVACY))
+ return FALSE;
+ if (ap_wpa || ap_rsn) {
+ if (!device_supports_ap_ciphers (wifi_caps, ap_wpa, TRUE))
+ if (!device_supports_ap_ciphers (wifi_caps, ap_rsn, TRUE))
+ return FALSE;
+ }
+ break;
+ case NMU_SEC_DYNAMIC_WEP:
+ if (adhoc)
+ return FALSE;
+ g_assert (have_ap);
+ if (ap_rsn || !(ap_flags & NM_802_11_AP_FLAGS_PRIVACY))
+ return FALSE;
+ /* Some APs broadcast minimal WPA-enabled beacons that must be handled */
+ if (ap_wpa) {
+ if (!(ap_wpa & NM_802_11_AP_SEC_KEY_MGMT_802_1X))
+ return FALSE;
+ if (!device_supports_ap_ciphers (wifi_caps, ap_wpa, FALSE))
+ return FALSE;
+ }
+ break;
+ case NMU_SEC_WPA_PSK:
+ if (adhoc)
+ return FALSE; /* FIXME: Kernel WPA Ad-Hoc support is buggy */
+ if (!(wifi_caps & NM_WIFI_DEVICE_CAP_WPA))
+ return FALSE;
+ if (have_ap) {
+ /* Ad-Hoc WPA APs won't necessarily have the PSK flag set, and
+ * they don't have any pairwise ciphers. */
+ if (adhoc) {
+ /* coverity[dead_error_line] */
+ if ( (ap_wpa & NM_802_11_AP_SEC_GROUP_TKIP)
+ && (wifi_caps & NM_WIFI_DEVICE_CAP_CIPHER_TKIP))
+ return TRUE;
+ if ( (ap_wpa & NM_802_11_AP_SEC_GROUP_CCMP)
+ && (wifi_caps & NM_WIFI_DEVICE_CAP_CIPHER_CCMP))
+ return TRUE;
+ } else {
+ if (ap_wpa & NM_802_11_AP_SEC_KEY_MGMT_PSK) {
+ if ( (ap_wpa & NM_802_11_AP_SEC_PAIR_TKIP)
+ && (wifi_caps & NM_WIFI_DEVICE_CAP_CIPHER_TKIP))
+ return TRUE;
+ if ( (ap_wpa & NM_802_11_AP_SEC_PAIR_CCMP)
+ && (wifi_caps & NM_WIFI_DEVICE_CAP_CIPHER_CCMP))
+ return TRUE;
+ }
+ }
+ return FALSE;
+ }
+ break;
+ case NMU_SEC_WPA2_PSK:
+ if (adhoc)
+ return FALSE; /* FIXME: Kernel WPA Ad-Hoc support is buggy */
+ if (!(wifi_caps & NM_WIFI_DEVICE_CAP_RSN))
+ return FALSE;
+ if (have_ap) {
+ /* Ad-Hoc WPA APs won't necessarily have the PSK flag set, and
+ * they don't have any pairwise ciphers, nor any RSA flags yet. */
+ if (adhoc) {
+ /* coverity[dead_error_line] */
+ if (wifi_caps & NM_WIFI_DEVICE_CAP_CIPHER_TKIP)
+ return TRUE;
+ if (wifi_caps & NM_WIFI_DEVICE_CAP_CIPHER_CCMP)
+ return TRUE;
+ } else {
+ if (ap_rsn & NM_802_11_AP_SEC_KEY_MGMT_PSK) {
+ if ( (ap_rsn & NM_802_11_AP_SEC_PAIR_TKIP)
+ && (wifi_caps & NM_WIFI_DEVICE_CAP_CIPHER_TKIP))
+ return TRUE;
+ if ( (ap_rsn & NM_802_11_AP_SEC_PAIR_CCMP)
+ && (wifi_caps & NM_WIFI_DEVICE_CAP_CIPHER_CCMP))
+ return TRUE;
+ }
+ }
+ return FALSE;
+ }
+ break;
+ case NMU_SEC_WPA_ENTERPRISE:
+ if (adhoc)
+ return FALSE;
+ if (!(wifi_caps & NM_WIFI_DEVICE_CAP_WPA))
+ return FALSE;
+ if (have_ap) {
+ if (!(ap_wpa & NM_802_11_AP_SEC_KEY_MGMT_802_1X))
+ return FALSE;
+ /* Ensure at least one WPA cipher is supported */
+ if (!device_supports_ap_ciphers (wifi_caps, ap_wpa, FALSE))
+ return FALSE;
+ }
+ break;
+ case NMU_SEC_WPA2_ENTERPRISE:
+ if (adhoc)
+ return FALSE;
+ if (!(wifi_caps & NM_WIFI_DEVICE_CAP_RSN))
+ return FALSE;
+ if (have_ap) {
+ if (!(ap_rsn & NM_802_11_AP_SEC_KEY_MGMT_802_1X))
+ return FALSE;
+ /* Ensure at least one WPA cipher is supported */
+ if (!device_supports_ap_ciphers (wifi_caps, ap_rsn, FALSE))
+ return FALSE;
+ }
+ break;
+ default:
+ good = FALSE;
+ break;
+ }
+
+ return good;
+}
+
+/**
+ * nm_utils_wep_key_valid:
+ * @key: a string that might be a WEP key
+ * @wep_type: the #NMWepKeyType type of the WEP key
+ *
+ * Checks if @key is a valid WEP key
+ *
+ * Returns: %TRUE if @key is a WEP key, %FALSE if not
+ *
+ * Since: 0.9.8
+ */
+gboolean
+nm_utils_wep_key_valid (const char *key, NMWepKeyType wep_type)
+{
+ int keylen, i;
+
+ if (!key)
+ return FALSE;
+
+ keylen = strlen (key);
+ if ( wep_type == NM_WEP_KEY_TYPE_KEY
+ || wep_type == NM_WEP_KEY_TYPE_UNKNOWN) {
+ if (keylen == 10 || keylen == 26) {
+ /* Hex key */
+ for (i = 0; i < keylen; i++) {
+ if (!g_ascii_isxdigit (key[i]))
+ return FALSE;
+ }
+ } else if (keylen == 5 || keylen == 13) {
+ /* ASCII key */
+ for (i = 0; i < keylen; i++) {
+ if (!g_ascii_isprint (key[i]))
+ return FALSE;
+ }
+ } else
+ return FALSE;
+
+ } else if (wep_type == NM_WEP_KEY_TYPE_PASSPHRASE) {
+ if (!keylen || keylen > 64)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ * nm_utils_wpa_psk_valid:
+ * @psk: a string that might be a WPA PSK
+ *
+ * Checks if @psk is a valid WPA PSK
+ *
+ * Returns: %TRUE if @psk is a WPA PSK, %FALSE if not
+ *
+ * Since: 0.9.8
+ */
+gboolean
+nm_utils_wpa_psk_valid (const char *psk)
+{
+ int psklen, i;
+
+ if (!psk)
+ return FALSE;
+
+ psklen = strlen (psk);
+ if (psklen < 8 || psklen > 64)
+ return FALSE;
+
+ if (psklen == 64) {
+ /* Hex PSK */
+ for (i = 0; i < psklen; i++) {
+ if (!g_ascii_isxdigit (psk[i]))
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+/**
+ * nm_utils_ip4_addresses_from_gvalue:
+ * @value: #GValue containing a #GPtrArray of #GArrays of #guint32s
+ *
+ * Utility function to convert a #GPtrArray of #GArrays of #guint32s representing
+ * a list of NetworkManager IPv4 addresses (which is a tuple of address, gateway,
+ * and prefix) into a #GSList of #NMIP4Address objects. The specific format of
+ * this serialization is not guaranteed to be stable and the #GArray may be
+ * extended in the future.
+ *
+ * Returns: (transfer full) (element-type NMIP4Address): a newly allocated #GSList of #NMIP4Address objects
+ **/
+GSList *
+nm_utils_ip4_addresses_from_gvalue (const GValue *value)
+{
+ GPtrArray *addresses;
+ int i;
+ GSList *list = NULL;
+
+ addresses = (GPtrArray *) g_value_get_boxed (value);
+ for (i = 0; addresses && (i < addresses->len); i++) {
+ GArray *array = (GArray *) g_ptr_array_index (addresses, i);
+ NMIP4Address *addr;
+
+ if (array->len < 3) {
+ g_warning ("Ignoring invalid IP4 address");
+ continue;
+ }
+
+ addr = nm_ip4_address_new ();
+ nm_ip4_address_set_address (addr, g_array_index (array, guint32, 0));
+ nm_ip4_address_set_prefix (addr, g_array_index (array, guint32, 1));
+ nm_ip4_address_set_gateway (addr, g_array_index (array, guint32, 2));
+ list = g_slist_prepend (list, addr);
+ }
+
+ return g_slist_reverse (list);
+}
+
+/**
+ * nm_utils_ip4_addresses_to_gvalue:
+ * @list: (element-type NMIP4Address): a list of #NMIP4Address objects
+ * @value: a pointer to a #GValue into which to place the converted addresses,
+ * which should be unset by the caller (when no longer needed) with
+ * g_value_unset().
+ *
+ * Utility function to convert a #GSList of #NMIP4Address objects into a
+ * #GPtrArray of #GArrays of #guint32s representing a list of NetworkManager IPv4
+ * addresses (which is a tuple of address, gateway, and prefix). The specific
+ * format of this serialization is not guaranteed to be stable and may be
+ * extended in the future.
+ **/
+void
+nm_utils_ip4_addresses_to_gvalue (GSList *list, GValue *value)
+{
+ GPtrArray *addresses;
+ GSList *iter;
+
+ addresses = g_ptr_array_new ();
+
+ for (iter = list; iter; iter = iter->next) {
+ NMIP4Address *addr = (NMIP4Address *) iter->data;
+ GArray *array;
+ guint32 tmp;
+
+ array = g_array_sized_new (FALSE, TRUE, sizeof (guint32), 3);
+
+ tmp = nm_ip4_address_get_address (addr);
+ g_array_append_val (array, tmp);
+
+ tmp = nm_ip4_address_get_prefix (addr);
+ g_array_append_val (array, tmp);
+
+ tmp = nm_ip4_address_get_gateway (addr);
+ g_array_append_val (array, tmp);
+
+ g_ptr_array_add (addresses, array);
+ }
+
+ g_value_take_boxed (value, addresses);
+}
+
+/**
+ * nm_utils_ip4_routes_from_gvalue:
+ * @value: #GValue containing a #GPtrArray of #GArrays of #guint32s
+ *
+ * Utility function to convert a #GPtrArray of #GArrays of #guint32s representing
+ * a list of NetworkManager IPv4 routes (which is a tuple of route, next hop,
+ * prefix, and metric) into a #GSList of #NMIP4Route objects. The specific
+ * format of this serialization is not guaranteed to be stable and may be
+ * extended in the future.
+ *
+ * Returns: (transfer full) (element-type NMIP4Route): a newly allocated #GSList of #NMIP4Route objects
+ **/
+GSList *
+nm_utils_ip4_routes_from_gvalue (const GValue *value)
+{
+ GPtrArray *routes;
+ int i;
+ GSList *list = NULL;
+
+ routes = (GPtrArray *) g_value_get_boxed (value);
+ for (i = 0; routes && (i < routes->len); i++) {
+ GArray *array = (GArray *) g_ptr_array_index (routes, i);
+ NMIP4Route *route;
+
+ if (array->len < 4) {
+ g_warning ("Ignoring invalid IP4 route");
+ continue;
+ }
+
+ route = nm_ip4_route_new ();
+ nm_ip4_route_set_dest (route, g_array_index (array, guint32, 0));
+ nm_ip4_route_set_prefix (route, g_array_index (array, guint32, 1));
+ nm_ip4_route_set_next_hop (route, g_array_index (array, guint32, 2));
+ nm_ip4_route_set_metric (route, g_array_index (array, guint32, 3));
+ list = g_slist_prepend (list, route);
+ }
+
+ return g_slist_reverse (list);
+}
+
+/**
+ * nm_utils_ip4_routes_to_gvalue:
+ * @list: (element-type NMIP4Route): a list of #NMIP4Route objects
+ * @value: a pointer to a #GValue into which to place the converted routes,
+ * which should be unset by the caller (when no longer needed) with
+ * g_value_unset().
+ *
+ * Utility function to convert a #GSList of #NMIP4Route objects into a
+ * #GPtrArray of #GArrays of #guint32s representing a list of NetworkManager IPv4
+ * routes (which is a tuple of route, next hop, prefix, and metric). The
+ * specific format of this serialization is not guaranteed to be stable and may
+ * be extended in the future.
+ **/
+void
+nm_utils_ip4_routes_to_gvalue (GSList *list, GValue *value)
+{
+ GPtrArray *routes;
+ GSList *iter;
+
+ routes = g_ptr_array_new ();
+
+ for (iter = list; iter; iter = iter->next) {
+ NMIP4Route *route = (NMIP4Route *) iter->data;
+ GArray *array;
+ guint32 tmp;
+
+ array = g_array_sized_new (FALSE, TRUE, sizeof (guint32), 3);
+
+ tmp = nm_ip4_route_get_dest (route);
+ g_array_append_val (array, tmp);
+
+ tmp = nm_ip4_route_get_prefix (route);
+ g_array_append_val (array, tmp);
+
+ tmp = nm_ip4_route_get_next_hop (route);
+ g_array_append_val (array, tmp);
+
+ tmp = nm_ip4_route_get_metric (route);
+ g_array_append_val (array, tmp);
+
+ g_ptr_array_add (routes, array);
+ }
+
+ g_value_take_boxed (value, routes);
+}
+
+/**
+ * nm_utils_ip4_netmask_to_prefix:
+ * @netmask: an IPv4 netmask in network byte order
+ *
+ * Returns: the CIDR prefix represented by the netmask
+ **/
+guint32
+nm_utils_ip4_netmask_to_prefix (guint32 netmask)
+{
+ guint32 prefix;
+ guint8 v;
+ const guint8 *p = (guint8 *) &netmask;
+
+ if (p[3]) {
+ prefix = 24;
+ v = p[3];
+ } else if (p[2]) {
+ prefix = 16;
+ v = p[2];
+ } else if (p[1]) {
+ prefix = 8;
+ v = p[1];
+ } else {
+ prefix = 0;
+ v = p[0];
+ }
+
+ while (v) {
+ prefix++;
+ v <<= 1;
+ }
+
+ return prefix;
+}
+
+/**
+ * nm_utils_ip4_prefix_to_netmask:
+ * @prefix: a CIDR prefix
+ *
+ * Returns: the netmask represented by the prefix, in network byte order
+ **/
+guint32
+nm_utils_ip4_prefix_to_netmask (guint32 prefix)
+{
+ return prefix < 32 ? ~htonl(0xFFFFFFFF >> prefix) : 0xFFFFFFFF;
+}
+
+
+/**
+ * nm_utils_ip4_get_default_prefix:
+ * @ip: an IPv4 address (in network byte order)
+ *
+ * When the Internet was originally set up, various ranges of IP addresses were
+ * segmented into three network classes: A, B, and C. This function will return
+ * a prefix that is associated with the IP address specified defining where it
+ * falls in the predefined classes.
+ *
+ * Returns: the default class prefix for the given IP
+ **/
+/* The function is originally from ipcalc.c of Red Hat's initscripts. */
+guint32
+nm_utils_ip4_get_default_prefix (guint32 ip)
+{
+ if (((ntohl (ip) & 0xFF000000) >> 24) <= 127)
+ return 8; /* Class A - 255.0.0.0 */
+ else if (((ntohl (ip) & 0xFF000000) >> 24) <= 191)
+ return 16; /* Class B - 255.255.0.0 */
+
+ return 24; /* Class C - 255.255.255.0 */
+}
+
+/**
+ * nm_utils_ip6_addresses_from_gvalue:
+ * @value: gvalue containing a GPtrArray of GValueArrays of (GArray of guchars) and #guint32
+ *
+ * Utility function to convert a #GPtrArray of #GValueArrays of (#GArray of guchars) and #guint32
+ * representing a list of NetworkManager IPv6 addresses (which is a tuple of address,
+ * prefix, and gateway), into a #GSList of #NMIP6Address objects. The specific format of
+ * this serialization is not guaranteed to be stable and the #GValueArray may be
+ * extended in the future.
+ *
+ * Returns: (transfer full) (element-type NMIP6Address): a newly allocated #GSList of #NMIP6Address objects
+ **/
+GSList *
+nm_utils_ip6_addresses_from_gvalue (const GValue *value)
+{
+ GPtrArray *addresses;
+ int i;
+ GSList *list = NULL;
+
+ addresses = (GPtrArray *) g_value_get_boxed (value);
+
+ for (i = 0; addresses && (i < addresses->len); i++) {
+ GValueArray *elements = (GValueArray *) g_ptr_array_index (addresses, i);
+ GValue *tmp;
+ GByteArray *ba_addr;
+ GByteArray *ba_gw = NULL;
+ NMIP6Address *addr;
+ guint32 prefix;
+
+ if (elements->n_values < 2 || elements->n_values > 3) {
+ g_warning ("%s: ignoring invalid IP6 address structure", __func__);
+ continue;
+ }
+
+ /* Third element (gateway) is optional */
+ if ( !_nm_utils_gvalue_array_validate (elements, 2, DBUS_TYPE_G_UCHAR_ARRAY, G_TYPE_UINT)
+ && !_nm_utils_gvalue_array_validate (elements, 3, DBUS_TYPE_G_UCHAR_ARRAY, G_TYPE_UINT, DBUS_TYPE_G_UCHAR_ARRAY)) {
+ g_warning ("%s: ignoring invalid IP6 address structure", __func__);
+ continue;
+ }
+
+ tmp = g_value_array_get_nth (elements, 0);
+ ba_addr = g_value_get_boxed (tmp);
+ if (ba_addr->len != 16) {
+ g_warning ("%s: ignoring invalid IP6 address of length %d",
+ __func__, ba_addr->len);
+ continue;
+ }
+
+ tmp = g_value_array_get_nth (elements, 1);
+ prefix = g_value_get_uint (tmp);
+ if (prefix > 128) {
+ g_warning ("%s: ignoring invalid IP6 prefix %d",
+ __func__, prefix);
+ continue;
+ }
+
+ if (elements->n_values == 3) {
+ tmp = g_value_array_get_nth (elements, 2);
+ ba_gw = g_value_get_boxed (tmp);
+ if (ba_gw->len != 16) {
+ g_warning ("%s: ignoring invalid IP6 gateway address of length %d",
+ __func__, ba_gw->len);
+ continue;
+ }
+ }
+
+ addr = nm_ip6_address_new ();
+ nm_ip6_address_set_prefix (addr, prefix);
+ nm_ip6_address_set_address (addr, (const struct in6_addr *) ba_addr->data);
+ if (ba_gw)
+ nm_ip6_address_set_gateway (addr, (const struct in6_addr *) ba_gw->data);
+
+ list = g_slist_prepend (list, addr);
+ }
+
+ return g_slist_reverse (list);
+}
+
+/**
+ * nm_utils_ip6_addresses_to_gvalue:
+ * @list: (element-type NMIP6Address): a list of #NMIP6Address objects
+ * @value: a pointer to a #GValue into which to place the converted addresses,
+ * which should be unset by the caller (when no longer needed) with
+ * g_value_unset().
+ *
+ * Utility function to convert a #GSList of #NMIP6Address objects into a
+ * #GPtrArray of #GValueArrays representing a list of NetworkManager IPv6 addresses
+ * (which is a tuple of address, prefix, and gateway). The specific format of
+ * this serialization is not guaranteed to be stable and may be extended in the
+ * future.
+ **/
+void
+nm_utils_ip6_addresses_to_gvalue (GSList *list, GValue *value)
+{
+ GPtrArray *addresses;
+ GSList *iter;
+
+ addresses = g_ptr_array_new ();
+
+ for (iter = list; iter; iter = iter->next) {
+ NMIP6Address *addr = (NMIP6Address *) iter->data;
+ GValueArray *array;
+ GValue element = G_VALUE_INIT;
+ GByteArray *ba;
+
+ array = g_value_array_new (3);
+
+ /* IP address */
+ g_value_init (&element, DBUS_TYPE_G_UCHAR_ARRAY);
+ ba = g_byte_array_new ();
+ g_byte_array_append (ba, (guint8 *) nm_ip6_address_get_address (addr), 16);
+ g_value_take_boxed (&element, ba);
+ g_value_array_append (array, &element);
+ g_value_unset (&element);
+
+ /* Prefix */
+ g_value_init (&element, G_TYPE_UINT);
+ g_value_set_uint (&element, nm_ip6_address_get_prefix (addr));
+ g_value_array_append (array, &element);
+ g_value_unset (&element);
+
+ /* Gateway */
+ g_value_init (&element, DBUS_TYPE_G_UCHAR_ARRAY);
+ ba = g_byte_array_new ();
+ g_byte_array_append (ba, (guint8 *) nm_ip6_address_get_gateway (addr), 16);
+ g_value_take_boxed (&element, ba);
+ g_value_array_append (array, &element);
+ g_value_unset (&element);
+
+ g_ptr_array_add (addresses, array);
+ }
+
+ g_value_take_boxed (value, addresses);
+}
+
+/**
+ * nm_utils_ip6_routes_from_gvalue:
+ * @value: #GValue containing a #GPtrArray of #GValueArrays of (#GArray of #guchars), #guint32,
+ * (#GArray of #guchars), and #guint32
+ *
+ * Utility function #GPtrArray of #GValueArrays of (#GArray of #guchars), #guint32,
+ * (#GArray of #guchars), and #guint32 representing a list of NetworkManager IPv6
+ * routes (which is a tuple of destination, prefix, next hop, and metric)
+ * into a #GSList of #NMIP6Route objects. The specific format of this serialization
+ * is not guaranteed to be stable and may be extended in the future.
+ *
+ * Returns: (transfer full) (element-type NMIP6Route): a newly allocated #GSList of #NMIP6Route objects
+ **/
+GSList *
+nm_utils_ip6_routes_from_gvalue (const GValue *value)
+{
+ GPtrArray *routes;
+ int i;
+ GSList *list = NULL;
+
+ routes = (GPtrArray *) g_value_get_boxed (value);
+ for (i = 0; routes && (i < routes->len); i++) {
+ GValueArray *route_values = (GValueArray *) g_ptr_array_index (routes, i);
+ GByteArray *dest, *next_hop;
+ guint prefix, metric;
+ NMIP6Route *route;
+
+ if (!_nm_utils_gvalue_array_validate (route_values, 4,
+ DBUS_TYPE_G_UCHAR_ARRAY,
+ G_TYPE_UINT,
+ DBUS_TYPE_G_UCHAR_ARRAY,
+ G_TYPE_UINT)) {
+ g_warning ("Ignoring invalid IP6 route");
+ continue;
+ }
+
+ dest = g_value_get_boxed (g_value_array_get_nth (route_values, 0));
+ if (dest->len != 16) {
+ g_warning ("%s: ignoring invalid IP6 dest address of length %d",
+ __func__, dest->len);
+ continue;
+ }
+
+ prefix = g_value_get_uint (g_value_array_get_nth (route_values, 1));
+
+ next_hop = g_value_get_boxed (g_value_array_get_nth (route_values, 2));
+ if (next_hop->len != 16) {
+ g_warning ("%s: ignoring invalid IP6 next_hop address of length %d",
+ __func__, next_hop->len);
+ continue;
+ }
+
+ metric = g_value_get_uint (g_value_array_get_nth (route_values, 3));
+
+ route = nm_ip6_route_new ();
+ nm_ip6_route_set_dest (route, (struct in6_addr *)dest->data);
+ nm_ip6_route_set_prefix (route, prefix);
+ nm_ip6_route_set_next_hop (route, (struct in6_addr *)next_hop->data);
+ nm_ip6_route_set_metric (route, metric);
+ list = g_slist_prepend (list, route);
+ }
+
+ return g_slist_reverse (list);
+}
+
+/**
+ * nm_utils_ip6_routes_to_gvalue:
+ * @list: (element-type NMIP6Route): a list of #NMIP6Route objects
+ * @value: a pointer to a #GValue into which to place the converted routes,
+ * which should be unset by the caller (when no longer needed) with
+ * g_value_unset().
+ *
+ * Utility function to convert a #GSList of #NMIP6Route objects into a #GPtrArray of
+ * #GValueArrays of (#GArray of #guchars), #guint32, (#GArray of #guchars), and #guint32
+ * representing a list of NetworkManager IPv6 routes (which is a tuple of destination,
+ * prefix, next hop, and metric). The specific format of this serialization is not
+ * guaranteed to be stable and may be extended in the future.
+ **/
+void
+nm_utils_ip6_routes_to_gvalue (GSList *list, GValue *value)
+{
+ GPtrArray *routes;
+ GSList *iter;
+
+ routes = g_ptr_array_new ();
+
+ for (iter = list; iter; iter = iter->next) {
+ NMIP6Route *route = (NMIP6Route *) iter->data;
+ GValueArray *array;
+ const struct in6_addr *addr;
+ GByteArray *ba;
+ GValue element = G_VALUE_INIT;
+
+ array = g_value_array_new (4);
+
+ g_value_init (&element, DBUS_TYPE_G_UCHAR_ARRAY);
+ addr = nm_ip6_route_get_dest (route);
+ ba = g_byte_array_new ();
+ g_byte_array_append (ba, (guchar *)addr, sizeof (*addr));
+ g_value_take_boxed (&element, ba);
+ g_value_array_append (array, &element);
+ g_value_unset (&element);
+
+ g_value_init (&element, G_TYPE_UINT);
+ g_value_set_uint (&element, nm_ip6_route_get_prefix (route));
+ g_value_array_append (array, &element);
+ g_value_unset (&element);
+
+ g_value_init (&element, DBUS_TYPE_G_UCHAR_ARRAY);
+ addr = nm_ip6_route_get_next_hop (route);
+ ba = g_byte_array_new ();
+ g_byte_array_append (ba, (guchar *)addr, sizeof (*addr));
+ g_value_take_boxed (&element, ba);
+ g_value_array_append (array, &element);
+ g_value_unset (&element);
+
+ g_value_init (&element, G_TYPE_UINT);
+ g_value_set_uint (&element, nm_ip6_route_get_metric (route));
+ g_value_array_append (array, &element);
+ g_value_unset (&element);
+
+ g_ptr_array_add (routes, array);
+ }
+
+ g_value_take_boxed (value, routes);
+}
+
+/**
+ * nm_utils_ip6_dns_from_gvalue: (skip)
+ * @value: a #GValue
+ *
+ * Converts a #GValue containing a #GPtrArray of IP6 DNS, represented as
+ * #GByteArrays into a #GSList of <literal><type>struct in6_addr</type></literal>s.
+ *
+ * Returns: a #GSList of IP6 addresses.
+ */
+GSList *
+nm_utils_ip6_dns_from_gvalue (const GValue *value)
+{
+ GPtrArray *dns;
+ int i;
+ GSList *list = NULL;
+
+ dns = (GPtrArray *) g_value_get_boxed (value);
+ for (i = 0; dns && (i < dns->len); i++) {
+ GByteArray *bytearray = (GByteArray *) g_ptr_array_index (dns, i);
+ struct in6_addr *addr;
+
+ if (bytearray->len != 16) {
+ g_warning ("%s: ignoring invalid IP6 address of length %d",
+ __func__, bytearray->len);
+ continue;
+ }
+
+ addr = g_malloc0 (sizeof (struct in6_addr));
+ memcpy (addr->s6_addr, bytearray->data, bytearray->len);
+ list = g_slist_prepend (list, addr);
+ }
+
+ return g_slist_reverse (list);
+}
+
+/**
+ * nm_utils_ip6_dns_to_gvalue: (skip)
+ * @list: a list of #NMIP6Route objects
+ * @value: a pointer to a #GValue into which to place the converted DNS server
+ * addresses, which should be unset by the caller (when no longer needed) with
+ * g_value_unset().
+ *
+ * Utility function to convert a #GSList of <literal><type>struct
+ * in6_addr</type></literal> structs into a #GPtrArray of #GByteArrays
+ * representing each server's IPv6 addresses in network byte order.
+ * The specific format of this serialization is not guaranteed to be
+ * stable and may be extended in the future.
+ */
+void
+nm_utils_ip6_dns_to_gvalue (GSList *list, GValue *value)
+{
+ GPtrArray *dns;
+ GSList *iter;
+
+ dns = g_ptr_array_new ();
+
+ for (iter = list; iter; iter = iter->next) {
+ struct in6_addr *addr = (struct in6_addr *) iter->data;
+ GByteArray *bytearray;
+
+ bytearray = g_byte_array_sized_new (16);
+ g_byte_array_append (bytearray, (guint8 *) addr->s6_addr, 16);
+ g_ptr_array_add (dns, bytearray);
+ }
+
+ g_value_take_boxed (value, dns);
+}
+
+/**
+ * nm_utils_uuid_generate:
+ *
+ * Returns: a newly allocated UUID suitable for use as the #NMSettingConnection
+ * object's #NMSettingConnection:id: property. Should be freed with g_free()
+ **/
+char *
+nm_utils_uuid_generate (void)
+{
+ uuid_t uuid;
+ char *buf;
+
+ buf = g_malloc0 (37);
+ uuid_generate_random (uuid);
+ uuid_unparse_lower (uuid, &buf[0]);
+ return buf;
+}
+
+/**
+ * nm_utils_uuid_generate_from_string:
+ * @s: a string to use as the seed for the UUID
+ *
+ * For a given @s, this function will always return the same UUID.
+ *
+ * Returns: a newly allocated UUID suitable for use as the #NMSettingConnection
+ * object's #NMSettingConnection:id: property
+ **/
+char *
+nm_utils_uuid_generate_from_string (const char *s)
+{
+ GError *error = NULL;
+ uuid_t *uuid;
+ char *buf = NULL;
+
+ if (!nm_utils_init (&error)) {
+ g_warning ("error initializing crypto: (%d) %s",
+ error ? error->code : 0,
+ error ? error->message : "unknown");
+ if (error)
+ g_error_free (error);
+ return NULL;
+ }
+
+ uuid = g_malloc0 (sizeof (*uuid));
+ if (!crypto_md5_hash (NULL, 0, s, strlen (s), (char *) uuid, sizeof (*uuid), &error)) {
+ g_warning ("error generating UUID: (%d) %s",
+ error ? error->code : 0,
+ error ? error->message : "unknown");
+ if (error)
+ g_error_free (error);
+ goto out;
+ }
+
+ buf = g_malloc0 (37);
+ uuid_unparse_lower (*uuid, &buf[0]);
+
+out:
+ g_free (uuid);
+ return buf;
+}
+
+static char *
+make_key (const char *cipher,
+ const char *salt,
+ const gsize salt_len,
+ const char *password,
+ gsize *out_len,
+ GError **error)
+{
+ char *key;
+ guint32 digest_len = 24; /* DES-EDE3-CBC */
+
+ g_return_val_if_fail (salt != NULL, NULL);
+ g_return_val_if_fail (salt_len >= 8, NULL);
+ g_return_val_if_fail (password != NULL, NULL);
+ g_return_val_if_fail (out_len != NULL, NULL);
+
+ if (!strcmp (cipher, "DES-EDE3-CBC"))
+ digest_len = 24;
+ else if (!strcmp (cipher, "AES-128-CBC"))
+ digest_len = 16;
+
+ key = g_malloc0 (digest_len + 1);
+
+ if (!crypto_md5_hash (salt, salt_len, password, strlen (password), key, digest_len, error)) {
+ *out_len = 0;
+ memset (key, 0, digest_len);
+ g_free (key);
+ key = NULL;
+ } else
+ *out_len = digest_len;
+
+ return key;
+}
+
+/**
+ * nm_utils_rsa_key_encrypt_helper:
+ * @cipher: cipher to use for encryption ("DES-EDE3-CBC" or "AES-128-CBC")
+ * @data: RSA private key data to be encrypted
+ * @in_password: (allow-none): existing password to use, if any
+ * @out_password: (out) (allow-none): if @in_password was %NULL, a random password will be generated
+ * and returned in this argument
+ * @error: detailed error information on return, if an error occurred
+ *
+ * Encrypts the given RSA private key data with the given password (or generates
+ * a password if no password was given) and converts the data to PEM format
+ * suitable for writing to a file.
+ *
+ * Returns: (transfer full): on success, PEM-formatted data suitable for writing to a PEM-formatted
+ * certificate/private key file.
+ **/
+static GByteArray *
+nm_utils_rsa_key_encrypt_helper (const char *cipher,
+ const GByteArray *data,
+ const char *in_password,
+ char **out_password,
+ GError **error)
+{
+ char salt[16];
+ int salt_len;
+ char *key = NULL, *enc = NULL, *pw_buf[32];
+ gsize key_len = 0, enc_len = 0;
+ GString *pem = NULL;
+ char *tmp, *tmp_password = NULL;
+ int left;
+ const char *p;
+ GByteArray *ret = NULL;
+
+ g_return_val_if_fail (!g_strcmp0 (cipher, CIPHER_DES_EDE3_CBC) || !g_strcmp0 (cipher, CIPHER_AES_CBC), NULL);
+ g_return_val_if_fail (data != NULL, NULL);
+ g_return_val_if_fail (data->len > 0, NULL);
+ if (out_password)
+ g_return_val_if_fail (*out_password == NULL, NULL);
+
+ /* Make the password if needed */
+ if (!in_password) {
+ if (!crypto_randomize (pw_buf, sizeof (pw_buf), error))
+ return NULL;
+ in_password = tmp_password = nm_utils_bin2hexstr ((const char *) pw_buf, sizeof (pw_buf), -1);
+ }
+
+ if (g_strcmp0 (cipher, CIPHER_AES_CBC) == 0)
+ salt_len = 16;
+ else
+ salt_len = 8;
+
+ if (!crypto_randomize (salt, salt_len, error))
+ goto out;
+
+ key = make_key (cipher, &salt[0], salt_len, in_password, &key_len, error);
+ if (!key)
+ goto out;
+
+ enc = crypto_encrypt (cipher, data, salt, salt_len, key, key_len, &enc_len, error);
+ if (!enc)
+ goto out;
+
+ pem = g_string_sized_new (enc_len * 2 + 100);
+ g_string_append (pem, "-----BEGIN RSA PRIVATE KEY-----\n");
+ g_string_append (pem, "Proc-Type: 4,ENCRYPTED\n");
+
+ /* Convert the salt to a hex string */
+ tmp = nm_utils_bin2hexstr ((const char *) salt, salt_len, salt_len * 2);
+ g_string_append_printf (pem, "DEK-Info: %s,%s\n\n", cipher, tmp);
+ g_free (tmp);
+
+ /* Convert the encrypted key to a base64 string */
+ p = tmp = g_base64_encode ((const guchar *) enc, enc_len);
+ left = strlen (tmp);
+ while (left > 0) {
+ g_string_append_len (pem, p, (left < 64) ? left : 64);
+ g_string_append_c (pem, '\n');
+ left -= 64;
+ p += 64;
+ }
+ g_free (tmp);
+
+ g_string_append (pem, "-----END RSA PRIVATE KEY-----\n");
+
+ ret = g_byte_array_sized_new (pem->len);
+ g_byte_array_append (ret, (const unsigned char *) pem->str, pem->len);
+ if (tmp_password && out_password)
+ *out_password = g_strdup (tmp_password);
+
+out:
+ if (key) {
+ memset (key, 0, key_len);
+ g_free (key);
+ }
+ if (enc) {
+ memset (enc, 0, enc_len);
+ g_free (enc);
+ }
+ if (pem)
+ g_string_free (pem, TRUE);
+
+ if (tmp_password) {
+ memset (tmp_password, 0, strlen (tmp_password));
+ g_free (tmp_password);
+ }
+
+ return ret;
+}
+
+/**
+ * nm_utils_rsa_key_encrypt:
+ * @data: RSA private key data to be encrypted
+ * @in_password: (allow-none): existing password to use, if any
+ * @out_password: (out) (allow-none): if @in_password was %NULL, a random password will be generated
+ * and returned in this argument
+ * @error: detailed error information on return, if an error occurred
+ *
+ * Encrypts the given RSA private key data with the given password (or generates
+ * a password if no password was given) and converts the data to PEM format
+ * suitable for writing to a file. It uses Triple DES cipher for the encryption.
+ *
+ * Returns: (transfer full): on success, PEM-formatted data suitable for writing to a PEM-formatted
+ * certificate/private key file.
+ **/
+GByteArray *
+nm_utils_rsa_key_encrypt (const GByteArray *data,
+ const char *in_password,
+ char **out_password,
+ GError **error)
+{
+
+
+ return nm_utils_rsa_key_encrypt_helper (CIPHER_DES_EDE3_CBC,
+ data,
+ in_password,
+ out_password,
+ error);
+}
+
+/**
+ * nm_utils_rsa_key_encrypt_aes:
+ * @data: RSA private key data to be encrypted
+ * @in_password: (allow-none): existing password to use, if any
+ * @out_password: (out) (allow-none): if @in_password was %NULL, a random password will be generated
+ * and returned in this argument
+ * @error: detailed error information on return, if an error occurred
+ *
+ * Encrypts the given RSA private key data with the given password (or generates
+ * a password if no password was given) and converts the data to PEM format
+ * suitable for writing to a file. It uses AES cipher for the encryption.
+ *
+ * Returns: (transfer full): on success, PEM-formatted data suitable for writing to a PEM-formatted
+ * certificate/private key file.
+ **/
+GByteArray *
+nm_utils_rsa_key_encrypt_aes (const GByteArray *data,
+ const char *in_password,
+ char **out_password,
+ GError **error)
+{
+
+ return nm_utils_rsa_key_encrypt_helper (CIPHER_AES_CBC,
+ data,
+ in_password,
+ out_password,
+ error);
+}
+
+/**
+ * nm_utils_file_is_pkcs12:
+ * @filename: name of the file to test
+ *
+ * Utility function to find out if the @filename is in PKCS#12 format.
+ *
+ * Returns: %TRUE if the file is PKCS#12, %FALSE if it is not
+ **/
+gboolean
+nm_utils_file_is_pkcs12 (const char *filename)
+{
+ return crypto_is_pkcs12_file (filename, NULL);
+}
+
+/* Band, channel/frequency stuff for wireless */
+struct cf_pair {
+ guint32 chan;
+ guint32 freq;
+};
+
+static struct cf_pair a_table[] = {
+ /* A band */
+ { 7, 5035 },
+ { 8, 5040 },
+ { 9, 5045 },
+ { 11, 5055 },
+ { 12, 5060 },
+ { 16, 5080 },
+ { 34, 5170 },
+ { 36, 5180 },
+ { 38, 5190 },
+ { 40, 5200 },
+ { 42, 5210 },
+ { 44, 5220 },
+ { 46, 5230 },
+ { 48, 5240 },
+ { 50, 5250 },
+ { 52, 5260 },
+ { 56, 5280 },
+ { 58, 5290 },
+ { 60, 5300 },
+ { 64, 5320 },
+ { 100, 5500 },
+ { 104, 5520 },
+ { 108, 5540 },
+ { 112, 5560 },
+ { 116, 5580 },
+ { 120, 5600 },
+ { 124, 5620 },
+ { 128, 5640 },
+ { 132, 5660 },
+ { 136, 5680 },
+ { 140, 5700 },
+ { 149, 5745 },
+ { 152, 5760 },
+ { 153, 5765 },
+ { 157, 5785 },
+ { 160, 5800 },
+ { 161, 5805 },
+ { 165, 5825 },
+ { 183, 4915 },
+ { 184, 4920 },
+ { 185, 4925 },
+ { 187, 4935 },
+ { 188, 4945 },
+ { 192, 4960 },
+ { 196, 4980 },
+ { 0, -1 }
+};
+
+static struct cf_pair bg_table[] = {
+ /* B/G band */
+ { 1, 2412 },
+ { 2, 2417 },
+ { 3, 2422 },
+ { 4, 2427 },
+ { 5, 2432 },
+ { 6, 2437 },
+ { 7, 2442 },
+ { 8, 2447 },
+ { 9, 2452 },
+ { 10, 2457 },
+ { 11, 2462 },
+ { 12, 2467 },
+ { 13, 2472 },
+ { 14, 2484 },
+ { 0, -1 }
+};
+
+/**
+ * nm_utils_wifi_freq_to_channel:
+ * @freq: frequency
+ *
+ * Utility function to translate a Wi-Fi frequency to its corresponding channel.
+ *
+ * Returns: the channel represented by the frequency or 0
+ **/
+guint32
+nm_utils_wifi_freq_to_channel (guint32 freq)
+{
+ int i = 0;
+
+ if (freq > 4900) {
+ while (a_table[i].chan && (a_table[i].freq != freq))
+ i++;
+ return a_table[i].chan;
+ } else {
+ while (bg_table[i].chan && (bg_table[i].freq != freq))
+ i++;
+ return bg_table[i].chan;
+ }
+
+ return 0;
+}
+
+/**
+ * nm_utils_wifi_channel_to_freq:
+ * @channel: channel
+ * @band: frequency band for wireless ("a" or "bg")
+ *
+ * Utility function to translate a Wi-Fi channel to its corresponding frequency.
+ *
+ * Returns: the frequency represented by the channel of the band,
+ * or -1 when the freq is invalid, or 0 when the band
+ * is invalid
+ **/
+guint32
+nm_utils_wifi_channel_to_freq (guint32 channel, const char *band)
+{
+ int i = 0;
+
+ if (!strcmp (band, "a")) {
+ while (a_table[i].chan && (a_table[i].chan != channel))
+ i++;
+ return a_table[i].freq;
+ } else if (!strcmp (band, "bg")) {
+ while (bg_table[i].chan && (bg_table[i].chan != channel))
+ i++;
+ return bg_table[i].freq;
+ }
+
+ return 0;
+}
+
+/**
+ * nm_utils_wifi_find_next_channel:
+ * @channel: current channel
+ * @direction: whether going downward (0 or less) or upward (1 or more)
+ * @band: frequency band for wireless ("a" or "bg")
+ *
+ * Utility function to find out next/previous Wi-Fi channel for a channel.
+ *
+ * Returns: the next channel in the specified direction or 0
+ **/
+guint32
+nm_utils_wifi_find_next_channel (guint32 channel, int direction, char *band)
+{
+ size_t a_size = sizeof (a_table) / sizeof (struct cf_pair);
+ size_t bg_size = sizeof (bg_table) / sizeof (struct cf_pair);
+ struct cf_pair *pair = NULL;
+
+ if (!strcmp (band, "a")) {
+ if (channel < a_table[0].chan)
+ return a_table[0].chan;
+ if (channel > a_table[a_size - 2].chan)
+ return a_table[a_size - 2].chan;
+ pair = &a_table[0];
+ } else if (!strcmp (band, "bg")) {
+ if (channel < bg_table[0].chan)
+ return bg_table[0].chan;
+ if (channel > bg_table[bg_size - 2].chan)
+ return bg_table[bg_size - 2].chan;
+ pair = &bg_table[0];
+ } else {
+ g_assert_not_reached ();
+ return 0;
+ }
+
+ while (pair->chan) {
+ if (channel == pair->chan)
+ return channel;
+ if ((channel < (pair+1)->chan) && (channel > pair->chan)) {
+ if (direction > 0)
+ return (pair+1)->chan;
+ else
+ return pair->chan;
+ }
+ pair++;
+ }
+ return 0;
+}
+
+/**
+ * nm_utils_wifi_is_channel_valid:
+ * @channel: channel
+ * @band: frequency band for wireless ("a" or "bg")
+ *
+ * Utility function to verify Wi-Fi channel validity.
+ *
+ * Returns: %TRUE or %FALSE
+ **/
+gboolean
+nm_utils_wifi_is_channel_valid (guint32 channel, const char *band)
+{
+ struct cf_pair *table = NULL;
+ int i = 0;
+
+ if (!strcmp (band, "a"))
+ table = a_table;
+ else if (!strcmp (band, "bg"))
+ table = bg_table;
+ else
+ return FALSE;
+
+ while (table[i].chan && (table[i].chan != channel))
+ i++;
+
+ if (table[i].chan != 0)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+/**
+ * nm_utils_hwaddr_len:
+ * @type: the type of address; either %ARPHRD_ETHER or %ARPHRD_INFINIBAND
+ *
+ * Returns the length in octets of a hardware address of type @type.
+ *
+ * Return value: the positive length, or -1 if the type is unknown/unsupported.
+ */
+int
+nm_utils_hwaddr_len (int type)
+{
+ if (type == ARPHRD_ETHER)
+ return ETH_ALEN;
+ else if (type == ARPHRD_INFINIBAND)
+ return INFINIBAND_ALEN;
+ else
+ return -1;
+}
+
+/**
+ * nm_utils_hwaddr_type:
+ * @len: the length of hardware address in bytes
+ *
+ * Returns the type (either %ARPHRD_ETHER or %ARPHRD_INFINIBAND) of
+ * the raw address given its length.
+ *
+ * Return value: the type, either %ARPHRD_ETHER or %ARPHRD_INFINIBAND.
+ * If the length is unexpected, return -1 (unsupported type/length).
+ *
+ * Deprecated: This could not be extended to cover other types, since
+ * there is not a one-to-one mapping between types and lengths. This
+ * was mostly only used to get a type to pass to
+ * nm_utils_hwaddr_ntoa() or nm_utils_hwaddr_aton() when you only had
+ * a length; but you can just use nm_utils_hwaddr_ntoa_len() or
+ * nm_utils_hwaddr_aton_len() now instead.
+ */
+int
+nm_utils_hwaddr_type (int len)
+{
+ if (len == ETH_ALEN)
+ return ARPHRD_ETHER;
+ else if (len == INFINIBAND_ALEN)
+ return ARPHRD_INFINIBAND;
+ else
+ return -1;
+}
+
+#define HEXVAL(c) ((c) <= '9' ? (c) - '0' : ((c) & 0x4F) - 'A' + 10)
+
+/**
+ * nm_utils_hwaddr_aton:
+ * @asc: the ASCII representation of a hardware address
+ * @type: the type of address; either %ARPHRD_ETHER or %ARPHRD_INFINIBAND
+ * @buffer: buffer to store the result into
+ *
+ * Parses @asc and converts it to binary form in @buffer. See
+ * nm_utils_hwaddr_atoba() if you'd rather have the result in a
+ * #GByteArray.
+ *
+ * See also nm_utils_hwaddr_aton_len(), which takes an output length
+ * instead of a type.
+ *
+ * Return value: @buffer, or %NULL if @asc couldn't be parsed
+ */
+guint8 *
+nm_utils_hwaddr_aton (const char *asc, int type, gpointer buffer)
+{
+ int len = nm_utils_hwaddr_len (type);
+
+ if (len <= 0) {
+ g_return_val_if_reached (NULL);
+ return NULL;
+ }
+ return nm_utils_hwaddr_aton_len (asc, buffer, len);
+}
+
+/**
+ * nm_utils_hwaddr_atoba:
+ * @asc: the ASCII representation of a hardware address
+ * @type: the type of address; either %ARPHRD_ETHER or %ARPHRD_INFINIBAND
+ *
+ * Parses @asc and converts it to binary form in a #GByteArray. See
+ * nm_utils_hwaddr_aton() if you don't want a #GByteArray.
+ *
+ * Return value: (transfer full): a new #GByteArray, or %NULL if @asc couldn't
+ * be parsed
+ */
+GByteArray *
+nm_utils_hwaddr_atoba (const char *asc, int type)
+{
+ GByteArray *ba;
+ int len = nm_utils_hwaddr_len (type);
+
+ if (len <= 0) {
+ g_return_val_if_reached (NULL);
+ return NULL;
+ }
+
+ ba = g_byte_array_sized_new (len);
+ g_byte_array_set_size (ba, len);
+ if (!nm_utils_hwaddr_aton_len (asc, ba->data, len)) {
+ g_byte_array_unref (ba);
+ return NULL;
+ }
+
+ return ba;
+}
+
+/**
+ * nm_utils_hwaddr_ntoa:
+ * @addr: a binary hardware address
+ * @type: the type of address; either %ARPHRD_ETHER or %ARPHRD_INFINIBAND
+ *
+ * Converts @addr to textual form.
+ *
+ * See also nm_utils_hwaddr_ntoa_len(), which takes a length instead of
+ * a type.
+ *
+ * Return value: (transfer full): the textual form of @addr
+ */
+char *
+nm_utils_hwaddr_ntoa (gconstpointer addr, int type)
+{
+ int len = nm_utils_hwaddr_len (type);
+
+ if (len <= 0) {
+ g_return_val_if_reached (NULL);
+ return NULL;
+ }
+
+ return nm_utils_hwaddr_ntoa_len (addr, len);
+}
+
+/**
+ * nm_utils_hwaddr_aton_len:
+ * @asc: the ASCII representation of a hardware address
+ * @buffer: buffer to store the result into
+ * @length: the expected length in bytes of the result and
+ * the size of the buffer in bytes.
+ *
+ * Parses @asc and converts it to binary form in @buffer.
+ * Bytes in @asc can be sepatared by colons (:), or hyphens (-), but not mixed.
+ *
+ * Return value: @buffer, or %NULL if @asc couldn't be parsed
+ * or would be shorter or longer than @length.
+ *
+ * Since: 0.9.10
+ */
+guint8 *
+nm_utils_hwaddr_aton_len (const char *asc, gpointer buffer, gsize length)
+{
+ const char *in = asc;
+ guint8 *out = (guint8 *)buffer;
+ char delimiter = '\0';
+
+ if (!asc) {
+ g_return_val_if_reached (NULL);
+ return NULL;
+ }
+ g_return_val_if_fail (buffer, NULL);
+ g_return_val_if_fail (length, NULL);
+
+ while (length && *in) {
+ guint8 d1 = in[0], d2 = in[1];
+
+ if (!g_ascii_isxdigit (d1))
+ return NULL;
+
+ /* If there's no leading zero (ie "aa:b:cc") then fake it */
+ if (d2 && g_ascii_isxdigit (d2)) {
+ *out++ = (HEXVAL (d1) << 4) + HEXVAL (d2);
+ in += 2;
+ } else {
+ /* Fake leading zero */
+ *out++ = (HEXVAL ('0') << 4) + HEXVAL (d1);
+ in += 1;
+ }
+
+ length--;
+ if (*in) {
+ if (delimiter == '\0') {
+ if (*in == ':' || *in == '-')
+ delimiter = *in;
+ else
+ return NULL;
+ } else {
+ if (*in != delimiter)
+ return NULL;
+ }
+ in++;
+ }
+ }
+
+ if (length == 0 && !*in)
+ return buffer;
+ else
+ return NULL;
+}
+
+/**
+ * nm_utils_hwaddr_ntoa_len:
+ * @addr: a binary hardware address
+ * @length: the length of @addr
+ *
+ * Converts @addr to textual form.
+ *
+ * Return value: (transfer full): the textual form of @addr
+ *
+ * Since: 0.9.10
+ */
+char *
+nm_utils_hwaddr_ntoa_len (gconstpointer addr, gsize length)
+{
+ const guint8 *in = addr;
+ char *out, *result;
+ const char *LOOKUP = "0123456789ABCDEF";
+
+ g_return_val_if_fail (addr != NULL, g_strdup (""));
+ g_return_val_if_fail (length != 0, g_strdup (""));
+
+ result = out = g_malloc (length * 3);
+ for (;;) {
+ guint8 v = *in++;
+
+ *out++ = LOOKUP[v >> 4];
+ *out++ = LOOKUP[v & 0x0F];
+ if (--length == 0) {
+ *out = 0;
+ return result;
+ }
+ *out++ = ':';
+ }
+}
+
+/**
+ * nm_utils_hwaddr_valid:
+ * @asc: the ASCII representation of a hardware address
+ *
+ * Parses @asc to see if it is a valid hardware address of some type.
+ *
+ * Return value: %TRUE if @asc appears to be a valid hardware address
+ * of some type, %FALSE if not.
+ *
+ * Since: 0.9.10
+ */
+gboolean
+nm_utils_hwaddr_valid (const char *asc)
+{
+ guint8 buf[NM_UTILS_HWADDR_LEN_MAX];
+ gsize in_len, out_len;
+
+ if (!asc || !*asc)
+ return FALSE;
+ in_len = strlen (asc);
+ if ((in_len + 1) % 3 != 0)
+ return FALSE;
+ out_len = (in_len + 1) / 3;
+ if (out_len > NM_UTILS_HWADDR_LEN_MAX)
+ return FALSE;
+ return nm_utils_hwaddr_aton_len (asc, buf, out_len) != NULL;
+}
+
+/**
+ * nm_utils_bin2hexstr:
+ * @bytes: an array of bytes
+ * @len: the length of the @bytes array
+ * @final_len: an index where to cut off the returned string, or -1
+ *
+ * Converts a byte-array @bytes into a hexadecimal string.
+ * If @final_len is greater than -1, the returned string is terminated at
+ * that index (returned_string[final_len] == '\0'),
+ *
+ * Return value: (transfer full): the textual form of @bytes
+ *
+ * Since: 0.9.10
+ */
+/*
+ * Code originally by Alex Larsson <alexl@redhat.com> and
+ * copyright Red Hat, Inc. under terms of the LGPL.
+ */
+char *
+nm_utils_bin2hexstr (const char *bytes, int len, int final_len)
+{
+ static char hex_digits[] = "0123456789abcdef";
+ char *result;
+ int i;
+ gsize buflen = (len * 2) + 1;
+
+ g_return_val_if_fail (bytes != NULL, NULL);
+ g_return_val_if_fail (len > 0, NULL);
+ g_return_val_if_fail (len < 4096, NULL); /* Arbitrary limit */
+ if (final_len > -1)
+ g_return_val_if_fail (final_len < buflen, NULL);
+
+ result = g_malloc0 (buflen);
+ for (i = 0; i < len; i++) {
+ result[2*i] = hex_digits[(bytes[i] >> 4) & 0xf];
+ result[2*i+1] = hex_digits[bytes[i] & 0xf];
+ }
+ /* Cut converted key off at the correct length for this cipher type */
+ if (final_len > -1)
+ result[final_len] = '\0';
+ else
+ result[buflen - 1] = '\0';
+
+ return result;
+}
+
+/* From hostap, Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi> */
+/**
+ * nm_utils_hex2byte:
+ * @hex: a string representing a hex byte
+ *
+ * Converts a hex string (2 characters) into its byte representation.
+ *
+ * Return value: a byte, or -1 if @hex doesn't represent a hex byte
+ *
+ * Since: 0.9.10
+ */
+int
+nm_utils_hex2byte (const char *hex)
+{
+ int a, b;
+ a = g_ascii_xdigit_value (*hex++);
+ if (a < 0)
+ return -1;
+ b = g_ascii_xdigit_value (*hex++);
+ if (b < 0)
+ return -1;
+ return (a << 4) | b;
+}
+
+/**
+ * nm_utils_hexstr2bin:
+ * @hex: an hex string
+ * @len: the length of the @hex string (it has to be even)
+ *
+ * Converts a hexadecimal string @hex into a byte-array. The returned array
+ * length is @len/2.
+ *
+ * Return value: (transfer full): a array of bytes, or %NULL on error
+ *
+ * Since: 0.9.10
+ */
+char *
+nm_utils_hexstr2bin (const char *hex, size_t len)
+{
+ size_t i;
+ int a;
+ const char * ipos = hex;
+ char * buf = NULL;
+ char * opos;
+
+ /* Length must be a multiple of 2 */
+ if ((len % 2) != 0)
+ return NULL;
+
+ opos = buf = g_malloc0 ((len / 2) + 1);
+ for (i = 0; i < len; i += 2) {
+ a = nm_utils_hex2byte (ipos);
+ if (a < 0) {
+ g_free (buf);
+ return NULL;
+ }
+ *opos++ = a;
+ ipos += 2;
+ }
+ return buf;
+}
+/* End from hostap */
+
+/**
+ * nm_utils_iface_valid_name:
+ * @name: Name of interface
+ *
+ * This function is a 1:1 copy of the kernel's interface validation
+ * function in net/core/dev.c.
+ *
+ * Returns: %TRUE if interface name is valid, otherwise %FALSE is returned.
+ *
+ * Since: 0.9.8
+ */
+gboolean
+nm_utils_iface_valid_name (const char *name)
+{
+ g_return_val_if_fail (name != NULL, FALSE);
+
+ if (*name == '\0')
+ return FALSE;
+
+ if (strlen (name) >= 16)
+ return FALSE;
+
+ if (!strcmp (name, ".") || !strcmp (name, ".."))
+ return FALSE;
+
+ while (*name) {
+ if (*name == '/' || g_ascii_isspace (*name))
+ return FALSE;
+ name++;
+ }
+
+ return TRUE;
+}
+
+/**
+ * nm_utils_is_uuid:
+ * @str: a string that might be a UUID
+ *
+ * Checks if @str is a UUID
+ *
+ * Returns: %TRUE if @str is a UUID, %FALSE if not
+ *
+ * Since: 0.9.8
+ */
+gboolean
+nm_utils_is_uuid (const char *str)
+{
+ const char *p = str;
+ int num_dashes = 0;
+
+ while (*p) {
+ if (*p == '-')
+ num_dashes++;
+ else if (!g_ascii_isxdigit (*p))
+ return FALSE;
+ p++;
+ }
+
+ if ((num_dashes == 4) && (p - str == 36))
+ return TRUE;
+
+ /* Backwards compat for older configurations */
+ if ((num_dashes == 0) && (p - str == 40))
+ return TRUE;
+
+ return FALSE;
+}
+
+static char _nm_utils_inet_ntop_buffer[NM_UTILS_INET_ADDRSTRLEN];
+
+/**
+ * nm_utils_inet4_ntop: (skip)
+ * @inaddr: the address that should be converted to string.
+ * @dst: the destination buffer, it must contain at least %INET_ADDRSTRLEN
+ * or %NM_UTILS_INET_ADDRSTRLEN characters. If set to %NULL, it will return
+ * a pointer to an internal, static buffer (shared with nm_utils_inet6_ntop()).
+ * Beware, that the internal buffer will be overwritten with ever new call
+ * of nm_utils_inet4_ntop() or nm_utils_inet6_ntop() that does not provied it's
+ * own @dst buffer. Also, using the internal buffer is not thread safe. When
+ * in doubt, pass your own @dst buffer to avoid these issues.
+ *
+ * Wrapper for inet_ntop.
+ *
+ * Returns: the input buffer @dst, or a pointer to an
+ * internal, static buffer. This function cannot fail.
+ *
+ * Since: 0.9.10
+ **/
+const char *
+nm_utils_inet4_ntop (in_addr_t inaddr, char *dst)
+{
+ return inet_ntop (AF_INET, &inaddr, dst ? dst : _nm_utils_inet_ntop_buffer,
+ INET_ADDRSTRLEN);
+}
+
+/**
+ * nm_utils_inet6_ntop: (skip)
+ * @in6addr: the address that should be converted to string.
+ * @dst: the destination buffer, it must contain at least %INET6_ADDRSTRLEN
+ * or %NM_UTILS_INET_ADDRSTRLEN characters. If set to %NULL, it will return
+ * a pointer to an internal, static buffer (shared with nm_utils_inet4_ntop()).
+ * Beware, that the internal buffer will be overwritten with ever new call
+ * of nm_utils_inet4_ntop() or nm_utils_inet6_ntop() that does not provied it's
+ * own @dst buffer. Also, using the internal buffer is not thread safe. When
+ * in doubt, pass your own @dst buffer to avoid these issues.
+ *
+ * Wrapper for inet_ntop.
+ *
+ * Returns: the input buffer @dst, or a pointer to an
+ * internal, static buffer. %NULL is not allowed as @in6addr,
+ * otherwise, this function cannot fail.
+ *
+ * Since: 0.9.10
+ **/
+const char *
+nm_utils_inet6_ntop (const struct in6_addr *in6addr, char *dst)
+{
+ g_return_val_if_fail (in6addr, NULL);
+ return inet_ntop (AF_INET6, in6addr, dst ? dst : _nm_utils_inet_ntop_buffer,
+ INET6_ADDRSTRLEN);
+}
+
+/**
+ * nm_utils_check_virtual_device_compatibility:
+ * @virtual_type: a virtual connection type
+ * @other_type: a connection type to test against @virtual_type
+ *
+ * Determines if a connection of type @virtual_type can (in the
+ * general case) work with connections of type @other_type.
+ *
+ * If @virtual_type is %NM_TYPE_SETTING_VLAN, then this checks if
+ * @other_type is a valid type for the parent of a VLAN.
+ *
+ * If @virtual_type is a "master" type (eg, %NM_TYPE_SETTING_BRIDGE),
+ * then this checks if @other_type is a valid type for a slave of that
+ * master.
+ *
+ * Note that even if this returns %TRUE it is not guaranteed that
+ * <emphasis>every</emphasis> connection of type @other_type is
+ * compatible with @virtual_type; it may depend on the exact
+ * configuration of the two connections, or on the capabilities of an
+ * underlying device driver.
+ *
+ * Returns: %TRUE or %FALSE
+ *
+ * Since: 0.9.10
+ */
+gboolean
+nm_utils_check_virtual_device_compatibility (GType virtual_type, GType other_type)
+{
+ g_return_val_if_fail (_nm_setting_type_is_base_type (virtual_type), FALSE);
+ g_return_val_if_fail (_nm_setting_type_is_base_type (other_type), FALSE);
+
+ if (virtual_type == NM_TYPE_SETTING_BOND) {
+ return ( other_type == NM_TYPE_SETTING_INFINIBAND
+ || other_type == NM_TYPE_SETTING_WIRED
+ || other_type == NM_TYPE_SETTING_BRIDGE
+ || other_type == NM_TYPE_SETTING_BOND
+ || other_type == NM_TYPE_SETTING_TEAM
+ || other_type == NM_TYPE_SETTING_VLAN);
+ } else if (virtual_type == NM_TYPE_SETTING_BRIDGE) {
+ return ( other_type == NM_TYPE_SETTING_WIRED
+ || other_type == NM_TYPE_SETTING_BOND
+ || other_type == NM_TYPE_SETTING_TEAM
+ || other_type == NM_TYPE_SETTING_VLAN);
+ } else if (virtual_type == NM_TYPE_SETTING_TEAM) {
+ return ( other_type == NM_TYPE_SETTING_WIRED
+ || other_type == NM_TYPE_SETTING_BRIDGE
+ || other_type == NM_TYPE_SETTING_BOND
+ || other_type == NM_TYPE_SETTING_TEAM
+ || other_type == NM_TYPE_SETTING_VLAN);
+ } else if (virtual_type == NM_TYPE_SETTING_VLAN) {
+ return ( other_type == NM_TYPE_SETTING_WIRED
+ || other_type == NM_TYPE_SETTING_WIRELESS
+ || other_type == NM_TYPE_SETTING_BRIDGE
+ || other_type == NM_TYPE_SETTING_BOND
+ || other_type == NM_TYPE_SETTING_TEAM
+ || other_type == NM_TYPE_SETTING_VLAN);
+ } else {
+ g_warn_if_reached ();
+ return FALSE;
+ }
+}
+
+/***********************************************************/
+
+/* Unused prototype to make the compiler happy */
+const NMUtilsPrivateData *nm_util_get_private (void);
+
+static const NMUtilsPrivateData data = {
+ .nm_setting_ip4_config_get_address_label = nm_setting_ip4_config_get_address_label,
+ .nm_setting_ip4_config_add_address_with_label = nm_setting_ip4_config_add_address_with_label,
+};
+
+/**
+ * nm_utils_get_private:
+ *
+ * Entry point for NetworkManager-internal API. You should not use this
+ * function for any reason.
+ *
+ * Returns: Who knows? It's a mystery.
+ *
+ * Since: 0.9.10
+ */
+const NMUtilsPrivateData *
+nm_utils_get_private (void)
+{
+ return &data;
+}
+
+/**
+ * nm_util_get_private:
+ *
+ * You should not use this function for any reason.
+ *
+ * Returns: Who knows? It's a mystery.
+ *
+ * Since: 0.9.10
+ */
+const NMUtilsPrivateData *
+nm_util_get_private (void)
+{
+ /* Compat function to preserve ABI */
+ return nm_utils_get_private ();
+}
diff --git a/libnm-core/nm-utils.h b/libnm-core/nm-utils.h
new file mode 100644
index 0000000000..896b3e92a8
--- /dev/null
+++ b/libnm-core/nm-utils.h
@@ -0,0 +1,180 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2005 - 2013 Red Hat, Inc.
+ */
+
+#ifndef NM_UTILS_H
+#define NM_UTILS_H
+
+#include <glib.h>
+
+#include "nm-connection.h"
+#include "nm-utils-enum-types.h"
+
+G_BEGIN_DECLS
+
+/* init, deinit nm_utils */
+gboolean nm_utils_init (GError **error);
+void nm_utils_deinit (void);
+
+/* SSID helpers */
+gboolean nm_utils_is_empty_ssid (const guint8 *ssid, int len);
+const char *nm_utils_escape_ssid (const guint8 *ssid, guint32 len);
+gboolean nm_utils_same_ssid (const GByteArray *ssid1,
+ const GByteArray *ssid2,
+ gboolean ignore_trailing_null);
+char * nm_utils_ssid_to_utf8 (const GByteArray *ssid);
+
+GHashTable *nm_utils_gvalue_hash_dup (GHashTable *hash);
+
+NM_DEPRECATED_IN_0_9_10
+void nm_utils_slist_free (GSList *list, GDestroyNotify elem_destroy_fn);
+
+/**
+ * NMUtilsSecurityType:
+ * @NMU_SEC_INVALID: unknown or invalid security, placeholder and not used
+ * @NMU_SEC_NONE: unencrypted and open
+ * @NMU_SEC_STATIC_WEP: static WEP keys are used for encryption
+ * @NMU_SEC_LEAP: Cisco LEAP is used for authentication and for generating the
+ * dynamic WEP keys automatically
+ * @NMU_SEC_DYNAMIC_WEP: standard 802.1x is used for authentication and
+ * generating the dynamic WEP keys automatically
+ * @NMU_SEC_WPA_PSK: WPA1 is used with Pre-Shared Keys (PSK)
+ * @NMU_SEC_WPA_ENTERPRISE: WPA1 is used with 802.1x authentication
+ * @NMU_SEC_WPA2_PSK: WPA2/RSN is used with Pre-Shared Keys (PSK)
+ * @NMU_SEC_WPA2_ENTERPRISE: WPA2 is used with 802.1x authentication
+ *
+ * Describes generic security mechanisms that 802.11 access points may offer.
+ * Used with nm_utils_security_valid() for checking whether a given access
+ * point is compatible with a network device.
+ **/
+typedef enum {
+ NMU_SEC_INVALID = 0,
+ NMU_SEC_NONE,
+ NMU_SEC_STATIC_WEP,
+ NMU_SEC_LEAP,
+ NMU_SEC_DYNAMIC_WEP,
+ NMU_SEC_WPA_PSK,
+ NMU_SEC_WPA_ENTERPRISE,
+ NMU_SEC_WPA2_PSK,
+ NMU_SEC_WPA2_ENTERPRISE
+} NMUtilsSecurityType;
+
+gboolean nm_utils_security_valid (NMUtilsSecurityType type,
+ NMDeviceWifiCapabilities wifi_caps,
+ gboolean have_ap,
+ gboolean adhoc,
+ NM80211ApFlags ap_flags,
+ NM80211ApSecurityFlags ap_wpa,
+ NM80211ApSecurityFlags ap_rsn);
+
+gboolean nm_utils_ap_mode_security_valid (NMUtilsSecurityType type,
+ NMDeviceWifiCapabilities wifi_caps);
+
+gboolean nm_utils_wep_key_valid (const char *key, NMWepKeyType wep_type);
+gboolean nm_utils_wpa_psk_valid (const char *psk);
+
+GSList *nm_utils_ip4_addresses_from_gvalue (const GValue *value);
+void nm_utils_ip4_addresses_to_gvalue (GSList *list, GValue *value);
+
+GSList *nm_utils_ip4_routes_from_gvalue (const GValue *value);
+void nm_utils_ip4_routes_to_gvalue (GSList *list, GValue *value);
+
+guint32 nm_utils_ip4_netmask_to_prefix (guint32 netmask);
+guint32 nm_utils_ip4_prefix_to_netmask (guint32 prefix);
+guint32 nm_utils_ip4_get_default_prefix (guint32 ip);
+
+GSList *nm_utils_ip6_addresses_from_gvalue (const GValue *value);
+void nm_utils_ip6_addresses_to_gvalue (GSList *list, GValue *value);
+
+GSList *nm_utils_ip6_routes_from_gvalue (const GValue *value);
+void nm_utils_ip6_routes_to_gvalue (GSList *list, GValue *value);
+
+GSList *nm_utils_ip6_dns_from_gvalue (const GValue *value);
+void nm_utils_ip6_dns_to_gvalue (GSList *list, GValue *value);
+
+char *nm_utils_uuid_generate (void);
+char *nm_utils_uuid_generate_from_string (const char *s);
+
+GByteArray *nm_utils_rsa_key_encrypt (const GByteArray *data,
+ const char *in_password,
+ char **out_password,
+ GError **error);
+GByteArray *nm_utils_rsa_key_encrypt_aes (const GByteArray *data,
+ const char *in_password,
+ char **out_password,
+ GError **error);
+gboolean nm_utils_file_is_pkcs12 (const char *filename);
+
+guint32 nm_utils_wifi_freq_to_channel (guint32 freq);
+guint32 nm_utils_wifi_channel_to_freq (guint32 channel, const char *band);
+guint32 nm_utils_wifi_find_next_channel (guint32 channel, int direction, char *band);
+gboolean nm_utils_wifi_is_channel_valid (guint32 channel, const char *band);
+
+/**
+ * NM_UTILS_HWADDR_LEN_MAX:
+ *
+ * The maximum length of hardware addresses handled by NetworkManager itself,
+ * nm_utils_hwaddr_len(), and nm_utils_hwaddr_aton().
+ */
+#define NM_UTILS_HWADDR_LEN_MAX 20 /* INFINIBAND_ALEN */
+
+int nm_utils_hwaddr_len (int type) G_GNUC_PURE;
+NM_DEPRECATED_IN_0_9_10
+int nm_utils_hwaddr_type (int len) G_GNUC_PURE;
+char *nm_utils_hwaddr_ntoa (gconstpointer addr, int type);
+GByteArray *nm_utils_hwaddr_atoba (const char *asc, int type);
+guint8 *nm_utils_hwaddr_aton (const char *asc, int type, gpointer buffer);
+
+NM_AVAILABLE_IN_0_9_10
+char *nm_utils_hwaddr_ntoa_len (gconstpointer addr, gsize length);
+NM_AVAILABLE_IN_0_9_10
+guint8 *nm_utils_hwaddr_aton_len (const char *asc, gpointer buffer, gsize length);
+
+NM_AVAILABLE_IN_0_9_10
+gboolean nm_utils_hwaddr_valid (const char *asc);
+
+NM_AVAILABLE_IN_0_9_10
+char *nm_utils_bin2hexstr (const char *bytes, int len, int final_len);
+NM_AVAILABLE_IN_0_9_10
+int nm_utils_hex2byte (const char *hex);
+NM_AVAILABLE_IN_0_9_10
+char *nm_utils_hexstr2bin (const char *hex, size_t len);
+
+gboolean nm_utils_iface_valid_name(const char *name);
+
+gboolean nm_utils_is_uuid (const char *str);
+
+/**
+ * NM_UTILS_INET_ADDRSTRLEN:
+ *
+ * Defines the minimal length for a char buffer that is suitable as @dst argument
+ * for both nm_utils_inet4_ntop() and nm_utils_inet6_ntop().
+ **/
+#define NM_UTILS_INET_ADDRSTRLEN INET6_ADDRSTRLEN
+NM_AVAILABLE_IN_0_9_10
+const char *nm_utils_inet4_ntop (in_addr_t inaddr, char *dst);
+NM_AVAILABLE_IN_0_9_10
+const char *nm_utils_inet6_ntop (const struct in6_addr *in6addr, char *dst);
+
+NM_AVAILABLE_IN_0_9_10
+gboolean nm_utils_check_virtual_device_compatibility (GType virtual_type, GType other_type);
+
+G_END_DECLS
+
+#endif /* NM_UTILS_H */
diff --git a/libnm-core/nm-value-transforms.c b/libnm-core/nm-value-transforms.c
new file mode 100644
index 0000000000..00e7e77579
--- /dev/null
+++ b/libnm-core/nm-value-transforms.c
@@ -0,0 +1,593 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2005 - 2014 Red Hat, Inc.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "nm-utils.h"
+#include "nm-utils-private.h"
+#include "nm-dbus-glib-types.h"
+#include "nm-glib-compat.h"
+
+static void
+_nm_utils_convert_op_to_string (const GValue *src_value, GValue *dest_value)
+{
+ g_return_if_fail (g_type_is_a (G_VALUE_TYPE (src_value), DBUS_TYPE_G_OBJECT_PATH));
+
+ g_value_set_string (dest_value, (const char *) g_value_get_boxed (src_value));
+}
+
+static void
+_nm_utils_convert_strv_to_slist (const GValue *src_value, GValue *dest_value)
+{
+ char **str;
+ GSList *list = NULL;
+ guint i = 0;
+
+ g_return_if_fail (g_type_is_a (G_VALUE_TYPE (src_value), G_TYPE_STRV));
+
+ str = (char **) g_value_get_boxed (src_value);
+
+ while (str && str[i])
+ list = g_slist_prepend (list, g_strdup (str[i++]));
+
+ g_value_take_boxed (dest_value, g_slist_reverse (list));
+}
+
+static void
+_nm_utils_convert_slist_to_strv (const GValue *src_value, GValue *dest_value)
+{
+ GSList *slist;
+ char **strv;
+ int len, i = 0;
+
+ slist = g_value_get_boxed (src_value);
+ len = g_slist_length (slist);
+
+ strv = g_new (char *, len + 1);
+ for (i = 0; slist; slist = slist->next, i++)
+ strv[i] = g_strdup (slist->data);
+ strv[i] = NULL;
+
+ g_value_take_boxed (dest_value, strv);
+}
+
+static void
+_nm_utils_convert_strv_to_ptrarray (const GValue *src_value, GValue *dest_value)
+{
+ char **str;
+ GPtrArray *array = NULL;
+ guint i = 0;
+
+ g_return_if_fail (g_type_is_a (G_VALUE_TYPE (src_value), G_TYPE_STRV));
+
+ str = (char **) g_value_get_boxed (src_value);
+
+ array = g_ptr_array_sized_new (3);
+ while (str && str[i])
+ g_ptr_array_add (array, g_strdup (str[i++]));
+
+ g_value_take_boxed (dest_value, array);
+}
+
+static void
+_nm_utils_convert_string_list_to_string (const GValue *src_value, GValue *dest_value)
+{
+ GSList *strings;
+ GString *printable;
+ GSList *iter;
+
+ g_return_if_fail (g_type_is_a (G_VALUE_TYPE (src_value), DBUS_TYPE_G_LIST_OF_STRING));
+
+ strings = (GSList *) g_value_get_boxed (src_value);
+
+ printable = g_string_new (NULL);
+ for (iter = strings; iter; iter = iter->next) {
+ if (iter != strings)
+ g_string_append_c (printable, ',');
+ g_string_append (printable, iter->data ? iter->data : "(null)");
+ }
+
+ g_value_take_string (dest_value, g_string_free (printable, FALSE));
+}
+
+static void
+_string_array_to_string (const GPtrArray *strings, GValue *dest_value)
+{
+ GString *printable;
+ guint i;
+
+ printable = g_string_new (NULL);
+ for (i = 0; strings && i < strings->len; i++) {
+ if (i > 0)
+ g_string_append_c (printable, ',');
+ g_string_append (printable, strings->pdata[i]);
+ }
+
+ g_value_take_string (dest_value, g_string_free (printable, FALSE));
+}
+
+static void
+_nm_utils_convert_string_array_to_string (const GValue *src_value, GValue *dest_value)
+{
+ const GPtrArray *strings;
+
+ g_return_if_fail (g_type_is_a (G_VALUE_TYPE (src_value), DBUS_TYPE_G_ARRAY_OF_STRING));
+
+ strings = (const GPtrArray *) g_value_get_boxed (src_value);
+ _string_array_to_string (strings, dest_value);
+}
+
+static void
+_nm_utils_convert_op_array_to_string (const GValue *src_value, GValue *dest_value)
+{
+ const GPtrArray *strings;
+
+ g_return_if_fail (g_type_is_a (G_VALUE_TYPE (src_value), DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH));
+
+ strings = (const GPtrArray *) g_value_get_boxed (src_value);
+ _string_array_to_string (strings, dest_value);
+}
+
+static void
+_nm_utils_convert_uint_array_to_string (const GValue *src_value, GValue *dest_value)
+{
+ GArray *array;
+ GString *printable;
+ guint i = 0;
+
+ g_return_if_fail (g_type_is_a (G_VALUE_TYPE (src_value), DBUS_TYPE_G_UINT_ARRAY));
+
+ array = (GArray *) g_value_get_boxed (src_value);
+
+ printable = g_string_new (NULL);
+ while (array && (i < array->len)) {
+ guint32 addr;
+
+ if (i > 0)
+ g_string_append (printable, ", ");
+
+ addr = g_array_index (array, guint32, i++);
+ g_string_append (printable, nm_utils_inet4_ntop (addr, NULL));
+ }
+
+ g_value_take_string (dest_value, g_string_free (printable, FALSE));
+}
+
+static void
+_nm_utils_convert_ip4_addr_route_struct_array_to_string (const GValue *src_value, GValue *dest_value)
+{
+ GPtrArray *ptr_array;
+ GString *printable;
+ guint i = 0;
+ char buf[INET_ADDRSTRLEN];
+
+ g_return_if_fail (g_type_is_a (G_VALUE_TYPE (src_value), DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UINT));
+
+ ptr_array = (GPtrArray *) g_value_get_boxed (src_value);
+
+ printable = g_string_new (NULL);
+ while (ptr_array && (i < ptr_array->len)) {
+ GArray *array;
+ gboolean is_addr; /* array contains address x route */
+
+ if (i > 0)
+ g_string_append (printable, "; ");
+
+ g_string_append (printable, "{ ");
+ array = (GArray *) g_ptr_array_index (ptr_array, i++);
+ if (array->len < 2) {
+ g_string_append (printable, "invalid");
+ continue;
+ }
+ is_addr = (array->len < 4);
+
+ nm_utils_inet4_ntop (g_array_index (array, guint32, 0), buf);
+ if (is_addr)
+ g_string_append_printf (printable, "ip = %s", buf);
+ else
+ g_string_append_printf (printable, "dst = %s", buf);
+
+ g_string_append_printf (printable, "/%u",
+ g_array_index (array, guint32, 1));
+
+ if (array->len > 2) {
+ nm_utils_inet4_ntop (g_array_index (array, guint32, 2), buf);
+ if (is_addr)
+ g_string_append_printf (printable, ", gw = %s", buf);
+ else
+ g_string_append_printf (printable, ", nh = %s", buf);
+ }
+
+ if (array->len > 3) {
+ g_string_append_printf (printable, ", mt = %u",
+ g_array_index (array, guint32, 3));
+ }
+
+ g_string_append (printable, " }");
+ }
+
+ g_value_take_string (dest_value, g_string_free (printable, FALSE));
+}
+
+static void
+convert_one_gvalue_hash_entry (gpointer key, gpointer value, gpointer user_data)
+{
+ GString *printable = (GString *) user_data;
+ char *value_as_string;
+
+ value_as_string = g_strdup_value_contents ((GValue *) value);
+ g_string_append_printf (printable, " { '%s': %s },", (const char *) key, value_as_string);
+ g_free (value_as_string);
+}
+
+static void
+_nm_utils_convert_gvalue_hash_to_string (const GValue *src_value, GValue *dest_value)
+{
+ GHashTable *hash;
+ GString *printable;
+
+ g_return_if_fail (g_type_is_a (G_VALUE_TYPE (src_value), DBUS_TYPE_G_MAP_OF_VARIANT));
+
+ hash = (GHashTable *) g_value_get_boxed (src_value);
+
+ printable = g_string_new ("[");
+ g_hash_table_foreach (hash, convert_one_gvalue_hash_entry, printable);
+ g_string_append (printable, " ]");
+
+ g_value_take_string (dest_value, printable->str);
+ g_string_free (printable, FALSE);
+}
+
+static void
+convert_one_string_hash_entry (gpointer key, gpointer value, gpointer user_data)
+{
+ GString *printable = (GString *) user_data;
+
+ if (printable->len)
+ g_string_append_c (printable, ',');
+ g_string_append_printf (printable, "%s=%s", (const char *) key, (const char *) value);
+}
+
+static void
+_nm_utils_convert_string_hash_to_string (const GValue *src_value, GValue *dest_value)
+{
+ GHashTable *hash;
+ GString *printable;
+
+ g_return_if_fail (g_type_is_a (G_VALUE_TYPE (src_value), DBUS_TYPE_G_MAP_OF_STRING));
+
+ hash = (GHashTable *) g_value_get_boxed (src_value);
+
+ printable = g_string_new (NULL);
+ if (hash)
+ g_hash_table_foreach (hash, convert_one_string_hash_entry, printable);
+
+ g_value_take_string (dest_value, g_string_free (printable, FALSE));
+}
+
+static void
+_nm_utils_convert_byte_array_to_string (const GValue *src_value, GValue *dest_value)
+{
+ GArray *array;
+ GString *printable;
+ guint i = 0;
+
+ g_return_if_fail (g_type_is_a (G_VALUE_TYPE (src_value), DBUS_TYPE_G_UCHAR_ARRAY));
+
+ array = (GArray *) g_value_get_boxed (src_value);
+
+ printable = g_string_new ("[");
+ if (array) {
+ while (i < MIN (array->len, 35)) {
+ if (i > 0)
+ g_string_append_c (printable, ' ');
+ g_string_append_printf (printable, "0x%02X",
+ g_array_index (array, unsigned char, i++));
+ }
+ if (i < array->len)
+ g_string_append (printable, " ... ");
+ }
+ g_string_append_c (printable, ']');
+
+ g_value_take_string (dest_value, g_string_free (printable, FALSE));
+}
+
+static void
+_nm_utils_convert_ip6_dns_array_to_string (const GValue *src_value, GValue *dest_value)
+{
+ GPtrArray *ptr_array;
+ GString *printable;
+ guint i = 0;
+
+ g_return_if_fail (g_type_is_a (G_VALUE_TYPE (src_value), DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UCHAR));
+
+ ptr_array = (GPtrArray *) g_value_get_boxed (src_value);
+
+ printable = g_string_new (NULL);
+ while (ptr_array && (i < ptr_array->len)) {
+ GByteArray *bytearray;
+ struct in6_addr *addr;
+
+ if (i > 0)
+ g_string_append (printable, ", ");
+
+ bytearray = (GByteArray *) g_ptr_array_index (ptr_array, i++);
+ if (bytearray->len != 16) {
+ g_string_append (printable, "invalid");
+ continue;
+ }
+ addr = (struct in6_addr *) bytearray->data;
+ g_string_append (printable, nm_utils_inet6_ntop (addr, NULL));
+ }
+
+ g_value_take_string (dest_value, g_string_free (printable, FALSE));
+}
+
+static void
+_nm_utils_convert_ip6_addr_struct_array_to_string (const GValue *src_value, GValue *dest_value)
+{
+ GPtrArray *ptr_array;
+ GString *printable;
+ guint i = 0;
+
+ g_return_if_fail (g_type_is_a (G_VALUE_TYPE (src_value), DBUS_TYPE_G_ARRAY_OF_IP6_ADDRESS));
+
+ ptr_array = (GPtrArray *) g_value_get_boxed (src_value);
+
+ printable = g_string_new (NULL);
+ while (ptr_array && (i < ptr_array->len)) {
+ GValueArray *elements;
+ GValue *tmp;
+ GByteArray *ba_addr;
+ struct in6_addr *addr;
+ guint32 prefix;
+
+ if (i > 0)
+ g_string_append (printable, "; ");
+
+ g_string_append (printable, "{ ");
+ elements = (GValueArray *) g_ptr_array_index (ptr_array, i++);
+ if (!_nm_utils_gvalue_array_validate (elements, 3,
+ DBUS_TYPE_G_UCHAR_ARRAY,
+ G_TYPE_UINT,
+ DBUS_TYPE_G_UCHAR_ARRAY)) {
+ g_string_append (printable, "invalid }");
+ continue;
+ }
+
+ /* IPv6 address */
+ tmp = g_value_array_get_nth (elements, 0);
+ ba_addr = g_value_get_boxed (tmp);
+ if (ba_addr->len != 16) {
+ g_string_append (printable, "invalid }");
+ continue;
+ }
+ addr = (struct in6_addr *) ba_addr->data;
+ g_string_append_printf (printable, "ip = %s", nm_utils_inet6_ntop (addr, NULL));
+
+ /* Prefix */
+ tmp = g_value_array_get_nth (elements, 1);
+ prefix = g_value_get_uint (tmp);
+ if (prefix > 128) {
+ g_string_append (printable, "/invalid }");
+ continue;
+ }
+ g_string_append_printf (printable, "/%u", prefix);
+ g_string_append (printable, ", ");
+
+ /* IPv6 Gateway */
+ tmp = g_value_array_get_nth (elements, 2);
+ ba_addr = g_value_get_boxed (tmp);
+ if (ba_addr->len != 16) {
+ g_string_append (printable, "invalid }");
+ continue;
+ }
+ addr = (struct in6_addr *) ba_addr->data;
+ g_string_append_printf (printable, "gw = %s", nm_utils_inet6_ntop (addr, NULL));
+ g_string_append (printable, " }");
+ }
+
+ g_value_take_string (dest_value, g_string_free (printable, FALSE));
+}
+
+static void
+_nm_utils_convert_ip6_route_struct_array_to_string (const GValue *src_value, GValue *dest_value)
+{
+ GPtrArray *ptr_array;
+ GString *printable;
+ guint i = 0;
+
+ g_return_if_fail (g_type_is_a (G_VALUE_TYPE (src_value), DBUS_TYPE_G_ARRAY_OF_IP6_ROUTE));
+
+ ptr_array = (GPtrArray *) g_value_get_boxed (src_value);
+
+ printable = g_string_new (NULL);
+ while (ptr_array && (i < ptr_array->len)) {
+ GValueArray *elements;
+ GValue *tmp;
+ GByteArray *ba_addr;
+ struct in6_addr *addr;
+ guint32 prefix, metric;
+
+ if (i > 0)
+ g_string_append (printable, "; ");
+
+ g_string_append (printable, "{ ");
+ elements = (GValueArray *) g_ptr_array_index (ptr_array, i++);
+ if (!_nm_utils_gvalue_array_validate (elements, 4,
+ DBUS_TYPE_G_UCHAR_ARRAY,
+ G_TYPE_UINT,
+ DBUS_TYPE_G_UCHAR_ARRAY,
+ G_TYPE_UINT)) {
+ g_string_append (printable, "invalid");
+ continue;
+ }
+
+ /* Destination address */
+ tmp = g_value_array_get_nth (elements, 0);
+ ba_addr = g_value_get_boxed (tmp);
+ if (ba_addr->len != 16) {
+ g_string_append (printable, "invalid");
+ continue;
+ }
+ addr = (struct in6_addr *) ba_addr->data;
+ g_string_append_printf (printable, "dst = %s", nm_utils_inet6_ntop (addr, NULL));
+
+ /* Prefix */
+ tmp = g_value_array_get_nth (elements, 1);
+ prefix = g_value_get_uint (tmp);
+ if (prefix > 128) {
+ g_string_append (printable, "/invalid");
+ continue;
+ }
+ g_string_append_printf (printable, "/%u", prefix);
+ g_string_append (printable, ", ");
+
+ /* Next hop addresses */
+ tmp = g_value_array_get_nth (elements, 2);
+ ba_addr = g_value_get_boxed (tmp);
+ if (ba_addr->len != 16) {
+ g_string_append (printable, "invalid");
+ continue;
+ }
+ addr = (struct in6_addr *) ba_addr->data;
+ g_string_append_printf (printable, "nh = %s", nm_utils_inet6_ntop (addr, NULL));
+ g_string_append (printable, ", ");
+
+ /* Metric */
+ tmp = g_value_array_get_nth (elements, 3);
+ metric = g_value_get_uint (tmp);
+ g_string_append_printf (printable, "mt = %u", metric);
+
+ g_string_append (printable, " }");
+ }
+
+ g_value_take_string (dest_value, g_string_free (printable, FALSE));
+}
+
+#define OLD_DBUS_TYPE_G_IP6_ADDRESS (dbus_g_type_get_struct ("GValueArray", DBUS_TYPE_G_UCHAR_ARRAY, G_TYPE_UINT, G_TYPE_INVALID))
+#define OLD_DBUS_TYPE_G_ARRAY_OF_IP6_ADDRESS (dbus_g_type_get_collection ("GPtrArray", OLD_DBUS_TYPE_G_IP6_ADDRESS))
+
+static void
+_nm_utils_convert_old_ip6_addr_array (const GValue *src_value, GValue *dst_value)
+{
+ GPtrArray *src_outer_array;
+ GPtrArray *dst_outer_array;
+ guint i;
+
+ g_return_if_fail (g_type_is_a (G_VALUE_TYPE (src_value), OLD_DBUS_TYPE_G_ARRAY_OF_IP6_ADDRESS));
+
+ src_outer_array = (GPtrArray *) g_value_get_boxed (src_value);
+ dst_outer_array = g_ptr_array_new ();
+
+ for (i = 0; src_outer_array && (i < src_outer_array->len); i++) {
+ GValueArray *src_addr_array;
+ GValueArray *dst_addr_array;
+ GValue element = G_VALUE_INIT;
+ GValue *src_addr, *src_prefix;
+ GByteArray *ba;
+
+ src_addr_array = (GValueArray *) g_ptr_array_index (src_outer_array, i);
+ if (!_nm_utils_gvalue_array_validate (src_addr_array, 2, DBUS_TYPE_G_UCHAR_ARRAY, G_TYPE_UINT)) {
+ g_warning ("%s: invalid old IPv6 address type", __func__);
+ return;
+ }
+
+ dst_addr_array = g_value_array_new (3);
+
+ src_addr = g_value_array_get_nth (src_addr_array, 0);
+ g_value_array_append (dst_addr_array, src_addr);
+ src_prefix = g_value_array_get_nth (src_addr_array, 1);
+ g_value_array_append (dst_addr_array, src_prefix);
+
+ /* Blank Gateway */
+ g_value_init (&element, DBUS_TYPE_G_UCHAR_ARRAY);
+ ba = g_byte_array_new ();
+ g_byte_array_append (ba, (guint8 *) "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16);
+ g_value_take_boxed (&element, ba);
+ g_value_array_append (dst_addr_array, &element);
+ g_value_unset (&element);
+
+ g_ptr_array_add (dst_outer_array, dst_addr_array);
+ }
+
+ g_value_take_boxed (dst_value, dst_outer_array);
+}
+
+void
+_nm_value_transforms_register (void)
+{
+ static gboolean registered = FALSE;
+
+ if (G_UNLIKELY (!registered)) {
+ g_value_register_transform_func (DBUS_TYPE_G_OBJECT_PATH,
+ G_TYPE_STRING,
+ _nm_utils_convert_op_to_string);
+ g_value_register_transform_func (G_TYPE_STRV,
+ DBUS_TYPE_G_LIST_OF_STRING,
+ _nm_utils_convert_strv_to_slist);
+ g_value_register_transform_func (DBUS_TYPE_G_LIST_OF_STRING,
+ G_TYPE_STRV,
+ _nm_utils_convert_slist_to_strv);
+ g_value_register_transform_func (G_TYPE_STRV,
+ DBUS_TYPE_G_ARRAY_OF_STRING,
+ _nm_utils_convert_strv_to_ptrarray);
+ g_value_register_transform_func (DBUS_TYPE_G_LIST_OF_STRING,
+ G_TYPE_STRING,
+ _nm_utils_convert_string_list_to_string);
+ g_value_register_transform_func (DBUS_TYPE_G_ARRAY_OF_STRING,
+ G_TYPE_STRING,
+ _nm_utils_convert_string_array_to_string);
+ g_value_register_transform_func (DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH,
+ G_TYPE_STRING,
+ _nm_utils_convert_op_array_to_string);
+ g_value_register_transform_func (DBUS_TYPE_G_UINT_ARRAY,
+ G_TYPE_STRING,
+ _nm_utils_convert_uint_array_to_string);
+ g_value_register_transform_func (DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UINT,
+ G_TYPE_STRING,
+ _nm_utils_convert_ip4_addr_route_struct_array_to_string);
+ g_value_register_transform_func (DBUS_TYPE_G_MAP_OF_VARIANT,
+ G_TYPE_STRING,
+ _nm_utils_convert_gvalue_hash_to_string);
+ g_value_register_transform_func (DBUS_TYPE_G_MAP_OF_STRING,
+ G_TYPE_STRING,
+ _nm_utils_convert_string_hash_to_string);
+ g_value_register_transform_func (DBUS_TYPE_G_UCHAR_ARRAY,
+ G_TYPE_STRING,
+ _nm_utils_convert_byte_array_to_string);
+ g_value_register_transform_func (DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UCHAR,
+ G_TYPE_STRING,
+ _nm_utils_convert_ip6_dns_array_to_string);
+ g_value_register_transform_func (DBUS_TYPE_G_ARRAY_OF_IP6_ADDRESS,
+ G_TYPE_STRING,
+ _nm_utils_convert_ip6_addr_struct_array_to_string);
+ g_value_register_transform_func (DBUS_TYPE_G_ARRAY_OF_IP6_ROUTE,
+ G_TYPE_STRING,
+ _nm_utils_convert_ip6_route_struct_array_to_string);
+ g_value_register_transform_func (OLD_DBUS_TYPE_G_ARRAY_OF_IP6_ADDRESS,
+ DBUS_TYPE_G_ARRAY_OF_IP6_ADDRESS,
+ _nm_utils_convert_old_ip6_addr_array);
+ registered = TRUE;
+ }
+}
diff --git a/libnm-core/nm-version.h.in b/libnm-core/nm-version.h.in
new file mode 100644
index 0000000000..0a33ac295e
--- /dev/null
+++ b/libnm-core/nm-version.h.in
@@ -0,0 +1,123 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2011 Red Hat, Inc.
+ */
+
+#ifndef NM_VERSION_H
+#define NM_VERSION_H
+
+/**
+ * NM_MAJOR_VERSION:
+ *
+ * Evaluates to the major version number of NetworkManager which this source
+ * is compiled against.
+ */
+#define NM_MAJOR_VERSION (@NM_MAJOR_VERSION@)
+
+/**
+ * NM_MINOR_VERSION:
+ *
+ * Evaluates to the minor version number of NetworkManager which this source
+ * is compiled against.
+ */
+#define NM_MINOR_VERSION (@NM_MINOR_VERSION@)
+
+/**
+ * NM_MICRO_VERSION:
+ *
+ * Evaluates to the micro version number of NetworkManager which this source
+ * compiled against.
+ */
+#define NM_MICRO_VERSION (@NM_MICRO_VERSION@)
+
+/**
+ * NM_CHECK_VERSION:
+ * @major: major version (e.g. 1 for version 1.2.5)
+ * @minor: minor version (e.g. 2 for version 1.2.5)
+ * @micro: micro version (e.g. 5 for version 1.2.5)
+ *
+ * Returns: %TRUE if the version of the NetworkManager header files
+ * is the same as or newer than the passed-in version.
+ */
+#define NM_CHECK_VERSION(major,minor,micro) \
+ (NM_MAJOR_VERSION > (major) || \
+ (NM_MAJOR_VERSION == (major) && NM_MINOR_VERSION > (minor)) || \
+ (NM_MAJOR_VERSION == (major) && NM_MINOR_VERSION == (minor) && NM_MICRO_VERSION >= (micro)))
+
+
+/* Deprecation / Availability macros */
+
+#define NM_ENCODE_VERSION(major,minor,micro) ((major) << 16 | (minor) << 8 | (micro))
+
+#define NM_VERSION_0_9_8 (NM_ENCODE_VERSION (0, 9, 8))
+#define NM_VERSION_0_9_10 (NM_ENCODE_VERSION (0, 9, 10))
+#define NM_VERSION_1_0 (NM_ENCODE_VERSION (1, 0, 0))
+
+#define NM_VERSION_CUR_STABLE NM_VERSION_0_9_10
+#define NM_VERSION_NEXT_STABLE NM_VERSION_1_0
+
+#if !defined (NM_VERSION_MIN_REQUIRED) || (NM_VERSION_MIN_REQUIRED == 0)
+# undef NM_VERSION_MIN_REQUIRED
+# define NM_VERSION_MIN_REQUIRED (NM_VERSION_CUR_STABLE)
+#endif
+
+#if !defined (NM_VERSION_MAX_ALLOWED) || (NM_VERSION_MAX_ALLOWED == 0)
+# undef NM_VERSION_MAX_ALLOWED
+# define NM_VERSION_MAX_ALLOWED (NM_VERSION_CUR_STABLE)
+#endif
+
+/* sanity checks */
+#if NM_VERSION_MIN_REQUIRED > NM_VERSION_NEXT_STABLE
+#error "NM_VERSION_MIN_REQUIRED must be <= NM_VERSION_NEXT_STABLE"
+#endif
+#if NM_VERSION_MAX_ALLOWED < NM_VERSION_MIN_REQUIRED
+#error "NM_VERSION_MAX_ALLOWED must be >= NM_VERSION_MIN_REQUIRED"
+#endif
+#if NM_VERSION_MIN_REQUIRED < NM_VERSION_0_9_8
+#error "NM_VERSION_MIN_REQUIRED must be >= NM_VERSION_0_9_8"
+#endif
+
+#if NM_VERSION_MIN_REQUIRED >= NM_VERSION_0_9_10
+# define NM_DEPRECATED_IN_0_9_10 G_DEPRECATED
+# define NM_DEPRECATED_IN_0_9_10_FOR(f) G_DEPRECATED_FOR(f)
+#else
+# define NM_DEPRECATED_IN_0_9_10
+# define NM_DEPRECATED_IN_0_9_10_FOR(f)
+#endif
+
+#if NM_VERSION_MIN_REQUIRED >= NM_VERSION_1_0
+# define NM_DEPRECATED_IN_1_0 G_DEPRECATED
+# define NM_DEPRECATED_IN_1_0_FOR(f) G_DEPRECATED_FOR(f)
+#else
+# define NM_DEPRECATED_IN_1_0
+# define NM_DEPRECATED_IN_1_0_FOR(f)
+#endif
+
+#if NM_VERSION_MAX_ALLOWED < NM_VERSION_0_9_10
+# define NM_AVAILABLE_IN_0_9_10 G_UNAVAILABLE(0.9,10)
+#else
+# define NM_AVAILABLE_IN_0_9_10
+#endif
+
+#if NM_VERSION_MAX_ALLOWED < NM_VERSION_1_0
+# define NM_AVAILABLE_IN_1_0 G_UNAVAILABLE(1,0)
+#else
+# define NM_AVAILABLE_IN_1_0
+#endif
+
+#endif /* NM_VERSION_H */
diff --git a/libnm-core/tests/test-crypto.c b/libnm-core/tests/test-crypto.c
new file mode 100644
index 0000000000..4a8e77524f
--- /dev/null
+++ b/libnm-core/tests/test-crypto.c
@@ -0,0 +1,384 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * Dan Williams <dcbw@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2011 Red Hat, Inc.
+ */
+
+#include <glib.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <glib/gi18n.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "crypto.h"
+#include "nm-utils.h"
+
+#include "nm-test-utils.h"
+
+#if 0
+static const char *pem_rsa_key_begin = "-----BEGIN RSA PRIVATE KEY-----";
+static const char *pem_rsa_key_end = "-----END RSA PRIVATE KEY-----";
+
+static const char *pem_dsa_key_begin = "-----BEGIN DSA PRIVATE KEY-----";
+static const char *pem_dsa_key_end = "-----END DSA PRIVATE KEY-----";
+
+static void
+dump_key_to_pem (const char *key, gsize key_len, int key_type)
+{
+ char *b64 = NULL;
+ GString *str = NULL;
+ const char *start_tag;
+ const char *end_tag;
+ char *p;
+
+ switch (key_type) {
+ case NM_CRYPTO_KEY_TYPE_RSA:
+ start_tag = pem_rsa_key_begin;
+ end_tag = pem_rsa_key_end;
+ break;
+ case NM_CRYPTO_KEY_TYPE_DSA:
+ start_tag = pem_dsa_key_begin;
+ end_tag = pem_dsa_key_end;
+ break;
+ default:
+ g_warning ("Unknown key type %d", key_type);
+ return;
+ }
+
+ b64 = g_base64_encode ((const unsigned char *) key, key_len);
+ if (!b64) {
+ g_warning ("Couldn't base64 encode the key.");
+ goto out;
+ }
+
+ str = g_string_new (NULL);
+
+ g_string_append (str, start_tag);
+ g_string_append_c (str, '\n');
+
+ for (p = b64; p < (b64 + strlen (b64)); p += 64) {
+ g_string_append_len (str, p, strnlen (p, 64));
+ g_string_append_c (str, '\n');
+ }
+
+ g_string_append (str, end_tag);
+ g_string_append_c (str, '\n');
+
+ g_message ("Decrypted private key:\n\n%s", str->str);
+
+out:
+ g_free (b64);
+ if (str)
+ g_string_free (str, TRUE);
+}
+#endif
+
+static void
+test_load_cert (const char *path, const char *desc)
+{
+ GByteArray *array;
+ NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
+ GError *error = NULL;
+
+ array = crypto_load_and_verify_certificate (path, &format, &error);
+ ASSERT (array != NULL, desc,
+ "couldn't read certificate file '%s': %d %s",
+ path, error->code, error->message);
+
+ ASSERT (format == NM_CRYPTO_FILE_FORMAT_X509, desc,
+ "%s: unexpected certificate format (expected %d, got %d)",
+ path, NM_CRYPTO_FILE_FORMAT_X509, format);
+
+ g_byte_array_free (array, TRUE);
+}
+
+static GByteArray *
+file_to_byte_array (const char *filename)
+{
+ char *contents;
+ GByteArray *array = NULL;
+ gsize length = 0;
+
+ if (g_file_get_contents (filename, &contents, &length, NULL)) {
+ array = g_byte_array_sized_new (length);
+ g_byte_array_append (array, (guint8 *) contents, length);
+ g_assert (array->len == length);
+ g_free (contents);
+ }
+ return array;
+}
+
+static void
+test_load_private_key (const char *path,
+ const char *password,
+ const char *decrypted_path,
+ gboolean expect_fail,
+ const char *desc)
+{
+ NMCryptoKeyType key_type = NM_CRYPTO_KEY_TYPE_UNKNOWN;
+ GByteArray *array, *decrypted;
+ GError *error = NULL;
+
+ array = crypto_decrypt_private_key (path, password, &key_type, &error);
+ if (expect_fail) {
+ ASSERT (array == NULL, desc,
+ "unexpected success reading private key file '%s' with "
+ "invalid password",
+ path);
+
+ ASSERT (key_type != NM_CRYPTO_KEY_TYPE_UNKNOWN, desc,
+ "unexpected failure determining private key file '%s' "
+ "type with invalid password (expected %d, got %d)",
+ path, NM_CRYPTO_KEY_TYPE_UNKNOWN, key_type);
+ return;
+ }
+
+ ASSERT (array != NULL, desc,
+ "couldn't read private key file '%s': %d %s",
+ path, error->code, error->message);
+
+ ASSERT (key_type == NM_CRYPTO_KEY_TYPE_RSA, desc,
+ "%s: unexpected private key type (expected %d, got %d)",
+ path, NM_CRYPTO_KEY_TYPE_RSA, key_type);
+
+ if (decrypted_path) {
+ /* Compare the crypto decrypted key against a known-good decryption */
+ decrypted = file_to_byte_array (decrypted_path);
+ ASSERT (decrypted != NULL, desc,
+ "couldn't read decrypted private key file '%s': %d %s",
+ decrypted_path, error->code, error->message);
+
+ ASSERT (decrypted->len > 0, desc, "decrypted key file invalid (size 0)");
+
+ ASSERT (decrypted->len == array->len,
+ desc, "decrypted key file (%d) and decrypted key data (%d) lengths don't match",
+ decrypted->len, array->len);
+
+ ASSERT (memcmp (decrypted->data, array->data, array->len) == 0,
+ desc, "decrypted key file and decrypted key data don't match");
+
+ g_byte_array_free (decrypted, TRUE);
+ }
+
+ g_byte_array_free (array, TRUE);
+}
+
+static void
+test_load_pkcs12 (const char *path,
+ const char *password,
+ gboolean expect_fail,
+ const char *desc)
+{
+ NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
+ GError *error = NULL;
+
+ format = crypto_verify_private_key (path, password, &error);
+ if (expect_fail) {
+ ASSERT (format == NM_CRYPTO_FILE_FORMAT_UNKNOWN, desc,
+ "unexpected success reading PKCS#12 private key file "
+ "'%s' with invalid password",
+ path);
+ } else {
+ ASSERT (format == NM_CRYPTO_FILE_FORMAT_PKCS12, desc,
+ "%s: unexpected PKCS#12 private key file format (expected %d, got "
+ "%d): %d %s",
+ path, NM_CRYPTO_FILE_FORMAT_PKCS12, format, error->code, error->message);
+ }
+}
+
+static void
+test_load_pkcs12_no_password (const char *path, const char *desc)
+{
+ NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
+ GError *error = NULL;
+
+ /* We should still get a valid returned crypto file format */
+ format = crypto_verify_private_key (path, NULL, &error);
+ ASSERT (format == NM_CRYPTO_FILE_FORMAT_PKCS12, desc,
+ "%s: unexpected PKCS#12 private key file format (expected %d, got "
+ "%d): %d %s",
+ path, NM_CRYPTO_FILE_FORMAT_PKCS12, format, error->code, error->message);
+}
+
+static void
+test_is_pkcs12 (const char *path, gboolean expect_fail, const char *desc)
+{
+ gboolean is_pkcs12;
+
+ is_pkcs12 = crypto_is_pkcs12_file (path, NULL);
+ if (expect_fail) {
+ ASSERT (is_pkcs12 == FALSE, desc,
+ "unexpected success reading non-PKCS#12 file '%s'",
+ path);
+ } else {
+ ASSERT (is_pkcs12 == TRUE, desc, "couldn't read PKCS#12 file '%s'", path);
+ }
+}
+
+static void
+test_load_pkcs8 (const char *path,
+ const char *password,
+ gboolean expect_fail,
+ const char *desc)
+{
+ NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
+ GError *error = NULL;
+
+ format = crypto_verify_private_key (path, password, &error);
+ if (expect_fail) {
+ ASSERT (format == NM_CRYPTO_FILE_FORMAT_UNKNOWN, desc,
+ "unexpected success reading PKCS#8 private key file "
+ "'%s' with invalid password",
+ path);
+ } else {
+ ASSERT (format == NM_CRYPTO_FILE_FORMAT_RAW_KEY, desc,
+ "%s: unexpected PKCS#8 private key file format (expected %d, got "
+ "%d): %d %s",
+ path, NM_CRYPTO_FILE_FORMAT_RAW_KEY, format, error->code, error->message);
+ }
+}
+
+static gboolean
+is_cipher_aes (const char *path)
+{
+ char *contents;
+ gsize length = 0;
+ const char *cipher;
+ gboolean is_aes = FALSE;
+
+ if (!g_file_get_contents (path, &contents, &length, NULL))
+ return FALSE;
+
+ cipher = strstr (contents, "DEK-Info: ");
+ if (cipher) {
+ cipher += strlen ("DEK-Info: ");
+ if (g_str_has_prefix (cipher, "AES-128-CBC"))
+ is_aes = TRUE;
+ }
+
+ g_free (contents);
+ return is_aes;
+}
+
+static void
+test_encrypt_private_key (const char *path,
+ const char *password,
+ const char *desc)
+{
+ NMCryptoKeyType key_type = NM_CRYPTO_KEY_TYPE_UNKNOWN;
+ GByteArray *array, *encrypted, *re_decrypted;
+ GError *error = NULL;
+
+ array = crypto_decrypt_private_key (path, password, &key_type, &error);
+ ASSERT (array != NULL, desc,
+ "couldn't read private key file '%s': %d %s",
+ path, error->code, error->message);
+
+ ASSERT (key_type == NM_CRYPTO_KEY_TYPE_RSA, desc,
+ "%s: unexpected private key type (expected %d, got %d)",
+ path, NM_CRYPTO_KEY_TYPE_RSA, key_type);
+
+ /* Now re-encrypt the private key */
+ if (is_cipher_aes (path))
+ encrypted = nm_utils_rsa_key_encrypt_aes (array, password, NULL, &error);
+ else
+ encrypted = nm_utils_rsa_key_encrypt (array, password, NULL, &error);
+ ASSERT (encrypted != NULL, desc,
+ "couldn't re-encrypt private key file '%s': %d %s",
+ path, error->code, error->message);
+
+ /* Then re-decrypt the private key */
+ key_type = NM_CRYPTO_KEY_TYPE_UNKNOWN;
+ re_decrypted = crypto_decrypt_private_key_data (encrypted, password, &key_type, &error);
+ ASSERT (re_decrypted != NULL, desc,
+ "couldn't read private key file '%s': %d %s",
+ path, error->code, error->message);
+
+ ASSERT (key_type == NM_CRYPTO_KEY_TYPE_RSA, desc,
+ "%s: unexpected private key type (expected %d, got %d)",
+ path, NM_CRYPTO_KEY_TYPE_RSA, key_type);
+
+ /* Compare the original decrypted key with the re-decrypted key */
+ ASSERT (array->len == re_decrypted->len, desc,
+ "%s: unexpected re-decrypted private key length (expected %d, got %d)",
+ path, array->len, re_decrypted->len);
+
+ ASSERT (!memcmp (array->data, re_decrypted->data, array->len), desc,
+ "%s: unexpected private key data",
+ path);
+
+ g_byte_array_free (re_decrypted, TRUE);
+ g_byte_array_free (encrypted, TRUE);
+ g_byte_array_free (array, TRUE);
+}
+
+int main (int argc, char **argv)
+{
+ GError *error = NULL;
+ char *progname;
+
+ ASSERT (argc > 2, "test-crypto",
+ "wrong number of arguments (expected at least an operation and an object)");
+
+ if (!crypto_init (&error))
+ FAIL ("crypto-init", "failed to initialize crypto: %s", error->message);
+
+ if (!strcmp (argv[1], "--cert"))
+ test_load_cert (argv[2], "cert");
+ else if (!strcmp (argv[1], "--key")) {
+ const char *decrypted_path = (argc == 5) ? argv[4] : NULL;
+
+ ASSERT (argc == 4 || argc == 5, "test-crypto",
+ "wrong number of arguments (--key <key file> <password> [<decrypted key file>])");
+
+ test_is_pkcs12 (argv[2], TRUE, "not-pkcs12");
+ test_load_private_key (argv[2], argv[3], decrypted_path, FALSE, "private-key");
+ test_load_private_key (argv[2], "blahblahblah", NULL, TRUE, "private-key-bad-password");
+ test_load_private_key (argv[2], NULL, NULL, TRUE, "private-key-no-password");
+ test_encrypt_private_key (argv[2], argv[3], "private-key-rencrypt");
+ } else if (!strcmp (argv[1], "--p12")) {
+ test_is_pkcs12 (argv[2], FALSE, "is-pkcs12");
+ test_load_pkcs12 (argv[2], argv[3], FALSE, "pkcs12-private-key");
+ test_load_pkcs12 (argv[2], "blahblahblah", TRUE, "pkcs12-private-key-bad-password");
+ test_load_pkcs12_no_password (argv[2], "pkcs12-private-key-no-password");
+ } else if (!strcmp (argv[1], "--pkcs8")) {
+ ASSERT (argc == 4, "test-crypto",
+ "wrong number of arguments (--pkcs8 <key file> <password>)");
+
+ test_is_pkcs12 (argv[2], TRUE, "not-pkcs12");
+ test_load_pkcs8 (argv[2], argv[3], FALSE, "pkcs8-private-key");
+ /* Until gnutls and NSS grow support for all the ciphers that openssl
+ * can use with PKCS#8, we can't actually verify the password. So we
+ * expect a bad password to work for the time being.
+ */
+ test_load_pkcs8 (argv[2], "blahblahblah", FALSE, "pkcs8-private-key-bad-password");
+ } else {
+ ASSERT (argc > 2, "test-crypto", "unknown test type (not --cert, --key, or --p12)");
+ }
+
+ crypto_deinit ();
+
+ progname = g_path_get_basename (argv[0]);
+ fprintf (stdout, "%s: SUCCESS\n", progname);
+ g_free (progname);
+ return 0;
+}
+
diff --git a/libnm-core/tests/test-general.c b/libnm-core/tests/test-general.c
new file mode 100644
index 0000000000..0b3283ed51
--- /dev/null
+++ b/libnm-core/tests/test-general.c
@@ -0,0 +1,2681 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2008 - 2011 Red Hat, Inc.
+ *
+ */
+
+#include <glib.h>
+#include <dbus/dbus-glib.h>
+#include <string.h>
+#include <netinet/ether.h>
+#include <linux/if_infiniband.h>
+
+#include <nm-utils.h>
+
+#include "nm-setting-private.h"
+#include "nm-setting-connection.h"
+#include "nm-setting-vpn.h"
+#include "nm-setting-gsm.h"
+#include "nm-setting-cdma.h"
+#include "nm-setting-wired.h"
+#include "nm-setting-wireless-security.h"
+#include "nm-setting-ip6-config.h"
+#include "nm-setting-ip4-config.h"
+#include "nm-setting-pppoe.h"
+#include "nm-setting-serial.h"
+#include "nm-setting-vlan.h"
+#include "nm-setting-bond.h"
+#include "nm-utils.h"
+#include "nm-utils-private.h"
+#include "nm-dbus-glib-types.h"
+
+#include "nm-test-utils.h"
+
+static void
+vpn_check_func (const char *key, const char *value, gpointer user_data)
+{
+ const char *test = user_data;
+
+ if (!strcmp (key, "foobar1")) {
+ ASSERT (strcmp (value, "blahblah1") == 0,
+ test, "unexpected vpn item '%s' / '%s'", key, value);
+ return;
+ }
+
+ if (!strcmp (key, "foobar2")) {
+ ASSERT (strcmp (value, "blahblah2") == 0,
+ test, "unexpected vpn item '%s' / '%s'", key, value);
+ return;
+ }
+
+ if (!strcmp (key, "foobar3")) {
+ ASSERT (strcmp (value, "blahblah3") == 0,
+ test, "unexpected vpn item '%s' / '%s'", key, value);
+ return;
+ }
+
+ if (!strcmp (key, "foobar4")) {
+ ASSERT (strcmp (value, "blahblah4") == 0,
+ test, "unexpected vpn item '%s' / '%s'", key, value);
+ return;
+ }
+
+ ASSERT (FALSE, test, "unexpected vpn item '%s'", key);
+}
+
+static void
+vpn_check_empty_func (const char *key, const char *value, gpointer user_data)
+{
+ const char *test = user_data;
+
+ /* We don't expect any values */
+ ASSERT (FALSE, test, "unexpected vpn item '%s'", key);
+}
+
+static void
+test_setting_vpn_items (void)
+{
+ NMSettingVPN *s_vpn;
+
+ s_vpn = (NMSettingVPN *) nm_setting_vpn_new ();
+ ASSERT (s_vpn != NULL,
+ "vpn-items",
+ "error creating vpn setting");
+
+ nm_setting_vpn_add_data_item (s_vpn, "foobar1", "blahblah1");
+ nm_setting_vpn_add_data_item (s_vpn, "foobar2", "blahblah2");
+ nm_setting_vpn_add_data_item (s_vpn, "foobar3", "blahblah3");
+ nm_setting_vpn_add_data_item (s_vpn, "foobar4", "blahblah4");
+
+ /* Ensure that added values are all present */
+ nm_setting_vpn_foreach_data_item (s_vpn, vpn_check_func, "vpn-data");
+ nm_setting_vpn_remove_data_item (s_vpn, "foobar1");
+ nm_setting_vpn_remove_data_item (s_vpn, "foobar2");
+ nm_setting_vpn_remove_data_item (s_vpn, "foobar3");
+ nm_setting_vpn_remove_data_item (s_vpn, "foobar4");
+
+ nm_setting_vpn_add_secret (s_vpn, "foobar1", "blahblah1");
+ nm_setting_vpn_add_secret (s_vpn, "foobar2", "blahblah2");
+ nm_setting_vpn_add_secret (s_vpn, "foobar3", "blahblah3");
+ nm_setting_vpn_add_secret (s_vpn, "foobar4", "blahblah4");
+
+ /* Ensure that added values are all present */
+ nm_setting_vpn_foreach_secret (s_vpn, vpn_check_func, "vpn-secrets");
+ nm_setting_vpn_remove_secret (s_vpn, "foobar1");
+ nm_setting_vpn_remove_secret (s_vpn, "foobar2");
+ nm_setting_vpn_remove_secret (s_vpn, "foobar3");
+ nm_setting_vpn_remove_secret (s_vpn, "foobar4");
+
+ /* Try to add some blank values and make sure they are rejected */
+ g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*key != NULL*");
+ nm_setting_vpn_add_data_item (s_vpn, NULL, NULL);
+ g_test_assert_expected_messages ();
+
+ g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*strlen (key) > 0*");
+ nm_setting_vpn_add_data_item (s_vpn, "", "");
+ g_test_assert_expected_messages ();
+
+ g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*item != NULL*");
+ nm_setting_vpn_add_data_item (s_vpn, "foobar1", NULL);
+ g_test_assert_expected_messages ();
+
+ g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*strlen (item) > 0*");
+ nm_setting_vpn_add_data_item (s_vpn, "foobar1", "");
+ g_test_assert_expected_messages ();
+
+ g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*key != NULL*");
+ nm_setting_vpn_add_data_item (s_vpn, NULL, "blahblah1");
+ g_test_assert_expected_messages ();
+
+ g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*strlen (key) > 0*");
+ nm_setting_vpn_add_data_item (s_vpn, "", "blahblah1");
+ g_test_assert_expected_messages ();
+
+ nm_setting_vpn_foreach_data_item (s_vpn, vpn_check_empty_func, "vpn-data-empty");
+
+ /* Try to add some blank secrets and make sure they are rejected */
+ g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*key != NULL*");
+ nm_setting_vpn_add_secret (s_vpn, NULL, NULL);
+ g_test_assert_expected_messages ();
+
+ g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*strlen (key) > 0*");
+ nm_setting_vpn_add_secret (s_vpn, "", "");
+ g_test_assert_expected_messages ();
+
+ g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*secret != NULL*");
+ nm_setting_vpn_add_secret (s_vpn, "foobar1", NULL);
+ g_test_assert_expected_messages ();
+
+ g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*strlen (secret) > 0*");
+ nm_setting_vpn_add_secret (s_vpn, "foobar1", "");
+ g_test_assert_expected_messages ();
+
+ g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*key != NULL*");
+ nm_setting_vpn_add_secret (s_vpn, NULL, "blahblah1");
+ g_test_assert_expected_messages ();
+
+ g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*strlen (key) > 0*");
+ nm_setting_vpn_add_secret (s_vpn, "", "blahblah1");
+ g_test_assert_expected_messages ();
+
+ nm_setting_vpn_foreach_secret (s_vpn, vpn_check_empty_func, "vpn-secrets-empty");
+
+ g_object_unref (s_vpn);
+}
+
+static void
+test_setting_vpn_update_secrets (void)
+{
+ NMConnection *connection;
+ NMSettingVPN *s_vpn;
+ GHashTable *settings, *vpn, *secrets;
+ GValue val = G_VALUE_INIT;
+ gboolean success;
+ GError *error = NULL;
+ const char *tmp;
+ const char *key1 = "foobar";
+ const char *key2 = "blahblah";
+ const char *val1 = "value1";
+ const char *val2 = "value2";
+
+ connection = nm_connection_new ();
+ ASSERT (connection != NULL,
+ "vpn-update-secrets",
+ "error creating connection");
+
+ s_vpn = (NMSettingVPN *) nm_setting_vpn_new ();
+ ASSERT (s_vpn != NULL,
+ "vpn-update-secrets",
+ "error creating vpn setting");
+ nm_connection_add_setting (connection, NM_SETTING (s_vpn));
+
+ settings = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify) g_hash_table_destroy);
+ vpn = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify) g_value_unset);
+ g_hash_table_insert (settings, NM_SETTING_VPN_SETTING_NAME, vpn);
+
+ secrets = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL);
+ g_value_init (&val, DBUS_TYPE_G_MAP_OF_STRING);
+ g_value_take_boxed (&val, secrets);
+ g_hash_table_insert (vpn, NM_SETTING_VPN_SECRETS, &val);
+
+ /* Add some secrets */
+ g_hash_table_insert (secrets, (char *) key1, (char *) val1);
+ g_hash_table_insert (secrets, (char *) key2, (char *) val2);
+
+ success = nm_connection_update_secrets (connection, NM_SETTING_VPN_SETTING_NAME, settings, &error);
+ ASSERT (success == TRUE,
+ "vpn-update-secrets", "failed to update VPN secrets: %s", error->message);
+
+ /* Read the secrets back out */
+ tmp = nm_setting_vpn_get_secret (s_vpn, key1);
+ ASSERT (tmp != NULL,
+ "vpn-update-secrets", "unexpected failure getting key #1");
+ ASSERT (strcmp (tmp, val1) == 0,
+ "vpn-update-secrets", "unexpected key #1 value");
+
+ tmp = nm_setting_vpn_get_secret (s_vpn, key2);
+ ASSERT (tmp != NULL,
+ "vpn-update-secrets", "unexpected failure getting key #2");
+ ASSERT (strcmp (tmp, val2) == 0,
+ "vpn-update-secrets", "unexpected key #2 value");
+
+ g_hash_table_destroy (settings);
+ g_object_unref (connection);
+}
+
+#define TO_DEL_NUM 50
+typedef struct {
+ NMSettingVPN *s_vpn;
+ char *to_del[TO_DEL_NUM];
+ guint called;
+} IterInfo;
+
+static void
+del_iter_func (const char *key, const char *value, gpointer user_data)
+{
+ IterInfo *info = user_data;
+ int i;
+
+ /* Record how many times this function gets called; it should get called
+ * exactly as many times as there are keys in the hash table, regardless
+ * of what keys we delete from the table.
+ */
+ info->called++;
+
+ /* During the iteration, remove a bunch of stuff from the table */
+ if (info->called == 1) {
+ for (i = 0; i < TO_DEL_NUM; i++)
+ nm_setting_vpn_remove_data_item (info->s_vpn, info->to_del[i]);
+ }
+}
+
+static void
+test_setting_vpn_modify_during_foreach (void)
+{
+ NMSettingVPN *s_vpn;
+ IterInfo info;
+ char *key, *val;
+ int i, u = 0;
+
+ s_vpn = (NMSettingVPN *) nm_setting_vpn_new ();
+ g_assert (s_vpn);
+
+ for (i = 0; i < TO_DEL_NUM * 2; i++) {
+ key = g_strdup_printf ("adsfasdfadf%d", i);
+ val = g_strdup_printf ("42263236236awt%d", i);
+ nm_setting_vpn_add_data_item (s_vpn, key, val);
+
+ /* Cache some keys to delete */
+ if (i % 2)
+ info.to_del[u++] = g_strdup (key);
+
+ g_free (key);
+ g_free (val);
+ }
+
+ /* Iterate over current table keys */
+ info.s_vpn = s_vpn;
+ info.called = 0;
+ nm_setting_vpn_foreach_data_item (s_vpn, del_iter_func, &info);
+
+ /* Make sure all the things we removed during iteration are really gone */
+ for (i = 0; i < TO_DEL_NUM; i++) {
+ g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, info.to_del[i]), ==, NULL);
+ g_free (info.to_del[i]);
+ }
+
+ /* And make sure the foreach callback was called the same number of times
+ * as there were keys in the table at the beginning of the foreach.
+ */
+ g_assert_cmpint (info.called, ==, TO_DEL_NUM * 2);
+
+ g_object_unref (s_vpn);
+}
+
+static void
+test_setting_ip4_config_labels (void)
+{
+ NMSettingIP4Config *s_ip4;
+ NMIP4Address *addr;
+ const char *label;
+ GPtrArray *addrs;
+ GSList *labels;
+ GError *error = NULL;
+
+ s_ip4 = (NMSettingIP4Config *) nm_setting_ip4_config_new ();
+ g_object_set (G_OBJECT (s_ip4),
+ NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_MANUAL,
+ NULL);
+
+ /* addr 1 */
+ addr = nm_ip4_address_new ();
+ nm_ip4_address_set_address (addr, 0x01010101);
+ nm_ip4_address_set_prefix (addr, 24);
+
+ nm_setting_ip4_config_add_address (s_ip4, addr);
+ nm_ip4_address_unref (addr);
+ nm_setting_verify (NM_SETTING (s_ip4), NULL, &error);
+ g_assert_no_error (error);
+
+ label = NM_UTILS_PRIVATE_CALL (nm_setting_ip4_config_get_address_label (s_ip4, 0));
+ g_assert_cmpstr (label, ==, NULL);
+
+ /* addr 2 */
+ addr = nm_ip4_address_new ();
+ nm_ip4_address_set_address (addr, 0x02020202);
+ nm_ip4_address_set_prefix (addr, 24);
+
+ NM_UTILS_PRIVATE_CALL (nm_setting_ip4_config_add_address_with_label (s_ip4, addr, "eth0:1"));
+ nm_ip4_address_unref (addr);
+ nm_setting_verify (NM_SETTING (s_ip4), NULL, &error);
+ g_assert_no_error (error);
+
+ label = NM_UTILS_PRIVATE_CALL (nm_setting_ip4_config_get_address_label (s_ip4, 1));
+ g_assert_cmpstr (label, ==, "eth0:1");
+
+ /* addr 3 */
+ addr = nm_ip4_address_new ();
+ nm_ip4_address_set_address (addr, 0x03030303);
+ nm_ip4_address_set_prefix (addr, 24);
+
+ NM_UTILS_PRIVATE_CALL (nm_setting_ip4_config_add_address_with_label (s_ip4, addr, NULL));
+ nm_ip4_address_unref (addr);
+ nm_setting_verify (NM_SETTING (s_ip4), NULL, &error);
+ g_assert_no_error (error);
+
+ label = NM_UTILS_PRIVATE_CALL (nm_setting_ip4_config_get_address_label (s_ip4, 2));
+ g_assert_cmpstr (label, ==, NULL);
+
+ /* Remove addr 1 and re-verify remaining addresses */
+ nm_setting_ip4_config_remove_address (s_ip4, 0);
+ nm_setting_verify (NM_SETTING (s_ip4), NULL, &error);
+ g_assert_no_error (error);
+
+ addr = nm_setting_ip4_config_get_address (s_ip4, 0);
+ g_assert_cmpint (nm_ip4_address_get_address (addr), ==, 0x02020202);
+ label = NM_UTILS_PRIVATE_CALL (nm_setting_ip4_config_get_address_label (s_ip4, 0));
+ g_assert_cmpstr (label, ==, "eth0:1");
+
+ addr = nm_setting_ip4_config_get_address (s_ip4, 1);
+ g_assert_cmpint (nm_ip4_address_get_address (addr), ==, 0x03030303);
+ label = NM_UTILS_PRIVATE_CALL (nm_setting_ip4_config_get_address_label (s_ip4, 1));
+ g_assert_cmpstr (label, ==, NULL);
+
+
+ /* Test explicit property assignment */
+ g_object_get (G_OBJECT (s_ip4),
+ NM_SETTING_IP4_CONFIG_ADDRESSES, &addrs,
+ "address-labels", &labels,
+ NULL);
+
+ nm_setting_ip4_config_clear_addresses (s_ip4);
+ g_assert_cmpint (nm_setting_ip4_config_get_num_addresses (s_ip4), ==, 0);
+
+ /* Setting addrs but not labels will result in empty labels */
+ g_object_set (G_OBJECT (s_ip4),
+ NM_SETTING_IP4_CONFIG_ADDRESSES, addrs,
+ NULL);
+ g_boxed_free (DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UINT, addrs);
+ nm_setting_verify (NM_SETTING (s_ip4), NULL, &error);
+ g_assert_no_error (error);
+ g_assert_cmpint (nm_setting_ip4_config_get_num_addresses (s_ip4), ==, 2);
+
+ addr = nm_setting_ip4_config_get_address (s_ip4, 0);
+ g_assert_cmpint (nm_ip4_address_get_address (addr), ==, 0x02020202);
+ label = NM_UTILS_PRIVATE_CALL (nm_setting_ip4_config_get_address_label (s_ip4, 0));
+ g_assert_cmpstr (label, ==, NULL);
+
+ addr = nm_setting_ip4_config_get_address (s_ip4, 1);
+ g_assert_cmpint (nm_ip4_address_get_address (addr), ==, 0x03030303);
+ label = NM_UTILS_PRIVATE_CALL (nm_setting_ip4_config_get_address_label (s_ip4, 1));
+ g_assert_cmpstr (label, ==, NULL);
+
+ /* Setting labels now will leave addresses untouched */
+ g_object_set (G_OBJECT (s_ip4),
+ "address-labels", labels,
+ NULL);
+ g_boxed_free (DBUS_TYPE_G_LIST_OF_STRING, labels);
+ nm_setting_verify (NM_SETTING (s_ip4), NULL, &error);
+ g_assert_no_error (error);
+ g_assert_cmpint (nm_setting_ip4_config_get_num_addresses (s_ip4), ==, 2);
+
+ addr = nm_setting_ip4_config_get_address (s_ip4, 0);
+ g_assert_cmpint (nm_ip4_address_get_address (addr), ==, 0x02020202);
+ label = NM_UTILS_PRIVATE_CALL (nm_setting_ip4_config_get_address_label (s_ip4, 0));
+ g_assert_cmpstr (label, ==, "eth0:1");
+
+ addr = nm_setting_ip4_config_get_address (s_ip4, 1);
+ g_assert_cmpint (nm_ip4_address_get_address (addr), ==, 0x03030303);
+ label = NM_UTILS_PRIVATE_CALL (nm_setting_ip4_config_get_address_label (s_ip4, 1));
+ g_assert_cmpstr (label, ==, NULL);
+
+ /* Setting labels to a value that's too short or too long will result in
+ * the setting not verifying.
+ */
+ labels = g_slist_append (NULL, "eth0:2");
+ g_object_set (G_OBJECT (s_ip4),
+ "address-labels", labels,
+ NULL);
+
+ nm_setting_verify (NM_SETTING (s_ip4), NULL, &error);
+ g_assert_error (error, NM_SETTING_IP4_CONFIG_ERROR, NM_SETTING_IP4_CONFIG_ERROR_INVALID_PROPERTY);
+ g_assert (g_str_has_prefix (error->message, "ipv4.address-labels:"));
+ g_clear_error (&error);
+
+ labels = g_slist_append (labels, "eth0:3");
+ g_object_set (G_OBJECT (s_ip4),
+ "address-labels", labels,
+ NULL);
+ nm_setting_verify (NM_SETTING (s_ip4), NULL, &error);
+ g_assert_no_error (error);
+
+ labels = g_slist_append (labels, "eth0:4");
+ g_object_set (G_OBJECT (s_ip4),
+ "address-labels", labels,
+ NULL);
+ nm_setting_verify (NM_SETTING (s_ip4), NULL, &error);
+ g_assert_error (error, NM_SETTING_IP4_CONFIG_ERROR, NM_SETTING_IP4_CONFIG_ERROR_INVALID_PROPERTY);
+ g_assert (g_str_has_prefix (error->message, "ipv4.address-labels:"));
+ g_clear_error (&error);
+
+
+ g_object_unref (s_ip4);
+}
+
+#define OLD_DBUS_TYPE_G_IP6_ADDRESS (dbus_g_type_get_struct ("GValueArray", DBUS_TYPE_G_UCHAR_ARRAY, G_TYPE_UINT, G_TYPE_INVALID))
+#define OLD_DBUS_TYPE_G_ARRAY_OF_IP6_ADDRESS (dbus_g_type_get_collection ("GPtrArray", OLD_DBUS_TYPE_G_IP6_ADDRESS))
+
+/* Test that setting the IPv6 setting's 'addresses' property using the old
+ * IPv6 address format still works, i.e. that the GValue transformation function
+ * from old->new is working correctly.
+ */
+static void
+test_setting_ip6_config_old_address_array (void)
+{
+ NMSettingIP6Config *s_ip6;
+ GPtrArray *addresses, *read_addresses;
+ GValueArray *array, *read_array;
+ GValue element = G_VALUE_INIT, written_value = G_VALUE_INIT, read_value = G_VALUE_INIT;
+ GByteArray *ba;
+ const guint8 addr[16] = { 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11,
+ 0x11, 0x22, 0x33, 0x44, 0x66, 0x77, 0x88, 0x99 };
+ const guint8 gw[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ guint32 prefix = 56;
+ GValue *read_addr, *read_prefix, *read_gw;
+
+ s_ip6 = (NMSettingIP6Config *) nm_setting_ip6_config_new ();
+ ASSERT (s_ip6 != NULL,
+ "ip6-old-addr", "error creating IP6 setting");
+
+ g_value_init (&written_value, OLD_DBUS_TYPE_G_ARRAY_OF_IP6_ADDRESS);
+
+ addresses = g_ptr_array_new ();
+ array = g_value_array_new (3);
+
+ /* IP address */
+ g_value_init (&element, DBUS_TYPE_G_UCHAR_ARRAY);
+ ba = g_byte_array_new ();
+ g_byte_array_append (ba, &addr[0], sizeof (addr));
+ g_value_take_boxed (&element, ba);
+ g_value_array_append (array, &element);
+ g_value_unset (&element);
+
+ /* Prefix */
+ g_value_init (&element, G_TYPE_UINT);
+ g_value_set_uint (&element, prefix);
+ g_value_array_append (array, &element);
+ g_value_unset (&element);
+
+ g_ptr_array_add (addresses, array);
+ g_value_set_boxed (&written_value, addresses);
+
+ /* Set the address array on the object */
+ g_object_set_property (G_OBJECT (s_ip6), NM_SETTING_IP6_CONFIG_ADDRESSES, &written_value);
+
+ /* Get it back so we can compare it */
+ g_value_init (&read_value, DBUS_TYPE_G_ARRAY_OF_IP6_ADDRESS);
+ g_object_get_property (G_OBJECT (s_ip6), NM_SETTING_IP6_CONFIG_ADDRESSES, &read_value);
+
+ ASSERT (G_VALUE_HOLDS (&read_value, DBUS_TYPE_G_ARRAY_OF_IP6_ADDRESS),
+ "ip6-old-addr", "wrong addresses property value type '%s'",
+ G_VALUE_TYPE_NAME (&read_value));
+
+ read_addresses = (GPtrArray *) g_value_get_boxed (&read_value);
+ ASSERT (read_addresses != NULL,
+ "ip6-old-addr", "missing addresses on readback");
+ ASSERT (read_addresses->len == 1,
+ "ip6-old-addr", "expected one address on readback");
+
+ read_array = (GValueArray *) g_ptr_array_index (read_addresses, 0);
+
+ read_addr = g_value_array_get_nth (read_array, 0);
+ ba = g_value_get_boxed (read_addr);
+ ASSERT (ba->len == sizeof (addr),
+ "ip6-old-addr", "unexpected address item length %d", ba->len);
+ ASSERT (memcmp (ba->data, &addr[0], sizeof (addr)) == 0,
+ "ip6-old-addr", "unexpected failure comparing addresses");
+
+ read_prefix = g_value_array_get_nth (read_array, 1);
+ ASSERT (g_value_get_uint (read_prefix) == prefix,
+ "ip6-old-addr", "unexpected failure comparing prefix");
+
+ /* Ensure the gateway is all zeros, which is how the 2-item to 3-item
+ * conversion happens.
+ */
+ read_gw = g_value_array_get_nth (read_array, 2);
+ ba = g_value_get_boxed (read_gw);
+ ASSERT (ba->len == sizeof (gw),
+ "ip6-old-addr", "unexpected gateway item length %d", ba->len);
+ ASSERT (memcmp (ba->data, &gw[0], sizeof (gw)) == 0,
+ "ip6-old-addr", "unexpected failure comparing gateways");
+
+ g_value_unset (&written_value);
+ g_value_unset (&read_value);
+ g_object_unref (s_ip6);
+}
+
+static void
+test_setting_gsm_apn_spaces (void)
+{
+ NMSettingGsm *s_gsm;
+ const char *tmp;
+
+ s_gsm = (NMSettingGsm *) nm_setting_gsm_new ();
+ ASSERT (s_gsm != NULL,
+ "gsm-apn-spaces",
+ "error creating GSM setting");
+
+ /* Trailing space */
+ g_object_set (s_gsm, NM_SETTING_GSM_APN, "foobar ", NULL);
+ tmp = nm_setting_gsm_get_apn (s_gsm);
+ ASSERT (tmp != NULL,
+ "gsm-apn-spaces", "empty APN");
+ ASSERT (strcmp (tmp, "foobar") == 0,
+ "gsm-apn-spaces", "unexpected APN");
+
+ /* Leading space */
+ g_object_set (s_gsm, NM_SETTING_GSM_APN, " foobar", NULL);
+ tmp = nm_setting_gsm_get_apn (s_gsm);
+ ASSERT (tmp != NULL,
+ "gsm-apn-spaces", "empty APN");
+ ASSERT (strcmp (tmp, "foobar") == 0,
+ "gsm-apn-spaces", "unexpected APN");
+}
+
+static void
+test_setting_gsm_apn_bad_chars (void)
+{
+ NMSettingGsm *s_gsm;
+
+ s_gsm = (NMSettingGsm *) nm_setting_gsm_new ();
+ ASSERT (s_gsm != NULL,
+ "gsm-apn-bad-chars",
+ "error creating GSM setting");
+
+ g_object_set (s_gsm, NM_SETTING_GSM_NUMBER, "*99#", NULL);
+
+ /* Make sure a valid APN works */
+ g_object_set (s_gsm, NM_SETTING_GSM_APN, "foobar123.-baz", NULL);
+ ASSERT (nm_setting_verify (NM_SETTING (s_gsm), NULL, NULL) == TRUE,
+ "gsm-apn-bad-chars", "unexpectedly invalid GSM setting");
+
+ /* Random invalid chars */
+ g_object_set (s_gsm, NM_SETTING_GSM_APN, "@#%$@#%@#%", NULL);
+ ASSERT (nm_setting_verify (NM_SETTING (s_gsm), NULL, NULL) == FALSE,
+ "gsm-apn-bad-chars", "unexpectedly valid GSM setting");
+
+ /* Spaces */
+ g_object_set (s_gsm, NM_SETTING_GSM_APN, "foobar baz", NULL);
+ ASSERT (nm_setting_verify (NM_SETTING (s_gsm), NULL, NULL) == FALSE,
+ "gsm-apn-bad-chars", "unexpectedly valid GSM setting");
+
+ /* 0 characters long */
+ g_object_set (s_gsm, NM_SETTING_GSM_APN, "", NULL);
+ ASSERT (nm_setting_verify (NM_SETTING (s_gsm), NULL, NULL) == FALSE,
+ "gsm-apn-bad-chars", "unexpectedly valid GSM setting");
+
+ /* 65-character long */
+ g_object_set (s_gsm, NM_SETTING_GSM_APN, "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijkl1", NULL);
+ ASSERT (nm_setting_verify (NM_SETTING (s_gsm), NULL, NULL) == FALSE,
+ "gsm-apn-bad-chars", "unexpectedly valid GSM setting");
+}
+
+static void
+test_setting_gsm_apn_underscore (void)
+{
+ NMSettingGsm *s_gsm;
+ GError *error = NULL;
+ gboolean success;
+
+ s_gsm = (NMSettingGsm *) nm_setting_gsm_new ();
+ g_assert (s_gsm);
+
+ g_object_set (s_gsm, NM_SETTING_GSM_NUMBER, "*99#", NULL);
+
+ /* 65-character long */
+ g_object_set (s_gsm, NM_SETTING_GSM_APN, "foobar_baz", NULL);
+ success = nm_setting_verify (NM_SETTING (s_gsm), NULL, &error);
+ g_assert_no_error (error);
+ g_assert (success == TRUE);
+}
+
+static void
+test_setting_gsm_without_number (void)
+{
+ NMSettingGsm *s_gsm;
+ GError *error = NULL;
+ gboolean success;
+
+ s_gsm = (NMSettingGsm *) nm_setting_gsm_new ();
+ g_assert (s_gsm);
+
+ g_object_set (s_gsm, NM_SETTING_GSM_NUMBER, NULL, NULL);
+ success = nm_setting_verify (NM_SETTING (s_gsm), NULL, &error);
+ g_assert_no_error (error);
+ g_assert (success == TRUE);
+
+ g_object_set (s_gsm, NM_SETTING_GSM_NUMBER, "", NULL);
+ success = nm_setting_verify (NM_SETTING (s_gsm), NULL, &error);
+ g_assert_error (error, NM_SETTING_GSM_ERROR, NM_SETTING_GSM_ERROR_INVALID_PROPERTY);
+ g_error_free (error);
+}
+
+static NMSettingWirelessSecurity *
+make_test_wsec_setting (const char *detail)
+{
+ NMSettingWirelessSecurity *s_wsec;
+
+ s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new ();
+ ASSERT (s_wsec != NULL, detail, "error creating setting");
+
+ g_object_set (s_wsec,
+ NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-psk",
+ NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME, "foobarbaz",
+ NM_SETTING_WIRELESS_SECURITY_PSK, "random psk",
+ NM_SETTING_WIRELESS_SECURITY_WEP_KEY0, "aaaaaaaaaa",
+ NULL);
+
+ return s_wsec;
+}
+
+static void
+test_setting_to_hash_all (void)
+{
+ NMSettingWirelessSecurity *s_wsec;
+ GHashTable *hash;
+
+ s_wsec = make_test_wsec_setting ("setting-to-hash-all");
+
+ hash = nm_setting_to_hash (NM_SETTING (s_wsec), NM_SETTING_HASH_FLAG_ALL);
+
+ /* Make sure all keys are there */
+ ASSERT (g_hash_table_lookup (hash, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT),
+ "setting-to-hash-all", "unexpectedly missing " NM_SETTING_WIRELESS_SECURITY_KEY_MGMT);
+ ASSERT (g_hash_table_lookup (hash, NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME),
+ "setting-to-hash-all", "unexpectedly missing " NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME);
+ ASSERT (g_hash_table_lookup (hash, NM_SETTING_WIRELESS_SECURITY_PSK),
+ "setting-to-hash-all", "unexpectedly missing " NM_SETTING_WIRELESS_SECURITY_PSK);
+ ASSERT (g_hash_table_lookup (hash, NM_SETTING_WIRELESS_SECURITY_WEP_KEY0),
+ "setting-to-hash-all", "unexpectedly missing " NM_SETTING_WIRELESS_SECURITY_WEP_KEY0);
+
+ g_hash_table_destroy (hash);
+ g_object_unref (s_wsec);
+}
+
+static void
+test_setting_to_hash_no_secrets (void)
+{
+ NMSettingWirelessSecurity *s_wsec;
+ GHashTable *hash;
+
+ s_wsec = make_test_wsec_setting ("setting-to-hash-no-secrets");
+
+ hash = nm_setting_to_hash (NM_SETTING (s_wsec), NM_SETTING_HASH_FLAG_NO_SECRETS);
+
+ /* Make sure non-secret keys are there */
+ ASSERT (g_hash_table_lookup (hash, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT),
+ "setting-to-hash-no-secrets", "unexpectedly missing " NM_SETTING_WIRELESS_SECURITY_KEY_MGMT);
+ ASSERT (g_hash_table_lookup (hash, NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME),
+ "setting-to-hash-no-secrets", "unexpectedly missing " NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME);
+
+ /* Make sure secrets are not there */
+ ASSERT (g_hash_table_lookup (hash, NM_SETTING_WIRELESS_SECURITY_PSK) == NULL,
+ "setting-to-hash-no-secrets", "unexpectedly present " NM_SETTING_WIRELESS_SECURITY_PSK);
+ ASSERT (g_hash_table_lookup (hash, NM_SETTING_WIRELESS_SECURITY_WEP_KEY0) == NULL,
+ "setting-to-hash-no-secrets", "unexpectedly present " NM_SETTING_WIRELESS_SECURITY_WEP_KEY0);
+
+ g_hash_table_destroy (hash);
+ g_object_unref (s_wsec);
+}
+
+static void
+test_setting_to_hash_only_secrets (void)
+{
+ NMSettingWirelessSecurity *s_wsec;
+ GHashTable *hash;
+
+ s_wsec = make_test_wsec_setting ("setting-to-hash-only-secrets");
+
+ hash = nm_setting_to_hash (NM_SETTING (s_wsec), NM_SETTING_HASH_FLAG_ONLY_SECRETS);
+
+ /* Make sure non-secret keys are there */
+ ASSERT (g_hash_table_lookup (hash, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT) == NULL,
+ "setting-to-hash-only-secrets", "unexpectedly present " NM_SETTING_WIRELESS_SECURITY_KEY_MGMT);
+ ASSERT (g_hash_table_lookup (hash, NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME) == NULL,
+ "setting-to-hash-only-secrets", "unexpectedly present " NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME);
+
+ /* Make sure secrets are not there */
+ ASSERT (g_hash_table_lookup (hash, NM_SETTING_WIRELESS_SECURITY_PSK),
+ "setting-to-hash-only-secrets", "unexpectedly missing " NM_SETTING_WIRELESS_SECURITY_PSK);
+ ASSERT (g_hash_table_lookup (hash, NM_SETTING_WIRELESS_SECURITY_WEP_KEY0),
+ "setting-to-hash-only-secrets", "unexpectedly missing " NM_SETTING_WIRELESS_SECURITY_WEP_KEY0);
+
+ g_hash_table_destroy (hash);
+ g_object_unref (s_wsec);
+}
+
+static void
+test_connection_to_hash_setting_name (void)
+{
+ NMConnection *connection;
+ NMSettingWirelessSecurity *s_wsec;
+ GHashTable *hash;
+
+ connection = nm_connection_new ();
+ s_wsec = make_test_wsec_setting ("connection-to-hash-setting-name");
+ nm_connection_add_setting (connection, NM_SETTING (s_wsec));
+
+ hash = nm_connection_to_hash (connection, NM_SETTING_HASH_FLAG_ALL);
+
+ /* Make sure the keys of the first level hash are setting names, not
+ * the GType name of the setting objects.
+ */
+ ASSERT (g_hash_table_lookup (hash, NM_SETTING_WIRELESS_SECURITY_SETTING_NAME) != NULL,
+ "connection-to-hash-setting-name", "unexpectedly missing " NM_SETTING_WIRELESS_SECURITY_SETTING_NAME);
+
+ g_hash_table_destroy (hash);
+ g_object_unref (connection);
+}
+
+static void
+test_setting_new_from_hash (void)
+{
+ NMSettingWirelessSecurity *s_wsec;
+ GHashTable *hash;
+
+ s_wsec = make_test_wsec_setting ("setting-to-hash-all");
+ hash = nm_setting_to_hash (NM_SETTING (s_wsec), NM_SETTING_HASH_FLAG_ALL);
+ g_object_unref (s_wsec);
+
+ s_wsec = (NMSettingWirelessSecurity *) nm_setting_new_from_hash (NM_TYPE_SETTING_WIRELESS_SECURITY, hash);
+ g_hash_table_destroy (hash);
+
+ g_assert (s_wsec);
+ g_assert_cmpstr (nm_setting_wireless_security_get_key_mgmt (s_wsec), ==, "wpa-psk");
+ g_assert_cmpstr (nm_setting_wireless_security_get_leap_username (s_wsec), ==, "foobarbaz");
+ g_assert_cmpstr (nm_setting_wireless_security_get_psk (s_wsec), ==, "random psk");
+ g_object_unref (s_wsec);
+}
+
+static NMConnection *
+new_test_connection (void)
+{
+ NMConnection *connection;
+ NMSetting *setting;
+ char *uuid;
+ guint64 timestamp = time (NULL);
+
+ connection = nm_connection_new ();
+
+ setting = nm_setting_connection_new ();
+ uuid = nm_utils_uuid_generate ();
+ g_object_set (G_OBJECT (setting),
+ NM_SETTING_CONNECTION_ID, "foobar",
+ NM_SETTING_CONNECTION_UUID, uuid,
+ NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME,
+ NM_SETTING_CONNECTION_TIMESTAMP, timestamp,
+ NULL);
+ g_free (uuid);
+ nm_connection_add_setting (connection, setting);
+
+ setting = nm_setting_wired_new ();
+ g_object_set (G_OBJECT (setting),
+ NM_SETTING_WIRED_MTU, 1592,
+ NULL);
+ nm_connection_add_setting (connection, setting);
+
+ setting = nm_setting_ip4_config_new ();
+ g_object_set (G_OBJECT (setting),
+ NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO,
+ NM_SETTING_IP4_CONFIG_DHCP_HOSTNAME, "eyeofthetiger",
+ NULL);
+ nm_connection_add_setting (connection, setting);
+
+ return connection;
+}
+
+static GValue *
+string_to_gvalue (const char *str)
+{
+ GValue *val;
+
+ val = g_slice_new0 (GValue);
+ g_value_init (val, G_TYPE_STRING);
+ g_value_set_string (val, str);
+ return val;
+}
+
+static void
+destroy_gvalue (gpointer data)
+{
+ g_value_unset ((GValue *) data);
+ g_slice_free (GValue, data);
+}
+
+static GHashTable *
+new_connection_hash (char **out_uuid,
+ const char **out_expected_id,
+ const char **out_expected_ip6_method)
+{
+ GHashTable *hash;
+ GHashTable *setting;
+
+ hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_hash_table_destroy);
+
+ *out_uuid = nm_utils_uuid_generate ();
+ *out_expected_id = "My happy connection";
+ *out_expected_ip6_method = NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL;
+
+ /* Connection setting */
+ setting = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, destroy_gvalue);
+ g_hash_table_insert (setting,
+ g_strdup (NM_SETTING_NAME),
+ string_to_gvalue (NM_SETTING_CONNECTION_SETTING_NAME));
+ g_hash_table_insert (setting,
+ g_strdup (NM_SETTING_CONNECTION_ID),
+ string_to_gvalue (*out_expected_id));
+ g_hash_table_insert (setting,
+ g_strdup (NM_SETTING_CONNECTION_UUID),
+ string_to_gvalue (*out_uuid));
+ g_hash_table_insert (setting,
+ g_strdup (NM_SETTING_CONNECTION_TYPE),
+ string_to_gvalue (NM_SETTING_WIRED_SETTING_NAME));
+ g_hash_table_insert (hash, g_strdup (NM_SETTING_CONNECTION_SETTING_NAME), setting);
+
+ /* Wired setting */
+ setting = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, destroy_gvalue);
+ g_hash_table_insert (hash, g_strdup (NM_SETTING_WIRED_SETTING_NAME), setting);
+
+ /* IP6 */
+ setting = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, destroy_gvalue);
+ g_hash_table_insert (setting,
+ g_strdup (NM_SETTING_IP6_CONFIG_METHOD),
+ string_to_gvalue (*out_expected_ip6_method));
+ g_hash_table_insert (hash, g_strdup (NM_SETTING_IP6_CONFIG_SETTING_NAME), setting);
+
+ return hash;
+}
+
+static void
+test_connection_replace_settings ()
+{
+ NMConnection *connection;
+ GHashTable *new_settings;
+ GError *error = NULL;
+ gboolean success;
+ NMSettingConnection *s_con;
+ NMSettingIP6Config *s_ip6;
+ char *uuid = NULL;
+ const char *expected_id = NULL, *expected_method = NULL;
+
+ connection = new_test_connection ();
+
+ new_settings = new_connection_hash (&uuid, &expected_id, &expected_method);
+ g_assert (new_settings);
+
+ /* Replace settings and test */
+ success = nm_connection_replace_settings (connection, new_settings, &error);
+ g_assert_no_error (error);
+ g_assert (success);
+
+ s_con = nm_connection_get_setting_connection (connection);
+ g_assert (s_con);
+ g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, expected_id);
+ g_assert_cmpstr (nm_setting_connection_get_uuid (s_con), ==, uuid);
+
+ g_assert (nm_connection_get_setting_wired (connection));
+ g_assert (!nm_connection_get_setting_ip4_config (connection));
+
+ s_ip6 = nm_connection_get_setting_ip6_config (connection);
+ g_assert (s_ip6);
+ g_assert_cmpstr (nm_setting_ip6_config_get_method (s_ip6), ==, expected_method);
+
+ g_free (uuid);
+ g_hash_table_destroy (new_settings);
+ g_object_unref (connection);
+}
+
+static void
+test_connection_replace_settings_from_connection ()
+{
+ NMConnection *connection, *replacement;
+ GError *error = NULL;
+ gboolean success;
+ NMSettingConnection *s_con;
+ NMSetting *setting;
+ GByteArray *ssid;
+ char *uuid = NULL;
+ const char *expected_id = "Awesome connection";
+
+ connection = new_test_connection ();
+ g_assert (connection);
+
+ replacement = nm_connection_new ();
+ g_assert (replacement);
+
+ /* New connection setting */
+ setting = nm_setting_connection_new ();
+ g_assert (setting);
+
+ uuid = nm_utils_uuid_generate ();
+ g_object_set (setting,
+ NM_SETTING_CONNECTION_ID, expected_id,
+ NM_SETTING_CONNECTION_UUID, uuid,
+ NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRELESS_SETTING_NAME,
+ NULL);
+ nm_connection_add_setting (replacement, setting);
+
+ /* New wifi setting */
+ setting = nm_setting_wireless_new ();
+ g_assert (setting);
+
+ ssid = g_byte_array_new ();
+ g_byte_array_append (ssid, (const guint8 *) "1234567", 7);
+ g_object_set (setting,
+ NM_SETTING_WIRELESS_SSID, ssid,
+ NM_SETTING_WIRELESS_MODE, "infrastructure",
+ NULL);
+ g_byte_array_free (ssid, TRUE);
+ nm_connection_add_setting (replacement, setting);
+
+ /* Replace settings and test */
+ success = nm_connection_replace_settings_from_connection (connection, replacement, &error);
+ g_assert_no_error (error);
+ g_assert (success);
+
+ s_con = nm_connection_get_setting_connection (connection);
+ g_assert (s_con);
+ g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, expected_id);
+ g_assert_cmpstr (nm_setting_connection_get_uuid (s_con), ==, uuid);
+
+ g_assert (!nm_connection_get_setting_wired (connection));
+ g_assert (!nm_connection_get_setting_ip6_config (connection));
+ g_assert (nm_connection_get_setting_wireless (connection));
+
+ g_free (uuid);
+ g_object_unref (replacement);
+ g_object_unref (connection);
+}
+
+static void
+test_connection_new_from_hash ()
+{
+ NMConnection *connection;
+ GHashTable *new_settings;
+ GError *error = NULL;
+ NMSettingConnection *s_con;
+ NMSettingIP6Config *s_ip6;
+ char *uuid = NULL;
+ const char *expected_id = NULL, *expected_method = NULL;
+
+ new_settings = new_connection_hash (&uuid, &expected_id, &expected_method);
+ g_assert (new_settings);
+
+ /* Replace settings and test */
+ connection = nm_connection_new_from_hash (new_settings, &error);
+ g_assert_no_error (error);
+ g_assert (connection);
+
+ s_con = nm_connection_get_setting_connection (connection);
+ g_assert (s_con);
+ g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, expected_id);
+ g_assert_cmpstr (nm_setting_connection_get_uuid (s_con), ==, uuid);
+
+ g_assert (nm_connection_get_setting_wired (connection));
+ g_assert (!nm_connection_get_setting_ip4_config (connection));
+
+ s_ip6 = nm_connection_get_setting_ip6_config (connection);
+ g_assert (s_ip6);
+ g_assert_cmpstr (nm_setting_ip6_config_get_method (s_ip6), ==, expected_method);
+
+ g_free (uuid);
+ g_hash_table_destroy (new_settings);
+ g_object_unref (connection);
+}
+
+static void
+check_permission (NMSettingConnection *s_con,
+ guint32 idx,
+ const char *expected_uname,
+ const char *tag)
+{
+ gboolean success;
+ const char *ptype = NULL, *pitem = NULL, *detail = NULL;
+
+ success = nm_setting_connection_get_permission (s_con, 0, &ptype, &pitem, &detail);
+ ASSERT (success == TRUE, tag, "unexpected failure getting added permission");
+
+ /* Permission type */
+ ASSERT (ptype != NULL, tag, "unexpected failure getting permission type");
+ ASSERT (strcmp (ptype, "user") == 0, tag, "retrieved unexpected permission type");
+
+ /* Permission item */
+ ASSERT (pitem != NULL, tag, "unexpected failure getting permission item");
+ ASSERT (strcmp (pitem, expected_uname) == 0, tag, "retrieved unexpected permission item");
+
+ ASSERT (detail == NULL, tag, "unexpected success getting permission detail");
+}
+
+#define TEST_UNAME "asdfasfasdf"
+
+static void
+test_setting_connection_permissions_helpers (void)
+{
+ NMSettingConnection *s_con;
+ gboolean success;
+ char buf[9] = { 0x61, 0x62, 0x63, 0xff, 0xfe, 0xfd, 0x23, 0x01, 0x00 };
+ GSList *list = NULL;
+ const char *expected_perm = "user:" TEST_UNAME ":";
+
+ s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ());
+
+ /* Ensure a bad [type] is rejected */
+ g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*strcmp (ptype, \"user\") == 0*");
+ success = nm_setting_connection_add_permission (s_con, "foobar", "blah", NULL);
+ g_test_assert_expected_messages ();
+ ASSERT (success == FALSE,
+ "setting-connection-permissions-helpers", "unexpected success adding bad permission type #1");
+
+ /* Ensure a bad [type] is rejected */
+ g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*ptype*");
+ success = nm_setting_connection_add_permission (s_con, NULL, "blah", NULL);
+ g_test_assert_expected_messages ();
+ ASSERT (success == FALSE,
+ "setting-connection-permissions-helpers", "unexpected success adding bad permission type #2");
+
+ /* Ensure a bad [item] is rejected */
+ g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*uname*");
+ g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*p != NULL*");
+ success = nm_setting_connection_add_permission (s_con, "user", NULL, NULL);
+ g_test_assert_expected_messages ();
+ ASSERT (success == FALSE,
+ "setting-connection-permissions-helpers", "unexpected success adding bad permission item #1");
+
+ /* Ensure a bad [item] is rejected */
+ g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*uname[0] != '\\0'*");
+ g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*p != NULL*");
+ success = nm_setting_connection_add_permission (s_con, "user", "", NULL);
+ g_test_assert_expected_messages ();
+ ASSERT (success == FALSE,
+ "setting-connection-permissions-helpers", "unexpected success adding bad permission item #2");
+
+ /* Ensure an [item] with ':' is rejected */
+ g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*strchr (uname, ':')*");
+ g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*p != NULL*");
+ success = nm_setting_connection_add_permission (s_con, "user", "ad:asdf", NULL);
+ g_test_assert_expected_messages ();
+ ASSERT (success == FALSE,
+ "setting-connection-permissions-helpers", "unexpected success adding bad permission item #3");
+
+ /* Ensure a non-UTF-8 [item] is rejected */
+ g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*g_utf8_validate (uname, -1, NULL)*");
+ g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*p != NULL*");
+ success = nm_setting_connection_add_permission (s_con, "user", buf, NULL);
+ g_test_assert_expected_messages ();
+ ASSERT (success == FALSE,
+ "setting-connection-permissions-helpers", "unexpected success adding bad permission item #4");
+
+ /* Ensure a non-NULL [detail] is rejected */
+ g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*detail == NULL*");
+ success = nm_setting_connection_add_permission (s_con, "user", "dafasdf", "asdf");
+ g_test_assert_expected_messages ();
+ ASSERT (success == FALSE,
+ "setting-connection-permissions-helpers", "unexpected success adding bad detail");
+
+ /* Ensure a valid call results in success */
+ success = nm_setting_connection_add_permission (s_con, "user", TEST_UNAME, NULL);
+ ASSERT (success == TRUE,
+ "setting-connection-permissions-helpers", "unexpected failure adding valid user permisson");
+
+ ASSERT (nm_setting_connection_get_num_permissions (s_con) == 1,
+ "setting-connection-permissions-helpers", "unexpected failure getting number of permissions");
+
+ check_permission (s_con, 0, TEST_UNAME, "setting-connection-permissions-helpers");
+
+ /* Check the actual GObject property just to be paranoid */
+ g_object_get (G_OBJECT (s_con), NM_SETTING_CONNECTION_PERMISSIONS, &list, NULL);
+ ASSERT (list != NULL,
+ "setting-connection-permissions-helpers", "unexpected failure getting permissions list");
+ ASSERT (g_slist_length (list) == 1,
+ "setting-connection-permissions-helpers", "unexpected failure getting number of permissions in list");
+ ASSERT (strcmp (list->data, expected_perm) == 0,
+ "setting-connection-permissions-helpers", "unexpected permission property data");
+ g_slist_free_full (list, g_free);
+
+ /* Now remove that permission and ensure we have 0 permissions */
+ nm_setting_connection_remove_permission (s_con, 0);
+ ASSERT (nm_setting_connection_get_num_permissions (s_con) == 0,
+ "setting-connection-permissions-helpers", "unexpected failure removing permission");
+
+ g_object_unref (s_con);
+}
+
+static void
+add_permission_property (NMSettingConnection *s_con,
+ const char *ptype,
+ const char *pitem,
+ int pitem_len,
+ const char *detail)
+{
+ GString *str;
+ GSList *list = NULL;
+
+ str = g_string_sized_new (50);
+ if (ptype)
+ g_string_append (str, ptype);
+ g_string_append_c (str, ':');
+
+ if (pitem) {
+ if (pitem_len >= 0)
+ g_string_append_len (str, pitem, pitem_len);
+ else
+ g_string_append (str, pitem);
+ }
+
+ g_string_append_c (str, ':');
+
+ if (detail)
+ g_string_append (str, detail);
+
+ list = g_slist_append (list, str->str);
+ g_object_set (G_OBJECT (s_con), NM_SETTING_CONNECTION_PERMISSIONS, list, NULL);
+
+ g_string_free (str, TRUE);
+ g_slist_free (list);
+}
+
+static void
+test_setting_connection_permissions_property (void)
+{
+ NMSettingConnection *s_con;
+ gboolean success;
+ char buf[9] = { 0x61, 0x62, 0x63, 0xff, 0xfe, 0xfd, 0x23, 0x01, 0x00 };
+
+ s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ());
+
+ /* Ensure a bad [type] is rejected */
+ g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*strncmp (str, PERM_USER_PREFIX, strlen (PERM_USER_PREFIX)) == 0*");
+ add_permission_property (s_con, "foobar", "blah", -1, NULL);
+ g_test_assert_expected_messages ();
+ ASSERT (nm_setting_connection_get_num_permissions (s_con) == 0,
+ "setting-connection-permissions-property", "unexpected success adding bad permission type #1");
+
+ /* Ensure a bad [type] is rejected */
+ g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*strncmp (str, PERM_USER_PREFIX, strlen (PERM_USER_PREFIX)) == 0*");
+ add_permission_property (s_con, NULL, "blah", -1, NULL);
+ g_test_assert_expected_messages ();
+ ASSERT (nm_setting_connection_get_num_permissions (s_con) == 0,
+ "setting-connection-permissions-property", "unexpected success adding bad permission type #2");
+
+ /* Ensure a bad [item] is rejected */
+ g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*last_colon > str*");
+ add_permission_property (s_con, "user", NULL, -1, NULL);
+ g_test_assert_expected_messages ();
+ ASSERT (nm_setting_connection_get_num_permissions (s_con) == 0,
+ "setting-connection-permissions-property", "unexpected success adding bad permission item #1");
+
+ /* Ensure a bad [item] is rejected */
+ g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*last_colon > str*");
+ add_permission_property (s_con, "user", "", -1, NULL);
+ g_test_assert_expected_messages ();
+ ASSERT (nm_setting_connection_get_num_permissions (s_con) == 0,
+ "setting-connection-permissions-property", "unexpected success adding bad permission item #2");
+
+ /* Ensure an [item] with ':' in the middle is rejected */
+ g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*str[i] != ':'*");
+ add_permission_property (s_con, "user", "ad:asdf", -1, NULL);
+ g_test_assert_expected_messages ();
+ ASSERT (nm_setting_connection_get_num_permissions (s_con) == 0,
+ "setting-connection-permissions-property", "unexpected success adding bad permission item #3");
+
+ /* Ensure an [item] with ':' at the end is rejected */
+ g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*str[i] != ':'*");
+ add_permission_property (s_con, "user", "adasdfaf:", -1, NULL);
+ g_test_assert_expected_messages ();
+ ASSERT (nm_setting_connection_get_num_permissions (s_con) == 0,
+ "setting-connection-permissions-property", "unexpected success adding bad permission item #4");
+
+ /* Ensure a non-UTF-8 [item] is rejected */
+ g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*g_utf8_validate (str, -1, NULL)*");
+ add_permission_property (s_con, "user", buf, (int) sizeof (buf), NULL);
+ g_test_assert_expected_messages ();
+ ASSERT (nm_setting_connection_get_num_permissions (s_con) == 0,
+ "setting-connection-permissions-property", "unexpected success adding bad permission item #5");
+
+ /* Ensure a non-NULL [detail] is rejected */
+ g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*(last_colon + 1) == '\\0'*");
+ add_permission_property (s_con, "user", "dafasdf", -1, "asdf");
+ g_test_assert_expected_messages ();
+ ASSERT (nm_setting_connection_get_num_permissions (s_con) == 0,
+ "setting-connection-permissions-property", "unexpected success adding bad detail");
+
+ /* Ensure a valid call results in success */
+ success = nm_setting_connection_add_permission (s_con, "user", TEST_UNAME, NULL);
+ ASSERT (nm_setting_connection_get_num_permissions (s_con) == 1,
+ "setting-connection-permissions-property", "unexpected failure adding valid user permisson");
+
+ check_permission (s_con, 0, TEST_UNAME, "setting-connection-permissions-property");
+
+ /* Now remove that permission and ensure we have 0 permissions */
+ nm_setting_connection_remove_permission (s_con, 0);
+ ASSERT (nm_setting_connection_get_num_permissions (s_con) == 0,
+ "setting-connection-permissions-property", "unexpected failure removing permission");
+
+ g_object_unref (s_con);
+}
+
+static void
+test_connection_compare_same (void)
+{
+ NMConnection *a, *b;
+
+ a = new_test_connection ();
+ b = nm_connection_duplicate (a);
+ g_assert (nm_connection_compare (a, b, NM_SETTING_COMPARE_FLAG_EXACT));
+ g_object_unref (a);
+ g_object_unref (b);
+}
+
+static void
+test_connection_compare_key_only_in_a (void)
+{
+ NMConnection *a, *b;
+ NMSettingConnection *s_con;
+
+ a = new_test_connection ();
+ b = nm_connection_duplicate (a);
+ s_con = (NMSettingConnection *) nm_connection_get_setting (b, NM_TYPE_SETTING_CONNECTION);
+ g_assert (s_con);
+ g_object_set (s_con, NM_SETTING_CONNECTION_TIMESTAMP, (guint64) 0, NULL);
+
+ g_assert (!nm_connection_compare (a, b, NM_SETTING_COMPARE_FLAG_EXACT));
+ g_object_unref (a);
+ g_object_unref (b);
+}
+
+static void
+test_connection_compare_setting_only_in_a (void)
+{
+ NMConnection *a, *b;
+
+ a = new_test_connection ();
+ b = nm_connection_duplicate (a);
+ nm_connection_remove_setting (b, NM_TYPE_SETTING_IP4_CONFIG);
+ g_assert (!nm_connection_compare (a, b, NM_SETTING_COMPARE_FLAG_EXACT));
+ g_object_unref (a);
+ g_object_unref (b);
+}
+
+static void
+test_connection_compare_key_only_in_b (void)
+{
+ NMConnection *a, *b;
+ NMSettingConnection *s_con;
+
+ a = new_test_connection ();
+ b = nm_connection_duplicate (a);
+ s_con = (NMSettingConnection *) nm_connection_get_setting (b, NM_TYPE_SETTING_CONNECTION);
+ g_assert (s_con);
+ g_object_set (s_con, NM_SETTING_CONNECTION_TIMESTAMP, (guint64) 0, NULL);
+
+ g_assert (!nm_connection_compare (a, b, NM_SETTING_COMPARE_FLAG_EXACT));
+ g_object_unref (a);
+ g_object_unref (b);
+}
+
+static void
+test_connection_compare_setting_only_in_b (void)
+{
+ NMConnection *a, *b;
+
+ a = new_test_connection ();
+ b = nm_connection_duplicate (a);
+ nm_connection_remove_setting (a, NM_TYPE_SETTING_IP4_CONFIG);
+ g_assert (!nm_connection_compare (a, b, NM_SETTING_COMPARE_FLAG_EXACT));
+ g_object_unref (a);
+ g_object_unref (b);
+}
+
+typedef struct {
+ const char *key_name;
+ guint32 result;
+} DiffKey;
+
+typedef struct {
+ const char *name;
+ DiffKey keys[30];
+} DiffSetting;
+
+#define ARRAY_LEN(a) (sizeof (a) / sizeof (a[0]))
+
+static void
+ensure_diffs (GHashTable *diffs, const DiffSetting *check, gsize n_check)
+{
+ guint i;
+
+ g_assert (g_hash_table_size (diffs) == n_check);
+
+ /* Loop through the settings */
+ for (i = 0; i < n_check; i++) {
+ GHashTable *setting_hash;
+ guint z = 0;
+
+ setting_hash = g_hash_table_lookup (diffs, check[i].name);
+ g_assert (setting_hash);
+
+ /* Get the number of keys to check */
+ while (check[i].keys[z].key_name)
+ z++;
+ g_assert (g_hash_table_size (setting_hash) == z);
+
+ /* Now compare the actual keys */
+ for (z = 0; check[i].keys[z].key_name; z++) {
+ NMSettingDiffResult result;
+
+ result = GPOINTER_TO_UINT (g_hash_table_lookup (setting_hash, check[i].keys[z].key_name));
+ g_assert (result == check[i].keys[z].result);
+ }
+ }
+}
+
+static void
+test_connection_diff_a_only (void)
+{
+ NMConnection *connection;
+ GHashTable *out_diffs = NULL;
+ gboolean same;
+ const DiffSetting settings[] = {
+ { NM_SETTING_CONNECTION_SETTING_NAME, {
+ { NM_SETTING_CONNECTION_ID, NM_SETTING_DIFF_RESULT_IN_A },
+ { NM_SETTING_CONNECTION_UUID, NM_SETTING_DIFF_RESULT_IN_A },
+ { NM_SETTING_CONNECTION_INTERFACE_NAME, NM_SETTING_DIFF_RESULT_IN_A },
+ { NM_SETTING_CONNECTION_TYPE, NM_SETTING_DIFF_RESULT_IN_A },
+ { NM_SETTING_CONNECTION_TIMESTAMP, NM_SETTING_DIFF_RESULT_IN_A },
+ { NM_SETTING_CONNECTION_AUTOCONNECT, NM_SETTING_DIFF_RESULT_IN_A },
+ { NM_SETTING_CONNECTION_READ_ONLY, NM_SETTING_DIFF_RESULT_IN_A },
+ { NM_SETTING_CONNECTION_PERMISSIONS, NM_SETTING_DIFF_RESULT_IN_A },
+ { NM_SETTING_CONNECTION_ZONE, NM_SETTING_DIFF_RESULT_IN_A },
+ { NM_SETTING_CONNECTION_MASTER, NM_SETTING_DIFF_RESULT_IN_A },
+ { NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_DIFF_RESULT_IN_A },
+ { NM_SETTING_CONNECTION_SECONDARIES, NM_SETTING_DIFF_RESULT_IN_A },
+ { NM_SETTING_CONNECTION_GATEWAY_PING_TIMEOUT, NM_SETTING_DIFF_RESULT_IN_A },
+ { NULL, NM_SETTING_DIFF_RESULT_UNKNOWN }
+ } },
+ { NM_SETTING_WIRED_SETTING_NAME, {
+ { NM_SETTING_WIRED_PORT, NM_SETTING_DIFF_RESULT_IN_A },
+ { NM_SETTING_WIRED_SPEED, NM_SETTING_DIFF_RESULT_IN_A },
+ { NM_SETTING_WIRED_DUPLEX, NM_SETTING_DIFF_RESULT_IN_A },
+ { NM_SETTING_WIRED_AUTO_NEGOTIATE, NM_SETTING_DIFF_RESULT_IN_A },
+ { NM_SETTING_WIRED_MAC_ADDRESS, NM_SETTING_DIFF_RESULT_IN_A },
+ { NM_SETTING_WIRED_CLONED_MAC_ADDRESS, NM_SETTING_DIFF_RESULT_IN_A },
+ { NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST, NM_SETTING_DIFF_RESULT_IN_A },
+ { NM_SETTING_WIRED_MTU, NM_SETTING_DIFF_RESULT_IN_A },
+ { NM_SETTING_WIRED_S390_SUBCHANNELS, NM_SETTING_DIFF_RESULT_IN_A },
+ { NM_SETTING_WIRED_S390_NETTYPE, NM_SETTING_DIFF_RESULT_IN_A },
+ { NM_SETTING_WIRED_S390_OPTIONS, NM_SETTING_DIFF_RESULT_IN_A },
+ { NULL, NM_SETTING_DIFF_RESULT_UNKNOWN },
+ } },
+ { NM_SETTING_IP4_CONFIG_SETTING_NAME, {
+ { NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_DIFF_RESULT_IN_A },
+ { NM_SETTING_IP4_CONFIG_DNS, NM_SETTING_DIFF_RESULT_IN_A },
+ { NM_SETTING_IP4_CONFIG_DNS_SEARCH, NM_SETTING_DIFF_RESULT_IN_A },
+ { NM_SETTING_IP4_CONFIG_ADDRESSES, NM_SETTING_DIFF_RESULT_IN_A },
+ { "address-labels", NM_SETTING_DIFF_RESULT_IN_A },
+ { NM_SETTING_IP4_CONFIG_ROUTES, NM_SETTING_DIFF_RESULT_IN_A },
+ { NM_SETTING_IP4_CONFIG_IGNORE_AUTO_ROUTES, NM_SETTING_DIFF_RESULT_IN_A },
+ { NM_SETTING_IP4_CONFIG_IGNORE_AUTO_DNS, NM_SETTING_DIFF_RESULT_IN_A },
+ { NM_SETTING_IP4_CONFIG_DHCP_CLIENT_ID, NM_SETTING_DIFF_RESULT_IN_A },
+ { NM_SETTING_IP4_CONFIG_DHCP_SEND_HOSTNAME, NM_SETTING_DIFF_RESULT_IN_A },
+ { NM_SETTING_IP4_CONFIG_DHCP_HOSTNAME, NM_SETTING_DIFF_RESULT_IN_A },
+ { NM_SETTING_IP4_CONFIG_NEVER_DEFAULT, NM_SETTING_DIFF_RESULT_IN_A },
+ { NM_SETTING_IP4_CONFIG_MAY_FAIL, NM_SETTING_DIFF_RESULT_IN_A },
+ { NULL, NM_SETTING_DIFF_RESULT_UNKNOWN },
+ } },
+ };
+
+ connection = new_test_connection ();
+
+ same = nm_connection_diff (connection, NULL, NM_SETTING_COMPARE_FLAG_EXACT, &out_diffs);
+ g_assert (same == FALSE);
+ g_assert (out_diffs != NULL);
+ g_assert (g_hash_table_size (out_diffs) > 0);
+
+ ensure_diffs (out_diffs, settings, ARRAY_LEN (settings));
+
+ g_hash_table_destroy (out_diffs);
+ g_object_unref (connection);
+}
+
+static void
+test_connection_diff_same (void)
+{
+ NMConnection *a, *b;
+ GHashTable *out_diffs = NULL;
+ gboolean same;
+
+ a = new_test_connection ();
+ b = nm_connection_duplicate (a);
+
+ same = nm_connection_diff (a, b, NM_SETTING_COMPARE_FLAG_EXACT, &out_diffs);
+ g_assert (same == TRUE);
+ g_assert (out_diffs == NULL);
+ g_object_unref (a);
+ g_object_unref (b);
+}
+
+static void
+test_connection_diff_different (void)
+{
+ NMConnection *a, *b;
+ GHashTable *out_diffs = NULL;
+ NMSettingIP4Config *s_ip4;
+ gboolean same;
+ const DiffSetting settings[] = {
+ { NM_SETTING_IP4_CONFIG_SETTING_NAME, {
+ { NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_DIFF_RESULT_IN_A | NM_SETTING_DIFF_RESULT_IN_B },
+ { NULL, NM_SETTING_DIFF_RESULT_UNKNOWN },
+ } },
+ };
+
+ a = new_test_connection ();
+ b = nm_connection_duplicate (a);
+ s_ip4 = nm_connection_get_setting_ip4_config (a);
+ g_assert (s_ip4);
+ g_object_set (G_OBJECT (s_ip4),
+ NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_MANUAL,
+ NULL);
+
+ same = nm_connection_diff (a, b, NM_SETTING_COMPARE_FLAG_EXACT, &out_diffs);
+ g_assert (same == FALSE);
+ g_assert (out_diffs != NULL);
+ g_assert (g_hash_table_size (out_diffs) > 0);
+
+ ensure_diffs (out_diffs, settings, ARRAY_LEN (settings));
+
+ g_hash_table_destroy (out_diffs);
+ g_object_unref (a);
+ g_object_unref (b);
+}
+
+static void
+test_connection_diff_no_secrets (void)
+{
+ NMConnection *a, *b;
+ GHashTable *out_diffs = NULL;
+ NMSetting *s_pppoe;
+ gboolean same;
+ const DiffSetting settings[] = {
+ { NM_SETTING_PPPOE_SETTING_NAME, {
+ { NM_SETTING_PPPOE_PASSWORD, NM_SETTING_DIFF_RESULT_IN_B },
+ { NULL, NM_SETTING_DIFF_RESULT_UNKNOWN },
+ } },
+ };
+
+ a = new_test_connection ();
+ s_pppoe = nm_setting_pppoe_new ();
+ g_object_set (G_OBJECT (s_pppoe),
+ NM_SETTING_PPPOE_USERNAME, "thomas",
+ NULL);
+ nm_connection_add_setting (a, s_pppoe);
+
+ b = nm_connection_duplicate (a);
+
+ /* Add a secret to B */
+ s_pppoe = NM_SETTING (nm_connection_get_setting_pppoe (b));
+ g_assert (s_pppoe);
+ g_object_set (G_OBJECT (s_pppoe),
+ NM_SETTING_PPPOE_PASSWORD, "secretpassword",
+ NULL);
+
+ /* Make sure the diff returns no results as secrets are ignored */
+ same = nm_connection_diff (a, b, NM_SETTING_COMPARE_FLAG_IGNORE_SECRETS, &out_diffs);
+ g_assert (same == TRUE);
+ g_assert (out_diffs == NULL);
+
+ /* Now make sure the diff returns results if secrets are not ignored */
+ same = nm_connection_diff (a, b, NM_SETTING_COMPARE_FLAG_EXACT, &out_diffs);
+ g_assert (same == FALSE);
+ g_assert (out_diffs != NULL);
+ g_assert (g_hash_table_size (out_diffs) > 0);
+
+ ensure_diffs (out_diffs, settings, ARRAY_LEN (settings));
+
+ g_hash_table_destroy (out_diffs);
+ g_object_unref (a);
+ g_object_unref (b);
+}
+
+static void
+test_connection_diff_inferrable (void)
+{
+ NMConnection *a, *b;
+ GHashTable *out_diffs = NULL;
+ gboolean same;
+ NMSettingConnection *s_con;
+ NMSettingWired *s_wired;
+ NMSettingIP4Config *s_ip4;
+ char *uuid;
+ const DiffSetting settings[] = {
+ { NM_SETTING_CONNECTION_SETTING_NAME, {
+ { NM_SETTING_CONNECTION_INTERFACE_NAME, NM_SETTING_DIFF_RESULT_IN_A },
+ { NULL, NM_SETTING_DIFF_RESULT_UNKNOWN },
+ } },
+ };
+
+ a = new_test_connection ();
+ b = nm_connection_duplicate (a);
+
+ /* Change the UUID, wired MTU, and set ignore-auto-dns */
+ s_con = nm_connection_get_setting_connection (a);
+ g_assert (s_con);
+ uuid = nm_utils_uuid_generate ();
+ g_object_set (G_OBJECT (s_con),
+ NM_SETTING_CONNECTION_UUID, uuid,
+ NM_SETTING_CONNECTION_ID, "really neat connection",
+ NULL);
+ g_free (uuid);
+
+ s_wired = nm_connection_get_setting_wired (a);
+ g_assert (s_wired);
+ g_object_set (G_OBJECT (s_wired), NM_SETTING_WIRED_MTU, 300, NULL);
+
+ s_ip4 = nm_connection_get_setting_ip4_config (a);
+ g_assert (s_ip4);
+ g_object_set (G_OBJECT (s_ip4), NM_SETTING_IP4_CONFIG_IGNORE_AUTO_DNS, TRUE, NULL);
+
+ /* Make sure the diff returns no results as secrets are ignored */
+ same = nm_connection_diff (a, b, NM_SETTING_COMPARE_FLAG_INFERRABLE, &out_diffs);
+ g_assert (same == TRUE);
+ g_assert (out_diffs == NULL);
+
+ /* And change a INFERRABLE property to ensure that it shows up in the diff results */
+ g_object_set (G_OBJECT (s_con), NM_SETTING_CONNECTION_INTERFACE_NAME, "usb0", NULL);
+
+ /* Make sure the diff returns no results as secrets are ignored */
+ same = nm_connection_diff (a, b, NM_SETTING_COMPARE_FLAG_INFERRABLE, &out_diffs);
+ g_assert (same == FALSE);
+ g_assert (out_diffs != NULL);
+ g_assert (g_hash_table_size (out_diffs) > 0);
+
+ ensure_diffs (out_diffs, settings, ARRAY_LEN (settings));
+
+ g_hash_table_destroy (out_diffs);
+ g_object_unref (a);
+ g_object_unref (b);
+}
+
+static void
+add_generic_settings (NMConnection *connection, const char *ctype)
+{
+ NMSetting *setting;
+ char *uuid;
+
+ uuid = nm_utils_uuid_generate ();
+
+ setting = nm_setting_connection_new ();
+ g_object_set (setting,
+ NM_SETTING_CONNECTION_ID, "asdfasdfadf",
+ NM_SETTING_CONNECTION_TYPE, ctype,
+ NM_SETTING_CONNECTION_UUID, uuid,
+ NULL);
+ nm_connection_add_setting (connection, setting);
+
+ g_free (uuid);
+
+ setting = nm_setting_ip4_config_new ();
+ g_object_set (setting, NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL);
+ nm_connection_add_setting (connection, setting);
+
+ setting = nm_setting_ip6_config_new ();
+ g_object_set (setting, NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_AUTO, NULL);
+ nm_connection_add_setting (connection, setting);
+}
+
+static void
+test_connection_good_base_types (void)
+{
+ NMConnection *connection;
+ NMSetting *setting;
+ gboolean success;
+ GError *error = NULL;
+ GByteArray *array;
+ const guint8 bdaddr[] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66 };
+
+ /* Try a basic wired connection */
+ connection = nm_connection_new ();
+ add_generic_settings (connection, NM_SETTING_WIRED_SETTING_NAME);
+ setting = nm_setting_wired_new ();
+ nm_connection_add_setting (connection, setting);
+
+ success = nm_connection_verify (connection, &error);
+ g_assert_no_error (error);
+ g_assert (success);
+ g_object_unref (connection);
+
+ /* Try a wired PPPoE connection */
+ connection = nm_connection_new ();
+ add_generic_settings (connection, NM_SETTING_PPPOE_SETTING_NAME);
+ setting = nm_setting_pppoe_new ();
+ g_object_set (setting, NM_SETTING_PPPOE_USERNAME, "bob smith", NULL);
+ nm_connection_add_setting (connection, setting);
+
+ success = nm_connection_verify (connection, &error);
+ g_assert_no_error (error);
+ g_assert (success);
+ g_object_unref (connection);
+
+ /* Wifi connection */
+ connection = nm_connection_new ();
+ add_generic_settings (connection, NM_SETTING_WIRELESS_SETTING_NAME);
+
+ setting = nm_setting_wireless_new ();
+ array = g_byte_array_new ();
+ g_byte_array_append (array, (const guint8 *) "1234567", 7);
+ g_object_set (setting,
+ NM_SETTING_WIRELESS_SSID, array,
+ NM_SETTING_WIRELESS_MODE, "infrastructure",
+ NULL);
+ g_byte_array_free (array, TRUE);
+ nm_connection_add_setting (connection, setting);
+
+ success = nm_connection_verify (connection, &error);
+ g_assert_no_error (error);
+ g_assert (success);
+ g_object_unref (connection);
+
+ /* Bluetooth connection */
+ connection = nm_connection_new ();
+ add_generic_settings (connection, NM_SETTING_BLUETOOTH_SETTING_NAME);
+
+ setting = nm_setting_bluetooth_new ();
+ array = g_byte_array_new ();
+ g_byte_array_append (array, bdaddr, sizeof (bdaddr));
+ g_object_set (setting,
+ NM_SETTING_BLUETOOTH_BDADDR, array,
+ NM_SETTING_CONNECTION_TYPE, NM_SETTING_BLUETOOTH_TYPE_PANU,
+ NULL);
+ g_byte_array_free (array, TRUE);
+ nm_connection_add_setting (connection, setting);
+
+ success = nm_connection_verify (connection, &error);
+ g_assert_no_error (error);
+ g_assert (success);
+ g_object_unref (connection);
+
+ /* WiMAX connection */
+ connection = nm_connection_new ();
+ add_generic_settings (connection, NM_SETTING_WIMAX_SETTING_NAME);
+ setting = nm_setting_wimax_new ();
+ g_object_set (setting, NM_SETTING_WIMAX_NETWORK_NAME, "CLEAR", NULL);
+ nm_connection_add_setting (connection, setting);
+
+ success = nm_connection_verify (connection, &error);
+ g_assert_no_error (error);
+ g_assert (success);
+ g_object_unref (connection);
+
+ /* GSM connection */
+ connection = nm_connection_new ();
+ add_generic_settings (connection, NM_SETTING_GSM_SETTING_NAME);
+
+ setting = nm_setting_gsm_new ();
+ g_object_set (setting,
+ NM_SETTING_GSM_NUMBER, "*99#",
+ NM_SETTING_GSM_APN, "metered.billing.sucks",
+ NULL);
+ nm_connection_add_setting (connection, setting);
+
+ /* CDMA connection */
+ connection = nm_connection_new ();
+ add_generic_settings (connection, NM_SETTING_CDMA_SETTING_NAME);
+
+ setting = nm_setting_cdma_new ();
+ g_object_set (setting,
+ NM_SETTING_CDMA_NUMBER, "#777",
+ NM_SETTING_CDMA_USERNAME, "foobar@vzw.com",
+ NULL);
+ nm_connection_add_setting (connection, setting);
+
+ success = nm_connection_verify (connection, &error);
+ g_assert_no_error (error);
+ g_assert (success);
+ g_object_unref (connection);
+}
+
+static void
+test_connection_bad_base_types (void)
+{
+ NMConnection *connection;
+ NMSetting *setting;
+ gboolean success;
+ GError *error = NULL;
+
+ /* Test various non-base connection types to make sure they are rejected;
+ * using a fake 'wired' connection so the rest of it verifies
+ */
+
+ /* Connection setting */
+ connection = nm_connection_new ();
+ add_generic_settings (connection, NM_SETTING_CONNECTION_SETTING_NAME);
+ setting = nm_setting_wired_new ();
+ nm_connection_add_setting (connection, setting);
+
+ success = nm_connection_verify (connection, &error);
+ g_assert_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_CONNECTION_TYPE_INVALID);
+ g_assert (success == FALSE);
+ g_object_unref (connection);
+ g_clear_error (&error);
+
+ /* PPP setting */
+ connection = nm_connection_new ();
+ add_generic_settings (connection, NM_SETTING_PPP_SETTING_NAME);
+ setting = nm_setting_wired_new ();
+ nm_connection_add_setting (connection, setting);
+ setting = nm_setting_ppp_new ();
+ nm_connection_add_setting (connection, setting);
+
+ success = nm_connection_verify (connection, &error);
+ g_assert_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_CONNECTION_TYPE_INVALID);
+ g_assert (success == FALSE);
+ g_object_unref (connection);
+ g_clear_error (&error);
+
+ /* Serial setting */
+ connection = nm_connection_new ();
+ add_generic_settings (connection, NM_SETTING_SERIAL_SETTING_NAME);
+ setting = nm_setting_wired_new ();
+ nm_connection_add_setting (connection, setting);
+ setting = nm_setting_serial_new ();
+ nm_connection_add_setting (connection, setting);
+
+ success = nm_connection_verify (connection, &error);
+ g_assert_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_CONNECTION_TYPE_INVALID);
+ g_assert (success == FALSE);
+ g_object_unref (connection);
+ g_clear_error (&error);
+
+ /* IP4 setting */
+ connection = nm_connection_new ();
+ add_generic_settings (connection, NM_SETTING_IP4_CONFIG_SETTING_NAME);
+ setting = nm_setting_wired_new ();
+ nm_connection_add_setting (connection, setting);
+
+ success = nm_connection_verify (connection, &error);
+ g_assert_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_CONNECTION_TYPE_INVALID);
+ g_assert (success == FALSE);
+ g_object_unref (connection);
+ g_clear_error (&error);
+
+ /* IP6 setting */
+ connection = nm_connection_new ();
+ add_generic_settings (connection, NM_SETTING_IP6_CONFIG_SETTING_NAME);
+ setting = nm_setting_wired_new ();
+ nm_connection_add_setting (connection, setting);
+
+ success = nm_connection_verify (connection, &error);
+ g_assert_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_CONNECTION_TYPE_INVALID);
+ g_assert (success == FALSE);
+ g_object_unref (connection);
+ g_clear_error (&error);
+}
+
+static void
+test_setting_compare_id (void)
+{
+ NMSetting *old, *new;
+ gboolean success;
+
+ old = nm_setting_connection_new ();
+ g_object_set (old,
+ NM_SETTING_CONNECTION_ID, "really awesome cool connection",
+ NM_SETTING_CONNECTION_UUID, "fbbd59d5-acab-4e30-8f86-258d272617e7",
+ NM_SETTING_CONNECTION_AUTOCONNECT, FALSE,
+ NULL);
+
+ new = nm_setting_duplicate (old);
+ g_object_set (new, NM_SETTING_CONNECTION_ID, "some different connection id", NULL);
+
+ /* First make sure they are different */
+ success = nm_setting_compare (old, new, NM_SETTING_COMPARE_FLAG_EXACT);
+ g_assert (success == FALSE);
+
+ success = nm_setting_compare (old, new, NM_SETTING_COMPARE_FLAG_IGNORE_ID);
+ g_assert (success);
+}
+
+static void
+test_setting_compare_secrets (NMSettingSecretFlags secret_flags,
+ NMSettingCompareFlags comp_flags,
+ gboolean remove_secret)
+{
+ NMSetting *old, *new;
+ gboolean success;
+
+ /* Make sure that a connection with transient/unsaved secrets compares
+ * successfully to the same connection without those secrets.
+ */
+
+ old = nm_setting_wireless_security_new ();
+ g_object_set (old,
+ NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-psk",
+ NM_SETTING_WIRELESS_SECURITY_PSK, "really cool psk",
+ NULL);
+ nm_setting_set_secret_flags (old, NM_SETTING_WIRELESS_SECURITY_PSK, secret_flags, NULL);
+
+ /* Clear the PSK from the duplicated setting */
+ new = nm_setting_duplicate (old);
+ if (remove_secret) {
+ g_object_set (new, NM_SETTING_WIRELESS_SECURITY_PSK, NULL, NULL);
+
+ success = nm_setting_compare (old, new, NM_SETTING_COMPARE_FLAG_EXACT);
+ g_assert (success == FALSE);
+ }
+
+ success = nm_setting_compare (old, new, comp_flags);
+ g_assert (success);
+}
+
+static void
+test_setting_compare_vpn_secrets (NMSettingSecretFlags secret_flags,
+ NMSettingCompareFlags comp_flags,
+ gboolean remove_secret)
+{
+ NMSetting *old, *new;
+ gboolean success;
+
+ /* Make sure that a connection with transient/unsaved secrets compares
+ * successfully to the same connection without those secrets.
+ */
+
+ old = nm_setting_vpn_new ();
+ nm_setting_vpn_add_secret (NM_SETTING_VPN (old), "foobarbaz", "really secret password");
+ nm_setting_vpn_add_secret (NM_SETTING_VPN (old), "asdfasdfasdf", "really adfasdfasdfasdf");
+ nm_setting_vpn_add_secret (NM_SETTING_VPN (old), "0123456778", "abcdefghijklmnpqrstuvqxyz");
+ nm_setting_vpn_add_secret (NM_SETTING_VPN (old), "borkbork", "yet another really secret password");
+ nm_setting_set_secret_flags (old, "borkbork", secret_flags, NULL);
+
+ /* Clear "borkbork" from the duplicated setting */
+ new = nm_setting_duplicate (old);
+ if (remove_secret) {
+ nm_setting_vpn_remove_secret (NM_SETTING_VPN (new), "borkbork");
+
+ /* First make sure they are different */
+ success = nm_setting_compare (old, new, NM_SETTING_COMPARE_FLAG_EXACT);
+ g_assert (success == FALSE);
+ }
+
+ success = nm_setting_compare (old, new, comp_flags);
+ g_assert (success);
+}
+
+static void
+test_hwaddr_aton_ether_normal (void)
+{
+ guint8 buf[100];
+ guint8 expected[ETH_ALEN] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 };
+
+ g_assert (nm_utils_hwaddr_aton ("00:11:22:33:44:55", ARPHRD_ETHER, buf) != NULL);
+ g_assert (memcmp (buf, expected, sizeof (expected)) == 0);
+}
+
+static void
+test_hwaddr_aton_ib_normal (void)
+{
+ guint8 buf[100];
+ const char *source = "00:11:22:33:44:55:66:77:88:99:01:12:23:34:45:56:67:78:89:90";
+ guint8 expected[INFINIBAND_ALEN] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66,
+ 0x77, 0x88, 0x99, 0x01, 0x12, 0x23, 0x34, 0x45, 0x56, 0x67, 0x78, 0x89,
+ 0x90 };
+
+ g_assert (nm_utils_hwaddr_aton (source, ARPHRD_INFINIBAND, buf) != NULL);
+ g_assert (memcmp (buf, expected, sizeof (expected)) == 0);
+}
+
+static void
+test_hwaddr_aton_no_leading_zeros (void)
+{
+ guint8 buf[100];
+ guint8 expected[ETH_ALEN] = { 0x00, 0x1A, 0x2B, 0x03, 0x44, 0x05 };
+
+ g_assert (nm_utils_hwaddr_aton ("0:1a:2B:3:44:5", ARPHRD_ETHER, buf) != NULL);
+ g_assert (memcmp (buf, expected, sizeof (expected)) == 0);
+}
+
+static void
+test_hwaddr_aton_malformed (void)
+{
+ guint8 buf[100];
+
+ g_assert (nm_utils_hwaddr_aton ("0:1a:2B:3:a@%%", ARPHRD_ETHER, buf) == NULL);
+}
+
+static void
+test_connection_changed_cb (NMConnection *connection, gboolean *data)
+{
+ *data = TRUE;
+}
+
+static void
+test_ip4_prefix_to_netmask (void)
+{
+ int i;
+
+ for (i = 0; i<=32; i++) {
+ guint32 netmask = nm_utils_ip4_prefix_to_netmask (i);
+ int plen = nm_utils_ip4_netmask_to_prefix (netmask);
+
+ g_assert_cmpint (i, ==, plen);
+ {
+ guint32 msk = 0x80000000;
+ guint32 netmask2 = 0;
+ guint32 prefix = i;
+ while (prefix > 0) {
+ netmask2 |= msk;
+ msk >>= 1;
+ prefix--;
+ }
+ g_assert_cmpint (netmask, ==, (guint32) htonl (netmask2));
+ }
+ }
+}
+
+static void
+test_ip4_netmask_to_prefix (void)
+{
+ int i, j;
+
+ GRand *rand = g_rand_new ();
+
+ g_rand_set_seed (rand, 1);
+
+ for (i = 2; i<=32; i++) {
+ guint32 netmask = nm_utils_ip4_prefix_to_netmask (i);
+ guint32 netmask_lowest_bit = netmask & ~nm_utils_ip4_prefix_to_netmask (i-1);
+
+ g_assert_cmpint (i, ==, nm_utils_ip4_netmask_to_prefix (netmask));
+
+ for (j = 0; j < 2*i; j++) {
+ guint32 r = g_rand_int (rand);
+ guint32 netmask_holey;
+ guint32 prefix_holey;
+
+ netmask_holey = (netmask & r) | netmask_lowest_bit;
+
+ if (netmask_holey == netmask)
+ continue;
+
+ /* create an invalid netmask with holes and check that the function
+ * returns the longest prefix. */
+ prefix_holey = nm_utils_ip4_netmask_to_prefix (netmask_holey);
+
+ g_assert_cmpint (i, ==, prefix_holey);
+ }
+ }
+
+ g_rand_free (rand);
+}
+
+#define ASSERT_CHANGED(statement) \
+{ \
+ changed = FALSE; \
+ statement; \
+ g_assert (changed); \
+}
+
+#define ASSERT_UNCHANGED(statement) \
+{ \
+ changed = FALSE; \
+ statement; \
+ g_assert (!changed); \
+}
+
+static void
+test_connection_changed_signal (void)
+{
+ NMConnection *connection;
+ gboolean changed = FALSE;
+
+ connection = new_test_connection ();
+ g_signal_connect (connection,
+ NM_CONNECTION_CHANGED,
+ (GCallback) test_connection_changed_cb,
+ &changed);
+
+ /* Add new setting */
+ ASSERT_CHANGED (nm_connection_add_setting (connection, nm_setting_vlan_new ()));
+
+ /* Remove existing setting */
+ ASSERT_CHANGED (nm_connection_remove_setting (connection, NM_TYPE_SETTING_VLAN));
+
+ /* Remove non-existing setting */
+ ASSERT_UNCHANGED (nm_connection_remove_setting (connection, NM_TYPE_SETTING_VLAN));
+
+ g_object_unref (connection);
+}
+
+static void
+test_setting_connection_changed_signal (void)
+{
+ NMConnection *connection;
+ gboolean changed = FALSE;
+ NMSettingConnection *s_con;
+ char *uuid;
+
+ connection = nm_connection_new ();
+ g_signal_connect (connection,
+ NM_CONNECTION_CHANGED,
+ (GCallback) test_connection_changed_cb,
+ &changed);
+
+ s_con = (NMSettingConnection *) nm_setting_connection_new ();
+ nm_connection_add_setting (connection, NM_SETTING (s_con));
+
+ ASSERT_CHANGED (g_object_set (s_con, NM_SETTING_CONNECTION_ID, "adfadfasdfaf", NULL));
+
+ ASSERT_CHANGED (nm_setting_connection_add_permission (s_con, "user", "billsmith", NULL));
+ ASSERT_CHANGED (nm_setting_connection_remove_permission (s_con, 0));
+
+ g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*iter != NULL*");
+ ASSERT_UNCHANGED (nm_setting_connection_remove_permission (s_con, 1));
+ g_test_assert_expected_messages ();
+
+ uuid = nm_utils_uuid_generate ();
+ ASSERT_CHANGED (nm_setting_connection_add_secondary (s_con, uuid));
+ ASSERT_CHANGED (nm_setting_connection_remove_secondary (s_con, 0));
+
+ g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*elt != NULL*");
+ ASSERT_UNCHANGED (nm_setting_connection_remove_secondary (s_con, 1));
+ g_test_assert_expected_messages ();
+
+ g_object_unref (connection);
+}
+
+static void
+test_setting_bond_changed_signal (void)
+{
+ NMConnection *connection;
+ gboolean changed = FALSE;
+ NMSettingBond *s_bond;
+
+ connection = nm_connection_new ();
+ g_signal_connect (connection,
+ NM_CONNECTION_CHANGED,
+ (GCallback) test_connection_changed_cb,
+ &changed);
+
+ s_bond = (NMSettingBond *) nm_setting_bond_new ();
+ nm_connection_add_setting (connection, NM_SETTING (s_bond));
+
+ ASSERT_CHANGED (nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_DOWNDELAY, "10"));
+ ASSERT_CHANGED (nm_setting_bond_remove_option (s_bond, NM_SETTING_BOND_OPTION_DOWNDELAY));
+ ASSERT_UNCHANGED (nm_setting_bond_remove_option (s_bond, NM_SETTING_BOND_OPTION_UPDELAY));
+
+ g_object_unref (connection);
+}
+
+static void
+test_setting_ip4_changed_signal (void)
+{
+ NMConnection *connection;
+ gboolean changed = FALSE;
+ NMSettingIP4Config *s_ip4;
+ NMIP4Address *addr;
+ NMIP4Route *route;
+
+ connection = nm_connection_new ();
+ g_signal_connect (connection,
+ NM_CONNECTION_CHANGED,
+ (GCallback) test_connection_changed_cb,
+ &changed);
+
+ s_ip4 = (NMSettingIP4Config *) nm_setting_ip4_config_new ();
+ nm_connection_add_setting (connection, NM_SETTING (s_ip4));
+
+ ASSERT_CHANGED (nm_setting_ip4_config_add_dns (s_ip4, 0x1122));
+ ASSERT_CHANGED (nm_setting_ip4_config_remove_dns (s_ip4, 0));
+
+ g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*i <= priv->dns->len*");
+ ASSERT_UNCHANGED (nm_setting_ip4_config_remove_dns (s_ip4, 1));
+ g_test_assert_expected_messages ();
+
+ nm_setting_ip4_config_add_dns (s_ip4, 0x3344);
+ ASSERT_CHANGED (nm_setting_ip4_config_clear_dns (s_ip4));
+
+ ASSERT_CHANGED (nm_setting_ip4_config_add_dns_search (s_ip4, "foobar.com"));
+ ASSERT_CHANGED (nm_setting_ip4_config_remove_dns_search (s_ip4, 0));
+
+ g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*elt != NULL*");
+ ASSERT_UNCHANGED (nm_setting_ip4_config_remove_dns_search (s_ip4, 1));
+ g_test_assert_expected_messages ();
+
+ ASSERT_CHANGED (nm_setting_ip4_config_add_dns_search (s_ip4, "foobar.com"));
+ ASSERT_CHANGED (nm_setting_ip4_config_clear_dns_searches (s_ip4));
+
+ addr = nm_ip4_address_new ();
+ nm_ip4_address_set_address (addr, 0x2233);
+ nm_ip4_address_set_prefix (addr, 24);
+ ASSERT_CHANGED (nm_setting_ip4_config_add_address (s_ip4, addr));
+ ASSERT_CHANGED (nm_setting_ip4_config_remove_address (s_ip4, 0));
+
+ g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*addr != NULL && label != NULL*");
+ ASSERT_UNCHANGED (nm_setting_ip4_config_remove_address (s_ip4, 1));
+ g_test_assert_expected_messages ();
+
+ nm_setting_ip4_config_add_address (s_ip4, addr);
+ ASSERT_CHANGED (nm_setting_ip4_config_clear_addresses (s_ip4));
+
+ route = nm_ip4_route_new ();
+ nm_ip4_route_set_dest (route, 0x2233);
+ nm_ip4_route_set_prefix (route, 24);
+
+ ASSERT_CHANGED (nm_setting_ip4_config_add_route (s_ip4, route));
+ ASSERT_CHANGED (nm_setting_ip4_config_remove_route (s_ip4, 0));
+
+ g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*elt != NULL*");
+ ASSERT_UNCHANGED (nm_setting_ip4_config_remove_route (s_ip4, 1));
+ g_test_assert_expected_messages ();
+
+ nm_setting_ip4_config_add_route (s_ip4, route);
+ ASSERT_CHANGED (nm_setting_ip4_config_clear_routes (s_ip4));
+
+ nm_ip4_address_unref (addr);
+ nm_ip4_route_unref (route);
+ g_object_unref (connection);
+}
+
+static void
+test_setting_ip6_changed_signal (void)
+{
+ NMConnection *connection;
+ gboolean changed = FALSE;
+ NMSettingIP6Config *s_ip6;
+ NMIP6Address *addr;
+ NMIP6Route *route;
+ const struct in6_addr t = { { { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 } } };
+
+ connection = nm_connection_new ();
+ g_signal_connect (connection,
+ NM_CONNECTION_CHANGED,
+ (GCallback) test_connection_changed_cb,
+ &changed);
+
+ s_ip6 = (NMSettingIP6Config *) nm_setting_ip6_config_new ();
+ nm_connection_add_setting (connection, NM_SETTING (s_ip6));
+
+ ASSERT_CHANGED (nm_setting_ip6_config_add_dns (s_ip6, &t));
+ ASSERT_CHANGED (nm_setting_ip6_config_remove_dns (s_ip6, 0));
+
+ g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*elt != NULL*");
+ ASSERT_UNCHANGED (nm_setting_ip6_config_remove_dns (s_ip6, 1));
+ g_test_assert_expected_messages ();
+
+ nm_setting_ip6_config_add_dns (s_ip6, &t);
+ ASSERT_CHANGED (nm_setting_ip6_config_clear_dns (s_ip6));
+
+ ASSERT_CHANGED (nm_setting_ip6_config_add_dns_search (s_ip6, "foobar.com"));
+ ASSERT_CHANGED (nm_setting_ip6_config_remove_dns_search (s_ip6, 0));
+
+ g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*elt != NULL*");
+ ASSERT_UNCHANGED (nm_setting_ip6_config_remove_dns_search (s_ip6, 1));
+ g_test_assert_expected_messages ();
+
+ nm_setting_ip6_config_add_dns_search (s_ip6, "foobar.com");
+ ASSERT_CHANGED (nm_setting_ip6_config_clear_dns_searches (s_ip6));
+
+ addr = nm_ip6_address_new ();
+ nm_ip6_address_set_address (addr, &t);
+ nm_ip6_address_set_prefix (addr, 64);
+
+ ASSERT_CHANGED (nm_setting_ip6_config_add_address (s_ip6, addr));
+ ASSERT_CHANGED (nm_setting_ip6_config_remove_address (s_ip6, 0));
+
+ g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*elt != NULL*");
+ ASSERT_UNCHANGED (nm_setting_ip6_config_remove_address (s_ip6, 1));
+ g_test_assert_expected_messages ();
+
+ nm_setting_ip6_config_add_address (s_ip6, addr);
+ ASSERT_CHANGED (nm_setting_ip6_config_clear_addresses (s_ip6));
+
+ route = nm_ip6_route_new ();
+ nm_ip6_route_set_dest (route, &t);
+ nm_ip6_route_set_prefix (route, 128);
+
+ ASSERT_CHANGED (nm_setting_ip6_config_add_route (s_ip6, route));
+ ASSERT_CHANGED (nm_setting_ip6_config_remove_route (s_ip6, 0));
+
+ g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*elt != NULL*");
+ ASSERT_UNCHANGED (nm_setting_ip6_config_remove_route (s_ip6, 1));
+ g_test_assert_expected_messages ();
+
+ nm_setting_ip6_config_add_route (s_ip6, route);
+ ASSERT_CHANGED (nm_setting_ip6_config_clear_routes (s_ip6));
+
+ nm_ip6_address_unref (addr);
+ nm_ip6_route_unref (route);
+ g_object_unref (connection);
+}
+
+static void
+test_setting_vlan_changed_signal (void)
+{
+ NMConnection *connection;
+ gboolean changed = FALSE;
+ NMSettingVlan *s_vlan;
+
+ connection = nm_connection_new ();
+ g_signal_connect (connection,
+ NM_CONNECTION_CHANGED,
+ (GCallback) test_connection_changed_cb,
+ &changed);
+
+ s_vlan = (NMSettingVlan *) nm_setting_vlan_new ();
+ nm_connection_add_setting (connection, NM_SETTING (s_vlan));
+
+ ASSERT_CHANGED (nm_setting_vlan_add_priority (s_vlan, NM_VLAN_INGRESS_MAP, 1, 3));
+ ASSERT_CHANGED (nm_setting_vlan_remove_priority (s_vlan, NM_VLAN_INGRESS_MAP, 0));
+ g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*idx < g_slist_length (list)*");
+ ASSERT_UNCHANGED (nm_setting_vlan_remove_priority (s_vlan, NM_VLAN_INGRESS_MAP, 1));
+ g_test_assert_expected_messages ();
+ ASSERT_CHANGED (nm_setting_vlan_add_priority_str (s_vlan, NM_VLAN_INGRESS_MAP, "1:3"));
+ ASSERT_CHANGED (nm_setting_vlan_clear_priorities (s_vlan, NM_VLAN_INGRESS_MAP));
+
+ ASSERT_CHANGED (nm_setting_vlan_add_priority (s_vlan, NM_VLAN_EGRESS_MAP, 1, 3));
+ ASSERT_CHANGED (nm_setting_vlan_remove_priority (s_vlan, NM_VLAN_EGRESS_MAP, 0));
+ g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*idx < g_slist_length (list)*");
+ ASSERT_UNCHANGED (nm_setting_vlan_remove_priority (s_vlan, NM_VLAN_EGRESS_MAP, 1));
+ g_test_assert_expected_messages ();
+ ASSERT_CHANGED (nm_setting_vlan_add_priority_str (s_vlan, NM_VLAN_EGRESS_MAP, "1:3"));
+ ASSERT_CHANGED (nm_setting_vlan_clear_priorities (s_vlan, NM_VLAN_EGRESS_MAP));
+
+ g_object_unref (connection);
+}
+
+static void
+test_setting_vpn_changed_signal (void)
+{
+ NMConnection *connection;
+ gboolean changed = FALSE;
+ NMSettingVPN *s_vpn;
+
+ connection = nm_connection_new ();
+ g_signal_connect (connection,
+ NM_CONNECTION_CHANGED,
+ (GCallback) test_connection_changed_cb,
+ &changed);
+
+ s_vpn = (NMSettingVPN *) nm_setting_vpn_new ();
+ nm_connection_add_setting (connection, NM_SETTING (s_vpn));
+
+ ASSERT_CHANGED (nm_setting_vpn_add_data_item (s_vpn, "foobar", "baz"));
+ ASSERT_CHANGED (nm_setting_vpn_remove_data_item (s_vpn, "foobar"));
+ ASSERT_UNCHANGED (nm_setting_vpn_remove_data_item (s_vpn, "not added"));
+
+ ASSERT_CHANGED (nm_setting_vpn_add_secret (s_vpn, "foobar", "baz"));
+ ASSERT_CHANGED (nm_setting_vpn_remove_secret (s_vpn, "foobar"));
+ ASSERT_UNCHANGED (nm_setting_vpn_remove_secret (s_vpn, "not added"));
+
+ g_object_unref (connection);
+}
+
+static void
+test_setting_wired_changed_signal (void)
+{
+ NMConnection *connection;
+ gboolean changed = FALSE;
+ NMSettingWired *s_wired;
+
+ connection = nm_connection_new ();
+ g_signal_connect (connection,
+ NM_CONNECTION_CHANGED,
+ (GCallback) test_connection_changed_cb,
+ &changed);
+
+ s_wired = (NMSettingWired *) nm_setting_wired_new ();
+ nm_connection_add_setting (connection, NM_SETTING (s_wired));
+
+ ASSERT_CHANGED (nm_setting_wired_add_s390_option (s_wired, "portno", "1"));
+ ASSERT_CHANGED (nm_setting_wired_remove_s390_option (s_wired, "portno"));
+ ASSERT_UNCHANGED (nm_setting_wired_remove_s390_option (s_wired, "layer2"));
+
+ g_object_unref (connection);
+}
+
+static void
+test_setting_wireless_changed_signal (void)
+{
+ NMConnection *connection;
+ gboolean changed = FALSE;
+ NMSettingWireless *s_wifi;
+
+ connection = nm_connection_new ();
+ g_signal_connect (connection,
+ NM_CONNECTION_CHANGED,
+ (GCallback) test_connection_changed_cb,
+ &changed);
+
+ s_wifi = (NMSettingWireless *) nm_setting_wireless_new ();
+ nm_connection_add_setting (connection, NM_SETTING (s_wifi));
+
+ ASSERT_CHANGED (nm_setting_wireless_add_seen_bssid (s_wifi, "00:11:22:33:44:55"));
+
+ g_object_unref (connection);
+}
+
+static void
+test_setting_wireless_security_changed_signal (void)
+{
+ NMConnection *connection;
+ gboolean changed = FALSE;
+ NMSettingWirelessSecurity *s_wsec;
+
+ connection = nm_connection_new ();
+ g_signal_connect (connection,
+ NM_CONNECTION_CHANGED,
+ (GCallback) test_connection_changed_cb,
+ &changed);
+
+ s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new ();
+ nm_connection_add_setting (connection, NM_SETTING (s_wsec));
+
+ /* Protos */
+ ASSERT_CHANGED (nm_setting_wireless_security_add_proto (s_wsec, "wpa"));
+ ASSERT_CHANGED (nm_setting_wireless_security_remove_proto (s_wsec, 0));
+ g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*elt != NULL*");
+ ASSERT_UNCHANGED (nm_setting_wireless_security_remove_proto (s_wsec, 1));
+ g_test_assert_expected_messages ();
+
+ nm_setting_wireless_security_add_proto (s_wsec, "wep");
+ ASSERT_CHANGED (nm_setting_wireless_security_clear_protos (s_wsec));
+
+ /* Pairwise ciphers */
+ ASSERT_CHANGED (nm_setting_wireless_security_add_pairwise (s_wsec, "tkip"));
+ ASSERT_CHANGED (nm_setting_wireless_security_remove_pairwise (s_wsec, 0));
+ g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*elt != NULL*");
+ ASSERT_UNCHANGED (nm_setting_wireless_security_remove_pairwise (s_wsec, 1));
+ g_test_assert_expected_messages ();
+
+ nm_setting_wireless_security_add_pairwise (s_wsec, "tkip");
+ ASSERT_CHANGED (nm_setting_wireless_security_clear_pairwise (s_wsec));
+
+ /* Group ciphers */
+ ASSERT_CHANGED (nm_setting_wireless_security_add_group (s_wsec, "ccmp"));
+ ASSERT_CHANGED (nm_setting_wireless_security_remove_group (s_wsec, 0));
+ g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*elt != NULL*");
+ ASSERT_UNCHANGED (nm_setting_wireless_security_remove_group (s_wsec, 1));
+ g_test_assert_expected_messages ();
+
+ nm_setting_wireless_security_add_group (s_wsec, "tkip");
+ ASSERT_CHANGED (nm_setting_wireless_security_clear_groups (s_wsec));
+
+ /* WEP key secret flags */
+ ASSERT_CHANGED (g_assert (nm_setting_set_secret_flags (NM_SETTING (s_wsec), "wep-key0", NM_SETTING_SECRET_FLAG_AGENT_OWNED, NULL)));
+ ASSERT_CHANGED (g_assert (nm_setting_set_secret_flags (NM_SETTING (s_wsec), "wep-key1", NM_SETTING_SECRET_FLAG_AGENT_OWNED, NULL)));
+ ASSERT_CHANGED (g_assert (nm_setting_set_secret_flags (NM_SETTING (s_wsec), "wep-key2", NM_SETTING_SECRET_FLAG_AGENT_OWNED, NULL)));
+ ASSERT_CHANGED (g_assert (nm_setting_set_secret_flags (NM_SETTING (s_wsec), "wep-key3", NM_SETTING_SECRET_FLAG_AGENT_OWNED, NULL)));
+
+ g_object_unref (connection);
+}
+
+static void
+test_setting_802_1x_changed_signal (void)
+{
+ NMConnection *connection;
+ gboolean changed = FALSE;
+ NMSetting8021x *s_8021x;
+
+ connection = nm_connection_new ();
+ g_signal_connect (connection,
+ NM_CONNECTION_CHANGED,
+ (GCallback) test_connection_changed_cb,
+ &changed);
+
+ s_8021x = (NMSetting8021x *) nm_setting_802_1x_new ();
+ nm_connection_add_setting (connection, NM_SETTING (s_8021x));
+
+ /* EAP methods */
+ ASSERT_CHANGED (nm_setting_802_1x_add_eap_method (s_8021x, "tls"));
+ ASSERT_CHANGED (nm_setting_802_1x_remove_eap_method (s_8021x, 0));
+ g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*elt != NULL*");
+ ASSERT_UNCHANGED (nm_setting_802_1x_remove_eap_method (s_8021x, 1));
+ g_test_assert_expected_messages ();
+
+ nm_setting_802_1x_add_eap_method (s_8021x, "ttls");
+ ASSERT_CHANGED (nm_setting_802_1x_clear_eap_methods (s_8021x));
+
+ /* alternate subject matches */
+ ASSERT_CHANGED (nm_setting_802_1x_add_altsubject_match (s_8021x, "EMAIL:server@example.com"));
+ ASSERT_CHANGED (nm_setting_802_1x_remove_altsubject_match (s_8021x, 0));
+ g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*elt != NULL*");
+ ASSERT_UNCHANGED (nm_setting_802_1x_remove_altsubject_match (s_8021x, 1));
+ g_test_assert_expected_messages ();
+
+ nm_setting_802_1x_add_altsubject_match (s_8021x, "EMAIL:server@example.com");
+ ASSERT_CHANGED (nm_setting_802_1x_clear_altsubject_matches (s_8021x));
+
+ /* phase2 alternate subject matches */
+ ASSERT_CHANGED (nm_setting_802_1x_add_phase2_altsubject_match (s_8021x, "EMAIL:server@example.com"));
+ ASSERT_CHANGED (nm_setting_802_1x_remove_phase2_altsubject_match (s_8021x, 0));
+ g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL, "*elt != NULL*");
+ ASSERT_UNCHANGED (nm_setting_802_1x_remove_phase2_altsubject_match (s_8021x, 1));
+ g_test_assert_expected_messages ();
+
+ nm_setting_802_1x_add_phase2_altsubject_match (s_8021x, "EMAIL:server@example.com");
+ ASSERT_CHANGED (nm_setting_802_1x_clear_phase2_altsubject_matches (s_8021x));
+
+ g_object_unref (connection);
+}
+
+static void
+test_setting_old_uuid (void)
+{
+ GError *error = NULL;
+ NMSetting *setting;
+ gboolean success;
+
+ /* NetworkManager-0.9.4.0 generated 40-character UUIDs with no dashes,
+ * like this one. Test that we maintain compatibility. */
+ const char *uuid = "f43bec2cdd60e5da381ebb1eb1fa39f3cc52660c";
+
+ setting = nm_setting_connection_new ();
+ g_object_set (G_OBJECT (setting),
+ NM_SETTING_CONNECTION_ID, "uuidtest",
+ NM_SETTING_CONNECTION_UUID, uuid,
+ NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME,
+ NULL);
+
+ success = nm_setting_verify (NM_SETTING (setting), NULL, &error);
+ g_assert_no_error (error);
+ g_assert (success == TRUE);
+}
+
+/*
+ * nm_connection_verify() modifies the connection by setting
+ * the interface-name property to the virtual_iface_name of
+ * the type specific settings.
+ *
+ * It would be preferable of verify() not to touch the connection,
+ * but as it is now, stick with it and test it.
+ **/
+static void
+test_connection_verify_sets_interface_name (void)
+{
+ NMConnection *con;
+ NMSettingConnection *s_con;
+ NMSettingBond *s_bond;
+ GError *error = NULL;
+ gboolean success;
+
+ s_con = (NMSettingConnection *) nm_setting_connection_new ();
+ g_object_set (G_OBJECT (s_con),
+ NM_SETTING_CONNECTION_ID, "test1",
+ NM_SETTING_CONNECTION_UUID, "22001632-bbb4-4616-b277-363dce3dfb5b",
+ NM_SETTING_CONNECTION_TYPE, NM_SETTING_BOND_SETTING_NAME,
+ NULL);
+ s_bond = (NMSettingBond *) nm_setting_bond_new ();
+ g_object_set (G_OBJECT (s_bond),
+ NM_SETTING_BOND_INTERFACE_NAME, "bond-x",
+ NULL);
+
+ con = nm_connection_new ();
+ nm_connection_add_setting (con, NM_SETTING (s_con));
+ nm_connection_add_setting (con, NM_SETTING (s_bond));
+
+ g_assert_cmpstr (nm_connection_get_interface_name (con), ==, NULL);
+
+ /* for backward compatiblity, normalizes the interface name */
+ success = nm_connection_verify (con, &error);
+ g_assert (success && !error);
+
+ g_assert_cmpstr (nm_connection_get_interface_name (con), ==, "bond-x");
+
+ g_object_unref (con);
+}
+
+/*
+ * Test normalization of interface-name
+ **/
+static void
+test_connection_normalize_virtual_iface_name (void)
+{
+ NMConnection *con;
+ NMSettingConnection *s_con;
+ NMSettingVlan *s_vlan;
+ NMSetting *setting;
+ GError *error = NULL;
+ gboolean success;
+ const char *IFACE_NAME = "iface";
+ const char *IFACE_VIRT = "iface-X";
+ gboolean modified = FALSE;
+
+ con = nm_connection_new ();
+
+ setting = nm_setting_ip4_config_new ();
+ g_object_set (setting,
+ NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO,
+ NULL);
+ nm_connection_add_setting (con, setting);
+
+ setting = nm_setting_ip6_config_new ();
+ g_object_set (setting,
+ NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_AUTO,
+ NM_SETTING_IP6_CONFIG_MAY_FAIL, TRUE,
+ NULL);
+ nm_connection_add_setting (con, setting);
+
+ s_con = (NMSettingConnection *) nm_setting_connection_new ();
+ g_object_set (G_OBJECT (s_con),
+ NM_SETTING_CONNECTION_ID, "test1",
+ NM_SETTING_CONNECTION_UUID, "22001632-bbb4-4616-b277-363dce3dfb5b",
+ NM_SETTING_CONNECTION_TYPE, NM_SETTING_VLAN_SETTING_NAME,
+ NM_SETTING_CONNECTION_INTERFACE_NAME, IFACE_NAME,
+ NULL);
+ s_vlan = (NMSettingVlan *) nm_setting_vlan_new ();
+ g_object_set (G_OBJECT (s_vlan),
+ NM_SETTING_VLAN_INTERFACE_NAME, IFACE_VIRT,
+ NM_SETTING_VLAN_PARENT, "eth0",
+ NULL);
+
+ nm_connection_add_setting (con, NM_SETTING (s_con));
+ nm_connection_add_setting (con, NM_SETTING (s_vlan));
+
+ g_assert_cmpstr (nm_connection_get_interface_name (con), ==, IFACE_NAME);
+ g_assert_cmpstr (nm_setting_vlan_get_interface_name (s_vlan), ==, IFACE_VIRT);
+
+ /* for backward compatiblity, normalizes the interface name */
+ success = nm_connection_verify (con, &error);
+ g_assert (success && !error);
+
+ g_assert_cmpstr (nm_connection_get_interface_name (con), ==, IFACE_NAME);
+ g_assert_cmpstr (nm_setting_vlan_get_interface_name (s_vlan), ==, IFACE_VIRT);
+
+ success = nm_connection_normalize (con, NULL, &modified, &error);
+ g_assert (success && !error);
+ g_assert (modified);
+
+ g_assert_cmpstr (nm_connection_get_interface_name (con), ==, IFACE_NAME);
+ g_assert_cmpstr (nm_setting_vlan_get_interface_name (s_vlan), ==, IFACE_NAME);
+
+ success = nm_connection_verify (con, &error);
+ g_assert (success && !error);
+
+ g_object_unref (con);
+}
+
+NMTST_DEFINE ();
+
+int main (int argc, char **argv)
+{
+ char *base;
+
+ nmtst_init (&argc, &argv, TRUE);
+
+ /* The tests */
+ test_setting_vpn_items ();
+ test_setting_vpn_update_secrets ();
+ test_setting_vpn_modify_during_foreach ();
+ test_setting_ip4_config_labels ();
+ test_setting_ip6_config_old_address_array ();
+ test_setting_gsm_apn_spaces ();
+ test_setting_gsm_apn_bad_chars ();
+ test_setting_gsm_apn_underscore ();
+ test_setting_gsm_without_number ();
+ test_setting_to_hash_all ();
+ test_setting_to_hash_no_secrets ();
+ test_setting_to_hash_only_secrets ();
+ test_setting_compare_id ();
+ test_setting_compare_secrets (NM_SETTING_SECRET_FLAG_AGENT_OWNED, NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS, TRUE);
+ test_setting_compare_secrets (NM_SETTING_SECRET_FLAG_NOT_SAVED, NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS, TRUE);
+ test_setting_compare_secrets (NM_SETTING_SECRET_FLAG_NONE, NM_SETTING_COMPARE_FLAG_IGNORE_SECRETS, TRUE);
+ test_setting_compare_secrets (NM_SETTING_SECRET_FLAG_NONE, NM_SETTING_COMPARE_FLAG_EXACT, FALSE);
+ test_setting_compare_vpn_secrets (NM_SETTING_SECRET_FLAG_AGENT_OWNED, NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS, TRUE);
+ test_setting_compare_vpn_secrets (NM_SETTING_SECRET_FLAG_NOT_SAVED, NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS, TRUE);
+ test_setting_compare_vpn_secrets (NM_SETTING_SECRET_FLAG_NONE, NM_SETTING_COMPARE_FLAG_IGNORE_SECRETS, TRUE);
+ test_setting_compare_vpn_secrets (NM_SETTING_SECRET_FLAG_NONE, NM_SETTING_COMPARE_FLAG_EXACT, FALSE);
+ test_setting_old_uuid ();
+
+ test_connection_to_hash_setting_name ();
+ test_setting_new_from_hash ();
+ test_connection_replace_settings ();
+ test_connection_replace_settings_from_connection ();
+ test_connection_new_from_hash ();
+ test_connection_verify_sets_interface_name ();
+ test_connection_normalize_virtual_iface_name ();
+
+ test_setting_connection_permissions_helpers ();
+ test_setting_connection_permissions_property ();
+
+ test_connection_compare_same ();
+ test_connection_compare_key_only_in_a ();
+ test_connection_compare_setting_only_in_a ();
+ test_connection_compare_key_only_in_b ();
+ test_connection_compare_setting_only_in_b ();
+
+ test_connection_diff_a_only ();
+ test_connection_diff_same ();
+ test_connection_diff_different ();
+ test_connection_diff_no_secrets ();
+ test_connection_diff_inferrable ();
+ test_connection_good_base_types ();
+ test_connection_bad_base_types ();
+
+ test_hwaddr_aton_ether_normal ();
+ test_hwaddr_aton_ib_normal ();
+ test_hwaddr_aton_no_leading_zeros ();
+ test_hwaddr_aton_malformed ();
+ test_ip4_prefix_to_netmask ();
+ test_ip4_netmask_to_prefix ();
+
+ test_connection_changed_signal ();
+ test_setting_connection_changed_signal ();
+ test_setting_bond_changed_signal ();
+ test_setting_ip4_changed_signal ();
+ test_setting_ip6_changed_signal ();
+ test_setting_vlan_changed_signal ();
+ test_setting_vpn_changed_signal ();
+ test_setting_wired_changed_signal ();
+ test_setting_wireless_changed_signal ();
+ test_setting_wireless_security_changed_signal ();
+ test_setting_802_1x_changed_signal ();
+
+ base = g_path_get_basename (argv[0]);
+ fprintf (stdout, "%s: SUCCESS\n", base);
+ g_free (base);
+ return 0;
+}
+
diff --git a/libnm-core/tests/test-secrets.c b/libnm-core/tests/test-secrets.c
new file mode 100644
index 0000000000..b829d12eed
--- /dev/null
+++ b/libnm-core/tests/test-secrets.c
@@ -0,0 +1,754 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2008 - 2011 Red Hat, Inc.
+ *
+ */
+
+#include <glib.h>
+#include <string.h>
+
+#include <nm-utils.h>
+
+#include "nm-setting-connection.h"
+#include "nm-setting-wired.h"
+#include "nm-setting-8021x.h"
+#include "nm-setting-ip4-config.h"
+#include "nm-setting-wireless.h"
+#include "nm-setting-wireless-security.h"
+#include "nm-setting-cdma.h"
+#include "nm-setting-gsm.h"
+#include "nm-setting-ppp.h"
+#include "nm-setting-pppoe.h"
+#include "nm-setting-vpn.h"
+
+#include "nm-test-utils.h"
+
+#define TEST_NEED_SECRETS_EAP_TLS_CA_CERT TEST_CERT_DIR "/test_ca_cert.pem"
+#define TEST_NEED_SECRETS_EAP_TLS_CLIENT_CERT TEST_CERT_DIR "/test_key_and_cert.pem"
+#define TEST_NEED_SECRETS_EAP_TLS_PRIVATE_KEY TEST_CERT_DIR "/test_key_and_cert.pem"
+
+static gboolean
+find_hints_item (GPtrArray *hints, const char *item)
+{
+ int i;
+
+ for (i = 0; i < hints->len; i++) {
+ if (!strcmp (item, (const char *) g_ptr_array_index (hints, i)))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static NMConnection *
+make_tls_connection (const char *detail, NMSetting8021xCKScheme scheme)
+{
+ NMConnection *connection;
+ NMSettingConnection *s_con;
+ NMSetting8021x *s_8021x;
+ NMSettingWired *s_wired;
+ NMSettingIP4Config *s_ip4;
+ char *uuid;
+ gboolean success;
+ GError *error = NULL;
+
+ connection = nm_connection_new ();
+
+ /* Connection setting */
+ s_con = (NMSettingConnection *) nm_setting_connection_new ();
+ nm_connection_add_setting (connection, NM_SETTING (s_con));
+
+ uuid = nm_utils_uuid_generate ();
+ g_object_set (s_con,
+ NM_SETTING_CONNECTION_ID, "Test Need TLS Secrets",
+ NM_SETTING_CONNECTION_UUID, uuid,
+ NM_SETTING_CONNECTION_AUTOCONNECT, TRUE,
+ NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME,
+ NULL);
+ g_free (uuid);
+
+ /* Wired setting */
+ s_wired = (NMSettingWired *) nm_setting_wired_new ();
+ nm_connection_add_setting (connection, NM_SETTING (s_wired));
+
+ /* Wireless security setting */
+ s_8021x = (NMSetting8021x *) nm_setting_802_1x_new ();
+ nm_connection_add_setting (connection, NM_SETTING (s_8021x));
+
+ g_object_set (s_8021x, NM_SETTING_802_1X_IDENTITY, "Bill Smith", NULL);
+
+ nm_setting_802_1x_add_eap_method (s_8021x, "tls");
+
+ success = nm_setting_802_1x_set_ca_cert (s_8021x,
+ TEST_NEED_SECRETS_EAP_TLS_CA_CERT,
+ scheme,
+ NULL,
+ &error);
+ ASSERT (success == TRUE,
+ detail, "failed to set CA certificate '%s': %s",
+ TEST_NEED_SECRETS_EAP_TLS_CA_CERT, error->message);
+
+ success = nm_setting_802_1x_set_client_cert (s_8021x,
+ TEST_NEED_SECRETS_EAP_TLS_CLIENT_CERT,
+ scheme,
+ NULL,
+ &error);
+ ASSERT (success == TRUE,
+ detail, "failed to set client certificate '%s': %s",
+ TEST_NEED_SECRETS_EAP_TLS_CLIENT_CERT, error->message);
+
+ success = nm_setting_802_1x_set_private_key (s_8021x,
+ TEST_NEED_SECRETS_EAP_TLS_PRIVATE_KEY,
+ "test",
+ scheme,
+ NULL,
+ &error);
+ ASSERT (success == TRUE,
+ detail, "failed to set private key '%s': %s",
+ TEST_NEED_SECRETS_EAP_TLS_PRIVATE_KEY, error->message);
+
+ /* IP4 setting */
+ s_ip4 = (NMSettingIP4Config *) nm_setting_ip4_config_new ();
+ nm_connection_add_setting (connection, NM_SETTING (s_ip4));
+
+ g_object_set (s_ip4, NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL);
+
+ ASSERT (nm_connection_verify (connection, &error) == TRUE,
+ detail, "failed to verify connection: %s",
+ (error && error->message) ? error->message : "(unknown)");
+
+ return connection;
+}
+
+static void
+test_need_tls_secrets_path (void)
+{
+ NMConnection *connection;
+ const char *setting_name;
+ GPtrArray *hints = NULL;
+
+ connection = make_tls_connection ("need-tls-secrets-path-key", NM_SETTING_802_1X_CK_SCHEME_PATH);
+ ASSERT (connection != NULL,
+ "need-tls-secrets-path-key",
+ "error creating test connection");
+
+ /* Ensure we don't need any secrets since we just set up the connection */
+ setting_name = nm_connection_need_secrets (connection, &hints);
+ ASSERT (setting_name == NULL,
+ "need-tls-secrets-path-key",
+ "secrets are unexpectedly required");
+ ASSERT (hints == NULL,
+ "need-tls-secrets-path-key",
+ "hints should be NULL since no secrets were required");
+
+ /* Connection is good; clear secrets and ensure private key password is then required */
+ nm_connection_clear_secrets (connection);
+
+ hints = NULL;
+ setting_name = nm_connection_need_secrets (connection, &hints);
+ ASSERT (setting_name != NULL,
+ "need-tls-secrets-path-key-password",
+ "unexpected secrets success");
+ ASSERT (strcmp (setting_name, NM_SETTING_802_1X_SETTING_NAME) == 0,
+ "need-tls-secrets-path-key-password",
+ "unexpected setting secrets required");
+
+ ASSERT (hints != NULL,
+ "need-tls-secrets-path-key-password",
+ "expected returned secrets hints");
+ ASSERT (find_hints_item (hints, NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD),
+ "need-tls-secrets-path-key-password",
+ "expected to require private key password, but it wasn't");
+
+ g_object_unref (connection);
+}
+
+static void
+test_need_tls_secrets_blob (void)
+{
+ NMConnection *connection;
+ const char *setting_name;
+ GPtrArray *hints = NULL;
+
+ connection = make_tls_connection ("need-tls-secrets-blob-key", NM_SETTING_802_1X_CK_SCHEME_BLOB);
+ ASSERT (connection != NULL,
+ "need-tls-secrets-blob-key",
+ "error creating test connection");
+
+ /* Ensure we don't need any secrets since we just set up the connection */
+ setting_name = nm_connection_need_secrets (connection, &hints);
+ ASSERT (setting_name == NULL,
+ "need-tls-secrets-blob-key",
+ "secrets are unexpectedly required");
+ ASSERT (hints == NULL,
+ "need-tls-secrets-blob-key",
+ "hints should be NULL since no secrets were required");
+
+ /* Clear secrets and ensure password is again required */
+ nm_connection_clear_secrets (connection);
+
+ hints = NULL;
+ setting_name = nm_connection_need_secrets (connection, &hints);
+ ASSERT (setting_name != NULL,
+ "need-tls-secrets-blob-key-password",
+ "unexpected secrets success");
+ ASSERT (strcmp (setting_name, NM_SETTING_802_1X_SETTING_NAME) == 0,
+ "need-tls-secrets-blob-key-password",
+ "unexpected setting secrets required");
+
+ ASSERT (hints != NULL,
+ "need-tls-secrets-blob-key-password",
+ "expected returned secrets hints");
+ ASSERT (find_hints_item (hints, NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD),
+ "need-tls-secrets-blob-key-password",
+ "expected to require private key password, but it wasn't");
+
+ g_object_unref (connection);
+}
+
+static NMConnection *
+make_tls_phase2_connection (const char *detail, NMSetting8021xCKScheme scheme)
+{
+ NMConnection *connection;
+ NMSettingConnection *s_con;
+ NMSetting8021x *s_8021x;
+ NMSettingWired *s_wired;
+ NMSettingIP4Config *s_ip4;
+ char *uuid;
+ gboolean success;
+ GError *error = NULL;
+
+ connection = nm_connection_new ();
+
+ /* Connection setting */
+ s_con = (NMSettingConnection *) nm_setting_connection_new ();
+ nm_connection_add_setting (connection, NM_SETTING (s_con));
+
+ uuid = nm_utils_uuid_generate ();
+ g_object_set (s_con,
+ NM_SETTING_CONNECTION_ID, "Test Need TLS Secrets",
+ NM_SETTING_CONNECTION_UUID, uuid,
+ NM_SETTING_CONNECTION_AUTOCONNECT, TRUE,
+ NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME,
+ NULL);
+ g_free (uuid);
+
+ /* Wired setting */
+ s_wired = (NMSettingWired *) nm_setting_wired_new ();
+ nm_connection_add_setting (connection, NM_SETTING (s_wired));
+
+ /* Wireless security setting */
+ s_8021x = (NMSetting8021x *) nm_setting_802_1x_new ();
+ nm_connection_add_setting (connection, NM_SETTING (s_8021x));
+
+ g_object_set (s_8021x, NM_SETTING_802_1X_ANONYMOUS_IDENTITY, "blahblah", NULL);
+ g_object_set (s_8021x, NM_SETTING_802_1X_IDENTITY, "Bill Smith", NULL);
+
+ nm_setting_802_1x_add_eap_method (s_8021x, "ttls");
+ g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTH, "tls", NULL);
+
+ success = nm_setting_802_1x_set_phase2_ca_cert (s_8021x,
+ TEST_NEED_SECRETS_EAP_TLS_CA_CERT,
+ scheme,
+ NULL,
+ &error);
+ ASSERT (success == TRUE,
+ detail, "failed to set phase2 CA certificate '%s': %s",
+ TEST_NEED_SECRETS_EAP_TLS_CA_CERT, error->message);
+
+ success = nm_setting_802_1x_set_phase2_client_cert (s_8021x,
+ TEST_NEED_SECRETS_EAP_TLS_CLIENT_CERT,
+ scheme,
+ NULL,
+ &error);
+ ASSERT (success == TRUE,
+ detail, "failed to set phase2 client certificate '%s': %s",
+ TEST_NEED_SECRETS_EAP_TLS_CLIENT_CERT, error->message);
+
+ success = nm_setting_802_1x_set_phase2_private_key (s_8021x,
+ TEST_NEED_SECRETS_EAP_TLS_PRIVATE_KEY,
+ "test",
+ scheme,
+ NULL,
+ &error);
+ ASSERT (success == TRUE,
+ detail, "failed to set phase2 private key '%s': %s",
+ TEST_NEED_SECRETS_EAP_TLS_PRIVATE_KEY, error->message);
+
+ /* IP4 setting */
+ s_ip4 = (NMSettingIP4Config *) nm_setting_ip4_config_new ();
+ nm_connection_add_setting (connection, NM_SETTING (s_ip4));
+
+ g_object_set (s_ip4, NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL);
+
+ ASSERT (nm_connection_verify (connection, &error) == TRUE,
+ detail, "failed to verify connection: %s",
+ (error && error->message) ? error->message : "(unknown)");
+
+ return connection;
+}
+
+static void
+test_need_tls_phase2_secrets_path (void)
+{
+ NMConnection *connection;
+ const char *setting_name;
+ GPtrArray *hints = NULL;
+
+ connection = make_tls_phase2_connection ("need-tls-phase2-secrets-path-key",
+ NM_SETTING_802_1X_CK_SCHEME_PATH);
+ ASSERT (connection != NULL,
+ "need-tls-phase2-secrets-path-key",
+ "error creating test connection");
+
+ /* Ensure we don't need any secrets since we just set up the connection */
+ setting_name = nm_connection_need_secrets (connection, &hints);
+ ASSERT (setting_name == NULL,
+ "need-tls-phase2-secrets-path-key",
+ "secrets are unexpectedly required");
+ ASSERT (hints == NULL,
+ "need-tls-phase2-secrets-path-key",
+ "hints should be NULL since no secrets were required");
+
+ /* Connection is good; clear secrets and ensure private key password is then required */
+ nm_connection_clear_secrets (connection);
+
+ hints = NULL;
+ setting_name = nm_connection_need_secrets (connection, &hints);
+ ASSERT (setting_name != NULL,
+ "need-tls-phase2-secrets-path-key-password",
+ "unexpected secrets success");
+ ASSERT (strcmp (setting_name, NM_SETTING_802_1X_SETTING_NAME) == 0,
+ "need-tls-phase2-secrets-path-key-password",
+ "unexpected setting secrets required");
+
+ ASSERT (hints != NULL,
+ "need-tls-phase2-secrets-path-key-password",
+ "expected returned secrets hints");
+ ASSERT (find_hints_item (hints, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD),
+ "need-tls-phase2-secrets-path-key-password",
+ "expected to require private key password, but it wasn't");
+
+ g_object_unref (connection);
+}
+
+static void
+test_need_tls_phase2_secrets_blob (void)
+{
+ NMConnection *connection;
+ const char *setting_name;
+ GPtrArray *hints = NULL;
+
+ connection = make_tls_phase2_connection ("need-tls-phase2-secrets-blob-key",
+ NM_SETTING_802_1X_CK_SCHEME_BLOB);
+ ASSERT (connection != NULL,
+ "need-tls-phase2-secrets-blob-key",
+ "error creating test connection");
+
+ /* Ensure we don't need any secrets since we just set up the connection */
+ setting_name = nm_connection_need_secrets (connection, &hints);
+ ASSERT (setting_name == NULL,
+ "need-tls-phase2-secrets-blob-key",
+ "secrets are unexpectedly required");
+ ASSERT (hints == NULL,
+ "need-tls-phase2-secrets-blob-key",
+ "hints should be NULL since no secrets were required");
+
+ /* Connection is good; clear secrets and ensure private key password is then required */
+ nm_connection_clear_secrets (connection);
+
+ hints = NULL;
+ setting_name = nm_connection_need_secrets (connection, &hints);
+ ASSERT (setting_name != NULL,
+ "need-tls-phase2-secrets-blob-key-password",
+ "unexpected secrets success");
+ ASSERT (strcmp (setting_name, NM_SETTING_802_1X_SETTING_NAME) == 0,
+ "need-tls-phase2-secrets-blob-key-password",
+ "unexpected setting secrets required");
+
+ ASSERT (hints != NULL,
+ "need-tls-phase2-secrets-blob-key-password",
+ "expected returned secrets hints");
+ ASSERT (find_hints_item (hints, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD),
+ "need-tls-phase2-secrets-blob-key-password",
+ "expected to require private key password, but it wasn't");
+
+ g_object_unref (connection);
+}
+
+static NMConnection *
+wifi_connection_new (void)
+{
+ NMConnection *connection;
+ NMSettingConnection *s_con;
+ NMSettingWireless *s_wifi;
+ NMSettingWirelessSecurity *s_wsec;
+ unsigned char tmpssid[] = { 0x31, 0x33, 0x33, 0x37 };
+ char *uuid;
+ GByteArray *ssid;
+
+ connection = nm_connection_new ();
+ g_assert (connection);
+
+ /* Connection setting */
+ s_con = (NMSettingConnection *) nm_setting_connection_new ();
+ g_assert (s_con);
+
+ uuid = nm_utils_uuid_generate ();
+ g_object_set (s_con,
+ NM_SETTING_CONNECTION_ID, "Test Wireless",
+ NM_SETTING_CONNECTION_UUID, uuid,
+ NM_SETTING_CONNECTION_AUTOCONNECT, FALSE,
+ NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRELESS_SETTING_NAME,
+ NULL);
+ g_free (uuid);
+ nm_connection_add_setting (connection, NM_SETTING (s_con));
+
+ /* Wireless setting */
+ s_wifi = (NMSettingWireless *) nm_setting_wireless_new ();
+ g_assert (s_wifi);
+
+ ssid = g_byte_array_sized_new (sizeof (tmpssid));
+ g_byte_array_append (ssid, &tmpssid[0], sizeof (tmpssid));
+ g_object_set (s_wifi,
+ NM_SETTING_WIRELESS_SSID, ssid,
+ NULL);
+ g_byte_array_free (ssid, TRUE);
+ nm_connection_add_setting (connection, NM_SETTING (s_wifi));
+
+ /* Wifi security */
+ s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new ();
+ g_assert (s_wsec);
+
+ g_object_set (G_OBJECT (s_wsec),
+ NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "none",
+ NULL);
+ nm_connection_add_setting (connection, NM_SETTING (s_wsec));
+
+ return connection;
+}
+
+static void
+value_destroy (gpointer data)
+{
+ GValue *value = (GValue *) data;
+
+ g_value_unset (value);
+ g_slice_free (GValue, value);
+}
+
+static GValue *
+string_to_gvalue (const char *str)
+{
+ GValue *val = g_slice_new0 (GValue);
+
+ g_value_init (val, G_TYPE_STRING);
+ g_value_set_string (val, str);
+ return val;
+}
+
+static GValue *
+uint_to_gvalue (guint32 i)
+{
+ GValue *val;
+
+ val = g_slice_new0 (GValue);
+ g_value_init (val, G_TYPE_UINT);
+ g_value_set_uint (val, i);
+ return val;
+}
+
+static void
+test_update_secrets_wifi_single_setting (void)
+{
+ NMConnection *connection;
+ NMSettingWirelessSecurity *s_wsec;
+ GHashTable *secrets;
+ GError *error = NULL;
+ gboolean success;
+ const char *wepkey = "11111111111111111111111111";
+ const char *tmp;
+
+ /* Test update with a hashed setting of 802-11-wireless secrets */
+
+ connection = wifi_connection_new ();
+
+ /* Build up the secrets hash */
+ secrets = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, value_destroy);
+ g_hash_table_insert (secrets, NM_SETTING_WIRELESS_SECURITY_WEP_KEY0, string_to_gvalue (wepkey));
+ g_hash_table_insert (secrets, NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, uint_to_gvalue (NM_WEP_KEY_TYPE_KEY));
+
+ success = nm_connection_update_secrets (connection,
+ NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
+ secrets,
+ &error);
+ g_assert_no_error (error);
+ g_assert (success);
+
+ /* Make sure the secret is now in the connection */
+ s_wsec = nm_connection_get_setting_wireless_security (connection);
+ g_assert (s_wsec);
+ tmp = nm_setting_wireless_security_get_wep_key (s_wsec, 0);
+ g_assert_cmpstr (tmp, ==, wepkey);
+
+ g_object_unref (connection);
+}
+
+static void
+test_update_secrets_wifi_full_hash (void)
+{
+ NMConnection *connection;
+ NMSettingWirelessSecurity *s_wsec;
+ GHashTable *secrets, *all;
+ GError *error = NULL;
+ gboolean success;
+ const char *wepkey = "11111111111111111111111111";
+ const char *tmp;
+
+ /* Test update with a hashed connection containing only 802-11-wireless
+ * setting and secrets.
+ */
+
+ connection = wifi_connection_new ();
+
+ /* Build up the secrets hash */
+ all = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify) g_hash_table_destroy);
+ secrets = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, value_destroy);
+ g_hash_table_insert (secrets, NM_SETTING_WIRELESS_SECURITY_WEP_KEY0, string_to_gvalue (wepkey));
+ g_hash_table_insert (secrets, NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, uint_to_gvalue (NM_WEP_KEY_TYPE_KEY));
+ g_hash_table_insert (all, NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, secrets);
+
+ success = nm_connection_update_secrets (connection,
+ NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
+ all,
+ &error);
+ g_assert_no_error (error);
+ g_assert (success);
+
+ /* Make sure the secret is now in the connection */
+ s_wsec = nm_connection_get_setting_wireless_security (connection);
+ g_assert (s_wsec);
+ tmp = nm_setting_wireless_security_get_wep_key (s_wsec, 0);
+ g_assert_cmpstr (tmp, ==, wepkey);
+
+ g_object_unref (connection);
+}
+
+static void
+test_update_secrets_wifi_bad_setting_name (void)
+{
+ NMConnection *connection;
+ GHashTable *secrets;
+ GError *error = NULL;
+ gboolean success;
+ const char *wepkey = "11111111111111111111111111";
+
+ /* Test that passing an invalid setting name to
+ * nm_connection_update_secrets() fails with the correct error.
+ */
+
+ connection = wifi_connection_new ();
+
+ /* Build up the secrets hash */
+ secrets = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, value_destroy);
+ g_hash_table_insert (secrets, NM_SETTING_WIRELESS_SECURITY_WEP_KEY0, string_to_gvalue (wepkey));
+ g_hash_table_insert (secrets, NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, uint_to_gvalue (NM_WEP_KEY_TYPE_KEY));
+
+ success = nm_connection_update_secrets (connection,
+ "asdfasdfasdfasf",
+ secrets,
+ &error);
+ g_assert_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_SETTING_NOT_FOUND);
+ g_assert (success == FALSE);
+
+ g_object_unref (connection);
+}
+
+static void
+test_update_secrets_whole_connection (void)
+{
+ NMConnection *connection;
+ NMSettingWirelessSecurity *s_wsec;
+ GHashTable *secrets, *wsec_hash;
+ GError *error = NULL;
+ gboolean success;
+ const char *wepkey = "11111111111111111111111111";
+
+ /* Test calling nm_connection_update_secrets() with an entire hashed
+ * connection including non-secrets.
+ */
+
+ connection = wifi_connection_new ();
+
+ /* Build up the secrets hash */
+ secrets = nm_connection_to_hash (connection, NM_SETTING_HASH_FLAG_ALL);
+ wsec_hash = g_hash_table_lookup (secrets, NM_SETTING_WIRELESS_SECURITY_SETTING_NAME);
+ g_assert (wsec_hash);
+ g_hash_table_insert (wsec_hash, NM_SETTING_WIRELESS_SECURITY_WEP_KEY0, string_to_gvalue (wepkey));
+
+ success = nm_connection_update_secrets (connection, NULL, secrets, &error);
+ g_assert_no_error (error);
+ g_assert (success == TRUE);
+
+ s_wsec = nm_connection_get_setting_wireless_security (connection);
+ g_assert (s_wsec);
+ g_assert_cmpstr (nm_setting_wireless_security_get_wep_key (s_wsec, 0), ==, wepkey);
+
+ g_object_unref (connection);
+}
+
+static void
+test_update_secrets_whole_connection_empty_hash (void)
+{
+ NMConnection *connection;
+ GHashTable *secrets;
+ GError *error = NULL;
+ gboolean success;
+
+ /* Test that updating secrets with an empty hash returns success */
+
+ connection = wifi_connection_new ();
+ secrets = g_hash_table_new (g_str_hash, g_str_equal);
+ success = nm_connection_update_secrets (connection, NULL, secrets, &error);
+ g_assert_no_error (error);
+ g_assert (success == TRUE);
+ g_object_unref (connection);
+}
+
+static void
+test_update_secrets_whole_connection_bad_setting (void)
+{
+ NMConnection *connection;
+ GHashTable *secrets, *wsec_hash;
+ GError *error = NULL;
+ gboolean success;
+ const char *wepkey = "11111111111111111111111111";
+
+ /* Test that sending a hashed connection containing an invalid setting
+ * name fails with the right error.
+ */
+
+ connection = wifi_connection_new ();
+
+ /* Build up the secrets hash */
+ secrets = nm_connection_to_hash (connection, NM_SETTING_HASH_FLAG_ALL);
+ wsec_hash = g_hash_table_lookup (secrets, NM_SETTING_WIRELESS_SECURITY_SETTING_NAME);
+ g_assert (wsec_hash);
+ g_hash_table_insert (wsec_hash, NM_SETTING_WIRELESS_SECURITY_WEP_KEY0, string_to_gvalue (wepkey));
+
+ /* Steal the wsec setting hash so it's not deallocated, and stuff it back
+ * in with a different name so we ensure libnm-util is returning the right
+ * error when it finds an entry in the connection hash that doesn't match
+ * any setting in the connection.
+ */
+ g_hash_table_steal (secrets, NM_SETTING_WIRELESS_SECURITY_SETTING_NAME);
+ g_hash_table_insert (secrets, "asdfasdfasdfasdf", wsec_hash);
+
+ success = nm_connection_update_secrets (connection, NULL, secrets, &error);
+ g_assert_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_SETTING_NOT_FOUND);
+ g_assert (success == FALSE);
+
+ g_object_unref (connection);
+}
+
+static void
+test_update_secrets_whole_connection_empty_base_setting (void)
+{
+ NMConnection *connection;
+ GHashTable *secrets;
+ GError *error = NULL;
+ gboolean success;
+
+ /* Test that a hashed connection which does not have any hashed secrets
+ * for the requested setting returns success.
+ */
+
+ connection = wifi_connection_new ();
+ secrets = nm_connection_to_hash (connection, NM_SETTING_HASH_FLAG_ONLY_SECRETS);
+ g_assert_cmpint (g_hash_table_size (secrets), ==, 1);
+ g_assert (g_hash_table_lookup (secrets, NM_SETTING_WIRELESS_SETTING_NAME));
+
+ success = nm_connection_update_secrets (connection,
+ NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
+ secrets,
+ &error);
+ g_assert_no_error (error);
+ g_assert (success);
+
+ g_hash_table_destroy (secrets);
+ g_object_unref (connection);
+}
+
+static void
+test_update_secrets_null_setting_name_with_setting_hash (void)
+{
+ NMConnection *connection;
+ GHashTable *secrets;
+ GError *error = NULL;
+ gboolean success;
+ const char *wepkey = "11111111111111111111111111";
+
+ /* Ensure that a NULL setting name and only a hashed setting fails */
+
+ connection = wifi_connection_new ();
+
+ secrets = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, value_destroy);
+ g_hash_table_insert (secrets, NM_SETTING_WIRELESS_SECURITY_WEP_KEY0, string_to_gvalue (wepkey));
+ g_hash_table_insert (secrets, NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, uint_to_gvalue (NM_WEP_KEY_TYPE_KEY));
+
+ success = nm_connection_update_secrets (connection, NULL, secrets, &error);
+ g_assert_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_SETTING_NOT_FOUND);
+ g_assert (!success);
+
+ g_hash_table_destroy (secrets);
+ g_object_unref (connection);
+}
+
+int main (int argc, char **argv)
+{
+ GError *error = NULL;
+ char *base;
+
+#if !GLIB_CHECK_VERSION (2, 35, 0)
+ g_type_init ();
+#endif
+
+ if (!nm_utils_init (&error))
+ FAIL ("nm-utils-init", "failed to initialize libnm-util: %s", error->message);
+
+ /* The tests */
+ test_need_tls_secrets_path ();
+ test_need_tls_secrets_blob ();
+ test_need_tls_phase2_secrets_path ();
+ test_need_tls_phase2_secrets_blob ();
+
+ test_update_secrets_wifi_single_setting ();
+ test_update_secrets_wifi_full_hash ();
+ test_update_secrets_wifi_bad_setting_name ();
+
+ test_update_secrets_whole_connection ();
+ test_update_secrets_whole_connection_empty_hash ();
+ test_update_secrets_whole_connection_bad_setting ();
+ test_update_secrets_whole_connection_empty_base_setting ();
+ test_update_secrets_null_setting_name_with_setting_hash ();
+
+ base = g_path_get_basename (argv[0]);
+ fprintf (stdout, "%s: SUCCESS\n", base);
+ g_free (base);
+ return 0;
+}
+
diff --git a/libnm-core/tests/test-setting-8021x.c b/libnm-core/tests/test-setting-8021x.c
new file mode 100644
index 0000000000..c496e47bcf
--- /dev/null
+++ b/libnm-core/tests/test-setting-8021x.c
@@ -0,0 +1,443 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2008 - 2011 Red Hat, Inc.
+ *
+ */
+
+#include <glib.h>
+#include <string.h>
+
+#include <nm-utils.h>
+
+#include "nm-setting-connection.h"
+#include "nm-setting-8021x.h"
+
+#include "nm-test-utils.h"
+
+static void
+compare_blob_data (const char *test,
+ const char *key_path,
+ const GByteArray *key)
+{
+ char *contents = NULL;
+ gsize len = 0;
+ GError *error = NULL;
+ gboolean success;
+
+ success = g_file_get_contents (key_path, &contents, &len, &error);
+ ASSERT (success == TRUE,
+ test, "failed to read blob key file: %s", error->message);
+
+ ASSERT (len > 0, test, "blob key file invalid (size 0)");
+
+ ASSERT (len == key->len,
+ test, "blob key file (%d) and setting key data (%d) lengths don't match",
+ len, key->len);
+
+ ASSERT (memcmp (contents, key->data, len) == 0,
+ test, "blob key file and blob key data don't match");
+
+ g_free (contents);
+}
+
+#define SCHEME_PATH "file://"
+
+static void
+check_scheme_path (GByteArray *value, const char *path)
+{
+ guint8 *p = value->data;
+
+ g_assert (memcmp (p, SCHEME_PATH, strlen (SCHEME_PATH)) == 0);
+ p += strlen (SCHEME_PATH);
+ g_assert (memcmp (p, path, strlen (path)) == 0);
+ p += strlen (path);
+ g_assert (*p == '\0');
+}
+
+static void
+test_private_key_import (const char *path,
+ const char *password,
+ NMSetting8021xCKScheme scheme)
+{
+ NMSetting8021x *s_8021x;
+ gboolean success;
+ NMSetting8021xCKFormat format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN;
+ NMSetting8021xCKFormat tmp_fmt;
+ GError *error = NULL;
+ GByteArray *tmp_key = NULL, *client_cert = NULL;
+ const char *pw;
+
+ s_8021x = (NMSetting8021x *) nm_setting_802_1x_new ();
+ ASSERT (s_8021x != NULL, "private-key-import", "setting was NULL");
+
+ success = nm_setting_802_1x_set_private_key (s_8021x,
+ path,
+ password,
+ scheme,
+ &format,
+ &error);
+ ASSERT (success == TRUE,
+ "private-key-import", "error reading private key: %s", error->message);
+ ASSERT (format != NM_SETTING_802_1X_CK_FORMAT_UNKNOWN,
+ "private-key-import", "unexpected private key format (got %d)", format);
+ tmp_fmt = nm_setting_802_1x_get_private_key_format (s_8021x);
+ ASSERT (tmp_fmt == format,
+ "private-key-import", "unexpected re-read private key format (expected %d, got %d)",
+ format, tmp_fmt);
+
+ /* Make sure the password is what we expect */
+ pw = nm_setting_802_1x_get_private_key_password (s_8021x);
+ ASSERT (pw != NULL,
+ "private-key-import", "failed to get previous private key password");
+ ASSERT (strcmp (pw, password) == 0,
+ "private-key-import", "failed to compare private key password");
+
+ if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) {
+ tmp_key = (GByteArray *) nm_setting_802_1x_get_private_key_blob (s_8021x);
+ ASSERT (tmp_key != NULL, "private-key-import", "missing private key blob");
+ compare_blob_data ("private-key-import", path, tmp_key);
+ } else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) {
+ g_object_get (s_8021x, NM_SETTING_802_1X_PRIVATE_KEY, &tmp_key, NULL);
+ ASSERT (tmp_key != NULL, "private-key-import", "missing private key value");
+ check_scheme_path (tmp_key, path);
+ g_byte_array_free (tmp_key, TRUE);
+ } else
+ g_assert_not_reached ();
+
+ /* If it's PKCS#12 ensure the client cert is the same value */
+ if (format == NM_SETTING_802_1X_CK_FORMAT_PKCS12) {
+ g_object_get (s_8021x, NM_SETTING_802_1X_PRIVATE_KEY, &tmp_key, NULL);
+ ASSERT (tmp_key != NULL, "private-key-import", "missing private key value");
+
+ g_object_get (s_8021x, NM_SETTING_802_1X_CLIENT_CERT, &client_cert, NULL);
+ ASSERT (client_cert != NULL, "private-key-import", "missing client certificate value");
+
+ /* make sure they are the same */
+ ASSERT (tmp_key->len == client_cert->len,
+ "private-key-import", "unexpected different private key and client cert lengths");
+ ASSERT (memcmp (tmp_key->data, client_cert->data, tmp_key->len) == 0,
+ "private-key-import", "unexpected different private key and client cert data");
+
+ g_byte_array_free (tmp_key, TRUE);
+ g_byte_array_free (client_cert, TRUE);
+ }
+
+ g_object_unref (s_8021x);
+}
+
+static void
+test_phase2_private_key_import (const char *path,
+ const char *password,
+ NMSetting8021xCKScheme scheme)
+{
+ NMSetting8021x *s_8021x;
+ gboolean success;
+ NMSetting8021xCKFormat format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN;
+ NMSetting8021xCKFormat tmp_fmt;
+ GError *error = NULL;
+ GByteArray *tmp_key = NULL, *client_cert = NULL;
+ const char *pw;
+
+ s_8021x = (NMSetting8021x *) nm_setting_802_1x_new ();
+ ASSERT (s_8021x != NULL, "phase2-private-key-import", "setting was NULL");
+
+ success = nm_setting_802_1x_set_phase2_private_key (s_8021x,
+ path,
+ password,
+ scheme,
+ &format,
+ &error);
+ ASSERT (success == TRUE,
+ "phase2-private-key-import", "error reading private key: %s", error->message);
+ ASSERT (format != NM_SETTING_802_1X_CK_FORMAT_UNKNOWN,
+ "phase2-private-key-import", "unexpected private key format");
+ tmp_fmt = nm_setting_802_1x_get_phase2_private_key_format (s_8021x);
+ ASSERT (tmp_fmt == format,
+ "phase2-private-key-import", "unexpected re-read private key format (expected %d, got %d)",
+ format, tmp_fmt);
+
+ /* Make sure the password is what we expect */
+ pw = nm_setting_802_1x_get_phase2_private_key_password (s_8021x);
+ ASSERT (pw != NULL,
+ "phase2-private-key-import", "failed to get previous private key password");
+ ASSERT (strcmp (pw, password) == 0,
+ "phase2-private-key-import", "failed to compare private key password");
+
+ if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) {
+ tmp_key = (GByteArray *) nm_setting_802_1x_get_phase2_private_key_blob (s_8021x);
+ ASSERT (tmp_key != NULL, "phase2-private-key-import", "missing private key blob");
+ compare_blob_data ("phase2-private-key-import", path, tmp_key);
+ } else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) {
+ g_object_get (s_8021x, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY, &tmp_key, NULL);
+ ASSERT (tmp_key != NULL, "phase2-private-key-import", "missing private key value");
+ check_scheme_path (tmp_key, path);
+ } else
+ g_assert_not_reached ();
+
+ /* If it's PKCS#12 ensure the client cert is the same value */
+ if (format == NM_SETTING_802_1X_CK_FORMAT_PKCS12) {
+ g_object_get (s_8021x, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY, &tmp_key, NULL);
+ ASSERT (tmp_key != NULL, "private-key-import", "missing private key value");
+
+ g_object_get (s_8021x, NM_SETTING_802_1X_PHASE2_CLIENT_CERT, &client_cert, NULL);
+ ASSERT (client_cert != NULL, "private-key-import", "missing client certificate value");
+
+ /* make sure they are the same */
+ ASSERT (tmp_key->len == client_cert->len,
+ "private-key-import", "unexpected different private key and client cert lengths");
+ ASSERT (memcmp (tmp_key->data, client_cert->data, tmp_key->len) == 0,
+ "private-key-import", "unexpected different private key and client cert data");
+
+ g_byte_array_free (tmp_key, TRUE);
+ g_byte_array_free (client_cert, TRUE);
+ }
+
+ g_object_unref (s_8021x);
+}
+
+static void
+test_wrong_password_keeps_data (const char *path, const char *password)
+{
+ NMSetting8021x *s_8021x;
+ gboolean success;
+ NMSetting8021xCKFormat format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN;
+ GError *error = NULL;
+ const char *pw;
+
+ s_8021x = (NMSetting8021x *) nm_setting_802_1x_new ();
+ ASSERT (s_8021x != NULL, "wrong-password-keeps-data", "setting was NULL");
+
+ success = nm_setting_802_1x_set_private_key (s_8021x,
+ path,
+ password,
+ NM_SETTING_802_1X_CK_SCHEME_BLOB,
+ &format,
+ &error);
+ ASSERT (success == TRUE,
+ "wrong-password-keeps-data", "error reading private key: %s", error->message);
+ ASSERT (format != NM_SETTING_802_1X_CK_FORMAT_UNKNOWN,
+ "wrong-password-keeps-data", "unexpected private key format (got %d)", format);
+
+ /* Now try to set it to something that's not a certificate */
+ format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN;
+ success = nm_setting_802_1x_set_private_key (s_8021x,
+ "Makefile.am",
+ password,
+ NM_SETTING_802_1X_CK_SCHEME_BLOB,
+ &format,
+ &error);
+ ASSERT (success == FALSE,
+ "wrong-password-keeps-data", "unexpected success reading private key");
+ ASSERT (error != NULL,
+ "wrong-password-keeps-data", "unexpected missing error");
+ ASSERT (format == NM_SETTING_802_1X_CK_FORMAT_UNKNOWN,
+ "wrong-password-keeps-data", "unexpected success reading private key format");
+
+ /* Make sure the password hasn't changed */
+ pw = nm_setting_802_1x_get_private_key_password (s_8021x);
+ ASSERT (pw != NULL,
+ "wrong-password-keeps-data", "failed to get previous private key password");
+ ASSERT (strcmp (pw, password) == 0,
+ "wrong-password-keeps-data", "failed to compare private key password");
+
+ g_object_unref (s_8021x);
+}
+
+static void
+test_clear_private_key (const char *path, const char *password)
+{
+ NMSetting8021x *s_8021x;
+ gboolean success;
+ NMSetting8021xCKFormat format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN;
+ GError *error = NULL;
+ const char *pw;
+
+ s_8021x = (NMSetting8021x *) nm_setting_802_1x_new ();
+ ASSERT (s_8021x != NULL, "clear-private-key", "setting was NULL");
+
+ success = nm_setting_802_1x_set_private_key (s_8021x,
+ path,
+ password,
+ NM_SETTING_802_1X_CK_SCHEME_BLOB,
+ &format,
+ &error);
+ ASSERT (success == TRUE,
+ "clear-private-key", "error reading private key: %s", error->message);
+ ASSERT (format != NM_SETTING_802_1X_CK_FORMAT_UNKNOWN,
+ "clear-private-key", "unexpected private key format (got %d)", format);
+
+ /* Make sure the password is what we expect */
+ pw = nm_setting_802_1x_get_private_key_password (s_8021x);
+ ASSERT (pw != NULL,
+ "clear-private-key", "failed to get previous private key password");
+ ASSERT (strcmp (pw, password) == 0,
+ "clear-private-key", "failed to compare private key password");
+
+ /* Now clear it */
+ success = nm_setting_802_1x_set_private_key (s_8021x,
+ NULL,
+ NULL,
+ NM_SETTING_802_1X_CK_SCHEME_BLOB,
+ NULL,
+ &error);
+ ASSERT (success == TRUE,
+ "clear-private-key", "unexpected failure clearing private key");
+ ASSERT (error == NULL,
+ "clear-private-key", "unexpected error clearing private key");
+
+ /* Ensure the password is also now clear */
+ ASSERT (nm_setting_802_1x_get_private_key_password (s_8021x) == NULL,
+ "clear-private-key", "unexpected private key password");
+
+ g_object_unref (s_8021x);
+}
+
+static void
+test_wrong_phase2_password_keeps_data (const char *path, const char *password)
+{
+ NMSetting8021x *s_8021x;
+ gboolean success;
+ NMSetting8021xCKFormat format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN;
+ GError *error = NULL;
+ const char *pw;
+
+ s_8021x = (NMSetting8021x *) nm_setting_802_1x_new ();
+ ASSERT (s_8021x != NULL, "wrong-phase2-password-keeps-data", "setting was NULL");
+
+ success = nm_setting_802_1x_set_phase2_private_key (s_8021x,
+ path,
+ password,
+ NM_SETTING_802_1X_CK_SCHEME_BLOB,
+ &format,
+ &error);
+ ASSERT (success == TRUE,
+ "wrong-phase2-password-keeps-data", "error reading private key: %s", error->message);
+ ASSERT (format != NM_SETTING_802_1X_CK_FORMAT_UNKNOWN,
+ "wrong-phase2-password-keeps-data", "unexpected private key format (got %d)", format);
+
+ /* Now try to set it to something that's not a certificate */
+ format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN;
+ success = nm_setting_802_1x_set_phase2_private_key (s_8021x,
+ "Makefile.am",
+ password,
+ NM_SETTING_802_1X_CK_SCHEME_BLOB,
+ &format,
+ &error);
+ ASSERT (success == FALSE,
+ "wrong-phase2-password-keeps-data", "unexpected success reading private key");
+ ASSERT (error != NULL,
+ "wrong-phase2-password-keeps-data", "unexpected missing error");
+ ASSERT (format == NM_SETTING_802_1X_CK_FORMAT_UNKNOWN,
+ "wrong-phase2-password-keeps-data", "unexpected success reading private key format");
+
+ /* Make sure the password hasn't changed */
+ pw = nm_setting_802_1x_get_phase2_private_key_password (s_8021x);
+ ASSERT (pw != NULL,
+ "wrong-phase2-password-keeps-data", "failed to get previous private key password");
+ ASSERT (strcmp (pw, password) == 0,
+ "wrong-phase2-password-keeps-data", "failed to compare private key password");
+
+ g_object_unref (s_8021x);
+}
+
+static void
+test_clear_phase2_private_key (const char *path, const char *password)
+{
+ NMSetting8021x *s_8021x;
+ gboolean success;
+ NMSetting8021xCKFormat format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN;
+ GError *error = NULL;
+ const char *pw;
+
+ s_8021x = (NMSetting8021x *) nm_setting_802_1x_new ();
+ ASSERT (s_8021x != NULL, "clear-phase2-private-key", "setting was NULL");
+
+ success = nm_setting_802_1x_set_phase2_private_key (s_8021x,
+ path,
+ password,
+ NM_SETTING_802_1X_CK_SCHEME_BLOB,
+ &format,
+ &error);
+ ASSERT (success == TRUE,
+ "clear-phase2-private-key", "error reading private key: %s", error->message);
+ ASSERT (format != NM_SETTING_802_1X_CK_FORMAT_UNKNOWN,
+ "clear-phase2-private-key", "unexpected private key format (got %d)", format);
+
+ /* Make sure the password is what we expect */
+ pw = nm_setting_802_1x_get_phase2_private_key_password (s_8021x);
+ ASSERT (pw != NULL,
+ "clear-phase2-private-key", "failed to get previous private key password");
+ ASSERT (strcmp (pw, password) == 0,
+ "clear-phase2-private-key", "failed to compare private key password");
+
+ /* Now clear it */
+ success = nm_setting_802_1x_set_phase2_private_key (s_8021x,
+ NULL,
+ NULL,
+ NM_SETTING_802_1X_CK_SCHEME_BLOB,
+ NULL,
+ &error);
+ ASSERT (success == TRUE,
+ "clear-phase2-private-key", "unexpected failure clearing private key");
+ ASSERT (error == NULL,
+ "clear-phase2-private-key", "unexpected error clearing private key");
+
+ /* Ensure the password is also now clear */
+ ASSERT (nm_setting_802_1x_get_phase2_private_key_password (s_8021x) == NULL,
+ "clear-phase2-private-key", "unexpected private key password");
+
+ g_object_unref (s_8021x);
+}
+
+int main (int argc, char **argv)
+{
+ GError *error = NULL;
+ char *base;
+
+ if (argc < 3)
+ FAIL ("init", "need at least two arguments: <path> <password>");
+
+#if !GLIB_CHECK_VERSION (2, 35, 0)
+ g_type_init ();
+#endif
+
+ if (!nm_utils_init (&error))
+ FAIL ("nm-utils-init", "failed to initialize libnm-util: %s", error->message);
+
+ /* Test phase1 and phase2 path scheme */
+ test_private_key_import (argv[1], argv[2], NM_SETTING_802_1X_CK_SCHEME_PATH);
+ test_phase2_private_key_import (argv[1], argv[2], NM_SETTING_802_1X_CK_SCHEME_PATH);
+
+ /* Test phase1 and phase2 blob scheme */
+ test_private_key_import (argv[1], argv[2], NM_SETTING_802_1X_CK_SCHEME_BLOB);
+ test_phase2_private_key_import (argv[1], argv[2], NM_SETTING_802_1X_CK_SCHEME_BLOB);
+
+ /* Test that using a wrong password does not change existing data */
+ test_wrong_password_keeps_data (argv[1], argv[2]);
+ test_wrong_phase2_password_keeps_data (argv[1], argv[2]);
+
+ /* Test clearing the private key */
+ test_clear_private_key (argv[1], argv[2]);
+ test_clear_phase2_private_key (argv[1], argv[2]);
+
+ base = g_path_get_basename (argv[0]);
+ fprintf (stdout, "%s: SUCCESS\n", base);
+ g_free (base);
+ return 0;
+}
+
diff --git a/libnm-core/tests/test-setting-dcb.c b/libnm-core/tests/test-setting-dcb.c
new file mode 100644
index 0000000000..4b114ada35
--- /dev/null
+++ b/libnm-core/tests/test-setting-dcb.c
@@ -0,0 +1,328 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ *
+ */
+
+#include <glib.h>
+#include <string.h>
+#include <nm-utils.h>
+#include <nm-glib-compat.h>
+#include "nm-setting-dcb.h"
+
+#define DCB_FLAGS_ALL (NM_SETTING_DCB_FLAG_ENABLE | \
+ NM_SETTING_DCB_FLAG_ADVERTISE | \
+ NM_SETTING_DCB_FLAG_WILLING)
+
+static void
+test_dcb_flags_valid (void)
+{
+ NMSettingDcb *s_dcb;
+ GError *error = NULL;
+ gboolean success;
+ guint i;
+
+ s_dcb = (NMSettingDcb *) nm_setting_dcb_new ();
+ g_assert (s_dcb);
+
+ g_assert_cmpint (nm_setting_dcb_get_app_fcoe_flags (s_dcb), ==, 0);
+ g_assert_cmpint (nm_setting_dcb_get_app_iscsi_flags (s_dcb), ==, 0);
+ g_assert_cmpint (nm_setting_dcb_get_app_fip_flags (s_dcb), ==, 0);
+ g_assert_cmpint (nm_setting_dcb_get_priority_flow_control_flags (s_dcb), ==, 0);
+ g_assert_cmpint (nm_setting_dcb_get_priority_group_flags (s_dcb), ==, 0);
+
+ g_object_set (G_OBJECT (s_dcb),
+ NM_SETTING_DCB_APP_FCOE_FLAGS, DCB_FLAGS_ALL,
+ NM_SETTING_DCB_APP_ISCSI_FLAGS, DCB_FLAGS_ALL,
+ NM_SETTING_DCB_APP_FIP_FLAGS, DCB_FLAGS_ALL,
+ NM_SETTING_DCB_PRIORITY_FLOW_CONTROL_FLAGS, DCB_FLAGS_ALL,
+ NM_SETTING_DCB_PRIORITY_GROUP_FLAGS, DCB_FLAGS_ALL,
+ NULL);
+ /* Priority Group Bandwidth must total 100% */
+ for (i = 0; i < 7; i++)
+ nm_setting_dcb_set_priority_group_bandwidth (s_dcb, i, 12);
+ nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 7, 16);
+
+ success = nm_setting_verify (NM_SETTING (s_dcb), NULL, &error);
+ g_assert_no_error (error);
+ g_assert (success);
+
+ g_assert_cmpint (nm_setting_dcb_get_app_fcoe_flags (s_dcb), ==, DCB_FLAGS_ALL);
+ g_assert_cmpint (nm_setting_dcb_get_app_iscsi_flags (s_dcb), ==, DCB_FLAGS_ALL);
+ g_assert_cmpint (nm_setting_dcb_get_app_fip_flags (s_dcb), ==, DCB_FLAGS_ALL);
+ g_assert_cmpint (nm_setting_dcb_get_priority_flow_control_flags (s_dcb), ==, DCB_FLAGS_ALL);
+ g_assert_cmpint (nm_setting_dcb_get_priority_group_flags (s_dcb), ==, DCB_FLAGS_ALL);
+}
+
+#define TEST_FLAG(p, f, v) \
+{ \
+ /* GObject property min/max should ensure the property does not get set to \
+ * the invalid value, so we ensure the value we just tried to set is 0 and \
+ * that verify is successful since the property never got set. \
+ */ \
+ g_object_set (G_OBJECT (s_dcb), p, v, NULL); \
+ g_assert_cmpint (f (s_dcb), ==, 0); \
+ success = nm_setting_verify (NM_SETTING (s_dcb), NULL, &error); \
+ g_assert_no_error (error); \
+ g_assert (success); \
+}
+
+static void
+test_dcb_flags_invalid (void)
+{
+ NMSettingDcb *s_dcb;
+ GError *error = NULL;
+ gboolean success;
+
+ s_dcb = (NMSettingDcb *) nm_setting_dcb_new ();
+ g_assert (s_dcb);
+
+ g_test_expect_message ("GLib-GObject", G_LOG_LEVEL_WARNING, "*invalid or out of range*");
+ TEST_FLAG (NM_SETTING_DCB_APP_FCOE_FLAGS, nm_setting_dcb_get_app_fcoe_flags, 0x332523);
+ g_test_assert_expected_messages ();
+
+ g_test_expect_message ("GLib-GObject", G_LOG_LEVEL_WARNING, "*invalid or out of range*");
+ TEST_FLAG (NM_SETTING_DCB_APP_ISCSI_FLAGS, nm_setting_dcb_get_app_iscsi_flags, 0xFF);
+ g_test_assert_expected_messages ();
+
+ g_test_expect_message ("GLib-GObject", G_LOG_LEVEL_WARNING, "*invalid or out of range*");
+ TEST_FLAG (NM_SETTING_DCB_APP_FIP_FLAGS, nm_setting_dcb_get_app_fip_flags, 0x1111);
+ g_test_assert_expected_messages ();
+
+ g_test_expect_message ("GLib-GObject", G_LOG_LEVEL_WARNING, "*invalid or out of range*");
+ TEST_FLAG (NM_SETTING_DCB_PRIORITY_FLOW_CONTROL_FLAGS, nm_setting_dcb_get_priority_flow_control_flags, G_MAXUINT32);
+ g_test_assert_expected_messages ();
+
+ g_test_expect_message ("GLib-GObject", G_LOG_LEVEL_WARNING, "*invalid or out of range*");
+ TEST_FLAG (NM_SETTING_DCB_PRIORITY_GROUP_FLAGS, nm_setting_dcb_get_priority_group_flags,
+ (NM_SETTING_DCB_FLAG_ENABLE | NM_SETTING_DCB_FLAG_ADVERTISE | NM_SETTING_DCB_FLAG_WILLING) + 1);
+ g_test_assert_expected_messages ();
+}
+
+#define TEST_APP_PRIORITY(lcprop, ucprop, v) \
+{ \
+ g_object_set (G_OBJECT (s_dcb), NM_SETTING_DCB_APP_##ucprop##_FLAGS, NM_SETTING_DCB_FLAG_NONE, NULL); \
+ \
+ g_object_set (G_OBJECT (s_dcb), NM_SETTING_DCB_APP_##ucprop##_PRIORITY, v, NULL); \
+ g_assert_cmpint (nm_setting_dcb_get_app_##lcprop##_priority (s_dcb), ==, v); \
+ \
+ /* Assert that the setting is invalid while the app is disabled unless v is default */ \
+ success = nm_setting_verify (NM_SETTING (s_dcb), NULL, &error); \
+ if (v >= 0) { \
+ g_assert_error (error, NM_SETTING_DCB_ERROR, NM_SETTING_DCB_ERROR_INVALID_PROPERTY); \
+ g_assert (success == FALSE); \
+ } else { \
+ g_assert_no_error (error); \
+ g_assert (success); \
+ } \
+ g_clear_error (&error); \
+ \
+ /* Set the enable flag and re-verify, this time it should be valid */ \
+ g_object_set (G_OBJECT (s_dcb), NM_SETTING_DCB_APP_##ucprop##_FLAGS, NM_SETTING_DCB_FLAG_ENABLE, NULL); \
+ success = nm_setting_verify (NM_SETTING (s_dcb), NULL, &error); \
+ g_assert_no_error (error); \
+ g_assert (success); \
+ \
+ g_object_set (G_OBJECT (s_dcb), NM_SETTING_DCB_APP_##ucprop##_PRIORITY, 0, NULL); \
+}
+
+static void
+test_dcb_app_priorities (void)
+{
+ NMSettingDcb *s_dcb;
+ GError *error = NULL;
+ gboolean success;
+
+ s_dcb = (NMSettingDcb *) nm_setting_dcb_new ();
+ g_assert (s_dcb);
+
+ /* Defaults */
+ g_assert_cmpint (nm_setting_dcb_get_app_fcoe_priority (s_dcb), ==, -1);
+ g_assert_cmpint (nm_setting_dcb_get_app_iscsi_priority (s_dcb), ==, -1);
+ g_assert_cmpint (nm_setting_dcb_get_app_fip_priority (s_dcb), ==, -1);
+
+ TEST_APP_PRIORITY (fcoe, FCOE, 6);
+ TEST_APP_PRIORITY (iscsi, ISCSI, 5);
+ TEST_APP_PRIORITY (fip, FIP, 4);
+
+ TEST_APP_PRIORITY (fcoe, FCOE, -1);
+ TEST_APP_PRIORITY (iscsi, ISCSI, -1);
+ TEST_APP_PRIORITY (fip, FIP, -1);
+}
+
+#define TEST_PRIORITY_VALID(fn, id, val, flagsprop, verify) \
+{ \
+ /* Assert that setting the value gets the same value back out */ \
+ nm_setting_dcb_set_priority_##fn (s_dcb, id, val); \
+ g_assert_cmpint (nm_setting_dcb_get_priority_##fn (s_dcb, id), ==, val); \
+ \
+ if (verify) { \
+ if (val != 0) { \
+ /* Assert that verify fails because the flags do not include 'enabled' \
+ * and a value has been set. \
+ */ \
+ success = nm_setting_verify (NM_SETTING (s_dcb), NULL, &error); \
+ g_assert_error (error, NM_SETTING_DCB_ERROR, NM_SETTING_DCB_ERROR_INVALID_PROPERTY); \
+ g_assert (success == FALSE); \
+ g_clear_error (&error); \
+ } \
+ \
+ /* Assert that adding the 'enabled' flag verifies the setting */ \
+ g_object_set (G_OBJECT (s_dcb), NM_SETTING_DCB_PRIORITY_##flagsprop##_FLAGS, NM_SETTING_DCB_FLAG_ENABLE, NULL); \
+ success = nm_setting_verify (NM_SETTING (s_dcb), NULL, &error); \
+ g_assert_no_error (error); \
+ g_assert (success); \
+ } \
+ \
+ /* Reset everything */ \
+ g_object_set (G_OBJECT (s_dcb), NM_SETTING_DCB_PRIORITY_##flagsprop##_FLAGS, NM_SETTING_DCB_FLAG_NONE, NULL); \
+ nm_setting_dcb_set_priority_##fn (s_dcb, id, 0); \
+}
+
+/* If Priority Groups are enabled, PG bandwidth must equal 100% */
+#define SET_VALID_PRIORITY_GROUP_BANDWIDTH \
+{ \
+ guint x; \
+ for (x = 0; x < 7; x++) \
+ nm_setting_dcb_set_priority_group_bandwidth (s_dcb, x, 12); \
+ nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 7, 16); \
+}
+
+static void
+test_dcb_priorities_valid (void)
+{
+ NMSettingDcb *s_dcb;
+ GError *error = NULL;
+ gboolean success;
+ guint i;
+
+ s_dcb = (NMSettingDcb *) nm_setting_dcb_new ();
+ g_assert (s_dcb);
+
+ for (i = 0; i < 8; i++)
+ TEST_PRIORITY_VALID (flow_control, i, TRUE, FLOW_CONTROL, TRUE);
+
+ SET_VALID_PRIORITY_GROUP_BANDWIDTH
+ for (i = 0; i < 8; i++) {
+ TEST_PRIORITY_VALID (group_id, i, i, GROUP, TRUE);
+ TEST_PRIORITY_VALID (group_id, i, 7 - i, GROUP, TRUE);
+ }
+
+ /* Clear PG bandwidth from earlier tests */
+ for (i = 0; i < 8; i++)
+ nm_setting_dcb_set_priority_group_bandwidth (s_dcb, i, 0);
+
+ /* Priority Group Bandwidth must add up to 100% if enabled, which requires
+ * some dancing for verifying individual values here.
+ */
+ for (i = 0; i < 8; i++) {
+ guint other = 7 - (i % 8);
+
+ /* Set another priority group to the remaining bandwidth */
+ nm_setting_dcb_set_priority_group_bandwidth (s_dcb, other, 100 - i);
+ TEST_PRIORITY_VALID (group_bandwidth, i, i, GROUP, TRUE);
+
+ /* Set another priority group to the remaining bandwidth */
+ nm_setting_dcb_set_priority_group_bandwidth (s_dcb, other, 100 - (7 - i));
+ TEST_PRIORITY_VALID (group_bandwidth, i, 7 - i, GROUP, TRUE);
+
+ /* Clear remaining bandwidth */
+ nm_setting_dcb_set_priority_group_bandwidth (s_dcb, other, 0);
+ }
+
+ SET_VALID_PRIORITY_GROUP_BANDWIDTH
+ for (i = 0; i < 8; i++) {
+ TEST_PRIORITY_VALID (bandwidth, i, i, GROUP, TRUE);
+ TEST_PRIORITY_VALID (bandwidth, i, 7 - i, GROUP, TRUE);
+ }
+
+ SET_VALID_PRIORITY_GROUP_BANDWIDTH
+ for (i = 0; i < 8; i++)
+ TEST_PRIORITY_VALID (strict_bandwidth, i, TRUE, GROUP, TRUE);
+
+ SET_VALID_PRIORITY_GROUP_BANDWIDTH
+ for (i = 0; i < 8; i++) {
+ TEST_PRIORITY_VALID (traffic_class, i, i, GROUP, TRUE);
+ TEST_PRIORITY_VALID (traffic_class, i, 7 - i, GROUP, TRUE);
+ }
+}
+
+static void
+test_dcb_bandwidth_sums (void)
+{
+ NMSettingDcb *s_dcb;
+ GError *error = NULL;
+ gboolean success;
+
+ s_dcb = (NMSettingDcb *) nm_setting_dcb_new ();
+ g_assert (s_dcb);
+
+ /* Assert that setting the value gets the same value back out */
+ nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 0, 9);
+ nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 1, 10);
+ nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 2, 11);
+ nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 3, 12);
+ nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 4, 13);
+ nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 5, 14);
+ nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 6, 15);
+ nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 7, 16);
+
+ /* Assert verify success when sums total 100% */
+ g_object_set (G_OBJECT (s_dcb), NM_SETTING_DCB_PRIORITY_GROUP_FLAGS, NM_SETTING_DCB_FLAG_ENABLE, NULL);
+ success = nm_setting_verify (NM_SETTING (s_dcb), NULL, &error);
+ g_assert_no_error (error);
+ g_assert (success);
+
+ /* Assert verify fails when sums do not total 100% */
+ nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 4, 20);
+ success = nm_setting_verify (NM_SETTING (s_dcb), NULL, &error);
+ g_assert_error (error, NM_SETTING_DCB_ERROR, NM_SETTING_DCB_ERROR_INVALID_PROPERTY);
+ g_assert (success == FALSE);
+ g_clear_error (&error);
+}
+
+#define TPATH "/libnm-util/settings/dcb/"
+
+int main (int argc, char **argv)
+{
+ GError *error = NULL;
+ gboolean success;
+
+ g_test_init (&argc, &argv, NULL);
+
+#if !GLIB_CHECK_VERSION (2, 35, 0)
+ g_type_init ();
+#endif
+
+ success = nm_utils_init (&error);
+ g_assert_no_error (error);
+ g_assert (success);
+
+#if !GLIB_CHECK_VERSION(2,34,0)
+ g_log_set_always_fatal (G_LOG_LEVEL_CRITICAL);
+#endif
+
+ g_test_add_func (TPATH "flags-valid", test_dcb_flags_valid);
+ g_test_add_func (TPATH "flags-invalid", test_dcb_flags_invalid);
+ g_test_add_func (TPATH "app-priorities", test_dcb_app_priorities);
+ g_test_add_func (TPATH "priorities", test_dcb_priorities_valid);
+ g_test_add_func (TPATH "bandwidth-sums", test_dcb_bandwidth_sums);
+
+ return g_test_run ();
+}
+
diff --git a/libnm-core/tests/test-settings-defaults.c b/libnm-core/tests/test-settings-defaults.c
new file mode 100644
index 0000000000..9104b4bc6c
--- /dev/null
+++ b/libnm-core/tests/test-settings-defaults.c
@@ -0,0 +1,134 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2008 - 2011 Red Hat, Inc.
+ *
+ */
+
+#include <glib.h>
+#include <string.h>
+
+#include <nm-utils.h>
+
+#include "nm-setting-8021x.h"
+#include "nm-setting-cdma.h"
+#include "nm-setting-connection.h"
+#include "nm-setting-gsm.h"
+#include "nm-setting-ip4-config.h"
+#include "nm-setting-ip6-config.h"
+#include "nm-setting-ppp.h"
+#include "nm-setting-pppoe.h"
+#include "nm-setting-serial.h"
+#include "nm-setting-vpn.h"
+#include "nm-setting-wired.h"
+#include "nm-setting-wireless.h"
+#include "nm-setting-wireless-security.h"
+
+#include "nm-test-utils.h"
+
+static void
+test_defaults (GType type, const char *name)
+{
+ GParamSpec **property_specs;
+ guint n_property_specs;
+ GObject *setting;
+ int i;
+
+ setting = g_object_new (type, NULL);
+
+ property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (setting), &n_property_specs);
+ ASSERT (property_specs != NULL,
+ name, "couldn't find property specs for object of type '%s'",
+ g_type_name (G_OBJECT_TYPE (setting)));
+
+ for (i = 0; i < n_property_specs; i++) {
+ GParamSpec *prop_spec = property_specs[i];
+ GValue value = G_VALUE_INIT;
+ GValue defvalue = G_VALUE_INIT;
+ char *actual, *expected;
+ gboolean ok = FALSE;
+
+ /* Ignore non-fundamental types since they won't really have
+ * defaults.
+ */
+ if (!G_TYPE_IS_FUNDAMENTAL (prop_spec->value_type))
+ continue;
+
+ g_value_init (&value, prop_spec->value_type);
+ g_object_get_property (G_OBJECT (setting), prop_spec->name, &value);
+
+ g_value_init (&defvalue, prop_spec->value_type);
+ g_param_value_set_default (prop_spec, &defvalue);
+
+ actual = g_strdup_value_contents (&value);
+ expected = g_strdup_value_contents (&defvalue);
+
+ if (!strcmp (prop_spec->name, NM_SETTING_NAME)) {
+ /* 'name' is always the setting name, not the default value */
+ ok = !strcmp (nm_setting_get_name (NM_SETTING (setting)), name);
+ g_free (expected);
+ expected = g_strdup (name);
+ } else
+ ok = g_param_value_defaults (prop_spec, &value);
+
+ ASSERT (ok,
+ name, "property '%s' value '%s' not the expected default value '%s'",
+ prop_spec->name, actual, expected);
+
+ g_free (actual);
+ g_free (expected);
+ g_value_unset (&value);
+ g_value_unset (&defvalue);
+ }
+
+ g_free (property_specs);
+ g_object_unref (setting);
+}
+
+int main (int argc, char **argv)
+{
+ GError *error = NULL;
+ char *base;
+
+#if !GLIB_CHECK_VERSION (2, 35, 0)
+ g_type_init ();
+#endif
+
+ if (!nm_utils_init (&error))
+ FAIL ("nm-utils-init", "failed to initialize libnm-util: %s", error->message);
+
+ /* The tests */
+ test_defaults (NM_TYPE_SETTING_CONNECTION, NM_SETTING_CONNECTION_SETTING_NAME);
+ test_defaults (NM_TYPE_SETTING_802_1X, NM_SETTING_802_1X_SETTING_NAME);
+ test_defaults (NM_TYPE_SETTING_CDMA, NM_SETTING_CDMA_SETTING_NAME);
+ test_defaults (NM_TYPE_SETTING_GSM, NM_SETTING_GSM_SETTING_NAME);
+ test_defaults (NM_TYPE_SETTING_IP4_CONFIG, NM_SETTING_IP4_CONFIG_SETTING_NAME);
+ test_defaults (NM_TYPE_SETTING_IP6_CONFIG, NM_SETTING_IP6_CONFIG_SETTING_NAME);
+ test_defaults (NM_TYPE_SETTING_PPP, NM_SETTING_PPP_SETTING_NAME);
+ test_defaults (NM_TYPE_SETTING_PPPOE, NM_SETTING_PPPOE_SETTING_NAME);
+ test_defaults (NM_TYPE_SETTING_SERIAL, NM_SETTING_SERIAL_SETTING_NAME);
+ test_defaults (NM_TYPE_SETTING_VPN, NM_SETTING_VPN_SETTING_NAME);
+ test_defaults (NM_TYPE_SETTING_WIRED, NM_SETTING_WIRED_SETTING_NAME);
+ test_defaults (NM_TYPE_SETTING_WIRELESS, NM_SETTING_WIRELESS_SETTING_NAME);
+ test_defaults (NM_TYPE_SETTING_WIRELESS_SECURITY, NM_SETTING_WIRELESS_SECURITY_SETTING_NAME);
+
+ base = g_path_get_basename (argv[0]);
+ fprintf (stdout, "%s: SUCCESS\n", base);
+ g_free (base);
+ return 0;
+}
+
diff --git a/libnm/nm-access-point.c b/libnm/nm-access-point.c
new file mode 100644
index 0000000000..b462a6b169
--- /dev/null
+++ b/libnm/nm-access-point.c
@@ -0,0 +1,673 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2008 Novell, Inc.
+ * Copyright 2007 - 2011 Red Hat, Inc.
+ */
+
+#include <config.h>
+#include <string.h>
+#include <netinet/ether.h>
+
+#include "nm-glib-compat.h"
+
+#include <nm-connection.h>
+#include <nm-setting-connection.h>
+#include <nm-setting-wireless.h>
+#include <nm-setting-wireless-security.h>
+#include <nm-utils.h>
+
+#include "nm-access-point.h"
+#include "NetworkManager.h"
+#include "nm-types-private.h"
+#include "nm-object-private.h"
+
+G_DEFINE_TYPE (NMAccessPoint, nm_access_point, NM_TYPE_OBJECT)
+
+#define NM_ACCESS_POINT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_ACCESS_POINT, NMAccessPointPrivate))
+
+typedef struct {
+ DBusGProxy *proxy;
+
+ NM80211ApFlags flags;
+ NM80211ApSecurityFlags wpa_flags;
+ NM80211ApSecurityFlags rsn_flags;
+ GByteArray *ssid;
+ guint32 frequency;
+ char *bssid;
+ NM80211Mode mode;
+ guint32 max_bitrate;
+ guint8 strength;
+} NMAccessPointPrivate;
+
+enum {
+ PROP_0,
+ PROP_FLAGS,
+ PROP_WPA_FLAGS,
+ PROP_RSN_FLAGS,
+ PROP_SSID,
+ PROP_FREQUENCY,
+ PROP_HW_ADDRESS,
+ PROP_MODE,
+ PROP_MAX_BITRATE,
+ PROP_STRENGTH,
+ PROP_BSSID,
+
+ LAST_PROP
+};
+
+/**
+ * nm_access_point_new:
+ * @connection: the #DBusGConnection
+ * @path: the DBusobject path of the access point
+ *
+ * Creates a new #NMAccessPoint.
+ *
+ * Returns: (transfer full): a new access point
+ **/
+GObject *
+nm_access_point_new (DBusGConnection *connection, const char *path)
+{
+ g_return_val_if_fail (connection != NULL, NULL);
+ g_return_val_if_fail (path != NULL, NULL);
+
+ return (GObject *) g_object_new (NM_TYPE_ACCESS_POINT,
+ NM_OBJECT_DBUS_CONNECTION, connection,
+ NM_OBJECT_DBUS_PATH, path,
+ NULL);
+}
+
+/**
+ * nm_access_point_get_flags:
+ * @ap: a #NMAccessPoint
+ *
+ * Gets the flags of the access point.
+ *
+ * Returns: the flags
+ **/
+NM80211ApFlags
+nm_access_point_get_flags (NMAccessPoint *ap)
+{
+ g_return_val_if_fail (NM_IS_ACCESS_POINT (ap), NM_802_11_AP_FLAGS_NONE);
+
+ _nm_object_ensure_inited (NM_OBJECT (ap));
+ return NM_ACCESS_POINT_GET_PRIVATE (ap)->flags;
+}
+
+/**
+ * nm_access_point_get_wpa_flags:
+ * @ap: a #NMAccessPoint
+ *
+ * Gets the WPA (version 1) flags of the access point.
+ *
+ * Returns: the WPA flags
+ **/
+NM80211ApSecurityFlags
+nm_access_point_get_wpa_flags (NMAccessPoint *ap)
+{
+ g_return_val_if_fail (NM_IS_ACCESS_POINT (ap), NM_802_11_AP_SEC_NONE);
+
+ _nm_object_ensure_inited (NM_OBJECT (ap));
+ return NM_ACCESS_POINT_GET_PRIVATE (ap)->wpa_flags;
+}
+
+/**
+ * nm_access_point_get_rsn_flags:
+ * @ap: a #NMAccessPoint
+ *
+ * Gets the RSN (Robust Secure Network, ie WPA version 2) flags of the access
+ * point.
+ *
+ * Returns: the RSN flags
+ **/
+NM80211ApSecurityFlags
+nm_access_point_get_rsn_flags (NMAccessPoint *ap)
+{
+ g_return_val_if_fail (NM_IS_ACCESS_POINT (ap), NM_802_11_AP_SEC_NONE);
+
+ _nm_object_ensure_inited (NM_OBJECT (ap));
+ return NM_ACCESS_POINT_GET_PRIVATE (ap)->rsn_flags;
+}
+
+/**
+ * nm_access_point_get_ssid:
+ * @ap: a #NMAccessPoint
+ *
+ * Gets the SSID of the access point.
+ *
+ * Returns: the #GByteArray containing the SSID. This is the internal copy used by the
+ * access point, and must not be modified.
+ **/
+const GByteArray *
+nm_access_point_get_ssid (NMAccessPoint *ap)
+{
+ g_return_val_if_fail (NM_IS_ACCESS_POINT (ap), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (ap));
+ return NM_ACCESS_POINT_GET_PRIVATE (ap)->ssid;
+}
+
+/**
+ * nm_access_point_get_frequency:
+ * @ap: a #NMAccessPoint
+ *
+ * Gets the frequency of the access point.
+ *
+ * Returns: the frequency
+ **/
+guint32
+nm_access_point_get_frequency (NMAccessPoint *ap)
+{
+ g_return_val_if_fail (NM_IS_ACCESS_POINT (ap), 0);
+
+ _nm_object_ensure_inited (NM_OBJECT (ap));
+ return NM_ACCESS_POINT_GET_PRIVATE (ap)->frequency;
+}
+
+/**
+ * nm_access_point_get_bssid:
+ * @ap: a #NMAccessPoint
+ *
+ * Gets the Basic Service Set ID (BSSID) of the Wi-Fi access point.
+ *
+ * Returns: the BSSID of the access point. This is an internal string and must
+ * not be modified or freed.
+ **/
+const char *
+nm_access_point_get_bssid (NMAccessPoint *ap)
+{
+ g_return_val_if_fail (NM_IS_ACCESS_POINT (ap), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (ap));
+ return NM_ACCESS_POINT_GET_PRIVATE (ap)->bssid;
+}
+
+/**
+ * nm_access_point_get_hw_address:
+ * @ap: a #NMAccessPoint
+ *
+ * Gets the hardware (MAC) address of the access point.
+ *
+ * Returns: the hardware address of the access point. This is the internal string used by the
+ * access point and must not be modified.
+ *
+ * Deprecated: 0.9: Use nm_access_point_get_bssid() instead.
+ **/
+const char *
+nm_access_point_get_hw_address (NMAccessPoint *ap)
+{
+ return nm_access_point_get_bssid (ap);
+}
+
+/**
+ * nm_access_point_get_mode:
+ * @ap: a #NMAccessPoint
+ *
+ * Gets the mode of the access point.
+ *
+ * Returns: the mode
+ **/
+NM80211Mode
+nm_access_point_get_mode (NMAccessPoint *ap)
+{
+ g_return_val_if_fail (NM_IS_ACCESS_POINT (ap), 0);
+
+ _nm_object_ensure_inited (NM_OBJECT (ap));
+ return NM_ACCESS_POINT_GET_PRIVATE (ap)->mode;
+}
+
+/**
+ * nm_access_point_get_max_bitrate:
+ * @ap: a #NMAccessPoint
+ *
+ * Gets the maximum bit rate of the access point in kbit/s.
+ *
+ * Returns: the maximum bit rate (kbit/s)
+ **/
+guint32
+nm_access_point_get_max_bitrate (NMAccessPoint *ap)
+{
+ g_return_val_if_fail (NM_IS_ACCESS_POINT (ap), 0);
+
+ _nm_object_ensure_inited (NM_OBJECT (ap));
+ return NM_ACCESS_POINT_GET_PRIVATE (ap)->max_bitrate;
+}
+
+/**
+ * nm_access_point_get_strength:
+ * @ap: a #NMAccessPoint
+ *
+ * Gets the current signal strength of the access point.
+ *
+ * Returns: the signal strength
+ **/
+guint8
+nm_access_point_get_strength (NMAccessPoint *ap)
+{
+ g_return_val_if_fail (NM_IS_ACCESS_POINT (ap), 0);
+
+ _nm_object_ensure_inited (NM_OBJECT (ap));
+ return NM_ACCESS_POINT_GET_PRIVATE (ap)->strength;
+}
+
+/**
+ * nm_access_point_connection_valid:
+ * @ap: an #NMAccessPoint to validate @connection against
+ * @connection: an #NMConnection to validate against @ap
+ *
+ * Validates a given connection against a given Wi-Fi access point to ensure that
+ * the connection may be activated with that AP. The connection must match the
+ * @ap's SSID, (if given) BSSID, and other attributes like security settings,
+ * channel, band, etc.
+ *
+ * Returns: %TRUE if the connection may be activated with this Wi-Fi AP,
+ * %FALSE if it cannot be.
+ **/
+gboolean
+nm_access_point_connection_valid (NMAccessPoint *ap, NMConnection *connection)
+{
+ NMSettingConnection *s_con;
+ NMSettingWireless *s_wifi;
+ NMSettingWirelessSecurity *s_wsec;
+ const char *ctype, *ap_bssid_str;
+ const GByteArray *setting_ssid;
+ const GByteArray *ap_ssid;
+ const GByteArray *setting_bssid;
+ struct ether_addr *ap_bssid;
+ const char *setting_mode;
+ NM80211Mode ap_mode;
+ const char *setting_band;
+ guint32 ap_freq, setting_chan, ap_chan;
+
+ s_con = nm_connection_get_setting_connection (connection);
+ g_assert (s_con);
+ ctype = nm_setting_connection_get_connection_type (s_con);
+ if (strcmp (ctype, NM_SETTING_WIRELESS_SETTING_NAME) != 0)
+ return FALSE;
+
+ s_wifi = nm_connection_get_setting_wireless (connection);
+ if (!s_wifi)
+ return FALSE;
+
+ /* SSID checks */
+ ap_ssid = nm_access_point_get_ssid (ap);
+ g_warn_if_fail (ap_ssid != NULL);
+ setting_ssid = nm_setting_wireless_get_ssid (s_wifi);
+ if (!setting_ssid || !ap_ssid || (setting_ssid->len != ap_ssid->len))
+ return FALSE;
+ if (memcmp (setting_ssid->data, ap_ssid->data, ap_ssid->len) != 0)
+ return FALSE;
+
+ /* BSSID checks */
+ ap_bssid_str = nm_access_point_get_bssid (ap);
+ g_warn_if_fail (ap_bssid_str);
+ setting_bssid = nm_setting_wireless_get_bssid (s_wifi);
+ if (setting_bssid && ap_bssid_str) {
+ g_assert (setting_bssid->len == ETH_ALEN);
+ ap_bssid = ether_aton (ap_bssid_str);
+ g_warn_if_fail (ap_bssid);
+ if (ap_bssid) {
+ if (memcmp (ap_bssid->ether_addr_octet, setting_bssid->data, ETH_ALEN) != 0)
+ return FALSE;
+ }
+ }
+
+ /* Mode */
+ ap_mode = nm_access_point_get_mode (ap);
+ g_warn_if_fail (ap_mode != NM_802_11_MODE_UNKNOWN);
+ setting_mode = nm_setting_wireless_get_mode (s_wifi);
+ if (setting_mode && ap_mode) {
+ if (!strcmp (setting_mode, "infrastructure") && (ap_mode != NM_802_11_MODE_INFRA))
+ return FALSE;
+ if (!strcmp (setting_mode, "adhoc") && (ap_mode != NM_802_11_MODE_ADHOC))
+ return FALSE;
+ /* Hotspot never matches against APs as it's a device-specific mode. */
+ if (!strcmp (setting_mode, "ap"))
+ return FALSE;
+ }
+
+ /* Band and Channel/Frequency */
+ ap_freq = nm_access_point_get_frequency (ap);
+ if (ap_freq) {
+ setting_band = nm_setting_wireless_get_band (s_wifi);
+ if (g_strcmp0 (setting_band, "a") == 0) {
+ if (ap_freq < 4915 || ap_freq > 5825)
+ return FALSE;
+ } else if (g_strcmp0 (setting_band, "bg") == 0) {
+ if (ap_freq < 2412 || ap_freq > 2484)
+ return FALSE;
+ }
+
+ setting_chan = nm_setting_wireless_get_channel (s_wifi);
+ if (setting_chan) {
+ ap_chan = nm_utils_wifi_freq_to_channel (ap_freq);
+ if (setting_chan != ap_chan)
+ return FALSE;
+ }
+ }
+
+ s_wsec = nm_connection_get_setting_wireless_security (connection);
+ if (!nm_setting_wireless_ap_security_compatible (s_wifi,
+ s_wsec,
+ nm_access_point_get_flags (ap),
+ nm_access_point_get_wpa_flags (ap),
+ nm_access_point_get_rsn_flags (ap),
+ ap_mode))
+ return FALSE;
+
+ return TRUE;
+}
+
+/**
+ * nm_access_point_filter_connections:
+ * @ap: an #NMAccessPoint to filter connections for
+ * @connections: (element-type NMConnection): a list of
+ * #NMConnection objects to filter
+ *
+ * Filters a given list of connections for a given #NMAccessPoint object and
+ * return connections which may be activated with the access point. Any
+ * returned connections will match the @ap's SSID and (if given) BSSID and
+ * other attributes like security settings, channel, etc.
+ *
+ * To obtain the list of connections that are compatible with this access point,
+ * use nm_remote_settings_list_connections() and then filter the returned list
+ * for a given #NMDevice using nm_device_filter_connections() and finally
+ * filter that list with this function.
+ *
+ * Returns: (transfer container) (element-type NMConnection): a
+ * list of #NMConnection objects that could be activated with the given @ap.
+ * The elements of the list are owned by their creator and should not be freed
+ * by the caller, but the returned list itself is owned by the caller and should
+ * be freed with g_slist_free() when it is no longer required.
+ **/
+GSList *
+nm_access_point_filter_connections (NMAccessPoint *ap, const GSList *connections)
+{
+ GSList *filtered = NULL;
+ const GSList *iter;
+
+ for (iter = connections; iter; iter = g_slist_next (iter)) {
+ NMConnection *candidate = NM_CONNECTION (iter->data);
+
+ if (nm_access_point_connection_valid (ap, candidate))
+ filtered = g_slist_prepend (filtered, candidate);
+ }
+
+ return g_slist_reverse (filtered);
+}
+
+/************************************************************/
+
+static void
+nm_access_point_init (NMAccessPoint *ap)
+{
+}
+
+static void
+dispose (GObject *object)
+{
+ NMAccessPointPrivate *priv = NM_ACCESS_POINT_GET_PRIVATE (object);
+
+ g_clear_object (&priv->proxy);
+
+ G_OBJECT_CLASS (nm_access_point_parent_class)->dispose (object);
+}
+
+static void
+finalize (GObject *object)
+{
+ NMAccessPointPrivate *priv = NM_ACCESS_POINT_GET_PRIVATE (object);
+
+ if (priv->ssid)
+ g_byte_array_free (priv->ssid, TRUE);
+
+ g_free (priv->bssid);
+
+ G_OBJECT_CLASS (nm_access_point_parent_class)->finalize (object);
+}
+
+static void
+get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NMAccessPoint *ap = NM_ACCESS_POINT (object);
+
+ _nm_object_ensure_inited (NM_OBJECT (object));
+
+ switch (prop_id) {
+ case PROP_FLAGS:
+ g_value_set_uint (value, nm_access_point_get_flags (ap));
+ break;
+ case PROP_WPA_FLAGS:
+ g_value_set_uint (value, nm_access_point_get_wpa_flags (ap));
+ break;
+ case PROP_RSN_FLAGS:
+ g_value_set_uint (value, nm_access_point_get_rsn_flags (ap));
+ break;
+ case PROP_SSID:
+ g_value_set_boxed (value, nm_access_point_get_ssid (ap));
+ break;
+ case PROP_FREQUENCY:
+ g_value_set_uint (value, nm_access_point_get_frequency (ap));
+ break;
+ case PROP_HW_ADDRESS:
+ g_value_set_string (value, nm_access_point_get_bssid (ap));
+ break;
+ case PROP_BSSID:
+ g_value_set_string (value, nm_access_point_get_bssid (ap));
+ break;
+ case PROP_MODE:
+ g_value_set_uint (value, nm_access_point_get_mode (ap));
+ break;
+ case PROP_MAX_BITRATE:
+ g_value_set_uint (value, nm_access_point_get_max_bitrate (ap));
+ break;
+ case PROP_STRENGTH:
+ g_value_set_uchar (value, nm_access_point_get_strength (ap));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static gboolean
+demarshal_ssid (NMObject *object, GParamSpec *pspec, GValue *value, gpointer field)
+{
+ if (!_nm_ssid_demarshal (value, (GByteArray **) field))
+ return FALSE;
+
+ _nm_object_queue_notify (object, NM_ACCESS_POINT_SSID);
+ return TRUE;
+}
+
+static void
+register_properties (NMAccessPoint *ap)
+{
+ NMAccessPointPrivate *priv = NM_ACCESS_POINT_GET_PRIVATE (ap);
+ const NMPropertiesInfo property_info[] = {
+ { NM_ACCESS_POINT_FLAGS, &priv->flags },
+ { NM_ACCESS_POINT_WPA_FLAGS, &priv->wpa_flags },
+ { NM_ACCESS_POINT_RSN_FLAGS, &priv->rsn_flags },
+ { NM_ACCESS_POINT_SSID, &priv->ssid, demarshal_ssid },
+ { NM_ACCESS_POINT_FREQUENCY, &priv->frequency },
+ { NM_ACCESS_POINT_HW_ADDRESS, &priv->bssid },
+ { NM_ACCESS_POINT_MODE, &priv->mode },
+ { NM_ACCESS_POINT_MAX_BITRATE, &priv->max_bitrate },
+ { NM_ACCESS_POINT_STRENGTH, &priv->strength },
+ { NULL },
+ };
+
+ _nm_object_register_properties (NM_OBJECT (ap),
+ priv->proxy,
+ property_info);
+}
+
+static void
+constructed (GObject *object)
+{
+ NMAccessPointPrivate *priv;
+
+ G_OBJECT_CLASS (nm_access_point_parent_class)->constructed (object);
+
+ priv = NM_ACCESS_POINT_GET_PRIVATE (object);
+ priv->proxy = _nm_object_new_proxy (NM_OBJECT (object), NULL, NM_DBUS_INTERFACE_ACCESS_POINT);
+ register_properties (NM_ACCESS_POINT (object));
+}
+
+
+static void
+nm_access_point_class_init (NMAccessPointClass *ap_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (ap_class);
+
+ g_type_class_add_private (ap_class, sizeof (NMAccessPointPrivate));
+
+ /* virtual methods */
+ object_class->constructed = constructed;
+ object_class->get_property = get_property;
+ object_class->dispose = dispose;
+ object_class->finalize = finalize;
+
+ /* properties */
+
+ /**
+ * NMAccessPoint:flags:
+ *
+ * The flags of the access point.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_FLAGS,
+ g_param_spec_uint (NM_ACCESS_POINT_FLAGS, "", "",
+ NM_802_11_AP_FLAGS_NONE,
+ NM_802_11_AP_FLAGS_PRIVACY,
+ NM_802_11_AP_FLAGS_NONE,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMAccessPoint:wpa-flags:
+ *
+ * The WPA flags of the access point.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_WPA_FLAGS,
+ g_param_spec_uint (NM_ACCESS_POINT_WPA_FLAGS, "", "",
+ 0, G_MAXUINT32, 0,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMAccessPoint:rsn-flags:
+ *
+ * The RSN flags of the access point.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_RSN_FLAGS,
+ g_param_spec_uint (NM_ACCESS_POINT_RSN_FLAGS, "", "",
+ 0, G_MAXUINT32, 0,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMAccessPoint:ssid:
+ *
+ * The SSID of the access point.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_SSID,
+ g_param_spec_boxed (NM_ACCESS_POINT_SSID, "", "",
+ NM_TYPE_SSID,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMAccessPoint:frequency:
+ *
+ * The frequency of the access point.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_FREQUENCY,
+ g_param_spec_uint (NM_ACCESS_POINT_FREQUENCY, "", "",
+ 0, 10000, 0,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMAccessPoint:bssid:
+ *
+ * The BSSID of the access point.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_BSSID,
+ g_param_spec_string (NM_ACCESS_POINT_BSSID, "", "",
+ NULL,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMAccessPoint:hw-address:
+ *
+ * The hardware address of the access point.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_HW_ADDRESS,
+ g_param_spec_string (NM_ACCESS_POINT_HW_ADDRESS, "", "",
+ NULL,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMAccessPoint:mode:
+ *
+ * The mode of the access point; either "infrastructure" (a central
+ * coordinator of the wireless network allowing clients to connect) or
+ * "ad-hoc" (a network with no central controller).
+ **/
+ g_object_class_install_property
+ (object_class, PROP_MODE,
+ g_param_spec_uint (NM_ACCESS_POINT_MODE, "", "",
+ NM_802_11_MODE_ADHOC, NM_802_11_MODE_INFRA, NM_802_11_MODE_INFRA,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMAccessPoint:max-bitrate:
+ *
+ * The maximum bit rate of the access point in kbit/s.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_MAX_BITRATE,
+ g_param_spec_uint (NM_ACCESS_POINT_MAX_BITRATE, "", "",
+ 0, G_MAXUINT32, 0,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMAccessPoint:strength:
+ *
+ * The current signal strength of the access point.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_STRENGTH,
+ g_param_spec_uchar (NM_ACCESS_POINT_STRENGTH, "", "",
+ 0, G_MAXUINT8, 0,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/libnm/nm-access-point.h b/libnm/nm-access-point.h
new file mode 100644
index 0000000000..d3150f8eee
--- /dev/null
+++ b/libnm/nm-access-point.h
@@ -0,0 +1,96 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2008 Novell, Inc.
+ * Copyright 2007 - 2011 Red Hat, Inc.
+ */
+
+#ifndef NM_ACCESS_POINT_H
+#define NM_ACCESS_POINT_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <NetworkManager.h>
+#include <nm-connection.h>
+#include "nm-object.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_ACCESS_POINT (nm_access_point_get_type ())
+#define NM_ACCESS_POINT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_ACCESS_POINT, NMAccessPoint))
+#define NM_ACCESS_POINT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_ACCESS_POINT, NMAccessPointClass))
+#define NM_IS_ACCESS_POINT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_ACCESS_POINT))
+#define NM_IS_ACCESS_POINT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_ACCESS_POINT))
+#define NM_ACCESS_POINT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_ACCESS_POINT, NMAccessPointClass))
+
+#define NM_ACCESS_POINT_FLAGS "flags"
+#define NM_ACCESS_POINT_WPA_FLAGS "wpa-flags"
+#define NM_ACCESS_POINT_RSN_FLAGS "rsn-flags"
+#define NM_ACCESS_POINT_SSID "ssid"
+#define NM_ACCESS_POINT_BSSID "bssid"
+#define NM_ACCESS_POINT_FREQUENCY "frequency"
+#define NM_ACCESS_POINT_MODE "mode"
+#define NM_ACCESS_POINT_MAX_BITRATE "max-bitrate"
+#define NM_ACCESS_POINT_STRENGTH "strength"
+
+/* DEPRECATED */
+#define NM_ACCESS_POINT_HW_ADDRESS "hw-address"
+
+
+typedef struct {
+ NMObject parent;
+} NMAccessPoint;
+
+typedef struct {
+ NMObjectClass parent;
+
+ /* Padding for future expansion */
+ void (*_reserved1) (void);
+ void (*_reserved2) (void);
+ void (*_reserved3) (void);
+ void (*_reserved4) (void);
+ void (*_reserved5) (void);
+ void (*_reserved6) (void);
+} NMAccessPointClass;
+
+GType nm_access_point_get_type (void);
+
+GObject *nm_access_point_new (DBusGConnection *connection, const char *path);
+
+NM80211ApFlags nm_access_point_get_flags (NMAccessPoint *ap);
+NM80211ApSecurityFlags nm_access_point_get_wpa_flags (NMAccessPoint *ap);
+NM80211ApSecurityFlags nm_access_point_get_rsn_flags (NMAccessPoint *ap);
+const GByteArray * nm_access_point_get_ssid (NMAccessPoint *ap);
+const char * nm_access_point_get_bssid (NMAccessPoint *ap);
+guint32 nm_access_point_get_frequency (NMAccessPoint *ap);
+NM80211Mode nm_access_point_get_mode (NMAccessPoint *ap);
+guint32 nm_access_point_get_max_bitrate (NMAccessPoint *ap);
+guint8 nm_access_point_get_strength (NMAccessPoint *ap);
+
+GSList * nm_access_point_filter_connections (NMAccessPoint *ap,
+ const GSList *connections);
+
+gboolean nm_access_point_connection_valid (NMAccessPoint *ap,
+ NMConnection *connection);
+
+/* DEPRECATED */
+NM_DEPRECATED_IN_0_9_10
+const char * nm_access_point_get_hw_address (NMAccessPoint *ap);
+
+G_END_DECLS
+
+#endif /* NM_ACCESS_POINT_H */
diff --git a/libnm/nm-active-connection.c b/libnm/nm-active-connection.c
new file mode 100644
index 0000000000..358264fd25
--- /dev/null
+++ b/libnm/nm-active-connection.c
@@ -0,0 +1,851 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2014 Red Hat, Inc.
+ * Copyright 2008 Novell, Inc.
+ */
+
+#include <string.h>
+
+#include "NetworkManager.h"
+#include "nm-active-connection.h"
+#include "nm-object-private.h"
+#include "nm-types-private.h"
+#include "nm-device.h"
+#include "nm-device-private.h"
+#include "nm-connection.h"
+#include "nm-vpn-connection.h"
+#include "nm-glib-compat.h"
+#include "nm-dbus-helpers-private.h"
+
+static GType _nm_active_connection_type_for_path (DBusGConnection *connection,
+ const char *path);
+static void _nm_active_connection_type_for_path_async (DBusGConnection *connection,
+ const char *path,
+ NMObjectTypeCallbackFunc callback,
+ gpointer user_data);
+
+G_DEFINE_TYPE_WITH_CODE (NMActiveConnection, nm_active_connection, NM_TYPE_OBJECT,
+ _nm_object_register_type_func (g_define_type_id,
+ _nm_active_connection_type_for_path,
+ _nm_active_connection_type_for_path_async);
+ )
+
+#define NM_ACTIVE_CONNECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_ACTIVE_CONNECTION, NMActiveConnectionPrivate))
+
+typedef struct {
+ DBusGProxy *proxy;
+
+ char *connection;
+ char *id;
+ char *uuid;
+ char *type;
+ char *specific_object;
+ GPtrArray *devices;
+ NMActiveConnectionState state;
+ gboolean is_default;
+ NMIP4Config *ip4_config;
+ NMDHCP4Config *dhcp4_config;
+ gboolean is_default6;
+ NMIP6Config *ip6_config;
+ NMDHCP6Config *dhcp6_config;
+ gboolean is_vpn;
+ char *master;
+} NMActiveConnectionPrivate;
+
+enum {
+ PROP_0,
+ PROP_CONNECTION,
+ PROP_ID,
+ PROP_UUID,
+ PROP_TYPE,
+ PROP_SPECIFIC_OBJECT,
+ PROP_DEVICES,
+ PROP_STATE,
+ PROP_DEFAULT,
+ PROP_IP4_CONFIG,
+ PROP_DHCP4_CONFIG,
+ PROP_DEFAULT6,
+ PROP_IP6_CONFIG,
+ PROP_DHCP6_CONFIG,
+ PROP_VPN,
+ PROP_MASTER,
+
+ LAST_PROP
+};
+
+/**
+ * nm_active_connection_new:
+ * @connection: the #DBusGConnection
+ * @path: the DBus object path of the device
+ *
+ * Creates a new #NMActiveConnection.
+ *
+ * Returns: (transfer full): a new active connection
+ **/
+GObject *
+nm_active_connection_new (DBusGConnection *connection, const char *path)
+{
+ g_return_val_if_fail (connection != NULL, NULL);
+ g_return_val_if_fail (path != NULL, NULL);
+
+ return g_object_new (NM_TYPE_ACTIVE_CONNECTION,
+ NM_OBJECT_DBUS_CONNECTION, connection,
+ NM_OBJECT_DBUS_PATH, path,
+ NULL);
+}
+
+static GType
+_nm_active_connection_type_for_path (DBusGConnection *connection,
+ const char *path)
+{
+ DBusGProxy *proxy;
+ GError *error = NULL;
+ GValue value = G_VALUE_INIT;
+ GType type;
+
+ proxy = _nm_dbus_new_proxy_for_connection (connection, path, "org.freedesktop.DBus.Properties");
+ if (!proxy) {
+ g_warning ("%s: couldn't create D-Bus object proxy.", __func__);
+ return G_TYPE_INVALID;
+ }
+
+ /* Have to create an NMVPNConnection if it's a VPN connection, otherwise
+ * a plain NMActiveConnection.
+ */
+ if (dbus_g_proxy_call (proxy,
+ "Get", &error,
+ G_TYPE_STRING, NM_DBUS_INTERFACE_ACTIVE_CONNECTION,
+ G_TYPE_STRING, "Vpn",
+ G_TYPE_INVALID,
+ G_TYPE_VALUE, &value, G_TYPE_INVALID)) {
+ if (g_value_get_boolean (&value))
+ type = NM_TYPE_VPN_CONNECTION;
+ else
+ type = NM_TYPE_ACTIVE_CONNECTION;
+ } else {
+ g_warning ("Error in getting active connection 'Vpn' property: (%d) %s",
+ error->code, error->message);
+ g_error_free (error);
+ type = G_TYPE_INVALID;
+ }
+
+ g_object_unref (proxy);
+ return type;
+}
+
+typedef struct {
+ DBusGConnection *connection;
+ NMObjectTypeCallbackFunc callback;
+ gpointer user_data;
+} NMActiveConnectionAsyncData;
+
+static void
+async_got_type (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data)
+{
+ NMActiveConnectionAsyncData *async_data = user_data;
+ GValue value = G_VALUE_INIT;
+ const char *path = dbus_g_proxy_get_path (proxy);
+ GError *error = NULL;
+ GType type;
+
+ if (dbus_g_proxy_end_call (proxy, call, &error,
+ G_TYPE_VALUE, &value,
+ G_TYPE_INVALID)) {
+ if (g_value_get_boolean (&value))
+ type = NM_TYPE_VPN_CONNECTION;
+ else
+ type = NM_TYPE_ACTIVE_CONNECTION;
+ } else {
+ g_warning ("%s: could not read properties for %s: %s", __func__, path, error->message);
+ type = G_TYPE_INVALID;
+ }
+
+ async_data->callback (type, async_data->user_data);
+
+ g_object_unref (proxy);
+ g_slice_free (NMActiveConnectionAsyncData, async_data);
+}
+
+static void
+_nm_active_connection_type_for_path_async (DBusGConnection *connection,
+ const char *path,
+ NMObjectTypeCallbackFunc callback,
+ gpointer user_data)
+{
+ NMActiveConnectionAsyncData *async_data;
+ DBusGProxy *proxy;
+
+ async_data = g_slice_new (NMActiveConnectionAsyncData);
+ async_data->connection = connection;
+ async_data->callback = callback;
+ async_data->user_data = user_data;
+
+ proxy = _nm_dbus_new_proxy_for_connection (connection, path, "org.freedesktop.DBus.Properties");
+ dbus_g_proxy_begin_call (proxy, "Get",
+ async_got_type, async_data, NULL,
+ G_TYPE_STRING, NM_DBUS_INTERFACE_ACTIVE_CONNECTION,
+ G_TYPE_STRING, "Vpn",
+ G_TYPE_INVALID);
+}
+
+/**
+ * nm_active_connection_get_connection:
+ * @connection: a #NMActiveConnection
+ *
+ * Gets the #NMConnection's DBus object path. This is often used with
+ * nm_remote_settings_get_connection_by_path() to retrieve the
+ * #NMRemoteConnection object that describes the connection.
+ *
+ * Returns: the object path of the #NMConnection which this #NMActiveConnection
+ * is an active instance of. This is the internal string used by the
+ * connection, and must not be modified.
+ **/
+const char *
+nm_active_connection_get_connection (NMActiveConnection *connection)
+{
+ g_return_val_if_fail (NM_IS_ACTIVE_CONNECTION (connection), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (connection));
+ return NM_ACTIVE_CONNECTION_GET_PRIVATE (connection)->connection;
+}
+
+/**
+ * nm_active_connection_get_id:
+ * @connection: a #NMActiveConnection
+ *
+ * Gets the #NMConnection's ID.
+ *
+ * Returns: the ID of the #NMConnection that backs the #NMActiveConnection.
+ * This is the internal string used by the connection, and must not be modified.
+ *
+ * Since: 0.9.10
+ **/
+const char *
+nm_active_connection_get_id (NMActiveConnection *connection)
+{
+ g_return_val_if_fail (NM_IS_ACTIVE_CONNECTION (connection), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (connection));
+ return NM_ACTIVE_CONNECTION_GET_PRIVATE (connection)->id;
+}
+
+/**
+ * nm_active_connection_get_uuid:
+ * @connection: a #NMActiveConnection
+ *
+ * Gets the #NMConnection's UUID.
+ *
+ * Returns: the UUID of the #NMConnection that backs the #NMActiveConnection.
+ * This is the internal string used by the connection, and must not be modified.
+ **/
+const char *
+nm_active_connection_get_uuid (NMActiveConnection *connection)
+{
+ g_return_val_if_fail (NM_IS_ACTIVE_CONNECTION (connection), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (connection));
+ return NM_ACTIVE_CONNECTION_GET_PRIVATE (connection)->uuid;
+}
+
+/**
+ * nm_active_connection_get_connection_type:
+ * @connection: a #NMActiveConnection
+ *
+ * Gets the #NMConnection's type.
+ *
+ * Returns: the type of the #NMConnection that backs the #NMActiveConnection.
+ * This is the internal string used by the connection, and must not be modified.
+ *
+ * Since: 0.9.10
+ **/
+const char *
+nm_active_connection_get_connection_type (NMActiveConnection *connection)
+{
+ g_return_val_if_fail (NM_IS_ACTIVE_CONNECTION (connection), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (connection));
+ return NM_ACTIVE_CONNECTION_GET_PRIVATE (connection)->type;
+}
+
+/**
+ * nm_active_connection_get_specific_object:
+ * @connection: a #NMActiveConnection
+ *
+ * Gets the "specific object" used at the activation.
+ *
+ * Returns: the specific object's DBus path. This is the internal string used by the
+ * connection, and must not be modified.
+ **/
+const char *
+nm_active_connection_get_specific_object (NMActiveConnection *connection)
+{
+ g_return_val_if_fail (NM_IS_ACTIVE_CONNECTION (connection), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (connection));
+ return NM_ACTIVE_CONNECTION_GET_PRIVATE (connection)->specific_object;
+}
+
+/**
+ * nm_active_connection_get_devices:
+ * @connection: a #NMActiveConnection
+ *
+ * Gets the #NMDevices used for the active connections.
+ *
+ * Returns: (element-type NMDevice): the #GPtrArray containing #NMDevices.
+ * This is the internal copy used by the connection, and must not be modified.
+ **/
+const GPtrArray *
+nm_active_connection_get_devices (NMActiveConnection *connection)
+{
+ g_return_val_if_fail (NM_IS_ACTIVE_CONNECTION (connection), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (connection));
+ return handle_ptr_array_return (NM_ACTIVE_CONNECTION_GET_PRIVATE (connection)->devices);
+}
+
+/**
+ * nm_active_connection_get_state:
+ * @connection: a #NMActiveConnection
+ *
+ * Gets the active connection's state.
+ *
+ * Returns: the state
+ **/
+NMActiveConnectionState
+nm_active_connection_get_state (NMActiveConnection *connection)
+{
+ g_return_val_if_fail (NM_IS_ACTIVE_CONNECTION (connection), NM_ACTIVE_CONNECTION_STATE_UNKNOWN);
+
+ _nm_object_ensure_inited (NM_OBJECT (connection));
+ return NM_ACTIVE_CONNECTION_GET_PRIVATE (connection)->state;
+}
+
+/**
+ * nm_active_connection_get_default:
+ * @connection: a #NMActiveConnection
+ *
+ * Whether the active connection is the default IPv4 one (that is, is used for
+ * the default IPv4 route and DNS information).
+ *
+ * Returns: %TRUE if the active connection is the default IPv4 connection
+ **/
+gboolean
+nm_active_connection_get_default (NMActiveConnection *connection)
+{
+ g_return_val_if_fail (NM_IS_ACTIVE_CONNECTION (connection), FALSE);
+
+ _nm_object_ensure_inited (NM_OBJECT (connection));
+ return NM_ACTIVE_CONNECTION_GET_PRIVATE (connection)->is_default;
+}
+
+/**
+ * nm_active_connection_get_ip4_config:
+ * @connection: an #NMActiveConnection
+ *
+ * Gets the current #NMIP4Config associated with the #NMActiveConnection.
+ *
+ * Returns: (transfer none): the #NMIP4Config, or %NULL if the
+ * connection is not in the %NM_ACTIVE_CONNECTION_STATE_ACTIVATED
+ * state.
+ *
+ * Since: 0.9.10
+ **/
+NMIP4Config *
+nm_active_connection_get_ip4_config (NMActiveConnection *connection)
+{
+ g_return_val_if_fail (NM_IS_ACTIVE_CONNECTION (connection), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (connection));
+ return NM_ACTIVE_CONNECTION_GET_PRIVATE (connection)->ip4_config;
+}
+
+/**
+ * nm_active_connection_get_dhcp4_config:
+ * @connection: an #NMActiveConnection
+ *
+ * Gets the current #NMDHCP4Config (if any) associated with the
+ * #NMActiveConnection.
+ *
+ * Returns: (transfer none): the #NMDHCP4Config, or %NULL if the
+ * connection does not use DHCP, or is not in the
+ * %NM_ACTIVE_CONNECTION_STATE_ACTIVATED state.
+ *
+ * Since: 0.9.10
+ **/
+NMDHCP4Config *
+nm_active_connection_get_dhcp4_config (NMActiveConnection *connection)
+{
+ g_return_val_if_fail (NM_IS_ACTIVE_CONNECTION (connection), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (connection));
+ return NM_ACTIVE_CONNECTION_GET_PRIVATE (connection)->dhcp4_config;
+}
+
+/**
+ * nm_active_connection_get_default6:
+ * @connection: a #NMActiveConnection
+ *
+ * Whether the active connection is the default IPv6 one (that is, is used for
+ * the default IPv6 route and DNS information).
+ *
+ * Returns: %TRUE if the active connection is the default IPv6 connection
+ **/
+gboolean
+nm_active_connection_get_default6 (NMActiveConnection *connection)
+{
+ g_return_val_if_fail (NM_IS_ACTIVE_CONNECTION (connection), FALSE);
+
+ _nm_object_ensure_inited (NM_OBJECT (connection));
+ return NM_ACTIVE_CONNECTION_GET_PRIVATE (connection)->is_default6;
+}
+
+/**
+ * nm_active_connection_get_ip6_config:
+ * @connection: an #NMActiveConnection
+ *
+ * Gets the current #NMIP6Config associated with the #NMActiveConnection.
+ *
+ * Returns: (transfer none): the #NMIP6Config, or %NULL if the
+ * connection is not in the %NM_ACTIVE_CONNECTION_STATE_ACTIVATED
+ * state.
+ *
+ * Since: 0.9.10
+ **/
+NMIP6Config *
+nm_active_connection_get_ip6_config (NMActiveConnection *connection)
+{
+ g_return_val_if_fail (NM_IS_ACTIVE_CONNECTION (connection), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (connection));
+ return NM_ACTIVE_CONNECTION_GET_PRIVATE (connection)->ip6_config;
+}
+
+/**
+ * nm_active_connection_get_dhcp6_config:
+ * @connection: an #NMActiveConnection
+ *
+ * Gets the current #NMDHCP6Config (if any) associated with the
+ * #NMActiveConnection.
+ *
+ * Returns: (transfer none): the #NMDHCP6Config, or %NULL if the
+ * connection does not use DHCPv6, or is not in the
+ * %NM_ACTIVE_CONNECTION_STATE_ACTIVATED state.
+ *
+ * Since: 0.9.10
+ **/
+NMDHCP6Config *
+nm_active_connection_get_dhcp6_config (NMActiveConnection *connection)
+{
+ g_return_val_if_fail (NM_IS_ACTIVE_CONNECTION (connection), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (connection));
+ return NM_ACTIVE_CONNECTION_GET_PRIVATE (connection)->dhcp6_config;
+}
+
+/**
+ * nm_active_connection_get_vpn:
+ * @connection: a #NMActiveConnection
+ *
+ * Whether the active connection is a VPN connection.
+ *
+ * Returns: %TRUE if the active connection is a VPN connection
+ *
+ * Since: 0.9.10
+ **/
+gboolean
+nm_active_connection_get_vpn (NMActiveConnection *connection)
+{
+ g_return_val_if_fail (NM_IS_ACTIVE_CONNECTION (connection), FALSE);
+
+ _nm_object_ensure_inited (NM_OBJECT (connection));
+ return NM_ACTIVE_CONNECTION_GET_PRIVATE (connection)->is_vpn;
+}
+
+/**
+ * nm_active_connection_get_master:
+ * @connection: a #NMActiveConnection
+ *
+ * Gets the path to the master #NMDevice of the connection.
+ *
+ * Returns: the path of the master #NMDevice of the #NMActiveConnection.
+ * This is the internal string used by the connection, and must not be modified.
+ **/
+const char *
+nm_active_connection_get_master (NMActiveConnection *connection)
+{
+ g_return_val_if_fail (NM_IS_ACTIVE_CONNECTION (connection), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (connection));
+ return NM_ACTIVE_CONNECTION_GET_PRIVATE (connection)->master;
+}
+
+static void
+nm_active_connection_init (NMActiveConnection *ap)
+{
+}
+
+static void
+dispose (GObject *object)
+{
+ NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (object);
+
+ if (priv->devices) {
+ g_ptr_array_set_free_func (priv->devices, g_object_unref);
+ g_ptr_array_free (priv->devices, TRUE);
+ priv->devices = NULL;
+ }
+
+ g_clear_object (&priv->ip4_config);
+ g_clear_object (&priv->dhcp4_config);
+ g_clear_object (&priv->ip6_config);
+ g_clear_object (&priv->dhcp6_config);
+
+ g_clear_object (&priv->proxy);
+
+ G_OBJECT_CLASS (nm_active_connection_parent_class)->dispose (object);
+}
+
+static void
+finalize (GObject *object)
+{
+ NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (object);
+
+ g_free (priv->connection);
+ g_free (priv->id);
+ g_free (priv->uuid);
+ g_free (priv->type);
+ g_free (priv->specific_object);
+ g_free (priv->master);
+
+ G_OBJECT_CLASS (nm_active_connection_parent_class)->finalize (object);
+}
+
+static void
+get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NMActiveConnection *self = NM_ACTIVE_CONNECTION (object);
+
+ _nm_object_ensure_inited (NM_OBJECT (object));
+
+ switch (prop_id) {
+ case PROP_CONNECTION:
+ g_value_set_string (value, nm_active_connection_get_connection (self));
+ break;
+ case PROP_ID:
+ g_value_set_string (value, nm_active_connection_get_id (self));
+ break;
+ case PROP_UUID:
+ g_value_set_string (value, nm_active_connection_get_uuid (self));
+ break;
+ case PROP_TYPE:
+ g_value_set_string (value, nm_active_connection_get_connection_type (self));
+ break;
+ case PROP_SPECIFIC_OBJECT:
+ g_value_set_boxed (value, nm_active_connection_get_specific_object (self));
+ break;
+ case PROP_DEVICES:
+ g_value_set_boxed (value, nm_active_connection_get_devices (self));
+ break;
+ case PROP_STATE:
+ g_value_set_uint (value, nm_active_connection_get_state (self));
+ break;
+ case PROP_DEFAULT:
+ g_value_set_boolean (value, nm_active_connection_get_default (self));
+ break;
+ case PROP_IP4_CONFIG:
+ g_value_set_object (value, nm_active_connection_get_ip4_config (self));
+ break;
+ case PROP_DHCP4_CONFIG:
+ g_value_set_object (value, nm_active_connection_get_dhcp4_config (self));
+ break;
+ case PROP_DEFAULT6:
+ g_value_set_boolean (value, nm_active_connection_get_default6 (self));
+ break;
+ case PROP_IP6_CONFIG:
+ g_value_set_object (value, nm_active_connection_get_ip6_config (self));
+ break;
+ case PROP_DHCP6_CONFIG:
+ g_value_set_object (value, nm_active_connection_get_dhcp6_config (self));
+ break;
+ case PROP_VPN:
+ g_value_set_boolean (value, nm_active_connection_get_vpn (self));
+ break;
+ case PROP_MASTER:
+ g_value_set_string (value, nm_active_connection_get_master (self));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+register_properties (NMActiveConnection *connection)
+{
+ NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (connection);
+ const NMPropertiesInfo property_info[] = {
+ { NM_ACTIVE_CONNECTION_CONNECTION, &priv->connection },
+ { NM_ACTIVE_CONNECTION_ID, &priv->id },
+ { NM_ACTIVE_CONNECTION_UUID, &priv->uuid },
+ { NM_ACTIVE_CONNECTION_TYPE, &priv->type },
+ { NM_ACTIVE_CONNECTION_SPECIFIC_OBJECT, &priv->specific_object },
+ { NM_ACTIVE_CONNECTION_DEVICES, &priv->devices, NULL, NM_TYPE_DEVICE },
+ { NM_ACTIVE_CONNECTION_STATE, &priv->state },
+ { NM_ACTIVE_CONNECTION_DEFAULT, &priv->is_default },
+ { NM_ACTIVE_CONNECTION_IP4_CONFIG, &priv->ip4_config, NULL, NM_TYPE_IP4_CONFIG },
+ { NM_ACTIVE_CONNECTION_DHCP4_CONFIG, &priv->dhcp4_config, NULL, NM_TYPE_DHCP4_CONFIG },
+ { NM_ACTIVE_CONNECTION_DEFAULT6, &priv->is_default6 },
+ { NM_ACTIVE_CONNECTION_IP6_CONFIG, &priv->ip6_config, NULL, NM_TYPE_IP6_CONFIG },
+ { NM_ACTIVE_CONNECTION_DHCP6_CONFIG, &priv->dhcp6_config, NULL, NM_TYPE_DHCP6_CONFIG },
+ { NM_ACTIVE_CONNECTION_VPN, &priv->is_vpn },
+ { NM_ACTIVE_CONNECTION_MASTER, &priv->master },
+
+ { NULL },
+ };
+
+ _nm_object_register_properties (NM_OBJECT (connection),
+ priv->proxy,
+ property_info);
+}
+
+static void
+constructed (GObject *object)
+{
+ NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (object);
+
+ G_OBJECT_CLASS (nm_active_connection_parent_class)->constructed (object);
+
+ priv->proxy = _nm_object_new_proxy (NM_OBJECT (object), NULL, NM_DBUS_INTERFACE_ACTIVE_CONNECTION);
+ register_properties (NM_ACTIVE_CONNECTION (object));
+}
+
+
+static void
+nm_active_connection_class_init (NMActiveConnectionClass *ap_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (ap_class);
+
+ g_type_class_add_private (ap_class, sizeof (NMActiveConnectionPrivate));
+
+ /* virtual methods */
+ object_class->constructed = constructed;
+ object_class->get_property = get_property;
+ object_class->dispose = dispose;
+ object_class->finalize = finalize;
+
+ /* properties */
+
+ /**
+ * NMActiveConnection:connection:
+ *
+ * The connection's path of the active connection.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_CONNECTION,
+ g_param_spec_string (NM_ACTIVE_CONNECTION_CONNECTION, "", "",
+ NULL,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMActiveConnection:id:
+ *
+ * The active connection's ID
+ *
+ * Since: 0.9.10
+ **/
+ g_object_class_install_property
+ (object_class, PROP_ID,
+ g_param_spec_string (NM_ACTIVE_CONNECTION_ID, "", "",
+ NULL,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMActiveConnection:uuid:
+ *
+ * The active connection's UUID
+ **/
+ g_object_class_install_property
+ (object_class, PROP_UUID,
+ g_param_spec_string (NM_ACTIVE_CONNECTION_UUID, "", "",
+ NULL,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMActiveConnection:type:
+ *
+ * The active connection's type
+ *
+ * Since: 0.9.10
+ **/
+ g_object_class_install_property
+ (object_class, PROP_TYPE,
+ g_param_spec_string (NM_ACTIVE_CONNECTION_TYPE, "", "",
+ NULL,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMActiveConnection:specific-object:
+ *
+ * The specific object's path of the active connection.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_SPECIFIC_OBJECT,
+ g_param_spec_string (NM_ACTIVE_CONNECTION_SPECIFIC_OBJECT, "", "",
+ NULL,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMActiveConnection:device:
+ *
+ * The devices (#NMDevice) of the active connection.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_DEVICES,
+ g_param_spec_boxed (NM_ACTIVE_CONNECTION_DEVICES, "", "",
+ NM_TYPE_OBJECT_ARRAY,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMActiveConnection:state:
+ *
+ * The state of the active connection.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_STATE,
+ g_param_spec_uint (NM_ACTIVE_CONNECTION_STATE, "", "",
+ NM_ACTIVE_CONNECTION_STATE_UNKNOWN,
+ NM_ACTIVE_CONNECTION_STATE_DEACTIVATING,
+ NM_ACTIVE_CONNECTION_STATE_UNKNOWN,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMActiveConnection:default:
+ *
+ * Whether the active connection is the default IPv4 one.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_DEFAULT,
+ g_param_spec_boolean (NM_ACTIVE_CONNECTION_DEFAULT, "", "",
+ FALSE,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMActiveConnection:ip4-config:
+ *
+ * The #NMIP4Config of the connection.
+ *
+ * Since: 0.9.10
+ **/
+ g_object_class_install_property
+ (object_class, PROP_IP4_CONFIG,
+ g_param_spec_object (NM_ACTIVE_CONNECTION_IP4_CONFIG, "", "",
+ NM_TYPE_IP4_CONFIG,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMActiveConnection:dhcp4-config:
+ *
+ * The #NMDHCP4Config of the connection.
+ *
+ * Since: 0.9.10
+ **/
+ g_object_class_install_property
+ (object_class, PROP_DHCP4_CONFIG,
+ g_param_spec_object (NM_ACTIVE_CONNECTION_DHCP4_CONFIG, "", "",
+ NM_TYPE_DHCP4_CONFIG,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMActiveConnection:default6:
+ *
+ * Whether the active connection is the default IPv6 one.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_DEFAULT6,
+ g_param_spec_boolean (NM_ACTIVE_CONNECTION_DEFAULT6, "", "",
+ FALSE,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMActiveConnection:ip6-config:
+ *
+ * The #NMIP6Config of the connection.
+ *
+ * Since: 0.9.10
+ **/
+ g_object_class_install_property
+ (object_class, PROP_IP6_CONFIG,
+ g_param_spec_object (NM_ACTIVE_CONNECTION_IP6_CONFIG, "", "",
+ NM_TYPE_IP6_CONFIG,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMActiveConnection:dhcp6-config:
+ *
+ * The #NMDHCP6Config of the connection.
+ *
+ * Since: 0.9.10
+ **/
+ g_object_class_install_property
+ (object_class, PROP_DHCP6_CONFIG,
+ g_param_spec_object (NM_ACTIVE_CONNECTION_DHCP6_CONFIG, "", "",
+ NM_TYPE_DHCP6_CONFIG,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMActiveConnection:vpn:
+ *
+ * Whether the active connection is a VPN connection.
+ *
+ * Since: 0.9.10
+ **/
+ g_object_class_install_property
+ (object_class, PROP_VPN,
+ g_param_spec_boolean (NM_ACTIVE_CONNECTION_VPN, "", "",
+ FALSE,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMActiveConnection:master:
+ *
+ * The path of the master device if one exists.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_MASTER,
+ g_param_spec_string (NM_ACTIVE_CONNECTION_MASTER, "", "",
+ NULL,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/libnm/nm-active-connection.h b/libnm/nm-active-connection.h
new file mode 100644
index 0000000000..e29415649c
--- /dev/null
+++ b/libnm/nm-active-connection.h
@@ -0,0 +1,105 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2014 Red Hat, Inc.
+ * Copyright 2008 Novell, Inc.
+ */
+
+#ifndef NM_ACTIVE_CONNECTION_H
+#define NM_ACTIVE_CONNECTION_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include "nm-object.h"
+#include <nm-connection.h>
+#include <NetworkManager.h>
+#include "nm-ip4-config.h"
+#include "nm-dhcp4-config.h"
+#include "nm-ip6-config.h"
+#include "nm-dhcp6-config.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_ACTIVE_CONNECTION (nm_active_connection_get_type ())
+#define NM_ACTIVE_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_ACTIVE_CONNECTION, NMActiveConnection))
+#define NM_ACTIVE_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_ACTIVE_CONNECTION, NMActiveConnectionClass))
+#define NM_IS_ACTIVE_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_ACTIVE_CONNECTION))
+#define NM_IS_ACTIVE_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_ACTIVE_CONNECTION))
+#define NM_ACTIVE_CONNECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_ACTIVE_CONNECTION, NMActiveConnectionClass))
+
+#define NM_ACTIVE_CONNECTION_CONNECTION "connection"
+#define NM_ACTIVE_CONNECTION_ID "id"
+#define NM_ACTIVE_CONNECTION_UUID "uuid"
+#define NM_ACTIVE_CONNECTION_TYPE "type"
+#define NM_ACTIVE_CONNECTION_SPECIFIC_OBJECT "specific-object"
+#define NM_ACTIVE_CONNECTION_DEVICES "devices"
+#define NM_ACTIVE_CONNECTION_STATE "state"
+#define NM_ACTIVE_CONNECTION_DEFAULT "default"
+#define NM_ACTIVE_CONNECTION_IP4_CONFIG "ip4-config"
+#define NM_ACTIVE_CONNECTION_DHCP4_CONFIG "dhcp4-config"
+#define NM_ACTIVE_CONNECTION_DEFAULT6 "default6"
+#define NM_ACTIVE_CONNECTION_IP6_CONFIG "ip6-config"
+#define NM_ACTIVE_CONNECTION_DHCP6_CONFIG "dhcp6-config"
+#define NM_ACTIVE_CONNECTION_VPN "vpn"
+#define NM_ACTIVE_CONNECTION_MASTER "master"
+
+typedef struct {
+ NMObject parent;
+} NMActiveConnection;
+
+typedef struct {
+ NMObjectClass parent;
+
+ /* Padding for future expansion */
+ void (*_reserved1) (void);
+ void (*_reserved2) (void);
+ void (*_reserved3) (void);
+ void (*_reserved4) (void);
+ void (*_reserved5) (void);
+ void (*_reserved6) (void);
+} NMActiveConnectionClass;
+
+GType nm_active_connection_get_type (void);
+
+GObject *nm_active_connection_new (DBusGConnection *connection, const char *path);
+
+const char * nm_active_connection_get_connection (NMActiveConnection *connection);
+NM_AVAILABLE_IN_0_9_10
+const char * nm_active_connection_get_id (NMActiveConnection *connection);
+const char * nm_active_connection_get_uuid (NMActiveConnection *connection);
+NM_AVAILABLE_IN_0_9_10
+const char * nm_active_connection_get_connection_type (NMActiveConnection *connection);
+const char * nm_active_connection_get_specific_object (NMActiveConnection *connection);
+const GPtrArray *nm_active_connection_get_devices (NMActiveConnection *connection);
+NMActiveConnectionState nm_active_connection_get_state (NMActiveConnection *connection);
+const char * nm_active_connection_get_master (NMActiveConnection *connection);
+gboolean nm_active_connection_get_default (NMActiveConnection *connection);
+NM_AVAILABLE_IN_0_9_10
+NMIP4Config * nm_active_connection_get_ip4_config (NMActiveConnection *connection);
+NM_AVAILABLE_IN_0_9_10
+NMDHCP4Config *nm_active_connection_get_dhcp4_config (NMActiveConnection *connection);
+gboolean nm_active_connection_get_default6 (NMActiveConnection *connection);
+NM_AVAILABLE_IN_0_9_10
+NMIP6Config * nm_active_connection_get_ip6_config (NMActiveConnection *connection);
+NM_AVAILABLE_IN_0_9_10
+NMDHCP6Config *nm_active_connection_get_dhcp6_config (NMActiveConnection *connection);
+NM_AVAILABLE_IN_0_9_10
+gboolean nm_active_connection_get_vpn (NMActiveConnection *connection);
+
+G_END_DECLS
+
+#endif /* NM_ACTIVE_CONNECTION_H */
diff --git a/libnm/nm-client.c b/libnm/nm-client.c
new file mode 100644
index 0000000000..711da7f4a0
--- /dev/null
+++ b/libnm/nm-client.c
@@ -0,0 +1,2442 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2008 Novell, Inc.
+ * Copyright 2007 - 2013 Red Hat, Inc.
+ */
+
+#include <dbus/dbus-glib.h>
+#include <string.h>
+#include <nm-utils.h>
+
+#include "nm-client.h"
+#include "nm-device-ethernet.h"
+#include "nm-device-wifi.h"
+#include "nm-device-private.h"
+#include "nm-types-private.h"
+#include "nm-object-private.h"
+#include "nm-active-connection.h"
+#include "nm-vpn-connection.h"
+#include "nm-object-cache.h"
+#include "nm-dbus-glib-types.h"
+#include "nm-glib-compat.h"
+
+void _nm_device_wifi_set_wireless_enabled (NMDeviceWifi *device, gboolean enabled);
+
+static void nm_client_initable_iface_init (GInitableIface *iface);
+static void nm_client_async_initable_iface_init (GAsyncInitableIface *iface);
+static GInitableIface *nm_client_parent_initable_iface;
+static GAsyncInitableIface *nm_client_parent_async_initable_iface;
+
+G_DEFINE_TYPE_WITH_CODE (NMClient, nm_client, NM_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, nm_client_initable_iface_init);
+ G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, nm_client_async_initable_iface_init);
+ )
+
+#define NM_CLIENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_CLIENT, NMClientPrivate))
+
+typedef struct {
+ DBusGProxy *client_proxy;
+ DBusGProxy *bus_proxy;
+ gboolean manager_running;
+ char *version;
+ NMState state;
+ gboolean startup;
+ GPtrArray *devices;
+ GPtrArray *active_connections;
+ NMConnectivityState connectivity;
+ NMActiveConnection *primary_connection;
+ NMActiveConnection *activating_connection;
+
+ DBusGProxyCall *perm_call;
+ GHashTable *permissions;
+
+ /* Activations waiting for their NMActiveConnection
+ * to appear and then their callback to be called.
+ */
+ GSList *pending_activations;
+
+ gboolean networking_enabled;
+ gboolean wireless_enabled;
+ gboolean wireless_hw_enabled;
+
+ gboolean wwan_enabled;
+ gboolean wwan_hw_enabled;
+
+ gboolean wimax_enabled;
+ gboolean wimax_hw_enabled;
+} NMClientPrivate;
+
+enum {
+ PROP_0,
+ PROP_VERSION,
+ PROP_STATE,
+ PROP_STARTUP,
+ PROP_MANAGER_RUNNING,
+ PROP_NETWORKING_ENABLED,
+ PROP_WIRELESS_ENABLED,
+ PROP_WIRELESS_HARDWARE_ENABLED,
+ PROP_WWAN_ENABLED,
+ PROP_WWAN_HARDWARE_ENABLED,
+ PROP_WIMAX_ENABLED,
+ PROP_WIMAX_HARDWARE_ENABLED,
+ PROP_ACTIVE_CONNECTIONS,
+ PROP_CONNECTIVITY,
+ PROP_PRIMARY_CONNECTION,
+ PROP_ACTIVATING_CONNECTION,
+ PROP_DEVICES,
+
+ LAST_PROP
+};
+
+enum {
+ DEVICE_ADDED,
+ DEVICE_REMOVED,
+ PERMISSION_CHANGED,
+
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+static void proxy_name_owner_changed (DBusGProxy *proxy,
+ const char *name,
+ const char *old_owner,
+ const char *new_owner,
+ gpointer user_data);
+
+/**********************************************************************/
+
+/**
+ * nm_client_error_quark:
+ *
+ * Registers an error quark for #NMClient if necessary.
+ *
+ * Returns: the error quark used for #NMClient errors.
+ *
+ * Since: 0.9.10
+ **/
+GQuark
+nm_client_error_quark (void)
+{
+ static GQuark quark;
+
+ if (G_UNLIKELY (!quark))
+ quark = g_quark_from_static_string ("nm-client-error-quark");
+ return quark;
+}
+
+/**********************************************************************/
+
+static void
+nm_client_init (NMClient *client)
+{
+ NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (client);
+
+ priv->state = NM_STATE_UNKNOWN;
+
+ priv->permissions = g_hash_table_new (g_direct_hash, g_direct_equal);
+}
+
+static void
+poke_wireless_devices_with_rf_status (NMClient *client)
+{
+ NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (client);
+ int i;
+
+ for (i = 0; priv->devices && (i < priv->devices->len); i++) {
+ NMDevice *device = g_ptr_array_index (priv->devices, i);
+
+ if (NM_IS_DEVICE_WIFI (device))
+ _nm_device_wifi_set_wireless_enabled (NM_DEVICE_WIFI (device), priv->wireless_enabled);
+ }
+}
+
+static void
+wireless_enabled_cb (GObject *object, GParamSpec *pspec, gpointer user_data)
+{
+ poke_wireless_devices_with_rf_status (NM_CLIENT (object));
+}
+
+static void
+register_properties (NMClient *client)
+{
+ NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (client);
+ const NMPropertiesInfo property_info[] = {
+ { NM_CLIENT_VERSION, &priv->version },
+ { NM_CLIENT_STATE, &priv->state },
+ { NM_CLIENT_STARTUP, &priv->startup },
+ { NM_CLIENT_NETWORKING_ENABLED, &priv->networking_enabled },
+ { NM_CLIENT_WIRELESS_ENABLED, &priv->wireless_enabled },
+ { NM_CLIENT_WIRELESS_HARDWARE_ENABLED, &priv->wireless_hw_enabled },
+ { NM_CLIENT_WWAN_ENABLED, &priv->wwan_enabled },
+ { NM_CLIENT_WWAN_HARDWARE_ENABLED, &priv->wwan_hw_enabled },
+ { NM_CLIENT_WIMAX_ENABLED, &priv->wimax_enabled },
+ { NM_CLIENT_WIMAX_HARDWARE_ENABLED, &priv->wimax_hw_enabled },
+ { NM_CLIENT_ACTIVE_CONNECTIONS, &priv->active_connections, NULL, NM_TYPE_ACTIVE_CONNECTION },
+ { NM_CLIENT_CONNECTIVITY, &priv->connectivity },
+ { NM_CLIENT_PRIMARY_CONNECTION, &priv->primary_connection, NULL, NM_TYPE_ACTIVE_CONNECTION },
+ { NM_CLIENT_ACTIVATING_CONNECTION, &priv->activating_connection, NULL, NM_TYPE_ACTIVE_CONNECTION },
+ { NM_CLIENT_DEVICES, &priv->devices, NULL, NM_TYPE_DEVICE, "device" },
+ { NULL },
+ };
+
+ _nm_object_register_properties (NM_OBJECT (client),
+ priv->client_proxy,
+ property_info);
+}
+
+#define NM_AUTH_PERMISSION_ENABLE_DISABLE_NETWORK "org.freedesktop.NetworkManager.enable-disable-network"
+#define NM_AUTH_PERMISSION_ENABLE_DISABLE_WIFI "org.freedesktop.NetworkManager.enable-disable-wifi"
+#define NM_AUTH_PERMISSION_ENABLE_DISABLE_WWAN "org.freedesktop.NetworkManager.enable-disable-wwan"
+#define NM_AUTH_PERMISSION_ENABLE_DISABLE_WIMAX "org.freedesktop.NetworkManager.enable-disable-wimax"
+#define NM_AUTH_PERMISSION_SLEEP_WAKE "org.freedesktop.NetworkManager.sleep-wake"
+#define NM_AUTH_PERMISSION_NETWORK_CONTROL "org.freedesktop.NetworkManager.network-control"
+#define NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED "org.freedesktop.NetworkManager.wifi.share.protected"
+#define NM_AUTH_PERMISSION_WIFI_SHARE_OPEN "org.freedesktop.NetworkManager.wifi.share.open"
+#define NM_AUTH_PERMISSION_SETTINGS_MODIFY_SYSTEM "org.freedesktop.NetworkManager.settings.modify.system"
+#define NM_AUTH_PERMISSION_SETTINGS_MODIFY_OWN "org.freedesktop.NetworkManager.settings.modify.own"
+#define NM_AUTH_PERMISSION_SETTINGS_MODIFY_HOSTNAME "org.freedesktop.NetworkManager.settings.modify.hostname"
+
+static NMClientPermission
+nm_permission_to_client (const char *nm)
+{
+ if (!strcmp (nm, NM_AUTH_PERMISSION_ENABLE_DISABLE_NETWORK))
+ return NM_CLIENT_PERMISSION_ENABLE_DISABLE_NETWORK;
+ else if (!strcmp (nm, NM_AUTH_PERMISSION_ENABLE_DISABLE_WIFI))
+ return NM_CLIENT_PERMISSION_ENABLE_DISABLE_WIFI;
+ else if (!strcmp (nm, NM_AUTH_PERMISSION_ENABLE_DISABLE_WWAN))
+ return NM_CLIENT_PERMISSION_ENABLE_DISABLE_WWAN;
+ else if (!strcmp (nm, NM_AUTH_PERMISSION_ENABLE_DISABLE_WIMAX))
+ return NM_CLIENT_PERMISSION_ENABLE_DISABLE_WIMAX;
+ else if (!strcmp (nm, NM_AUTH_PERMISSION_SLEEP_WAKE))
+ return NM_CLIENT_PERMISSION_SLEEP_WAKE;
+ else if (!strcmp (nm, NM_AUTH_PERMISSION_NETWORK_CONTROL))
+ return NM_CLIENT_PERMISSION_NETWORK_CONTROL;
+ else if (!strcmp (nm, NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED))
+ return NM_CLIENT_PERMISSION_WIFI_SHARE_PROTECTED;
+ else if (!strcmp (nm, NM_AUTH_PERMISSION_WIFI_SHARE_OPEN))
+ return NM_CLIENT_PERMISSION_WIFI_SHARE_OPEN;
+ else if (!strcmp (nm, NM_AUTH_PERMISSION_SETTINGS_MODIFY_SYSTEM))
+ return NM_CLIENT_PERMISSION_SETTINGS_MODIFY_SYSTEM;
+ else if (!strcmp (nm, NM_AUTH_PERMISSION_SETTINGS_MODIFY_OWN))
+ return NM_CLIENT_PERMISSION_SETTINGS_MODIFY_OWN;
+ else if (!strcmp (nm, NM_AUTH_PERMISSION_SETTINGS_MODIFY_HOSTNAME))
+ return NM_CLIENT_PERMISSION_SETTINGS_MODIFY_HOSTNAME;
+
+ return NM_CLIENT_PERMISSION_NONE;
+}
+
+static NMClientPermissionResult
+nm_permission_result_to_client (const char *nm)
+{
+ if (!strcmp (nm, "yes"))
+ return NM_CLIENT_PERMISSION_RESULT_YES;
+ else if (!strcmp (nm, "no"))
+ return NM_CLIENT_PERMISSION_RESULT_NO;
+ else if (!strcmp (nm, "auth"))
+ return NM_CLIENT_PERMISSION_RESULT_AUTH;
+ return NM_CLIENT_PERMISSION_RESULT_UNKNOWN;
+}
+
+static void
+update_permissions (NMClient *self, GHashTable *permissions)
+{
+ NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (self);
+ GHashTableIter iter;
+ gpointer key, value;
+ NMClientPermission perm;
+ NMClientPermissionResult perm_result;
+ GList *keys, *keys_iter;
+
+ /* get list of old permissions for change notification */
+ keys = g_hash_table_get_keys (priv->permissions);
+ g_hash_table_remove_all (priv->permissions);
+
+ if (permissions) {
+ /* Process new permissions */
+ g_hash_table_iter_init (&iter, permissions);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ perm = nm_permission_to_client ((const char *) key);
+ perm_result = nm_permission_result_to_client ((const char *) value);
+ if (perm) {
+ g_hash_table_insert (priv->permissions,
+ GUINT_TO_POINTER (perm),
+ GUINT_TO_POINTER (perm_result));
+
+ /* Remove this permission from the list of previous permissions
+ * we'll be sending NM_CLIENT_PERMISSION_RESULT_UNKNOWN for
+ * in the change signal since it is still a known permission.
+ */
+ keys = g_list_remove (keys, GUINT_TO_POINTER (perm));
+ }
+ }
+ }
+
+ /* Signal changes in all updated permissions */
+ g_hash_table_iter_init (&iter, priv->permissions);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ g_signal_emit (self, signals[PERMISSION_CHANGED], 0,
+ GPOINTER_TO_UINT (key),
+ GPOINTER_TO_UINT (value));
+ }
+
+ /* And signal changes in all permissions that used to be valid but for
+ * some reason weren't received in the last request (if any).
+ */
+ for (keys_iter = keys; keys_iter; keys_iter = g_list_next (keys_iter)) {
+ g_signal_emit (self, signals[PERMISSION_CHANGED], 0,
+ GPOINTER_TO_UINT (keys_iter->data),
+ NM_CLIENT_PERMISSION_RESULT_UNKNOWN);
+ }
+ g_list_free (keys);
+}
+
+static gboolean
+get_permissions_sync (NMClient *self, GError **error)
+{
+ gboolean success;
+ GHashTable *permissions = NULL;
+
+ success = dbus_g_proxy_call_with_timeout (NM_CLIENT_GET_PRIVATE (self)->client_proxy,
+ "GetPermissions", 3000, error,
+ G_TYPE_INVALID,
+ DBUS_TYPE_G_MAP_OF_STRING, &permissions, G_TYPE_INVALID);
+ update_permissions (self, success ? permissions : NULL);
+ if (permissions)
+ g_hash_table_destroy (permissions);
+
+ return success;
+}
+
+static void
+get_permissions_reply (DBusGProxy *proxy,
+ DBusGProxyCall *call,
+ gpointer user_data)
+{
+ NMClient *self = NM_CLIENT (user_data);
+ GHashTable *permissions;
+ GError *error = NULL;
+
+ dbus_g_proxy_end_call (proxy, call, &error,
+ DBUS_TYPE_G_MAP_OF_STRING, &permissions,
+ G_TYPE_INVALID);
+ NM_CLIENT_GET_PRIVATE (self)->perm_call = NULL;
+ update_permissions (NM_CLIENT (user_data), error ? NULL : permissions);
+ g_clear_error (&error);
+}
+
+static void
+client_recheck_permissions (DBusGProxy *proxy, gpointer user_data)
+{
+ NMClient *self = NM_CLIENT (user_data);
+ NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (self);
+
+ if (!priv->perm_call) {
+ priv->perm_call = dbus_g_proxy_begin_call (NM_CLIENT_GET_PRIVATE (self)->client_proxy, "GetPermissions",
+ get_permissions_reply, self, NULL,
+ G_TYPE_INVALID);
+ }
+}
+
+/**
+ * nm_client_get_devices:
+ * @client: a #NMClient
+ *
+ * Gets all the known network devices. Use nm_device_get_type() or the
+ * NM_IS_DEVICE_XXXX() functions to determine what kind of device member of the
+ * returned array is, and then you may use device-specific methods such as
+ * nm_device_ethernet_get_hw_address().
+ *
+ * Returns: (transfer none) (element-type NMDevice): a #GPtrArray
+ * containing all the #NMDevices. The returned array is owned by the
+ * #NMClient object and should not be modified.
+ **/
+const GPtrArray *
+nm_client_get_devices (NMClient *client)
+{
+ g_return_val_if_fail (NM_IS_CLIENT (client), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (client));
+
+ return handle_ptr_array_return (NM_CLIENT_GET_PRIVATE (client)->devices);
+}
+
+/**
+ * nm_client_get_device_by_path:
+ * @client: a #NMClient
+ * @object_path: the object path to search for
+ *
+ * Gets a #NMDevice from a #NMClient.
+ *
+ * Returns: (transfer none): the #NMDevice for the given @object_path or %NULL if none is found.
+ **/
+NMDevice *
+nm_client_get_device_by_path (NMClient *client, const char *object_path)
+{
+ const GPtrArray *devices;
+ int i;
+ NMDevice *device = NULL;
+
+ g_return_val_if_fail (NM_IS_CLIENT (client), NULL);
+ g_return_val_if_fail (object_path, NULL);
+
+ devices = nm_client_get_devices (client);
+ if (!devices)
+ return NULL;
+
+ for (i = 0; i < devices->len; i++) {
+ NMDevice *candidate = g_ptr_array_index (devices, i);
+ if (!strcmp (nm_object_get_path (NM_OBJECT (candidate)), object_path)) {
+ device = candidate;
+ break;
+ }
+ }
+
+ return device;
+}
+
+/**
+ * nm_client_get_device_by_iface:
+ * @client: a #NMClient
+ * @iface: the interface name to search for
+ *
+ * Gets a #NMDevice from a #NMClient.
+ *
+ * Returns: (transfer none): the #NMDevice for the given @iface or %NULL if none is found.
+ **/
+NMDevice *
+nm_client_get_device_by_iface (NMClient *client, const char *iface)
+{
+ const GPtrArray *devices;
+ int i;
+ NMDevice *device = NULL;
+
+ g_return_val_if_fail (NM_IS_CLIENT (client), NULL);
+ g_return_val_if_fail (iface, NULL);
+
+ devices = nm_client_get_devices (client);
+ if (!devices)
+ return NULL;
+
+ for (i = 0; i < devices->len; i++) {
+ NMDevice *candidate = g_ptr_array_index (devices, i);
+ if (!strcmp (nm_device_get_iface (candidate), iface)) {
+ device = candidate;
+ break;
+ }
+ }
+
+ return device;
+}
+
+typedef struct {
+ NMClient *client;
+ NMClientActivateFn act_fn;
+ NMClientAddActivateFn add_act_fn;
+ char *active_path;
+ char *new_connection_path;
+ guint idle_id;
+ gpointer user_data;
+} ActivateInfo;
+
+static void
+activate_info_free (ActivateInfo *info)
+{
+ if (info->idle_id)
+ g_source_remove (info->idle_id);
+ g_free (info->active_path);
+ g_free (info->new_connection_path);
+ memset (info, 0, sizeof (*info));
+ g_slice_free (ActivateInfo, info);
+}
+
+static void
+activate_info_complete (ActivateInfo *info,
+ NMActiveConnection *active,
+ GError *error)
+{
+ NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (info->client);
+
+ if (info->act_fn)
+ info->act_fn (info->client, error ? NULL : active, error, info->user_data);
+ else if (info->add_act_fn) {
+ info->add_act_fn (info->client,
+ error ? NULL : active,
+ error ? NULL : info->new_connection_path,
+ error,
+ info->user_data);
+ } else if (error)
+ g_warning ("Device activation failed: (%d) %s", error->code, error->message);
+
+ priv->pending_activations = g_slist_remove (priv->pending_activations, info);
+}
+
+static void
+recheck_pending_activations (NMClient *self, const char *failed_path, GError *error)
+{
+ NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (self);
+ GSList *iter, *next;
+ const GPtrArray *active_connections;
+ gboolean found_in_active = FALSE;
+ gboolean found_in_pending = FALSE;
+ ActivateInfo *ainfo = NULL;
+ int i;
+
+ active_connections = nm_client_get_active_connections (self);
+
+ /* For each pending activation, look for a active connection that has
+ * the pending activation's object path, and call pending connection's
+ * callback.
+ * If the connection to activate doesn't make it to active_connections,
+ * due to an error, we have to call the callback for failed_path.
+ */
+ for (iter = priv->pending_activations; iter; iter = next) {
+ ActivateInfo *info = iter->data;
+
+ next = g_slist_next (iter);
+
+ if (!found_in_pending && failed_path && g_strcmp0 (failed_path, info->active_path) == 0) {
+ found_in_pending = TRUE;
+ ainfo = info;
+ }
+
+ for (i = 0; active_connections && i < active_connections->len; i++) {
+ NMActiveConnection *active = g_ptr_array_index (active_connections, i);
+ const char *active_path = nm_object_get_path (NM_OBJECT (active));
+
+ if (!found_in_active && failed_path && g_strcmp0 (failed_path, active_path) == 0)
+ found_in_active = TRUE;
+
+ if (g_strcmp0 (info->active_path, active_path) == 0) {
+ /* Call the pending activation's callback and it all up */
+ activate_info_complete (info, active, NULL);
+ activate_info_free (info);
+ break;
+ }
+ }
+ }
+
+ if (!found_in_active && found_in_pending) {
+ /* A newly activated connection failed due to some immediate error
+ * and disappeared from active connection list. Make sure the
+ * callback gets called.
+ */
+ activate_info_complete (ainfo, NULL, error);
+ activate_info_free (ainfo);
+ }
+}
+
+static void
+activate_cb (DBusGProxy *proxy,
+ DBusGProxyCall *call,
+ gpointer user_data)
+{
+ ActivateInfo *info = user_data;
+ char *path;
+ GError *error = NULL;
+
+ dbus_g_proxy_end_call (proxy, call, &error,
+ DBUS_TYPE_G_OBJECT_PATH, &path,
+ G_TYPE_INVALID);
+ if (error) {
+ activate_info_complete (info, NULL, error);
+ activate_info_free (info);
+ g_clear_error (&error);
+ } else {
+ info->active_path = path;
+ recheck_pending_activations (info->client, NULL, NULL);
+ }
+}
+
+static gboolean
+activate_nm_not_running (gpointer user_data)
+{
+ ActivateInfo *info = user_data;
+ GError *error;
+
+ info->idle_id = 0;
+
+ error = g_error_new_literal (NM_CLIENT_ERROR,
+ NM_CLIENT_ERROR_MANAGER_NOT_RUNNING,
+ "NetworkManager is not running");
+ activate_info_complete (info, NULL, error);
+ activate_info_free (info);
+ g_clear_error (&error);
+ return FALSE;
+}
+
+/**
+ * nm_client_activate_connection:
+ * @client: a #NMClient
+ * @connection: (allow-none): an #NMConnection
+ * @device: (allow-none): the #NMDevice
+ * @specific_object: (allow-none): the object path of a connection-type-specific
+ * object this activation should use. This parameter is currently ignored for
+ * wired and mobile broadband connections, and the value of %NULL should be used
+ * (ie, no specific object). For Wi-Fi or WiMAX connections, pass the object
+ * path of a #NMAccessPoint or #NMWimaxNsp owned by @device, which you can
+ * get using nm_object_get_path(), and which will be used to complete the
+ * details of the newly added connection.
+ * @callback: (scope async) (allow-none): the function to call when the call is done
+ * @user_data: (closure): user data to pass to the callback function
+ *
+ * Starts a connection to a particular network using the configuration settings
+ * from @connection and the network device @device. Certain connection types
+ * also take a "specific object" which is the object path of a connection-
+ * specific object, like an #NMAccessPoint for Wi-Fi connections, or an
+ * #NMWimaxNsp for WiMAX connections, to which you wish to connect. If the
+ * specific object is not given, NetworkManager can, in some cases, automatically
+ * determine which network to connect to given the settings in @connection.
+ *
+ * If @connection is not given for a device-based activation, NetworkManager
+ * picks the best available connection for the device and activates it.
+ **/
+void
+nm_client_activate_connection (NMClient *client,
+ NMConnection *connection,
+ NMDevice *device,
+ const char *specific_object,
+ NMClientActivateFn callback,
+ gpointer user_data)
+{
+ NMClientPrivate *priv;
+ ActivateInfo *info;
+
+ g_return_if_fail (NM_IS_CLIENT (client));
+ if (device)
+ g_return_if_fail (NM_IS_DEVICE (device));
+ if (connection)
+ g_return_if_fail (NM_IS_CONNECTION (connection));
+
+ info = g_slice_new0 (ActivateInfo);
+ info->act_fn = callback;
+ info->user_data = user_data;
+ info->client = client;
+
+ priv = NM_CLIENT_GET_PRIVATE (client);
+ priv->pending_activations = g_slist_prepend (priv->pending_activations, info);
+
+ if (priv->manager_running == FALSE) {
+ info->idle_id = g_idle_add (activate_nm_not_running, info);
+ return;
+ }
+
+ dbus_g_proxy_begin_call (priv->client_proxy, "ActivateConnection",
+ activate_cb, info, NULL,
+ DBUS_TYPE_G_OBJECT_PATH, connection ? nm_connection_get_path (connection) : "/",
+ DBUS_TYPE_G_OBJECT_PATH, device ? nm_object_get_path (NM_OBJECT (device)) : "/",
+ DBUS_TYPE_G_OBJECT_PATH, specific_object ? specific_object : "/",
+ G_TYPE_INVALID);
+}
+
+static void
+add_activate_cb (DBusGProxy *proxy,
+ DBusGProxyCall *call,
+ gpointer user_data)
+{
+ ActivateInfo *info = user_data;
+ char *connection_path;
+ char *active_path;
+ GError *error = NULL;
+
+ dbus_g_proxy_end_call (proxy, call, &error,
+ DBUS_TYPE_G_OBJECT_PATH, &connection_path,
+ DBUS_TYPE_G_OBJECT_PATH, &active_path,
+ G_TYPE_INVALID);
+ if (error) {
+ activate_info_complete (info, NULL, error);
+ activate_info_free (info);
+ } else {
+ info->new_connection_path = connection_path;
+ info->active_path = active_path;
+ recheck_pending_activations (info->client, NULL, NULL);
+ }
+}
+
+/**
+ * nm_client_add_and_activate_connection:
+ * @client: a #NMClient
+ * @partial: (allow-none): an #NMConnection to add; the connection may be
+ * partially filled (or even %NULL) and will be completed by NetworkManager
+ * using the given @device and @specific_object before being added
+ * @device: the #NMDevice
+ * @specific_object: (allow-none): the object path of a connection-type-specific
+ * object this activation should use. This parameter is currently ignored for
+ * wired and mobile broadband connections, and the value of %NULL should be used
+ * (ie, no specific object). For Wi-Fi or WiMAX connections, pass the object
+ * path of a #NMAccessPoint or #NMWimaxNsp owned by @device, which you can
+ * get using nm_object_get_path(), and which will be used to complete the
+ * details of the newly added connection.
+ * @callback: (scope async) (allow-none): the function to call when the call is done
+ * @user_data: (closure): user data to pass to the callback function
+ *
+ * Adds a new connection using the given details (if any) as a template,
+ * automatically filling in missing settings with the capabilities of the
+ * given device and specific object. The new connection is then activated.
+ * Cannot be used for VPN connections at this time.
+ **/
+void
+nm_client_add_and_activate_connection (NMClient *client,
+ NMConnection *partial,
+ NMDevice *device,
+ const char *specific_object,
+ NMClientAddActivateFn callback,
+ gpointer user_data)
+{
+ NMClientPrivate *priv;
+ ActivateInfo *info;
+ GHashTable *hash = NULL;
+
+ g_return_if_fail (NM_IS_CLIENT (client));
+ g_return_if_fail (NM_IS_DEVICE (device));
+
+ info = g_slice_new0 (ActivateInfo);
+ info->add_act_fn = callback;
+ info->user_data = user_data;
+ info->client = client;
+
+ if (partial)
+ hash = nm_connection_to_hash (partial, NM_SETTING_HASH_FLAG_ALL);
+ if (!hash)
+ hash = g_hash_table_new (g_str_hash, g_str_equal);
+
+ priv = NM_CLIENT_GET_PRIVATE (client);
+ priv->pending_activations = g_slist_prepend (priv->pending_activations, info);
+
+ if (priv->manager_running) {
+ dbus_g_proxy_begin_call (priv->client_proxy, "AddAndActivateConnection",
+ add_activate_cb, info, NULL,
+ DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, hash,
+ DBUS_TYPE_G_OBJECT_PATH, nm_object_get_path (NM_OBJECT (device)),
+ DBUS_TYPE_G_OBJECT_PATH, specific_object ? specific_object : "/",
+ G_TYPE_INVALID);
+ } else
+ info->idle_id = g_idle_add (activate_nm_not_running, info);
+
+ g_hash_table_unref (hash);
+}
+
+static void
+active_connections_changed_cb (GObject *object, GParamSpec *pspec, gpointer user_data)
+{
+ recheck_pending_activations (NM_CLIENT (object), NULL, NULL);
+}
+
+static void
+object_creation_failed_cb (GObject *object, GError *error, char *failed_path)
+{
+ if (error)
+ recheck_pending_activations (NM_CLIENT (object), failed_path, error);
+}
+
+/**
+ * nm_client_deactivate_connection:
+ * @client: a #NMClient
+ * @active: the #NMActiveConnection to deactivate
+ *
+ * Deactivates an active #NMActiveConnection.
+ **/
+void
+nm_client_deactivate_connection (NMClient *client, NMActiveConnection *active)
+{
+ NMClientPrivate *priv;
+ const char *path;
+ GError *error = NULL;
+
+ g_return_if_fail (NM_IS_CLIENT (client));
+ g_return_if_fail (NM_IS_ACTIVE_CONNECTION (active));
+
+ priv = NM_CLIENT_GET_PRIVATE (client);
+ if (!priv->manager_running)
+ return;
+
+ path = nm_object_get_path (NM_OBJECT (active));
+ if (!dbus_g_proxy_call (priv->client_proxy, "DeactivateConnection", &error,
+ DBUS_TYPE_G_OBJECT_PATH, path,
+ G_TYPE_INVALID,
+ G_TYPE_INVALID)) {
+ g_warning ("Could not deactivate connection '%s': %s", path, error->message);
+ g_error_free (error);
+ }
+}
+
+/**
+ * nm_client_get_active_connections:
+ * @client: a #NMClient
+ *
+ * Gets the active connections.
+ *
+ * Returns: (transfer none) (element-type NMActiveConnection): a #GPtrArray
+ * containing all the active #NMActiveConnections.
+ * The returned array is owned by the client and should not be modified.
+ **/
+const GPtrArray *
+nm_client_get_active_connections (NMClient *client)
+{
+ NMClientPrivate *priv;
+
+ g_return_val_if_fail (NM_IS_CLIENT (client), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (client));
+
+ priv = NM_CLIENT_GET_PRIVATE (client);
+ if (!priv->manager_running)
+ return NULL;
+
+ return handle_ptr_array_return (priv->active_connections);
+}
+
+/**
+ * nm_client_wireless_get_enabled:
+ * @client: a #NMClient
+ *
+ * Determines whether the wireless is enabled.
+ *
+ * Returns: %TRUE if wireless is enabled
+ **/
+gboolean
+nm_client_wireless_get_enabled (NMClient *client)
+{
+ g_return_val_if_fail (NM_IS_CLIENT (client), FALSE);
+
+ _nm_object_ensure_inited (NM_OBJECT (client));
+ return NM_CLIENT_GET_PRIVATE (client)->wireless_enabled;
+}
+
+/**
+ * nm_client_wireless_set_enabled:
+ * @client: a #NMClient
+ * @enabled: %TRUE to enable wireless
+ *
+ * Enables or disables wireless devices.
+ **/
+void
+nm_client_wireless_set_enabled (NMClient *client, gboolean enabled)
+{
+ GValue value = G_VALUE_INIT;
+
+ g_return_if_fail (NM_IS_CLIENT (client));
+
+ if (!NM_CLIENT_GET_PRIVATE (client)->manager_running)
+ return;
+
+ g_value_init (&value, G_TYPE_BOOLEAN);
+ g_value_set_boolean (&value, enabled);
+
+ _nm_object_set_property (NM_OBJECT (client),
+ NM_DBUS_INTERFACE,
+ "WirelessEnabled",
+ &value);
+}
+
+/**
+ * nm_client_wireless_hardware_get_enabled:
+ * @client: a #NMClient
+ *
+ * Determines whether the wireless hardware is enabled.
+ *
+ * Returns: %TRUE if the wireless hardware is enabled
+ **/
+gboolean
+nm_client_wireless_hardware_get_enabled (NMClient *client)
+{
+ g_return_val_if_fail (NM_IS_CLIENT (client), FALSE);
+
+ _nm_object_ensure_inited (NM_OBJECT (client));
+ return NM_CLIENT_GET_PRIVATE (client)->wireless_hw_enabled;
+}
+
+/**
+ * nm_client_wwan_get_enabled:
+ * @client: a #NMClient
+ *
+ * Determines whether WWAN is enabled.
+ *
+ * Returns: %TRUE if WWAN is enabled
+ **/
+gboolean
+nm_client_wwan_get_enabled (NMClient *client)
+{
+ g_return_val_if_fail (NM_IS_CLIENT (client), FALSE);
+
+ _nm_object_ensure_inited (NM_OBJECT (client));
+ return NM_CLIENT_GET_PRIVATE (client)->wwan_enabled;
+}
+
+/**
+ * nm_client_wwan_set_enabled:
+ * @client: a #NMClient
+ * @enabled: %TRUE to enable WWAN
+ *
+ * Enables or disables WWAN devices.
+ **/
+void
+nm_client_wwan_set_enabled (NMClient *client, gboolean enabled)
+{
+ GValue value = G_VALUE_INIT;
+
+ g_return_if_fail (NM_IS_CLIENT (client));
+
+ if (!NM_CLIENT_GET_PRIVATE (client)->manager_running)
+ return;
+
+ g_value_init (&value, G_TYPE_BOOLEAN);
+ g_value_set_boolean (&value, enabled);
+
+ _nm_object_set_property (NM_OBJECT (client),
+ NM_DBUS_INTERFACE,
+ "WwanEnabled",
+ &value);
+}
+
+/**
+ * nm_client_wwan_hardware_get_enabled:
+ * @client: a #NMClient
+ *
+ * Determines whether the WWAN hardware is enabled.
+ *
+ * Returns: %TRUE if the WWAN hardware is enabled
+ **/
+gboolean
+nm_client_wwan_hardware_get_enabled (NMClient *client)
+{
+ g_return_val_if_fail (NM_IS_CLIENT (client), FALSE);
+
+ _nm_object_ensure_inited (NM_OBJECT (client));
+ return NM_CLIENT_GET_PRIVATE (client)->wwan_hw_enabled;
+}
+
+/**
+ * nm_client_wimax_get_enabled:
+ * @client: a #NMClient
+ *
+ * Determines whether WiMAX is enabled.
+ *
+ * Returns: %TRUE if WiMAX is enabled
+ **/
+gboolean
+nm_client_wimax_get_enabled (NMClient *client)
+{
+ g_return_val_if_fail (NM_IS_CLIENT (client), FALSE);
+
+ _nm_object_ensure_inited (NM_OBJECT (client));
+ return NM_CLIENT_GET_PRIVATE (client)->wimax_enabled;
+}
+
+/**
+ * nm_client_wimax_set_enabled:
+ * @client: a #NMClient
+ * @enabled: %TRUE to enable WiMAX
+ *
+ * Enables or disables WiMAX devices.
+ **/
+void
+nm_client_wimax_set_enabled (NMClient *client, gboolean enabled)
+{
+ GValue value = G_VALUE_INIT;
+
+ g_return_if_fail (NM_IS_CLIENT (client));
+
+ if (!NM_CLIENT_GET_PRIVATE (client)->manager_running)
+ return;
+
+ g_value_init (&value, G_TYPE_BOOLEAN);
+ g_value_set_boolean (&value, enabled);
+
+ _nm_object_set_property (NM_OBJECT (client),
+ NM_DBUS_INTERFACE,
+ "WimaxEnabled",
+ &value);
+}
+
+/**
+ * nm_client_wimax_hardware_get_enabled:
+ * @client: a #NMClient
+ *
+ * Determines whether the WiMAX hardware is enabled.
+ *
+ * Returns: %TRUE if the WiMAX hardware is enabled
+ **/
+gboolean
+nm_client_wimax_hardware_get_enabled (NMClient *client)
+{
+ g_return_val_if_fail (NM_IS_CLIENT (client), FALSE);
+
+ _nm_object_ensure_inited (NM_OBJECT (client));
+ return NM_CLIENT_GET_PRIVATE (client)->wimax_hw_enabled;
+}
+
+/**
+ * nm_client_get_version:
+ * @client: a #NMClient
+ *
+ * Gets NetworkManager version.
+ *
+ * Returns: string with the version
+ **/
+const char *
+nm_client_get_version (NMClient *client)
+{
+ NMClientPrivate *priv;
+
+ g_return_val_if_fail (NM_IS_CLIENT (client), NULL);
+
+ priv = NM_CLIENT_GET_PRIVATE (client);
+
+ _nm_object_ensure_inited (NM_OBJECT (client));
+
+ return priv->manager_running ? priv->version : NULL;
+}
+
+/**
+ * nm_client_get_state:
+ * @client: a #NMClient
+ *
+ * Gets the current daemon state.
+ *
+ * Returns: the current %NMState
+ **/
+NMState
+nm_client_get_state (NMClient *client)
+{
+ g_return_val_if_fail (NM_IS_CLIENT (client), NM_STATE_UNKNOWN);
+
+ _nm_object_ensure_inited (NM_OBJECT (client));
+
+ return NM_CLIENT_GET_PRIVATE (client)->state;
+}
+
+/**
+ * nm_client_get_startup:
+ * @client: a #NMClient
+ *
+ * Tests whether the daemon is still in the process of activating
+ * connections at startup.
+ *
+ * Returns: whether the daemon is still starting up
+ *
+ * Since: 0.9.10
+ **/
+gboolean
+nm_client_get_startup (NMClient *client)
+{
+ g_return_val_if_fail (NM_IS_CLIENT (client), NM_STATE_UNKNOWN);
+
+ _nm_object_ensure_inited (NM_OBJECT (client));
+
+ return NM_CLIENT_GET_PRIVATE (client)->startup;
+}
+
+/**
+ * nm_client_networking_get_enabled:
+ * @client: a #NMClient
+ *
+ * Whether networking is enabled or disabled.
+ *
+ * Returns: %TRUE if networking is enabled, %FALSE if networking is disabled
+ **/
+gboolean
+nm_client_networking_get_enabled (NMClient *client)
+{
+ g_return_val_if_fail (NM_IS_CLIENT (client), FALSE);
+
+ _nm_object_ensure_inited (NM_OBJECT (client));
+ return NM_CLIENT_GET_PRIVATE (client)->networking_enabled;
+}
+
+/**
+ * nm_client_networking_set_enabled:
+ * @client: a #NMClient
+ * @enabled: %TRUE to set networking enabled, %FALSE to set networking disabled
+ *
+ * Enables or disables networking. When networking is disabled, all controlled
+ * interfaces are disconnected and deactivated. When networking is enabled,
+ * all controlled interfaces are available for activation.
+ **/
+void
+nm_client_networking_set_enabled (NMClient *client, gboolean enable)
+{
+ GError *err = NULL;
+
+ g_return_if_fail (NM_IS_CLIENT (client));
+
+ if (!NM_CLIENT_GET_PRIVATE (client)->manager_running)
+ return;
+
+ if (!dbus_g_proxy_call (NM_CLIENT_GET_PRIVATE (client)->client_proxy, "Enable", &err,
+ G_TYPE_BOOLEAN, enable,
+ G_TYPE_INVALID,
+ G_TYPE_INVALID)) {
+ g_warning ("Error enabling/disabling networking: %s", err->message);
+ g_error_free (err);
+ }
+}
+
+/**
+ * nm_client_sleep:
+ * @client: a #NMClient
+ * @sleep_: %TRUE to put the daemon to sleep
+ *
+ * Deprecated; use nm_client_networking_set_enabled() instead.
+ **/
+void
+nm_client_sleep (NMClient *client, gboolean sleep_)
+{
+ nm_client_networking_set_enabled (client, !sleep_);
+}
+
+/**
+ * nm_client_get_manager_running:
+ * @client: a #NMClient
+ *
+ * Determines whether the daemon is running.
+ *
+ * Returns: %TRUE if the daemon is running
+ **/
+gboolean
+nm_client_get_manager_running (NMClient *client)
+{
+ g_return_val_if_fail (NM_IS_CLIENT (client), FALSE);
+
+ return NM_CLIENT_GET_PRIVATE (client)->manager_running;
+}
+
+/**
+ * nm_client_get_permission_result:
+ * @client: a #NMClient
+ * @permission: the permission for which to return the result, one of #NMClientPermission
+ *
+ * Requests the result of a specific permission, which indicates whether the
+ * client can or cannot perform the action the permission represents
+ *
+ * Returns: the permission's result, one of #NMClientPermissionResult
+ **/
+NMClientPermissionResult
+nm_client_get_permission_result (NMClient *client, NMClientPermission permission)
+{
+ gpointer result;
+
+ g_return_val_if_fail (NM_IS_CLIENT (client), NM_CLIENT_PERMISSION_RESULT_UNKNOWN);
+
+ result = g_hash_table_lookup (NM_CLIENT_GET_PRIVATE (client)->permissions,
+ GUINT_TO_POINTER (permission));
+ return GPOINTER_TO_UINT (result);
+}
+
+/**
+ * nm_client_get_logging:
+ * @client: a #NMClient
+ * @level: (allow-none): return location for logging level string
+ * @domains: (allow-none): return location for log domains string. The string is
+ * a list of domains separated by ","
+ * @error: (allow-none): return location for a #GError, or %NULL
+ *
+ * Gets NetworkManager current logging level and domains.
+ *
+ * Returns: %TRUE on success, %FALSE otherwise
+ *
+ * Since: 0.9.8
+ **/
+gboolean
+nm_client_get_logging (NMClient *client, char **level, char **domains, GError **error)
+{
+ NMClientPrivate *priv;
+
+ g_return_val_if_fail (NM_IS_CLIENT (client), FALSE);
+ g_return_val_if_fail (level == NULL || *level == NULL, FALSE);
+ g_return_val_if_fail (domains == NULL || *domains == NULL, FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ priv = NM_CLIENT_GET_PRIVATE (client);
+ if (!priv->manager_running) {
+ g_set_error_literal (error,
+ NM_CLIENT_ERROR,
+ NM_CLIENT_ERROR_MANAGER_NOT_RUNNING,
+ "NetworkManager is not running");
+ return FALSE;
+ }
+
+ if (!level && !domains)
+ return TRUE;
+
+ return dbus_g_proxy_call (priv->client_proxy, "GetLogging", error,
+ G_TYPE_INVALID,
+ G_TYPE_STRING, level,
+ G_TYPE_STRING, domains,
+ G_TYPE_INVALID);
+}
+
+/**
+ * nm_client_set_logging:
+ * @client: a #NMClient
+ * @level: (allow-none): logging level to set (%NULL or an empty string for no change)
+ * @domains: (allow-none): logging domains to set. The string should be a list of log
+ * domains separated by ",". (%NULL or an empty string for no change)
+ * @error: (allow-none): return location for a #GError, or %NULL
+ *
+ * Sets NetworkManager logging level and/or domains.
+ *
+ * Returns: %TRUE on success, %FALSE otherwise
+ *
+ * Since: 0.9.8
+ **/
+gboolean
+nm_client_set_logging (NMClient *client, const char *level, const char *domains, GError **error)
+{
+ NMClientPrivate *priv;
+
+ g_return_val_if_fail (NM_IS_CLIENT (client), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ priv = NM_CLIENT_GET_PRIVATE (client);
+ if (!priv->manager_running) {
+ g_set_error_literal (error,
+ NM_CLIENT_ERROR,
+ NM_CLIENT_ERROR_MANAGER_NOT_RUNNING,
+ "NetworkManager is not running");
+ return FALSE;
+ }
+
+ if (!level && !domains)
+ return TRUE;
+
+ return dbus_g_proxy_call (priv->client_proxy, "SetLogging", error,
+ G_TYPE_STRING, level ? level : "",
+ G_TYPE_STRING, domains ? domains : "",
+ G_TYPE_INVALID,
+ G_TYPE_INVALID);
+}
+
+/**
+ * nm_client_get_primary_connection:
+ * @client: an #NMClient
+ *
+ * Gets the #NMActiveConnection corresponding to the primary active
+ * network device.
+ *
+ * In particular, when there is no VPN active, or the VPN does not
+ * have the default route, this returns the active connection that has
+ * the default route. If there is a VPN active with the default route,
+ * then this function returns the active connection that contains the
+ * route to the VPN endpoint.
+ *
+ * If there is no default route, or the default route is over a
+ * non-NetworkManager-recognized device, this will return %NULL.
+ *
+ * Returns: (transfer none): the appropriate #NMActiveConnection, if
+ * any
+ *
+ * Since: 0.9.8.6
+ */
+NMActiveConnection *
+nm_client_get_primary_connection (NMClient *client)
+{
+ g_return_val_if_fail (NM_IS_CLIENT (client), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (client));
+ return NM_CLIENT_GET_PRIVATE (client)->primary_connection;
+}
+
+/**
+ * nm_client_get_activating_connection:
+ * @client: an #NMClient
+ *
+ * Gets the #NMActiveConnection corresponding to a
+ * currently-activating connection that is expected to become the new
+ * #NMClient:primary-connection upon successful activation.
+ *
+ * Returns: (transfer none): the appropriate #NMActiveConnection, if
+ * any.
+ *
+ * Since: 0.9.8.6
+ */
+NMActiveConnection *
+nm_client_get_activating_connection (NMClient *client)
+{
+ g_return_val_if_fail (NM_IS_CLIENT (client), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (client));
+ return NM_CLIENT_GET_PRIVATE (client)->activating_connection;
+}
+
+/****************************************************************/
+
+static void
+free_devices (NMClient *client, gboolean emit_signals)
+{
+ NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (client);
+ GPtrArray *devices;
+ NMDevice *device;
+ int i;
+
+ if (!priv->devices)
+ return;
+
+ devices = priv->devices;
+ priv->devices = NULL;
+ for (i = 0; i < devices->len; i++) {
+ device = devices->pdata[i];
+ if (emit_signals)
+ g_signal_emit (client, signals[DEVICE_REMOVED], 0, device);
+ g_object_unref (device);
+ }
+ g_ptr_array_free (devices, TRUE);
+}
+
+static void
+free_active_connections (NMClient *client, gboolean emit_signals)
+{
+ NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (client);
+ GPtrArray *active_connections;
+ NMActiveConnection *active_connection;
+ int i;
+
+ if (!priv->active_connections)
+ return;
+
+ active_connections = priv->active_connections;
+ priv->active_connections = NULL;
+ for (i = 0; i < active_connections->len; i++) {
+ active_connection = active_connections->pdata[i];
+ /* Break circular refs */
+ g_object_run_dispose (G_OBJECT (active_connection));
+ g_object_unref (active_connection);
+ }
+ g_ptr_array_free (active_connections, TRUE);
+
+ if (emit_signals)
+ g_object_notify (G_OBJECT (client), NM_CLIENT_ACTIVE_CONNECTIONS);
+}
+
+static void
+updated_properties (GObject *object, GAsyncResult *result, gpointer user_data)
+{
+ NMClient *client = NM_CLIENT (user_data);
+ GError *error = NULL;
+
+ if (!_nm_object_reload_properties_finish (NM_OBJECT (object), result, &error)) {
+ g_warning ("%s: error reading NMClient properties: %s", __func__, error->message);
+ g_error_free (error);
+ }
+
+ _nm_object_queue_notify (NM_OBJECT (client), NM_CLIENT_MANAGER_RUNNING);
+}
+
+static void
+proxy_name_owner_changed (DBusGProxy *proxy,
+ const char *name,
+ const char *old_owner,
+ const char *new_owner,
+ gpointer user_data)
+{
+ NMClient *client = NM_CLIENT (user_data);
+ NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (client);
+ gboolean old_good = (old_owner && strlen (old_owner));
+ gboolean new_good = (new_owner && strlen (new_owner));
+ gboolean new_running = FALSE;
+
+ if (!name || strcmp (name, NM_DBUS_SERVICE))
+ return;
+
+ if (!old_good && new_good)
+ new_running = TRUE;
+ else if (old_good && !new_good)
+ new_running = FALSE;
+
+ if (new_running == priv->manager_running)
+ return;
+
+ priv->manager_running = new_running;
+ if (!priv->manager_running) {
+ priv->state = NM_STATE_UNKNOWN;
+ priv->startup = FALSE;
+ _nm_object_queue_notify (NM_OBJECT (client), NM_CLIENT_MANAGER_RUNNING);
+ _nm_object_suppress_property_updates (NM_OBJECT (client), TRUE);
+ poke_wireless_devices_with_rf_status (client);
+ free_devices (client, TRUE);
+ free_active_connections (client, TRUE);
+ update_permissions (client, NULL);
+ priv->wireless_enabled = FALSE;
+ priv->wireless_hw_enabled = FALSE;
+ priv->wwan_enabled = FALSE;
+ priv->wwan_hw_enabled = FALSE;
+ priv->wimax_enabled = FALSE;
+ priv->wimax_hw_enabled = FALSE;
+ g_free (priv->version);
+ priv->version = NULL;
+
+ /* Clear object cache to ensure bad refcounting by clients doesn't
+ * keep objects in the cache.
+ */
+ _nm_object_cache_clear ();
+ } else {
+ _nm_object_suppress_property_updates (NM_OBJECT (client), FALSE);
+ _nm_object_reload_properties_async (NM_OBJECT (client), updated_properties, client);
+ client_recheck_permissions (priv->client_proxy, client);
+ }
+}
+
+/**
+ * nm_client_get_connectivity:
+ * @client: an #NMClient
+ *
+ * Gets the current network connectivity state. Contrast
+ * nm_client_check_connectivity() and
+ * nm_client_check_connectivity_async(), which re-check the
+ * connectivity state first before returning any information.
+ *
+ * Returns: the current connectivity state
+ * Since: 0.9.8.6
+ */
+NMConnectivityState
+nm_client_get_connectivity (NMClient *client)
+{
+ NMClientPrivate *priv;
+
+ g_return_val_if_fail (NM_IS_CLIENT (client), NM_STATE_UNKNOWN);
+ priv = NM_CLIENT_GET_PRIVATE (client);
+
+ _nm_object_ensure_inited (NM_OBJECT (client));
+
+ return priv->connectivity;
+}
+
+/**
+ * nm_client_check_connectivity:
+ * @client: an #NMClient
+ * @cancellable: a #GCancellable
+ * @error: return location for a #GError
+ *
+ * Updates the network connectivity state and returns the (new)
+ * current state. Contrast nm_client_get_connectivity(), which returns
+ * the most recent known state without re-checking.
+ *
+ * This is a blocking call; use nm_client_check_connectivity_async()
+ * if you do not want to block.
+ *
+ * Returns: the (new) current connectivity state
+ * Since: 0.9.8.6
+ */
+NMConnectivityState
+nm_client_check_connectivity (NMClient *client,
+ GCancellable *cancellable,
+ GError **error)
+{
+ NMClientPrivate *priv;
+ NMConnectivityState connectivity;
+
+ g_return_val_if_fail (NM_IS_CLIENT (client), NM_CONNECTIVITY_UNKNOWN);
+ priv = NM_CLIENT_GET_PRIVATE (client);
+
+ if (!dbus_g_proxy_call (priv->client_proxy, "CheckConnectivity", error,
+ G_TYPE_INVALID,
+ G_TYPE_UINT, &connectivity,
+ G_TYPE_INVALID))
+ connectivity = NM_CONNECTIVITY_UNKNOWN;
+
+ return connectivity;
+}
+
+typedef struct {
+ NMClient *client;
+ DBusGProxyCall *call;
+ GCancellable *cancellable;
+ guint cancelled_id;
+ NMConnectivityState connectivity;
+} CheckConnectivityData;
+
+static void
+check_connectivity_data_free (CheckConnectivityData *ccd)
+{
+ if (ccd->cancellable) {
+ if (ccd->cancelled_id)
+ g_signal_handler_disconnect (ccd->cancellable, ccd->cancelled_id);
+ g_object_unref (ccd->cancellable);
+ }
+
+ g_slice_free (CheckConnectivityData, ccd);
+}
+
+static void
+check_connectivity_cb (DBusGProxy *proxy,
+ DBusGProxyCall *call,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple = user_data;
+ CheckConnectivityData *ccd = g_simple_async_result_get_op_res_gpointer (simple);
+ GError *error = NULL;
+
+ if (ccd->cancellable) {
+ g_signal_handler_disconnect (ccd->cancellable, ccd->cancelled_id);
+ ccd->cancelled_id = 0;
+ }
+
+ if (!dbus_g_proxy_end_call (proxy, call, &error,
+ G_TYPE_UINT, &ccd->connectivity,
+ G_TYPE_INVALID))
+ g_simple_async_result_take_error (simple, error);
+
+ g_simple_async_result_complete (simple);
+ g_object_unref (simple);
+}
+
+static void
+check_connectivity_cancelled_cb (GCancellable *cancellable,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple = user_data;
+ CheckConnectivityData *ccd = g_simple_async_result_get_op_res_gpointer (simple);
+
+ g_signal_handler_disconnect (cancellable, ccd->cancelled_id);
+ ccd->cancelled_id = 0;
+
+ dbus_g_proxy_cancel_call (NM_CLIENT_GET_PRIVATE (ccd->client)->client_proxy, ccd->call);
+ g_simple_async_result_complete_in_idle (simple);
+}
+
+/**
+ * nm_client_check_connectivity_async:
+ * @client: an #NMClient
+ * @cancellable: a #GCancellable
+ * @callback: callback to call with the result
+ * @user_data: data for @callback.
+ *
+ * Asynchronously updates the network connectivity state and invokes
+ * @callback when complete. Contrast nm_client_get_connectivity(),
+ * which (immediately) returns the most recent known state without
+ * re-checking, and nm_client_check_connectivity(), which blocks.
+ *
+ * Since: 0.9.8.6
+ */
+void
+nm_client_check_connectivity_async (NMClient *client,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ NMClientPrivate *priv;
+ GSimpleAsyncResult *simple;
+ CheckConnectivityData *ccd;
+
+ g_return_if_fail (NM_IS_CLIENT (client));
+ priv = NM_CLIENT_GET_PRIVATE (client);
+
+ ccd = g_slice_new (CheckConnectivityData);
+ ccd->client = client;
+
+ simple = g_simple_async_result_new (G_OBJECT (client), callback, user_data,
+ nm_client_check_connectivity_async);
+ g_simple_async_result_set_op_res_gpointer (simple, ccd, (GDestroyNotify) check_connectivity_data_free);
+
+ if (cancellable) {
+ ccd->cancellable = g_object_ref (cancellable);
+ ccd->cancelled_id = g_signal_connect (cancellable, "cancelled",
+ G_CALLBACK (check_connectivity_cancelled_cb),
+ simple);
+ g_simple_async_result_set_check_cancellable (simple, cancellable);
+ }
+
+ ccd->call = dbus_g_proxy_begin_call (priv->client_proxy, "CheckConnectivity",
+ check_connectivity_cb, simple, NULL,
+ G_TYPE_INVALID);
+}
+
+/**
+ * nm_client_check_connectivity_finish:
+ * @client: an #NMClient
+ * @result: the #GAsyncResult
+ * @error: return location for a #GError
+ *
+ * Retrieves the result of an nm_client_check_connectivity_async()
+ * call.
+ *
+ * Returns: the (new) current connectivity state
+ * Since: 0.9.8.6
+ */
+NMConnectivityState
+nm_client_check_connectivity_finish (NMClient *client,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+ CheckConnectivityData *ccd;
+
+ g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (client), nm_client_check_connectivity_async), NM_CONNECTIVITY_UNKNOWN);
+
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+ ccd = g_simple_async_result_get_op_res_gpointer (simple);
+
+ if (g_simple_async_result_propagate_error (simple, error))
+ return NM_CONNECTIVITY_UNKNOWN;
+
+ return ccd->connectivity;
+}
+
+/****************************************************************/
+
+/**
+ * nm_client_new:
+ *
+ * Creates a new #NMClient.
+ *
+ * Note that this will do blocking D-Bus calls to initialize the
+ * client. You can use nm_client_new_async() if you want to avoid
+ * that.
+ *
+ * NOTE: #NMClient provides information about devices and a mechanism to
+ * control them. To access and modify network configuration data, use the
+ * #NMRemoteSettings object.
+ *
+ * Returns: a new #NMClient or NULL on an error
+ **/
+NMClient *
+nm_client_new (void)
+{
+ NMClient *client;
+
+ client = g_object_new (NM_TYPE_CLIENT, NM_OBJECT_DBUS_PATH, NM_DBUS_PATH, NULL);
+
+ /* NMObject's constructor() can fail on a D-Bus connection error. So we can
+ * get NULL here instead of a valid NMClient object.
+ */
+ if (client)
+ _nm_object_ensure_inited (NM_OBJECT (client));
+
+ return client;
+}
+
+static void
+client_inited (GObject *source, GAsyncResult *result, gpointer user_data)
+{
+ GSimpleAsyncResult *simple = user_data;
+ GError *error = NULL;
+
+ if (!g_async_initable_init_finish (G_ASYNC_INITABLE (source), result, &error))
+ g_simple_async_result_take_error (simple, error);
+ else
+ g_simple_async_result_set_op_res_gpointer (simple, source, g_object_unref);
+ g_simple_async_result_complete (simple);
+ g_object_unref (simple);
+}
+
+/**
+ * nm_client_new_async:
+ * @cancellable: a #GCancellable, or %NULL
+ * @callback: callback to call when the client is created
+ * @user_data: data for @callback
+ *
+ * Creates a new #NMClient and begins asynchronously initializing it.
+ * @callback will be called when it is done; use
+ * nm_client_new_finish() to get the result. Note that on an error,
+ * the callback can be invoked with two first parameters as NULL.
+ *
+ * NOTE: #NMClient provides information about devices and a mechanism to
+ * control them. To access and modify network configuration data, use the
+ * #NMRemoteSettings object.
+ **/
+void
+nm_client_new_async (GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ NMClient *client;
+ GSimpleAsyncResult *simple;
+
+ client = g_object_new (NM_TYPE_CLIENT, NM_OBJECT_DBUS_PATH, NM_DBUS_PATH, NULL);
+ /* When client is NULL, do no continue with initialization and run callback
+ * directly with result == NULL indicating NMClient creation failure.
+ */
+ if (!client) {
+ callback (NULL, NULL, user_data);
+ return;
+ }
+
+ simple = g_simple_async_result_new (NULL, callback, user_data, nm_client_new_async);
+ g_async_initable_init_async (G_ASYNC_INITABLE (client), G_PRIORITY_DEFAULT,
+ cancellable, client_inited, simple);
+}
+
+/**
+ * nm_client_new_finish:
+ * @result: a #GAsyncResult
+ * @error: location for a #GError, or %NULL
+ *
+ * Gets the result of an nm_client_new_async() call.
+ *
+ * Returns: a new #NMClient, or %NULL on error
+ **/
+NMClient *
+nm_client_new_finish (GAsyncResult *result, GError **error)
+{
+ GSimpleAsyncResult *simple;
+
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ if (!result) {
+ g_set_error_literal (error,
+ NM_CLIENT_ERROR,
+ NM_CLIENT_ERROR_UNKNOWN,
+ "NMClient initialization failed (or you passed NULL 'result' by mistake)");
+ return NULL;
+ }
+
+ g_return_val_if_fail (g_simple_async_result_is_valid (result, NULL, nm_client_new_async), NULL);
+
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+ if (g_simple_async_result_propagate_error (simple, error))
+ return NULL;
+ else
+ return g_object_ref (g_simple_async_result_get_op_res_gpointer (simple));
+}
+
+/*
+ * Validate D-Bus object path.
+ * The function is copied and adjusted version of
+ * g_variant_serialiser_is_object_path() from glib.
+ * FIXME: The function can be replaced by g_variant_is_object_path()
+ * when we start using GLib >= 2.24
+ */
+static gboolean
+_nm_client_is_object_path (const char *string)
+{
+ gsize i;
+
+ if (!g_utf8_validate (string, -1, NULL))
+ return FALSE;
+
+ /* The path must begin with an ASCII '/' (integer 47) character */
+ if (string[0] != '/')
+ return FALSE;
+
+ for (i = 1; string[i]; i++) {
+ /* Each element must only contain the ASCII characters
+ * "[A-Z][a-z][0-9]_"
+ */
+ if (g_ascii_isalnum (string[i]) || string[i] == '_')
+ ;
+ /* must consist of elements separated by slash characters. */
+ else if (string[i] == '/') {
+ /* No element may be the empty string. */
+ /* Multiple '/' characters cannot occur in sequence. */
+ if (string[i - 1] == '/')
+ return FALSE;
+ } else
+ return FALSE;
+ }
+
+ /* A trailing '/' character is not allowed unless the path is the
+ * root path (a single '/' character).
+ */
+ if (i > 1 && string[i - 1] == '/')
+ return FALSE;
+
+ return TRUE;
+}
+
+/*
+ * constructor() shouldn't be overriden in most cases, rather constructed()
+ * method is preferred and more useful.
+ * But, this serves as a workaround for bindings (use) calling the constructor()
+ * directly instead of nm_client_new() function, and neither providing
+ * construction properties. So, we fill "dbus-path" here if it was not specified
+ * (was set to default value (NULL)).
+ *
+ * It allows this python code:
+ * from gi.repository import NMClient
+ * nmclient = NMClient.Client()
+ * print nmclient.get_active_connections()
+ *
+ * instead of proper
+ * nmclient = NMClient.Client().new()
+ *
+ * Note:
+ * A nice overview of GObject construction is here:
+ * http://blogs.gnome.org/desrt/2012/02/26/a-gentle-introduction-to-gobject-construction
+ * It is much better explanation than the official docs
+ * http://developer.gnome.org/gobject/unstable/chapter-gobject.html#gobject-instantiation
+ */
+static GObject*
+constructor (GType type,
+ guint n_construct_params,
+ GObjectConstructParam *construct_params)
+{
+ GObject *object;
+ guint i;
+ const char *dbus_path;
+
+ for (i = 0; i < n_construct_params; i++) {
+ if (strcmp (construct_params[i].pspec->name, NM_OBJECT_DBUS_PATH) == 0) {
+ dbus_path = g_value_get_string (construct_params[i].value);
+ if (dbus_path == NULL) {
+ g_value_set_static_string (construct_params[i].value, NM_DBUS_PATH);
+ } else {
+ if (!_nm_client_is_object_path (dbus_path)) {
+ g_warning ("Passsed D-Bus object path '%s' is invalid; using default '%s' instead",
+ dbus_path, NM_DBUS_PATH);
+ g_value_set_static_string (construct_params[i].value, NM_DBUS_PATH);
+ }
+ }
+ break;
+ }
+ }
+
+ object = G_OBJECT_CLASS (nm_client_parent_class)->constructor (type,
+ n_construct_params,
+ construct_params);
+
+ return object;
+}
+
+static void
+constructed (GObject *object)
+{
+ NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (object);
+ GError *error = NULL;
+
+ if (!nm_utils_init (&error)) {
+ g_warning ("Couldn't initilize nm-utils/crypto system: %d %s",
+ error->code, error->message);
+ g_clear_error (&error);
+ }
+
+ G_OBJECT_CLASS (nm_client_parent_class)->constructed (object);
+
+ priv->client_proxy = _nm_object_new_proxy (NM_OBJECT (object), NULL, NM_DBUS_INTERFACE);
+
+ register_properties (NM_CLIENT (object));
+
+ /* Permissions */
+ dbus_g_proxy_add_signal (priv->client_proxy, "CheckPermissions", G_TYPE_INVALID);
+ dbus_g_proxy_connect_signal (priv->client_proxy,
+ "CheckPermissions",
+ G_CALLBACK (client_recheck_permissions),
+ object,
+ NULL);
+
+ if (_nm_object_is_connection_private (NM_OBJECT (object)))
+ priv->manager_running = TRUE;
+ else {
+ priv->bus_proxy = dbus_g_proxy_new_for_name (nm_object_get_connection (NM_OBJECT (object)),
+ DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS);
+ g_assert (priv->bus_proxy);
+
+ dbus_g_proxy_add_signal (priv->bus_proxy, "NameOwnerChanged",
+ G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
+ G_TYPE_INVALID);
+ dbus_g_proxy_connect_signal (priv->bus_proxy,
+ "NameOwnerChanged",
+ G_CALLBACK (proxy_name_owner_changed),
+ object, NULL);
+ }
+
+ g_signal_connect (object, "notify::" NM_CLIENT_WIRELESS_ENABLED,
+ G_CALLBACK (wireless_enabled_cb), NULL);
+
+ g_signal_connect (object, "notify::" NM_CLIENT_ACTIVE_CONNECTIONS,
+ G_CALLBACK (active_connections_changed_cb), NULL);
+
+ g_signal_connect (object, "object-creation-failed",
+ G_CALLBACK (object_creation_failed_cb), NULL);
+}
+
+static gboolean
+init_sync (GInitable *initable, GCancellable *cancellable, GError **error)
+{
+ NMClient *client = NM_CLIENT (initable);
+ NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (client);
+
+ if (!nm_client_parent_initable_iface->init (initable, cancellable, error))
+ return FALSE;
+
+ if (!_nm_object_is_connection_private (NM_OBJECT (client))) {
+ if (!dbus_g_proxy_call (priv->bus_proxy,
+ "NameHasOwner", error,
+ G_TYPE_STRING, NM_DBUS_SERVICE,
+ G_TYPE_INVALID,
+ G_TYPE_BOOLEAN, &priv->manager_running,
+ G_TYPE_INVALID))
+ return FALSE;
+ }
+
+ if (priv->manager_running && !get_permissions_sync (client, error))
+ return FALSE;
+
+ return TRUE;
+}
+
+typedef struct {
+ NMClient *client;
+ GSimpleAsyncResult *result;
+ gboolean properties_pending;
+ gboolean permissions_pending;
+} NMClientInitData;
+
+static void
+init_async_complete (NMClientInitData *init_data)
+{
+ if (init_data->properties_pending || init_data->permissions_pending)
+ return;
+
+ g_simple_async_result_complete (init_data->result);
+ g_object_unref (init_data->result);
+ g_slice_free (NMClientInitData, init_data);
+}
+
+static void
+init_async_got_permissions (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data)
+{
+ NMClientInitData *init_data = user_data;
+ GHashTable *permissions;
+ GError *error = NULL;
+
+ dbus_g_proxy_end_call (proxy, call, &error,
+ DBUS_TYPE_G_MAP_OF_STRING, &permissions,
+ G_TYPE_INVALID);
+ update_permissions (init_data->client, error ? NULL : permissions);
+ g_clear_error (&error);
+
+ init_data->permissions_pending = FALSE;
+ init_async_complete (init_data);
+}
+
+static void
+init_async_got_properties (GObject *source, GAsyncResult *result, gpointer user_data)
+{
+ NMClientInitData *init_data = user_data;
+ GError *error = NULL;
+
+ if (!nm_client_parent_async_initable_iface->init_finish (G_ASYNC_INITABLE (source), result, &error))
+ g_simple_async_result_take_error (init_data->result, error);
+
+ init_data->properties_pending = FALSE;
+ init_async_complete (init_data);
+}
+
+static void
+finish_init (NMClientInitData *init_data)
+{
+ NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (init_data->client);
+
+ nm_client_parent_async_initable_iface->init_async (G_ASYNC_INITABLE (init_data->client),
+ G_PRIORITY_DEFAULT, NULL, /* FIXME cancellable */
+ init_async_got_properties, init_data);
+ init_data->properties_pending = TRUE;
+
+ dbus_g_proxy_begin_call (priv->client_proxy, "GetPermissions",
+ init_async_got_permissions, init_data, NULL,
+ G_TYPE_INVALID);
+ init_data->permissions_pending = TRUE;
+}
+
+static void
+init_async_got_manager_running (DBusGProxy *proxy, DBusGProxyCall *call,
+ gpointer user_data)
+{
+ NMClientInitData *init_data = user_data;
+ NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (init_data->client);
+ GError *error = NULL;
+
+ if (!dbus_g_proxy_end_call (proxy, call, &error,
+ G_TYPE_BOOLEAN, &priv->manager_running,
+ G_TYPE_INVALID)) {
+ g_simple_async_result_take_error (init_data->result, error);
+ init_async_complete (init_data);
+ return;
+ }
+
+ if (!priv->manager_running) {
+ init_async_complete (init_data);
+ return;
+ }
+
+ finish_init (init_data);
+}
+
+static void
+init_async (GAsyncInitable *initable, int io_priority,
+ GCancellable *cancellable, GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ NMClientInitData *init_data;
+ NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (initable);
+
+ init_data = g_slice_new0 (NMClientInitData);
+ init_data->client = NM_CLIENT (initable);
+ init_data->result = g_simple_async_result_new (G_OBJECT (initable), callback,
+ user_data, init_async);
+ g_simple_async_result_set_op_res_gboolean (init_data->result, TRUE);
+
+ if (_nm_object_is_connection_private (NM_OBJECT (init_data->client)))
+ finish_init (init_data);
+ else {
+ /* Check if NM is running */
+ dbus_g_proxy_begin_call (priv->bus_proxy, "NameHasOwner",
+ init_async_got_manager_running,
+ init_data, NULL,
+ G_TYPE_STRING, NM_DBUS_SERVICE,
+ G_TYPE_INVALID);
+ }
+}
+
+static gboolean
+init_finish (GAsyncInitable *initable, GAsyncResult *result, GError **error)
+{
+ GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
+
+ if (g_simple_async_result_propagate_error (simple, error))
+ return FALSE;
+ else
+ return TRUE;
+}
+
+static void
+dispose (GObject *object)
+{
+ NMClient *client = NM_CLIENT (object);
+ NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (object);
+
+ if (priv->perm_call) {
+ dbus_g_proxy_cancel_call (priv->client_proxy, priv->perm_call);
+ priv->perm_call = NULL;
+ }
+
+ g_clear_object (&priv->client_proxy);
+ g_clear_object (&priv->bus_proxy);
+
+ free_devices (client, FALSE);
+ free_active_connections (client, FALSE);
+ g_clear_object (&priv->primary_connection);
+ g_clear_object (&priv->activating_connection);
+
+ g_slist_free_full (priv->pending_activations, (GDestroyNotify) activate_info_free);
+ priv->pending_activations = NULL;
+
+ g_hash_table_destroy (priv->permissions);
+ priv->permissions = NULL;
+
+ G_OBJECT_CLASS (nm_client_parent_class)->dispose (object);
+}
+
+static void
+finalize (GObject *object)
+{
+ NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (object);
+
+ g_free (priv->version);
+
+ G_OBJECT_CLASS (nm_client_parent_class)->finalize (object);
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (object);
+ gboolean b;
+
+ switch (prop_id) {
+ case PROP_NETWORKING_ENABLED:
+ b = g_value_get_boolean (value);
+ if (priv->networking_enabled != b) {
+ nm_client_networking_set_enabled (NM_CLIENT (object), b);
+ /* Let the property value flip when we get the change signal from NM */
+ }
+ break;
+ case PROP_WIRELESS_ENABLED:
+ b = g_value_get_boolean (value);
+ if (priv->wireless_enabled != b) {
+ nm_client_wireless_set_enabled (NM_CLIENT (object), b);
+ /* Let the property value flip when we get the change signal from NM */
+ }
+ break;
+ case PROP_WWAN_ENABLED:
+ b = g_value_get_boolean (value);
+ if (priv->wwan_enabled != b) {
+ nm_client_wwan_set_enabled (NM_CLIENT (object), b);
+ /* Let the property value flip when we get the change signal from NM */
+ }
+ break;
+ case PROP_WIMAX_ENABLED:
+ b = g_value_get_boolean (value);
+ if (priv->wimax_enabled != b) {
+ nm_client_wimax_set_enabled (NM_CLIENT (object), b);
+ /* Let the property value flip when we get the change signal from NM */
+ }
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NMClient *self = NM_CLIENT (object);
+ NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE (self);
+
+ _nm_object_ensure_inited (NM_OBJECT (object));
+
+ switch (prop_id) {
+ case PROP_VERSION:
+ g_value_set_string (value, nm_client_get_version (self));
+ break;
+ case PROP_STATE:
+ g_value_set_uint (value, nm_client_get_state (self));
+ break;
+ case PROP_STARTUP:
+ g_value_set_boolean (value, nm_client_get_startup (self));
+ break;
+ case PROP_MANAGER_RUNNING:
+ g_value_set_boolean (value, priv->manager_running);
+ break;
+ case PROP_NETWORKING_ENABLED:
+ g_value_set_boolean (value, nm_client_networking_get_enabled (self));
+ break;
+ case PROP_WIRELESS_ENABLED:
+ g_value_set_boolean (value, priv->wireless_enabled);
+ break;
+ case PROP_WIRELESS_HARDWARE_ENABLED:
+ g_value_set_boolean (value, priv->wireless_hw_enabled);
+ break;
+ case PROP_WWAN_ENABLED:
+ g_value_set_boolean (value, priv->wwan_enabled);
+ break;
+ case PROP_WWAN_HARDWARE_ENABLED:
+ g_value_set_boolean (value, priv->wwan_hw_enabled);
+ break;
+ case PROP_WIMAX_ENABLED:
+ g_value_set_boolean (value, priv->wimax_enabled);
+ break;
+ case PROP_WIMAX_HARDWARE_ENABLED:
+ g_value_set_boolean (value, priv->wimax_hw_enabled);
+ break;
+ case PROP_ACTIVE_CONNECTIONS:
+ g_value_set_boxed (value, nm_client_get_active_connections (self));
+ break;
+ case PROP_CONNECTIVITY:
+ g_value_set_uint (value, priv->connectivity);
+ break;
+ case PROP_PRIMARY_CONNECTION:
+ g_value_set_object (value, priv->primary_connection);
+ break;
+ case PROP_ACTIVATING_CONNECTION:
+ g_value_set_object (value, priv->activating_connection);
+ break;
+ case PROP_DEVICES:
+ g_value_set_boxed (value, nm_client_get_devices (self));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nm_client_class_init (NMClientClass *client_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (client_class);
+
+ g_type_class_add_private (client_class, sizeof (NMClientPrivate));
+
+ /* virtual methods */
+ object_class->constructor = constructor;
+ object_class->constructed = constructed;
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+ object_class->dispose = dispose;
+ object_class->finalize = finalize;
+
+ /* properties */
+
+ /**
+ * NMClient:version:
+ *
+ * The NetworkManager version.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_VERSION,
+ g_param_spec_string (NM_CLIENT_VERSION, "", "",
+ NULL,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMClient:state:
+ *
+ * The current daemon state.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_STATE,
+ g_param_spec_uint (NM_CLIENT_STATE, "", "",
+ NM_STATE_UNKNOWN, NM_STATE_CONNECTED_GLOBAL, NM_STATE_UNKNOWN,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMClient:startup:
+ *
+ * Whether the daemon is still starting up.
+ *
+ * Since: 0.9.10
+ **/
+ g_object_class_install_property
+ (object_class, PROP_STARTUP,
+ g_param_spec_boolean (NM_CLIENT_STARTUP, "", "",
+ FALSE,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMClient:manager-running:
+ *
+ * Whether the daemon is running.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_MANAGER_RUNNING,
+ g_param_spec_boolean (NM_CLIENT_MANAGER_RUNNING, "", "",
+ FALSE,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMClient:networking-enabled:
+ *
+ * Whether networking is enabled.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_NETWORKING_ENABLED,
+ g_param_spec_boolean (NM_CLIENT_NETWORKING_ENABLED, "", "",
+ TRUE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMClient:wireless-enabled:
+ *
+ * Whether wireless is enabled.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_WIRELESS_ENABLED,
+ g_param_spec_boolean (NM_CLIENT_WIRELESS_ENABLED, "", "",
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMClient:wireless-hardware-enabled:
+ *
+ * Whether the wireless hardware is enabled.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_WIRELESS_HARDWARE_ENABLED,
+ g_param_spec_boolean (NM_CLIENT_WIRELESS_HARDWARE_ENABLED, "", "",
+ TRUE,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMClient:wwan-enabled:
+ *
+ * Whether WWAN functionality is enabled.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_WWAN_ENABLED,
+ g_param_spec_boolean (NM_CLIENT_WWAN_ENABLED, "", "",
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMClient:wwan-hardware-enabled:
+ *
+ * Whether the WWAN hardware is enabled.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_WWAN_HARDWARE_ENABLED,
+ g_param_spec_boolean (NM_CLIENT_WWAN_HARDWARE_ENABLED, "", "",
+ FALSE,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMClient:wimax-enabled:
+ *
+ * Whether WiMAX functionality is enabled.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_WIMAX_ENABLED,
+ g_param_spec_boolean (NM_CLIENT_WIMAX_ENABLED, "", "",
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMClient:wimax-hardware-enabled:
+ *
+ * Whether the WiMAX hardware is enabled.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_WIMAX_HARDWARE_ENABLED,
+ g_param_spec_boolean (NM_CLIENT_WIMAX_HARDWARE_ENABLED, "", "",
+ FALSE,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMClient:active-connections:
+ *
+ * The active connections.
+ * Type: GLib.PtrArray
+ **/
+ g_object_class_install_property
+ (object_class, PROP_ACTIVE_CONNECTIONS,
+ g_param_spec_boxed (NM_CLIENT_ACTIVE_CONNECTIONS, "", "",
+ NM_TYPE_OBJECT_ARRAY,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMClient:connectivity:
+ *
+ * The network connectivity state.
+ *
+ * Since: 0.9.8.6
+ */
+ g_object_class_install_property
+ (object_class, PROP_CONNECTIVITY,
+ g_param_spec_uint (NM_CLIENT_CONNECTIVITY, "", "",
+ NM_CONNECTIVITY_UNKNOWN, NM_CONNECTIVITY_FULL, NM_CONNECTIVITY_UNKNOWN,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMClient:primary-connection:
+ *
+ * The #NMActiveConnection of the device with the default route;
+ * see nm_client_get_primary_connection() for more details.
+ *
+ * Since: 0.9.8.6
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PRIMARY_CONNECTION,
+ g_param_spec_object (NM_CLIENT_PRIMARY_CONNECTION, "", "",
+ NM_TYPE_ACTIVE_CONNECTION,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMClient:activating-connection:
+ *
+ * The #NMActiveConnection of the activating connection that is
+ * likely to become the new #NMClient:primary-connection.
+ *
+ * Since: 0.9.8.6
+ **/
+ g_object_class_install_property
+ (object_class, PROP_ACTIVATING_CONNECTION,
+ g_param_spec_object (NM_CLIENT_ACTIVATING_CONNECTION, "", "",
+ NM_TYPE_ACTIVE_CONNECTION,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMClient:devices:
+ *
+ * List of known network devices.
+ *
+ * Since: 0.9.10
+ **/
+ g_object_class_install_property
+ (object_class, PROP_DEVICES,
+ g_param_spec_boxed (NM_CLIENT_DEVICES, "", "",
+ NM_TYPE_OBJECT_ARRAY,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /* signals */
+
+ /**
+ * NMClient::device-added:
+ * @client: the client that received the signal
+ * @device: (type NMDevice): the new device
+ *
+ * Notifies that a #NMDevice is added.
+ **/
+ signals[DEVICE_ADDED] =
+ g_signal_new ("device-added",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NMClientClass, device_added),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 1,
+ G_TYPE_OBJECT);
+
+ /**
+ * NMClient::device-removed:
+ * @client: the client that received the signal
+ * @device: (type NMDevice): the removed device
+ *
+ * Notifies that a #NMDevice is removed.
+ **/
+ signals[DEVICE_REMOVED] =
+ g_signal_new ("device-removed",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NMClientClass, device_removed),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 1,
+ G_TYPE_OBJECT);
+
+ /**
+ * NMClient::permission-changed:
+ * @client: the client that received the signal
+ * @permission: a permission from #NMClientPermission
+ * @result: the permission's result, one of #NMClientPermissionResult
+ *
+ * Notifies that a permission has changed
+ **/
+ signals[PERMISSION_CHANGED] =
+ g_signal_new ("permission-changed",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0, NULL, NULL, NULL,
+ G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT);
+}
+
+static void
+nm_client_initable_iface_init (GInitableIface *iface)
+{
+ nm_client_parent_initable_iface = g_type_interface_peek_parent (iface);
+
+ iface->init = init_sync;
+}
+
+static void
+nm_client_async_initable_iface_init (GAsyncInitableIface *iface)
+{
+ nm_client_parent_async_initable_iface = g_type_interface_peek_parent (iface);
+
+ iface->init_async = init_async;
+ iface->init_finish = init_finish;
+}
diff --git a/libnm/nm-client.h b/libnm/nm-client.h
new file mode 100644
index 0000000000..ffe513cb03
--- /dev/null
+++ b/libnm/nm-client.h
@@ -0,0 +1,255 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2008 Novell, Inc.
+ * Copyright 2007 - 2012 Red Hat, Inc.
+ */
+
+#ifndef NM_CLIENT_H
+#define NM_CLIENT_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gio/gio.h>
+#include <dbus/dbus-glib.h>
+#include <NetworkManager.h>
+#include "nm-object.h"
+#include "nm-device.h"
+#include "nm-active-connection.h"
+#include "nm-vpn-connection.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_CLIENT (nm_client_get_type ())
+#define NM_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_CLIENT, NMClient))
+#define NM_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_CLIENT, NMClientClass))
+#define NM_IS_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_CLIENT))
+#define NM_IS_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_CLIENT))
+#define NM_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_CLIENT, NMClientClass))
+
+#define NM_CLIENT_VERSION "version"
+#define NM_CLIENT_STATE "state"
+#define NM_CLIENT_STARTUP "startup"
+#define NM_CLIENT_MANAGER_RUNNING "manager-running"
+#define NM_CLIENT_NETWORKING_ENABLED "networking-enabled"
+#define NM_CLIENT_WIRELESS_ENABLED "wireless-enabled"
+#define NM_CLIENT_WIRELESS_HARDWARE_ENABLED "wireless-hardware-enabled"
+#define NM_CLIENT_WWAN_ENABLED "wwan-enabled"
+#define NM_CLIENT_WWAN_HARDWARE_ENABLED "wwan-hardware-enabled"
+#define NM_CLIENT_WIMAX_ENABLED "wimax-enabled"
+#define NM_CLIENT_WIMAX_HARDWARE_ENABLED "wimax-hardware-enabled"
+#define NM_CLIENT_ACTIVE_CONNECTIONS "active-connections"
+#define NM_CLIENT_CONNECTIVITY "connectivity"
+#define NM_CLIENT_PRIMARY_CONNECTION "primary-connection"
+#define NM_CLIENT_ACTIVATING_CONNECTION "activating-connection"
+#define NM_CLIENT_DEVICES "devices"
+
+/**
+ * NMClientPermission:
+ * @NM_CLIENT_PERMISSION_NONE: unknown or no permission
+ * @NM_CLIENT_PERMISSION_ENABLE_DISABLE_NETWORK: controls whether networking
+ * can be globally enabled or disabled
+ * @NM_CLIENT_PERMISSION_ENABLE_DISABLE_WIFI: controls whether Wi-Fi can be
+ * globally enabled or disabled
+ * @NM_CLIENT_PERMISSION_ENABLE_DISABLE_WWAN: controls whether WWAN (3G) can be
+ * globally enabled or disabled
+ * @NM_CLIENT_PERMISSION_ENABLE_DISABLE_WIMAX: controls whether WiMAX can be
+ * globally enabled or disabled
+ * @NM_CLIENT_PERMISSION_SLEEP_WAKE: controls whether the client can ask
+ * NetworkManager to sleep and wake
+ * @NM_CLIENT_PERMISSION_NETWORK_CONTROL: controls whether networking connections
+ * can be started, stopped, and changed
+ * @NM_CLIENT_PERMISSION_WIFI_SHARE_PROTECTED: controls whether a password
+ * protected Wi-Fi hotspot can be created
+ * @NM_CLIENT_PERMISSION_WIFI_SHARE_OPEN: controls whether an open Wi-Fi hotspot
+ * can be created
+ * @NM_CLIENT_PERMISSION_SETTINGS_MODIFY_SYSTEM: controls whether connections
+ * that are available to all users can be modified
+ * @NM_CLIENT_PERMISSION_SETTINGS_MODIFY_OWN: controls whether connections
+ * owned by the current user can be modified
+ * @NM_CLIENT_PERMISSION_SETTINGS_MODIFY_HOSTNAME: controls whether the
+ * persistent hostname can be changed
+ * @NM_CLIENT_PERMISSION_LAST: a reserved boundary value
+ *
+ * #NMClientPermission values indicate various permissions that NetworkManager
+ * clients can obtain to perform certain tasks on behalf of the current user.
+ **/
+typedef enum {
+ NM_CLIENT_PERMISSION_NONE = 0,
+ NM_CLIENT_PERMISSION_ENABLE_DISABLE_NETWORK = 1,
+ NM_CLIENT_PERMISSION_ENABLE_DISABLE_WIFI = 2,
+ NM_CLIENT_PERMISSION_ENABLE_DISABLE_WWAN = 3,
+ NM_CLIENT_PERMISSION_ENABLE_DISABLE_WIMAX = 4,
+ NM_CLIENT_PERMISSION_SLEEP_WAKE = 5,
+ NM_CLIENT_PERMISSION_NETWORK_CONTROL = 6,
+ NM_CLIENT_PERMISSION_WIFI_SHARE_PROTECTED = 7,
+ NM_CLIENT_PERMISSION_WIFI_SHARE_OPEN = 8,
+ NM_CLIENT_PERMISSION_SETTINGS_MODIFY_SYSTEM = 9,
+ NM_CLIENT_PERMISSION_SETTINGS_MODIFY_OWN = 10,
+ NM_CLIENT_PERMISSION_SETTINGS_MODIFY_HOSTNAME = 11,
+
+ NM_CLIENT_PERMISSION_LAST = NM_CLIENT_PERMISSION_SETTINGS_MODIFY_HOSTNAME
+} NMClientPermission;
+
+/**
+ * NMClientPermissionResult:
+ * @NM_CLIENT_PERMISSION_RESULT_UNKNOWN: unknown or no authorization
+ * @NM_CLIENT_PERMISSION_RESULT_YES: the permission is available
+ * @NM_CLIENT_PERMISSION_RESULT_AUTH: authorization is necessary before the
+ * permission is available
+ * @NM_CLIENT_PERMISSION_RESULT_NO: permission to perform the operation is
+ * denied by system policy
+ *
+ * #NMClientPermissionResult values indicate what authorizations and permissions
+ * the user requires to obtain a given #NMClientPermission
+ **/
+typedef enum {
+ NM_CLIENT_PERMISSION_RESULT_UNKNOWN = 0,
+ NM_CLIENT_PERMISSION_RESULT_YES,
+ NM_CLIENT_PERMISSION_RESULT_AUTH,
+ NM_CLIENT_PERMISSION_RESULT_NO
+} NMClientPermissionResult;
+
+/**
+ * NMClientError:
+ * @NM_CLIENT_ERROR_UNKNOWN: unknown or unclassified error
+ * @NM_CLIENT_ERROR_MANAGER_NOT_RUNNING: an operation that requires NetworkManager
+ * failed because NetworkManager is not running
+ *
+ * Describes errors that may result from operations involving a #NMClient.
+ **/
+typedef enum {
+ NM_CLIENT_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/
+ NM_CLIENT_ERROR_MANAGER_NOT_RUNNING, /*< nick=ManagerNotRunning >*/
+} NMClientError;
+
+#define NM_CLIENT_ERROR nm_client_error_quark ()
+NM_AVAILABLE_IN_0_9_10
+GQuark nm_client_error_quark (void);
+
+typedef struct {
+ NMObject parent;
+} NMClient;
+
+typedef struct {
+ NMObjectClass parent;
+
+ /* Signals */
+ void (*device_added) (NMClient *client, NMDevice *device);
+ void (*device_removed) (NMClient *client, NMDevice *device);
+ void (*permission_changed) (NMClient *client,
+ NMClientPermission permission,
+ NMClientPermissionResult result);
+
+ /* Padding for future expansion */
+ void (*_reserved1) (void);
+ void (*_reserved2) (void);
+ void (*_reserved3) (void);
+ void (*_reserved4) (void);
+ void (*_reserved5) (void);
+ void (*_reserved6) (void);
+} NMClientClass;
+
+GType nm_client_get_type (void);
+
+NMClient *nm_client_new (void);
+
+void nm_client_new_async (GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+NMClient *nm_client_new_finish (GAsyncResult *result,
+ GError **error);
+
+const GPtrArray *nm_client_get_devices (NMClient *client);
+NMDevice *nm_client_get_device_by_path (NMClient *client, const char *object_path);
+NMDevice *nm_client_get_device_by_iface (NMClient *client, const char *iface);
+
+typedef void (*NMClientActivateFn) (NMClient *client,
+ NMActiveConnection *active_connection,
+ GError *error,
+ gpointer user_data);
+
+void nm_client_activate_connection (NMClient *client,
+ NMConnection *connection,
+ NMDevice *device,
+ const char *specific_object,
+ NMClientActivateFn callback,
+ gpointer user_data);
+
+typedef void (*NMClientAddActivateFn) (NMClient *client,
+ NMActiveConnection *connection,
+ const char *new_connection_path,
+ GError *error,
+ gpointer user_data);
+
+void nm_client_add_and_activate_connection (NMClient *client,
+ NMConnection *partial,
+ NMDevice *device,
+ const char *specific_object,
+ NMClientAddActivateFn callback,
+ gpointer user_data);
+
+void nm_client_deactivate_connection (NMClient *client, NMActiveConnection *active);
+
+gboolean nm_client_networking_get_enabled (NMClient *client);
+void nm_client_networking_set_enabled (NMClient *client, gboolean enabled);
+
+gboolean nm_client_wireless_get_enabled (NMClient *client);
+void nm_client_wireless_set_enabled (NMClient *client, gboolean enabled);
+gboolean nm_client_wireless_hardware_get_enabled (NMClient *client);
+
+gboolean nm_client_wwan_get_enabled (NMClient *client);
+void nm_client_wwan_set_enabled (NMClient *client, gboolean enabled);
+gboolean nm_client_wwan_hardware_get_enabled (NMClient *client);
+
+gboolean nm_client_wimax_get_enabled (NMClient *client);
+void nm_client_wimax_set_enabled (NMClient *client, gboolean enabled);
+gboolean nm_client_wimax_hardware_get_enabled (NMClient *client);
+
+const char *nm_client_get_version (NMClient *client);
+NMState nm_client_get_state (NMClient *client);
+NM_AVAILABLE_IN_0_9_10
+gboolean nm_client_get_startup (NMClient *client);
+gboolean nm_client_get_manager_running (NMClient *client);
+const GPtrArray *nm_client_get_active_connections (NMClient *client);
+void nm_client_sleep (NMClient *client, gboolean sleep_);
+
+NMClientPermissionResult nm_client_get_permission_result (NMClient *client,
+ NMClientPermission permission);
+
+gboolean nm_client_get_logging (NMClient *client, char **level, char **domains, GError **error);
+gboolean nm_client_set_logging (NMClient *client, const char *level, const char *domains, GError **error);
+
+NMConnectivityState nm_client_get_connectivity (NMClient *client);
+
+NMConnectivityState nm_client_check_connectivity (NMClient *client,
+ GCancellable *cancellable,
+ GError **error);
+void nm_client_check_connectivity_async (NMClient *client,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+NMConnectivityState nm_client_check_connectivity_finish (NMClient *client,
+ GAsyncResult *result,
+ GError **error);
+
+NMActiveConnection *nm_client_get_primary_connection (NMClient *client);
+NMActiveConnection *nm_client_get_activating_connection (NMClient *client);
+
+G_END_DECLS
+
+#endif /* NM_CLIENT_H */
diff --git a/libnm/nm-dbus-helpers-private.h b/libnm/nm-dbus-helpers-private.h
new file mode 100644
index 0000000000..8a350e783d
--- /dev/null
+++ b/libnm/nm-dbus-helpers-private.h
@@ -0,0 +1,36 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NM_DBUS_HELPERS_PRIVATE_H
+#define NM_DBUS_HELPERS_PRIVATE_H
+
+#include <gio/gio.h>
+#include <dbus/dbus.h>
+#include <dbus/dbus-glib-lowlevel.h>
+
+DBusGConnection *_nm_dbus_new_connection (GError **error);
+
+gboolean _nm_dbus_is_connection_private (DBusGConnection *connection);
+
+DBusGProxy * _nm_dbus_new_proxy_for_connection (DBusGConnection *connection,
+ const char *path,
+ const char *interface);
+
+#endif /* NM_DBUS_HELPERS_PRIVATE_H */
diff --git a/libnm/nm-dbus-helpers.c b/libnm/nm-dbus-helpers.c
new file mode 100644
index 0000000000..25004310d9
--- /dev/null
+++ b/libnm/nm-dbus-helpers.c
@@ -0,0 +1,99 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#include <string.h>
+#include <config.h>
+#include <gio/gio.h>
+#include <dbus/dbus.h>
+#include <dbus/dbus-glib-lowlevel.h>
+#include "nm-dbus-helpers-private.h"
+#include "NetworkManager.h"
+
+static dbus_int32_t priv_slot = -1;
+
+static gboolean
+_ensure_dbus_data_slot (void)
+{
+ static gsize init_value = 0;
+ gboolean success = TRUE;
+
+ if (g_once_init_enter (&init_value)) {
+ success = dbus_connection_allocate_data_slot (&priv_slot);
+ g_once_init_leave (&init_value, 1);
+ }
+ return success;
+}
+
+DBusGConnection *
+_nm_dbus_new_connection (GError **error)
+{
+ DBusGConnection *connection = NULL;
+
+ if (!_ensure_dbus_data_slot ()) {
+ g_set_error (error, DBUS_GERROR, DBUS_GERROR_FAILED, "failed to allocated data slot");
+ return NULL;
+ }
+
+#if HAVE_DBUS_GLIB_100
+ /* If running as root try the private bus first */
+ if (0 == geteuid ()) {
+ connection = dbus_g_connection_open ("unix:path=" NMRUNDIR "/private", error);
+ if (connection) {
+ DBusConnection *dbus_connection = dbus_g_connection_get_connection (connection);
+
+ /* Mark this connection as private */
+ dbus_connection_set_data (dbus_connection, priv_slot, GUINT_TO_POINTER (TRUE), NULL);
+ dbus_connection_set_exit_on_disconnect (dbus_connection, FALSE);
+ return connection;
+ }
+ /* Fall back to a bus if for some reason private socket isn't available */
+ g_clear_error (error);
+ }
+#endif
+
+ if (connection == NULL)
+ connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, error);
+
+ return connection;
+}
+
+gboolean
+_nm_dbus_is_connection_private (DBusGConnection *connection)
+{
+ if (!_ensure_dbus_data_slot ())
+ return FALSE;
+ return !!dbus_connection_get_data (dbus_g_connection_get_connection (connection), priv_slot);
+}
+
+DBusGProxy *
+_nm_dbus_new_proxy_for_connection (DBusGConnection *connection,
+ const char *path,
+ const char *interface)
+{
+ /* Private connections can't use dbus_g_proxy_new_for_name() or
+ * dbus_g_proxy_new_for_name_owner() because peer-to-peer connections don't
+ * have either a bus daemon or name owners, both of which those functions
+ * require.
+ */
+ if (_nm_dbus_is_connection_private (connection))
+ return dbus_g_proxy_new_for_peer (connection, path, interface);
+
+ return dbus_g_proxy_new_for_name (connection, NM_DBUS_SERVICE, path, interface);
+}
diff --git a/libnm/nm-device-adsl.c b/libnm/nm-device-adsl.c
new file mode 100644
index 0000000000..eb54957413
--- /dev/null
+++ b/libnm/nm-device-adsl.c
@@ -0,0 +1,242 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * author: Pantelis Koukousoulas <pktoss@gmail.com>
+ * Copyright 2009 - 2011 Red Hat, Inc.
+ */
+
+#include "nm-device-adsl.h"
+#include "nm-device-private.h"
+#include "nm-object-private.h"
+
+#include "nm-setting-adsl.h"
+
+#include <string.h>
+
+G_DEFINE_TYPE (NMDeviceAdsl, nm_device_adsl, NM_TYPE_DEVICE)
+
+#define NM_DEVICE_ADSL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_ADSL, NMDeviceAdslPrivate))
+
+typedef struct {
+ DBusGProxy *proxy;
+
+ gboolean carrier;
+
+ gboolean disposed;
+} NMDeviceAdslPrivate;
+
+enum {
+ PROP_0,
+ PROP_CARRIER,
+ LAST_PROP
+};
+
+/**
+ * nm_device_adsl_error_quark:
+ *
+ * Registers an error quark for #NMDeviceAdsl if necessary.
+ *
+ * Returns: the error quark used for #NMDeviceAdsl errors.
+ **/
+GQuark
+nm_device_adsl_error_quark (void)
+{
+ static GQuark quark = 0;
+
+ if (G_UNLIKELY (quark == 0))
+ quark = g_quark_from_static_string ("nm-device-adsl-error-quark");
+ return quark;
+}
+
+/**
+ * nm_device_adsl_new:
+ * @connection: the #DBusGConnection
+ * @path: the DBus object path of the device
+ *
+ * Creates a new #NMDeviceAdsl.
+ *
+ * Returns: (transfer full): a new device
+ **/
+GObject *
+nm_device_adsl_new (DBusGConnection *connection, const char *path)
+{
+ GObject *device;
+
+ g_return_val_if_fail (connection != NULL, NULL);
+ g_return_val_if_fail (path != NULL, NULL);
+
+ device = g_object_new (NM_TYPE_DEVICE_ADSL,
+ NM_OBJECT_DBUS_CONNECTION, connection,
+ NM_OBJECT_DBUS_PATH, path,
+ NULL);
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return device;
+}
+
+/**
+ * nm_device_adsl_get_carrier:
+ * @device: a #NMDeviceAdsl
+ *
+ * Whether the device has carrier.
+ *
+ * Returns: %TRUE if the device has carrier
+ **/
+gboolean
+nm_device_adsl_get_carrier (NMDeviceAdsl *device)
+{
+ g_return_val_if_fail (NM_IS_DEVICE_ADSL (device), FALSE);
+
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return NM_DEVICE_ADSL_GET_PRIVATE (device)->carrier;
+}
+
+static gboolean
+connection_compatible (NMDevice *device, NMConnection *connection, GError **error)
+{
+ NMSettingConnection *s_con;
+ NMSettingAdsl *s_adsl;
+ const char *ctype;
+
+ s_con = nm_connection_get_setting_connection (connection);
+ g_assert (s_con);
+
+ ctype = nm_setting_connection_get_connection_type (s_con);
+ if (strcmp (ctype, NM_SETTING_ADSL_SETTING_NAME) != 0) {
+ g_set_error (error, NM_DEVICE_ADSL_ERROR, NM_DEVICE_ADSL_ERROR_NOT_ADSL_CONNECTION,
+ "The connection was not an ADSL connection.");
+ return FALSE;
+ }
+
+ s_adsl = nm_connection_get_setting_adsl (connection);
+ if (!s_adsl) {
+ g_set_error (error, NM_DEVICE_ADSL_ERROR, NM_DEVICE_ADSL_ERROR_INVALID_ADSL_CONNECTION,
+ "The connection was not a valid ADSL connection.");
+ return FALSE;
+ }
+
+ return NM_DEVICE_CLASS (nm_device_adsl_parent_class)->connection_compatible (device, connection, error);
+}
+
+static GType
+get_setting_type (NMDevice *device)
+{
+ return NM_TYPE_SETTING_ADSL;
+}
+
+/******************************************************************/
+
+static void
+nm_device_adsl_init (NMDeviceAdsl *device)
+{
+ _nm_device_set_device_type (NM_DEVICE (device), NM_DEVICE_TYPE_ADSL);
+}
+
+static void
+register_properties (NMDeviceAdsl *device)
+{
+ NMDeviceAdslPrivate *priv = NM_DEVICE_ADSL_GET_PRIVATE (device);
+ const NMPropertiesInfo property_info[] = {
+ { NM_DEVICE_ADSL_CARRIER, &priv->carrier },
+ { NULL },
+ };
+
+ _nm_object_register_properties (NM_OBJECT (device),
+ priv->proxy,
+ property_info);
+}
+
+static void
+constructed (GObject *object)
+{
+ NMDeviceAdslPrivate *priv = NM_DEVICE_ADSL_GET_PRIVATE (object);
+
+ G_OBJECT_CLASS (nm_device_adsl_parent_class)->constructed (object);
+
+ priv->proxy = _nm_object_new_proxy (NM_OBJECT (object), NULL, NM_DBUS_INTERFACE_DEVICE_ADSL);
+ register_properties (NM_DEVICE_ADSL (object));
+}
+
+static void
+dispose (GObject *object)
+{
+ NMDeviceAdslPrivate *priv = NM_DEVICE_ADSL_GET_PRIVATE (object);
+
+ if (priv->disposed) {
+ G_OBJECT_CLASS (nm_device_adsl_parent_class)->dispose (object);
+ return;
+ }
+
+ priv->disposed = TRUE;
+
+ g_object_unref (priv->proxy);
+
+ G_OBJECT_CLASS (nm_device_adsl_parent_class)->dispose (object);
+}
+
+static void
+finalize (GObject *object)
+{
+ G_OBJECT_CLASS (nm_device_adsl_parent_class)->finalize (object);
+}
+
+static void
+get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NMDeviceAdsl *device = NM_DEVICE_ADSL (object);
+
+ switch (prop_id) {
+ case PROP_CARRIER:
+ g_value_set_boolean (value, nm_device_adsl_get_carrier (device));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nm_device_adsl_class_init (NMDeviceAdslClass *adsl_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (adsl_class);
+ NMDeviceClass *device_class = NM_DEVICE_CLASS (adsl_class);
+
+ g_type_class_add_private (object_class, sizeof (NMDeviceAdslPrivate));
+
+ /* virtual methods */
+ object_class->constructed = constructed;
+ object_class->dispose = dispose;
+ object_class->finalize = finalize;
+ object_class->get_property = get_property;
+ device_class->connection_compatible = connection_compatible;
+ device_class->get_setting_type = get_setting_type;
+
+ /* properties */
+ /**
+ * NMDeviceAdsl:carrier:
+ *
+ * Whether the device has carrier.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_CARRIER,
+ g_param_spec_boolean (NM_DEVICE_ADSL_CARRIER, "", "",
+ FALSE,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/libnm/nm-device-adsl.h b/libnm/nm-device-adsl.h
new file mode 100644
index 0000000000..35d2731a2c
--- /dev/null
+++ b/libnm/nm-device-adsl.h
@@ -0,0 +1,75 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2011 Pantelis Koukousoulas <pktoss@gmail.com>
+ */
+
+#ifndef NM_DEVICE_ADSL_H
+#define NM_DEVICE_ADSL_H
+
+#include "nm-device.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_DEVICE_ADSL (nm_device_adsl_get_type ())
+#define NM_DEVICE_ADSL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_ADSL, NMDeviceAdsl))
+#define NM_DEVICE_ADSL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_ADSL, NMDeviceAdslClass))
+#define NM_IS_DEVICE_ADSL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_ADSL))
+#define NM_IS_DEVICE_ADSL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_ADSL))
+#define NM_DEVICE_ADSL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_ADSL, NMDeviceAdslClass))
+
+/**
+ * NMDeviceAdslError:
+ * @NM_DEVICE_ADSL_ERROR_UNKNOWN: unknown or unclassified error
+ * @NM_DEVICE_ADSL_ERROR_NOT_ADSL_CONNECTION: the connection was not of ADSL type
+ * @NM_DEVICE_ADSL_ERROR_INVALID_ADSL_CONNECTION: the ADSL connection was invalid
+ */
+typedef enum {
+ NM_DEVICE_ADSL_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/
+ NM_DEVICE_ADSL_ERROR_NOT_ADSL_CONNECTION, /*< nick=NotAdslConnection >*/
+ NM_DEVICE_ADSL_ERROR_INVALID_ADSL_CONNECTION, /*< nick=InvalidAdslConnection >*/
+} NMDeviceAdslError;
+
+#define NM_DEVICE_ADSL_ERROR nm_device_adsl_error_quark ()
+GQuark nm_device_adsl_error_quark (void);
+
+#define NM_DEVICE_ADSL_CARRIER "carrier"
+
+typedef struct {
+ NMDevice parent;
+} NMDeviceAdsl;
+
+typedef struct {
+ NMDeviceClass parent;
+
+ /* Padding for future expansion */
+ void (*_reserved1) (void);
+ void (*_reserved2) (void);
+ void (*_reserved3) (void);
+ void (*_reserved4) (void);
+ void (*_reserved5) (void);
+ void (*_reserved6) (void);
+} NMDeviceAdslClass;
+
+GType nm_device_adsl_get_type (void);
+
+GObject *nm_device_adsl_new (DBusGConnection *connection, const char *path);
+gboolean nm_device_adsl_get_carrier (NMDeviceAdsl *device);
+
+G_END_DECLS
+
+#endif /* NM_DEVICE_ADSL_H */
diff --git a/libnm/nm-device-bond.c b/libnm/nm-device-bond.c
new file mode 100644
index 0000000000..662b2f4a38
--- /dev/null
+++ b/libnm/nm-device-bond.c
@@ -0,0 +1,347 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2012 Red Hat, Inc.
+ */
+
+#include <config.h>
+#include <string.h>
+#include <netinet/ether.h>
+
+#include "nm-glib-compat.h"
+
+#include <nm-setting-connection.h>
+#include <nm-setting-bond.h>
+#include <nm-utils.h>
+
+#include "nm-device-bond.h"
+#include "nm-device-private.h"
+#include "nm-object-private.h"
+#include "nm-types.h"
+
+G_DEFINE_TYPE (NMDeviceBond, nm_device_bond, NM_TYPE_DEVICE)
+
+#define NM_DEVICE_BOND_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_BOND, NMDeviceBondPrivate))
+
+typedef struct {
+ DBusGProxy *proxy;
+
+ char *hw_address;
+ gboolean carrier;
+ GPtrArray *slaves;
+} NMDeviceBondPrivate;
+
+enum {
+ PROP_0,
+ PROP_HW_ADDRESS,
+ PROP_CARRIER,
+ PROP_SLAVES,
+
+ LAST_PROP
+};
+
+/**
+ * nm_device_bond_error_quark:
+ *
+ * Registers an error quark for #NMDeviceBond if necessary.
+ *
+ * Returns: the error quark used for #NMDeviceBond errors.
+ **/
+GQuark
+nm_device_bond_error_quark (void)
+{
+ static GQuark quark = 0;
+
+ if (G_UNLIKELY (quark == 0))
+ quark = g_quark_from_static_string ("nm-device-bond-error-quark");
+ return quark;
+}
+
+/**
+ * nm_device_bond_new:
+ * @connection: the #DBusGConnection
+ * @path: the DBus object path of the device
+ *
+ * Creates a new #NMDeviceBond.
+ *
+ * Returns: (transfer full): a new device
+ **/
+GObject *
+nm_device_bond_new (DBusGConnection *connection, const char *path)
+{
+ GObject *device;
+
+ g_return_val_if_fail (connection != NULL, NULL);
+ g_return_val_if_fail (path != NULL, NULL);
+
+ device = g_object_new (NM_TYPE_DEVICE_BOND,
+ NM_OBJECT_DBUS_CONNECTION, connection,
+ NM_OBJECT_DBUS_PATH, path,
+ NULL);
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return device;
+}
+
+/**
+ * nm_device_bond_get_hw_address:
+ * @device: a #NMDeviceBond
+ *
+ * Gets the hardware (MAC) address of the #NMDeviceBond
+ *
+ * Returns: the hardware address. This is the internal string used by the
+ * device, and must not be modified.
+ **/
+const char *
+nm_device_bond_get_hw_address (NMDeviceBond *device)
+{
+ g_return_val_if_fail (NM_IS_DEVICE_BOND (device), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return NM_DEVICE_BOND_GET_PRIVATE (device)->hw_address;
+}
+
+/**
+ * nm_device_bond_get_carrier:
+ * @device: a #NMDeviceBond
+ *
+ * Whether the device has carrier.
+ *
+ * Returns: %TRUE if the device has carrier
+ **/
+gboolean
+nm_device_bond_get_carrier (NMDeviceBond *device)
+{
+ g_return_val_if_fail (NM_IS_DEVICE_BOND (device), FALSE);
+
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return NM_DEVICE_BOND_GET_PRIVATE (device)->carrier;
+}
+
+/**
+ * nm_device_bond_get_slaves:
+ * @device: a #NMDeviceBond
+ *
+ * Gets the devices currently slaved to @device.
+ *
+ * Returns: (element-type NMDevice): the #GPtrArray containing
+ * #NMDevices that are slaves of @device. This is the internal
+ * copy used by the device, and must not be modified.
+ *
+ * Since: 0.9.6.4
+ **/
+const GPtrArray *
+nm_device_bond_get_slaves (NMDeviceBond *device)
+{
+ g_return_val_if_fail (NM_IS_DEVICE_BOND (device), FALSE);
+
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return handle_ptr_array_return (NM_DEVICE_BOND_GET_PRIVATE (device)->slaves);
+}
+
+static gboolean
+connection_compatible (NMDevice *device, NMConnection *connection, GError **error)
+{
+ NMSettingConnection *s_con;
+ NMSettingBond *s_bond;
+ const char *ctype, *dev_iface_name, *bond_iface_name;
+
+ s_con = nm_connection_get_setting_connection (connection);
+ g_assert (s_con);
+
+ ctype = nm_setting_connection_get_connection_type (s_con);
+ if (strcmp (ctype, NM_SETTING_BOND_SETTING_NAME) != 0) {
+ g_set_error (error, NM_DEVICE_BOND_ERROR, NM_DEVICE_BOND_ERROR_NOT_BOND_CONNECTION,
+ "The connection was not a bond connection.");
+ return FALSE;
+ }
+
+ s_bond = nm_connection_get_setting_bond (connection);
+ if (!s_bond) {
+ g_set_error (error, NM_DEVICE_BOND_ERROR, NM_DEVICE_BOND_ERROR_INVALID_BOND_CONNECTION,
+ "The connection was not a valid bond connection.");
+ return FALSE;
+ }
+
+ dev_iface_name = nm_device_get_iface (device);
+ bond_iface_name = nm_setting_bond_get_interface_name (s_bond);
+ if (g_strcmp0 (dev_iface_name, bond_iface_name) != 0) {
+ g_set_error (error, NM_DEVICE_BOND_ERROR, NM_DEVICE_BOND_ERROR_INTERFACE_MISMATCH,
+ "The interfaces of the device and the connection didn't match.");
+ return FALSE;
+ }
+
+ /* FIXME: check slaves? */
+
+ return NM_DEVICE_CLASS (nm_device_bond_parent_class)->connection_compatible (device, connection, error);
+}
+
+static GType
+get_setting_type (NMDevice *device)
+{
+ return NM_TYPE_SETTING_BOND;
+}
+
+static const char *
+get_hw_address (NMDevice *device)
+{
+ return nm_device_bond_get_hw_address (NM_DEVICE_BOND (device));
+}
+
+/***********************************************************/
+
+static void
+nm_device_bond_init (NMDeviceBond *device)
+{
+ _nm_device_set_device_type (NM_DEVICE (device), NM_DEVICE_TYPE_BOND);
+}
+
+static void
+register_properties (NMDeviceBond *device)
+{
+ NMDeviceBondPrivate *priv = NM_DEVICE_BOND_GET_PRIVATE (device);
+ const NMPropertiesInfo property_info[] = {
+ { NM_DEVICE_BOND_HW_ADDRESS, &priv->hw_address },
+ { NM_DEVICE_BOND_CARRIER, &priv->carrier },
+ { NM_DEVICE_BOND_SLAVES, &priv->slaves, NULL, NM_TYPE_DEVICE },
+ { NULL },
+ };
+
+ _nm_object_register_properties (NM_OBJECT (device),
+ priv->proxy,
+ property_info);
+}
+
+static void
+constructed (GObject *object)
+{
+ NMDeviceBondPrivate *priv = NM_DEVICE_BOND_GET_PRIVATE (object);
+
+ G_OBJECT_CLASS (nm_device_bond_parent_class)->constructed (object);
+
+ priv->proxy = _nm_object_new_proxy (NM_OBJECT (object), NULL, NM_DBUS_INTERFACE_DEVICE_BOND);
+ register_properties (NM_DEVICE_BOND (object));
+}
+
+static void
+dispose (GObject *object)
+{
+ NMDeviceBondPrivate *priv = NM_DEVICE_BOND_GET_PRIVATE (object);
+
+ g_clear_object (&priv->proxy);
+
+ if (priv->slaves) {
+ g_ptr_array_set_free_func (priv->slaves, g_object_unref);
+ g_ptr_array_free (priv->slaves, TRUE);
+ priv->slaves = NULL;
+ }
+
+ G_OBJECT_CLASS (nm_device_bond_parent_class)->dispose (object);
+}
+
+static void
+finalize (GObject *object)
+{
+ NMDeviceBondPrivate *priv = NM_DEVICE_BOND_GET_PRIVATE (object);
+
+ g_free (priv->hw_address);
+
+ G_OBJECT_CLASS (nm_device_bond_parent_class)->finalize (object);
+}
+
+static void
+get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NMDeviceBond *device = NM_DEVICE_BOND (object);
+
+ _nm_object_ensure_inited (NM_OBJECT (object));
+
+ switch (prop_id) {
+ case PROP_HW_ADDRESS:
+ g_value_set_string (value, nm_device_bond_get_hw_address (device));
+ break;
+ case PROP_CARRIER:
+ g_value_set_boolean (value, nm_device_bond_get_carrier (device));
+ break;
+ case PROP_SLAVES:
+ g_value_set_boxed (value, nm_device_bond_get_slaves (device));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nm_device_bond_class_init (NMDeviceBondClass *eth_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (eth_class);
+ NMDeviceClass *device_class = NM_DEVICE_CLASS (eth_class);
+
+ g_type_class_add_private (eth_class, sizeof (NMDeviceBondPrivate));
+
+ /* virtual methods */
+ object_class->constructed = constructed;
+ object_class->dispose = dispose;
+ object_class->finalize = finalize;
+ object_class->get_property = get_property;
+ device_class->connection_compatible = connection_compatible;
+ device_class->get_setting_type = get_setting_type;
+ device_class->get_hw_address = get_hw_address;
+
+ /* properties */
+
+ /**
+ * NMDeviceBond:hw-address:
+ *
+ * The hardware (MAC) address of the device.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_HW_ADDRESS,
+ g_param_spec_string (NM_DEVICE_BOND_HW_ADDRESS, "", "",
+ NULL,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMDeviceBond:carrier:
+ *
+ * Whether the device has carrier.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_CARRIER,
+ g_param_spec_boolean (NM_DEVICE_BOND_CARRIER, "", "",
+ FALSE,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMDeviceBond:slaves:
+ *
+ * The devices (#NMDevice) slaved to the bond device.
+ *
+ * Since: 0.9.8
+ **/
+ g_object_class_install_property
+ (object_class, PROP_SLAVES,
+ g_param_spec_boxed (NM_DEVICE_BOND_SLAVES, "", "",
+ NM_TYPE_OBJECT_ARRAY,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/libnm/nm-device-bond.h b/libnm/nm-device-bond.h
new file mode 100644
index 0000000000..cd1a60271f
--- /dev/null
+++ b/libnm/nm-device-bond.h
@@ -0,0 +1,82 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2012 Red Hat, Inc.
+ */
+
+#ifndef NM_DEVICE_BOND_H
+#define NM_DEVICE_BOND_H
+
+#include "nm-device.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_DEVICE_BOND (nm_device_bond_get_type ())
+#define NM_DEVICE_BOND(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_BOND, NMDeviceBond))
+#define NM_DEVICE_BOND_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_BOND, NMDeviceBondClass))
+#define NM_IS_DEVICE_BOND(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_BOND))
+#define NM_IS_DEVICE_BOND_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_BOND))
+#define NM_DEVICE_BOND_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_BOND, NMDeviceBondClass))
+
+/**
+ * NMDeviceBondError:
+ * @NM_DEVICE_BOND_ERROR_UNKNOWN: unknown or unclassified error
+ * @NM_DEVICE_BOND_ERROR_NOT_BOND_CONNECTION: the connection was not of bond type
+ * @NM_DEVICE_BOND_ERROR_INVALID_BOND_CONNECTION: the bond connection was invalid
+ * @NM_DEVICE_BOND_ERROR_INTERFACE_MISMATCH: the interfaces of the connection and the device mismatched
+ */
+typedef enum {
+ NM_DEVICE_BOND_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/
+ NM_DEVICE_BOND_ERROR_NOT_BOND_CONNECTION, /*< nick=NotBondConnection >*/
+ NM_DEVICE_BOND_ERROR_INVALID_BOND_CONNECTION, /*< nick=InvalidBondConnection >*/
+ NM_DEVICE_BOND_ERROR_INTERFACE_MISMATCH, /*< nick=InterfaceMismatch >*/
+} NMDeviceBondError;
+
+#define NM_DEVICE_BOND_ERROR nm_device_bond_error_quark ()
+GQuark nm_device_bond_error_quark (void);
+
+#define NM_DEVICE_BOND_HW_ADDRESS "hw-address"
+#define NM_DEVICE_BOND_CARRIER "carrier"
+#define NM_DEVICE_BOND_SLAVES "slaves"
+
+typedef struct {
+ NMDevice parent;
+} NMDeviceBond;
+
+typedef struct {
+ NMDeviceClass parent;
+
+ /* Padding for future expansion */
+ void (*_reserved1) (void);
+ void (*_reserved2) (void);
+ void (*_reserved3) (void);
+ void (*_reserved4) (void);
+ void (*_reserved5) (void);
+ void (*_reserved6) (void);
+} NMDeviceBondClass;
+
+GType nm_device_bond_get_type (void);
+
+GObject *nm_device_bond_new (DBusGConnection *connection, const char *path);
+
+const char *nm_device_bond_get_hw_address (NMDeviceBond *device);
+gboolean nm_device_bond_get_carrier (NMDeviceBond *device);
+const GPtrArray *nm_device_bond_get_slaves (NMDeviceBond *device);
+
+G_END_DECLS
+
+#endif /* NM_DEVICE_BOND_H */
diff --git a/libnm/nm-device-bridge.c b/libnm/nm-device-bridge.c
new file mode 100644
index 0000000000..bf19737555
--- /dev/null
+++ b/libnm/nm-device-bridge.c
@@ -0,0 +1,359 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2012 Red Hat, Inc.
+ */
+
+#include <config.h>
+#include <string.h>
+#include <netinet/ether.h>
+
+#include "nm-glib-compat.h"
+
+#include <nm-setting-connection.h>
+#include <nm-setting-bridge.h>
+#include <nm-utils.h>
+
+#include "nm-device-bridge.h"
+#include "nm-device-private.h"
+#include "nm-object-private.h"
+#include "nm-types.h"
+
+G_DEFINE_TYPE (NMDeviceBridge, nm_device_bridge, NM_TYPE_DEVICE)
+
+#define NM_DEVICE_BRIDGE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_BRIDGE, NMDeviceBridgePrivate))
+
+typedef struct {
+ DBusGProxy *proxy;
+
+ char *hw_address;
+ gboolean carrier;
+ GPtrArray *slaves;
+} NMDeviceBridgePrivate;
+
+enum {
+ PROP_0,
+ PROP_HW_ADDRESS,
+ PROP_CARRIER,
+ PROP_SLAVES,
+
+ LAST_PROP
+};
+
+/**
+ * nm_device_bridge_error_quark:
+ *
+ * Registers an error quark for #NMDeviceBridge if necessary.
+ *
+ * Returns: the error quark used for #NMDeviceBridge errors.
+ *
+ * Since: 0.9.8
+ **/
+GQuark
+nm_device_bridge_error_quark (void)
+{
+ static GQuark quark = 0;
+
+ if (G_UNLIKELY (quark == 0))
+ quark = g_quark_from_static_string ("nm-device-bridge-error-quark");
+ return quark;
+}
+
+/**
+ * nm_device_bridge_new:
+ * @connection: the #DBusGConnection
+ * @path: the DBus object path of the device
+ *
+ * Creates a new #NMDeviceBridge.
+ *
+ * Returns: (transfer full): a new device
+ *
+ * Since: 0.9.8
+ **/
+GObject *
+nm_device_bridge_new (DBusGConnection *connection, const char *path)
+{
+ GObject *device;
+
+ g_return_val_if_fail (connection != NULL, NULL);
+ g_return_val_if_fail (path != NULL, NULL);
+
+ device = g_object_new (NM_TYPE_DEVICE_BRIDGE,
+ NM_OBJECT_DBUS_CONNECTION, connection,
+ NM_OBJECT_DBUS_PATH, path,
+ NULL);
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return device;
+}
+
+/**
+ * nm_device_bridge_get_hw_address:
+ * @device: a #NMDeviceBridge
+ *
+ * Gets the hardware (MAC) address of the #NMDeviceBridge
+ *
+ * Returns: the hardware address. This is the internal string used by the
+ * device, and must not be modified.
+ *
+ * Since: 0.9.8
+ **/
+const char *
+nm_device_bridge_get_hw_address (NMDeviceBridge *device)
+{
+ g_return_val_if_fail (NM_IS_DEVICE_BRIDGE (device), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return NM_DEVICE_BRIDGE_GET_PRIVATE (device)->hw_address;
+}
+
+/**
+ * nm_device_bridge_get_carrier:
+ * @device: a #NMDeviceBridge
+ *
+ * Whether the device has carrier.
+ *
+ * Returns: %TRUE if the device has carrier
+ *
+ * Since: 0.9.8
+ **/
+gboolean
+nm_device_bridge_get_carrier (NMDeviceBridge *device)
+{
+ g_return_val_if_fail (NM_IS_DEVICE_BRIDGE (device), FALSE);
+
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return NM_DEVICE_BRIDGE_GET_PRIVATE (device)->carrier;
+}
+
+/**
+ * nm_device_bridge_get_slaves:
+ * @device: a #NMDeviceBridge
+ *
+ * Gets the devices currently slaved to @device.
+ *
+ * Returns: (element-type NMDevice): the #GPtrArray containing
+ * #NMDevices that are slaves of @device. This is the internal
+ * copy used by the device, and must not be modified.
+ *
+ * Since: 0.9.8
+ **/
+const GPtrArray *
+nm_device_bridge_get_slaves (NMDeviceBridge *device)
+{
+ g_return_val_if_fail (NM_IS_DEVICE_BRIDGE (device), FALSE);
+
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return handle_ptr_array_return (NM_DEVICE_BRIDGE_GET_PRIVATE (device)->slaves);
+}
+
+static gboolean
+connection_compatible (NMDevice *device, NMConnection *connection, GError **error)
+{
+ NMSettingConnection *s_con;
+ NMSettingBridge *s_bridge;
+ const char *ctype, *dev_iface_name, *bridge_iface_name;
+
+ s_con = nm_connection_get_setting_connection (connection);
+ g_assert (s_con);
+
+ ctype = nm_setting_connection_get_connection_type (s_con);
+ if (strcmp (ctype, NM_SETTING_BRIDGE_SETTING_NAME) != 0) {
+ g_set_error (error, NM_DEVICE_BRIDGE_ERROR, NM_DEVICE_BRIDGE_ERROR_NOT_BRIDGE_CONNECTION,
+ "The connection was not a bridge connection.");
+ return FALSE;
+ }
+
+ s_bridge = nm_connection_get_setting_bridge (connection);
+ if (!s_bridge) {
+ g_set_error (error, NM_DEVICE_BRIDGE_ERROR, NM_DEVICE_BRIDGE_ERROR_INVALID_BRIDGE_CONNECTION,
+ "The connection was not a valid bridge connection.");
+ return FALSE;
+ }
+
+ dev_iface_name = nm_device_get_iface (device);
+ bridge_iface_name = nm_setting_bridge_get_interface_name (s_bridge);
+ if (g_strcmp0 (dev_iface_name, bridge_iface_name) != 0) {
+ g_set_error (error, NM_DEVICE_BRIDGE_ERROR, NM_DEVICE_BRIDGE_ERROR_INTERFACE_MISMATCH,
+ "The interfaces of the device and the connection didn't match.");
+ return FALSE;
+ }
+
+ /* FIXME: check ports? */
+
+ return NM_DEVICE_CLASS (nm_device_bridge_parent_class)->connection_compatible (device, connection, error);
+}
+
+static GType
+get_setting_type (NMDevice *device)
+{
+ return NM_TYPE_SETTING_BRIDGE;
+}
+
+static const char *
+get_hw_address (NMDevice *device)
+{
+ return nm_device_bridge_get_hw_address (NM_DEVICE_BRIDGE (device));
+}
+
+/***********************************************************/
+
+static void
+nm_device_bridge_init (NMDeviceBridge *device)
+{
+ _nm_device_set_device_type (NM_DEVICE (device), NM_DEVICE_TYPE_BRIDGE);
+}
+
+static void
+register_properties (NMDeviceBridge *device)
+{
+ NMDeviceBridgePrivate *priv = NM_DEVICE_BRIDGE_GET_PRIVATE (device);
+ const NMPropertiesInfo property_info[] = {
+ { NM_DEVICE_BRIDGE_HW_ADDRESS, &priv->hw_address },
+ { NM_DEVICE_BRIDGE_CARRIER, &priv->carrier },
+ { NM_DEVICE_BRIDGE_SLAVES, &priv->slaves, NULL, NM_TYPE_DEVICE },
+ { NULL },
+ };
+
+ _nm_object_register_properties (NM_OBJECT (device),
+ priv->proxy,
+ property_info);
+}
+
+static void
+constructed (GObject *object)
+{
+ NMDeviceBridgePrivate *priv = NM_DEVICE_BRIDGE_GET_PRIVATE (object);
+
+ G_OBJECT_CLASS (nm_device_bridge_parent_class)->constructed (object);
+
+ priv->proxy = _nm_object_new_proxy (NM_OBJECT (object), NULL, NM_DBUS_INTERFACE_DEVICE_BRIDGE);
+ register_properties (NM_DEVICE_BRIDGE (object));
+}
+
+static void
+dispose (GObject *object)
+{
+ NMDeviceBridgePrivate *priv = NM_DEVICE_BRIDGE_GET_PRIVATE (object);
+
+ g_clear_object (&priv->proxy);
+
+ if (priv->slaves) {
+ g_ptr_array_set_free_func (priv->slaves, g_object_unref);
+ g_ptr_array_free (priv->slaves, TRUE);
+ priv->slaves = NULL;
+ }
+
+ G_OBJECT_CLASS (nm_device_bridge_parent_class)->dispose (object);
+}
+
+static void
+finalize (GObject *object)
+{
+ NMDeviceBridgePrivate *priv = NM_DEVICE_BRIDGE_GET_PRIVATE (object);
+
+ g_free (priv->hw_address);
+
+ G_OBJECT_CLASS (nm_device_bridge_parent_class)->finalize (object);
+}
+
+static void
+get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NMDeviceBridge *device = NM_DEVICE_BRIDGE (object);
+
+ _nm_object_ensure_inited (NM_OBJECT (object));
+
+ switch (prop_id) {
+ case PROP_HW_ADDRESS:
+ g_value_set_string (value, nm_device_bridge_get_hw_address (device));
+ break;
+ case PROP_CARRIER:
+ g_value_set_boolean (value, nm_device_bridge_get_carrier (device));
+ break;
+ case PROP_SLAVES:
+ g_value_set_boxed (value, nm_device_bridge_get_slaves (device));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nm_device_bridge_class_init (NMDeviceBridgeClass *bridge_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (bridge_class);
+ NMDeviceClass *device_class = NM_DEVICE_CLASS (bridge_class);
+
+ g_type_class_add_private (bridge_class, sizeof (NMDeviceBridgePrivate));
+
+ /* virtual methods */
+ object_class->constructed = constructed;
+ object_class->dispose = dispose;
+ object_class->finalize = finalize;
+ object_class->get_property = get_property;
+ device_class->connection_compatible = connection_compatible;
+ device_class->get_setting_type = get_setting_type;
+ device_class->get_hw_address = get_hw_address;
+
+ /* properties */
+
+ /**
+ * NMDeviceBridge:hw-address:
+ *
+ * The hardware (MAC) address of the device.
+ *
+ * Since: 0.9.8
+ **/
+ g_object_class_install_property
+ (object_class, PROP_HW_ADDRESS,
+ g_param_spec_string (NM_DEVICE_BRIDGE_HW_ADDRESS, "", "",
+ NULL,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMDeviceBridge:carrier:
+ *
+ * Whether the device has carrier.
+ *
+ * Since: 0.9.8
+ **/
+ g_object_class_install_property
+ (object_class, PROP_CARRIER,
+ g_param_spec_boolean (NM_DEVICE_BRIDGE_CARRIER, "", "",
+ FALSE,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMDeviceBridge:slaves:
+ *
+ * The devices (#NMDevice) slaved to the bridge device.
+ *
+ * Since: 0.9.8
+ **/
+ g_object_class_install_property
+ (object_class, PROP_SLAVES,
+ g_param_spec_boxed (NM_DEVICE_BRIDGE_SLAVES, "", "",
+ NM_TYPE_OBJECT_ARRAY,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/libnm/nm-device-bridge.h b/libnm/nm-device-bridge.h
new file mode 100644
index 0000000000..2eaf475d8c
--- /dev/null
+++ b/libnm/nm-device-bridge.h
@@ -0,0 +1,84 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2012 Red Hat, Inc.
+ */
+
+#ifndef NM_DEVICE_BRIDGE_H
+#define NM_DEVICE_BRIDGE_H
+
+#include "nm-device.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_DEVICE_BRIDGE (nm_device_bridge_get_type ())
+#define NM_DEVICE_BRIDGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_BRIDGE, NMDeviceBridge))
+#define NM_DEVICE_BRIDGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_BRIDGE, NMDeviceBridgeClass))
+#define NM_IS_DEVICE_BRIDGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_BRIDGE))
+#define NM_IS_DEVICE_BRIDGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_BRIDGE))
+#define NM_DEVICE_BRIDGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_BRIDGE, NMDeviceBridgeClass))
+
+/**
+ * NMDeviceBridgeError:
+ * @NM_DEVICE_BRIDGE_ERROR_UNKNOWN: unknown or unclassified error
+ * @NM_DEVICE_BRIDGE_ERROR_NOT_BRIDGE_CONNECTION: the connection was not of bridge type
+ * @NM_DEVICE_BRIDGE_ERROR_INVALID_BRIDGE_CONNECTION: the bridge connection was invalid
+ * @NM_DEVICE_BRIDGE_ERROR_INTERFACE_MISMATCH: the interfaces of the connection and the device mismatched
+ *
+ * Since: 0.9.8
+ */
+typedef enum {
+ NM_DEVICE_BRIDGE_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/
+ NM_DEVICE_BRIDGE_ERROR_NOT_BRIDGE_CONNECTION, /*< nick=NotBridgeConnection >*/
+ NM_DEVICE_BRIDGE_ERROR_INVALID_BRIDGE_CONNECTION, /*< nick=InvalidBridgeConnection >*/
+ NM_DEVICE_BRIDGE_ERROR_INTERFACE_MISMATCH, /*< nick=InterfaceMismatch >*/
+} NMDeviceBridgeError;
+
+#define NM_DEVICE_BRIDGE_ERROR nm_device_bridge_error_quark ()
+GQuark nm_device_bridge_error_quark (void);
+
+#define NM_DEVICE_BRIDGE_HW_ADDRESS "hw-address"
+#define NM_DEVICE_BRIDGE_CARRIER "carrier"
+#define NM_DEVICE_BRIDGE_SLAVES "slaves"
+
+typedef struct {
+ NMDevice parent;
+} NMDeviceBridge;
+
+typedef struct {
+ NMDeviceClass parent;
+
+ /* Padding for future expansion */
+ void (*_reserved1) (void);
+ void (*_reserved2) (void);
+ void (*_reserved3) (void);
+ void (*_reserved4) (void);
+ void (*_reserved5) (void);
+ void (*_reserved6) (void);
+} NMDeviceBridgeClass;
+
+GType nm_device_bridge_get_type (void);
+
+GObject * nm_device_bridge_new (DBusGConnection *connection, const char *path);
+
+const char *nm_device_bridge_get_hw_address (NMDeviceBridge *device);
+gboolean nm_device_bridge_get_carrier (NMDeviceBridge *device);
+const GPtrArray *nm_device_bridge_get_slaves (NMDeviceBridge *device);
+
+G_END_DECLS
+
+#endif /* NM_DEVICE_BRIDGE_H */
diff --git a/libnm/nm-device-bt.c b/libnm/nm-device-bt.c
new file mode 100644
index 0000000000..00616a95a8
--- /dev/null
+++ b/libnm/nm-device-bt.c
@@ -0,0 +1,373 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2008 Novell, Inc.
+ * Copyright 2007 - 2012 Red Hat, Inc.
+ */
+
+#include <config.h>
+#include <string.h>
+#include <netinet/ether.h>
+
+#include "nm-glib-compat.h"
+
+#include <nm-setting-connection.h>
+#include <nm-setting-bluetooth.h>
+
+#include "nm-device-bt.h"
+#include "nm-device-private.h"
+#include "nm-object-private.h"
+
+G_DEFINE_TYPE (NMDeviceBt, nm_device_bt, NM_TYPE_DEVICE)
+
+#define NM_DEVICE_BT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_BT, NMDeviceBtPrivate))
+
+typedef struct {
+ DBusGProxy *proxy;
+
+ char *hw_address;
+ char *name;
+ guint32 bt_capabilities;
+} NMDeviceBtPrivate;
+
+enum {
+ PROP_0,
+ PROP_HW_ADDRESS,
+ PROP_NAME,
+ PROP_BT_CAPABILITIES,
+
+ LAST_PROP
+};
+
+/**
+ * nm_device_bt_error_quark:
+ *
+ * Registers an error quark for #NMDeviceBt if necessary.
+ *
+ * Returns: the error quark used for #NMDeviceBt errors.
+ **/
+GQuark
+nm_device_bt_error_quark (void)
+{
+ static GQuark quark = 0;
+
+ if (G_UNLIKELY (quark == 0))
+ quark = g_quark_from_static_string ("nm-device-bt-error-quark");
+ return quark;
+}
+
+/**
+ * nm_device_bt_new:
+ * @connection: the #DBusGConnection
+ * @path: the DBus object path of the device
+ *
+ * Creates a new #NMDeviceBt.
+ *
+ * Returns: (transfer full): a new device
+ **/
+GObject *
+nm_device_bt_new (DBusGConnection *connection, const char *path)
+{
+ g_return_val_if_fail (connection != NULL, NULL);
+ g_return_val_if_fail (path != NULL, NULL);
+
+ return g_object_new (NM_TYPE_DEVICE_BT,
+ NM_OBJECT_DBUS_CONNECTION, connection,
+ NM_OBJECT_DBUS_PATH, path,
+ NULL);
+}
+
+/**
+ * nm_device_bt_get_hw_address:
+ * @device: a #NMDeviceBt
+ *
+ * Gets the hardware (MAC) address of the #NMDeviceBt
+ *
+ * Returns: the hardware address. This is the internal string used by the
+ * device, and must not be modified.
+ **/
+const char *
+nm_device_bt_get_hw_address (NMDeviceBt *device)
+{
+ g_return_val_if_fail (NM_IS_DEVICE_BT (device), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return NM_DEVICE_BT_GET_PRIVATE (device)->hw_address;
+}
+
+/**
+ * nm_device_bt_get_name:
+ * @device: a #NMDeviceBt
+ *
+ * Gets the name of the #NMDeviceBt.
+ *
+ * Returns: the name of the device
+ **/
+const char *
+nm_device_bt_get_name (NMDeviceBt *device)
+{
+ g_return_val_if_fail (NM_IS_DEVICE_BT (device), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return NM_DEVICE_BT_GET_PRIVATE (device)->name;
+}
+
+/**
+ * nm_device_bt_get_capabilities:
+ * @device: a #NMDeviceBt
+ *
+ * Returns the Bluetooth device's usable capabilities.
+ *
+ * Returns: a combination of #NMBluetoothCapabilities
+ **/
+NMBluetoothCapabilities
+nm_device_bt_get_capabilities (NMDeviceBt *device)
+{
+ g_return_val_if_fail (NM_IS_DEVICE_BT (device), NM_BT_CAPABILITY_NONE);
+
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return NM_DEVICE_BT_GET_PRIVATE (device)->bt_capabilities;
+}
+
+static NMBluetoothCapabilities
+get_connection_bt_type (NMConnection *connection)
+{
+ NMSettingBluetooth *s_bt;
+ const char *bt_type;
+
+ s_bt = nm_connection_get_setting_bluetooth (connection);
+ if (!s_bt)
+ return NM_BT_CAPABILITY_NONE;
+
+ bt_type = nm_setting_bluetooth_get_connection_type (s_bt);
+ g_assert (bt_type);
+
+ if (!strcmp (bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN))
+ return NM_BT_CAPABILITY_DUN;
+ else if (!strcmp (bt_type, NM_SETTING_BLUETOOTH_TYPE_PANU))
+ return NM_BT_CAPABILITY_NAP;
+
+ return NM_BT_CAPABILITY_NONE;
+}
+
+static gboolean
+connection_compatible (NMDevice *device, NMConnection *connection, GError **error)
+{
+ NMSettingConnection *s_con;
+ NMSettingBluetooth *s_bt;
+ const char *ctype;
+ const GByteArray *mac;
+ const char *hw_str;
+ struct ether_addr *hw_mac;
+ NMBluetoothCapabilities dev_caps;
+ NMBluetoothCapabilities bt_type;
+
+ s_con = nm_connection_get_setting_connection (connection);
+ g_assert (s_con);
+
+ ctype = nm_setting_connection_get_connection_type (s_con);
+ if (strcmp (ctype, NM_SETTING_BLUETOOTH_SETTING_NAME) != 0) {
+ g_set_error (error, NM_DEVICE_BT_ERROR, NM_DEVICE_BT_ERROR_NOT_BT_CONNECTION,
+ "The connection was not a Bluetooth connection.");
+ return FALSE;
+ }
+
+ s_bt = nm_connection_get_setting_bluetooth (connection);
+ if (!s_bt) {
+ g_set_error (error, NM_DEVICE_BT_ERROR, NM_DEVICE_BT_ERROR_INVALID_BT_CONNECTION,
+ "The connection was not a valid Bluetooth connection.");
+ return FALSE;
+ }
+
+ /* Check BT address */
+ hw_str = nm_device_bt_get_hw_address (NM_DEVICE_BT (device));
+ if (hw_str) {
+ hw_mac = ether_aton (hw_str);
+ if (!hw_mac) {
+ g_set_error (error, NM_DEVICE_BT_ERROR, NM_DEVICE_BT_ERROR_INVALID_DEVICE_MAC,
+ "Invalid device MAC address.");
+ return FALSE;
+ }
+ mac = nm_setting_bluetooth_get_bdaddr (s_bt);
+ if (mac && hw_mac && memcmp (mac->data, hw_mac->ether_addr_octet, ETH_ALEN)) {
+ g_set_error (error, NM_DEVICE_BT_ERROR, NM_DEVICE_BT_ERROR_MAC_MISMATCH,
+ "The MACs of the device and the connection didn't match.");
+ return FALSE;
+ }
+ }
+
+ dev_caps = nm_device_bt_get_capabilities (NM_DEVICE_BT (device));
+ bt_type = get_connection_bt_type (connection);
+ if (!(bt_type & dev_caps)) {
+ g_set_error (error, NM_DEVICE_BT_ERROR, NM_DEVICE_BT_ERROR_MISSING_DEVICE_CAPS,
+ "The device missed BT capabilities required by the connection.");
+ return FALSE;
+ }
+
+ return NM_DEVICE_CLASS (nm_device_bt_parent_class)->connection_compatible (device, connection, error);
+}
+
+static GType
+get_setting_type (NMDevice *device)
+{
+ return NM_TYPE_SETTING_BLUETOOTH;
+}
+
+static const char *
+get_hw_address (NMDevice *device)
+{
+ return nm_device_bt_get_hw_address (NM_DEVICE_BT (device));
+}
+
+/************************************************************/
+
+static void
+nm_device_bt_init (NMDeviceBt *device)
+{
+ _nm_device_set_device_type (NM_DEVICE (device), NM_DEVICE_TYPE_BT);
+}
+
+static void
+register_properties (NMDeviceBt *device)
+{
+ NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device);
+ const NMPropertiesInfo property_info[] = {
+ { NM_DEVICE_BT_HW_ADDRESS, &priv->hw_address },
+ { NM_DEVICE_BT_NAME, &priv->name },
+ { NM_DEVICE_BT_CAPABILITIES, &priv->bt_capabilities },
+ { NULL },
+ };
+
+ _nm_object_register_properties (NM_OBJECT (device),
+ priv->proxy,
+ property_info);
+}
+
+static void
+constructed (GObject *object)
+{
+ NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (object);
+
+ G_OBJECT_CLASS (nm_device_bt_parent_class)->constructed (object);
+
+ priv->proxy = _nm_object_new_proxy (NM_OBJECT (object), NULL, NM_DBUS_INTERFACE_DEVICE_BLUETOOTH);
+ register_properties (NM_DEVICE_BT (object));
+}
+
+static void
+dispose (GObject *object)
+{
+ NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (object);
+
+ g_clear_object (&priv->proxy);
+
+ G_OBJECT_CLASS (nm_device_bt_parent_class)->dispose (object);
+}
+
+static void
+finalize (GObject *object)
+{
+ NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (object);
+
+ g_free (priv->hw_address);
+ g_free (priv->name);
+
+ G_OBJECT_CLASS (nm_device_bt_parent_class)->finalize (object);
+}
+
+static void
+get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NMDeviceBt *device = NM_DEVICE_BT (object);
+
+ _nm_object_ensure_inited (NM_OBJECT (object));
+
+ switch (prop_id) {
+ case PROP_HW_ADDRESS:
+ g_value_set_string (value, nm_device_bt_get_hw_address (device));
+ break;
+ case PROP_NAME:
+ g_value_set_string (value, nm_device_bt_get_name (device));
+ break;
+ case PROP_BT_CAPABILITIES:
+ g_value_set_uint (value, nm_device_bt_get_capabilities (device));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nm_device_bt_class_init (NMDeviceBtClass *bt_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (bt_class);
+ NMDeviceClass *device_class = NM_DEVICE_CLASS (bt_class);
+
+ g_type_class_add_private (bt_class, sizeof (NMDeviceBtPrivate));
+
+ /* virtual methods */
+ object_class->constructed = constructed;
+ object_class->dispose = dispose;
+ object_class->finalize = finalize;
+ object_class->get_property = get_property;
+ device_class->connection_compatible = connection_compatible;
+ device_class->get_setting_type = get_setting_type;
+ device_class->get_hw_address = get_hw_address;
+
+ /* properties */
+
+ /**
+ * NMDeviceBt:hw-address:
+ *
+ * The hardware (MAC) address of the device.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_HW_ADDRESS,
+ g_param_spec_string (NM_DEVICE_BT_HW_ADDRESS, "", "",
+ NULL,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMDeviceBt:name:
+ *
+ * The name of the bluetooth device.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_NAME,
+ g_param_spec_string (NM_DEVICE_BT_NAME, "", "",
+ NULL,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMDeviceBt:bt-capabilities:
+ *
+ * The device's bluetooth capabilities, a combination of #NMBluetoothCapabilities.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_BT_CAPABILITIES,
+ g_param_spec_uint (NM_DEVICE_BT_CAPABILITIES, "", "",
+ NM_BT_CAPABILITY_NONE, G_MAXUINT32, NM_BT_CAPABILITY_NONE,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+}
diff --git a/libnm/nm-device-bt.h b/libnm/nm-device-bt.h
new file mode 100644
index 0000000000..51dc86e39e
--- /dev/null
+++ b/libnm/nm-device-bt.h
@@ -0,0 +1,90 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2008 - 2012 Red Hat, Inc.
+ * Copyright 2008 Novell, Inc.
+ */
+
+#ifndef NM_DEVICE_BT_H
+#define NM_DEVICE_BT_H
+
+#include "NetworkManager.h"
+#include "nm-device.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_DEVICE_BT (nm_device_bt_get_type ())
+#define NM_DEVICE_BT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_BT, NMDeviceBt))
+#define NM_DEVICE_BT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_BT, NMDeviceBtClass))
+#define NM_IS_DEVICE_BT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_BT))
+#define NM_IS_DEVICE_BT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_BT))
+#define NM_DEVICE_BT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_BT, NMDeviceBtClass))
+
+/**
+ * NMDeviceBtError:
+ * @NM_DEVICE_BT_ERROR_UNKNOWN: unknown or unclassified error
+ * @NM_DEVICE_BT_ERROR_NOT_BT_CONNECTION: the connection was not of bluetooth type
+ * @NM_DEVICE_BT_ERROR_INVALID_BT_CONNECTION: the bluetooth connection was invalid
+ * @NM_DEVICE_BT_ERROR_INVALID_DEVICE_MAC: the device's MAC was invalid
+ * @NM_DEVICE_BT_ERROR_MAC_MISMATCH: the MACs of the connection and the device mismatched
+ * @NM_DEVICE_BT_ERROR_MISSING_DEVICE_CAPS: the device missed required capabilities
+ */
+typedef enum {
+ NM_DEVICE_BT_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/
+ NM_DEVICE_BT_ERROR_NOT_BT_CONNECTION, /*< nick=NotBtConnection >*/
+ NM_DEVICE_BT_ERROR_INVALID_BT_CONNECTION, /*< nick=InvalidBtConnection >*/
+ NM_DEVICE_BT_ERROR_INVALID_DEVICE_MAC, /*< nick=InvalidDeviceMac >*/
+ NM_DEVICE_BT_ERROR_MAC_MISMATCH, /*< nick=MacMismatch >*/
+ NM_DEVICE_BT_ERROR_MISSING_DEVICE_CAPS, /*< nick=MissingDeviceCaps >*/
+} NMDeviceBtError;
+
+#define NM_DEVICE_BT_ERROR nm_device_bt_error_quark ()
+GQuark nm_device_bt_error_quark (void);
+
+#define NM_DEVICE_BT_HW_ADDRESS "hw-address"
+#define NM_DEVICE_BT_NAME "name"
+#define NM_DEVICE_BT_CAPABILITIES "bt-capabilities"
+
+typedef struct {
+ NMDevice parent;
+} NMDeviceBt;
+
+typedef struct {
+ NMDeviceClass parent;
+
+ /* Padding for future expansion */
+ void (*_reserved1) (void);
+ void (*_reserved2) (void);
+ void (*_reserved3) (void);
+ void (*_reserved4) (void);
+ void (*_reserved5) (void);
+ void (*_reserved6) (void);
+} NMDeviceBtClass;
+
+GType nm_device_bt_get_type (void);
+
+GObject *nm_device_bt_new (DBusGConnection *connection, const char *path);
+
+const char *nm_device_bt_get_hw_address (NMDeviceBt *device);
+
+const char *nm_device_bt_get_name (NMDeviceBt *device);
+
+NMBluetoothCapabilities nm_device_bt_get_capabilities (NMDeviceBt *device);
+
+G_END_DECLS
+
+#endif /* NM_DEVICE_BT_H */
diff --git a/libnm/nm-device-ethernet.c b/libnm/nm-device-ethernet.c
new file mode 100644
index 0000000000..690041a515
--- /dev/null
+++ b/libnm/nm-device-ethernet.c
@@ -0,0 +1,392 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2008 Novell, Inc.
+ * Copyright 2007 - 2012 Red Hat, Inc.
+ */
+
+#include <config.h>
+#include <string.h>
+#include <netinet/ether.h>
+
+#include "nm-glib-compat.h"
+
+#include <nm-setting-connection.h>
+#include <nm-setting-wired.h>
+#include <nm-setting-pppoe.h>
+
+#include "nm-device-ethernet.h"
+#include "nm-device-private.h"
+#include "nm-object-private.h"
+
+G_DEFINE_TYPE (NMDeviceEthernet, nm_device_ethernet, NM_TYPE_DEVICE)
+
+#define NM_DEVICE_ETHERNET_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_ETHERNET, NMDeviceEthernetPrivate))
+
+typedef struct {
+ DBusGProxy *proxy;
+
+ char *hw_address;
+ char *perm_hw_address;
+ guint32 speed;
+ gboolean carrier;
+} NMDeviceEthernetPrivate;
+
+enum {
+ PROP_0,
+ PROP_HW_ADDRESS,
+ PROP_PERM_HW_ADDRESS,
+ PROP_SPEED,
+ PROP_CARRIER,
+
+ LAST_PROP
+};
+
+/**
+ * nm_device_ethernet_error_quark:
+ *
+ * Registers an error quark for #NMDeviceEthernet if necessary.
+ *
+ * Returns: the error quark used for #NMDeviceEthernet errors.
+ **/
+GQuark
+nm_device_ethernet_error_quark (void)
+{
+ static GQuark quark = 0;
+
+ if (G_UNLIKELY (quark == 0))
+ quark = g_quark_from_static_string ("nm-device-ethernet-error-quark");
+ return quark;
+}
+
+/**
+ * nm_device_ethernet_new:
+ * @connection: the #DBusGConnection
+ * @path: the DBus object path of the device
+ *
+ * Creates a new #NMDeviceEthernet.
+ *
+ * Returns: (transfer full): a new device
+ **/
+GObject *
+nm_device_ethernet_new (DBusGConnection *connection, const char *path)
+{
+ GObject *device;
+
+ g_return_val_if_fail (connection != NULL, NULL);
+ g_return_val_if_fail (path != NULL, NULL);
+
+ device = g_object_new (NM_TYPE_DEVICE_ETHERNET,
+ NM_OBJECT_DBUS_CONNECTION, connection,
+ NM_OBJECT_DBUS_PATH, path,
+ NULL);
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return device;
+}
+
+/**
+ * nm_device_ethernet_get_hw_address:
+ * @device: a #NMDeviceEthernet
+ *
+ * Gets the active hardware (MAC) address of the #NMDeviceEthernet
+ *
+ * Returns: the active hardware address. This is the internal string used by the
+ * device, and must not be modified.
+ **/
+const char *
+nm_device_ethernet_get_hw_address (NMDeviceEthernet *device)
+{
+ g_return_val_if_fail (NM_IS_DEVICE_ETHERNET (device), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return NM_DEVICE_ETHERNET_GET_PRIVATE (device)->hw_address;
+}
+
+/**
+ * nm_device_ethernet_get_permanent_hw_address:
+ * @device: a #NMDeviceEthernet
+ *
+ * Gets the permanent hardware (MAC) address of the #NMDeviceEthernet
+ *
+ * Returns: the permanent hardware address. This is the internal string used by the
+ * device, and must not be modified.
+ **/
+const char *
+nm_device_ethernet_get_permanent_hw_address (NMDeviceEthernet *device)
+{
+ g_return_val_if_fail (NM_IS_DEVICE_ETHERNET (device), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return NM_DEVICE_ETHERNET_GET_PRIVATE (device)->perm_hw_address;
+}
+
+/**
+ * nm_device_ethernet_get_speed:
+ * @device: a #NMDeviceEthernet
+ *
+ * Gets the speed of the #NMDeviceEthernet.
+ *
+ * Returns: the speed of the device
+ **/
+guint32
+nm_device_ethernet_get_speed (NMDeviceEthernet *device)
+{
+ g_return_val_if_fail (NM_IS_DEVICE_ETHERNET (device), 0);
+
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return NM_DEVICE_ETHERNET_GET_PRIVATE (device)->speed;
+}
+
+/**
+ * nm_device_ethernet_get_carrier:
+ * @device: a #NMDeviceEthernet
+ *
+ * Whether the device has carrier.
+ *
+ * Returns: %TRUE if the device has carrier
+ **/
+gboolean
+nm_device_ethernet_get_carrier (NMDeviceEthernet *device)
+{
+ g_return_val_if_fail (NM_IS_DEVICE_ETHERNET (device), FALSE);
+
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return NM_DEVICE_ETHERNET_GET_PRIVATE (device)->carrier;
+}
+
+static gboolean
+connection_compatible (NMDevice *device, NMConnection *connection, GError **error)
+{
+ NMSettingConnection *s_con;
+ NMSettingWired *s_wired;
+ const char *ctype;
+ gboolean is_pppoe = FALSE;
+
+ s_con = nm_connection_get_setting_connection (connection);
+ g_assert (s_con);
+
+ ctype = nm_setting_connection_get_connection_type (s_con);
+ if (!strcmp (ctype, NM_SETTING_PPPOE_SETTING_NAME))
+ is_pppoe = TRUE;
+ else if (strcmp (ctype, NM_SETTING_WIRED_SETTING_NAME) != 0) {
+ g_set_error (error, NM_DEVICE_ETHERNET_ERROR, NM_DEVICE_ETHERNET_ERROR_NOT_ETHERNET_CONNECTION,
+ "The connection was not a wired or PPPoE connection.");
+ return FALSE;
+ }
+
+ s_wired = nm_connection_get_setting_wired (connection);
+ /* Wired setting optional for PPPoE */
+ if (!is_pppoe && !s_wired) {
+ g_set_error (error, NM_DEVICE_ETHERNET_ERROR, NM_DEVICE_ETHERNET_ERROR_INVALID_ETHERNET_CONNECTION,
+ "The connection was not a valid Ethernet connection.");
+ return FALSE;
+ }
+
+ if (s_wired) {
+ const GByteArray *mac;
+ const char *perm_str;
+ struct ether_addr *perm_mac;
+
+ /* FIXME: filter using s390 subchannels when they are exported over the bus */
+
+ /* Check MAC address */
+ perm_str = nm_device_ethernet_get_permanent_hw_address (NM_DEVICE_ETHERNET (device));
+ if (perm_str) {
+ perm_mac = ether_aton (perm_str);
+ if (!perm_mac) {
+ g_set_error (error, NM_DEVICE_ETHERNET_ERROR, NM_DEVICE_ETHERNET_ERROR_INVALID_DEVICE_MAC,
+ "Invalid device MAC address.");
+ return FALSE;
+ }
+ mac = nm_setting_wired_get_mac_address (s_wired);
+ if (mac && perm_mac && memcmp (mac->data, perm_mac->ether_addr_octet, ETH_ALEN)) {
+ g_set_error (error, NM_DEVICE_ETHERNET_ERROR, NM_DEVICE_ETHERNET_ERROR_MAC_MISMATCH,
+ "The MACs of the device and the connection didn't match.");
+ return FALSE;
+ }
+ }
+ }
+
+ return NM_DEVICE_CLASS (nm_device_ethernet_parent_class)->connection_compatible (device, connection, error);
+}
+
+static GType
+get_setting_type (NMDevice *device)
+{
+ return NM_TYPE_SETTING_WIRED;
+}
+
+static const char *
+get_hw_address (NMDevice *device)
+{
+ return nm_device_ethernet_get_hw_address (NM_DEVICE_ETHERNET (device));
+}
+
+/***********************************************************/
+
+static void
+nm_device_ethernet_init (NMDeviceEthernet *device)
+{
+ _nm_device_set_device_type (NM_DEVICE (device), NM_DEVICE_TYPE_ETHERNET);
+}
+
+static void
+register_properties (NMDeviceEthernet *device)
+{
+ NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (device);
+ const NMPropertiesInfo property_info[] = {
+ { NM_DEVICE_ETHERNET_HW_ADDRESS, &priv->hw_address },
+ { NM_DEVICE_ETHERNET_PERMANENT_HW_ADDRESS, &priv->perm_hw_address },
+ { NM_DEVICE_ETHERNET_SPEED, &priv->speed },
+ { NM_DEVICE_ETHERNET_CARRIER, &priv->carrier },
+ { NULL },
+ };
+
+ _nm_object_register_properties (NM_OBJECT (device),
+ priv->proxy,
+ property_info);
+}
+
+static void
+constructed (GObject *object)
+{
+ NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (object);
+
+ G_OBJECT_CLASS (nm_device_ethernet_parent_class)->constructed (object);
+
+ priv->proxy = _nm_object_new_proxy (NM_OBJECT (object), NULL, NM_DBUS_INTERFACE_DEVICE_WIRED);
+ register_properties (NM_DEVICE_ETHERNET (object));
+}
+
+static void
+dispose (GObject *object)
+{
+ NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (object);
+
+ g_clear_object (&priv->proxy);
+
+ G_OBJECT_CLASS (nm_device_ethernet_parent_class)->dispose (object);
+}
+
+static void
+finalize (GObject *object)
+{
+ NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (object);
+
+ g_free (priv->hw_address);
+ g_free (priv->perm_hw_address);
+
+ G_OBJECT_CLASS (nm_device_ethernet_parent_class)->finalize (object);
+}
+
+static void
+get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NMDeviceEthernet *device = NM_DEVICE_ETHERNET (object);
+
+ _nm_object_ensure_inited (NM_OBJECT (object));
+
+ switch (prop_id) {
+ case PROP_HW_ADDRESS:
+ g_value_set_string (value, nm_device_ethernet_get_hw_address (device));
+ break;
+ case PROP_PERM_HW_ADDRESS:
+ g_value_set_string (value, nm_device_ethernet_get_permanent_hw_address (device));
+ break;
+ case PROP_SPEED:
+ g_value_set_uint (value, nm_device_ethernet_get_speed (device));
+ break;
+ case PROP_CARRIER:
+ g_value_set_boolean (value, nm_device_ethernet_get_carrier (device));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nm_device_ethernet_class_init (NMDeviceEthernetClass *eth_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (eth_class);
+ NMDeviceClass *device_class = NM_DEVICE_CLASS (eth_class);
+
+ g_type_class_add_private (eth_class, sizeof (NMDeviceEthernetPrivate));
+
+ /* virtual methods */
+ object_class->constructed = constructed;
+ object_class->dispose = dispose;
+ object_class->finalize = finalize;
+ object_class->get_property = get_property;
+ device_class->connection_compatible = connection_compatible;
+ device_class->get_setting_type = get_setting_type;
+ device_class->get_hw_address = get_hw_address;
+
+ /* properties */
+
+ /**
+ * NMDeviceEthernet:hw-address:
+ *
+ * The active hardware (MAC) address of the device.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_HW_ADDRESS,
+ g_param_spec_string (NM_DEVICE_ETHERNET_HW_ADDRESS, "", "",
+ NULL,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMDeviceEthernet:perm-hw-address:
+ *
+ * The permanent hardware (MAC) address of the device.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PERM_HW_ADDRESS,
+ g_param_spec_string (NM_DEVICE_ETHERNET_PERMANENT_HW_ADDRESS, "", "",
+ NULL,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMDeviceEthernet:speed:
+ *
+ * The speed of the device.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_SPEED,
+ g_param_spec_uint (NM_DEVICE_ETHERNET_SPEED, "", "",
+ 0, G_MAXUINT32, 0,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMDeviceEthernet:carrier:
+ *
+ * Whether the device has carrier.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_CARRIER,
+ g_param_spec_boolean (NM_DEVICE_ETHERNET_CARRIER, "", "",
+ FALSE,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+}
diff --git a/libnm/nm-device-ethernet.h b/libnm/nm-device-ethernet.h
new file mode 100644
index 0000000000..be289cdfcc
--- /dev/null
+++ b/libnm/nm-device-ethernet.h
@@ -0,0 +1,87 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2008 Novell, Inc.
+ * Copyright 2007 - 2012 Red Hat, Inc.
+ */
+
+#ifndef NM_DEVICE_ETHERNET_H
+#define NM_DEVICE_ETHERNET_H
+
+#include "nm-device.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_DEVICE_ETHERNET (nm_device_ethernet_get_type ())
+#define NM_DEVICE_ETHERNET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_ETHERNET, NMDeviceEthernet))
+#define NM_DEVICE_ETHERNET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_ETHERNET, NMDeviceEthernetClass))
+#define NM_IS_DEVICE_ETHERNET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_ETHERNET))
+#define NM_IS_DEVICE_ETHERNET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_ETHERNET))
+#define NM_DEVICE_ETHERNET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_ETHERNET, NMDeviceEthernetClass))
+
+/**
+ * NMDeviceEthernetError:
+ * @NM_DEVICE_ETHERNET_ERROR_UNKNOWN: unknown or unclassified error
+ * @NM_DEVICE_ETHERNET_ERROR_NOT_ETHERNET_CONNECTION: the connection was not of Ethernet or PPPoE type
+ * @NM_DEVICE_ETHERNET_ERROR_INVALID_ETHERNET_CONNECTION: the Ethernet connection was invalid
+ * @NM_DEVICE_ETHERNET_ERROR_INVALID_DEVICE_MAC: the device's MAC was invalid
+ * @NM_DEVICE_ETHERNET_ERROR_MAC_MISMATCH: the MACs of the connection and the device mismatched
+ */
+typedef enum {
+ NM_DEVICE_ETHERNET_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/
+ NM_DEVICE_ETHERNET_ERROR_NOT_ETHERNET_CONNECTION, /*< nick=NotEthernetConnection >*/
+ NM_DEVICE_ETHERNET_ERROR_INVALID_ETHERNET_CONNECTION, /*< nick=InvalidEthernetConnection >*/
+ NM_DEVICE_ETHERNET_ERROR_INVALID_DEVICE_MAC, /*< nick=InvalidDeviceMac >*/
+ NM_DEVICE_ETHERNET_ERROR_MAC_MISMATCH, /*< nick=MacMismatch >*/
+} NMDeviceEthernetError;
+
+#define NM_DEVICE_ETHERNET_ERROR nm_device_ethernet_error_quark ()
+GQuark nm_device_ethernet_error_quark (void);
+
+#define NM_DEVICE_ETHERNET_HW_ADDRESS "hw-address"
+#define NM_DEVICE_ETHERNET_PERMANENT_HW_ADDRESS "perm-hw-address"
+#define NM_DEVICE_ETHERNET_SPEED "speed"
+#define NM_DEVICE_ETHERNET_CARRIER "carrier"
+
+typedef struct {
+ NMDevice parent;
+} NMDeviceEthernet;
+
+typedef struct {
+ NMDeviceClass parent;
+
+ /* Padding for future expansion */
+ void (*_reserved1) (void);
+ void (*_reserved2) (void);
+ void (*_reserved3) (void);
+ void (*_reserved4) (void);
+ void (*_reserved5) (void);
+ void (*_reserved6) (void);
+} NMDeviceEthernetClass;
+
+GType nm_device_ethernet_get_type (void);
+
+GObject *nm_device_ethernet_new (DBusGConnection *connection, const char *path);
+
+const char * nm_device_ethernet_get_hw_address (NMDeviceEthernet *device);
+const char * nm_device_ethernet_get_permanent_hw_address (NMDeviceEthernet *device);
+guint32 nm_device_ethernet_get_speed (NMDeviceEthernet *device);
+gboolean nm_device_ethernet_get_carrier (NMDeviceEthernet *device);
+
+G_END_DECLS
+
+#endif /* NM_DEVICE_ETHERNET_H */
diff --git a/libnm/nm-device-generic.c b/libnm/nm-device-generic.c
new file mode 100644
index 0000000000..875ea8eb39
--- /dev/null
+++ b/libnm/nm-device-generic.c
@@ -0,0 +1,284 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#include <config.h>
+
+#include <string.h>
+
+#include "nm-device-generic.h"
+#include "nm-device-private.h"
+#include "nm-object-private.h"
+#include "nm-setting-generic.h"
+
+G_DEFINE_TYPE (NMDeviceGeneric, nm_device_generic, NM_TYPE_DEVICE)
+
+#define NM_DEVICE_GENERIC_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_GENERIC, NMDeviceGenericPrivate))
+
+typedef struct {
+ DBusGProxy *proxy;
+
+ char *hw_address;
+ char *type_description;
+} NMDeviceGenericPrivate;
+
+enum {
+ PROP_0,
+ PROP_HW_ADDRESS,
+ PROP_TYPE_DESCRIPTION,
+
+ LAST_PROP
+};
+
+/**
+ * nm_device_generic_error_quark:
+ *
+ * Registers an error quark for #NMDeviceGeneric if necessary.
+ *
+ * Returns: the error quark used for #NMDeviceGeneric errors.
+ *
+ * Since: 0.9.10
+ **/
+GQuark
+nm_device_generic_error_quark (void)
+{
+ static GQuark quark = 0;
+
+ if (G_UNLIKELY (quark == 0))
+ quark = g_quark_from_static_string ("nm-device-generic-error-quark");
+ return quark;
+}
+
+/**
+ * nm_device_generic_new:
+ * @connection: the #DBusGConnection
+ * @path: the DBus object path of the device
+ *
+ * Creates a new #NMDeviceGeneric.
+ *
+ * Returns: (transfer full): a new device
+ *
+ * Since: 0.9.10
+ **/
+GObject *
+nm_device_generic_new (DBusGConnection *connection, const char *path)
+{
+ GObject *device;
+
+ g_return_val_if_fail (connection != NULL, NULL);
+ g_return_val_if_fail (path != NULL, NULL);
+
+ device = g_object_new (NM_TYPE_DEVICE_GENERIC,
+ NM_OBJECT_DBUS_CONNECTION, connection,
+ NM_OBJECT_DBUS_PATH, path,
+ NULL);
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return device;
+}
+
+/**
+ * nm_device_generic_get_hw_address:
+ * @device: a #NMDeviceGeneric
+ *
+ * Gets the hardware address of the #NMDeviceGeneric
+ *
+ * Returns: the hardware address. This is the internal string used by the
+ * device, and must not be modified.
+ *
+ * Since: 0.9.10
+ **/
+const char *
+nm_device_generic_get_hw_address (NMDeviceGeneric *device)
+{
+ g_return_val_if_fail (NM_IS_DEVICE_GENERIC (device), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return NM_DEVICE_GENERIC_GET_PRIVATE (device)->hw_address;
+}
+
+/***********************************************************/
+
+static const char *
+get_type_description (NMDevice *device)
+{
+ NMDeviceGenericPrivate *priv = NM_DEVICE_GENERIC_GET_PRIVATE (device);
+
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return priv->type_description;
+}
+
+static const char *
+get_hw_address (NMDevice *device)
+{
+ return nm_device_generic_get_hw_address (NM_DEVICE_GENERIC (device));
+}
+
+static gboolean
+connection_compatible (NMDevice *device, NMConnection *connection, GError **error)
+{
+ NMSettingConnection *s_con;
+ const char *ctype, *iface_name;
+
+ s_con = nm_connection_get_setting_connection (connection);
+ g_assert (s_con);
+
+ ctype = nm_setting_connection_get_connection_type (s_con);
+ if (strcmp (ctype, NM_SETTING_GENERIC_SETTING_NAME) != 0) {
+ g_set_error (error, NM_DEVICE_GENERIC_ERROR, NM_DEVICE_GENERIC_ERROR_NOT_GENERIC_CONNECTION,
+ "The connection was not a generic connection.");
+ return FALSE;
+ }
+
+ iface_name = nm_setting_connection_get_interface_name (s_con);
+ if (!iface_name) {
+ g_set_error (error, NM_DEVICE_GENERIC_ERROR, NM_DEVICE_GENERIC_ERROR_MISSING_INTERFACE_NAME,
+ "The connection did not specify an interface name.");
+ return FALSE;
+ }
+
+ return NM_DEVICE_CLASS (nm_device_generic_parent_class)->connection_compatible (device, connection, error);
+}
+
+static GType
+get_setting_type (NMDevice *device)
+{
+ return NM_TYPE_SETTING_GENERIC;
+}
+
+/***********************************************************/
+
+static void
+nm_device_generic_init (NMDeviceGeneric *device)
+{
+ _nm_device_set_device_type (NM_DEVICE (device), NM_DEVICE_TYPE_GENERIC);
+}
+
+static void
+register_properties (NMDeviceGeneric *device)
+{
+ NMDeviceGenericPrivate *priv = NM_DEVICE_GENERIC_GET_PRIVATE (device);
+ const NMPropertiesInfo property_info[] = {
+ { NM_DEVICE_GENERIC_HW_ADDRESS, &priv->hw_address },
+ { NM_DEVICE_GENERIC_TYPE_DESCRIPTION, &priv->type_description },
+ { NULL },
+ };
+
+ _nm_object_register_properties (NM_OBJECT (device),
+ priv->proxy,
+ property_info);
+}
+
+static void
+constructed (GObject *object)
+{
+ NMDeviceGenericPrivate *priv = NM_DEVICE_GENERIC_GET_PRIVATE (object);
+
+ G_OBJECT_CLASS (nm_device_generic_parent_class)->constructed (object);
+
+ priv->proxy = _nm_object_new_proxy (NM_OBJECT (object), NULL, NM_DBUS_INTERFACE_DEVICE_GENERIC);
+ register_properties (NM_DEVICE_GENERIC (object));
+}
+
+static void
+dispose (GObject *object)
+{
+ NMDeviceGenericPrivate *priv = NM_DEVICE_GENERIC_GET_PRIVATE (object);
+
+ g_clear_object (&priv->proxy);
+
+ G_OBJECT_CLASS (nm_device_generic_parent_class)->dispose (object);
+}
+
+static void
+finalize (GObject *object)
+{
+ NMDeviceGenericPrivate *priv = NM_DEVICE_GENERIC_GET_PRIVATE (object);
+
+ g_free (priv->hw_address);
+ g_free (priv->type_description);
+
+ G_OBJECT_CLASS (nm_device_generic_parent_class)->finalize (object);
+}
+
+static void
+get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NMDeviceGenericPrivate *priv = NM_DEVICE_GENERIC_GET_PRIVATE (object);
+
+ _nm_object_ensure_inited (NM_OBJECT (object));
+
+ switch (prop_id) {
+ case PROP_HW_ADDRESS:
+ g_value_set_string (value, priv->hw_address);
+ break;
+ case PROP_TYPE_DESCRIPTION:
+ g_value_set_string (value, priv->type_description);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nm_device_generic_class_init (NMDeviceGenericClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ NMDeviceClass *device_class = NM_DEVICE_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (NMDeviceGenericPrivate));
+
+ object_class->constructed = constructed;
+ object_class->dispose = dispose;
+ object_class->finalize = finalize;
+ object_class->get_property = get_property;
+
+ device_class->get_type_description = get_type_description;
+ device_class->get_hw_address = get_hw_address;
+ device_class->connection_compatible = connection_compatible;
+ device_class->get_setting_type = get_setting_type;
+
+ /**
+ * NMDeviceGeneric:hw-address:
+ *
+ * The hardware address of the device.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_HW_ADDRESS,
+ g_param_spec_string (NM_DEVICE_GENERIC_HW_ADDRESS, "", "",
+ NULL,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMDeviceGeneric:type-description:
+ *
+ * A description of the specific type of device this is, or %NULL
+ * if not known.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_TYPE_DESCRIPTION,
+ g_param_spec_string (NM_DEVICE_GENERIC_TYPE_DESCRIPTION, "", "",
+ NULL,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/libnm/nm-device-generic.h b/libnm/nm-device-generic.h
new file mode 100644
index 0000000000..5bff1e3f55
--- /dev/null
+++ b/libnm/nm-device-generic.h
@@ -0,0 +1,79 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NM_DEVICE_GENERIC_H
+#define NM_DEVICE_GENERIC_H
+
+#include "nm-device.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_DEVICE_GENERIC (nm_device_generic_get_type ())
+#define NM_DEVICE_GENERIC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_GENERIC, NMDeviceGeneric))
+#define NM_DEVICE_GENERIC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_GENERIC, NMDeviceGenericClass))
+#define NM_IS_DEVICE_GENERIC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_GENERIC))
+#define NM_IS_DEVICE_GENERIC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_GENERIC))
+#define NM_DEVICE_GENERIC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_GENERIC, NMDeviceGenericClass))
+
+/**
+ * NMDeviceGenericError:
+ * @NM_DEVICE_GENERIC_ERROR_UNKNOWN: unknown or unclassified error
+ * @NM_DEVICE_GENERIC_ERROR_NOT_GENERIC_CONNECTION: the connection was not of generic type
+ * @NM_DEVICE_GENERIC_ERROR_MISSING_INTERFACE_NAME: the connection did not specify the interface name
+ */
+typedef enum {
+ NM_DEVICE_GENERIC_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/
+ NM_DEVICE_GENERIC_ERROR_NOT_GENERIC_CONNECTION, /*< nick=NotGenericConnection >*/
+ NM_DEVICE_GENERIC_ERROR_MISSING_INTERFACE_NAME, /*< nick=MissingInterfaceName >*/
+} NMDeviceGenericError;
+
+#define NM_DEVICE_GENERIC_ERROR nm_device_generic_error_quark ()
+GQuark nm_device_generic_error_quark (void);
+
+#define NM_DEVICE_GENERIC_HW_ADDRESS "hw-address"
+#define NM_DEVICE_GENERIC_TYPE_DESCRIPTION "type-description"
+
+typedef struct {
+ NMDevice parent;
+} NMDeviceGeneric;
+
+typedef struct {
+ NMDeviceClass parent;
+
+ /* Padding for future expansion */
+ void (*_reserved1) (void);
+ void (*_reserved2) (void);
+ void (*_reserved3) (void);
+ void (*_reserved4) (void);
+ void (*_reserved5) (void);
+ void (*_reserved6) (void);
+} NMDeviceGenericClass;
+
+NM_AVAILABLE_IN_0_9_10
+GType nm_device_generic_get_type (void);
+
+NM_AVAILABLE_IN_0_9_10
+GObject *nm_device_generic_new (DBusGConnection *connection, const char *path);
+
+const char *nm_device_generic_get_hw_address (NMDeviceGeneric *device);
+
+G_END_DECLS
+
+#endif /* NM_DEVICE_GENERIC_H */
diff --git a/libnm/nm-device-infiniband.c b/libnm/nm-device-infiniband.c
new file mode 100644
index 0000000000..d740ed006e
--- /dev/null
+++ b/libnm/nm-device-infiniband.c
@@ -0,0 +1,311 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2011 - 2012 Red Hat, Inc.
+ */
+
+#include <config.h>
+#include <string.h>
+#include <linux/if_infiniband.h>
+#include <netinet/ether.h>
+
+#include "nm-glib-compat.h"
+
+#include <nm-setting-connection.h>
+#include <nm-setting-infiniband.h>
+#include <nm-utils.h>
+
+#include "nm-device-infiniband.h"
+#include "nm-device-private.h"
+#include "nm-object-private.h"
+
+G_DEFINE_TYPE (NMDeviceInfiniband, nm_device_infiniband, NM_TYPE_DEVICE)
+
+#define NM_DEVICE_INFINIBAND_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_INFINIBAND, NMDeviceInfinibandPrivate))
+
+typedef struct {
+ DBusGProxy *proxy;
+
+ char *hw_address;
+ gboolean carrier;
+} NMDeviceInfinibandPrivate;
+
+enum {
+ PROP_0,
+ PROP_HW_ADDRESS,
+ PROP_CARRIER,
+
+ LAST_PROP
+};
+
+/**
+ * nm_device_infiniband_error_quark:
+ *
+ * Registers an error quark for #NMDeviceInfiniband if necessary.
+ *
+ * Returns: the error quark used for #NMDeviceInfiniband errors.
+ **/
+GQuark
+nm_device_infiniband_error_quark (void)
+{
+ static GQuark quark = 0;
+
+ if (G_UNLIKELY (quark == 0))
+ quark = g_quark_from_static_string ("nm-device-infiniband-error-quark");
+ return quark;
+}
+
+/**
+ * nm_device_infiniband_new:
+ * @connection: the #DBusGConnection
+ * @path: the DBus object path of the device
+ *
+ * Creates a new #NMDeviceInfiniband.
+ *
+ * Returns: (transfer full): a new device
+ **/
+GObject *
+nm_device_infiniband_new (DBusGConnection *connection, const char *path)
+{
+ GObject *device;
+
+ g_return_val_if_fail (connection != NULL, NULL);
+ g_return_val_if_fail (path != NULL, NULL);
+
+ device = g_object_new (NM_TYPE_DEVICE_INFINIBAND,
+ NM_OBJECT_DBUS_CONNECTION, connection,
+ NM_OBJECT_DBUS_PATH, path,
+ NULL);
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return device;
+}
+
+/**
+ * nm_device_infiniband_get_hw_address:
+ * @device: a #NMDeviceInfiniband
+ *
+ * Gets the hardware (MAC) address of the #NMDeviceInfiniband
+ *
+ * Returns: the hardware address. This is the internal string used by the
+ * device, and must not be modified.
+ **/
+const char *
+nm_device_infiniband_get_hw_address (NMDeviceInfiniband *device)
+{
+ g_return_val_if_fail (NM_IS_DEVICE_INFINIBAND (device), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return NM_DEVICE_INFINIBAND_GET_PRIVATE (device)->hw_address;
+}
+
+/**
+ * nm_device_infiniband_get_carrier:
+ * @device: a #NMDeviceInfiniband
+ *
+ * Whether the device has carrier.
+ *
+ * Returns: %TRUE if the device has carrier
+ **/
+gboolean
+nm_device_infiniband_get_carrier (NMDeviceInfiniband *device)
+{
+ g_return_val_if_fail (NM_IS_DEVICE_INFINIBAND (device), FALSE);
+
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return NM_DEVICE_INFINIBAND_GET_PRIVATE (device)->carrier;
+}
+
+static gboolean
+connection_compatible (NMDevice *device, NMConnection *connection, GError **error)
+{
+ NMSettingConnection *s_con;
+ NMSettingInfiniband *s_infiniband;
+ const char *ctype, *hwaddr_str;
+ const GByteArray *mac;
+ guint8 *hwaddr, hwaddr_buf[INFINIBAND_ALEN];
+
+ s_con = nm_connection_get_setting_connection (connection);
+ g_assert (s_con);
+
+ ctype = nm_setting_connection_get_connection_type (s_con);
+ if (strcmp (ctype, NM_SETTING_INFINIBAND_SETTING_NAME) != 0) {
+ g_set_error (error, NM_DEVICE_INFINIBAND_ERROR, NM_DEVICE_INFINIBAND_ERROR_NOT_INFINIBAND_CONNECTION,
+ "The connection was not a InfiniBand connection.");
+ return FALSE;
+ }
+
+ s_infiniband = nm_connection_get_setting_infiniband (connection);
+ if (!s_infiniband) {
+ g_set_error (error, NM_DEVICE_INFINIBAND_ERROR, NM_DEVICE_INFINIBAND_ERROR_INVALID_INFINIBAND_CONNECTION,
+ "The connection was not a valid InfiniBand connection.");
+ return FALSE;
+ }
+
+ hwaddr_str = nm_device_infiniband_get_hw_address (NM_DEVICE_INFINIBAND (device));
+ if (hwaddr_str) {
+ hwaddr = nm_utils_hwaddr_aton (hwaddr_str, ARPHRD_INFINIBAND, hwaddr_buf);
+ if (!hwaddr) {
+ g_set_error (error, NM_DEVICE_INFINIBAND_ERROR, NM_DEVICE_INFINIBAND_ERROR_INVALID_DEVICE_MAC,
+ "Invalid device MAC address.");
+ return FALSE;
+ }
+ mac = nm_setting_infiniband_get_mac_address (s_infiniband);
+
+ /* We only match against the last 8 bytes */
+ if (mac && hwaddr && memcmp (mac->data + INFINIBAND_ALEN - 8, hwaddr + INFINIBAND_ALEN - 8, 8)) {
+ g_set_error (error, NM_DEVICE_INFINIBAND_ERROR, NM_DEVICE_INFINIBAND_ERROR_MAC_MISMATCH,
+ "The MACs of the device and the connection didn't match.");
+ return FALSE;
+ }
+ }
+
+ return NM_DEVICE_CLASS (nm_device_infiniband_parent_class)->connection_compatible (device, connection, error);
+}
+
+static GType
+get_setting_type (NMDevice *device)
+{
+ return NM_TYPE_SETTING_INFINIBAND;
+}
+
+static const char *
+get_hw_address (NMDevice *device)
+{
+ return nm_device_infiniband_get_hw_address (NM_DEVICE_INFINIBAND (device));
+}
+
+/***********************************************************/
+
+static void
+nm_device_infiniband_init (NMDeviceInfiniband *device)
+{
+ _nm_device_set_device_type (NM_DEVICE (device), NM_DEVICE_TYPE_INFINIBAND);
+}
+
+static void
+register_properties (NMDeviceInfiniband *device)
+{
+ NMDeviceInfinibandPrivate *priv = NM_DEVICE_INFINIBAND_GET_PRIVATE (device);
+ const NMPropertiesInfo property_info[] = {
+ { NM_DEVICE_INFINIBAND_HW_ADDRESS, &priv->hw_address },
+ { NM_DEVICE_INFINIBAND_CARRIER, &priv->carrier },
+ { NULL },
+ };
+
+ _nm_object_register_properties (NM_OBJECT (device),
+ priv->proxy,
+ property_info);
+}
+
+static void
+constructed (GObject *object)
+{
+ NMDeviceInfinibandPrivate *priv = NM_DEVICE_INFINIBAND_GET_PRIVATE (object);
+
+ G_OBJECT_CLASS (nm_device_infiniband_parent_class)->constructed (object);
+
+ priv->proxy = _nm_object_new_proxy (NM_OBJECT (object), NULL, NM_DBUS_INTERFACE_DEVICE_INFINIBAND);
+ register_properties (NM_DEVICE_INFINIBAND (object));
+}
+
+static void
+dispose (GObject *object)
+{
+ NMDeviceInfinibandPrivate *priv = NM_DEVICE_INFINIBAND_GET_PRIVATE (object);
+
+ g_clear_object (&priv->proxy);
+
+ G_OBJECT_CLASS (nm_device_infiniband_parent_class)->dispose (object);
+}
+
+static void
+finalize (GObject *object)
+{
+ NMDeviceInfinibandPrivate *priv = NM_DEVICE_INFINIBAND_GET_PRIVATE (object);
+
+ g_free (priv->hw_address);
+
+ G_OBJECT_CLASS (nm_device_infiniband_parent_class)->finalize (object);
+}
+
+static void
+get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NMDeviceInfiniband *device = NM_DEVICE_INFINIBAND (object);
+
+ _nm_object_ensure_inited (NM_OBJECT (object));
+
+ switch (prop_id) {
+ case PROP_HW_ADDRESS:
+ g_value_set_string (value, nm_device_infiniband_get_hw_address (device));
+ break;
+ case PROP_CARRIER:
+ g_value_set_boolean (value, nm_device_infiniband_get_carrier (device));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nm_device_infiniband_class_init (NMDeviceInfinibandClass *eth_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (eth_class);
+ NMDeviceClass *device_class = NM_DEVICE_CLASS (eth_class);
+
+ g_type_class_add_private (eth_class, sizeof (NMDeviceInfinibandPrivate));
+
+ /* virtual methods */
+ object_class->constructed = constructed;
+ object_class->dispose = dispose;
+ object_class->finalize = finalize;
+ object_class->get_property = get_property;
+ device_class->connection_compatible = connection_compatible;
+ device_class->get_setting_type = get_setting_type;
+ device_class->get_hw_address = get_hw_address;
+
+ /* properties */
+
+ /**
+ * NMDeviceInfiniband:hw-address:
+ *
+ * The hardware (MAC) address of the device.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_HW_ADDRESS,
+ g_param_spec_string (NM_DEVICE_INFINIBAND_HW_ADDRESS, "", "",
+ NULL,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMDeviceInfiniband:carrier:
+ *
+ * Whether the device has carrier.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_CARRIER,
+ g_param_spec_boolean (NM_DEVICE_INFINIBAND_CARRIER, "", "",
+ FALSE,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+}
diff --git a/libnm/nm-device-infiniband.h b/libnm/nm-device-infiniband.h
new file mode 100644
index 0000000000..ba587d93b9
--- /dev/null
+++ b/libnm/nm-device-infiniband.h
@@ -0,0 +1,82 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2011 - 2012 Red Hat, Inc.
+ */
+
+#ifndef NM_DEVICE_INFINIBAND_H
+#define NM_DEVICE_INFINIBAND_H
+
+#include "nm-device.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_DEVICE_INFINIBAND (nm_device_infiniband_get_type ())
+#define NM_DEVICE_INFINIBAND(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_INFINIBAND, NMDeviceInfiniband))
+#define NM_DEVICE_INFINIBAND_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_INFINIBAND, NMDeviceInfinibandClass))
+#define NM_IS_DEVICE_INFINIBAND(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_INFINIBAND))
+#define NM_IS_DEVICE_INFINIBAND_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_INFINIBAND))
+#define NM_DEVICE_INFINIBAND_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_INFINIBAND, NMDeviceInfinibandClass))
+
+/**
+ * NMDeviceInfinibandError:
+ * @NM_DEVICE_INFINIBAND_ERROR_UNKNOWN: unknown or unclassified error
+ * @NM_DEVICE_INFINIBAND_ERROR_NOT_INFINIBAND_CONNECTION: the connection was not of InfiniBand type
+ * @NM_DEVICE_INFINIBAND_ERROR_INVALID_INFINIBAND_CONNECTION: the InfiniBand connection was invalid
+ * @NM_DEVICE_INFINIBAND_ERROR_INVALID_DEVICE_MAC: the device's MAC was invalid
+ * @NM_DEVICE_INFINIBAND_ERROR_MAC_MISMATCH: the MACs of the connection and the device mismatched
+ */
+typedef enum {
+ NM_DEVICE_INFINIBAND_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/
+ NM_DEVICE_INFINIBAND_ERROR_NOT_INFINIBAND_CONNECTION, /*< nick=NotInfinibandConnection >*/
+ NM_DEVICE_INFINIBAND_ERROR_INVALID_INFINIBAND_CONNECTION, /*< nick=InvalidInfinibandConnection >*/
+ NM_DEVICE_INFINIBAND_ERROR_INVALID_DEVICE_MAC, /*< nick=InvalidDeviceMac >*/
+ NM_DEVICE_INFINIBAND_ERROR_MAC_MISMATCH, /*< nick=MacMismatch >*/
+} NMDeviceInfinibandError;
+
+#define NM_DEVICE_INFINIBAND_ERROR nm_device_infiniband_error_quark ()
+GQuark nm_device_infiniband_error_quark (void);
+
+#define NM_DEVICE_INFINIBAND_HW_ADDRESS "hw-address"
+#define NM_DEVICE_INFINIBAND_CARRIER "carrier"
+
+typedef struct {
+ NMDevice parent;
+} NMDeviceInfiniband;
+
+typedef struct {
+ NMDeviceClass parent;
+
+ /* Padding for future expansion */
+ void (*_reserved1) (void);
+ void (*_reserved2) (void);
+ void (*_reserved3) (void);
+ void (*_reserved4) (void);
+ void (*_reserved5) (void);
+ void (*_reserved6) (void);
+} NMDeviceInfinibandClass;
+
+GType nm_device_infiniband_get_type (void);
+
+GObject *nm_device_infiniband_new (DBusGConnection *connection, const char *path);
+
+const char * nm_device_infiniband_get_hw_address (NMDeviceInfiniband *device);
+gboolean nm_device_infiniband_get_carrier (NMDeviceInfiniband *device);
+
+G_END_DECLS
+
+#endif /* NM_DEVICE_INFINIBAND_H */
diff --git a/libnm/nm-device-modem.c b/libnm/nm-device-modem.c
new file mode 100644
index 0000000000..7d286190f3
--- /dev/null
+++ b/libnm/nm-device-modem.c
@@ -0,0 +1,291 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2011 - 2012 Red Hat, Inc.
+ * Copyright 2008 Novell, Inc.
+ */
+
+#include <config.h>
+#include <string.h>
+
+#include "nm-glib-compat.h"
+
+#include <nm-setting-connection.h>
+#include <nm-setting-gsm.h>
+#include <nm-setting-cdma.h>
+
+#include "nm-device-modem.h"
+#include "nm-device-private.h"
+#include "nm-object-private.h"
+
+G_DEFINE_TYPE (NMDeviceModem, nm_device_modem, NM_TYPE_DEVICE)
+
+#define NM_DEVICE_MODEM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_MODEM, NMDeviceModemPrivate))
+
+typedef struct {
+ DBusGProxy *proxy;
+
+ NMDeviceModemCapabilities caps;
+ NMDeviceModemCapabilities current_caps;
+} NMDeviceModemPrivate;
+
+enum {
+ PROP_0,
+ PROP_MODEM_CAPS,
+ PROP_CURRENT_CAPS,
+ LAST_PROP
+};
+
+/**
+ * nm_device_modem_error_quark:
+ *
+ * Registers an error quark for #NMDeviceModem if necessary.
+ *
+ * Returns: the error quark used for #NMDeviceModem errors.
+ **/
+GQuark
+nm_device_modem_error_quark (void)
+{
+ static GQuark quark = 0;
+
+ if (G_UNLIKELY (quark == 0))
+ quark = g_quark_from_static_string ("nm-device-modem-error-quark");
+ return quark;
+}
+
+/**
+ * nm_device_modem_get_modem_capabilities:
+ * @self: a #NMDeviceModem
+ *
+ * Returns a bitfield of the generic access technology families the modem
+ * supports. Not all capabilities are available concurrently however; some
+ * may require a firmware reload or reinitialization.
+ *
+ * Returns: the generic access technology families the modem supports
+ **/
+NMDeviceModemCapabilities
+nm_device_modem_get_modem_capabilities (NMDeviceModem *self)
+{
+ g_return_val_if_fail (NM_IS_DEVICE_MODEM (self), NM_DEVICE_MODEM_CAPABILITY_NONE);
+
+ _nm_object_ensure_inited (NM_OBJECT (self));
+ return NM_DEVICE_MODEM_GET_PRIVATE (self)->caps;
+}
+
+/**
+ * nm_device_modem_get_current_capabilities:
+ * @self: a #NMDeviceModem
+ *
+ * Returns a bitfield of the generic access technology families the modem
+ * supports without a firmware reload or reinitialization. This value
+ * represents the network types the modem can immediately connect to.
+ *
+ * Returns: the generic access technology families the modem supports without
+ * a firmware reload or other reinitialization
+ **/
+NMDeviceModemCapabilities
+nm_device_modem_get_current_capabilities (NMDeviceModem *self)
+{
+ g_return_val_if_fail (NM_IS_DEVICE_MODEM (self), NM_DEVICE_MODEM_CAPABILITY_NONE);
+
+ _nm_object_ensure_inited (NM_OBJECT (self));
+ return NM_DEVICE_MODEM_GET_PRIVATE (self)->current_caps;
+}
+
+static const char *
+get_type_description (NMDevice *device)
+{
+ NMDeviceModemCapabilities caps;
+
+ caps = nm_device_modem_get_current_capabilities (NM_DEVICE_MODEM (device));
+ if (caps & NM_DEVICE_MODEM_CAPABILITY_GSM_UMTS)
+ return "gsm";
+ else if (caps & NM_DEVICE_MODEM_CAPABILITY_CDMA_EVDO)
+ return "cdma";
+ else
+ return NULL;
+}
+
+#define MODEM_CAPS_3GPP(caps) (caps & (NM_DEVICE_MODEM_CAPABILITY_GSM_UMTS | \
+ NM_DEVICE_MODEM_CAPABILITY_LTE))
+
+#define MODEM_CAPS_3GPP2(caps) (caps & (NM_DEVICE_MODEM_CAPABILITY_CDMA_EVDO))
+
+static gboolean
+connection_compatible (NMDevice *device, NMConnection *connection, GError **error)
+{
+ NMSettingConnection *s_con;
+ NMSettingGsm *s_gsm;
+ NMSettingCdma *s_cdma;
+ const char *ctype;
+ NMDeviceModemCapabilities current_caps;
+
+ s_con = nm_connection_get_setting_connection (connection);
+ g_assert (s_con);
+
+ ctype = nm_setting_connection_get_connection_type (s_con);
+ if ( strcmp (ctype, NM_SETTING_GSM_SETTING_NAME) != 0
+ && strcmp (ctype, NM_SETTING_CDMA_SETTING_NAME) != 0) {
+ g_set_error (error, NM_DEVICE_MODEM_ERROR, NM_DEVICE_MODEM_ERROR_NOT_MODEM_CONNECTION,
+ "The connection was not a modem connection.");
+ return FALSE;
+ }
+
+ s_gsm = nm_connection_get_setting_gsm (connection);
+ s_cdma = nm_connection_get_setting_cdma (connection);
+ if (!s_cdma && !s_gsm) {
+ g_set_error (error, NM_DEVICE_MODEM_ERROR, NM_DEVICE_MODEM_ERROR_INVALID_MODEM_CONNECTION,
+ "The connection was not a valid modem connection.");
+ return FALSE;
+ }
+
+ current_caps = nm_device_modem_get_current_capabilities (NM_DEVICE_MODEM (device));
+ if (!(s_gsm && MODEM_CAPS_3GPP (current_caps)) && !(s_cdma && MODEM_CAPS_3GPP2 (current_caps))) {
+ g_set_error (error, NM_DEVICE_MODEM_ERROR, NM_DEVICE_MODEM_ERROR_MISSING_DEVICE_CAPS,
+ "The device missed capabilities required by the GSM/CDMA connection.");
+ return FALSE;
+ }
+
+ return NM_DEVICE_CLASS (nm_device_modem_parent_class)->connection_compatible (device, connection, error);
+}
+
+static GType
+get_setting_type (NMDevice *device)
+{
+ NMDeviceModemCapabilities caps;
+
+ caps = nm_device_modem_get_current_capabilities (NM_DEVICE_MODEM (device));
+ if (caps & (NM_DEVICE_MODEM_CAPABILITY_GSM_UMTS | NM_DEVICE_MODEM_CAPABILITY_LTE))
+ return NM_TYPE_SETTING_GSM;
+ else if (caps & NM_DEVICE_MODEM_CAPABILITY_CDMA_EVDO)
+ return NM_TYPE_SETTING_CDMA;
+ else
+ return G_TYPE_INVALID;
+}
+
+/*******************************************************************/
+
+static void
+nm_device_modem_init (NMDeviceModem *device)
+{
+ _nm_device_set_device_type (NM_DEVICE (device), NM_DEVICE_TYPE_MODEM);
+}
+
+static void
+register_properties (NMDeviceModem *device)
+{
+ NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (device);
+ const NMPropertiesInfo property_info[] = {
+ { NM_DEVICE_MODEM_MODEM_CAPABILITIES, &priv->caps },
+ { NM_DEVICE_MODEM_CURRENT_CAPABILITIES, &priv->current_caps },
+ { NULL },
+ };
+
+ _nm_object_register_properties (NM_OBJECT (device),
+ priv->proxy,
+ property_info);
+}
+
+static void
+constructed (GObject *object)
+{
+ NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (object);
+
+ G_OBJECT_CLASS (nm_device_modem_parent_class)->constructed (object);
+
+ priv->proxy = _nm_object_new_proxy (NM_OBJECT (object), NULL, NM_DBUS_INTERFACE_DEVICE_MODEM);
+ register_properties (NM_DEVICE_MODEM (object));
+}
+
+static void
+get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NMDeviceModem *self = NM_DEVICE_MODEM (object);
+
+ _nm_object_ensure_inited (NM_OBJECT (object));
+
+ switch (prop_id) {
+ case PROP_MODEM_CAPS:
+ g_value_set_uint (value, nm_device_modem_get_modem_capabilities (self));
+ break;
+ case PROP_CURRENT_CAPS:
+ g_value_set_uint (value, nm_device_modem_get_current_capabilities (self));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+dispose (GObject *object)
+{
+ NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (object);
+
+ g_clear_object (&priv->proxy);
+
+ G_OBJECT_CLASS (nm_device_modem_parent_class)->dispose (object);
+}
+
+static void
+nm_device_modem_class_init (NMDeviceModemClass *modem_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (modem_class);
+ NMDeviceClass *device_class = NM_DEVICE_CLASS (modem_class);
+
+ g_type_class_add_private (modem_class, sizeof (NMDeviceModemPrivate));
+
+ /* virtual methods */
+ object_class->constructed = constructed;
+ object_class->get_property = get_property;
+ object_class->dispose = dispose;
+
+ device_class->get_type_description = get_type_description;
+ device_class->connection_compatible = connection_compatible;
+ device_class->get_setting_type = get_setting_type;
+
+ /**
+ * NMDeviceModem:modem-capabilities:
+ *
+ * The generic family of access technologies the modem supports. Not all
+ * capabilities are available at the same time however; some modems require
+ * a firmware reload or other reinitialization to switch between eg
+ * CDMA/EVDO and GSM/UMTS.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_MODEM_CAPS,
+ g_param_spec_uint (NM_DEVICE_MODEM_MODEM_CAPABILITIES, "", "",
+ 0, G_MAXUINT32, 0,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMDeviceModem:current-capabilities:
+ *
+ * The generic family of access technologies the modem currently supports
+ * without a firmware reload or reinitialization.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_CURRENT_CAPS,
+ g_param_spec_uint (NM_DEVICE_MODEM_CURRENT_CAPABILITIES, "", "",
+ 0, G_MAXUINT32, 0,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/libnm/nm-device-modem.h b/libnm/nm-device-modem.h
new file mode 100644
index 0000000000..226cd04695
--- /dev/null
+++ b/libnm/nm-device-modem.h
@@ -0,0 +1,79 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2011 - 2012 Red Hat, Inc.
+ * Copyright 2008 Novell, Inc.
+ */
+
+#ifndef NM_DEVICE_MODEM_H
+#define NM_DEVICE_MODEM_H
+
+#include "nm-device.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_DEVICE_MODEM (nm_device_modem_get_type ())
+#define NM_DEVICE_MODEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_MODEM, NMDeviceModem))
+#define NM_DEVICE_MODEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_MODEM, NMDeviceModemClass))
+#define NM_IS_DEVICE_MODEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_MODEM))
+#define NM_IS_DEVICE_MODEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_MODEM))
+#define NM_DEVICE_MODEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_MODEM, NMDeviceModemClass))
+
+/**
+ * NMDeviceModemError:
+ * @NM_DEVICE_MODEM_ERROR_UNKNOWN: unknown or unclassified error
+ * @NM_DEVICE_MODEM_ERROR_NOT_MODEM_CONNECTION: the connection was not of modem type
+ * @NM_DEVICE_MODEM_ERROR_INVALID_MODEM_CONNECTION: the modem connection was invalid
+ * @NM_DEVICE_MODEM_ERROR_MISSING_DEVICE_CAPS: the device missed required capabilities
+ */
+typedef enum {
+ NM_DEVICE_MODEM_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/
+ NM_DEVICE_MODEM_ERROR_NOT_MODEM_CONNECTION, /*< nick=NotModemConnection >*/
+ NM_DEVICE_MODEM_ERROR_INVALID_MODEM_CONNECTION, /*< nick=InvalidModemConnection >*/
+ NM_DEVICE_MODEM_ERROR_MISSING_DEVICE_CAPS, /*< nick=MissingDeviceCaps >*/
+} NMDeviceModemError;
+
+#define NM_DEVICE_MODEM_ERROR nm_device_modem_error_quark ()
+GQuark nm_device_modem_error_quark (void);
+
+#define NM_DEVICE_MODEM_MODEM_CAPABILITIES "modem-capabilities"
+#define NM_DEVICE_MODEM_CURRENT_CAPABILITIES "current-capabilities"
+
+typedef struct {
+ NMDevice parent;
+} NMDeviceModem;
+
+typedef struct {
+ NMDeviceClass parent;
+
+ /* Padding for future expansion */
+ void (*_reserved1) (void);
+ void (*_reserved2) (void);
+ void (*_reserved3) (void);
+ void (*_reserved4) (void);
+ void (*_reserved5) (void);
+ void (*_reserved6) (void);
+} NMDeviceModemClass;
+
+GType nm_device_modem_get_type (void);
+
+NMDeviceModemCapabilities nm_device_modem_get_modem_capabilities (NMDeviceModem *self);
+NMDeviceModemCapabilities nm_device_modem_get_current_capabilities (NMDeviceModem *self);
+
+G_END_DECLS
+
+#endif /* NM_DEVICE_MODEM_H */
diff --git a/libnm/nm-device-olpc-mesh.c b/libnm/nm-device-olpc-mesh.c
new file mode 100644
index 0000000000..666ddc2636
--- /dev/null
+++ b/libnm/nm-device-olpc-mesh.c
@@ -0,0 +1,326 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2012 Red Hat, Inc.
+ */
+
+#include <config.h>
+#include <string.h>
+
+#include "nm-glib-compat.h"
+
+#include <nm-setting-connection.h>
+#include <nm-setting-olpc-mesh.h>
+
+#include "nm-device-olpc-mesh.h"
+#include "nm-device-private.h"
+#include "nm-object-private.h"
+#include "nm-device-wifi.h"
+
+G_DEFINE_TYPE (NMDeviceOlpcMesh, nm_device_olpc_mesh, NM_TYPE_DEVICE)
+
+#define NM_DEVICE_OLPC_MESH_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_OLPC_MESH, NMDeviceOlpcMeshPrivate))
+
+typedef struct {
+ DBusGProxy *proxy;
+
+ char *hw_address;
+ NMDeviceWifi *companion;
+ guint32 active_channel;
+} NMDeviceOlpcMeshPrivate;
+
+enum {
+ PROP_0,
+ PROP_HW_ADDRESS,
+ PROP_COMPANION,
+ PROP_ACTIVE_CHANNEL,
+
+ LAST_PROP
+};
+
+/**
+ * nm_device_olpc_mesh_error_quark:
+ *
+ * Registers an error quark for #NMDeviceOlpcMesh if necessary.
+ *
+ * Returns: the error quark used for #NMDeviceOlpcMesh errors.
+ **/
+GQuark
+nm_device_olpc_mesh_error_quark (void)
+{
+ static GQuark quark = 0;
+
+ if (G_UNLIKELY (quark == 0))
+ quark = g_quark_from_static_string ("nm-device-olpc-mesh-error-quark");
+ return quark;
+}
+
+/**
+ * nm_device_olpc_mesh_new:
+ * @connection: the #DBusGConnection
+ * @path: the DBus object path of the device
+ *
+ * Creates a new #NMDeviceOlpcMesh.
+ *
+ * Returns: (transfer full): a new OlpcMesh device
+ **/
+GObject *
+nm_device_olpc_mesh_new (DBusGConnection *connection, const char *path)
+{
+ GObject *device;
+
+ g_return_val_if_fail (connection != NULL, NULL);
+ g_return_val_if_fail (path != NULL, NULL);
+
+ device = g_object_new (NM_TYPE_DEVICE_OLPC_MESH,
+ NM_OBJECT_DBUS_CONNECTION, connection,
+ NM_OBJECT_DBUS_PATH, path,
+ NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return device;
+}
+
+/**
+ * nm_device_olpc_mesh_get_hw_address:
+ * @device: a #NMDeviceOlpcMesh
+ *
+ * Gets the hardware (MAC) address of the #NMDeviceOlpcMesh
+ *
+ * Returns: the hardware address. This is the internal string used by the
+ * device, and must not be modified.
+ **/
+const char *
+nm_device_olpc_mesh_get_hw_address (NMDeviceOlpcMesh *device)
+{
+ g_return_val_if_fail (NM_IS_DEVICE_OLPC_MESH (device), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return NM_DEVICE_OLPC_MESH_GET_PRIVATE (device)->hw_address;
+}
+
+/**
+ * nm_device_olpc_mesh_get_companion:
+ * @device: a #NMDeviceOlpcMesh
+ *
+ * Gets the companion device of the #NMDeviceOlpcMesh.
+ *
+ * Returns: (transfer none): the companion of the device of %NULL
+ **/
+NMDeviceWifi *
+nm_device_olpc_mesh_get_companion (NMDeviceOlpcMesh *device)
+{
+ g_return_val_if_fail (NM_IS_DEVICE_OLPC_MESH (device), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return NM_DEVICE_OLPC_MESH_GET_PRIVATE (device)->companion;
+}
+
+/**
+ * nm_device_olpc_mesh_get_active_channel:
+ * @device: a #NMDeviceOlpcMesh
+ *
+ * Returns the active channel of the #NMDeviceOlpcMesh device.
+ *
+ * Returns: active channel of the device
+ **/
+guint32
+nm_device_olpc_mesh_get_active_channel (NMDeviceOlpcMesh *device)
+{
+ g_return_val_if_fail (NM_IS_DEVICE_OLPC_MESH (device), 0);
+
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return NM_DEVICE_OLPC_MESH_GET_PRIVATE (device)->active_channel;
+}
+
+static const char *
+get_hw_address (NMDevice *device)
+{
+ return nm_device_olpc_mesh_get_hw_address (NM_DEVICE_OLPC_MESH (device));
+}
+
+static gboolean
+connection_compatible (NMDevice *device, NMConnection *connection, GError **error)
+{
+ NMSettingConnection *s_con;
+ NMSettingOlpcMesh *s_olpc_mesh;
+ const char *ctype;
+
+ s_con = nm_connection_get_setting_connection (connection);
+ g_assert (s_con);
+
+ ctype = nm_setting_connection_get_connection_type (s_con);
+ if (strcmp (ctype, NM_SETTING_OLPC_MESH_SETTING_NAME) != 0) {
+ g_set_error (error, NM_DEVICE_OLPC_MESH_ERROR, NM_DEVICE_OLPC_MESH_ERROR_NOT_OLPC_MESH_CONNECTION,
+ "The connection was not a Olpc Mesh connection.");
+ return FALSE;
+ }
+
+ s_olpc_mesh = nm_connection_get_setting_olpc_mesh (connection);
+ if (!s_olpc_mesh) {
+ g_set_error (error, NM_DEVICE_OLPC_MESH_ERROR, NM_DEVICE_OLPC_MESH_ERROR_INVALID_OLPC_MESH_CONNECTION,
+ "The connection was not a valid Olpc Mesh connection.");
+ return FALSE;
+ }
+
+ return NM_DEVICE_CLASS (nm_device_olpc_mesh_parent_class)->connection_compatible (device, connection, error);
+}
+
+static GType
+get_setting_type (NMDevice *device)
+{
+ return NM_TYPE_SETTING_OLPC_MESH;
+}
+
+/**************************************************************/
+
+static void
+nm_device_olpc_mesh_init (NMDeviceOlpcMesh *device)
+{
+ _nm_device_set_device_type (NM_DEVICE (device), NM_DEVICE_TYPE_OLPC_MESH);
+}
+
+static void
+register_properties (NMDeviceOlpcMesh *device)
+{
+ NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (device);
+ const NMPropertiesInfo property_info[] = {
+ { NM_DEVICE_OLPC_MESH_HW_ADDRESS, &priv->hw_address },
+ { NM_DEVICE_OLPC_MESH_COMPANION, &priv->companion, NULL, NM_TYPE_DEVICE_WIFI },
+ { NM_DEVICE_OLPC_MESH_ACTIVE_CHANNEL, &priv->active_channel },
+ { NULL },
+ };
+
+ _nm_object_register_properties (NM_OBJECT (device),
+ priv->proxy,
+ property_info);
+}
+
+static void
+constructed (GObject *object)
+{
+ NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (object);
+
+ G_OBJECT_CLASS (nm_device_olpc_mesh_parent_class)->constructed (object);
+
+ priv->proxy = _nm_object_new_proxy (NM_OBJECT (object), NULL, NM_DBUS_INTERFACE_DEVICE_OLPC_MESH);
+ register_properties (NM_DEVICE_OLPC_MESH (object));
+}
+
+static void
+dispose (GObject *object)
+{
+ NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (object);
+
+ g_clear_object (&priv->companion);
+ g_clear_object (&priv->proxy);
+
+ G_OBJECT_CLASS (nm_device_olpc_mesh_parent_class)->dispose (object);
+}
+
+static void
+finalize (GObject *object)
+{
+ NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (object);
+
+ g_free (priv->hw_address);
+
+ G_OBJECT_CLASS (nm_device_olpc_mesh_parent_class)->finalize (object);
+}
+
+static void
+get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NMDeviceOlpcMesh *device = NM_DEVICE_OLPC_MESH (object);
+
+ _nm_object_ensure_inited (NM_OBJECT (object));
+
+ switch (prop_id) {
+ case PROP_HW_ADDRESS:
+ g_value_set_string (value, nm_device_olpc_mesh_get_hw_address (device));
+ break;
+ case PROP_COMPANION:
+ g_value_set_object (value, nm_device_olpc_mesh_get_companion (device));
+ break;
+ case PROP_ACTIVE_CHANNEL:
+ g_value_set_uint (value, nm_device_olpc_mesh_get_active_channel (device));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nm_device_olpc_mesh_class_init (NMDeviceOlpcMeshClass *olpc_mesh_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (olpc_mesh_class);
+ NMDeviceClass *device_class = NM_DEVICE_CLASS (olpc_mesh_class);
+
+ g_type_class_add_private (olpc_mesh_class, sizeof (NMDeviceOlpcMeshPrivate));
+
+ /* virtual methods */
+ object_class->constructed = constructed;
+ object_class->dispose = dispose;
+ object_class->finalize = finalize;
+ object_class->get_property = get_property;
+ device_class->connection_compatible = connection_compatible;
+ device_class->get_setting_type = get_setting_type;
+ device_class->get_hw_address = get_hw_address;
+
+ /* properties */
+
+ /**
+ * NMDeviceOlpcMesh:hw-address:
+ *
+ * The hardware (MAC) address of the device.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_HW_ADDRESS,
+ g_param_spec_string (NM_DEVICE_OLPC_MESH_HW_ADDRESS, "", "",
+ NULL,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMDeviceOlpcMesh:companion:
+ *
+ * The companion device.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_COMPANION,
+ g_param_spec_object (NM_DEVICE_OLPC_MESH_COMPANION, "", "",
+ NM_TYPE_DEVICE_WIFI,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMDeviceOlpcMesh:active-channel:
+ *
+ * The device's active channel.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_ACTIVE_CHANNEL,
+ g_param_spec_uint (NM_DEVICE_OLPC_MESH_ACTIVE_CHANNEL, "", "",
+ 0, G_MAXUINT32, 0,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+}
diff --git a/libnm/nm-device-olpc-mesh.h b/libnm/nm-device-olpc-mesh.h
new file mode 100644
index 0000000000..fcef83bcd1
--- /dev/null
+++ b/libnm/nm-device-olpc-mesh.h
@@ -0,0 +1,81 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2012 Red Hat, Inc.
+ */
+
+#ifndef NM_DEVICE_OLPC_MESH_H
+#define NM_DEVICE_OLPC_MESH_H
+
+#include "nm-device.h"
+#include "nm-device-wifi.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_DEVICE_OLPC_MESH (nm_device_olpc_mesh_get_type ())
+#define NM_DEVICE_OLPC_MESH(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_OLPC_MESH, NMDeviceOlpcMesh))
+#define NM_DEVICE_OLPC_MESH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_OLPC_MESH, NMDeviceOlpcMeshClass))
+#define NM_IS_DEVICE_OLPC_MESH(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_OLPC_MESH))
+#define NM_IS_DEVICE_OLPC_MESH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_OLPC_MESH))
+#define NM_DEVICE_OLPC_MESH_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_OLPC_MESH, NMDeviceOlpcMeshClass))
+
+/**
+ * NMDeviceOlpcMeshError:
+ * @NM_DEVICE_OLPC_MESH_ERROR_UNKNOWN: unknown or unclassified error
+ * @NM_DEVICE_OLPC_MESH_ERROR_NOT_OLPC_MESH_CONNECTION: the connection was not of Olpc Mesh type
+ * @NM_DEVICE_OLPC_MESH_ERROR_INVALID_OLPC_MESH_CONNECTION: the Olpc Mesh connection was invalid
+ */
+typedef enum {
+ NM_DEVICE_OLPC_MESH_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/
+ NM_DEVICE_OLPC_MESH_ERROR_NOT_OLPC_MESH_CONNECTION, /*< nick=NotOlpcMeshConnection >*/
+ NM_DEVICE_OLPC_MESH_ERROR_INVALID_OLPC_MESH_CONNECTION, /*< nick=InvalidOlpcMeshConnection >*/
+} NMDeviceOlpcMeshError;
+
+#define NM_DEVICE_OLPC_MESH_ERROR nm_device_olpc_mesh_error_quark ()
+GQuark nm_device_olpc_mesh_error_quark (void);
+
+#define NM_DEVICE_OLPC_MESH_HW_ADDRESS "hw-address"
+#define NM_DEVICE_OLPC_MESH_COMPANION "companion"
+#define NM_DEVICE_OLPC_MESH_ACTIVE_CHANNEL "active-channel"
+
+typedef struct {
+ NMDevice parent;
+} NMDeviceOlpcMesh;
+
+typedef struct {
+ NMDeviceClass parent;
+
+ /* Padding for future expansion */
+ void (*_reserved1) (void);
+ void (*_reserved2) (void);
+ void (*_reserved3) (void);
+ void (*_reserved4) (void);
+ void (*_reserved5) (void);
+ void (*_reserved6) (void);
+} NMDeviceOlpcMeshClass;
+
+GType nm_device_olpc_mesh_get_type (void);
+
+GObject *nm_device_olpc_mesh_new (DBusGConnection *connection, const char *path);
+
+const char *nm_device_olpc_mesh_get_hw_address (NMDeviceOlpcMesh *device);
+NMDeviceWifi *nm_device_olpc_mesh_get_companion (NMDeviceOlpcMesh *device);
+guint32 nm_device_olpc_mesh_get_active_channel (NMDeviceOlpcMesh *device);
+
+G_END_DECLS
+
+#endif /* NM_DEVICE_OLPC_MESH_H */
diff --git a/libnm/nm-device-private.h b/libnm/nm-device-private.h
new file mode 100644
index 0000000000..82d676f92e
--- /dev/null
+++ b/libnm/nm-device-private.h
@@ -0,0 +1,26 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2012 Red Hat, Inc.
+ */
+
+#ifndef NM_DEVICE_PRIVATE_H
+#define NM_DEVICE_PRIVATE_H
+
+void _nm_device_set_device_type (NMDevice *device, NMDeviceType dtype);
+
+#endif /* NM_DEVICE_PRIVATE_H */
diff --git a/libnm/nm-device-team.c b/libnm/nm-device-team.c
new file mode 100644
index 0000000000..c9ac2d8b85
--- /dev/null
+++ b/libnm/nm-device-team.c
@@ -0,0 +1,353 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2013 Jiri Pirko <jiri@resnulli.us>
+ */
+
+#include <config.h>
+#include <string.h>
+#include <netinet/ether.h>
+
+#include "nm-glib-compat.h"
+
+#include <nm-setting-connection.h>
+#include <nm-setting-team.h>
+#include <nm-utils.h>
+
+#include "nm-device-team.h"
+#include "nm-device-private.h"
+#include "nm-object-private.h"
+#include "nm-types.h"
+
+G_DEFINE_TYPE (NMDeviceTeam, nm_device_team, NM_TYPE_DEVICE)
+
+#define NM_DEVICE_TEAM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_TEAM, NMDeviceTeamPrivate))
+
+typedef struct {
+ DBusGProxy *proxy;
+
+ char *hw_address;
+ gboolean carrier;
+ GPtrArray *slaves;
+} NMDeviceTeamPrivate;
+
+enum {
+ PROP_0,
+ PROP_HW_ADDRESS,
+ PROP_CARRIER,
+ PROP_SLAVES,
+
+ LAST_PROP
+};
+
+/**
+ * nm_device_team_error_quark:
+ *
+ * Registers an error quark for #NMDeviceTeam if necessary.
+ *
+ * Returns: the error quark used for #NMDeviceTeam errors.
+ *
+ * Since: 0.9.10
+ **/
+GQuark
+nm_device_team_error_quark (void)
+{
+ static GQuark quark = 0;
+
+ if (G_UNLIKELY (quark == 0))
+ quark = g_quark_from_static_string ("nm-device-team-error-quark");
+ return quark;
+}
+
+/**
+ * nm_device_team_new:
+ * @connection: the #DBusGConnection
+ * @path: the DBus object path of the device
+ *
+ * Creates a new #NMDeviceTeam.
+ *
+ * Returns: (transfer full): a new device
+ *
+ * Since: 0.9.10
+ **/
+GObject *
+nm_device_team_new (DBusGConnection *connection, const char *path)
+{
+ GObject *device;
+
+ g_return_val_if_fail (connection != NULL, NULL);
+ g_return_val_if_fail (path != NULL, NULL);
+
+ device = g_object_new (NM_TYPE_DEVICE_TEAM,
+ NM_OBJECT_DBUS_CONNECTION, connection,
+ NM_OBJECT_DBUS_PATH, path,
+ NULL);
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return device;
+}
+
+/**
+ * nm_device_team_get_hw_address:
+ * @device: a #NMDeviceTeam
+ *
+ * Gets the hardware (MAC) address of the #NMDeviceTeam
+ *
+ * Returns: the hardware address. This is the internal string used by the
+ * device, and must not be modified.
+ *
+ * Since: 0.9.10
+ **/
+const char *
+nm_device_team_get_hw_address (NMDeviceTeam *device)
+{
+ g_return_val_if_fail (NM_IS_DEVICE_TEAM (device), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return NM_DEVICE_TEAM_GET_PRIVATE (device)->hw_address;
+}
+
+/**
+ * nm_device_team_get_carrier:
+ * @device: a #NMDeviceTeam
+ *
+ * Whether the device has carrier.
+ *
+ * Returns: %TRUE if the device has carrier
+ *
+ * Since: 0.9.10
+ **/
+gboolean
+nm_device_team_get_carrier (NMDeviceTeam *device)
+{
+ g_return_val_if_fail (NM_IS_DEVICE_TEAM (device), FALSE);
+
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return NM_DEVICE_TEAM_GET_PRIVATE (device)->carrier;
+}
+
+/**
+ * nm_device_team_get_slaves:
+ * @device: a #NMDeviceTeam
+ *
+ * Gets the devices currently enslaved to @device.
+ *
+ * Returns: (element-type NMDevice): the #GPtrArray containing
+ * #NMDevices that are slaves of @device. This is the internal
+ * copy used by the device, and must not be modified.
+ *
+ * Since: 0.9.10
+ **/
+const GPtrArray *
+nm_device_team_get_slaves (NMDeviceTeam *device)
+{
+ g_return_val_if_fail (NM_IS_DEVICE_TEAM (device), FALSE);
+
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return handle_ptr_array_return (NM_DEVICE_TEAM_GET_PRIVATE (device)->slaves);
+}
+
+static const char *
+get_hw_address (NMDevice *device)
+{
+ return nm_device_team_get_hw_address (NM_DEVICE_TEAM (device));
+}
+
+static gboolean
+connection_compatible (NMDevice *device, NMConnection *connection, GError **error)
+{
+ NMSettingConnection *s_con;
+ NMSettingTeam *s_team;
+ const char *ctype, *dev_iface_name, *team_iface_name;
+
+ s_con = nm_connection_get_setting_connection (connection);
+ g_assert (s_con);
+
+ ctype = nm_setting_connection_get_connection_type (s_con);
+ if (strcmp (ctype, NM_SETTING_TEAM_SETTING_NAME) != 0) {
+ g_set_error (error, NM_DEVICE_TEAM_ERROR, NM_DEVICE_TEAM_ERROR_NOT_TEAM_CONNECTION,
+ "The connection was not a team connection.");
+ return FALSE;
+ }
+
+ s_team = nm_connection_get_setting_team (connection);
+ if (!s_team) {
+ g_set_error (error, NM_DEVICE_TEAM_ERROR, NM_DEVICE_TEAM_ERROR_INVALID_TEAM_CONNECTION,
+ "The connection was not a valid team connection.");
+ return FALSE;
+ }
+
+ dev_iface_name = nm_device_get_iface (device);
+ team_iface_name = nm_setting_team_get_interface_name (s_team);
+ if (g_strcmp0 (dev_iface_name, team_iface_name) != 0) {
+ g_set_error (error, NM_DEVICE_TEAM_ERROR, NM_DEVICE_TEAM_ERROR_INTERFACE_MISMATCH,
+ "The interfaces of the device and the connection didn't match.");
+ return FALSE;
+ }
+
+ /* FIXME: check slaves? */
+
+ return NM_DEVICE_CLASS (nm_device_team_parent_class)->connection_compatible (device, connection, error);
+}
+
+static GType
+get_setting_type (NMDevice *device)
+{
+ return NM_TYPE_SETTING_TEAM;
+}
+
+/***********************************************************/
+
+static void
+nm_device_team_init (NMDeviceTeam *device)
+{
+ _nm_device_set_device_type (NM_DEVICE (device), NM_DEVICE_TYPE_TEAM);
+}
+
+static void
+register_properties (NMDeviceTeam *device)
+{
+ NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (device);
+ const NMPropertiesInfo property_info[] = {
+ { NM_DEVICE_TEAM_HW_ADDRESS, &priv->hw_address },
+ { NM_DEVICE_TEAM_CARRIER, &priv->carrier },
+ { NM_DEVICE_TEAM_SLAVES, &priv->slaves, NULL, NM_TYPE_DEVICE },
+ { NULL },
+ };
+
+ _nm_object_register_properties (NM_OBJECT (device),
+ priv->proxy,
+ property_info);
+}
+
+static void
+constructed (GObject *object)
+{
+ NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (object);
+
+ G_OBJECT_CLASS (nm_device_team_parent_class)->constructed (object);
+
+ priv->proxy = _nm_object_new_proxy (NM_OBJECT (object), NULL, NM_DBUS_INTERFACE_DEVICE_TEAM);
+ register_properties (NM_DEVICE_TEAM (object));
+}
+
+static void
+dispose (GObject *object)
+{
+ NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (object);
+
+ g_clear_object (&priv->proxy);
+
+ if (priv->slaves) {
+ g_ptr_array_set_free_func (priv->slaves, g_object_unref);
+ g_ptr_array_free (priv->slaves, TRUE);
+ priv->slaves = NULL;
+ }
+
+ G_OBJECT_CLASS (nm_device_team_parent_class)->dispose (object);
+}
+
+static void
+finalize (GObject *object)
+{
+ NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (object);
+
+ g_free (priv->hw_address);
+
+ G_OBJECT_CLASS (nm_device_team_parent_class)->finalize (object);
+}
+
+static void
+get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NMDeviceTeam *device = NM_DEVICE_TEAM (object);
+
+ _nm_object_ensure_inited (NM_OBJECT (object));
+
+ switch (prop_id) {
+ case PROP_HW_ADDRESS:
+ g_value_set_string (value, nm_device_team_get_hw_address (device));
+ break;
+ case PROP_CARRIER:
+ g_value_set_boolean (value, nm_device_team_get_carrier (device));
+ break;
+ case PROP_SLAVES:
+ g_value_set_boxed (value, nm_device_team_get_slaves (device));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nm_device_team_class_init (NMDeviceTeamClass *eth_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (eth_class);
+ NMDeviceClass *device_class = NM_DEVICE_CLASS (eth_class);
+
+ g_type_class_add_private (eth_class, sizeof (NMDeviceTeamPrivate));
+
+ /* virtual methods */
+ object_class->constructed = constructed;
+ object_class->dispose = dispose;
+ object_class->finalize = finalize;
+ object_class->get_property = get_property;
+ device_class->connection_compatible = connection_compatible;
+ device_class->get_setting_type = get_setting_type;
+ device_class->get_hw_address = get_hw_address;
+
+ /* properties */
+
+ /**
+ * NMDeviceTeam:hw-address:
+ *
+ * The hardware (MAC) address of the device.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_HW_ADDRESS,
+ g_param_spec_string (NM_DEVICE_TEAM_HW_ADDRESS, "", "",
+ NULL,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMDeviceTeam:carrier:
+ *
+ * Whether the device has carrier.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_CARRIER,
+ g_param_spec_boolean (NM_DEVICE_TEAM_CARRIER, "", "",
+ FALSE,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMDeviceTeam:slaves:
+ *
+ * The devices (#NMDevice) enslaved to the team device.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_SLAVES,
+ g_param_spec_boxed (NM_DEVICE_TEAM_SLAVES, "", "",
+ NM_TYPE_OBJECT_ARRAY,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/libnm/nm-device-team.h b/libnm/nm-device-team.h
new file mode 100644
index 0000000000..46e910bad1
--- /dev/null
+++ b/libnm/nm-device-team.h
@@ -0,0 +1,85 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2013 Jiri Pirko <jiri@resnulli.us>
+ */
+
+#ifndef NM_DEVICE_TEAM_H
+#define NM_DEVICE_TEAM_H
+
+#include "nm-device.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_DEVICE_TEAM (nm_device_team_get_type ())
+#define NM_DEVICE_TEAM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_TEAM, NMDeviceTeam))
+#define NM_DEVICE_TEAM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_TEAM, NMDeviceTeamClass))
+#define NM_IS_DEVICE_TEAM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_TEAM))
+#define NM_IS_DEVICE_TEAM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_TEAM))
+#define NM_DEVICE_TEAM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_TEAM, NMDeviceTeamClass))
+
+/**
+ * NMDeviceTeamError:
+ * @NM_DEVICE_TEAM_ERROR_UNKNOWN: unknown or unclassified error
+ * @NM_DEVICE_TEAM_ERROR_NOT_TEAM_CONNECTION: the connection was not of team type
+ * @NM_DEVICE_TEAM_ERROR_INVALID_TEAM_CONNECTION: the team connection was invalid
+ * @NM_DEVICE_TEAM_ERROR_INTERFACE_MISMATCH: the interfaces of the connection and the device mismatched
+ */
+typedef enum {
+ NM_DEVICE_TEAM_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/
+ NM_DEVICE_TEAM_ERROR_NOT_TEAM_CONNECTION, /*< nick=NotTeamConnection >*/
+ NM_DEVICE_TEAM_ERROR_INVALID_TEAM_CONNECTION, /*< nick=InvalidTeamConnection >*/
+ NM_DEVICE_TEAM_ERROR_INTERFACE_MISMATCH, /*< nick=InterfaceMismatch >*/
+} NMDeviceTeamError;
+
+#define NM_DEVICE_TEAM_ERROR nm_device_team_error_quark ()
+NM_AVAILABLE_IN_0_9_10
+GQuark nm_device_team_error_quark (void);
+
+#define NM_DEVICE_TEAM_HW_ADDRESS "hw-address"
+#define NM_DEVICE_TEAM_CARRIER "carrier"
+#define NM_DEVICE_TEAM_SLAVES "slaves"
+
+typedef struct {
+ NMDevice parent;
+} NMDeviceTeam;
+
+typedef struct {
+ NMDeviceClass parent;
+
+ /* Padding for future expansion */
+ void (*_reserved1) (void);
+ void (*_reserved2) (void);
+ void (*_reserved3) (void);
+ void (*_reserved4) (void);
+ void (*_reserved5) (void);
+ void (*_reserved6) (void);
+} NMDeviceTeamClass;
+
+NM_AVAILABLE_IN_0_9_10
+GType nm_device_team_get_type (void);
+
+NM_AVAILABLE_IN_0_9_10
+GObject *nm_device_team_new (DBusGConnection *connection, const char *path);
+
+const char *nm_device_team_get_hw_address (NMDeviceTeam *device);
+gboolean nm_device_team_get_carrier (NMDeviceTeam *device);
+const GPtrArray *nm_device_team_get_slaves (NMDeviceTeam *device);
+
+G_END_DECLS
+
+#endif /* NM_DEVICE_TEAM_H */
diff --git a/libnm/nm-device-vlan.c b/libnm/nm-device-vlan.c
new file mode 100644
index 0000000000..8df3025ebb
--- /dev/null
+++ b/libnm/nm-device-vlan.c
@@ -0,0 +1,353 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2012 Red Hat, Inc.
+ */
+
+#include <config.h>
+#include <string.h>
+#include <netinet/ether.h>
+
+#include "nm-glib-compat.h"
+
+#include <nm-setting-connection.h>
+#include <nm-setting-vlan.h>
+#include <nm-utils.h>
+
+#include "nm-device-vlan.h"
+#include "nm-device-private.h"
+#include "nm-object-private.h"
+
+G_DEFINE_TYPE (NMDeviceVlan, nm_device_vlan, NM_TYPE_DEVICE)
+
+#define NM_DEVICE_VLAN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_VLAN, NMDeviceVlanPrivate))
+
+typedef struct {
+ DBusGProxy *proxy;
+
+ char *hw_address;
+ gboolean carrier;
+ guint vlan_id;
+} NMDeviceVlanPrivate;
+
+enum {
+ PROP_0,
+ PROP_HW_ADDRESS,
+ PROP_CARRIER,
+ PROP_VLAN_ID,
+
+ LAST_PROP
+};
+
+/**
+ * nm_device_vlan_error_quark:
+ *
+ * Registers an error quark for #NMDeviceVlan if necessary.
+ *
+ * Returns: the error quark used for #NMDeviceVlan errors.
+ **/
+GQuark
+nm_device_vlan_error_quark (void)
+{
+ static GQuark quark = 0;
+
+ if (G_UNLIKELY (quark == 0))
+ quark = g_quark_from_static_string ("nm-device-vlan-error-quark");
+ return quark;
+}
+
+/**
+ * nm_device_vlan_new:
+ * @connection: the #DBusGConnection
+ * @path: the DBus object path of the device
+ *
+ * Creates a new #NMDeviceVlan.
+ *
+ * Returns: (transfer full): a new device
+ **/
+GObject *
+nm_device_vlan_new (DBusGConnection *connection, const char *path)
+{
+ GObject *device;
+
+ g_return_val_if_fail (connection != NULL, NULL);
+ g_return_val_if_fail (path != NULL, NULL);
+
+ device = g_object_new (NM_TYPE_DEVICE_VLAN,
+ NM_OBJECT_DBUS_CONNECTION, connection,
+ NM_OBJECT_DBUS_PATH, path,
+ NULL);
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return device;
+}
+
+/**
+ * nm_device_vlan_get_hw_address:
+ * @device: a #NMDeviceVlan
+ *
+ * Gets the hardware (MAC) address of the #NMDeviceVlan
+ *
+ * Returns: the hardware address. This is the internal string used by the
+ * device, and must not be modified.
+ **/
+const char *
+nm_device_vlan_get_hw_address (NMDeviceVlan *device)
+{
+ g_return_val_if_fail (NM_IS_DEVICE_VLAN (device), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return NM_DEVICE_VLAN_GET_PRIVATE (device)->hw_address;
+}
+
+/**
+ * nm_device_vlan_get_carrier:
+ * @device: a #NMDeviceVlan
+ *
+ * Whether the device has carrier.
+ *
+ * Returns: %TRUE if the device has carrier
+ **/
+gboolean
+nm_device_vlan_get_carrier (NMDeviceVlan *device)
+{
+ g_return_val_if_fail (NM_IS_DEVICE_VLAN (device), FALSE);
+
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return NM_DEVICE_VLAN_GET_PRIVATE (device)->carrier;
+}
+
+/**
+ * nm_device_vlan_get_vlan_id:
+ * @device: a #NMDeviceVlan
+ *
+ * Returns: the device's VLAN ID
+ **/
+guint
+nm_device_vlan_get_vlan_id (NMDeviceVlan *device)
+{
+ g_return_val_if_fail (NM_IS_DEVICE_VLAN (device), FALSE);
+
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return NM_DEVICE_VLAN_GET_PRIVATE (device)->vlan_id;
+}
+
+static gboolean
+connection_compatible (NMDevice *device, NMConnection *connection, GError **error)
+{
+ NMSettingConnection *s_con;
+ NMSettingVlan *s_vlan;
+ NMSettingWired *s_wired;
+ const char *ctype, *dev_iface_name, *vlan_iface_name;
+ const GByteArray *mac_address;
+ char *mac_address_str;
+
+ s_con = nm_connection_get_setting_connection (connection);
+ g_assert (s_con);
+
+ ctype = nm_setting_connection_get_connection_type (s_con);
+ if (strcmp (ctype, NM_SETTING_VLAN_SETTING_NAME) != 0) {
+ g_set_error (error, NM_DEVICE_VLAN_ERROR, NM_DEVICE_VLAN_ERROR_NOT_VLAN_CONNECTION,
+ "The connection was not a VLAN connection.");
+ return FALSE;
+ }
+
+ s_vlan = nm_connection_get_setting_vlan (connection);
+ if (!s_vlan) {
+ g_set_error (error, NM_DEVICE_VLAN_ERROR, NM_DEVICE_VLAN_ERROR_INVALID_VLAN_CONNECTION,
+ "The connection was not a valid VLAN connection.");
+ return FALSE;
+ }
+
+ if (nm_setting_vlan_get_id (s_vlan) != nm_device_vlan_get_vlan_id (NM_DEVICE_VLAN (device))) {
+ g_set_error (error, NM_DEVICE_VLAN_ERROR, NM_DEVICE_VLAN_ERROR_ID_MISMATCH,
+ "The VLAN identifiers of the device and the connection didn't match.");
+ return FALSE;
+ }
+
+ dev_iface_name = nm_device_get_iface (device);
+ vlan_iface_name = nm_setting_vlan_get_interface_name (s_vlan);
+ if (vlan_iface_name && g_strcmp0 (dev_iface_name, vlan_iface_name) != 0) {
+ g_set_error (error, NM_DEVICE_VLAN_ERROR, NM_DEVICE_VLAN_ERROR_INTERFACE_MISMATCH,
+ "The interfaces of the device and the connection didn't match.");
+ return FALSE;
+ }
+
+ s_wired = nm_connection_get_setting_wired (connection);
+ if (s_wired)
+ mac_address = nm_setting_wired_get_mac_address (s_wired);
+ else
+ mac_address = NULL;
+ if (mac_address) {
+ mac_address_str = nm_utils_hwaddr_ntoa_len (mac_address->data, mac_address->len);
+ if (!g_strcmp0 (mac_address_str, NM_DEVICE_VLAN_GET_PRIVATE (device)->hw_address)) {
+ g_set_error (error, NM_DEVICE_VLAN_ERROR, NM_DEVICE_VLAN_ERROR_MAC_MISMATCH,
+ "The hardware address of the device and the connection didn't match.");
+ }
+ g_free (mac_address_str);
+ }
+
+ return NM_DEVICE_CLASS (nm_device_vlan_parent_class)->connection_compatible (device, connection, error);
+}
+
+static GType
+get_setting_type (NMDevice *device)
+{
+ return NM_TYPE_SETTING_VLAN;
+}
+
+static const char *
+get_hw_address (NMDevice *device)
+{
+ return nm_device_vlan_get_hw_address (NM_DEVICE_VLAN (device));
+}
+
+/***********************************************************/
+
+static void
+nm_device_vlan_init (NMDeviceVlan *device)
+{
+ _nm_device_set_device_type (NM_DEVICE (device), NM_DEVICE_TYPE_VLAN);
+}
+
+static void
+register_properties (NMDeviceVlan *device)
+{
+ NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (device);
+ const NMPropertiesInfo property_info[] = {
+ { NM_DEVICE_VLAN_HW_ADDRESS, &priv->hw_address },
+ { NM_DEVICE_VLAN_CARRIER, &priv->carrier },
+ { NM_DEVICE_VLAN_VLAN_ID, &priv->vlan_id },
+ { NULL },
+ };
+
+ _nm_object_register_properties (NM_OBJECT (device),
+ priv->proxy,
+ property_info);
+}
+
+static void
+constructed (GObject *object)
+{
+ NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (object);
+
+ G_OBJECT_CLASS (nm_device_vlan_parent_class)->constructed (object);
+
+ priv->proxy = _nm_object_new_proxy (NM_OBJECT (object), NULL, NM_DBUS_INTERFACE_DEVICE_VLAN);
+ register_properties (NM_DEVICE_VLAN (object));
+}
+
+static void
+dispose (GObject *object)
+{
+ NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (object);
+
+ g_clear_object (&priv->proxy);
+
+ G_OBJECT_CLASS (nm_device_vlan_parent_class)->dispose (object);
+}
+
+static void
+finalize (GObject *object)
+{
+ NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (object);
+
+ g_free (priv->hw_address);
+
+ G_OBJECT_CLASS (nm_device_vlan_parent_class)->finalize (object);
+}
+
+static void
+get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NMDeviceVlan *device = NM_DEVICE_VLAN (object);
+
+ _nm_object_ensure_inited (NM_OBJECT (object));
+
+ switch (prop_id) {
+ case PROP_HW_ADDRESS:
+ g_value_set_string (value, nm_device_vlan_get_hw_address (device));
+ break;
+ case PROP_CARRIER:
+ g_value_set_boolean (value, nm_device_vlan_get_carrier (device));
+ break;
+ case PROP_VLAN_ID:
+ g_value_set_uint (value, nm_device_vlan_get_vlan_id (device));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nm_device_vlan_class_init (NMDeviceVlanClass *eth_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (eth_class);
+ NMDeviceClass *device_class = NM_DEVICE_CLASS (eth_class);
+
+ g_type_class_add_private (eth_class, sizeof (NMDeviceVlanPrivate));
+
+ /* virtual methods */
+ object_class->constructed = constructed;
+ object_class->dispose = dispose;
+ object_class->finalize = finalize;
+ object_class->get_property = get_property;
+ device_class->connection_compatible = connection_compatible;
+ device_class->get_setting_type = get_setting_type;
+ device_class->get_hw_address = get_hw_address;
+
+ /* properties */
+
+ /**
+ * NMDeviceVlan:hw-address:
+ *
+ * The hardware (MAC) address of the device.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_HW_ADDRESS,
+ g_param_spec_string (NM_DEVICE_VLAN_HW_ADDRESS, "", "",
+ NULL,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMDeviceVlan:carrier:
+ *
+ * Whether the device has carrier.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_CARRIER,
+ g_param_spec_boolean (NM_DEVICE_VLAN_CARRIER, "", "",
+ FALSE,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMDeviceVlan:vlan-id:
+ *
+ * The device's VLAN ID.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_VLAN_ID,
+ g_param_spec_uint (NM_DEVICE_VLAN_VLAN_ID, "", "",
+ 0, 4095, 0,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/libnm/nm-device-vlan.h b/libnm/nm-device-vlan.h
new file mode 100644
index 0000000000..511d02215b
--- /dev/null
+++ b/libnm/nm-device-vlan.h
@@ -0,0 +1,86 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2012 Red Hat, Inc.
+ */
+
+#ifndef NM_DEVICE_VLAN_H
+#define NM_DEVICE_VLAN_H
+
+#include "nm-device.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_DEVICE_VLAN (nm_device_vlan_get_type ())
+#define NM_DEVICE_VLAN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_VLAN, NMDeviceVlan))
+#define NM_DEVICE_VLAN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_VLAN, NMDeviceVlanClass))
+#define NM_IS_DEVICE_VLAN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_VLAN))
+#define NM_IS_DEVICE_VLAN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_VLAN))
+#define NM_DEVICE_VLAN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_VLAN, NMDeviceVlanClass))
+
+/**
+ * NMDeviceVlanError:
+ * @NM_DEVICE_VLAN_ERROR_UNKNOWN: unknown or unclassified error
+ * @NM_DEVICE_VLAN_ERROR_NOT_VLAN_CONNECTION: the connection was not of VLAN type
+ * @NM_DEVICE_VLAN_ERROR_INVALID_VLAN_CONNECTION: the VLAN connection was invalid
+ * @NM_DEVICE_VLAN_ERROR_ID_MISMATCH: the VLAN identifiers of the connection and the device mismatched
+ * @NM_DEVICE_VLAN_ERROR_INTERFACE_MISMATCH: the interfaces of the connection and the device mismatched
+ * @NM_DEVICE_VLAN_ERROR_MAC_MISMATCH: the MACs of the connection and the device mismatched
+ */
+typedef enum {
+ NM_DEVICE_VLAN_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/
+ NM_DEVICE_VLAN_ERROR_NOT_VLAN_CONNECTION, /*< nick=NotVlanConnection >*/
+ NM_DEVICE_VLAN_ERROR_INVALID_VLAN_CONNECTION, /*< nick=InvalidVlanConnection >*/
+ NM_DEVICE_VLAN_ERROR_ID_MISMATCH, /*< nick=IdMismatch >*/
+ NM_DEVICE_VLAN_ERROR_INTERFACE_MISMATCH, /*< nick=InterfaceMismatch >*/
+ NM_DEVICE_VLAN_ERROR_MAC_MISMATCH, /*< nick=MacMismatch >*/
+} NMDeviceVlanError;
+
+#define NM_DEVICE_VLAN_ERROR nm_device_vlan_error_quark ()
+GQuark nm_device_vlan_error_quark (void);
+
+#define NM_DEVICE_VLAN_HW_ADDRESS "hw-address"
+#define NM_DEVICE_VLAN_CARRIER "carrier"
+#define NM_DEVICE_VLAN_VLAN_ID "vlan-id"
+
+typedef struct {
+ NMDevice parent;
+} NMDeviceVlan;
+
+typedef struct {
+ NMDeviceClass parent;
+
+ /* Padding for future expansion */
+ void (*_reserved1) (void);
+ void (*_reserved2) (void);
+ void (*_reserved3) (void);
+ void (*_reserved4) (void);
+ void (*_reserved5) (void);
+ void (*_reserved6) (void);
+} NMDeviceVlanClass;
+
+GType nm_device_vlan_get_type (void);
+
+GObject *nm_device_vlan_new (DBusGConnection *connection, const char *path);
+
+const char * nm_device_vlan_get_hw_address (NMDeviceVlan *device);
+gboolean nm_device_vlan_get_carrier (NMDeviceVlan *device);
+guint nm_device_vlan_get_vlan_id (NMDeviceVlan *device);
+
+G_END_DECLS
+
+#endif /* NM_DEVICE_VLAN_H */
diff --git a/libnm/nm-device-wifi.c b/libnm/nm-device-wifi.c
new file mode 100644
index 0000000000..a4c108fb08
--- /dev/null
+++ b/libnm/nm-device-wifi.c
@@ -0,0 +1,838 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2008 Novell, Inc.
+ * Copyright 2007 - 2012 Red Hat, Inc.
+ */
+
+#include <config.h>
+#include <string.h>
+#include <netinet/ether.h>
+
+#include "nm-glib-compat.h"
+
+#include <nm-setting-connection.h>
+#include <nm-setting-wireless.h>
+#include <nm-setting-wireless-security.h>
+
+#include "nm-device-wifi.h"
+#include "nm-device-private.h"
+#include "nm-object-private.h"
+#include "nm-object-cache.h"
+#include "nm-dbus-glib-types.h"
+#include "nm-types-private.h"
+
+G_DEFINE_TYPE (NMDeviceWifi, nm_device_wifi, NM_TYPE_DEVICE)
+
+#define NM_DEVICE_WIFI_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_WIFI, NMDeviceWifiPrivate))
+
+void _nm_device_wifi_set_wireless_enabled (NMDeviceWifi *device, gboolean enabled);
+
+typedef struct {
+ NMDeviceWifi *device;
+ NMDeviceWifiRequestScanFn callback;
+ gpointer user_data;
+} RequestScanInfo;
+
+typedef struct {
+ DBusGProxy *proxy;
+
+ char *hw_address;
+ char *perm_hw_address;
+ NM80211Mode mode;
+ guint32 rate;
+ NMAccessPoint *active_ap;
+ NMDeviceWifiCapabilities wireless_caps;
+ GPtrArray *aps;
+
+ DBusGProxyCall *scan_call;
+ RequestScanInfo *scan_info;
+} NMDeviceWifiPrivate;
+
+enum {
+ PROP_0,
+ PROP_HW_ADDRESS,
+ PROP_PERM_HW_ADDRESS,
+ PROP_MODE,
+ PROP_BITRATE,
+ PROP_ACTIVE_ACCESS_POINT,
+ PROP_WIRELESS_CAPABILITIES,
+ PROP_ACCESS_POINTS,
+
+ LAST_PROP
+};
+
+enum {
+ ACCESS_POINT_ADDED,
+ ACCESS_POINT_REMOVED,
+
+ LAST_SIGNAL
+};
+static guint signals[LAST_SIGNAL] = { 0 };
+
+/**
+ * nm_device_wifi_error_quark:
+ *
+ * Registers an error quark for #NMDeviceWifi if necessary.
+ *
+ * Returns: the error quark used for #NMDeviceWifi errors.
+ **/
+GQuark
+nm_device_wifi_error_quark (void)
+{
+ static GQuark quark = 0;
+
+ if (G_UNLIKELY (quark == 0))
+ quark = g_quark_from_static_string ("nm-device-wifi-error-quark");
+ return quark;
+}
+
+/**
+ * nm_device_wifi_new:
+ * @connection: the #DBusGConnection
+ * @path: the DBus object path of the device
+ *
+ * Creates a new #NMDeviceWifi.
+ *
+ * Returns: (transfer full): a new Wi-Fi device
+ **/
+GObject *
+nm_device_wifi_new (DBusGConnection *connection, const char *path)
+{
+ GObject *device;
+
+ g_return_val_if_fail (connection != NULL, NULL);
+ g_return_val_if_fail (path != NULL, NULL);
+
+ device = g_object_new (NM_TYPE_DEVICE_WIFI,
+ NM_OBJECT_DBUS_CONNECTION, connection,
+ NM_OBJECT_DBUS_PATH, path,
+ NULL);
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return device;
+}
+
+/**
+ * nm_device_wifi_get_hw_address:
+ * @device: a #NMDeviceWifi
+ *
+ * Gets the actual hardware (MAC) address of the #NMDeviceWifi
+ *
+ * Returns: the actual hardware address. This is the internal string used by the
+ * device, and must not be modified.
+ **/
+const char *
+nm_device_wifi_get_hw_address (NMDeviceWifi *device)
+{
+ g_return_val_if_fail (NM_IS_DEVICE_WIFI (device), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return NM_DEVICE_WIFI_GET_PRIVATE (device)->hw_address;
+}
+
+/**
+ * nm_device_wifi_get_permanent_hw_address:
+ * @device: a #NMDeviceWifi
+ *
+ * Gets the permanent hardware (MAC) address of the #NMDeviceWifi
+ *
+ * Returns: the permanent hardware address. This is the internal string used by the
+ * device, and must not be modified.
+ **/
+const char *
+nm_device_wifi_get_permanent_hw_address (NMDeviceWifi *device)
+{
+ g_return_val_if_fail (NM_IS_DEVICE_WIFI (device), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return NM_DEVICE_WIFI_GET_PRIVATE (device)->perm_hw_address;
+}
+
+/**
+ * nm_device_wifi_get_mode:
+ * @device: a #NMDeviceWifi
+ *
+ * Gets the #NMDeviceWifi mode.
+ *
+ * Returns: the mode
+ **/
+NM80211Mode
+nm_device_wifi_get_mode (NMDeviceWifi *device)
+{
+ g_return_val_if_fail (NM_IS_DEVICE_WIFI (device), 0);
+
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return NM_DEVICE_WIFI_GET_PRIVATE (device)->mode;
+}
+
+/**
+ * nm_device_wifi_get_bitrate:
+ * @device: a #NMDeviceWifi
+ *
+ * Gets the bit rate of the #NMDeviceWifi in kbit/s.
+ *
+ * Returns: the bit rate (kbit/s)
+ **/
+guint32
+nm_device_wifi_get_bitrate (NMDeviceWifi *device)
+{
+ NMDeviceState state;
+
+ g_return_val_if_fail (NM_IS_DEVICE_WIFI (device), 0);
+
+ state = nm_device_get_state (NM_DEVICE (device));
+ switch (state) {
+ case NM_DEVICE_STATE_IP_CONFIG:
+ case NM_DEVICE_STATE_IP_CHECK:
+ case NM_DEVICE_STATE_SECONDARIES:
+ case NM_DEVICE_STATE_ACTIVATED:
+ case NM_DEVICE_STATE_DEACTIVATING:
+ break;
+ default:
+ return 0;
+ }
+
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return NM_DEVICE_WIFI_GET_PRIVATE (device)->rate;
+}
+
+/**
+ * nm_device_wifi_get_capabilities:
+ * @device: a #NMDeviceWifi
+ *
+ * Gets the Wi-Fi capabilities of the #NMDeviceWifi.
+ *
+ * Returns: the capabilities
+ **/
+NMDeviceWifiCapabilities
+nm_device_wifi_get_capabilities (NMDeviceWifi *device)
+{
+ g_return_val_if_fail (NM_IS_DEVICE_WIFI (device), 0);
+
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return NM_DEVICE_WIFI_GET_PRIVATE (device)->wireless_caps;
+}
+
+/**
+ * nm_device_wifi_get_active_access_point:
+ * @device: a #NMDeviceWifi
+ *
+ * Gets the active #NMAccessPoint.
+ *
+ * Returns: (transfer none): the access point or %NULL if none is active
+ **/
+NMAccessPoint *
+nm_device_wifi_get_active_access_point (NMDeviceWifi *device)
+{
+ NMDeviceState state;
+
+ g_return_val_if_fail (NM_IS_DEVICE_WIFI (device), NULL);
+
+ state = nm_device_get_state (NM_DEVICE (device));
+ switch (state) {
+ case NM_DEVICE_STATE_PREPARE:
+ case NM_DEVICE_STATE_CONFIG:
+ case NM_DEVICE_STATE_NEED_AUTH:
+ case NM_DEVICE_STATE_IP_CONFIG:
+ case NM_DEVICE_STATE_IP_CHECK:
+ case NM_DEVICE_STATE_SECONDARIES:
+ case NM_DEVICE_STATE_ACTIVATED:
+ case NM_DEVICE_STATE_DEACTIVATING:
+ break;
+ default:
+ return NULL;
+ break;
+ }
+
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return NM_DEVICE_WIFI_GET_PRIVATE (device)->active_ap;
+}
+
+/**
+ * nm_device_wifi_get_access_points:
+ * @device: a #NMDeviceWifi
+ *
+ * Gets all the scanned access points of the #NMDeviceWifi.
+ *
+ * Returns: (element-type NMAccessPoint): a #GPtrArray containing all the
+ * scanned #NMAccessPoints.
+ * The returned array is owned by the client and should not be modified.
+ **/
+const GPtrArray *
+nm_device_wifi_get_access_points (NMDeviceWifi *device)
+{
+ g_return_val_if_fail (NM_IS_DEVICE_WIFI (device), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return handle_ptr_array_return (NM_DEVICE_WIFI_GET_PRIVATE (device)->aps);
+}
+
+/**
+ * nm_device_wifi_get_access_point_by_path:
+ * @device: a #NMDeviceWifi
+ * @path: the object path of the access point
+ *
+ * Gets a #NMAccessPoint by path.
+ *
+ * Returns: (transfer none): the access point or %NULL if none is found.
+ **/
+NMAccessPoint *
+nm_device_wifi_get_access_point_by_path (NMDeviceWifi *device,
+ const char *path)
+{
+ const GPtrArray *aps;
+ int i;
+ NMAccessPoint *ap = NULL;
+
+ g_return_val_if_fail (NM_IS_DEVICE_WIFI (device), NULL);
+ g_return_val_if_fail (path != NULL, NULL);
+
+ aps = nm_device_wifi_get_access_points (device);
+ if (!aps)
+ return NULL;
+
+ for (i = 0; i < aps->len; i++) {
+ NMAccessPoint *candidate = g_ptr_array_index (aps, i);
+ if (!strcmp (nm_object_get_path (NM_OBJECT (candidate)), path)) {
+ ap = candidate;
+ break;
+ }
+ }
+
+ return ap;
+}
+
+static void
+request_scan_cb (DBusGProxy *proxy,
+ DBusGProxyCall *call,
+ gpointer user_data)
+{
+ RequestScanInfo *info = user_data;
+ NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (info->device);
+ GError *error = NULL;
+
+ dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_INVALID);
+
+ if (info->callback)
+ info->callback (info->device, error, info->user_data);
+
+ g_clear_error (&error);
+ g_slice_free (RequestScanInfo, info);
+
+ priv->scan_call = NULL;
+ priv->scan_info = NULL;
+}
+
+/**
+ * nm_device_wifi_request_scan_simple:
+ * @device: a #NMDeviceWifi
+ * @callback: (scope async) (allow-none): the function to call when the call is done
+ * @user_data: (closure): user data to pass to the callback function
+ *
+ * Request NM to scan for access points on the #NMDeviceWifi. This function only
+ * instructs NM to perform scanning. Use nm_device_wifi_get_access_points()
+ * to get available access points.
+ *
+ * Since: 0.9.8
+ **/
+void
+nm_device_wifi_request_scan_simple (NMDeviceWifi *device,
+ NMDeviceWifiRequestScanFn callback,
+ gpointer user_data)
+{
+ RequestScanInfo *info;
+ GHashTable *options;
+ NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (device);
+
+ g_return_if_fail (NM_IS_DEVICE_WIFI (device));
+
+ /* If a scan is in progress, just return */
+ if (priv->scan_call)
+ return;
+
+ options = g_hash_table_new (g_str_hash, g_str_equal);
+
+ info = g_slice_new0 (RequestScanInfo);
+ info->device = device;
+ info->callback = callback;
+ info->user_data = user_data;
+
+ priv->scan_info = info;
+ priv->scan_call = dbus_g_proxy_begin_call (NM_DEVICE_WIFI_GET_PRIVATE (device)->proxy, "RequestScan",
+ request_scan_cb, info, NULL,
+ DBUS_TYPE_G_MAP_OF_VARIANT, options,
+ G_TYPE_INVALID);
+
+ g_hash_table_unref (options);
+}
+
+static void
+clean_up_aps (NMDeviceWifi *self, gboolean notify)
+{
+ NMDeviceWifiPrivate *priv;
+
+ g_return_if_fail (NM_IS_DEVICE_WIFI (self));
+
+ priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+
+ if (priv->active_ap) {
+ g_object_unref (priv->active_ap);
+ priv->active_ap = NULL;
+ }
+
+ if (priv->aps) {
+ while (priv->aps->len) {
+ NMAccessPoint *ap = NM_ACCESS_POINT (g_ptr_array_index (priv->aps, 0));
+
+ if (notify)
+ g_signal_emit (self, signals[ACCESS_POINT_REMOVED], 0, ap);
+ g_ptr_array_remove (priv->aps, ap);
+ g_object_unref (ap);
+ }
+ g_ptr_array_free (priv->aps, TRUE);
+ priv->aps = NULL;
+ }
+}
+
+/**
+ * _nm_device_wifi_set_wireless_enabled:
+ * @device: a #NMDeviceWifi
+ * @enabled: %TRUE to enable the device
+ *
+ * Enables or disables the wireless device.
+ **/
+void
+_nm_device_wifi_set_wireless_enabled (NMDeviceWifi *device,
+ gboolean enabled)
+{
+ g_return_if_fail (NM_IS_DEVICE_WIFI (device));
+
+ if (!enabled)
+ clean_up_aps (device, TRUE);
+}
+
+#define WPA_CAPS (NM_WIFI_DEVICE_CAP_CIPHER_TKIP | \
+ NM_WIFI_DEVICE_CAP_CIPHER_CCMP | \
+ NM_WIFI_DEVICE_CAP_WPA | \
+ NM_WIFI_DEVICE_CAP_RSN)
+
+#define RSN_CAPS (NM_WIFI_DEVICE_CAP_CIPHER_CCMP | NM_WIFI_DEVICE_CAP_RSN)
+
+static gboolean
+has_proto (NMSettingWirelessSecurity *s_wsec, const char *proto)
+{
+ int i;
+
+ for (i = 0; i < nm_setting_wireless_security_get_num_protos (s_wsec); i++) {
+ if (g_strcmp0 (proto, nm_setting_wireless_security_get_proto (s_wsec, i)) == 0)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static gboolean
+connection_compatible (NMDevice *device, NMConnection *connection, GError **error)
+{
+ NMSettingConnection *s_con;
+ NMSettingWireless *s_wifi;
+ NMSettingWirelessSecurity *s_wsec;
+ const char *ctype;
+ const GByteArray *mac;
+ const char *hw_str;
+ struct ether_addr *hw_mac;
+ NMDeviceWifiCapabilities wifi_caps;
+ const char *key_mgmt;
+
+ s_con = nm_connection_get_setting_connection (connection);
+ g_assert (s_con);
+
+ ctype = nm_setting_connection_get_connection_type (s_con);
+ if (strcmp (ctype, NM_SETTING_WIRELESS_SETTING_NAME) != 0) {
+ g_set_error (error, NM_DEVICE_WIFI_ERROR, NM_DEVICE_WIFI_ERROR_NOT_WIFI_CONNECTION,
+ "The connection was not a Wi-Fi connection.");
+ return FALSE;
+ }
+
+ s_wifi = nm_connection_get_setting_wireless (connection);
+ if (!s_wifi) {
+ g_set_error (error, NM_DEVICE_WIFI_ERROR, NM_DEVICE_WIFI_ERROR_INVALID_WIFI_CONNECTION,
+ "The connection was not a valid Wi-Fi connection.");
+ return FALSE;
+ }
+
+ /* Check MAC address */
+ hw_str = nm_device_wifi_get_permanent_hw_address (NM_DEVICE_WIFI (device));
+ if (hw_str) {
+ hw_mac = ether_aton (hw_str);
+ if (!hw_mac) {
+ g_set_error (error, NM_DEVICE_WIFI_ERROR, NM_DEVICE_WIFI_ERROR_INVALID_DEVICE_MAC,
+ "Invalid device MAC address.");
+ return FALSE;
+ }
+ mac = nm_setting_wireless_get_mac_address (s_wifi);
+ if (mac && hw_mac && memcmp (mac->data, hw_mac->ether_addr_octet, ETH_ALEN)) {
+ g_set_error (error, NM_DEVICE_WIFI_ERROR, NM_DEVICE_WIFI_ERROR_MAC_MISMATCH,
+ "The MACs of the device and the connection didn't match.");
+ return FALSE;
+ }
+ }
+
+ /* Check device capabilities; we assume all devices can do WEP at least */
+ wifi_caps = nm_device_wifi_get_capabilities (NM_DEVICE_WIFI (device));
+
+ s_wsec = nm_connection_get_setting_wireless_security (connection);
+ if (s_wsec) {
+ /* Connection has security, verify it against the device's capabilities */
+ key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wsec);
+ if ( !g_strcmp0 (key_mgmt, "wpa-none")
+ || !g_strcmp0 (key_mgmt, "wpa-psk")
+ || !g_strcmp0 (key_mgmt, "wpa-eap")) {
+
+ /* Is device only WEP capable? */
+ if (!(wifi_caps & WPA_CAPS)) {
+ g_set_error (error, NM_DEVICE_WIFI_ERROR, NM_DEVICE_WIFI_ERROR_MISSING_DEVICE_WPA_CAPS,
+ "The device missed WPA capabilities required by the connection.");
+ return FALSE;
+ }
+
+ /* Make sure WPA2/RSN-only connections don't get chosen for WPA-only cards */
+ if (has_proto (s_wsec, "rsn") && !has_proto (s_wsec, "wpa") && !(wifi_caps & RSN_CAPS)) {
+ g_set_error (error, NM_DEVICE_WIFI_ERROR, NM_DEVICE_WIFI_ERROR_MISSING_DEVICE_RSN_CAPS,
+ "The device missed WPA2/RSN capabilities required by the connection.");
+ return FALSE;
+ }
+ }
+ }
+
+ return NM_DEVICE_CLASS (nm_device_wifi_parent_class)->connection_compatible (device, connection, error);
+}
+
+static GType
+get_setting_type (NMDevice *device)
+{
+ return NM_TYPE_SETTING_WIRELESS;
+}
+
+static const char *
+get_hw_address (NMDevice *device)
+{
+ return nm_device_wifi_get_hw_address (NM_DEVICE_WIFI (device));
+}
+
+/**************************************************************/
+
+static void
+nm_device_wifi_init (NMDeviceWifi *device)
+{
+ _nm_device_set_device_type (NM_DEVICE (device), NM_DEVICE_TYPE_WIFI);
+}
+
+static void
+get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NMDeviceWifi *self = NM_DEVICE_WIFI (object);
+
+ _nm_object_ensure_inited (NM_OBJECT (object));
+
+ switch (prop_id) {
+ case PROP_HW_ADDRESS:
+ g_value_set_string (value, nm_device_wifi_get_hw_address (self));
+ break;
+ case PROP_PERM_HW_ADDRESS:
+ g_value_set_string (value, nm_device_wifi_get_permanent_hw_address (self));
+ break;
+ case PROP_MODE:
+ g_value_set_uint (value, nm_device_wifi_get_mode (self));
+ break;
+ case PROP_BITRATE:
+ g_value_set_uint (value, nm_device_wifi_get_bitrate (self));
+ break;
+ case PROP_ACTIVE_ACCESS_POINT:
+ g_value_set_object (value, nm_device_wifi_get_active_access_point (self));
+ break;
+ case PROP_WIRELESS_CAPABILITIES:
+ g_value_set_uint (value, nm_device_wifi_get_capabilities (self));
+ break;
+ case PROP_ACCESS_POINTS:
+ g_value_set_boxed (value, nm_device_wifi_get_access_points (self));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+state_changed_cb (NMDevice *device, GParamSpec *pspec, gpointer user_data)
+{
+ NMDeviceWifi *self = NM_DEVICE_WIFI (device);
+ NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+
+ switch (nm_device_get_state (device)) {
+ case NM_DEVICE_STATE_UNKNOWN:
+ case NM_DEVICE_STATE_UNMANAGED:
+ case NM_DEVICE_STATE_UNAVAILABLE:
+ case NM_DEVICE_STATE_DISCONNECTED:
+ case NM_DEVICE_STATE_FAILED:
+ /* Just clear active AP; don't clear the AP list unless wireless is disabled completely */
+ if (priv->active_ap) {
+ g_object_unref (priv->active_ap);
+ priv->active_ap = NULL;
+ }
+ _nm_object_queue_notify (NM_OBJECT (device), NM_DEVICE_WIFI_ACTIVE_ACCESS_POINT);
+ priv->rate = 0;
+ _nm_object_queue_notify (NM_OBJECT (device), NM_DEVICE_WIFI_BITRATE);
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+register_properties (NMDeviceWifi *device)
+{
+ NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (device);
+ const NMPropertiesInfo property_info[] = {
+ { NM_DEVICE_WIFI_HW_ADDRESS, &priv->hw_address },
+ { NM_DEVICE_WIFI_PERMANENT_HW_ADDRESS, &priv->perm_hw_address },
+ { NM_DEVICE_WIFI_MODE, &priv->mode },
+ { NM_DEVICE_WIFI_BITRATE, &priv->rate },
+ { NM_DEVICE_WIFI_ACTIVE_ACCESS_POINT, &priv->active_ap, NULL, NM_TYPE_ACCESS_POINT },
+ { NM_DEVICE_WIFI_CAPABILITIES, &priv->wireless_caps },
+ { NM_DEVICE_WIFI_ACCESS_POINTS, &priv->aps, NULL, NM_TYPE_ACCESS_POINT, "access-point" },
+ { NULL },
+ };
+
+ _nm_object_register_properties (NM_OBJECT (device),
+ priv->proxy,
+ property_info);
+}
+
+static void
+access_point_removed (NMDeviceWifi *self, NMAccessPoint *ap)
+{
+ NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+
+ if (ap == priv->active_ap) {
+ g_object_unref (priv->active_ap);
+ priv->active_ap = NULL;
+ _nm_object_queue_notify (NM_OBJECT (self), NM_DEVICE_WIFI_ACTIVE_ACCESS_POINT);
+
+ priv->rate = 0;
+ _nm_object_queue_notify (NM_OBJECT (self), NM_DEVICE_WIFI_BITRATE);
+ }
+}
+
+static void
+constructed (GObject *object)
+{
+ NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (object);
+
+ G_OBJECT_CLASS (nm_device_wifi_parent_class)->constructed (object);
+
+ priv->proxy = _nm_object_new_proxy (NM_OBJECT (object), NULL, NM_DBUS_INTERFACE_DEVICE_WIRELESS);
+ register_properties (NM_DEVICE_WIFI (object));
+
+ g_signal_connect (NM_DEVICE (object),
+ "notify::" NM_DEVICE_STATE,
+ G_CALLBACK (state_changed_cb),
+ NULL);
+}
+
+static void
+dispose (GObject *object)
+{
+ NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (object);
+ GError *error = NULL;
+
+ if (priv->scan_call) {
+ g_set_error_literal (&error, NM_DEVICE_WIFI_ERROR, NM_DEVICE_WIFI_ERROR_UNKNOWN,
+ "Wi-Fi device was destroyed");
+ if (priv->scan_info) {
+ if (priv->scan_info->callback)
+ priv->scan_info->callback (NULL, error, priv->scan_info->user_data);
+ g_slice_free (RequestScanInfo, priv->scan_info);
+ priv->scan_info = NULL;
+ }
+ g_clear_error (&error);
+
+ dbus_g_proxy_cancel_call (priv->proxy, priv->scan_call);
+ priv->scan_call = NULL;
+ }
+
+ clean_up_aps (NM_DEVICE_WIFI (object), FALSE);
+ g_clear_object (&priv->proxy);
+
+ G_OBJECT_CLASS (nm_device_wifi_parent_class)->dispose (object);
+}
+
+static void
+finalize (GObject *object)
+{
+ NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (object);
+
+ g_free (priv->hw_address);
+ g_free (priv->perm_hw_address);
+
+ G_OBJECT_CLASS (nm_device_wifi_parent_class)->finalize (object);
+}
+
+static void
+nm_device_wifi_class_init (NMDeviceWifiClass *wifi_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (wifi_class);
+ NMDeviceClass *device_class = NM_DEVICE_CLASS (wifi_class);
+
+ g_type_class_add_private (wifi_class, sizeof (NMDeviceWifiPrivate));
+
+ /* virtual methods */
+ object_class->constructed = constructed;
+ object_class->get_property = get_property;
+ object_class->dispose = dispose;
+ object_class->finalize = finalize;
+ device_class->connection_compatible = connection_compatible;
+ device_class->get_setting_type = get_setting_type;
+ device_class->get_hw_address = get_hw_address;
+ wifi_class->access_point_removed = access_point_removed;
+
+ /* properties */
+
+ /**
+ * NMDeviceWifi:hw-address:
+ *
+ * The hardware (MAC) address of the device.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_HW_ADDRESS,
+ g_param_spec_string (NM_DEVICE_WIFI_HW_ADDRESS, "", "",
+ NULL,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMDeviceWifi:perm-hw-address:
+ *
+ * The hardware (MAC) address of the device.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PERM_HW_ADDRESS,
+ g_param_spec_string (NM_DEVICE_WIFI_PERMANENT_HW_ADDRESS, "", "",
+ NULL,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMDeviceWifi:mode:
+ *
+ * The mode of the device.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_MODE,
+ g_param_spec_uint (NM_DEVICE_WIFI_MODE, "", "",
+ NM_802_11_MODE_UNKNOWN, NM_802_11_MODE_AP, NM_802_11_MODE_INFRA,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMDeviceWifi:bitrate:
+ *
+ * The bit rate of the device in kbit/s.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_BITRATE,
+ g_param_spec_uint (NM_DEVICE_WIFI_BITRATE, "", "",
+ 0, G_MAXUINT32, 0,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMDeviceWifi:active-access-point:
+ *
+ * The active #NMAccessPoint of the device.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_ACTIVE_ACCESS_POINT,
+ g_param_spec_object (NM_DEVICE_WIFI_ACTIVE_ACCESS_POINT, "", "",
+ NM_TYPE_ACCESS_POINT,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMDeviceWifi:wireless-capabilities:
+ *
+ * The wireless capabilities of the device.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_WIRELESS_CAPABILITIES,
+ g_param_spec_uint (NM_DEVICE_WIFI_CAPABILITIES, "", "",
+ 0, G_MAXUINT32, 0,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMDeviceWifi:access-points:
+ *
+ * List of all Wi-Fi access points the device can see.
+ *
+ * Since: 0.9.10
+ **/
+ g_object_class_install_property
+ (object_class, PROP_ACCESS_POINTS,
+ g_param_spec_boxed (NM_DEVICE_WIFI_ACCESS_POINTS, "", "",
+ NM_TYPE_OBJECT_ARRAY,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /* signals */
+
+ /**
+ * NMDeviceWifi::access-point-added:
+ * @device: the Wi-Fi device that received the signal
+ * @ap: the new access point
+ *
+ * Notifies that a #NMAccessPoint is added to the Wi-Fi device.
+ **/
+ signals[ACCESS_POINT_ADDED] =
+ g_signal_new ("access-point-added",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NMDeviceWifiClass, access_point_added),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1,
+ G_TYPE_OBJECT);
+
+ /**
+ * NMDeviceWifi::access-point-removed:
+ * @device: the Wi-Fi device that received the signal
+ * @ap: the removed access point
+ *
+ * Notifies that a #NMAccessPoint is removed from the Wi-Fi device.
+ **/
+ signals[ACCESS_POINT_REMOVED] =
+ g_signal_new ("access-point-removed",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NMDeviceWifiClass, access_point_removed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1,
+ G_TYPE_OBJECT);
+}
diff --git a/libnm/nm-device-wifi.h b/libnm/nm-device-wifi.h
new file mode 100644
index 0000000000..2bb432a7b0
--- /dev/null
+++ b/libnm/nm-device-wifi.h
@@ -0,0 +1,115 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2008 Novell, Inc.
+ * Copyright 2007 - 2012 Red Hat, Inc.
+ */
+
+#ifndef NM_DEVICE_WIFI_H
+#define NM_DEVICE_WIFI_H
+
+#include "nm-device.h"
+#include "nm-access-point.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_DEVICE_WIFI (nm_device_wifi_get_type ())
+#define NM_DEVICE_WIFI(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_WIFI, NMDeviceWifi))
+#define NM_DEVICE_WIFI_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_WIFI, NMDeviceWifiClass))
+#define NM_IS_DEVICE_WIFI(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_WIFI))
+#define NM_IS_DEVICE_WIFI_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_WIFI))
+#define NM_DEVICE_WIFI_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_WIFI, NMDeviceWifiClass))
+
+/**
+ * NMDeviceWifiError:
+ * @NM_DEVICE_WIFI_ERROR_UNKNOWN: unknown or unclassified error
+ * @NM_DEVICE_WIFI_ERROR_NOT_WIFI_CONNECTION: the connection was not of Wi-Fi type
+ * @NM_DEVICE_WIFI_ERROR_INVALID_WIFI_CONNECTION: the Wi-Fi connection was invalid
+ * @NM_DEVICE_WIFI_ERROR_INVALID_DEVICE_MAC: the device's MAC was invalid
+ * @NM_DEVICE_WIFI_ERROR_MAC_MISMATCH: the MACs of the connection and the device mismatched
+ * @NM_DEVICE_WIFI_ERROR_MISSING_DEVICE_WPA_CAPS: the device missed WPA capabilities
+ * required by the connection
+ * @NM_DEVICE_WIFI_ERROR_MISSING_DEVICE_RSN_CAPS: the device missed RSN capabilities
+ * required by the connection
+ */
+typedef enum {
+ NM_DEVICE_WIFI_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/
+ NM_DEVICE_WIFI_ERROR_NOT_WIFI_CONNECTION, /*< nick=NotWifiConnection >*/
+ NM_DEVICE_WIFI_ERROR_INVALID_WIFI_CONNECTION, /*< nick=InvalidWifiConnection >*/
+ NM_DEVICE_WIFI_ERROR_INVALID_DEVICE_MAC, /*< nick=InvalidDeviceMac >*/
+ NM_DEVICE_WIFI_ERROR_MAC_MISMATCH, /*< nick=MacMismatch >*/
+ NM_DEVICE_WIFI_ERROR_MISSING_DEVICE_WPA_CAPS, /*< nick=MissingDeviceWpaCaps >*/
+ NM_DEVICE_WIFI_ERROR_MISSING_DEVICE_RSN_CAPS, /*< nick=MissingDeviceRsnCaps >*/
+} NMDeviceWifiError;
+
+#define NM_DEVICE_WIFI_ERROR nm_device_wifi_error_quark ()
+GQuark nm_device_wifi_error_quark (void);
+
+#define NM_DEVICE_WIFI_HW_ADDRESS "hw-address"
+#define NM_DEVICE_WIFI_PERMANENT_HW_ADDRESS "perm-hw-address"
+#define NM_DEVICE_WIFI_MODE "mode"
+#define NM_DEVICE_WIFI_BITRATE "bitrate"
+#define NM_DEVICE_WIFI_ACTIVE_ACCESS_POINT "active-access-point"
+#define NM_DEVICE_WIFI_CAPABILITIES "wireless-capabilities"
+#define NM_DEVICE_WIFI_ACCESS_POINTS "access-points"
+
+typedef struct {
+ NMDevice parent;
+} NMDeviceWifi;
+
+typedef struct {
+ NMDeviceClass parent;
+
+ /* Signals */
+ void (*access_point_added) (NMDeviceWifi *device, NMAccessPoint *ap);
+ void (*access_point_removed) (NMDeviceWifi *device, NMAccessPoint *ap);
+
+ /* Padding for future expansion */
+ void (*_reserved1) (void);
+ void (*_reserved2) (void);
+ void (*_reserved3) (void);
+ void (*_reserved4) (void);
+ void (*_reserved5) (void);
+ void (*_reserved6) (void);
+} NMDeviceWifiClass;
+
+GType nm_device_wifi_get_type (void);
+
+GObject *nm_device_wifi_new (DBusGConnection *connection, const char *path);
+
+const char * nm_device_wifi_get_hw_address (NMDeviceWifi *device);
+const char * nm_device_wifi_get_permanent_hw_address (NMDeviceWifi *device);
+NM80211Mode nm_device_wifi_get_mode (NMDeviceWifi *device);
+guint32 nm_device_wifi_get_bitrate (NMDeviceWifi *device);
+NMDeviceWifiCapabilities nm_device_wifi_get_capabilities (NMDeviceWifi *device);
+NMAccessPoint * nm_device_wifi_get_active_access_point (NMDeviceWifi *device);
+
+NMAccessPoint * nm_device_wifi_get_access_point_by_path (NMDeviceWifi *device,
+ const char *path);
+
+const GPtrArray * nm_device_wifi_get_access_points (NMDeviceWifi *device);
+
+typedef void (*NMDeviceWifiRequestScanFn) (NMDeviceWifi *device,
+ GError *error,
+ gpointer user_data);
+void nm_device_wifi_request_scan_simple (NMDeviceWifi *device,
+ NMDeviceWifiRequestScanFn callback,
+ gpointer user_data);
+
+G_END_DECLS
+
+#endif /* NM_DEVICE_WIFI_H */
diff --git a/libnm/nm-device-wimax.c b/libnm/nm-device-wimax.c
new file mode 100644
index 0000000000..8fac2a5ac7
--- /dev/null
+++ b/libnm/nm-device-wimax.c
@@ -0,0 +1,755 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2011 - 2012 Red Hat, Inc.
+ * Copyright 2009 Novell, Inc.
+ */
+
+#include <config.h>
+#include <string.h>
+#include <netinet/ether.h>
+
+#include "nm-glib-compat.h"
+
+#include <nm-setting-connection.h>
+#include <nm-setting-wimax.h>
+
+#include "nm-device-wimax.h"
+#include "nm-object-private.h"
+#include "nm-object-cache.h"
+#include "nm-dbus-glib-types.h"
+#include "nm-types-private.h"
+#include "nm-device-private.h"
+
+G_DEFINE_TYPE (NMDeviceWimax, nm_device_wimax, NM_TYPE_DEVICE)
+
+#define NM_DEVICE_WIMAX_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_WIMAX, NMDeviceWimaxPrivate))
+
+void _nm_device_wimax_set_wireless_enabled (NMDeviceWimax *wimax, gboolean enabled);
+
+typedef struct {
+ DBusGProxy *proxy;
+
+ char *hw_address;
+ NMWimaxNsp *active_nsp;
+ GPtrArray *nsps;
+
+ guint center_freq;
+ gint rssi;
+ gint cinr;
+ gint tx_power;
+ char *bsid;
+} NMDeviceWimaxPrivate;
+
+enum {
+ PROP_0,
+ PROP_HW_ADDRESS,
+ PROP_ACTIVE_NSP,
+ PROP_CENTER_FREQ,
+ PROP_RSSI,
+ PROP_CINR,
+ PROP_TX_POWER,
+ PROP_BSID,
+ PROP_NSPS,
+
+ LAST_PROP
+};
+
+enum {
+ NSP_ADDED,
+ NSP_REMOVED,
+
+ LAST_SIGNAL
+};
+static guint signals[LAST_SIGNAL] = { 0 };
+
+/**
+ * nm_device_wimax_error_quark:
+ *
+ * Registers an error quark for #NMDeviceWimax if necessary.
+ *
+ * Returns: the error quark used for #NMDeviceWimax errors.
+ **/
+GQuark
+nm_device_wimax_error_quark (void)
+{
+ static GQuark quark = 0;
+
+ if (G_UNLIKELY (quark == 0))
+ quark = g_quark_from_static_string ("nm-device-wimax-error-quark");
+ return quark;
+}
+
+/**
+ * nm_device_wimax_new:
+ * @connection: the #DBusGConnection
+ * @path: the D-Bus object path of the WiMAX device
+ *
+ * Creates a new #NMDeviceWimax.
+ *
+ * Returns: (transfer full): a new WiMAX device
+ **/
+GObject *
+nm_device_wimax_new (DBusGConnection *connection, const char *path)
+{
+ GObject *device;
+
+ g_return_val_if_fail (connection != NULL, NULL);
+ g_return_val_if_fail (path != NULL, NULL);
+
+ device = g_object_new (NM_TYPE_DEVICE_WIMAX,
+ NM_OBJECT_DBUS_CONNECTION, connection,
+ NM_OBJECT_DBUS_PATH, path,
+ NULL);
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return device;
+}
+
+/**
+ * nm_device_wimax_get_hw_address:
+ * @wimax: a #NMDeviceWimax
+ *
+ * Gets the hardware (MAC) address of the #NMDeviceWimax
+ *
+ * Returns: the hardware address. This is the internal string used by the
+ * device, and must not be modified.
+ **/
+const char *
+nm_device_wimax_get_hw_address (NMDeviceWimax *wimax)
+{
+ g_return_val_if_fail (NM_IS_DEVICE_WIMAX (wimax), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (wimax));
+ return NM_DEVICE_WIMAX_GET_PRIVATE (wimax)->hw_address;
+}
+
+/**
+ * nm_device_wimax_get_active_nsp:
+ * @wimax: a #NMDeviceWimax
+ *
+ * Gets the active #NMWimaxNsp.
+ *
+ * Returns: (transfer full): the access point or %NULL if none is active
+ **/
+NMWimaxNsp *
+nm_device_wimax_get_active_nsp (NMDeviceWimax *wimax)
+{
+ NMDeviceState state;
+
+ g_return_val_if_fail (NM_IS_DEVICE_WIMAX (wimax), NULL);
+
+ state = nm_device_get_state (NM_DEVICE (wimax));
+ switch (state) {
+ case NM_DEVICE_STATE_PREPARE:
+ case NM_DEVICE_STATE_CONFIG:
+ case NM_DEVICE_STATE_NEED_AUTH:
+ case NM_DEVICE_STATE_IP_CONFIG:
+ case NM_DEVICE_STATE_IP_CHECK:
+ case NM_DEVICE_STATE_SECONDARIES:
+ case NM_DEVICE_STATE_ACTIVATED:
+ case NM_DEVICE_STATE_DEACTIVATING:
+ break;
+ default:
+ return NULL;
+ break;
+ }
+
+ _nm_object_ensure_inited (NM_OBJECT (wimax));
+ return NM_DEVICE_WIMAX_GET_PRIVATE (wimax)->active_nsp;
+}
+
+/**
+ * nm_device_wimax_get_nsps:
+ * @wimax: a #NMDeviceWimax
+ *
+ * Gets all the scanned NSPs of the #NMDeviceWimax.
+ *
+ * Returns: (element-type NMWimaxNsp): a #GPtrArray containing
+ * all the scanned #NMWimaxNsps.
+ * The returned array is owned by the client and should not be modified.
+ **/
+const GPtrArray *
+nm_device_wimax_get_nsps (NMDeviceWimax *wimax)
+{
+ g_return_val_if_fail (NM_IS_DEVICE_WIMAX (wimax), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (wimax));
+ return handle_ptr_array_return (NM_DEVICE_WIMAX_GET_PRIVATE (wimax)->nsps);
+}
+
+/**
+ * nm_device_wimax_get_nsp_by_path:
+ * @wimax: a #NMDeviceWimax
+ * @path: the object path of the NSP
+ *
+ * Gets a #NMWimaxNsp by path.
+ *
+ * Returns: (transfer none): the access point or %NULL if none is found.
+ **/
+NMWimaxNsp *
+nm_device_wimax_get_nsp_by_path (NMDeviceWimax *wimax,
+ const char *path)
+{
+ const GPtrArray *nsps;
+ int i;
+ NMWimaxNsp *nsp = NULL;
+
+ g_return_val_if_fail (NM_IS_DEVICE_WIMAX (wimax), NULL);
+ g_return_val_if_fail (path != NULL, NULL);
+
+ nsps = nm_device_wimax_get_nsps (wimax);
+ if (!nsps)
+ return NULL;
+
+ for (i = 0; i < nsps->len; i++) {
+ NMWimaxNsp *candidate = g_ptr_array_index (nsps, i);
+ if (!strcmp (nm_object_get_path (NM_OBJECT (candidate)), path)) {
+ nsp = candidate;
+ break;
+ }
+ }
+
+ return nsp;
+}
+
+static void
+clean_up_nsps (NMDeviceWimax *self, gboolean notify)
+{
+ NMDeviceWimaxPrivate *priv;
+
+ g_return_if_fail (NM_IS_DEVICE_WIMAX (self));
+
+ priv = NM_DEVICE_WIMAX_GET_PRIVATE (self);
+
+ if (priv->active_nsp) {
+ g_object_unref (priv->active_nsp);
+ priv->active_nsp = NULL;
+ }
+
+ if (priv->nsps) {
+ while (priv->nsps->len) {
+ NMWimaxNsp *nsp = NM_WIMAX_NSP (g_ptr_array_index (priv->nsps, 0));
+
+ if (notify)
+ g_signal_emit (self, signals[NSP_REMOVED], 0, nsp);
+ g_ptr_array_remove (priv->nsps, nsp);
+ g_object_unref (nsp);
+ }
+ g_ptr_array_free (priv->nsps, TRUE);
+ priv->nsps = NULL;
+ }
+}
+
+/**
+ * nm_device_wimax_get_center_frequency:
+ * @self: a #NMDeviceWimax
+ *
+ * Gets the center frequency (in KHz) of the radio channel the device is using
+ * to communicate with the network when connected. Has no meaning when the
+ * device is not connected.
+ *
+ * Returns: the center frequency in KHz, or 0
+ **/
+guint
+nm_device_wimax_get_center_frequency (NMDeviceWimax *self)
+{
+ g_return_val_if_fail (NM_IS_DEVICE_WIMAX (self), 0);
+
+ _nm_object_ensure_inited (NM_OBJECT (self));
+ return NM_DEVICE_WIMAX_GET_PRIVATE (self)->center_freq;
+}
+
+/**
+ * nm_device_wimax_get_rssi:
+ * @self: a #NMDeviceWimax
+ *
+ * Gets the RSSI of the current radio link in dBm. This value indicates how
+ * strong the raw received RF signal from the base station is, but does not
+ * indicate the overall quality of the radio link. Has no meaning when the
+ * device is not connected.
+ *
+ * Returns: the RSSI in dBm, or 0
+ **/
+gint
+nm_device_wimax_get_rssi (NMDeviceWimax *self)
+{
+ g_return_val_if_fail (NM_IS_DEVICE_WIMAX (self), 0);
+
+ _nm_object_ensure_inited (NM_OBJECT (self));
+ return NM_DEVICE_WIMAX_GET_PRIVATE (self)->rssi;
+}
+
+/**
+ * nm_device_wimax_get_cinr:
+ * @self: a #NMDeviceWimax
+ *
+ * Gets the CINR (Carrier to Interference + Noise Ratio) of the current radio
+ * link in dB. CINR is a more accurate measure of radio link quality. Has no
+ * meaning when the device is not connected.
+ *
+ * Returns: the CINR in dB, or 0
+ **/
+gint
+nm_device_wimax_get_cinr (NMDeviceWimax *self)
+{
+ g_return_val_if_fail (NM_IS_DEVICE_WIMAX (self), 0);
+
+ _nm_object_ensure_inited (NM_OBJECT (self));
+ return NM_DEVICE_WIMAX_GET_PRIVATE (self)->cinr;
+}
+
+/**
+ * nm_device_wimax_get_tx_power:
+ * @self: a #NMDeviceWimax
+ *
+ * Average power of the last burst transmitted by the device, in units of
+ * 0.5 dBm. i.e. a TxPower of -11 represents an actual device TX power of
+ * -5.5 dBm. Has no meaning when the device is not connected.
+ *
+ * Returns: the TX power in dBm, or 0
+ **/
+gint
+nm_device_wimax_get_tx_power (NMDeviceWimax *self)
+{
+ g_return_val_if_fail (NM_IS_DEVICE_WIMAX (self), 0);
+
+ _nm_object_ensure_inited (NM_OBJECT (self));
+ return NM_DEVICE_WIMAX_GET_PRIVATE (self)->tx_power;
+}
+
+/**
+ * nm_device_wimax_get_bsid:
+ * @self: a #NMDeviceWimax
+ *
+ * Gets the ID of the serving Base Station when the device is connected.
+ *
+ * Returns: the ID of the serving Base Station, or %NULL
+ **/
+const char *
+nm_device_wimax_get_bsid (NMDeviceWimax *self)
+{
+ g_return_val_if_fail (NM_IS_DEVICE_WIMAX (self), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (self));
+ return NM_DEVICE_WIMAX_GET_PRIVATE (self)->bsid;
+}
+
+static gboolean
+connection_compatible (NMDevice *device, NMConnection *connection, GError **error)
+{
+ NMSettingConnection *s_con;
+ NMSettingWimax *s_wimax;
+ const char *ctype;
+ const GByteArray *mac;
+ const char *hw_str;
+ struct ether_addr *hw_mac;
+
+ s_con = nm_connection_get_setting_connection (connection);
+ g_assert (s_con);
+
+ ctype = nm_setting_connection_get_connection_type (s_con);
+ if (strcmp (ctype, NM_SETTING_WIMAX_SETTING_NAME) != 0) {
+ g_set_error (error, NM_DEVICE_WIMAX_ERROR, NM_DEVICE_WIMAX_ERROR_NOT_WIMAX_CONNECTION,
+ "The connection was not a Wimax connection.");
+ return FALSE;
+ }
+
+ s_wimax = nm_connection_get_setting_wimax (connection);
+ if (!s_wimax) {
+ g_set_error (error, NM_DEVICE_WIMAX_ERROR, NM_DEVICE_WIMAX_ERROR_INVALID_WIMAX_CONNECTION,
+ "The connection was not a valid Wimax connection.");
+ return FALSE;
+ }
+
+ /* Check MAC address */
+ hw_str = nm_device_wimax_get_hw_address (NM_DEVICE_WIMAX (device));
+ if (hw_str) {
+ hw_mac = ether_aton (hw_str);
+ if (!hw_mac) {
+ g_set_error (error, NM_DEVICE_WIMAX_ERROR, NM_DEVICE_WIMAX_ERROR_INVALID_DEVICE_MAC,
+ "Invalid device MAC address.");
+ return FALSE;
+ }
+ mac = nm_setting_wimax_get_mac_address (s_wimax);
+ if (mac && hw_mac && memcmp (mac->data, hw_mac->ether_addr_octet, ETH_ALEN)) {
+ g_set_error (error, NM_DEVICE_WIMAX_ERROR, NM_DEVICE_WIMAX_ERROR_MAC_MISMATCH,
+ "The MACs of the device and the connection didn't match.");
+ return FALSE;
+ }
+ }
+
+ return NM_DEVICE_CLASS (nm_device_wimax_parent_class)->connection_compatible (device, connection, error);
+}
+
+static GType
+get_setting_type (NMDevice *device)
+{
+ return NM_TYPE_SETTING_WIMAX;
+}
+
+static const char *
+get_hw_address (NMDevice *device)
+{
+ return nm_device_wimax_get_hw_address (NM_DEVICE_WIMAX (device));
+}
+
+/**************************************************************/
+
+static void
+nm_device_wimax_init (NMDeviceWimax *device)
+{
+ _nm_device_set_device_type (NM_DEVICE (device), NM_DEVICE_TYPE_WIMAX);
+}
+
+static void
+get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NMDeviceWimax *self = NM_DEVICE_WIMAX (object);
+
+ _nm_object_ensure_inited (NM_OBJECT (object));
+
+ switch (prop_id) {
+ case PROP_HW_ADDRESS:
+ g_value_set_string (value, nm_device_wimax_get_hw_address (self));
+ break;
+ case PROP_ACTIVE_NSP:
+ g_value_set_object (value, nm_device_wimax_get_active_nsp (self));
+ break;
+ case PROP_CENTER_FREQ:
+ g_value_set_uint (value, nm_device_wimax_get_center_frequency (self));
+ break;
+ case PROP_RSSI:
+ g_value_set_int (value, nm_device_wimax_get_rssi (self));
+ break;
+ case PROP_CINR:
+ g_value_set_int (value, nm_device_wimax_get_cinr (self));
+ break;
+ case PROP_TX_POWER:
+ g_value_set_int (value, nm_device_wimax_get_tx_power (self));
+ break;
+ case PROP_BSID:
+ g_value_set_string (value, nm_device_wimax_get_bsid (self));
+ break;
+ case PROP_NSPS:
+ g_value_set_boxed (value, nm_device_wimax_get_nsps (self));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+clear_link_status (NMDeviceWimax *self)
+{
+ NMDeviceWimaxPrivate *priv = NM_DEVICE_WIMAX_GET_PRIVATE (self);
+
+ if (priv->center_freq) {
+ priv->center_freq = 0;
+ _nm_object_queue_notify (NM_OBJECT (self), NM_DEVICE_WIMAX_CENTER_FREQUENCY);
+ }
+
+ if (priv->rssi) {
+ priv->rssi = 0;
+ _nm_object_queue_notify (NM_OBJECT (self), NM_DEVICE_WIMAX_RSSI);
+ }
+
+ if (priv->cinr) {
+ priv->cinr = 0;
+ _nm_object_queue_notify (NM_OBJECT (self), NM_DEVICE_WIMAX_CINR);
+ }
+
+ if (priv->tx_power) {
+ priv->tx_power = 0;
+ _nm_object_queue_notify (NM_OBJECT (self), NM_DEVICE_WIMAX_TX_POWER);
+ }
+
+ if (priv->bsid) {
+ g_free (priv->bsid);
+ priv->bsid = NULL;
+ _nm_object_queue_notify (NM_OBJECT (self), NM_DEVICE_WIMAX_BSID);
+ }
+}
+
+static void
+state_changed_cb (NMDevice *device, GParamSpec *pspec, gpointer user_data)
+{
+ NMDeviceWimax *self = NM_DEVICE_WIMAX (device);
+ NMDeviceWimaxPrivate *priv = NM_DEVICE_WIMAX_GET_PRIVATE (self);
+ NMDeviceState state;
+
+ state = nm_device_get_state (device);
+ switch (state) {
+ case NM_DEVICE_STATE_UNKNOWN:
+ case NM_DEVICE_STATE_UNMANAGED:
+ case NM_DEVICE_STATE_UNAVAILABLE:
+ case NM_DEVICE_STATE_DISCONNECTED:
+ case NM_DEVICE_STATE_FAILED:
+ if (priv->active_nsp) {
+ g_object_unref (priv->active_nsp);
+ priv->active_nsp = NULL;
+ }
+ _nm_object_queue_notify (NM_OBJECT (device), NM_DEVICE_WIMAX_ACTIVE_NSP);
+ clear_link_status (self);
+ break;
+ case NM_DEVICE_STATE_PREPARE:
+ case NM_DEVICE_STATE_CONFIG:
+ case NM_DEVICE_STATE_NEED_AUTH:
+ case NM_DEVICE_STATE_IP_CONFIG:
+ clear_link_status (self);
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+register_properties (NMDeviceWimax *wimax)
+{
+ NMDeviceWimaxPrivate *priv = NM_DEVICE_WIMAX_GET_PRIVATE (wimax);
+ const NMPropertiesInfo property_info[] = {
+ { NM_DEVICE_WIMAX_HW_ADDRESS, &priv->hw_address },
+ { NM_DEVICE_WIMAX_ACTIVE_NSP, &priv->active_nsp, NULL, NM_TYPE_WIMAX_NSP },
+ { NM_DEVICE_WIMAX_CENTER_FREQUENCY, &priv->center_freq },
+ { NM_DEVICE_WIMAX_RSSI, &priv->rssi },
+ { NM_DEVICE_WIMAX_CINR, &priv->cinr },
+ { NM_DEVICE_WIMAX_TX_POWER, &priv->tx_power },
+ { NM_DEVICE_WIMAX_BSID, &priv->bsid },
+ { NM_DEVICE_WIMAX_NSPS, &priv->nsps, NULL, NM_TYPE_WIMAX_NSP, "nsp" },
+ { NULL },
+ };
+
+ _nm_object_register_properties (NM_OBJECT (wimax),
+ priv->proxy,
+ property_info);
+}
+
+static void
+nsp_removed (NMDeviceWimax *self, NMWimaxNsp *nsp)
+{
+ NMDeviceWimaxPrivate *priv = NM_DEVICE_WIMAX_GET_PRIVATE (self);
+
+ if (nsp == priv->active_nsp) {
+ g_object_unref (priv->active_nsp);
+ priv->active_nsp = NULL;
+ _nm_object_queue_notify (NM_OBJECT (self), NM_DEVICE_WIMAX_ACTIVE_NSP);
+ }
+}
+
+static void
+constructed (GObject *object)
+{
+ NMDeviceWimaxPrivate *priv = NM_DEVICE_WIMAX_GET_PRIVATE (object);
+
+ G_OBJECT_CLASS (nm_device_wimax_parent_class)->constructed (object);
+
+ priv->proxy = _nm_object_new_proxy (NM_OBJECT (object), NULL, NM_DBUS_INTERFACE_DEVICE_WIMAX);
+ register_properties (NM_DEVICE_WIMAX (object));
+
+ g_signal_connect (object,
+ "notify::" NM_DEVICE_STATE,
+ G_CALLBACK (state_changed_cb),
+ NULL);
+}
+
+static void
+dispose (GObject *object)
+{
+ NMDeviceWimaxPrivate *priv = NM_DEVICE_WIMAX_GET_PRIVATE (object);
+
+ if (priv->hw_address) {
+ g_free (priv->hw_address);
+ priv->hw_address = NULL;
+ }
+
+ if (priv->bsid) {
+ g_free (priv->bsid);
+ priv->bsid = NULL;
+ }
+
+ clean_up_nsps (NM_DEVICE_WIMAX (object), FALSE);
+ g_clear_object (&priv->proxy);
+
+ G_OBJECT_CLASS (nm_device_wimax_parent_class)->dispose (object);
+}
+
+static void
+nm_device_wimax_class_init (NMDeviceWimaxClass *wimax_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (wimax_class);
+ NMDeviceClass *device_class = NM_DEVICE_CLASS (wimax_class);
+
+ g_type_class_add_private (wimax_class, sizeof (NMDeviceWimaxPrivate));
+
+ /* virtual methods */
+ object_class->constructed = constructed;
+ object_class->get_property = get_property;
+ object_class->dispose = dispose;
+ device_class->connection_compatible = connection_compatible;
+ device_class->get_setting_type = get_setting_type;
+ device_class->get_hw_address = get_hw_address;
+ wimax_class->nsp_removed = nsp_removed;
+
+ /* properties */
+
+ /**
+ * NMDeviceWimax:hw-address:
+ *
+ * The hardware (MAC) address of the device.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_HW_ADDRESS,
+ g_param_spec_string (NM_DEVICE_WIMAX_HW_ADDRESS, "", "",
+ NULL,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMDeviceWimax:active-nsp:
+ *
+ * The active #NMWimaxNsp of the device.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_ACTIVE_NSP,
+ g_param_spec_object (NM_DEVICE_WIMAX_ACTIVE_NSP, "", "",
+ NM_TYPE_WIMAX_NSP,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMDeviceWimax:center-frequency:
+ *
+ * The center frequency (in KHz) of the radio channel the device is using to
+ * communicate with the network when connected. Has no meaning when the
+ * device is not connected.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_CENTER_FREQ,
+ g_param_spec_uint (NM_DEVICE_WIMAX_CENTER_FREQUENCY, "", "",
+ 0, G_MAXUINT, 0,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMDeviceWimax:rssi:
+ *
+ * RSSI of the current radio link in dBm. This value indicates how strong
+ * the raw received RF signal from the base station is, but does not
+ * indicate the overall quality of the radio link. Has no meaning when the
+ * device is not connected.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_RSSI,
+ g_param_spec_int (NM_DEVICE_WIMAX_RSSI, "", "",
+ G_MININT, G_MAXINT, 0,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMDeviceWimax:cinr:
+ *
+ * CINR (Carrier to Interference + Noise Ratio) of the current radio link
+ * in dB. CINR is a more accurate measure of radio link quality. Has no
+ * meaning when the device is not connected.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_CINR,
+ g_param_spec_int (NM_DEVICE_WIMAX_CINR, "", "",
+ G_MININT, G_MAXINT, 0,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMDeviceWimax:tx-power:
+ *
+ * Average power of the last burst transmitted by the device, in units of
+ * 0.5 dBm. i.e. a TxPower of -11 represents an actual device TX power of
+ * -5.5 dBm. Has no meaning when the device is not connected.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_TX_POWER,
+ g_param_spec_int (NM_DEVICE_WIMAX_TX_POWER, "", "",
+ G_MININT, G_MAXINT, 0,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMDeviceWimax:bsid:
+ *
+ * The ID of the serving base station as received from the network. Has
+ * no meaning when the device is not connected.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_BSID,
+ g_param_spec_string (NM_DEVICE_WIMAX_BSID, "", "",
+ NULL,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMDeviceWimax:nsps:
+ *
+ * List of all WiMAX Network Service Providers the device can see.
+ *
+ * Since: 0.9.10
+ **/
+ g_object_class_install_property
+ (object_class, PROP_NSPS,
+ g_param_spec_boxed (NM_DEVICE_WIMAX_NSPS, "", "",
+ NM_TYPE_OBJECT_ARRAY,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /* signals */
+
+ /**
+ * NMDeviceWimax::nsp-added:
+ * @self: the wimax device that received the signal
+ * @nsp: the new NSP
+ *
+ * Notifies that a #NMWimaxNsp is added to the wimax device.
+ **/
+ signals[NSP_ADDED] =
+ g_signal_new ("nsp-added",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NMDeviceWimaxClass, nsp_added),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1,
+ G_TYPE_OBJECT);
+
+ /**
+ * NMDeviceWimax::nsp-removed:
+ * @self: the wimax device that received the signal
+ * @nsp: the removed NSP
+ *
+ * Notifies that a #NMWimaxNsp is removed from the wimax device.
+ **/
+ signals[NSP_REMOVED] =
+ g_signal_new ("nsp-removed",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NMDeviceWimaxClass, nsp_removed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1,
+ G_TYPE_OBJECT);
+}
diff --git a/libnm/nm-device-wimax.h b/libnm/nm-device-wimax.h
new file mode 100644
index 0000000000..1b889d95fe
--- /dev/null
+++ b/libnm/nm-device-wimax.h
@@ -0,0 +1,97 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2011 - 2012 Red Hat, Inc.
+ * Copyright 2009 Novell, Inc.
+ */
+
+#ifndef NM_DEVICE_WIMAX_H
+#define NM_DEVICE_WIMAX_H
+
+#include "nm-device.h"
+#include "nm-wimax-nsp.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_DEVICE_WIMAX (nm_device_wimax_get_type ())
+#define NM_DEVICE_WIMAX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_WIMAX, NMDeviceWimax))
+#define NM_DEVICE_WIMAX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_WIMAX, NMDeviceWimaxClass))
+#define NM_IS_DEVICE_WIMAX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_WIMAX))
+#define NM_IS_DEVICE_WIMAX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_WIMAX))
+#define NM_DEVICE_WIMAX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_WIMAX, NMDeviceWimaxClass))
+
+/**
+ * NMDeviceWimaxError:
+ * @NM_DEVICE_WIMAX_ERROR_UNKNOWN: unknown or unclassified error
+ * @NM_DEVICE_WIMAX_ERROR_NOT_WIMAX_CONNECTION: the connection was not of WiMax type
+ * @NM_DEVICE_WIMAX_ERROR_INVALID_WIMAX_CONNECTION: the WiMax connection was invalid
+ * @NM_DEVICE_WIMAX_ERROR_INVALID_DEVICE_MAC: the device's MAC was invalid
+ * @NM_DEVICE_WIMAX_ERROR_MAC_MISMATCH: the MACs of the connection and the device mismatched
+ */
+typedef enum {
+ NM_DEVICE_WIMAX_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/
+ NM_DEVICE_WIMAX_ERROR_NOT_WIMAX_CONNECTION, /*< nick=NotWimaxConnection >*/
+ NM_DEVICE_WIMAX_ERROR_INVALID_WIMAX_CONNECTION, /*< nick=InvalidWimaxConnection >*/
+ NM_DEVICE_WIMAX_ERROR_INVALID_DEVICE_MAC, /*< nick=InvalidDeviceMac >*/
+ NM_DEVICE_WIMAX_ERROR_MAC_MISMATCH, /*< nick=MacMismatch >*/
+} NMDeviceWimaxError;
+
+#define NM_DEVICE_WIMAX_ERROR nm_device_wimax_error_quark ()
+GQuark nm_device_wimax_error_quark (void);
+
+#define NM_DEVICE_WIMAX_HW_ADDRESS "hw-address"
+#define NM_DEVICE_WIMAX_ACTIVE_NSP "active-nsp"
+#define NM_DEVICE_WIMAX_CENTER_FREQUENCY "center-frequency"
+#define NM_DEVICE_WIMAX_RSSI "rssi"
+#define NM_DEVICE_WIMAX_CINR "cinr"
+#define NM_DEVICE_WIMAX_TX_POWER "tx-power"
+#define NM_DEVICE_WIMAX_BSID "bsid"
+#define NM_DEVICE_WIMAX_NSPS "nsps"
+
+typedef struct {
+ NMDevice parent;
+} NMDeviceWimax;
+
+typedef struct {
+ NMDeviceClass parent;
+
+ /* Signals */
+ void (*nsp_added) (NMDeviceWimax *self, NMWimaxNsp *nsp);
+ void (*nsp_removed) (NMDeviceWimax *self, NMWimaxNsp *nsp);
+} NMDeviceWimaxClass;
+
+GType nm_device_wimax_get_type (void);
+
+GObject *nm_device_wimax_new (DBusGConnection *connection,
+ const char *path);
+
+const char *nm_device_wimax_get_hw_address (NMDeviceWimax *wimax);
+NMWimaxNsp *nm_device_wimax_get_active_nsp (NMDeviceWimax *wimax);
+NMWimaxNsp *nm_device_wimax_get_nsp_by_path (NMDeviceWimax *wimax,
+ const char *path);
+
+const GPtrArray *nm_device_wimax_get_nsps (NMDeviceWimax *wimax);
+
+guint nm_device_wimax_get_center_frequency (NMDeviceWimax *self);
+gint nm_device_wimax_get_rssi (NMDeviceWimax *self);
+gint nm_device_wimax_get_cinr (NMDeviceWimax *self);
+gint nm_device_wimax_get_tx_power (NMDeviceWimax *self);
+const char * nm_device_wimax_get_bsid (NMDeviceWimax *self);
+
+G_END_DECLS
+
+#endif /* NM_DEVICE_WIMAX_H */
diff --git a/libnm/nm-device.c b/libnm/nm-device.c
new file mode 100644
index 0000000000..64b5dd1861
--- /dev/null
+++ b/libnm/nm-device.c
@@ -0,0 +1,2310 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2008 Novell, Inc.
+ * Copyright 2007 - 2012 Red Hat, Inc.
+ */
+
+#include <string.h>
+
+#include <glib/gi18n.h>
+#include <gudev/gudev.h>
+
+#include "NetworkManager.h"
+#include "nm-device-ethernet.h"
+#include "nm-device-adsl.h"
+#include "nm-device-wifi.h"
+#include "nm-device-modem.h"
+#include "nm-device-bt.h"
+#include "nm-device-olpc-mesh.h"
+#include "nm-device-wimax.h"
+#include "nm-device-infiniband.h"
+#include "nm-device-bond.h"
+#include "nm-device-team.h"
+#include "nm-device-bridge.h"
+#include "nm-device-vlan.h"
+#include "nm-device-generic.h"
+#include "nm-device.h"
+#include "nm-device-private.h"
+#include "nm-object-private.h"
+#include "nm-object-cache.h"
+#include "nm-remote-connection.h"
+#include "nm-types.h"
+#include "nm-dbus-glib-types.h"
+#include "nm-glib-compat.h"
+#include "nm-utils.h"
+#include "nm-dbus-helpers-private.h"
+
+static GType _nm_device_type_for_path (DBusGConnection *connection,
+ const char *path);
+static void _nm_device_type_for_path_async (DBusGConnection *connection,
+ const char *path,
+ NMObjectTypeCallbackFunc callback,
+ gpointer user_data);
+gboolean connection_compatible (NMDevice *device, NMConnection *connection, GError **error);
+
+G_DEFINE_TYPE_WITH_CODE (NMDevice, nm_device, NM_TYPE_OBJECT,
+ _nm_object_register_type_func (g_define_type_id, _nm_device_type_for_path,
+ _nm_device_type_for_path_async);
+ )
+
+#define DBUS_G_TYPE_UINT_STRUCT (dbus_g_type_get_struct ("GValueArray", G_TYPE_UINT, G_TYPE_UINT, G_TYPE_INVALID))
+
+#define NM_DEVICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE, NMDevicePrivate))
+
+typedef struct {
+ DBusGProxy *proxy;
+
+ char *iface;
+ char *ip_iface;
+ NMDeviceType device_type;
+ char *udi;
+ char *driver;
+ char *driver_version;
+ char *firmware_version;
+ char *type_description;
+ NMDeviceCapabilities capabilities;
+ gboolean managed;
+ gboolean firmware_missing;
+ gboolean autoconnect;
+ NMIP4Config *ip4_config;
+ NMDHCP4Config *dhcp4_config;
+ NMIP6Config *ip6_config;
+ NMDHCP6Config *dhcp6_config;
+ NMDeviceState state;
+ NMDeviceState last_seen_state;
+ NMDeviceStateReason reason;
+
+ NMActiveConnection *active_connection;
+ GPtrArray *available_connections;
+
+ GUdevClient *client;
+ char *product, *short_product;
+ char *vendor, *short_vendor;
+ char *description, *bus_name;
+
+ char *physical_port_id;
+ guint32 mtu;
+} NMDevicePrivate;
+
+enum {
+ PROP_0,
+ PROP_INTERFACE,
+ PROP_UDI,
+ PROP_DRIVER,
+ PROP_DRIVER_VERSION,
+ PROP_FIRMWARE_VERSION,
+ PROP_CAPABILITIES,
+ PROP_MANAGED,
+ PROP_AUTOCONNECT,
+ PROP_FIRMWARE_MISSING,
+ PROP_IP4_CONFIG,
+ PROP_DHCP4_CONFIG,
+ PROP_IP6_CONFIG,
+ PROP_STATE,
+ PROP_STATE_REASON,
+ PROP_PRODUCT,
+ PROP_VENDOR,
+ PROP_DHCP6_CONFIG,
+ PROP_IP_INTERFACE,
+ PROP_DEVICE_TYPE,
+ PROP_ACTIVE_CONNECTION,
+ PROP_AVAILABLE_CONNECTIONS,
+ PROP_PHYSICAL_PORT_ID,
+ PROP_MTU,
+
+ LAST_PROP
+};
+
+enum {
+ STATE_CHANGED,
+
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+/**
+ * nm_device_error_quark:
+ *
+ * Registers an error quark for #NMDevice if necessary.
+ *
+ * Returns: the error quark used for #NMDevice errors.
+ *
+ * Since: 0.9.10
+ **/
+GQuark
+nm_device_error_quark (void)
+{
+ static GQuark quark = 0;
+
+ if (G_UNLIKELY (quark == 0))
+ quark = g_quark_from_static_string ("nm-device-error-quark");
+ return quark;
+}
+
+static void
+nm_device_init (NMDevice *device)
+{
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
+
+ priv->state = NM_DEVICE_STATE_UNKNOWN;
+ priv->reason = NM_DEVICE_STATE_REASON_NONE;
+}
+
+static gboolean
+demarshal_state_reason (NMObject *object, GParamSpec *pspec, GValue *value, gpointer field)
+{
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (object);
+
+ if (!G_VALUE_HOLDS (value, DBUS_G_TYPE_UINT_STRUCT))
+ return FALSE;
+
+ dbus_g_type_struct_get (value,
+ 0, &priv->state,
+ 1, &priv->reason,
+ G_MAXUINT);
+
+ _nm_object_queue_notify (object, NM_DEVICE_STATE_REASON);
+ return TRUE;
+}
+
+static void
+register_properties (NMDevice *device)
+{
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
+ const NMPropertiesInfo property_info[] = {
+ { NM_DEVICE_UDI, &priv->udi },
+ { NM_DEVICE_INTERFACE, &priv->iface },
+ { NM_DEVICE_IP_INTERFACE, &priv->ip_iface },
+ { NM_DEVICE_DRIVER, &priv->driver },
+ { NM_DEVICE_DRIVER_VERSION, &priv->driver_version },
+ { NM_DEVICE_FIRMWARE_VERSION, &priv->firmware_version },
+ { NM_DEVICE_CAPABILITIES, &priv->capabilities },
+ { NM_DEVICE_MANAGED, &priv->managed },
+ { NM_DEVICE_AUTOCONNECT, &priv->autoconnect },
+ { NM_DEVICE_FIRMWARE_MISSING, &priv->firmware_missing },
+ { NM_DEVICE_IP4_CONFIG, &priv->ip4_config, NULL, NM_TYPE_IP4_CONFIG },
+ { NM_DEVICE_DHCP4_CONFIG, &priv->dhcp4_config, NULL, NM_TYPE_DHCP4_CONFIG },
+ { NM_DEVICE_IP6_CONFIG, &priv->ip6_config, NULL, NM_TYPE_IP6_CONFIG },
+ { NM_DEVICE_DHCP6_CONFIG, &priv->dhcp6_config, NULL, NM_TYPE_DHCP6_CONFIG },
+ { NM_DEVICE_STATE, &priv->state },
+ { NM_DEVICE_STATE_REASON, &priv->state, demarshal_state_reason },
+ { NM_DEVICE_ACTIVE_CONNECTION, &priv->active_connection, NULL, NM_TYPE_ACTIVE_CONNECTION },
+ { NM_DEVICE_AVAILABLE_CONNECTIONS, &priv->available_connections, NULL, NM_TYPE_REMOTE_CONNECTION },
+ { NM_DEVICE_PHYSICAL_PORT_ID, &priv->physical_port_id },
+ { NM_DEVICE_MTU, &priv->mtu },
+
+ /* Properties that exist in D-Bus but that we don't track */
+ { "ip4-address", NULL },
+ { "device-type", NULL },
+
+ { NULL },
+ };
+
+ _nm_object_register_properties (NM_OBJECT (device),
+ priv->proxy,
+ property_info);
+}
+
+typedef struct {
+ NMDeviceState old_state;
+ NMDeviceState new_state;
+ NMDeviceStateReason reason;
+} StateChangeData;
+
+static void
+device_state_change_reloaded (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ NMDevice *self = NM_DEVICE (object);
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+ StateChangeData *data = user_data;
+ NMDeviceState old_state = data->old_state;
+ NMDeviceState new_state = data->new_state;
+ NMDeviceStateReason reason = data->reason;
+
+ g_slice_free (StateChangeData, data);
+
+ _nm_object_reload_properties_finish (NM_OBJECT (object), result, NULL);
+
+ /* If the device changes state several times in rapid succession, then we'll
+ * queue several reload_properties() calls, and there's no guarantee that
+ * they'll finish in the right order. In that case, only emit the signal
+ * for the last one.
+ */
+ if (priv->last_seen_state != new_state)
+ return;
+
+ /* Ensure that nm_device_get_state() will return the right value even if
+ * we haven't processed the corresponding PropertiesChanged yet.
+ */
+ priv->state = new_state;
+
+ g_signal_emit (self, signals[STATE_CHANGED], 0,
+ new_state, old_state, reason);
+}
+
+static void
+device_state_changed (DBusGProxy *proxy,
+ NMDeviceState new_state,
+ NMDeviceState old_state,
+ NMDeviceStateReason reason,
+ gpointer user_data)
+{
+ NMDevice *self = NM_DEVICE (user_data);
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+ if (old_state != new_state) {
+ StateChangeData *data;
+
+ /* Our object-valued properties (eg, ip4_config) will still
+ * have their old values at this point, because NMObject is
+ * in the process of asynchronously reading the new values.
+ * Wait for that to finish before emitting the signal.
+ */
+ priv->last_seen_state = new_state;
+
+ data = g_slice_new (StateChangeData);
+ data->old_state = old_state;
+ data->new_state = new_state;
+ data->reason = reason;
+ _nm_object_reload_properties_async (NM_OBJECT (user_data),
+ device_state_change_reloaded,
+ data);
+ }
+}
+
+static GType
+_nm_device_gtype_from_dtype (NMDeviceType dtype)
+{
+ switch (dtype) {
+ case NM_DEVICE_TYPE_ETHERNET:
+ return NM_TYPE_DEVICE_ETHERNET;
+ case NM_DEVICE_TYPE_WIFI:
+ return NM_TYPE_DEVICE_WIFI;
+ case NM_DEVICE_TYPE_MODEM:
+ return NM_TYPE_DEVICE_MODEM;
+ case NM_DEVICE_TYPE_BT:
+ return NM_TYPE_DEVICE_BT;
+ case NM_DEVICE_TYPE_ADSL:
+ return NM_TYPE_DEVICE_ADSL;
+ case NM_DEVICE_TYPE_OLPC_MESH:
+ return NM_TYPE_DEVICE_OLPC_MESH;
+ case NM_DEVICE_TYPE_WIMAX:
+ return NM_TYPE_DEVICE_WIMAX;
+ case NM_DEVICE_TYPE_INFINIBAND:
+ return NM_TYPE_DEVICE_INFINIBAND;
+ case NM_DEVICE_TYPE_BOND:
+ return NM_TYPE_DEVICE_BOND;
+ case NM_DEVICE_TYPE_TEAM:
+ return NM_TYPE_DEVICE_TEAM;
+ case NM_DEVICE_TYPE_BRIDGE:
+ return NM_TYPE_DEVICE_BRIDGE;
+ case NM_DEVICE_TYPE_VLAN:
+ return NM_TYPE_DEVICE_VLAN;
+ case NM_DEVICE_TYPE_GENERIC:
+ return NM_TYPE_DEVICE_GENERIC;
+ default:
+ g_warning ("Unknown device type %d", dtype);
+ return G_TYPE_INVALID;
+ }
+}
+
+static void
+constructed (GObject *object)
+{
+ NMDevicePrivate *priv;
+
+ G_OBJECT_CLASS (nm_device_parent_class)->constructed (object);
+
+ priv = NM_DEVICE_GET_PRIVATE (object);
+ /* Catch failure of subclasses to call _nm_device_set_device_type() */
+ g_warn_if_fail (priv->device_type != NM_DEVICE_TYPE_UNKNOWN);
+ /* Catch a subclass setting the wrong type */
+ g_warn_if_fail (G_OBJECT_TYPE (object) == _nm_device_gtype_from_dtype (priv->device_type));
+
+ priv->proxy = _nm_object_new_proxy (NM_OBJECT (object), NULL, NM_DBUS_INTERFACE_DEVICE);
+
+ register_properties (NM_DEVICE (object));
+
+ dbus_g_object_register_marshaller (g_cclosure_marshal_generic,
+ G_TYPE_NONE,
+ G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT,
+ G_TYPE_INVALID);
+
+ dbus_g_proxy_add_signal (priv->proxy,
+ "StateChanged",
+ G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT,
+ G_TYPE_INVALID);
+
+ dbus_g_proxy_connect_signal (priv->proxy, "StateChanged",
+ G_CALLBACK (device_state_changed),
+ NM_DEVICE (object),
+ NULL);
+}
+
+static void
+dispose (GObject *object)
+{
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (object);
+
+ g_clear_object (&priv->proxy);
+ g_clear_object (&priv->ip4_config);
+ g_clear_object (&priv->dhcp4_config);
+ g_clear_object (&priv->ip6_config);
+ g_clear_object (&priv->dhcp6_config);
+ g_clear_object (&priv->client);
+ g_clear_object (&priv->active_connection);
+
+ if (priv->available_connections) {
+ int i;
+
+ for (i = 0; i < priv->available_connections->len; i++)
+ g_object_unref (priv->available_connections->pdata[i]);
+ g_ptr_array_free (priv->available_connections, TRUE);
+ priv->available_connections = NULL;
+ }
+
+ G_OBJECT_CLASS (nm_device_parent_class)->dispose (object);
+}
+
+static void
+finalize (GObject *object)
+{
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (object);
+
+ g_free (priv->iface);
+ g_free (priv->ip_iface);
+ g_free (priv->udi);
+ g_free (priv->driver);
+ g_free (priv->driver_version);
+ g_free (priv->firmware_version);
+ g_free (priv->product);
+ g_free (priv->short_product);
+ g_free (priv->vendor);
+ g_free (priv->short_vendor);
+ g_free (priv->description);
+ g_free (priv->bus_name);
+ g_free (priv->type_description);
+ g_free (priv->physical_port_id);
+
+ G_OBJECT_CLASS (nm_device_parent_class)->finalize (object);
+}
+
+static void
+get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NMDevice *device = NM_DEVICE (object);
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
+
+ _nm_object_ensure_inited (NM_OBJECT (object));
+
+ switch (prop_id) {
+ case PROP_DEVICE_TYPE:
+ g_value_set_uint (value, nm_device_get_device_type (device));
+ break;
+ case PROP_UDI:
+ g_value_set_string (value, nm_device_get_udi (device));
+ break;
+ case PROP_INTERFACE:
+ g_value_set_string (value, nm_device_get_iface (device));
+ break;
+ case PROP_IP_INTERFACE:
+ g_value_set_string (value, nm_device_get_ip_iface (device));
+ break;
+ case PROP_DRIVER:
+ g_value_set_string (value, nm_device_get_driver (device));
+ break;
+ case PROP_DRIVER_VERSION:
+ g_value_set_string (value, nm_device_get_driver_version (device));
+ break;
+ case PROP_FIRMWARE_VERSION:
+ g_value_set_string (value, nm_device_get_firmware_version (device));
+ break;
+ case PROP_CAPABILITIES:
+ g_value_set_uint (value, nm_device_get_capabilities (device));
+ break;
+ case PROP_MANAGED:
+ g_value_set_boolean (value, nm_device_get_managed (device));
+ break;
+ case PROP_AUTOCONNECT:
+ g_value_set_boolean (value, nm_device_get_autoconnect (device));
+ break;
+ case PROP_FIRMWARE_MISSING:
+ g_value_set_boolean (value, nm_device_get_firmware_missing (device));
+ break;
+ case PROP_IP4_CONFIG:
+ g_value_set_object (value, nm_device_get_ip4_config (device));
+ break;
+ case PROP_DHCP4_CONFIG:
+ g_value_set_object (value, nm_device_get_dhcp4_config (device));
+ break;
+ case PROP_IP6_CONFIG:
+ g_value_set_object (value, nm_device_get_ip6_config (device));
+ break;
+ case PROP_DHCP6_CONFIG:
+ g_value_set_object (value, nm_device_get_dhcp6_config (device));
+ break;
+ case PROP_STATE:
+ g_value_set_uint (value, nm_device_get_state (device));
+ break;
+ case PROP_STATE_REASON:
+ g_value_set_boxed (value,
+ dbus_g_type_specialized_construct (DBUS_G_TYPE_UINT_STRUCT));
+ dbus_g_type_struct_set (value,
+ 0, priv->state,
+ 1, priv->reason,
+ G_MAXUINT);
+ break;
+ case PROP_ACTIVE_CONNECTION:
+ g_value_set_object (value, nm_device_get_active_connection (device));
+ break;
+ case PROP_AVAILABLE_CONNECTIONS:
+ g_value_set_boxed (value, nm_device_get_available_connections (device));
+ break;
+ case PROP_PRODUCT:
+ g_value_set_string (value, nm_device_get_product (device));
+ break;
+ case PROP_VENDOR:
+ g_value_set_string (value, nm_device_get_vendor (device));
+ break;
+ case PROP_PHYSICAL_PORT_ID:
+ g_value_set_string (value, nm_device_get_physical_port_id (device));
+ break;
+ case PROP_MTU:
+ g_value_set_uint (value, nm_device_get_mtu (device));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ NMDevice *self = NM_DEVICE (object);
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+ gboolean b;
+
+ switch (prop_id) {
+ case PROP_DEVICE_TYPE:
+ /* Construct only */
+ priv->device_type = g_value_get_uint (value);
+ break;
+ case PROP_AUTOCONNECT:
+ b = g_value_get_boolean (value);
+ if (priv->autoconnect != b)
+ nm_device_set_autoconnect (NM_DEVICE (object), b);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nm_device_class_init (NMDeviceClass *device_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (device_class);
+
+ g_type_class_add_private (device_class, sizeof (NMDevicePrivate));
+
+ /* virtual methods */
+ object_class->constructed = constructed;
+ object_class->get_property = get_property;
+ object_class->set_property = set_property;
+ object_class->dispose = dispose;
+ object_class->finalize = finalize;
+
+ device_class->connection_compatible = connection_compatible;
+
+ /* properties */
+
+ /**
+ * NMDevice:interface:
+ *
+ * The interface of the device.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_INTERFACE,
+ g_param_spec_string (NM_DEVICE_INTERFACE, "", "",
+ NULL,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMDevice:ip-interface:
+ *
+ * The IP interface of the device which should be used for all IP-related
+ * operations like addressing and routing.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_IP_INTERFACE,
+ g_param_spec_string (NM_DEVICE_IP_INTERFACE, "", "",
+ NULL,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMDevice:device-type:
+ *
+ * The numeric type of the device.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_DEVICE_TYPE,
+ g_param_spec_uint (NM_DEVICE_DEVICE_TYPE, "", "",
+ NM_DEVICE_TYPE_UNKNOWN, G_MAXUINT32, NM_DEVICE_TYPE_UNKNOWN,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+ /**
+ * NMDevice:udi:
+ *
+ * An operating-system specific device hardware identifier; this is not
+ * unique to a specific hardware device across reboots or hotplugs. It
+ * is an opaque string which for some device types (Bluetooth, Modem)
+ * contains an identifier provided by the underlying hardware service daemon
+ * such as Bluez or ModemManager, and clients can use this property to
+ * request more information about the device from those services.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_UDI,
+ g_param_spec_string (NM_DEVICE_UDI, "", "",
+ NULL,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMDevice:driver:
+ *
+ * The driver of the device.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_DRIVER,
+ g_param_spec_string (NM_DEVICE_DRIVER, "", "",
+ NULL,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMDevice:driver-version:
+ *
+ * The version of the device driver.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_DRIVER_VERSION,
+ g_param_spec_string (NM_DEVICE_DRIVER_VERSION, "", "",
+ NULL,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMDevice:firmware-version:
+ *
+ * The firmware version of the device.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_FIRMWARE_VERSION,
+ g_param_spec_string (NM_DEVICE_FIRMWARE_VERSION, "", "",
+ NULL,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMDevice:capabilities:
+ *
+ * The capabilities of the device.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_CAPABILITIES,
+ g_param_spec_uint (NM_DEVICE_CAPABILITIES, "", "",
+ 0, G_MAXUINT32, 0,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMDevice:managed:
+ *
+ * Whether the device is managed by NetworkManager.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_MANAGED,
+ g_param_spec_boolean (NM_DEVICE_MANAGED, "", "",
+ FALSE,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMDevice:autoconnect:
+ *
+ * Whether the device can auto-activate a connection.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_AUTOCONNECT,
+ g_param_spec_boolean (NM_DEVICE_AUTOCONNECT, "", "",
+ TRUE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMDevice:firmware-missing:
+ *
+ * When %TRUE indicates the device is likely missing firmware required
+ * for its operation.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_FIRMWARE_MISSING,
+ g_param_spec_boolean (NM_DEVICE_FIRMWARE_MISSING, "", "",
+ FALSE,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMDevice:ip4-config:
+ *
+ * The #NMIP4Config of the device.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_IP4_CONFIG,
+ g_param_spec_object (NM_DEVICE_IP4_CONFIG, "", "",
+ NM_TYPE_IP4_CONFIG,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMDevice:dhcp4-config:
+ *
+ * The #NMDHCP4Config of the device.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_DHCP4_CONFIG,
+ g_param_spec_object (NM_DEVICE_DHCP4_CONFIG, "", "",
+ NM_TYPE_DHCP4_CONFIG,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMDevice:ip6-config:
+ *
+ * The #NMIP6Config of the device.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_IP6_CONFIG,
+ g_param_spec_object (NM_DEVICE_IP6_CONFIG, "", "",
+ NM_TYPE_IP6_CONFIG,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMDevice:dhcp6-config:
+ *
+ * The #NMDHCP6Config of the device.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_DHCP6_CONFIG,
+ g_param_spec_object (NM_DEVICE_DHCP6_CONFIG, "", "",
+ NM_TYPE_DHCP6_CONFIG,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMDevice:state:
+ *
+ * The state of the device.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_STATE,
+ g_param_spec_uint (NM_DEVICE_STATE, "", "",
+ 0, G_MAXUINT32, 0,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMDevice:state-reason:
+ *
+ * The state and reason of the device.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_STATE_REASON,
+ g_param_spec_boxed (NM_DEVICE_STATE_REASON, "", "",
+ DBUS_G_TYPE_UINT_STRUCT,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMDevice:active-connection:
+ *
+ * The #NMActiveConnection object that "owns" this device during activation.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_ACTIVE_CONNECTION,
+ g_param_spec_object (NM_DEVICE_ACTIVE_CONNECTION, "", "",
+ NM_TYPE_ACTIVE_CONNECTION,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMDevice:available-connections:
+ *
+ * The available connections (#NMRemoteConnection) of the device
+ *
+ * Since: 0.9.8
+ **/
+ g_object_class_install_property
+ (object_class, PROP_AVAILABLE_CONNECTIONS,
+ g_param_spec_boxed (NM_DEVICE_AVAILABLE_CONNECTIONS, "", "",
+ NM_TYPE_OBJECT_ARRAY,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMDevice:vendor:
+ *
+ * The vendor string of the device.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_VENDOR,
+ g_param_spec_string (NM_DEVICE_VENDOR, "", "",
+ NULL,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMDevice:product:
+ *
+ * The product string of the device.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PRODUCT,
+ g_param_spec_string (NM_DEVICE_PRODUCT, "", "",
+ NULL,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMDevice:physical-port-id:
+ *
+ * The physical port ID of the device. (See
+ * nm_device_get_physical_port_id().)
+ *
+ * Since: 0.9.10
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PHYSICAL_PORT_ID,
+ g_param_spec_string (NM_DEVICE_PHYSICAL_PORT_ID, "", "",
+ NULL,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMDevice:mtu:
+ *
+ * The MTU of the device.
+ *
+ * Since: 0.9.10
+ **/
+ g_object_class_install_property
+ (object_class, PROP_MTU,
+ g_param_spec_uint (NM_DEVICE_MTU, "", "",
+ 0, G_MAXUINT32, 1500,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /* signals */
+
+ /**
+ * NMDevice::state-changed:
+ * @device: the device object that received the signal
+ * @new_state: the new state of the device
+ * @old_state: the previous state of the device
+ * @reason: the reason describing the state change
+ *
+ * Notifies the state change of a #NMDevice.
+ **/
+ signals[STATE_CHANGED] =
+ g_signal_new ("state-changed",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NMDeviceClass, state_changed),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 3,
+ G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT);
+}
+
+/**
+ * _nm_device_set_device_type:
+ * @device: the device
+ * @dtype: the NM device type
+ *
+ * Sets the NM device type if it wasn't set during construction. INTERNAL
+ * ONLY METHOD.
+ **/
+void
+_nm_device_set_device_type (NMDevice *device, NMDeviceType dtype)
+{
+ NMDevicePrivate *priv;
+
+ g_return_if_fail (device != NULL);
+ g_return_if_fail (dtype != NM_DEVICE_TYPE_UNKNOWN);
+
+ priv = NM_DEVICE_GET_PRIVATE (device);
+ if (priv->device_type == NM_DEVICE_TYPE_UNKNOWN)
+ priv->device_type = dtype;
+ else
+ g_warn_if_fail (dtype == priv->device_type);
+}
+
+static GType
+_nm_device_type_for_path (DBusGConnection *connection,
+ const char *path)
+{
+ DBusGProxy *proxy;
+ GError *err = NULL;
+ GValue value = G_VALUE_INIT;
+ NMDeviceType nm_dtype;
+
+ proxy = _nm_dbus_new_proxy_for_connection (connection, path, "org.freedesktop.DBus.Properties");
+ if (!proxy) {
+ g_warning ("%s: couldn't create D-Bus object proxy.", __func__);
+ return G_TYPE_INVALID;
+ }
+
+ if (!dbus_g_proxy_call (proxy,
+ "Get", &err,
+ G_TYPE_STRING, NM_DBUS_INTERFACE_DEVICE,
+ G_TYPE_STRING, "DeviceType",
+ G_TYPE_INVALID,
+ G_TYPE_VALUE, &value, G_TYPE_INVALID)) {
+ g_warning ("Error in get_property: %s\n", err->message);
+ g_error_free (err);
+ g_object_unref (proxy);
+ return G_TYPE_INVALID;
+ }
+ g_object_unref (proxy);
+
+ nm_dtype = g_value_get_uint (&value);
+ return _nm_device_gtype_from_dtype (nm_dtype);
+}
+
+/**
+ * nm_device_new:
+ * @connection: the #DBusGConnection
+ * @path: the DBus object path of the device
+ *
+ * Creates a new #NMDevice.
+ *
+ * Returns: (transfer full): a new device
+ **/
+GObject *
+nm_device_new (DBusGConnection *connection, const char *path)
+{
+ GType dtype;
+ NMDevice *device = NULL;
+
+ g_return_val_if_fail (connection != NULL, NULL);
+ g_return_val_if_fail (path != NULL, NULL);
+
+ dtype = _nm_device_type_for_path (connection, path);
+ if (dtype == G_TYPE_INVALID)
+ return NULL;
+
+ device = (NMDevice *) g_object_new (dtype,
+ NM_OBJECT_DBUS_CONNECTION, connection,
+ NM_OBJECT_DBUS_PATH, path,
+ NULL);
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return G_OBJECT (device);
+}
+
+typedef struct {
+ DBusGConnection *connection;
+ NMObjectTypeCallbackFunc callback;
+ gpointer user_data;
+} NMDeviceAsyncData;
+
+static void
+async_got_type (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data)
+{
+ NMDeviceAsyncData *async_data = user_data;
+ GValue value = G_VALUE_INIT;
+ const char *path = dbus_g_proxy_get_path (proxy);
+ GError *error = NULL;
+ GType type;
+
+ if (dbus_g_proxy_end_call (proxy, call, &error,
+ G_TYPE_VALUE, &value,
+ G_TYPE_INVALID)) {
+ NMDeviceType dtype;
+
+ dtype = g_value_get_uint (&value);
+ type = _nm_device_gtype_from_dtype (dtype);
+ } else {
+ g_warning ("%s: could not read properties for %s: %s", __func__, path, error->message);
+ g_error_free (error);
+ type = G_TYPE_INVALID;
+ }
+
+ async_data->callback (type, async_data->user_data);
+ g_object_unref (proxy);
+ g_slice_free (NMDeviceAsyncData, async_data);
+}
+
+static void
+_nm_device_type_for_path_async (DBusGConnection *connection,
+ const char *path,
+ NMObjectTypeCallbackFunc callback,
+ gpointer user_data)
+{
+ NMDeviceAsyncData *async_data;
+ DBusGProxy *proxy;
+
+ async_data = g_slice_new (NMDeviceAsyncData);
+ async_data->connection = connection;
+ async_data->callback = callback;
+ async_data->user_data = user_data;
+
+ proxy = _nm_dbus_new_proxy_for_connection (connection, path, "org.freedesktop.DBus.Properties");
+ dbus_g_proxy_begin_call (proxy, "Get",
+ async_got_type, async_data, NULL,
+ G_TYPE_STRING, NM_DBUS_INTERFACE_DEVICE,
+ G_TYPE_STRING, "DeviceType",
+ G_TYPE_INVALID);
+}
+
+/**
+ * nm_device_get_iface:
+ * @device: a #NMDevice
+ *
+ * Gets the interface name of the #NMDevice.
+ *
+ * Returns: the interface of the device. This is the internal string used by the
+ * device, and must not be modified.
+ **/
+const char *
+nm_device_get_iface (NMDevice *device)
+{
+ g_return_val_if_fail (NM_IS_DEVICE (device), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return NM_DEVICE_GET_PRIVATE (device)->iface;
+}
+
+/**
+ * nm_device_get_ip_iface:
+ * @device: a #NMDevice
+ *
+ * Gets the IP interface name of the #NMDevice over which IP traffic flows
+ * when the device is in the ACTIVATED state.
+ *
+ * Returns: the IP traffic interface of the device. This is the internal string
+ * used by the device, and must not be modified.
+ **/
+const char *
+nm_device_get_ip_iface (NMDevice *device)
+{
+ g_return_val_if_fail (NM_IS_DEVICE (device), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return NM_DEVICE_GET_PRIVATE (device)->ip_iface;
+}
+
+/**
+ * nm_device_get_device_type:
+ * @device: a #NMDevice
+ *
+ * Returns the numeric type of the #NMDevice, ie Ethernet, Wi-Fi, etc.
+ *
+ * Returns: the device type
+ **/
+NMDeviceType
+nm_device_get_device_type (NMDevice *self)
+{
+ g_return_val_if_fail (NM_IS_DEVICE (self), NM_DEVICE_TYPE_UNKNOWN);
+
+ return NM_DEVICE_GET_PRIVATE (self)->device_type;
+}
+
+/**
+ * nm_device_get_udi:
+ * @device: a #NMDevice
+ *
+ * Gets the Unique Device Identifier of the #NMDevice.
+ *
+ * Returns: the Unique Device Identifier of the device. This identifier may be
+ * used to gather more information about the device from various operating
+ * system services like udev or sysfs.
+ **/
+const char *
+nm_device_get_udi (NMDevice *device)
+{
+ g_return_val_if_fail (NM_IS_DEVICE (device), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return NM_DEVICE_GET_PRIVATE (device)->udi;
+}
+
+/**
+ * nm_device_get_driver:
+ * @device: a #NMDevice
+ *
+ * Gets the driver of the #NMDevice.
+ *
+ * Returns: the driver of the device. This is the internal string used by the
+ * device, and must not be modified.
+ **/
+const char *
+nm_device_get_driver (NMDevice *device)
+{
+ g_return_val_if_fail (NM_IS_DEVICE (device), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return NM_DEVICE_GET_PRIVATE (device)->driver;
+}
+
+/**
+ * nm_device_get_driver_version:
+ * @device: a #NMDevice
+ *
+ * Gets the driver version of the #NMDevice.
+ *
+ * Returns: the version of the device driver. This is the internal string used by the
+ * device, and must not be modified.
+ **/
+const char *
+nm_device_get_driver_version (NMDevice *device)
+{
+ g_return_val_if_fail (NM_IS_DEVICE (device), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return NM_DEVICE_GET_PRIVATE (device)->driver_version;
+}
+
+/**
+ * nm_device_get_firmware_version:
+ * @device: a #NMDevice
+ *
+ * Gets the firmware version of the #NMDevice.
+ *
+ * Returns: the firmware version of the device. This is the internal string used by the
+ * device, and must not be modified.
+ **/
+const char *
+nm_device_get_firmware_version (NMDevice *device)
+{
+ g_return_val_if_fail (NM_IS_DEVICE (device), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return NM_DEVICE_GET_PRIVATE (device)->firmware_version;
+}
+
+/**
+ * nm_device_get_type_description:
+ * @device: a #NMDevice
+ *
+ * Gets a (non-localized) description of the type of device that
+ * @device is.
+ *
+ * Returns: the type description of the device. This is the internal
+ * string used by the device, and must not be modified.
+ *
+ * Since: 0.9.10
+ **/
+const char *
+nm_device_get_type_description (NMDevice *device)
+{
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
+ const char *desc, *typename;
+
+ g_return_val_if_fail (NM_IS_DEVICE (device), NULL);
+
+ if (priv->type_description)
+ return priv->type_description;
+
+ if (NM_DEVICE_GET_CLASS (device)->get_type_description) {
+ desc = NM_DEVICE_GET_CLASS (device)->get_type_description (device);
+ if (desc)
+ return desc;
+ }
+
+ typename = G_OBJECT_TYPE_NAME (device);
+ if (g_str_has_prefix (typename, "NMDevice"))
+ typename += 8;
+ priv->type_description = g_ascii_strdown (typename, -1);
+
+ return priv->type_description;
+}
+
+/**
+ * nm_device_get_hw_address:
+ * @device: a #NMDevice
+ *
+ * Gets the current a hardware address (MAC) for the @device.
+ *
+ * Returns: the current MAC of the device, or %NULL.
+ * This is the internal string used by the device, and must not be modified.
+ *
+ * Since: 0.9.10
+ **/
+const char *
+nm_device_get_hw_address (NMDevice *device)
+{
+ g_return_val_if_fail (NM_IS_DEVICE (device), NULL);
+
+ if (NM_DEVICE_GET_CLASS (device)->get_hw_address)
+ return NM_DEVICE_GET_CLASS (device)->get_hw_address (device);
+
+ return NULL;
+}
+
+/**
+ * nm_device_get_capabilities:
+ * @device: a #NMDevice
+ *
+ * Gets the device' capabilities.
+ *
+ * Returns: the capabilities
+ **/
+NMDeviceCapabilities
+nm_device_get_capabilities (NMDevice *device)
+{
+ g_return_val_if_fail (NM_IS_DEVICE (device), 0);
+
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return NM_DEVICE_GET_PRIVATE (device)->capabilities;
+}
+
+/**
+ * nm_device_get_managed:
+ * @device: a #NMDevice
+ *
+ * Whether the #NMDevice is managed by NetworkManager.
+ *
+ * Returns: %TRUE if the device is managed by NetworkManager
+ **/
+gboolean
+nm_device_get_managed (NMDevice *device)
+{
+ g_return_val_if_fail (NM_IS_DEVICE (device), 0);
+
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return NM_DEVICE_GET_PRIVATE (device)->managed;
+}
+
+/**
+ * nm_device_get_autoconnect:
+ * @device: a #NMDevice
+ *
+ * Whether the #NMDevice can be autoconnected.
+ *
+ * Returns: %TRUE if the device is allowed to be autoconnected
+ **/
+gboolean
+nm_device_get_autoconnect (NMDevice *device)
+{
+ g_return_val_if_fail (NM_IS_DEVICE (device), FALSE);
+
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return NM_DEVICE_GET_PRIVATE (device)->autoconnect;
+}
+
+/**
+ * nm_device_set_autoconnect:
+ * @device: a #NMDevice
+ * @autoconnect: %TRUE to enable autoconnecting
+ *
+ * Enables or disables automatic activation of the #NMDevice.
+ **/
+void
+nm_device_set_autoconnect (NMDevice *device, gboolean autoconnect)
+{
+ GValue value = G_VALUE_INIT;
+
+ g_return_if_fail (NM_IS_DEVICE (device));
+
+ g_value_init (&value, G_TYPE_BOOLEAN);
+ g_value_set_boolean (&value, autoconnect);
+
+
+ NM_DEVICE_GET_PRIVATE (device)->autoconnect = autoconnect;
+
+ _nm_object_set_property (NM_OBJECT (device),
+ NM_DBUS_INTERFACE_DEVICE,
+ "Autoconnect",
+ &value);
+}
+
+/**
+ * nm_device_get_firmware_missing:
+ * @device: a #NMDevice
+ *
+ * Indicates that firmware required for the device's operation is likely
+ * to be missing.
+ *
+ * Returns: %TRUE if firmware required for the device's operation is likely
+ * to be missing.
+ **/
+gboolean
+nm_device_get_firmware_missing (NMDevice *device)
+{
+ g_return_val_if_fail (NM_IS_DEVICE (device), 0);
+
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return NM_DEVICE_GET_PRIVATE (device)->firmware_missing;
+}
+
+/**
+ * nm_device_get_ip4_config:
+ * @device: a #NMDevice
+ *
+ * Gets the current #NMIP4Config associated with the #NMDevice.
+ *
+ * Note that as of NetworkManager 0.9.10, you can alternatively use
+ * nm_active_connection_get_ip4_config(), which also works with VPN
+ * connections.
+ *
+ * Returns: (transfer none): the #NMIP4Config or %NULL if the device is not activated.
+ **/
+NMIP4Config *
+nm_device_get_ip4_config (NMDevice *device)
+{
+ g_return_val_if_fail (NM_IS_DEVICE (device), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return NM_DEVICE_GET_PRIVATE (device)->ip4_config;
+}
+
+/**
+ * nm_device_get_dhcp4_config:
+ * @device: a #NMDevice
+ *
+ * Gets the current #NMDHCP4Config associated with the #NMDevice.
+ *
+ * Note that as of NetworkManager 0.9.10, you can alternatively use
+ * nm_active_connection_get_dhcp4_config(), which also works with VPN
+ * connections.
+ *
+ * Returns: (transfer none): the #NMDHCP4Config or %NULL if the device is not activated or not
+ * using DHCP.
+ **/
+NMDHCP4Config *
+nm_device_get_dhcp4_config (NMDevice *device)
+{
+ g_return_val_if_fail (NM_IS_DEVICE (device), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return NM_DEVICE_GET_PRIVATE (device)->dhcp4_config;
+}
+
+/**
+ * nm_device_get_ip6_config:
+ * @device: a #NMDevice
+ *
+ * Gets the current #NMIP6Config associated with the #NMDevice.
+ *
+ * Note that as of NetworkManager 0.9.10, you can alternatively use
+ * nm_active_connection_get_ip6_config(), which also works with VPN
+ * connections.
+ *
+ * Returns: (transfer none): the #NMIP6Config or %NULL if the device is not activated.
+ **/
+NMIP6Config *
+nm_device_get_ip6_config (NMDevice *device)
+{
+ g_return_val_if_fail (NM_IS_DEVICE (device), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return NM_DEVICE_GET_PRIVATE (device)->ip6_config;
+}
+
+/**
+ * nm_device_get_dhcp6_config:
+ * @device: a #NMDevice
+ *
+ * Gets the current #NMDHCP6Config associated with the #NMDevice.
+ *
+ * Note that as of NetworkManager 0.9.10, you can alternatively use
+ * nm_active_connection_get_dhcp6_config(), which also works with VPN
+ * connections.
+ *
+ * Returns: (transfer none): the #NMDHCP6Config or %NULL if the device is not activated or not
+ * using DHCP.
+ **/
+NMDHCP6Config *
+nm_device_get_dhcp6_config (NMDevice *device)
+{
+ g_return_val_if_fail (NM_IS_DEVICE (device), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return NM_DEVICE_GET_PRIVATE (device)->dhcp6_config;
+}
+
+/**
+ * nm_device_get_state:
+ * @device: a #NMDevice
+ *
+ * Gets the current #NMDevice state.
+ *
+ * Returns: the current device state
+ **/
+NMDeviceState
+nm_device_get_state (NMDevice *device)
+{
+ g_return_val_if_fail (NM_IS_DEVICE (device), NM_DEVICE_STATE_UNKNOWN);
+
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return NM_DEVICE_GET_PRIVATE (device)->state;
+}
+
+/**
+ * nm_device_get_state_reason:
+ * @device: a #NMDevice
+ * @reason: (out) (allow-none): location to store reason (#NMDeviceStateReason), or %NULL
+ *
+ * Gets the current #NMDevice state (return value) and the reason for entering
+ * the state (@reason argument).
+ *
+ * Returns: the current device state
+ **/
+NMDeviceState
+nm_device_get_state_reason (NMDevice *device, NMDeviceStateReason *reason)
+{
+ g_return_val_if_fail (NM_IS_DEVICE (device), NM_DEVICE_STATE_UNKNOWN);
+
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ if (reason)
+ *reason = NM_DEVICE_GET_PRIVATE (device)->reason;
+ return NM_DEVICE_GET_PRIVATE (device)->state;
+}
+
+/**
+ * nm_device_get_active_connection:
+ * @device: a #NMDevice
+ *
+ * Gets the #NMActiveConnection object which owns this device during activation.
+ *
+ * Returns: (transfer none): the #NMActiveConnection or %NULL if the device is
+ * not part of an active connection
+ **/
+NMActiveConnection *
+nm_device_get_active_connection (NMDevice *device)
+{
+ g_return_val_if_fail (NM_IS_DEVICE (device), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return NM_DEVICE_GET_PRIVATE (device)->active_connection;
+}
+
+/**
+ * nm_device_get_available_connections:
+ * @device: a #NMDevice
+ *
+ * Gets the #NMRemoteConnections currently known to the daemon that could
+ * be activated on @device.
+ *
+ * Returns: (element-type NMRemoteConnection): the #GPtrArray
+ * containing #NMRemoteConnections. This is the internal copy used by
+ * the connection, and must not be modified.
+ *
+ * Since: 0.9.8
+ **/
+const GPtrArray *
+nm_device_get_available_connections (NMDevice *device)
+{
+ g_return_val_if_fail (NM_IS_DEVICE (device), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return handle_ptr_array_return (NM_DEVICE_GET_PRIVATE (device)->available_connections);
+}
+
+static char *
+get_decoded_property (GUdevDevice *device, const char *property)
+{
+ const char *orig, *p;
+ char *unescaped, *n;
+ guint len;
+
+ p = orig = g_udev_device_get_property (device, property);
+ if (!orig)
+ return NULL;
+
+ len = strlen (orig);
+ n = unescaped = g_malloc0 (len + 1);
+ while (*p) {
+ if ((len >= 4) && (*p == '\\') && (*(p+1) == 'x')) {
+ *n++ = (char) nm_utils_hex2byte (p + 2);
+ p += 4;
+ len -= 4;
+ } else {
+ *n++ = *p++;
+ len--;
+ }
+ }
+
+ return unescaped;
+}
+
+static gboolean
+ensure_udev_client (NMDevice *device)
+{
+ static const char *const subsys[3] = { "net", "tty", NULL };
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
+
+ if (!priv->client)
+ priv->client = g_udev_client_new (subsys);
+
+ return priv->client != NULL;
+}
+
+static char *
+_get_udev_property (NMDevice *device,
+ const char *enc_prop, /* ID_XXX_ENC */
+ const char *db_prop) /* ID_XXX_FROM_DATABASE */
+{
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
+ GUdevDevice *udev_device = NULL, *tmpdev, *olddev;
+ const char *ifname;
+ guint32 count = 0;
+ char *enc_value = NULL, *db_value = NULL;
+
+ if (!ensure_udev_client (device))
+ return NULL;
+
+ ifname = nm_device_get_iface (device);
+ if (!ifname)
+ return NULL;
+
+ udev_device = g_udev_client_query_by_subsystem_and_name (priv->client, "net", ifname);
+ if (!udev_device)
+ udev_device = g_udev_client_query_by_subsystem_and_name (priv->client, "tty", ifname);
+ if (!udev_device)
+ return NULL;
+
+ /* Walk up the chain of the device and its parents a few steps to grab
+ * vendor and device ID information off it.
+ */
+
+ /* Ref the device again because we have to unref it each iteration,
+ * as g_udev_device_get_parent() returns a ref-ed object.
+ */
+ tmpdev = g_object_ref (udev_device);
+ while ((count++ < 3) && tmpdev && !enc_value) {
+ if (!enc_value)
+ enc_value = get_decoded_property (tmpdev, enc_prop);
+ if (!db_value)
+ db_value = g_strdup (g_udev_device_get_property (tmpdev, db_prop));
+
+ olddev = tmpdev;
+ tmpdev = g_udev_device_get_parent (tmpdev);
+ g_object_unref (olddev);
+ }
+
+ /* Unref the last device if we found what we needed before running out
+ * of parents.
+ */
+ if (tmpdev)
+ g_object_unref (tmpdev);
+
+ /* Balance the initial g_udev_client_query_by_subsystem_and_name() */
+ g_object_unref (udev_device);
+
+ /* Prefer the encoded value which comes directly from the device
+ * over the hwdata database value.
+ */
+ if (enc_value) {
+ g_free (db_value);
+ return enc_value;
+ }
+
+ return db_value;
+}
+
+/**
+ * nm_device_get_product:
+ * @device: a #NMDevice
+ *
+ * Gets the product string of the #NMDevice.
+ *
+ * Returns: the product name of the device. This is the internal string used by the
+ * device, and must not be modified.
+ **/
+const char *
+nm_device_get_product (NMDevice *device)
+{
+ NMDevicePrivate *priv;
+
+ g_return_val_if_fail (NM_IS_DEVICE (device), NULL);
+
+ priv = NM_DEVICE_GET_PRIVATE (device);
+ if (!priv->product) {
+ priv->product = _get_udev_property (device, "ID_MODEL_ENC", "ID_MODEL_FROM_DATABASE");
+ if (!priv->product) {
+ /* Sometimes ID_PRODUCT_FROM_DATABASE is used? */
+ priv->product = _get_udev_property (device, "ID_MODEL_ENC", "ID_PRODUCT_FROM_DATABASE");
+ }
+ _nm_object_queue_notify (NM_OBJECT (device), NM_DEVICE_PRODUCT);
+ }
+ return priv->product;
+}
+
+/**
+ * nm_device_get_vendor:
+ * @device: a #NMDevice
+ *
+ * Gets the vendor string of the #NMDevice.
+ *
+ * Returns: the vendor name of the device. This is the internal string used by the
+ * device, and must not be modified.
+ **/
+const char *
+nm_device_get_vendor (NMDevice *device)
+{
+ NMDevicePrivate *priv;
+
+ g_return_val_if_fail (NM_IS_DEVICE (device), NULL);
+
+ priv = NM_DEVICE_GET_PRIVATE (device);
+ if (!priv->vendor) {
+ priv->vendor = _get_udev_property (device, "ID_VENDOR_ENC", "ID_VENDOR_FROM_DATABASE");
+ _nm_object_queue_notify (NM_OBJECT (device), NM_DEVICE_VENDOR);
+ }
+ return priv->vendor;
+}
+
+static const char * const ignored_words[] = {
+ "Semiconductor",
+ "Components",
+ "Corporation",
+ "Communications",
+ "Company",
+ "Corp.",
+ "Corp",
+ "Co.",
+ "Inc.",
+ "Inc",
+ "Incorporated",
+ "Ltd.",
+ "Limited.",
+ "Intel?",
+ "chipset",
+ "adapter",
+ "[hex]",
+ "NDIS",
+ "Module",
+ NULL
+};
+
+static const char * const ignored_phrases[] = {
+ "Multiprotocol MAC/baseband processor",
+ "Wireless LAN Controller",
+ "Wireless LAN Adapter",
+ "Wireless Adapter",
+ "Network Connection",
+ "Wireless Cardbus Adapter",
+ "Wireless CardBus Adapter",
+ "54 Mbps Wireless PC Card",
+ "Wireless PC Card",
+ "Wireless PC",
+ "PC Card with XJACK(r) Antenna",
+ "Wireless cardbus",
+ "Wireless LAN PC Card",
+ "Technology Group Ltd.",
+ "Communication S.p.A.",
+ "Business Mobile Networks BV",
+ "Mobile Broadband Minicard Composite Device",
+ "Mobile Communications AB",
+ "(PC-Suite Mode)",
+ NULL
+};
+
+static char *
+fixup_desc_string (const char *desc)
+{
+ char *p, *temp;
+ char **words, **item;
+ GString *str;
+ int i;
+
+ if (!desc)
+ return NULL;
+
+ p = temp = g_strdup (desc);
+ while (*p) {
+ if (*p == '_' || *p == ',')
+ *p = ' ';
+ p++;
+ }
+
+ /* Attempt to shorten ID by ignoring certain phrases */
+ for (i = 0; ignored_phrases[i]; i++) {
+ p = strstr (temp, ignored_phrases[i]);
+ if (p) {
+ guint32 ignored_len = strlen (ignored_phrases[i]);
+
+ memmove (p, p + ignored_len, strlen (p + ignored_len) + 1); /* +1 for the \0 */
+ }
+ }
+
+ /* Attempt to shorten ID by ignoring certain individual words */
+ words = g_strsplit (temp, " ", 0);
+ str = g_string_new_len (NULL, strlen (temp));
+ g_free (temp);
+
+ for (item = words; *item; item++) {
+ gboolean ignore = FALSE;
+
+ if (**item == '\0')
+ continue;
+
+ for (i = 0; ignored_words[i]; i++) {
+ if (!strcmp (*item, ignored_words[i])) {
+ ignore = TRUE;
+ break;
+ }
+ }
+
+ if (!ignore) {
+ if (str->len)
+ g_string_append_c (str, ' ');
+ g_string_append (str, *item);
+ }
+ }
+ g_strfreev (words);
+
+ temp = str->str;
+ g_string_free (str, FALSE);
+
+ return temp;
+}
+
+static void
+get_description (NMDevice *device)
+{
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
+ const char *dev_product;
+ const char *dev_vendor;
+ char *pdown;
+ char *vdown;
+ GString *str;
+
+ dev_product = nm_device_get_product (device);
+ priv->short_product = fixup_desc_string (dev_product);
+
+ dev_vendor = nm_device_get_vendor (device);
+ priv->short_vendor = fixup_desc_string (dev_vendor);
+
+ if (!dev_product || !dev_vendor) {
+ priv->description = g_strdup (nm_device_get_iface (device));
+ return;
+ }
+
+ str = g_string_new_len (NULL, strlen (priv->short_vendor) + strlen (priv->short_product) + 1);
+
+ /* Another quick hack; if all of the fixed up vendor string
+ * is found in product, ignore the vendor.
+ */
+ pdown = g_ascii_strdown (priv->short_product, -1);
+ vdown = g_ascii_strdown (priv->short_vendor, -1);
+ if (!strstr (pdown, vdown)) {
+ g_string_append (str, priv->short_vendor);
+ g_string_append_c (str, ' ');
+ }
+ g_free (pdown);
+ g_free (vdown);
+
+ g_string_append (str, priv->short_product);
+
+ priv->description = g_string_free (str, FALSE);
+}
+
+static const char *
+get_short_vendor (NMDevice *device)
+{
+ NMDevicePrivate *priv;
+
+ g_return_val_if_fail (NM_IS_DEVICE (device), NULL);
+
+ priv = NM_DEVICE_GET_PRIVATE (device);
+
+ if (!priv->description)
+ get_description (device);
+
+ return priv->short_vendor;
+}
+
+/**
+ * nm_device_get_description:
+ * @device: an #NMDevice
+ *
+ * Gets a description of @device, incorporating the results of
+ * nm_device_get_short_vendor() and nm_device_get_short_product().
+ *
+ * Returns: a description of @device. If either the vendor or the
+ * product name is unknown, this returns the interface name.
+ *
+ * Since: 0.9.10
+ */
+const char *
+nm_device_get_description (NMDevice *device)
+{
+ NMDevicePrivate *priv;
+
+ g_return_val_if_fail (NM_IS_DEVICE (device), NULL);
+
+ priv = NM_DEVICE_GET_PRIVATE (device);
+
+ if (!priv->description)
+ get_description (device);
+
+ return priv->description;
+}
+
+static const char *
+get_type_name (NMDevice *device)
+{
+ switch (nm_device_get_device_type (device)) {
+ case NM_DEVICE_TYPE_ETHERNET:
+ return _("Ethernet");
+ case NM_DEVICE_TYPE_WIFI:
+ return _("Wi-Fi");
+ case NM_DEVICE_TYPE_BT:
+ return _("Bluetooth");
+ case NM_DEVICE_TYPE_OLPC_MESH:
+ return _("OLPC Mesh");
+ case NM_DEVICE_TYPE_WIMAX:
+ return _("WiMAX");
+ case NM_DEVICE_TYPE_MODEM:
+ return _("Mobile Broadband");
+ case NM_DEVICE_TYPE_INFINIBAND:
+ return _("InfiniBand");
+ case NM_DEVICE_TYPE_BOND:
+ return _("Bond");
+ case NM_DEVICE_TYPE_TEAM:
+ return _("Team");
+ case NM_DEVICE_TYPE_BRIDGE:
+ return _("Bridge");
+ case NM_DEVICE_TYPE_VLAN:
+ return _("VLAN");
+ case NM_DEVICE_TYPE_ADSL:
+ return _("ADSL");
+ default:
+ return _("Unknown");
+ }
+}
+
+static char *
+get_device_type_name_with_iface (NMDevice *device)
+{
+ const char *type_name = get_type_name (device);
+
+ switch (nm_device_get_device_type (device)) {
+ case NM_DEVICE_TYPE_BOND:
+ case NM_DEVICE_TYPE_TEAM:
+ case NM_DEVICE_TYPE_BRIDGE:
+ case NM_DEVICE_TYPE_VLAN:
+ return g_strdup_printf ("%s (%s)", type_name, nm_device_get_iface (device));
+ default:
+ return g_strdup (type_name);
+ }
+}
+
+static char *
+get_device_generic_type_name_with_iface (NMDevice *device)
+{
+ switch (nm_device_get_device_type (device)) {
+ case NM_DEVICE_TYPE_ETHERNET:
+ case NM_DEVICE_TYPE_INFINIBAND:
+ return g_strdup (_("Wired"));
+ default:
+ return get_device_type_name_with_iface (device);
+ }
+}
+
+static const char *
+get_bus_name (NMDevice *device)
+{
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
+ GUdevDevice *udevice;
+ const char *ifname, *bus;
+
+ if (priv->bus_name)
+ goto out;
+
+ if (!ensure_udev_client (device))
+ return NULL;
+
+ ifname = nm_device_get_iface (device);
+ if (!ifname)
+ return NULL;
+
+ udevice = g_udev_client_query_by_subsystem_and_name (priv->client, "net", ifname);
+ if (!udevice)
+ udevice = g_udev_client_query_by_subsystem_and_name (priv->client, "tty", ifname);
+ if (!udevice)
+ return NULL;
+
+ bus = g_udev_device_get_property (udevice, "ID_BUS");
+ if (!g_strcmp0 (bus, "pci"))
+ priv->bus_name = g_strdup (_("PCI"));
+ else if (!g_strcmp0 (bus, "usb"))
+ priv->bus_name = g_strdup (_("USB"));
+ else {
+ /* Use "" instead of NULL so we can tell later that we've
+ * already tried.
+ */
+ priv->bus_name = g_strdup ("");
+ }
+
+out:
+ if (*priv->bus_name)
+ return priv->bus_name;
+ else
+ return NULL;
+}
+
+static gboolean
+find_duplicates (char **names,
+ gboolean *duplicates,
+ int num_devices)
+{
+ int i, j;
+ gboolean found_any = FALSE;
+
+ memset (duplicates, 0, num_devices * sizeof (gboolean));
+ for (i = 0; i < num_devices; i++) {
+ if (duplicates[i])
+ continue;
+ for (j = i + 1; j < num_devices; j++) {
+ if (duplicates[j])
+ continue;
+ if (!strcmp (names[i], names[j]))
+ duplicates[i] = duplicates[j] = found_any = TRUE;
+ }
+ }
+
+ return found_any;
+}
+
+/**
+ * nm_device_disambiguate_names:
+ * @devices: (array length=num_devices): an array of #NMDevice
+ * @num_devices: length of @devices
+ *
+ * Generates a list of short-ish unique presentation names for the
+ * devices in @devices.
+ *
+ * Returns: (transfer full) (array zero-terminated=1): the device names
+ *
+ * Since: 0.9.10
+ */
+char **
+nm_device_disambiguate_names (NMDevice **devices,
+ int num_devices)
+{
+ char **names;
+ gboolean *duplicates;
+ int i;
+
+ names = g_new (char *, num_devices + 1);
+ duplicates = g_new (gboolean, num_devices);
+
+ /* Generic device name */
+ for (i = 0; i < num_devices; i++)
+ names[i] = get_device_generic_type_name_with_iface (devices[i]);
+ if (!find_duplicates (names, duplicates, num_devices))
+ goto done;
+
+ /* Try specific names (eg, "Ethernet" and "InfiniBand" rather
+ * than "Wired")
+ */
+ for (i = 0; i < num_devices; i++) {
+ if (duplicates[i]) {
+ g_free (names[i]);
+ names[i] = get_device_type_name_with_iface (devices[i]);
+ }
+ }
+ if (!find_duplicates (names, duplicates, num_devices))
+ goto done;
+
+ /* Try prefixing bus name (eg, "PCI Ethernet" vs "USB Ethernet") */
+ for (i = 0; i < num_devices; i++) {
+ if (duplicates[i]) {
+ const char *bus = get_bus_name (devices[i]);
+ char *name;
+
+ if (!bus)
+ continue;
+
+ g_free (names[i]);
+ name = get_device_type_name_with_iface (devices[i]);
+ /* Translators: the first %s is a bus name (eg, "USB") or
+ * product name, the second is a device type (eg,
+ * "Ethernet"). You can change this to something like
+ * "%2$s (%1$s)" if there's no grammatical way to combine
+ * the strings otherwise.
+ */
+ names[i] = g_strdup_printf (C_("long device name", "%s %s"),
+ bus, name);
+ g_free (name);
+ }
+ }
+ if (!find_duplicates (names, duplicates, num_devices))
+ goto done;
+
+ /* Try prefixing vendor name */
+ for (i = 0; i < num_devices; i++) {
+ if (duplicates[i]) {
+ const char *vendor = get_short_vendor (devices[i]);
+ char *name;
+
+ if (!vendor)
+ continue;
+
+ g_free (names[i]);
+ name = get_device_type_name_with_iface (devices[i]);
+ names[i] = g_strdup_printf (C_("long device name", "%s %s"),
+ vendor,
+ get_type_name (devices[i]));
+ g_free (name);
+ }
+ }
+ if (!find_duplicates (names, duplicates, num_devices))
+ goto done;
+
+ /* We have multiple identical network cards, so we have to differentiate
+ * them by interface name.
+ */
+ for (i = 0; i < num_devices; i++) {
+ if (duplicates[i]) {
+ const char *interface = nm_device_get_iface (devices[i]);
+
+ if (!interface)
+ continue;
+
+ g_free (names[i]);
+ names[i] = g_strdup_printf ("%s (%s)",
+ get_type_name (devices[i]),
+ interface);
+ }
+ }
+
+done:
+ g_free (duplicates);
+ names[num_devices] = NULL;
+ return names;
+}
+
+/**
+ * nm_device_get_physical_port_id:
+ * @device: a #NMDevice
+ *
+ * Gets the physical port ID of the #NMDevice. If non-%NULL, this is
+ * an opaque string that can be used to recognize when
+ * seemingly-unrelated #NMDevices are actually just different virtual
+ * ports on a single physical port. (Eg, NPAR / SR-IOV.)
+ *
+ * Returns: the physical port ID of the device, or %NULL if the port
+ * ID is unknown. This is the internal string used by the device and
+ * must not be modified.
+ *
+ * Since: 0.9.10
+ **/
+const char *
+nm_device_get_physical_port_id (NMDevice *device)
+{
+ NMDevicePrivate *priv;
+
+ g_return_val_if_fail (NM_IS_DEVICE (device), NULL);
+
+ priv = NM_DEVICE_GET_PRIVATE (device);
+
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ if (priv->physical_port_id && *priv->physical_port_id)
+ return priv->physical_port_id;
+ else
+ return NULL;
+}
+
+/**
+ * nm_device_get_mtu:
+ * @device: a #NMDevice
+ *
+ * Gets the MTU of the #NMDevice.
+ *
+ * Returns: the MTU of the device.
+ *
+ * Since: 0.9.10
+ **/
+guint32
+nm_device_get_mtu (NMDevice *device)
+{
+ g_return_val_if_fail (NM_IS_DEVICE (device), 0);
+
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return NM_DEVICE_GET_PRIVATE (device)->mtu;
+}
+
+/**
+ * nm_device_is_software:
+ * @device: a #NMDevice
+ *
+ * Whether the device is a software device.
+ *
+ * Returns: %TRUE if @device is a software device, %FALSE if it is a hardware device.
+ *
+ * Since: 1.0
+ **/
+gboolean
+nm_device_is_software (NMDevice *device)
+{
+ g_return_val_if_fail (NM_IS_DEVICE (device), FALSE);
+
+ _nm_object_ensure_inited (NM_OBJECT (device));
+ return !!(NM_DEVICE_GET_PRIVATE (device)->capabilities & NM_DEVICE_CAP_IS_SOFTWARE);
+}
+
+typedef struct {
+ NMDevice *device;
+ NMDeviceCallbackFn fn;
+ gpointer user_data;
+ const char *method;
+} DeviceCallbackInfo;
+
+static void
+device_operation_cb (DBusGProxy *proxy,
+ DBusGProxyCall *call,
+ gpointer user_data)
+{
+ DeviceCallbackInfo *info = user_data;
+ GError *error = NULL;
+
+ dbus_g_proxy_end_call (proxy, call, &error,
+ G_TYPE_INVALID);
+ if (info->fn)
+ info->fn (info->device, error, info->user_data);
+ else if (error) {
+ g_warning ("%s: device %s %s failed: (%d) %s",
+ __func__,
+ nm_object_get_path (NM_OBJECT (info->device)),
+ info->method,
+ error ? error->code : -1,
+ error && error->message ? error->message : "(unknown)");
+ }
+ g_clear_error (&error);
+
+ g_object_unref (info->device);
+ g_slice_free (DeviceCallbackInfo, info);
+}
+
+/**
+ * nm_device_disconnect:
+ * @device: a #NMDevice
+ * @callback: (scope async) (allow-none): callback to be called when disconnect
+ * operation completes
+ * @user_data: (closure): caller-specific data passed to @callback
+ *
+ * Disconnects the device if currently connected, and prevents the device from
+ * automatically connecting to networks until the next manual network connection
+ * request.
+ **/
+void
+nm_device_disconnect (NMDevice *device,
+ NMDeviceCallbackFn callback,
+ gpointer user_data)
+{
+ DeviceCallbackInfo *info;
+
+ g_return_if_fail (NM_IS_DEVICE (device));
+
+ info = g_slice_new (DeviceCallbackInfo);
+ info->fn = callback;
+ info->user_data = user_data;
+ info->method = "Disconnect";
+ info->device = g_object_ref (device);
+
+ dbus_g_proxy_begin_call (NM_DEVICE_GET_PRIVATE (device)->proxy, "Disconnect",
+ device_operation_cb, info, NULL,
+ G_TYPE_INVALID);
+}
+
+/**
+ * nm_device_delete:
+ * @device: a #NMDevice
+ * @callback: (scope async) (allow-none): callback to be called when delete
+ * operation completes
+ * @user_data: (closure): caller-specific data passed to @callback
+ *
+ * Deletes the software device. Hardware devices can't be deleted.
+ *
+ * Since: 1.0
+ **/
+void
+nm_device_delete (NMDevice *device,
+ NMDeviceCallbackFn callback,
+ gpointer user_data)
+{
+ DeviceCallbackInfo *info;
+
+ g_return_if_fail (NM_IS_DEVICE (device));
+
+ info = g_slice_new (DeviceCallbackInfo);
+ info->fn = callback;
+ info->user_data = user_data;
+ info->method = "Delete";
+ info->device = g_object_ref (device);
+
+ dbus_g_proxy_begin_call (NM_DEVICE_GET_PRIVATE (device)->proxy, "Delete",
+ device_operation_cb, info, NULL,
+ G_TYPE_INVALID);
+}
+
+/**
+ * nm_device_connection_valid:
+ * @device: an #NMDevice to validate @connection against
+ * @connection: an #NMConnection to validate against @device
+ *
+ * Validates a given connection for a given #NMDevice object and returns
+ * whether the connection may be activated with the device. For example if
+ * @device is a Wi-Fi device that supports only WEP encryption, the connection
+ * will only be valid if it is a Wi-Fi connection which describes a WEP or open
+ * network, and will not be valid if it describes a WPA network, or if it is
+ * an Ethernet, Bluetooth, WWAN, etc connection that is incompatible with the
+ * device.
+ *
+ * Returns: %TRUE if the connection may be activated with this device, %FALSE
+ * if is incompatible with the device's capabilities and characteristics.
+ **/
+gboolean
+nm_device_connection_valid (NMDevice *device, NMConnection *connection)
+{
+ return nm_device_connection_compatible (device, connection, NULL);
+}
+
+gboolean
+connection_compatible (NMDevice *device, NMConnection *connection, GError **error)
+{
+ NMSettingConnection *s_con;
+ const char *config_iface, *device_iface;
+
+ s_con = nm_connection_get_setting_connection (connection);
+ g_assert (s_con);
+
+ config_iface = nm_setting_connection_get_interface_name (s_con);
+ device_iface = nm_device_get_iface (device);
+ if (config_iface && g_strcmp0 (config_iface, device_iface) != 0) {
+ g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INTERFACE_MISMATCH,
+ "The interface names of the device and the connection didn't match.");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ * nm_device_connection_compatible:
+ * @device: an #NMDevice to validate @connection against
+ * @connection: an #NMConnection to validate against @device
+ * @error: return location for a #GError, or %NULL
+ *
+ * Validates a given connection for a given #NMDevice object and returns
+ * whether the connection may be activated with the device. For example if
+ * @device is a Wi-Fi device that supports only WEP encryption, the connection
+ * will only be valid if it is a Wi-Fi connection which describes a WEP or open
+ * network, and will not be valid if it describes a WPA network, or if it is
+ * an Ethernet, Bluetooth, WWAN, etc connection that is incompatible with the
+ * device.
+ *
+ * This function does the same as nm_device_connection_valid(), i.e. checking
+ * compatibility of the given device and connection. But, in addition, it sets
+ * GError when FALSE is returned.
+ *
+ * Returns: %TRUE if the connection may be activated with this device, %FALSE
+ * if is incompatible with the device's capabilities and characteristics.
+ **/
+gboolean
+nm_device_connection_compatible (NMDevice *device, NMConnection *connection, GError **error)
+{
+ g_return_val_if_fail (NM_IS_DEVICE (device), FALSE);
+ g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ return NM_DEVICE_GET_CLASS (device)->connection_compatible (device, connection, error);
+}
+
+/**
+ * nm_device_filter_connections:
+ * @device: an #NMDevice to filter connections for
+ * @connections: (element-type NMConnection): a list of #NMConnection objects to filter
+ *
+ * Filters a given list of connections for a given #NMDevice object and return
+ * connections which may be activated with the device. For example if @device
+ * is a Wi-Fi device that supports only WEP encryption, the returned list will
+ * contain any Wi-Fi connections in @connections that allow connection to
+ * unencrypted or WEP-enabled SSIDs. The returned list will not contain
+ * Ethernet, Bluetooth, Wi-Fi WPA connections, or any other connection that is
+ * incompatible with the device. To get the full list of connections see
+ * nm_remote_settings_list_connections().
+ *
+ * Returns: (transfer container) (element-type NMConnection): a
+ * list of #NMConnection objects that could be activated with the given @device.
+ * The elements of the list are owned by their creator and should not be freed
+ * by the caller, but the returned list itself is owned by the caller and should
+ * be freed with g_slist_free() when it is no longer required.
+ **/
+GSList *
+nm_device_filter_connections (NMDevice *device, const GSList *connections)
+{
+ GSList *filtered = NULL;
+ const GSList *iter;
+
+ for (iter = connections; iter; iter = g_slist_next (iter)) {
+ NMConnection *candidate = NM_CONNECTION (iter->data);
+
+ /* Connection applies to this device */
+ if (nm_device_connection_valid (device, candidate))
+ filtered = g_slist_prepend (filtered, candidate);
+ }
+
+ return g_slist_reverse (filtered);
+}
+
+/**
+ * nm_device_get_setting_type:
+ * @device: an #NMDevice
+ *
+ * Gets the (primary) #NMSetting subtype associated with connections
+ * that can be used on @device.
+ *
+ * Returns: @device's associated #NMSetting type
+ *
+ * Since: 0.9.10
+ */
+GType
+nm_device_get_setting_type (NMDevice *device)
+{
+ g_return_val_if_fail (NM_IS_DEVICE (device), G_TYPE_INVALID);
+ g_return_val_if_fail (NM_DEVICE_GET_CLASS (device)->get_setting_type != NULL, G_TYPE_INVALID);
+
+ return NM_DEVICE_GET_CLASS (device)->get_setting_type (device);
+}
diff --git a/libnm/nm-device.h b/libnm/nm-device.h
new file mode 100644
index 0000000000..707fddf82a
--- /dev/null
+++ b/libnm/nm-device.h
@@ -0,0 +1,186 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2008 Novell, Inc.
+ * Copyright 2007 - 2013 Red Hat, Inc.
+ */
+
+#ifndef NM_DEVICE_H
+#define NM_DEVICE_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <dbus/dbus-glib.h>
+#include "nm-object.h"
+#include "NetworkManager.h"
+#include "nm-ip4-config.h"
+#include "nm-dhcp4-config.h"
+#include "nm-ip6-config.h"
+#include "nm-dhcp6-config.h"
+#include "nm-connection.h"
+#include "nm-active-connection.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_DEVICE (nm_device_get_type ())
+#define NM_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE, NMDevice))
+#define NM_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE, NMDeviceClass))
+#define NM_IS_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE))
+#define NM_IS_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE))
+#define NM_DEVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE, NMDeviceClass))
+
+/**
+ * NMDeviceError:
+ * @NM_DEVICE_ERROR_UNKNOWN: unknown or unclassified error
+ * @NM_DEVICE_ERROR_INTERFACE_MISMATCH: the interface names of the connection and the
+ * device mismatched
+ */
+typedef enum {
+ NM_DEVICE_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/
+ NM_DEVICE_ERROR_INTERFACE_MISMATCH, /*< nick=InterfaceMismatch >*/
+} NMDeviceError;
+
+#define NM_DEVICE_ERROR nm_device_error_quark ()
+NM_AVAILABLE_IN_0_9_10
+GQuark nm_device_error_quark (void);
+
+#define NM_DEVICE_DEVICE_TYPE "device-type"
+#define NM_DEVICE_UDI "udi"
+#define NM_DEVICE_INTERFACE "interface"
+#define NM_DEVICE_IP_INTERFACE "ip-interface"
+#define NM_DEVICE_DRIVER "driver"
+#define NM_DEVICE_DRIVER_VERSION "driver-version"
+#define NM_DEVICE_FIRMWARE_VERSION "firmware-version"
+#define NM_DEVICE_CAPABILITIES "capabilities"
+#define NM_DEVICE_MANAGED "managed"
+#define NM_DEVICE_AUTOCONNECT "autoconnect"
+#define NM_DEVICE_FIRMWARE_MISSING "firmware-missing"
+#define NM_DEVICE_IP4_CONFIG "ip4-config"
+#define NM_DEVICE_DHCP4_CONFIG "dhcp4-config"
+#define NM_DEVICE_IP6_CONFIG "ip6-config"
+#define NM_DEVICE_DHCP6_CONFIG "dhcp6-config"
+#define NM_DEVICE_STATE "state"
+#define NM_DEVICE_STATE_REASON "state-reason"
+#define NM_DEVICE_ACTIVE_CONNECTION "active-connection"
+#define NM_DEVICE_AVAILABLE_CONNECTIONS "available-connections"
+#define NM_DEVICE_VENDOR "vendor"
+#define NM_DEVICE_PRODUCT "product"
+#define NM_DEVICE_PHYSICAL_PORT_ID "physical-port-id"
+#define NM_DEVICE_MTU "mtu"
+
+typedef struct {
+ NMObject parent;
+} NMDevice;
+
+typedef struct {
+ NMObjectClass parent;
+
+ /* Signals */
+ void (*state_changed) (NMDevice *device,
+ NMDeviceState new_state,
+ NMDeviceState old_state,
+ NMDeviceStateReason reason);
+
+ gboolean (*connection_compatible) (NMDevice *device,
+ NMConnection *connection,
+ GError **error);
+
+ const char * (*get_type_description) (NMDevice *device);
+ const char * (*get_hw_address) (NMDevice *device);
+
+ GType (*get_setting_type) (NMDevice *device);
+
+ /* Padding for future expansion */
+ void (*_reserved1) (void);
+ void (*_reserved2) (void);
+ void (*_reserved3) (void);
+} NMDeviceClass;
+
+GType nm_device_get_type (void);
+
+GObject * nm_device_new (DBusGConnection *connection, const char *path);
+
+const char * nm_device_get_iface (NMDevice *device);
+const char * nm_device_get_ip_iface (NMDevice *device);
+NMDeviceType nm_device_get_device_type (NMDevice *device);
+const char * nm_device_get_udi (NMDevice *device);
+const char * nm_device_get_driver (NMDevice *device);
+const char * nm_device_get_driver_version (NMDevice *device);
+const char * nm_device_get_firmware_version (NMDevice *device);
+NM_AVAILABLE_IN_0_9_10
+const char * nm_device_get_type_description (NMDevice *device);
+NM_AVAILABLE_IN_0_9_10
+const char * nm_device_get_hw_address (NMDevice *device);
+NMDeviceCapabilities nm_device_get_capabilities (NMDevice *device);
+gboolean nm_device_get_managed (NMDevice *device);
+gboolean nm_device_get_autoconnect (NMDevice *device);
+void nm_device_set_autoconnect (NMDevice *device, gboolean autoconnect);
+gboolean nm_device_get_firmware_missing (NMDevice *device);
+NMIP4Config * nm_device_get_ip4_config (NMDevice *device);
+NMDHCP4Config * nm_device_get_dhcp4_config (NMDevice *device);
+NMIP6Config * nm_device_get_ip6_config (NMDevice *device);
+NMDHCP6Config * nm_device_get_dhcp6_config (NMDevice *device);
+NMDeviceState nm_device_get_state (NMDevice *device);
+NMDeviceState nm_device_get_state_reason (NMDevice *device, NMDeviceStateReason *reason);
+NMActiveConnection * nm_device_get_active_connection(NMDevice *device);
+const GPtrArray * nm_device_get_available_connections(NMDevice *device);
+NM_AVAILABLE_IN_0_9_10
+const char * nm_device_get_physical_port_id (NMDevice *device);
+NM_AVAILABLE_IN_0_9_10
+guint32 nm_device_get_mtu (NMDevice *device);
+NM_AVAILABLE_IN_1_0
+gboolean nm_device_is_software (NMDevice *device);
+
+const char * nm_device_get_product (NMDevice *device);
+const char * nm_device_get_vendor (NMDevice *device);
+NM_AVAILABLE_IN_0_9_10
+const char * nm_device_get_description (NMDevice *device);
+NM_AVAILABLE_IN_0_9_10
+char ** nm_device_disambiguate_names (NMDevice **devices,
+ int num_devices);
+
+typedef void (*NMDeviceCallbackFn) (NMDevice *device, GError *error, gpointer user_data);
+
+void nm_device_disconnect (NMDevice *device,
+ NMDeviceCallbackFn callback,
+ gpointer user_data);
+
+NM_AVAILABLE_IN_1_0
+void nm_device_delete (NMDevice *device,
+ NMDeviceCallbackFn callback,
+ gpointer user_data);
+
+GSList * nm_device_filter_connections (NMDevice *device,
+ const GSList *connections);
+
+gboolean nm_device_connection_valid (NMDevice *device,
+ NMConnection *connection);
+
+gboolean nm_device_connection_compatible (NMDevice *device,
+ NMConnection *connection,
+ GError **error);
+
+NM_AVAILABLE_IN_0_9_10
+GType nm_device_get_setting_type (NMDevice *device);
+
+/* Deprecated */
+NM_DEPRECATED_IN_1_0
+typedef void (*NMDeviceDeactivateFn) (NMDevice *device, GError *error, gpointer user_data);
+
+G_END_DECLS
+
+#endif /* NM_DEVICE_H */
diff --git a/libnm/nm-dhcp4-config.c b/libnm/nm-dhcp4-config.c
new file mode 100644
index 0000000000..4fccee9034
--- /dev/null
+++ b/libnm/nm-dhcp4-config.c
@@ -0,0 +1,215 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2008 - 2011 Red Hat, Inc.
+ * Copyright 2008 Novell, Inc.
+ */
+
+#include <string.h>
+
+#include "nm-dhcp4-config.h"
+#include "NetworkManager.h"
+#include "nm-types-private.h"
+#include "nm-object-private.h"
+#include "nm-utils.h"
+
+G_DEFINE_TYPE (NMDHCP4Config, nm_dhcp4_config, NM_TYPE_OBJECT)
+
+#define NM_DHCP4_CONFIG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DHCP4_CONFIG, NMDHCP4ConfigPrivate))
+
+typedef struct {
+ DBusGProxy *proxy;
+
+ GHashTable *options;
+} NMDHCP4ConfigPrivate;
+
+enum {
+ PROP_0,
+ PROP_OPTIONS,
+
+ LAST_PROP
+};
+
+static void
+nm_dhcp4_config_init (NMDHCP4Config *config)
+{
+}
+
+static gboolean
+demarshal_dhcp4_options (NMObject *object, GParamSpec *pspec, GValue *value, gpointer field)
+{
+ NMDHCP4ConfigPrivate *priv = NM_DHCP4_CONFIG_GET_PRIVATE (object);
+ GHashTable *new_options;
+ GHashTableIter iter;
+ const char *key;
+ GValue *opt;
+
+ g_hash_table_remove_all (priv->options);
+
+ new_options = g_value_get_boxed (value);
+ if (new_options) {
+ g_hash_table_iter_init (&iter, new_options);
+ while (g_hash_table_iter_next (&iter, (gpointer) &key, (gpointer) &opt))
+ g_hash_table_insert (priv->options, g_strdup (key), g_value_dup_string (opt));
+ }
+
+ _nm_object_queue_notify (object, NM_DHCP4_CONFIG_OPTIONS);
+ return TRUE;
+}
+
+static void
+register_properties (NMDHCP4Config *config)
+{
+ NMDHCP4ConfigPrivate *priv = NM_DHCP4_CONFIG_GET_PRIVATE (config);
+ const NMPropertiesInfo property_info[] = {
+ { NM_DHCP4_CONFIG_OPTIONS, &priv->options, demarshal_dhcp4_options },
+ { NULL },
+ };
+
+ _nm_object_register_properties (NM_OBJECT (config),
+ priv->proxy,
+ property_info);
+}
+
+static void
+constructed (GObject *object)
+{
+ NMDHCP4ConfigPrivate *priv = NM_DHCP4_CONFIG_GET_PRIVATE (object);
+
+ G_OBJECT_CLASS (nm_dhcp4_config_parent_class)->constructed (object);
+
+ priv->options = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+
+ priv->proxy = _nm_object_new_proxy (NM_OBJECT (object), NULL, NM_DBUS_INTERFACE_DHCP4_CONFIG);
+ register_properties (NM_DHCP4_CONFIG (object));
+}
+
+static void
+finalize (GObject *object)
+{
+ NMDHCP4ConfigPrivate *priv = NM_DHCP4_CONFIG_GET_PRIVATE (object);
+
+ if (priv->options)
+ g_hash_table_destroy (priv->options);
+
+ g_object_unref (priv->proxy);
+
+ G_OBJECT_CLASS (nm_dhcp4_config_parent_class)->finalize (object);
+}
+
+static void
+get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NMDHCP4Config *self = NM_DHCP4_CONFIG (object);
+
+ _nm_object_ensure_inited (NM_OBJECT (object));
+
+ switch (prop_id) {
+ case PROP_OPTIONS:
+ g_value_set_boxed (value, nm_dhcp4_config_get_options (self));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nm_dhcp4_config_class_init (NMDHCP4ConfigClass *config_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (config_class);
+
+ g_type_class_add_private (config_class, sizeof (NMDHCP4ConfigPrivate));
+
+ /* virtual methods */
+ object_class->constructed = constructed;
+ object_class->get_property = get_property;
+ object_class->finalize = finalize;
+
+ /* properties */
+
+ /**
+ * NMDHCP4Config:options:
+ *
+ * The #GHashTable containing options of the configuration.
+ *
+ * Type: GLib.HashTable(utf8,GObject.Value)
+ **/
+ g_object_class_install_property
+ (object_class, PROP_OPTIONS,
+ g_param_spec_boxed (NM_DHCP4_CONFIG_OPTIONS, "", "",
+ G_TYPE_HASH_TABLE,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+}
+
+/**
+ * nm_dhcp4_config_new:
+ * @connection: the #DBusGConnection
+ * @object_path: the DBus object path of the device
+ *
+ * Creates a new #NMDHCP4Config.
+ *
+ * Returns: (transfer full): a new configuration
+ **/
+GObject *
+nm_dhcp4_config_new (DBusGConnection *connection, const char *object_path)
+{
+ return (GObject *) g_object_new (NM_TYPE_DHCP4_CONFIG,
+ NM_OBJECT_DBUS_CONNECTION, connection,
+ NM_OBJECT_DBUS_PATH, object_path,
+ NULL);
+}
+
+/**
+ * nm_dhcp4_config_get_options:
+ * @config: a #NMDHCP4Config
+ *
+ * Gets all the options contained in the configuration.
+ *
+ * Returns: (transfer none) (element-type utf8 GObject.Value): the #GHashTable containing strings for keys and values.
+ * This is the internal copy used by the configuration, and must not be modified.
+ **/
+GHashTable *
+nm_dhcp4_config_get_options (NMDHCP4Config *config)
+{
+ g_return_val_if_fail (NM_IS_DHCP4_CONFIG (config), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (config));
+ return NM_DHCP4_CONFIG_GET_PRIVATE (config)->options;
+}
+
+/**
+ * nm_dhcp4_config_get_one_option:
+ * @config: a #NMDHCP4Config
+ * @option: the option to retrieve
+ *
+ * Gets one option by option name.
+ *
+ * Returns: the configuration option's value. This is the internal string used by the
+ * configuration, and must not be modified.
+ **/
+const char *
+nm_dhcp4_config_get_one_option (NMDHCP4Config *config, const char *option)
+{
+ g_return_val_if_fail (NM_IS_DHCP4_CONFIG (config), NULL);
+
+ return g_hash_table_lookup (nm_dhcp4_config_get_options (config), option);
+}
diff --git a/libnm/nm-dhcp4-config.h b/libnm/nm-dhcp4-config.h
new file mode 100644
index 0000000000..c64e9a9c8d
--- /dev/null
+++ b/libnm/nm-dhcp4-config.h
@@ -0,0 +1,66 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2008 Red Hat, Inc.
+ * Copyright 2008 Novell, Inc.
+ */
+
+#ifndef NM_DHCP4_CONFIG_H
+#define NM_DHCP4_CONFIG_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <dbus/dbus-glib.h>
+#include "nm-object.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_DHCP4_CONFIG (nm_dhcp4_config_get_type ())
+#define NM_DHCP4_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DHCP4_CONFIG, NMDHCP4Config))
+#define NM_DHCP4_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DHCP4_CONFIG, NMDHCP4ConfigClass))
+#define NM_IS_DHCP4_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DHCP4_CONFIG))
+#define NM_IS_DHCP4_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DHCP4_CONFIG))
+
+typedef struct {
+ NMObject parent;
+} NMDHCP4Config;
+
+typedef struct {
+ NMObjectClass parent;
+
+ /* Padding for future expansion */
+ void (*_reserved1) (void);
+ void (*_reserved2) (void);
+ void (*_reserved3) (void);
+ void (*_reserved4) (void);
+ void (*_reserved5) (void);
+ void (*_reserved6) (void);
+} NMDHCP4ConfigClass;
+
+#define NM_DHCP4_CONFIG_OPTIONS "options"
+
+GType nm_dhcp4_config_get_type (void);
+
+GObject *nm_dhcp4_config_new (DBusGConnection *connection, const char *object_path);
+
+GHashTable * nm_dhcp4_config_get_options (NMDHCP4Config *config);
+
+const char * nm_dhcp4_config_get_one_option (NMDHCP4Config *config, const char *option);
+
+G_END_DECLS
+
+#endif /* NM_DHCP4_CONFIG_H */
diff --git a/libnm/nm-dhcp6-config.c b/libnm/nm-dhcp6-config.c
new file mode 100644
index 0000000000..32cc3b4755
--- /dev/null
+++ b/libnm/nm-dhcp6-config.c
@@ -0,0 +1,215 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2008 - 2011 Red Hat, Inc.
+ * Copyright 2008 Novell, Inc.
+ */
+
+#include <string.h>
+
+#include "nm-dhcp6-config.h"
+#include "NetworkManager.h"
+#include "nm-types-private.h"
+#include "nm-object-private.h"
+#include "nm-utils.h"
+
+G_DEFINE_TYPE (NMDHCP6Config, nm_dhcp6_config, NM_TYPE_OBJECT)
+
+#define NM_DHCP6_CONFIG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DHCP6_CONFIG, NMDHCP6ConfigPrivate))
+
+typedef struct {
+ DBusGProxy *proxy;
+
+ GHashTable *options;
+} NMDHCP6ConfigPrivate;
+
+enum {
+ PROP_0,
+ PROP_OPTIONS,
+
+ LAST_PROP
+};
+
+static void
+nm_dhcp6_config_init (NMDHCP6Config *config)
+{
+}
+
+static gboolean
+demarshal_dhcp6_options (NMObject *object, GParamSpec *pspec, GValue *value, gpointer field)
+{
+ NMDHCP6ConfigPrivate *priv = NM_DHCP6_CONFIG_GET_PRIVATE (object);
+ GHashTable *new_options;
+ GHashTableIter iter;
+ const char *key;
+ GValue *opt;
+
+ g_hash_table_remove_all (priv->options);
+
+ new_options = g_value_get_boxed (value);
+ if (new_options) {
+ g_hash_table_iter_init (&iter, new_options);
+ while (g_hash_table_iter_next (&iter, (gpointer) &key, (gpointer) &opt))
+ g_hash_table_insert (priv->options, g_strdup (key), g_value_dup_string (opt));
+ }
+
+ _nm_object_queue_notify (object, NM_DHCP6_CONFIG_OPTIONS);
+ return TRUE;
+}
+
+static void
+register_properties (NMDHCP6Config *config)
+{
+ NMDHCP6ConfigPrivate *priv = NM_DHCP6_CONFIG_GET_PRIVATE (config);
+ const NMPropertiesInfo property_info[] = {
+ { NM_DHCP6_CONFIG_OPTIONS, &priv->options, demarshal_dhcp6_options },
+ { NULL },
+ };
+
+ _nm_object_register_properties (NM_OBJECT (config),
+ priv->proxy,
+ property_info);
+}
+
+static void
+constructed (GObject *object)
+{
+ NMDHCP6ConfigPrivate *priv = NM_DHCP6_CONFIG_GET_PRIVATE (object);
+
+ G_OBJECT_CLASS (nm_dhcp6_config_parent_class)->constructed (object);
+
+ priv->options = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+
+ priv->proxy = _nm_object_new_proxy (NM_OBJECT (object), NULL, NM_DBUS_INTERFACE_DHCP6_CONFIG);
+ register_properties (NM_DHCP6_CONFIG (object));
+}
+
+static void
+finalize (GObject *object)
+{
+ NMDHCP6ConfigPrivate *priv = NM_DHCP6_CONFIG_GET_PRIVATE (object);
+
+ if (priv->options)
+ g_hash_table_destroy (priv->options);
+
+ g_object_unref (priv->proxy);
+
+ G_OBJECT_CLASS (nm_dhcp6_config_parent_class)->finalize (object);
+}
+
+static void
+get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NMDHCP6Config *self = NM_DHCP6_CONFIG (object);
+
+ _nm_object_ensure_inited (NM_OBJECT (object));
+
+ switch (prop_id) {
+ case PROP_OPTIONS:
+ g_value_set_boxed (value, nm_dhcp6_config_get_options (self));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nm_dhcp6_config_class_init (NMDHCP6ConfigClass *config_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (config_class);
+
+ g_type_class_add_private (config_class, sizeof (NMDHCP6ConfigPrivate));
+
+ /* virtual methods */
+ object_class->constructed = constructed;
+ object_class->get_property = get_property;
+ object_class->finalize = finalize;
+
+ /* properties */
+
+ /**
+ * NMDHCP6Config:options:
+ *
+ * The #GHashTable containing options of the configuration.
+ *
+ * Type: GLib.HashTable(utf8,GObject.Value)
+ **/
+ g_object_class_install_property
+ (object_class, PROP_OPTIONS,
+ g_param_spec_boxed (NM_DHCP6_CONFIG_OPTIONS, "", "",
+ G_TYPE_HASH_TABLE,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+}
+
+/**
+ * nm_dhcp6_config_new:
+ * @connection: the #DBusGConnection
+ * @object_path: the DBus object path of the device
+ *
+ * Creates a new #NMDHCP6Config.
+ *
+ * Returns: (transfer full): a new configuration
+ **/
+GObject *
+nm_dhcp6_config_new (DBusGConnection *connection, const char *object_path)
+{
+ return (GObject *) g_object_new (NM_TYPE_DHCP6_CONFIG,
+ NM_OBJECT_DBUS_CONNECTION, connection,
+ NM_OBJECT_DBUS_PATH, object_path,
+ NULL);
+}
+
+/**
+ * nm_dhcp6_config_get_options:
+ * @config: a #NMDHCP6Config
+ *
+ * Gets all the options contained in the configuration.
+ *
+ * Returns: (transfer none) (element-type utf8 GObject.Value): the #GHashTable containing strings for keys and values.
+ * This is the internal copy used by the configuration, and must not be modified.
+ **/
+GHashTable *
+nm_dhcp6_config_get_options (NMDHCP6Config *config)
+{
+ g_return_val_if_fail (NM_IS_DHCP6_CONFIG (config), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (config));
+ return NM_DHCP6_CONFIG_GET_PRIVATE (config)->options;
+}
+
+/**
+ * nm_dhcp6_config_get_one_option:
+ * @config: a #NMDHCP6Config
+ * @option: the option to retrieve
+ *
+ * Gets one option by option name.
+ *
+ * Returns: the configuration option's value. This is the internal string used by the
+ * configuration, and must not be modified.
+ **/
+const char *
+nm_dhcp6_config_get_one_option (NMDHCP6Config *config, const char *option)
+{
+ g_return_val_if_fail (NM_IS_DHCP6_CONFIG (config), NULL);
+
+ return g_hash_table_lookup (nm_dhcp6_config_get_options (config), option);
+}
diff --git a/libnm/nm-dhcp6-config.h b/libnm/nm-dhcp6-config.h
new file mode 100644
index 0000000000..939b3fdbe4
--- /dev/null
+++ b/libnm/nm-dhcp6-config.h
@@ -0,0 +1,66 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2008 - 2010 Red Hat, Inc.
+ * Copyright 2008 Novell, Inc.
+ */
+
+#ifndef NM_DHCP6_CONFIG_H
+#define NM_DHCP6_CONFIG_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <dbus/dbus-glib.h>
+#include "nm-object.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_DHCP6_CONFIG (nm_dhcp6_config_get_type ())
+#define NM_DHCP6_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DHCP6_CONFIG, NMDHCP6Config))
+#define NM_DHCP6_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DHCP6_CONFIG, NMDHCP6ConfigClass))
+#define NM_IS_DHCP6_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DHCP6_CONFIG))
+#define NM_IS_DHCP6_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DHCP6_CONFIG))
+
+typedef struct {
+ NMObject parent;
+} NMDHCP6Config;
+
+typedef struct {
+ NMObjectClass parent;
+
+ /* Padding for future expansion */
+ void (*_reserved1) (void);
+ void (*_reserved2) (void);
+ void (*_reserved3) (void);
+ void (*_reserved4) (void);
+ void (*_reserved5) (void);
+ void (*_reserved6) (void);
+} NMDHCP6ConfigClass;
+
+#define NM_DHCP6_CONFIG_OPTIONS "options"
+
+GType nm_dhcp6_config_get_type (void);
+
+GObject *nm_dhcp6_config_new (DBusGConnection *connection, const char *object_path);
+
+GHashTable * nm_dhcp6_config_get_options (NMDHCP6Config *config);
+
+const char * nm_dhcp6_config_get_one_option (NMDHCP6Config *config, const char *option);
+
+G_END_DECLS
+
+#endif /* NM_DHCP6_CONFIG_H */
diff --git a/libnm/nm-ip4-config.c b/libnm/nm-ip4-config.c
new file mode 100644
index 0000000000..3ac75254cf
--- /dev/null
+++ b/libnm/nm-ip4-config.c
@@ -0,0 +1,467 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2011 Novell, Inc.
+ * Copyright 2008 Red Hat, Inc.
+ */
+
+#include <string.h>
+
+#include <nm-setting-ip4-config.h>
+#include "nm-ip4-config.h"
+#include "NetworkManager.h"
+#include "nm-types-private.h"
+#include "nm-object-private.h"
+#include "nm-utils.h"
+
+G_DEFINE_TYPE (NMIP4Config, nm_ip4_config, NM_TYPE_OBJECT)
+
+#define NM_IP4_CONFIG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_IP4_CONFIG, NMIP4ConfigPrivate))
+
+typedef struct {
+ DBusGProxy *proxy;
+
+ char *gateway;
+ GSList *addresses;
+ GSList *routes;
+ GArray *nameservers;
+ GPtrArray *domains;
+ GPtrArray *searches;
+ GArray *wins;
+} NMIP4ConfigPrivate;
+
+enum {
+ PROP_0,
+ PROP_GATEWAY,
+ PROP_ADDRESSES,
+ PROP_ROUTES,
+ PROP_NAMESERVERS,
+ PROP_DOMAINS,
+ PROP_SEARCHES,
+ PROP_WINS_SERVERS,
+
+ LAST_PROP
+};
+
+static void
+nm_ip4_config_init (NMIP4Config *config)
+{
+}
+
+static gboolean
+demarshal_ip4_address_array (NMObject *object, GParamSpec *pspec, GValue *value, gpointer field)
+{
+ NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (object);
+
+ g_slist_free_full (priv->addresses, (GDestroyNotify) nm_ip4_address_unref);
+ priv->addresses = NULL;
+
+ priv->addresses = nm_utils_ip4_addresses_from_gvalue (value);
+ _nm_object_queue_notify (object, NM_IP4_CONFIG_ADDRESSES);
+
+ return TRUE;
+}
+
+static gboolean
+demarshal_ip4_array (NMObject *object, GParamSpec *pspec, GValue *value, gpointer field)
+{
+ if (!_nm_uint_array_demarshal (value, (GArray **) field))
+ return FALSE;
+
+ if (!strcmp (pspec->name, NM_IP4_CONFIG_NAMESERVERS))
+ _nm_object_queue_notify (object, NM_IP4_CONFIG_NAMESERVERS);
+ else if (!strcmp (pspec->name, NM_IP4_CONFIG_WINS_SERVERS))
+ _nm_object_queue_notify (object, NM_IP4_CONFIG_WINS_SERVERS);
+
+ return TRUE;
+}
+
+static gboolean
+demarshal_string_array (NMObject *object, GParamSpec *pspec, GValue *value, gpointer field)
+{
+ if (!_nm_string_array_demarshal (value, (GPtrArray **) field))
+ return FALSE;
+
+ _nm_object_queue_notify (object, pspec->name);
+ return TRUE;
+}
+
+static gboolean
+demarshal_ip4_routes_array (NMObject *object, GParamSpec *pspec, GValue *value, gpointer field)
+{
+ NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (object);
+
+ g_slist_free_full (priv->routes, (GDestroyNotify) nm_ip4_route_unref);
+ priv->routes = NULL;
+
+ priv->routes = nm_utils_ip4_routes_from_gvalue (value);
+ _nm_object_queue_notify (object, NM_IP4_CONFIG_ROUTES);
+
+ return TRUE;
+}
+
+static void
+register_properties (NMIP4Config *config)
+{
+ NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+ const NMPropertiesInfo property_info[] = {
+ { NM_IP4_CONFIG_GATEWAY, &priv->gateway, },
+ { NM_IP4_CONFIG_ADDRESSES, &priv->addresses, demarshal_ip4_address_array },
+ { NM_IP4_CONFIG_ROUTES, &priv->routes, demarshal_ip4_routes_array },
+ { NM_IP4_CONFIG_NAMESERVERS, &priv->nameservers, demarshal_ip4_array },
+ { NM_IP4_CONFIG_DOMAINS, &priv->domains, demarshal_string_array },
+ { NM_IP4_CONFIG_SEARCHES, &priv->searches, demarshal_string_array },
+ { NM_IP4_CONFIG_WINS_SERVERS, &priv->wins, demarshal_ip4_array },
+ { NULL },
+ };
+
+ _nm_object_register_properties (NM_OBJECT (config),
+ priv->proxy,
+ property_info);
+}
+
+static void
+constructed (GObject *object)
+{
+ NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (object);
+
+ G_OBJECT_CLASS (nm_ip4_config_parent_class)->constructed (object);
+
+ priv->proxy = _nm_object_new_proxy (NM_OBJECT (object), NULL, NM_DBUS_INTERFACE_IP4_CONFIG);
+ register_properties (NM_IP4_CONFIG (object));
+}
+
+static void
+finalize (GObject *object)
+{
+ NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (object);
+
+ g_free (priv->gateway);
+
+ g_slist_free_full (priv->addresses, (GDestroyNotify) nm_ip4_address_unref);
+ g_slist_free_full (priv->routes, (GDestroyNotify) nm_ip4_route_unref);
+
+ if (priv->nameservers)
+ g_array_free (priv->nameservers, TRUE);
+
+ if (priv->wins)
+ g_array_free (priv->wins, TRUE);
+
+ if (priv->domains) {
+ g_ptr_array_set_free_func (priv->domains, g_free);
+ g_ptr_array_free (priv->domains, TRUE);
+ }
+
+ if (priv->searches) {
+ g_ptr_array_set_free_func (priv->searches, g_free);
+ g_ptr_array_free (priv->searches, TRUE);
+ }
+
+ g_object_unref (priv->proxy);
+
+ G_OBJECT_CLASS (nm_ip4_config_parent_class)->finalize (object);
+}
+
+static void
+get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NMIP4Config *self = NM_IP4_CONFIG (object);
+ NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self);
+
+ _nm_object_ensure_inited (NM_OBJECT (object));
+
+ switch (prop_id) {
+ case PROP_GATEWAY:
+ g_value_set_string (value, nm_ip4_config_get_gateway (self));
+ break;
+ case PROP_ADDRESSES:
+ nm_utils_ip4_addresses_to_gvalue (priv->addresses, value);
+ break;
+ case PROP_ROUTES:
+ nm_utils_ip4_routes_to_gvalue (priv->routes, value);
+ break;
+ case PROP_NAMESERVERS:
+ g_value_set_boxed (value, nm_ip4_config_get_nameservers (self));
+ break;
+ case PROP_DOMAINS:
+ g_value_set_boxed (value, nm_ip4_config_get_domains (self));
+ break;
+ case PROP_SEARCHES:
+ g_value_set_boxed (value, nm_ip4_config_get_searches (self));
+ break;
+ case PROP_WINS_SERVERS:
+ g_value_set_boxed (value, nm_ip4_config_get_wins_servers (self));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nm_ip4_config_class_init (NMIP4ConfigClass *config_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (config_class);
+
+ g_type_class_add_private (config_class, sizeof (NMIP4ConfigPrivate));
+
+ /* virtual methods */
+ object_class->constructed = constructed;
+ object_class->get_property = get_property;
+ object_class->finalize = finalize;
+
+ /* properties */
+
+ /**
+ * NMIP4Config:gateway:
+ *
+ * The IP4 gateway address of the configuration as string.
+ *
+ * Since: 0.9.10
+ **/
+ g_object_class_install_property
+ (object_class, PROP_GATEWAY,
+ g_param_spec_string (NM_IP4_CONFIG_GATEWAY, "", "",
+ NULL,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMIP4Config:addresses:
+ *
+ * The #GPtrArray containing #NMIP4Address<!-- -->es of the configuration.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_ADDRESSES,
+ g_param_spec_pointer (NM_IP4_CONFIG_ADDRESSES, "", "",
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMIP4Config:routes:
+ *
+ * The #GPtrArray containing #NMSettingIP4Routes of the configuration.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_ROUTES,
+ g_param_spec_pointer (NM_IP4_CONFIG_ROUTES, "", "",
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMIP4Config:nameservers:
+ *
+ * The #GArray containing name servers (#guint32s) of the configuration.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_NAMESERVERS,
+ g_param_spec_boxed (NM_IP4_CONFIG_NAMESERVERS, "", "",
+ NM_TYPE_UINT_ARRAY,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMIP4Config:domains:
+ *
+ * The #GPtrArray containing domain strings of the configuration.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_DOMAINS,
+ g_param_spec_boxed (NM_IP4_CONFIG_DOMAINS, "", "",
+ NM_TYPE_STRING_ARRAY,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMIP4Config:searches:
+ *
+ * The #GPtrArray containing dns search strings of the configuration.
+ *
+ * Since: 0.9.10
+ **/
+ g_object_class_install_property
+ (object_class, PROP_SEARCHES,
+ g_param_spec_boxed (NM_IP4_CONFIG_SEARCHES, "", "",
+ NM_TYPE_STRING_ARRAY,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMIP4Config:wins-servers:
+ *
+ * The #GArray containing WINS servers (#guint32s) of the configuration.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_WINS_SERVERS,
+ g_param_spec_boxed (NM_IP4_CONFIG_WINS_SERVERS, "", "",
+ NM_TYPE_UINT_ARRAY,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+}
+
+/**
+ * nm_ip4_config_new:
+ * @connection: the #DBusGConnection
+ * @object_path: the DBus object path of the device
+ *
+ * Creates a new #NMIP4Config.
+ *
+ * Returns: (transfer full): a new IP4 configuration
+ **/
+GObject *
+nm_ip4_config_new (DBusGConnection *connection, const char *object_path)
+{
+ return (GObject *) g_object_new (NM_TYPE_IP4_CONFIG,
+ NM_OBJECT_DBUS_CONNECTION, connection,
+ NM_OBJECT_DBUS_PATH, object_path,
+ NULL);
+}
+
+/**
+ * nm_ip4_config_get_gateway:
+ * @config: a #NMIP4Config
+ *
+ * Gets the IP4 gateway address.
+ *
+ * Returns: the IP4 address of the gateway.
+ *
+ * Since: 0.9.10
+ **/
+const char *
+nm_ip4_config_get_gateway (NMIP4Config *config)
+{
+ g_return_val_if_fail (NM_IS_IP4_CONFIG (config), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (config));
+ return NM_IP4_CONFIG_GET_PRIVATE (config)->gateway;
+}
+
+/**
+ * nm_ip4_config_get_addresses:
+ * @config: a #NMIP4Config
+ *
+ * Gets the IP4 addresses (containing the address, prefix, and gateway).
+ *
+ * Returns: (element-type NMIP4Address): the #GSList containing #NMIP4Address<!-- -->es.
+ * This is the internal copy used by the configuration and must not be modified.
+ **/
+const GSList *
+nm_ip4_config_get_addresses (NMIP4Config *config)
+{
+ g_return_val_if_fail (NM_IS_IP4_CONFIG (config), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (config));
+ return NM_IP4_CONFIG_GET_PRIVATE (config)->addresses;
+}
+
+/**
+ * nm_ip4_config_get_nameservers:
+ * @config: a #NMIP4Config
+ *
+ * Gets the domain name servers (DNS).
+ *
+ * Returns: (element-type guint32): the #GArray containing #guint32s.
+ * This is the internal copy used by the configuration and must not be
+ * modified.
+ **/
+const GArray *
+nm_ip4_config_get_nameservers (NMIP4Config *config)
+{
+ g_return_val_if_fail (NM_IS_IP4_CONFIG (config), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (config));
+ return NM_IP4_CONFIG_GET_PRIVATE (config)->nameservers;
+}
+
+/**
+ * nm_ip4_config_get_domains:
+ * @config: a #NMIP4Config
+ *
+ * Gets the domain names.
+ *
+ * Returns: (element-type utf8): the #GPtrArray containing domains as strings. This is the
+ * internal copy used by the configuration, and must not be modified.
+ **/
+const GPtrArray *
+nm_ip4_config_get_domains (NMIP4Config *config)
+{
+ g_return_val_if_fail (NM_IS_IP4_CONFIG (config), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (config));
+ return handle_ptr_array_return (NM_IP4_CONFIG_GET_PRIVATE (config)->domains);
+}
+
+/**
+ * nm_ip4_config_get_searches:
+ * @config: a #NMIP4Config
+ *
+ * Gets the dns searches.
+ *
+ * Returns: (element-type utf8): the #GPtrArray containing dns searches as strings. This is the
+ * internal copy used by the configuration, and must not be modified.
+ *
+ * Since: 0.9.10
+ **/
+const GPtrArray *
+nm_ip4_config_get_searches (NMIP4Config *config)
+{
+ g_return_val_if_fail (NM_IS_IP4_CONFIG (config), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (config));
+ return handle_ptr_array_return (NM_IP4_CONFIG_GET_PRIVATE (config)->searches);
+}
+
+/**
+ * nm_ip4_config_get_wins_servers:
+ * @config: a #NMIP4Config
+ *
+ * Gets the Windows Internet Name Service servers (WINS).
+ *
+ * Returns: (element-type guint32): the #GArray containing #guint32s.
+ * This is the internal copy used by the configuration and must not be
+ * modified.
+ **/
+const GArray *
+nm_ip4_config_get_wins_servers (NMIP4Config *config)
+{
+ g_return_val_if_fail (NM_IS_IP4_CONFIG (config), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (config));
+ return NM_IP4_CONFIG_GET_PRIVATE (config)->wins;
+}
+
+/**
+ * nm_ip4_config_get_routes:
+ * @config: a #NMIP4Config
+ *
+ * Gets the routes.
+ *
+ * Returns: (element-type NMIP4Route): the #GSList containing
+ * #NMIP4Routes. This is the internal copy used by the configuration,
+ * and must not be modified.
+ **/
+const GSList *
+nm_ip4_config_get_routes (NMIP4Config *config)
+{
+ g_return_val_if_fail (NM_IS_IP4_CONFIG (config), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (config));
+ return NM_IP4_CONFIG_GET_PRIVATE (config)->routes;
+}
diff --git a/libnm/nm-ip4-config.h b/libnm/nm-ip4-config.h
new file mode 100644
index 0000000000..58b6b3714d
--- /dev/null
+++ b/libnm/nm-ip4-config.h
@@ -0,0 +1,79 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2008 Novell, Inc.
+ * Copyright 2008 Red Hat, Inc.
+ */
+
+#ifndef NM_IP4_CONFIG_H
+#define NM_IP4_CONFIG_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <dbus/dbus-glib.h>
+#include "nm-object.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_IP4_CONFIG (nm_ip4_config_get_type ())
+#define NM_IP4_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_IP4_CONFIG, NMIP4Config))
+#define NM_IP4_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_IP4_CONFIG, NMIP4ConfigClass))
+#define NM_IS_IP4_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_IP4_CONFIG))
+#define NM_IS_IP4_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_IP4_CONFIG))
+#define NM_IP4_CONFIG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_IP4_CONFIG, NMIP4ConfigClass))
+
+typedef struct {
+ NMObject parent;
+} NMIP4Config;
+
+typedef struct {
+ NMObjectClass parent;
+
+ /* Padding for future expansion */
+ void (*_reserved1) (void);
+ void (*_reserved2) (void);
+ void (*_reserved3) (void);
+ void (*_reserved4) (void);
+ void (*_reserved5) (void);
+ void (*_reserved6) (void);
+} NMIP4ConfigClass;
+
+#define NM_IP4_CONFIG_GATEWAY "gateway"
+#define NM_IP4_CONFIG_ADDRESSES "addresses"
+#define NM_IP4_CONFIG_ROUTES "routes"
+#define NM_IP4_CONFIG_NAMESERVERS "nameservers"
+#define NM_IP4_CONFIG_DOMAINS "domains"
+#define NM_IP4_CONFIG_SEARCHES "searches"
+#define NM_IP4_CONFIG_WINS_SERVERS "wins-servers"
+
+GType nm_ip4_config_get_type (void);
+
+GObject *nm_ip4_config_new (DBusGConnection *connection, const char *object_path);
+
+NM_AVAILABLE_IN_0_9_10
+const char * nm_ip4_config_get_gateway (NMIP4Config *config);
+const GSList * nm_ip4_config_get_addresses (NMIP4Config *config);
+const GSList * nm_ip4_config_get_routes (NMIP4Config *config);
+const GArray * nm_ip4_config_get_nameservers (NMIP4Config *config);
+const GPtrArray *nm_ip4_config_get_domains (NMIP4Config *config);
+NM_AVAILABLE_IN_0_9_10
+const GPtrArray *nm_ip4_config_get_searches (NMIP4Config *config);
+const GArray * nm_ip4_config_get_wins_servers (NMIP4Config *config);
+
+G_END_DECLS
+
+#endif /* NM_IP4_CONFIG_H */
diff --git a/libnm/nm-ip6-config.c b/libnm/nm-ip6-config.c
new file mode 100644
index 0000000000..21156090bd
--- /dev/null
+++ b/libnm/nm-ip6-config.c
@@ -0,0 +1,493 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2008 Novell, Inc.
+ * Copyright 2008 - 2014 Red Hat, Inc.
+ */
+
+#include <string.h>
+
+#include <nm-setting-ip6-config.h>
+#include "nm-ip6-config.h"
+#include "NetworkManager.h"
+#include "nm-types-private.h"
+#include "nm-object-private.h"
+#include "nm-utils.h"
+
+G_DEFINE_TYPE (NMIP6Config, nm_ip6_config, NM_TYPE_OBJECT)
+
+#define NM_IP6_CONFIG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_IP6_CONFIG, NMIP6ConfigPrivate))
+
+typedef struct {
+ DBusGProxy *proxy;
+
+ char *gateway;
+ GSList *addresses;
+ GSList *routes;
+ GSList *nameservers;
+ GPtrArray *domains;
+ GPtrArray *searches;
+} NMIP6ConfigPrivate;
+
+enum {
+ PROP_0,
+ PROP_GATEWAY,
+ PROP_ADDRESSES,
+ PROP_ROUTES,
+ PROP_NAMESERVERS,
+ PROP_DOMAINS,
+ PROP_SEARCHES,
+
+ LAST_PROP
+};
+
+/**
+ * nm_ip6_config_new:
+ * @connection: the #DBusGConnection
+ * @object_path: the DBus object path of the device
+ *
+ * Creates a new #NMIP6Config.
+ *
+ * Returns: (transfer full): a new IP6 configuration
+ **/
+GObject *
+nm_ip6_config_new (DBusGConnection *connection, const char *object_path)
+{
+ return (GObject *) g_object_new (NM_TYPE_IP6_CONFIG,
+ NM_OBJECT_DBUS_CONNECTION, connection,
+ NM_OBJECT_DBUS_PATH, object_path,
+ NULL);
+}
+
+static gboolean
+demarshal_ip6_address_array (NMObject *object, GParamSpec *pspec, GValue *value, gpointer field)
+{
+ NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (object);
+
+ g_slist_free_full (priv->addresses, (GDestroyNotify) nm_ip6_address_unref);
+ priv->addresses = NULL;
+
+ priv->addresses = nm_utils_ip6_addresses_from_gvalue (value);
+ _nm_object_queue_notify (object, NM_IP6_CONFIG_ADDRESSES);
+
+ return TRUE;
+}
+
+static gboolean
+demarshal_ip6_nameserver_array (NMObject *object, GParamSpec *pspec, GValue *value, gpointer field)
+{
+ if (!_nm_ip6_address_array_demarshal (value, (GSList **) field))
+ return FALSE;
+
+ if (pspec && !strcmp (pspec->name, NM_IP6_CONFIG_NAMESERVERS))
+ _nm_object_queue_notify (object, NM_IP6_CONFIG_NAMESERVERS);
+
+ return TRUE;
+}
+
+static gboolean
+demarshal_domains (NMObject *object, GParamSpec *pspec, GValue *value, gpointer field)
+{
+ if (!_nm_string_array_demarshal (value, (GPtrArray **) field))
+ return FALSE;
+
+ _nm_object_queue_notify (object, NM_IP6_CONFIG_DOMAINS);
+ return TRUE;
+}
+
+static gboolean
+demarshal_searches (NMObject *object, GParamSpec *pspec, GValue *value, gpointer field)
+{
+ if (!_nm_string_array_demarshal (value, (GPtrArray **) field))
+ return FALSE;
+
+ _nm_object_queue_notify (object, NM_IP6_CONFIG_SEARCHES);
+ return TRUE;
+}
+
+static gboolean
+demarshal_ip6_routes_array (NMObject *object, GParamSpec *pspec, GValue *value, gpointer field)
+{
+ NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (object);
+
+ g_slist_free_full (priv->routes, (GDestroyNotify) nm_ip6_route_unref);
+ priv->routes = NULL;
+
+ priv->routes = nm_utils_ip6_routes_from_gvalue (value);
+ _nm_object_queue_notify (object, NM_IP6_CONFIG_ROUTES);
+
+ return TRUE;
+}
+
+static void
+register_properties (NMIP6Config *config)
+{
+ NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
+ const NMPropertiesInfo property_info[] = {
+ { NM_IP6_CONFIG_GATEWAY, &priv->gateway, },
+ { NM_IP6_CONFIG_ADDRESSES, &priv->addresses, demarshal_ip6_address_array },
+ { NM_IP6_CONFIG_ROUTES, &priv->routes, demarshal_ip6_routes_array },
+ { NM_IP6_CONFIG_NAMESERVERS, &priv->nameservers, demarshal_ip6_nameserver_array },
+ { NM_IP6_CONFIG_DOMAINS, &priv->domains, demarshal_domains },
+ { NM_IP6_CONFIG_SEARCHES, &priv->searches, demarshal_searches },
+ { NULL },
+ };
+
+ _nm_object_register_properties (NM_OBJECT (config),
+ priv->proxy,
+ property_info);
+}
+
+/**
+ * nm_ip6_config_get_gateway:
+ * @config: a #NMIP6Config
+ *
+ * Gets the IP6 gateway.
+ *
+ * Returns: the IPv6 gateway of the configuration.
+ *
+ * Since: 0.9.10
+ **/
+const char *
+nm_ip6_config_get_gateway (NMIP6Config *config)
+{
+ g_return_val_if_fail (NM_IS_IP6_CONFIG (config), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (config));
+ return NM_IP6_CONFIG_GET_PRIVATE (config)->gateway;
+}
+
+/**
+ * nm_ip6_config_get_addresses:
+ * @config: a #NMIP6Config
+ *
+ * Gets the IP6 addresses (containing the address, prefix, and gateway).
+ *
+ * Returns: (element-type NMIP6Address): the #GSList containing
+ * #NMIP6Address<!-- -->es. This is the internal copy used by the configuration
+ * and must not be modified.
+ **/
+const GSList *
+nm_ip6_config_get_addresses (NMIP6Config *config)
+{
+ g_return_val_if_fail (NM_IS_IP6_CONFIG (config), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (config));
+ return NM_IP6_CONFIG_GET_PRIVATE (config)->addresses;
+}
+
+/**
+ * nm_ip6_config_get_num_nameservers:
+ * @config: a #NMIP6Config
+ *
+ * Gets the number of the domain name servers in the configuration.
+ *
+ * Returns: the number of domain name servers
+ *
+ * Since: 0.9.10
+ **/
+guint32
+nm_ip6_config_get_num_nameservers (NMIP6Config *config)
+{
+ g_return_val_if_fail (NM_IS_IP6_CONFIG (config), 0);
+
+ _nm_object_ensure_inited (NM_OBJECT (config));
+ return g_slist_length (NM_IP6_CONFIG_GET_PRIVATE (config)->nameservers);
+}
+
+/**
+ * nm_ip6_config_get_nameserver:
+ * @config: a #NMIP6Config
+ * @idx: index of the nameserver to return
+ *
+ * Gets the domain name server at index @idx in the configuration.
+ *
+ * Returns: (array fixed-size=16) (element-type guint8) (transfer none):
+ * the IPv6 address of domain name server at index @iidx
+ *
+ * Since: 0.9.10
+ **/
+const struct in6_addr *
+nm_ip6_config_get_nameserver (NMIP6Config *config, guint32 idx)
+{
+ NMIP6ConfigPrivate *priv;
+ GSList *item;
+ guint32 i = 0;
+
+ g_return_val_if_fail (NM_IS_IP6_CONFIG (config), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (config));
+ priv = NM_IP6_CONFIG_GET_PRIVATE (config);
+
+ for (item = priv->nameservers; item && i < idx; i++)
+ item = item->next;
+
+ g_return_val_if_fail (item, NULL);
+ return item ? (const struct in6_addr *) item->data : NULL;
+}
+
+/* FIXME: like in libnm_util, in6_addr is not introspectable, so skipping here */
+/**
+ * nm_ip6_config_get_nameservers: (skip)
+ * @config: a #NMIP6Config
+ *
+ * Gets the domain name servers (DNS).
+ *
+ * Returns: a #GSList containing elements of type 'struct in6_addr' which
+ * contain the addresses of nameservers of the configuration. This is the
+ * internal copy used by the configuration and must not be modified.
+ **/
+const GSList *
+nm_ip6_config_get_nameservers (NMIP6Config *config)
+{
+ g_return_val_if_fail (NM_IS_IP6_CONFIG (config), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (config));
+ return NM_IP6_CONFIG_GET_PRIVATE (config)->nameservers;
+}
+
+/**
+ * nm_ip6_config_get_domains:
+ * @config: a #NMIP6Config
+ *
+ * Gets the domain names.
+ *
+ * Returns: (element-type utf8): the #GPtrArray containing domains as strings.
+ * This is the internal copy used by the configuration, and must not be modified.
+ **/
+const GPtrArray *
+nm_ip6_config_get_domains (NMIP6Config *config)
+{
+ g_return_val_if_fail (NM_IS_IP6_CONFIG (config), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (config));
+ return handle_ptr_array_return (NM_IP6_CONFIG_GET_PRIVATE (config)->domains);
+}
+
+/**
+ * nm_ip6_config_get_searches:
+ * @config: a #NMIP6Config
+ *
+ * Gets the dns searches.
+ *
+ * Returns: (element-type utf8): the #GPtrArray containing dns searches as strings.
+ * This is the internal copy used by the configuration, and must not be modified.
+ *
+ * Since: 0.9.10
+ **/
+const GPtrArray *
+nm_ip6_config_get_searches (NMIP6Config *config)
+{
+ g_return_val_if_fail (NM_IS_IP6_CONFIG (config), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (config));
+ return handle_ptr_array_return (NM_IP6_CONFIG_GET_PRIVATE (config)->searches);
+}
+
+/**
+ * nm_ip6_config_get_routes:
+ * @config: a #NMIP6Config
+ *
+ * Gets the routes.
+ *
+ * Returns: (element-type NMIP6Route): the #GSList containing
+ * #NMIP6Routes. This is the internal copy used by the configuration,
+ * and must not be modified.
+ **/
+const GSList *
+nm_ip6_config_get_routes (NMIP6Config *config)
+{
+ g_return_val_if_fail (NM_IS_IP6_CONFIG (config), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (config));
+ return NM_IP6_CONFIG_GET_PRIVATE (config)->routes;
+}
+
+static void
+constructed (GObject *object)
+{
+ NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (object);
+
+ G_OBJECT_CLASS (nm_ip6_config_parent_class)->constructed (object);
+
+ priv->proxy = _nm_object_new_proxy (NM_OBJECT (object), NULL, NM_DBUS_INTERFACE_IP6_CONFIG);
+ register_properties (NM_IP6_CONFIG (object));
+}
+
+static void
+finalize (GObject *object)
+{
+ NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (object);
+
+ g_free (priv->gateway);
+
+ g_slist_free_full (priv->addresses, (GDestroyNotify) nm_ip6_address_unref);
+ g_slist_free_full (priv->routes, (GDestroyNotify) nm_ip6_route_unref);
+ g_slist_free_full (priv->nameservers, g_free);
+
+ if (priv->domains) {
+ g_ptr_array_set_free_func (priv->domains, g_free);
+ g_ptr_array_free (priv->domains, TRUE);
+ }
+
+ if (priv->searches) {
+ g_ptr_array_set_free_func (priv->searches, g_free);
+ g_ptr_array_free (priv->searches, TRUE);
+ }
+
+ g_object_unref (priv->proxy);
+
+ G_OBJECT_CLASS (nm_ip6_config_parent_class)->finalize (object);
+}
+
+static void
+get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NMIP6Config *self = NM_IP6_CONFIG (object);
+ NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self);
+
+ _nm_object_ensure_inited (NM_OBJECT (object));
+
+ switch (prop_id) {
+ case PROP_GATEWAY:
+ g_value_set_string (value, nm_ip6_config_get_gateway (self));
+ break;
+ case PROP_ADDRESSES:
+ nm_utils_ip6_addresses_to_gvalue (priv->addresses, value);
+ break;
+ case PROP_ROUTES:
+ nm_utils_ip6_routes_to_gvalue (priv->routes, value);
+ break;
+ case PROP_NAMESERVERS:
+ g_value_set_boxed (value, nm_ip6_config_get_nameservers (self));
+ break;
+ case PROP_DOMAINS:
+ g_value_set_boxed (value, nm_ip6_config_get_domains (self));
+ break;
+ case PROP_SEARCHES:
+ g_value_set_boxed (value, nm_ip6_config_get_searches (self));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nm_ip6_config_init (NMIP6Config *config)
+{
+}
+
+static void
+nm_ip6_config_class_init (NMIP6ConfigClass *config_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (config_class);
+
+ g_type_class_add_private (config_class, sizeof (NMIP6ConfigPrivate));
+
+ /* virtual methods */
+ object_class->constructed = constructed;
+ object_class->get_property = get_property;
+ object_class->finalize = finalize;
+
+ /* properties */
+
+ /**
+ * NMIP6Config:gateway:
+ *
+ * The IPv6 gateway as string
+ *
+ * Since: 0.9.10
+ **/
+ g_object_class_install_property
+ (object_class, PROP_GATEWAY,
+ g_param_spec_string (NM_IP6_CONFIG_GATEWAY, "", "",
+ NULL,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMIP6Config:addresses:
+ *
+ * The #GPtrArray containing the IPv6 addresses; use
+ * nm_utils_ip6_addresses_from_gvalue() to return a #GSList of
+ * #NMSettingIP6Address objects that is more usable than the raw data.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_ADDRESSES,
+ g_param_spec_boxed (NM_IP6_CONFIG_ADDRESSES, "", "",
+ NM_TYPE_IP6_ADDRESS_OBJECT_ARRAY,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMIP6Config:routes:
+ *
+ * The #GPtrArray containing the IPv6 routes; use
+ * nm_utils_ip6_routes_from_gvalue() to return a #GSList of
+ * #NMSettingIP6Address objects that is more usable than the raw data.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_ROUTES,
+ g_param_spec_boxed (NM_IP6_CONFIG_ROUTES, "", "",
+ NM_TYPE_IP6_ROUTE_OBJECT_ARRAY,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMIP6Config:nameservers:
+ *
+ * The #GPtrArray containing elements of type 'struct ip6_addr' which
+ * contain the addresses of nameservers of the configuration.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_NAMESERVERS,
+ g_param_spec_boxed (NM_IP6_CONFIG_NAMESERVERS, "", "",
+ NM_TYPE_IP6_ADDRESS_ARRAY,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMIP6Config:domains:
+ *
+ * The #GPtrArray containing domain strings of the configuration.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_DOMAINS,
+ g_param_spec_boxed (NM_IP6_CONFIG_DOMAINS, "", "",
+ NM_TYPE_STRING_ARRAY,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMIP6Config:searches:
+ *
+ * The #GPtrArray containing dns search strings of the configuration.
+ *
+ * Since: 0.9.10
+ **/
+ g_object_class_install_property
+ (object_class, PROP_SEARCHES,
+ g_param_spec_boxed (NM_IP6_CONFIG_SEARCHES, "", "",
+ NM_TYPE_STRING_ARRAY,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+}
diff --git a/libnm/nm-ip6-config.h b/libnm/nm-ip6-config.h
new file mode 100644
index 0000000000..7a64805b68
--- /dev/null
+++ b/libnm/nm-ip6-config.h
@@ -0,0 +1,81 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2008 Novell, Inc.
+ * Copyright 2008 - 2014 Red Hat, Inc.
+ */
+
+#ifndef NM_IP6_CONFIG_H
+#define NM_IP6_CONFIG_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <dbus/dbus-glib.h>
+#include "nm-object.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_IP6_CONFIG (nm_ip6_config_get_type ())
+#define NM_IP6_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_IP6_CONFIG, NMIP6Config))
+#define NM_IP6_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_IP6_CONFIG, NMIP6ConfigClass))
+#define NM_IS_IP6_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_IP6_CONFIG))
+#define NM_IS_IP6_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_IP6_CONFIG))
+#define NM_IP6_CONFIG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_IP6_CONFIG, NMIP6ConfigClass))
+
+typedef struct {
+ NMObject parent;
+} NMIP6Config;
+
+typedef struct {
+ NMObjectClass parent;
+
+ /* Padding for future expansion */
+ void (*_reserved1) (void);
+ void (*_reserved2) (void);
+ void (*_reserved3) (void);
+ void (*_reserved4) (void);
+ void (*_reserved5) (void);
+ void (*_reserved6) (void);
+} NMIP6ConfigClass;
+
+#define NM_IP6_CONFIG_GATEWAY "gateway"
+#define NM_IP6_CONFIG_ADDRESSES "addresses"
+#define NM_IP6_CONFIG_ROUTES "routes"
+#define NM_IP6_CONFIG_NAMESERVERS "nameservers"
+#define NM_IP6_CONFIG_DOMAINS "domains"
+#define NM_IP6_CONFIG_SEARCHES "searches"
+
+GType nm_ip6_config_get_type (void);
+
+GObject *nm_ip6_config_new (DBusGConnection *connection, const char *object_path);
+
+NM_AVAILABLE_IN_0_9_10
+const char * nm_ip6_config_get_gateway (NMIP6Config *config);
+const GSList * nm_ip6_config_get_addresses (NMIP6Config *config);
+const GSList * nm_ip6_config_get_routes (NMIP6Config *config);
+NM_AVAILABLE_IN_0_9_10
+guint32 nm_ip6_config_get_num_nameservers (NMIP6Config *config);
+NM_AVAILABLE_IN_0_9_10
+const struct in6_addr *nm_ip6_config_get_nameserver (NMIP6Config *config, guint32 idx);
+const GSList * nm_ip6_config_get_nameservers (NMIP6Config *config);
+const GPtrArray * nm_ip6_config_get_domains (NMIP6Config *config);
+NM_AVAILABLE_IN_0_9_10
+const GPtrArray * nm_ip6_config_get_searches (NMIP6Config *config);
+
+G_END_DECLS
+
+#endif /* NM_IP6_CONFIG_H */
diff --git a/libnm/nm-object-cache.c b/libnm/nm-object-cache.c
new file mode 100644
index 0000000000..fe388803e7
--- /dev/null
+++ b/libnm/nm-object-cache.c
@@ -0,0 +1,88 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2008 Red Hat, Inc.
+ */
+
+#include <string.h>
+#include <glib.h>
+#include "nm-object-cache.h"
+#include "nm-object.h"
+
+static GHashTable *cache = NULL;
+
+static void
+_init_cache (void)
+{
+ if (G_UNLIKELY (cache == NULL))
+ cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+}
+
+static void
+_nm_object_cache_remove_by_path (char *path)
+{
+ _init_cache ();
+ g_hash_table_remove (cache, path);
+ g_free (path);
+}
+
+void
+_nm_object_cache_add (NMObject *object)
+{
+ char *path;
+
+ _init_cache ();
+ path = g_strdup (nm_object_get_path (object));
+ g_hash_table_insert (cache, path, object);
+ g_object_set_data_full (G_OBJECT (object), "nm-object-cache-tag",
+ g_strdup (path), (GDestroyNotify) _nm_object_cache_remove_by_path);
+}
+
+NMObject *
+_nm_object_cache_get (const char *path)
+{
+ NMObject *object;
+
+ _init_cache ();
+ object = g_hash_table_lookup (cache, path);
+ return object ? g_object_ref (object) : NULL;
+}
+
+void
+_nm_object_cache_clear (void)
+{
+ GHashTableIter iter;
+ GObject *obj;
+ const char *path;
+ char *foo;
+
+ if (!cache)
+ return;
+
+ g_hash_table_iter_init (&iter, cache);
+ while (g_hash_table_iter_next (&iter, (gpointer) &path, (gpointer) &obj)) {
+ /* Remove the callback so that if the object isn't yet released
+ * by a client, when it does finally get unrefed, it won't trigger
+ * the cache removal for a new object with the same path as the
+ * one being released.
+ */
+ foo = g_object_steal_data (obj, "nm-object-cache-tag");
+ g_free (foo);
+
+ g_hash_table_iter_remove (&iter);
+ }
+}
diff --git a/libnm/nm-object-cache.h b/libnm/nm-object-cache.h
new file mode 100644
index 0000000000..7aca3b4fba
--- /dev/null
+++ b/libnm/nm-object-cache.h
@@ -0,0 +1,37 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2008 Red Hat, Inc.
+ */
+
+#ifndef NM_OBJECT_CACHE_H
+#define NM_OBJECT_CACHE_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include "nm-object.h"
+
+G_BEGIN_DECLS
+
+/* Returns referenced object from the cache */
+NMObject *_nm_object_cache_get (const char *path);
+void _nm_object_cache_add (NMObject *object);
+void _nm_object_cache_clear (void);
+
+G_END_DECLS
+
+#endif /* NM_OBJECT_CACHE_H */
diff --git a/libnm/nm-object-private.h b/libnm/nm-object-private.h
new file mode 100644
index 0000000000..75e63b2e7e
--- /dev/null
+++ b/libnm/nm-object-private.h
@@ -0,0 +1,92 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2008 - 2011 Red Hat, Inc.
+ */
+
+#ifndef NM_OBJECT_PRIVATE_H
+#define NM_OBJECT_PRIVATE_H
+
+#include <gio/gio.h>
+#include "nm-object.h"
+
+void _nm_object_ensure_inited (NMObject *object);
+
+typedef gboolean (*PropertyMarshalFunc) (NMObject *, GParamSpec *, GValue *, gpointer);
+
+typedef GObject * (*NMObjectCreatorFunc) (DBusGConnection *, const char *);
+
+typedef struct {
+ const char *name;
+ gpointer field;
+ PropertyMarshalFunc func;
+ GType object_type;
+ const char *signal_prefix;
+} NMPropertiesInfo;
+
+DBusGProxy *_nm_object_new_proxy (NMObject *self,
+ const char *path,
+ const char *interface);
+
+gboolean _nm_object_is_connection_private (NMObject *self);
+
+void _nm_object_register_properties (NMObject *object,
+ DBusGProxy *proxy,
+ const NMPropertiesInfo *info);
+
+gboolean _nm_object_reload_properties (NMObject *object, GError **error);
+
+void _nm_object_reload_properties_async (NMObject *object,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean _nm_object_reload_properties_finish (NMObject *object,
+ GAsyncResult *result,
+ GError **error);
+
+void _nm_object_queue_notify (NMObject *object, const char *property);
+
+void _nm_object_suppress_property_updates (NMObject *object, gboolean suppress);
+
+/* DBus property accessors */
+
+void _nm_object_reload_property (NMObject *object,
+ const char *interface,
+ const char *prop_name);
+
+void _nm_object_set_property (NMObject *object,
+ const char *interface,
+ const char *prop_name,
+ GValue *value);
+
+static inline const GPtrArray *
+handle_ptr_array_return (GPtrArray *array)
+{
+ /* zero-length is special-case; return NULL */
+ if (!array || !array->len)
+ return NULL;
+ return array;
+}
+
+/* object demarshalling support */
+typedef GType (*NMObjectTypeFunc) (DBusGConnection *, const char *);
+typedef void (*NMObjectTypeCallbackFunc) (GType, gpointer);
+typedef void (*NMObjectTypeAsyncFunc) (DBusGConnection *, const char *, NMObjectTypeCallbackFunc, gpointer);
+
+void _nm_object_register_type_func (GType base_type, NMObjectTypeFunc type_func,
+ NMObjectTypeAsyncFunc type_async_func);
+
+#endif /* NM_OBJECT_PRIVATE_H */
diff --git a/libnm/nm-object.c b/libnm/nm-object.c
new file mode 100644
index 0000000000..3550677df6
--- /dev/null
+++ b/libnm/nm-object.c
@@ -0,0 +1,1445 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2008 Novell, Inc.
+ * Copyright 2007 - 2012 Red Hat, Inc.
+ */
+
+#include <string.h>
+#include <gio/gio.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <nm-utils.h>
+#include "NetworkManager.h"
+#include "nm-object.h"
+#include "nm-object-cache.h"
+#include "nm-object-private.h"
+#include "nm-dbus-glib-types.h"
+#include "nm-glib-compat.h"
+#include "nm-types.h"
+#include "nm-dbus-helpers-private.h"
+
+static gboolean debug = FALSE;
+#define dbgmsg(f,...) if (G_UNLIKELY (debug)) { g_message (f, ## __VA_ARGS__ ); }
+
+static void nm_object_initable_iface_init (GInitableIface *iface);
+static void nm_object_async_initable_iface_init (GAsyncInitableIface *iface);
+
+static GHashTable *type_funcs, *type_async_funcs;
+
+G_DEFINE_ABSTRACT_TYPE_WITH_CODE (NMObject, nm_object, G_TYPE_OBJECT,
+ type_funcs = g_hash_table_new (NULL, NULL);
+ type_async_funcs = g_hash_table_new (NULL, NULL);
+ G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, nm_object_initable_iface_init);
+ G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, nm_object_async_initable_iface_init);
+ )
+
+#define NM_OBJECT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_OBJECT, NMObjectPrivate))
+
+typedef struct {
+ PropertyMarshalFunc func;
+ GType object_type;
+ gpointer field;
+ const char *signal_prefix;
+} PropertyInfo;
+
+static void reload_complete (NMObject *object);
+
+typedef struct {
+ DBusGConnection *connection;
+ DBusGProxy *bus_proxy;
+ gboolean nm_running;
+
+ char *path;
+ DBusGProxy *properties_proxy;
+ GSList *property_interfaces;
+ GSList *property_tables;
+ NMObject *parent;
+ gboolean suppress_property_updates;
+
+ GSList *notify_props;
+ guint32 notify_id;
+ gboolean inited;
+
+ GSList *reload_results;
+ guint reload_remaining;
+ GError *reload_error;
+} NMObjectPrivate;
+
+enum {
+ PROP_0,
+ PROP_DBUS_CONNECTION,
+ PROP_DBUS_PATH,
+
+ LAST_PROP
+};
+
+enum {
+ OBJECT_CREATION_FAILED,
+
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+/**
+ * nm_object_error_quark:
+ *
+ * Registers an error quark for #NMObject if necessary.
+ *
+ * Returns: the error quark used for #NMObject errors.
+ **/
+GQuark
+nm_object_error_quark (void)
+{
+ static GQuark quark;
+
+ if (G_UNLIKELY (!quark))
+ quark = g_quark_from_static_string ("nm-object-error-quark");
+ return quark;
+}
+
+static void
+proxy_name_owner_changed (DBusGProxy *proxy,
+ const char *name,
+ const char *old_owner,
+ const char *new_owner,
+ gpointer user_data)
+{
+ NMObject *self = NM_OBJECT (user_data);
+ NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (self);
+
+ if (g_strcmp0 (name, NM_DBUS_SERVICE) == 0) {
+ gboolean old_good = (old_owner && old_owner[0]);
+ gboolean new_good = (new_owner && new_owner[0]);
+
+ if (!old_good && new_good)
+ priv->nm_running = TRUE;
+ else if (old_good && !new_good)
+ priv->nm_running = FALSE;
+ }
+}
+
+static void
+nm_object_init (NMObject *object)
+{
+}
+
+static GObject*
+constructor (GType type,
+ guint n_construct_params,
+ GObjectConstructParam *construct_params)
+{
+ GObject *object;
+ NMObjectPrivate *priv;
+
+ object = G_OBJECT_CLASS (nm_object_parent_class)->constructor (type,
+ n_construct_params,
+ construct_params);
+
+ priv = NM_OBJECT_GET_PRIVATE (object);
+
+ if (priv->connection == NULL || priv->path == NULL) {
+ g_warn_if_reached ();
+ g_object_unref (object);
+ return NULL;
+ }
+
+ return object;
+}
+
+static void
+constructed (GObject *object)
+{
+ NMObject *self = NM_OBJECT (object);
+ NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object);
+
+ if (G_OBJECT_CLASS (nm_object_parent_class)->constructed)
+ G_OBJECT_CLASS (nm_object_parent_class)->constructed (object);
+
+ priv->properties_proxy = _nm_object_new_proxy (self, NULL, "org.freedesktop.DBus.Properties");
+
+ if (_nm_object_is_connection_private (self))
+ priv->nm_running = TRUE;
+ else {
+ priv->bus_proxy = dbus_g_proxy_new_for_name (priv->connection,
+ DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS);
+ g_assert (priv->bus_proxy);
+
+ dbus_g_proxy_add_signal (priv->bus_proxy, "NameOwnerChanged",
+ G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
+ G_TYPE_INVALID);
+ dbus_g_proxy_connect_signal (priv->bus_proxy,
+ "NameOwnerChanged",
+ G_CALLBACK (proxy_name_owner_changed),
+ object, NULL);
+ }
+}
+
+static gboolean
+init_sync (GInitable *initable, GCancellable *cancellable, GError **error)
+{
+ NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (initable);
+
+ if (priv->bus_proxy) {
+ if (!dbus_g_proxy_call (priv->bus_proxy,
+ "NameHasOwner", error,
+ G_TYPE_STRING, NM_DBUS_SERVICE,
+ G_TYPE_INVALID,
+ G_TYPE_BOOLEAN, &priv->nm_running,
+ G_TYPE_INVALID))
+ return FALSE;
+ }
+
+ priv->inited = TRUE;
+ return _nm_object_reload_properties (NM_OBJECT (initable), error);
+}
+
+/* Takes ownership of @error */
+static void
+init_async_complete (GSimpleAsyncResult *simple, GError *error)
+{
+ if (error)
+ g_simple_async_result_take_error (simple, error);
+ else
+ g_simple_async_result_set_op_res_gboolean (simple, TRUE);
+ g_simple_async_result_complete (simple);
+ g_object_unref (simple);
+}
+
+static void
+init_async_got_properties (GObject *object, GAsyncResult *result, gpointer user_data)
+{
+ GSimpleAsyncResult *simple = user_data;
+ GError *error = NULL;
+
+ NM_OBJECT_GET_PRIVATE (object)->inited = TRUE;
+ if (!_nm_object_reload_properties_finish (NM_OBJECT (object), result, &error))
+ g_assert (error);
+ init_async_complete (simple, error);
+}
+
+static void
+init_async_got_manager_running (DBusGProxy *proxy, DBusGProxyCall *call,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple = user_data;
+ NMObject *self;
+ NMObjectPrivate *priv;
+ GError *error = NULL;
+
+ self = NM_OBJECT (g_async_result_get_source_object (G_ASYNC_RESULT (simple)));
+ priv = NM_OBJECT_GET_PRIVATE (self);
+
+ if (!dbus_g_proxy_end_call (proxy, call, &error,
+ G_TYPE_BOOLEAN, &priv->nm_running,
+ G_TYPE_INVALID)) {
+ init_async_complete (simple, error);
+ } else if (!priv->nm_running) {
+ priv->inited = TRUE;
+ init_async_complete (simple, NULL);
+ } else
+ _nm_object_reload_properties_async (self, init_async_got_properties, simple);
+
+ /* g_async_result_get_source_object() adds a ref */
+ g_object_unref (self);
+}
+
+static void
+init_async (GAsyncInitable *initable, int io_priority,
+ GCancellable *cancellable, GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (initable);
+ GSimpleAsyncResult *simple;
+
+ simple = g_simple_async_result_new (G_OBJECT (initable), callback, user_data, init_async);
+
+ if (_nm_object_is_connection_private (NM_OBJECT (initable)))
+ _nm_object_reload_properties_async (NM_OBJECT (initable), init_async_got_properties, simple);
+ else {
+ /* Check if NM is running */
+ dbus_g_proxy_begin_call (priv->bus_proxy, "NameHasOwner",
+ init_async_got_manager_running,
+ simple, NULL,
+ G_TYPE_STRING, NM_DBUS_SERVICE,
+ G_TYPE_INVALID);
+ }
+}
+
+static gboolean
+init_finish (GAsyncInitable *initable, GAsyncResult *result, GError **error)
+{
+ GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
+
+ if (g_simple_async_result_propagate_error (simple, error))
+ return FALSE;
+ else
+ return TRUE;
+}
+
+static void
+dispose (GObject *object)
+{
+ NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object);
+
+ if (priv->notify_id) {
+ g_source_remove (priv->notify_id);
+ priv->notify_id = 0;
+ }
+
+ g_slist_free_full (priv->notify_props, g_free);
+ priv->notify_props = NULL;
+
+ g_slist_free_full (priv->property_interfaces, g_free);
+ priv->property_interfaces = NULL;
+
+ g_clear_object (&priv->properties_proxy);
+ g_clear_object (&priv->bus_proxy);
+
+ if (priv->connection) {
+ dbus_g_connection_unref (priv->connection);
+ priv->connection = NULL;
+ }
+
+ G_OBJECT_CLASS (nm_object_parent_class)->dispose (object);
+}
+
+static void
+finalize (GObject *object)
+{
+ NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object);
+
+ g_slist_free_full (priv->property_tables, (GDestroyNotify) g_hash_table_destroy);
+ g_free (priv->path);
+
+ G_OBJECT_CLASS (nm_object_parent_class)->finalize (object);
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_DBUS_CONNECTION:
+ /* Construct only */
+ priv->connection = g_value_dup_boxed (value);
+ if (!priv->connection)
+ priv->connection = _nm_dbus_new_connection (NULL);
+ break;
+ case PROP_DBUS_PATH:
+ /* Construct only */
+ priv->path = g_value_dup_string (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_DBUS_CONNECTION:
+ g_value_set_boxed (value, priv->connection);
+ break;
+ case PROP_DBUS_PATH:
+ g_value_set_string (value, priv->path);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nm_object_class_init (NMObjectClass *nm_object_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (nm_object_class);
+
+ g_type_class_add_private (nm_object_class, sizeof (NMObjectPrivate));
+
+ /* virtual methods */
+ object_class->constructor = constructor;
+ object_class->constructed = constructed;
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+ object_class->dispose = dispose;
+ object_class->finalize = finalize;
+
+ /* Properties */
+
+ /**
+ * NMObject:connection:
+ *
+ * The #DBusGConnection of the object.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_DBUS_CONNECTION,
+ g_param_spec_boxed (NM_OBJECT_DBUS_CONNECTION, "", "",
+ DBUS_TYPE_G_CONNECTION,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMObject:path:
+ *
+ * The DBus object path.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_DBUS_PATH,
+ g_param_spec_string (NM_OBJECT_DBUS_PATH, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ /* signals */
+
+ /**
+ * NMObject::object-creation-failed:
+ * @master_object: the object that received the signal
+ * @error: the error that occured while creating object
+ * @failed_path: object path of the failed object
+ *
+ * Indicates that an error occured while creating an #NMObject object
+ * during property handling of @master_object.
+ *
+ * Note: Be aware that the signal is private for libnm-glib's internal
+ * use.
+ **/
+ signals[OBJECT_CREATION_FAILED] =
+ g_signal_new ("object-creation-failed",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NMObjectClass, object_creation_failed),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
+}
+
+static void
+nm_object_initable_iface_init (GInitableIface *iface)
+{
+ iface->init = init_sync;
+}
+
+static void
+nm_object_async_initable_iface_init (GAsyncInitableIface *iface)
+{
+ iface->init_async = init_async;
+ iface->init_finish = init_finish;
+}
+
+/**
+ * nm_object_get_connection:
+ * @object: a #NMObject
+ *
+ * Gets the #NMObject's DBusGConnection.
+ *
+ * Returns: (transfer none): the connection
+ **/
+DBusGConnection *
+nm_object_get_connection (NMObject *object)
+{
+ g_return_val_if_fail (NM_IS_OBJECT (object), NULL);
+
+ return NM_OBJECT_GET_PRIVATE (object)->connection;
+}
+
+/**
+ * nm_object_get_path:
+ * @object: a #NMObject
+ *
+ * Gets the DBus path of the #NMObject.
+ *
+ * Returns: the object's path. This is the internal string used by the
+ * device, and must not be modified.
+ **/
+const char *
+nm_object_get_path (NMObject *object)
+{
+ g_return_val_if_fail (NM_IS_OBJECT (object), NULL);
+
+ return NM_OBJECT_GET_PRIVATE (object)->path;
+}
+
+static gboolean
+deferred_notify_cb (gpointer data)
+{
+ NMObject *object = NM_OBJECT (data);
+ NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object);
+ GSList *props, *iter;
+
+ priv->notify_id = 0;
+
+ /* Clear priv->notify_props early so that an NMObject subclass that
+ * listens to property changes can queue up other property changes
+ * during the g_object_notify() call separately from the property
+ * list we're iterating.
+ */
+ props = g_slist_reverse (priv->notify_props);
+ priv->notify_props = NULL;
+
+ g_object_ref (object);
+ for (iter = props; iter; iter = g_slist_next (iter)) {
+ g_object_notify (G_OBJECT (object), (const char *) iter->data);
+ g_free (iter->data);
+ }
+ g_object_unref (object);
+
+ g_slist_free (props);
+ return FALSE;
+}
+
+void
+_nm_object_queue_notify (NMObject *object, const char *property)
+{
+ NMObjectPrivate *priv;
+ gboolean found = FALSE;
+ GSList *iter;
+
+ g_return_if_fail (NM_IS_OBJECT (object));
+ g_return_if_fail (property != NULL);
+
+ priv = NM_OBJECT_GET_PRIVATE (object);
+ if (!priv->notify_id)
+ priv->notify_id = g_idle_add_full (G_PRIORITY_LOW, deferred_notify_cb, object, NULL);
+
+ for (iter = priv->notify_props; iter; iter = g_slist_next (iter)) {
+ if (!strcmp ((char *) iter->data, property)) {
+ found = TRUE;
+ break;
+ }
+ }
+
+ if (!found)
+ priv->notify_props = g_slist_prepend (priv->notify_props, g_strdup (property));
+}
+
+void
+_nm_object_register_type_func (GType base_type, NMObjectTypeFunc type_func,
+ NMObjectTypeAsyncFunc type_async_func)
+{
+ g_hash_table_insert (type_funcs,
+ GSIZE_TO_POINTER (base_type),
+ type_func);
+ g_hash_table_insert (type_async_funcs,
+ GSIZE_TO_POINTER (base_type),
+ type_async_func);
+}
+
+static GObject *
+_nm_object_create (GType type, DBusGConnection *connection, const char *path)
+{
+ NMObjectTypeFunc type_func;
+ GObject *object;
+ GError *error = NULL;
+
+ type_func = g_hash_table_lookup (type_funcs, GSIZE_TO_POINTER (type));
+ if (type_func)
+ type = type_func (connection, path);
+
+ if (type == G_TYPE_INVALID) {
+ dbgmsg ("Could not create object for %s: unknown object type", path);
+ return NULL;
+ }
+
+ object = g_object_new (type,
+ NM_OBJECT_DBUS_CONNECTION, connection,
+ NM_OBJECT_DBUS_PATH, path,
+ NULL);
+ if (NM_IS_OBJECT (object))
+ _nm_object_cache_add (NM_OBJECT (object));
+ if (!g_initable_init (G_INITABLE (object), NULL, &error)) {
+ dbgmsg ("Could not create object for %s: %s", path, error->message);
+ g_error_free (error);
+ g_clear_object (&object);
+ }
+
+ return object;
+}
+
+typedef void (*NMObjectCreateCallbackFunc) (GObject *, const char *, gpointer);
+typedef struct {
+ DBusGConnection *connection;
+ char *path;
+ NMObjectCreateCallbackFunc callback;
+ gpointer user_data;
+} NMObjectTypeAsyncData;
+
+static void
+create_async_complete (GObject *object, NMObjectTypeAsyncData *async_data)
+{
+ async_data->callback (object, async_data->path, async_data->user_data);
+
+ g_free (async_data->path);
+ g_slice_free (NMObjectTypeAsyncData, async_data);
+}
+
+static const char *
+nm_object_or_connection_get_path (gpointer instance)
+{
+ if (NM_IS_OBJECT (instance))
+ return nm_object_get_path (instance);
+ else if (NM_IS_CONNECTION (instance))
+ return nm_connection_get_path (instance);
+
+ g_assert_not_reached ();
+}
+
+static void
+async_inited (GObject *source, GAsyncResult *result, gpointer user_data)
+{
+ NMObjectTypeAsyncData *async_data = user_data;
+ GObject *object = G_OBJECT (source);
+ GError *error = NULL;
+
+ if (!g_async_initable_init_finish (G_ASYNC_INITABLE (object), result, &error)) {
+ dbgmsg ("Could not create object for %s: %s",
+ nm_object_or_connection_get_path (object),
+ error->message);
+ g_error_free (error);
+ g_clear_object (&object);
+ }
+
+ create_async_complete (object, async_data);
+}
+
+static void
+async_got_type (GType type, gpointer user_data)
+{
+ NMObjectTypeAsyncData *async_data = user_data;
+ GObject *object;
+
+ /* Ensure we don't have the object already; we may get multiple type
+ * requests for the same object if there are multiple properties on
+ * other objects that refer to the object at this path. One of those
+ * other requests may have already completed.
+ */
+ object = (GObject *) _nm_object_cache_get (async_data->path);
+ if (object) {
+ create_async_complete (object, async_data);
+ return;
+ }
+
+ if (type == G_TYPE_INVALID) {
+ /* Don't know how to create this object */
+ create_async_complete (NULL, async_data);
+ return;
+ }
+
+ object = g_object_new (type,
+ NM_OBJECT_DBUS_CONNECTION, async_data->connection,
+ NM_OBJECT_DBUS_PATH, async_data->path,
+ NULL);
+ g_warn_if_fail (object != NULL);
+ if (NM_IS_OBJECT (object))
+ _nm_object_cache_add (NM_OBJECT (object));
+ g_async_initable_init_async (G_ASYNC_INITABLE (object), G_PRIORITY_DEFAULT,
+ NULL, async_inited, async_data);
+}
+
+static void
+_nm_object_create_async (GType type, DBusGConnection *connection, const char *path,
+ NMObjectCreateCallbackFunc callback, gpointer user_data)
+{
+ NMObjectTypeAsyncFunc type_async_func;
+ NMObjectTypeFunc type_func;
+ NMObjectTypeAsyncData *async_data;
+
+ async_data = g_slice_new (NMObjectTypeAsyncData);
+ async_data->connection = connection;
+ async_data->path = g_strdup (path);
+ async_data->callback = callback;
+ async_data->user_data = user_data;
+
+ type_async_func = g_hash_table_lookup (type_async_funcs, GSIZE_TO_POINTER (type));
+ if (type_async_func) {
+ type_async_func (connection, path, async_got_type, async_data);
+ return;
+ }
+
+ type_func = g_hash_table_lookup (type_funcs, GSIZE_TO_POINTER (type));
+ if (type_func)
+ type = type_func (connection, path);
+
+ async_got_type (type, async_data);
+}
+
+/* Stolen from dbus-glib */
+static char*
+wincaps_to_dash (const char *caps)
+{
+ const char *p;
+ GString *str;
+
+ str = g_string_new (NULL);
+ p = caps;
+ while (*p) {
+ if (g_ascii_isupper (*p)) {
+ if (str->len > 0 && (str->len < 2 || str->str[str->len-2] != '-'))
+ g_string_append_c (str, '-');
+ g_string_append_c (str, g_ascii_tolower (*p));
+ } else
+ g_string_append_c (str, *p);
+ ++p;
+ }
+
+ return g_string_free (str, FALSE);
+}
+
+/* Adds object to array if it's not already there */
+static void
+add_to_object_array_unique (GPtrArray *array, GObject *obj)
+{
+ guint i;
+
+ g_return_if_fail (array != NULL);
+
+ if (obj != NULL) {
+ for (i = 0; i < array->len; i++) {
+ if (g_ptr_array_index (array, i) == obj) {
+ g_object_unref (obj);
+ return;
+ }
+ }
+ g_ptr_array_add (array, obj);
+ }
+}
+
+typedef struct {
+ NMObject *self;
+ PropertyInfo *pi;
+
+ GObject **objects;
+ int length, remaining;
+
+ gboolean array;
+ const char *property_name;
+} ObjectCreatedData;
+
+/* Places items from 'needles' that are not in 'haystack' into 'diff' */
+static void
+array_diff (GPtrArray *needles, GPtrArray *haystack, GPtrArray *diff)
+{
+ guint i, j;
+ GObject *obj;
+
+ g_assert (needles);
+ g_assert (haystack);
+ g_assert (diff);
+
+ for (i = 0; i < needles->len; i++) {
+ obj = g_ptr_array_index (needles, i);
+
+ for (j = 0; j < haystack->len; j++) {
+ if (g_ptr_array_index (haystack, j) == obj)
+ break;
+ }
+
+ if (j == haystack->len)
+ g_ptr_array_add (diff, obj);
+ }
+}
+
+static void
+emit_added_removed_signal (NMObject *self,
+ const char *signal_prefix,
+ NMObject *changed,
+ gboolean added)
+{
+ char buf[50];
+ int ret;
+
+ ret = g_snprintf (buf, sizeof (buf), "%s-%s", signal_prefix, added ? "added" : "removed");
+ g_assert (ret < sizeof (buf));
+ g_signal_emit_by_name (self, buf, changed);
+}
+
+static void
+object_property_complete (ObjectCreatedData *odata)
+{
+ NMObject *self = odata->self;
+ NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (self);
+ PropertyInfo *pi = odata->pi;
+ gboolean different = TRUE;
+
+ if (odata->array) {
+ GPtrArray *old = *((GPtrArray **) pi->field);
+ GPtrArray *new;
+ int i;
+
+ /* Build up new array */
+ new = g_ptr_array_sized_new (odata->length);
+ for (i = 0; i < odata->length; i++)
+ add_to_object_array_unique (new, odata->objects[i]);
+
+ if (pi->signal_prefix) {
+ GPtrArray *added = g_ptr_array_sized_new (3);
+ GPtrArray *removed = g_ptr_array_sized_new (3);
+
+ if (old) {
+ /* Find objects in 'old' that do not exist in 'new' */
+ array_diff (old, new, removed);
+
+ /* Find objects in 'new' that do not exist in old */
+ array_diff (new, old, added);
+ } else {
+ for (i = 0; i < new->len; i++)
+ g_ptr_array_add (added, g_ptr_array_index (new, i));
+ }
+
+ *((GPtrArray **) pi->field) = new;
+
+ /* Emit added & removed */
+ for (i = 0; i < removed->len; i++) {
+ emit_added_removed_signal (self,
+ pi->signal_prefix,
+ g_ptr_array_index (removed, i),
+ FALSE);
+ }
+
+ for (i = 0; i < added->len; i++) {
+ emit_added_removed_signal (self,
+ pi->signal_prefix,
+ g_ptr_array_index (added, i),
+ TRUE);
+ }
+
+ different = removed->len || added->len;
+ g_ptr_array_free (added, TRUE);
+ g_ptr_array_free (removed, TRUE);
+ } else {
+ /* No added/removed signals to send, just replace the property with
+ * the new values.
+ */
+ *((GPtrArray **) pi->field) = new;
+ different = TRUE;
+ }
+
+ /* Free old array last since it will release references, thus freeing
+ * any objects in the 'removed' array.
+ */
+ if (old)
+ g_boxed_free (NM_TYPE_OBJECT_ARRAY, old);
+ } else {
+ GObject **obj_p = pi->field;
+
+ different = (*obj_p != odata->objects[0]);
+ if (*obj_p)
+ g_object_unref (*obj_p);
+ *obj_p = odata->objects[0];
+ }
+
+ if (different && odata->property_name)
+ _nm_object_queue_notify (self, odata->property_name);
+
+ if (priv->reload_results && --priv->reload_remaining == 0)
+ reload_complete (self);
+
+ g_object_unref (self);
+ g_free (odata->objects);
+ g_slice_free (ObjectCreatedData, odata);
+}
+
+static void
+object_created (GObject *obj, const char *path, gpointer user_data)
+{
+ ObjectCreatedData *odata = user_data;
+
+ /* We assume that on error, the creator_func printed something */
+
+ if (obj == NULL && g_strcmp0 (path, "/") != 0 ) {
+ GError *error;
+ error = g_error_new (NM_OBJECT_ERROR,
+ NM_OBJECT_ERROR_OBJECT_CREATION_FAILURE,
+ "Creating object for path '%s' failed in libnm-glib.",
+ path);
+ /* Emit a signal about the error. */
+ g_signal_emit (odata->self, signals[OBJECT_CREATION_FAILED], 0, error, path);
+ g_error_free (error);
+ }
+
+ odata->objects[--odata->remaining] = obj;
+ if (!odata->remaining)
+ object_property_complete (odata);
+}
+
+static gboolean
+handle_object_property (NMObject *self, const char *property_name, GValue *value,
+ PropertyInfo *pi, gboolean synchronously)
+{
+ NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (self);
+ GObject *obj;
+ const char *path;
+ ObjectCreatedData *odata;
+
+ odata = g_slice_new (ObjectCreatedData);
+ odata->self = g_object_ref (self);
+ odata->pi = pi;
+ odata->objects = g_new (GObject *, 1);
+ odata->length = odata->remaining = 1;
+ odata->array = FALSE;
+ odata->property_name = property_name;
+
+ if (priv->reload_results)
+ priv->reload_remaining++;
+
+ path = g_value_get_boxed (value);
+
+ if (!strcmp (path, "/")) {
+ object_created (NULL, path, odata);
+ return TRUE;
+ }
+
+ obj = G_OBJECT (_nm_object_cache_get (path));
+ if (obj) {
+ object_created (obj, path, odata);
+ return TRUE;
+ } else if (synchronously) {
+ obj = _nm_object_create (pi->object_type, priv->connection, path);
+ object_created (obj, path, odata);
+ return obj != NULL;
+ } else {
+ _nm_object_create_async (pi->object_type, priv->connection, path,
+ object_created, odata);
+ /* Assume success */
+ return TRUE;
+ }
+}
+
+static gboolean
+handle_object_array_property (NMObject *self, const char *property_name, GValue *value,
+ PropertyInfo *pi, gboolean synchronously)
+{
+ NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (self);
+ GObject *obj;
+ GPtrArray *paths;
+ GPtrArray **array = pi->field;
+ const char *path;
+ ObjectCreatedData *odata;
+ int i;
+
+ paths = g_value_get_boxed (value);
+
+ odata = g_slice_new (ObjectCreatedData);
+ odata->self = g_object_ref (self);
+ odata->pi = pi;
+ odata->objects = g_new0 (GObject *, paths->len);
+ odata->length = odata->remaining = paths->len;
+ odata->array = TRUE;
+ odata->property_name = property_name;
+
+ if (priv->reload_results)
+ priv->reload_remaining++;
+
+ if (paths->len == 0) {
+ object_property_complete (odata);
+ return TRUE;
+ }
+
+ for (i = 0; i < paths->len; i++) {
+ path = paths->pdata[i];
+ if (!strcmp (path, "/")) {
+ /* FIXME: can't happen? */
+ continue;
+ }
+
+ obj = G_OBJECT (_nm_object_cache_get (path));
+ if (obj) {
+ object_created (obj, path, odata);
+ } else if (synchronously) {
+ obj = _nm_object_create (pi->object_type, priv->connection, path);
+ object_created (obj, path, odata);
+ } else {
+ _nm_object_create_async (pi->object_type, priv->connection, path,
+ object_created, odata);
+ }
+ }
+
+ if (!synchronously) {
+ /* Assume success */
+ return TRUE;
+ }
+
+ return *array && ((*array)->len == paths->len);
+}
+
+static void
+handle_property_changed (NMObject *self, const char *dbus_name, GValue *value, gboolean synchronously)
+{
+ NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (self);
+ char *prop_name;
+ PropertyInfo *pi;
+ GParamSpec *pspec;
+ gboolean success = FALSE, found = FALSE;
+ GSList *iter;
+
+ prop_name = wincaps_to_dash (dbus_name);
+
+ /* Iterate through the object and its parents to find the property */
+ for (iter = priv->property_tables; iter; iter = g_slist_next (iter)) {
+ pi = g_hash_table_lookup ((GHashTable *) iter->data, prop_name);
+ if (pi) {
+ if (!pi->field) {
+ /* We know about this property but aren't tracking changes on it. */
+ goto out;
+ }
+
+ found = TRUE;
+ break;
+ }
+ }
+
+ if (!found) {
+ dbgmsg ("Property '%s' unhandled.", prop_name);
+ goto out;
+ }
+
+ pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (G_OBJECT (self)), prop_name);
+ if (!pspec) {
+ dbgmsg ("%s: property '%s' changed but wasn't defined by object type %s.",
+ __func__,
+ prop_name,
+ G_OBJECT_TYPE_NAME (self));
+ goto out;
+ }
+
+ if (G_UNLIKELY (debug)) {
+ char *s;
+ s = g_strdup_value_contents (value);
+ dbgmsg ("PC: (%p) %s::%s => '%s' (%s%s%s)",
+ self, G_OBJECT_TYPE_NAME (self),
+ prop_name,
+ s,
+ G_VALUE_TYPE_NAME (value),
+ pi->object_type ? " / " : "",
+ pi->object_type ? g_type_name (pi->object_type) : "");
+ g_free (s);
+ }
+
+ if (pi->object_type) {
+ if (G_VALUE_HOLDS (value, DBUS_TYPE_G_OBJECT_PATH))
+ success = handle_object_property (self, pspec->name, value, pi, synchronously);
+ else if (G_VALUE_HOLDS (value, DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH))
+ success = handle_object_array_property (self, pspec->name, value, pi, synchronously);
+ else {
+ g_warn_if_reached ();
+ goto out;
+ }
+ } else
+ success = (*(pi->func)) (self, pspec, value, pi->field);
+
+ if (!success) {
+ dbgmsg ("%s: failed to update property '%s' of object type %s.",
+ __func__,
+ prop_name,
+ G_OBJECT_TYPE_NAME (self));
+ }
+
+out:
+ g_free (prop_name);
+}
+
+static void
+process_properties_changed (NMObject *self, GHashTable *properties, gboolean synchronously)
+{
+ NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (self);
+ GHashTableIter iter;
+ gpointer name, value;
+
+ if (priv->suppress_property_updates)
+ return;
+
+ g_hash_table_iter_init (&iter, properties);
+ while (g_hash_table_iter_next (&iter, &name, &value)) {
+ if (value)
+ handle_property_changed (self, name, value, synchronously);
+ else {
+ dbgmsg ("%s:%d %s(): object %s property '%s' value is unexpectedly NULL",
+ __FILE__, __LINE__, __func__, G_OBJECT_TYPE_NAME (self), (const char *) name);
+ }
+ }
+}
+
+static void
+properties_changed_proxy (DBusGProxy *proxy,
+ GHashTable *properties,
+ gpointer user_data)
+{
+ process_properties_changed (NM_OBJECT (user_data), properties, FALSE);
+}
+
+#define HANDLE_TYPE(ucase, lcase, getter) \
+ } else if (pspec->value_type == G_TYPE_##ucase) { \
+ if (G_VALUE_HOLDS_##ucase (value)) { \
+ g##lcase *param = (g##lcase *) field; \
+ *param = g_value_get_##getter (value); \
+ } else { \
+ success = FALSE; \
+ goto done; \
+ }
+
+static gboolean
+demarshal_generic (NMObject *object,
+ GParamSpec *pspec,
+ GValue *value,
+ gpointer field)
+{
+ gboolean success = TRUE;
+
+ if (pspec->value_type == G_TYPE_STRING) {
+ if (G_VALUE_HOLDS_STRING (value)) {
+ char **param = (char **) field;
+ g_free (*param);
+ *param = g_value_dup_string (value);
+ } else if (G_VALUE_HOLDS (value, DBUS_TYPE_G_OBJECT_PATH)) {
+ char **param = (char **) field;
+ g_free (*param);
+ *param = g_strdup (g_value_get_boxed (value));
+ /* Handle "NULL" object paths */
+ if (g_strcmp0 (*param, "/") == 0) {
+ g_free (*param);
+ *param = NULL;
+ }
+ } else {
+ success = FALSE;
+ goto done;
+ }
+ HANDLE_TYPE(BOOLEAN, boolean, boolean)
+ HANDLE_TYPE(CHAR, char, schar)
+ HANDLE_TYPE(UCHAR, uchar, uchar)
+ HANDLE_TYPE(DOUBLE, double, double)
+ HANDLE_TYPE(INT, int, int)
+ HANDLE_TYPE(UINT, uint, uint)
+ HANDLE_TYPE(INT64, int, int)
+ HANDLE_TYPE(UINT64, uint, uint)
+ HANDLE_TYPE(LONG, long, long)
+ HANDLE_TYPE(ULONG, ulong, ulong)
+ } else {
+ dbgmsg ("%s: %s/%s unhandled type %s.",
+ __func__,
+ G_OBJECT_TYPE_NAME (object),
+ pspec->name,
+ g_type_name (pspec->value_type));
+ success = FALSE;
+ }
+
+done:
+ if (success) {
+ _nm_object_queue_notify (object, pspec->name);
+ } else {
+ dbgmsg ("%s: %s/%s (type %s) couldn't be set with type %s.",
+ __func__, G_OBJECT_TYPE_NAME (object), pspec->name,
+ g_type_name (pspec->value_type), G_VALUE_TYPE_NAME (value));
+ }
+ return success;
+}
+
+void
+_nm_object_register_properties (NMObject *object,
+ DBusGProxy *proxy,
+ const NMPropertiesInfo *info)
+{
+ NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object);
+ static gsize dval = 0;
+ const char *debugstr;
+ NMPropertiesInfo *tmp;
+ GHashTable *instance;
+
+ g_return_if_fail (NM_IS_OBJECT (object));
+ g_return_if_fail (proxy != NULL);
+ g_return_if_fail (info != NULL);
+
+ if (g_once_init_enter (&dval)) {
+ debugstr = getenv ("LIBNM_GLIB_DEBUG");
+ if (debugstr && strstr (debugstr, "properties-changed"))
+ debug = TRUE;
+ g_once_init_leave (&dval, 1);
+ }
+
+ priv->property_interfaces = g_slist_prepend (priv->property_interfaces,
+ g_strdup (dbus_g_proxy_get_interface (proxy)));
+
+ dbus_g_proxy_add_signal (proxy, "PropertiesChanged", DBUS_TYPE_G_MAP_OF_VARIANT, G_TYPE_INVALID);
+ dbus_g_proxy_connect_signal (proxy,
+ "PropertiesChanged",
+ G_CALLBACK (properties_changed_proxy),
+ object,
+ NULL);
+
+ instance = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+ priv->property_tables = g_slist_prepend (priv->property_tables, instance);
+
+ for (tmp = (NMPropertiesInfo *) info; tmp->name; tmp++) {
+ PropertyInfo *pi;
+
+ if (!tmp->name || (tmp->func && !tmp->field)) {
+ g_warning ("%s: missing field in NMPropertiesInfo", __func__);
+ continue;
+ }
+
+ pi = g_malloc0 (sizeof (PropertyInfo));
+ pi->func = tmp->func ? tmp->func : demarshal_generic;
+ pi->object_type = tmp->object_type;
+ pi->field = tmp->field;
+ pi->signal_prefix = tmp->signal_prefix;
+ g_hash_table_insert (instance, g_strdup (tmp->name), pi);
+ }
+}
+
+gboolean
+_nm_object_reload_properties (NMObject *object, GError **error)
+{
+ NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object);
+ GHashTable *props = NULL;
+ GSList *p;
+
+ if (!priv->property_interfaces || !priv->nm_running)
+ return TRUE;
+
+ for (p = priv->property_interfaces; p; p = p->next) {
+ if (!dbus_g_proxy_call (priv->properties_proxy, "GetAll", error,
+ G_TYPE_STRING, p->data,
+ G_TYPE_INVALID,
+ DBUS_TYPE_G_MAP_OF_VARIANT, &props,
+ G_TYPE_INVALID))
+ return FALSE;
+
+ process_properties_changed (object, props, TRUE);
+ g_hash_table_destroy (props);
+ }
+
+ return TRUE;
+}
+
+void
+_nm_object_suppress_property_updates (NMObject *object, gboolean suppress)
+{
+ NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object);
+
+ priv->suppress_property_updates = suppress;
+}
+
+
+void
+_nm_object_ensure_inited (NMObject *object)
+{
+ NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object);
+ GError *error = NULL;
+
+ if (!priv->inited) {
+ if (!g_initable_init (G_INITABLE (object), NULL, &error)) {
+ dbgmsg ("Could not initialize %s %s: %s",
+ G_OBJECT_TYPE_NAME (object),
+ priv->path,
+ error->message);
+ g_error_free (error);
+
+ /* Only warn once */
+ priv->inited = TRUE;
+ }
+ }
+}
+
+void
+_nm_object_reload_property (NMObject *object,
+ const char *interface,
+ const char *prop_name)
+{
+ GValue value = G_VALUE_INIT;
+ GError *err = NULL;
+
+ g_return_if_fail (NM_IS_OBJECT (object));
+ g_return_if_fail (interface != NULL);
+ g_return_if_fail (prop_name != NULL);
+
+ if (!NM_OBJECT_GET_PRIVATE (object)->nm_running)
+ return;
+
+ if (!dbus_g_proxy_call_with_timeout (NM_OBJECT_GET_PRIVATE (object)->properties_proxy,
+ "Get", 15000, &err,
+ G_TYPE_STRING, interface,
+ G_TYPE_STRING, prop_name,
+ G_TYPE_INVALID,
+ G_TYPE_VALUE, &value,
+ G_TYPE_INVALID)) {
+ dbgmsg ("%s: Error getting '%s' for %s: (%d) %s\n",
+ __func__,
+ prop_name,
+ nm_object_get_path (object),
+ err->code,
+ err->message);
+ g_clear_error (&err);
+ return;
+ }
+
+ handle_property_changed (object, prop_name, &value, TRUE);
+ g_value_unset (&value);
+}
+
+void
+_nm_object_set_property (NMObject *object,
+ const char *interface,
+ const char *prop_name,
+ GValue *value)
+{
+ g_return_if_fail (NM_IS_OBJECT (object));
+ g_return_if_fail (interface != NULL);
+ g_return_if_fail (prop_name != NULL);
+ g_return_if_fail (G_IS_VALUE (value));
+
+ if (!NM_OBJECT_GET_PRIVATE (object)->nm_running)
+ return;
+
+ if (!dbus_g_proxy_call_with_timeout (NM_OBJECT_GET_PRIVATE (object)->properties_proxy,
+ "Set", 2000, NULL,
+ G_TYPE_STRING, interface,
+ G_TYPE_STRING, prop_name,
+ G_TYPE_VALUE, value,
+ G_TYPE_INVALID)) {
+
+ /* Ignore errors. dbus_g_proxy_call_with_timeout() is called instead of
+ * dbus_g_proxy_call_no_reply() to give NM chance to authenticate the caller.
+ */
+ }
+}
+
+static void
+reload_complete (NMObject *object)
+{
+ NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object);
+ GSimpleAsyncResult *simple;
+ GSList *results, *iter;
+ GError *error;
+
+ results = priv->reload_results;
+ priv->reload_results = NULL;
+ error = priv->reload_error;
+ priv->reload_error = NULL;
+
+ for (iter = results; iter; iter = iter->next) {
+ simple = iter->data;
+
+ if (error)
+ g_simple_async_result_set_from_error (simple, error);
+ else
+ g_simple_async_result_set_op_res_gboolean (simple, TRUE);
+
+ g_simple_async_result_complete (simple);
+ g_object_unref (simple);
+ }
+ g_slist_free (results);
+ g_clear_error (&error);
+}
+
+static void
+reload_got_properties (DBusGProxy *proxy, DBusGProxyCall *call,
+ gpointer user_data)
+{
+ NMObject *object = user_data;
+ NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object);
+ GHashTable *props = NULL;
+ GError *error = NULL;
+
+ if (dbus_g_proxy_end_call (proxy, call, &error,
+ DBUS_TYPE_G_MAP_OF_VARIANT, &props,
+ G_TYPE_INVALID)) {
+ process_properties_changed (object, props, FALSE);
+ g_hash_table_destroy (props);
+ } else {
+ if (priv->reload_error)
+ g_error_free (error);
+ else
+ priv->reload_error = error;
+ }
+
+ if (--priv->reload_remaining == 0)
+ reload_complete (object);
+}
+
+void
+_nm_object_reload_properties_async (NMObject *object, GAsyncReadyCallback callback, gpointer user_data)
+{
+ NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (object);
+ GSimpleAsyncResult *simple;
+ GSList *p;
+
+ simple = g_simple_async_result_new (G_OBJECT (object), callback,
+ user_data, _nm_object_reload_properties_async);
+
+ if (!priv->property_interfaces) {
+ g_simple_async_result_complete_in_idle (simple);
+ g_object_unref (simple);
+ return;
+ }
+
+ priv->reload_results = g_slist_prepend (priv->reload_results, simple);
+
+ /* If there was already a reload happening, we don't need to
+ * re-read the properties again, we just need to wait for the
+ * existing reload to finish.
+ */
+ if (priv->reload_results->next)
+ return;
+
+ for (p = priv->property_interfaces; p; p = p->next) {
+ priv->reload_remaining++;
+ dbus_g_proxy_begin_call (priv->properties_proxy, "GetAll",
+ reload_got_properties, object, NULL,
+ G_TYPE_STRING, p->data,
+ G_TYPE_INVALID);
+ }
+}
+
+gboolean
+_nm_object_reload_properties_finish (NMObject *object, GAsyncResult *result, GError **error)
+{
+ GSimpleAsyncResult *simple;
+
+ g_return_val_if_fail (NM_IS_OBJECT (object), FALSE);
+ g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (object), _nm_object_reload_properties_async), FALSE);
+
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+ if (g_simple_async_result_propagate_error (simple, error))
+ return FALSE;
+
+ return g_simple_async_result_get_op_res_gboolean (simple);
+}
+
+DBusGProxy *
+_nm_object_new_proxy (NMObject *self, const char *path, const char *interface)
+{
+ NMObjectPrivate *priv = NM_OBJECT_GET_PRIVATE (self);
+
+ return _nm_dbus_new_proxy_for_connection (priv->connection, path ? path : priv->path, interface);
+}
+
+gboolean
+_nm_object_is_connection_private (NMObject *self)
+{
+ return _nm_dbus_is_connection_private (NM_OBJECT_GET_PRIVATE (self)->connection);
+}
diff --git a/libnm/nm-object.h b/libnm/nm-object.h
new file mode 100644
index 0000000000..07348f2491
--- /dev/null
+++ b/libnm/nm-object.h
@@ -0,0 +1,91 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2008 Novell, Inc.
+ * Copyright 2007 - 2012 Red Hat, Inc.
+ */
+
+#ifndef NM_OBJECT_H
+#define NM_OBJECT_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <dbus/dbus-glib.h>
+
+#include <nm-version.h>
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_OBJECT (nm_object_get_type ())
+#define NM_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_OBJECT, NMObject))
+#define NM_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_OBJECT, NMObjectClass))
+#define NM_IS_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_OBJECT))
+#define NM_IS_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_OBJECT))
+#define NM_OBJECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_OBJECT, NMObjectClass))
+
+/**
+ * NMObjectError:
+ * @NM_OBJECT_ERROR_UNKNOWN: unknown or unclassified error
+ * @NM_OBJECT_ERROR_OBJECT_CREATION_FAILURE: an error ocured while creating an #NMObject
+ *
+ * Describes errors that may result from operations involving a #NMObject.
+ *
+ **/
+typedef enum {
+ NM_OBJECT_ERROR_UNKNOWN = 0,
+ NM_OBJECT_ERROR_OBJECT_CREATION_FAILURE,
+} NMObjectError;
+
+#define NM_OBJECT_ERROR nm_object_error_quark ()
+GQuark nm_object_error_quark (void);
+
+#define NM_OBJECT_DBUS_CONNECTION "dbus-connection"
+#define NM_OBJECT_DBUS_PATH "dbus-path"
+
+typedef struct {
+ GObject parent;
+} NMObject;
+
+typedef struct {
+ GObjectClass parent;
+
+ /* Signals */
+ /* The "object-creation-failed" signal is PRIVATE for libnm-glib and
+ * is not meant for any external usage. It indicates that an error
+ * occured during creation of an object.
+ */
+ void (*object_creation_failed) (NMObject *master_object,
+ GError *error,
+ char *failed_path);
+
+ /* Padding for future expansion */
+ void (*_reserved1) (void);
+ void (*_reserved2) (void);
+ void (*_reserved3) (void);
+ void (*_reserved4) (void);
+ void (*_reserved5) (void);
+ void (*_reserved6) (void);
+} NMObjectClass;
+
+GType nm_object_get_type (void);
+
+DBusGConnection *nm_object_get_connection (NMObject *object);
+const char *nm_object_get_path (NMObject *object);
+
+G_END_DECLS
+
+#endif /* NM_OBJECT_H */
diff --git a/libnm/nm-remote-connection-private.h b/libnm/nm-remote-connection-private.h
new file mode 100644
index 0000000000..e3f35428cd
--- /dev/null
+++ b/libnm/nm-remote-connection-private.h
@@ -0,0 +1,33 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2009 Red Hat, Inc.
+ */
+
+#ifndef __NM_REMOTE_CONNECTION_PRIVATE_H__
+#define __NM_REMOTE_CONNECTION_PRIVATE_H__
+
+#define NM_REMOTE_CONNECTION_INIT_RESULT "init-result"
+
+typedef enum {
+ NM_REMOTE_CONNECTION_INIT_RESULT_UNKNOWN = 0,
+ NM_REMOTE_CONNECTION_INIT_RESULT_SUCCESS,
+ NM_REMOTE_CONNECTION_INIT_RESULT_ERROR,
+ NM_REMOTE_CONNECTION_INIT_RESULT_INVISIBLE,
+} NMRemoteConnectionInitResult;
+
+#endif /* __NM_REMOTE_CONNECTION_PRIVATE__ */
diff --git a/libnm/nm-remote-connection.c b/libnm/nm-remote-connection.c
new file mode 100644
index 0000000000..e3050b0169
--- /dev/null
+++ b/libnm/nm-remote-connection.c
@@ -0,0 +1,963 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2008 Novell, Inc.
+ * Copyright 2007 - 2011 Red Hat, Inc.
+ */
+
+#include <string.h>
+#include <gio/gio.h>
+#include <glib/gi18n.h>
+
+#include <NetworkManager.h>
+#include <nm-utils.h>
+#include <nm-setting-connection.h>
+#include "nm-remote-connection.h"
+#include "nm-remote-connection-private.h"
+#include "nm-object-private.h"
+#include "nm-dbus-glib-types.h"
+#include "nm-glib-compat.h"
+#include "nm-dbus-helpers-private.h"
+
+#define NM_REMOTE_CONNECTION_BUS "bus"
+#define NM_REMOTE_CONNECTION_DBUS_CONNECTION "dbus-connection"
+#define NM_REMOTE_CONNECTION_DBUS_PATH "dbus-path"
+
+static void nm_remote_connection_initable_iface_init (GInitableIface *iface);
+static void nm_remote_connection_async_initable_iface_init (GAsyncInitableIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (NMRemoteConnection, nm_remote_connection, NM_TYPE_CONNECTION,
+ G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, nm_remote_connection_initable_iface_init);
+ G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, nm_remote_connection_async_initable_iface_init);
+ )
+
+enum {
+ PROP_0,
+ PROP_BUS,
+ PROP_DBUS_CONNECTION,
+ PROP_DBUS_PATH,
+ PROP_UNSAVED,
+
+ LAST_PROP
+};
+
+enum {
+ UPDATED,
+ REMOVED,
+ VISIBLE,
+
+ LAST_SIGNAL
+};
+static guint signals[LAST_SIGNAL] = { 0 };
+
+typedef struct RemoteCall RemoteCall;
+typedef void (*RemoteCallFetchResultCb) (RemoteCall *call, DBusGProxyCall *proxy_call, GError *error);
+
+
+struct RemoteCall {
+ NMRemoteConnection *self;
+ DBusGProxyCall *call;
+ RemoteCallFetchResultCb fetch_result_cb;
+ GFunc callback;
+ gpointer user_data;
+};
+
+typedef struct {
+ DBusGConnection *bus;
+ DBusGProxy *proxy;
+ DBusGProxy *props_proxy;
+ gboolean proxy_is_destroyed;
+ GSList *calls;
+
+ gboolean inited;
+ gboolean unsaved;
+
+ gboolean visible;
+} NMRemoteConnectionPrivate;
+
+#define NM_REMOTE_CONNECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_REMOTE_CONNECTION, NMRemoteConnectionPrivate))
+
+/**
+ * nm_remote_connection_error_quark:
+ *
+ * Registers an error quark for #NMRemoteConnection if necessary.
+ *
+ * Returns: the error quark used for #NMRemoteConnection errors.
+ **/
+GQuark
+nm_remote_connection_error_quark (void)
+{
+ static GQuark quark = 0;
+
+ if (G_UNLIKELY (quark == 0))
+ quark = g_quark_from_static_string ("nm-remote-connection-error-quark");
+ return quark;
+}
+
+/****************************************************************/
+
+static void
+_nm_remote_connection_ensure_inited (NMRemoteConnection *self)
+{
+ NMRemoteConnectionPrivate *priv = NM_REMOTE_CONNECTION_GET_PRIVATE (self);
+ GError *error = NULL;
+
+ if (!priv->inited) {
+ if (!g_initable_init (G_INITABLE (self), NULL, &error)) {
+ /* Don't warn when the call times out because the settings service can't
+ * be activated or whatever.
+ */
+ if (!g_error_matches (error, DBUS_GERROR, DBUS_GERROR_NO_REPLY)) {
+ g_warning ("%s: (NMRemoteConnection) error initializing: %s\n",
+ __func__, error->message);
+ }
+ g_error_free (error);
+ }
+ priv->inited = TRUE;
+ }
+}
+
+/****************************************************************/
+
+static void
+remote_call_dbus_cb (DBusGProxy *proxy, DBusGProxyCall *proxy_call, gpointer user_data)
+{
+ RemoteCall *call = user_data;
+ NMRemoteConnectionPrivate *priv = NM_REMOTE_CONNECTION_GET_PRIVATE (call->self);
+ GError *error = NULL;
+
+ g_assert ( (!proxy && !proxy_call && priv->proxy_is_destroyed) ||
+ ( proxy && proxy_call && !priv->proxy_is_destroyed && proxy == priv->proxy) );
+
+ if (priv->proxy_is_destroyed) {
+ error = g_error_new_literal (NM_REMOTE_CONNECTION_ERROR,
+ NM_REMOTE_CONNECTION_ERROR_DISCONNECTED,
+ _("Disconnected by D-Bus"));
+ }
+ call->fetch_result_cb (call, proxy_call, error);
+ g_clear_error (&error);
+
+ priv->calls = g_slist_remove (priv->calls, call);
+ g_object_unref (call->self);
+ g_free (call);
+}
+
+static gboolean
+remote_call_cleanup_cb (void *user_data)
+{
+ remote_call_dbus_cb (NULL, NULL, user_data);
+ return G_SOURCE_REMOVE;
+}
+
+static RemoteCall *
+remote_call_new (NMRemoteConnection *self,
+ RemoteCallFetchResultCb fetch_result_cb,
+ GFunc callback,
+ gpointer user_data)
+{
+ RemoteCall *call;
+ NMRemoteConnectionPrivate *priv = NM_REMOTE_CONNECTION_GET_PRIVATE (self);
+
+ g_assert (fetch_result_cb);
+
+ if (priv->proxy_is_destroyed && !callback)
+ return NULL;
+
+ call = g_malloc0 (sizeof (RemoteCall));
+ call->self = g_object_ref (self);
+ call->fetch_result_cb = fetch_result_cb;
+ call->user_data = user_data;
+ call->callback = callback;
+
+ if (priv->proxy_is_destroyed) {
+ g_idle_add (remote_call_cleanup_cb, call);
+ return NULL;
+ }
+ priv->calls = g_slist_prepend (priv->calls, call);
+ return call;
+}
+
+static void
+proxy_set_destroyed (NMRemoteConnection *self)
+{
+ NMRemoteConnectionPrivate *priv = NM_REMOTE_CONNECTION_GET_PRIVATE (self);
+
+ if (priv->proxy_is_destroyed) {
+ g_assert (!priv->calls);
+ return;
+ }
+
+ priv->proxy_is_destroyed = TRUE;
+
+ priv->calls = g_slist_reverse (priv->calls);
+ while (priv->calls)
+ remote_call_dbus_cb (NULL, NULL, priv->calls->data);
+}
+
+static void
+proxy_destroy_cb (DBusGProxy* proxy, gpointer user_data) {
+ proxy_set_destroyed (user_data);
+}
+
+/****************************************************************/
+
+static void
+result_cb (RemoteCall *call, DBusGProxyCall *proxy_call, GError *error)
+{
+ NMRemoteConnectionResultFunc func = (NMRemoteConnectionResultFunc) call->callback;
+ GError *local_error = NULL;
+
+ if (!error) {
+ dbus_g_proxy_end_call (NM_REMOTE_CONNECTION_GET_PRIVATE (call->self)->proxy,
+ proxy_call, &local_error, G_TYPE_INVALID);
+ error = local_error;
+ }
+ if (func)
+ (*func) (call->self, error, call->user_data);
+ g_clear_error (&local_error);
+}
+
+/**
+ * nm_remote_connection_commit_changes:
+ * @connection: the #NMRemoteConnection
+ * @callback: (scope async) (allow-none): a function to be called when the
+ * commit completes
+ * @user_data: (closure): caller-specific data to be passed to @callback
+ *
+ * Send any local changes to the settings and properties of this connection to
+ * NetworkManager, which will immediately save them to disk.
+ **/
+void
+nm_remote_connection_commit_changes (NMRemoteConnection *self,
+ NMRemoteConnectionResultFunc callback,
+ gpointer user_data)
+{
+ NMRemoteConnectionPrivate *priv;
+ RemoteCall *call;
+ GHashTable *settings;
+
+ g_return_if_fail (NM_IS_REMOTE_CONNECTION (self));
+
+ priv = NM_REMOTE_CONNECTION_GET_PRIVATE (self);
+
+ call = remote_call_new (self, result_cb, (GFunc) callback, user_data);
+ if (!call)
+ return;
+
+ settings = nm_connection_to_hash (NM_CONNECTION (self), NM_SETTING_HASH_FLAG_ALL);
+ call->call = dbus_g_proxy_begin_call (priv->proxy, "Update",
+ remote_call_dbus_cb, call, NULL,
+ DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, settings,
+ G_TYPE_INVALID);
+ g_assert (call->call);
+ g_hash_table_destroy (settings);
+}
+
+/**
+ * nm_remote_connection_commit_changes_unsaved:
+ * @connection: the #NMRemoteConnection
+ * @callback: (scope async) (allow-none): a function to be called when the
+ * commit completes
+ * @user_data: (closure): caller-specific data to be passed to @callback
+ *
+ * Send any local changes to the settings and properties of this connection to
+ * NetworkManager. The changes are not saved to disk until either
+ * nm_remote_connection_save() or nm_remote_connection_commit_changes() is
+ * called.
+ *
+ * Since: 0.9.10
+ **/
+void
+nm_remote_connection_commit_changes_unsaved (NMRemoteConnection *connection,
+ NMRemoteConnectionResultFunc callback,
+ gpointer user_data)
+{
+ NMRemoteConnectionPrivate *priv;
+ GHashTable *settings = NULL;
+ RemoteCall *call;
+
+ g_return_if_fail (NM_IS_REMOTE_CONNECTION (connection));
+
+ priv = NM_REMOTE_CONNECTION_GET_PRIVATE (connection);
+
+ call = remote_call_new (connection, result_cb, (GFunc) callback, user_data);
+ if (!call)
+ return;
+
+ settings = nm_connection_to_hash (NM_CONNECTION (connection), NM_SETTING_HASH_FLAG_ALL);
+ call->call = dbus_g_proxy_begin_call (priv->proxy, "UpdateUnsaved",
+ remote_call_dbus_cb, call, NULL,
+ DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, settings,
+ G_TYPE_INVALID);
+ g_assert (call->call);
+ g_hash_table_destroy (settings);
+}
+
+/**
+ * nm_remote_connection_save:
+ * @connection: the #NMRemoteConnection
+ * @callback: (scope async) (allow-none): a function to be called when the
+ * save completes
+ * @user_data: (closure): caller-specific data to be passed to @callback
+ *
+ * Saves the connection to disk if the connection has changes that have not yet
+ * been written to disk, or if the connection has never been saved.
+ *
+ * Since: 0.9.10
+ **/
+void
+nm_remote_connection_save (NMRemoteConnection *connection,
+ NMRemoteConnectionResultFunc callback,
+ gpointer user_data)
+{
+ NMRemoteConnectionPrivate *priv;
+ RemoteCall *call;
+
+ g_return_if_fail (NM_IS_REMOTE_CONNECTION (connection));
+
+ priv = NM_REMOTE_CONNECTION_GET_PRIVATE (connection);
+
+ call = remote_call_new (connection, result_cb, (GFunc) callback, user_data);
+ if (!call)
+ return;
+
+ call->call = dbus_g_proxy_begin_call (priv->proxy, "Save", remote_call_dbus_cb, call, NULL, G_TYPE_INVALID);
+ g_assert (call->call);
+}
+
+/**
+ * nm_remote_connection_delete:
+ * @connection: the #NMRemoteConnection
+ * @callback: (scope async) (allow-none): a function to be called when the delete completes
+ * @user_data: (closure): caller-specific data to be passed to @callback
+ *
+ * Delete the connection.
+ **/
+void
+nm_remote_connection_delete (NMRemoteConnection *self,
+ NMRemoteConnectionResultFunc callback,
+ gpointer user_data)
+{
+ NMRemoteConnectionPrivate *priv;
+ RemoteCall *call;
+
+ g_return_if_fail (NM_IS_REMOTE_CONNECTION (self));
+
+ priv = NM_REMOTE_CONNECTION_GET_PRIVATE (self);
+
+ call = remote_call_new (self, result_cb, (GFunc) callback, user_data);
+ if (!call)
+ return;
+
+ call->call = dbus_g_proxy_begin_call (priv->proxy, "Delete",
+ remote_call_dbus_cb, call, NULL,
+ G_TYPE_INVALID);
+ g_assert (call->call);
+}
+
+static void
+get_secrets_cb (RemoteCall *call, DBusGProxyCall *proxy_call, GError *error)
+{
+ NMRemoteConnectionGetSecretsFunc func = (NMRemoteConnectionGetSecretsFunc) call->callback;
+ GHashTable *secrets = NULL;
+ GError *local_error = NULL;
+
+ if (!error) {
+ dbus_g_proxy_end_call (NM_REMOTE_CONNECTION_GET_PRIVATE (call->self)->proxy,
+ proxy_call, &local_error,
+ DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, &secrets,
+ G_TYPE_INVALID);
+ error = local_error;
+ }
+ if (func)
+ (*func) (call->self, error ? NULL : secrets, error, call->user_data);
+ g_clear_error (&local_error);
+ if (secrets)
+ g_hash_table_destroy (secrets);
+}
+
+
+/**
+ * nm_remote_connection_get_secrets:
+ * @connection: the #NMRemoteConnection
+ * @setting_name: the #NMSetting object name to get secrets for
+ * @callback: (scope async): a function to be called when the update completes;
+ * must not be %NULL
+ * @user_data: (closure): caller-specific data to be passed to @callback
+ *
+ * Request the connection's secrets.
+ **/
+void
+nm_remote_connection_get_secrets (NMRemoteConnection *self,
+ const char *setting_name,
+ NMRemoteConnectionGetSecretsFunc callback,
+ gpointer user_data)
+{
+ NMRemoteConnectionPrivate *priv;
+ RemoteCall *call;
+
+ g_return_if_fail (NM_IS_REMOTE_CONNECTION (self));
+ g_return_if_fail (callback != NULL);
+
+ priv = NM_REMOTE_CONNECTION_GET_PRIVATE (self);
+
+ call = remote_call_new (self, get_secrets_cb, (GFunc) callback, user_data);
+ if (!call)
+ return;
+
+ call->call = dbus_g_proxy_begin_call (priv->proxy, "GetSecrets",
+ remote_call_dbus_cb, call, NULL,
+ G_TYPE_STRING, setting_name,
+ G_TYPE_INVALID);
+ g_assert (call->call);
+}
+
+/**
+ * nm_remote_connection_get_unsaved:
+ * @connection: the #NMRemoteConnection
+ *
+ * Returns: %TRUE if the remote connection contains changes that have not
+ * been saved to disk, %FALSE if the connection is the same as its on-disk
+ * representation.
+ *
+ * Since: 0.9.10
+ **/
+gboolean
+nm_remote_connection_get_unsaved (NMRemoteConnection *connection)
+{
+ g_return_val_if_fail (NM_IS_REMOTE_CONNECTION (connection), FALSE);
+
+ _nm_remote_connection_ensure_inited (connection);
+ return NM_REMOTE_CONNECTION_GET_PRIVATE (connection)->unsaved;
+}
+
+/****************************************************************/
+
+static void
+replace_settings (NMRemoteConnection *self, GHashTable *new_settings)
+{
+ GError *error = NULL;
+
+ if (nm_connection_replace_settings (NM_CONNECTION (self), new_settings, &error))
+ g_signal_emit (self, signals[UPDATED], 0, new_settings);
+ else {
+ g_warning ("%s: error updating connection %s settings: (%d) %s",
+ __func__,
+ nm_connection_get_path (NM_CONNECTION (self)),
+ error ? error->code : -1,
+ (error && error->message) ? error->message : "(unknown)");
+ g_clear_error (&error);
+
+ g_signal_emit (self, signals[REMOVED], 0);
+ }
+}
+
+static void
+updated_get_settings_cb (DBusGProxy *proxy,
+ DBusGProxyCall *call,
+ gpointer user_data)
+{
+ NMRemoteConnection *self = user_data;
+ NMRemoteConnectionPrivate *priv = NM_REMOTE_CONNECTION_GET_PRIVATE (self);
+ GHashTable *new_settings;
+ GError *error = NULL;
+
+ dbus_g_proxy_end_call (proxy, call, &error,
+ DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, &new_settings,
+ G_TYPE_INVALID);
+ if (error) {
+ GHashTable *hash;
+
+ g_error_free (error);
+
+ /* Connection is no longer visible to this user. Let the settings
+ * service handle this via 'visible'. The settings service will emit
+ * the "removed" signal for us since it handles the lifetime of this
+ * object.
+ */
+ hash = g_hash_table_new (g_str_hash, g_str_equal);
+ nm_connection_replace_settings (NM_CONNECTION (self), hash, NULL);
+ g_hash_table_destroy (hash);
+
+ priv->visible = FALSE;
+ g_signal_emit (self, signals[VISIBLE], 0, FALSE);
+ } else {
+ replace_settings (self, new_settings);
+ g_hash_table_destroy (new_settings);
+
+ /* Settings service will handle announcing the connection to clients */
+ if (priv->visible == FALSE) {
+ priv->visible = TRUE;
+ g_signal_emit (self, signals[VISIBLE], 0, TRUE);
+ }
+ }
+}
+
+static void
+updated_cb (DBusGProxy *proxy, gpointer user_data)
+{
+ NMRemoteConnection *self = NM_REMOTE_CONNECTION (user_data);
+ NMRemoteConnectionPrivate *priv = NM_REMOTE_CONNECTION_GET_PRIVATE (self);
+
+ /* The connection got updated; request the replacement settings */
+ if (!priv->proxy_is_destroyed) {
+ dbus_g_proxy_begin_call (priv->proxy, "GetSettings",
+ updated_get_settings_cb, self, NULL,
+ G_TYPE_INVALID);
+ }
+}
+
+static void
+removed_cb (DBusGProxy *proxy, gpointer user_data)
+{
+ g_signal_emit (G_OBJECT (user_data), signals[REMOVED], 0);
+}
+
+static void
+properties_changed_cb (DBusGProxy *proxy,
+ GHashTable *properties,
+ gpointer user_data)
+{
+ NMRemoteConnection *self = NM_REMOTE_CONNECTION (user_data);
+ GHashTableIter iter;
+ const char *key;
+ GValue *value;
+
+ g_hash_table_iter_init (&iter, properties);
+ while (g_hash_table_iter_next (&iter, (gpointer) &key, (gpointer) &value)) {
+ if (!strcmp (key, "Unsaved")) {
+ NM_REMOTE_CONNECTION_GET_PRIVATE (self)->unsaved = g_value_get_boolean (value);
+ g_object_notify (G_OBJECT (self), NM_REMOTE_CONNECTION_UNSAVED);
+ }
+ }
+}
+
+/****************************************************************/
+
+/**
+ * nm_remote_connection_new:
+ * @bus: a valid and connected D-Bus connection
+ * @path: the D-Bus path of the connection as exported by the settings service
+ *
+ * Creates a new object representing the remote connection.
+ *
+ * Returns: the new remote connection object on success, or %NULL on failure
+ **/
+NMRemoteConnection *
+nm_remote_connection_new (DBusGConnection *bus,
+ const char *path)
+{
+ g_return_val_if_fail (bus != NULL, NULL);
+ g_return_val_if_fail (path != NULL, NULL);
+
+ return (NMRemoteConnection *) g_object_new (NM_TYPE_REMOTE_CONNECTION,
+ NM_REMOTE_CONNECTION_BUS, bus,
+ NM_CONNECTION_PATH, path,
+ NULL);
+}
+
+static void
+constructed (GObject *object)
+{
+ NMRemoteConnectionPrivate *priv = NM_REMOTE_CONNECTION_GET_PRIVATE (object);
+
+ G_OBJECT_CLASS (nm_remote_connection_parent_class)->constructed (object);
+
+ g_assert (priv->bus);
+ g_assert (nm_connection_get_path (NM_CONNECTION (object)));
+
+ priv->proxy = _nm_dbus_new_proxy_for_connection (priv->bus,
+ nm_connection_get_path (NM_CONNECTION (object)),
+ NM_DBUS_IFACE_SETTINGS_CONNECTION);
+ g_assert (priv->proxy);
+ dbus_g_proxy_set_default_timeout (priv->proxy, G_MAXINT);
+
+ dbus_g_proxy_add_signal (priv->proxy, "Updated", G_TYPE_INVALID);
+ dbus_g_proxy_connect_signal (priv->proxy, "Updated", G_CALLBACK (updated_cb), object, NULL);
+
+ dbus_g_proxy_add_signal (priv->proxy, "Removed", G_TYPE_INVALID);
+ dbus_g_proxy_connect_signal (priv->proxy, "Removed", G_CALLBACK (removed_cb), object, NULL);
+
+ g_signal_connect (priv->proxy, "destroy", G_CALLBACK (proxy_destroy_cb), object);
+
+ /* Monitor properties */
+ dbus_g_object_register_marshaller (g_cclosure_marshal_VOID__BOXED,
+ G_TYPE_NONE,
+ DBUS_TYPE_G_MAP_OF_VARIANT,
+ G_TYPE_INVALID);
+ dbus_g_proxy_add_signal (priv->proxy, "PropertiesChanged",
+ DBUS_TYPE_G_MAP_OF_VARIANT,
+ G_TYPE_INVALID);
+ dbus_g_proxy_connect_signal (priv->proxy, "PropertiesChanged",
+ G_CALLBACK (properties_changed_cb),
+ object,
+ NULL);
+
+ priv->props_proxy = _nm_dbus_new_proxy_for_connection (priv->bus,
+ nm_connection_get_path (NM_CONNECTION (object)),
+ DBUS_INTERFACE_PROPERTIES);
+ g_assert (priv->props_proxy);
+}
+
+static gboolean
+init_sync (GInitable *initable, GCancellable *cancellable, GError **error)
+{
+ NMRemoteConnectionPrivate *priv = NM_REMOTE_CONNECTION_GET_PRIVATE (initable);
+ GHashTable *hash;
+
+ if (!dbus_g_proxy_call (priv->proxy, "GetSettings", error,
+ G_TYPE_INVALID,
+ DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, &hash,
+ G_TYPE_INVALID))
+ return FALSE;
+ priv->visible = TRUE;
+ replace_settings (NM_REMOTE_CONNECTION (initable), hash);
+ g_hash_table_destroy (hash);
+
+ /* Get properties */
+ hash = NULL;
+ if (!dbus_g_proxy_call (priv->props_proxy, "GetAll", error,
+ G_TYPE_STRING, NM_DBUS_IFACE_SETTINGS_CONNECTION,
+ G_TYPE_INVALID,
+ DBUS_TYPE_G_MAP_OF_VARIANT, &hash,
+ G_TYPE_INVALID))
+ return FALSE;
+ properties_changed_cb (priv->props_proxy, hash, NM_REMOTE_CONNECTION (initable));
+ g_hash_table_destroy (hash);
+
+ return TRUE;
+}
+
+typedef struct {
+ NMRemoteConnection *connection;
+ GSimpleAsyncResult *result;
+} NMRemoteConnectionInitData;
+
+static void
+init_async_complete (NMRemoteConnectionInitData *init_data, GError *error)
+{
+ if (error)
+ g_simple_async_result_take_error (init_data->result, error);
+ else {
+ g_simple_async_result_set_op_res_gboolean (init_data->result, TRUE);
+ NM_REMOTE_CONNECTION_GET_PRIVATE (init_data->connection)->inited = TRUE;
+ }
+
+ g_simple_async_result_complete (init_data->result);
+ g_object_unref (init_data->result);
+ g_slice_free (NMRemoteConnectionInitData, init_data);
+}
+
+static void
+init_async_got_properties (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data)
+{
+ NMRemoteConnectionInitData *init_data = user_data;
+ NMRemoteConnectionPrivate *priv = NM_REMOTE_CONNECTION_GET_PRIVATE (init_data->connection);
+ GHashTable *props;
+ GError *error = NULL;
+
+ if (dbus_g_proxy_end_call (proxy, call, &error,
+ DBUS_TYPE_G_MAP_OF_VARIANT, &props,
+ G_TYPE_INVALID)) {
+ properties_changed_cb (priv->props_proxy, props, init_data->connection);
+ g_hash_table_destroy (props);
+ }
+ init_async_complete (init_data, error);
+}
+
+static void
+init_get_settings_cb (DBusGProxy *proxy,
+ DBusGProxyCall *call,
+ gpointer user_data)
+{
+ NMRemoteConnectionInitData *init_data = user_data;
+ NMRemoteConnectionPrivate *priv = NM_REMOTE_CONNECTION_GET_PRIVATE (init_data->connection);
+ GHashTable *settings;
+ GError *error = NULL;
+
+ dbus_g_proxy_end_call (proxy, call, &error,
+ DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, &settings,
+ G_TYPE_INVALID);
+ if (error) {
+ init_async_complete (init_data, error);
+ return;
+ }
+
+ priv->visible = TRUE;
+ replace_settings (init_data->connection, settings);
+ g_hash_table_destroy (settings);
+
+ /* Grab properties */
+ dbus_g_proxy_begin_call (priv->props_proxy, "GetAll",
+ init_async_got_properties, init_data, NULL,
+ G_TYPE_STRING, NM_DBUS_IFACE_SETTINGS_CONNECTION,
+ G_TYPE_INVALID);
+}
+
+static void
+init_async (GAsyncInitable *initable, int io_priority,
+ GCancellable *cancellable, GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ NMRemoteConnectionInitData *init_data;
+ NMRemoteConnectionPrivate *priv = NM_REMOTE_CONNECTION_GET_PRIVATE (initable);
+
+
+ init_data = g_slice_new0 (NMRemoteConnectionInitData);
+ init_data->connection = NM_REMOTE_CONNECTION (initable);
+ init_data->result = g_simple_async_result_new (G_OBJECT (initable), callback,
+ user_data, init_async);
+
+ dbus_g_proxy_begin_call (priv->proxy, "GetSettings",
+ init_get_settings_cb, init_data, NULL,
+ G_TYPE_INVALID);
+}
+
+static gboolean
+init_finish (GAsyncInitable *initable, GAsyncResult *result, GError **error)
+{
+ GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
+
+ if (g_simple_async_result_propagate_error (simple, error))
+ return FALSE;
+ else
+ return TRUE;
+}
+
+static void
+nm_remote_connection_init (NMRemoteConnection *self)
+{
+}
+
+static GObject *
+constructor (GType type, guint n_construct_properties,
+ GObjectConstructParam *construct_properties)
+{
+ static GParamSpec *nm_connection_path = NULL;
+ static GParamSpec *nm_remote_connection_dbus_path = NULL;
+ int i, path_index = -1, dbus_path_index = -1;
+
+ if (!nm_connection_path) {
+ nm_connection_path =
+ g_object_class_find_property (g_type_class_peek (NM_TYPE_CONNECTION),
+ NM_CONNECTION_PATH);
+ nm_remote_connection_dbus_path =
+ g_object_class_find_property (g_type_class_peek (NM_TYPE_REMOTE_CONNECTION),
+ NM_REMOTE_CONNECTION_DBUS_PATH);
+ }
+
+ /* Find the two properties */
+ for (i = 0; i < n_construct_properties; i++) {
+ if (construct_properties[i].pspec == nm_connection_path)
+ path_index = i;
+ else if (construct_properties[i].pspec == nm_remote_connection_dbus_path)
+ dbus_path_index = i;
+ }
+ g_assert (path_index != -1 && dbus_path_index != -1);
+
+ /* If NMRemoteConnection:dbus-path is set, and NMConnection:path
+ * is not, then copy the value of the former to the latter.
+ */
+ if (g_value_get_string (construct_properties[dbus_path_index].value) &&
+ !g_value_get_string (construct_properties[path_index].value))
+ construct_properties[path_index].value = construct_properties[dbus_path_index].value;
+
+ return G_OBJECT_CLASS (nm_remote_connection_parent_class)->
+ constructor (type, n_construct_properties, construct_properties);
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ _nm_remote_connection_ensure_inited (NM_REMOTE_CONNECTION (object));
+
+ switch (prop_id) {
+ case PROP_UNSAVED:
+ g_value_set_boolean (value, NM_REMOTE_CONNECTION_GET_PRIVATE (object)->unsaved);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ NMRemoteConnectionPrivate *priv = NM_REMOTE_CONNECTION_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_BUS:
+ case PROP_DBUS_CONNECTION:
+ /* Construct only */
+ /* priv->bus is set from either of two properties so that it (a) remains
+ * backwards compatible with the previous "bus" property, and that (b)
+ * it can be created just like an NMObject using the "dbus-connection",
+ * even though it's not a subclass of NMObject. So don't overwrite the
+ * a valid value that the other property set with NULL, if one of the
+ * properties isn't specified at construction time.
+ */
+ if (!priv->bus)
+ priv->bus = g_value_dup_boxed (value);
+ break;
+ case PROP_DBUS_PATH:
+ /* Don't need to do anything; see constructor(). */
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+dispose (GObject *object)
+{
+ NMRemoteConnection *self = NM_REMOTE_CONNECTION (object);
+ NMRemoteConnectionPrivate *priv = NM_REMOTE_CONNECTION_GET_PRIVATE (self);
+
+ proxy_set_destroyed (self);
+
+ if (priv->proxy) {
+ g_signal_handlers_disconnect_by_func (priv->proxy, proxy_destroy_cb, object);
+ g_clear_object (&priv->proxy);
+ }
+ g_clear_object (&priv->props_proxy);
+
+ if (priv->bus) {
+ dbus_g_connection_unref (priv->bus);
+ priv->bus = NULL;
+ }
+
+ G_OBJECT_CLASS (nm_remote_connection_parent_class)->dispose (object);
+}
+
+static void
+nm_remote_connection_class_init (NMRemoteConnectionClass *remote_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (remote_class);
+
+ g_type_class_add_private (object_class, sizeof (NMRemoteConnectionPrivate));
+
+ /* virtual methods */
+ object_class->constructor = constructor;
+ object_class->get_property = get_property;
+ object_class->set_property = set_property;
+ object_class->dispose = dispose;
+ object_class->constructed = constructed;
+
+ /* Properties */
+ /**
+ * NMRemoteConnection:bus:
+ *
+ * The #DBusGConnection that the #NMRemoteConnection is connected to.
+ */
+ g_object_class_install_property
+ (object_class, PROP_BUS,
+ g_param_spec_boxed (NM_REMOTE_CONNECTION_BUS, "", "",
+ DBUS_TYPE_G_CONNECTION,
+ G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ /* These are needed so _nm_object_create() can create NMRemoteConnections */
+ g_object_class_install_property
+ (object_class, PROP_DBUS_CONNECTION,
+ g_param_spec_boxed (NM_REMOTE_CONNECTION_DBUS_CONNECTION, "", "",
+ DBUS_TYPE_G_CONNECTION,
+ G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property
+ (object_class, PROP_DBUS_PATH,
+ g_param_spec_string (NM_REMOTE_CONNECTION_DBUS_PATH, "", "",
+ NULL,
+ G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMRemoteConnection:unsaved:
+ *
+ * %TRUE if the remote connection contains changes that have not been saved
+ * to disk, %FALSE if the connection is the same as its on-disk representation.
+ *
+ * Since: 0.9.10
+ **/
+ g_object_class_install_property
+ (object_class, PROP_UNSAVED,
+ g_param_spec_boolean (NM_REMOTE_CONNECTION_UNSAVED, "", "",
+ FALSE,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /* Signals */
+ /**
+ * NMRemoteConnection::updated:
+ * @connection: a #NMConnection
+ *
+ * This signal is emitted when a connection changes, and it is
+ * still visible to the user.
+ */
+ signals[UPDATED] =
+ g_signal_new (NM_REMOTE_CONNECTION_UPDATED,
+ G_TYPE_FROM_CLASS (remote_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NMRemoteConnectionClass, updated),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ /**
+ * NMRemoteConnection::removed:
+ * @connection: a #NMConnection
+ *
+ * This signal is emitted when a connection is either deleted or becomes
+ * invisible to the current user.
+ */
+ signals[REMOVED] =
+ g_signal_new (NM_REMOTE_CONNECTION_REMOVED,
+ G_TYPE_FROM_CLASS (remote_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NMRemoteConnectionClass, removed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ /* Private signal */
+ signals[VISIBLE] =
+ g_signal_new ("visible",
+ G_TYPE_FROM_CLASS (remote_class),
+ G_SIGNAL_RUN_FIRST,
+ 0, NULL, NULL,
+ g_cclosure_marshal_VOID__BOOLEAN,
+ G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
+}
+
+static void
+nm_remote_connection_initable_iface_init (GInitableIface *iface)
+{
+ iface->init = init_sync;
+}
+
+static void
+nm_remote_connection_async_initable_iface_init (GAsyncInitableIface *iface)
+{
+ iface->init_async = init_async;
+ iface->init_finish = init_finish;
+}
diff --git a/libnm/nm-remote-connection.h b/libnm/nm-remote-connection.h
new file mode 100644
index 0000000000..8292c23545
--- /dev/null
+++ b/libnm/nm-remote-connection.h
@@ -0,0 +1,149 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2008 Novell, Inc.
+ * Copyright 2007 - 2011 Red Hat, Inc.
+ */
+
+#ifndef __NM_REMOTE_CONNECTION_H__
+#define __NM_REMOTE_CONNECTION_H__
+
+#include <glib-object.h>
+#include <dbus/dbus-glib.h>
+
+#include <nm-connection.h>
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_REMOTE_CONNECTION (nm_remote_connection_get_type ())
+#define NM_REMOTE_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_REMOTE_CONNECTION, NMRemoteConnection))
+#define NM_REMOTE_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_REMOTE_CONNECTION, NMRemoteConnectionClass))
+#define NM_IS_REMOTE_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_REMOTE_CONNECTION))
+#define NM_IS_REMOTE_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_REMOTE_CONNECTION))
+#define NM_REMOTE_CONNECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_REMOTE_CONNECTION, NMRemoteConnectionClass))
+
+
+/**
+ * NMRemoteConnectionError:
+ * @NM_REMOTE_CONNECTION_ERROR_UNKNOWN: unknown or unclassified error
+ * @NM_REMOTE_CONNECTION_ERROR_DISCONNECTED: dbus disconnected
+ */
+typedef enum {
+ NM_REMOTE_CONNECTION_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/
+ NM_REMOTE_CONNECTION_ERROR_DISCONNECTED, /*< nick=Disconnected >*/
+} NMRemoteConnectionError;
+
+#define NM_REMOTE_CONNECTION_ERROR (nm_remote_connection_error_quark ())
+GQuark nm_remote_connection_error_quark (void);
+
+/* Properties */
+#define NM_REMOTE_CONNECTION_UNSAVED "unsaved"
+
+/* Signals */
+#define NM_REMOTE_CONNECTION_UPDATED "updated"
+#define NM_REMOTE_CONNECTION_REMOVED "removed"
+
+typedef struct {
+ NMConnection parent;
+} NMRemoteConnection;
+
+typedef struct {
+ NMConnectionClass parent_class;
+
+ /* Signals */
+ void (*updated) (NMRemoteConnection *connection,
+ GHashTable *new_settings);
+
+ void (*removed) (NMRemoteConnection *connection);
+
+ /* Padding for future expansion */
+ void (*_reserved1) (void);
+ void (*_reserved2) (void);
+ void (*_reserved3) (void);
+ void (*_reserved4) (void);
+ void (*_reserved5) (void);
+ void (*_reserved6) (void);
+} NMRemoteConnectionClass;
+
+/**
+ * NMRemoteConnectionResultFunc:
+ * @connection: the connection for which an operation was performed
+ * @error: on failure, a descriptive error
+ * @user_data: user data passed to function which began the operation
+ *
+ * Called when NetworkManager has finished an asynchronous operation on a
+ * connection, like commit changes, deleting, saving, etc.
+ */
+typedef void (*NMRemoteConnectionResultFunc) (NMRemoteConnection *connection,
+ GError *error,
+ gpointer user_data);
+
+/* Backwards compatibility */
+typedef NMRemoteConnectionResultFunc NMRemoteConnectionCommitFunc;
+typedef NMRemoteConnectionResultFunc NMRemoteConnectionDeleteFunc;
+
+/**
+ * NMRemoteConnectionGetSecretsFunc:
+ * @connection: the connection for which secrets were requested
+ * @secrets: (element-type utf8 GLib.HashTable): on success, a hash table of
+ * hash tables, with each inner hash mapping a setting property to a #GValue
+ * containing that property's value
+ * @error: on failure, a descriptive error
+ * @user_data: user data passed to nm_remote_connection_get_secrets()
+ *
+ * Called when NetworkManager returns secrets in response to a request for
+ * secrets via nm_remote_connection_get_secrets().
+ */
+typedef void (*NMRemoteConnectionGetSecretsFunc) (NMRemoteConnection *connection,
+ GHashTable *secrets,
+ GError *error,
+ gpointer user_data);
+
+GType nm_remote_connection_get_type (void);
+
+NMRemoteConnection *nm_remote_connection_new (DBusGConnection *bus,
+ const char *path);
+
+void nm_remote_connection_commit_changes (NMRemoteConnection *connection,
+ NMRemoteConnectionResultFunc callback,
+ gpointer user_data);
+
+NM_AVAILABLE_IN_0_9_10
+void nm_remote_connection_commit_changes_unsaved (NMRemoteConnection *connection,
+ NMRemoteConnectionResultFunc callback,
+ gpointer user_data);
+
+NM_AVAILABLE_IN_0_9_10
+void nm_remote_connection_save (NMRemoteConnection *connection,
+ NMRemoteConnectionResultFunc callback,
+ gpointer user_data);
+
+void nm_remote_connection_delete (NMRemoteConnection *connection,
+ NMRemoteConnectionResultFunc callback,
+ gpointer user_data);
+
+void nm_remote_connection_get_secrets (NMRemoteConnection *connection,
+ const char *setting_name,
+ NMRemoteConnectionGetSecretsFunc callback,
+ gpointer user_data);
+
+NM_AVAILABLE_IN_0_9_10
+gboolean nm_remote_connection_get_unsaved (NMRemoteConnection *connection);
+
+G_END_DECLS
+
+#endif /* __NM_REMOTE_CONNECTION__ */
diff --git a/libnm/nm-remote-settings.c b/libnm/nm-remote-settings.c
new file mode 100644
index 0000000000..49d7780e6e
--- /dev/null
+++ b/libnm/nm-remote-settings.c
@@ -0,0 +1,1566 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2008 Novell, Inc.
+ * Copyright 2009 - 2012 Red Hat, Inc.
+ */
+
+#include <string.h>
+#include <NetworkManager.h>
+#include <nm-connection.h>
+
+#include "nm-dbus-glib-types.h"
+#include "nm-remote-settings.h"
+#include "nm-remote-connection-private.h"
+#include "nm-object-private.h"
+#include "nm-dbus-helpers-private.h"
+#include "nm-glib-compat.h"
+#include "nm-object-private.h"
+
+/**
+ * SECTION:nm-remote-settings
+ * @Short_description: A helper for NetworkManager's settings API
+ * @Title: NMRemoteSettings
+ * @See_also:#NMRemoteConnection, #NMClient
+ *
+ * The #NMRemoteSettings object represents NetworkManager's "settings" service,
+ * which stores network configuration and allows authenticated clients to
+ * add, delete, and modify that configuration. The data required to connect
+ * to a specific network is called a "connection" and encapsulated by the
+ * #NMConnection object. Once a connection is known to NetworkManager, having
+ * either been added by a user or read from on-disk storage, the
+ * #NMRemoteSettings object creates a #NMRemoteConnection object which
+ * represents this stored connection. Use the #NMRemoteConnection object to
+ * perform any operations like modification or deletion.
+ *
+ * To add a new network connection to the NetworkManager settings service, first
+ * build up a template #NMConnection object. Since this connection is not yet
+ * added to NetworkManager, it is known only to your program and is not yet
+ * an #NMRemoteConnection. Then ask #NMRemoteSettings to add your connection.
+ * When the connection is added successfully, the supplied callback is called
+ * and returns to your program the new #NMRemoteConnection which represents
+ * the stored object known to NetworkManager.
+ *
+ * |[<!-- language="C" -->
+ * static void
+ * added_cb (NMRemoteSettings *settings,
+ * NMRemoteConnection *remote,
+ * GError *error,
+ * gpointer user_data)
+ * {
+ * if (error)
+ * g_print ("Error adding connection: %s", error->message);
+ * else {
+ * g_print ("Added: %s\n", nm_connection_get_path (NM_CONNECTION (remote)));
+ * /&ast; Use 'remote' with nm_remote_connection_commit_changes() to save
+ * * changes and nm_remote_connection_delete() to delete the connection &ast;/
+ * }
+ * }
+ *
+ * static gboolean
+ * add_wired_connection (const char *human_name)
+ * {
+ * NMConnection *connection;
+ * NMSettingConnection *s_con;
+ * NMSettingWired *s_wired;
+ * char *uuid;
+ * gboolean success;
+ *
+ * connection = nm_connection_new ();
+ *
+ * /&ast; Build up the 'connection' setting &ast;/
+ * s_con = (NMSettingConnection *) nm_setting_connection_new ();
+ * uuid = nm_utils_uuid_generate ();
+ * g_object_set (G_OBJECT (s_con),
+ * NM_SETTING_CONNECTION_UUID, uuid,
+ * NM_SETTING_CONNECTION_ID, human_name,
+ * NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME,
+ * NULL);
+ * g_free (uuid);
+ * nm_connection_add_setting (connection, NM_SETTING (s_con));
+ *
+ * /&ast; Add the required 'wired' setting as this is a wired connection &ast;/
+ * nm_connection_add_setting (connection, nm_setting_wired_new ());
+ *
+ * /&ast; Add an 'ipv4' setting using AUTO configuration (eg DHCP) &ast;/
+ * s_ip4 = (NMSettingIP4Config *) nm_setting_ip4_config_new ();
+ * g_object_set (G_OBJECT (s_ip4),
+ * NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO,
+ * NULL);
+ * nm_connection_add_setting (connection, NM_SETTING (s_ip4));
+ *
+ * /&ast; Ask NetworkManager to store the connection &ast;/
+ * success = nm_remote_settings_add_connection (settings, connection, added_cb, loop);
+ *
+ * /&ast; Release the template connection; the actual stored connection will
+ * * be returned in added_cb() &ast;/
+ * g_object_unref (connection);
+ *
+ * /&ast; Let glib event loop run and added_cb() will be called when NetworkManager
+ * * is done adding the new connection. &ast;/
+ *
+ * return success;
+ * }
+ * ]|
+ */
+
+static void nm_remote_settings_initable_iface_init (GInitableIface *iface);
+static void nm_remote_settings_async_initable_iface_init (GAsyncInitableIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (NMRemoteSettings, nm_remote_settings, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, nm_remote_settings_initable_iface_init);
+ G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, nm_remote_settings_async_initable_iface_init);
+ )
+
+#define NM_REMOTE_SETTINGS_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_REMOTE_SETTINGS, NMRemoteSettingsPrivate))
+
+typedef struct {
+ DBusGConnection *bus;
+ gboolean private_bus;
+ gboolean inited;
+
+ DBusGProxy *proxy;
+ GHashTable *connections;
+ GHashTable *pending; /* Connections we don't have settings for yet */
+ gboolean service_running;
+ guint32 init_left;
+
+ /* AddConnectionInfo objects that are waiting for the connection to become initialized */
+ GSList *add_list;
+
+ DBusGProxy *props_proxy;
+ char *hostname;
+ gboolean can_modify;
+
+ DBusGProxy *dbus_proxy;
+
+ DBusGProxyCall *listcon_call;
+} NMRemoteSettingsPrivate;
+
+enum {
+ PROP_0,
+ PROP_BUS,
+ PROP_SERVICE_RUNNING,
+ PROP_HOSTNAME,
+ PROP_CAN_MODIFY,
+
+ LAST_PROP
+};
+
+/* Signals */
+enum {
+ NEW_CONNECTION,
+ CONNECTIONS_READ,
+
+ LAST_SIGNAL
+};
+static guint signals[LAST_SIGNAL] = { 0 };
+
+/**********************************************************************/
+
+/**
+ * nm_remote_settings_error_quark:
+ *
+ * Registers an error quark for #NMRemoteSettings if necessary.
+ *
+ * Returns: the error quark used for #NMRemoteSettings errors.
+ **/
+GQuark
+nm_remote_settings_error_quark (void)
+{
+ static GQuark quark;
+
+ if (G_UNLIKELY (!quark))
+ quark = g_quark_from_static_string ("nm-remote-settings-error-quark");
+ return quark;
+}
+
+/**********************************************************************/
+
+static void
+_nm_remote_settings_ensure_inited (NMRemoteSettings *self)
+{
+ NMRemoteSettingsPrivate *priv = NM_REMOTE_SETTINGS_GET_PRIVATE (self);
+ GError *error = NULL;
+
+ if (!priv->inited) {
+ if (!g_initable_init (G_INITABLE (self), NULL, &error)) {
+ /* Don't warn when the call times out because the settings service can't
+ * be activated or whatever.
+ */
+ if (!g_error_matches (error, DBUS_GERROR, DBUS_GERROR_NO_REPLY)) {
+ g_warning ("%s: (NMRemoteSettings) error initializing: %s\n",
+ __func__, error->message);
+ }
+ g_error_free (error);
+ }
+ priv->inited = TRUE;
+ }
+}
+
+/**********************************************************************/
+
+typedef struct {
+ NMRemoteSettings *self;
+ NMRemoteSettingsAddConnectionFunc callback;
+ gpointer callback_data;
+ NMRemoteConnection *connection;
+} AddConnectionInfo;
+
+static AddConnectionInfo *
+add_connection_info_find (NMRemoteSettings *self, NMRemoteConnection *connection)
+{
+ NMRemoteSettingsPrivate *priv = NM_REMOTE_SETTINGS_GET_PRIVATE (self);
+ GSList *iter;
+
+ for (iter = priv->add_list; iter; iter = g_slist_next (iter)) {
+ AddConnectionInfo *info = iter->data;
+
+ if (info->connection == connection)
+ return info;
+ }
+
+ return NULL;
+}
+
+static void
+add_connection_info_dispose (NMRemoteSettings *self, AddConnectionInfo *info)
+{
+ NMRemoteSettingsPrivate *priv = NM_REMOTE_SETTINGS_GET_PRIVATE (self);
+
+ priv->add_list = g_slist_remove (priv->add_list, info);
+
+ g_free (info);
+}
+
+static void
+add_connection_info_complete (NMRemoteSettings *self,
+ AddConnectionInfo *info,
+ GError *error)
+{
+ g_return_if_fail (info != NULL);
+
+ info->callback (info->self, error ? NULL : info->connection, error, info->callback_data);
+ add_connection_info_dispose (self, info);
+}
+
+/**
+ * nm_remote_settings_get_connection_by_id:
+ * @settings: the %NMRemoteSettings
+ * @id: the id of the remote connection
+ *
+ * Returns the first matching %NMRemoteConnection matching a given @id.
+ *
+ * Returns: (transfer none): the remote connection object on success, or %NULL if no
+ * matching object was found.
+ *
+ * Since: 0.9.10
+ **/
+NMRemoteConnection *
+nm_remote_settings_get_connection_by_id (NMRemoteSettings *settings, const char *id)
+{
+ NMRemoteSettingsPrivate *priv;
+
+ g_return_val_if_fail (NM_IS_REMOTE_SETTINGS (settings), NULL);
+ g_return_val_if_fail (id != NULL, NULL);
+
+ priv = NM_REMOTE_SETTINGS_GET_PRIVATE (settings);
+
+ _nm_remote_settings_ensure_inited (settings);
+
+ if (priv->service_running) {
+ GHashTableIter iter;
+ NMConnection *candidate;
+
+ g_hash_table_iter_init (&iter, priv->connections);
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &candidate)) {
+
+ if (!strcmp (id, nm_connection_get_id (candidate)))
+ return NM_REMOTE_CONNECTION (candidate);
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * nm_remote_settings_get_connection_by_path:
+ * @settings: the %NMRemoteSettings
+ * @path: the D-Bus object path of the remote connection
+ *
+ * Returns the %NMRemoteConnection representing the connection at @path.
+ *
+ * Returns: (transfer none): the remote connection object on success, or %NULL if the object was
+ * not known
+ **/
+NMRemoteConnection *
+nm_remote_settings_get_connection_by_path (NMRemoteSettings *settings, const char *path)
+{
+ NMRemoteSettingsPrivate *priv;
+
+ g_return_val_if_fail (NM_IS_REMOTE_SETTINGS (settings), NULL);
+ g_return_val_if_fail (path != NULL, NULL);
+
+ priv = NM_REMOTE_SETTINGS_GET_PRIVATE (settings);
+
+ _nm_remote_settings_ensure_inited (settings);
+
+ return priv->service_running ? g_hash_table_lookup (priv->connections, path) : NULL;
+}
+
+/**
+ * nm_remote_settings_get_connection_by_uuid:
+ * @settings: the %NMRemoteSettings
+ * @uuid: the UUID of the remote connection
+ *
+ * Returns the %NMRemoteConnection identified by @uuid.
+ *
+ * Returns: (transfer none): the remote connection object on success, or %NULL if the object was
+ * not known
+ **/
+NMRemoteConnection *
+nm_remote_settings_get_connection_by_uuid (NMRemoteSettings *settings, const char *uuid)
+{
+ NMRemoteSettingsPrivate *priv;
+ GHashTableIter iter;
+ NMRemoteConnection *candidate;
+
+ g_return_val_if_fail (NM_IS_REMOTE_SETTINGS (settings), NULL);
+ g_return_val_if_fail (uuid != NULL, NULL);
+
+ priv = NM_REMOTE_SETTINGS_GET_PRIVATE (settings);
+
+ _nm_remote_settings_ensure_inited (settings);
+
+ if (priv->service_running) {
+ g_hash_table_iter_init (&iter, priv->connections);
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer) &candidate)) {
+ if (g_strcmp0 (uuid, nm_connection_get_uuid (NM_CONNECTION (candidate))) == 0)
+ return candidate;
+ }
+ }
+
+ return NULL;
+}
+
+static void
+connection_removed_cb (NMRemoteConnection *remote, gpointer user_data)
+{
+ NMRemoteSettings *self = NM_REMOTE_SETTINGS (user_data);
+ NMRemoteSettingsPrivate *priv = NM_REMOTE_SETTINGS_GET_PRIVATE (self);
+ AddConnectionInfo *addinfo;
+ GError *add_error;
+ const char *path;
+
+ /* Might have been removed while it was waiting to be initialized */
+ addinfo = add_connection_info_find (self, remote);
+ if (addinfo) {
+ add_error = g_error_new_literal (NM_REMOTE_SETTINGS_ERROR,
+ NM_REMOTE_SETTINGS_ERROR_CONNECTION_REMOVED,
+ "Connection removed before it was initialized");
+ add_connection_info_complete (self, addinfo, add_error);
+ g_error_free (add_error);
+ }
+
+ path = nm_connection_get_path (NM_CONNECTION (remote));
+ g_hash_table_remove (priv->connections, path);
+ g_hash_table_remove (priv->pending, path);
+}
+
+static void connection_visible_cb (NMRemoteConnection *remote,
+ gboolean visible,
+ gpointer user_data);
+
+/* Takes a reference to the connection when adding to 'to' */
+static void
+move_connection (NMRemoteSettings *self,
+ NMRemoteConnection *remote,
+ GHashTable *from,
+ GHashTable *to)
+{
+ const char *path = nm_connection_get_path (NM_CONNECTION (remote));
+
+ g_hash_table_insert (to, g_strdup (path), g_object_ref (remote));
+ if (from)
+ g_hash_table_remove (from, path);
+
+ /* Setup connection signals since removing from 'from' clears them, but
+ * also the first time the connection is added to a hash if 'from' is NULL.
+ */
+ if (!g_signal_handler_find (remote, G_SIGNAL_MATCH_FUNC,
+ 0, 0, NULL, connection_removed_cb, NULL)) {
+ g_signal_connect (remote,
+ NM_REMOTE_CONNECTION_REMOVED,
+ G_CALLBACK (connection_removed_cb),
+ self);
+ }
+
+ if (!g_signal_handler_find (remote, G_SIGNAL_MATCH_FUNC,
+ 0, 0, NULL, connection_visible_cb, NULL)) {
+ g_signal_connect (remote,
+ "visible",
+ G_CALLBACK (connection_visible_cb),
+ self);
+ }
+}
+
+static void
+connection_visible_cb (NMRemoteConnection *remote,
+ gboolean visible,
+ gpointer user_data)
+{
+ NMRemoteSettings *self = NM_REMOTE_SETTINGS (user_data);
+ NMRemoteSettingsPrivate *priv = NM_REMOTE_SETTINGS_GET_PRIVATE (self);
+ const char *path;
+
+ path = nm_connection_get_path (NM_CONNECTION (remote));
+ g_assert (path);
+
+ /* When a connection becomes invisible, we put it back in the pending
+ * hash until it becomes visible again. When it does, we move it back to
+ * the normal connections hash.
+ */
+ if (visible) {
+ /* Connection visible to this user again */
+ if (g_hash_table_lookup (priv->pending, path)) {
+ /* Move connection from pending to visible hash; emit for clients */
+ move_connection (self, remote, priv->pending, priv->connections);
+ g_signal_emit (self, signals[NEW_CONNECTION], 0, remote);
+ }
+ } else {
+ /* Connection now invisible to this user */
+ if (g_hash_table_lookup (priv->connections, path)) {
+ /* Move connection to pending hash and wait for it to become visible again */
+ move_connection (self, remote, priv->connections, priv->pending);
+
+ /* Signal to clients that the connection is gone; but we have to
+ * block our connection removed handler so we don't destroy
+ * the connection when the signal is emitted.
+ */
+ g_signal_handlers_block_by_func (remote, connection_removed_cb, self);
+ g_signal_emit_by_name (remote, NM_REMOTE_CONNECTION_REMOVED);
+ g_signal_handlers_unblock_by_func (remote, connection_removed_cb, self);
+ }
+ }
+}
+
+static void
+connection_inited (GObject *source, GAsyncResult *result, gpointer user_data)
+{
+ NMRemoteConnection *remote = NM_REMOTE_CONNECTION (source);
+ NMRemoteSettings *self = NM_REMOTE_SETTINGS (user_data);
+ NMRemoteSettingsPrivate *priv = NM_REMOTE_SETTINGS_GET_PRIVATE (self);
+ AddConnectionInfo *addinfo;
+ const char *path;
+ GError *error = NULL, *local;
+
+ path = nm_connection_get_path (NM_CONNECTION (remote));
+ addinfo = add_connection_info_find (self, remote);
+
+ if (g_async_initable_init_finish (G_ASYNC_INITABLE (remote), result, &error)) {
+ /* Connection is initialized and visible; expose it to clients */
+ move_connection (self, remote, priv->pending, priv->connections);
+
+ /* If there's a pending AddConnection request, complete that here before
+ * signaling new-connection.
+ */
+ if (addinfo)
+ add_connection_info_complete (self, addinfo, NULL);
+
+ /* Finally, let users know of the new connection now that it has all
+ * its settings and is valid.
+ */
+ g_signal_emit (self, signals[NEW_CONNECTION], 0, remote);
+ } else {
+ if (addinfo) {
+ local = g_error_new (NM_REMOTE_SETTINGS_ERROR,
+ NM_REMOTE_SETTINGS_ERROR_CONNECTION_UNAVAILABLE,
+ "Connection not visible or not available: %s",
+ error ? error->message : "(unknown)");
+ add_connection_info_complete (self, addinfo, local);
+ g_error_free (local);
+ }
+
+ /* PermissionDenied means the connection isn't visible to this user, so
+ * keep it in priv->pending to be notified later of visibility changes.
+ * Otherwise forget it.
+ */
+ if (!dbus_g_error_has_name (error, "org.freedesktop.NetworkManager.Settings.PermissionDenied"))
+ g_hash_table_remove (priv->pending, path);
+
+ g_error_free (error);
+ }
+
+ /* Let listeners know that all connections have been found */
+ priv->init_left--;
+ if (priv->init_left == 0)
+ g_signal_emit (self, signals[CONNECTIONS_READ], 0);
+}
+
+static NMRemoteConnection *
+new_connection_cb (DBusGProxy *proxy, const char *path, gpointer user_data)
+{
+ NMRemoteSettings *self = NM_REMOTE_SETTINGS (user_data);
+ NMRemoteSettingsPrivate *priv = NM_REMOTE_SETTINGS_GET_PRIVATE (self);
+ NMRemoteConnection *connection = NULL;
+
+ /* Make double-sure we don't already have it */
+ connection = g_hash_table_lookup (priv->pending, path);
+ if (connection)
+ return connection;
+ connection = g_hash_table_lookup (priv->connections, path);
+ if (connection)
+ return connection;
+
+ /* Create a new connection object for it */
+ connection = nm_remote_connection_new (priv->bus, path);
+ if (connection) {
+ g_async_initable_init_async (G_ASYNC_INITABLE (connection),
+ G_PRIORITY_DEFAULT, NULL,
+ connection_inited, self);
+
+ /* Add the connection to the pending table to wait for it to retrieve
+ * it's settings asynchronously over D-Bus. The connection isn't
+ * really valid until it has all its settings, so hide it until it does.
+ */
+ move_connection (self, connection, NULL, priv->pending);
+ g_object_unref (connection); /* move_connection() takes a ref */
+ }
+ return connection;
+}
+
+static void
+fetch_connections_done (DBusGProxy *proxy,
+ DBusGProxyCall *call,
+ gpointer user_data)
+{
+ NMRemoteSettings *self = NM_REMOTE_SETTINGS (user_data);
+ NMRemoteSettingsPrivate *priv = NM_REMOTE_SETTINGS_GET_PRIVATE (self);
+ GPtrArray *connections;
+ GError *error = NULL;
+ int i;
+
+ g_warn_if_fail (priv->listcon_call == call);
+ priv->listcon_call = NULL;
+
+ if (!dbus_g_proxy_end_call (proxy, call, &error,
+ DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH, &connections,
+ G_TYPE_INVALID)) {
+ if ( !g_error_matches (error, DBUS_GERROR, DBUS_GERROR_SERVICE_UNKNOWN)
+ && !g_error_matches (error, DBUS_GERROR, DBUS_GERROR_NAME_HAS_NO_OWNER)
+ && priv->service_running) {
+ g_warning ("%s: error fetching connections: (%d) %s.",
+ __func__,
+ error->code,
+ error->message ? error->message : "(unknown)");
+ }
+ g_clear_error (&error);
+
+ /* We tried to read connections and failed */
+ g_signal_emit (self, signals[CONNECTIONS_READ], 0);
+ return;
+ }
+
+ /* Let listeners know we are done getting connections */
+ if (connections->len == 0)
+ g_signal_emit (self, signals[CONNECTIONS_READ], 0);
+ else {
+ priv->init_left = connections->len;
+ for (i = 0; i < connections->len; i++) {
+ char *path = g_ptr_array_index (connections, i);
+
+ new_connection_cb (proxy, path, user_data);
+ g_free (path);
+ }
+ }
+
+ g_ptr_array_free (connections, TRUE);
+}
+
+/**
+ * nm_remote_settings_list_connections:
+ * @settings: the %NMRemoteSettings
+ *
+ * Returns: (transfer container) (element-type NMRemoteConnection): a
+ * list containing all connections provided by the remote settings service.
+ * Each element of the returned list is a %NMRemoteConnection instance, which is
+ * owned by the %NMRemoteSettings object and should not be freed by the caller.
+ * The returned list is, however, owned by the caller and should be freed
+ * using g_slist_free() when no longer required.
+ **/
+GSList *
+nm_remote_settings_list_connections (NMRemoteSettings *settings)
+{
+ NMRemoteSettingsPrivate *priv;
+ GSList *list = NULL;
+ GHashTableIter iter;
+ gpointer value;
+
+ g_return_val_if_fail (NM_IS_REMOTE_SETTINGS (settings), NULL);
+
+ priv = NM_REMOTE_SETTINGS_GET_PRIVATE (settings);
+
+ _nm_remote_settings_ensure_inited (settings);
+
+ if (priv->service_running) {
+ g_hash_table_iter_init (&iter, priv->connections);
+ while (g_hash_table_iter_next (&iter, NULL, &value))
+ list = g_slist_prepend (list, NM_REMOTE_CONNECTION (value));
+ }
+
+ return list;
+}
+
+static void
+add_connection_done (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data)
+{
+ AddConnectionInfo *info = user_data;
+ GError *error = NULL;
+ char *path = NULL;
+
+ if (dbus_g_proxy_end_call (proxy, call, &error, DBUS_TYPE_G_OBJECT_PATH, &path, G_TYPE_INVALID)) {
+ info->connection = new_connection_cb (proxy, path, info->self);
+ g_assert (info->connection);
+ /* Wait until this connection is fully initialized before calling the callback */
+ g_free (path);
+ } else
+ add_connection_info_complete (info->self, info, error);
+
+ g_clear_error (&error);
+}
+
+/**
+ * nm_remote_settings_add_connection:
+ * @settings: the %NMRemoteSettings
+ * @connection: the connection to add. Note that this object's settings will be
+ * added, not the object itself
+ * @callback: (scope async): callback to be called when the add operation completes
+ * @user_data: (closure): caller-specific data passed to @callback
+ *
+ * Requests that the remote settings service add the given settings to a new
+ * connection. The connection is immediately written to disk. @connection is
+ * untouched by this function and only serves as a template of the settings to
+ * add. The #NMRemoteConnection object that represents what NetworkManager
+ * actually added is returned to @callback when the addition operation is complete.
+ *
+ * Note that the #NMRemoteConnection returned in @callback may not contain
+ * identical settings to @connection as NetworkManager may perform automatic
+ * completion and/or normalization of connection properties.
+ *
+ * Returns: %TRUE if the request was successful, %FALSE if it failed
+ **/
+gboolean
+nm_remote_settings_add_connection (NMRemoteSettings *settings,
+ NMConnection *connection,
+ NMRemoteSettingsAddConnectionFunc callback,
+ gpointer user_data)
+{
+ NMRemoteSettingsPrivate *priv;
+ AddConnectionInfo *info;
+ GHashTable *new_settings;
+
+ g_return_val_if_fail (NM_IS_REMOTE_SETTINGS (settings), FALSE);
+ g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE);
+ g_return_val_if_fail (callback != NULL, FALSE);
+
+ priv = NM_REMOTE_SETTINGS_GET_PRIVATE (settings);
+
+ _nm_remote_settings_ensure_inited (settings);
+
+ if (!priv->service_running)
+ return FALSE;
+
+ info = g_malloc0 (sizeof (AddConnectionInfo));
+ info->self = settings;
+ info->callback = callback;
+ info->callback_data = user_data;
+
+ new_settings = nm_connection_to_hash (connection, NM_SETTING_HASH_FLAG_ALL);
+ dbus_g_proxy_begin_call (priv->proxy, "AddConnection",
+ add_connection_done,
+ info,
+ NULL,
+ DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, new_settings,
+ G_TYPE_INVALID);
+ g_hash_table_destroy (new_settings);
+
+ priv->add_list = g_slist_append (priv->add_list, info);
+
+ return TRUE;
+}
+
+/**
+ * nm_remote_settings_add_connection_unsaved:
+ * @settings: the %NMRemoteSettings
+ * @connection: the connection to add. Note that this object's settings will be
+ * added, not the object itself
+ * @callback: (scope async): callback to be called when the add operation completes
+ * @user_data: (closure): caller-specific data passed to @callback
+ *
+ * Requests that the remote settings service add the given settings to a new
+ * connection. The connection is not written to disk, which may be done at
+ * a later time by calling the connection's nm_remote_connection_commit_changes()
+ * method.
+ *
+ * Returns: %TRUE if the request was successful, %FALSE if it failed
+ *
+ * Since: 0.9.10
+ **/
+gboolean
+nm_remote_settings_add_connection_unsaved (NMRemoteSettings *settings,
+ NMConnection *connection,
+ NMRemoteSettingsAddConnectionFunc callback,
+ gpointer user_data)
+{
+ NMRemoteSettingsPrivate *priv;
+ AddConnectionInfo *info;
+ GHashTable *new_settings;
+
+ g_return_val_if_fail (NM_IS_REMOTE_SETTINGS (settings), FALSE);
+ g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE);
+ g_return_val_if_fail (callback != NULL, FALSE);
+
+ priv = NM_REMOTE_SETTINGS_GET_PRIVATE (settings);
+
+ _nm_remote_settings_ensure_inited (settings);
+
+ if (!priv->service_running)
+ return FALSE;
+
+ info = g_malloc0 (sizeof (AddConnectionInfo));
+ info->self = settings;
+ info->callback = callback;
+ info->callback_data = user_data;
+
+ new_settings = nm_connection_to_hash (connection, NM_SETTING_HASH_FLAG_ALL);
+ dbus_g_proxy_begin_call (priv->proxy, "AddConnectionUnsaved",
+ add_connection_done,
+ info,
+ NULL,
+ DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, new_settings,
+ G_TYPE_INVALID);
+ g_hash_table_destroy (new_settings);
+
+ priv->add_list = g_slist_append (priv->add_list, info);
+
+ return TRUE;
+}
+
+/**
+ * nm_remote_settings_load_connections:
+ * @settings: the %NMRemoteSettings
+ * @filenames: %NULL-terminated array of filenames to load
+ * @failures: (out) (transfer full): on return, a %NULL-terminated array of
+ * filenames that failed to load
+ * @error: return location for #GError
+ *
+ * Requests that the remote settings service load or reload the given files,
+ * adding or updating the connections described within.
+ *
+ * The changes to the indicated files will not yet be reflected in
+ * @settings's connections array when the function returns.
+ *
+ * If all of the indicated files were successfully loaded, the
+ * function will return %TRUE, and @failures will be set to %NULL. If
+ * NetworkManager tried to load the files, but some (or all) failed,
+ * then @failures will be set to a %NULL-terminated array of the
+ * filenames that failed to load.
+
+ * Returns: %TRUE if NetworkManager at least tried to load @filenames,
+ * %FALSE if an error occurred (eg, permission denied).
+ *
+ * Since: 0.9.10
+ **/
+gboolean
+nm_remote_settings_load_connections (NMRemoteSettings *settings,
+ char **filenames,
+ char ***failures,
+ GError **error)
+{
+ NMRemoteSettingsPrivate *priv;
+ char **my_failures = NULL;
+ gboolean ret;
+
+ g_return_val_if_fail (NM_IS_REMOTE_SETTINGS (settings), FALSE);
+ g_return_val_if_fail (filenames != NULL, FALSE);
+
+ priv = NM_REMOTE_SETTINGS_GET_PRIVATE (settings);
+
+ _nm_remote_settings_ensure_inited (settings);
+
+ if (!priv->service_running) {
+ g_set_error_literal (error, NM_REMOTE_SETTINGS_ERROR,
+ NM_REMOTE_SETTINGS_ERROR_SERVICE_UNAVAILABLE,
+ "NetworkManager is not running.");
+ return FALSE;
+ }
+
+ if (!dbus_g_proxy_call (priv->proxy, "LoadConnections", error,
+ G_TYPE_STRV, filenames,
+ G_TYPE_INVALID,
+ G_TYPE_BOOLEAN, &ret,
+ G_TYPE_STRV, &my_failures,
+ G_TYPE_INVALID))
+ ret = FALSE;
+
+ if (failures) {
+ if (my_failures && !*my_failures)
+ g_clear_pointer (&my_failures, g_free);
+ *failures = my_failures;
+ } else
+ g_strfreev (my_failures);
+
+ return ret;
+}
+
+/**
+ * nm_remote_settings_reload_connections:
+ * @settings: the #NMRemoteSettings
+ * @error: return location for #GError
+ *
+ * Requests that the remote settings service reload all connection
+ * files from disk, adding, updating, and removing connections until
+ * the in-memory state matches the on-disk state.
+ *
+ * Return value: %TRUE on success, %FALSE on failure
+ *
+ * Since: 0.9.10
+ **/
+gboolean
+nm_remote_settings_reload_connections (NMRemoteSettings *settings,
+ GError **error)
+{
+ NMRemoteSettingsPrivate *priv;
+ gboolean success;
+
+ g_return_val_if_fail (NM_IS_REMOTE_SETTINGS (settings), FALSE);
+
+ priv = NM_REMOTE_SETTINGS_GET_PRIVATE (settings);
+
+ _nm_remote_settings_ensure_inited (settings);
+
+ if (!priv->service_running) {
+ g_set_error_literal (error, NM_REMOTE_SETTINGS_ERROR,
+ NM_REMOTE_SETTINGS_ERROR_SERVICE_UNAVAILABLE,
+ "NetworkManager is not running.");
+ return FALSE;
+ }
+
+ if (!dbus_g_proxy_call (priv->proxy, "ReloadConnections", error,
+ G_TYPE_INVALID,
+ G_TYPE_BOOLEAN, &success,
+ G_TYPE_INVALID))
+ return FALSE;
+ return success;
+}
+
+static void
+clear_one_hash (GHashTable *table)
+{
+ GHashTableIter iter;
+ gpointer value;
+ GSList *list = NULL, *list_iter;
+
+ /* Build up the list of connections; we can't emit "removed" during hash
+ * table iteration because emission of the "removed" signal may trigger code
+ * that explicitly removes the connection from the hash table somewhere
+ * else.
+ */
+ g_hash_table_iter_init (&iter, table);
+ while (g_hash_table_iter_next (&iter, NULL, &value))
+ list = g_slist_prepend (list, NM_REMOTE_CONNECTION (value));
+
+ for (list_iter = list; list_iter; list_iter = g_slist_next (list_iter))
+ g_signal_emit_by_name (NM_REMOTE_CONNECTION (list_iter->data), NM_REMOTE_CONNECTION_REMOVED);
+ g_slist_free (list);
+
+ g_hash_table_remove_all (table);
+}
+
+typedef struct {
+ NMRemoteSettings *settings;
+ NMRemoteSettingsSaveHostnameFunc callback;
+ gpointer callback_data;
+} SaveHostnameInfo;
+
+static void
+save_hostname_cb (DBusGProxy *proxy,
+ DBusGProxyCall *call,
+ gpointer user_data)
+{
+ SaveHostnameInfo *info = user_data;
+ GError *error = NULL;
+
+ dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_INVALID);
+ if (info->callback != NULL)
+ info->callback (info->settings, error, info->callback_data);
+ g_clear_error (&error);
+}
+
+/**
+ * nm_remote_settings_save_hostname:
+ * @settings: the %NMRemoteSettings
+ * @hostname: the new persistent hostname to set, or %NULL to clear any existing
+ * persistent hostname
+ * @callback: (scope async) (allow-none): callback to be called when the
+ * hostname operation completes
+ * @user_data: (closure): caller-specific data passed to @callback
+ *
+ * Requests that the machine's persistent hostname be set to the specified value
+ * or cleared.
+ *
+ * Returns: %TRUE if the request was successful, %FALSE if it failed
+ **/
+gboolean
+nm_remote_settings_save_hostname (NMRemoteSettings *settings,
+ const char *hostname,
+ NMRemoteSettingsSaveHostnameFunc callback,
+ gpointer user_data)
+{
+ NMRemoteSettingsPrivate *priv;
+ SaveHostnameInfo *info;
+
+ g_return_val_if_fail (NM_IS_REMOTE_SETTINGS (settings), FALSE);
+ g_return_val_if_fail (hostname != NULL, FALSE);
+ g_return_val_if_fail (callback != NULL, FALSE);
+
+ priv = NM_REMOTE_SETTINGS_GET_PRIVATE (settings);
+
+ _nm_remote_settings_ensure_inited (settings);
+
+ if (!priv->service_running)
+ return FALSE;
+
+ info = g_malloc0 (sizeof (SaveHostnameInfo));
+ info->settings = settings;
+ info->callback = callback;
+ info->callback_data = user_data;
+
+ dbus_g_proxy_begin_call (priv->proxy, "SaveHostname",
+ save_hostname_cb,
+ info,
+ g_free,
+ G_TYPE_STRING, hostname ? hostname : "",
+ G_TYPE_INVALID);
+ return TRUE;
+}
+
+static void
+properties_changed_cb (DBusGProxy *proxy,
+ GHashTable *properties,
+ gpointer user_data)
+{
+ NMRemoteSettings *self = NM_REMOTE_SETTINGS (user_data);
+ NMRemoteSettingsPrivate *priv = NM_REMOTE_SETTINGS_GET_PRIVATE (self);
+ GHashTableIter iter;
+ gpointer key, tmp;
+
+ g_hash_table_iter_init (&iter, properties);
+ while (g_hash_table_iter_next (&iter, &key, &tmp)) {
+ GValue *value = tmp;
+
+ if (!strcmp ((const char *) key, "Hostname")) {
+ g_free (priv->hostname);
+ priv->hostname = g_value_dup_string (value);
+ g_object_notify (G_OBJECT (self), NM_REMOTE_SETTINGS_HOSTNAME);
+ }
+
+ if (!strcmp ((const char *) key, "CanModify")) {
+ priv->can_modify = g_value_get_boolean (value);
+ g_object_notify (G_OBJECT (self), NM_REMOTE_SETTINGS_CAN_MODIFY);
+ }
+ }
+}
+
+static void
+nm_appeared_got_properties (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data)
+{
+ NMRemoteSettings *self = NM_REMOTE_SETTINGS (user_data);
+ NMRemoteSettingsPrivate *priv = NM_REMOTE_SETTINGS_GET_PRIVATE (self);
+ GHashTable *props = NULL;
+
+ if (dbus_g_proxy_end_call (proxy, call, NULL,
+ DBUS_TYPE_G_MAP_OF_VARIANT, &props,
+ G_TYPE_INVALID)) {
+ properties_changed_cb (priv->props_proxy, props, self);
+ g_hash_table_destroy (props);
+ }
+}
+
+static void
+name_owner_changed (DBusGProxy *proxy,
+ const char *name,
+ const char *old_owner,
+ const char *new_owner,
+ gpointer user_data)
+{
+ NMRemoteSettings *self = NM_REMOTE_SETTINGS (user_data);
+ NMRemoteSettingsPrivate *priv = NM_REMOTE_SETTINGS_GET_PRIVATE (self);
+ const char *sname = NM_DBUS_SERVICE;
+
+ if (!strcmp (name, sname)) {
+ if (new_owner && strlen (new_owner) > 0) {
+ priv->service_running = TRUE;
+
+ priv->listcon_call = dbus_g_proxy_begin_call (priv->proxy, "ListConnections",
+ fetch_connections_done, self, NULL,
+ G_TYPE_INVALID);
+
+ dbus_g_proxy_begin_call (priv->props_proxy, "GetAll",
+ nm_appeared_got_properties, self, NULL,
+ G_TYPE_STRING, NM_DBUS_IFACE_SETTINGS,
+ G_TYPE_INVALID);
+ } else {
+ priv->service_running = FALSE;
+
+ clear_one_hash (priv->pending);
+ clear_one_hash (priv->connections);
+
+ /* Clear properties */
+ g_free (priv->hostname);
+ priv->hostname = NULL;
+ g_object_notify (G_OBJECT (self), NM_REMOTE_SETTINGS_HOSTNAME);
+
+ priv->can_modify = FALSE;
+ g_object_notify (G_OBJECT (self), NM_REMOTE_SETTINGS_CAN_MODIFY);
+
+ if (priv->listcon_call) {
+ dbus_g_proxy_cancel_call (priv->proxy, priv->listcon_call);
+ priv->listcon_call = NULL;
+ }
+ }
+ g_object_notify (G_OBJECT (self), NM_REMOTE_SETTINGS_SERVICE_RUNNING);
+ }
+}
+
+/****************************************************************/
+
+/**
+ * nm_remote_settings_new:
+ * @bus: (allow-none): a valid and connected D-Bus connection
+ *
+ * Creates a new object representing the remote settings service.
+ *
+ * Note that this will do blocking D-Bus calls to initialize the
+ * settings object. You can use nm_remote_settings_new_async() if you
+ * want to avoid that.
+ *
+ * Returns: the new remote settings object on success, or %NULL on failure
+ **/
+NMRemoteSettings *
+nm_remote_settings_new (DBusGConnection *bus)
+{
+ NMRemoteSettings *self;
+
+ self = g_object_new (NM_TYPE_REMOTE_SETTINGS, NM_REMOTE_SETTINGS_BUS, bus, NULL);
+ _nm_remote_settings_ensure_inited (self);
+ return self;
+}
+
+static void
+remote_settings_inited (GObject *source, GAsyncResult *result, gpointer user_data)
+{
+ GSimpleAsyncResult *simple = user_data;
+ GError *error = NULL;
+
+ if (!g_async_initable_init_finish (G_ASYNC_INITABLE (source), result, &error))
+ g_simple_async_result_take_error (simple, error);
+ else
+ g_simple_async_result_set_op_res_gpointer (simple, source, g_object_unref);
+ g_simple_async_result_complete (simple);
+ g_object_unref (simple);
+}
+
+/**
+ * nm_remote_settings_new_async:
+ * @bus: (allow-none): a valid and connected D-Bus connection
+ * @cancellable: a #GCancellable, or %NULL
+ * @callback: callback to call when the settings object is created
+ * @user_data: data for @callback
+ *
+ * Creates a new object representing the remote settings service and
+ * begins asynchronously initializing it. @callback will be called
+ * when it is done; use nm_remote_settings_new_finish() to get the
+ * result.
+ **/
+void
+nm_remote_settings_new_async (DBusGConnection *bus, GCancellable *cancellable,
+ GAsyncReadyCallback callback, gpointer user_data)
+{
+ NMRemoteSettings *self;
+ GSimpleAsyncResult *simple;
+
+ simple = g_simple_async_result_new (NULL, callback, user_data, nm_remote_settings_new_async);
+
+ self = g_object_new (NM_TYPE_REMOTE_SETTINGS,
+ NM_REMOTE_SETTINGS_BUS, bus,
+ NULL);
+ g_async_initable_init_async (G_ASYNC_INITABLE (self), G_PRIORITY_DEFAULT,
+ cancellable, remote_settings_inited, simple);
+}
+
+/**
+ * nm_remote_settings_new_finish:
+ * @result: a #GAsyncResult
+ * @error: location for a #GError, or %NULL
+ *
+ * Gets the result of an nm_remote_settings_new_async() call.
+ *
+ * Returns: a new #NMRemoteSettings object, or %NULL on error
+ **/
+NMRemoteSettings *
+nm_remote_settings_new_finish (GAsyncResult *result, GError **error)
+{
+ GSimpleAsyncResult *simple;
+
+ g_return_val_if_fail (g_simple_async_result_is_valid (result, NULL, nm_remote_settings_new_async), NULL);
+
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+ if (g_simple_async_result_propagate_error (simple, error))
+ return NULL;
+ else
+ return g_object_ref (g_simple_async_result_get_op_res_gpointer (simple));
+}
+
+static void
+forget_connection (gpointer user_data)
+{
+ NMRemoteConnection *remote = NM_REMOTE_CONNECTION (user_data);
+
+ g_signal_handlers_disconnect_matched (remote, G_SIGNAL_MATCH_FUNC,
+ 0, 0, NULL, connection_removed_cb, NULL);
+ g_signal_handlers_disconnect_matched (remote, G_SIGNAL_MATCH_FUNC,
+ 0, 0, NULL, connection_visible_cb, NULL);
+ g_object_unref (remote);
+}
+
+static void
+nm_remote_settings_init (NMRemoteSettings *self)
+{
+ NMRemoteSettingsPrivate *priv = NM_REMOTE_SETTINGS_GET_PRIVATE (self);
+
+ priv->connections = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, forget_connection);
+ priv->pending = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, forget_connection);
+}
+
+static void
+constructed (GObject *object)
+{
+ NMRemoteSettingsPrivate *priv;
+
+ priv = NM_REMOTE_SETTINGS_GET_PRIVATE (object);
+
+ if (priv->private_bus == FALSE) {
+ /* D-Bus proxy for clearing connections on NameOwnerChanged */
+ priv->dbus_proxy = dbus_g_proxy_new_for_name (priv->bus,
+ DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS);
+ g_assert (priv->dbus_proxy);
+
+ dbus_g_object_register_marshaller (g_cclosure_marshal_generic,
+ G_TYPE_NONE,
+ G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
+ G_TYPE_INVALID);
+ dbus_g_proxy_add_signal (priv->dbus_proxy, "NameOwnerChanged",
+ G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
+ G_TYPE_INVALID);
+ dbus_g_proxy_connect_signal (priv->dbus_proxy,
+ "NameOwnerChanged",
+ G_CALLBACK (name_owner_changed),
+ object, NULL);
+ }
+
+ priv->proxy = _nm_dbus_new_proxy_for_connection (priv->bus,
+ NM_DBUS_PATH_SETTINGS,
+ NM_DBUS_IFACE_SETTINGS);
+ g_assert (priv->proxy);
+ dbus_g_proxy_set_default_timeout (priv->proxy, G_MAXINT);
+
+ dbus_g_proxy_add_signal (priv->proxy, "NewConnection",
+ DBUS_TYPE_G_OBJECT_PATH,
+ G_TYPE_INVALID);
+ dbus_g_proxy_connect_signal (priv->proxy, "NewConnection",
+ G_CALLBACK (new_connection_cb),
+ object,
+ NULL);
+
+ /* D-Bus properties proxy */
+ priv->props_proxy = _nm_dbus_new_proxy_for_connection (priv->bus,
+ NM_DBUS_PATH_SETTINGS,
+ "org.freedesktop.DBus.Properties");
+ g_assert (priv->props_proxy);
+
+ /* Monitor properties */
+ dbus_g_object_register_marshaller (g_cclosure_marshal_generic,
+ G_TYPE_NONE,
+ DBUS_TYPE_G_MAP_OF_VARIANT,
+ G_TYPE_INVALID);
+ dbus_g_proxy_add_signal (priv->proxy, "PropertiesChanged",
+ DBUS_TYPE_G_MAP_OF_VARIANT,
+ G_TYPE_INVALID);
+ dbus_g_proxy_connect_signal (priv->proxy, "PropertiesChanged",
+ G_CALLBACK (properties_changed_cb),
+ object,
+ NULL);
+}
+
+static gboolean
+init_sync (GInitable *initable, GCancellable *cancellable, GError **error)
+{
+ NMRemoteSettings *settings = NM_REMOTE_SETTINGS (initable);
+ NMRemoteSettingsPrivate *priv = NM_REMOTE_SETTINGS_GET_PRIVATE (settings);
+ GHashTable *props;
+
+ if (priv->private_bus == FALSE) {
+ if (!dbus_g_proxy_call (priv->dbus_proxy, "NameHasOwner", error,
+ G_TYPE_STRING, NM_DBUS_SERVICE,
+ G_TYPE_INVALID,
+ G_TYPE_BOOLEAN, &priv->service_running,
+ G_TYPE_INVALID)) {
+ priv->service_running = FALSE;
+ return FALSE;
+ }
+
+ /* If NM isn't running we'll grab properties from name_owner_changed()
+ * when it starts.
+ */
+ if (!priv->service_running)
+ return TRUE;
+ } else
+ priv->service_running = TRUE;
+
+ priv->listcon_call = dbus_g_proxy_begin_call (priv->proxy, "ListConnections",
+ fetch_connections_done, NM_REMOTE_SETTINGS (initable), NULL,
+ G_TYPE_INVALID);
+
+ /* Get properties */
+ if (!dbus_g_proxy_call (priv->props_proxy, "GetAll", error,
+ G_TYPE_STRING, NM_DBUS_IFACE_SETTINGS,
+ G_TYPE_INVALID,
+ DBUS_TYPE_G_MAP_OF_VARIANT, &props,
+ G_TYPE_INVALID))
+ return FALSE;
+ properties_changed_cb (priv->props_proxy, props, settings);
+ g_hash_table_destroy (props);
+
+ return TRUE;
+}
+
+typedef struct {
+ NMRemoteSettings *settings;
+ GSimpleAsyncResult *result;
+} NMRemoteSettingsInitData;
+
+static void
+init_async_complete (NMRemoteSettingsInitData *init_data)
+{
+ NMRemoteSettingsPrivate *priv = NM_REMOTE_SETTINGS_GET_PRIVATE (init_data->settings);
+
+ priv->inited = TRUE;
+
+ g_simple_async_result_complete (init_data->result);
+ g_object_unref (init_data->result);
+ g_slice_free (NMRemoteSettingsInitData, init_data);
+}
+
+static void
+init_read_connections (NMRemoteSettings *settings, gpointer user_data)
+{
+ NMRemoteSettingsInitData *init_data = user_data;
+
+ g_signal_handlers_disconnect_by_func (settings, G_CALLBACK (init_read_connections), user_data);
+
+ init_async_complete (init_data);
+}
+
+static void
+init_async_got_properties (DBusGProxy *proxy, DBusGProxyCall *call,
+ gpointer user_data)
+{
+ NMRemoteSettingsInitData *init_data = user_data;
+ NMRemoteSettingsPrivate *priv = NM_REMOTE_SETTINGS_GET_PRIVATE (init_data->settings);
+ GHashTable *props;
+ GError *error = NULL;
+
+ if (dbus_g_proxy_end_call (proxy, call, &error,
+ DBUS_TYPE_G_MAP_OF_VARIANT, &props,
+ G_TYPE_INVALID)) {
+ properties_changed_cb (priv->props_proxy, props, init_data->settings);
+ g_hash_table_destroy (props);
+ g_simple_async_result_set_op_res_gboolean (init_data->result, TRUE);
+ } else
+ g_simple_async_result_take_error (init_data->result, error);
+
+ /* Read connections and wait for the result */
+ priv->listcon_call = dbus_g_proxy_begin_call (priv->proxy, "ListConnections",
+ fetch_connections_done, init_data->settings, NULL,
+ G_TYPE_INVALID);
+ g_signal_connect (init_data->settings, "connections-read",
+ G_CALLBACK (init_read_connections), init_data);
+}
+
+static void
+init_get_properties (NMRemoteSettingsInitData *init_data)
+{
+ NMRemoteSettingsPrivate *priv = NM_REMOTE_SETTINGS_GET_PRIVATE (init_data->settings);
+
+ dbus_g_proxy_begin_call (priv->props_proxy, "GetAll",
+ init_async_got_properties, init_data, NULL,
+ G_TYPE_STRING, NM_DBUS_IFACE_SETTINGS,
+ G_TYPE_INVALID);
+}
+
+static void
+init_async_got_manager_running (DBusGProxy *proxy, DBusGProxyCall *call,
+ gpointer user_data)
+{
+ NMRemoteSettingsInitData *init_data = user_data;
+ NMRemoteSettingsPrivate *priv = NM_REMOTE_SETTINGS_GET_PRIVATE (init_data->settings);
+ GError *error = NULL;
+
+ if (!dbus_g_proxy_end_call (proxy, call, &error,
+ G_TYPE_BOOLEAN, &priv->service_running,
+ G_TYPE_INVALID)) {
+ g_simple_async_result_take_error (init_data->result, error);
+ init_async_complete (init_data);
+ return;
+ }
+
+ if (!priv->service_running) {
+ g_simple_async_result_set_op_res_gboolean (init_data->result, TRUE);
+ init_async_complete (init_data);
+ return;
+ }
+
+ init_get_properties (init_data);
+}
+
+static void
+init_async (GAsyncInitable *initable, int io_priority,
+ GCancellable *cancellable, GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ NMRemoteSettingsInitData *init_data;
+ NMRemoteSettingsPrivate *priv = NM_REMOTE_SETTINGS_GET_PRIVATE (initable);
+
+ init_data = g_slice_new0 (NMRemoteSettingsInitData);
+ init_data->settings = NM_REMOTE_SETTINGS (initable);
+ init_data->result = g_simple_async_result_new (G_OBJECT (initable), callback,
+ user_data, init_async);
+
+ if (priv->private_bus)
+ init_get_properties (init_data);
+ else {
+ /* Check if NM is running */
+ dbus_g_proxy_begin_call (priv->dbus_proxy, "NameHasOwner",
+ init_async_got_manager_running,
+ init_data, NULL,
+ G_TYPE_STRING, NM_DBUS_SERVICE,
+ G_TYPE_INVALID);
+ }
+}
+
+static gboolean
+init_finish (GAsyncInitable *initable, GAsyncResult *result, GError **error)
+{
+ GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
+
+ if (g_simple_async_result_propagate_error (simple, error))
+ return FALSE;
+ else
+ return TRUE;
+}
+
+static void
+dispose (GObject *object)
+{
+ NMRemoteSettings *self = NM_REMOTE_SETTINGS (object);
+ NMRemoteSettingsPrivate *priv = NM_REMOTE_SETTINGS_GET_PRIVATE (self);
+
+ while (g_slist_length (priv->add_list))
+ add_connection_info_dispose (self, (AddConnectionInfo *) priv->add_list->data);
+
+ if (priv->connections) {
+ g_hash_table_destroy (priv->connections);
+ priv->connections = NULL;
+ }
+
+ if (priv->pending) {
+ g_hash_table_destroy (priv->pending);
+ priv->pending = NULL;
+ }
+
+ g_free (priv->hostname);
+ priv->hostname = NULL;
+
+ g_clear_object (&priv->dbus_proxy);
+ g_clear_object (&priv->proxy);
+ g_clear_object (&priv->props_proxy);
+
+ if (priv->bus) {
+ dbus_g_connection_unref (priv->bus);
+ priv->bus = NULL;
+ }
+
+ G_OBJECT_CLASS (nm_remote_settings_parent_class)->dispose (object);
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ NMRemoteSettingsPrivate *priv = NM_REMOTE_SETTINGS_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_BUS:
+ /* Construct only */
+ priv->bus = g_value_dup_boxed (value);
+ if (!priv->bus) {
+ priv->bus = _nm_dbus_new_connection (NULL);
+ priv->private_bus = _nm_dbus_is_connection_private (priv->bus);
+ }
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ NMRemoteSettingsPrivate *priv = NM_REMOTE_SETTINGS_GET_PRIVATE (object);
+
+ _nm_remote_settings_ensure_inited (NM_REMOTE_SETTINGS (object));
+
+ switch (prop_id) {
+ case PROP_BUS:
+ g_value_set_boxed (value, priv->bus);
+ break;
+ case PROP_SERVICE_RUNNING:
+ g_value_set_boolean (value, priv->service_running);
+ break;
+ case PROP_HOSTNAME:
+ g_value_set_string (value, priv->hostname);
+ break;
+ case PROP_CAN_MODIFY:
+ g_value_set_boolean (value, priv->can_modify);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nm_remote_settings_class_init (NMRemoteSettingsClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+ g_type_class_add_private (class, sizeof (NMRemoteSettingsPrivate));
+
+ /* Virtual methods */
+ object_class->constructed = constructed;
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+ object_class->dispose = dispose;
+
+ /* Properties */
+
+ /**
+ * NMRemoteSettings:bus:
+ *
+ * The #DBusGConnection that the #NMRemoteSettings is connected to. Defaults
+ * to the system bus if not specified.
+ */
+ g_object_class_install_property
+ (object_class, PROP_BUS,
+ g_param_spec_boxed (NM_REMOTE_SETTINGS_BUS, "", "",
+ DBUS_TYPE_G_CONNECTION,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMRemoteSettings:service-running:
+ *
+ * Whether the settings service is running.
+ */
+ g_object_class_install_property
+ (object_class, PROP_SERVICE_RUNNING,
+ g_param_spec_boolean (NM_REMOTE_SETTINGS_SERVICE_RUNNING, "", "",
+ FALSE,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMRemoteSettings:hostname:
+ *
+ * The machine hostname stored in persistent configuration. This can be
+ * modified by calling nm_remote_settings_save_hostname().
+ */
+ g_object_class_install_property
+ (object_class, PROP_HOSTNAME,
+ g_param_spec_string (NM_REMOTE_SETTINGS_HOSTNAME, "", "",
+ NULL,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMRemoteSettings:can-modify:
+ *
+ * If %TRUE, adding and modifying connections is supported.
+ */
+ g_object_class_install_property
+ (object_class, PROP_CAN_MODIFY,
+ g_param_spec_boolean (NM_REMOTE_SETTINGS_CAN_MODIFY, "", "",
+ FALSE,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /* Signals */
+ signals[NEW_CONNECTION] =
+ g_signal_new (NM_REMOTE_SETTINGS_NEW_CONNECTION,
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NMRemoteSettingsClass, new_connection),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 1, G_TYPE_OBJECT);
+
+ signals[CONNECTIONS_READ] =
+ g_signal_new (NM_REMOTE_SETTINGS_CONNECTIONS_READ,
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NMRemoteSettingsClass, connections_read),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+}
+
+static void
+nm_remote_settings_initable_iface_init (GInitableIface *iface)
+{
+ iface->init = init_sync;
+}
+
+static void
+nm_remote_settings_async_initable_iface_init (GAsyncInitableIface *iface)
+{
+ iface->init_async = init_async;
+ iface->init_finish = init_finish;
+}
diff --git a/libnm/nm-remote-settings.h b/libnm/nm-remote-settings.h
new file mode 100644
index 0000000000..92049adee0
--- /dev/null
+++ b/libnm/nm-remote-settings.h
@@ -0,0 +1,162 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2008 Novell, Inc.
+ * Copyright 2009 - 2011 Red Hat, Inc.
+ */
+
+#ifndef NM_REMOTE_SETTINGS_H
+#define NM_REMOTE_SETTINGS_H
+
+#include <gio/gio.h>
+#include <dbus/dbus-glib.h>
+#include <nm-connection.h>
+#include <nm-remote-connection.h>
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_REMOTE_SETTINGS (nm_remote_settings_get_type ())
+#define NM_REMOTE_SETTINGS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_REMOTE_SETTINGS, NMRemoteSettings))
+#define NM_REMOTE_SETTINGS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_REMOTE_SETTINGS, NMRemoteSettingsClass))
+#define NM_IS_REMOTE_SETTINGS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_REMOTE_SETTINGS))
+#define NM_IS_REMOTE_SETTINGS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_REMOTE_SETTINGS))
+#define NM_REMOTE_SETTINGS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_REMOTE_SETTINGS, NMRemoteSettingsClass))
+
+/**
+ * NMRemoteSettingsError:
+ * @NM_REMOTE_SETTINGS_ERROR_UNKNOWN: unknown or unclassified error
+ * @NM_REMOTE_SETTINGS_ERROR_CONNECTION_REMOVED: the #NMRemoteConnection object
+ * was removed before it was completely initialized
+ * @NM_REMOTE_SETTINGS_ERROR_CONNECTION_UNAVAILABLE: the #NMRemoteConnection object
+ * is not visible or otherwise unreadable
+ * @NM_REMOTE_SETTINGS_ERROR_SERVICE_UNAVAILABLE: NetworkManager is not running.
+ * (Since 0.9.10)
+ *
+ * Describes errors that may result from operations involving a #NMRemoteSettings.
+ *
+ **/
+typedef enum {
+ NM_REMOTE_SETTINGS_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/
+ NM_REMOTE_SETTINGS_ERROR_CONNECTION_REMOVED, /*< nick=ConnectionRemoved >*/
+ NM_REMOTE_SETTINGS_ERROR_CONNECTION_UNAVAILABLE, /*< nick=ConnectionUnavailable >*/
+ NM_REMOTE_SETTINGS_ERROR_SERVICE_UNAVAILABLE, /*< nick=ServiceUnavailable >*/
+} NMRemoteSettingsError;
+
+#define NM_REMOTE_SETTINGS_ERROR nm_remote_settings_error_quark ()
+GQuark nm_remote_settings_error_quark (void);
+
+
+#define NM_REMOTE_SETTINGS_BUS "bus"
+#define NM_REMOTE_SETTINGS_SERVICE_RUNNING "service-running"
+#define NM_REMOTE_SETTINGS_HOSTNAME "hostname"
+#define NM_REMOTE_SETTINGS_CAN_MODIFY "can-modify"
+
+#define NM_REMOTE_SETTINGS_NEW_CONNECTION "new-connection"
+#define NM_REMOTE_SETTINGS_CONNECTIONS_READ "connections-read"
+
+typedef struct _NMRemoteSettings NMRemoteSettings;
+typedef struct _NMRemoteSettingsClass NMRemoteSettingsClass;
+
+
+typedef void (*NMRemoteSettingsAddConnectionFunc) (NMRemoteSettings *settings,
+ NMRemoteConnection *connection,
+ GError *error,
+ gpointer user_data);
+
+typedef void (*NMRemoteSettingsLoadConnectionsFunc) (NMRemoteSettings *settings,
+ char **failures,
+ GError *error,
+ gpointer user_data);
+
+typedef void (*NMRemoteSettingsSaveHostnameFunc) (NMRemoteSettings *settings,
+ GError *error,
+ gpointer user_data);
+
+
+struct _NMRemoteSettings {
+ GObject parent;
+};
+
+struct _NMRemoteSettingsClass {
+ GObjectClass parent;
+
+ /* Signals */
+ void (*new_connection) (NMRemoteSettings *settings,
+ NMRemoteConnection *connection);
+
+ void (*connections_read) (NMRemoteSettings *settings);
+
+ /* Padding for future expansion */
+ void (*_reserved1) (void);
+ void (*_reserved2) (void);
+ void (*_reserved3) (void);
+ void (*_reserved4) (void);
+ void (*_reserved5) (void);
+ void (*_reserved6) (void);
+};
+
+GType nm_remote_settings_get_type (void);
+
+NMRemoteSettings *nm_remote_settings_new (DBusGConnection *bus);
+
+void nm_remote_settings_new_async (DBusGConnection *bus,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+NMRemoteSettings *nm_remote_settings_new_finish (GAsyncResult *result,
+ GError **error);
+
+GSList *nm_remote_settings_list_connections (NMRemoteSettings *settings);
+
+NMRemoteConnection *nm_remote_settings_get_connection_by_id (NMRemoteSettings *settings,
+ const char *id);
+
+NMRemoteConnection * nm_remote_settings_get_connection_by_path (NMRemoteSettings *settings,
+ const char *path);
+
+NMRemoteConnection *nm_remote_settings_get_connection_by_uuid (NMRemoteSettings *settings,
+ const char *uuid);
+
+gboolean nm_remote_settings_add_connection (NMRemoteSettings *settings,
+ NMConnection *connection,
+ NMRemoteSettingsAddConnectionFunc callback,
+ gpointer user_data);
+
+NM_AVAILABLE_IN_0_9_10
+gboolean nm_remote_settings_add_connection_unsaved (NMRemoteSettings *settings,
+ NMConnection *connection,
+ NMRemoteSettingsAddConnectionFunc callback,
+ gpointer user_data);
+
+NM_AVAILABLE_IN_0_9_10
+gboolean nm_remote_settings_load_connections (NMRemoteSettings *settings,
+ char **filenames,
+ char ***failures,
+ GError **error);
+
+NM_AVAILABLE_IN_0_9_10
+gboolean nm_remote_settings_reload_connections (NMRemoteSettings *settings,
+ GError **error);
+
+gboolean nm_remote_settings_save_hostname (NMRemoteSettings *settings,
+ const char *hostname,
+ NMRemoteSettingsSaveHostnameFunc callback,
+ gpointer user_data);
+
+G_END_DECLS
+
+#endif /* NM_REMOTE_SETTINGS_H */
diff --git a/libnm/nm-secret-agent.c b/libnm/nm-secret-agent.c
new file mode 100644
index 0000000000..ea6197743c
--- /dev/null
+++ b/libnm/nm-secret-agent.c
@@ -0,0 +1,1077 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2010 - 2011 Red Hat, Inc.
+ */
+
+#include <config.h>
+#include <string.h>
+#include <dbus/dbus-glib-lowlevel.h>
+
+#include "nm-glib-compat.h"
+#include "NetworkManager.h"
+#include "nm-secret-agent.h"
+#include "nm-glib-enum-types.h"
+#include "nm-dbus-helpers-private.h"
+
+static void impl_secret_agent_get_secrets (NMSecretAgent *self,
+ GHashTable *connection_hash,
+ const char *connection_path,
+ const char *setting_name,
+ const char **hints,
+ guint32 flags,
+ DBusGMethodInvocation *context);
+
+static void impl_secret_agent_cancel_get_secrets (NMSecretAgent *self,
+ const char *connection_path,
+ const char *setting_name,
+ DBusGMethodInvocation *context);
+
+static void impl_secret_agent_save_secrets (NMSecretAgent *self,
+ GHashTable *connection_hash,
+ const char *connection_path,
+ DBusGMethodInvocation *context);
+
+static void impl_secret_agent_delete_secrets (NMSecretAgent *self,
+ GHashTable *connection_hash,
+ const char *connection_path,
+ DBusGMethodInvocation *context);
+
+#include "nm-secret-agent-glue.h"
+
+G_DEFINE_ABSTRACT_TYPE (NMSecretAgent, nm_secret_agent, G_TYPE_OBJECT)
+
+#define NM_SECRET_AGENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SECRET_AGENT, NMSecretAgentPrivate))
+
+static gboolean auto_register_cb (gpointer user_data);
+
+typedef struct {
+ gboolean registered;
+ NMSecretAgentCapabilities capabilities;
+
+ DBusGConnection *bus;
+ gboolean private_bus;
+ DBusGProxy *dbus_proxy;
+ DBusGProxy *manager_proxy;
+ DBusGProxyCall *reg_call;
+
+ /* GetSecretsInfo structs of in-flight GetSecrets requests */
+ GSList *pending_gets;
+
+ char *nm_owner;
+
+ char *identifier;
+ gboolean auto_register;
+ gboolean suppress_auto;
+ gboolean auto_register_id;
+} NMSecretAgentPrivate;
+
+enum {
+ PROP_0,
+ PROP_IDENTIFIER,
+ PROP_AUTO_REGISTER,
+ PROP_REGISTERED,
+ PROP_CAPABILITIES,
+
+ LAST_PROP
+};
+
+enum {
+ REGISTRATION_RESULT,
+
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+
+/********************************************************************/
+
+GQuark
+nm_secret_agent_error_quark (void)
+{
+ static GQuark ret = 0;
+
+ if (G_UNLIKELY (ret == 0))
+ ret = g_quark_from_static_string ("nm-secret-agent-error");
+ return ret;
+}
+
+/*************************************************************/
+
+static const char *
+get_nm_owner (NMSecretAgent *self)
+{
+ NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self);
+ GError *error = NULL;
+ char *owner;
+
+ if (!priv->nm_owner) {
+ if (!dbus_g_proxy_call_with_timeout (priv->dbus_proxy,
+ "GetNameOwner", 2000, &error,
+ G_TYPE_STRING, NM_DBUS_SERVICE,
+ G_TYPE_INVALID,
+ G_TYPE_STRING, &owner,
+ G_TYPE_INVALID))
+ return NULL;
+
+ priv->nm_owner = g_strdup (owner);
+ g_free (owner);
+ }
+
+ return priv->nm_owner;
+}
+
+static void
+_internal_unregister (NMSecretAgent *self)
+{
+ NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self);
+
+ if (priv->registered) {
+ dbus_g_connection_unregister_g_object (priv->bus, G_OBJECT (self));
+ priv->registered = FALSE;
+ g_object_notify (G_OBJECT (self), NM_SECRET_AGENT_REGISTERED);
+ }
+}
+
+typedef struct {
+ char *path;
+ char *setting_name;
+ DBusGMethodInvocation *context;
+} GetSecretsInfo;
+
+static void
+get_secrets_info_finalize (NMSecretAgent *self, GetSecretsInfo *info)
+{
+ NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self);
+
+ g_return_if_fail (info != NULL);
+
+ priv->pending_gets = g_slist_remove (priv->pending_gets, info);
+
+ g_free (info->path);
+ g_free (info->setting_name);
+ memset (info, 0, sizeof (*info));
+ g_free (info);
+}
+
+static void
+name_owner_changed (DBusGProxy *proxy,
+ const char *name,
+ const char *old_owner,
+ const char *new_owner,
+ gpointer user_data)
+{
+ NMSecretAgent *self = NM_SECRET_AGENT (user_data);
+ NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self);
+ gboolean old_owner_good = (old_owner && strlen (old_owner));
+ gboolean new_owner_good = (new_owner && strlen (new_owner));
+ GSList *iter;
+
+ if (strcmp (name, NM_DBUS_SERVICE) == 0) {
+ g_free (priv->nm_owner);
+ priv->nm_owner = g_strdup (new_owner);
+
+ if (!old_owner_good && new_owner_good) {
+ /* NM appeared */
+ auto_register_cb (self);
+ } else if (old_owner_good && !new_owner_good) {
+ /* Cancel any pending secrets requests */
+ for (iter = priv->pending_gets; iter; iter = g_slist_next (iter)) {
+ GetSecretsInfo *info = iter->data;
+
+ NM_SECRET_AGENT_GET_CLASS (self)->cancel_get_secrets (self,
+ info->path,
+ info->setting_name);
+ }
+ g_slist_free (priv->pending_gets);
+ priv->pending_gets = NULL;
+
+ /* NM disappeared */
+ _internal_unregister (self);
+ } else if (old_owner_good && new_owner_good && strcmp (old_owner, new_owner)) {
+ /* Hmm, NM magically restarted */
+ _internal_unregister (self);
+ auto_register_cb (self);
+ }
+ }
+}
+
+static gboolean
+verify_sender (NMSecretAgent *self,
+ DBusGMethodInvocation *context,
+ GError **error)
+{
+ NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self);
+ DBusConnection *bus;
+ char *sender;
+ const char *nm_owner;
+ DBusError dbus_error;
+ uid_t sender_uid = G_MAXUINT;
+ gboolean allowed = FALSE;
+
+ g_return_val_if_fail (context != NULL, FALSE);
+
+ /* Private bus connection is always to NetworkManager, which is always
+ * UID 0.
+ */
+ if (priv->private_bus)
+ return TRUE;
+
+ /* Verify the sender's UID is 0, and that the sender is the same as
+ * NetworkManager's bus name owner.
+ */
+
+ nm_owner = get_nm_owner (self);
+ if (!nm_owner) {
+ g_set_error_literal (error,
+ NM_SECRET_AGENT_ERROR,
+ NM_SECRET_AGENT_ERROR_NOT_AUTHORIZED,
+ "NetworkManager bus name owner unknown.");
+ return FALSE;
+ }
+
+ bus = dbus_g_connection_get_connection (priv->bus);
+ if (!bus) {
+ g_set_error_literal (error,
+ NM_SECRET_AGENT_ERROR,
+ NM_SECRET_AGENT_ERROR_NOT_AUTHORIZED,
+ "Failed to get DBus connection.");
+ return FALSE;
+ }
+
+ sender = dbus_g_method_get_sender (context);
+ if (!sender) {
+ g_set_error_literal (error,
+ NM_SECRET_AGENT_ERROR,
+ NM_SECRET_AGENT_ERROR_NOT_AUTHORIZED,
+ "Failed to get request sender.");
+ return FALSE;
+ }
+
+ /* Check that the sender matches the current NM bus name owner */
+ if (strcmp (sender, nm_owner) != 0) {
+ g_set_error_literal (error,
+ NM_SECRET_AGENT_ERROR,
+ NM_SECRET_AGENT_ERROR_NOT_AUTHORIZED,
+ "Request sender does not match NetworkManager bus name owner.");
+ goto out;
+ }
+
+ dbus_error_init (&dbus_error);
+ sender_uid = dbus_bus_get_unix_user (bus, sender, &dbus_error);
+ if (dbus_error_is_set (&dbus_error)) {
+ g_set_error (error,
+ NM_SECRET_AGENT_ERROR,
+ NM_SECRET_AGENT_ERROR_NOT_AUTHORIZED,
+ "Failed to get request unix user: (%s) %s.",
+ dbus_error.name, dbus_error.message);
+ dbus_error_free (&dbus_error);
+ goto out;
+ }
+
+ /* We only accept requests from NM, which always runs as root */
+ if (0 != sender_uid) {
+ g_set_error_literal (error,
+ NM_SECRET_AGENT_ERROR,
+ NM_SECRET_AGENT_ERROR_NOT_AUTHORIZED,
+ "Request sender is not root.");
+ goto out;
+ }
+
+ allowed = TRUE;
+
+out:
+ g_free (sender);
+ return allowed;
+}
+
+static gboolean
+verify_request (NMSecretAgent *self,
+ DBusGMethodInvocation *context,
+ GHashTable *connection_hash,
+ const char *connection_path,
+ NMConnection **out_connection,
+ GError **error)
+{
+ NMConnection *connection = NULL;
+ GError *local = NULL;
+
+ if (!verify_sender (self, context, error))
+ return FALSE;
+
+ /* No connection? If the sender verified, then we allow the request */
+ if (connection_hash == NULL)
+ return TRUE;
+
+ /* If we have a connection hash, we require a path too */
+ if (connection_path == NULL) {
+ g_set_error_literal (error,
+ NM_SECRET_AGENT_ERROR,
+ NM_SECRET_AGENT_ERROR_INVALID_CONNECTION,
+ "Invalid connection: no connection path given.");
+ return FALSE;
+ }
+
+ /* Make sure the given connection is valid */
+ g_assert (out_connection);
+ connection = nm_connection_new_from_hash (connection_hash, &local);
+ if (connection) {
+ nm_connection_set_path (connection, connection_path);
+ *out_connection = connection;
+ } else {
+ g_set_error (error,
+ NM_SECRET_AGENT_ERROR,
+ NM_SECRET_AGENT_ERROR_INVALID_CONNECTION,
+ "Invalid connection: (%d) %s",
+ local ? local->code : -1,
+ (local && local->message) ? local->message : "(unknown)");
+ g_clear_error (&local);
+ }
+
+ return !!connection;
+}
+
+static void
+get_secrets_cb (NMSecretAgent *self,
+ NMConnection *connection,
+ GHashTable *secrets,
+ GError *error,
+ gpointer user_data)
+{
+ GetSecretsInfo *info = user_data;
+
+ if (error)
+ dbus_g_method_return_error (info->context, error);
+ else
+ dbus_g_method_return (info->context, secrets);
+
+ /* Remove the request from internal tracking */
+ get_secrets_info_finalize (self, info);
+}
+
+static void
+impl_secret_agent_get_secrets (NMSecretAgent *self,
+ GHashTable *connection_hash,
+ const char *connection_path,
+ const char *setting_name,
+ const char **hints,
+ guint32 flags,
+ DBusGMethodInvocation *context)
+{
+ NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self);
+ GError *error = NULL;
+ NMConnection *connection = NULL;
+ GetSecretsInfo *info;
+
+ /* Make sure the request comes from NetworkManager and is valid */
+ if (!verify_request (self, context, connection_hash, connection_path, &connection, &error)) {
+ dbus_g_method_return_error (context, error);
+ g_clear_error (&error);
+ return;
+ }
+
+ info = g_malloc0 (sizeof (GetSecretsInfo));
+ info->path = g_strdup (connection_path);
+ info->setting_name = g_strdup (setting_name);
+ info->context = context;
+ priv->pending_gets = g_slist_append (priv->pending_gets, info);
+
+ NM_SECRET_AGENT_GET_CLASS (self)->get_secrets (self,
+ connection,
+ connection_path,
+ setting_name,
+ hints,
+ flags,
+ get_secrets_cb,
+ info);
+ g_object_unref (connection);
+}
+
+static GetSecretsInfo *
+find_get_secrets_info (GSList *list, const char *path, const char *setting_name)
+{
+ GSList *iter;
+
+ for (iter = list; iter; iter = g_slist_next (iter)) {
+ GetSecretsInfo *candidate = iter->data;
+
+ if ( g_strcmp0 (path, candidate->path) == 0
+ && g_strcmp0 (setting_name, candidate->setting_name) == 0)
+ return candidate;
+ }
+ return NULL;
+}
+
+static void
+impl_secret_agent_cancel_get_secrets (NMSecretAgent *self,
+ const char *connection_path,
+ const char *setting_name,
+ DBusGMethodInvocation *context)
+{
+ NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self);
+ GError *error = NULL;
+ GetSecretsInfo *info;
+
+ /* Make sure the request comes from NetworkManager and is valid */
+ if (!verify_request (self, context, NULL, NULL, NULL, &error)) {
+ dbus_g_method_return_error (context, error);
+ g_clear_error (&error);
+ return;
+ }
+
+ info = find_get_secrets_info (priv->pending_gets, connection_path, setting_name);
+ if (!info) {
+ g_set_error_literal (&error,
+ NM_SECRET_AGENT_ERROR,
+ NM_SECRET_AGENT_ERROR_INTERNAL_ERROR,
+ "No secrets request in progress for this connection.");
+ dbus_g_method_return_error (context, error);
+ g_clear_error (&error);
+ return;
+ }
+
+ /* Send the cancel request up to the subclass and finalize it */
+ NM_SECRET_AGENT_GET_CLASS (self)->cancel_get_secrets (self,
+ info->path,
+ info->setting_name);
+ dbus_g_method_return (context);
+}
+
+static void
+save_secrets_cb (NMSecretAgent *self,
+ NMConnection *connection,
+ GError *error,
+ gpointer user_data)
+{
+ DBusGMethodInvocation *context = user_data;
+
+ if (error)
+ dbus_g_method_return_error (context, error);
+ else
+ dbus_g_method_return (context);
+}
+
+static void
+impl_secret_agent_save_secrets (NMSecretAgent *self,
+ GHashTable *connection_hash,
+ const char *connection_path,
+ DBusGMethodInvocation *context)
+{
+ GError *error = NULL;
+ NMConnection *connection = NULL;
+
+ /* Make sure the request comes from NetworkManager and is valid */
+ if (!verify_request (self, context, connection_hash, connection_path, &connection, &error)) {
+ dbus_g_method_return_error (context, error);
+ g_clear_error (&error);
+ return;
+ }
+
+ NM_SECRET_AGENT_GET_CLASS (self)->save_secrets (self,
+ connection,
+ connection_path,
+ save_secrets_cb,
+ context);
+ g_object_unref (connection);
+}
+
+static void
+delete_secrets_cb (NMSecretAgent *self,
+ NMConnection *connection,
+ GError *error,
+ gpointer user_data)
+{
+ DBusGMethodInvocation *context = user_data;
+
+ if (error)
+ dbus_g_method_return_error (context, error);
+ else
+ dbus_g_method_return (context);
+}
+
+static void
+impl_secret_agent_delete_secrets (NMSecretAgent *self,
+ GHashTable *connection_hash,
+ const char *connection_path,
+ DBusGMethodInvocation *context)
+{
+ GError *error = NULL;
+ NMConnection *connection = NULL;
+
+ /* Make sure the request comes from NetworkManager and is valid */
+ if (!verify_request (self, context, connection_hash, connection_path, &connection, &error)) {
+ dbus_g_method_return_error (context, error);
+ g_clear_error (&error);
+ return;
+ }
+
+ NM_SECRET_AGENT_GET_CLASS (self)->delete_secrets (self,
+ connection,
+ connection_path,
+ delete_secrets_cb,
+ context);
+ g_object_unref (connection);
+}
+
+/**************************************************************/
+
+static void
+reg_result (NMSecretAgent *self, GError *error)
+{
+ NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self);
+
+ if (error) {
+ /* If registration failed we shouldn't expose ourselves on the bus */
+ _internal_unregister (self);
+ } else {
+ priv->registered = TRUE;
+ g_object_notify (G_OBJECT (self), NM_SECRET_AGENT_REGISTERED);
+ }
+
+ g_signal_emit (self, signals[REGISTRATION_RESULT], 0, error);
+}
+
+static void
+reg_request_cb (DBusGProxy *proxy,
+ DBusGProxyCall *call,
+ gpointer user_data)
+{
+ NMSecretAgent *self = NM_SECRET_AGENT (user_data);
+ NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self);
+ GError *error = NULL;
+
+ priv->reg_call = NULL;
+
+ dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_INVALID);
+ reg_result (self, error);
+ g_clear_error (&error);
+}
+
+static void
+reg_with_caps_cb (DBusGProxy *proxy,
+ DBusGProxyCall *call,
+ gpointer user_data)
+{
+ NMSecretAgent *self = NM_SECRET_AGENT (user_data);
+ NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self);
+
+ priv->reg_call = NULL;
+
+ if (dbus_g_proxy_end_call (proxy, call, NULL, G_TYPE_INVALID)) {
+ reg_result (self, NULL);
+ return;
+ }
+
+ /* Might be an old NetworkManager that doesn't support capabilities;
+ * fall back to old Register() method instead.
+ */
+ priv->reg_call = dbus_g_proxy_begin_call_with_timeout (priv->manager_proxy,
+ "Register",
+ reg_request_cb,
+ self,
+ NULL,
+ 5000,
+ G_TYPE_STRING, priv->identifier,
+ G_TYPE_INVALID);
+}
+
+/**
+ * nm_secret_agent_register:
+ * @self: a #NMSecretAgent
+ *
+ * Registers the #NMSecretAgent with the NetworkManager secret manager,
+ * indicating to NetworkManager that the agent is able to provide and save
+ * secrets for connections on behalf of its user. Registration is an
+ * asynchronous operation and its success or failure is indicated via the
+ * 'registration-result' signal.
+ *
+ * Returns: a new %TRUE if registration was successfully requested (this does
+ * not mean registration itself was successful), %FALSE if registration was not
+ * successfully requested.
+ **/
+gboolean
+nm_secret_agent_register (NMSecretAgent *self)
+{
+ NMSecretAgentPrivate *priv;
+ NMSecretAgentClass *class;
+
+ g_return_val_if_fail (NM_IS_SECRET_AGENT (self), FALSE);
+
+ priv = NM_SECRET_AGENT_GET_PRIVATE (self);
+
+ g_return_val_if_fail (priv->registered == FALSE, FALSE);
+ g_return_val_if_fail (priv->reg_call == NULL, FALSE);
+ g_return_val_if_fail (priv->bus != NULL, FALSE);
+ g_return_val_if_fail (priv->manager_proxy != NULL, FALSE);
+
+ /* Also make sure the subclass can actually respond to secrets requests */
+ class = NM_SECRET_AGENT_GET_CLASS (self);
+ g_return_val_if_fail (class->get_secrets != NULL, FALSE);
+ g_return_val_if_fail (class->save_secrets != NULL, FALSE);
+ g_return_val_if_fail (class->delete_secrets != NULL, FALSE);
+
+ if (!priv->nm_owner && !priv->private_bus)
+ return FALSE;
+
+ priv->suppress_auto = FALSE;
+
+ /* Export our secret agent interface before registering with the manager */
+ dbus_g_connection_register_g_object (priv->bus,
+ NM_DBUS_PATH_SECRET_AGENT,
+ G_OBJECT (self));
+
+ priv->reg_call = dbus_g_proxy_begin_call_with_timeout (priv->manager_proxy,
+ "RegisterWithCapabilities",
+ reg_with_caps_cb,
+ self,
+ NULL,
+ 5000,
+ G_TYPE_STRING, priv->identifier,
+ G_TYPE_UINT, priv->capabilities,
+ G_TYPE_INVALID);
+ return TRUE;
+}
+
+/**
+ * nm_secret_agent_unregister:
+ * @self: a #NMSecretAgent
+ *
+ * Unregisters the #NMSecretAgent with the NetworkManager secret manager,
+ * indicating to NetworkManager that the agent is will no longer provide or
+ * store secrets on behalf of this user.
+ *
+ * Returns: a new %TRUE if unregistration was successful, %FALSE if it was not.
+ **/
+gboolean
+nm_secret_agent_unregister (NMSecretAgent *self)
+{
+ NMSecretAgentPrivate *priv;
+
+ g_return_val_if_fail (NM_IS_SECRET_AGENT (self), FALSE);
+
+ priv = NM_SECRET_AGENT_GET_PRIVATE (self);
+
+ g_return_val_if_fail (priv->registered == TRUE, FALSE);
+ g_return_val_if_fail (priv->bus != NULL, FALSE);
+ g_return_val_if_fail (priv->manager_proxy != NULL, FALSE);
+
+ if (!priv->nm_owner && !priv->private_bus)
+ return FALSE;
+
+ dbus_g_proxy_call_no_reply (priv->manager_proxy, "Unregister", G_TYPE_INVALID);
+
+ _internal_unregister (self);
+ priv->suppress_auto = TRUE;
+
+ return TRUE;
+}
+
+/**
+ * nm_secret_agent_get_registered:
+ * @self: a #NMSecretAgent
+ *
+ * Returns: a %TRUE if the agent is registered, %FALSE if it is not.
+ **/
+gboolean
+nm_secret_agent_get_registered (NMSecretAgent *self)
+{
+ g_return_val_if_fail (NM_IS_SECRET_AGENT (self), FALSE);
+
+ return NM_SECRET_AGENT_GET_PRIVATE (self)->registered;
+}
+
+static gboolean
+auto_register_cb (gpointer user_data)
+{
+ NMSecretAgent *self = NM_SECRET_AGENT (user_data);
+ NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self);
+
+ priv->auto_register_id = 0;
+ if (priv->auto_register && !priv->suppress_auto && (priv->reg_call == NULL))
+ nm_secret_agent_register (self);
+ return FALSE;
+}
+
+/**************************************************************/
+
+/**
+ * nm_secret_agent_get_secrets:
+ * @self: a #NMSecretAgent
+ * @connection: the #NMConnection for which we're asked secrets
+ * @setting_name: the name of the secret setting
+ * @hints: (array zero-terminated=1): hints to the agent
+ * @flags: flags that modify the behavior of the request
+ * @callback: (scope async): a callback, to be invoked when the operation is done
+ * @user_data: (closure): caller-specific data to be passed to @callback
+ *
+ * Asyncronously retrieve secrets belonging to @connection for the
+ * setting @setting_name. @flags indicate specific behavior that the secret
+ * agent should use when performing the request, for example returning only
+ * existing secrets without user interaction, or requesting entirely new
+ * secrets from the user.
+ *
+ * Virtual: get_secrets
+ */
+void
+nm_secret_agent_get_secrets (NMSecretAgent *self,
+ NMConnection *connection,
+ const char *setting_name,
+ const char **hints,
+ NMSecretAgentGetSecretsFlags flags,
+ NMSecretAgentGetSecretsFunc callback,
+ gpointer user_data)
+{
+ g_return_if_fail (NM_IS_SECRET_AGENT (self));
+ g_return_if_fail (NM_IS_CONNECTION (connection));
+ g_return_if_fail (nm_connection_get_path (connection));
+ g_return_if_fail (setting_name != NULL);
+ g_return_if_fail (strlen (setting_name) > 0);
+ g_return_if_fail (callback != NULL);
+
+ NM_SECRET_AGENT_GET_CLASS (self)->get_secrets (self,
+ connection,
+ nm_connection_get_path (connection),
+ setting_name,
+ hints,
+ flags,
+ callback,
+ user_data);
+}
+
+/**
+ * nm_secret_agent_save_secrets:
+ * @self: a #NMSecretAgent
+ * @connection: a #NMConnection
+ * @callback: (scope async): a callback, to be invoked when the operation is done
+ * @user_data: (closure): caller-specific data to be passed to @callback
+ *
+ * Asyncronously ensure that all secrets inside @connection
+ * are stored to disk.
+ *
+ * Virtual: save_secrets
+ */
+void
+nm_secret_agent_save_secrets (NMSecretAgent *self,
+ NMConnection *connection,
+ NMSecretAgentSaveSecretsFunc callback,
+ gpointer user_data)
+{
+ g_return_if_fail (NM_IS_SECRET_AGENT (self));
+ g_return_if_fail (NM_IS_CONNECTION (connection));
+ g_return_if_fail (nm_connection_get_path (connection));
+
+ NM_SECRET_AGENT_GET_CLASS (self)->save_secrets (self,
+ connection,
+ nm_connection_get_path (connection),
+ callback,
+ user_data);
+}
+
+/**
+ * nm_secret_agent_delete_secrets:
+ * @self: a #NMSecretAgent
+ * @connection: a #NMConnection
+ * @callback: (scope async): a callback, to be invoked when the operation is done
+ * @user_data: (closure): caller-specific data to be passed to @callback
+ *
+ * Asynchronously ask the agent to delete all saved secrets belonging to
+ * @connection.
+ *
+ * Virtual: delete_secrets
+ */
+void
+nm_secret_agent_delete_secrets (NMSecretAgent *self,
+ NMConnection *connection,
+ NMSecretAgentDeleteSecretsFunc callback,
+ gpointer user_data)
+{
+ g_return_if_fail (NM_IS_SECRET_AGENT (self));
+ g_return_if_fail (NM_IS_CONNECTION (connection));
+ g_return_if_fail (nm_connection_get_path (connection));
+
+ NM_SECRET_AGENT_GET_CLASS (self)->delete_secrets (self,
+ connection,
+ nm_connection_get_path (connection),
+ callback,
+ user_data);
+}
+
+/**************************************************************/
+
+static gboolean
+validate_identifier (const char *identifier)
+{
+ const char *p = identifier;
+ size_t id_len;
+
+ /* Length between 3 and 255 characters inclusive */
+ id_len = strlen (identifier);
+ if (id_len < 3 || id_len > 255)
+ return FALSE;
+
+ if ((identifier[0] == '.') || (identifier[id_len - 1] == '.'))
+ return FALSE;
+
+ /* FIXME: do complete validation here */
+ while (p && *p) {
+ if (!g_ascii_isalnum (*p) && (*p != '_') && (*p != '-') && (*p != '.'))
+ return FALSE;
+ if ((*p == '.') && (*(p + 1) == '.'))
+ return FALSE;
+ p++;
+ }
+
+ return TRUE;
+}
+
+static void
+nm_secret_agent_init (NMSecretAgent *self)
+{
+ NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self);
+ GError *error = NULL;
+
+ priv->bus = _nm_dbus_new_connection (&error);
+ if (!priv->bus) {
+ g_warning ("Couldn't connect to system bus: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+ priv->private_bus = _nm_dbus_is_connection_private (priv->bus);
+
+ if (priv->private_bus == FALSE) {
+ priv->dbus_proxy = dbus_g_proxy_new_for_name (priv->bus,
+ DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS);
+ g_assert (priv->dbus_proxy);
+
+ dbus_g_object_register_marshaller (g_cclosure_marshal_generic,
+ G_TYPE_NONE,
+ G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
+ G_TYPE_INVALID);
+ dbus_g_proxy_add_signal (priv->dbus_proxy, "NameOwnerChanged",
+ G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
+ G_TYPE_INVALID);
+ dbus_g_proxy_connect_signal (priv->dbus_proxy,
+ "NameOwnerChanged",
+ G_CALLBACK (name_owner_changed),
+ self, NULL);
+
+ get_nm_owner (self);
+ }
+
+ priv->manager_proxy = _nm_dbus_new_proxy_for_connection (priv->bus,
+ NM_DBUS_PATH_AGENT_MANAGER,
+ NM_DBUS_INTERFACE_AGENT_MANAGER);
+ if (!priv->manager_proxy) {
+ g_warning ("Couldn't create NM agent manager proxy.");
+ return;
+ }
+
+ if (priv->nm_owner || priv->private_bus)
+ priv->auto_register_id = g_idle_add (auto_register_cb, self);
+}
+
+static void
+get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_IDENTIFIER:
+ g_value_set_string (value, priv->identifier);
+ break;
+ case PROP_AUTO_REGISTER:
+ g_value_set_boolean (value, priv->auto_register);
+ break;
+ case PROP_REGISTERED:
+ g_value_set_boolean (value, priv->registered);
+ break;
+ case PROP_CAPABILITIES:
+ g_value_set_flags (value, priv->capabilities);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (object);
+ const char *identifier;
+
+ switch (prop_id) {
+ case PROP_IDENTIFIER:
+ identifier = g_value_get_string (value);
+
+ g_return_if_fail (validate_identifier (identifier));
+
+ g_free (priv->identifier);
+ priv->identifier = g_strdup (identifier);
+ break;
+ case PROP_AUTO_REGISTER:
+ priv->auto_register = g_value_get_boolean (value);
+ break;
+ case PROP_CAPABILITIES:
+ priv->capabilities = g_value_get_flags (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+dispose (GObject *object)
+{
+ NMSecretAgent *self = NM_SECRET_AGENT (object);
+ NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self);
+
+ if (priv->registered)
+ nm_secret_agent_unregister (self);
+
+ if (priv->auto_register_id) {
+ g_source_remove (priv->auto_register_id);
+ priv->auto_register_id = 0;
+ }
+
+ g_free (priv->identifier);
+ priv->identifier = NULL;
+ g_free (priv->nm_owner);
+ priv->nm_owner = NULL;
+
+ while (priv->pending_gets)
+ get_secrets_info_finalize (self, priv->pending_gets->data);
+
+ g_clear_object (&priv->dbus_proxy);
+ g_clear_object (&priv->manager_proxy);
+
+ if (priv->bus) {
+ dbus_g_connection_unref (priv->bus);
+ priv->bus = NULL;
+ }
+
+ G_OBJECT_CLASS (nm_secret_agent_parent_class)->dispose (object);
+}
+
+static void
+nm_secret_agent_class_init (NMSecretAgentClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+ g_type_class_add_private (class, sizeof (NMSecretAgentPrivate));
+
+ /* Virtual methods */
+ object_class->dispose = dispose;
+ object_class->get_property = get_property;
+ object_class->set_property = set_property;
+
+ /**
+ * NMSecretAgent:identifier:
+ *
+ * Identifies this agent; only one agent in each user session may use the
+ * same identifier. Identifier formatting follows the same rules as
+ * D-Bus bus names with the exception that the ':' character is not
+ * allowed. The valid set of characters is "[A-Z][a-z][0-9]_-." and the
+ * identifier is limited in length to 255 characters with a minimum
+ * of 3 characters. An example valid identifier is 'org.gnome.nm-applet'
+ * (without quotes).
+ **/
+ g_object_class_install_property
+ (object_class, PROP_IDENTIFIER,
+ g_param_spec_string (NM_SECRET_AGENT_IDENTIFIER, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSecretAgent:auto-register:
+ *
+ * If TRUE, the agent will attempt to automatically register itself after
+ * it is created (via an idle handler) and to re-register itself if
+ * NetworkManager restarts. If FALSE, the agent does not automatically
+ * register with NetworkManager, and nm_secret_agent_register() must be
+ * called. If 'auto-register' is TRUE, calling nm_secret_agent_unregister()
+ * will suppress auto-registration until nm_secret_agent_register() is
+ * called, which re-enables auto-registration.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_AUTO_REGISTER,
+ g_param_spec_boolean (NM_SECRET_AGENT_AUTO_REGISTER, "", "",
+ TRUE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSecretAgent:registered:
+ *
+ * %TRUE if the agent is registered with NetworkManager, %FALSE if not.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_REGISTERED,
+ g_param_spec_boolean (NM_SECRET_AGENT_REGISTERED, "", "",
+ FALSE,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSecretAgent:capabilities:
+ *
+ * A bitfield of %NMSecretAgentCapabilities.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_CAPABILITIES,
+ g_param_spec_flags (NM_SECRET_AGENT_CAPABILITIES, "", "",
+ NM_TYPE_SECRET_AGENT_CAPABILITIES,
+ NM_SECRET_AGENT_CAPABILITY_NONE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSecretAgent::registration-result:
+ * @agent: the agent that received the signal
+ * @error: the error, if any, that occured while registering
+ *
+ * Indicates the result of a registration request; if @error is NULL the
+ * request was successful.
+ **/
+ signals[REGISTRATION_RESULT] =
+ g_signal_new (NM_SECRET_AGENT_REGISTRATION_RESULT,
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0, NULL, NULL, NULL,
+ G_TYPE_NONE, 1, G_TYPE_POINTER);
+
+ dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (class),
+ &dbus_glib_nm_secret_agent_object_info);
+
+ dbus_g_error_domain_register (NM_SECRET_AGENT_ERROR,
+ NM_DBUS_INTERFACE_SECRET_AGENT,
+ NM_TYPE_SECRET_AGENT_ERROR);
+}
diff --git a/libnm/nm-secret-agent.h b/libnm/nm-secret-agent.h
new file mode 100644
index 0000000000..f593b3e648
--- /dev/null
+++ b/libnm/nm-secret-agent.h
@@ -0,0 +1,307 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2010 - 2011 Red Hat, Inc.
+ */
+
+#ifndef NM_SECRET_AGENT_H
+#define NM_SECRET_AGENT_H
+
+#include <nm-connection.h>
+
+G_BEGIN_DECLS
+
+#define NM_SECRET_AGENT_ERROR (nm_secret_agent_error_quark ())
+
+GQuark nm_secret_agent_error_quark (void);
+
+/**
+ * NMSecretAgentError:
+ * @NM_SECRET_AGENT_ERROR_NOT_AUTHORIZED: the caller (ie, NetworkManager) is not
+ * authorized to make this request
+ * @NM_SECRET_AGENT_ERROR_INVALID_CONNECTION: the connection for which secrets
+ * were requested could not be found
+ * @NM_SECRET_AGENT_ERROR_USER_CANCELED: the request was canceled by the user
+ * @NM_SECRET_AGENT_ERROR_AGENT_CANCELED: the agent canceled the request
+ * because it was requested to do so by NetworkManager
+ * @NM_SECRET_AGENT_ERROR_INTERNAL_ERROR: some internal error in the agent caused
+ * the request to fail
+ * @NM_SECRET_AGENT_ERROR_NO_SECRETS: the agent cannot find any secrets for this
+ * connection
+ *
+ * #NMSecretAgentError values are passed by secret agents back to NetworkManager
+ * when they encounter problems retrieving secrets on behalf of NM.
+ */
+typedef enum {
+ NM_SECRET_AGENT_ERROR_NOT_AUTHORIZED = 0, /*< nick=NotAuthorized >*/
+ NM_SECRET_AGENT_ERROR_INVALID_CONNECTION, /*< nick=InvalidConnection >*/
+ NM_SECRET_AGENT_ERROR_USER_CANCELED, /*< nick=UserCanceled >*/
+ NM_SECRET_AGENT_ERROR_AGENT_CANCELED, /*< nick=AgentCanceled >*/
+ NM_SECRET_AGENT_ERROR_INTERNAL_ERROR, /*< nick=InternalError >*/
+ NM_SECRET_AGENT_ERROR_NO_SECRETS, /*< nick=NoSecrets >*/
+} NMSecretAgentError;
+
+/**
+ * NMSecretAgentCapabilities:
+ * @NM_SECRET_AGENT_CAPABILITY_NONE: the agent supports no special capabilities
+ * @NM_SECRET_AGENT_CAPABILITY_VPN_HINTS: the agent supports sending hints given
+ * by the NMSecretAgentClass::get_secrets() class method to VPN plugin
+ * authentication dialogs.
+ * @NM_SECRET_AGENT_CAPABILITY_LAST: bounds checking value; should not be used.
+ *
+ * #NMSecretAgentCapabilities indicate various capabilities of the agent.
+ *
+ * Since: 0.9.10
+ */
+typedef enum /*< flags >*/ {
+ NM_SECRET_AGENT_CAPABILITY_NONE = 0x0,
+ NM_SECRET_AGENT_CAPABILITY_VPN_HINTS = 0x1,
+
+ /* boundary value */
+ NM_SECRET_AGENT_CAPABILITY_LAST = NM_SECRET_AGENT_CAPABILITY_VPN_HINTS
+} NMSecretAgentCapabilities;
+
+/**
+ * NMSecretAgentGetSecretsFlags:
+ * @NM_SECRET_AGENT_GET_SECRETS_FLAG_NONE: no special behavior; by default no
+ * user interaction is allowed and requests for secrets are fulfilled from
+ * persistent storage, or if no secrets are available an error is returned.
+ * @NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION: allows the request to
+ * interact with the user, possibly prompting via UI for secrets if any are
+ * required, or if none are found in persistent storage.
+ * @NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW: explicitly prompt for new
+ * secrets from the user. This flag signals that NetworkManager thinks any
+ * existing secrets are invalid or wrong. This flag implies that interaction
+ * is allowed.
+ * @NM_SECRET_AGENT_GET_SECRETS_FLAG_USER_REQUESTED: set if the request was
+ * initiated by user-requested action via the D-Bus interface, as opposed to
+ * automatically initiated by NetworkManager in response to (for example) scan
+ * results or carrier changes.
+ *
+ * #NMSecretAgentGetSecretsFlags values modify the behavior of a GetSecrets request.
+ */
+typedef enum /*< flags >*/ {
+ NM_SECRET_AGENT_GET_SECRETS_FLAG_NONE = 0x0,
+ NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION = 0x1,
+ NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW = 0x2,
+ NM_SECRET_AGENT_GET_SECRETS_FLAG_USER_REQUESTED = 0x4
+} NMSecretAgentGetSecretsFlags;
+
+#define NM_TYPE_SECRET_AGENT (nm_secret_agent_get_type ())
+#define NM_SECRET_AGENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SECRET_AGENT, NMSecretAgent))
+#define NM_SECRET_AGENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SECRET_AGENT, NMSecretAgentClass))
+#define NM_IS_SECRET_AGENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SECRET_AGENT))
+#define NM_IS_SECRET_AGENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SECRET_AGENT))
+#define NM_SECRET_AGENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SECRET_AGENT, NMSecretAgentClass))
+
+#define NM_SECRET_AGENT_IDENTIFIER "identifier"
+#define NM_SECRET_AGENT_AUTO_REGISTER "auto-register"
+#define NM_SECRET_AGENT_REGISTERED "registered"
+#define NM_SECRET_AGENT_CAPABILITIES "capabilities"
+
+#define NM_SECRET_AGENT_REGISTRATION_RESULT "registration-result"
+
+typedef struct {
+ GObject parent;
+} NMSecretAgent;
+
+/**
+ * NMSecretAgentGetSecretsFunc:
+ * @agent: the secret agent object
+ * @connection: (transfer none): the connection for which secrets were requested,
+ * note that this object will be unrefed after the callback has returned, use
+ * g_object_ref()/g_object_unref() if you want to use this object after the callback
+ * has returned
+ * @secrets: (element-type utf8 GLib.HashTable): the #GHashTable containing
+ * the requested secrets in the same format as an #NMConnection hash (as
+ * created by nm_connection_to_hash() for example). Each key in @secrets
+ * should be the name of a #NMSetting object (like "802-11-wireless-security")
+ * and each value should be a #GHashTable. The sub-hashes map string:#GValue
+ * where the string is the setting property name (like "psk") and the value
+ * is the secret
+ * @error: if the secrets request failed, give a descriptive error here
+ * @user_data: caller-specific data to be passed to the function
+ *
+ * Called as a result of a request by NM to retrieve secrets. When the
+ * #NMSecretAgent subclass has finished retrieving secrets and is ready to
+ * return them, or to return an error, this function should be called with
+ * those secrets or the error.
+ *
+ * To easily create the hash table to return the Wi-Fi PSK, you could do
+ * something like this:
+ * <example>
+ * <title>Creating a secrets hash</title>
+ * <programlisting>
+ * NMConnection *secrets;
+ * NMSettingWirelessSecurity *s_wsec;
+ * GHashTable *secrets_hash;
+ *
+ * secrets = nm_connection_new ();
+ * s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new ();
+ * g_object_set (G_OBJECT (s_wsec),
+ * NM_SETTING_WIRELESS_SECURITY_PSK, "my really cool PSK",
+ * NULL);
+ * nm_connection_add_setting (secrets, NM_SETTING (s_wsec));
+ * secrets_hash = nm_connection_to_hash (secrets, NM_SETTING_HASH_FLAG_ALL);
+ *
+ * (call the NMSecretAgentGetSecretsFunc with secrets_hash)
+ *
+ * g_object_unref (secrets);
+ * g_hash_table_unref (secrets_hash);
+ * </programlisting>
+ * </example>
+ */
+typedef void (*NMSecretAgentGetSecretsFunc) (NMSecretAgent *agent,
+ NMConnection *connection,
+ GHashTable *secrets,
+ GError *error,
+ gpointer user_data);
+
+/**
+ * NMSecretAgentSaveSecretsFunc:
+ * @agent: the secret agent object
+ * @connection: (transfer none): the connection for which secrets were to be saved,
+ * note that this object will be unrefed after the callback has returned, use
+ * g_object_ref()/g_object_unref() if you want to use this object after the callback
+ * has returned
+ * @error: if the saving secrets failed, give a descriptive error here
+ * @user_data: caller-specific data to be passed to the function
+ *
+ * Called as a result of a request by NM to save secrets. When the
+ * #NMSecretAgent subclass has finished saving the secrets, this function
+ * should be called.
+ */
+typedef void (*NMSecretAgentSaveSecretsFunc) (NMSecretAgent *agent,
+ NMConnection *connection,
+ GError *error,
+ gpointer user_data);
+
+/**
+ * NMSecretAgentDeleteSecretsFunc:
+ * @agent: the secret agent object
+ * @connection: (transfer none): the connection for which secrets were to be deleted,
+ * note that this object will be unrefed after the callback has returned, use
+ * g_object_ref()/g_object_unref() if you want to use this object after the callback
+ * has returned
+ * @error: if the deleting secrets failed, give a descriptive error here
+ * @user_data: caller-specific data to be passed to the function
+ *
+ * Called as a result of a request by NM to delete secrets. When the
+ * #NMSecretAgent subclass has finished deleting the secrets, this function
+ * should be called.
+ */
+typedef void (*NMSecretAgentDeleteSecretsFunc) (NMSecretAgent *agent,
+ NMConnection *connection,
+ GError *error,
+ gpointer user_data);
+
+typedef struct {
+ GObjectClass parent;
+
+ /* Virtual methods for subclasses */
+
+ /* Called when the subclass should retrieve and return secrets. Subclass
+ * must copy or reference any arguments it may require after returning from
+ * this method, as the arguments will freed (except for 'self', 'callback',
+ * and 'user_data' of course). If the request is canceled, the callback
+ * should still be called, but with the NM_SECRET_AGENT_ERROR_AGENT_CANCELED
+ * error.
+ */
+ void (*get_secrets) (NMSecretAgent *self,
+ NMConnection *connection,
+ const char *connection_path,
+ const char *setting_name,
+ const char **hints,
+ NMSecretAgentGetSecretsFlags flags,
+ NMSecretAgentGetSecretsFunc callback,
+ gpointer user_data);
+
+ /* Called when the subclass should cancel an outstanding request to
+ * get secrets for a given connection. Canceling the request MUST
+ * call the callback that was passed along with the initial get_secrets
+ * call, sending the NM_SECRET_AGENT_ERROR/NM_SECRET_AGENT_ERROR_AGENT_CANCELED
+ * error to that callback.
+ */
+ void (*cancel_get_secrets) (NMSecretAgent *self,
+ const char *connection_path,
+ const char *setting_name);
+
+ /* Called when the subclass should save the secrets contained in the
+ * connection to backing storage. Subclass must copy or reference any
+ * arguments it may require after returning from this method, as the
+ * arguments will freed (except for 'self', 'callback', and 'user_data'
+ * of course).
+ */
+ void (*save_secrets) (NMSecretAgent *self,
+ NMConnection *connection,
+ const char *connection_path,
+ NMSecretAgentSaveSecretsFunc callback,
+ gpointer user_data);
+
+ /* Called when the subclass should delete the secrets contained in the
+ * connection from backing storage. Subclass must copy or reference any
+ * arguments it may require after returning from this method, as the
+ * arguments will freed (except for 'self', 'callback', and 'user_data'
+ * of course).
+ */
+ void (*delete_secrets) (NMSecretAgent *self,
+ NMConnection *connection,
+ const char *connection_path,
+ NMSecretAgentDeleteSecretsFunc callback,
+ gpointer user_data);
+
+ /* Signals */
+ void (*registration_result) (NMSecretAgent *agent, GError *error);
+
+ /* Padding for future expansion */
+ void (*_reserved1) (void);
+ void (*_reserved2) (void);
+ void (*_reserved3) (void);
+ void (*_reserved4) (void);
+ void (*_reserved5) (void);
+ void (*_reserved6) (void);
+} NMSecretAgentClass;
+
+GType nm_secret_agent_get_type (void);
+
+gboolean nm_secret_agent_register (NMSecretAgent *self);
+
+gboolean nm_secret_agent_unregister (NMSecretAgent *self);
+
+gboolean nm_secret_agent_get_registered (NMSecretAgent *self);
+
+void nm_secret_agent_get_secrets (NMSecretAgent *self,
+ NMConnection *connection,
+ const char *setting_name,
+ const char **hints,
+ NMSecretAgentGetSecretsFlags flags,
+ NMSecretAgentGetSecretsFunc callback,
+ gpointer user_data);
+
+void nm_secret_agent_save_secrets (NMSecretAgent *self,
+ NMConnection *connection,
+ NMSecretAgentSaveSecretsFunc callback,
+ gpointer user_data);
+
+void nm_secret_agent_delete_secrets (NMSecretAgent *self,
+ NMConnection *connection,
+ NMSecretAgentDeleteSecretsFunc callback,
+ gpointer user_data);
+
+G_END_DECLS
+
+#endif /* NM_SECRET_AGENT_H */
diff --git a/libnm/nm-types-private.h b/libnm/nm-types-private.h
new file mode 100644
index 0000000000..c34d9a8f7c
--- /dev/null
+++ b/libnm/nm-types-private.h
@@ -0,0 +1,37 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2008 Red Hat, Inc.
+ */
+
+#ifndef NM_TYPES_PRIVATE_H
+#define NM_TYPES_PRIVATE_H
+
+#include <dbus/dbus-glib.h>
+#include "nm-types.h"
+#include "nm-object-private.h"
+
+gboolean _nm_ssid_demarshal (GValue *value, GByteArray **dest);
+gboolean _nm_uint_array_demarshal (GValue *value, GArray **dest);
+gboolean _nm_string_array_demarshal (GValue *value, GPtrArray **dest);
+gboolean _nm_object_array_demarshal (GValue *value,
+ GPtrArray **dest,
+ DBusGConnection *connection,
+ NMObjectCreatorFunc func);
+gboolean _nm_ip6_address_array_demarshal (GValue *value, GSList **dest);
+
+#endif /* NM_TYPES_PRIVATE_H */
diff --git a/libnm/nm-types.c b/libnm/nm-types.c
new file mode 100644
index 0000000000..cb0ff8ba6c
--- /dev/null
+++ b/libnm/nm-types.c
@@ -0,0 +1,419 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2008 Red Hat, Inc.
+ */
+
+#include <glib.h>
+#include <dbus/dbus-glib.h>
+#include <string.h>
+#include "nm-types.h"
+#include "nm-types-private.h"
+#include "nm-object-private.h"
+#include "nm-object-cache.h"
+#include "nm-dbus-glib-types.h"
+#include "nm-setting-ip6-config.h"
+
+static gpointer
+_nm_ssid_copy (GByteArray *src)
+{
+ GByteArray *dest;
+
+ dest = g_byte_array_sized_new (src->len);
+ g_byte_array_append (dest, src->data, src->len);
+ return dest;
+}
+
+static void
+_nm_ssid_free (GByteArray *ssid)
+{
+ g_byte_array_free (ssid, TRUE);
+}
+
+GType
+nm_ssid_get_type (void)
+{
+ static GType our_type = 0;
+
+ if (our_type == 0)
+ our_type = g_boxed_type_register_static (g_intern_static_string ("NMSsid"),
+ (GBoxedCopyFunc) _nm_ssid_copy,
+ (GBoxedFreeFunc) _nm_ssid_free);
+ return our_type;
+}
+
+gboolean
+_nm_ssid_demarshal (GValue *value, GByteArray **dest)
+{
+ GByteArray *array;
+
+ if (!G_VALUE_HOLDS (value, DBUS_TYPE_G_UCHAR_ARRAY))
+ return FALSE;
+
+ if (*dest) {
+ g_boxed_free (NM_TYPE_SSID, *dest);
+ *dest = NULL;
+ }
+
+ array = (GByteArray *) g_value_get_boxed (value);
+ if (array && (array->len > 0)) {
+ *dest = g_byte_array_sized_new (array->len);
+ (*dest)->len = array->len;
+ memcpy ((*dest)->data, array->data, array->len);
+ }
+
+ return TRUE;
+}
+
+/*****************************/
+
+static gpointer
+_nm_uint_array_copy (GArray *src)
+{
+ GArray *dest;
+
+ dest = g_array_sized_new (FALSE, TRUE, sizeof (guint32), src->len);
+ g_array_append_vals (dest, src->data, src->len);
+ return dest;
+}
+
+static void
+_nm_uint_array_free (GArray *array)
+{
+ g_array_free (array, TRUE);
+}
+
+GType
+nm_uint_array_get_type (void)
+{
+ static GType our_type = 0;
+
+ if (our_type == 0)
+ our_type = g_boxed_type_register_static (g_intern_static_string ("NMUintArray"),
+ (GBoxedCopyFunc) _nm_uint_array_copy,
+ (GBoxedFreeFunc) _nm_uint_array_free);
+ return our_type;
+}
+
+gboolean
+_nm_uint_array_demarshal (GValue *value, GArray **dest)
+{
+ GArray *array;
+
+ if (!G_VALUE_HOLDS (value, DBUS_TYPE_G_UINT_ARRAY))
+ return FALSE;
+
+ if (*dest) {
+ g_boxed_free (NM_TYPE_UINT_ARRAY, *dest);
+ *dest = NULL;
+ }
+
+ array = (GArray *) g_value_get_boxed (value);
+ if (array && (array->len > 0)) {
+ *dest = g_array_sized_new (FALSE, TRUE, sizeof (guint32), array->len);
+ g_array_append_vals (*dest, array->data, array->len);
+ }
+
+ return TRUE;
+}
+
+/*****************************/
+
+static gpointer
+_nm_string_array_copy (GPtrArray *src)
+{
+ GPtrArray *dest;
+ int i;
+
+ dest = g_ptr_array_sized_new (src->len);
+ for (i = 0; i < src->len; i++)
+ g_ptr_array_add (dest, g_strdup (g_ptr_array_index (src, i)));
+ return dest;
+}
+
+static void
+_nm_string_array_free (GPtrArray *array)
+{
+ int i;
+
+ for (i = 0; i < array->len; i++)
+ g_free (g_ptr_array_index (array, i));
+ g_ptr_array_free (array, TRUE);
+}
+
+GType
+nm_string_array_get_type (void)
+{
+ static GType our_type = 0;
+
+ if (our_type == 0)
+ our_type = g_boxed_type_register_static (g_intern_static_string ("NMStringArray"),
+ (GBoxedCopyFunc) _nm_string_array_copy,
+ (GBoxedFreeFunc) _nm_string_array_free);
+ return our_type;
+}
+
+gboolean
+_nm_string_array_demarshal (GValue *value, GPtrArray **dest)
+{
+ char **array;
+
+ if (!G_VALUE_HOLDS (value, G_TYPE_STRV))
+ return FALSE;
+
+ if (*dest) {
+ g_boxed_free (NM_TYPE_STRING_ARRAY, *dest);
+ *dest = NULL;
+ }
+
+ array = (char **) g_value_get_boxed (value);
+ if (array && array[0]) {
+ int i;
+
+ *dest = g_ptr_array_new ();
+ for (i = 0; array[i]; i++)
+ g_ptr_array_add (*dest, g_strdup (array[i]));
+ }
+
+ return TRUE;
+}
+
+/*****************************/
+
+static gpointer
+_nm_object_array_copy (GPtrArray *src)
+{
+ GPtrArray *dest;
+ int i;
+
+ dest = g_ptr_array_sized_new (src->len);
+ for (i = 0; i < src->len; i++)
+ g_ptr_array_add (dest, g_object_ref (g_ptr_array_index (src, i)));
+ return dest;
+}
+
+static void
+_nm_object_array_free (GPtrArray *array)
+{
+ int i;
+
+ for (i = 0; i < array->len; i++)
+ g_object_unref (g_ptr_array_index (array, i));
+ g_ptr_array_free (array, TRUE);
+}
+
+GType
+nm_object_array_get_type (void)
+{
+ static GType our_type = 0;
+
+ if (our_type == 0)
+ our_type = g_boxed_type_register_static (g_intern_static_string ("NMObjectArray"),
+ (GBoxedCopyFunc) _nm_object_array_copy,
+ (GBoxedFreeFunc) _nm_object_array_free);
+ return our_type;
+}
+
+gboolean
+_nm_object_array_demarshal (GValue *value,
+ GPtrArray **dest,
+ DBusGConnection *connection,
+ NMObjectCreatorFunc func)
+{
+ GPtrArray *temp = NULL;
+ GPtrArray *array;
+
+ if (!G_VALUE_HOLDS (value, DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH))
+ return FALSE;
+
+ array = (GPtrArray *) g_value_get_boxed (value);
+ if (array && array->len) {
+ int i;
+
+ temp = g_ptr_array_sized_new (array->len);
+ for (i = 0; i < array->len; i++) {
+ const char *path;
+ GObject *object;
+
+ path = g_ptr_array_index (array, i);
+ object = G_OBJECT (_nm_object_cache_get (path));
+ if (object)
+ g_ptr_array_add (temp, object);
+ else {
+ object = (*func) (connection, path);
+ if (object)
+ g_ptr_array_add (temp, object);
+ else
+ g_warning ("%s: couldn't create object for %s", __func__, path);
+ }
+ }
+ } else
+ temp = g_ptr_array_new ();
+
+ /* Deallocate after to ensure that an object that might already
+ * be in the array doesn't get destroyed due to refcounting.
+ */
+ if (*dest)
+ g_boxed_free (NM_TYPE_OBJECT_ARRAY, *dest);
+ *dest = temp;
+
+ return TRUE;
+}
+
+/*****************************/
+
+static gpointer
+_nm_ip6_address_object_array_copy (GPtrArray *src)
+{
+ GPtrArray *dest;
+ int i;
+
+ dest = g_ptr_array_sized_new (src->len);
+ for (i = 0; i < src->len; i++)
+ g_ptr_array_add (dest, nm_ip6_address_dup (g_ptr_array_index (src, i)));
+ return dest;
+}
+
+static void
+_nm_ip6_address_object_array_free (GPtrArray *array)
+{
+ int i;
+
+ for (i = 0; i < array->len; i++)
+ nm_ip6_address_unref (g_ptr_array_index (array, i));
+ g_ptr_array_free (array, TRUE);
+}
+
+GType
+nm_ip6_address_object_array_get_type (void)
+{
+ static GType our_type = 0;
+
+ if (our_type == 0)
+ our_type = g_boxed_type_register_static (g_intern_static_string ("NMIP6AddressObjectArray"),
+ (GBoxedCopyFunc) _nm_ip6_address_object_array_copy,
+ (GBoxedFreeFunc) _nm_ip6_address_object_array_free);
+ return our_type;
+}
+
+/*****************************/
+
+static gpointer
+_nm_ip6_address_array_copy (GPtrArray *src)
+{
+ GPtrArray *dest;
+ int i;
+
+ dest = g_ptr_array_sized_new (src->len);
+ for (i = 0; i < src->len; i++) {
+ struct in6_addr *addr = g_ptr_array_index (src, i);
+ struct in6_addr *copy;
+
+ copy = g_malloc0 (sizeof (struct in6_addr));
+ memcpy (copy, addr, sizeof (struct in6_addr));
+ g_ptr_array_add (dest, copy);
+ }
+ return dest;
+}
+
+static void
+_nm_ip6_address_array_free (GPtrArray *array)
+{
+ int i;
+
+ for (i = 0; i < array->len; i++)
+ g_free (g_ptr_array_index (array, i));
+ g_ptr_array_free (array, TRUE);
+}
+
+GType
+nm_ip6_address_array_get_type (void)
+{
+ static GType our_type = 0;
+
+ if (our_type == 0)
+ our_type = g_boxed_type_register_static (g_intern_static_string ("NMIP6AddressArray"),
+ (GBoxedCopyFunc) _nm_ip6_address_array_copy,
+ (GBoxedFreeFunc) _nm_ip6_address_array_free);
+ return our_type;
+}
+
+gboolean
+_nm_ip6_address_array_demarshal (GValue *value, GSList **dest)
+{
+ GPtrArray *array;
+
+ if (!G_VALUE_HOLDS (value, DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UCHAR))
+ return FALSE;
+
+ if (*dest) {
+ g_slist_free_full (*dest, g_free);
+ *dest = NULL;
+ }
+
+ array = (GPtrArray *) g_value_get_boxed (value);
+ if (array && array->len) {
+ int i;
+
+ for (i = 0; i < array->len; i++) {
+ GByteArray *bytearray = (GByteArray *) g_ptr_array_index (array, i);
+ struct in6_addr *addr;
+
+ addr = g_malloc0 (sizeof (struct in6_addr));
+ memcpy (addr->s6_addr, bytearray->data, bytearray->len);
+ *dest = g_slist_append (*dest, addr);
+ }
+ }
+
+ return TRUE;
+}
+
+/*****************************/
+
+static gpointer
+_nm_ip6_route_object_array_copy (GPtrArray *src)
+{
+ GPtrArray *dest;
+ int i;
+
+ dest = g_ptr_array_sized_new (src->len);
+ for (i = 0; i < src->len; i++)
+ g_ptr_array_add (dest, nm_ip6_route_dup (g_ptr_array_index (src, i)));
+ return dest;
+}
+
+static void
+_nm_ip6_route_object_array_free (GPtrArray *array)
+{
+ int i;
+
+ for (i = 0; i < array->len; i++)
+ nm_ip6_route_unref (g_ptr_array_index (array, i));
+ g_ptr_array_free (array, TRUE);
+}
+
+GType
+nm_ip6_route_object_array_get_type (void)
+{
+ static GType our_type = 0;
+
+ if (our_type == 0)
+ our_type = g_boxed_type_register_static (g_intern_static_string ("NMIP6RouteObjectArray"),
+ (GBoxedCopyFunc) _nm_ip6_route_object_array_copy,
+ (GBoxedFreeFunc) _nm_ip6_route_object_array_free);
+ return our_type;
+}
diff --git a/libnm/nm-types.h b/libnm/nm-types.h
new file mode 100644
index 0000000000..81bd299e69
--- /dev/null
+++ b/libnm/nm-types.h
@@ -0,0 +1,54 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2008 Red Hat, Inc.
+ */
+
+#ifndef NM_TYPES_H
+#define NM_TYPES_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <nm-glib-enum-types.h>
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_SSID (nm_ssid_get_type ())
+GType nm_ssid_get_type (void) G_GNUC_CONST;
+
+#define NM_TYPE_UINT_ARRAY (nm_uint_array_get_type ())
+GType nm_uint_array_get_type (void) G_GNUC_CONST;
+
+#define NM_TYPE_STRING_ARRAY (nm_string_array_get_type ())
+GType nm_string_array_get_type (void) G_GNUC_CONST;
+
+#define NM_TYPE_OBJECT_ARRAY (nm_object_array_get_type ())
+GType nm_object_array_get_type (void) G_GNUC_CONST;
+
+#define NM_TYPE_IP6_ADDRESS_OBJECT_ARRAY (nm_ip6_address_object_array_get_type ())
+GType nm_ip6_address_object_array_get_type (void) G_GNUC_CONST;
+
+#define NM_TYPE_IP6_ADDRESS_ARRAY (nm_ip6_address_array_get_type ())
+GType nm_ip6_address_array_get_type (void) G_GNUC_CONST;
+
+#define NM_TYPE_IP6_ROUTE_OBJECT_ARRAY (nm_ip6_route_object_array_get_type ())
+GType nm_ip6_route_object_array_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* NM_TYPES_H */
diff --git a/libnm/nm-vpn-connection.c b/libnm/nm-vpn-connection.c
new file mode 100644
index 0000000000..689f58fd8d
--- /dev/null
+++ b/libnm/nm-vpn-connection.c
@@ -0,0 +1,268 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2008 Novell, Inc.
+ * Copyright 2007 - 2012 Red Hat, Inc.
+ */
+
+#include <string.h>
+#include "nm-vpn-connection.h"
+#include "NetworkManager.h"
+#include "nm-utils.h"
+#include "nm-object-private.h"
+#include "nm-active-connection.h"
+
+G_DEFINE_TYPE (NMVPNConnection, nm_vpn_connection, NM_TYPE_ACTIVE_CONNECTION)
+
+#define NM_VPN_CONNECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_VPN_CONNECTION, NMVPNConnectionPrivate))
+
+typedef struct {
+ DBusGProxy *proxy;
+ char *banner;
+ NMVPNConnectionState vpn_state;
+} NMVPNConnectionPrivate;
+
+enum {
+ PROP_0,
+ PROP_VPN_STATE,
+ PROP_BANNER,
+
+ LAST_PROP
+};
+
+enum {
+ VPN_STATE_CHANGED,
+
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+
+/**
+ * nm_vpn_connection_new:
+ * @connection: the #DBusGConnection
+ * @path: the DBus object path of the new connection
+ *
+ * Creates a new #NMVPNConnection.
+ *
+ * Returns: (transfer full): a new connection object
+ **/
+GObject *
+nm_vpn_connection_new (DBusGConnection *connection, const char *path)
+{
+ g_return_val_if_fail (connection != NULL, NULL);
+ g_return_val_if_fail (path != NULL, NULL);
+
+ return g_object_new (NM_TYPE_VPN_CONNECTION,
+ NM_OBJECT_DBUS_CONNECTION, connection,
+ NM_OBJECT_DBUS_PATH, path,
+ NULL);
+}
+
+/**
+ * nm_vpn_connection_get_banner:
+ * @vpn: a #NMVPNConnection
+ *
+ * Gets the VPN login banner of the active #NMVPNConnection.
+ *
+ * Returns: the VPN login banner of the VPN connection. This is the internal
+ * string used by the connection, and must not be modified.
+ **/
+const char *
+nm_vpn_connection_get_banner (NMVPNConnection *vpn)
+{
+ NMVPNConnectionPrivate *priv;
+
+ g_return_val_if_fail (NM_IS_VPN_CONNECTION (vpn), NULL);
+
+ priv = NM_VPN_CONNECTION_GET_PRIVATE (vpn);
+
+ /* We need to update vpn_state first in case it's unknown. */
+ _nm_object_ensure_inited (NM_OBJECT (vpn));
+
+ if (priv->vpn_state != NM_VPN_CONNECTION_STATE_ACTIVATED)
+ return NULL;
+
+ return priv->banner;
+}
+
+/**
+ * nm_vpn_connection_get_vpn_state:
+ * @vpn: a #NMVPNConnection
+ *
+ * Gets the current #NMVPNConnection state.
+ *
+ * Returns: the VPN state of the active VPN connection.
+ **/
+NMVPNConnectionState
+nm_vpn_connection_get_vpn_state (NMVPNConnection *vpn)
+{
+ g_return_val_if_fail (NM_IS_VPN_CONNECTION (vpn), NM_VPN_CONNECTION_STATE_UNKNOWN);
+
+ _nm_object_ensure_inited (NM_OBJECT (vpn));
+ return NM_VPN_CONNECTION_GET_PRIVATE (vpn)->vpn_state;
+}
+
+static void
+vpn_state_changed_proxy (DBusGProxy *proxy,
+ NMVPNConnectionState vpn_state,
+ NMVPNConnectionStateReason reason,
+ gpointer user_data)
+{
+ NMVPNConnection *connection = NM_VPN_CONNECTION (user_data);
+ NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
+
+ if (priv->vpn_state != vpn_state) {
+ priv->vpn_state = vpn_state;
+ g_signal_emit (connection, signals[VPN_STATE_CHANGED], 0, vpn_state, reason);
+ g_object_notify (G_OBJECT (connection), NM_VPN_CONNECTION_VPN_STATE);
+ }
+}
+
+/*****************************************************************************/
+
+static void
+nm_vpn_connection_init (NMVPNConnection *connection)
+{
+ NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
+
+ priv->vpn_state = NM_VPN_CONNECTION_STATE_UNKNOWN;
+}
+
+static void
+register_properties (NMVPNConnection *connection)
+{
+ NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
+ const NMPropertiesInfo property_info[] = {
+ { NM_VPN_CONNECTION_BANNER, &priv->banner },
+ { NM_VPN_CONNECTION_VPN_STATE, &priv->vpn_state },
+ { NULL },
+ };
+
+ _nm_object_register_properties (NM_OBJECT (connection),
+ priv->proxy,
+ property_info);
+}
+
+static void
+constructed (GObject *object)
+{
+ NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (object);
+
+ G_OBJECT_CLASS (nm_vpn_connection_parent_class)->constructed (object);
+
+ priv->proxy = _nm_object_new_proxy (NM_OBJECT (object), NULL, NM_DBUS_INTERFACE_VPN_CONNECTION);
+
+ dbus_g_object_register_marshaller (g_cclosure_marshal_generic,
+ G_TYPE_NONE,
+ G_TYPE_UINT, G_TYPE_UINT,
+ G_TYPE_INVALID);
+ dbus_g_proxy_add_signal (priv->proxy, "VpnStateChanged", G_TYPE_UINT, G_TYPE_UINT, G_TYPE_INVALID);
+ dbus_g_proxy_connect_signal (priv->proxy,
+ "VpnStateChanged",
+ G_CALLBACK (vpn_state_changed_proxy),
+ object,
+ NULL);
+
+ register_properties (NM_VPN_CONNECTION (object));
+}
+
+static void
+finalize (GObject *object)
+{
+ NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (object);
+
+ g_free (priv->banner);
+ g_object_unref (priv->proxy);
+
+ G_OBJECT_CLASS (nm_vpn_connection_parent_class)->finalize (object);
+}
+
+static void
+get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NMVPNConnection *self = NM_VPN_CONNECTION (object);
+
+ _nm_object_ensure_inited (NM_OBJECT (object));
+
+ switch (prop_id) {
+ case PROP_VPN_STATE:
+ g_value_set_uint (value, nm_vpn_connection_get_vpn_state (self));
+ break;
+ case PROP_BANNER:
+ g_value_set_string (value, nm_vpn_connection_get_banner (self));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nm_vpn_connection_class_init (NMVPNConnectionClass *connection_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (connection_class);
+
+ g_type_class_add_private (connection_class, sizeof (NMVPNConnectionPrivate));
+
+ /* virtual methods */
+ object_class->constructed = constructed;
+ object_class->get_property = get_property;
+ object_class->finalize = finalize;
+
+ /* properties */
+
+ /**
+ * NMVPNConnection:vpn-state:
+ *
+ * The VPN state of the active VPN connection.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_VPN_STATE,
+ g_param_spec_uint (NM_VPN_CONNECTION_VPN_STATE, "", "",
+ NM_VPN_CONNECTION_STATE_UNKNOWN,
+ NM_VPN_CONNECTION_STATE_DISCONNECTED,
+ NM_VPN_CONNECTION_STATE_UNKNOWN,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMVPNConnection:banner:
+ *
+ * The VPN login banner of the active VPN connection.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_BANNER,
+ g_param_spec_string (NM_VPN_CONNECTION_BANNER, "", "",
+ NULL,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /* signals */
+ signals[VPN_STATE_CHANGED] =
+ g_signal_new ("vpn-state-changed",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NMVPNConnectionClass, vpn_state_changed),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 2,
+ G_TYPE_UINT, G_TYPE_UINT);
+}
diff --git a/libnm/nm-vpn-connection.h b/libnm/nm-vpn-connection.h
new file mode 100644
index 0000000000..0495310f38
--- /dev/null
+++ b/libnm/nm-vpn-connection.h
@@ -0,0 +1,73 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2008 Novell, Inc.
+ * Copyright 2007 - 2010 Red Hat, Inc.
+ */
+
+#ifndef NM_VPN_CONNECTION_H
+#define NM_VPN_CONNECTION_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <dbus/dbus-glib.h>
+#include "nm-active-connection.h"
+#include "NetworkManagerVPN.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_VPN_CONNECTION (nm_vpn_connection_get_type ())
+#define NM_VPN_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_VPN_CONNECTION, NMVPNConnection))
+#define NM_VPN_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_VPN_CONNECTION, NMVPNConnectionClass))
+#define NM_IS_VPN_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_VPN_CONNECTION))
+#define NM_IS_VPN_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_VPN_CONNECTION))
+#define NM_VPN_CONNECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_VPN_CONNECTION, NMVPNConnectionClass))
+
+#define NM_VPN_CONNECTION_VPN_STATE "vpn-state"
+#define NM_VPN_CONNECTION_BANNER "banner"
+
+typedef struct {
+ NMActiveConnection parent;
+} NMVPNConnection;
+
+typedef struct {
+ NMActiveConnectionClass parent;
+
+ /* Signals */
+ void (*vpn_state_changed) (NMVPNConnection *connection,
+ NMVPNConnectionState state,
+ NMVPNConnectionStateReason reason);
+
+ /* Padding for future expansion */
+ void (*_reserved1) (void);
+ void (*_reserved2) (void);
+ void (*_reserved3) (void);
+ void (*_reserved4) (void);
+ void (*_reserved5) (void);
+ void (*_reserved6) (void);
+} NMVPNConnectionClass;
+
+GType nm_vpn_connection_get_type (void);
+
+GObject * nm_vpn_connection_new (DBusGConnection *connection, const char *path);
+
+NMVPNConnectionState nm_vpn_connection_get_vpn_state (NMVPNConnection *vpn);
+const char * nm_vpn_connection_get_banner (NMVPNConnection *vpn);
+
+G_END_DECLS
+
+#endif /* NM_VPN_CONNECTION_H */
diff --git a/libnm/nm-vpn-plugin-ui-interface.c b/libnm/nm-vpn-plugin-ui-interface.c
new file mode 100644
index 0000000000..a4a10836e8
--- /dev/null
+++ b/libnm/nm-vpn-plugin-ui-interface.c
@@ -0,0 +1,247 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2008 - 2010 Red Hat, Inc.
+ * Copyright 2008 Novell, Inc.
+ */
+
+#include "nm-vpn-plugin-ui-interface.h"
+
+static void
+interface_init (gpointer g_iface)
+{
+ static gboolean initialized = FALSE;
+
+ if (initialized)
+ return;
+
+ /* Properties */
+
+ /**
+ * NMVPNPluginUiInterface:name:
+ *
+ * Short display name of the VPN plugin.
+ */
+ g_object_interface_install_property (g_iface,
+ g_param_spec_string (NM_VPN_PLUGIN_UI_INTERFACE_NAME, "", "",
+ NULL,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMVPNPluginUiInterface:desc:
+ *
+ * Longer description of the VPN plugin.
+ */
+ g_object_interface_install_property (g_iface,
+ g_param_spec_string (NM_VPN_PLUGIN_UI_INTERFACE_DESC, "", "",
+ NULL,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMVPNPluginUiInterface:service:
+ *
+ * D-Bus service name of the plugin's VPN service.
+ */
+ g_object_interface_install_property (g_iface,
+ g_param_spec_string (NM_VPN_PLUGIN_UI_INTERFACE_SERVICE, "", "",
+ NULL,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ initialized = TRUE;
+}
+
+
+GType
+nm_vpn_plugin_ui_interface_get_type (void)
+{
+ static GType vpn_plugin_ui_interface_type = 0;
+
+ if (!vpn_plugin_ui_interface_type) {
+ const GTypeInfo vpn_plugin_ui_interface_info = {
+ sizeof (NMVpnPluginUiInterface), /* class_size */
+ interface_init, /* base_init */
+ NULL, /* base_finalize */
+ NULL,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ 0,
+ 0, /* n_preallocs */
+ NULL
+ };
+
+ vpn_plugin_ui_interface_type = g_type_register_static (G_TYPE_INTERFACE,
+ "NMVpnPluginUiInterface",
+ &vpn_plugin_ui_interface_info,
+ 0);
+
+ g_type_interface_add_prerequisite (vpn_plugin_ui_interface_type, G_TYPE_OBJECT);
+ }
+
+ return vpn_plugin_ui_interface_type;
+}
+
+
+NMVpnPluginUiWidgetInterface *
+nm_vpn_plugin_ui_interface_ui_factory (NMVpnPluginUiInterface *iface,
+ NMConnection *connection,
+ GError **error)
+{
+ g_return_val_if_fail (NM_IS_VPN_PLUGIN_UI_INTERFACE (iface), NULL);
+
+ return NM_VPN_PLUGIN_UI_INTERFACE_GET_INTERFACE (iface)->ui_factory (iface, connection, error);
+}
+
+guint32
+nm_vpn_plugin_ui_interface_get_capabilities (NMVpnPluginUiInterface *iface)
+{
+ g_return_val_if_fail (NM_IS_VPN_PLUGIN_UI_INTERFACE (iface), 0);
+
+ return NM_VPN_PLUGIN_UI_INTERFACE_GET_INTERFACE (iface)->get_capabilities (iface);
+}
+
+NMConnection *
+nm_vpn_plugin_ui_interface_import (NMVpnPluginUiInterface *iface,
+ const char *path,
+ GError **error)
+{
+ g_return_val_if_fail (NM_IS_VPN_PLUGIN_UI_INTERFACE (iface), NULL);
+
+ if (nm_vpn_plugin_ui_interface_get_capabilities (iface) & NM_VPN_PLUGIN_UI_CAPABILITY_IMPORT) {
+ g_return_val_if_fail (NM_VPN_PLUGIN_UI_INTERFACE_GET_INTERFACE (iface)->import_from_file != NULL, NULL);
+ return NM_VPN_PLUGIN_UI_INTERFACE_GET_INTERFACE (iface)->import_from_file (iface, path, error);
+ }
+ return NULL;
+}
+
+gboolean
+nm_vpn_plugin_ui_interface_export (NMVpnPluginUiInterface *iface,
+ const char *path,
+ NMConnection *connection,
+ GError **error)
+{
+ g_return_val_if_fail (NM_IS_VPN_PLUGIN_UI_INTERFACE (iface), FALSE);
+
+ if (nm_vpn_plugin_ui_interface_get_capabilities (iface) & NM_VPN_PLUGIN_UI_CAPABILITY_EXPORT) {
+ g_return_val_if_fail (NM_VPN_PLUGIN_UI_INTERFACE_GET_INTERFACE (iface)->export_to_file != NULL, FALSE);
+ return NM_VPN_PLUGIN_UI_INTERFACE_GET_INTERFACE (iface)->export_to_file (iface, path, connection, error);
+ }
+ return FALSE;
+}
+
+char *
+nm_vpn_plugin_ui_interface_get_suggested_name (NMVpnPluginUiInterface *iface,
+ NMConnection *connection)
+{
+ g_return_val_if_fail (NM_IS_VPN_PLUGIN_UI_INTERFACE (iface), NULL);
+
+ if (NM_VPN_PLUGIN_UI_INTERFACE_GET_INTERFACE (iface)->get_suggested_name)
+ return NM_VPN_PLUGIN_UI_INTERFACE_GET_INTERFACE (iface)->get_suggested_name (iface, connection);
+ return NULL;
+}
+
+gboolean
+nm_vpn_plugin_ui_interface_delete_connection (NMVpnPluginUiInterface *iface,
+ NMConnection *connection,
+ GError **error)
+{
+ /* Deprecated and no longer used */
+ return TRUE;
+}
+
+
+static void
+widget_interface_init (gpointer g_iface)
+{
+ GType iface_type = G_TYPE_FROM_INTERFACE (g_iface);
+ static gboolean initialized = FALSE;
+
+ if (initialized)
+ return;
+
+ /* Signals */
+ g_signal_new ("changed",
+ iface_type,
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NMVpnPluginUiWidgetInterface, changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ initialized = TRUE;
+}
+
+GType
+nm_vpn_plugin_ui_widget_interface_get_type (void)
+{
+ static GType vpn_plugin_ui_widget_interface_type = 0;
+
+ if (!vpn_plugin_ui_widget_interface_type) {
+ const GTypeInfo vpn_plugin_ui_widget_interface_info = {
+ sizeof (NMVpnPluginUiWidgetInterface), /* class_size */
+ widget_interface_init, /* base_init */
+ NULL, /* base_finalize */
+ NULL,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ 0,
+ 0, /* n_preallocs */
+ NULL
+ };
+
+ vpn_plugin_ui_widget_interface_type = g_type_register_static (G_TYPE_INTERFACE,
+ "NMVpnPluginUiWidgetInterface",
+ &vpn_plugin_ui_widget_interface_info,
+ 0);
+
+ g_type_interface_add_prerequisite (vpn_plugin_ui_widget_interface_type, G_TYPE_OBJECT);
+ }
+
+ return vpn_plugin_ui_widget_interface_type;
+}
+
+GObject *
+nm_vpn_plugin_ui_widget_interface_get_widget (NMVpnPluginUiWidgetInterface *iface)
+{
+ g_return_val_if_fail (NM_IS_VPN_PLUGIN_UI_WIDGET_INTERFACE (iface), NULL);
+
+ return NM_VPN_PLUGIN_UI_WIDGET_INTERFACE_GET_INTERFACE (iface)->get_widget (iface);
+}
+
+gboolean
+nm_vpn_plugin_ui_widget_interface_update_connection (NMVpnPluginUiWidgetInterface *iface,
+ NMConnection *connection,
+ GError **error)
+{
+ g_return_val_if_fail (NM_IS_VPN_PLUGIN_UI_WIDGET_INTERFACE (iface), FALSE);
+
+ if (error)
+ g_return_val_if_fail (*error == NULL, FALSE);
+
+ return NM_VPN_PLUGIN_UI_WIDGET_INTERFACE_GET_INTERFACE (iface)->update_connection (iface, connection, error);
+}
+
+gboolean
+nm_vpn_plugin_ui_widget_interface_save_secrets (NMVpnPluginUiWidgetInterface *iface,
+ NMConnection *connection,
+ GError **error)
+{
+ /* Deprecated and no longer used */
+ return TRUE;
+}
diff --git a/libnm/nm-vpn-plugin-ui-interface.h b/libnm/nm-vpn-plugin-ui-interface.h
new file mode 100644
index 0000000000..9c6e49ebe6
--- /dev/null
+++ b/libnm/nm-vpn-plugin-ui-interface.h
@@ -0,0 +1,229 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2008 - 2010 Red Hat, Inc.
+ * Copyright 2008 Novell, Inc.
+ */
+
+#ifndef NM_VPN_PLUGIN_UI_INTERFACE_H
+#define NM_VPN_PLUGIN_UI_INTERFACE_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <nm-connection.h>
+
+G_BEGIN_DECLS
+
+typedef struct _NMVpnPluginUiInterface NMVpnPluginUiInterface;
+typedef struct _NMVpnPluginUiWidgetInterface NMVpnPluginUiWidgetInterface;
+
+/* Plugin's factory function that returns a GObject that implements
+ * NMVpnPluginUiInterface.
+ */
+typedef NMVpnPluginUiInterface * (*NMVpnPluginUiFactory) (GError **error);
+NMVpnPluginUiInterface *nm_vpn_plugin_ui_factory (GError **error);
+
+
+/**************************************************/
+/* Plugin interface */
+/**************************************************/
+
+#define NM_TYPE_VPN_PLUGIN_UI_INTERFACE (nm_vpn_plugin_ui_interface_get_type ())
+#define NM_VPN_PLUGIN_UI_INTERFACE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_VPN_PLUGIN_UI_INTERFACE, NMVpnPluginUiInterface))
+#define NM_IS_VPN_PLUGIN_UI_INTERFACE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_VPN_PLUGIN_UI_INTERFACE))
+#define NM_VPN_PLUGIN_UI_INTERFACE_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), NM_TYPE_VPN_PLUGIN_UI_INTERFACE, NMVpnPluginUiInterface))
+
+/**
+ * NMVpnPluginUiCapability:
+ * @NM_VPN_PLUGIN_UI_CAPABILITY_NONE: unknown or no capability
+ * @NM_VPN_PLUGIN_UI_CAPABILITY_IMPORT: the plugin can import new connections
+ * @NM_VPN_PLUGIN_UI_CAPABILITY_EXPORT: the plugin can export connections
+ * @NM_VPN_PLUGIN_UI_CAPABILITY_IPV6: the plugin supports IPv6 addressing
+ *
+ * Flags that indicate to UI programs certain capabilities of the plugin.
+ **/
+typedef enum /*< flags >*/ {
+ NM_VPN_PLUGIN_UI_CAPABILITY_NONE = 0x00,
+ NM_VPN_PLUGIN_UI_CAPABILITY_IMPORT = 0x01,
+ NM_VPN_PLUGIN_UI_CAPABILITY_EXPORT = 0x02,
+ NM_VPN_PLUGIN_UI_CAPABILITY_IPV6 = 0x04
+} NMVpnPluginUiCapability;
+
+/* Short display name of the VPN plugin */
+#define NM_VPN_PLUGIN_UI_INTERFACE_NAME "name"
+
+/* Longer description of the VPN plugin */
+#define NM_VPN_PLUGIN_UI_INTERFACE_DESC "desc"
+
+/* D-Bus service name of the plugin's VPN service */
+#define NM_VPN_PLUGIN_UI_INTERFACE_SERVICE "service"
+
+/**
+ * NMVpnPluginUiInterfaceProp:
+ * @NM_VPN_PLUGIN_UI_INTERFACE_PROP_NAME: the VPN plugin's name
+ * @NM_VPN_PLUGIN_UI_INTERFACE_PROP_DESC: description of the VPN plugin and what
+ * VPN services it supports
+ * @NM_VPN_PLUGIN_UI_INTERFACE_PROP_SERVICE: the D-Bus service name used by the
+ * plugin's VPN service daemon
+ *
+ * #GObject property numbers that plugins should override to provide certain
+ * information to UI programs.
+ **/
+typedef enum {
+ /* private */
+ NM_VPN_PLUGIN_UI_INTERFACE_PROP_FIRST = 0x1000,
+
+ /* public */
+ NM_VPN_PLUGIN_UI_INTERFACE_PROP_NAME = NM_VPN_PLUGIN_UI_INTERFACE_PROP_FIRST,
+ NM_VPN_PLUGIN_UI_INTERFACE_PROP_DESC,
+ NM_VPN_PLUGIN_UI_INTERFACE_PROP_SERVICE
+} NMVpnPluginUiInterfaceProp;
+
+
+struct _NMVpnPluginUiInterface {
+ GTypeInterface g_iface;
+
+ /* Plugin's factory function that returns a GObject that implements
+ * NMVpnPluginUiWidgetInterface, pre-filled with values from 'connection'
+ * if non-NULL.
+ */
+ NMVpnPluginUiWidgetInterface * (*ui_factory) (NMVpnPluginUiInterface *iface,
+ NMConnection *connection,
+ GError **error);
+
+ /* Plugin's capabiltity function that returns a bitmask of capabilities
+ * described by NM_VPN_PLUGIN_UI_CAPABILITY_* defines.
+ */
+ guint32 (*get_capabilities) (NMVpnPluginUiInterface *iface);
+
+ /* Try to import a connection from the specified path. On success, return a
+ * partial NMConnection object. On error, return NULL and set 'error' with
+ * additional information. Note that 'error' can be NULL, in which case no
+ * additional error information should be provided.
+ */
+ NMConnection * (*import_from_file) (NMVpnPluginUiInterface *iface,
+ const char *path,
+ GError **error);
+
+ /* Export the given connection to the specified path. Return TRUE on success.
+ * On error, return FALSE and set 'error' with additional error information.
+ * Note that 'error' can be NULL, in which case no additional error information
+ * should be provided.
+ */
+ gboolean (*export_to_file) (NMVpnPluginUiInterface *iface,
+ const char *path,
+ NMConnection *connection,
+ GError **error);
+
+ /* For a given connection, return a suggested file name. Returned value should
+ * be NULL or a suggested file name allocated via g_malloc/g_new/etc to be freed
+ * by the caller.
+ */
+ char * (*get_suggested_name) (NMVpnPluginUiInterface *iface, NMConnection *connection);
+
+ /* Deprecated and no longer used */
+ gboolean (*delete_connection) (NMVpnPluginUiInterface *iface, NMConnection *connection, GError **error);
+
+ /* Padding for future expansion */
+ void (*_reserved1) (void);
+ void (*_reserved2) (void);
+ void (*_reserved3) (void);
+ void (*_reserved4) (void);
+ void (*_reserved5) (void);
+ void (*_reserved6) (void);
+};
+
+GType nm_vpn_plugin_ui_interface_get_type (void);
+
+NMVpnPluginUiWidgetInterface *nm_vpn_plugin_ui_interface_ui_factory (NMVpnPluginUiInterface *iface,
+ NMConnection *connection,
+ GError **error);
+
+guint32 nm_vpn_plugin_ui_interface_get_capabilities (NMVpnPluginUiInterface *iface);
+
+NMConnection *nm_vpn_plugin_ui_interface_import (NMVpnPluginUiInterface *iface,
+ const char *path,
+ GError **error);
+
+gboolean nm_vpn_plugin_ui_interface_export (NMVpnPluginUiInterface *iface,
+ const char *path,
+ NMConnection *connection,
+ GError **error);
+
+char *nm_vpn_plugin_ui_interface_get_suggested_name (NMVpnPluginUiInterface *iface,
+ NMConnection *connection);
+
+/* Deprecated and no longer used */
+NM_DEPRECATED_IN_0_9_10
+gboolean nm_vpn_plugin_ui_interface_delete_connection (NMVpnPluginUiInterface *iface,
+ NMConnection *connection,
+ GError **error);
+
+
+/**************************************************/
+/* UI widget interface */
+/**************************************************/
+
+#define NM_TYPE_VPN_PLUGIN_UI_WIDGET_INTERFACE (nm_vpn_plugin_ui_widget_interface_get_type ())
+#define NM_VPN_PLUGIN_UI_WIDGET_INTERFACE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_VPN_PLUGIN_UI_WIDGET_INTERFACE, NMVpnPluginUiWidgetInterface))
+#define NM_IS_VPN_PLUGIN_UI_WIDGET_INTERFACE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_VPN_PLUGIN_UI_WIDGET_INTERFACE))
+#define NM_VPN_PLUGIN_UI_WIDGET_INTERFACE_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), NM_TYPE_VPN_PLUGIN_UI_WIDGET_INTERFACE, NMVpnPluginUiWidgetInterface))
+
+struct _NMVpnPluginUiWidgetInterface {
+ GTypeInterface g_iface;
+
+ /* Return the GtkWidget for the VPN's UI */
+ GObject * (*get_widget) (NMVpnPluginUiWidgetInterface *iface);
+
+ /* Called to save the user-entered options to the connection object. Should
+ * return FALSE and set 'error' if the current options are invalid. 'error'
+ * should contain enough information for the plugin to determine which UI
+ * widget is invalid at a later point in time. For example, creating unique
+ * error codes for what error occurred and populating the message field
+ * of 'error' with the name of the invalid property.
+ */
+ gboolean (*update_connection) (NMVpnPluginUiWidgetInterface *iface,
+ NMConnection *connection,
+ GError **error);
+
+ /* Deprecated and no longer used */
+ gboolean (*save_secrets) (NMVpnPluginUiWidgetInterface *iface,
+ NMConnection *connection,
+ GError **error);
+
+ /* Emitted when the value of a UI widget changes. May trigger a validity
+ * check via update_connection() to write values to the connection */
+ void (*changed) (NMVpnPluginUiWidgetInterface *iface);
+};
+
+GType nm_vpn_plugin_ui_widget_interface_get_type (void);
+
+GObject * nm_vpn_plugin_ui_widget_interface_get_widget (NMVpnPluginUiWidgetInterface *iface);
+
+gboolean nm_vpn_plugin_ui_widget_interface_update_connection (NMVpnPluginUiWidgetInterface *iface,
+ NMConnection *connection,
+ GError **error);
+
+/* Deprecated and no longer used */
+NM_DEPRECATED_IN_0_9_10
+gboolean nm_vpn_plugin_ui_widget_interface_save_secrets (NMVpnPluginUiWidgetInterface *iface,
+ NMConnection *connection,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* NM_VPN_PLUGIN_UI_INTERFACE_H */
diff --git a/libnm/nm-vpn-plugin-utils.c b/libnm/nm-vpn-plugin-utils.c
new file mode 100644
index 0000000000..0e800e9c16
--- /dev/null
+++ b/libnm/nm-vpn-plugin-utils.c
@@ -0,0 +1,189 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2011 Red Hat, Inc.
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "nm-vpn-plugin-utils.h"
+#include "nm-vpn-plugin.h"
+#include "nm-setting-private.h"
+#include "nm-dbus-glib-types.h"
+
+#define DATA_KEY_TAG "DATA_KEY="
+#define DATA_VAL_TAG "DATA_VAL="
+#define SECRET_KEY_TAG "SECRET_KEY="
+#define SECRET_VAL_TAG "SECRET_VAL="
+
+static void
+free_secret (gpointer data)
+{
+ char *secret = data;
+
+ memset (secret, 0, strlen (secret));
+ g_free (secret);
+}
+
+/**
+ * nm_vpn_plugin_utils_read_vpn_details:
+ * @fd: file descriptor to read from, usually stdin (0)
+ * @out_data: (out) (transfer full): on successful return, a hash table
+ * (mapping char*:char*) containing the key/value pairs of VPN data items
+ * @out_secrets: (out) (transfer full): on successful return, a hash table
+ * (mapping char*:char*) containing the key/value pairsof VPN secrets
+ *
+ * Parses key/value pairs from a file descriptor (normally stdin) passed by
+ * an applet when the applet calls the authentication dialog of the VPN plugin.
+ *
+ * Returns: %TRUE if reading values was successful, %FALSE if not
+ **/
+gboolean
+nm_vpn_plugin_utils_read_vpn_details (int fd,
+ GHashTable **out_data,
+ GHashTable **out_secrets)
+{
+ GHashTable *data, *secrets;
+ gboolean success = FALSE;
+ char *key = NULL, *val = NULL;
+ GString *line;
+ gchar c;
+
+ if (out_data)
+ g_return_val_if_fail (*out_data == NULL, FALSE);
+ if (out_secrets)
+ g_return_val_if_fail (*out_secrets == NULL, FALSE);
+
+ data = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+ secrets = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, free_secret);
+
+ line = g_string_new (NULL);
+
+ /* Read stdin for data and secret items until we get a DONE */
+ while (1) {
+ ssize_t nr;
+ GHashTable *hash = NULL;
+
+ errno = 0;
+ nr = read (fd, &c, 1);
+ if (nr == -1) {
+ if (errno == EAGAIN) {
+ g_usleep (100);
+ continue;
+ }
+ break;
+ }
+
+ if (c != '\n') {
+ g_string_append_c (line, c);
+ continue;
+ }
+
+ /* Check for the finish marker */
+ if (strcmp (line->str, "DONE") == 0)
+ break;
+
+ /* Otherwise it's a data/secret item */
+ if (strncmp (line->str, DATA_KEY_TAG, strlen (DATA_KEY_TAG)) == 0) {
+ hash = data;
+ key = g_strdup (line->str + strlen (DATA_KEY_TAG));
+ } else if (strncmp (line->str, DATA_VAL_TAG, strlen (DATA_VAL_TAG)) == 0) {
+ hash = data;
+ val = g_strdup (line->str + strlen (DATA_VAL_TAG));
+ } else if (strncmp (line->str, SECRET_KEY_TAG, strlen (SECRET_KEY_TAG)) == 0) {
+ hash = secrets;
+ key = g_strdup (line->str + strlen (SECRET_KEY_TAG));
+ } else if (strncmp (line->str, SECRET_VAL_TAG, strlen (SECRET_VAL_TAG)) == 0) {
+ hash = secrets;
+ val = g_strdup (line->str + strlen (SECRET_VAL_TAG));
+ }
+ g_string_truncate (line, 0);
+
+ if (key && val && hash) {
+ g_hash_table_insert (hash, key, val);
+ key = NULL;
+ val = NULL;
+ success = TRUE; /* Got at least one value */
+ }
+ }
+
+ if (success) {
+ if (out_data)
+ *out_data = data;
+ else
+ g_hash_table_destroy (data);
+
+ if (out_secrets)
+ *out_secrets = secrets;
+ else
+ g_hash_table_destroy (secrets);
+ } else {
+ g_hash_table_destroy (data);
+ g_hash_table_destroy (secrets);
+ }
+
+ g_string_free (line, TRUE);
+ return success;
+}
+
+/**
+ * nm_vpn_plugin_utils_get_secret_flags:
+ * @data: hash table containing VPN key/value pair data items
+ * @secret_name: VPN secret key name for which to retrieve flags for
+ * @out_flags: (out): on success, the flags associated with @secret_name
+ *
+ * Given a VPN secret key name, attempts to find the corresponding flags data
+ * item in @data. If found, converts the flags data item to
+ * #NMSettingSecretFlags and returns it.
+ *
+ * Returns: %TRUE if the flag data item was found and successfully converted
+ * to flags, %FALSE if not
+ **/
+gboolean
+nm_vpn_plugin_utils_get_secret_flags (GHashTable *data,
+ const char *secret_name,
+ NMSettingSecretFlags *out_flags)
+{
+ char *flag_name;
+ const char *val;
+ unsigned long tmp;
+ gboolean success = FALSE;
+
+ g_return_val_if_fail (data != NULL, FALSE);
+ g_return_val_if_fail (secret_name != NULL, FALSE);
+ g_return_val_if_fail (out_flags != NULL, FALSE);
+ g_return_val_if_fail (*out_flags == NM_SETTING_SECRET_FLAG_NONE, FALSE);
+
+ flag_name = g_strdup_printf ("%s-flags", secret_name);
+
+ /* Try new flags value first */
+ val = g_hash_table_lookup (data, flag_name);
+ if (val) {
+ errno = 0;
+ tmp = strtoul (val, NULL, 10);
+ if (errno == 0 && tmp <= NM_SETTING_SECRET_FLAGS_ALL) {
+ *out_flags = (NMSettingSecretFlags) tmp;
+ success = TRUE;
+ }
+ }
+
+ g_free (flag_name);
+ return success;
+}
diff --git a/libnm/nm-vpn-plugin-utils.h b/libnm/nm-vpn-plugin-utils.h
new file mode 100644
index 0000000000..d87ef16aa4
--- /dev/null
+++ b/libnm/nm-vpn-plugin-utils.h
@@ -0,0 +1,39 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2011 Red Hat, Inc.
+ */
+
+#ifndef NM_VPN_PLUGIN_UTILS_H
+#define NM_VPN_PLUGIN_UTILS_H
+
+#include <glib.h>
+#include <nm-setting.h>
+
+G_BEGIN_DECLS
+
+gboolean nm_vpn_plugin_utils_read_vpn_details (int fd,
+ GHashTable **out_data,
+ GHashTable **out_secrets);
+
+gboolean nm_vpn_plugin_utils_get_secret_flags (GHashTable *data,
+ const char *secret_name,
+ NMSettingSecretFlags *out_flags);
+
+G_END_DECLS
+
+#endif /* NM_VPN_PLUGIN_UTILS_H */
diff --git a/libnm/nm-vpn-plugin.c b/libnm/nm-vpn-plugin.c
new file mode 100644
index 0000000000..08fb647ada
--- /dev/null
+++ b/libnm/nm-vpn-plugin.c
@@ -0,0 +1,1066 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2008 Novell, Inc.
+ * Copyright 2007 - 2008 Red Hat, Inc.
+ */
+
+#include <signal.h>
+#include "nm-glib-compat.h"
+#include "nm-vpn-plugin.h"
+#include "nm-vpn-enum-types.h"
+#include "nm-utils.h"
+#include "nm-connection.h"
+#include "nm-dbus-glib-types.h"
+
+static gboolean impl_vpn_plugin_connect (NMVPNPlugin *plugin,
+ GHashTable *connection,
+ GError **error);
+
+static gboolean impl_vpn_plugin_connect_interactive (NMVPNPlugin *plugin,
+ GHashTable *connection,
+ GHashTable *details,
+ GError **error);
+
+static gboolean impl_vpn_plugin_need_secrets (NMVPNPlugin *plugin,
+ GHashTable *connection,
+ char **service_name,
+ GError **err);
+
+static gboolean impl_vpn_plugin_new_secrets (NMVPNPlugin *plugin,
+ GHashTable *connection,
+ GError **err);
+
+static gboolean impl_vpn_plugin_disconnect (NMVPNPlugin *plugin,
+ GError **err);
+
+static gboolean impl_vpn_plugin_set_config (NMVPNPlugin *plugin,
+ GHashTable *config,
+ GError **err);
+
+static gboolean impl_vpn_plugin_set_ip4_config (NMVPNPlugin *plugin,
+ GHashTable *config,
+ GError **err);
+
+static gboolean impl_vpn_plugin_set_ip6_config (NMVPNPlugin *plugin,
+ GHashTable *config,
+ GError **err);
+
+static gboolean impl_vpn_plugin_set_failure (NMVPNPlugin *plugin,
+ char *reason,
+ GError **err);
+
+#include "nm-vpn-plugin-glue.h"
+
+#define NM_VPN_PLUGIN_QUIT_TIMER 20
+
+G_DEFINE_ABSTRACT_TYPE (NMVPNPlugin, nm_vpn_plugin, G_TYPE_OBJECT)
+
+typedef struct {
+ NMVPNServiceState state;
+
+ /* DBUS-y stuff */
+ DBusGConnection *connection;
+ char *dbus_service_name;
+
+ /* Temporary stuff */
+ guint connect_timer;
+ guint quit_timer;
+ guint fail_stop_id;
+ gboolean interactive;
+
+ gboolean got_config;
+ gboolean has_ip4, got_ip4;
+ gboolean has_ip6, got_ip6;
+
+ /* Config stuff copied from config to ip4config */
+ GValue banner, tundev, gateway, mtu;
+} NMVPNPluginPrivate;
+
+#define NM_VPN_PLUGIN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_VPN_PLUGIN, NMVPNPluginPrivate))
+
+enum {
+ STATE_CHANGED,
+ CONFIG,
+ IP4_CONFIG,
+ IP6_CONFIG,
+ LOGIN_BANNER,
+ FAILURE,
+ QUIT,
+ SECRETS_REQUIRED,
+
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+enum {
+ PROP_0,
+ PROP_DBUS_SERVICE_NAME,
+ PROP_STATE,
+
+ LAST_PROP
+};
+
+static GSList *active_plugins = NULL;
+
+
+GQuark
+nm_vpn_plugin_error_quark (void)
+{
+ static GQuark quark = 0;
+
+ if (!quark)
+ quark = g_quark_from_static_string ("nm_vpn_plugin_error");
+
+ return quark;
+}
+
+
+static void
+nm_vpn_plugin_set_connection (NMVPNPlugin *plugin,
+ DBusGConnection *connection)
+{
+ NMVPNPluginPrivate *priv = NM_VPN_PLUGIN_GET_PRIVATE (plugin);
+
+ if (priv->connection)
+ dbus_g_connection_unref (priv->connection);
+
+ priv->connection = connection;
+}
+
+DBusGConnection *
+nm_vpn_plugin_get_connection (NMVPNPlugin *plugin)
+{
+ DBusGConnection *connection;
+
+ g_return_val_if_fail (NM_IS_VPN_PLUGIN (plugin), NULL);
+
+ connection = NM_VPN_PLUGIN_GET_PRIVATE (plugin)->connection;
+
+ if (connection)
+ dbus_g_connection_ref (connection);
+
+ return connection;
+}
+
+NMVPNServiceState
+nm_vpn_plugin_get_state (NMVPNPlugin *plugin)
+{
+ g_return_val_if_fail (NM_IS_VPN_PLUGIN (plugin), NM_VPN_SERVICE_STATE_UNKNOWN);
+
+ return NM_VPN_PLUGIN_GET_PRIVATE (plugin)->state;
+}
+
+void
+nm_vpn_plugin_set_state (NMVPNPlugin *plugin,
+ NMVPNServiceState state)
+{
+ NMVPNPluginPrivate *priv;
+
+ g_return_if_fail (NM_IS_VPN_PLUGIN (plugin));
+
+ priv = NM_VPN_PLUGIN_GET_PRIVATE (plugin);
+ if (priv->state != state) {
+ priv->state = state;
+ g_signal_emit (plugin, signals[STATE_CHANGED], 0, state);
+ }
+}
+
+void
+nm_vpn_plugin_set_login_banner (NMVPNPlugin *plugin,
+ const char *banner)
+{
+ g_return_if_fail (NM_IS_VPN_PLUGIN (plugin));
+ g_return_if_fail (banner != NULL);
+
+ g_signal_emit (plugin, signals[LOGIN_BANNER], 0, banner);
+}
+
+void
+nm_vpn_plugin_failure (NMVPNPlugin *plugin,
+ NMVPNPluginFailure reason)
+{
+ g_return_if_fail (NM_IS_VPN_PLUGIN (plugin));
+
+ g_signal_emit (plugin, signals[FAILURE], 0, reason);
+}
+
+gboolean
+nm_vpn_plugin_disconnect (NMVPNPlugin *plugin, GError **err)
+{
+ gboolean ret = FALSE;
+ NMVPNServiceState state;
+
+ g_return_val_if_fail (NM_IS_VPN_PLUGIN (plugin), FALSE);
+
+ state = nm_vpn_plugin_get_state (plugin);
+ switch (state) {
+ case NM_VPN_SERVICE_STATE_STOPPING:
+ g_set_error (err,
+ NM_VPN_PLUGIN_ERROR,
+ NM_VPN_PLUGIN_ERROR_STOPPING_IN_PROGRESS,
+ "%s",
+ "Could not process the request because the VPN connection is already being stopped.");
+ break;
+ case NM_VPN_SERVICE_STATE_STOPPED:
+ g_set_error (err,
+ NM_VPN_PLUGIN_ERROR,
+ NM_VPN_PLUGIN_ERROR_ALREADY_STOPPED,
+ "%s",
+ "Could not process the request because no VPN connection was active.");
+ break;
+ case NM_VPN_SERVICE_STATE_STARTING:
+ case NM_VPN_SERVICE_STATE_STARTED:
+ nm_vpn_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_STOPPING);
+ ret = NM_VPN_PLUGIN_GET_CLASS (plugin)->disconnect (plugin, err);
+ nm_vpn_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_STOPPED);
+ break;
+ case NM_VPN_SERVICE_STATE_INIT:
+ ret = TRUE;
+ break;
+
+ default:
+ g_warning ("Unhandled VPN service state %d", state);
+ g_assert_not_reached ();
+ break;
+ }
+
+ return ret;
+}
+
+static void
+nm_vpn_plugin_emit_quit (NMVPNPlugin *plugin)
+{
+ g_signal_emit (plugin, signals[QUIT], 0);
+}
+
+static gboolean
+connect_timer_expired (gpointer data)
+{
+ NMVPNPlugin *plugin = NM_VPN_PLUGIN (data);
+ GError *err = NULL;
+
+ g_message ("Connect timer expired, disconnecting.");
+ nm_vpn_plugin_disconnect (plugin, &err);
+ if (err) {
+ g_warning ("Disconnect failed: %s", err->message);
+ g_error_free (err);
+ }
+
+ return FALSE;
+}
+
+static gboolean
+quit_timer_expired (gpointer data)
+{
+ NMVPNPlugin *plugin = NM_VPN_PLUGIN (data);
+
+ nm_vpn_plugin_emit_quit (plugin);
+
+ return FALSE;
+}
+
+static gboolean
+fail_stop (gpointer data)
+{
+ NMVPNPlugin *plugin = NM_VPN_PLUGIN (data);
+
+ nm_vpn_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_STOPPED);
+ return FALSE;
+}
+
+static void
+schedule_fail_stop (NMVPNPlugin *plugin)
+{
+ NMVPNPluginPrivate *priv = NM_VPN_PLUGIN_GET_PRIVATE (plugin);
+
+ if (priv->fail_stop_id)
+ g_source_remove (priv->fail_stop_id);
+ priv->fail_stop_id = g_idle_add (fail_stop, plugin);
+}
+
+static void
+_g_value_set (GValue *dst, GValue *src)
+{
+ if (src) {
+ GType type = G_VALUE_TYPE (src);
+
+ if (G_IS_VALUE (dst))
+ g_value_unset (dst);
+ g_value_init (dst, type);
+ g_value_copy (src, dst);
+ } else if (G_IS_VALUE (dst))
+ g_value_unset (dst);
+}
+
+void
+nm_vpn_plugin_set_config (NMVPNPlugin *plugin,
+ GHashTable *config)
+{
+ NMVPNPluginPrivate *priv = NM_VPN_PLUGIN_GET_PRIVATE (plugin);
+ GValue *val;
+
+ g_return_if_fail (NM_IS_VPN_PLUGIN (plugin));
+ g_return_if_fail (config != NULL);
+
+ priv->got_config = TRUE;
+
+ val = g_hash_table_lookup (config, NM_VPN_PLUGIN_CONFIG_HAS_IP4);
+ if (val && g_value_get_boolean (val))
+ priv->has_ip4 = TRUE;
+ val = g_hash_table_lookup (config, NM_VPN_PLUGIN_CONFIG_HAS_IP6);
+ if (val && g_value_get_boolean (val))
+ priv->has_ip6 = TRUE;
+
+ g_warn_if_fail (priv->has_ip4 || priv->has_ip6);
+
+ /* Record the items that need to also be inserted into the
+ * ip4config, for compatibility with older daemons.
+ */
+ _g_value_set (&priv->banner, g_hash_table_lookup (config, NM_VPN_PLUGIN_CONFIG_BANNER));
+ _g_value_set (&priv->tundev, g_hash_table_lookup (config, NM_VPN_PLUGIN_CONFIG_TUNDEV));
+ _g_value_set (&priv->gateway, g_hash_table_lookup (config, NM_VPN_PLUGIN_CONFIG_EXT_GATEWAY));
+ _g_value_set (&priv->mtu, g_hash_table_lookup (config, NM_VPN_PLUGIN_CONFIG_MTU));
+
+ g_signal_emit (plugin, signals[CONFIG], 0, config);
+}
+
+void
+nm_vpn_plugin_set_ip4_config (NMVPNPlugin *plugin,
+ GHashTable *ip4_config)
+{
+ NMVPNPluginPrivate *priv = NM_VPN_PLUGIN_GET_PRIVATE (plugin);
+ GHashTable *combined_config;
+ GHashTableIter iter;
+ gpointer key, value;
+
+ g_return_if_fail (NM_IS_VPN_PLUGIN (plugin));
+ g_return_if_fail (ip4_config != NULL);
+
+ priv->got_ip4 = TRUE;
+
+ /* Old plugins won't send the "config" signal and thus can't send
+ * NM_VPN_PLUGIN_CONFIG_HAS_IP4 either. But since they don't support IPv6,
+ * we can safely assume that, if we don't receive a "config" signal but do
+ * receive an "ip4-config" signal, the old plugin supports IPv4.
+ */
+ if (!priv->got_config)
+ priv->has_ip4 = TRUE;
+
+ /* Older NetworkManager daemons expect all config info to be in
+ * the ip4 config, so they won't even notice the "config" signal
+ * being emitted. So just copy all of that data into the ip4
+ * config too.
+ */
+ combined_config = g_hash_table_new (g_str_hash, g_str_equal);
+ g_hash_table_iter_init (&iter, ip4_config);
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ g_hash_table_insert (combined_config, key, value);
+
+ if (G_VALUE_TYPE (&priv->banner) != G_TYPE_INVALID)
+ g_hash_table_insert (combined_config, NM_VPN_PLUGIN_IP4_CONFIG_BANNER, &priv->banner);
+ if (G_VALUE_TYPE (&priv->tundev) != G_TYPE_INVALID)
+ g_hash_table_insert (combined_config, NM_VPN_PLUGIN_IP4_CONFIG_TUNDEV, &priv->tundev);
+ if (G_VALUE_TYPE (&priv->gateway) != G_TYPE_INVALID)
+ g_hash_table_insert (combined_config, NM_VPN_PLUGIN_IP4_CONFIG_EXT_GATEWAY, &priv->gateway);
+ if (G_VALUE_TYPE (&priv->mtu) != G_TYPE_INVALID)
+ g_hash_table_insert (combined_config, NM_VPN_PLUGIN_IP4_CONFIG_MTU, &priv->mtu);
+
+ g_signal_emit (plugin, signals[IP4_CONFIG], 0, combined_config);
+ g_hash_table_destroy (combined_config);
+
+ if ( priv->has_ip4 == priv->got_ip4
+ && priv->has_ip6 == priv->got_ip6)
+ nm_vpn_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_STARTED);
+}
+
+void
+nm_vpn_plugin_set_ip6_config (NMVPNPlugin *plugin,
+ GHashTable *ip6_config)
+{
+ NMVPNPluginPrivate *priv = NM_VPN_PLUGIN_GET_PRIVATE (plugin);
+
+ g_return_if_fail (NM_IS_VPN_PLUGIN (plugin));
+ g_return_if_fail (ip6_config != NULL);
+
+ priv->got_ip6 = TRUE;
+ g_signal_emit (plugin, signals[IP6_CONFIG], 0, ip6_config);
+
+ if ( priv->has_ip4 == priv->got_ip4
+ && priv->has_ip6 == priv->got_ip6)
+ nm_vpn_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_STARTED);
+}
+
+static void
+connect_timer_removed (gpointer data)
+{
+ NM_VPN_PLUGIN_GET_PRIVATE (data)->connect_timer = 0;
+}
+
+static void
+connect_timer_start (NMVPNPlugin *plugin)
+{
+ NMVPNPluginPrivate *priv = NM_VPN_PLUGIN_GET_PRIVATE (plugin);
+
+ priv->connect_timer = g_timeout_add_seconds_full (G_PRIORITY_DEFAULT,
+ 60,
+ connect_timer_expired,
+ plugin,
+ connect_timer_removed);
+}
+
+static gboolean
+_connect_generic (NMVPNPlugin *plugin,
+ GHashTable *properties,
+ GHashTable *details,
+ GError **error)
+{
+ NMVPNPluginPrivate *priv = NM_VPN_PLUGIN_GET_PRIVATE (plugin);
+ NMVPNPluginClass *vpn_class = NM_VPN_PLUGIN_GET_CLASS (plugin);
+ NMConnection *connection;
+ gboolean success = FALSE;
+ GError *local = NULL;
+
+ if (priv->state != NM_VPN_SERVICE_STATE_STOPPED &&
+ priv->state != NM_VPN_SERVICE_STATE_INIT) {
+ g_set_error (error, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_WRONG_STATE,
+ "Could not start connection: wrong plugin state %d",
+ priv->state);
+ return FALSE;
+ }
+
+ connection = nm_connection_new_from_hash (properties, &local);
+ if (!connection) {
+ g_set_error (error, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
+ "Invalid connection: (%d) %s",
+ local->code, local->message);
+ g_clear_error (&local);
+ return FALSE;
+ }
+
+
+ priv->interactive = FALSE;
+ if (details && !vpn_class->connect_interactive) {
+ g_set_error_literal (error, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_INTERACTIVE_NOT_SUPPORTED,
+ "Plugin does not implement ConnectInteractive()");
+ return FALSE;
+ }
+
+ nm_vpn_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_STARTING);
+
+ if (details) {
+ priv->interactive = TRUE;
+ success = vpn_class->connect_interactive (plugin, connection, details, error);
+ } else
+ success = vpn_class->connect (plugin, connection, error);
+
+ if (success) {
+ /* Add a timer to make sure we do not wait indefinitely for the successful connect. */
+ connect_timer_start (plugin);
+ } else {
+ /* Stop the plugin from an idle handler so that the Connect
+ * method return gets sent before the STOP StateChanged signal.
+ */
+ schedule_fail_stop (plugin);
+ }
+
+ g_object_unref (connection);
+ return success;
+}
+
+static gboolean
+impl_vpn_plugin_connect (NMVPNPlugin *plugin,
+ GHashTable *connection,
+ GError **error)
+{
+ return _connect_generic (plugin, connection, NULL, error);
+}
+
+static gboolean
+impl_vpn_plugin_connect_interactive (NMVPNPlugin *plugin,
+ GHashTable *connection,
+ GHashTable *details,
+ GError **error)
+{
+ return _connect_generic (plugin, connection, details, error);
+}
+
+/***************************************************************/
+
+static gboolean
+impl_vpn_plugin_need_secrets (NMVPNPlugin *plugin,
+ GHashTable *properties,
+ char **setting_name,
+ GError **err)
+{
+ gboolean ret = FALSE;
+ NMConnection *connection;
+ char *sn = NULL;
+ GError *ns_err = NULL;
+ gboolean needed = FALSE;
+ GError *cnfh_err = NULL;
+
+ g_return_val_if_fail (NM_IS_VPN_PLUGIN (plugin), FALSE);
+ g_return_val_if_fail (properties != NULL, FALSE);
+
+ connection = nm_connection_new_from_hash (properties, &cnfh_err);
+ if (!connection) {
+ g_set_error (err,
+ NM_VPN_PLUGIN_ERROR,
+ NM_VPN_PLUGIN_ERROR_CONNECTION_INVALID,
+ "The connection was invalid: '%s' / '%s' invalid: %d.",
+ g_type_name (nm_connection_lookup_setting_type_by_quark (cnfh_err->domain)),
+ cnfh_err->message, cnfh_err->code);
+ g_error_free (cnfh_err);
+ return FALSE;
+ }
+
+ if (!NM_VPN_PLUGIN_GET_CLASS (plugin)->need_secrets) {
+ *setting_name = "";
+ ret = TRUE;
+ goto out;
+ }
+
+ needed = NM_VPN_PLUGIN_GET_CLASS (plugin)->need_secrets (plugin, connection, &sn, &ns_err);
+ if (ns_err) {
+ *err = g_error_copy (ns_err);
+ g_error_free (ns_err);
+ goto out;
+ }
+
+ ret = TRUE;
+ if (needed) {
+ g_assert (sn);
+ *setting_name = g_strdup (sn);
+ } else {
+ /* No secrets required */
+ *setting_name = g_strdup ("");
+ }
+
+out:
+ return ret;
+}
+
+static gboolean
+impl_vpn_plugin_new_secrets (NMVPNPlugin *plugin,
+ GHashTable *properties,
+ GError **error)
+{
+ NMVPNPluginPrivate *priv = NM_VPN_PLUGIN_GET_PRIVATE (plugin);
+ NMConnection *connection;
+ GError *local = NULL;
+ gboolean success;
+
+ if (priv->state != NM_VPN_SERVICE_STATE_STARTING) {
+ g_set_error (error, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_WRONG_STATE,
+ "Could not accept new secrets: wrong plugin state %d",
+ priv->state);
+ return FALSE;
+ }
+
+ connection = nm_connection_new_from_hash (properties, &local);
+ if (!connection) {
+ g_set_error (error, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
+ "Invalid connection: (%d) %s",
+ local->code, local->message);
+ g_clear_error (&local);
+ return FALSE;
+ }
+
+ if (!NM_VPN_PLUGIN_GET_CLASS (plugin)->new_secrets) {
+ g_set_error_literal (error, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_INTERACTIVE_NOT_SUPPORTED,
+ "Could not accept new secrets: plugin cannot process interactive secrets");
+ g_object_unref (connection);
+ return FALSE;
+ }
+
+ success = NM_VPN_PLUGIN_GET_CLASS (plugin)->new_secrets (plugin, connection, error);
+ if (success) {
+ /* Add a timer to make sure we do not wait indefinitely for the successful connect. */
+ connect_timer_start (plugin);
+ } else {
+ /* Stop the plugin from and idle handler so that the NewSecrets
+ * method return gets sent before the STOP StateChanged signal.
+ */
+ schedule_fail_stop (plugin);
+ }
+
+ g_object_unref (connection);
+ return success;
+}
+
+/**
+ * nm_vpn_plugin_secrets_required:
+ * @plugin: the #NMVPNPlugin
+ * @message: an information message about why secrets are required, if any
+ * @hints: VPN specific secret names for required new secrets
+ *
+ * Called by VPN plugin implementations to signal to NetworkManager that secrets
+ * are required during the connection process. This signal may be used to
+ * request new secrets when the secrets originally provided by NetworkManager
+ * are insufficient, or the VPN process indicates that it needs additional
+ * information to complete the request.
+ *
+ * Since: 0.9.10
+ */
+void
+nm_vpn_plugin_secrets_required (NMVPNPlugin *plugin,
+ const char *message,
+ const char **hints)
+{
+ NMVPNPluginPrivate *priv = NM_VPN_PLUGIN_GET_PRIVATE (plugin);
+
+ /* Plugin must be able to accept the new secrets if it calls this method */
+ g_return_if_fail (NM_VPN_PLUGIN_GET_CLASS (plugin)->new_secrets);
+
+ /* Plugin cannot call this method if NetworkManager didn't originally call
+ * ConnectInteractive().
+ */
+ g_return_if_fail (priv->interactive == TRUE);
+
+ /* Cancel the connect timer since secrets might take a while. It'll
+ * get restarted when the secrets come back via NewSecrets().
+ */
+ if (priv->connect_timer)
+ g_source_remove (priv->connect_timer);
+
+ g_signal_emit (plugin, signals[SECRETS_REQUIRED], 0, message, hints);
+}
+
+/***************************************************************/
+
+static gboolean
+impl_vpn_plugin_disconnect (NMVPNPlugin *plugin,
+ GError **err)
+{
+ return nm_vpn_plugin_disconnect (plugin, err);
+}
+
+static gboolean
+impl_vpn_plugin_set_config (NMVPNPlugin *plugin,
+ GHashTable *config,
+ GError **err)
+{
+ nm_vpn_plugin_set_config (plugin, config);
+
+ return TRUE;
+}
+
+static gboolean
+impl_vpn_plugin_set_ip4_config (NMVPNPlugin *plugin,
+ GHashTable *config,
+ GError **err)
+{
+ nm_vpn_plugin_set_ip4_config (plugin, config);
+
+ return TRUE;
+}
+
+static gboolean
+impl_vpn_plugin_set_ip6_config (NMVPNPlugin *plugin,
+ GHashTable *config,
+ GError **err)
+{
+ nm_vpn_plugin_set_ip6_config (plugin, config);
+
+ return TRUE;
+}
+
+static gboolean
+impl_vpn_plugin_set_failure (NMVPNPlugin *plugin,
+ char *reason,
+ GError **err)
+{
+ nm_vpn_plugin_failure (plugin, NM_VPN_PLUGIN_FAILURE_BAD_IP_CONFIG);
+
+ return TRUE;
+}
+
+/*********************************************************************/
+
+static void
+sigterm_handler (int signum)
+{
+ g_slist_foreach (active_plugins, (GFunc) nm_vpn_plugin_emit_quit, NULL);
+}
+
+static void
+setup_unix_signal_handler (void)
+{
+ struct sigaction action;
+ sigset_t block_mask;
+
+ action.sa_handler = sigterm_handler;
+ sigemptyset (&block_mask);
+ action.sa_mask = block_mask;
+ action.sa_flags = 0;
+ sigaction (SIGINT, &action, NULL);
+ sigaction (SIGTERM, &action, NULL);
+}
+
+/*********************************************************************/
+
+static void
+one_plugin_destroyed (gpointer data,
+ GObject *object)
+{
+ active_plugins = g_slist_remove (active_plugins, object);
+}
+
+static void
+nm_vpn_plugin_init (NMVPNPlugin *plugin)
+{
+ active_plugins = g_slist_append (active_plugins, plugin);
+ g_object_weak_ref (G_OBJECT (plugin),
+ one_plugin_destroyed,
+ NULL);
+}
+
+static GObject *
+constructor (GType type,
+ guint n_construct_params,
+ GObjectConstructParam *construct_params)
+{
+ GObject *object;
+ NMVPNPlugin *plugin;
+ NMVPNPluginPrivate *priv;
+ DBusGConnection *connection;
+ DBusGProxy *proxy;
+ guint request_name_result;
+ GError *err = NULL;
+
+ object = G_OBJECT_CLASS (nm_vpn_plugin_parent_class)->constructor (type,
+ n_construct_params,
+ construct_params);
+ if (!object)
+ return NULL;
+
+ priv = NM_VPN_PLUGIN_GET_PRIVATE (object);
+ if (!priv->dbus_service_name)
+ goto err;
+
+ connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &err);
+ if (!connection)
+ goto err;
+
+ proxy = dbus_g_proxy_new_for_name (connection,
+ "org.freedesktop.DBus",
+ "/org/freedesktop/DBus",
+ "org.freedesktop.DBus");
+
+ if (!dbus_g_proxy_call (proxy, "RequestName", &err,
+ G_TYPE_STRING, priv->dbus_service_name,
+ G_TYPE_UINT, 0,
+ G_TYPE_INVALID,
+ G_TYPE_UINT, &request_name_result,
+ G_TYPE_INVALID)) {
+ g_object_unref (proxy);
+ goto err;
+ }
+
+ g_object_unref (proxy);
+
+ dbus_g_connection_register_g_object (connection,
+ NM_VPN_DBUS_PLUGIN_PATH,
+ object);
+
+ plugin = NM_VPN_PLUGIN (object);
+
+ nm_vpn_plugin_set_connection (plugin, connection);
+ nm_vpn_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_INIT);
+
+ return object;
+
+ err:
+ if (err) {
+ g_warning ("Failed to initialize VPN plugin: %s", err->message);
+ g_error_free (err);
+ }
+
+ if (object)
+ g_object_unref (object);
+
+ return NULL;
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ NMVPNPluginPrivate *priv = NM_VPN_PLUGIN_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_DBUS_SERVICE_NAME:
+ /* Construct-only */
+ priv->dbus_service_name = g_strdup (g_value_get_string (value));
+ break;
+ case PROP_STATE:
+ nm_vpn_plugin_set_state (NM_VPN_PLUGIN (object),
+ (NMVPNServiceState) g_value_get_uint (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ NMVPNPluginPrivate *priv = NM_VPN_PLUGIN_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_DBUS_SERVICE_NAME:
+ g_value_set_string (value, priv->dbus_service_name);
+ break;
+ case PROP_STATE:
+ g_value_set_uint (value, nm_vpn_plugin_get_state (NM_VPN_PLUGIN (object)));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+dispose (GObject *object)
+{
+ NMVPNPlugin *plugin = NM_VPN_PLUGIN (object);
+ NMVPNPluginPrivate *priv = NM_VPN_PLUGIN_GET_PRIVATE (plugin);
+ NMVPNServiceState state;
+ GError *err = NULL;
+
+ if (priv->fail_stop_id) {
+ g_source_remove (priv->fail_stop_id);
+ priv->fail_stop_id = 0;
+ }
+
+ state = nm_vpn_plugin_get_state (plugin);
+
+ if (state == NM_VPN_SERVICE_STATE_STARTED ||
+ state == NM_VPN_SERVICE_STATE_STARTING)
+ nm_vpn_plugin_disconnect (plugin, &err);
+
+ if (err) {
+ g_warning ("Error disconnecting VPN connection: %s", err->message);
+ g_error_free (err);
+ }
+
+ G_OBJECT_CLASS (nm_vpn_plugin_parent_class)->dispose (object);
+}
+
+static void
+finalize (GObject *object)
+{
+ NMVPNPlugin *plugin = NM_VPN_PLUGIN (object);
+ NMVPNPluginPrivate *priv = NM_VPN_PLUGIN_GET_PRIVATE (plugin);
+
+ nm_vpn_plugin_set_connection (plugin, NULL);
+ g_free (priv->dbus_service_name);
+
+ if (G_IS_VALUE (&priv->banner))
+ g_value_unset (&priv->banner);
+ if (G_IS_VALUE (&priv->tundev))
+ g_value_unset (&priv->tundev);
+ if (G_IS_VALUE (&priv->gateway))
+ g_value_unset (&priv->gateway);
+ if (G_IS_VALUE (&priv->mtu))
+ g_value_unset (&priv->mtu);
+
+ G_OBJECT_CLASS (nm_vpn_plugin_parent_class)->finalize (object);
+}
+
+static void
+quit_timer_removed (gpointer data)
+{
+ NM_VPN_PLUGIN_GET_PRIVATE (data)->quit_timer = 0;
+}
+
+static void
+state_changed (NMVPNPlugin *plugin, NMVPNServiceState state)
+{
+ NMVPNPluginPrivate *priv = NM_VPN_PLUGIN_GET_PRIVATE (plugin);
+
+ switch (state) {
+ case NM_VPN_SERVICE_STATE_STARTING:
+ /* Remove the quit timer. */
+ if (priv->quit_timer)
+ g_source_remove (priv->quit_timer);
+
+ if (priv->fail_stop_id) {
+ g_source_remove (priv->fail_stop_id);
+ priv->fail_stop_id = 0;
+ }
+ break;
+ case NM_VPN_SERVICE_STATE_STOPPED:
+ priv->quit_timer = g_timeout_add_seconds_full (G_PRIORITY_DEFAULT,
+ NM_VPN_PLUGIN_QUIT_TIMER,
+ quit_timer_expired,
+ plugin,
+ quit_timer_removed);
+ break;
+ default:
+ /* Clean up all timers we might have set up. */
+ if (priv->connect_timer)
+ g_source_remove (priv->connect_timer);
+
+ if (priv->quit_timer)
+ g_source_remove (priv->quit_timer);
+
+ if (priv->fail_stop_id) {
+ g_source_remove (priv->fail_stop_id);
+ priv->fail_stop_id = 0;
+ }
+ break;
+ }
+}
+
+static void
+nm_vpn_plugin_class_init (NMVPNPluginClass *plugin_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (plugin_class);
+
+ g_type_class_add_private (object_class, sizeof (NMVPNPluginPrivate));
+
+ dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (plugin_class),
+ &dbus_glib_nm_vpn_plugin_object_info);
+
+ /* virtual methods */
+ object_class->constructor = constructor;
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+ object_class->dispose = dispose;
+ object_class->finalize = finalize;
+
+ plugin_class->state_changed = state_changed;
+
+ /* properties */
+
+ /**
+ * NMVPNPlugin:service-name:
+ *
+ * The D-Bus service name of this plugin.
+ */
+ g_object_class_install_property
+ (object_class, PROP_DBUS_SERVICE_NAME,
+ g_param_spec_string (NM_VPN_PLUGIN_DBUS_SERVICE_NAME, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMVPNPlugin:state:
+ *
+ * The state of the plugin.
+ */
+ g_object_class_install_property
+ (object_class, PROP_STATE,
+ g_param_spec_uint (NM_VPN_PLUGIN_STATE, "", "",
+ NM_VPN_SERVICE_STATE_UNKNOWN,
+ NM_VPN_SERVICE_STATE_STOPPED,
+ NM_VPN_SERVICE_STATE_INIT,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /* signals */
+ signals[STATE_CHANGED] =
+ g_signal_new ("state-changed",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NMVPNPluginClass, state_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__UINT,
+ G_TYPE_NONE, 1,
+ G_TYPE_UINT);
+
+ signals[SECRETS_REQUIRED] =
+ g_signal_new ("secrets-required",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0, NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRV);
+
+ signals[CONFIG] =
+ g_signal_new ("config",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NMVPNPluginClass, config),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__BOXED,
+ G_TYPE_NONE, 1,
+ DBUS_TYPE_G_MAP_OF_VARIANT);
+
+ signals[IP4_CONFIG] =
+ g_signal_new ("ip4-config",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NMVPNPluginClass, ip4_config),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__BOXED,
+ G_TYPE_NONE, 1,
+ DBUS_TYPE_G_MAP_OF_VARIANT);
+
+ signals[IP6_CONFIG] =
+ g_signal_new ("ip6-config",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NMVPNPluginClass, ip6_config),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__BOXED,
+ G_TYPE_NONE, 1,
+ DBUS_TYPE_G_MAP_OF_VARIANT);
+
+ signals[LOGIN_BANNER] =
+ g_signal_new ("login-banner",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NMVPNPluginClass, login_banner),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE, 1,
+ G_TYPE_STRING);
+
+ signals[FAILURE] =
+ g_signal_new ("failure",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NMVPNPluginClass, failure),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__UINT,
+ G_TYPE_NONE, 1,
+ G_TYPE_UINT);
+
+ signals[QUIT] =
+ g_signal_new ("quit",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NMVPNPluginClass, quit),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0,
+ G_TYPE_NONE);
+
+ dbus_g_error_domain_register (NM_VPN_PLUGIN_ERROR,
+ NM_DBUS_VPN_ERROR_PREFIX,
+ NM_TYPE_VPN_PLUGIN_ERROR);
+
+ setup_unix_signal_handler ();
+}
diff --git a/libnm/nm-vpn-plugin.h b/libnm/nm-vpn-plugin.h
new file mode 100644
index 0000000000..323017f035
--- /dev/null
+++ b/libnm/nm-vpn-plugin.h
@@ -0,0 +1,175 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2007 - 2008 Novell, Inc.
+ * Copyright 2007 - 2013 Red Hat, Inc.
+ */
+
+#ifndef NM_VPN_PLUGIN_H
+#define NM_VPN_PLUGIN_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <dbus/dbus-glib.h>
+#include <NetworkManagerVPN.h>
+#include <nm-connection.h>
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_VPN_PLUGIN (nm_vpn_plugin_get_type ())
+#define NM_VPN_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_VPN_PLUGIN, NMVPNPlugin))
+#define NM_VPN_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_VPN_PLUGIN, NMVPNPluginClass))
+#define NM_IS_VPN_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_VPN_PLUGIN))
+#define NM_IS_VPN_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_VPN_PLUGIN))
+#define NM_VPN_PLUGIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_VPN_PLUGIN, NMVPNPluginClass))
+
+#define NM_VPN_PLUGIN_DBUS_SERVICE_NAME "service-name"
+#define NM_VPN_PLUGIN_STATE "state"
+
+/**
+ * NMVPNPluginError:
+ * @NM_VPN_PLUGIN_ERROR_GENERAL: general failure
+ * @NM_VPN_PLUGIN_ERROR_STARTING_IN_PROGRESS: the plugin is already starting,
+ * and another connect request was received
+ * @NM_VPN_PLUGIN_ERROR_ALREADY_STARTED: the plugin is already connected, and
+ * another connect request was received
+ * @NM_VPN_PLUGIN_ERROR_STOPPING_IN_PROGRESS: the plugin is already stopping,
+ * and another stop request was received
+ * @NM_VPN_PLUGIN_ERROR_ALREADY_STOPPED: the plugin is already stopped, and
+ * another disconnect request was received
+ * @NM_VPN_PLUGIN_ERROR_WRONG_STATE: the operation could not be performed in
+ * this state
+ * @NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS: the operation could not be performed as
+ * the request contained malformed arguments, or arguments of unexpected type.
+ * Usually means that one of the VPN setting data items or secrets was not of
+ * the expected type (ie int, string, bool, etc).
+ * @NM_VPN_PLUGIN_ERROR_LAUNCH_FAILED: a child process failed to launch
+ * @NM_VPN_PLUGIN_ERROR_CONNECTION_INVALID: the operation could not be performed
+ * because the connection was invalid. Usually means that the connection's
+ * VPN setting was missing some required data item or secret.
+ * @NM_VPN_PLUGIN_ERROR_INTERACTIVE_NOT_SUPPORTED: the operation could not be
+ * performed as the plugin does not support interactive operations, such as
+ * ConnectInteractive() or NewSecrets()
+ *
+ * Returned by the VPN service plugin to indicate errors.
+ **/
+typedef enum {
+ NM_VPN_PLUGIN_ERROR_GENERAL, /*< nick=General >*/
+ NM_VPN_PLUGIN_ERROR_STARTING_IN_PROGRESS, /*< nick=StartingInProgress >*/
+ NM_VPN_PLUGIN_ERROR_ALREADY_STARTED, /*< nick=AlreadyStarted >*/
+ NM_VPN_PLUGIN_ERROR_STOPPING_IN_PROGRESS, /*< nick=StoppingInProgress >*/
+ NM_VPN_PLUGIN_ERROR_ALREADY_STOPPED, /*< nick=AlreadyStopped >*/
+ NM_VPN_PLUGIN_ERROR_WRONG_STATE, /*< nick=WrongState >*/
+ NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS, /*< nick=BadArguments >*/
+ NM_VPN_PLUGIN_ERROR_LAUNCH_FAILED, /*< nick=LaunchFailed >*/
+ NM_VPN_PLUGIN_ERROR_CONNECTION_INVALID, /*< nick=ConnectionInvalid >*/
+ NM_VPN_PLUGIN_ERROR_INTERACTIVE_NOT_SUPPORTED /*< nick=InteractiveNotSupported >*/
+} NMVPNPluginError;
+
+#define NM_VPN_PLUGIN_ERROR (nm_vpn_plugin_error_quark ())
+
+typedef struct {
+ GObject parent;
+} NMVPNPlugin;
+
+typedef struct {
+ GObjectClass parent;
+
+ /* virtual methods */
+ gboolean (*connect) (NMVPNPlugin *plugin,
+ NMConnection *connection,
+ GError **err);
+
+ gboolean (*need_secrets) (NMVPNPlugin *plugin,
+ NMConnection *connection,
+ char **setting_name,
+ GError **error);
+
+ gboolean (*disconnect) (NMVPNPlugin *plugin,
+ GError **err);
+
+ /* Signals */
+ void (*state_changed) (NMVPNPlugin *plugin,
+ NMVPNServiceState state);
+
+ void (*ip4_config) (NMVPNPlugin *plugin,
+ GHashTable *ip4_config);
+
+ void (*login_banner) (NMVPNPlugin *plugin,
+ const char *banner);
+
+ void (*failure) (NMVPNPlugin *plugin,
+ NMVPNPluginFailure reason);
+
+ void (*quit) (NMVPNPlugin *plugin);
+
+ void (*config) (NMVPNPlugin *plugin,
+ GHashTable *config);
+
+ void (*ip6_config) (NMVPNPlugin *plugin,
+ GHashTable *config);
+
+ /* more methods */
+ gboolean (*new_secrets) (NMVPNPlugin *plugin,
+ NMConnection *connection,
+ GError **error);
+
+ gboolean (*connect_interactive) (NMVPNPlugin *plugin,
+ NMConnection *connection,
+ GHashTable *details,
+ GError **error);
+
+ /* Padding for future expansion */
+ void (*_reserved1) (void);
+ void (*_reserved2) (void);
+} NMVPNPluginClass;
+
+GType nm_vpn_plugin_get_type (void);
+GQuark nm_vpn_plugin_error_quark (void);
+GType nm_vpn_plugin_error_get_type (void);
+
+DBusGConnection *nm_vpn_plugin_get_connection (NMVPNPlugin *plugin);
+NMVPNServiceState nm_vpn_plugin_get_state (NMVPNPlugin *plugin);
+void nm_vpn_plugin_set_state (NMVPNPlugin *plugin,
+ NMVPNServiceState state);
+
+NM_AVAILABLE_IN_0_9_10
+void nm_vpn_plugin_secrets_required (NMVPNPlugin *plugin,
+ const char *message,
+ const char **hints);
+
+void nm_vpn_plugin_set_login_banner (NMVPNPlugin *plugin,
+ const char *banner);
+
+void nm_vpn_plugin_failure (NMVPNPlugin *plugin,
+ NMVPNPluginFailure reason);
+
+void nm_vpn_plugin_set_config (NMVPNPlugin *plugin,
+ GHashTable *config);
+
+void nm_vpn_plugin_set_ip4_config (NMVPNPlugin *plugin,
+ GHashTable *ip4_config);
+
+void nm_vpn_plugin_set_ip6_config (NMVPNPlugin *plugin,
+ GHashTable *ip6_config);
+
+gboolean nm_vpn_plugin_disconnect (NMVPNPlugin *plugin,
+ GError **err);
+
+G_END_DECLS
+
+#endif /* NM_VPN_PLUGIN_H */
diff --git a/libnm/nm-wimax-nsp.c b/libnm/nm-wimax-nsp.c
new file mode 100644
index 0000000000..1cbd3eefd0
--- /dev/null
+++ b/libnm/nm-wimax-nsp.c
@@ -0,0 +1,334 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2011 Red Hat, Inc.
+ */
+
+#include <config.h>
+#include <string.h>
+
+#include "nm-glib-compat.h"
+
+#include <nm-connection.h>
+#include <nm-setting-connection.h>
+#include <nm-setting-wimax.h>
+
+#include "nm-wimax-nsp.h"
+#include "NetworkManager.h"
+#include "nm-types-private.h"
+#include "nm-object-private.h"
+
+G_DEFINE_TYPE (NMWimaxNsp, nm_wimax_nsp, NM_TYPE_OBJECT)
+
+#define NM_WIMAX_NSP_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_WIMAX_NSP, NMWimaxNspPrivate))
+
+typedef struct {
+ DBusGProxy *proxy;
+
+ char *name;
+ guint32 signal_quality;
+ NMWimaxNspNetworkType network_type;
+} NMWimaxNspPrivate;
+
+enum {
+ PROP_0,
+ PROP_NAME,
+ PROP_SIGNAL_QUALITY,
+ PROP_NETWORK_TYPE,
+
+ LAST_PROP
+};
+
+/**
+ * nm_wimax_nsp_new:
+ * @connection: the #DBusGConnection
+ * @path: the D-Bus object path of the WiMAX NSP
+ *
+ * Creates a new #NMWimaxNsp.
+ *
+ * Returns: (transfer full): a new WiMAX NSP
+ **/
+GObject *
+nm_wimax_nsp_new (DBusGConnection *connection, const char *path)
+{
+ g_return_val_if_fail (connection != NULL, NULL);
+ g_return_val_if_fail (path != NULL, NULL);
+
+ return (GObject *) g_object_new (NM_TYPE_WIMAX_NSP,
+ NM_OBJECT_DBUS_CONNECTION, connection,
+ NM_OBJECT_DBUS_PATH, path,
+ NULL);
+}
+
+/**
+ * nm_wimax_nsp_get_name:
+ * @nsp: a #NMWimaxNsp
+ *
+ * Gets the name of the wimax NSP
+ *
+ * Returns: the name
+ **/
+const char *
+nm_wimax_nsp_get_name (NMWimaxNsp *nsp)
+{
+ g_return_val_if_fail (NM_IS_WIMAX_NSP (nsp), NULL);
+
+ _nm_object_ensure_inited (NM_OBJECT (nsp));
+ return NM_WIMAX_NSP_GET_PRIVATE (nsp)->name;
+}
+
+/**
+ * nm_wimax_nsp_get_signal_quality:
+ * @nsp: a #NMWimaxNsp
+ *
+ * Gets the WPA signal quality of the wimax NSP.
+ *
+ * Returns: the signal quality
+ **/
+guint32
+nm_wimax_nsp_get_signal_quality (NMWimaxNsp *nsp)
+{
+ g_return_val_if_fail (NM_IS_WIMAX_NSP (nsp), 0);
+
+ _nm_object_ensure_inited (NM_OBJECT (nsp));
+ return NM_WIMAX_NSP_GET_PRIVATE (nsp)->signal_quality;
+}
+
+/**
+ * nm_wimax_nsp_get_network_type:
+ * @nsp: a #NMWimaxNsp
+ *
+ * Gets the network type of the wimax NSP.
+ *
+ * Returns: the network type
+ **/
+NMWimaxNspNetworkType
+nm_wimax_nsp_get_network_type (NMWimaxNsp *nsp)
+{
+ g_return_val_if_fail (NM_IS_WIMAX_NSP (nsp), NM_WIMAX_NSP_NETWORK_TYPE_UNKNOWN);
+
+ _nm_object_ensure_inited (NM_OBJECT (nsp));
+ return NM_WIMAX_NSP_GET_PRIVATE (nsp)->network_type;
+}
+
+/**
+ * nm_wimax_nsp_connection_valid:
+ * @nsp: an #NMWimaxNsp to validate @connection against
+ * @connection: an #NMConnection to validate against @nsp
+ *
+ * Validates a given connection against a given WiMAX NSP to ensure that the
+ * connection may be activated with that NSP. The connection must match the
+ * @nsp's network name and other attributes.
+ *
+ * Returns: %TRUE if the connection may be activated with this WiMAX NSP,
+ * %FALSE if it cannot be.
+ **/
+gboolean
+nm_wimax_nsp_connection_valid (NMWimaxNsp *nsp, NMConnection *connection)
+{
+ NMSettingConnection *s_con;
+ NMSettingWimax *s_wimax;
+ const char *ctype;
+ const char *nsp_name;
+ const char *setting_name;
+
+ s_con = nm_connection_get_setting_connection (connection);
+ g_assert (s_con);
+ ctype = nm_setting_connection_get_connection_type (s_con);
+ if (strcmp (ctype, NM_SETTING_WIMAX_SETTING_NAME) != 0)
+ return FALSE;
+
+ s_wimax = nm_connection_get_setting_wimax (connection);
+ if (!s_wimax)
+ return FALSE;
+
+ setting_name = nm_setting_wimax_get_network_name (s_wimax);
+ if (!setting_name)
+ return FALSE;
+
+ nsp_name = nm_wimax_nsp_get_name (nsp);
+ g_warn_if_fail (nsp_name != NULL);
+ if (g_strcmp0 (nsp_name, setting_name) != 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+/**
+ * nm_wimax_nsp_filter_connections:
+ * @nsp: an #NMWimaxNsp to filter connections for
+ * @connections: (element-type NMConnection): a list of
+ * #NMConnection objects to filter
+ *
+ * Filters a given list of connections for a given #NMWimaxNsp object and
+ * return connections which may be activated with the access point. Any
+ * returned connections will match the @nsp's network name and other attributes.
+ *
+ * Returns: (transfer container) (element-type NMConnection): a
+ * list of #NMConnection objects that could be activated with the given @nsp.
+ * The elements of the list are owned by their creator and should not be freed
+ * by the caller, but the returned list itself is owned by the caller and should
+ * be freed with g_slist_free() when it is no longer required.
+ **/
+GSList *
+nm_wimax_nsp_filter_connections (NMWimaxNsp *nsp, const GSList *connections)
+{
+ GSList *filtered = NULL;
+ const GSList *iter;
+
+ for (iter = connections; iter; iter = g_slist_next (iter)) {
+ NMConnection *candidate = NM_CONNECTION (iter->data);
+
+ if (nm_wimax_nsp_connection_valid (nsp, candidate))
+ filtered = g_slist_prepend (filtered, candidate);
+ }
+
+ return g_slist_reverse (filtered);
+}
+
+/************************************************************/
+
+static void
+nm_wimax_nsp_init (NMWimaxNsp *nsp)
+{
+}
+
+static void
+dispose (GObject *object)
+{
+ NMWimaxNspPrivate *priv = NM_WIMAX_NSP_GET_PRIVATE (object);
+
+ g_clear_object (&priv->proxy);
+
+ G_OBJECT_CLASS (nm_wimax_nsp_parent_class)->dispose (object);
+}
+
+static void
+finalize (GObject *object)
+{
+ NMWimaxNspPrivate *priv = NM_WIMAX_NSP_GET_PRIVATE (object);
+
+ g_free (priv->name);
+
+ G_OBJECT_CLASS (nm_wimax_nsp_parent_class)->finalize (object);
+}
+
+static void
+get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NMWimaxNsp *nsp = NM_WIMAX_NSP (object);
+
+ _nm_object_ensure_inited (NM_OBJECT (object));
+
+ switch (prop_id) {
+ case PROP_NAME:
+ g_value_set_string (value, nm_wimax_nsp_get_name (nsp));
+ break;
+ case PROP_SIGNAL_QUALITY:
+ g_value_set_uint (value, nm_wimax_nsp_get_signal_quality (nsp));
+ break;
+ case PROP_NETWORK_TYPE:
+ g_value_set_uint (value, nm_wimax_nsp_get_network_type (nsp));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+register_properties (NMWimaxNsp *nsp)
+{
+ NMWimaxNspPrivate *priv = NM_WIMAX_NSP_GET_PRIVATE (nsp);
+ const NMPropertiesInfo property_info[] = {
+ { NM_WIMAX_NSP_NAME, &priv->name },
+ { NM_WIMAX_NSP_SIGNAL_QUALITY, &priv->signal_quality },
+ { NM_WIMAX_NSP_NETWORK_TYPE, &priv->network_type },
+ { NULL },
+ };
+
+ _nm_object_register_properties (NM_OBJECT (nsp),
+ priv->proxy,
+ property_info);
+}
+
+static void
+constructed (GObject *object)
+{
+ NMWimaxNspPrivate *priv = NM_WIMAX_NSP_GET_PRIVATE (object);
+
+ G_OBJECT_CLASS (nm_wimax_nsp_parent_class)->constructed (object);
+
+ priv->proxy = _nm_object_new_proxy (NM_OBJECT (object), NULL, NM_DBUS_INTERFACE_WIMAX_NSP);
+ register_properties (NM_WIMAX_NSP (object));
+}
+
+
+static void
+nm_wimax_nsp_class_init (NMWimaxNspClass *nsp_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (nsp_class);
+
+ g_type_class_add_private (nsp_class, sizeof (NMWimaxNspPrivate));
+
+ /* virtual methods */
+ object_class->constructed = constructed;
+ object_class->get_property = get_property;
+ object_class->dispose = dispose;
+ object_class->finalize = finalize;
+
+ /* properties */
+
+ /**
+ * NMWimaxNsp:name:
+ *
+ * The name of the WiMAX NSP.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_NAME,
+ g_param_spec_string (NM_WIMAX_NSP_NAME, "", "",
+ NULL,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMWimaxNsp:signal-quality:
+ *
+ * The signal quality of the WiMAX NSP.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_SIGNAL_QUALITY,
+ g_param_spec_uint (NM_WIMAX_NSP_SIGNAL_QUALITY, "", "",
+ 0, 100, 0,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMWimaxNsp:network-type:
+ *
+ * The network type of the WiMAX NSP.
+ **/
+ g_object_class_install_property
+ (object_class, PROP_NETWORK_TYPE,
+ g_param_spec_uint (NM_WIMAX_NSP_NETWORK_TYPE, "", "",
+ 0, G_MAXUINT32, 0,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/libnm/nm-wimax-nsp.h b/libnm/nm-wimax-nsp.h
new file mode 100644
index 0000000000..e0cfe1e895
--- /dev/null
+++ b/libnm/nm-wimax-nsp.h
@@ -0,0 +1,82 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2011 Red Hat, Inc.
+ * Copyright 2009 Novell, Inc.
+ */
+
+#ifndef NM_WIMAX_NSP_H
+#define NM_WIMAX_NSP_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <NetworkManager.h>
+#include "nm-object.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_WIMAX_NSP (nm_wimax_nsp_get_type ())
+#define NM_WIMAX_NSP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_WIMAX_NSP, NMWimaxNsp))
+#define NM_WIMAX_NSP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_WIMAX_NSP, NMWimaxNspClass))
+#define NM_IS_WIMAX_NSP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_WIMAX_NSP))
+#define NM_IS_WIMAX_NSP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_WIMAX_NSP))
+#define NM_WIMAX_NSP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_WIMAX_NSP, NMWimaxNspClass))
+
+#define NM_WIMAX_NSP_NAME "name"
+#define NM_WIMAX_NSP_SIGNAL_QUALITY "signal-quality"
+#define NM_WIMAX_NSP_NETWORK_TYPE "network-type"
+
+typedef enum {
+ NM_WIMAX_NSP_NETWORK_TYPE_UNKNOWN = 0,
+ NM_WIMAX_NSP_NETWORK_TYPE_HOME = 1,
+ NM_WIMAX_NSP_NETWORK_TYPE_PARTNER = 2,
+ NM_WIMAX_NSP_NETWORK_TYPE_ROAMING_PARTNER = 3
+} NMWimaxNspNetworkType;
+
+typedef struct {
+ NMObject parent;
+} NMWimaxNsp;
+
+typedef struct {
+ NMObjectClass parent;
+
+ /* Padding for future expansion */
+ void (*_reserved1) (void);
+ void (*_reserved2) (void);
+ void (*_reserved3) (void);
+ void (*_reserved4) (void);
+ void (*_reserved5) (void);
+ void (*_reserved6) (void);
+} NMWimaxNspClass;
+
+GType nm_wimax_nsp_get_type (void);
+
+GObject *nm_wimax_nsp_new (DBusGConnection *connection, const char *path);
+
+const char * nm_wimax_nsp_get_name (NMWimaxNsp *nsp);
+guint32 nm_wimax_nsp_get_signal_quality (NMWimaxNsp *nsp);
+NMWimaxNspNetworkType nm_wimax_nsp_get_network_type (NMWimaxNsp *nsp);
+
+GSList * nm_wimax_nsp_filter_connections (NMWimaxNsp *nsp,
+ const GSList *connections);
+
+gboolean nm_wimax_nsp_connection_valid (NMWimaxNsp *nsp,
+ NMConnection *connection);
+
+G_END_DECLS
+
+#endif /* NM_WIMAX_NSP_H */
diff --git a/libnm/tests/common.c b/libnm/tests/common.c
new file mode 100644
index 0000000000..0dbdac54c2
--- /dev/null
+++ b/libnm/tests/common.c
@@ -0,0 +1,119 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2010 - 2014 Red Hat, Inc.
+ *
+ */
+
+#include <dbus/dbus.h>
+#include <glib.h>
+#include <string.h>
+
+#include "NetworkManager.h"
+
+#include "common.h"
+
+static gboolean
+name_exists (GDBusConnection *c, const char *name)
+{
+ GVariant *reply;
+ gboolean exists = FALSE;
+
+ reply = g_dbus_connection_call_sync (c,
+ DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS,
+ "GetNameOwner",
+ g_variant_new ("(s)", name),
+ NULL,
+ G_DBUS_CALL_FLAGS_NO_AUTO_START,
+ -1,
+ NULL,
+ NULL);
+ if (reply != NULL) {
+ exists = TRUE;
+ g_variant_unref (reply);
+ }
+
+ return exists;
+}
+
+NMTestServiceInfo *
+nm_test_service_init (void)
+{
+ NMTestServiceInfo *info;
+ const char *args[2] = { TEST_NM_SERVICE, NULL };
+ GError *error = NULL;
+ int i;
+
+ info = g_malloc0 (sizeof (*info));
+
+ info->bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
+ g_assert_no_error (error);
+
+ /* Spawn the test service. info->keepalive_fd will be a pipe to the service's
+ * stdin; if it closes, the service will exit immediately. We use this to
+ * make sure the service exits if the test program crashes.
+ */
+ g_spawn_async_with_pipes (NULL, (char **) args, NULL, 0, NULL, NULL,
+ &info->pid, &info->keepalive_fd, NULL, NULL, &error);
+ g_assert_no_error (error);
+
+ /* Wait until the service is registered on the bus */
+ for (i = 100; i > 0; i--) {
+ if (name_exists (info->bus, "org.freedesktop.NetworkManager"))
+ break;
+ g_usleep (G_USEC_PER_SEC / 50);
+ }
+ g_assert (i > 0);
+
+ /* Grab a proxy to our fake NM service to trigger tests */
+ info->proxy = g_dbus_proxy_new_sync (info->bus,
+ G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
+ G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS |
+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
+ NULL,
+ NM_DBUS_SERVICE,
+ NM_DBUS_PATH,
+ "org.freedesktop.NetworkManager.LibnmGlibTest",
+ NULL, &error);
+ g_assert_no_error (error);
+
+ return info;
+}
+
+void
+nm_test_service_cleanup (NMTestServiceInfo *info)
+{
+ int i;
+
+ g_object_unref (info->proxy);
+ kill (info->pid, SIGTERM);
+
+ /* Wait until the bus notices the service is gone */
+ for (i = 100; i > 0; i--) {
+ if (!name_exists (info->bus, "org.freedesktop.NetworkManager"))
+ break;
+ g_usleep (G_USEC_PER_SEC / 50);
+ }
+ g_assert (i > 0);
+
+ g_object_unref (info->bus);
+ close (info->keepalive_fd);
+
+ memset (info, 0, sizeof (*info));
+ g_free (info);
+}
diff --git a/libnm/tests/common.h b/libnm/tests/common.h
new file mode 100644
index 0000000000..7c49d2532b
--- /dev/null
+++ b/libnm/tests/common.h
@@ -0,0 +1,31 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2014 Red Hat, Inc.
+ */
+
+#include <gio/gio.h>
+
+typedef struct {
+ GDBusConnection *bus;
+ GDBusProxy *proxy;
+ GPid pid;
+ int keepalive_fd;
+} NMTestServiceInfo;
+
+NMTestServiceInfo *nm_test_service_init (void);
+void nm_test_service_cleanup (NMTestServiceInfo *info);
diff --git a/libnm/tests/test-nm-client.c b/libnm/tests/test-nm-client.c
new file mode 100644
index 0000000000..eebf8358c7
--- /dev/null
+++ b/libnm/tests/test-nm-client.c
@@ -0,0 +1,898 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2010 - 2014 Red Hat, Inc.
+ *
+ */
+
+#include <dbus/dbus.h>
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+#include <glib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <signal.h>
+
+#include <NetworkManager.h>
+#include "nm-client.h"
+#include "nm-device-wifi.h"
+#include "nm-device-ethernet.h"
+#include "nm-device-wimax.h"
+#include "nm-glib-compat.h"
+
+#include "common.h"
+
+static GMainLoop *loop = NULL;
+static NMTestServiceInfo *sinfo;
+
+/*******************************************************************/
+
+static NMClient *
+test_client_new (void)
+{
+ NMClient *client;
+ DBusGConnection *bus;
+ GError *error = NULL;
+
+ bus = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
+ g_assert_no_error (error);
+
+ client = g_object_new (NM_TYPE_CLIENT,
+ NM_OBJECT_DBUS_CONNECTION, bus,
+ NM_OBJECT_DBUS_PATH, NM_DBUS_PATH,
+ NULL);
+ g_assert (client != NULL);
+
+ dbus_g_connection_unref (bus);
+
+ g_initable_init (G_INITABLE (client), NULL, &error);
+ g_assert_no_error (error);
+
+ return client;
+}
+
+/*******************************************************************/
+
+static gboolean
+loop_quit (gpointer user_data)
+{
+ g_main_loop_quit ((GMainLoop *) user_data);
+ return G_SOURCE_REMOVE;
+}
+
+static gboolean
+add_device (const char *method, const char *ifname, char **out_path)
+{
+ GError *error = NULL;
+ GVariant *ret;
+
+ ret = g_dbus_proxy_call_sync (sinfo->proxy,
+ method,
+ g_variant_new ("(s)", ifname),
+ G_DBUS_CALL_FLAGS_NO_AUTO_START,
+ 3000,
+ NULL,
+ &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+ g_assert_cmpstr (g_variant_get_type_string (ret), ==, "(o)");
+ if (out_path)
+ g_variant_get (ret, "(o)", out_path);
+ g_variant_unref (ret);
+ return TRUE;
+}
+
+/*******************************************************************/
+
+typedef struct {
+ GMainLoop *loop;
+ gboolean signaled;
+ gboolean notified;
+ guint quit_count;
+ guint quit_id;
+} DeviceAddedInfo;
+
+static void
+device_add_check_quit (DeviceAddedInfo *info)
+{
+ info->quit_count--;
+ if (info->quit_count == 0) {
+ g_source_remove (info->quit_id);
+ info->quit_id = 0;
+ g_main_loop_quit (info->loop);
+ }
+}
+
+static void
+device_added_cb (NMClient *c,
+ NMDevice *device,
+ DeviceAddedInfo *info)
+{
+ g_assert (device);
+ g_assert_cmpstr (nm_device_get_iface (device), ==, "eth0");
+ info->signaled = TRUE;
+ device_add_check_quit (info);
+}
+
+static void
+devices_notify_cb (NMClient *c,
+ GParamSpec *pspec,
+ DeviceAddedInfo *info)
+{
+ const GPtrArray *devices;
+ NMDevice *device;
+
+ devices = nm_client_get_devices (c);
+ g_assert (devices);
+ g_assert_cmpint (devices->len, ==, 1);
+
+ device = g_ptr_array_index (devices, 0);
+ g_assert (device);
+ g_assert_cmpstr (nm_device_get_iface (device), ==, "eth0");
+
+ info->notified = TRUE;
+
+ device_add_check_quit (info);
+}
+
+static void
+test_device_added (void)
+{
+ NMClient *client;
+ const GPtrArray *devices;
+ NMDevice *device;
+ DeviceAddedInfo info = { loop, FALSE, FALSE, 0, 0 };
+
+ sinfo = nm_test_service_init ();
+ client = test_client_new ();
+
+ devices = nm_client_get_devices (client);
+ g_assert (devices == NULL);
+
+ /* Tell the test service to add a new device */
+ add_device ("AddWiredDevice", "eth0", NULL);
+
+ g_signal_connect (client,
+ "device-added",
+ (GCallback) device_added_cb,
+ &info);
+ info.quit_count++;
+
+ g_signal_connect (client,
+ "notify::devices",
+ (GCallback) devices_notify_cb,
+ &info);
+ info.quit_count++;
+
+ /* Wait for libnm-glib to find the device */
+ info.quit_id = g_timeout_add_seconds (5, loop_quit, loop);
+ g_main_loop_run (loop);
+
+ g_assert (info.signaled);
+ g_assert (info.notified);
+
+ g_signal_handlers_disconnect_by_func (client, device_added_cb, &info);
+ g_signal_handlers_disconnect_by_func (client, devices_notify_cb, &info);
+
+ devices = nm_client_get_devices (client);
+ g_assert (devices);
+ g_assert_cmpint (devices->len, ==, 1);
+
+ device = g_ptr_array_index (devices, 0);
+ g_assert (device);
+ g_assert_cmpstr (nm_device_get_iface (device), ==, "eth0");
+
+ g_object_unref (client);
+ g_clear_pointer (&sinfo, nm_test_service_cleanup);
+}
+
+/*******************************************************************/
+
+static const char *expected_bssid = "66:55:44:33:22:11";
+
+typedef struct {
+ GMainLoop *loop;
+ gboolean found;
+ char *ap_path;
+ gboolean signaled;
+ gboolean notified;
+ guint quit_id;
+ guint quit_count;
+} WifiApInfo;
+
+static void
+wifi_check_quit (WifiApInfo *info)
+{
+ info->quit_count--;
+ if (info->quit_count == 0) {
+ g_source_remove (info->quit_id);
+ info->quit_id = 0;
+ g_main_loop_quit (info->loop);
+ }
+}
+
+static void
+wifi_device_added_cb (NMClient *c,
+ NMDevice *device,
+ WifiApInfo *info)
+{
+ g_assert_cmpstr (nm_device_get_iface (device), ==, "wlan0");
+ info->found = TRUE;
+ wifi_check_quit (info);
+}
+
+static void
+got_ap_path (WifiApInfo *info, const char *path)
+{
+ if (info->ap_path)
+ g_assert_cmpstr (info->ap_path, ==, path);
+ else
+ info->ap_path = g_strdup (path);
+}
+
+static void
+wifi_ap_added_cb (NMDeviceWifi *w,
+ NMAccessPoint *ap,
+ WifiApInfo *info)
+{
+ g_assert (ap);
+ g_assert_cmpstr (nm_access_point_get_bssid (ap), ==, expected_bssid);
+ got_ap_path (info, nm_object_get_path (NM_OBJECT (ap)));
+
+ info->signaled = TRUE;
+ wifi_check_quit (info);
+}
+
+static void
+wifi_ap_add_notify_cb (NMDeviceWifi *w,
+ GParamSpec *pspec,
+ WifiApInfo *info)
+{
+ const GPtrArray *aps;
+ NMAccessPoint *ap;
+
+ aps = nm_device_wifi_get_access_points (w);
+ g_assert (aps);
+ g_assert_cmpint (aps->len, ==, 1);
+
+ ap = g_ptr_array_index (aps, 0);
+ g_assert (ap);
+ g_assert_cmpstr (nm_access_point_get_bssid (ap), ==, "66:55:44:33:22:11");
+ got_ap_path (info, nm_object_get_path (NM_OBJECT (ap)));
+
+ info->notified = TRUE;
+ wifi_check_quit (info);
+}
+
+static void
+wifi_ap_removed_cb (NMDeviceWifi *w,
+ NMAccessPoint *ap,
+ WifiApInfo *info)
+{
+ g_assert (ap);
+ g_assert_cmpstr (info->ap_path, ==, nm_object_get_path (NM_OBJECT (ap)));
+
+ info->signaled = TRUE;
+ wifi_check_quit (info);
+}
+
+static void
+wifi_ap_remove_notify_cb (NMDeviceWifi *w,
+ GParamSpec *pspec,
+ WifiApInfo *info)
+{
+ const GPtrArray *aps;
+
+ aps = nm_device_wifi_get_access_points (w);
+ g_assert (aps == NULL);
+
+ info->notified = TRUE;
+ wifi_check_quit (info);
+}
+
+static void
+test_wifi_ap_added_removed (void)
+{
+ NMClient *client;
+ NMDeviceWifi *wifi;
+ WifiApInfo info = { loop, FALSE, FALSE, 0, 0 };
+ GVariant *ret;
+ GError *error = NULL;
+ char *expected_path = NULL;
+
+ sinfo = nm_test_service_init ();
+ client = test_client_new ();
+
+ /*************************************/
+ /* Add the wifi device */
+ add_device ("AddWifiDevice", "wlan0", NULL);
+
+ g_signal_connect (client,
+ "device-added",
+ (GCallback) wifi_device_added_cb,
+ &info);
+ info.quit_count = 1;
+
+ /* Wait for libnm-glib to find the device */
+ info.quit_id = g_timeout_add_seconds (5, loop_quit, loop);
+ g_main_loop_run (loop);
+
+ g_assert (info.found);
+ g_signal_handlers_disconnect_by_func (client, wifi_device_added_cb, &info);
+
+ wifi = (NMDeviceWifi *) nm_client_get_device_by_iface (client, "wlan0");
+ g_assert (NM_IS_DEVICE_WIFI (wifi));
+
+ /*************************************/
+ /* Add the wifi device */
+ info.signaled = FALSE;
+ info.notified = FALSE;
+ info.quit_id = 0;
+
+ ret = g_dbus_proxy_call_sync (sinfo->proxy,
+ "AddWifiAp",
+ g_variant_new ("(sss)", "wlan0", "test-ap", expected_bssid),
+ G_DBUS_CALL_FLAGS_NO_AUTO_START,
+ 3000,
+ NULL,
+ &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+ g_assert_cmpstr (g_variant_get_type_string (ret), ==, "(o)");
+ g_variant_get (ret, "(o)", &expected_path);
+ g_variant_unref (ret);
+
+ g_signal_connect (wifi,
+ "access-point-added",
+ (GCallback) wifi_ap_added_cb,
+ &info);
+ info.quit_count = 1;
+
+ g_signal_connect (wifi,
+ "notify::access-points",
+ (GCallback) wifi_ap_add_notify_cb,
+ &info);
+ info.quit_count++;
+
+ /* Wait for libnm-glib to find the AP */
+ info.quit_id = g_timeout_add_seconds (5, loop_quit, loop);
+ g_main_loop_run (loop);
+
+ g_assert (info.signaled);
+ g_assert (info.notified);
+ g_assert (info.ap_path);
+ g_assert_cmpstr (info.ap_path, ==, expected_path);
+ g_signal_handlers_disconnect_by_func (wifi, wifi_ap_added_cb, &info);
+ g_signal_handlers_disconnect_by_func (wifi, wifi_ap_add_notify_cb, &info);
+
+ /*************************************/
+ /* Remove the wifi device */
+ info.signaled = FALSE;
+ info.notified = FALSE;
+ info.quit_id = 0;
+
+ ret = g_dbus_proxy_call_sync (sinfo->proxy,
+ "RemoveWifiAp",
+ g_variant_new ("(so)", "wlan0", expected_path),
+ G_DBUS_CALL_FLAGS_NO_AUTO_START,
+ 3000,
+ NULL,
+ &error);
+ g_assert_no_error (error);
+ g_clear_pointer (&ret, g_variant_unref);
+
+ g_signal_connect (wifi,
+ "access-point-removed",
+ (GCallback) wifi_ap_removed_cb,
+ &info);
+ info.quit_count = 1;
+
+ g_signal_connect (wifi,
+ "notify::access-points",
+ (GCallback) wifi_ap_remove_notify_cb,
+ &info);
+ info.quit_count++;
+
+ /* Wait for libnm-glib to find the AP */
+ info.quit_id = g_timeout_add_seconds (5, loop_quit, loop);
+ g_main_loop_run (loop);
+
+ g_assert (info.signaled);
+ g_assert (info.notified);
+ g_signal_handlers_disconnect_by_func (wifi, wifi_ap_removed_cb, &info);
+ g_signal_handlers_disconnect_by_func (wifi, wifi_ap_remove_notify_cb, &info);
+
+ g_free (info.ap_path);
+ g_free (expected_path);
+
+ g_object_unref (client);
+ g_clear_pointer (&sinfo, nm_test_service_cleanup);
+}
+
+/*******************************************************************/
+
+static const char *expected_nsp_name = "Clear";
+
+typedef struct {
+ GMainLoop *loop;
+ gboolean found;
+ char *nsp_path;
+ gboolean signaled;
+ gboolean notified;
+ guint quit_id;
+ guint quit_count;
+} WimaxNspInfo;
+
+static void
+wimax_check_quit (WimaxNspInfo *info)
+{
+ info->quit_count--;
+ if (info->quit_count == 0) {
+ g_source_remove (info->quit_id);
+ info->quit_id = 0;
+ g_main_loop_quit (info->loop);
+ }
+}
+
+static void
+wimax_device_added_cb (NMClient *c,
+ NMDevice *device,
+ WimaxNspInfo *info)
+{
+ g_assert_cmpstr (nm_device_get_iface (device), ==, "wmx0");
+ info->found = TRUE;
+ wimax_check_quit (info);
+}
+
+static void
+got_nsp_path (WimaxNspInfo *info, const char *path)
+{
+ if (info->nsp_path)
+ g_assert_cmpstr (info->nsp_path, ==, path);
+ else
+ info->nsp_path = g_strdup (path);
+}
+
+static void
+wimax_nsp_added_cb (NMDeviceWimax *w,
+ NMWimaxNsp *nsp,
+ WimaxNspInfo *info)
+{
+ g_assert (nsp);
+ g_assert_cmpstr (nm_wimax_nsp_get_name (nsp), ==, expected_nsp_name);
+ got_nsp_path (info, nm_object_get_path (NM_OBJECT (nsp)));
+
+ info->signaled = TRUE;
+ wimax_check_quit (info);
+}
+
+static void
+wimax_nsp_add_notify_cb (NMDeviceWimax *w,
+ GParamSpec *pspec,
+ WimaxNspInfo *info)
+{
+ const GPtrArray *nsps;
+ NMWimaxNsp *nsp;
+
+ nsps = nm_device_wimax_get_nsps (w);
+ g_assert (nsps);
+ g_assert_cmpint (nsps->len, ==, 1);
+
+ nsp = g_ptr_array_index (nsps, 0);
+ g_assert (nsp);
+ g_assert_cmpstr (nm_wimax_nsp_get_name (nsp), ==, expected_nsp_name);
+ got_nsp_path (info, nm_object_get_path (NM_OBJECT (nsp)));
+
+ info->notified = TRUE;
+ wimax_check_quit (info);
+}
+
+static void
+wimax_nsp_removed_cb (NMDeviceWimax *w,
+ NMWimaxNsp *nsp,
+ WimaxNspInfo *info)
+{
+ g_assert (nsp);
+ g_assert_cmpstr (info->nsp_path, ==, nm_object_get_path (NM_OBJECT (nsp)));
+
+ info->signaled = TRUE;
+ wimax_check_quit (info);
+}
+
+static void
+wimax_nsp_remove_notify_cb (NMDeviceWimax *w,
+ GParamSpec *pspec,
+ WimaxNspInfo *info)
+{
+ const GPtrArray *nsps;
+
+ nsps = nm_device_wimax_get_nsps (w);
+ g_assert (nsps == NULL);
+
+ info->notified = TRUE;
+ wimax_check_quit (info);
+}
+
+static void
+test_wimax_nsp_added_removed (void)
+{
+ NMClient *client;
+ NMDeviceWimax *wimax;
+ WimaxNspInfo info = { loop, FALSE, FALSE, 0, 0 };
+ GVariant *ret;
+ GError *error = NULL;
+ char *expected_path = NULL;
+
+ sinfo = nm_test_service_init ();
+ client = test_client_new ();
+
+ /*************************************/
+ /* Add the wimax device */
+ add_device ("AddWimaxDevice", "wmx0", NULL);
+
+ g_signal_connect (client,
+ "device-added",
+ (GCallback) wimax_device_added_cb,
+ &info);
+ info.quit_count = 1;
+
+ /* Wait for libnm-glib to find the device */
+ info.quit_id = g_timeout_add_seconds (5, loop_quit, loop);
+ g_main_loop_run (loop);
+
+ g_assert (info.found);
+ g_signal_handlers_disconnect_by_func (client, wimax_device_added_cb, &info);
+
+ wimax = (NMDeviceWimax *) nm_client_get_device_by_iface (client, "wmx0");
+ g_assert (NM_IS_DEVICE_WIMAX (wimax));
+
+ /*************************************/
+ /* Add the wimax NSP */
+ info.signaled = FALSE;
+ info.notified = FALSE;
+ info.quit_id = 0;
+
+ ret = g_dbus_proxy_call_sync (sinfo->proxy,
+ "AddWimaxNsp",
+ g_variant_new ("(ss)", "wmx0", expected_nsp_name),
+ G_DBUS_CALL_FLAGS_NO_AUTO_START,
+ 3000,
+ NULL,
+ &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+ g_assert_cmpstr (g_variant_get_type_string (ret), ==, "(o)");
+ g_variant_get (ret, "(o)", &expected_path);
+ g_variant_unref (ret);
+
+ g_signal_connect (wimax,
+ "nsp-added",
+ (GCallback) wimax_nsp_added_cb,
+ &info);
+ info.quit_count = 1;
+
+ g_signal_connect (wimax,
+ "notify::nsps",
+ (GCallback) wimax_nsp_add_notify_cb,
+ &info);
+ info.quit_count++;
+
+ /* Wait for libnm-glib to find the AP */
+ info.quit_id = g_timeout_add_seconds (5, loop_quit, loop);
+ g_main_loop_run (loop);
+
+ g_assert (info.signaled);
+ g_assert (info.notified);
+ g_assert (info.nsp_path);
+ g_assert_cmpstr (info.nsp_path, ==, expected_path);
+ g_signal_handlers_disconnect_by_func (wimax, wimax_nsp_added_cb, &info);
+ g_signal_handlers_disconnect_by_func (wimax, wimax_nsp_add_notify_cb, &info);
+
+ /*************************************/
+ /* Remove the wimax NSP */
+ info.signaled = FALSE;
+ info.notified = FALSE;
+ info.quit_id = 0;
+
+ ret = g_dbus_proxy_call_sync (sinfo->proxy,
+ "RemoveWimaxNsp",
+ g_variant_new ("(so)", "wmx0", expected_path),
+ G_DBUS_CALL_FLAGS_NO_AUTO_START,
+ 3000,
+ NULL,
+ &error);
+ g_assert_no_error (error);
+ g_clear_pointer (&ret, g_variant_unref);
+
+ g_signal_connect (wimax,
+ "nsp-removed",
+ (GCallback) wimax_nsp_removed_cb,
+ &info);
+ info.quit_count = 1;
+
+ g_signal_connect (wimax,
+ "notify::nsps",
+ (GCallback) wimax_nsp_remove_notify_cb,
+ &info);
+ info.quit_count++;
+
+ /* Wait for libnm-glib to find the AP */
+ info.quit_id = g_timeout_add_seconds (5, loop_quit, loop);
+ g_main_loop_run (loop);
+
+ g_assert (info.signaled);
+ g_assert (info.notified);
+ g_signal_handlers_disconnect_by_func (wimax, wimax_nsp_removed_cb, &info);
+ g_signal_handlers_disconnect_by_func (wimax, wimax_nsp_remove_notify_cb, &info);
+
+ g_free (info.nsp_path);
+ g_free (expected_path);
+
+ g_object_unref (client);
+ g_clear_pointer (&sinfo, nm_test_service_cleanup);
+}
+
+/*******************************************************************/
+
+typedef struct {
+ GMainLoop *loop;
+ gboolean signaled;
+ gboolean notified;
+ guint quit_count;
+ guint quit_id;
+} DaInfo;
+
+static void
+da_check_quit (DaInfo *info)
+{
+ info->quit_count--;
+ if (info->quit_count == 0) {
+ g_source_remove (info->quit_id);
+ info->quit_id = 0;
+ g_main_loop_quit (info->loop);
+ }
+}
+
+static void
+da_device_added_cb (NMClient *c,
+ NMDevice *device,
+ DaInfo *info)
+{
+ da_check_quit (info);
+}
+
+static void
+da_device_removed_cb (NMClient *c,
+ NMDevice *device,
+ DaInfo *info)
+{
+ g_assert_cmpstr (nm_device_get_iface (device), ==, "eth0");
+ info->signaled = TRUE;
+ da_check_quit (info);
+}
+
+static void
+da_devices_notify_cb (NMClient *c,
+ GParamSpec *pspec,
+ DaInfo *info)
+{
+ const GPtrArray *devices;
+ NMDevice *device;
+ guint i;
+ const char *iface;
+
+ devices = nm_client_get_devices (c);
+ g_assert (devices);
+ g_assert_cmpint (devices->len, ==, 2);
+
+ for (i = 0; i < devices->len; i++) {
+ device = g_ptr_array_index (devices, i);
+ iface = nm_device_get_iface (device);
+
+ g_assert (!strcmp (iface, "wlan0") || !strcmp (iface, "eth1"));
+ }
+
+ info->notified = TRUE;
+ da_check_quit (info);
+}
+
+static void
+test_devices_array (void)
+{
+ NMClient *client;
+ DaInfo info = { loop };
+ char *paths[3] = { NULL, NULL, NULL };
+ NMDevice *device;
+ const GPtrArray *devices;
+ GError *error = NULL;
+ GVariant *ret;
+
+ sinfo = nm_test_service_init ();
+ client = test_client_new ();
+
+ /*************************************/
+ /* Add some devices */
+ add_device ("AddWifiDevice", "wlan0", &paths[0]);
+ add_device ("AddWiredDevice", "eth0", &paths[1]);
+ add_device ("AddWiredDevice", "eth1", &paths[2]);
+ info.quit_count = 3;
+
+ g_signal_connect (client,
+ "device-added",
+ (GCallback) da_device_added_cb,
+ &info);
+
+ /* Wait for libnm-glib to find the device */
+ info.quit_id = g_timeout_add_seconds (5, loop_quit, loop);
+ g_main_loop_run (loop);
+
+ g_assert_cmpint (info.quit_count, ==, 0);
+ g_signal_handlers_disconnect_by_func (client, da_device_added_cb, &info);
+
+ /* Ensure the devices now exist */
+ devices = nm_client_get_devices (client);
+ g_assert (devices);
+ g_assert_cmpint (devices->len, ==, 3);
+
+ device = nm_client_get_device_by_iface (client, "wlan0");
+ g_assert (NM_IS_DEVICE_WIFI (device));
+
+ device = nm_client_get_device_by_iface (client, "eth0");
+ g_assert (NM_IS_DEVICE_ETHERNET (device));
+
+ device = nm_client_get_device_by_iface (client, "eth1");
+ g_assert (NM_IS_DEVICE_ETHERNET (device));
+
+ /********************************/
+ /* Now remove the device in the middle */
+ ret = g_dbus_proxy_call_sync (sinfo->proxy,
+ "RemoveDevice",
+ g_variant_new ("(o)", paths[1]),
+ G_DBUS_CALL_FLAGS_NO_AUTO_START,
+ 3000,
+ NULL,
+ &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+ g_variant_unref (ret);
+
+ g_signal_connect (client,
+ "device-removed",
+ (GCallback) da_device_removed_cb,
+ &info);
+
+ g_signal_connect (client,
+ "notify::devices",
+ (GCallback) da_devices_notify_cb,
+ &info);
+ info.quit_count = 2;
+
+ /* Wait for libnm-glib to find the device */
+ info.quit_id = g_timeout_add_seconds (5, loop_quit, loop);
+ g_main_loop_run (loop);
+
+ g_assert_cmpint (info.quit_count, ==, 0);
+ g_signal_handlers_disconnect_by_func (client, da_device_removed_cb, &info);
+ g_signal_handlers_disconnect_by_func (client, da_devices_notify_cb, &info);
+
+ /* Ensure only two are left */
+ devices = nm_client_get_devices (client);
+ g_assert (devices);
+ g_assert_cmpint (devices->len, ==, 2);
+
+ device = nm_client_get_device_by_iface (client, "wlan0");
+ g_assert (NM_IS_DEVICE_WIFI (device));
+
+ device = nm_client_get_device_by_iface (client, "eth1");
+ g_assert (NM_IS_DEVICE_ETHERNET (device));
+
+ g_free (paths[0]);
+ g_free (paths[1]);
+ g_free (paths[2]);
+
+ g_object_unref (client);
+ g_clear_pointer (&sinfo, nm_test_service_cleanup);
+}
+
+static void
+manager_running_changed (GObject *client,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ int *running_changed = user_data;
+
+ (*running_changed)++;
+ g_main_loop_quit (loop);
+}
+
+static void
+test_client_manager_running (void)
+{
+ NMClient *client1, *client2;
+ guint quit_id;
+ int running_changed = 0;
+ GError *error = NULL;
+
+ client1 = test_client_new ();
+
+ g_assert (!nm_client_get_manager_running (client1));
+ g_assert_cmpstr (nm_client_get_version (client1), ==, NULL);
+
+ g_assert (!nm_client_networking_get_enabled (client1));
+ /* This will have no effect, but it shouldn't cause any warnings either. */
+ nm_client_networking_set_enabled (client1, TRUE);
+ g_assert (!nm_client_networking_get_enabled (client1));
+
+ /* OTOH, this should result in an error */
+ nm_client_set_logging (client1, "DEFAULT", "INFO", &error);
+ g_assert_error (error, NM_CLIENT_ERROR, NM_CLIENT_ERROR_MANAGER_NOT_RUNNING);
+ g_clear_error (&error);
+
+ /* Now start the test service. */
+ sinfo = nm_test_service_init ();
+ client2 = test_client_new ();
+
+ /* client2 should know that NM is running, but the previously-created
+ * client1 hasn't gotten the news yet.
+ */
+ g_assert (!nm_client_get_manager_running (client1));
+ g_assert (nm_client_get_manager_running (client2));
+
+ g_signal_connect (client1, "notify::" NM_CLIENT_MANAGER_RUNNING,
+ G_CALLBACK (manager_running_changed), &running_changed);
+ quit_id = g_timeout_add_seconds (5, loop_quit, loop);
+ g_main_loop_run (loop);
+ g_assert_cmpint (running_changed, ==, 1);
+ g_assert (nm_client_get_manager_running (client1));
+ g_source_remove (quit_id);
+
+ /* And kill it */
+ g_clear_pointer (&sinfo, nm_test_service_cleanup);
+
+ g_assert (nm_client_get_manager_running (client1));
+
+ quit_id = g_timeout_add_seconds (5, loop_quit, loop);
+ g_main_loop_run (loop);
+ g_assert_cmpint (running_changed, ==, 2);
+ g_assert (!nm_client_get_manager_running (client1));
+ g_source_remove (quit_id);
+
+ g_object_unref (client1);
+ g_object_unref (client2);
+}
+
+/*******************************************************************/
+
+int
+main (int argc, char **argv)
+{
+#if !GLIB_CHECK_VERSION (2, 35, 0)
+ g_type_init ();
+#endif
+
+ g_test_init (&argc, &argv, NULL);
+
+ loop = g_main_loop_new (NULL, FALSE);
+
+ g_test_add_func ("/libnm-glib/device-added", test_device_added);
+ g_test_add_func ("/libnm-glib/wifi-ap-added-removed", test_wifi_ap_added_removed);
+ g_test_add_func ("/libnm-glib/wimax-nsp-added-removed", test_wimax_nsp_added_removed);
+ g_test_add_func ("/libnm-glib/devices-array", test_devices_array);
+ g_test_add_func ("/libnm-glib/client-manager-running", test_client_manager_running);
+
+ return g_test_run ();
+}
+
diff --git a/libnm/tests/test-remote-settings-client.c b/libnm/tests/test-remote-settings-client.c
new file mode 100644
index 0000000000..987845c1bb
--- /dev/null
+++ b/libnm/tests/test-remote-settings-client.c
@@ -0,0 +1,456 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2010 - 2011 Red Hat, Inc.
+ *
+ */
+
+#include <dbus/dbus.h>
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+#include <glib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <signal.h>
+
+#include <NetworkManager.h>
+
+#include <nm-setting-connection.h>
+#include <nm-setting-wired.h>
+#include <nm-utils.h>
+
+#include "nm-remote-settings.h"
+#include "common.h"
+
+static NMTestServiceInfo *sinfo;
+static NMRemoteSettings *settings = NULL;
+DBusGConnection *bus = NULL;
+NMRemoteConnection *remote = NULL;
+
+/*******************************************************************/
+
+static void
+add_cb (NMRemoteSettings *s,
+ NMRemoteConnection *connection,
+ GError *error,
+ gpointer user_data)
+{
+ if (error)
+ g_warning ("Add error: %s", error->message);
+
+ *((gboolean *) user_data) = TRUE;
+ remote = connection;
+ g_object_add_weak_pointer (G_OBJECT (connection), (void **) &remote);
+}
+
+#define TEST_CON_ID "blahblahblah"
+
+static void
+test_add_connection (void)
+{
+ NMConnection *connection;
+ NMSettingConnection *s_con;
+ NMSettingWired *s_wired;
+ char *uuid;
+ gboolean success;
+ time_t start, now;
+ gboolean done = FALSE;
+
+ connection = nm_connection_new ();
+
+ s_con = (NMSettingConnection *) nm_setting_connection_new ();
+ uuid = nm_utils_uuid_generate ();
+ g_object_set (G_OBJECT (s_con),
+ NM_SETTING_CONNECTION_ID, TEST_CON_ID,
+ NM_SETTING_CONNECTION_UUID, uuid,
+ NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME,
+ NULL);
+ g_free (uuid);
+ nm_connection_add_setting (connection, NM_SETTING (s_con));
+
+ s_wired = (NMSettingWired *) nm_setting_wired_new ();
+ nm_connection_add_setting (connection, NM_SETTING (s_wired));
+
+ success = nm_remote_settings_add_connection (settings,
+ connection,
+ add_cb,
+ &done);
+ g_assert (success == TRUE);
+
+ start = time (NULL);
+ do {
+ now = time (NULL);
+ g_main_context_iteration (NULL, FALSE);
+ } while ((done == FALSE) && (now - start < 5));
+ g_assert (done == TRUE);
+ g_assert (remote != NULL);
+
+ /* Make sure the connection is the same as what we added */
+ g_assert (nm_connection_compare (connection,
+ NM_CONNECTION (remote),
+ NM_SETTING_COMPARE_FLAG_EXACT) == TRUE);
+}
+
+/*******************************************************************/
+
+static void
+set_visible_cb (DBusGProxy *proxy,
+ DBusGProxyCall *call,
+ gpointer user_data)
+{
+ GError *error = NULL;
+ gboolean success;
+
+ success = dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_INVALID);
+ g_assert_no_error (error);
+ g_assert (success == TRUE);
+}
+
+static void
+invis_removed_cb (NMRemoteConnection *connection, gboolean *done)
+{
+ *done = TRUE;
+}
+
+static void
+invis_has_settings_cb (NMSetting *setting,
+ const char *key,
+ const GValue *value,
+ GParamFlags flags,
+ gpointer user_data)
+{
+ *((gboolean *) user_data) = TRUE;
+}
+
+static void
+test_make_invisible (void)
+{
+ time_t start, now;
+ GSList *list, *iter;
+ DBusGProxy *proxy;
+ gboolean done = FALSE, has_settings = FALSE;
+ char *path;
+
+ g_assert (remote != NULL);
+
+ /* Listen for the remove event when the connection becomes invisible */
+ g_signal_connect (remote, "removed", G_CALLBACK (invis_removed_cb), &done);
+
+ path = g_strdup (nm_connection_get_path (NM_CONNECTION (remote)));
+ proxy = dbus_g_proxy_new_for_name (bus,
+ NM_DBUS_SERVICE,
+ path,
+ NM_DBUS_IFACE_SETTINGS_CONNECTION);
+ g_assert (proxy != NULL);
+
+ /* Bypass the NMRemoteSettings object so we can test it independently */
+ dbus_g_proxy_begin_call (proxy, "SetVisible", set_visible_cb, NULL, NULL,
+ G_TYPE_BOOLEAN, FALSE, G_TYPE_INVALID);
+
+ /* Wait for the connection to be removed */
+ start = time (NULL);
+ do {
+ now = time (NULL);
+ g_main_context_iteration (NULL, FALSE);
+ } while ((done == FALSE) && (now - start < 5));
+ g_assert (done == TRUE);
+
+ g_assert (remote);
+ g_signal_handlers_disconnect_by_func (remote, G_CALLBACK (invis_removed_cb), &done);
+
+ /* Ensure NMRemoteSettings no longer has the connection */
+ list = nm_remote_settings_list_connections (settings);
+ for (iter = list; iter; iter = g_slist_next (iter)) {
+ NMConnection *candidate = NM_CONNECTION (iter->data);
+
+ g_assert ((gpointer) remote != (gpointer) candidate);
+ g_assert (strcmp (path, nm_connection_get_path (candidate)) != 0);
+ }
+
+ /* And ensure the invisible connection no longer has any settings */
+ g_assert (remote);
+ nm_connection_for_each_setting_value (NM_CONNECTION (remote),
+ invis_has_settings_cb,
+ &has_settings);
+ g_assert (has_settings == FALSE);
+
+ g_free (path);
+ g_object_unref (proxy);
+}
+
+/*******************************************************************/
+
+static void
+vis_new_connection_cb (NMRemoteSettings *foo,
+ NMRemoteConnection *connection,
+ NMRemoteConnection **new)
+{
+ *new = connection;
+}
+
+static void
+test_make_visible (void)
+{
+ time_t start, now;
+ GSList *list, *iter;
+ DBusGProxy *proxy;
+ gboolean found = FALSE;
+ char *path;
+ NMRemoteConnection *new = NULL;
+
+ g_assert (remote != NULL);
+
+ /* Wait for the new-connection signal when the connection is visible again */
+ g_signal_connect (settings, NM_REMOTE_SETTINGS_NEW_CONNECTION,
+ G_CALLBACK (vis_new_connection_cb), &new);
+
+ path = g_strdup (nm_connection_get_path (NM_CONNECTION (remote)));
+ proxy = dbus_g_proxy_new_for_name (bus,
+ NM_DBUS_SERVICE,
+ path,
+ NM_DBUS_IFACE_SETTINGS_CONNECTION);
+ g_assert (proxy != NULL);
+
+ /* Bypass the NMRemoteSettings object so we can test it independently */
+ dbus_g_proxy_begin_call (proxy, "SetVisible", set_visible_cb, NULL, NULL,
+ G_TYPE_BOOLEAN, TRUE, G_TYPE_INVALID);
+
+
+ /* Wait for the settings service to announce the connection again */
+ start = time (NULL);
+ do {
+ now = time (NULL);
+ g_main_context_iteration (NULL, FALSE);
+ } while ((new == NULL) && (now - start < 5));
+
+ /* Ensure the new connection is the same as the one we made visible again */
+ g_assert (new);
+ g_assert (new == remote);
+
+ g_signal_handlers_disconnect_by_func (settings, G_CALLBACK (vis_new_connection_cb), &new);
+
+ /* Ensure NMRemoteSettings has the connection */
+ list = nm_remote_settings_list_connections (settings);
+ for (iter = list; iter; iter = g_slist_next (iter)) {
+ NMConnection *candidate = NM_CONNECTION (iter->data);
+
+ if ((gpointer) remote == (gpointer) candidate) {
+ g_assert_cmpstr (path, ==, nm_connection_get_path (candidate));
+ g_assert_cmpstr (TEST_CON_ID, ==, nm_connection_get_id (candidate));
+ found = TRUE;
+ break;
+ }
+ }
+ g_assert (found == TRUE);
+
+ g_free (path);
+ g_object_unref (proxy);
+}
+
+/*******************************************************************/
+
+static void
+deleted_cb (DBusGProxy *proxy,
+ DBusGProxyCall *call,
+ gpointer user_data)
+{
+ GError *error = NULL;
+ gboolean success;
+
+ success = dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_INVALID);
+ g_assert_no_error (error);
+ g_assert (success == TRUE);
+}
+
+static void
+removed_cb (NMRemoteConnection *connection, gboolean *done)
+{
+ *done = TRUE;
+}
+
+static void
+test_remove_connection (void)
+{
+ NMRemoteConnection *connection;
+ time_t start, now;
+ GSList *list, *iter;
+ DBusGProxy *proxy;
+ gboolean done = FALSE;
+ char *path;
+
+ /* Find a connection to delete */
+ list = nm_remote_settings_list_connections (settings);
+ g_assert_cmpint (g_slist_length (list), >, 0);
+
+ connection = NM_REMOTE_CONNECTION (list->data);
+ g_assert (connection);
+ g_assert (remote == connection);
+ path = g_strdup (nm_connection_get_path (NM_CONNECTION (connection)));
+ g_signal_connect (connection, "removed", G_CALLBACK (removed_cb), &done);
+
+ proxy = dbus_g_proxy_new_for_name (bus,
+ NM_DBUS_SERVICE,
+ path,
+ NM_DBUS_IFACE_SETTINGS_CONNECTION);
+ g_assert (proxy != NULL);
+
+ /* Bypass the NMRemoteSettings object so we can test it independently */
+ dbus_g_proxy_begin_call (proxy, "Delete", deleted_cb, NULL, NULL, G_TYPE_INVALID);
+
+ start = time (NULL);
+ do {
+ now = time (NULL);
+ g_main_context_iteration (NULL, FALSE);
+ } while ((done == FALSE) && (now - start < 5));
+ g_assert (done == TRUE);
+
+ g_assert (!remote);
+
+ /* Ensure NMRemoteSettings no longer has the connection */
+ list = nm_remote_settings_list_connections (settings);
+ for (iter = list; iter; iter = g_slist_next (iter)) {
+ NMConnection *candidate = NM_CONNECTION (iter->data);
+
+ g_assert ((gpointer) connection != (gpointer) candidate);
+ g_assert_cmpstr (path, ==, nm_connection_get_path (candidate));
+ }
+
+ g_free (path);
+ g_object_unref (proxy);
+}
+
+/*******************************************************************/
+
+static GMainLoop *loop;
+
+static gboolean
+loop_quit (gpointer user_data)
+{
+ g_main_loop_quit (loop);
+ return G_SOURCE_REMOVE;
+}
+
+static void
+settings_service_running_changed (GObject *client,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ int *running_changed = user_data;
+
+ (*running_changed)++;
+ g_main_loop_quit (loop);
+}
+
+static void
+test_service_running (void)
+{
+ NMRemoteSettings *settings2;
+ guint quit_id;
+ int running_changed = 0;
+ gboolean running;
+
+ loop = g_main_loop_new (NULL, FALSE);
+
+ g_object_get (G_OBJECT (settings),
+ NM_REMOTE_SETTINGS_SERVICE_RUNNING, &running,
+ NULL);
+ g_assert (running == TRUE);
+
+ /* Now kill the test service. */
+ nm_test_service_cleanup (sinfo);
+
+ settings2 = nm_remote_settings_new (bus);
+
+ /* settings2 should know that NM is running, but the previously-created
+ * settings hasn't gotten the news yet.
+ */
+ g_object_get (G_OBJECT (settings2),
+ NM_REMOTE_SETTINGS_SERVICE_RUNNING, &running,
+ NULL);
+ g_assert (running == FALSE);
+ g_object_get (G_OBJECT (settings),
+ NM_REMOTE_SETTINGS_SERVICE_RUNNING, &running,
+ NULL);
+ g_assert (running == TRUE);
+
+ g_signal_connect (settings, "notify::" NM_REMOTE_SETTINGS_SERVICE_RUNNING,
+ G_CALLBACK (settings_service_running_changed), &running_changed);
+ quit_id = g_timeout_add_seconds (5, loop_quit, loop);
+ g_main_loop_run (loop);
+ g_assert_cmpint (running_changed, ==, 1);
+ g_source_remove (quit_id);
+
+ g_object_get (G_OBJECT (settings2),
+ NM_REMOTE_SETTINGS_SERVICE_RUNNING, &running,
+ NULL);
+ g_assert (running == FALSE);
+
+ /* Now restart it */
+ sinfo = nm_test_service_init ();
+
+ quit_id = g_timeout_add_seconds (5, loop_quit, loop);
+ g_main_loop_run (loop);
+ g_assert_cmpint (running_changed, ==, 2);
+ g_source_remove (quit_id);
+
+ g_object_get (G_OBJECT (settings2),
+ NM_REMOTE_SETTINGS_SERVICE_RUNNING, &running,
+ NULL);
+ g_assert (running == TRUE);
+
+ g_object_unref (settings2);
+}
+
+/*******************************************************************/
+
+int
+main (int argc, char **argv)
+{
+ int ret;
+ GError *error = NULL;
+
+#if !GLIB_CHECK_VERSION (2, 35, 0)
+ g_type_init ();
+#endif
+
+ g_test_init (&argc, &argv, NULL);
+
+ bus = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
+ g_assert_no_error (error);
+
+ sinfo = nm_test_service_init ();
+
+ settings = nm_remote_settings_new (bus);
+ g_assert (settings != NULL);
+
+ /* FIXME: these tests assume that they get run in order, but g_test_run()
+ * does not actually guarantee that!
+ */
+ g_test_add_func ("/remote_settings/add_connection", test_add_connection);
+ g_test_add_func ("/remote_settings/make_invisible", test_make_invisible);
+ g_test_add_func ("/remote_settings/make_visible", test_make_visible);
+ g_test_add_func ("/remote_settings/remove_connection", test_remove_connection);
+ g_test_add_func ("/remote_settings/service_running", test_service_running);
+
+ ret = g_test_run ();
+
+ nm_test_service_cleanup (sinfo);
+ g_object_unref (settings);
+ dbus_g_connection_unref (bus);
+
+ return ret;
+}
+