summaryrefslogtreecommitdiff
path: root/libnm
diff options
context:
space:
mode:
authorDan Winship <danw@gnome.org>2014-07-24 08:53:33 -0400
committerDan Winship <danw@gnome.org>2014-08-01 14:34:04 -0400
commitd595f7843e31e8312c0baf25b476b6489cff59a7 (patch)
tree3c078ff077d3486af4aa4382189066f3cfba40fd /libnm
parentc123a24dc472b867d9e8a0f83776d258d156f0c1 (diff)
downloadNetworkManager-d595f7843e31e8312c0baf25b476b6489cff59a7.tar.gz
libnm: add libnm/libnm-core (part 1)
This commit begins creating the new "libnm", which will replace libnm-util and libnm-glib. The main reason for the libnm-util/libnm-glib split is that the daemon needs to link to libnm-util (to get NMSettings, NMConnection, etc), but can't link to libnm-glib (because it uses many of the same type names as the NetworkManager daemon. eg, NMDevice). So the daemon links to only libnm-util, but basically all clients link to both. With libnm, there will be only a single client-visible library, and NetworkManager will internally link against a private "libnm-core" containing the parts that used to be in libnm-util. (The "libnm-core" parts still need to be in their own directory so that the daemon can see those header files without also seeing the ones in libnm/ that conflict with its own headers.) [This commit just copies the source code from libnm-util/ to libnm-core/, and libnm-glib/ to libnm/: mkdir -p libnm-core/tests/ mkdir -p libnm/tests/ cp libnm-util/*.[ch] libnm-util/nm-version.h.in libnm-core/ rm -f libnm-core/nm-version.h libnm-core/nm-setting-template.[ch] libnm-core/nm-utils-enum-types.[ch] cp libnm-util/tests/*.[ch] libnm-core/tests/ cp libnm-glib/*.[ch] libnm/ rm -f libnm/libnm_glib.[ch] libnm/libnm-glib-test.c libnm/nm-glib-enum-types.[ch] cp libnm-glib/tests/*.[ch] libnm/tests/ ]
Diffstat (limited to 'libnm')
-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
74 files changed, 25833 insertions, 0 deletions
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;
+}
+